MQL5 – Торговые операции

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


Предыдущие уроки


MQL5 Программирование: Торговые операции

Ордера

Ордера – это принятые торговым сервером запросы на совершение торговых операций, оформленные в соответствии с требованиями платформы MetaTrader 5. Ордера могут быть немедленного исполнения, например, купить или продать по текущей рыночной цене определенный объем на указанном финансовом инструменте. Другой вид ордеров – отложенные ордера, которые содержат приказ на совершение торговой операции по некоему условию. Отложенные ордера могут также содержать ограничение на время их действия – дату истечения (экспирации).

Действующие (отложенные) ордера, которые находятся в ожидании условий их исполнения или отмены, показываются в терминале в закладке «Торговля». Эти ордера можно модифицировать или отменять. Постановка, отмена и модификация ордеров производится с помощью функции OrderSend(), которую мы рассмотрим сегодня. Если ордер был отменен, или истекло время его действия, или был исполнен, то он перемещается в историю ордеров. Исполненные и отмененные ордера показываются в терминале в закладке «История». Ордера из истории недоступны для модификации, они уже отработали.

Сделки

Сделки – это результат выполнения ордера (приказа на совершение торговой операции). Каждая сделка базируется на одном конкретном ордере, но один ордер может порождать множество сделок. Например, приказ на покупку 10 лотов может быть исполнен с помощью нескольких последовательных сделок при частичном исполнении. Сделки всегда находятся в истории торговли и не могут модифицироваться. В терминале сделки отображаются в закладке «История».

Позиции

Позиции – это наличие купленных или проданных контрактов по финансовому инструменту. Длинная позиция (Long) образуется в результате покупок в ожидании повышения цены, короткая позиция (Short) – результат продажи актива в расчете на снижение цены в будущем. На одном счете по каждому финансовому инструменту может существовать только одна позиция. По каждому символу в любой момент времени может быть только одна открытая позиция – длинная или короткая.

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

Действующие ордера и позиции всегда отображаются на вкладке «Торговля», а сделки и ордера из истории всегда отражаются в закладке «История».

Торговые операции

Теперь разберемся с типами торговых операций. Всего их пять:

  • открытие рыночных позиций;
  • установка отложенных ордеров;
  • модификация Стоп-лосс и Тейк-профит позиции;
  • модификация параметров отложенного ордера;
  • удаление отложенного ордера.

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

Функция onDeinit

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

Коды причины деинициализации эксперта, возвращаемые функцией UninitializeReason(), могут иметь значения от 0 до 9. Напишем функцию, возвращающую строковое описание кода причины деинициализации:

//+------------------------------------------------------------------+
//| Функция возвращает причину деинициализации советника             |
//+------------------------------------------------------------------+
string getUninitReasonText(int reasonCode)
{
   string text="";
   
   switch(reasonCode)
   {
      case REASON_PROGRAM: // 0
      {
         text="Эксперт "+__FILE__+" прекратил свою работу, вызвав функцию ExpertRemove()";
         break;
      }
      case REASON_REMOVE: // 1
      {
         text="Эксперт "+__FILE__+" удален с графика";
         break;
      }
      case REASON_RECOMPILE: // 2
      {
         text="Эксперт "+__FILE__+" перекомпилирован";
         break;
      }
      case REASON_CHARTCHANGE: // 3
      {
         text="Символ или период графика был изменен";
         break;
      }
      case REASON_CHARTCLOSE: // 4
      {
         text="График закрыт";
         break;
      }
      case REASON_PARAMETERS: // 5
      {
         text="Входные параметры были изменены пользователем";
         break;
      }
      case REASON_ACCOUNT: // 6
      {
         text="Активирован другой счет либо произошло переподключение к торговому серверу вследствие изменения настроек счета";
         break;
      }
      case REASON_TEMPLATE: // 7
      {
         text="Применен другой шаблон графика";
         break;
      }
      case REASON_INITFAILED: // 8
      {
         text="Обработчик OnInit() вернул ненулевое значение";
         break;
      }
      default:
      {
         text="Неизвестная причина деинициализации";
      }
   }
   
   return text;
}

Также добавим код в обработчик onDeinit():

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   Print(__FUNCTION__, " - Код причины деинициализации:", reason);
   Print(__FUNCTION__, " - Причина деинициализации:", getUninitReasonText(reason));
}

