iqoption

Как создать торгового робота для Московской биржи MOEX на MetaTrader 5?

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

 

Торговать на бирже с помощью роботов — это просто

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

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


MetaTrader 5 предлагает 6 типов торговых операций

Существует несколько основных типов торговых операций, которые вам могут понадобиться в торговом роботе:

  1. покупка/продажа по текущей цене,
  2. установка отложенного ордера на покупку/продажу по некоторому условию,
  3. модификация/удаление отложенного ордера,
  4. закрытие/наращивание/сокращение/переворот позиции.

Все эти операции реализуются с помощью функции OrderSend(), существует также и асинхронный вариант этой функции — OrderSendAsync(). Всё многообразие торговых операций описывается структурой MqlTradeRequest, содержащей описание торгового запроса. Поэтому единственные трудности с торговыми операциями могут заключаться только в правильном заполнении структуры MqlTradeRequest и обработке результата выполнения запроса.

В соответствии с правилами вашей торговой системы вы можете совершить покупку или продажу по цене рынка (BUY или SELL), а можете поместить отложенный ордер на совершение покупки/продажи на некотором расстоянии от текущей цены рынка:

  • BUY STOP, SELL STOP — покупка или продажа при пробитии указанного уровня (хуже текущей цены);
  • BUY LIMIT, SELL LIMIT — покупка или продажа при достижении указанного уровня (лучше текущей цены);
  • BUY STOP LIMIT, SELL STOP LIMIT — установка ордера BUY LIMIT или SELL LIMIT при достижении указанной цены.

Типы этих стандартных ордеров соответствуют перечислению ENUM_ORDER_TYPE

 

Кроме  того, вам может понадобиться модифицировать или вовсе удалить отложенный ордер, это также делается с помощью функций OrderSend()/OrderSendAsync(). Изменение открытой позиций тоже не представляет сложности, так как происходит в результате совершения всё тех же торговых операций.

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

 

Работа с торговым счетом

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

Для работы со счетом есть класс CAccountInfo, который как раз и разрабатывался для этих целей. Добавим в наш код подключение файла  AccountInfo.mqh и объявим переменную этого класса account:

#include <Trade\AccountInfo.mqh>
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- объект для работы со счетом
  CAccountInfo account;
//--- получим номер счета, на котором запущен советник
   long login=account.Login();
   Print("Login=",login);
//--- выведем валюту счета    
   Print("Валюта счета: ",account.Currency());   
//--- выведем баланс и текущую прибыль на счете
   Print("Balance=",account.Balance(),"  Profit=",account.Profit(),"   Equity=",account.Equity());
//--- выведем тип счета    
   Print("Тип счета: ",account.TradeModeDescription());
//--- выясним, можно ли вообще торговать на данном счете
   if(account.TradeAllowed())
      Print("Торговля на данном счете разрешена");
   else
      Print("Торговля на счете запрещена: возможно, вход был совершен по инвест-паролю");
//--- режим вычисления маржи
   Print("Режим вычисления маржи: ",account.MarginModeDescription());
//--- выясним, разрешено ли торговать на счете с помощью эксперта
   if(account.TradeExpert())
      Print("Автоматическая торговля на счете разрешена");
   else
      Print("Запрещена автоматическая торговля с помощью экспертов и скриптов");
//--- допустимое количество ордеров задано или нет
   int orders_limit=account.LimitOrders();
   if(orders_limit!=0)Print("Максимально допустимое количество действующих отложенных ордеров: ",orders_limit);
//--- выведем имя компании и сервера
   Print(account.Company(),": server ",account.Server());
   Print(__FUNCTION__,"  completed"); //---   
  }

Как видно из приведенного кода, с помощью переменной account в функции OnInit() можно получить много полезной информации. Вы можете добавить этот код в своего эксперта и вам будет гораздо проще разбирать логи при анализе его работы.

Результат запуска скрипта показан на картинке.

 

Получение свойств финансового инструмента

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

#include<Trade\SymbolInfo.mqh>
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- объект для получения свойств символа
  CSymbolInfo symbol_info;
//--- зададим имя символа, для которого будем получать информацию
   symbol_info.Name(_Symbol);
