Стратегия Форекс "20/200 pips" на MQL5 для Metatrader 5.

Опубликовано: 01.01.2007

Ни что не стоит на месте, все движется и развивается, так и Metatrader 4 получил свое ожидаемое продолжение – Metatrader 5 с обновленным языком программирования – MQL5. Каждый трейдер сам для себя решит, на каком уровне он хочет знать MQL5, но азы этого языка придется знать всем трейдерам использующим Metatrader 5 как основу для автоматической торговли на рынке Форекс. Для первого знакомства с MQL5, предлагаю воспользоваться кодом советника для механической торговой системы "20/200 pips", переписанного на MQL5 для работы в Metatrader 5.

Язык программирования MQL5.

Читаем справку по MQL5:

Язык MetaQuotes Language 5 (MQL5) является объектно-ориентированным языком программирования высокого уровня и предназначен для написания автоматических торговых стратегий, пользовательских технических индикаторов для анализа разнообразных финансовых рынков. Он позволяет не только писать разнообразные экспертные системы, предназначенные для работы в режиме реального времени, но и создавать собственные графические инструменты, помогающие принимать торговые решения.

Да, все правильно – это объектно-ориентированный язык программирования. В этом и заключаются основные сложности перехода с MQL4 на MQL5. Например, мне лично «неуютно» программировать на объектно-ориентированных языках, потому что я достаточно долгое время программировал на Си и Ассемблере. И мое мышление заточено под эти языки – мне проще использовать функции, нежели методы классов, мне привычней оперировать с переменными, нежели использовать структуры и т.д. Однако ООП (объектно-ориентированное программирование) имеет свои неоспоримые плюсы. Именно поэтому оно так популярно. В ООП код программы всегда более структурирован и понятен. Его легко переносить из проекта в проект, для этого, как правило, пишутся библиотеки готовых классов и затем прикрепляются к новому проекту. И много других плюсов, о которых можно узнать, программируя на языках ООП, к которым и относится и MQL5.

Советник на MQL5 без использования ООП.

Как я уже говорил, мне «неуютно» использовать ООП, поэтому советника я переписал без использования методов объектно-ориентированного программирования. Да, вы правильно поняли, на языке MQL5, оказывается, можно писать без использования ООП. Ведь стандартные функции и переменные никто не отменял. И ничего в этом нет плохого, просто мы не будем использовать все возможности MQL5, а лишь ограничимся необходимыми нам – теми, что мы понимаем и которыми пользовались в языке MQL4. Но, полностью избежать ООП не удастся, т.к. обращение к торговым функциям, и другие возможности реализованы с помощью методов объектно-ориентированного программирования, что, в общем-то, логично.

Код советника на MQL5.

Итак, представляю вам код советника механической торговой системы "20/200 pips" на MQL5:

//+------------------------------------------------------------------+
//|                                          20_200_pips_MQL5_v1.mq5 |
//|                                    Copyright 2010, Смирнов Павел |
//|                                          http://www.autoforex.ru |
//+------------------------------------------------------------------+

#property copyright "Copyright 2010, Смирнов Павел"
#property link      "http://www.autoforex.ru"
#property version   "1.00"

//Объявляем внешние переменные. Они будут доступны извне и их значения можно будет менять перед запуском тестера

input int      TakeProfit=200;
input int      StopLoss=2000;
input int      TradeTime=18;
input int      t1=7;
input int      t2=2;
input int      delta=70;
input double   lot=0.1;

bool cantrade=true;// флаг для разрешения или запрета торговли.
double Ask; // здесь будем хранить цену Ask для нового тика (так удобней)
double Bid; // здесь будем хранить цену Bid для нового тика (так удобней)

