Как написать советника для mt4 своими руками

Все мы знаем, что основное достоинство терминала Metatrader 4 – это возможность создания роботов и торговля с их помощью. 

Далеко не все трейдеры владеют программированием на mql4/5. Разработчики программы Forex Tester смогли решить эту проблему. Теперь можно создать робота или реализовать торговую идею в виде индикатора на ресурсе «Визуальный конструктор стратегий» (Visual Strategy Builder). Без навыков программирования.

Как это сделать, плюсы и минусы бесплатного (на текущий момент) конструктора роботов – в нашем материале.

Visual Strategy Builder — Автоматизируем Стратегии без программирования

Постоянные читатели нашего сайта знакомы с Forex Tester 4 – уникальным симулятором торгов, выступающим в роли тренажера, тестера и анализатора. Программа имела функцию написания на заказ индикаторов и советников, совместимых с Metatrader. Теперь разработчики платформы решили дать каждому трейдеру шанс самостоятельно создать собственные стратегии. 

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

Сервис Visual Strategy Builder расположен по адресу: https://tools.forextester.com/

Зачем нужен Visual Strategy Builder?

Visual Strategy Builder (VSB) представляет собой программную оболочку с набором инструментов теханализа, которые хорошо знакомы пользователям Metatrader 4. В отличие от этой торговой платформы VSB позволяет задавать в индикаторах правила открытия позиции и установки ордеров тейк-профит и стоп-лосс.

Любой новичок может выбрать сигнал на открытие позиции из набора готовых опций (кроссоверы, пересечения уровней, больше/меньше и т. д.). Эти настройки открывают недоступную для Metatrader 4 возможность протестировать работу одного или нескольких индикаторов. 

Например, можно получить реальный торговый результат по стратегии пересечений скользящих средних линий. Такой советник создается в VSB за 5 минут, потом его можно экспортировать в Metatrader 4 или Forex Tester.

Разобравшись с созданием одного индикатора в Visual Strategy Builder, трейдер может в несколько кликов создать рабочую автоматизированную стратегию и тоже проверить ее в тестере. Если советник показывает положительные результаты, то по его сигналам можно торговать на реальном счете через Metatrader 4.

Кстати, тестировать стратегию однозначно лучше через Forex Tester – там точнее котировки и можно быстро эмулировать сессию за любой торговый день. Например, выбрав какой-нибудь «черный вторник», чтобы воочию понять, как аномальная волатильность повлияет на настройки мани менеджмента.

Профессиональные трейдеры могут экспортировать код советников прямо в VSB, соединять его с созданными там программами или редактировать в Metaeditor и отправлять обратно в Metatrader 4.

Visual Strategy Builder будет полезен тем, кто ищет новые идеи – программа поддерживает библиотеку стратегий пользователей. Любой желающий без навыков программирования сразу поймет «внутренности алгоритма» по составу индикаторов и описанным правилам торговых сигналов. Тут же можно самостоятельно убедиться в результативности торговой системы.

Как работать на платформе Visual Strategy Builder

Сервис Visual Strategy Builder расположен по адресу: https://tools.forextester.com/

Использование VSB требует регистрации – это быстрая и несложная процедура. Введите адрес электронной почты и придумайте пароль для входа. Он должен быть не менее 8 символов, содержать буквы разного регистра, почтовый ящик лучше указать gmail.

После заполнения вышеуказанных строк программа сразу открывает окно конструктора стратегий, автоматически переходя на русскоязычную версию.

Валютным спекулянтам доступно создание стратегии, использование шаблона от разработчиков или других пользователей VSB, а также создание собственного «индикатора мечты».  Если пользователь планирует собрать торговую систему, надо нажать на иконку «Стратегии».

Платформа удобно сохраняет их под выбранным пользователем именем с кратким описанием сути системы. На странице предусмотрена кнопка возврата, если трейдер допустил ошибку на предыдущем этапе.

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

  • Уровни (Ценность);
  • Конкретная цена;
  • Диапазон (цены открытия, закрытия, максимумы и минимумы свечей);
  • Открытие по дням недели, месяца (опция время);
  • По конкретному объему или его абстрактному критерию;
  • Таймфрейм.

Перечисленные опции можно объединять друг с другом, накладывая различные условия выполнения сделки, например, пробой уровня только до американской сессии и т. д. Определитесь с главным инструментом своей стратегии и перетащите его в окно «Состояние 1».

Как только он там отобразится, можно приступать к редактированию параметров. Наведите курсор на поле присоединенного Bollinger Bands (BB), чтобы увидеть эту опцию.

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

Набор правил входа в позицию доступен после нажатия рядом окна «Операции», вместе с которым активируется аналогичная опция слева со списком шаблонов. Учитывая, что работа идет с верхней линией, а вход в рынок планируется по контртренду, выбираем кроссовер сверху вниз.

Теперь конкретизируем тип позиции и условия мани менеджмента с помощью пункта меню на левой панели «Действия». Перетаскиваем в поле ордер на продажу и редактируем его параметры.

Пройдем по пунктам снизу вверх – индикатор на графике предусматривает многократное открытие позиции, поэтому ставим «Нет» на предложение открыть ордер один раз. Магический номер необходим, если открытие позиций по сигналам Bollinger Bands будет пересекаться с работающими на графике другими советниками.

Остальные опции понятны, следует только внимательно отнестись к пунктам: 25 – это для четырехзначной системы котировок. При пяти знаках умножайте значения пунктов на 10.

Для нижней линии Боллинджера потребуется создать второе правило, опцию можно выбрать слева вверху, повторив все вышеописанные операции со следующими изменениями:

  • В настройках индикатора выбираем нижнюю линию;
  • Кроссовер – снизу вверх;
  • Тип ордера, открываемого по рынку – «Покупка».

Полученную стратегию можно сохранить и экспортировать в ForexTester или Metatrader 4. В последнем случае используйте латинские буквы в названии файла. Стратегия сохраняется в файл ex4 в папку, выбранную трейдером.

Запустить написанный советник можно сразу после его установки в Metatrader 4. Это делается обычным способом, описанным на нашем сайте.

Заключение

Основной плюс Конструктора стратегий от Forex Tester – простота интерфейса и функциональность шаблонов. Это позволяет трейдеру частично автоматизировать стратегии или дополнить ручную торговлю дополнительными сигналами, которые приходилось искать визуально на графике.

Любителей сеток особо порадует наличие опции «Мартингейл», а также команд на закрытие всех ордеров по условию. Среди минусов VSB следует отметить баги бета-версии и будущую запланированную плату за опцию. Так что спешим писать и экспортировать советников, пока платформа имеет открытую лицензию.

На ней можно попробовать создать прототип своей торговой идеи и, если она окажется прибыльной, обратиться к программистам для полноценной ее реализации.

Тема на форуме

С уважением, Алексей Вергунов
Tlap.com

Come to my page!

Если вы задавались вопросом «Как написать советник на языке программирования MQL4», то данный пост создан именно для вас. Сегодня мы будем создавать самый простой из всевозможных советников для МТ4, который будет определять незамысловатое условие на вход, открывать ордер и модифицировать его цели.

Алгоритм программирования советника на MQL4

Выше представлена схема процесса работы торгового робота, который мы планируем написать. Любой советник должен иметь как минимум три функции обработки событий:

OnInit()

Она генерируется только один раз за время работы советника в самом начале. Нужна, чтобы внутри нее определить, рассчитать и задать те переменные и массивы данных, которые не нуждаются в дальнейшей корректировке по мере обновления тиков. То же самое касается и графических объектов. В советнике раздел OnInit выполняет ту же роль, что мы проходили в теме создания первого индикатора.

OnDeinit()

Функция вызывается советником только один раз перед непосредственным удалением его с графика. Она используется также и в индикаторах. Раздел OnDeinit нужен, чтобы подчистить график после советника, обнулить глобальные переменные и выдать окончательный расчет или текст пользователю. Более подробно мы ее проходили при создании первого индикатора.

OnTick()

Данная функция новая в наших уроках. Раздел OnTick генерирует события исключительно для экспертов и не может быть вызвана в индикаторах или скриптах. Ее тип данных void и набор параметров отсутствует. По своей логике работы она схожа с функцией для индикаторов OnCalculate,  только она не возвращает никакого значения после завершения. Задача у нее одна — запускаться каждый новый тик и проходить циклом весь написанный код в ней от начала до конца. Так как любой советник должен проверять условия для входа/выхода из рынка, считать количество открытых ордеров и выполнять их сопровождение, то можно уверенно сказать, что функция OnTick является самой важной в коде любого эксперта.

Продолжим разбирать алгоритм работы. Вначале советник инициализируется. Далее запускается функция OnTick, в которой выполняются все дальнейшие действия. Для данного советника сначала необходимо проверить наличие уже открытых им ордеров. Если они есть — дальнейший расчет и поиск условия на вход не выполняются, потому что в рынке у нас должен быть только один ордер за раз. Если же открытых сделок нет, то идет определение направления будущей позиции. В зависимости от него запускается пользовательская функция на открытие Buy или Sell ордера. Если по какой-то причине сделка не смогла открыться, расчет возвращается назад в функцию OnTick, чтобы попробовать выставить ордер снова. Если же ордер открылся, то он модифицируется (выставляется Тейк-Профит и Стоп-Лосс). На этом алгоритм заканчивает свою работу по работе с ордерами, потому что счетчик новых ордеров уже будет учитывать этот открывшийся ордер, делая проверку каждый тик. И только после того, как ордер закроется по достижению своей цели (ТП или СЛ), цикл проверки условия на открытие ордера запустится снова. Функция OnDeinit запустится только тогда, когда вы удалите советник с графика.

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

