MetaQuotes Software
MetaQuotes Software Блог компании MetaQuotes Software
08 августа 2018, 11:19

Алготрейдинг проще, чем вы думаете

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

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

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

  1. Что торговать?
  2. Когда торговать?
  3. Как торговать?


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

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

А третий вопрос на этом фоне совсем простой — как совершать операции покупки и продажи на данном языке программирования?

Сейчас мы рассмотрим каким образом можно реализовать торговые операции в алгоритмической торговле на языке MQL5.

Алготрейдинг проще, чем вы думаете

 

Что предлагает MQL5 для алготрейдинга?

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

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

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

Но это уже другая тема и вы можете посмотреть примеры реализации в многочисленных статьях.


Торговые операции — это просто!

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

  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 для проверки торгового счета

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

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

#include <Trade\AccountInfo.mqh>
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- объект для работы со счетом
CAccountInfo account;
//--- получим номер счета, на котором запущен советник
   long login=account.Login();
   Print("Login=",login);
//--- выясним тип счета
   ENUM_ACCOUNT_TRADE_MODE account_type=account.TradeMode();
//--- если счет оказался реальным, прекращаем работу эксперта немедленно!
   if(account_type==ACCOUNT_TRADE_MODE_REAL)
     {
      MessageBox("Работа на реальном счете запрещена, выходим","Эксперт запущен на реальном счете!");
      return(-1);
     }
//--- выведем тип счета    
   Print("Тип счета: ",EnumToString(account_type));
//--- выясним, можно ли вообще торговать на данном счете
   if(account.TradeAllowed())
      Print("Торговля на данном счете разрешена");
   else
      Print("Торговля на счете запрещена: возможно, вход был совершен по инвест-паролю");
//--- выясним, разрешено ли торговать на счете с помощью эксперта
   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("Balance=",account.Balance(),"  Profit=",account.Profit(),"   Equity=",account.Equity());
   Print(__FUNCTION__,"  completed"); //---
   return(0);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---

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

  }

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

Результат запуска советника на конкурсном счете Automated Trading Championship 2012 показан на картинке.

Алготрейдинг проще, чем вы думаете
 

CSymbolInfo поможет нам получить свойства символа

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

Мы приведем в примере только небольшую их часть.

#include<Trade\SymbolInfo.mqh>
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- объект для получения свойств символа
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("StopsLevel=",symbol_info.StopsLevel()," pips, FreezeLevel=",
         symbol_info.FreezeLevel()," pips");
//--- получим количество знаков после запятой и размер пункта
   Print("Digits=",symbol_info.Digits(),
         ", Point=",DoubleToString(symbol_info.Point(),symbol_info.Digits()));
//--- информация о спреде
   Print("SpreadFloat=",symbol_info.SpreadFloat(),", Spread(текущий)=",
         symbol_info.Spread()," pips");
//--- запросим тип исполнения ордеров, нет ли ограничений
   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(),
         " (",symbol_info.CurrencyBase(),")");
//--- минимальный, максимальный размеры объема в торговых операциях
   Print("Volume info: LotsMin=",symbol_info.LotsMin(),"  LotsMax=",symbol_info.LotsMax(),
         "  LotsStep=",symbol_info.LotsStep());
//--- 
   Print(__FUNCTION__,"  completed");
//---
   return(0);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---

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

  }

И на рисунке показаны свойства символа EURUSD на Чемпионате Automated Trading Championship. Таким образом, вы готовы перейти непосредственно к торговле.

Алготрейдинг проще, чем вы думаете

CTrade — удобный класс для торговых операций

Торговля в 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);
  }

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

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

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

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

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

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

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

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

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

//--- 3. пример покупки по указанному символу символу с заданными SL и TP
   double volume=0.1;         // укажем объем торговой операции
   string symbol="GBPUSD";    // укажем символ, на котором проводится операция
   int    digits=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS); // количество знаков после запятой
   double point=SymbolInfoDouble(symbol,SYMBOL_POINT);         // пункт
   double bid=SymbolInfoDouble(symbol,SYMBOL_BID);             // текущая цена для закрытия LONG
   double SL=bid-1000*point;                                   // ненормализованное значение SL
   SL=NormalizeDouble(SL,digits);                              // нормализуем Stop Loss
   double TP=bid+1000*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(),")");
     }

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

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

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

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

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

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