//Функция открытия длинной (Long) позиции. Указываем также значения переменных по умолчанию
int OpenLong(double volume=0.1,int slippage=
10,string comment="EUR/USD 20 pips expert (Long)",int magic=0)
  {
   MqlTradeRequest my_trade={0};//объявляем структуру типа MqlTradeRequest для формирования запроса
   MqlTradeResult my_trade_result={0};//в этой структуре будет ответ сервера на запрос.

   //далее заполняеем все НЕОБХОДИМЫЕ поля структуры запроса.
   my_trade.action=TRADE_ACTION_DEAL;//Установить торговый ордер на немедленное совершение сделки с указанными 

                                     //параметрами (поставить рыночный ордер)
   my_trade.symbol=Symbol();//указываем в качестве валютной пары - текущую валютную пару 
                            //(ту, на которой запущен советник)
   my_trade.volume=NormalizeDouble(volume,1);//размер лота
   my_trade.price=NormalizeDouble(Ask,_Digits);//Цена, при достижении которой ордер 
должен быть исполнен. 
   //В нашем случае для TRADE_ACTION_DEAL это текущая цена и ее, согласно инструкции указывать не обязательно.
   my_trade.sl=NormalizeDouble(Ask-StopLoss*_Point,_Digits);//стоплосс ордера (цена при которой следует закрыть 
                                                            //убыточную сделку)
   my_trade.tp=NormalizeDouble(Ask+TakeProfit*_Point,_Digits);//тейкпрофит (цена при которой следует закрыть
                                                              // прибыльную сделку)
   my_trade.deviation=slippage;//проскальзывание в пунктах (при тестировании особой роли не играет, т.к. 
                               //проскальзывания не бывает на тестах)
   my_trade.type=ORDER_TYPE_BUY;//тип рыночного ордера (покупаем)
   my_trade.type_filling=ORDER_FILLING_FOK;//Указываем как исполнять ордер. (All Or Nothing - все или ничего) 

   //Сделка может быть совершена исключительно в указанном объеме и по цене равной или лучше указанной в ордере.
   my_trade.comment=comment;//комментарий ордера
   my_trade.magic=magic;//магическое число ордера

   ResetLastError();//обнуляем код последней ошибки 
   if(OrderSend(my_trade,my_trade_result))//отправляем запрос на открытие позиции. 
При этом проверяем 
                                          //успешно ли прошла отправка запроса
     {
      // Если сервер принял ордер то смортрим на результат 
      Print("Код результата операции - ",my_trade_result.retcode);
     }
   else
     {
      //Сервер не принял ордер в нем есть ошибки, выводим их в журнал
      Print("Код результата операции - ",my_trade_result.retcode);
      Print("Ошибка открытия ордера = ",GetLastError());
     }
return(0);// Выходим из функции открытия ордера     
}

//функция открытия короткой (Short) позиции. Аналогична функции открытия длинной позиции.
int OpenShort(double volume=0.1,int slippage=
10,string comment="EUR/USD 20 pips expert (Short)",int magic=0)
  {
   MqlTradeRequest my_trade;
   MqlTradeResult my_trade_result;
   my_trade.action=TRADE_ACTION_DEAL;
   my_trade.symbol=Symbol();
   my_trade.volume=NormalizeDouble(volume,1);
   my_trade.price=NormalizeDouble(Bid,_Digits);
   my_trade.sl=NormalizeDouble(Bid+StopLoss*_Point,_Digits);
   my_trade.tp=NormalizeDouble(Bid-TakeProfit*_Point,_Digits);
   my_trade.deviation=slippage;
   my_trade.type=ORDER_TYPE_SELL;
   my_trade.type_filling=ORDER_FILLING_FOK;
   my_trade.comment=comment;
   my_trade.magic=magic;

   ResetLastError();
   if(OrderSend(my_trade,my_trade_result))
     {
      Print("Код результата операции - ",my_trade_result.retcode);
     }
   else
     {
      Print("Код результата операции - ",my_trade_result.retcode);
      Print("Ошибка открытия ордера = ",GetLastError());
      }
return(0);
}

int OnInit()
  {
   return(0);
  }


void OnDeinit(const int reason){}

void OnTick()
   {
   double Open[];//массив где будут храниться цены открытия баров (включая 
Open[t1] и Open[t2])
   MqlDateTime mqldt;//в этой структуре храним текущее время.
   TimeCurrent(mqldt);//обновляем данные о текущем времени.
   int len;//переменная определяющая длинну массива Open[].

   MqlTick last_tick;//Здесь будут храниться цены последнего пришедшего тика
   SymbolInfoTick(_Symbol,last_tick);//заполняем структуру last_tick последними 
ценами текущего символа.
   Ask=last_tick.ask;//Обновляем переменные Ask и Bid для дальнейшего использования
   Bid=last_tick.bid;

   ArraySetAsSeries(Open,true);//для удобства работы 
определяем массив Open[] как таймсерию.

   //далее определим длину массива такую, чтобы обязательно вошли значения Open[t1] и Open[t2]
   if (t1>=t2)len=t1+1;//t1 и t2 - номера баров на которых 
сравниваются цены. Берем большее из них
   else len=t2+1;      //и добавляем 1 (т.к. есть еще и нулевой бар)

   CopyOpen(_Symbol,PERIOD_H1,0,len,Open);//заполняем массив Open[] актуальными значениями

   //Переключаем флаг cantarde на true, т.е. разрешим советнику снова открывать позиции
   if(((mqldt.hour)>TradeTime)) cantrade=true;

   // проверяем возможность встать в позицию:
   if(!PositionSelect(_Symbol))// Если еще нет открытой 
позиции
   {
      if((mqldt.hour==TradeTime) && (cantrade))//Если пришло время торговать
        {
         if(Open[t1]>(Open[t2]+delta*_Point))//Проверяем условие для открытия короткой сделки (продажи)
           {
               OpenShort(lot,10,"EUR/USD 20 pips expert (Short)",1234);//открываем позицию Short 
               cantrade=false;// Переключаем флаг (запрещаем торговать), чтобы не откывал больше 
позиций
               //до следующего дня
               return;//выходим
           }
         if((Open[t1]+delta*_Point)<Open[t2])//Проверяем условие для открытия длинной сделки (покупки)
           {
               OpenLong(lot,10,"EUR/USD 20 pips expert (Long)",1234);//открываем позицию Long
               cantrade=false;// Переключаем флаг (запрещаем торговать), чтобы не откывал больше 
позиций
               //до следующего дня
               return;//выходим
           }
        }
   }
   return;
  }
