Скачать MetaTrader 5

Торговые операции на MQL5 - это просто

21 августа 2012, 17:03
MetaQuotes Software Corp.
21
17 256

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

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

  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 гораздо проще, чем выучить иностранный язык или следовать за трендом!


Прикрепленные файлы |
Последние комментарии | Перейти к обсуждению на форуме трейдеров (21)
Николай Осипов
Николай Осипов | 15 янв 2013 в 02:32

Здравствуйте,
При использовании в коде функции BuyStop для торговли фьючерсом RTS-3.13 (RIH3) постоянно возникает ошибка : 
10022 TRADE_RETCODE_INVALID_EXPIRATION - Неверная дата истечения ордера

В спецификации фьючерса  RTS-3.13 указано, что тип ордеров - Действительные до отмены. http://savepic.org/2499182.jpg
Т.е. можно указывать тип ORDER_TIME_GTC - Ордер будет находится в очереди до тех пор, пока не будет снят.
Пробовал использовать другие типы ORDER_TIME_DAY, ORDER_TIME_SPECIFIED, ORDER_TIME_SPECIFIED_DAY  - ошибка не исчезает. 


Часть кода:

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

 В журнале Тестера стратегий записи :

2012.12.18 19:44:59   digits=0 point=1 ask=151830 price=1000
2012.12.18 19:44:59   failed buy stop 1.00 RTS-3.13 at 1000 [Invalid expiration]
2012.12.18 19:44:59   CTrade::OrderSend: buy stop 1.00 RTS-3.13 at 1000 [invalid expiration]
2012.12.18 19:44:59   Метод BuyStop() потерпел неудачу. Код возврата=10022. Описание кода: invalid expiration

В чем м.б. проблема ? Подскажите , пож-та. 
Буду благодарен если ответите на почту nfo52^mail.ru, ICQ UIN: 4002787, skype: osipov52

Спасибо.  

Win7 64 разр.,  МТ5 ,  Демо-счет БД Открытие. 

Rashid Zeynalov
Rashid Zeynalov | 19 фев 2013 в 07:58
Nikos52:

Здравствуйте,
При использовании в коде функции BuyStop для торговли фьючерсом RTS-3.13 (RIH3) постоянно возникает ошибка : 
10022 TRADE_RETCODE_INVALID_EXPIRATION - Неверная дата истечения ордера

В спецификации фьючерса  RTS-3.13 указано, что тип ордеров - Действительные до отмены. http://savepic.org/2499182.jpg
Т.е. можно указывать тип ORDER_TIME_GTC - Ордер будет находится в очереди до тех пор, пока не будет снят.
Пробовал использовать другие типы ORDER_TIME_DAY, ORDER_TIME_SPECIFIED, ORDER_TIME_SPECIFIED_DAY  - ошибка не исчезает. 


Часть кода:

 В журнале Тестера стратегий записи :

В чем м.б. проблема ? Подскажите , пож-та. 
Буду благодарен если ответите на почту nfo52^mail.ru, ICQ UIN: 4002787, skype: osipov52

Спасибо.  

Win7 64 разр.,  МТ5 ,  Демо-счет БД Открытие. 

Аналогичная проблема на RTS-3.13 Открытие-демо, перепробовал все (приэтом лимит ордера ставятся на ORDER_TIME_SPECIFIED_DAY  как написано в профиле символа) !!!! При том что buy-stop ордер отлично открывается в рынке в ручную. Это глюк терминала, надо писать разработчикам
Ruslan Fakhretdinov
Ruslan Fakhretdinov | 17 июл 2013 в 08:36
Ага, прям проще не бывает! Намудрили там кучу всего, и при этом с использованием стандартных библиотек, а если без них, так сказать с нуля??? Самое просто и быстро, качественно запрограммировать советника на MQl4!!! У меня советник на mql4 занимает 70 строк, я переписал его на mql5, и получилось 198!!!!!! Так что название со словом "просто" - не уместно.
Dennis Kirichenko
Dennis Kirichenko | 17 июл 2013 в 08:49
zlodei:
...У меня советник на mql4 занимает 70 строк, я переписал его на mql5, и получилось 198!!!!!! Так что название со словом "просто" - не уместно.
Это если идея советника несложная. Имхо, чем сложнее идея, тем больше будет разница в числе строк кода между MQL4 и MQL5 в пользу последнего. Так что я бы не был столь категоричным...
Pavel Trofimov
Pavel Trofimov | 9 апр 2015 в 21:09