Торговый запрос

Что такое структуры, мы уже проходили в 8 уроке, посвященном дате и времени.

Структуры MqlTradeRequest и MqlTradeResult служат параметрами для функции OrderSend, которая шлет на сервер торговые приказы. Разберем эти структуры подробнее.

Создадим торговый запрос на продажу по рынку:

MqlTradeRequest trade_request = {0}; // Инициализация структуры торгового запроса
trade_request.action = TRADE_ACTION_DEAL; // Тип – по рынку
   // TRADE_ACTION_PENDING Отложенный ордер
   // TRADE_ACTION_SLTP Модификация TP SL
   // TRADE_ACTION_MODIFY Модификация отложенного ордера
   // TRADE_ACTION_REMOVE Удаление отложенного ордера
   // TRADE_ACTION_CLOSE_BY Закрыть позицию встречной
trade_request.symbol = _Symbol; // Текущий инструмент
trade_request.volume = 0.1; // Объем 0.1 лота
trade_request.price = SymbolInfoDouble(_Symbol, SYMBOL_BID); // Цена bid
trade_request.type = ORDER_TYPE_SELL; // Тип ордера на продажу
trade_request.type_filling = ORDER_FILLING_FOK; // Политика исполнения (немедленно или отмена)

Тут мы видим переменную _Symbol. Это одна из зарезервированных переменных mql5, среди которых есть следующие:

   // _AppliedTo Позволяет узнать в индикаторе тип данных, на которых он расcчитывается
   // _Digits Количество десятичных знаков после запятой
   // _Point Размер пункта текущего инструмента в валюте котировки
   // _LastError Значение последней ошибки
   // _Period Значение таймфрейма текущего графика
   // _RandomSeed Текущее состояние генератора псевдослучайных целых чисел
   // _StopFlag Флаг остановки программы
   // _Symbol Имя символа текущего графика
   // _UninitReason Код причины деинициализации программы
   // _IsX64 Переменная _IsX64 позволяет узнать, в каком терминале запущена MQL5-программа

Общий вид структуры MqlTradeRequest:

   struct MqlTradeRequest
   {
      ENUM_TRADE_REQUEST_ACTIONS action; // Тип выполняемого действия
      ulong magic; // Штамп эксперта (идентификатор magic number)
      ulong order; // Тикет ордера
      string symbol; // Имя торгового инструмента
      double volume; // Запрашиваемый объем сделки в лотах
      double price; // Цена
      double stoplimit; // Уровень StopLimit ордера
      double sl; // Уровень Stop Loss ордера
      double tp; // Уровень Take Profit ордера
      ulong deviation; // Максимально приемлемое отклонение от запрашиваемой цены
      ENUM_ORDER_TYPE type; // Тип ордера
      ENUM_ORDER_TYPE_FILLING type_filling; // Тип ордера по исполнению
      ENUM_ORDER_TYPE_TIME type_time; // Тип ордера по времени действия
      datetime expiration; // Срок истечения ордера (для ордеров типа ORDER_TIME_SPECIFIED)
      string comment; // Комментарий к ордеру
      1ulong position; // Тикет позиции
      ulong position_by; // Тикет встречной позиции
   };

Эта структура MqlTradeRequest и структура MqlTradeResult являются параметрами функции OrderSend():

bool  OrderSend(
   MqlTradeRequest&  request,      // структура запроса
   MqlTradeResult&   result        // структура ответа
   );

Как видите, возвращает она тип bool. В случае успеха вернется true, но это не означает успешного выполнения запроса, а только то, что запрос достиг сервера и был получен ответ. А вот о том, успешно ли был выполнен запрос, мы можем судить как раз по структуре MqlTradeResult.

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

   struct MqlTradeResult
   {
      uint retcode; // Код результата операции
      ulong deal; // Тикет сделки, если она совершена
      ulong order; // Тикет ордера, если он выставлен
      double volume; // Объем сделки, подтверждённый брокером
      double price; // Цена в сделке, подтверждённая брокером
      double bid; // Текущая рыночная цена предложения (цены реквота)
      double ask; // Текущая рыночная цена спроса (цены реквота)
      string comment; // Комментарий брокера к операции (по умолчанию заполняется расшифровкой кода возврата торгового сервера)
      uint request_id; // Идентификатор запроса, устанавливается терминалом при отправке
      uint retcode_external; // Код ответа внешней торговой системы
   };

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