//--- получим текущие котировки и выведем
   symbol_info.RefreshRates();
   Print(symbol_info.Name()," (",symbol_info.Description(),")",
         "  Bid=",symbol_info.Bid(),"   Ask=",symbol_info.Ask());
//--- получим количество знаков после запятой и размер пункта
   Print("Digits=",symbol_info.Digits(),
         ", Point=",DoubleToString(symbol_info.Point(),symbol_info.Digits()));
//--- запросим тип исполнения ордеров, нет ли ограничений
   Print("Ограничения на торговые операции: ",EnumToString(symbol_info.TradeMode()),
         " (",symbol_info.TradeModeDescription(),")");
//--- выясним режим заключения сделок
   Print("Режим исполнения сделок: ",EnumToString(symbol_info.TradeExecution()),
         " (",symbol_info.TradeExecutionDescription(),")");
//--- выясним способ вычисления стоимости контрактов
   Print("Вычисление стоимости контракта: ",EnumToString(symbol_info.TradeCalcMode()),
         " (",symbol_info.TradeCalcModeDescription(),")");
//--- размер контракта
   Print("Размер стандартного контракта: ",symbol_info.ContractSize());
//--- размер начальной маржи для 1 контракта
   Print("Начальная маржа для стандартного контракта: ",symbol_info.MarginInitial()," ",symbol_info.CurrencyBase());
//--- минимальный, максимальный размеры объема в торговых операциях
   Print("Volume info: LotsMin=",symbol_info.LotsMin(),"  LotsMax=",symbol_info.LotsMax(),
         "  LotsStep=",symbol_info.LotsStep());
//--- 
   Print(__FUNCTION__,"  completed");   
  }

И на рисунке показаны свойства символа Si-6.16 из секции срочного рынка Московской биржи (FORTS). Теперь вы готовы перейти непосредственно к торговле.

 

Программирование торговых операций

Для отправки торговых приказов в языке MQL5 существует две функции — OrderSend() и OrderSendAsync(). На самом деле это две реализации одной функции. Если OrderSend() отправляет торговый запрос и ждет результата его выполнения, то асинхронная OrderSendAsync() просто выстреливает запрос и позволяет работать программе дальше, не дожидаясь ответа торгового сервера. Таким образом, торговать в MQL5 действительно просто, достаточно использовать только одну функцию для всех торговых операций

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

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

#include<Trade\Trade.mqh>
//--- объект для проведения торговых операций
CTrade  trade;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- зададим MagicNumber для идентификации своих ордеров
   int MagicNumber=123456;
   trade.SetExpertMagicNumber(MagicNumber);
//--- установим допустимое проскальзывание в пунктах при совершении покупки/продажи
   int deviation=10;
   trade.SetDeviationInPoints(deviation);
//--- режим заполнения ордера, нужно использовать тот режим, который разрешается сервером
   trade.SetTypeFilling(ORDER_FILLING_RETURN);
//--- режим логирования: лучше не вызывать этот метод вообще, класс сам выставит оптимальный режим
   trade.LogLevel(1); 
//--- какую функцию использовать для торговли: true - OrderSendAsync(), false - OrderSend()
   trade.SetAsyncMode(true);
//---
   return(0);
  }

Для торговли на бирже как правило используется режим исполнения ORDER_FILLING_RETURN. Справка гласит:

Данный режим используется только в режимах «Исполнение по рынку» и «Биржевое исполнение»: для рыночных (ORDER_TYPE_BUY и ORDER_TYPE_SELL), лимитных и стоп-лимитных ордеров (ORDER_TYPE_BUY_LIMIT, ORDER_TYPE_SELL_LIMIT, ORDER_TYPE_BUY_STOP_LIMIT и ORDER_TYPE_SELL_STOP_LIMIT). В случае частичного исполнения рыночный или лимитный ордер с остаточным объемом не снимается, а продолжает действовать.

Для ордеров ORDER_TYPE_BUY_STOP_LIMIT и ORDER_TYPE_SELL_STOP_LIMIT при активации будет создан соответствующий лимитный ордер ORDER_TYPE_BUY_LIMIT/ORDER_TYPE_SELL_LIMIT с типом исполнения ORDER_FILLING_RETURN.

 