Торговая система «Монетка»

форекс тс монетка

Когда перед нами стоит вопрос «как создать Форекс советник», то в первую очередь в голову приходит мысль о торговой системе, заложенной в нем, логике открытия ордеров. Так как в этом уроке не стоит цель создать так называемый на сленге «Грааль», то для получения опыта в написании первого эксперта мы возьмем на вооружение самую простую ТС, которую только можно выдумать.

Период советника установим М15. Мы будем входить в рынок по воле случая, или, если хотите, удачи. Мы начнем условно подкидывать монетку и смотреть на результат этого несложного действия. Орел или Решка — только два исхода событий будет у советника. Вариант «ребро» в данном случае не рассматривается 🙂

Зачем использовать такую простую систему? Чтобы понять, что произойдет в результате этого эксперимента и ответить себе на вопрос: можно ли заработать на Форекс входя в рынок наобум? Получится ли прибыльно торговать не имея четко спланированной торговой системы?

Проверка советника на ошибки: GetLastError()

При написании советника (как и при его использовании) очень важно следить за возможными ошибками в коде. Речь идет не об ошибках компиляции, их вы можете увидеть во вкладке «Ошибки» окна «Инструменты». Разговор о скрытых ошибках, которые проявляют себя во время тестирования или работы советника на реальном счете. Проверку на эти ошибки нужно делать как минимум один раз за тик в функции обработки событий OnTick. Как максимум — в каждой вашей пользовательской функции.

Чтобы получить номер возможной ошибки, нужно вызвать функцию GetLastError(). Она имеет тип int и возвращает целое число, значение системной переменной _LastError. Тут нужно обратить внимание, что на языке MQL4 после вызова функции GetLastError ее значение обнуляется и повторный вызов ее в том же месте кода вернет значение 0, поэтому ее значение нужно сохранять в отдельную переменную.

Язык MQL различает как минимум 150 разных ошибок. Соответственно каждый возвращаемый номер имеет свою расшифровку. Ноль значит это ошибки нет и все отлично, 134 значит, что недостаточно денег на счете и т.д.

Чтобы получить примерную расшифровку ошибки на английском языке, нужно обратиться ко встроенной библиотекой stdlib.mq4, которая находится в папке Libraries терминала. Для этого нужно воспользоваться препроцессором включение файлов #include. Для этого прописываем импорт данной библиотеке в самом начале кода вне функций.

#property copyright «Copyright (c) DaVinci FX Group»

#property link «https://www.davinci-fx.com/»

#property version «1.00»

#property strict

#include <..Librariesstdlib.mq4> //библиотека для расшифровки ошибок

Теперь осталось прописать проверку в самом конце функции обработки событий OnTick()

int Error = GetLastError(); //поиск ошибок по завершению тика

if(Error != 0) Print(«OnTick() Error «,Error,«: «,ErrorDescription(Error));

В этом коде вначале сохраняется значение ошибки в переменную Error, далее проверяется, чтобы ошибка точно была (значение не было равно нулю) и если это так, то выводится принт в журнал с номером ошибки и ее расшифровка.

Напиши в комментариях, если вы хотите видеть более подробную расшифровку ошибок на русском языке в будущих уроках.

Пишем свой первый советник

Чтобы создать советник, нажмите «Файл»  — «Создать». Затем выберите вариант «Советник (шаблон)», клик по кнопке Далее, придумываете ему имя, гордо записывая себя, как автора и добавляете ссылку на ваш (или наш) сайт. Параметры добавлять не нужно, мы их пропишем вручную, тык по кнопке Далее. Обработчики событий нам не нужны, поэтому еще раз клик на Далее. Функция OnTester полезная при оптимизации сов, но в этом уроке она не понадобится (мы рассмотрим ее в будущих статьях). Жмем Готово.

Программа создаст стандартный шаблон из описания и трех функций обработки событий: OnInit, OnDeinit и OnTick.

Переходим к созданию внешних переменных советника, чтобы у пользователя была возможность задать свои параметры работы советника, а также запустить оптимизацию разных настроек. Основными базовыми параметрами любого советника являются:

  • Lot — торговый лот, с которым будет открываться ордер. По умолчанию задаем его минимальным. Тип переменной будет double, т.к. это лот не может быть целым числом.
  • Slippage — максимально допустимое проскальзывание в пунктах, больше которого ордер не будет открыт.
  • Stop Loss — Стоп Лосс (ограничение убытка) для каждого ордера советника. По умолчанию задается в пунктах.
  • Take Profit — Тейк Профит нужен для закрытие ордера при достижении заданной прибыли. По умолчанию задается в пунктах.
  • Comments — текстовый комментарий, с которым будет открыт ордер. Его можно будет увидеть в окнах «Торговля» и «История Счета» в столбце «Комментарий». Он полезен, если на счете торгует несколько советников, либо используется ручная торговля. Некоторые брокеры в комментарий прописывают проскальзывания при открытии конкретного ордера. Также он хранит информацию, если ордер был частично закрыт.
  • MagicNumber — номер (целое число), который задается всем ордерам конкретного советника, открытым по текущему символу. Магик нужен для того, чтобы советник мог отличить свои сделки от чужих. Для каждой пары и советника рекомендуется задавать свой уникальный магик. Ордера, открытые вручную имеют нулевой магик. Магик также можно увидеть на мониторинге сервисов MyFxBook или FxBlue, и сделать по нему фильтрацию.

Данные шесть параметров являются необходимыми даже для самого простого советника. Чтобы их можно было видеть при настройке советника, воспользуемся классом памяти extern, который определит наши переменные как внешние. Данный блок кода прописывается в самом начале редактора.

0

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

//+——————————————————————+

//| 3.0 Coin EA |

//| Copyright (c) DaVinci FX Group |

//| https://www.davinci-fx.com/ |

//+——————————————————————+

#property copyright «Copyright (c) DaVinci FX Group»

#property link «https://www.davinci-fx.com/»

#property version «1.00»

#property strict

#include <..Librariesstdlib.mq4>

extern string s0 = «<== General Settings ==>»; //> > >

extern double Lot = 0.01;

extern int Slippage = 5;

extern double StopLoss = 20;

extern double TakeProfit = 20;

extern string Comments = «DaVinci EA»;

extern int MagicNumber = 123123;

Переходим к функции OnInit. Так как на Форекс до сих пор существуют типы счетов с котировками, у которых разное количество знаков после запятой, то необходимо добавить проверку для тех параметров, значение которых указываются в пунктах. Изначально мы задали значения проскальзывания, Стоп Лосса и Тейк Профита в старых пунктах. Если же у валютной пары три знака (для JPY) или пять (для остальных валют) после запятой, то нам необходимо перевести старые пункты в новые, умножив это значение на десять.

int OnInit() {

if (Digits == 3 || Digits == 5) {

Slippage *= 10;

StopLoss *= 10;

TakeProfit *= 10;

}

return(INIT_SUCCEEDED);

}

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

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

Мы дошли до функции OnTick. В ней дальнейший код будет совершать свой цикл каждый тик. В соответствии со схемой работы нашего алгоритма вначале мы должны проверить, что в рынке нет открытых ордеров у данного советника. Для этого напишем пользовательскую функцию анализа сделок CountOrder().

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

int CountOrder() {

int orders=0;

for(int i=OrdersTotal()-1;i>=0;i—){

if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)==false) continue;

if(OrderSymbol() !=_Symbol || OrderMagicNumber() != MagicNumber) continue;

if(OrderType() > 1) continue;

orders++;

}

return orders;

}

Теперь эту функцию нужно добавить в тело OnTick и проверить условие, чтобы открытых ордеров не было.

void OnTick() {

if(CountOrder() == 0) { //проверка, чтобы не было открытых ордеров

// дальнейший код

}

int Error = GetLastError();

if(Error != 0) Print(«OnTick() Error «,Error,«: «,ErrorDescription(Error));

}

Далее нам необходимо определиться с направлением сделки. Как говорилось выше, мы будем входить по системе «Монетка», т.е. нам нужно рандомно (наугад) определить направление входа. С этим поможет справиться функция MathRand(), она возвращает псевдослучайное целое число в диапазоне от 0 до 32767. Так как нам нужно получить только два значения: 0 (для покупок) или 1 (для продаж), то просто добавим знак %, с помощью которого получим остаток от деления на 2. Каждый тик результат будет случайно определяться между 0 и 1.

int TradeDirection = MathRand()%2; //поиск рандомного направления сделки

Пользовательская функция OpenTrade()

С условием на вход определились, осталось в зависимости от него осуществить покупку или продажу. Для этого введем еще одну пользовательскую функцию OpenTrade(). Ей будет передаваться один параметр — направление на вход. В зависимости от направления сделки нужно определить:

  • Price — цена открытия ордера. Рыночные ордера на покупку открываются по цене Ask, а закрываются по цене Bid. И наоборот, рыночные ордера на продажу открываются по цене Bid, и закрываются по цене Ask.
  • Цвет стрелочки да пометки открытия ордера на графике. По обычаю покупки имеют синий цвет, продажи красный.
  • Текст для открытия ордера.

Для упрощения работы мы воспользуемся условным оператором ? 

