вторник, 8 мая 2012 г.

Пишем индикатор для Forex.

Добро пожаловать в мир практических курсов MQL4; добро пожаловать в пост где я попробую подробно описать процесс написания индикатора для рынка Форекс в MQL4.
Уверен, этот и последующие несколько постов по программированию на языке MQL4 будут интересны не только теоретикам, но и практикам.

Важно: рекомендую проситать предыдущие посты очень внимательно, прежде, чем переходить к этому и последующим постам. Мы будем очень обширно пользоваться вышеописанном материалом.

Теперь мы создадим простой индикатор, который будет значить немного для нашей торговли, но будет значить очень много для нашего понимания программирования на MQL4.

Он будет просто-напросто рассчитывать разницу High [] – Low []. Не торопитесь, скоро Вы всё поймёте.
Поехали!
MetaEditor

Это название той встроенной в MetaTrader 4 программы, которая позволяет Вам писать программы, читать помощь по MQL4, компилировать программы и многое другое.
У меня на рабочем столе есть ярлык MetaEditor, чтобы проще его запускать.
Запуск MetaEditor — у Вас есть три возможности:
1 — Запустите MT4, затем либо нажмите F4, либо выберите MetaEditor из вкладки «сервис», либо нажмите на значок MetaEditor (см. рис. 1).
2 — Пуск -> Программы -> /Группа MetaTrader 4/ -> MetaEditor .
3 — Зайти в папку установки MT4 (например: C:Program FilesMetaTrader 4), найти MetaEditor.exe и запустить (рекомедую сделать ярлык на рабочем столе).
Рис. 1 — Стандартные кнопки MT4.
В любом случае Вы попадаете в программу MetaEditor 4.
Рис. 2 — Окна MetaEditor.
1 — Окно редактора. Здесь Вы пишите свою программу.

2 — Окно инструментария. Содержит четыре вкладки:


  • Ошибки. Здесь Вам покажут возникшие при компиляции ошибки.
  • Поиск в файлах. Здесь Вы можете просматривать файлы, найденные с помощью соотетствующей комманды из вкладки правка (Ctrl+Shift+F).
  • Библиотека. Online — библиотека.
  • Справка. Выделяете в коде нужное слово и жмёте F1. Появится справка.

3 — Окно навигатора. Содержит три вкладки:


  • Файлы. Для простого доступа к файлам, сохранённым в папке MT4.
  • Словарь. Доступ к справке по MQL4.
  • Поиск. Поиск в справке MQL4.

Советую ознакомиться с окнами MetaEditor.

А теперь приступим к созданию нашего первого индикатора.

Заметка: пользовательский индикатор — это программа, которая позволяет Вам использовать функции технического анализа, но не может автоматизировать Ваши сделки.

Первые три шага

В дальнейшем Вы научитесь пропускать эти три скучные шага, но пока мы будем их выполнять.

Шаг 1: Нажмите Файл -> Создать (или просто Ctrl + N)
Появится такое окошко:
Рис. 3 — окошко нового проекта.
Выберите пункт «пользовательский индикатор» и нажмите Next.

Шаг 2:
Появится такое окошко:
Рис. 4 — окошко свойств проекта.
1 — Имя Вашей программы.
2 — Автор программы.
3 — Ссылка на Ваш сайт или e-mail.
4 — Параметры — список внешних (extern) переменных. Это те переменные, которые пользователь сможет изменять в окошке настроек индикатора (см. урок про переменные).

В нашем примере нам не потребуются внешние переменные. Заполните первые три поля и нажмите Next.

Шаг 3:
Появится такое окошко:
Рис. 5 — окошко свойств отображения индикатора.
В этом окошке Вы можете устанавливать свойства рисования Вашего индикатора, например: сколько у него будет линий, их цвета, где рисовать Ваш индикатор (на графике цены или в отдельном окне).

1 — Индикатор в отдельном окне. Думаю, понятно, что эта опция делает.
2 — Минимум. Если индикатор рисуется в отдельном окне, эта опция устанавливает нижнюю границу для этого окна.
3 — Максимум. Если индикатор рисуется в отдельном окне, эта опция устанавливает верхнюю границу для этого окна.
4 — Список индексов. Сюда Вы добавляете линии индикатора и ставите их цвет-по-умолчанию.