Ну а теперь пришло время посмотреть, как CTrade помогает в торговых операциях.

Покупка/продажа по текущей цене

Часто в торговых стратегиях необходимо совершить покупку или продажу по текущей цене прямо сейчас. CTrade знаком с такой ситуацией и просит лишь необходимый объем торговой операции. Все остальные параметры — цену открытия и название символа, уровни Stop Loss и Take Profit, комментарий к ордеру — можно не указывать.

//--- 1. пример покупки по текущему символу
   if(!trade.Buy(1))
     {
      //--- сообщим о неудаче
      Print("Метод Buy() потерпел неудачу. Код возврата=",trade.ResultRetcode(),
            ". Описание кода: ",trade.ResultRetcodeDescription());
     }
   else
     {
      Print("Метод Buy() выполнен успешно. Код возврата=",trade.ResultRetcode(),
            " (",trade.ResultRetcodeDescription(),")");
     }

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

//--- 2. пример покупки по указанному символу
   if(!trade.Buy(1,"Si-6.16"))
     {
      //--- сообщим о неудаче
      Print("Метод Buy() потерпел неудачу. Код возврата=",trade.ResultRetcode(),
            ". Описание кода: ",trade.ResultRetcodeDescription());
     }
   else
     {
      Print("Метод Buy() выполнен успешно. Код возврата=",trade.ResultRetcode(),
            " (",trade.ResultRetcodeDescription(),")");
     }

Можно указать все параметры ордера: уровни Stop Loss/Take Profit, цена открытия и комментарий.

//--- 3. пример покупки по указанному символу символу с заданными SL и TP
   double volume=1;           // укажем объем торговой операции
   string symbol="Si-6.16";   // укажем символ, на котором проводится операция
   int    digits=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS); // количество знаков после запятой
   double point=SymbolInfoDouble(symbol,SYMBOL_POINT);         // пункт
   double bid=SymbolInfoDouble(symbol,SYMBOL_BID);             // текущая цена для закрытия LONG
   double SL=bid-100*point;                                    // ненормализованное значение SL
   SL=NormalizeDouble(SL,digits);                              // нормализуем Stop Loss
   double TP=bid+100*point;                                    // ненормализованное значение TP
   TP=NormalizeDouble(TP,digits);                              // нормализуем Take Profit
//--- получим текущую цену открытия для LONG позиций
   double open_price=SymbolInfoDouble(symbol,SYMBOL_ASK);
   string comment=StringFormat("Buy %s %G lots at %s, SL=%s TP=%s",
                               symbol,volume,
                               DoubleToString(open_price,digits),
                               DoubleToString(SL,digits),
                               DoubleToString(TP,digits));
   if(!trade.Buy(volume,symbol,open_price,SL,TP,comment))
     {
      //--- сообщим о неудаче
      Print("Метод Buy() потерпел неудачу. Код возврата=",trade.ResultRetcode(),
            ". Описание кода: ",trade.ResultRetcodeDescription());
     }
   else
     {
      Print("Метод Buy() выполнен успешно. Код возврата=",trade.ResultRetcode(),
            " (",trade.ResultRetcodeDescription(),")");
     }

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

Выставление лимитного ордера

Для отправки лимитного ордера используется соответствующий метод класса BuyLimit() или SellLimit(). Для большинства случаев может подойти укороченный вариант, когда указываются только цена открытия и объем. Цена открытия для BuyLimit должна быть ниже текущей цены, а для SellLimit должна быть выше. То есть эти ордера используются для входа в рынок по лучшей цене, например, в стратегиях с расчетом на отскок от уровня поддержки. При этом используется тот символ, на котором запущен эксперт:

//--- 1. пример установки отложенного ордера BuyLimit
   string symbol="Si-6.16";                                    // укажем символ, на котором выставляется ордер
   int    digits=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS); // количество знаков после запятой
   double point=SymbolInfoDouble(symbol,SYMBOL_POINT);         // пункт
   double ask=SymbolInfoDouble(symbol,SYMBOL_ASK);             // текущая цена покупки
   double price=ask-100*point;                                 // ненормализованное цена открытия
   price=NormalizeDouble(price,digits);                        // нормализуем цену открытия
