Краткий курс программирования ЕА на MQL5 без ООП и стандартной библиотеки

AlexeyVik

Участник
Регистрация
22 Ноя 2018
Сообщения
224
Поблагодарили
1
Пол
Муж.
Здравствуйте уважаемые посетители данной ветки форума. Этот курс предназначен для начинающих знакомство с языком программирования MQL5.
Надеюсь, с азами программирования вы знакомы. Знаете что такое переменная, типы переменных, видимость переменных. Знаете что такое функция, пользовательская функция и прочие минимальные знания программирования вам не чужды.
Для примера напишем советник по избитой теме торговли, основанную на пересечении двух индикаторов Moving Average.
Замечание: Наш советник предназначен для обучения и не нацелен на извлечение прибыли.

Итак, начинаем:
Также надеюсь, знаете, как создать новый файл будущего советника. Меню «Файл» затем «Создать» отметить, что создаём советник и далее. Либо через контекстное меню «Новый файл»... и так далее. MetaEditor в помощь нам, создаёт такой код.
Код:
//+------------------------------------------------------------------+
//|                                                    MyFirstEA.mq5 |
//|                                                         Viktorov |
//|                                                v4forex@yandex.ru |
//+------------------------------------------------------------------+
#property copyright "Viktorov"
#property link      "v4forex@yandex.ru"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---

//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---

  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---

  }
//+------------------------------------------------------------------+
Теперь начинаем вставлять свой код. Для начала нам надо задать параметры торговли и используемых индикаторов.
Обращение к индикаторам в MQL5 происходит не так как в MQL4, поэтому надо объявить переменные, в которых будем хранить хендлы индикаторов. А поскольку индикаторов у нас два, то и хендлов должно быть два.
Затем в функции int OnInit() получим хендлы необходимых индикаторов.

В итоге наш код будет выглядеть уже вот так.
Код:
//+------------------------------------------------------------------+
//|                                                    MyFirstEA.mq5 |
//|                                                         Viktorov |
//|                                                v4forex@yandex.ru |
//+------------------------------------------------------------------+
#property copyright "Viktorov"
#property link      "v4forex@yandex.ru"
#property version   "1.00"

//---- input parameters
input double               lot            =  0.1;  // Размер лота
input int                  tacke          =  400;  // TackeProfit
input int                  loss           =  200;  // StopLoss
input int                  periodFastMa   =  8;    // Период усреднения быстрой МА
input ENUM_MA_METHOD       metodFastMa    =  1;    // Метод усреднения быстрой МА
input ENUM_APPLIED_PRICE   priceFastMA    =  1;    // Используемая цена быстрой МА
input int                  periodSlowMa   =  13;   // Период усреднения медленной МА
input ENUM_MA_METHOD       metodSlowMa    =  1;    // Метод усреднения медленной МА
input ENUM_APPLIED_PRICE   priceSlowMA    =  1;    // Используемая цена медленной МА

int handleFastMA  // хендл быстрой МА
  , handleSlowMA; // хендл медленной МА

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   handleFastMA = iMA(_Symbol, PERIOD_CURRENT, periodFastMa, 0, metodFastMa, priceFastMA);
   handleSlowMA = iMA(_Symbol, PERIOD_CURRENT, periodSlowMa, 0, metodSlowMa, priceSlowMA);
   if(handleFastMA == INVALID_HANDLE || handleSlowMA == INVALID_HANDLE)
   return(INIT_FAILED);
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---

  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---

  }