bool OpenTrade(int OP_Type) {

double price = (OP_Type == OP_BUY ? Ask : Bid);

color col_type = (OP_Type == OP_BUY ? clrBlue : clrRed);

string op_str = (OP_Type == OP_BUY ? «на покупку» : «на продажу»);

Print(«Открываем ордер « + op_str);

//дальнейший код

return(false);

}

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

  • symbol — символ, по которому необходимо открыть ордер. Текущий символ задается как Symbol().
  • cmd — торговая операция, т.е. один из шести типов ордера, который нужно открыть: рыночные (buy или sell) и отложенные (buylimit, selllimit, buystop, sellstop).
  • volume — размер торгового лота.
  • price — цена открытия ордера. Для рыночных ордеров нами она рассчитана выше.
  • slippage — проскальзывание в пунктах, берется из внешней переменной.
  • stoploss — Стоп Лосс рассчитанный по цене котировок.
  • takeprofit — Тейк Профит рассчитанный по цене котировок.
  • comment — комментарий к ордеру, берется из внешней переменной.
  • magic — магик номер, берется из внешней переменной.
  • expiration — срок жизни ордера. Нужен только для отложенных ордеров, поэтому оставляется равный нулю.
  • arrow_color — цвет стрелочки на графике при открытии ордера.

Открываем ордер.

int ticket = OrderSend(Symbol(), OP_Type, Lot, price, Slippage, 0, 0, Comments, MagicNumber, 0, col_type);

Пометка: после появления современных технологий торговли, таких как ECN, STP и NDD, цели для ордера нельзя выставлять сразу при подаче заявки на открытие ордера. Его нужно модифицировать только после успешного открытия сделки. Для старых типов счетов можно задать его сразу, но зачем, когда можно провести модификацию и после.

Ордер открылся, теперь ему необходимо выставить цели (Тейк Профит и Стоп Лосс) в случае, если он сработал без ошибок. Для этого данный ордер нужно выделить по номеру тикета с помощью функции OrderSelect.

Для расчета цены Стоп Лосса по текущим котировкам:

  • для покупок от текущей цены открытия ордера (OrderOpenPrice) вычитаем значение переменной StopLoss, умноженной на Point.
  • для продаж к текущей цене открытия ордера (OrderOpenPrice) прибавляем значение переменной StopLoss, умноженной на Point.

Для расчета цены Тейк Профит по текущим котировкам:

  • для покупок к текущей цене открытия ордера прибавляем значение переменной TakeProfit, умноженной на Point.
  • для продаж от текущей цены открытия ордера вычитаем значение переменной TakeProfit, умноженной на Point.

Модификацию выполняем с помощью торговой функции OrderModify. Она содержит почти такие же параметры, как и функция OrderSend, плюс ей передается номер тикета ордера, который нужно модифицировать.

Собрав все описанное выше вместе у нас получается готовая пользовательская функция для открытия и модификации ордера.

0

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

bool OpenTrade(int OP_Type) {

double price = (OP_Type == OP_BUY ? Ask : Bid);

color col_type = (OP_Type == OP_BUY ? clrBlue : clrRed);

string op_str = (OP_Type == OP_BUY ? «на покупку» : «на продажу»);

Print(«Открываем ордер « + op_str);

int ticket = OrderSend(Symbol(), OP_Type, Lot, price, Slippage, 0, 0, Comments, MagicNumber, 0, col_type);

if(ticket > 0) { //Если ордер был открыт

if(OrderSelect(ticket, SELECT_BY_TICKET, MODE_TRADES)) {

double SL = 0, TP = 0;

if(OP_Type == OP_BUY) {

SL = OrderOpenPrice()-StopLoss*Point;

TP = OrderOpenPrice()+TakeProfit*Point;

}

else {

SL = OrderOpenPrice()+StopLoss*Point;

TP = OrderOpenPrice()-TakeProfit*Point;

}

Print(«Модифицируем ордер « + op_str);

if(!OrderModify(ticket, price, SL, TP, 0, clrNONE)) {

int Error = GetLastError();

Print(«Ошибка модификации ордера «,Error,«: «,ErrorDescription(Error));

}

else return(true);

}

}

else {

int Error = GetLastError();

Print(«Ошибка открытия ордера «,Error,«: «,ErrorDescription(Error));

}

return(false);

}

Соответственно, у нас определяется цена открытия ордера, цвет и текст. Далее открывается ордер и, если его тикет больше нуля, тогда в бой вступает расчет целей для ордера в значении котировок на графике. Затем происходит попытка модифицировать ордер, и если все прошло гладко, то функция OpenTrade вернет true, иначе же выдаст принт об ошибке и ее номер с расшифровкой. Если же ордер не открылся — советник также выдаст принт об ошибке и попытается произвести открытие на следующем тике.

Нам осталось вернуться в функцию OnTick и добавить эту пользовательскую функцию в его тело.

void OnTick() {

if(CountOrder() == 0) { //проверка, чтобы не было открытых ордеров

int TradeDirection = MathRand()%2; //поиск рандомного направления сделки

if(TradeDirection == OP_BUY) {

if(OpenTrade(OP_BUY)) Print(«Ордер на покупку открыт и модифицирован»);

}

else if(TradeDirection == OP_SELL) {

if(OpenTrade(OP_SELL)) Print(«Ордер на продажу открыт и модифицирован»);

}

}

int Error = GetLastError(); //поиск ошибок по завершению тика

if(Error != 0) Print(«OnTick() Error «,Error,«: «,ErrorDescription(Error));

}

Проверка советника в тестере

Мы не знаем, какой таймфрейм нужен для данного советника, поэтому можно использовать любой. То же самое и с котировками, их можно использовать и с 90% качеством, для проверки работоспособности кода этого достаточно. Наша основная цель — запустить визуализацию и просмотреть глазами выполнение его логики торговли.

Хочу заметить, что раз вы взялись за изучение программирования на MQL4, то при тестировании самый основной режим для вас всегда будет режим визуализации. Только так можно найти и устранить основные ошибки в первоначальной логике кода. Визуализация плотно совмещается с работой с журналом. График и огромное количество ваших принтов смогут помочь найти большинство ошибок.

Полученный код должен выдавать три принта при каждом новом появлении ордера (об открытии, модификации и об этом всем вместе). Никаких других ошибок в журнале быть не должно. Смотрим в журнал.

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

Посмотрим на график тестирования системы «Монетка» и сделаем выводы.

Заключение

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

Одной из целей данного урока было доказать, что необдуманная торговля не в состоянии принести прибыль. В данном примере мы использовали рандомный вход с абсолютно одинаковыми ТП и СЛ, равными 20 пунктов каждый. Т.е. при таком раскладе, если 55% сделок были бы прибыльными, то график доходности шел вверх. В нашем случае кривая прибыли нацелена строго вниз, что говорит нам самую простую истину: вы никогда не заработаете на рынке, если у вас не будет четкой и строго сформулированной торговой системы. Форекс это далеко не казино и на волю случая тут нельзя полагаться. Без четких правил входа, сопровождения ордеров и выхода из сделки вас ждет неудача.

На этом все, код советника приложен ниже. Если пост был вам полезен, то вы знаете, что делать! Оставляйте комментарии и делитесь им в тематических сообществах. Всем профитов!

0. Начало работы

Блог для успешных трейдеров на рынке Форекс. Полезный сайт о Forex — DaVinci FX Group.

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

progr

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

Итак, время, потраченное впустую – это опыт. Время – это деньги. Грубо говоря, тратя свое время, вы покупаете опыт. Но вы, как разумный человек, наверняка захотите сохранить лишнюю копейку, не ошибаясь там, где можно обойти грабли, тем более, если существует такая возможность. На этом моменте мне придется попросить немного вашего времени, дабы рассказать, как на самом деле стоит подготовиться к вопросу создания машины для печатания денег торгового робота, и, конечно же, как не наделать кучу ошибок.

1. Разработку форекс робота начинаем с идеи

Разработка робота для торговли на рынках – это непрерывный поиск идей и, как частное, поиск Святого Грааля. Вопреки утверждениям непросвещенных граждан, Грааль находится достаточно просто – скажем «спасибо» технологическому прогрессу. Попробуйте вбить в гугл «Святой Грааль Форекс» – скорее всего, советник вашей мечты находится на первой странице выдачи, к тому же абсолютно бесплатен.

Собственно, на этом рассказ можно было бы закончить. Если идеальный советник уже существует, зачем продолжать поиски? Но следом возникает второй вопрос: почему, если идеальный советник уже найден, в Африке до сих пор существует дефицит питьевой воды? Если ответ для вас очевиден, поздравляю, вы прошли первый этап становления трейдера разумного, преодолев уровень новичка или алгофанатика, не видящего перспектив своего развития, и нацеленных лишь на результат.

Написать форекс советника

Уверен, идея вашего советника абсолютно уникальна и не имеет аналогов. Но, в любом случае, глупо не попытаться найти похожие разработки других авторов – масштаб проделанных исследований сложно оценить, пока не столкнешься с ним лицом к лицу. Человечество идет на пути к технологической сингулярности, и новые разработки внедряются настолько быстро, что осмыслить весь выложенный материал часто не представляется возможным. Если же подобных разработок все же не нашлось, и протестировать идею без проведения собственных исследований нельзя, пора переходить к следующему этапу.

2. 4 важных вопроса перед разработкой

Но перед этим попытайтесь ответить себе на следующие вопросы:

  • В чем состоит ваша торговая идея?
  • Какие задачи будет решать советник, и какое объективное преимущество это может дать в торговле?
  • Возможно, вам нужен графический индикатор или вспомогательный скрипт?
  • Насколько идея реализуема технически, и можете ли вы самостоятельно оценить ее сложность?

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