//--- все готово, отправляем на сервер отложенный ордер Buy Limit
   if(!trade.BuyLimit(1,price))
     {
      //--- сообщим о неудаче
      Print("Метод BuyLimit() потерпел неудачу. Код возврата=",trade.ResultRetcode(),
            ". Описание кода: ",trade.ResultRetcodeDescription());
     }
   else
     {
      Print("Метод BuyLimit() выполнен успешно. Код возврата=",trade.ResultRetcode(),
            " (",trade.ResultRetcodeDescription(),")");
     }

Можно использовать и более подробный вариант с указанием всех параметров: уровни SL/TP, время истечения, название инструмента и комментарий к ордеру.

//--- 2. пример установки отложенного ордера BuyLimit со всеми параметрами
   double volume=1;
   string symbol="Si-6.16";                                    // укажем символ, на котором выставляется ордер
   int    digits=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS); // количество знаков после запятой
   double point=SymbolInfoDouble(symbol,SYMBOL_POINT);         // пункт
   double ask=SymbolInfoDouble(symbol,SYMBOL_ASK);             // текущая цена покупки
   double price=ask-100*point;                                 // ненормализованное цена открытия
   price=NormalizeDouble(price,digits);                        // нормализуем цену открытия
   int SL_pips=100;                                            // Stop Loss в пунктах
   int TP_pips=100;                                            // Take Profit в пунктах
   double SL=price-SL_pips*point;                              // ненормализованное значение SL
   SL=NormalizeDouble(SL,digits);                              // нормализуем Stop Loss
   double TP=price+TP_pips*point;                              // ненормализованное значение TP
   TP=NormalizeDouble(TP,digits);                              // нормализуем Take Profit
   datetime expiration=TimeTradeServer()+PeriodSeconds(PERIOD_D1);
   string comment=StringFormat("Buy Limit %s %G lots at %s, SL=%s TP=%s",
                               symbol,volume,
                               DoubleToString(price,digits),
                               DoubleToString(SL,digits),
                               DoubleToString(TP,digits));
//--- все готово, отправляем на сервер отложенный ордер Buy Limit
   if(!trade.BuyLimit(volume,price,symbol,SL,TP,ORDER_TIME_DAY,expiration,comment))
     {
      //--- сообщим о неудаче
      Print("Метод BuyLimit() потерпел неудачу. Код возврата=",trade.ResultRetcode(),
            ". Описание кода: ",trade.ResultRetcodeDescription());
     }
   else
     {
      Print("Метод BuyLimit() выполнен успешно. Код возврата=",trade.ResultRetcode(),
            " (",trade.ResultRetcodeDescription(),")");
     }

Во втором варианте необходимо правильно указать уровни SL и TP. Не забывайте, что для покупок уровень Take Profit должен быть выше цены открытия, а уровень Stop Loss — ниже цены открытия. Для ордеров SellLimit всё наоборот. Вы легко можете узнать о своей ошибке при тестировании эксперта на исторических данных, класс CTrade автоматически выводит в таких случаях сообщения (если вы сами не вызывали функцию LogLevel).

 

Выставление стопового ордера

Для отправки стопового ордера используются аналогичные методы BuyStop() и SellStop(). Цена открытия для Buy Stop должна быть выше текущей цены, а для SellStop должна быть ниже. Стоповые ордера используются в стратегиях, которые входят на прорыве некоего уровня сопротивления, а также для ограничения убытков. Простой вариант:

//--- 1. пример установки отложенного ордера Buy Stop
   string symbol="RTS-6.16";    // укажем символ, на котором выставляется ордер
   int    digits=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS); // количество знаков после запятой
   double point=SymbolInfoDouble(symbol,SYMBOL_POINT);         // пункт
   double ask=SymbolInfoDouble(symbol,SYMBOL_ASK);             // текущая цена покупки
   double price=ask+100*point;                                 // ненормализованное цена открытия
   price=NormalizeDouble(price,digits);                        // нормализуем цену открытия