//+------------------------------------------------------------------+
Теперь надо как-то определить, что произошло пересечение линий индикаторов. Для этого напишем пользовательскую функцию определяющую факт пересечения, назовём её Crossing. Возвращать функция будет 0, 1 или -1 следовательно тип функции будет int.
Код:
int Crossing()
{
   double   fastMA[2], slowMA[2];
   CopyBuffer(handleFastMA, 0, 1, 2, fastMA);
   CopyBuffer(handleSlowMA, 0, 1, 2, slowMA);

   if(fastMA[0] < slowMA[0] && fastMA[1] > slowMA[1])
    return(0);
   if(fastMA[0] > slowMA[0] && fastMA[1] < slowMA[1])
    return(1);

  return(-1);
}/*******************************************************************/
Но вот незадача… Получается что пересечение действительно на каждом тике пока не закроется текущий бар… Следовательно откроется несколько позиций подряд. Чтобы это предотвратить надо организовать открытие позиций только один раз на баре в момент его первого тика. Для этого напишем ещё одну пользовательскую функцию.

Выглядит она так.
Код:
bool newBar()
{
 static datetime timeLastBar;
  MqlRates mqlRates[];
   int s = 0;
    do
     {
      s++;
     }
    while(CopyRates(_Symbol, PERIOD_CURRENT, 0, 1, mqlRates) < 0 && s < 7);
   bool ret = timeLastBar != mqlRates[0].time;
   if(ret)
    timeLastBar = mqlRates[0].time;
   return(ret);
}/*******************************************************************/
В этой функции объявлена статическая static переменная типа datetime timeLastBar которая хранит время открытия бара на котором было последнее обращение к этой функции. Для определения времени открытия текущего бара будем использовать функцию CopyRates() и помещать результат в массив структур типа MqlRates. Затем полученное время последнего бара сравнивается с временем хранящимся в переменной timeLastBar и если оно изменилось в переменную запишется время последнего бара и функция вернёт true. Если-же время не изменилось, возвращается false.
Поместим его опять-же в самый конец нашего кода и исправим вызов функции Crossing()

Таким образом, вызов функции выглядит так.
Код:
void OnTick()
  {
//---
   int isCrossed = Crossing();
   if(newBar())
    {
     if(isCrossed == 0)
     Print("Откроем позицию Buy");
     if(isCrossed == 1)
     Print("Откроем позицию Sell");
    }
  }
Теперь можно запустить код в тестере стратегий и понаблюдать когда в журнале будет печататься наши сообщения.
 
  • Like
Благодарности: Tokio

AlexeyVik

Участник
Регистрация
22 Ноя 2018
Сообщения
224
Поблагодарили
1
Пол
Муж.
И вот теперь самое трудное и новое. Будем писать функции открытия и модификации позиций.
Начнём с того, что определим имя функции, типы и имена входящих параметров, объявим и обнулим переменные структур которые используются в функции проверки OrderCheck( ) и функции запроса OrderSend()
Код:
bool openPosition(ENUM_ORDER_TYPE type // Тип ордера
                 , double volume       // Запрашиваемый объем сделки в лотах
                 , int sl              // Stop Loss ордера в пунктах
                 , int tp              // Take Profit ордера в пунктах
                 , int magic = 0       // MagickNumber ордера
                 , string comm = NULL  // комментарий ордера/позиции
                 )
{
 MqlTradeCheckResult checkResult;
   ZeroMemory(checkResult);
 MqlTradeRequest     request;
  ZeroMemory(request);
 MqlTradeResult      tradeResult;
    ZeroMemory(tradeResult);

  return(false);
}/*******************************************************************/
Затем объявим переменную для хранения актуальных цен и заполним структуру mqlTick.
Код:
MqlTick             mqlTick;
      int s = 0;
       do
        {
         s++;
        }
       while(!SymbolInfoTick(_Symbol, mqlTick) && s < 7);
        double price = type == ORDER_TYPE_BUY ? mqlTick.ask : mqlTick.bid;
      double stop = type == ORDER_TYPE_BUY ? price-(sl*_Point) : price+(sl*_Point)
           , take = type == ORDER_TYPE_BUY ? price+(tp*_Point) : price-(tp*_Point);
Важно:
Для правильного заполнения поля type_filling нам необходимо знать режимы заключения сделок по конкретному инструменту.