Для получения торговых транзакций, примененных к счету, в MQL5 предусмотрен специальный обработчик OnTradeTransaction(). В первый параметр этого обработчика передается структура MqlTradeTransaction, описывающая торговые транзакции:

struct MqlTradeTransaction
{
    ulong deal; // Тикет сделки
    ulong order; // Тикет ордера
    string symbol; // Имя торгового инструмента, по которому совершена транзакция
    ENUM_TRADE_TRANSACTION_TYPE type; // Тип торговой транзакции
    ENUM_ORDER_TYPE order_type; // Тип торгового ордера
    ENUM_ORDER_STATE order_state; // Состояние торгового ордера
    ENUM_DEAL_TYPE deal_type; // Тип сделки
    ENUM_ORDER_TYPE_TIME time_type; // Тип ордера по времени действия
    datetime time_expiration; // Срок истечения отложенного ордера
    double price; // Цена. В зависимости от типа торговой транзакции может быть ценой ордера, сделки или позиции.
    double price_trigger; // Цена срабатывания стоп-лимитного ордера
    double price_sl; // Уровень Stop Loss
    double price_tp; // Уровень Take Profit
    double volume; // Объем в лотах. В зависимости от типа торговой транзакции может указывать на текущий объем ордера, объем сделки или объем позиции.
    ulong position; // Тикет позиции, на которую повлияла транзакция
    ulong position_by; // Тикет встречной позиции. Используется при закрытии позиции встречной – открытой по тому же инструменту, но в противоположном направлении.
};

Определяющим параметром для анализа поступившей транзакции является ее тип, который передается в поле type. Например, если транзакция является типом TRADE_TRANSACTION_REQUEST (получен результат обработки торгового запроса сервером), то структура имеет только одно заполненное поле type, остальные поля анализировать не нужно. В этом случае можно провести анализ двух дополнительных параметров request и result, которые передаются в обработчик OnTradeTransaction().

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

Давайте напишем функции, которые будут выводить описание структур MqlTradeRequest, MqlTradeResult и MqlTradeTransaction:

//+------------------------------------------------------------------+
//| Возвращает текстовое описание транзакции                         |
//+------------------------------------------------------------------+
string TransactionDescription(const MqlTradeTransaction &trans)
{
   string desc=EnumToString(trans.type)+"\r\n";
   desc+="Symbol: "+trans.symbol+"\r\n";
   desc+="Deal ticket: "+(string)trans.deal+"\r\n";
   desc+="Deal type: "+EnumToString(trans.deal_type)+"\r\n";
   desc+="Order ticket: "+(string)trans.order+"\r\n";
   desc+="Order type: "+EnumToString(trans.order_type)+"\r\n";
   desc+="Order state: "+EnumToString(trans.order_state)+"\r\n";
   desc+="Order time type: "+EnumToString(trans.time_type)+"\r\n";
   desc+="Order expiration: "+TimeToString(trans.time_expiration)+"\r\n";
   desc+="Price: "+StringFormat("%G",trans.price)+"\r\n";
   desc+="Price trigger: "+StringFormat("%G",trans.price_trigger)+"\r\n";
   desc+="Stop Loss: "+StringFormat("%G",trans.price_sl)+"\r\n";
   desc+="Take Profit: "+StringFormat("%G",trans.price_tp)+"\r\n";
   desc+="Volume: "+StringFormat("%G",trans.volume)+"\r\n";
   desc+="Position: "+(string)trans.position+"\r\n";
   desc+="Position by: "+(string)trans.position_by+"\r\n";
   //--- вернем полученную строку
   return desc;
}