[info_block align=»right» linkText=»Форекс советники» linkUrl=»https://fortrader.org/forex-ea-testing/» imageUrl=»http://files.fortrader.org/uploads/2016/08/robot.jpg»]Исследование торговых роботов с возможностью скачать советника.[/info_block]

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

3. Конструктор советников – не проходим мимо!

У новичков на валютном рынке часто возникает множество вопросов, ответы на которые бывают не совсем очевидны, либо являются лишь вопросом неопытности оного. Будучи начинающим трейдером, достаточно прочитать одну книгу Билла Вильямса, чтобы на всю жизнь подменить понятие торгового хаоса торговым порядком. Затем такие люди удивляются, почему вот здесь да не купить, а вот здесь да не продать, и заказывают у программистов советник по пересечению двух скользящих средних. А ведь на начальных этапах обучения это вполне можно отдать на откуп готовым инструментам разработки. Одной из таких как раз является MQL5 Wizard – мастер по созданию советников для MetaTrader 5.

Для реализации простых вещей Мастер идеален. Вам не нужно обладать никакими магическими способностями – навыки программирования здесь не требуются. Несколько простых шагов, и советник готов. Уже готовый советник можно протестировать в том же терминале MetaTrader. Держите комбайн по производству простецких Граалей.

4. MQL5 Wizard. Сделать советник за 5 минут

Итак, для начала запускаем MetaTrader 5 и переходим в редактор приложений (кнопка F4). На верхнем меню жмем кнопку «Создать» и в появившимся диалоге выбираем генерацию нового советника.

Мастер советников MetaTrader

Дальше нужно указать название советника и основные входные параметры.

Мастер советников MetaTrader

Добавляем модули торговых сигналов. Модули могут быть как стандартные, так и разработанные сообществом (вы тоже можете поучаствовать в данном процессе).

Параметры сигналов советника

Собственно, все стандартные сигналы хорошо описаны в онлайн справке на официальном сайте. Там же можно скачать и кастомные.

Варианты торговых сигналов советника

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

параметры модуля сигналов

Обычный трейлинг стоп или закрытие сделки по значениям индикатора – на ваш выбор.

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

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

5. Хочу программировать непростого советника? Написание технического задания

Итак, вы уже обладаете некоторым торговым опытом, вы проверили свою идею в торговле или имеете точное представление о том, как она будет работать на реальном рынке. Значит, пора переходить к написанию формальных правил вашей стратегии. Часто на этом шаге начинаются проблемы у большинства последователей гуманитарных наук – люди сталкиваются с непреодолимой стеной непонимания, когда абсолютно идеально отработанная в голове схема перестает работать при переводе на технический язык.

Написание ТЗ для робота

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

По сути, правильно составленное задание – это уже половина работы. Однако, это не работа программиста. Если вы будете просить программиста написать задание за вас, будьте готовы натолкнуться на непонимание, в лучшем случае, и негодование в абсолютном большинстве ситуаций. Именно разработчик идеи несет ответственность за ее точную формализацию, а никак не реализатор – если хотите, переводчик на язык низкоуровневых команд.

6. Напишите мне советник по индикатору

Пример плохого задания для создания торгового советника – его отсутствие. Само намерение заказчика может быть и понятно: есть индикатор, нужно написать торгового робота, ну а дальше что?

[info_block align=»right» linkText=»Вникайте в процесс!»]»Старайтесь вникать в процесс работы и всего, что непосредственно касается реализации идеи вашего советника»[/info_block]

Как закрывать сделку? Как ее сопровождать? Какие выбирать объемы? Что делать с проскальзываниями и реквотами? Нужно понимать, что программист не обладает экстрасенсорными способностями. Если вы такого найдете, обязательно дайте знать, такие люди не должны пропадать.

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

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

7. Выбор исполнителя

Если у вас нет опыта работы с фриланс-биржами, попробуйте представить себе субэкваториальную версию ада. На самом деле, не все так страшно, но некоторые нюансы все же стоит учитывать.

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

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

Реализация торгового советника

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

[info_block align=»right» linkText=»Все в ваших руках»]»В ваших интересах контролировать процесс создания советника вашей мечты. Просите проводить промежуточные этапы работ, участвуйте в бета-тесте и не брезгуйте узнавать технические подробности.»[/info_block]

Один из типичных примеров – недопонимание форматов файлов и их значения. Вроде: «У меня тут есть .dll библиотека (.exe файл или .ex4 программа), не могли бы вы быстренько поправить то и то, а я вам за это копеечку закину? Нет? А почему? Василий мне сделал советника за 5 баксов, а вы плохой разработчик значит!».

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

8. Сдача советника – включайтесь в работу!

Этап сдачи работы – самый сложный. Будьте уверены, каждый исполнитель с горя пьет успокоительный отвар, черным маркером отмечая день календаря, когда его в очередной раз просят поправить и без того исчерпавший все сроки заказ. Как и вселенной, совершенству нет предела, но в определенный момент нужно собраться и сказать – задача завершена.

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

Принять участие в разработке своего робота

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

Если задача небольшая, исполнитель вполне может пойти на уступки, и исправить работу в соответствии с новым ТЗ. Если работа проделана масштабная – что-либо менять уже поздно. Частая проблема – тотальная отрешенность в процессе разработки, например, отказ от участия в бета-тестировании: «Зачем вы мне скинули советника с ошибками, я что за вас тестировать еще должен?» или: «Откуда я знаю, как этот ваш тестер запускать, я программист что-ли?».

[info_block align=»right» linkText=»Безопасный Мартингейл» linkUrl=»https://fortrader.org/learn/forex-trader/bezopasnyj-martingejl-kak-otdelnymi-elementami-martingejla-uvelichit-pribylnost-strategii.html» imageUrl=»http://files.fortrader.org/uploads/2016/07/coins-730×487.jpg»]Как отдельными элементами Мартингейла увеличить прибыльность стратегии?[/info_block]

Для адекватного человека такие заявления режут слух, исполнитель же впадает в депрессию. Разработка торгового робота – это, в первую очередь, работа. Если вы ищете готовое решение, существует магазин готовых советников, где советника за вас и протестируют, и вымоют до блеска, попутно документируя его историческую успешность. В таком случае, обращаться на фриланс-биржу не имеет смысла.

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

Заключение

Великие деятели, как Альберт Хофманн, не дадут соврать. Опыт создания своего продукта бесценен. Если же вы пришли на валютный рынок по собственному желанию, поверьте, это надолго.

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

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

Вам также будет интересно

  • Как из запаздывающего индикатора сделать работающий Грааль?
  • ECN или MTF – выбираем агрегатор ликвидности
  • Сетка ордеров на форекс: 2 варианта популярной стратегии

В этом руководстве по MQL4 вы научитесь программировать свой собственный торговый советник в MetaEditor.

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

  • int — это обычное число. Например: 1, 15, 521.
  • double — число с десятичным знаком. Например: 1,154, 0,0144, 255,145.
  • string — строка, слово, фраза. Например: «Ордер на покупку», «Ордер на продажу успешно размещен».
  • bool — принимает значения либо false, либо true.

Скоро вы увидите, как их можно использовать.

Торговый советник три белых солдата

Для того, чтобы создать свой собственный торговый советник, для начала вы должны переключиться на MetaEditor. Вы смоете сделать это, нажав на иконку MetaEditor. В MetaEditor нажмите «Создать» и выберите «Советник (шаблон)». В следующем окне напишем имя нашего эксперта, нажмем на кнопку «Далее» и добавим первые стандартные переменные, нажав на кнопку «Добавить».

Мы добавим TakeProfit, StopLoss, LotSize, Slippage и MagicNumber. Все они являются типом int, кроме LotSize. LotSize должен иметь тип double.

мой торговый советник

Затем нажимаем «Далее» и «Готово». Мы видим, что MetaEditor сгенерировал файл с вашими предопределенными переменными.

Вы можете увидеть 3 области. Области OnInit(), OnDeinit() и OnTick(). Весь код, который вы пишете, попадает в одну из этих областей.

Код, который мы хотим вызвать, если прикрепим советник к графику, выполняется в области OnInit(). Код в OnDeinit() выполняется, когда мы отсоединяем советник. И, наконец, область, в которой мы будем работать больше всего — область OnTick(). Функция OnTick() вызывается каждый раз, когда мы получаем от брокера новую цену (тик) торгового инструмента, к которому прикреплен советник.

Если вы скомпилируете советника с помощью F7 и вернетесь в Metatrader, вы сможете найти советника в «Навигаторе» MetaTrader (Crtl + N). Если вы попытаетесь присоединить его к графику и перейдете на вкладку «Входные параметры», вы сможете увидеть наши предопределенные переменные.

торговый советник: входные параметры

Мы научимся программировать советника, который торгует по паттерну 3 белых солдата. Это простая стратегия, когда советник открывает сделку на покупку, когда последние 3 свечи были бычьими. Давайте начнем!

Добавим следующие строчки кода:

void OnTick() { 
if(Close[1] &gt; Open[1] &amp;&amp; Close[2] &gt; Open[2] &amp;&amp; Close[3] &gt; Open[3]) { //Ордер на покупку } 
}

Для этой стратегии мы используем предопределенную переменную Close[], который является типом double. Close[] дает нам цену закрытия (например, 1.24577) для каждого свечи текущего графика. Close[0] означает текущую цену закрытия. Close[1] — предыдущую цену, и так далее. То же самое справедливо для предопределенных переменных Open[], Low[] и High[].

//Ордер на покупку — это подсказка для нас, что мы создали здесь функцию открытия ордера на покупку. Две обратных черты (//) означают, что эта строка закомментирована. Советник будет игнорировать ее при компиляции.

Если вы поместите курсор на предопределенную функцию или переменную и нажмете F1, откроется справка по MQL4, где вы сможете прочитать, что делает эта данная функция или переменная.

Далее мы используем функцию OrderSend, чтобы отправить ордер на покупку нашему брокеру. Добавим следующий код:

if(Close[1] > Open[1] && Close[2] > Open[2] && Close[3] > Open[3])
      {
         //Ордер на покупку
         OrderSend(_Symbol,OP_BUY,LotSize,Ask,Slippage,Ask-StopLoss*_Point,Ask+TakeProfit*_Point,"Покупка",MagicNumber);
      }

Что из себя представляет функция OrderSend?

  • _Symbol — возвращает текущий торговый инструмент, к которому прикреплен советник.
  • OP_BUY — мы хотим открыть ордер на покупку.
  • LotSize — размер лота, который мы определили в самом начале.
  • Ask — текущая цена Ask, по которой открываются ордера на покупку.
  • Slippage — размер проскальзывания.
  • StopLoss — это наш StopLoss. Или цена Ask за вычетом цены StopLoss. Запомните, что StopLoss — это целое число (500 пунктов), а Ask — это цена (например, 1.20521). Теперь, чтобы вычесть стоп-лосс из цены Ask, мы должны умножить его на 0,00001 или 0,001. Мы делаем это с помощью предопределенной переменной _Point, которая отражает текущие значением пунктов торгового инструмента.
  • TakeProfit — то же самое, как и StopLoss.
  • Комментарий — комментарий, который должен появиться в журнале, когда торговый советник разместит наш ордер.
  • MagicNumer — магическое число, которое было определено нами вначале.

Нажмите F7, чтобы скомпилировать советник и проверить результат его работы в тестере стратегий. Вы получите что-то вроде этого:

торговый советник открывает ордера на каждом тике

Наш торговый советник выставляет ордера на покупку на каждом тике, если 3 последних бара были бычьими. Это не то что мы хотим, но, по крайней мере, что-то происходит.

Мы должны запрограммировать новую функцию, которая позволит нашему советнику одновременно открывать только одну сделку. MQL4 уже имеет свои предопределенные функции, например, OrderSend(). Но мы также можем написать свою собственную функцию, в которой мы можем вызвать другие пользовательские или предопределенные функции.

Давайте перейдем к концу нашего файла и запрограммируем новую функцию под названием TotalOpenOrders. Эта функция будет иметь тип int. Она будет возвращать количество открытых ордеров.

int TotalOpenOrders()
{
   int total_orders = 0;
 
   for(int order = 0; order < OrdersTotal(); order++) 
   {
      if(OrderSelect(order,SELECT_BY_POS,MODE_TRADES)==false) break;
 
      if(OrderMagicNumber() == MagicNumber && OrderSymbol() == _Symbol)
         {
            total_orders++;
         }
   }
 
   return(total_orders);
}

Затем добавим данную функцию в область OnTick и добавим проверку на количество открытых ордеров. Если общее количество сделок равно 0, наш торговый советник продолжает свою работу с кодом внутри скобок.

   if(TotalOpenOrders() == 0)
   { 
      if(Close[1] > Open[1] && Close[2] > Open[2] && Close[3] > Open[3])
      {
         //Ордер на покупку         
OrderSend(_Symbol,OP_BUY,LotSize,Ask,Slippage,Ask-StopLoss*_Point,Ask+TakeProfit*_Point),"Покупка",MagicNumber);
      }
   }

торговый советник открывает только один ордер

Что из себя представляет функция TotalOpenOrders?

В этой функции у нас есть счетчик (total_orders), который в конце возвращает общее количество открытых ордеров. Также у нас есть цикл for, который перебирает все открытые ордера. Если советник найдет ордер, соответствующий вашему символу и MagicNumer, счетчик увеличится на 1 (это записывается с помощью ++).

Теперь у нас все еще есть проблема. Если свеча слишком длинная, советник открывает вторую сделку на покупку непосредственно после того, как первая сделка на покупку закрывается с помощью StopLoss или TakeProfit. Мы должны добавить вторую функцию «IsNewBar».

Эта функция будет представлять тип bool. Она вернет true, если в Metatrader сгенерирована новая свеча, иначе она вернет false.

Давайте поместим эту функцию прямо над функцией TotalOpenOrder и вызовем ее в области OnTick вместе с TotalOpenOrders().

void OnTick()
  {
//---
 
   if(TotalOpenOrders() == 0 && IsNewBar() == true)
   { 
      if(Close[1] > Open[1] && Close[2] > Open[2] && Close[3] > Open[3])
      {
         //Ордер на покупку
         OrderSend(_Symbol,OP_BUY,LotSize,Ask,Slippage,Ask-StopLoss*_Point,Ask+TakeProfit*_Point,"Покупка",MagicNumber);
      }
   }
 
  }
//+------------------------------------------------------------------+
 
//Проверка новой свечи
bool IsNewBar()   
{        
      static datetime RegBarTime=0;
      datetime ThisBarTime = Time[0];
 
      if (ThisBarTime == RegBarTime)
      {
         return(false);
      }
      else
      {
         RegBarTime = ThisBarTime;
         return(true);
      }
}   
 
// Возвращаем количество открытых ордеров
int TotalOpenOrders()
{
   int total_orders = 0;
 
   for(int order = 0; order < OrdersTotal(); order++) 
   {
      if(OrderSelect(order,SELECT_BY_POS,MODE_TRADES)==false) break;
 
      if(OrderMagicNumber() == MagicNumber && OrderSymbol() == _Symbol)
         {
            total_orders++;
         }
   }
 
   return(total_orders);
}

4-5 или 2-3 значные котировки

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

Сейчас мы используем предопределенную переменную _Point для преобразования TakeProfit и StopLoss. Но мы хотим создать функцию, которая предоставит нам одинаковое значение для 4 и 5 и соответственно 2-х и 3-х торговых инструментов. Для начала мы создаем глобальные переменные MyPoint и MySlippage. Поместим их прямо под нашими входными параметрами:

//--- глобальные переменные
double MyPoint;
int    MySlippage;

Теперь мы создаем функцию, которая сохраняет правильное значение в этих 2 переменных (MyPoint и MySlippage) и помещаем их в наши пользовательские функции:

//Получаем My Points   
double MyPoint()
{
   double CalcPoint = 0;
 
   if(_Digits == 2 || _Digits == 3) CalcPoint = 0.01;
   else if(_Digits == 4 || _Digits == 5) CalcPoint = 0.0001;
 
   return(CalcPoint);
}
 
 
//Получаем My Slippage
double MySlippage()
{
   double CalcSlippage = 0;
 
   if(_Digits == 2 || _Digits == 4) CalcSlippage = Slippage;
   else if(_Digits == 3 || _Digits == 5) CalcSlippage = Slippage * 10;
 
   return(CalcSlippage);
}

Мы должны рассчитать эти значения только один раз. Только когда мы прикрепляем наш торговый советник к графику. Поэтому мы вызываем эти функции в разделе OnInit():

int OnInit()
  {
//---
   MyPoint = MyPoint();
   MySlippage = MySlippage();
 
//---
   return(INIT_SUCCEEDED);
  }

Теперь мы можем возвратить наши TakeProfit, StopLoss и Slippage к номальным значениям:

//--- входные параметры
input int      TakeProfit=50;
input int      StopLoss=50;
input double   LotSize=0.1;
input int      Slippage=3;
input int      MagicNumber=5555;

Далее мы заменяем все Slippage и _Point в функциях OnTick нашими 2 новыми глобальными переменными MyPoint и MySlippage. Таким образом, функция OrderSend будут выглядеть следующим образом:

//Ордер на покупку
OrderSend(_Symbol,OP_BUY,LotSize,Ask,MySlippage,Ask-StopLoss*MyPoint,Ask+TakeProfit*MyPoint,"Ордер на покупку",MagicNumber);

Торговый советник и ECN брокеры

На ECN брокерах мы не можем отправить ордер с TakeProfit или StopLoss. Сначала нам придется отправить наш ордер без них, а затем изменить его.

Сначала мы сохраняем номер тикета из только что открытого ордера в переменной int ticket. Затем мы проверяем, получен ли мы данный ticket от нашего брокера. Далее мы вызываем функцию OrderModify. Функция OrderModify возвращает true или false, которые мы сохраняем в переменной bool res. Мы проверяем результат с помощью if (! Res) (так же, как if (res == false)) и выводим соответствующее сообщение.

   if(TotalOpenOrders() == 0 && IsNewBar() == true)
   { 
      // Buy Logic
      if(Close[1] > Open[1] && Close[2] > Open[2] && Close[3] > Open[3])
      {
         //Ордер на покупку
         int ticket = OrderSend(_Symbol,OP_BUY,LotSize,Ask,MySlippage,0,0,"Ордер на покупку",MagicNumber);
            if(ticket<0)
            {
               Print("Ошибка #",GetLastError());
            }
            else
            {
               Print("Ордер размещен успешно");
            }
 
         // Изменяем наш ордер
         bool res = OrderModify(ticket,OrderOpenPrice(),Ask-StopLoss*MyPoint,Ask+TakeProfit*MyPoint,0,Blue);
            if(!res)
            {
               Print("Ошибка=",GetLastError());
            }
            else
            {
               Print("Ордер изменем успешно.");
            }
      }

Теперь наш код будет работать на всех ECN и не ECN брокерах.

Очистим наш код

Наш исходный код выглядит сейчас слишком сложным. Но мы можем его упростить. Для этого мы помещаем торговую логику и OpenOrder / ModifyOrder в пользовательскую функцию и вызываем ее в области OnTick().

   if(TotalOpenOrders() == 0 && IsNewBar() == true)
   { 
      // Проверяем параметры на вход
      if(BuySignal() == true)
         {
            OpenBuy();
         }
         }

Добавляем индикаторы

Теперь я покажу вам, как можно добавить индикаторы в наш торговый советник. Мы добавим индикатор RSI с фильтром скользящей средней.

Во-первых, нам нужны глобальные переменные для хранения значений наших индикаторов, чтобы мы могли работать с ними позже. Добавим их после MyPoint и MySlippage:

//--- индикаторы
double RSI;
double MA;

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

//Инициализируем индикаторы.
void InitIndicators()
{
   // Индикатор RSI
   RSI = iRSI(_Symbol,PERIOD_CURRENT,14,PRICE_CLOSE,1);
 
   // Скользящая средняя
   MA = iMA(_Symbol,PERIOD_CURRENT,200,0,MODE_SMA,PRICE_CLOSE,1);
}

Мы вызываем функции iRSI и iMA и сохраняем эти цены в переменных. В обоих функциях мы сначала используем текущий символ и таймфрейм, для которых мы хотим получить показания индикаторов. В нашем случае это символ и таймфрейм (период), к которому мы прикрепляем советник.

Для RSI мы хотим, чтобы период 14 рассчитывался по цене закрытия. Скользящая средняя должна быть периодом 200, Сдвиг 0. Что такое 1 в конце? Здесь мы определяем, от какой свечи мы хотим получать эти значения. 0 означает текущую свечу, 1 — предыдущую и т. д.

Чтобы эта функция работала, мы должны вызывать ее в разделе OnTick:

   if(TotalOpenOrders() == 0 && IsNewBar() == true)
   { 
      // Инициализируем индикаторы
      InitIndicators();
 
      // Сигнал на вход
      if(BuySignal() == true)
         {
            OpenBuy();
         }
   }

Теперь у нас есть все необходимые значения для создания нашей логики входа. Мы хотим открыть ордер на покупку, если RSI находится ниже уровня 30, а минимум предыдущей свечи находится выше скользящей средней:

// Вход на покупку
bool BuySignal()
{
   if(RSI <= 30 && Low[1] >= MA)
   {
      return(true);
   }
   else
   {
      return(false);
   }
}

Изменяем параметры индикаторов

На данный момент, если мы хотим изменить, скажем, период RSI от 14 до 20, мы должны сделать это в исходном коде. Теперь я покажу вам, как вы можете изменить все параметры индикаторов как обычный входной параметр в Свойствах эксперта. Для этого мы пишем:

//--- входные параметры
input int            TakeProfit=50;
input int            StopLoss=50;
input double         LotSize=0.1;
input int            Slippage=3;
input int            MagicNumber=5555;
 
//--- индикаторы
sinput string        indi = "";                // ------ Индикаторы -----  
input int            RSI_Period = 14;          // RSI период
input int            RSI_Level  = 30;          // Значение RSI
input int            MA_Period  = 200;         // MA период
input ENUM_MA_METHOD MA_Method  = MODE_SMA;    // MA метод

Теперь мы должны связать эти параметры с нашими индикаторами и торговой логикой. Для этого мы изменим наш код на следующий:

 // индикатор RSI
   RSI = iRSI(_Symbol,PERIOD_CURRENT,RSI_Period,PRICE_CLOSE,1);
 
   // Скользящая средняя
   MA = iMA(_Symbol,PERIOD_CURRENT,MA_Period,0,MA_Method,PRICE_CLOSE,1);
   if(RSI &lt;= RSI_Level &amp;&amp; Low[1] &gt;= MA)

Вот полный код нашего первого торгового советника:

//+------------------------------------------------------------------+
//|                                                  My First EA.mq4 |
//|                                     
//|                                                                  |
//+------------------------------------------------------------------+
#property copyright "https://traderblog.net/"
#property link      "https://traderblog.net/"
#property strict
 
//--- входные параметры
input int            TakeProfit=50;
input int            StopLoss=50;
input double         LotSize=0.1;
input int            Slippage=3;
input int            MagicNumber=5555;
 
//--- индикаторы
sinput string        indi = "";                // ------ Индикаторы -----  
input int            RSI_Period = 14;          // RSI период
input int            RSI_Level  = 30;          // Значение RSI
input int            MA_Period  = 200;         // MA период
input ENUM_MA_METHOD MA_Method  = MODE_SMA;    // MA метод
 
//--- глобальные переменные
double MyPoint;
int    MySlippage;
 
//--- индикаторы
double RSI;
double MA;
 
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   MyPoint = MyPoint();
   MySlippage = MySlippage();
 
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
 
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
 
   if(TotalOpenOrders() == 0 && IsNewBar() == true)
   { 
      // Инициализиуем индикаторы
      InitIndicators();
 
      // Проверяем ордер на покупку
      if(BuySignal() == true)
         {
            OpenBuy();
         }
 
   }
 
  }
//+------------------------------------------------------------------+
//| Пользовательские функции                                           
//+------------------------------------------------------------------+
 
 
// Инициализируем индикаторы
void InitIndicators()
{
   // индикатор RSI
   RSI = iRSI(_Symbol,PERIOD_CURRENT,RSI_Period,PRICE_CLOSE,1);
 
   // Скользящая средняя
   MA = iMA(_Symbol,PERIOD_CURRENT,MA_Period,0,MA_Method,PRICE_CLOSE,1);
}
 
 
// Логика на покупку
bool BuySignal()
{
   if(RSI <= RSI_Level && Low[1] >= MA)
   {
      return(true);
   }
   else
   {
      return(false);
   }
} 
 
// Размещаем ордер на покупку
void OpenBuy()
{
   // Open Buy Order
   int ticket = OrderSend(_Symbol,OP_BUY,LotSize,Ask,MySlippage,0,0,"Ордер на покупку",MagicNumber);
 
      if(ticket<0)
      {
         Print("Ошибка",GetLastError());
      }
      else
      {
         Print("Ордер успешно открыт");
      }
 
   // Modify Buy Order
   bool res = OrderModify(ticket,OrderOpenPrice(),Ask-StopLoss*MyPoint,Ask+TakeProfit*MyPoint,0);
 
      if(!res)
      {
         Print("Ошибка",GetLastError());
      }
      else
      {
         Print("Ордер успешно изменен.");
      }
}
 
 
// Рассчитываем котировки 
double MyPoint()
{
   double CalcPoint = 0;
 
   if(_Digits == 2 || _Digits == 3) CalcPoint = 0.01;
   else if(_Digits == 4 || _Digits == 5) CalcPoint = 0.0001;
 
   return(CalcPoint);
}
 
 
// Рассчитываем проскальзывание
int MySlippage()
{
   int CalcSlippage = 0;
 
   if(_Digits == 2 || _Digits == 4) CalcSlippage = Slippage;
   else if(_Digits == 3 || _Digits == 5) CalcSlippage = Slippage * 10;
 
   return(CalcSlippage);
}
 
 
// Проворяем свечу
bool IsNewBar()   
{        
   static datetime RegBarTime=0;
   datetime ThisBarTime = Time[0];
 
   if (ThisBarTime == RegBarTime)
   {
      return(false);
   }
   else
   {
      RegBarTime = ThisBarTime;
      return(true);
   }
}   
 
 
// Возвращаем количество открытых ордеров
int TotalOpenOrders()
{
   int total_orders = 0;
 
   for(int order = 0; order < OrdersTotal(); order++) 
   {
      if(OrderSelect(order,SELECT_BY_POS,MODE_TRADES)==false) break;
 
      if(OrderMagicNumber() == MagicNumber && OrderSymbol() == _Symbol)
         {
            total_orders++;
         }
   }
 
   return(total_orders);
}

Создание

Для создания советников используются язык программирования торговых стратегий MetaQuotes Language 4 (MQL4) и редактор MetaEditor. Чтобы запустить редактор советников, необходимо выполнить команду «Создать» контекстного меню окна «Навигатор — Советники», команду меню «Сервис — Редактор MetaQuotes Language», нажать клавишу F4 или кнопку Редактор MetaEditor панели «Стандартная».

ea_create_navigator

При создании эксперта автоматически откроется мастер создания советников, позволяющий быстро приступить к работе с новой MQL4-программой. В нем в качестве типа создаваемого объекта необходимо указать «Советник» и заполнить поля требуемыми данными:

  • Имя — название советника;
  • Автор — имя автора;
  • Ссылка — адрес сайта разработчика;
  • Параметры — список входных параметров советника. Чтобы добавить новый параметр, необходимо нажать кнопку «Добавить», чтобы удалить — кнопку «Удалить».

После этого в редакторе открывается окно нового советника с заданными параметрами. Файл с исходным текстом (*.MQ4) советника автоматически помещается в папку /EXPERTS клиентского терминала. С этого момента можно приступать к написанию кода эксперта.

По завершении разработки необходимо скомпилировать советник. Для этого в редакторе советников необходимо выполнить команду меню «Файл — Компилировать», нажать клавишу F5 или кнопку Компилировать панели инструментов. В результате успешной компиляции создается исполняемый файл программы с расширением *.EX4, автоматически помещаемый в папку /EXPERTS. Список скомпилированных экспертов можно просмотреть в окне «Навигатор — Советники» клиентского терминала. Если компиляция не была успешно завершена, пиктограмма эксперта будет закрашена серым цветом. Это означает, что его использовать нельзя.

Редактирование советников

Чтобы приступить к редактированию уже существующего советника из терминала, необходимо выполнить команду «Изменить» контекстного меню окна «Навигатор — Советники». При этом откроется редактор советников, в который уже будет загружен исходный текст выбранного эксперта. После изменения исходного текста советника необходимо повторно скомпилировать его и получить новый исполняемый EX4-файл. В противном случае в терминале будет использоваться неизмененная, предыдущая версия эксперта.

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

На прошлом уроке мы с вами писали индикатор, который указывает входы в рынок. Соответственно сегодня мы займёмся разработкой эксперта, использующего его сигналы.

Начнём с создания шаблона советника(как это сделать мы разбирали на уроке №10).

Опишем входные параметры эксперта:

extern double Lots       = 0.1;    // объём сделки в лотах
extern int    StopLoss   = 40;     // ограничение убытка в пунктах 
extern int    TakeProfit = 70;     // профит в пунктах
extern int    Magic      = 123;    // магический номер ордеров
extern int    Slippage   = 5;      // проскальзывание в пунктах
extern string comment    = "Мой первый советник";

Значения StopLoss, TakeProfit и Slippage, присвоенные в параметрах, указаны для 4-х значного брокера, соответственно, чтобы эти же значения корректно работали у 5-ти значного ДЦ их нужно умножить на 10, что мы и сделаем в функции OnInit:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
   if (Digits == 3 || Digits == 5)
   {
       StopLoss     *= 10;
       TakeProfit   *= 10;
       Slippage     *= 10;
   }
   return(INIT_SUCCEEDED);
}

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

Параметры индикатора "MyIndicator"

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

//+------------------------------------------------------------------+
extern double Lots            = 0.1; // объём сделки в лотах
extern int    StopLoss        = 40;  // ограничение убытка в пунктах
extern int    TakeProfit      = 70;  // профит в пунктах
extern int    Magic           = 123; // магический номер ордеров
extern int    Slippage        = 5;   // проскальзывание в пунктах
extern string comment         = "Мой первый советник";
//+------------------------------------------------------------------+
extern int    MA_Period       = 100;
extern double MACDOpenLevel   = 5;
extern int    fast_ema_period = 8;   // период быстрой средней 
extern int    slow_ema_period = 16;  // период медленной средней 
extern int    signal_period   = 9;   // период сигнальной линии 
//+------------------------------------------------------------------+

Переходим к функции OnTick и пишем заготовку обработчика, где описываю алгоритм работы эксперта:

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
    if (CountBuy() == 0)
    {
       BuySignal = ...
       
       if (BuySignal > 0)
       {
           CloseSell();

           // Open buy order 
           
       }
    }

    if (CountSell() == 0)
    {
       SellSignal = ...

       if (SellSignal > 0)
       {
           CloseBuy();

           // Open sell order
           
       }
    }
}

