Список форумов forum.radioforex.ru
 FAQFAQ   ПоискПоиск   ПользователиПользователи   ГруппыГруппы   РегистрацияРегистрация 
 ПрофильПрофиль   Войти и проверить личные сообщенияВойти и проверить личные сообщения   ВходВход 

Уроки языка MQL4
На страницу Пред.  1, 2, 3, 4  След.
 
Начать новую тему   Эта тема закрыта, вы не можете писать ответы и редактировать сообщения.    Список форумов forum.radioforex.ru -> Forex для начинающих
Предыдущая тема :: Следующая тема  
Автор Сообщение
scriptong
Модератор RadioFOREX.ru


Зарегистрирован: 30.05.2007
Сообщения: 652
Откуда: Украина

СообщениеДобавлено: Пн Янв 28, 2008 13:11    Заголовок сообщения: Урок 9. Продолжение. Ответить с цитатой

Первые две строки пояснений не требуют, так как точно такие же имеются в советниках. В третьей строке используется специальный параметр indicator_chart_window, выводящий значения индикатора в окно графика цен. Противоположный ему параметр называется indicator_separate_window. Он как раз и отвечает за вывод индикатора в отдельное окно.
Следующий специальный параметр indicator_buffers задает количество используемых в индикаторе массивов (буферов), в данном случае количество типов стрелок. Хотя индикатор может представлять собой еще гистограммы и линии.
Специальные параметры indicator_color1 и indicator_color2 определяют цвет отображаемых значений для первого буфера данных, использующийся по умолчанию. Пользователь всегда может его изменить во вкладке «Цвета» на любой другой.
Далее следуют входные параметры, тут пояснений не требуется.
И, наконец, объявление буферов, значения которых и будет отображать индикатор:
Код:

double ExtMapBuffer1[];
double ExtMapBuffer2[];

Такое объявление означает, что ExtMapBuffer1 представляет собой безразмерный массив вещественных чисел. Безразмерный он потому, что с каждым новым баром в него будет добавляться один элемент.
С шапкой разобрались. Переходим к функциям. Первая, как всегда, функция init. В ней мы видим четыре типа функций. В первой строке используется функция, имеющая такой синтаксис:
Код:

void SetIndexStyle(int index, int type, int style, int width, color clr)

Index – номер буфера, параметры которого изменяются (считается с нуля)
Type – тип отрисовки индикатора. Может принимать такие значения:
Код:

Константа        Значение   Описание
DRAW_LINE               0   Простая линия
DRAW_SECTION   1   Отрезки между непустыми значениями линии
DRAW_HISTOGRAM   2   Гистограмма
DRAW_ARROW               3   Стрелки (символы)
DRAW_ZIGZAG               4   Отрезки между непустыми значениями чётной и нечётной линий (зигзаг)
DRAW_NONE               12   Отсутствие какого-либо рисования

В нашем случае используется тип Arrow - стрелка.
Style – стиль отрисовки линии толщиной в 1 пиксель. Может принимать такие значения:
Код:

Константа        Значение   Описание
STYLE_SOLID               0   Сплошная линия
STYLE_DASH               1   Штриховая линия
STYLE_DOT               2   Пунктирная линия
STYLE_DASHDOT   3   Штрихпунктирная линия
STYLE_DASHDOTDOT   4   Штрихпунктирная линия с двойными точками

Этот параметр, если не указан, принимается равным 0, т. е. сплошная линия, или не происходит изменение текущего параметра при повторном вызове функции.
Width – толщина линии от 1 до 5. Если параметр не указан, то толщина линии изменяться не будет или принимается равным 1 при первом вызове функции.
Color – цвет линии. Если не указан, то принимает значение, заданное в параметре indicator_colorN.
Итак, функция SetIndexStyle задает стиль отрисовки значений каждого буфера. В нашем случае задан номер буфера 0 (т. е. первый буфер) и тип отрисовки – стрелка. Стиль, толщина и цвет принимают значения по умолчанию. Отредактируем параметры функции. В данном случае нас интересует значение толщины (в данном случае величины) стрелки. Для этого в параметры обеих функций дописываем третий и четвертый параметры:
Код:

SetIndexStyle(0,DRAW_ARROW, EMPTY, 3);
SetIndexStyle(1,DRAW_ARROW, EMPTY, 3);
Вернуться к началу
Посмотреть профиль Отправить личное сообщение Посетить сайт автора
scriptong
Модератор RadioFOREX.ru


Зарегистрирован: 30.05.2007
Сообщения: 652
Откуда: Украина

СообщениеДобавлено: Пн Янв 28, 2008 13:16    Заголовок сообщения: Урок 9. Продолжение. Ответить с цитатой

Теперь рассмотрим вторую функцию, расположенную в блоке инициализации:
Код:

void SetIndexArrow(int index, int code);

При ее помощи выбирается символ для нужного буфера. В параметре index задается номер буфера, а в параметре code – код нужного символа. Для стрелки вверх код 241, для стрелки вниз – 242.
Третья функция связывает индекс индикатора с буфером и изменение значений любого элемента массива будет отображаться индикатором:
Код:

bool SetIndexBuffer(int index, double array[])

Index – порядковый номер буфера.
Array[] – ссылка на массив, который будет связан с расчетным буфером.

Четвертая функция задает значения (value) по умолчанию для всех элементов массива (index):
Код:

void SetIndexEmptyValue(int index, int value);

Тело функции deinit пока пустое.
А вот в функции start уже есть некий код:
Код:

int    counted_bars=IndicatorCounted();

Переменной counted_bars присваивается значение функции IndicatorCounted. Эта функция возвращает количество баров, которые остались неизменными после последнего вызова функции start. Для чего это нужно? Дело в том, что индикатор на каждом тике должен полностью себя перерисовать в соответствии с новыми данными. Представьте, что максимальное количество баров в МТ4 установлено 250 000. И на каждом тике происходит пересчет значения на каждом из этих 250 000 баров. Хорошо, если вычисления простые и не занимают много машинного времени. А если сложные, громоздкие, да еще и производительность системы низкая? Такая ситуация может привести к существенному замедлению работы компьютера, не говоря уж о работе в МТ4. Поэтому, вызвав IndicatorCounted, можно вычислить сколько баров необходимо пересчитать:
Код:

int limit = Bars-counted_bars;

Эту строку сазу же добавляем в тело функции start. Единственный технический момент. Бывают случаи, когда последний тик предыдущего бара не был пересчитан. Например, была высокая частота тиков при выходе новостей и приход последнего тика пришелся на обработку предпоследнего тика. Поэтому желательно предпослений бар всегда пересчитывать. Исходя из этого, перед расчетом переменной limit обычно вставляют такую строку:
Код:

if(counted_bars>0) counted_bars--;

Counted_bars может быть равной нулю при первом вызове функции start, поэтому и была вставлена проверка на неравенство нулю.
Еще один момент. Раз есть у нас какой-то период для расчета значений, а это и является количеством баров, то необходимо проверять, есть ли для анализа нужное количество баров. Поэтому значение переменной limit всегда должно быть больше каждого периода во входных параметрах. Это делается таким образом:
Код:

if(limit > Bars-MathMax(MAFastPeriod, MASlowPeriod))
     limit = Bars-MathMax(MAFastPeriod, MASlowPeriod);
Вернуться к началу
Посмотреть профиль Отправить личное сообщение Посетить сайт автора
scriptong
Модератор RadioFOREX.ru


Зарегистрирован: 30.05.2007
Сообщения: 652
Откуда: Украина

СообщениеДобавлено: Пн Янв 28, 2008 13:18    Заголовок сообщения: Урок 9. Продолжение. Ответить с цитатой

Вычисляется максимальное значение из входных параметров, отнимается от Bars и с этим значением сравнивается limit. То есть нельзя, чтобы limit принимало значение близкое к Bars, так как расчет будет некорректным.
Итак, подготовительные работы закончены, пора переходить непосредственно к расчетам значения индикатора. Для начала неплохо бы определиться, что именно мы будем рассчитывать. Одной из самых распространенных задач является обозначение пересечений средних, подобно тому, как это мы делали в примере советника SimpleMA3. Но так как это индикатор, вместо сделок у нас будут наноситься стрелки на график – синяя стрелка вверх ниже свечи, если быстрая МА пересечет медленную снизу вверх, и красная стрелка вниз, если быстрая МА пересекла медленную сверху вниз.
Для того чтобы отобразить все значения в истории, необходимо в цикле произвести расчет для каждого бара. При первом вызове функции start переменная limit будет равна Bars-MAFastPeriod (если пользователь выставит верно, т. е. MAFastPeriod < MASlowPeriod). Если произвести расчеты для всех баров от limit до нулевого, то получим значения индикатора на протяжении всей истории. В дальнейшем потребуется производить расчет только для одного бара, того, который обновляется с каждым тиком, то есть нулевого бара.
В теле цикла будет происходить вычисление значения медленной и быстрой средних за последние два бара и сравниваться на предмет пересечения. В зависимости от типа пересечения на открытии нового бара будет присваиваться значение соответствующему буферу, в результате чего появится стрелка:
Код:

for(int i = limit; i >= 0; i--)
    {
     double MAF1 = iMA(Symbol(), 0, MAFastPeriod, 0, MODE_EMA, PRICE_CLOSE, i+1);
     double MAF2 = iMA(Symbol(), 0, MAFastPeriod, 0, MODE_EMA, PRICE_CLOSE, i+2);
     double MAS1 = iMA(Symbol(), 0, MASlowPeriod, 0, MODE_EMA, PRICE_CLOSE, i+1);
     double MAS2 = iMA(Symbol(), 0, MASlowPeriod, 0, MODE_EMA, PRICE_CLOSE, i+2);
     if(MAF2 > MAS2 && MAF1 < MAS1)
      ExtMapBuffer2[i] = High[i]+20*Point;
     if(MAF2 < MAS2 && MAF1 > MAS1)
      ExtMapBuffer1[i] = Low[i]-20*Point;
     }


Как видно из кода, стрелка будет отстоять от максимума или минимума свечи на 20 пунктов. Этот параметр также можно вынести во входные параметры, так как на разных временных периодах отступ нужно делать отличный от других. Другим распространенным способом решения данной проблемы является использование значение волатильности за некий период (допустим, 14). Такой индикатор есть во встроенных средствах МТ4, это ATR. Вызывается он просто:
Код:

double iATR(string symbol(), int timeframe, int period, int shift)

Поэтому вместо 20*Point можно записать строки присвоения значений индикатору так:
Код:

ExtMapBuffer2[i] = High[i]+iATR(Symbol(), 0, 14, i);
ExtMapBuffer1[i] = Low[i]-iATR(Symbol(), 0, 14, i);

С помощью индикаторов намного удобнее производить оценку работоспособности стратегии, так как можно на истории визуально определить, где входы были правильными, а где нет. Индикатор, к тому же, написать немного проще, так как не нужно производить учет ордеров и позиций, как в советнике, занимаясь только самой техникой входов.



SimpleIndicator.zip
 Описание:

Скачать
 Имя файла:  SimpleIndicator.zip
 Размер файла:  766 Байт
 Скачано:  574 раз(а)

Вернуться к началу
Посмотреть профиль Отправить личное сообщение Посетить сайт автора
scriptong
Модератор RadioFOREX.ru


Зарегистрирован: 30.05.2007
Сообщения: 652
Откуда: Украина

СообщениеДобавлено: Пт Фев 15, 2008 19:22    Заголовок сообщения: Урок 10. "Рисование объектов" Ответить с цитатой

Урок 10. "Рисование объектов"

Наряду с использованием буферов индикаторов, можно напрямую рисовать графические объекты в окне графика цены. Простейшим примером таких объектов являются вертикальные и горизонтальные линии. Их можно построить вручную при помощи пункта меню МТ4 «Вставка - Линии». Я же предлагаю рассмотреть возможность построения (рисования) объектов программно.
Арсенал графических объектов в МТ4 такой:

1. Линии
2. Каналы
3. Объекты Ганна
4. Объекты Фибоначчи
5. Геометрические фигуры (прямоугольники, треугольники, эллипсы)
6. Значки (стрелки и т. д.)

Тут же замечу, что рисовать объекты можно как в индикаторах, так и в советниках и скриптах. Для начала попытаемся строить объекты при работе индикатора. Создайте индикатор с названием CurrentTrend без добавления буферов индикатора с отображением в поле графика цены.
Вкратце определю содержание создаваемого индикатора. На заданном таймфрейме будем искать два последних Up-фрактала и два Down-фрактала. Если они расположены согласованно (образуют четкий нисходящий или восходящий тренд), то индикатор обозначит такой тренд каналом. Для отображения канала удобно использовать объект «Равноудаленный канал». Итак, для задания используемого таймфрейма пользователем, создаем переменную:
Код:

extern string    A1="Период, по которому считаются фракталы";
extern string    A2="H1 - 60, H4 - 240, D1 - 1440, W1 - 10080";
extern int       FractalTF=240;

Первые две переменные служат для объяснения пользователю назначения переменной FractalTF.
Технология работы с объектами довольно простая. Сначала объект нужно создать, потом можно его перемещать или менять свойства, а по окончании работы – удалить. По крайней мере, это является признаком хорошего тона в программировании – за собой необходимо прибирать. Отличают объекты друг от друга тоже просто – по имени. Поэтому имена объектов должны быть уникальные и не совпадать друг с другом. При попытке создания объекта с именем уже существующего объекта, возникнет ошибка. Создание объекта производится очень понятной по названию функцией:
Код:

bool ObjectCreate(string name, int type, int window, datetime time1, double price1, datetime time2=0, double price2=0, datetime time3=0, double price3=0)


Последний раз редактировалось: scriptong (Пт Фев 15, 2008 19:54), всего редактировалось 1 раз
Вернуться к началу
Посмотреть профиль Отправить личное сообщение Посетить сайт автора
scriptong
Модератор RadioFOREX.ru


Зарегистрирован: 30.05.2007
Сообщения: 652
Откуда: Украина

СообщениеДобавлено: Пт Фев 15, 2008 19:23    Заголовок сообщения: Урок 10. Продолжение. Ответить с цитатой

В случае успешного создания объекта будет возвращено значение True, в противном случае – False.
Строковый параметр name задает имя создаваемого объекта, которое, как и положено строке, заключается в кавычки.
Параметром type определяют тип будущего объекта. Он может принимать довольно обширный список значений:

    Константа Значение Описание
    OBJ_VLINE 0 Вертикальная линия. Использует время в качестве первой координаты, цена игнорируется
    OBJ_HLINE 1 Горизонтальная линия. Использует цену в качестве первой координаты, время игнорируется
    OBJ_TREND 2 Трендовая линия. Использует 2 координаты
    OBJ_TRENDBYANGLE 3 Трендовая линия по углу. Использует 2 координаты, либо первую координату и угол. Для установки угла линии (свойство OBJPROP_ANGLE) используется функция ObjectSet()

    OBJ_REGRESSION 4 Канал линейной регрессии. Использует временные составляющие 2 координат, ценовые составляющие игнорируются
    OBJ_CHANNEL 5 Равноудаленный канал. Использует 3 координаты
    OBJ_STDDEVCHANNEL 6 Канал стандартных отклонений. Использует временные составляющие 2 координат, ценовые составляющие игнорируются
    OBJ_GANNLINE 7 Линия Ганна. Использует 2 координаты, но ценовая составляющая второй координаты игнорируется. Для установки соотношения между временной и ценовой шкалами (свойство OBJPROP_SCALE) используется функция ObjectSet()

    OBJ_GANNFAN 8 Веер Ганна. Использует 2 координаты, но ценовая составляющая второй координаты игнорируется. Для установки соотношения между временной и ценовой шкалами (свойство OBJPROP_SCALE) используется функция ObjectSet()

    OBJ_GANNGRID 9 Сетка Ганна. Использует 2 координаты, но ценовая составляющая второй координаты игнорируется. Для установки соотношения между временной и ценовой шкалами (свойство OBJPROP_SCALE) используется функция ObjectSet()

    OBJ_FIBO 10 Уровни Фибоначчи. Использует 2 координаты. Для установки количества уровней (свойство OBJPROP_FIBOLEVELS) и значения уровней (свойство OBJPROP_FIRSTLEVEL+n) используется функция ObjectSet()

    OBJ_FIBOTIMES 11 Временные зоны Фибоначчи. Использует 2 координаты. Для установки количества уровней (свойство OBJPROP_FIBOLEVELS) и значения уровней (свойство OBJPROP_FIRSTLEVEL+n) используется функция ObjectSet()

    OBJ_FIBOFAN 12 Веер Фибоначчи. Использует 2 координаты. Для установки количества уровней (свойство OBJPROP_FIBOLEVELS) и значения уровней (свойство OBJPROP_FIRSTLEVEL+n) используется функция ObjectSet()

    OBJ_FIBOARC 13 Дуги Фибоначчи. Использует 2 координаты. Для установки количества уровней (свойство OBJPROP_FIBOLEVELS) и значения уровней (свойство OBJPROP_FIRSTLEVEL+n) используется функция ObjectSet()

    OBJ_EXPANSION 14 Расширение Фибоначчи. Использует 3 координаты. Для установки количества уровней (свойство OBJPROP_FIBOLEVELS) и значения уровней (свойство OBJPROP_FIRSTLEVEL+n) используется функция ObjectSet()

    OBJ_FIBOCHANNEL 15 Канал Фибоначчи. Использует 3 координаты. Для установки количества уровней (свойство OBJPROP_FIBOLEVELS) и значения уровней (свойство OBJPROP_FIRSTLEVEL+n) используется функция ObjectSet()

    OBJ_RECTANGLE 16 Прямоугольник. Использует 2 координаты
    OBJ_TRIANGLE 17 Треугольник. Использует 3 координаты
    OBJ_ELLIPSE 18 Эллипс. Использует 2 координаты. Для установки соотношения между временной и ценовой шкалами (свойство OBJPROP_SCALE) используется функция ObjectSet()

    OBJ_PITCHFORK 19 Вилы Эндрюса. Использует 3 координаты
    OBJ_CYCLES 20 Временные ряды (циклические линии). Использует 2 координаты
    OBJ_TEXT 21 Текст. Использует 1 координату. Для установки угла выводимого текста (свойство OBJPROP_ANGLE) используется функция ObjectSet(). Для изменения текста используется функция ObjectSetText()

    OBJ_ARROW 22 Стрелки (символы). Использует 1 координату. Для установки кода символа (свойство OBJPROP_ARROWCODE) используется функция ObjectSet()

    OBJ_LABEL 23 Текстовая метка. Не использует координат. Для установки координат, задаваемых в пикселях относительно угла привязки (свойства OBJPROP_CORNER, OBJPROP_XDISTANCE, OBJPROP_YDISTANCE) используется функция ObjectSet(). Для изменения текста используется функция ObjectSetText()