//+------------------------------------------------------------------+
//| Возвращает текстовое описание торгового запроса                  |
//+------------------------------------------------------------------+
string RequestDescription(const MqlTradeRequest &request)
{
   string desc=EnumToString(request.action)+"\r\n";
   desc+="Symbol: "+request.symbol+"\r\n";
   desc+="Magic Number: "+StringFormat("%d",request.magic)+"\r\n";
   desc+="Order ticket: "+(string)request.order+"\r\n";
   desc+="Order type: "+EnumToString(request.type)+"\r\n";
   desc+="Order filling: "+EnumToString(request.type_filling)+"\r\n";
   desc+="Order time type: "+EnumToString(request.type_time)+"\r\n";
   desc+="Order expiration: "+TimeToString(request.expiration)+"\r\n";
   desc+="Price: "+StringFormat("%G",request.price)+"\r\n";
   desc+="Deviation points: "+StringFormat("%G",request.deviation)+"\r\n";
   desc+="Stop Loss: "+StringFormat("%G",request.sl)+"\r\n";
   desc+="Take Profit: "+StringFormat("%G",request.tp)+"\r\n";
   desc+="Stop Limit: "+StringFormat("%G",request.stoplimit)+"\r\n";
   desc+="Volume: "+StringFormat("%G",request.volume)+"\r\n";
   desc+="Comment: "+request.comment+"\r\n";
   //--- вернем полученную строку
   return desc;
}

//+------------------------------------------------------------------+
//| Возвращает текстовое описание результата обработки запроса       |
//+------------------------------------------------------------------+
string TradeResultDescription(const MqlTradeResult &result)
{
   string desc="Retcode "+(string)result.retcode+"\r\n";
   desc+="Request ID: "+StringFormat("%d",result.request_id)+"\r\n";
   desc+="Order ticket: "+(string)result.order+"\r\n";
   desc+="Deal ticket: "+(string)result.deal+"\r\n";
   desc+="Volume: "+StringFormat("%G",result.volume)+"\r\n";
   desc+="Price: "+StringFormat("%G",result.price)+"\r\n";
   desc+="Ask: "+StringFormat("%G",result.ask)+"\r\n";
   desc+="Bid: "+StringFormat("%G",result.bid)+"\r\n";
   desc+="Comment: "+result.comment+"\r\n";
   //--- вернем полученную строку
   return desc;
}

Теперь можно доработать обработчик OnTradeTransaction():

//+------------------------------------------------------------------+
//| TradeTransaction function                                        |
//+------------------------------------------------------------------+
void OnTradeTransaction(const MqlTradeTransaction& trans,
                        const MqlTradeRequest& request,
                        const MqlTradeResult& result)
{
   //--- получим тип транзакции в виде значения перечисления
   ENUM_TRADE_TRANSACTION_TYPE type = (ENUM_TRADE_TRANSACTION_TYPE)trans.type;
   //--- если транзакция является результатом обработки запроса, выведем только её название
   if (type == TRADE_TRANSACTION_REQUEST) {
      Print(EnumToString(type));
      //--- выведем строковое описание обработанного запроса
      Print("------------RequestDescription\r\n",RequestDescription(request));
      //--- выведем описание результата запроса
      Print("------------ResultDescription\r\n",TradeResultDescription(result));
      //--- запомним тикет ордера для его удаления на следующей обработке в OnTick()
      if (result.order!=0) {
         //--- удалим этот ордер по его тикету при следующем вызове OnTick()
         order_ticket=result.order;
         Print(" Тикет отложенного ордера ",order_ticket,"\r\n");
      }
   }
   else {   // для транзакций другого типа выведем полное описание
      //--- выведем описание полученной транзакции в Журнал
      Print("------------TransactionDescription\r\n",TransactionDescription(trans));
   }
}

Класс CTrade

Подключим класс CTrade:

input int MagicNumber = 1234567;

//--- подключим торговый класс CTrade и объявим переменную этого типа
#include <Trade\Trade.mqh>
CTrade trade;

//--- флаги для установки и удаления отложенного ордера
bool pending_done=false;
bool pending_deleted=false;
//--- здесь будем хранить тикет отложенного ордера
ulong order_ticket;

Применим его в обработчике onInit:

//+------------------------------------------------------------------+
//| Expert initialization function                                                     |
//+------------------------------------------------------------------+
int OnInit()
{
   //--- установим MagicNumber, которым будут помечаться все наши ордера
   trade.SetExpertMagicNumber(MagicNumber);
   //--- торговые запросы будем отправлять в асинхронном режиме с помощью функции OrderSendAsync()
   trade.SetAsyncMode(true);
   //--- инициализируем переменную нулем
   order_ticket=0;
   return(INIT_SUCCEEDED);
}