Итак, по шагам:

  1. if (CountBuy() == 0) — я проверяю есть-ли у меня ордера на покупку, находящиеся в рынке, и если нет, то шаг 2.
  2. BuySignal = … — проверяем наличие сигнала на покупку по индикатору, кстати, это будет переменная, которую необходимо объявить заранее.
  3. if (BuySignal > 0) — если есть сигнал на покупку, то переходим к шагу 4.
  4. CloseSell() — закрываем открытые ордера на продажу, если они есть и переходим к шагу 5.
  5. Открываем ордер на покупку.

Точно такой же блок делается и для продаж.

Исходя из написанного алгоритма сразу видно, что предварительно нам потребуется несколько функций: CountBuy(), CountSell(), CloseBuy(), CloseSell(), а также написать код получения сигнала с индикатора.

Объявим в коде несколько переменных для дальнейшей работы и необходимые функции:

double BuySignal, SellSignal,
       SL, TP;
int    ticket;
bool   res;

Функция CountBuy():

//+------------------------------------------------------------------+
//| Функция возвращает количество ордеров на покупку                 |
//| находящихся в рынке                                              |
//+------------------------------------------------------------------+
int CountBuy() 
{  // объявляем переменную, в которой будем хранить количество ордеров
   // с типом OP_BUY 
   int count = 0;

   // Объявляем цикл с перебором ордеров   
   for (int trade = OrdersTotal() - 1; trade >= 0; trade--) 
   {
      // Если удалось выбрать ордер, находящийся в рынке
      if (OrderSelect(trade, SELECT_BY_POS, MODE_TRADES))
      {
         // у этого ордера совпадает валютная пара и его магический номер
         if (OrderSymbol() == Symbol() && OrderMagicNumber() == Magic)
         {
            // а также ордер является ордером на покупку,
            if (OrderType() == OP_BUY) 
            // то увеличиваем значение счётчика count на единицу
               count++;
         }
      }
   }
   // возвращаем количество ордеров на покупку
   return (count);
}