Далее мы более подробно разберём эти опции, поэтому не спешите.
А сейчас сделайте всё, как на рис. 5.
Когда Вы нажмёте кнопку Finish, начнётся волшебство. Окошко помощника исчезнет, Вы опять окажетесь в MetaEditor и… Угадайте….
Вы получили шаблон для своего первого индикатора.

Примерно такой код Вы получите:
Код:
//+------------------------------------------------------------------+
//| MyFirstIndicator.mq4 |
//| Kirill |
//| StockProgrammer@mail.ru |
//+------------------------------------------------------------------+
#property copyright "Kirill"
#property link "StockProgrammer@mail.ru"

#property indicator_separate_window
#property indicator_buffers 1
#property indicator_color1 Red
//---- buffers
double ExtMapBuffer1[];
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int init()
 {
//---- indicators
 SetIndexStyle(0,DRAW_LINE);
 SetIndexBuffer(0,ExtMapBuffer1);
//----
 return(0);
 }
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function |
//+------------------------------------------------------------------+
int deinit()
 {
//----

//----
 return(0);
 }
//+------------------------------------------------------------------+
//| Custom indicator iteration function |
//+------------------------------------------------------------------+
int start()
 {
 int counted_bars=IndicatorCounted();
//----

//----
 return(0);
 }
//+------------------------------------------------------------------+
Как Вы видите, помощник написал достаточно много кода за Вас.

Изучаем основной материал:
Пишем первый индикатор

Теперь мы добавим пару строк в уже имеющийся код, чтобы программа стала более полезной.
После этого мы разберём весь код строчку за строчкой.

Начнём кодить!

Я выделил жирным код, который нужно добавить.

Цитата:
//+——————————————————————+
//| MyFirstIndicator.mq4 |
//| Kirill |
//| StockProgrammer@mail.ru |
//+——————————————————————+
#property copyright «Kirill»
#property link «StockProgrammer@mail.ru»#property indicator_separate_window
#property indicator_buffers 1
#property indicator_color1 Red
//—- buffers
double ExtMapBuffer1[];
//+——————————————————————+
//| Custom indicator initialization function |
//+——————————————————————+
int init()
{
//—- indicators
SetIndexStyle(0,DRAW_LINE);
SetIndexBuffer(0,ExtMapBuffer1); string short_name = «Your first indicator is running!»;
IndicatorShortName(short_name);//—-
return(0);
}
//+——————————————————————+
//| Custom indicator deinitialization function |
//+——————————————————————+
int deinit()
{
//—-//—-
return(0);
}
//+——————————————————————+
//| Custom indicator iteration function |
//+——————————————————————+
int start()
{
int counted_bars=IndicatorCounted();//—- check for possible errors
if (counted_bars<0) return(-1);
//—- last counted bar will be recounted
if (counted_bars>0) counted_bars—;

int pos=Bars-counted_bars;

double dHigh , dLow , dResult;

Comment(«Hi! I’m here on the main chart window!»);

//—- main calculation loop

while(pos>=0)
{
dHigh = High[pos];
dLow = Low[pos];
dResult = dHigh — dLow;

ExtMapBuffer1[pos]= dResult ;

pos—;

}

//—-

return(0);
}
//+——————————————————————+

Как он будет работать?
Код:
//+------------------------------------------------------------------+
//| MyFirstIndicator.mq4 |
//| Kirill |
//| StockProgrammer@mail.ru |
//+------------------------------------------------------------------+
Разбор:

Комментарии
Первые пять строчек (выделены серым цветом у Вас в редакторе) — это комментарии.
Напомню, что комментарии мы используем для того, чтобы вставлять в код строчки, которые компилятор должен игнорировать.
Есть много причин, по которым могут потребоваться комментарии:
- Сделать код более красивым.
- Задокументировать такие моменты, как право собственности, дату создания и т.д.
- Сделать код понятнее.
- Объяснить, как код работает.

Комментарии бывают однострочные и многострочные (см. предыдущий пост).

В нашей программе помощник собрал введённую нами информацию о названии программы, её авторе и ссылке в самом верху.