Вернуться к началу
Посмотреть профиль Отправить личное сообщение Посетить сайт автора
scriptong
Модератор RadioFOREX.ru


Зарегистрирован: 30.05.2007
Сообщения: 652
Откуда: Украина

СообщениеДобавлено: Пт Фев 15, 2008 19:31    Заголовок сообщения: Урок 10. Продолжение. Ответить с цитатой

Целочисленный параметр window задает номер окна, в котором необходимо отобразить создаваемый объект. Нулевое значение определяет окно графика цены. Последующие значения задают номера подокон в порядке их создания.
Следующие шесть параметров задают координаты объекта. Количество указываемых координат зависит от типа объекта.
Например, для создания линии (вспоминаем школьную геометрию), необходимо две точки. Каждая точка в двумерном пространстве имеет координаты по осям X и Y. Таким образом, в функции создания объекта нужно указать четыре значения после номера окна. График цены по оси X представлен временной шкалой. Поэтому в качестве X-координаты указывается время. Чаще всего его задают временем создания бара, например Time[0] – время создания нулевого бара. По оси Y у нас непосредственно цена. Значит, для указания Y-координаты используем необходимый уровень цены. Как и в геометрии, последовательность Х- и Y-координат сохранена – сначала указывается Х, затем Y.
В теле функции init создаем объект:
Код:

if(!ObjectCreate("Channel", OBJ_CHANNEL, 0, 0, 0, 0, 0, 0, 0))
    return(0);

Здесь сразу же производится проверка успешности создания. Если объект не создан, то мы выйдем из функции сразу. Объект будет называться Channel, а его тип – равноудаленный канал. Этот тип объектов предполагает наличие трех точек – две задают основную линию, а третья – одну точку параллельной линии. Так как канал еще не рассчитан, все координаты нулевые и объект нам не будет виден.
Чтобы при ошибке создания объекта не произошло выполнение функции start, после строки создания объекта в функции init присвоим глобальной переменной Activate значение True, а при ее описании инициализируем ее значением False:
Код:

bool Activate = False;

int init()
{
  ….
  Activate = True;
  return(0);
   }

При входе в функцию start будем проверять значение Activate и, если оно не равно True, будем производить выход:
Код:

if(!Activate)  return(0);

Для отображения канала нам необходимо рассчитать три координаты, которые можно задать, если найти по два последних фрактала сверху и снизу. Оформим это в виде функции FindFractals:
Код:

int FindFractals()
{
   UpFractal1 = 0;
   UpFractal2 = 0;
   DownFractal1 = 0;
   DownFractal2 = 0;
   TU1 = 0; TU2 = 0;
   TD1 = 0; TD2 = 0;
   
   int i = 3;
   while((UpFractal1 == 0 || UpFractal2 == 0 || DownFractal1 == 0 || DownFractal2 == 0) && i < Bars)
     {
       double Up = iFractals(Symbol(), FractalTF, MODE_UPPER, i);
       double Dn = iFractals(Symbol(), FractalTF, MODE_LOWER, i);
       if(Up != 0)
         if(UpFractal1 == 0)
            {
              UpFractal1 = Up;
              TU1 = iTime(Symbol(), FractalTF, i);
             }
           else
            if(UpFractal2 == 0)
              {
                UpFractal2 = Up;
                TU2 = iTime(Symbol(), FractalTF, i);
               }
       if(Dn != 0)
         if(DownFractal1 == 0)
           {
             DownFractal1 = Dn;
             TD1 = iTime(Symbol(), FractalTF, i);
            }
           else
             if(DownFractal2 == 0)
               {
                 DownFractal2 = Dn;       
                 TD2 = iTime(Symbol(), FractalTF, i);
                }
       i++;
      }
   // Поиск окончен   

 return(i);
 } 

Переменные UpFractal1, UpFractal2, DownFractal1 и DownFractal2 – имеют тип double, так как в них будет храниться значение цен соответствующих фракталов. Переменные TU1, TU2, TD1 и TD2 – объявляем как datetime. В них будет закладываться время открытия бара, на котором возник фрактал. Все восемь переменных нужно объявить на глобальном уровне, чтобы к ним был доступ из любой функции индикатора.
Функция iTime подобна таймсерии Time с тем отличием, что может брать данные с другого символа и таймфрейма.
Далее в цикле находим четыре фрактала и заносим их параметры в соответствующие переменные. Выход из цикла будет происходить только по нахождению всех четырех фракталов или по достижении последнего бара, что является ошибкой в истории. Эту ошибку обрабатываем в функции start:
Код:

if(FindFractals() == Bars)
   return(0);   
Вернуться к началу
Посмотреть профиль Отправить личное сообщение Посетить сайт автора
scriptong
Модератор RadioFOREX.ru


Зарегистрирован: 30.05.2007
Сообщения: 652
Откуда: Украина

СообщениеДобавлено: Пт Фев 15, 2008 19:33    Заголовок сообщения: Урок 10. Продолжение. Ответить с цитатой

Условием формирования канала будем считать только две ситуации:

    1) Восходящий канал - второй верхний фрактал ниже первого, а второй нижний фрактал также ниже первого нижнего.
    2) Нисходящий канал – второй верхний фрактал выше первого, а второй нижний фрактал также выше первого нижнего.

Все остальные случаи считаем отсутствием канала.
Код:

   // Вариант первый - восходящий тренд
   // То есть UpFractal1 больше, чем UpFractal2. То же самое с нижними фракталами 
   if(UpFractal1 > UpFractal2 && DownFractal1 > DownFractal2)
     {
      // Рисуем восходящий канал
       return(0);
      }   

   // Вариант второй - нисходящий тренд
   // То есть UpFractal1 меньше, чем UpFractal2. То же самое с нижними фракталами 
   if(UpFractal1 < UpFractal2 && DownFractal1 < DownFractal2)
     {
     // Рисуем нисходящий канал
       return(0);
      }   

   // Убираем с графика канал   
   Comment("Нет выраженного тренда на таймфрейме ", CalcTF());   

В функции CalcTF просто преобразуем число, характеризующее заданный таймфрейм, в строку:
Код:

string CalcTF()
{
  string Res = "";
  if(FractalTF == 0)
    FractalTF = Period();
   
  switch(FractalTF)
    {
     case 1: Res = "M1"; break;
     case 5: Res = "M5"; break;
     case 15: Res = "M15"; break;
     case 30: Res = "M30"; break;
     case 60: Res = "H1"; break;
     case 240: Res = "H4"; break;
     case 1440: Res = "D1"; break;
     case 10080: Res = "W1"; break;
     case 43200: Res = "MN1"; break;
     }
  return(Res);   
 }

Заметьте, здесь корректно обрабатывается случай, когда пользователь задал значение FractalTF равным нулю, то есть текущий таймфрейм.
Теперь перейдем непосредственно к функции, которая будет отображать канал. Для этого нам потребуется функция:
Код:

bool ObjectMove(string name, int point, datetime time1, double price1)

Передвигается точка номер point объекта с названием name. Параметры time1 и price1 задают новые координаты точки. Нам необходимо изменить координаты трех точек, значит, функцию будем вызывать три раза:
Код:

void MoveChannel(datetime X1, double Y1, datetime X2, double Y2, datetime X3, double Y3)
{
   ObjectMove("Channel", 0, X1, Y1);
   ObjectMove("Channel", 1, X2, Y2);
   ObjectMove("Channel", 2, X3, Y3);
   return;
 }
Вернуться к началу
Посмотреть профиль Отправить личное сообщение Посетить сайт автора
scriptong
Модератор RadioFOREX.ru


Зарегистрирован: 30.05.2007
Сообщения: 652
Откуда: Украина

СообщениеДобавлено: Пт Фев 15, 2008 19:39    Заголовок сообщения: Урок 10. Продолжение. Ответить с цитатой

Оформив отображение канала в виде функции MoveChannel, мы избавляем себя от необходимости усложнять код функции start, что тоже является правилом хорошего тона. Теперь вместо надписи «// Рисуем восходящий канал» можно вызвать функцию с нужными параметрами:
Код:

MoveChannel(TU1, UpFractal1, TU2, UpFractal2, TD1, DownFractal1);

То есть мы задаем две координаты максимумов верхних фракталов для верхней линии канала и одну координату минимума нижнего фрактала для параллельной нижней линии. Подобным образом отображаем нисходящий канал:
Код:

MoveChannel(TD1, DownFractal1, TD2, DownFractal2, TU1, UpFractal1);