Подскажите, возможна ли реализация вот такой конструкции из MQL4 с использованием m_Trade.PositionModify? (m_Trade - член класса CTrade)

...
if(TrendUp==true) 
   for(int i=0; i<10; i++)
      {
        ticket = OrderSend(Symbol(),OP_BUY,Lots,Ask,30,0,0);
        if(ticket>0 && i<9) OrderModify(ticket,0,0,Ask+SPS*take_koef[i],0);
      };
...

Смысл такой скрипт открывал по рынку покупку заданным количеством лот, и затем выставлял 10-ью частями тейк-профит. Я уперся в то, что без выставления стоп-лосса PositionModify выдает ошибку 10016 (Неправильные стопы в запросе). А задача ставить стоп не стоит :). В справке указано, что вроде можно не указывать значение:

Параметры

symbol

[in]  Наименование торгового инструмента, по которому предполагается модифицировать позицию.

sl

[in]  Новая цена, по которой сработает Stop Loss (либо, если изменение не нужно, предыдущее значение).

tp

[in]  Новая цена, по которой сработает Take Profit (либо, если изменение не нужно, предыдущее значение).

Я пробовал передавать значение полученное из предыдущего запроса на покупку (там ноль естественно), ну и те же грабли только в профиль. Вот мой код:

...
         m_Trade.Buy(Lots);//покупаем по рынку весь объем
         if(m_Trade.ResultRetcode()==10008)//если покупка удачная изменяем позицию, выставляя тейки
           {
            S="Заявка выполнена. Номер ордера: "+IntegerToString(m_Trade.RequestOrder());
            Comment(S);
            sl=m_Trade.RequestSL();//получаем стоп лосс с последнего запроса
            for(int i=0;i<=10;i++)
              {
               if(m_Trade.PositionModify(symbol,sl,Ask+SPS*take_koef[i]))
                 {
                  S+="\nРазмещен тейк: "+IntegerToString(i);
                  Comment(S);
                 }
               else
                 {
                  S+="\nПроизошла ошибка изменения позиции с кодом: "+IntegerToString(m_Trade.ResultRetcode());
                  Comment(S);
                  return(4);
                 };
              };
...

Заранее благодарен за помощь.

Автоматное программирование как новый способ создания автоматических торговых систем Автоматное программирование как новый способ создания автоматических торговых систем

Статья посвящена абсолютно новому направлению в программировании советников, индикаторов, скриптов на MQL4 и MQL5. В будущем данный способ программирования постепенно станет базовым стандартом реализации советников для всех трейдеров. А разработчики языка MQL5 и платформы MetaTrader 5 в будущем смогут в стиле автоматного программирования создать новый язык MQL6 и новую платформу MetaTrader 6.

Интервью с Ириной Коробейниковой (irishka.rf) Интервью с Ириной Коробейниковой (irishka.rf)

Редко можно встретить представительниц прекрасного пола среди участников MQL5.community. Уникальный в своем роде случай подсказал идею небольшого интервью. Ирина Коробейникова (irishka.rf) - пятнадцатилетний программист из Ижевска. На данный момент она является единственной девушкой, которая принимает активное участие в сервисе "Работа" и входит в Топ разработчиков.

MetaQuotes ID в мобильном терминале MetaTrader MetaQuotes ID в мобильном терминале MetaTrader

Телефоны на базе Android и iOS предлагают нам кучу возможностей, о многих из которых мы даже не догадываемся или не пользуемся в полной мере. Одной из таких новинок является технология Push-уведомлений, которая позволяет через Интернет персонально получать сообщения независимо от номера мобильного телефона или оператора сотовой связи. В мобильном терминале MetaTrader уже реализована возможность получать такие сообщения прямо из вашего торгового робота. Для этого достаточно знать идентификатор MetaQuotes ID своего аппарата, и уже более 9 000 000 мобильных терминалов получили его.

Статистический Carry Trading Статистический Carry Trading

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