//+------------------------------------------------------------------+

Код советника хорошо закомментирован, поэтому остановлюсь только на некоторых моментах.

функции входа в рынок OpenShort(…) и OpenLong(…) используют две структуры: MqlTradeRequest и MqlTradeResult.

Вообще, структура – это место для хранения данных. Объявив новую структуру типа MqlTradeRequest, мы можем хранить в ней данные согласно ее устройству. А устроена эта структура следующим образом:

struct MqlTradeRequest
{
ENUM_TRADE_REQUEST_ACTIONS action; 	// Тип выполняемого действия
ulong 	magic; 		// Штамп эксперта (идентификатор magic number)
ulong 	order; 		// Тикет ордера
string 	symbol; 	// Имя торгового инструмента
double 	volume; 	// Запрашиваемый объем сделки в лотах
double 	price; 		// Цена 
double 	stoplimit; 	// Уровень StopLimit ордера
double 	sl; 		// Уровень Stop Loss ордера
double 	tp; 		// Уровень Take Profit ордера
ulong 	deviation; 		// Максимально приемлемое отклонение от запрашиваемой цены
ENUM_ORDER_TYPE type; 		// Тип ордера
ENUM_ORDER_TYPE_FILLING type_filling; 	// Тип ордера по исполнению
ENUM_ORDER_TYPE_TIME type_time; // Тип ордера по времени действия
datetime 	expiration; 	// Срок истечения ордера (для ордеров типа ORDER_TIME_SPECIFIED)
string 	comment; 	// Комментарий к ордеру
};

В коде советника структура объявляется следующей строкой:

    MqlTradeRequest my_trade;

Теперь в my_trade могут храниться все те данные, которые описаны в структуре MqlTradeRequest. Эти данные мы и заполняем перед вызовом торговой функции. Теперь, чтобы передать все эти данные серверу нам необходимо вызвать функцию:

    OrderSend(my_trade,my_trade_result);

Чтобы увидеть один из плюсов ООП сравним ее с аналогичной функцией из MQL4:

    OrderSend(Symbol(),OP_BUY,volume, Ask,slippage,StopLoss,TakeProfit,comment,magic,0,arrow_color);

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

Также в функции OrderSend(my_trade,my_trade_result); вторым параметром указывается другая структура типа – MqlTradeResult. Вот ее описание:

struct MqlTradeResult
  {
   uint     retcode;          // Код результата операции
   ulong    deal;             // Тикет сделки, если она совершена
   ulong    order;            // Тикет ордера, если он выставлен
   double   volume;           // Объем сделки, подтверждённый брокером
   double   price;            // Цена в сделке, подтверждённая брокером
   double   bid;              // Текущая рыночная цена предложения (цены реквота)
   double   ask;              // Текущая рыночная цена спроса (цены реквота)
   string   comment;          // Комментарий брокера к операции (по умолчанию заполняется расшифровкой)

  };

В этой структуре будет сохранен ответ сервера после выполнения запроса на открытие позиции. Т.е. после выполнения функции OrderSend(…) проверив поля этой структуры можно определить код выполнения нашего запроса, можно посмотреть тикет ордера, если он был выставлен и др. информацию. На данный момент нас интересует только код результата операции, и посмотреть мы его можем следующим образом: my_trade_result.retcode именно там, в структуре my_trade_result, лежит код выполнения запроса.

Аналогично, для доступа к текущему времени или текущим ценам Ask и Bid в коде советника используются соответствующие структуры – mqldt и last_tick и функции запроса данных - TimeCurrent(mqldt) и SymbolInfoTick(_Symbol,last_tick). Для поддержания актуальности данных в этих структурах, функции запроса данных вызываются в коде при каждом поступлении нового тика.

Еще одна особенность MQL5 – у нас нет доступа к таймсерии Open[], как это было в MQL4, т.е. мы не можем напрямую посмотреть цену открытия i-го бара текущего символа, обратившись к данным через Open[i]. Поэтому данный массив мы заполняем сами, правда, не весь, а только тот участок цен, который нам необходим:

//далее определим длину массива такую, чтобы обязательно вошли значения Open[t1] и Open[t2] 
if (t1>=t2)len=t1+1;//t1 и t2 - номера баров на которых 
сравниваются цены. Берем большее из них
else len=t2+1;      //и добавляем 1 (т.к. есть еще и нулевой бар)

CopyOpen(_Symbol,PERIOD_H1,0,len,Open);//заполняем массив Open[] актуальными значениями

Вот, в общем-то, и все нюансы перехода на MQL5 для случая с советником торговой системы "20/200 pips". Как видите ничего сложного, хотя и много непонятного. Однако, в Metatrader 5, как и в предыдущей версии программы, встроен превосходный справочник, поэтому любые сложности в написании программ преодолеваются без особых проблем.

Заключение.

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