Код:
#property copyright "Kirill"
#property link "StockProgrammer@mail.ru"

#property indicator_separate_window
#property indicator_buffers 1
#property indicator_color1 Red
Разбор:

Директива property . (см. предыдущий пост)

#property copyright — здесь хранится имя автора программы. Вы ввели его на втором шаге помощника. Тип данных — string.

#property link — ссылка на Вашу домашнюю страницу или Ваш e-mail. Эти данные Вы также ввели на втором шаге помощника. Тип данных — string.

#property indicator_separate_window — таким образом мы говорим препроцессору, что мы хотим, чтобы наш индикатор рисовался в отдельном окне. Тип данных — void (нет принимаемого значения).
* #property indicator_chart_window — алтернатива — индикатор рисуется в окне графика. Обе опции использовать одновременно нельзя.

#property indicator_buffers 1 — с помощью indicator_buffers мы устанавливаем количество массивов, выделяемых под линии нашего индикатора. В кажлом индикаторе разрешается не больше 8 линий. В нашем случае мы рисуем только одну линию.

#property indicator_color1 Red — indicator_colorN устанавливает цвет линии номер N. Пользователь может изменить этот цвет в настройках индикатора. Тип данных — color.

Код:
//---- buffers
double ExtMapBuffer1[];
Разбор:

Массивы (Arrays)

В жизни мы часто группируем похожие объекты. В программировании тоже очень удобно группировать данные одного типа. Для достижения этой цели используются массивы.
Массив — это упорядоченное множество элементов одного типа. Нумерация в массиве начинается с нуля.
Объявление массива:

Код:
int my_array[50];
Здесь мы объявили массив, в котором может содержатся до 50 (включительно) элементов типа integer.
Доступ к элементу происходит по его индексу.
Например, доступ к 0-вому элементу массива и присваивания ему значения 16 выглядит так:

Код:
my_array[0] = 16;
Массив можно инициализировать в строчке его объявления. Делается это так:

Код:
int my_array[5] = {16,24,15,8901,17}
В нашей программе используется такой код:

Код:
double ExtMapBuffer1[];
Таким образом мы объявили массив типа double. Этот массив мы будем использовать для подсчёта значений, которые необходимо рисовать на графике индикатора.

Код:

Код:
int init()
Разбор:

В MQL4 есть три спец. функции: init(), start(), deinit(). Подробнее — см. пост — Функции.

Код:

Код:
//---- indicators
 SetIndexStyle(0,DRAW_LINE);
 SetIndexBuffer(0,ExtMapBuffer1);

 string short_name = "Your first indicator is running!"; IndicatorShortName(short_name);

//----
Разбор:

Функции пользовательских индикаторов.

Я не могу дать Вам описания всех функций индикаторов в этом посте, но те, которые мы использовали, мы обсудим.

SetIndexStyle():
void SetIndexStyle( int index, int type, int style=EMPTY, int width=EMPTY, color clr=CLR_NONE)
- Устанавливает новый тип, стиль, ширину и цвет для указанной линии индикатора.
index — Порядковый номер линии. Должен быть от 0 до 7. Это потому что у нас может быть всего 8 линий, а нумерация в массиве, где они хранятся начинается с нуля.
type — Стиль отрисовки линии индикатора. Может быть одним из перечисленных стилей отрисовки линии:
DRAW_LINE — Простая линия
DRAW_SECTION — Отрезки между непустыми значениями линии
DRAW_HISTOGRAM — Гистограмма
DRAW_ARROW — Стрелки (символы)
DRAW_ZIGZAG — Отрезки между непустыми значениями чётной и нечётной линий (зигзаг)
DRAW_NONE — Отсутствие какого-либо рисования

style — Стиль линии. Используется для линий толщиной в 1 пиксель. Может быть одним из перечисленных стилей линии. Пустое значение (EMPTY) указывает, что стиль не будет изменен.
DRAW_LINE — Простая линия
DRAW_SECTION — Отрезки между непустыми значениями линии
DRAW_HISTOGRAM — Гистограмма
DRAW_ARROW — Стрелки (символы)
DRAW_ZIGZAG — Отрезки между непустыми значениями чётной и нечётной линий (зигзаг)
DRAW_NONE — Отсутствие какого-либо рисования

