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

AlexeyVik

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

Итак, начинаем:
Также надеюсь, знаете, как создать новый файл будущего советника. Меню «Файл» затем «Создать» отметить, что создаём советник и далее. Либо через контекстное меню «Новый файл»... и так далее. MetaEditor в помощь нам, создаёт такой код.
Код:
//+------------------------------------------------------------------+
//|                                                    MyFirstEA.mq5 |
//|                                                         Viktorov |
//|                                                [email protected] |
//+------------------------------------------------------------------+
#property copyright "Viktorov"
#property link      "[email protected]"
#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 |
//|                                                [email protected] |
//+------------------------------------------------------------------+
#property copyright "Viktorov"
#property link      "[email protected]"
#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
И вот теперь самое трудное и новое. Будем писать функции открытия и модификации позиций.
Начнём с того, что определим имя функции, типы и имена входящих параметров, объявим и обнулим переменные структур которые используются в функции проверки 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);
    }
  }
Полный код советника в прикреплённом файле.
 

Вложения

Здравствуйте,а продолжение еще будет очень интересно но хотелось бы еще чего нибудь для разнообразия а то честно говоря я почти ничего не понял как то тут все написано очень кратко.
P.S. с наступающим Новым 2019 Годом.
 
Здравствуйте,а продолжение еще будет очень интересно но хотелось бы еще чего нибудь для разнообразия а то честно говоря я почти ничего не понял как то тут все написано очень кратко.
P.S. с наступающим Новым 2019 Годом.

Ну, так в названии темы так и сказано "Краткий...". А какое продолжение ждёте? Советник написан, работоспособен, прибыли никто не обещал... Что не понятно спрашивайте.
 
Ну, так в названии темы так и сказано "Краткий...". А какое продолжение ждёте? Советник написан, работоспособен, прибыли никто не обещал... Что не понятно спрашивайте.

Ну если честно то хотелось-бы еще парочку простеньких открытых кодов я по ним начинаю учить язык программирования MQL5 я беру открытые коды и сравниваю с кодами на языке MQL4 и нахожу расбежности в их написании и с помощью справочника пробую писать.
 
Ну если честно то хотелось-бы еще парочку простеньких открытых кодов я по ним начинаю учить язык программирования MQL5 я беру открытые коды и сравниваю с кодами на языке MQL4 и нахожу расбежности в их написании и с помощью справочника пробую писать.

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

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

Вложения

  • photo5575.png
    photo5575.png
    497.6 KB · Просмотры: 276
Доброго времени суток всем. У кого нибудь найдется видео урок по написанию ЕА на мт5
 
Здравствуйте ! Просветите пожалуйста, в каких случаях на пересечениях МАшек Вашего советника, не срабатывают открытия и закрытия , что нужно понимать и делать, для безусловного исполнения условий кода по пересечениям МАшек, когда по ним не срабатывает терминал МТ5 ? Погонял советника в тестерах пяти брокеров, весьма широкозахватно и кропотливо, закономерности несрабатываний в пересечениях не заметил, что хочет, то и творит). СПАСИБО.
 
Последнее редактирование:
IMG_0354.webp





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

Введение в MQL5 без ООП и стандартной библиотеки


MQL5 — это язык программирования, разработанный для создания торговых роботов и индикаторов на платформе MetaTrader 5. Он основан на C++, но для упрощения работы трейдеров существуют различные стандартные библиотеки и возможности объектно-ориентированного программирования (ООП). Однако, не всегда удобно и необходимо использовать все эти инструменты. В данном курсе мы рассмотрим, как можно создать торгового робота (Expert Advisor, ЕА) на MQL5 без использования ООП и стандартной библиотеки, что особенно полезно для новичков или тех, кто хочет писать код более линейным способом.

Основные компоненты торгового робота в MQL5

1. Структура программы
Основные компоненты любой программы на MQL5 — это функции OnInit(), OnTick() и OnDeinit(). Эти функции вызываются в определенные моменты времени:
• OnInit() — при запуске робота.
• OnTick() — каждый раз, когда приходит новый тик цены.
• OnDeinit() — при завершении работы робота.
2. Пример минимальной программы
Чтобы начать, давайте создадим простейшего робота, который просто будет выводить сообщение в журнал при запуске, новом тике и завершении.

int OnInit()
{
Print("Робот запущен");
return INIT_SUCCEEDED;
}

void OnTick()
{
Print("Новый тик");
}

void OnDeinit(const int reason)
{
Print("Робот завершил работу");
}

Этот код не делает торговых операций, но демонстрирует базовую структуру программы на MQL5.

Создание торговых операций

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

3. Добавление торговой логики
Для этого нам нужно:

• Получить текущую цену.
• Рассчитать скользящую среднюю.
• Открыть сделку, если цена выше средней.

void OnTick()
{
double price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double ma = iMA(_Symbol, 0, 20, 0, MODE_SMA, PRICE_CLOSE, 0);

if (price > ma)
{
Print("Открытие сделки на покупку");
OpenBuy();
}
}

void OpenBuy()
{
trade.Buy(0.1);
}

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

Управление торговыми операциями

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

void OpenBuy()
{
if (PositionsTotal() == 0)
{
trade.Buy(0.1);
Print("Открыта сделка на покупку");
}
}

void CloseAll()
{
for (int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
trade.PositionClose(ticket);
Print("Закрыта сделка с тикетом: ", ticket);
}
}

Функция OpenBuy() проверяет, есть ли открытые позиции, и если их нет, открывает новую сделку. CloseAll() закрывает все текущие позиции.

Заключение

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

Новые темы