Аналогично пишется функция CountSell(), разве что проверяется тип ордера OP_SELL:

//+------------------------------------------------------------------+
//| Функция возвращает количество ордеров на продажу                 |
//| находящихся в рынке                                              |
//+------------------------------------------------------------------+
int CountSell() 
{
   int count = 0;
   
   for (int trade = OrdersTotal() - 1; trade >= 0; trade--) 
   {
      if (OrderSelect(trade, SELECT_BY_POS, MODE_TRADES))
      {
         if (OrderSymbol() == Symbol() && OrderMagicNumber() == Magic)
         {
            if (OrderType() == OP_SELL) 
               count++;
         }
      }
   }
   return (count);
}
//+------------------------------------------------------------------+

Далее пишем функцию CloseBuy():

//+------------------------------------------------------------------+
//| Фукнция закрывает все ордера на покупку, находящиеся в рынке     |
//+------------------------------------------------------------------+
void CloseBuy()
{
   // объявляем цикл, в котором выполним перебор все ордеров
   // находящихся в рынке
   for(int index = OrdersTotal()-1; index >= 0; index--)
   {
      // если удалось выбрать рыночный ордер
      if (OrderSelect(index, SELECT_BY_POS, MODE_TRADES))
      {
            // если валютная пара ордера, магический номер ордера и тип ордера совпадают
            // с тем, что нам необходимо,
            if (OrderSymbol() == Symbol() && OrderMagicNumber() == Magic && OrderType() == OP_BUY)
                 // то пробуем закрыть ордер по рыночной цене
                  if (!OrderClose(OrderTicket(), OrderLots(), MarketInfo(OrderSymbol(), MODE_BID), Slippage, Black))
                     // иначе выводим в журнал регистрации сообщение об ошибке
                     Print("Ошибка закрытия ордера на покупку, ticket = " + DoubleToStr(OrderTicket()));
      }
   }
}