Здесь делаем наоборот – две координаты для нижней линии от нижних фракталов и одна – для верхней.
Когда канал необходимо убрать с графика, функцию нужно вызвать с такими параметрами:
Код:

MoveChannel(0, 0, 0, 0, 0, 0);

В индикаторе остались мелкие нюансы. Например, нужно предусмотреть установку пользователем нестандартного значения таймфрейма в параметре FractalTF. Для этого в начале функции init напишем следующее:
Код:

if(CalcTF() == "")
  {
     Comment("Неверно задан таймфрейм!");
     return(0);
    }

Так как функция CalcTF возвращает значение локальной переменной Res, которая по умолчанию равна пустой строке, то такое ее значение говорит о том, что не найден нужный таймфрейм.
Следующим нюансом является то, о чем я говорил в самом начале урока – уборка за собой. Нужно сделать так, чтобы после удаления индикатора с графика исчезали все созданные им объекты. Удаление объекта производится функцией:
Код:

ObjectDelete(string name);

Ее с именем Channel вписываем в тело функции deinit. Туда же вписываем функцию с очисткой комментариев: Comment(“”).
И последний, третий нюанс, заключается в оптимизации по быстродействию. В данной редакции на каждом тике происходит перерасчет всех фракталов и перерисовка канала. Но ведь это нужно делать только в случае возникновения нового бара на том таймфрейме, который задан во входном параметре FractalTF. Для этого используется такая конструкция, которую помещаем в функцию start перед вызовом функции расчета фракталов:
Код:

if(LastBarTime == iTime(Symbol(), FractalTF, 0))
     return(0);
   LastBarTime = iTime(Symbol(), FractalTF, 0); 

Переменную LastBarTime объявляют на глобальном уровне с типом datetime. Теперь перерасчет и перерисовка будет происходить только с открытием нового бара.
Можно приступать к проверке работы индикатора. Очень удобно задать таймфрейм Н4 (FractalTF = 240) и работать при этом на более мелком таймфрейме (допустим Н1).
Вернуться к началу
Посмотреть профиль Отправить личное сообщение Посетить сайт автора
scriptong
Модератор RadioFOREX.ru


Зарегистрирован: 30.05.2007
Сообщения: 652
Откуда: Украина

СообщениеДобавлено: Пт Фев 15, 2008 19:44    Заголовок сообщения: Урок 10. Продолжение. Ответить с цитатой

Но здесь не помешает небольшая модернизация. Предлагаю добавить к отображению канала еще и сетку Фибоначчи, которую растянем от точки 0 канала до точки 2. Если будет происходить откат от основного движения, то можно будет его оценить в плане коррекция это или разворот тенденции.
Создаем объект «сетка Фибоначчи» (в функции инициализации):
Код:

if(!ObjectCreate("Fibo", OBJ_FIBO, 0, 0, 0, 0))
   return(0);

Сетка использует только две координаты – уровень ноля и уровень 100%. Для отображения различных уровней, необходимо установить их количество и характеристики. Используем функцию установки свойств:
Код:

bool ObjectSet(string name, int prop_id, double value)

Устанавливается свойство prop_id для объекта с именем name в значение value. Параметр prop_id может принимать такие значения:

    Константа Значение Тип Описание
    OBJPROP_TIME1 0 datetime Получает/устанавливает первую координату времени
    OBJPROP_PRICE1 1 double Получает/устанавливает первую координату цены
    OBJPROP_TIME2 2 datetime Получает/устанавливает вторую координату времени
    OBJPROP_PRICE2 3 double Получает/устанавливает вторую координату цены
    OBJPROP_TIME3 4 datetime Получает/устанавливает третью координату времени
    OBJPROP_PRICE3 5 double Получает/устанавливает третью координату цены
    OBJPROP_COLOR 6 color Получает/устанавливает цвет объекта

    OBJPROP_STYLE 7 int Получает/устанавливает стиль линии объекта

    OBJPROP_WIDTH 8 int Получает/устанавливает ширину линии объекта
    OBJPROP_BACK 9 bool Получает/устанавливает флаг фонового отображения объекта
    OBJPROP_RAY 10 bool Получает/устанавливает флаг свойства луч для объектов типа OBJ_TREND и ему подобных
    OBJPROP_ELLIPSE 11 bool Получает/устанавливает флаг отображения полного эллипса для объекта OBJ_FIBOARC
    OBJPROP_SCALE 12 double Получает/устанавливает значение масштаба объекта
    OBJPROP_ANGLE 13 double Получает/устанавливает значение угла в градусах объекта OBJ_TRENDBYANGLE
    OBJPROP_ARROWCODE 14 int Получает/устанавливает код стрелки объекта OBJ_ARROW. Может быть одним из символов wingdings или один из предопределенных кодов стрелок

    OBJPROP_TIMEFRAMES 15 int Получает/устанавливает свойство отображения объекта на различных периодах. Может быть одним или комбинацией нескольких из констант видимости объекта.

    OBJPROP_DEVIATION 16 double Получает/устанавливает размер отклонения для объекта OBJ_STDDEVCHANNEL
    OBJPROP_FONTSIZE 100 int Получает/устанавливает размер шрифта для объектов OBJ_TEXT и OBJ_LABEL
    OBJPROP_CORNER 101 int Получает/устанавливает номер угла привязки для объекта OBJ_LABEL. Принимает значения 0-3
    OBJPROP_XDISTANCE 102 int Получает/устанавливает расстояние X-координаты в пикселях относительно угла привязки для объекта OBJ_LABEL
    OBJPROP_YDISTANCE 103 int Получает/устанавливает расстояние Y-координаты в пикселях относительно угла привязки для объекта OBJ_LABEL
    OBJPROP_FIBOLEVELS 200 int Получает/устанавливает число уровней объекта Fibonacci. Может быть от 1 до 32
    OBJPROP_LEVELCOLOR 201 color Получает/устанавливает цвет линии уровня объекта

    OBJPROP_LEVELSTYLE 202 int Получает/устанавливает стиль линии уровня объекта
    OBJPROP_LEVELWIDTH 203 int Получает/устанавливает ширину линии уровня объекта
    OBJPROP_FIRSTLEVEL+n 210+n int Получает/устанавливает номер уровня объекта, где n - индекс устанавливаемого/получаемого уровня. Может быть от 0 до 31


Последний раз редактировалось: scriptong (Пт Фев 15, 2008 19:46), всего редактировалось 1 раз
Вернуться к началу
Посмотреть профиль Отправить личное сообщение Посетить сайт автора
scriptong
Модератор RadioFOREX.ru


Зарегистрирован: 30.05.2007
Сообщения: 652
Откуда: Украина

СообщениеДобавлено: Пт Фев 15, 2008 19:45    Заголовок сообщения: Урок 10. Продолжение. Ответить с цитатой

Устанавливаем количество уровней Фибоначчи:
Код:

ObjectSet("Fibo", OBJPROP_FIBOLEVELS, 10);

Дальше нужно указать значения уровней и подписать их. Для подписи уровня используется отдельная функция:
Код:

bool ObjectSetFiboDescription(string name, int index, string text)

Параметр index задает номер уровня (считается с нуля), а text – любая подпись. Теперь создаем уровни и подписываем их:
Код:

   ObjectSet("Fibo", OBJPROP_FIRSTLEVEL, 0.0);
   ObjectSetFiboDescription("Fibo", 0, "+0.0     ");
   ObjectSet("Fibo", OBJPROP_FIRSTLEVEL+1, 0.382);
   ObjectSetFiboDescription("Fibo", 1, "+38.2     ");
   ObjectSet("Fibo", OBJPROP_FIRSTLEVEL+2, 0.5);
   ObjectSetFiboDescription("Fibo", 2, "+50.0     ");
   ObjectSet("Fibo", OBJPROP_FIRSTLEVEL+3, 0.618);
   ObjectSetFiboDescription("Fibo", 3, "+61.8     ");
   ObjectSet("Fibo", OBJPROP_FIRSTLEVEL+4, 1.0);
   ObjectSetFiboDescription("Fibo", 4, "+100.0     ");
   ObjectSet("Fibo", OBJPROP_FIRSTLEVEL+5, 1.382);
   ObjectSetFiboDescription("Fibo", 5, "+138.2     ");
   ObjectSet("Fibo", OBJPROP_FIRSTLEVEL+6, 1.618);
   ObjectSetFiboDescription("Fibo", 6, "+161.8     ");
   ObjectSet("Fibo", OBJPROP_FIRSTLEVEL+7, 2.0);
   ObjectSetFiboDescription("Fibo", 7, "+200.0     ");
   ObjectSet("Fibo", OBJPROP_FIRSTLEVEL+8, 2.618);
   ObjectSetFiboDescription("Fibo", 8, "+261.8     ");
   ObjectSet("Fibo", OBJPROP_FIRSTLEVEL+9, 3.0);
   ObjectSetFiboDescription("Fibo", 9, "+300.0     ");

Чтобы сетка правильно отображалась, нужно добавить изменение ее координат в тело функции MoveChannel:
Код:

ObjectMove("Fibo", 1, X1, Y1);
ObjectMove("Fibo", 0, X3, Y3);

А для «приборки за собой» добавляем удаление сетки в функцию деинициализации:
Код:

ObjectDelete("Fibo");