//--- 2. пример установки отложенного ордера BuyLimit со всеми параметрами
   double volume=0.1;
   string symbol="GBPUSD";    // укажем символ, на котором выставляется ордер
   int    digits=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS); // количество знаков после запятой
   double point=SymbolInfoDouble(symbol,SYMBOL_POINT);         // пункт
   double ask=SymbolInfoDouble(symbol,SYMBOL_ASK);             // текущая цена покупки
   double price=1000*point;                                    // ненормализованное цена открытия
   price=NormalizeDouble(price,digits);                        // нормализуем цену открытия
   int SL_pips=300;                                            // Stop Loss в пунктах
   int TP_pips=500;                                            // 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_GTC,expiration,comment))
     {
      //--- сообщим о неудаче
      Print("Метод BuyLimit() потерпел неудачу. Код возврата=",trade.ResultRetcode(),
            ". Описание кода: ",trade.ResultRetcodeDescription());
     }
   else
     {
      Print("Метод BuyLimit() выполнен успешно. Код возврата=",trade.ResultRetcode(),
            " (",trade.ResultRetcodeDescription(),")");
     }

Ваша задача во втором варианте — правильно указать уровни SL и TP. Не забывайте, что для покупок уровень Take Profit должен быть выше цены открытия, а уровень Stop Loss — ниже цены открытия. Для ордеров Sell Limit всё наоборот.

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

 

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

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

Простой вариант:

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

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

//--- 2. пример установки отложенного ордера Buy Stop со всеми параметрами
   double volume=0.1;
   string symbol="USDJPY";    // укажем символ, на котором выставляется ордер
   int    digits=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS); // количество знаков после запятой
   double point=SymbolInfoDouble(symbol,SYMBOL_POINT);         // пункт
   double ask=SymbolInfoDouble(symbol,SYMBOL_ASK);             // текущая цена покупки
   double price=1000*point;                                    // ненормализованное цена открытия
   price=NormalizeDouble(price,digits);                        // нормализуем цену открытия
   int SL_pips=300;                                            // Stop Loss в пунктах
   int TP_pips=500;                                            // 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_GTC,expiration,comment))
     {
      //--- сообщим о неудаче
      Print("Метод BuyStop() потерпел неудачу. Код возврата=",trade.ResultRetcode(),
            ". Описание кода: ",trade.ResultRetcodeDescription());
     }
   else
     {
      Print("Метод BuyStop() выполнен успешно. Код возврата=",trade.ResultRetcode(),
            " (",trade.ResultRetcodeDescription(),")");
     }

Для отправки ордера Sell Stop применяется соответствующий метод класса 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-1000*point,digits);
   double TP=NormalizeDouble(price+1000*point,digits);
//--- заполним комментарий
   string comment="Buy "+_Symbol+" 0.1 at "+DoubleToString(price,digits);
//--- все готово, делаем попытку открыть позицию на покупку
   if(!trade.PositionOpen(_Symbol,ORDER_TYPE_BUY,0.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(),")");
     }

Для модификации у открытой позиции доступны только уровни Stop Loss и Take Profit. Это делается с помощью метода PositionModify().

//--- количество знаков после запятой
   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-1000*point,digits);
   double TP=NormalizeDouble(price+1000*point,digits);
//--- все готово, делаем попытку модифицировать позицию на покупку
   if(!trade.PositionModify(_Symbol,SL,TP))
     {
      //--- сообщим о неудаче
      Print("Метод PositionModify() потерпел неудачу. Код возврата=",trade.ResultRetcode(),
            ". Описание кода: ",trade.ResultRetcodeDescription());
     }
   else
     {
      Print("Метод PositionModify() выполнен успешно. Код возврата=",trade.ResultRetcode(),
            " (",trade.ResultRetcodeDescription(),")");
     }

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

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

//--- тикет ордера указан только для примера, его нужно получить
   ulong ticket=1234556;
//--- символ также указан для примера, его нужно получить
   string symbol="EURUSD";