Пишем практически всё то же самое и для закрытия ордеров на продажу, разница лишь в типе проверяемого ордера и цене закрытия, функция CloseSell():

//+------------------------------------------------------------------+
//| Фукнция закрывает все ордера на продажу, находящиеся в рынке     |
//+------------------------------------------------------------------+
void CloseSell()
{
   for(int index = OrdersTotal()-1; index >= 0; index--)
   {
      if (OrderSelect(index, SELECT_BY_POS, MODE_TRADES))
      {
            if (OrderSymbol() == Symbol() && OrderMagicNumber() == Magic && OrderType() == OP_SELL)
                  if (!OrderClose(OrderTicket(), OrderLots(), MarketInfo(OrderSymbol(), MODE_BID), Slippage, Black))
                     Print("Ошибка закрытия ордера на продажу, ticket = " + DoubleToStr(OrderTicket()));
      }
   }
}

На текущий момент нам осталось получить сигналы от индикаторы и написать код, открывающий ордера по его сигналам.

Для получения сигнала от любого пользовательского индикатора используется функция iCustom, обратившись к справке (F1) видим формат вызова функции:

iCustom

Теперь мы можем с легкостью получить сигнал на покупку в нашем советнике:

BuySignal = iCustom(NULL, 0, "MyIndicator", MA_Period, MACDOpenLevel, fast_ema_period, slow_ema_period, signal_period, 0, 1);

заодно и сигнал на продажу:

SellSignal = iCustom(NULL, 0, "MyIndicator", MA_Period, MACDOpenLevel, fast_ema_period, slow_ema_period, signal_period, 1, 1);

кстати, обратите внимание на предпоследний параметр mode, который мы передаём в функцию iCustom — это номер буфера индикатора, где хранятся сигналы покупок и продаж.

Таким образом наша функция OnTick() будет выглядеть следующим образом:

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
    if (CountBuy() == 0)
    {
       // Получаем сигнал на покупку
       BuySignal = iCustom(NULL, 0, "MyIndicator", MA_Period, MACDOpenLevel, fast_ema_period, slow_ema_period, signal_period, 0, 1);
       
       // если есть сигнал индикатора
       if (BuySignal > 0)
       {
           // закрываем открытые ордера SELL
           CloseSell();
           
           // Open buy order
           // открываем ордер на покупку
           ticket = OrderSend(Symbol(), OP_BUY, Lots, Ask, Slippage, 0, 0, comment, Magic, 0, Blue);

           // если ордер был успешно открыт,
           if (ticket > 0)
           {
               // то его необходимо выбрать, для дальнейшей установки ему StopLoss и TakeProfit
               if (OrderSelect(ticket, SELECT_BY_TICKET, MODE_TRADES))
               {
                  // рассчитываем StopLoss и нормализуем цену
                  SL = NormalizeDouble(Ask-StopLoss*Point, Digits);

                  // рассчитываем TakeProfit и нормализуем цену
                  TP = NormalizeDouble(Ask+TakeProfit*Point, Digits);
                  
                  // попытаемся установить ордеру рассчитанные уровни StopLoss и TakeProfit
                  res = OrderModify(OrderTicket(), OrderOpenPrice(), SL, TP, 0);
                  if (!res)
                     // и если не удалось, то выведем сообщение в журнал регистрации для дальнейшего анализа причин
                     Print("Ошибка модификации ордера на покупку, ASK=" + DoubleToStr(Ask) + ", SL=" + DoubleToStr(SL) + ", TP=" + DoubleToStr(TP)); 
               }
           }
       }
    }

    if (CountSell() == 0)
    {
       SellSignal = iCustom(NULL, 0, "MyIndicator", MA_Period, MACDOpenLevel, fast_ema_period, slow_ema_period, signal_period, 1, 1);

       if (SellSignal > 0)
       {
           CloseBuy();
           
           ticket = OrderSend(Symbol(), OP_SELL, Lots, Bid, Slippage, 0, 0, comment, Magic, 0, Red);
           if (ticket > 0)
           {
               if (OrderSelect(ticket, SELECT_BY_TICKET, MODE_TRADES))
               {
                  SL = NormalizeDouble(Bid+StopLoss*Point, Digits);
                  TP = NormalizeDouble(Bid-TakeProfit*Point, Digits);
                  res = OrderModify(OrderTicket(), OrderOpenPrice(), SL, TP, 0);
                  if (!res)
                     Print("Ошибка модификации ордера на продажу, BID=" + DoubleToStr(Bid) + ", SL=" + DoubleToStr(SL) + ", TP=" + DoubleToStr(TP)); 
               }
           }
       }
    }
}