И пример применения класса в обработчике onTick:

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
   //--- установка отложенного ордера
   if(!pending_done)
   {
      double ask = SymbolInfoDouble(_Symbol,SYMBOL_ASK);
      double buy_stop_price = NormalizeDouble(ask+1000*_Point,(int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS));
      bool res=trade.BuyStop(0.1,buy_stop_price,_Symbol);
      
      //--- если функция BuyStop() отработала успешно
      if (res) {
         pending_done=true;
         //--- получим результат отправки запроса из ctrade
         MqlTradeResult trade_result;
         trade.Result(trade_result);
         //--- получим request_id для отправленного запроса
         uint request_id=trade_result.request_id;
         Print("Отправлен запрос на установку отложенного ордера. Идентификатор запроса Request_ID=",request_id);
         //--- запомним тикет ордера (при использовании асинхронного режима отправки в CTrade будет равен нулю)
         order_ticket=trade_result.order;
         //--- всё сделано, поэтому досрочно выходим из обработчика OnTick()
         return;
      }
   }
   //--- удаление отложенного ордера
   if (!pending_deleted) {
      //--- дополнительная проверка
      if (pending_done && (order_ticket!=0)) {
         //--- попытаемся удалить отложенный ордер
         bool res=trade.OrderDelete(order_ticket);
         Print("OrderDelete=",res);
         //--- при успешной отправке запроса на удаление
         if (res) {
            pending_deleted=true;
            //--- получим результат выполнения запроса
            MqlTradeResult trade_result;
            trade.Result(trade_result);
            //--- вытащим из результата идентификатор запроса
            uint request_id=trade_result.request_id;
            //--- выведем в Журнал
            Print("Отправлен запрос на удаление отложенного ордера #",order_ticket,
            ". Идентификатор запроса Request_ID=",request_id,
            "\r\n");
            //--- запишем из результата запроса тикет ордера
            order_ticket=trade_result.order;
         }
      }
   }
}

Торговля без использования сторонних библиотек

Теперь напишем метод, который открывает сделку на продажу по рынку:

//+------------------------------------------------------------------+
//| Продажа по рынку                                                 |
//+------------------------------------------------------------------+
void marketSell()
{
   //--- сбросим код последней ошибки в ноль
   ResetLastError();
   MqlTradeRequest trade_request = {}; // Инициализация структуры торгового запроса
   /*
   struct MqlTradeRequest
   {
      ENUM_TRADE_REQUEST_ACTIONS action; // Тип выполняемого действия
      ulong magic; // Штамп эксперта (идентификатор magic number)
      ulong order; // Тикет ордера
      string symbol; // Имя торгового инструмента
      double volume; // Запрашиваемый объем сделки в лотах
      double price; // Цена
      double stoplimit; // Уровень StopLimit ордера
      double sl; // Уровень Stop Loss ордера
      double tp; // Уровень Take Profit ордера
      ulong deviation; // Максимально приемлемое отклонение от запрашиваемой цены
      ENUM_ORDER_TYPE type; // Тип ордера
      ENUM_ORDER_TYPE_FILLING type_filling; // Тип ордера по исполнению
      ENUM_ORDER_TYPE_TIME type_time; // Тип ордера по времени действия
      datetime expiration; // Срок истечения ордера (для ордеров типа ORDER_TIME_SPECIFIED)
      string comment; // Комментарий к ордеру
      1ulong position; // Тикет позиции
      ulong position_by; // Тикет встречной позиции
   };
   */
   trade_request.action = TRADE_ACTION_DEAL; // Тип – по рынку
   // TRADE_ACTION_PENDING Отложенный ордер
   // TRADE_ACTION_SLTP Модификация TP SL
   // TRADE_ACTION_MODIFY Модификация отложенного ордера
   // TRADE_ACTION_REMOVE Удаление отложенного ордера
   // TRADE_ACTION_CLOSE_BY Закрыть позицию встречной
   trade_request.symbol = _Symbol; // Текущий инструмент
   // _AppliedTo Позволяет узнать в индикаторе тип данных, на которых он расcчитывается
   // _Digits Количество десятичных знаков после запятой
   // _Point Размер пункта текущего инструмента в валюте котировки
   // _LastError Значение последней ошибки
   // _Period Значение таймфрейма текущего графика
   // _RandomSeed Текущее состояние генератора псевдослучайных целых чисел
   // _StopFlag Флаг остановки программы
   // _Symbol Имя символа текущего графика
   // _UninitReason Код причины деинициализации программы
   // _IsX64 Переменная _IsX64 позволяет узнать, в каком терминале запущена MQL5-программа
   trade_request.volume = 0.1; // Объем 0.1 лота
   trade_request.price = SymbolInfoDouble(_Symbol, SYMBOL_BID); // Цена bid
   trade_request.type = ORDER_TYPE_SELL; // Тип ордера на продажу
   trade_request.type_filling = ORDER_FILLING_FOK; // Политика исполнения (немедленно или отмена)
   
   MqlTradeResult trade_result = {}; // Инициализация структуры результата
   /*
   struct MqlTradeResult
   {
      uint retcode; // Код результата операции
      ulong deal; // Тикет сделки, если она совершена
      ulong order; // Тикет ордера, если он выставлен
      double volume; // Объем сделки, подтверждённый брокером
      double price; // Цена в сделке, подтверждённая брокером
      double bid; // Текущая рыночная цена предложения (цены реквота)
      double ask; // Текущая рыночная цена спроса (цены реквота)
      string comment; // Комментарий брокера к операции (по умолчанию заполняется расшифровкой кода возврата торгового сервера)
      uint request_id; // Идентификатор запроса, устанавливается терминалом при отправке
      uint retcode_external; // Код ответа внешней торговой системы
   };
   */
   MqlTradeCheckResult check_result = {}; // Инициализация структуры проверки запроса
   bool res_check = OrderCheck(trade_request, check_result);
   if (!res_check) {
   
      Print(__FUNCTION__, " - результат проверки отрицательный, код ответа:", check_result.retcode);
      Print(__FUNCTION__, " - расшифровка кода ответа:", checkResultRetcodeText(check_result.retcode));
      // Анализируем check_result
      /*
         struct MqlTradeCheckResult
         {
            uint retcode; // Код ответа
            double balance; // Баланс после совершения сделки
            double equity; // Эквити после совершения сделки
            double profit; // Плавающая прибыль
            double margin; // Маржевые требования
            double margin_free; // Свободная маржа
            double margin_level; // Уровень маржи
            string comment; // Комментарий к коду ответа (описание ошибки)
         };
      */
   }
   bool res_send = OrderSendAsync(trade_request, trade_result); // Отправка торгового запроса
   if (!res_send) {
      uint answer = trade_result.retcode;
      Print(__FUNCTION__, " - результат отправки отрицательный, код ответа:", answer);
      Print(__FUNCTION__, " - расшифровка кода ответа:", sendResultRetcodeText(answer));
   }
}

string checkResultRetcodeText(int retcode)
{
   switch (retcode) {
      case TRADE_RETCODE_REQUOTE: // 10004
      {
         return "Реквота";
      }
      default:
      {
         return "Другая причина";
      }
   }
}

string sendResultRetcodeText(int retcode)
{
   switch (retcode) {
      case TRADE_RETCODE_REQUOTE: // 10004
      {
         return "Реквота";
      }
      default:
      {
         return "Другая причина";
      }
   }
}

Методы checkResultRetcodeText и sendResultRetcodeText, возвращающие текстовые описания кода возврата функций OrderSend и OrderCheck, дописаны не полностью, они остаются в качестве домашнего задания.

Также у нас появилась новая структура MqlTradeCheckResult:

         struct MqlTradeCheckResult
         {
            uint retcode; // Код ответа
            double balance; // Баланс после совершения сделки
            double equity; // Эквити после совершения сделки
            double profit; // Плавающая прибыль
            double margin; // Маржевые требования
            double margin_free; // Свободная маржа
            double margin_level; // Уровень маржи
            string comment; // Комментарий к коду ответа (описание ошибки)
         };
Рекомендуется самостоятельно проверить запрос перед отправкой его торговому серверу. Для проверки запроса существует функция OrderCheck():
bool OrderCheck(
    MqlTradeRequest& request, // Указатель на структуру типа MqlTradeRequest, которая описывает требуемое торговое действие
    MqlTradeCheckResult& result // Указатель на структуру типа MqlTradeCheckResult, в которую будет помещен результат проверки
);