//--- количество знаков после запятой
   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-1000*point,digits);
   double TP=NormalizeDouble(price+1000*point,digits);
   //--- зададим срок действия одни сутки
   datetime expiration=TimeTradeServer()+PeriodSeconds(PERIOD_D1);   
//--- все готово, делаем попытку модифицировать ордер 
   if(!trade.OrderModify(ticket,price,SL,TP,ORDER_TIME_GTC,expiration))
     {
      //--- сообщим о неудаче
      Print("Метод OrderModify() потерпел неудачу. Код возврата=",trade.ResultRetcode(),
            ". Описание кода: ",trade.ResultRetcodeDescription());
     }
   else
     {
      Print("Метод OrderModify() выполнен успешно. Код возврата=",trade.ResultRetcode(),
            " (",trade.ResultRetcodeDescription(),")");
     }

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

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

//--- тикет ордера указан только для примера, его нужно получить
   ulong ticket=1234556;
//--- все готово, делаем попытку модифицировать позицию на покупку
   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. Большинство новичков в алготрейдинге начинают с изучения простых классических торговых систем, например, сигналов на пересечении скользящих средних.

Для этого вам потребуется научиться работать с техническими индикаторами — как их создавать и как их использовать в своем торговом роботе.

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

Чтобы сразу получить представление о том, как работать с индикаторами и начать их использовать, смотрите статью MQL5 для «чайников»: Получение значений технических индикаторов в своих экспертах.

 

Делайте сложные вещи простыми

В любом новом деле первые трудности по прошествии небольшого отрезка времени оказываются самыми простыми вопросами, которые вам приходилось и приходится решать.

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

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

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

Поверьте, создать торгового робота на MQL5 гораздо проще, чем выучить иностранный язык или следовать за трендом!