width — Ширина линии. Допустимые значения — 1,2,3,4,5. Пустое значение (EMPTY) указывает, что ширина не будет изменена.

clr — Цвет линии. Отсутствие параметра означает, что цвет не будет изменен.

В нашем коде:

Код:
SetIndexStyle(0,DRAW_LINE);
index = 0 — это означает, что мы будем работать с первой (и единственной) нашей линией.
type = DRAW_LINE — это означает, что мы хотим рисовать линию.
Остальные параметры мы оставили по умолчанию.

SetIndexBuffer()

bool SetIndexBuffer(int index, double array[])

- Связывает переменную-массив, объявленный на глобальном уровне, с предопределенным буфером пользовательского индикатора. Количество буферов, необходимых для расчета индикатора, задается с помощью функции IndicatorBuffers() и не может быть больше 8. В случае успешного связывания возвращается TRUE, иначе FALSE. Чтобы получить расширенные сведения об ошибке, следует вызвать функцию GetLastError().

Как мы уже замечали ранее, расчитанные данные для отрисовки на график в нашей программе будут храниться в массиве ExtMapBuffer1[]. Его мы и связываем с нашей единственной линией, имеющей индекс 0.

IndicatorShortName();

void IndicatorShortName(string name)

- Установка «короткого» имени пользовательского индикатора для отображения в подокне индикатора и в окне DataWindow.

Мы в нашей программе завели переменную short_name типа string, которой присвоили значение «Your first indicator is running!». Затем мы передали эту переменную в функцию IndicatorShortName(); .

Код:

Код:
return (0);
Разбор:

Функция init() возвращает 0 и завершает свою работу. Управление переходит функции start().

Код:

Код:
int deinit()
 {
//----

//----
 return(0);
 }
Разбор:

Ничего нового про функцию deinit() сказать не могу.

Теперь продолжим разбирать код

Выше мы разбирали код нашего индикатора строчку за строчкой и дошли до функции start().
Очень надеюсь, что Вы прекрасно понимаете.
Теперь мы изучим функцию start() и её содержимое, и, наконец-то, мы скомпилируем и запустим наш первый индикатор.
Давайте разберём оставшийся код!

Наш код:

Цитата:
//+——————————————————————+
//| MyFirstIndicator.mq4 |
//| Kirill |
//| StockProgrammer@mail.ru |
//+——————————————————————+
#property copyright «Kirill»
#property link «StockProgrammer@mail.ru»#property indicator_separate_window
#property indicator_buffers 1
#property indicator_color1 Red
//—- buffers
double ExtMapBuffer1[];
//+——————————————————————+
//| Custom indicator initialization function |
//+——————————————————————+
int init()
{
//—- indicators
SetIndexStyle(0,DRAW_LINE);
SetIndexBuffer(0,ExtMapBuffer1);string short_name = «Your first indicator is running!»;
IndicatorShortName(short_name);//—-
return(0);
}
//+——————————————————————+
//| Custom indicator deinitialization function |
//+——————————————————————+
int deinit()
{
//—-//—-
return(0);
}
//+——————————————————————+
//| Custom indicator iteration function |
//+——————————————————————+
int start()
{
int counted_bars=IndicatorCounted();//—- check for possible errors
if (counted_bars<0) return(-1);
//—- last counted bar will be recounted
if (counted_bars>0) counted_bars—;

int pos=Bars-counted_bars;

double dHigh , dLow , dResult;

Comment(«Hi! I’m here on the main chart windows!»);

//—- main calculation loop

while(pos>=0)
{
dHigh = High[pos];
dLow = Low[pos];
dResult = dHigh — dLow;

ExtMapBuffer1[pos]= dResult ;

pos—;

}

//—-

return(0);
}
//+——————————————————————+

Код:

Код:
int start()
 {

 }
Разбор:

Как я Вам уже говорил, мы будем проводить 90% нашей программистской жизни внутри фигурных скобок тела функцииstart(). Это так, потому что она самая важная из всех трёх спец. функций MQL4. В отличии от функций init() и deinit()функция start() не будет вызвана (клиентским терминалом) только единожды. Она будет вызываться при каждом поступление новых котировок. Функция start() возвращает значение типа integer, как и все остальные спец. функции языка MQL4. 0 означает, что функция отработала без ошибок, а любое другое число означает, что произошла ошибка.

Код:

Код:
 int counted_bars=IndicatorCounted();
Разбор:

Здесь мы объявили переменную counted_bars типа integer и инициализировали её значением, возвращаемым функцией IndicatorCounted(); .

int IndicatorCounted( )

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

Замечание: самый последний бар не считается посчитанным, и в большинстве случаев необходимо пересчитывать только его. Однако бывают пограничные случаи, когда вызов пользовательского индикатора производится из эксперта на первом тике нового бара. Возможна ситуация, что последний тик предыдущего бара не обработан (по той причине, что в момент прихода этого последнего тика обрабатывался предпоследний тик), и пользовательский индикатор не был вызван и поэтому не был рассчитан. Чтобы избежать в такой ситуации ошибок расчета индикатора, функция IndicatorCounted() возвращает реально посчитанное количество баров минус один.

ОЧЕНЬ ВАЖНО: на будущее запомните, что в MQL4 бары нумеруются задом-наперёд. Нулевой бар — это текущий бар, следующий (более старый — левее на графике) — это первый бар, за ним — второй и т.д. При появлении нового бара они все перенумеровываются, и текущий опять становится нулевым.

Код:

Код:
//---- check for possible errors
 if (counted_bars<0) return(-1);
Разбор:

Очевидно, что число баров, не измененных после последнего вызова индикатора должно быть = 0 или > 0. Если же оно < 0, значит произошла ошибка. Мы терминируем функцию start() и сообщаем об ошибке, возвращая не 0.

Код:

Код:
//---- last counted bar will be recounted
 if (counted_bars>0) counted_bars--;
Разбор:

Притворяемся, что посчитано на один бар меньше, чтобы пересчитать последний бар. На самом деле, это перестраховка, т.к. функция IndicatorCounted( ) и так возвращает число на 1 меньше. Но ничего страшного в том, что мы пересчитаем ещё один лишний бар нет. PS: надеюсь, вы помните, что —; — это оператор декремента — уменьшение на единицу.

Код:

Код:
 int pos=Bars-counted_bars;
Разбор:

Здесь мы объявляем переменную pos, которая указывает, сколько раз должен сработать наш счётный цикл (про цикл while см. далее). ‘Функция’ Bars возвращает количество уже имеющихся на графике баров. Чтобы вычислить pos мы из общего количества баров графика вычитаем количество уже посчитанных баров.

Кстати, хороший момент обсудить ‘функцию’ Bars и её братьев.

Предопределённые переменные языка MQL4:

Ask, Bid, Bars, Close, Open, High, Low, Time и Volume — являются функциями, хотя в MQL4 они называются предопределёнными переменными и после них не надо ставить круглые скобки.
И я докажу Вам, что они скорее функции, чем переменные.
Переменная — означает область в памяти + тип данных, который Вы указываете.
Функция — означает сделать что-то и вернуть какое-то значение. Например, Bars считает и возвращает количество баров на графике. Так что же? Это переменная?
Если ввести следующий код:
Bars = 1;
Вы получите ошибку: ‘Bars’ — unexpected token
Это потому что они не переменные, и Вы не можете присваивать им значения.
Итак, обсудим эти функции.

int Bars

Эта функция возвращает значение типа integer, в котором содержится информация о количестве имеющихся на текущем графике барах.

double Ask

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

double Bid

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

Замечание: Например, USD/JPY = 105.11/105.14 — здесь левая цена — это bid (цена, по которой Вы продаёте), правая цена — это ask (цена, по которой Вы покупаете).

double Open[]

Эта функция возвращает значение типа double, в котором содержится информация о цене открытия (далее — везде ‘цена’ — это bid) для указанного бара.

Например: Open[0] вернёт цену открытия текущего бара.

double Close[]

Эта функция возвращает значение типа double, в котором содержится информация о цене закрытия для указанного бара.

Замечание: Т.к. цена закрытия текущего бара ещё никому не известна, Close[0] возвращает текущую цену bid.