Получим это свойство в переменную типа перечисления.
Код:
     ENUM_SYMBOL_TRADE_EXECUTION exec = (ENUM_SYMBOL_TRADE_EXECUTION)SymbolInfoInteger(_Symbol, SYMBOL_TRADE_EXEMODE);
Теперь самое время заполнять структуру запроса. От режима заключения сделок зависит не только поле type_filling но и как заполнять поля sl и tp
Если тип исполнения Market то стопы и тейки можно устанавливать только после открытия позиции посредством модификации позиции. Соответственно при заполнении учтём этот факт.

Заполнение структуры:
Код:
    request.action         =  TRADE_ACTION_DEAL;
     request.symbol         =  _Symbol;
      request.volume         =  volume;
       request.price          =  price;
        request.sl             =  exec == SYMBOL_TRADE_EXECUTION_MARKET ? 0.0 : sl == 0 ? 0.0 : NormalizeDouble(stop, _Digits);
         request.tp             =  exec == SYMBOL_TRADE_EXECUTION_MARKET ? 0.0 : tp == 0 ? 0.0 : NormalizeDouble(take, _Digits);
        request.deviation      =  555;
       request.type           =  type;
      request.type_filling   =  exec == SYMBOL_TRADE_EXECUTION_MARKET ? ORDER_FILLING_FOK : ORDER_FILLING_RETURN;
     request.magic          =  magic;
    request.comment        =  comm;
ВСЁ… Теперь проверим достаточность средств и правильность заполнения структуры запроса и отправим запрос на исполнение.
И последнее, если тип исполнения Market то после открытия позиции надо будет её выбрать для дальнейшей работы с ней и установить Stop Loss и Take Profit.
Так выглядит код открытия позиции.
Код:
bool openPosition(ENUM_ORDER_TYPE type // Тип ордера
                 , double volume       // Запрашиваемый объем сделки в лотах
                 , int sl              // Stop Loss ордера в пунктах
                 , int tp              // Take Profit ордера в пунктах
                 , int magic = 0       // MagickNumber ордера
                 , string comm = NULL  // комментарий ордера/позиции
                 )
{
 MqlTradeCheckResult checkResult;
   ZeroMemory(checkResult);
 MqlTradeRequest     request;
  ZeroMemory(request);
 MqlTradeResult      tradeResult;
    ZeroMemory(tradeResult);
 MqlTick             mqlTick;
      int s = 0;
       do
        {
         s++;
        }
       while(!SymbolInfoTick(_Symbol, mqlTick) && s < 7);
        double price = type == ORDER_TYPE_BUY ? mqlTick.ask : mqlTick.bid;
      double stop = type == ORDER_TYPE_BUY ? price-(sl*_Point) : price+(sl*_Point)
           , take = type == ORDER_TYPE_BUY ? price+(tp*_Point) : price-(tp*_Point);
     ENUM_SYMBOL_TRADE_EXECUTION exec = (ENUM_SYMBOL_TRADE_EXECUTION)SymbolInfoInteger(_Symbol, SYMBOL_TRADE_EXEMODE);
    request.action         =  TRADE_ACTION_DEAL;
     request.symbol         =  _Symbol;
      request.volume         =  volume;
       request.price          =  price;
        request.sl             =  exec == SYMBOL_TRADE_EXECUTION_MARKET ? 0.0 : sl == 0 ? 0.0 : NormalizeDouble(stop, _Digits);
         request.tp             =  exec == SYMBOL_TRADE_EXECUTION_MARKET ? 0.0 : tp == 0 ? 0.0 : NormalizeDouble(take, _Digits);
        request.deviation      =  555;
       request.type           =  type;
      request.type_filling   =  exec == SYMBOL_TRADE_EXECUTION_MARKET ? ORDER_FILLING_FOK : ORDER_FILLING_RETURN;
     request.magic          =  magic;
    request.comment        =  comm;
     if(!OrderCheck(request, checkResult))
      Print(__FUNCTION__, " ", checkResult.retcode, " ", request.volume, " ", checkResult.margin_free-checkResult.margin);
    else
      {
       if(!OrderSend(request, tradeResult))
        Print(__FUNCTION__, " ", tradeResult.retcode);
       if(exec == SYMBOL_TRADE_EXECUTION_MARKET)
        {
         int n = 0;
          do
           {
            n++;
           }
          while(!PositionSelectByTicket(tradeResult.order) && n < 5);
          sl_tp_Modification(stop, take, tradeResult.order);
        }
       return(true);
      }
  return(false);
}/*******************************************************************/
Поскольку модификация позиций выполняется той-же функцией OrderSend() подробно разбирать её не имеет смысла. Отличие только в заполняемых полях структуры.