//--- все готово, отправляем на сервер отложенный ордер Buy Stop 
   if(!trade.BuyStop(1,price))
     {
      //--- сообщим о неудаче
      Print("Метод BuyStop() потерпел неудачу. Код возврата=",trade.ResultRetcode(),
            ". Описание кода: ",trade.ResultRetcodeDescription());
     }
   else
     {
      Print("Метод BuyStop() выполнен успешно. Код возврата=",trade.ResultRetcode(),
            " (",trade.ResultRetcodeDescription(),")");
     }

И более подробный, когда нужно указать максимум параметров для отложенного ордера BuyStop:

//--- 2. пример установки отложенного ордера Buy Stop со всеми параметрами
   double volume=1;
   string symbol="RTS-6.16";    // укажем символ, на котором выставляется ордер
   int    digits=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS); // количество знаков после запятой
   double point=SymbolInfoDouble(symbol,SYMBOL_POINT);         // пункт
   double ask=SymbolInfoDouble(symbol,SYMBOL_ASK);             // текущая цена покупки
   double price=ask+100*point;                                 // ненормализованное цена открытия
   price=NormalizeDouble(price,digits);                        // нормализуем цену открытия
   int SL_pips=100;                                            // Stop Loss в пунктах
   int TP_pips=100;                                            // Take Profit в пунктах
   double SL=price-SL_pips*point;                              // ненормализованное значение SL
   SL=NormalizeDouble(SL,digits);                              // нормализуем Stop Loss
   double TP=price+TP_pips*point;                              // ненормализованное значение TP
   TP=NormalizeDouble(TP,digits);                              // нормализуем Take Profit
   datetime expiration=TimeTradeServer()+PeriodSeconds(PERIOD_D1);
   string comment=StringFormat("Buy Stop %s %G lots at %s, SL=%s TP=%s",
                               symbol,volume,
                               DoubleToString(price,digits),
                               DoubleToString(SL,digits),
                               DoubleToString(TP,digits));
//--- все готово, отправляем на сервер отложенный ордер Buy Stop 
   if(!trade.BuyStop(volume,price,symbol,SL,TP,ORDER_TIME_DAY,expiration,comment))
     {
      //--- сообщим о неудаче
      Print("Метод BuyStop() потерпел неудачу. Код возврата=",trade.ResultRetcode(),
            ". Описание кода: ",trade.ResultRetcodeDescription());
     }
   else
     {
      Print("Метод BuyStop() выполнен успешно. Код возврата=",trade.ResultRetcode(),
            " (",trade.ResultRetcodeDescription(),")");
     }

Для отправки ордера SellStop применяется соответствующий метод класса CTrade, главное — правильно указывать цены.

Работа с позицией

Вы можете вместо использования методов Buy() и Sell() пользоваться методами для открытия позиции. Правда, в этом случае придется указать больше деталей:

//--- количество знаков после запятой
   int    digits=(int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS);
//--- значение пункта
   double point=SymbolInfoDouble(_Symbol,SYMBOL_POINT);
//--- получим цену покупки
   double price=SymbolInfoDouble(_Symbol,SYMBOL_ASK);
//--- вычислим и нормализуем уровни SL и TP
   double SL=NormalizeDouble(price-100*point,digits);
   double TP=NormalizeDouble(price+100*point,digits);
//--- заполним комментарий
   string comment="Buy "+_Symbol+" 1 at "+DoubleToString(price,digits);
//--- все готово, делаем попытку открыть позицию на покупку
   if(!trade.PositionOpen(_Symbol,ORDER_TYPE_BUY,1,price,SL,TP,comment))
     {
      //--- сообщим о неудаче
      Print("Метод PositionOpen() потерпел неудачу. Код возврата=",trade.ResultRetcode(),
            ". Описание кода: ",trade.ResultRetcodeDescription());
     }
   else
     {
      Print("Метод PositionOpen() выполнен успешно. Код возврата=",trade.ResultRetcode(),
            " (",trade.ResultRetcodeDescription(),")");
     }

Для закрытия позиции достаточно указать имя инструмента, остальное класс CTrade сделает сам.