Работа с объектами является одновременно сложным, но и интересным направлением. Именно с помощью объектов можно создать систему, которая будет помогать трейдеру в работе, а не полностью заменять его. При помощи объектов можно в визуальной форме изменять условия работы советника не только в плане параметров, но и в плане самой стратегии. К созданию подобной системы мы и придем посредством уроков MQL4.



CurrentTrend.zip
 Описание:

Скачать
 Имя файла:  CurrentTrend.zip
 Размер файла:  1.96 KB
 Скачано:  579 раз(а)

Вернуться к началу
Посмотреть профиль Отправить личное сообщение Посетить сайт автора
scriptong
Модератор RadioFOREX.ru


Зарегистрирован: 30.05.2007
Сообщения: 652
Откуда: Украина

СообщениеДобавлено: Вт Мар 04, 2008 21:18    Заголовок сообщения: Урок 11. "Скрипты". Ответить с цитатой

Урок 11. "Скрипты".

До сих пор рассматривались только два вида программ системы Meta Trader 4: советники (программа, которая вновь запускалась по приходу нового тика и могла осуществлять торговые операции) и индикаторы (их основной функцией является отображение информации, не имея при этом возможности исполнять торговые приказы).
Третьим видом программ являются скрипты. В принципе можно сказать, что это те же советники, которые выполняются сразу же при запуске, не дожидаясь прихода тика и автоматически удаляющиеся с графика по завершении работы. Поэтому в скриптах нет стандартных функций init и deinit. Все действия происходят в теле функции start, которой передается управление сразу же после присоединения скрипта к графику. При этом не накладывается никаких ограничений на существование в скрипте других функций.
Для создания нового скрипта нажмите Insert, если у вас активно окно Навигатора или Ctrl+N, если активно окно Meta Editor. В открывшемся диалоге выберите «Пользовательский скрипт», жмем Далее. В следующем окне как обычно вводим имя программы и жмем Готово.
В качестве примера создадим скрипт простой по функциональности (обычное открытие позиции или отложенного ордера), но довольно серьезный в плане обработки возможных ошибок. Такие проверки в будущем необходимо переносить в советники, так как до сих пор мы их мало проводили.
Начнем с входных параметров. Так как скрипт должен устанавливать различные значения профита, стопа, размера позиции, на разном расстоянии от цены и т. д., предлагаю следующий набор параметров:
Код:

extern int     TakeProfit = 200;
extern int     StopLoss = 200;
extern double  Lots = 0.1;
extern int     Slippage = 10;
extern int     TypeOrder = 0; 
extern int     Offset = 100;   
extern int     MagicNumber = 42367;

С первыми тремя параметрами все понятно. Далее идет Slippage. Кто забыл, так в функции OrderSend называется параметр, отвечающий за величину проскальзывания при открытии позиции. То же самое он означает и здесь. А вот параметр TypeOrder как раз и будет отвечать за тип открываемого ордера своим значением. Значения будут совпадать с типами: OP_BUY = 0, OP_SELL = 1, OP_BUYSTOP = 2, OP_SELLSTOP = 3, OP_BUYLIMIT = 4, OP_SELLLIMIT = 5. В Offset будет задаваться расстояние от текущей цены до цены открытия отложенного ордера. В MagicNumber можно задать ненулевое значение, которое при совпадении с MagicNumber одного из работающих экспертов, сделает ордер «своим» для одного из экспертов. В принципе только для этого подобный скрипт и будет актуален, так как в остальных случаях удобнее и быстрее использовать стандартную функцию Meta Trader для открытия позиции вручную.
Обычно скрипты запускаются сразу же после присоединения к графику, без вывода каких-либо сообщений. Чтобы наш скрипт выводил окно редактирования входных параметров необходимо применить директиву препроцессора:
Код:

#property show_inputs

Если же окно параметров выводить не нужно, а уведомить пользователя, что будет выполнен скрипт, нужно, то используется такая директива:
Код:

#property show_confirm

Тогда перед запуском пользователю будет задан вопрос, хочет ли он запустить скрипт.
В нашем случае достаточно и первой директивы. Поэтому ее и ставим.
Сам скрипт логически разделим на три части:

    1. Проверка правильности входных параметров
    2. Расчет входной цены, уровней профита и стопа.
    3. Непосредственно открытие позиции.

Что необходимо проверять в первой части? Конечно же, величины профита и стопа. Они могут быть слишком малы. Для получения минимальных величин уровня стопов и спрэда пишем строки, которые должны быть уже знакомы:
Код:

int Spread = MarketInfo(Symbol(), MODE_SPREAD);
int StopLevel = MarketInfo(Symbol(), MODE_STOPLEVEL);
Вернуться к началу
Посмотреть профиль Отправить личное сообщение Посетить сайт автора
scriptong
Модератор RadioFOREX.ru


Зарегистрирован: 30.05.2007
Сообщения: 652
Откуда: Украина

СообщениеДобавлено: Вт Мар 04, 2008 21:51    Заголовок сообщения: Урок 11. Продолжение. Ответить с цитатой

Так как при открытии позиции мы получаем отрицательный баланс величиной в спрэд, то стоплевел нужно прибавлять к спрэду при проверке величины стопа, а при проверки профита – из стоплевела вычитать спрэд:
Код:

if(StopLoss <= StopLevel+Spread || TakeProfit <= StopLevel-Spread)
  {
      Print("Заданы неверные значения StopLoss или TakeProfit. Работа остановлена!");
      return(0);
     }

Далее параметр расстояния для отложенного ордера – Offset. Если нужно открывать позицию, то его можно не проверять (т. е. если TypeOrder < 2). Во всех остальных случаях (TypeOrder > 1) проверяем его на соответствие уровню стопов:
Код:

if(Offset < StopLevel+1 && TypeOrder > 1)
  {
      Print("Слишком малое значение параметра Offset. Работа остановлена!");
      return(0);
    }

Из входных параметров далее следует Lots – объем позиции. Насчет проверки принадлежности его к диапазону от минимального до максимального значения, должно быть понятно:
Код:

if(Lots < MarketInfo(Symbol(), MODE_MINLOT) || Lots > MarketInfo(Symbol(), MODE_MAXLOT) )
   {
      Print("Задано неверное значение параметра Lots. Работа остановлена!");
      return(0);
     }

А что, если пользователь задаст значение, не соответствующее шагу приращения лота – MarketInfo(Symbol(), MODE_LOTSTEP)? Можно конечно в цикле перебрать все возможные значения от минимума до максимума, но есть более красивый способ.
Как и в любом другом языке, в MQL4 присутствует набор математических функций, при помощи которых можно вычислять тригонометрические, экспоненциальные, логарифмические функции и т. п. Полный список этих инструментов находится во встроенном справочнике MQL4 в разделе «Математические функции». Все они легко идентифицируются в коде, так как имеют приставку Math.
Сейчас свое внимание обратим на возможность расчета остатка от деления двух чисел:
Код:

double MathMod(double value, double value2)

Здесь главное соблюсти очередность указания параметров, потому что именно value делится на value2, а, как известно из математики, в случае перемены мест делителя и делимого, частное меняется.
Как эта функция может нам помочь в определении правильности заданного объема сделки? Очень просто. Как определить кратность одного числа другому? Разделить и посмотреть остаток отделения. Если он равен нулю, значит первое кратно второму. В нашем случае Lots нужно разделить на LotsStep и, если остаток не равен нулю, то это означает неверность параметра Lots:
Код:

MathMod(Lots, MarketInfo(Symbol(), MODE_LOTSTEP))

Вроде бы и все, но не стоит забывать о том, что в MQL4 ни одно вещественное число не равно целому, в данном случае нулю. Поэтому всегда нужно прибегать к округлению. В нашем случае прибегнем к округлению с запасом. Мало ли чего там введет пользователь?
Код:

NormalizeDouble(MathMod(Lots, MarketInfo(Symbol(), MODE_LOTSTEP)), 10)

Таким образом, полностью проверка правильности объема позиции будет выглядеть так:
Код:

    if(Lots < MarketInfo(Symbol(), MODE_MINLOT) || Lots > MarketInfo(Symbol(), MODE_MAXLOT)
      || NormalizeDouble(MathMod(Lots, MarketInfo(Symbol(), MODE_LOTSTEP)), 10) != 0)
       {
         Print("Задано неверное значение параметра Lots. Работа остановлена!");
         return(0);
        }
Вернуться к началу
Посмотреть профиль Отправить личное сообщение Посетить сайт автора
scriptong
Модератор RadioFOREX.ru


Зарегистрирован: 30.05.2007
Сообщения: 652
Откуда: Украина

СообщениеДобавлено: Вт Мар 04, 2008 22:26    Заголовок сообщения: Урок 11. Продолжение. Ответить с цитатой

Следующим параметром, который необходимо проверять, является TypeOrder. Так как его допустимые значения лежат в пределах от 0 до 5 включительно, то проверка будет довольно простая:
Код:

if(TypeOrder < 0 || TypeOrder > 5)
      {
        Print("Неправильно задан тип ордера. Работа остановлена!");
        return(0);
       }