Так выглядит код модификации позиции.
Код:
bool sl_tp_Modification(double stopLoss
                       , double takeProfit
                       , ulong  ticket
                       )
{
 bool ret = false;
 MqlTradeRequest     request;
 MqlTradeCheckResult checkResult;
 MqlTradeResult      tradeResult;
  ZeroMemory(request);
   ZeroMemory(checkResult);
    ZeroMemory(tradeResult);
   if(PositionSelectByTicket(ticket))
    {
     request.action         =  TRADE_ACTION_SLTP;
      request.symbol         =  PositionGetString(POSITION_SYMBOL);
       request.sl             =  stopLoss;
      request.tp             =  takeProfit;
     request.position       =  ticket;
    if(!OrderCheck(request, checkResult))
     {
     Print("****** StopLoss ", stopLoss);
     Print("****** Take_Profit ", takeProfit);
     Print("****** ticket ", ticket);
     return(ret);
     }
    }
  ret = OrderSend(request, tradeResult);
  if(!ret)  Print(__FUNCTION__, " ", tradeResult.retcode);
   return(ret);
}/*******************************************************************/
Вот теперь можно внести изменения и вместо печати в журнал открывать позиции.
Код:
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   int isCrossed = Crossing();
   if(newBar())
    {
     if(isCrossed == 0)
      openPosition(ORDER_TYPE_BUY, lot, loss, tacke);
     if(isCrossed == 1)
      openPosition(ORDER_TYPE_SELL, lot, loss, tacke);
    }
  }
Полный код советника в прикреплённом файле.
 

Вложения

Yra

Новичок
Регистрация
25 Дек 2018
Сообщения
524
Поблагодарили
1
Пол
Муж.
Здравствуйте,а продолжение еще будет очень интересно но хотелось бы еще чего нибудь для разнообразия а то честно говоря я почти ничего не понял как то тут все написано очень кратко.
P.S. с наступающим Новым 2019 Годом.
 

AlexeyVik

Участник
Регистрация
22 Ноя 2018
Сообщения
224
Поблагодарили
1
Пол
Муж.
Здравствуйте,а продолжение еще будет очень интересно но хотелось бы еще чего нибудь для разнообразия а то честно говоря я почти ничего не понял как то тут все написано очень кратко.
P.S. с наступающим Новым 2019 Годом.
Ну, так в названии темы так и сказано "Краткий...". А какое продолжение ждёте? Советник написан, работоспособен, прибыли никто не обещал... Что не понятно спрашивайте.
 

Yra

Новичок
Регистрация
25 Дек 2018
Сообщения
524
Поблагодарили
1
Пол
Муж.
Ну, так в названии темы так и сказано "Краткий...". А какое продолжение ждёте? Советник написан, работоспособен, прибыли никто не обещал... Что не понятно спрашивайте.
Ну если честно то хотелось-бы еще парочку простеньких открытых кодов я по ним начинаю учить язык программирования MQL5 я беру открытые коды и сравниваю с кодами на языке MQL4 и нахожу расбежности в их написании и с помощью справочника пробую писать.
 

AlexeyVik