//--- закрываем позицию по текущему символу
   if(!trade.PositionClose(_Symbol))
     {
      //--- сообщим о неудаче
      Print("Метод PositionClose() потерпел неудачу. Код возврата=",trade.ResultRetcode(),
            ". Описание кода: ",trade.ResultRetcodeDescription());
     }
   else
     {
      Print("Метод PositionClose() выполнен успешно. Код возврата=",trade.ResultRetcode(),
            " (",trade.ResultRetcodeDescription(),")");
     }

У открытой позиции можно изменять уровни StopLoss и TakeProfit. Это делается с помощью метода ModifyPosition().

//--- количество знаков после запятой
   int    digits=(int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS);
//--- значение пункта
   double point=SymbolInfoDouble(_Symbol,SYMBOL_POINT);
//--- получим текущую цену Bid
   double price=SymbolInfoDouble(_Symbol,SYMBOL_BID);
//--- вычислим и нормализуем уровни SL и TP
   double SL=NormalizeDouble(price-100*point,digits);
   double TP=NormalizeDouble(price+100*point,digits);
//--- все готово, делаем попытку модифицировать позицию на покупку
   if(!trade.PositionModify(_Symbol,SL,TP))
     {
      //--- сообщим о неудаче
      Print("Метод PositionModify() потерпел неудачу. Код возврата=",trade.ResultRetcode(),
            ". Описание кода: ",trade.ResultRetcodeDescription());
     }
   else
     {
      Print("Метод PositionModify() выполнен успешно. Код возврата=",trade.ResultRetcode(),
            " (",trade.ResultRetcodeDescription(),")");
     }

Модификация и удаление ордера

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

//--- проверим наличие ордера  
   if(!OrderSelect(ticket))
     {
      Print("Ордер #",ticket," не найден");
      return;
     }
//--- символ 
   string symbol=OrderGetString(ORDER_SYMBOL);
//--- количество знаков после запятой
   int    digits=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS);
//--- значение пункта
   double point=SymbolInfoDouble(symbol,SYMBOL_POINT);
//--- получим цену открытия
   double price=OrderGetDouble(ORDER_PRICE_OPEN);
//--- вычислим и нормализуем уровни SL и TP
   double SL=NormalizeDouble(price-200*point,digits);
   double TP=NormalizeDouble(price+200*point,digits);
//--- все готово, делаем попытку модифицировать ордер 
//   if(!trade.OrderModify(ticket,price,SL,TP,(ENUM_ORDER_TYPE_TIME)OrderGetInteger(ORDER_TYPE_TIME),0))

   if(!trade.OrderModify(ticket,price,SL,TP,ORDER_TIME_DAY,0))
     {
      //--- сообщим о неудаче
      Print("Метод OrderModify() потерпел неудачу. Код возврата=",trade.ResultRetcode(),
            ". Описание кода: ",trade.ResultRetcodeDescription());
     }
   else
     {
      Print("Метод OrderModify() выполнен успешно. Код возврата=",trade.ResultRetcode(),
            " (",trade.ResultRetcodeDescription(),")");
     }

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

Для удаления отложенного ордера достаточно знать его тикет:

//--- проверим наличие ордера  
   if(!OrderSelect(ticket))
     {
      Print("Ордер #",ticket," не найден");
      return;
     }
//--- все готово, делаем попытку удалить ордер
   if(!trade.OrderDelete(ticket))
     {
      //--- сообщим о неудаче
      Print("Метод OrderDelete() потерпел неудачу. Код возврата=",trade.ResultRetcode(),
            ". Описание кода: ",trade.ResultRetcodeDescription());
     }
   else
     {
      Print("Метод OrderDelete() выполнен успешно. Код возврата=",trade.ResultRetcode(),
            " (",trade.ResultRetcodeDescription(),")");
     }

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


Что еще посмотреть в торговых классах

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

  • COrderInfo — для работы с ордерами;
  • CHistoryOrderInfo — для работы с отработанными ордерами, попавшими в историю торговли;
  • CPositionInfo — для работы с позициями;
  • CDealInfo — для работы со сделками;
  • CTerminalInfo — для получения информации о самом терминале.

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

Начните свой путь в алготрейдинг с простых скриптов

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

Начните с выполнения простых скриптов (https://c.mql5.com/36/9/mql5-moex-first-steps.zip), и вы поймете, что создать торгового робота гораздо проще, чем вы думали!