Функция не только проверит достаточность средств для совершения торговой операции, но и вернет в результатах проверки торгового запроса многие другие полезные параметры:

  1. код возврата, который сообщит об ошибке в проверяемом запросе;
  2. значение баланса, которое будет после выполнения торговой операции;
  3. значение собственных средств, которое будет после выполнения торговой операции;
  4. значение плавающей прибыли, которое будет после выполнения торговой операции;
  5. размер маржи, необходимый для требуемой торговой операции;
  6. размер свободных собственных средств, которые останутся после выполнения требуемой торговой операции;
  7. уровень маржи, который установится после выполнения требуемой торговой операции;
  8. комментарий к коду ответа, описание ошибки.

В случае нехватки средств или ошибочно заполненных параметров функция возвращает false. В случае успешной базовой проверки структур (проверка указателей) возвращается true – это не является свидетельством того, что запрашиваемая торговая операция непременно выполнится успешно. Для получения подробного описания результата выполнения функции следует анализировать поля структуры result. Чтобы получить информацию об ошибке, необходимо вызвать функцию GetLastError().

По аналогии напишем функцию, выставляющую отложенный лимитный ордер на покупку, и функцию, модифицирующую параметры отложенного ордера:

//+------------------------------------------------------------------+
//| Отложенная покупка buy limit                                     |
//+------------------------------------------------------------------+
void buyLimit()
{
   MqlTradeRequest trade_request = {}; // Инициализация структуры торгового запроса
   trade_request.action = TRADE_ACTION_PENDING; // Тип – отложенный
   trade_request.symbol = _Symbol; // Текущий инструмент
   trade_request.volume = 0.1; // Объем 0.1 лота
   trade_request.price = SymbolInfoDouble(_Symbol, SYMBOL_ASK); // Цена ask
   trade_request.sl = trade_request.price - 500 * _Point;
   trade_request.tp = trade_request.price + 500 * _Point;
   trade_request.type = ORDER_TYPE_BUY_LIMIT; // Тип ордера на покупку
   trade_request.type_filling = ORDER_FILLING_FOK; // Политика исполнения
   trade_request.expiration = ORDER_TIME_GTC; // Срок действия ордера (до отмены)
   // ORDER_TIME_GTC - Ордер будет находиться в очереди до тех пор, пока не будет снят
   // ORDER_TIME_DAY - Ордер будет действовать только в течение текущего торгового дня
   // ORDER_TIME_SPECIFIED - Ордер будет действовать до даты истечения
   // ORDER_TIME_SPECIFIED_DAY - Ордер будет действовать до 23:59:59 указанного дня. Если это время не попадает на торговую сессию, истечение наступит в ближайшее торговое время.
   
   MqlTradeResult trade_result = {0}; // Инициализация структуры результата
   bool res_send = OrderSend(trade_request, trade_result); // Отправка торгового запроса

}
//+------------------------------------------------------------------+
//| Модификация отложенного ордера на покупку buy limit              |
//+------------------------------------------------------------------+
void buyLimitModify()
{
   // 1. Создаем запрос
   MqlTradeRequest trade_request = {}; // Инициализация структуры торгового запроса
   trade_request.action = TRADE_ACTION_MODIFY; // Тип – изменение параметров
   trade_request.order = 123456; 
   trade_request.price = SymbolInfoDouble(_Symbol, SYMBOL_ASK) - 250 * _Point; // Цена
   trade_request.sl = trade_request.price - 600 * _Point;
   trade_request.tp = trade_request.price + 800 * _Point;
   trade_request.type = ORDER_TYPE_BUY_LIMIT; // Тип ордера на покупку
   trade_request.expiration = ORDER_TIME_GTC; // Срок действия ордера (до отмены)
   
   // 2. Отправить торговый приказ
   MqlTradeResult trade_result = {}; // Инициализация структуры результата
   bool res_send = OrderSend(trade_request, trade_result); // Отправка торгового запроса
   
}

Заключение

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

В качестве домашнего задания предлагаю написать аналогичные разобранным в уроке функции для покупок по рынку, а также для отложенного лимитного ордера на продажу и отложенных стоп-ордеров. В функциях buyLimit и buyLimitModify стоит предусмотреть проверку при помощи функции OrderCheck, как мы делали это в функции marketSell. Кроме того, в уроке мы не дописали функции checkResultRetcodeText и sendResultRetcodeText.

На этом все, всем пока!

Тема на форуме

С уважением, Дмитрий аkа Silentspec
Tlap.com

Уроки по MQL5 , , ,