double High[]

Эта функция возвращает значение типа double, в котором содержится информация о самой высокой цене (цене high) для указанного бара.

double High[]

Эта функция возвращает значение типа double, в котором содержится информация о самой низкой цене (цене low) для указанного бара.

double Volume[]

Эта функция возвращает значение типа double, в котором содержится информация ( для forex объёмы не контролируются, поэтому: ) о количестве изменений котировки для указанного бара.

int Digits

Эта функция возвращает значение типа integer, в котором содержится информация о количестве знаков после запятой для котировки данной валюты. Обычно 4.

int Point

Эта функция возвращает значение типа double равное одному пункту для данной валютной пары. Обычно 0.0001 .

datetime Time[]

Эта функция возвращает значение типа datetime, в котором содержится информация о времени открытия указанного бара.

Код:

Код:
 double dHigh , dLow , dResult;
Разбор:

Мы объявили три переменные типа double, которые мы используем позже. Заметьте, как мы объявили все три в одной строчке, разделив их запятыми.

Код:

Код:
 Comment("Hi! I'm here on the main chart window!");
Разбор:

В этой строчке мы используем функцию Comment, чтобы распечатать текст «Hi! I’m here on the main chart window!» в левом верхнем углу основного графика.

Всего таких информирующих функции три:

void Comment()

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

void Print( …)

- Печатает некоторое сообщение в журнал экспертов. Параметры могут иметь любой тип. Количество параметров не может превышать 64.

void Alert( …)

- Отображает диалоговое окно, содержащие пользовательские данные. Параметры могут быть любого типа. Количество параметров не может превышать 64.

Код:
Код:
//---- main calculation loop

 while(pos>=0)
 {
 dHigh = High[pos];
 dLow = Low[pos];
 dResult = dHigh - dLow;

 ExtMapBuffer1[pos]= dResult ;

 pos--;

 }
Разбор:
Пришло время войти в цикл расчёта отображаемых нашим индикатором точек. Любое значение, которое мы положим в массив ExtMapBuffer1[] будет отображено на графике (потому что, используя функцию SetIndexBuffer(), мы связали этот массив с линией, индексируемой нулём).

ОЧЕНЬ ВАЖНО: когда мы связываем массив с линией, массив приобретает ещё одно спец. свойство. При появлении нового бара на графике, все элементы массива сдвигаются влево на один, т.е. N-й становится N+1-ым, … , 1-й становится 2-м, 0-й становится 1-м. Таким образом, высвобождается место для нового нулевого элемента. Это сделано для того,чтобы при пересчёте только новых баров графика, информация о значении индикатора на старых барах сохранялась в элементах масива, индексируемых теми же числами, что и сами бары.

Переменной цикла (она регулирует число его прохождений) у нас является переменная pos. Мы её используем для обращения к неподсчитанным барам. Например High[pos] вернёт максимальное значение цены на баре с индексом pos.
В теле цикла мы присваиваем переменной dHigh значение цены high на баре pos.
Аналогично, мы присваиваем переменной dLow значение цены low на баре pos.
Разницу dHigh — dLow мы присваиваем переменной dResult.
Затем Мы используем dResult для отрисовки линии индикатора, присваивая его значение соответствующему элементу массива ExtMapBuffer1[] (элементу с индексом pos).
Последняя строчка цикла — мы применяем оператор декремента к переменной цикла pos. Когда, pos станет = -1, цикл завершит свою работу.

Наконец-то мы можем скомпилировать наш индикатор!

Нажмите F5 или нажмите кнопку Компилировать.
В результате сгенерируется исполняемый файл «MyFirstIndicator.ex4″, который Вы можете запустить в своём терминале.
Чтобы это сделать нажмите F4 или вручную откройте терминал. В окне навигатора в терминале выберите раздел «Пользовательские Индикаторы«, найдите MyFirstIndicator и перетащите его на график цены.
Замечание: Ваш индикатор показывает волатильность рынка.
Надеюсь, Вам понравился Ваш только, что созданный первый индикатор и материал был усвоен.


0 Responses to “Пишем индикатор для Forex.”

Отправить комментарий