3.1 Создаем советник, торгующий по сигналу двух скользящих средних
Всем привет. На этом уроке мы продолжим изучать теорию создания торговых советников. На прошлом занятии мы использовали систему рандомного входа в рынок, которая показала свою несостоятельность, ведь Форекс это не игра в казино и случайности не могут принести доход трейдеру.
Давайте же попробуем модифицировать уже изученный код и добавим условие входа по пересечению двух скользящих средних (МА) разного периода. Суть урока — научиться использовать сигналы классических индикаторов в торговой системе.
Алгоритм работы советника на MQL4
Представленная последовательность действий схематически показывает процесс работы кода будущего советника. Все действия эксперта также будут происходить в функции обработки событий OnTick. Попробую вкратце описать логику работы: в терминал поступил новый тик, функция OnTick запустился в работу. Советник циклом перебирает все открытые ордера, чтобы провести их подсчет, а также модифицировать те ордера, у которых по какой-либо причине нет выставленных целей. Дальнейшие действия происходят только раз в свечу, а именно: анализ показаний индикаторов, поиск сигнала и открытие ордера с последующим закрытием противоположного.
Торговая система по пересечению двух скользящих средних
Так как этот урок по созданию советника mql4 является вторым по счету, то особо мудрить с торговой системой мы не будем и обратимся к классике. Одной из самых первых стратегий, что я узнал, изучая мир Форекс, была торговля по пересечению скользящих средних. Суть ее логики проста, как две копейки: берутся показания двух Moving Average и сравниваются их положения на графике относительно друг друга. Если быстрая МА пересекла медленную сверху вниз, то стоит предположить, что тренд вниз и это сигнал на продажу. Соответственно при пересечении снизу вверх дает сигнал на покупку. Период торговли пусть будет М15.
На скриншоте медленная МА показана фиолетовым цветом, а быстрая желтым.
Основной минус при использовании скользящих средних заключается в том, что они запаздывают и показывают направление тренда уже после того, как тот сформировался. Плюс при наступлении флета она дает очень большое количество ложных сигналов. Что ж, попробуем проверить как себя покажет советник по этой тривиальной торговой системе.
Пишем программный код советника
Как я уже писал выше, мы будем модифицировать код, который изучили на прошлом уроке, так что можете использовать его в качестве подложки для редактирования, если вам так удобнее.
Начнем с включения библиотеки ошибок и объявления внешних переменных, необходимых для открытия ордеров. Это размер торгового лота, проскальзывание, СЛ, ТП, комментарий и магик номер. Из новых переменных у нас появится bool переключатель для закрытия существующего ордера при появлении противоположного сигнала индикатора, а также настройки скользящей средней. Для МА нам важно знать ее период, а также тип сглаживания.
//| 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 //библиотека для расшифровки ошибок
extern string s0 = » » ; //> > >
extern double Lot = 0.01 ;
extern int Slippage = 5 ;
extern double StopLoss = 30 ;
extern double TakeProfit = 40 ;
extern string Comments = «DaVinci EA» ;
extern int MagicNumber = 123123 ;
extern bool CloseOppositeOrder = true ;
extern string s1 = » » ; //> > >
extern int ma1_period = 50 ;
extern int ma2_period = 100 ;
extern int ma1_method = MODE_EMA ;
extern int ma2_method = MODE_EMA ;
Функция OnInit() будет включать в себя только оператор, ответственный за умножение внешних параметров в пунктах на десять при условии, чтобы брокер использует пятизнак (трех- для JPY).
int OnInit () < if ( Digits == 3 || Digits == 5 ) < Slippage *= 10 ; StopLoss *= 10 ; TakeProfit *= 10 ; return ( INIT_SUCCEEDED );
Функция OnDeinit() в нашем коде использоваться не будет, нет нужды.
Переходим к функции OnTick(). Сначала мы активируем цикл for, который будет подсчитывать и модифицировать уже открытые ордера. Подсчет нужен, чтобы не открыть второй ордер в таком же направлении, когда первый еще в рынке. Соответственно, переменная cnt_b будет хранить в себе актуальное количество открытых ордеров на покупку, а cnt_s на продажу. Перебор идет только по рыночным ордерам (выделяется по MODE_TRADES), начиная от максимального количества открытых ордеров OrdersTotal() в сторону уменьшения. После выделения ордера идет стандартная проверка на то, чтобы он был рыночным, его символ совпадал с текущим, как и магик номер соответствовал магику вашего советника.
Чтобы не возвращаться потом к этому циклу, мы сразу же добавим в него проверку на наличие целей у ордеров и последующую модификацию при их отсутствии. Тут все просто. Если у ордера нет ТП или СЛ, то идет его расчет в зависимости от внешних значений и происходит модификация с последующим принтом об успешности операции.
int cnt_b = 0 , cnt_s = 0 ;
int _OrdersTotal = OrdersTotal ();
for ( int pos = _OrdersTotal — 1 ; pos > = 0 ; pos —) < if (! OrderSelect ( pos , SELECT_BY_POS , MODE_TRADES )) < Print ( __FUNCTION__ + ": не удалось выделить ордер! " + ErrorDescription ( GetLastError ())); else if ( OrderType () = 2 && OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber) < if(OrderType() == OP_BUY) cnt_b++; else cnt_s ++; //проверка на наличие целей у ордеров и их модификация if ( OrderTakeProfit () == 0 || OrderStopLoss () == 0 ) < double SL = 0 , TP = 0 ; if ( OrderType () == OP_BUY ) < SL = OrderOpenPrice ()- StopLoss * Point ; TP = OrderOpenPrice ()+ TakeProfit * Point ; SL = OrderOpenPrice ()+ StopLoss * Point ; TP = OrderOpenPrice ()- TakeProfit * Point ; if (! OrderModify ( OrderTicket (), OrderOpenPrice (), SL , TP , 0 , clrNONE )) < int Error = GetLastError (); Print ( "Ошибка модификации ордера " , Error , ": " , ErrorDescription ( Error )); else Print ( "Ордер #" + IntegerToString ( OrderTicket ()) + " успешно модифицирован" );
Если вы спрашиваете себя, зачем вначале модифицировать ордера, а уже после только отрывать, то тут все логично. Как я объяснял на прошлом уроке, для современных типов торговых счетов, таких как NDD, ECN, STP нельзя модифицировать ордер сразу при его открытии, поэтому действие выполняется поэтапно — открыли, потом выставили цели. В прошлом примере советника мы не учти возможность того, что сервер нам может не позволить модифицировать уже открытый ордер по какой-либо внутренней ошибке. В таком случае этот ордер останется болтаться в рынке без ТП и СЛ, пока вы этого не заметите сами. Это небезопасный подход. Поэтому в этой версии советника мы вводим проверку на наличие целей у ордеров каждый тик. Соответственно на первом тике ордер открывается, а на втором уже модифицируется. Если модификация не пройдет по какой-либо ошибке на этом тике, то на следующем будет предпринята очередная попытка. Данная вариация также полезна, если вдруг пользователь вашей программы или вы по случайности удалите заданные алгоритмом цели. В этом случае советник их просто восстановит, ибо нечего лезть руками в настроенную программу.
Открытие ордера по показаниям индикатора
Переходим к извлечению показаний из индикатора МА. Нам нужно найти тот момент, когда одна линия пересечет вторую и закрепится за ней. Это можно узнать только после закрытия текущей свечи. Соответственно нам нет никакого смысла каждый тик узнавать значение скользящих средних и тормозить тем самым работу советника. Получение данных индикатора и проверка условий для входа в рынок будут происходить один раз в момент открытия новой свечи. Для этого введем переменную Update_Time для хранения времени на глобальном уровне, которая будет сравнивать время открытия текущий свечи со значением, сохраненным в ней. Как только откроется новая свеча, время станет различным и запустится условие проверки с перезаписью значения этой переменной. Прописываем следующий код после цикла проверки ордеров:
Источник https://www.davinci-fx.com/mql-two-ma/