Собственно говоря, на этом всё, разработка советника на пользовательском индикаторе завершена.

Полный код эксперта:

//+------------------------------------------------------------------+
//|                                                    My_Expert.mq4 |
//|                              Copyright 2017, xbms, http://mql.su |
//|                                              mailto:xbms@mail.ru |
//+------------------------------------------------------------------+
#property copyright "Copyright 2017, xbms"
#property link      "http://mql.su"
#property version   "1.00"
#property strict
//+------------------------------------------------------------------+
extern double     Lots            = 0.1;
extern int        StopLoss        = 40;
extern int        TakeProfit      = 70;
extern int        Magic           = 123;
extern int        Slippage        = 5;
extern string     comment         = "Мой первый советник";
//+------------------------------------------------------------------+
extern int        MA_Period       = 100;
extern double     MACDOpenLevel   = 5;
extern int        fast_ema_period = 8;   // период быстрой средней 
extern int        slow_ema_period = 16;  // период медленной средней 
extern int        signal_period   = 9;   // период сигнальной линии 
//+------------------------------------------------------------------+
double BuySignal, SellSignal,
SL, TP;
int    ticket;
bool   res;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
if (Digits == 3 || Digits == 5)
{
StopLoss     *= 10;
TakeProfit   *= 10;
Slippage     *= 10;
}
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
}
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
if (CountBuy() == 0)
{
// Получаем сигнал на покупку
BuySignal = iCustom(NULL, 0, "MyIndicator", MA_Period, MACDOpenLevel, fast_ema_period, slow_ema_period, signal_period, 0, 1);
// если есть сигнал индикатора
if (BuySignal > 0)
{
// закрываем открытые ордера SELL
CloseSell();
// Open buy order
// открываем ордер на покупку
ticket = OrderSend(Symbol(), OP_BUY, Lots, Ask, Slippage, 0, 0, comment, Magic, 0, Blue);
// если ордер был успешно открыт,
if (ticket > 0)
{
// то его необходимо выбрать, для дальнейшей установки ему StopLoss и TakeProfit
if (OrderSelect(ticket, SELECT_BY_TICKET, MODE_TRADES))
{
// рассчитываем StopLoss и нормализуем цену
SL = NormalizeDouble(Ask-StopLoss*Point, Digits);
// рассчитываем TakeProfit и нормализуем цену
TP = NormalizeDouble(Ask+TakeProfit*Point, Digits);
// попытаемся установить ордеру рассчитанные уровни StopLoss и TakeProfit
res = OrderModify(OrderTicket(), OrderOpenPrice(), SL, TP, 0);
if (!res)
// и если не удалось, то выведем сообщение в журнал регистрации для дальнейшего анализа причин
Print("Ошибка модификации ордера на покупку, ASK=" + DoubleToStr(Ask) + ", SL=" + DoubleToStr(SL) + ", TP=" + DoubleToStr(TP)); 
}
}
}
}
if (CountSell() == 0)
{
SellSignal = iCustom(NULL, 0, "MyIndicator", MA_Period, MACDOpenLevel, fast_ema_period, slow_ema_period, signal_period, 1, 1);
if (SellSignal > 0)
{
CloseBuy();
ticket = OrderSend(Symbol(), OP_SELL, Lots, Bid, Slippage, 0, 0, comment, Magic, 0, Red);
if (ticket > 0)
{
if (OrderSelect(ticket, SELECT_BY_TICKET, MODE_TRADES))
{
SL = NormalizeDouble(Bid+StopLoss*Point, Digits);
TP = NormalizeDouble(Bid-TakeProfit*Point, Digits);
res = OrderModify(OrderTicket(), OrderOpenPrice(), SL, TP, 0);
if (!res)
Print("Ошибка модификации ордера на продажу, BID=" + DoubleToStr(Bid) + ", SL=" + DoubleToStr(SL) + ", TP=" + DoubleToStr(TP)); 
}
}
}
}
}
//+------------------------------------------------------------------+
//| Фукнция закрывает все ордера на покупку, находящиеся в рынке     |
//+------------------------------------------------------------------+
void CloseBuy()
{
// объявляем цикл, в котором выполним перебор все ордеров
// находящихся в рынке
for(int index = OrdersTotal()-1; index >= 0; index--)
{
// если удалось выбрать рыночный ордер
if (OrderSelect(index, SELECT_BY_POS, MODE_TRADES))
{
// если валютная пара ордера, магический номер ордера и тип ордера совпадают
// с тем, что нам необходимо,
if (OrderSymbol() == Symbol() && OrderMagicNumber() == Magic && OrderType() == OP_BUY)
// то пробуем закрыть ордер по рыночной цене
if (!OrderClose(OrderTicket(), OrderLots(), MarketInfo(OrderSymbol(), MODE_BID), Slippage, Black))
// иначе выводим в журнал регистрации сообщение об ошибке
Print("Ошибка закрытия ордера на покупку, ticket = " + DoubleToStr(OrderTicket()));
}
}
} 
//+------------------------------------------------------------------+
//| Фукнция закрывает все ордера на продажу, находящиеся в рынке     |
//+------------------------------------------------------------------+
void CloseSell()
{
for(int index = OrdersTotal()-1; index >= 0; index--)
{
if (OrderSelect(index, SELECT_BY_POS, MODE_TRADES))
{
if (OrderSymbol() == Symbol() && OrderMagicNumber() == Magic && OrderType() == OP_SELL)
if (!OrderClose(OrderTicket(), OrderLots(), MarketInfo(OrderSymbol(), MODE_BID), Slippage, Black))
Print("Ошибка закрытия ордера на продажу, ticket = " + DoubleToStr(OrderTicket()));
}
}
} 
//+------------------------------------------------------------------+
//| Функция возвращает количество ордеров на покупку                 |
//| находящихся в рынке                                              |
//+------------------------------------------------------------------+
int CountBuy() 
{  // объявляем переменную, в которой будем хранить количество ордеров
// с типом OP_BUY 
int count = 0;
// Объявляем цикл с перебором ордеров   
for (int trade = OrdersTotal() - 1; trade >= 0; trade--) 
{
// Если удалось выбрать ордер, находящийся в рынке
if (OrderSelect(trade, SELECT_BY_POS, MODE_TRADES))
{
// у этого ордера совпадает валютная пара и его магический номер
if (OrderSymbol() == Symbol() && OrderMagicNumber() == Magic)
{
// а также ордер является ордером на покупку,
if (OrderType() == OP_BUY) 
// то увеличиваем значение счётчика count на единицу
count++;
}
}
}
// возвращаем количество ордеров на покупку
return (count);
}
//+------------------------------------------------------------------+
//| Функция возвращает количество ордеров на продажу                 |
//| находящихся в рынке                                              |
//+------------------------------------------------------------------+
int CountSell() 
{
int count = 0;
for (int trade = OrdersTotal() - 1; trade >= 0; trade--) 
{
if (OrderSelect(trade, SELECT_BY_POS, MODE_TRADES))
{
if (OrderSymbol() == Symbol() && OrderMagicNumber() == Magic)
{
if (OrderType() == OP_SELL) 
count++;
}
}
}
return (count);
}
//+------------------------------------------------------------------+

Исходный код советника доступен для скачивания My_Expert.mq4

Как написать советника для mt4

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

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

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

Ориентируясь на рекомендации о том, как написать советника для mt4 своими руками, вы можете избежать существенных недочетов при конструировании и сократить сроки отладки. Если требуется создание простого советника, то при тестировании  не возникнет трудностей. А если приложение представляет собой внушительный по объему программный код, отладка обычно занимает массу времени. Упростить задачу по созданию советников можно либо с использованием библиотек функций,  либо с помощью альтернативных программ.

Завершающим и наиболее ответственным этапом создания любого советника является его тестирование.  Для этого необходимо в режиме реального времени наблюдать за работой советника и корректной подачей его сигналов. Положительная оценка работоспособности вашего приложения, основанная на сравнении результатов с ручным тестированием в прошлом, — верный признак того, что вы в достаточной степени владеете знаниями, как самому  написать советника для mt4.

Содержание данной статьи является исключительно частным мнением автора и может не совпадать с официальной позицией LiteFinance. Материалы, публикуемые на данной странице, предоставлены исключительно в информационных целях и не могут рассматриваться как инвестиционный совет или консультация для целей Директивы 2004/39 /EC.

Понравилась статья? Поделить с друзьями:
  • Как написать советника для mt4 для автоматической торговли
  • Как написать советник для metatrader 5
  • Как написать сова никогда не спит чтобы забанили vk
  • Как написать собственный учебник
  • Как написать собственный троян