Участник
Регистрация
22 Ноя 2018
Сообщения
224
Поблагодарили
1
Пол
Муж.
Ну если честно то хотелось-бы еще парочку простеньких открытых кодов я по ним начинаю учить язык программирования MQL5 я беру открытые коды и сравниваю с кодами на языке MQL4 и нахожу расбежности в их написании и с помощью справочника пробую писать.
На мой взгляд неправильный подход. Хотя я начал именно так. Вскоре запутался, один пишет так, другой по-другому... в результате как правильно никто не угадает. Логически всё что я смотрел не правильно... Потом я начал подходить к пониманию примерно так:
Нужно получить значение индикатора. Открываешь документацию, находишь раздел "Доступ к таймсериям и индикаторам", видишь там функцию CopyBufer и читаешь
Получает в массив buffer данные указанного буфера указанного индикатора в указанном количестве.
Затем начинаешь проверять что получится. Если что-то не получается идёшь на форум и спрашиваешь что сделал не так... Мне спрашивать не приходилось.
В принципе, на текущий момент, для написания советника, отличия языков только в отправке приказа OrderSend и в получении значений индикаторов.
И ещё одно замечание. Всё что я тут написал, это всё исключительно для понимания самого процесса программирования и понимания отличий языков.
В mql5 есть неплохая стандартная библиотека. Подключается так
Код:
#include <Trade/Trade.mqh>
CTrade Trade;
Затем чтобы открыть позицию Buy достаточно написать
Код:
Trade.Buy(Lot);
И ВСЁ...
Ну, почти всё. В ините надо всунуть магик. Для этого есть спец функция
Код:
Trade.SetExpertMagicNumber(magick);
 

Yra

Новичок
Регистрация
25 Дек 2018
Сообщения
524
Поблагодарили
1
Пол
Муж.
На мой взгляд неправильный подход. Хотя я начал именно так. Вскоре запутался, один пишет так, другой по-другому... в результате как правильно никто не угадает. Логически всё что я смотрел не правильно... Потом я начал подходить к пониманию примерно так:
Нужно получить значение индикатора. Открываешь документацию, находишь раздел "Доступ к таймсериям и индикаторам", видишь там функцию CopyBufer и читаешь

Затем начинаешь проверять что получится. Если что-то не получается идёшь на форум и спрашиваешь что сделал не так... Мне спрашивать не приходилось.
В принципе, на текущий момент, для написания советника, отличия языков только в отправке приказа OrderSend и в получении значений индикаторов.
И ещё одно замечание. Всё что я тут написал, это всё исключительно для понимания самого процесса программирования и понимания отличий языков.
Приветствую ну Ты молодец я тебе очень благодарен хорошенько просветил, по больше бы таких людей на форум, вроде мелочь, но очень полезно может потому что я знаком с MQL4 пятый уже на много проще пойдет, основы знаю, а последовательность символов и букв мне покажет справочник .Большое тебе спасибо, за то что не жалеешь свои знания.
 
Последнее редактирование:

AlexeyVik

Участник
Регистрация
22 Ноя 2018
Сообщения
224
Поблагодарили
1
Пол
Муж.
Приветствую ну Ты молодец я тебе очень благодарен хорошенько просветил, по больше бы таких людей на форум, вроде мелочь, но очень полезно может потому что я знаком с MQL4 пятый уже на много проще пойдет, основы знаю, а последовательность символов и букв мне покажет справочник .Большое тебе спасибо, за то что не жалеешь свои знания.
 

Вложения

Sultan

Новичок
Регистрация
10 Ноя 2019
Сообщения
6
Поблагодарили
1
Пол
Муж.
Доброго времени суток всем. У кого нибудь найдется видео урок по написанию ЕА на мт5
 

AlexeyVik

Участник
Регистрация
22 Ноя 2018
Сообщения
224
Поблагодарили
1
Пол
Муж.
А читать лениво?
 
Сверху