Последняя часть проверочного блока должна содержать проверку возможности автоматической торговли, потому что зачастую пользователь забывает поставить «галочки» в соответствующих разделах при запуске советника или скрипта. Функция, возвращающая состояние флага разрешения автоторговли, вызывается так:
Код:

bool IsTradeAllowed()

Если автоторговля разрешена, то будет возвращено значение True, иначе будет False. Поэтому пишем следующее:
Код:

    if(!IsTradeAllowed())
      {
        Print("Автоторговля запрещена настройками терминала. Работа остановлена!");
        return(0);
       }

С блоком проверок разобрались.
Далее по алгоритму идет блок расчета цен. В нем все довольно просто – в зависимости от заданного типа позиции определяем цену входа и рассчитываем уровни стопа и профита:
Код:

switch(TypeOrder)
      {
        case 0:
          double Price = Ask;
          double SL = Ask - StopLoss*Point;
          double TP = Ask + TakeProfit*Point;
          break;
        case 1:
          Price = Bid;
          SL = Bid + StopLoss*Point;
          TP = Bid - TakeProfit*Point;
          break;
        case 2:
          Price = Ask-Offset*Point;
          SL = Price - StopLoss*Point;
          TP = Price + TakeProfit*Point;
          break;
        case 3:
          Price = Bid+Offset*Point;
          SL = Price + StopLoss*Point;
          TP = Price - TakeProfit*Point;
          break;
        case 4:
          Price = Ask+Offset*Point;
          SL = Price - StopLoss*Point;
          TP = Price + TakeProfit*Point;
          break;
        case 5:
          Price = Bid-Offset*Point;
          SL = Price + StopLoss*Point;
          TP = Price - TakeProfit*Point;
          break;
       }
Вернуться к началу
Посмотреть профиль Отправить личное сообщение Посетить сайт автора
scriptong
Модератор RadioFOREX.ru


Зарегистрирован: 30.05.2007
Сообщения: 652
Откуда: Украина

СообщениеДобавлено: Вт Мар 04, 2008 22:32    Заголовок сообщения: Урок 11. Продолжение. Ответить с цитатой

Остается только открыть позицию по рассчитанным значениям. Для этого создадим специальную функцию, в которой сразу же будет происходить проверка ошибок, связанных с открытием ордеров. Входные параметры этой функции будут совпадать с параметрами функции OrderSend:
Код:

void OpenOrder(string sym, int cmd, double Vol, double Price, int Slip, double SL, double TP, string Com, int Mag, datetime exp, color arr)

Что в реальной торговле необходимо проверить перед открытием позиции? Как минимум две вещи: достаточность свободных средств для открытия и свободность торгового потока.
Если вы торгуете на реальном счете, то заметили, что при отправке запроса на открытие позиции ответ приходит далеко не сразу. Во всяком случае, позже, чем на демо-счете этого же ДЦ. Если в это время, пока посылается запрос серверу и ожидается ответ, попробовать отправить еще один запрос, то получим ответ – торговый поток занят (или Trade Context Is Busy). Чтобы не бомбардировать терминал подобными запросами, необходимо проверять доступность торгового потока, используя функцию:
Код:

bool IsTradeContextBusy()

В случае занятого потока получим True, иначе – False - торговать можно.
Достаточность свободных средств можно проверить функцией:
Код:

double AccountFreeMarginCheck(string symbol, int cmd, double Lots)

Она возвращает сумму средств, которые останутся при открытии позиции типа cmd объемом Lots. Но так как параметр cmd должен быть только OP_BUY или OP_SELL, нужно привести все остальные типы команд к этим двум. Если вы заметили, типы операций расположены в четком соответствии: Buy-типы четные, а Sell-типы – нечетные. Поэтому вновь воспользуемся помощью функции MathMod:
Код:

int check = MathMod(cmd, 2)

Теперь можно полностью формулировать условие, проверяющее ошибки до открытия позиции:
Код:

  if(!IsTradeContextBusy() && AccountFreeMarginCheck(sym, check, Vol) > 0)

Для того чтобы информировать пользователя о происходящем перед операцией открытия выведем сообщение, а после открытия сотрем его:
Код:

    Comment("Подождите. Выполняется открытие позиции...");
    int ticket = OrderSend(sym, cmd, Vol, Price, Slip, SL, TP, Com, Mag, exp, arr);
    Comment("");

Остается только обработка ошибок после открытия. Думаю, в комментариях не нуждается:
Код:

if(ticket < 0)
     {
       switch(cmd)
        {
          case OP_BUY: string S = "Buy"; break;
          case OP_SELL: S = "Sell"; break;
          case OP_SELLSTOP: S = "SellStop"; break;
          case OP_SELLLIMIT: S = "SellLimit"; break;
          case OP_BUYSTOP: S = "BuyStop"; break;
          case OP_BUYLIMIT: S = "BuyLimit"; break;
          default: S = "не опознан";
         }
       Print("Не удалось открыть ордер ", S,". Ошибка N", GetLastError(), ", Price=", Price,", TP=", TP, ", SL=", SL, ", Bid=", Bid, ", Lots=", Vol);
      }

Вот и вся функция OpenOrder. Вызвать ее можно также, как и функцию OrderSend:
Код:

OpenOrder(Symbol(), TypeOrder, Lots, Price, Slippage, SL, TP, NULL, MagicNumber, 0, CLR_NONE);   

Как уже упоминалось выше, практического применения данный скрипт почти не имеет, так как гораздо удобнее пользоваться стандартными средствами Meta Trader. Единственная область применения – вместе с другими советниками. Допустим, советник какое-то время не работал (не работал компьютер или интернета не было), а при последующем включении прямого сигнала нет. И для того, чтобы все же выставить позицию, а советник ее впоследствии «подхватил», считая своей, можно воспользоваться этим скриптом, указав ненулевое значение MagicNumber.
Более практичный скрипт мы создадим в следующем уроке, где посредством скрипта, работающего постоянно, позиции будут открываться простым перетаскиванием соответствующих значков.



OpenOrder.zip
 Описание:

Скачать
 Имя файла:  OpenOrder.zip
 Размер файла:  1.57 KB
 Скачано:  506 раз(а)

Вернуться к началу
Посмотреть профиль Отправить личное сообщение Посетить сайт автора
scriptong
Модератор RadioFOREX.ru


Зарегистрирован: 30.05.2007
Сообщения: 652
Откуда: Украина

СообщениеДобавлено: Чт Мар 13, 2008 18:47    Заголовок сообщения: Урок 12. "Простой пример графической оболочки" Ответить с цитатой

Урок 12. "Простой пример графической оболочки".

Во многих книгах по трейдингу часто высказывается мысль, что скорость принятия решений на Forex в лучшем случае никак не влияет, а в худшем только приводит к плачевным результатам торговли. Перед принятием решения о входе в рынок в основном рекомендуется провести основательный анализ и только потом открывать позицию. Поэтому 10-20 пунктов отклонения от запланированной цены входа никак не должны влиять на сделку. Но существует целый класс стратегий, которые зависят от вот этих самых 10-20 пунктов и как раз в них скорость реакции трейдера крайне важна. Чаще всего такие стратегии называют пипсовкой или скальпированием рынка. Во многих источниках высказывается резко отрицательное мнение о пипсовке. Хотя лично я придерживаюсь мнения, что если эти стратегии до сих пор существуют, значит это кому-то нужно. К тому же, никогда не стоит отказываться от мелкой прибыли, если риск получения убытка явно минимален. Поэтому, наряду с основными стратегиями, многие трейдеры используют скальпинг (заметьте, речь идет о применении пипсовки не в качестве основной стратегии).
Как же ускорить процесс выставления объема, стопа и профита при совершении сделки? Можно конечно перед каждой сессией ставить значения по умолчанию в скрипт OpenOrder, рассмотренный в прошлом уроке, компилировать его, а затем привязать к нему горячую клавишу и менять только тип позиции при вызове. Но это все же не так наглядно. Человеку свойственно воспринимать больше рисованные образы, чем цифры. Поэтому нужно представить шесть возможных позиций в виде значков. Представим себе такой вид «панели инструментов»:



panel_for_trade.gif
 Описание:
 Размер файла:  1.52 KB
 Просмотрено:  9443 раз(а)

panel_for_trade.gif




Последний раз редактировалось: scriptong (Пн Мар 17, 2008 21:11), всего редактировалось 2 раз(а)
Вернуться к началу
Посмотреть профиль Отправить личное сообщение Посетить сайт автора
scriptong
Модератор RadioFOREX.ru


Зарегистрирован: 30.05.2007
Сообщения: 652
Откуда: Украина

СообщениеДобавлено: Чт Мар 13, 2008 18:55    Заголовок сообщения: Урок 12. Продолжение. Ответить с цитатой