Приложенные файлы:
56 Комментариев
  • Replikant_mih
    08 августа 2018, 12:18

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

     

     

    • Boris Litvinov
      08 августа 2018, 19:12
      Replikant_mih, вам то же полезно смотреть в сторону MT5.
      По тому как альтернативы по сути лишь собственная разработка! 
      Но не как не известные глючные API с которым лишь потеря времени
  • robomakerr
    08 августа 2018, 14:42
    MetaQuotes, вы про Easy Language что-нибудь слышали? А ваше «проще» сильно напоминает WinApi 90-х годов)
    • Константин
      08 августа 2018, 17:08

      robomakerr, WinApi было продумано и ошибок как правило в релизах не было, тут же проблема на проблеме с новыми релизами, на днях пришлось старый код робота модернизировать, так он стал крашить память, в итоге пришлось отказаться от структур в пользу мизерного класса, времени на разбор просто жалко


      при чем на сколько я понял проблема с ArrayInitialize и(или) ArrayResize при частом вызове

      а еще проблема с чартами и GUI, если объектов данных (не на чарте) слишком много, то система просто не успевает все поудалять и в итоге ппц какой то ))

      детских болячек масса

        • Константин
          08 августа 2018, 19:16
          MetaQuotes Software, это билд 1881, проблема я так понял с доступом в структуре, в прежней версии робота была простая структура с методом перегрузки = и конструктором копирования, я добавил лишь в нее динамические массивы и добавил методы распределения памяти под них и инициализации 0 на замену ZeroMemory(), и появилась эта проблема
          забейте. я классом воспользовался и все реализовал через указатели ))
      • robomakerr
        08 августа 2018, 18:53
        MetaQuotes Software, всё это так. Только сделать робота на EL — это десяток строчек интуитивно понятного кода. А сделать робота на MQL5 — это бесконечные пляски с бубном.
        Я не критикую, не надо от меня отбиваться. Я подсказываю, в какую сторону было бы правильнее развиваться.
  • Boris Litvinov
    08 августа 2018, 18:41
    Почти добил основы С++, собираюсь вступить в ряды MT5
    Интересно, процедурным программированием можно написать торгового бота? 
    • Константин
      08 августа 2018, 19:21
      Борис Литвинов, наверное вы сильно ужаснетесь начав писать сложный код на MQL5, в отличии от С++, где куча продуманных библиотек и есть описанные стандарты языка ))
      а лучше связка Python <---> С++, сейчас сам этим занимаюсь ))
        • Константин
          08 августа 2018, 19:38
          MetaQuotes Software, этим сейчас и занят )) но напрягает то, что вы как разработчики не прислушиваетесь к мнению пользователей, как пример я выше писал про структуру MqlTick, почему в нее не включили поле id сделок? я понимаю, что у вас внутренний учет на серверах МТ5 — id сделок, ордеров и позиций свои, а не с биржи, и теперь не понятно как это поле вставить в структуру и зачем оно будет там нужно, но зачем нужно было это делать? к примеру собрать аггрегированные сделки не возможно в МТ5 без подтяжки данных из вне и это не просто будет костыль, а целые ходули ))
          и вообще не пойму, зачем нужно было создавать копию С++, только с проблемами? из-за мнимой безопасности в окружении терминала? но ведь это не решает проблему, реверс через ассемблер ни кто не отменял ))

          ps. если чем то оскорбил, приношу извинения заранее, но просто уже надоело подпрыгивать из-за проблем с каждым новым обновлением МТ5, стабильности нет, стандарты вы не хотите писать, вот поэтому у вас пополняется (временно) армия начинающих программистов, а потом они начинают понимать через несколько лет, что потратили время зря на МТ5 и в итоге уходят на другие платформы, либо пишут как вы выразились свои костыли, но при этом более функциональные чем МТ5
          в МТ5 есть много качественных решений, но они сводятся к нулю проблемами и ограничениями в MQL5
            • Константин
              08 августа 2018, 20:00
              MetaQuotes Software, популярность только среди начинающих и форексников, биржевики используют в подавляющем большинстве другие платформы
              а насчет прислушиваться — введите многопоточность, решите массу проблем сразу с производительностью и не ссылайтесь на прикладной язык, Python тоже прикладной язык, но в нем многопоточность есть хоть и со скрипом GiL
      • Boris Litvinov
        08 августа 2018, 19:30
        Константин, учу язык для себя. что бы писать свой привод. Но MT5 это лучшее зло из всех зол. Когда читал их решения задач. У них они аналогичны моему восприятию. То есть там по моему мнению не просто программисты а люди осознающие и понимающие рынок! Если бы не стереотипы кухонных шаблонов про них, то с них бы и начинал. 
  • MetaQuotes Software,Алготрейдинг проще, чем вы думаете. Glavnoe — eto algoritm, a torgovat' mozhno i vruchnuyu. A vashe software tol'ko uslozhnyaet torgovlyu, ne reshaya pri etom glavnoi celi algotreidinga
  • Boris Litvinov
    08 августа 2018, 19:21
    Вот смотрю и понимаю. Что за всё время людей которые к трейдингу подход через алго торговлю на СЛ, не добавилось! 
    Одни и те же люди. Вот интересно с чем связана такая тенденция, не ужели тупинизм в РФ побеждает созидателей и творцов?
    • Константин
      08 августа 2018, 19:26
      Борис Литвинов, ну почему же )) вот вам на MQL5 торговый робот

      роботы добавляются постоянно ))


      • Boris Litvinov
        08 августа 2018, 19:36
        Константин, да, но мой посыл в том, что все эти роботы пишут одни и те же. На СЛ новых алго трейдеров практически нет!
        Одни и те же люди развиваются. Остальные получается льют и дальше к папке воровать.
        • Константин
          08 августа 2018, 19:42
          Борис Литвинов, все просто, те кто находят свой грааль, заняты чем то другим, а не просиживанием на СЛ ))
          как найду свой грааль, так же может не буду тут находиться ))
          • Boris Litvinov
            08 августа 2018, 19:57
            Константин, я не про тех кто нашел свой грааль, а про тех кто начинает, и заканчивает в течении 6 месяцев. Таковы цифры статистики папкиных обосранцев. Сливших бабушкину квартиру.
            Немного слукавил, там средний счет маловат. Но среди них есть и те, кто квартиры льет
          • Boris Litvinov
            08 августа 2018, 20:20
            Константин, вам от меня грааль. Грааль заключается в его поиске. Развитие есть грааль. Поймали момент зарабатывайте. И следом поиск другого. Разве нет? Есть люди которые тестируют 10 лет. А нужно уметь взять в моменте. В этом грааль.
    • Константин
      08 августа 2018, 19:45
      MetaQuotes Software, ойойой ))) про AlgLib зря написали, да и в других библиотеках есть проблемы, к примеру в вашей AlgLib матрицы развернуты в отличии от оригинала, есть функции в библиотеках статистики с ошибками, приходилось свои реализации писать, а теперь представьте, я на С++ подключаю нужную библиотеку и работаю, а не изобретаю велосипед, та же сериализация, есть около десятка библиотек хорошо реализованных, а вообще если хочешь что то написать, посмотри в boosts, там наверняка что нить подыщешь из алгоритмов ))
      а про Python даже писать не стоит, на нем прототипизация делается со скоростью звука ))
        • Константин
          08 августа 2018, 20:06
          MetaQuotes Software, странный вы человек )) я написал про ошибку в AlgLib? вроде как про проблему с развернутыми наоборот матрицами )) просто год назад несколько дней убил прежде чем понял куда копать, пришлось матрицы наоборот заполнить в отличии от оригинальной библиотеки
          разве я не указал проблему?
          а в статбиблиотеках уже и не помню что пытался использовать, но в итоге пришлось писать свои реализации, если честно, то лень уже искать т.к. занят другим терминалом ))
          ну и про свои юнит тесты — наверное стоит их пересмотреть и все таки увидеть проблему, библиотеки то выдаете в открытое пользование без достаточных тестов среди сообщества программистов
          • Boris Litvinov
            08 августа 2018, 23:06
            Константин, 
            занят другим терминалом 
            каким если не секрет? 
            • Константин
              09 августа 2018, 04:37
              Борис Литвинов, назвал я его Phoenix ))
            • Константин
              09 августа 2018, 09:27
              MetaQuotes Software, 
              покажите конкретное место с конкретной перевернутой матрицей, пожалуйста.

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

              Кроме того, у нас более 15000 собственных тестов(тестов, а не строк) проводятся над компилятором и системой исполнения.

              впечатляет производительность, но видимо не полные тесты раз при выходе новых билдов у народа в кодах появляются проблемы, как пример проблемы с union, с момента их ввода я код нескольких классов переписывал раза три уже
              да вы и сами знаете, что ваша справка это не принятый стандарт языка, раз вы постоянно меняете поведение сущностей и синтаксис языка, не публикуя этих изменений, народ только наткнувшись на проблему начинает выяснять и иногда с высокого позволения Рената, ответ получают, но чаще заканчивается все банами на форуме
              как думаете, сколько отсеивается после нескольких банов народа?

              ps. хотите обижайтесь, хотите нет, но есть принятое сообществом программистов словосочетание — го… код, судя по объему кода, который вы приводите в пример и судя по проблемам, вы либо не дорабатываете либо пишите го… код а не юнит-тесты
              не вы лично конечно, а кто то в вашей команде
                • Константин
                  09 августа 2018, 10:31
                  MetaQuotes Software, можете считать и дальше так, но я не собираюсь вам выискивать какие то аргументы, что в памяти сразу всплыло то и написал, про AlgLib так же уже несколько раз вам написал, что у меня нет на это сейчас времени, да и не хочу я помогать тем, кто явно не намерен конструктивно подойти к диалогу ))
                  мне достаточно стабильных версий С++ и Python, ошибок там минимум, но для этого есть багрепорт, вольностей ни кто не допускает т.к. четко описаны стандарты и изменения в них, есть много примеров в сети по алгоритмам и коду, что еще нужно для счастья ))
                    • Константин
                      09 августа 2018, 12:23
                      MetaQuotes Software, вы это серьезно про нестабильность С++? если он нестабилен по вашим словам, то mql5 тогда вообще на коленке писан где куча «детских болячек в функциях языка ))
                      а про версии Python так вы вообще зря говорите, на то они и имеют порядковые номера и язык хорошо развивается, 2-я ветка языка существует по моему до 19-го года, дальше не будет поддерживаться т.к. 3-я ветка когда была реализована, то это была именно замена 2-й ветке, у вас то тоже mql5, а не mql, понимаете о чем я?
                      кстати в апреле в Канаде именно Россияне предложили в С++17 идеи из Python т.к. это удобнее и теперь эти идеи закреплены в стандарте, язык то развивается и это хорошо, а не стоит на месте как mql5, который вы передрали с С++ и теперь пытаетесь всем сказать что это чистая ваша разработка не откуда не заимствованная, сами то думаете с чем сравниваете?
                      вы еще форточников обвините за C# в который был так же заимствован синтаксис Си и С++, и то что там реализовали более удобный интерфейс в .NET Framework, это ведь развитие языка
                      косяки есть всегда и везде, но у вас развитие буксует из-за того, что начинаете спорить, а не прислушиваетесь, вон сколько уже нафлеймили тут, а воз и ныне там ))
    • Константин
      08 августа 2018, 20:08
      MetaQuotes Software, ну в целом то молодцы, подняли такую сложную и обширную тему )) интересно сколько вас там трудится

      ps. кстати почему не напишите аналог STL?
        • Константин
          09 августа 2018, 09:34
          MetaQuotes Software, это не аналог STL судя по интерфейсу библиотеки и комментам в соответствующей ветке на форуме
          кроме того, у вас нет operator new, а без этого вы не реализуете аллокаторы правильно, из-за чего страдает производительность

          прим:operator new разделы Стандарта С++
          5.3.4
          The new-expression attempts to create an object of the type-id (8.1) or new-type-id to which it is applied. /*дальше нам не интересно*/
          18.6.1
          void* operator new(std::size_t size) throw(std::bad_alloc);
          Effects: The allocation function called by a new-expression (5.3.4) to allocate size bytes of
          storage suitably aligned to represent any object of that size /*дальше нам не интересно*/

          ps. дальнейший диалог думаю тут не стоит продолжать т.к. я устал что то вам писать, а вы в ответ не принимаете ни чего, приводя свои аргументы, нужно просто признать, что есть проблемы, без них в работе ни куда, но у вас их слишком много и это факт
          еще раз скажу — народ от МТ5 уходит не просто так
            • Константин
              09 августа 2018, 10:24
              MetaQuotes Software, судя по написанному вы зацикливаетесь сами на своем и не читаете то что вам пишут, как пример по operatror new — этого у вас нет, прочтите что я вам дал из стандарта и не путайте operator и new ))
              про популярность МТ5 вы пишите постоянно, но я не знаю нормально торгующих трейдеров с крупными депозитами на этом терминале, с мелкими счетами полно, но это лишь подтверждает то, что я писал выше
                • Константин
                  09 августа 2018, 12:14
                  MetaQuotes Software, странные заявления вы делаете:
                  1.про максимально опасный язык С++
                  2. про пеереопределение new
                  3. про полностью управляемый и защищенный язык mql5

                  по п.1 -  у вас в команде наверное мозахисты, если вы взяли за основу именно этот синтаксис ))
                  по п.2 — вы видимо не понимаете о чем я вам написал про operator new, там не переопределение new или operator, а совершенно другой оператор с другим поведением, если интересно, то прочтите в стандарте про new, operator и operator new, может тогда поймете о чем я говорю, а еще для примера откройте исходник аллокаторов в stl::list
                  я хотел пару лет назад написать библиотеку контейнеров максимально приближенную по синтаксису к STL, но именно этот момент с operator new в аллокаторах не смог обойти
                  по п.3 — вы видимо крутитесь в своем мирке и не знаете, что МТ4 и МТ5 уже реверснули и люди пользуют их, ссылки на это давать не буду, но поройтесь в сети TOP, может тогда поймете что изобретаете велосипед, я имею в виду mql5, а не использование того же C# или Python или Java, про С++ не говорю т.к. он для вас очень ужасный и опасный ))
                  кроме того скомпилированные роботы вскрываются, хотя и дороже это теперь, чем было года 4 назад, говорю же — реверс и ассемблер ни кто не отменял

Активные форумы
Что сейчас обсуждают

Старый дизайн
Старый
дизайн