Перенос любого из значков из панели инструментов на график цены будет служить сигналом для совершения операции с установленными заранее значениями объема, профита, стопа и расстояния от текущей цены для отложенных ордеров.
На данный момент в MQL4, в отличие от С++, нет функций-обработчиков событий (таких как нажатие клавиш, перемещение мыши и т. д.). Поэтому, если требуется мгновенно среагировать на перемещение объекта (в данном примере значки-стрелки из приведенной выше панели инструментов), то потребуется зацикливание скрипта или эксперта. Если же реакция может подождать до следующего тика, то можно нижеследующее оформить экспертом без цикла. Я же в дальнейшем буду исходить из мгновенной реакции на событие. Будет использоваться скрипт с бесконечным циклом, выход из которого будет возможен только при удалении скрипта.
Создаем новый скрипт (я назову его GraphicOrders), копируем в него все входные параметры из OpenOrder, а также блок проверки правильности входных параметров в начале функции start.
Первым делом отобразим саму панель с объектами. Для простоты разделим панель на четыре строки. Первая строка будет содержать непосредственно поясняющие надписи к значкам: Buy, Sell, BS (BuyStop), SS (SellStop), BL (BuyLimit), SL (SellLimit). Вторая и четвертая строки – обычные линии. Их можно было бы оформить как объекты-линии, но в данном примере хотелось бы показать как представить линию текстовым объектом. Третья строка будет состоять из шести объектов в виде значков-стрелок.
Чтобы не плодить по шесть строк кода для каждого объекта напишем функцию создания текстовой метки:
Код:

bool TextObject(string Name, string Text, color Col, int X, int Y, int Size, string Font)
 {
        if(!ObjectCreate(Name, OBJ_LABEL, 0, 0, 0) || !ObjectSetText(Name, Text, Size, Font, Col)
       || !ObjectSet(Name, OBJPROP_CORNER, 1) || !ObjectSet(Name, OBJPROP_XDISTANCE, X)
       || !ObjectSet(Name, OBJPROP_YDISTANCE, Y))
         return(False);
    return(True);   
 }

В первой функции создается объект текстовая метка с именем, указанным во входном параметре Name. Текстовая метка не использует координат. Поэтому мы их и не устанавливаем. Для задания координат сначала устанавливается номер угла привязки, то есть один из четырех углов графика цен, от которого будет вестись отсчет координат для данного объекта. Углы нумеруются так: 0 – левый верхний, 1 – правый верхний, 2 – левый нижний, 3 – правый нижний. Угол привязки назначается в третьей функции. В четвертой и пятой устанавливаются Х и Y координаты метки.
Во второй функции изменяются содержание текстовой метки (входной параметр Text), размер шрифта (входной параметр Size), наименование шрифта (входной параметр Font) и его цвет (входной параметр Col).
Все вызовы функций проверяются на результат. Если хотя бы одна из функции вернет результат False, вся функция TextObject вернет отрицательный результат.
Теперь для отображения первой строки панели объектов нужно написать такую строку:
Код:

TextObject("T_Lab", "Buy  Sell   BS    SS    BL    SL  ", White, 1, 1, 10, "MS Sans Serif");

где:
T_Lab – имя метки;
Buy Sell BS SS BL SL – сама надпись;
White – цвет надписи будет белым;
1 – Х-координата метки;
1 – Y-координата метки;
10 – размер шрифта;
MS Sans Serif – название шрифта. Доступные шрифты можно найти в папке Windows\Fonts.
Вернуться к началу
Посмотреть профиль Отправить личное сообщение Посетить сайт автора
scriptong
Модератор RadioFOREX.ru


Зарегистрирован: 30.05.2007
Сообщения: 652
Откуда: Украина

СообщениеДобавлено: Чт Мар 13, 2008 19:06    Заголовок сообщения: Урок 12. Продолжение. Ответить с цитатой

Таким же образом создаем горизонтальные линии:
Код:

TextObject("T_Lab2", "-------------------------------------------", White, 1, 10, 10, "MS Sans Serif");
TextObject("T_Lab3", "-------------------------------------------", White, 1, 40, 10, "MS Sans Serif");

Все вызовы также необходимо проверить. Точно также объединяем их в одно условие и, в случае неудачи, выходим из скрипта:
Код:

    if(!TextObject("T_Lab", "Buy  Sell   BS    SS    BL    SL  ", White, 1, 1, 10, "MS Sans Serif") ||
       !TextObject("T_Lab2", "-------------------------------------------", White, 1, 10, 10, "MS Sans Serif") ||
       !TextObject("T_Lab3", "-------------------------------------------", White, 1, 40, 10, "MS Sans Serif"))
      {
        Print("Не удалось создать заголовки панели. Работа остановлена!");
        return(0);
       }

Остались только непосредственно значки. Их мы зададим кодами стрелок шрифта Wingdings. Для задания кода стрелки в строке, необходимо использовать такую функцию:
Код:

string StringSetChar(string text, int pos, int value)

где
text – строка, в которую нужно вставить символ;
pos – номер позиции, в которую вставляется символ (считается с 0);
value – код символа;
Сначала объявляем текстовую переменную, содержащую один пробел, а затем в нее будем вставлять нужный код стрелки:
Код:

string s = " ";
    TextObject("T_Buy", StringSetChar(s, 0, 242), Red, 150, 20, 20, "Wingdings");   
    TextObject("T_Sell", StringSetChar(s, 0, 241), Blue, 120, 20, 20, "Wingdings");
    TextObject("T_BuyStop", StringSetChar(s, 0, 199), Red, 90, 20, 20, "Wingdings");   
    TextObject("T_SellStop", StringSetChar(s, 0, 201), Blue, 60, 20, 20, "Wingdings");
    TextObject("T_BuyLimit", StringSetChar(s, 0, 200), Red, 30, 20, 20, "Wingdings");   
    TextObject("T_SellLimit", StringSetChar(s, 0, 202), Blue, 1, 20, 20, "Wingdings");
Вернуться к началу
Посмотреть профиль Отправить личное сообщение Посетить сайт автора
scriptong
Модератор RadioFOREX.ru


Зарегистрирован: 30.05.2007
Сообщения: 652
Откуда: Украина

СообщениеДобавлено: Чт Мар 13, 2008 19:14    Заголовок сообщения: Урок 12. Продолжение. Ответить с цитатой

Опять же, объединяем все вызовы в одну функцию для проверки ошибок:
Код:

    if(!TextObject("T_Buy", StringSetChar(s, 0, 242), Red, 150, 20, 20, "Wingdings") ||
       !TextObject("T_Sell", StringSetChar(s, 0, 241), Blue, 120, 20, 20, "Wingdings") ||
       !TextObject("T_BuyStop", StringSetChar(s, 0, 199), Red, 90, 20, 20, "Wingdings") ||
       !TextObject("T_SellStop", StringSetChar(s, 0, 201), Blue, 60, 20, 20, "Wingdings") ||
       !TextObject("T_BuyLimit", StringSetChar(s, 0, 200), Red, 30, 20, 20, "Wingdings") ||
       !TextObject("T_SellLimit", StringSetChar(s, 0, 202), Blue, 1, 20, 20, "Wingdings"))
      {
        Print("Не удалось создать объекты панели. Работа остановлена!");
        return(0);
       }

Работа по созданию панели окончена. В некоторых случаях после создания сразу нескольких объектов, они могут не отобразиться или отобразиться, но не все. Для того чтобы дать терминалу команду, что в ближайшее время новые объекты отображаться не будут и можно перерисовать имеющиеся, нужно вызвать функцию:
Код:

void WindowRedraw();

которая как раз и сделает все в лучшем виде.
Дальше сделаем заготовку к основной части скрипта, которая будет выполняться в цикле до тех пор, пока пользователь не отсоединит скрипт от графика. Чтобы отследить это событие, используется функция:
Код:

bool IsStopped()

Будет возвращено значение True, если пользователь выбрал пункт «Удалить скрипт», и False во всех остальных случаях. Заготовка будет выглядеть так:
Код:

    while(!IsStopped())
       {
          // тело цикла
         }

После выхода из цикла (поступила команда удаления скрипта) нужно прибрать после себя, удалив все созданные объекты:
Код:

    ObjectDelete("T_Buy");   
    ObjectDelete("T_BuyStop");   
    ObjectDelete("T_BuyLimit");   
    ObjectDelete("T_Sell");   
    ObjectDelete("T_SellLimit");   
    ObjectDelete("T_SellStop");   
    ObjectDelete("T_Lab");   
    ObjectDelete("T_Lab2");   
    ObjectDelete("T_Lab3");   

В теле же цикла необходимо произвести проверку перемещения каждого из шести объектов. Объект будет считаться перемещенным, когда его координаты будут находиться за пределами панели. Самая большая X-координата из используемых объектов принадлежит объекту T_Buy (красная стрелка для открытия позиции Buy). Она составляет 150. Прибавив расстояние между объектами в 30 пикселей, получим 180. То есть, это граница, оказавшись за которой объект будет считаться перенесенным. Таким же образом находим самую большую Y-координату. Она принадлежит объекту T_Lab3 (нижняя линия панели объектов) и составляет 40. Прибавляем шаг в 20 пикселей и определяем, что максимальная Y-координата должна быть 60.
Для определения координат объектов нужно применить обратную ObjectSet функцию:
Код:

double ObjectGet(string name, int prop_id)

где
name – имя существующего объекта;
prop_id – номер свойства объекта, значение которого необходимо получить.

Например, для определения Х-координаты объекта T_Buy вызов функции будет таким:
Код:

  double X = ObjectGet(“T_Buy”, OBJPROP_XDISTANCE);

Так как одно и то же действие будет производиться шесть раз (по разу для каждого объекта), то целесообразно оформить функцию:
Код:

bool CheckObjectMove(string Name)
{
  double X = ObjectGet(Name, OBJPROP_XDISTANCE);
  double Y = ObjectGet(Name, OBJPROP_YDISTANCE);
  if(X > 180 || Y > 60)
    return(True);
   else
    return(False);
 }
Вернуться к началу
Посмотреть профиль Отправить личное сообщение Посетить сайт автора
scriptong
Модератор RadioFOREX.ru


Зарегистрирован: 30.05.2007
Сообщения: 652
Откуда: Украина

СообщениеДобавлено: Чт Мар 13, 2008 19:25    Заголовок сообщения: Урок 12. Продолжение. Ответить с цитатой

Функция будет возвращать True, если объект вышел за пределы панели инструментов, и False, если находится в ее пределах.
Если определено перемещение объекта за пределы панели инструментов, то в подтверждение нужно издать какой-нибудь звуковой сигнал, переместить объект на первоначальное место и открыть нужную позицию. Для воспроизведения звукового файла используется функция:
Код:

void PlaySound(string filename)

где filename – имя файла, который необходимо воспроизвести. Файл должен находиться в папке MT4\sound и иметь формат wave.
Функция возвращения объекта в панель инструментов будет выглядеть так:
Код:

bool MoveObject(string Name, int X, int Y)
{
  if(ObjectSet(Name, OBJPROP_XDISTANCE, X))
   if(ObjectSet(Name, OBJPROP_YDISTANCE, Y))
    {
      PlaySound("Space.wav");
      WindowRedraw();
      return(True);
     }
  return(False);   
 }

Сначала устанавливается Х-координата объекта. Если оно прошло успешно, то устанавливается Y-координата, и только при повторном успехе проигрывается звуковой файл, происходит перерисовка окна и функция возвращает результат True. Если не удалось переместить объект, то функция MoveObject возвращает False без воспроизведения звукового сигнала и перерисовки окна.
В случае успешного высполнения обоих функций (CheckObjectMove и MoveObject), можно открывать позицию. Это будем делать при помощи функции OpenOrder, которая была создана в прошлом уроке в теле одноименного скрипта. Правда в теле функции OpenOrder необходимо произвести небольшую доработку. Дело в том, что скрипт выполняется не потикового, а постоянно. Поэтому данные по котировкам у него могут быть не самые последние. Чтобы обновить значения предопределенных переменных (Bid, Ask) и массивов-таймсерий (Time, High, Low, Open и т. д.) используется функция:
Код:

bool RefreshRates()

Ее вставляем перед вызовом функции OpenOrder.
Теперь можно заполнять тело цикла, который исполняется до выключения скрипта. Для открытия позиции Buy последовательность будет следующая:
Код:

        if(CheckObjectMove("T_Buy"))
           if(MoveObject("T_Buy", 150, 20))
             {
                RefreshRates();
                OpenOrder(Symbol(), OP_BUY, Lots, Ask, Slippage, Ask-StopLoss*Point, Ask+TakeProfit*Point, NULL, MagicNumber, 0, CLR_NONE);
               }

То есть, проверили объект T_Buy на предмет перемещения. Если переместился, то возвращаем на место и подтверждаем звуковым сигналом и, если снова все успешно, то открываем позицию.
Аналогично заполняем условия для других типов ордеров:
Код:

        // Проверка для Sell
        if(CheckObjectMove("T_Sell"))
           if(MoveObject("T_Sell", 120, 20))
             {
               RefreshRates();
               OpenOrder(Symbol(), OP_SELL, _Lots(OP_SELL), Bid, Slippage, Bid+StopLoss*Point, Bid-TakeProfit*Point, NULL, MagicNumber, 0, CLR_NONE);
              }
         
        // Проверка для BuyStop
        if(CheckObjectMove("T_BuyStop"))
           if(MoveObject("T_BuyStop", 90, 20))
             {
               RefreshRates();
               OpenOrder(Symbol(), OP_BUYSTOP, _Lots(OP_BUY), Ask+Offset*Point, Slippage, Ask+(Offset-StopLoss)*Point, Ask+(Offset+TakeProfit)*Point, NULL, MagicNumber, 0, CLR_NONE);
              }
           
        // Проверка для SellStop
        if(CheckObjectMove("T_SellStop"))
           if(MoveObject("T_SellStop", 60, 20))
             {
               RefreshRates();
               OpenOrder(Symbol(), OP_SELLSTOP, _Lots(OP_SELL), Bid-Offset*Point, Slippage, Bid-(Offset-StopLoss)*Point, Bid-(Offset+TakeProfit)*Point, NULL, MagicNumber, 0, CLR_NONE);
              }
           
        // Проверка для BuyLimit
        if(CheckObjectMove("T_BuyLimit"))
          if(MoveObject("T_BuyLimit", 30, 20))
             {
               RefreshRates();
               OpenOrder(Symbol(), OP_BUYLIMIT, _Lots(OP_BUY), Ask-Offset*Point, Slippage, Ask-(Offset+StopLoss)*Point, Ask-(Offset-TakeProfit)*Point, NULL, MagicNumber, 0, CLR_NONE);
              }
           
        // Проверка для SellLimit
        if(CheckObjectMove("T_SellLimit"))
          if(MoveObject("T_SellLimit", 1, 20)) 
             {
               RefreshRates();
               OpenOrder(Symbol(), OP_SELLLIMIT, _Lots(OP_SELL), Ask+Offset*Point, Slippage, Ask+(Offset+StopLoss)*Point, Ask+(Offset-TakeProfit)*Point, NULL, MagicNumber, 0, CLR_NONE);
              }       

Работа над скриптом завершена.
Перед использованием не забудьте поставить разрешение торговли советниками в меню Сервис-Настройки-Советники и в закладке Общие при запуске скрипта. Также правильно заполняйте значения входных параметров. Если ничего не происходит после присоединения скрипта, то посмотрите в закладку «Эксперты» в окне терминала. Там могут быть сообщения об ошибках.
После успешного запуска скрипта для открытия какой-нибудь позиции выделите двойным щелчком нужную стрелку и перетащите ее за пределы панели инструментов. При этом не забывайте, что пока вы тянете объект, скрипт не будет реагировать на его перемещения. Только после того, как вы отпустите левую клавишу мыши, будет начато открытие позиции.


Последний раз редактировалось: scriptong (Пн Мар 17, 2008 19:31), всего редактировалось 1 раз
Вернуться к началу
Посмотреть профиль Отправить личное сообщение Посетить сайт автора
scriptong
Модератор RadioFOREX.ru


Зарегистрирован: 30.05.2007
Сообщения: 652
Откуда: Украина

СообщениеДобавлено: Чт Мар 13, 2008 19:36    Заголовок сообщения: Урок 12. Продолжение. Ответить с цитатой

Одной из распространенных потребностей трейдеров также является предварительный расчет объема позиции, в зависимости от баланса, средств или свободных средств. Этот расчет также можно автоматизировать, добавив соответствующую функцию в скрипт GraphicOrders.
Для начала добавим такие входные параметры:
Код:

extern string    S = "Объем позиции. Если Lots=0, то считается по DepoPercent в процентах";
extern double    Lots = 10;
extern double    DepoPercent = 30;
extern string    S1 = "Процент считать от баланса, эквити или свободных средств";
extern bool      Balance = True;
extern bool      Equity = False;
extern bool      FreeMargin = False;

Сам параметр Lots добавлять не надо. Я показал, в какое место удобнее добавить остальные. Как указано в пояснениях, расчет лота будет происходить только при Lots, равного нулю. Но ведь в блоке проверок входных параметров произойдет ошибка и скрипт не будет запущен (Lots < MarketInfo(Symbol(), MODE_MINLOT)). Поэтому необходимо немного подправить это условие:
Код:

if((Lots < MarketInfo(Symbol(), MODE_MINLOT) || Lots > MarketInfo(Symbol(), MODE_MAXLOT)
      || NormalizeDouble(MathMod(Lots, MarketInfo(Symbol(), MODE_LOTSTEP)), 10) != 0) && Lots != 0)

Теперь при значении Lots = 0, ошибка возникать не будет. Зато при неравном нулю неправильном значении все останется по-прежнему.
Для расчета объема сделки нам потребуются следующие значения: минимальный лот, максимальный лот и количество знаков после запятой у минимального лота.
Как получить минимальное и максимальное значения лота должно быть понятно. Для начала нужно объявить переменные:
Код:

double MinLot, MaxLot;
int MinDigits;

Далее в самом начале функции start пишем:
Код:

    MinLot = MarketInfo(Symbol(), MODE_MINLOT);
    MaxLot = MarketInfo(Symbol(), MODE_MAXLOT);
Вернуться к началу
Посмотреть профиль Отправить личное сообщение Посетить сайт автора
Показать сообщения:   
Начать новую тему   Эта тема закрыта, вы не можете писать ответы и редактировать сообщения.    Список форумов forum.radioforex.ru -> Forex для начинающих Часовой пояс: GMT + 3
На страницу Пред.  1, 2, 3, 4  След.
Страница 3 из 4

 
Перейти:  
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы можете вкладывать файлы
Вы можете скачивать файлы