Библиотека для простого и быстрого создания программ для MetaTrader (Часть XXVII): Работа с торговыми запросами - выставление отложенных ордеров

Artyom Trishkin | 12 декабря, 2019

Содержание

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

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

Это неудобно, так как требует вместо автоматической корректировки значений самой библиотекой, обязательного знания спецификации символа и ввода точных значений вручную прямо в коде программы. Поэтому мы внесём небольшие исправления в логику работы с торговым классом — в советнике в обработчике OnInit() все торгуемые объекты символов — их торговые объекты будут инициализироваться автоматическим выбором корректных значений. В торговые методы торгового класса по умолчанию будут передаваться значения -1 для типов заливки и экспирации ордеров, что будет сигналом к использованию заранее установленных корректных умолчательных значений. Если же из программы в торговый метод будет передано иное значение, то в этом случае будет использовано оно, и в случае, если значение окажется ошибочным, то оно будет скорректировано при обработке ошибок в торговом классе.

Подготовка данных

Помимо исправления торгового класса допишем в класс объекта-отложенного запроса описание запроса — вывод в журнал всех его параметров. Это облегчит нам в дальнейшем тестирование работы с объектами отложенных запросов.
Ну и в первую очередь добавим все необходимые сообщения в массив сообщений библиотеки.

Откроем файл Datas.mqh, и впишем в него индексы новых сообщений:

//+------------------------------------------------------------------+
//| Список индексов текстовых сообщений библиотеки                   |
//+------------------------------------------------------------------+
enum ENUM_MESSAGES_LIB
  {
   MSG_LIB_PARAMS_LIST_BEG=ERR_USER_ERROR_FIRST,      // Начало списка параметров
   MSG_LIB_PARAMS_LIST_END,                           // Конец списка параметров
   MSG_LIB_PROP_NOT_SUPPORTED,                        // Свойство не поддерживается
   MSG_LIB_PROP_NOT_SUPPORTED_MQL4,                   // Свойство не поддерживается в MQL4
   MSG_LIB_PROP_NOT_SUPPORTED_MT5_LESS_2155,          // Свойство не поддерживается в MetaTrader5 версии ниже 2155
   MSG_LIB_PROP_NOT_SUPPORTED_POSITION,               // Свойство не поддерживается у позиции
   MSG_LIB_PROP_NOT_SUPPORTED_PENDING,                // Свойство не поддерживается у отложенного ордера
   MSG_LIB_PROP_NOT_SUPPORTED_MARKET,                 // Свойство не поддерживается у маркет-ордера
   MSG_LIB_PROP_NOT_SUPPORTED_MARKET_HIST,            // Свойство не поддерживается у исторического маркет-ордера
   MSG_LIB_PROP_NOT_SET,                              // Значение не задано
   MSG_LIB_PROP_EMPTY,                                // Отсутствует
   MSG_LIB_PROP_AS_IN_ORDER,                          // В соответствии с режимом истечения ордера
   
   MSG_LIB_SYS_ERROR,                                 // Ошибка

...

   MSG_LIB_TEXT_FAILING_CREATE_PENDING_REQ,           // Не удалось создать отложенный запрос
   MSG_LIB_TEXT_TRY_N,                                // Торговая попытка #
   MSG_LIB_TEXT_RE_TRY_N,                             // Повторная торговая попытка #
   
   MSG_LIB_TEXT_REQUEST_ACTION,                       // Тип выполняемого действия
   MSG_LIB_TEXT_REQUEST_MAGIC,                        // Штамп эксперта (magic number)
   MSG_LIB_TEXT_REQUEST_ORDER,                        // Тикет ордера
   MSG_LIB_TEXT_REQUEST_SYMBOL,                       // Имя торгового инструмента
   MSG_LIB_TEXT_REQUEST_VOLUME,                       // Запрашиваемый объем сделки в лотах
   MSG_LIB_TEXT_REQUEST_PRICE,                        // Цена
   MSG_LIB_TEXT_REQUEST_STOPLIMIT,                    // Уровень StopLimit ордера
   MSG_LIB_TEXT_REQUEST_SL,                           // Уровень Stop Loss ордера
   MSG_LIB_TEXT_REQUEST_TP,                           // Уровень Take Profit ордера
   MSG_LIB_TEXT_REQUEST_DEVIATION,                    // Максимальное отклонение от цены
   MSG_LIB_TEXT_REQUEST_TYPE,                         // Тип ордера
   MSG_LIB_TEXT_REQUEST_TYPE_FILLING,                 // Тип ордера по исполнению
   MSG_LIB_TEXT_REQUEST_TYPE_TIME,                    // Тип ордера по времени действия
   MSG_LIB_TEXT_REQUEST_EXPIRATION,                   // Срок истечения ордера
   MSG_LIB_TEXT_REQUEST_COMMENT,                      // Комментарий к ордеру
   MSG_LIB_TEXT_REQUEST_POSITION,                     // Тикет позиции
   MSG_LIB_TEXT_REQUEST_POSITION_BY,                  // Тикет встречной позиции
   
   MSG_LIB_TEXT_REQUEST_ACTION_DEAL,                  // Поставить рыночный ордер
   MSG_LIB_TEXT_REQUEST_ACTION_PENDING,               // Установить отложенный ордер
   MSG_LIB_TEXT_REQUEST_ACTION_SLTP,                  // Изменить значения Stop Loss и Take Profit у открытой позиции
   MSG_LIB_TEXT_REQUEST_ACTION_MODIFY,                // Изменить параметры ранее установленного торгового ордера
   MSG_LIB_TEXT_REQUEST_ACTION_REMOVE,                // Удалить ранее выставленный отложенный ордер
   MSG_LIB_TEXT_REQUEST_ACTION_CLOSE_BY,              // Закрыть позицию встречной
   MSG_LIB_TEXT_REQUEST_ACTION_UNCNOWN,               // Неизвестный тип торговой операции
   
   MSG_LIB_TEXT_REQUEST_ORDER_FILLING_FOK,            // Ордер исполняется исключительно в указанном объеме, иначе отменяется
   MSG_LIB_TEXT_REQUEST_ORDER_FILLING_IOK,            // Ордер исполняется на доступный объем, неисполненный отменяется
   MSG_LIB_TEXT_REQUEST_ORDER_FILLING_RETURN,         // Ордер исполняется на доступный объем, неисполненный остаётся
   
   MSG_LIB_TEXT_REQUEST_ORDER_TIME_GTC,               // Ордер действителен до явной отмены
   MSG_LIB_TEXT_REQUEST_ORDER_TIME_DAY,               // Ордер действителен только в течение текущего торгового дня
   MSG_LIB_TEXT_REQUEST_ORDER_TIME_SPECIFIED,         // Ордер действителен до даты истечения
   MSG_LIB_TEXT_REQUEST_ORDER_TIME_SPECIFIED_DAY,     // Ордер действителен до 23:59:59 указанного дня
   
   MSG_LIB_TEXT_REQUEST_DATAS,                        // Параметры торгового запроса
   MSG_LIB_TEXT_PEND_REQUEST_DATAS,                   // Параметры отложенного торгового запроса
   MSG_LIB_TEXT_PEND_REQUEST_CREATED,                 // Создан отложенный запрос
   MSG_LIB_TEXT_PEND_REQUEST_DELETED,                 // Отложенный запрос удалён в связи с окончанием времени его действия

   MSG_LIB_TEXT_PEND_REQUEST_PRICE_CREATE,            // Цена в момент создания запроса
   MSG_LIB_TEXT_PEND_REQUEST_TIME_CREATE,             // Время создания запроса
   MSG_LIB_TEXT_PEND_REQUEST_TIME_ACTIVATE,           // Время активации запроса
   MSG_LIB_TEXT_PEND_REQUEST_WAITING,                 // Время ожидания между торговыми попытками
   MSG_LIB_TEXT_PEND_REQUEST_CURRENT_ATTEMPT,         // Текущая торговая попытка
   MSG_LIB_TEXT_PEND_REQUEST_TOTAL_ATTEMPTS,          // Общее количество торговых попыток
   MSG_LIB_TEXT_PEND_REQUEST_ID,                      // Идентификатор торгового запроса
   MSG_LIB_TEXT_PEND_REQUEST_RETCODE,                 // Код возврата, на основании которого создан запрос
   MSG_LIB_TEXT_PEND_REQUEST_TYPE,                    // Тип отложенного запроса
   
   MSG_LIB_TEXT_PEND_REQUEST_BY_ERROR,                // Отложенный запрос, созданный по коду возврата сервера
   MSG_LIB_TEXT_PEND_REQUEST_BY_REQUEST,              // Отложенный запрос, созданный по запросу
   MSG_LIB_TEXT_PEND_REQUEST_WAITING_ONSET,           // Ожидание наступления времени первой торговой попытки
   
  };

и соответствующие индексам тексты сообщений:

//+------------------------------------------------------------------+
string messages_library[][TOTAL_LANG]=
  {
   {"Начало списка параметров","The beginning of the event parameter list"},
   {"Конец списка параметров","End of the parameter list"},
   {"Свойство не поддерживается","Property is not support"},
   {"Свойство не поддерживается в MQL4","Property is not supported in MQL4"},
   {"Свойство не поддерживается в MetaTrader5 версии ниже 2155","The property is not supported in MetaTrader5, build lower than 2155"},
   {"Свойство не поддерживается у позиции","Property not supported for position"},
   {"Свойство не поддерживается у отложенного ордера","The property is not supported for a pending order"},
   {"Свойство не поддерживается у маркет-ордера","The property is not supported for a market-order"},
   {"Свойство не поддерживается у исторического маркет-ордера","The property is not supported for a history market-order"},
   {"Значение не задано","Value not set"},
   {"Отсутствует","Not set"},
   {"В соответствии с режимом истечения ордера","In accordance with the order expiration mode"},
   
   {"Ошибка ","Error"},

...

   {"Не удалось создать отложенный запрос","Failed to create pending request"},
   {"Торговая попытка #","Trading attempt #"},
   {"Повторная торговая попытка #","Retry trading attempt #"},
   
   {"Тип выполняемого действия","Trade operation type"},
   {"Штамп эксперта (magic number)","Expert Advisor ID (magic number)"},
   {"Тикет ордера","Order ticket"},
   {"Имя торгового инструмента","Trade symbol"},
   {"Запрашиваемый объем сделки в лотах","Requested volume for a deal in lots"},
   {"Цена","Price"},
   {"Уровень StopLimit ордера","StopLimit level of the order"},
   {"Уровень Stop Loss ордера","Stop Loss level of the order"},
   {"Уровень Take Profit ордера","Take Profit level of the order"},
   {"Максимальное отклонение от цены","Maximal deviation from the price"},
   {"Тип ордера","Order type"},
   {"Тип ордера по исполнению","Order execution type"},
   {"Тип ордера по времени действия","Order expiration type"},
   {"Срок истечения ордера","Order expiration time"},
   {"Комментарий к ордеру","Order comment"},
   {"Тикет позиции","Position ticket"},
   {"Тикет встречной позиции","Opposite position ticket"},
   
   {"Поставить рыночный ордер","Place market order"},
   {"Установить отложенный ордер","Place pending order"},
   {"Изменить значения Stop Loss и Take Profit у открытой позиции","Modify Stop Loss and Take Profit values of an opened position"},
   {"Изменить параметры ранее установленного торгового ордера","Modify the parameters of the order placed previously"},
   {"Удалить ранее выставленный отложенный ордер","Delete the pending order placed previously"},
   {"Закрыть позицию встречной","Close a position by an opposite one"},
   {"Неизвестный тип торговой операции","Unknown trade action type"},
   
   {"Ордер исполняется исключительно в указанном объеме, иначе отменяется (FOK)","The order is executed exclusively in the specified volume, otherwise it is canceled (FOK)"},
   {"Ордер исполняется на доступный объем, неисполненный отменяется (IOK)","The order is executed on the available volume, the unfulfilled is canceled (IOK)"},
   {"Ордер исполняется на доступный объем, неисполненный остаётся (Return)","The order is executed at an available volume, unfulfilled remains in the market (Return)"},
   
   {"Ордер действителен до явной отмены","Good till cancel order"},
   {"Ордер действителен только в течение текущего торгового дня","Good till current trade day order"},
   {"Ордер действителен до даты истечения","Good till expired order"},
   {"Ордер действителен до 23:59:59 указанного дня","The order will be effective till 23:59:59 of the specified day"},
   {"Параметры торгового запроса","Trade request's parameters"},
   {"Параметры отложенного торгового запроса","Pending trade request's parameters"},
   {"Создан отложенный запрос","Pending request created"},
   {"Отложенный запрос удалён в связи с окончанием времени его действия","Pending request deleted due to expiration"},
   
   {"Цена в момент создания запроса: ","Price at time of request create: "},
   {"Время создания запроса: ","Request creation time: "},
   {"Время активации запроса: ","Request activation time: "},
   {"Время ожидания между торговыми попытками: ","Waiting time between trading attempts: "},
   {"Текущая торговая попытка: ","Current trading attempt: "},
   {"Общее количество торговых попыток: ","Total trade attempts: "},
   {"Идентификатор торгового запроса: ","Trade request ID: "},
   {"Код возврата, на основании которого создан запрос: ","Return code based on which the request was created: "},
   {"Тип отложенного запроса: ","Pending request type: "},
   {"Отложенный запрос, созданный по коду возврата сервера","Pending request that was created as a result of the server code"},
   {"Отложенный запрос, созданный по запросу","Pending request created by request"},
   {"Ожидание наступления времени первой торговой попытки","Waiting for the onset time of the first trading attempt"},
   
  };
//+---------------------------------------------------------------------+

Помимо описанных выше недочётов, я заметил, что у нас есть пересечение значений идентификаторов коллекций с идентификаторами типов объектов в стандартной библиотеке. В частности — идентификатор коллекции исторических ордеров и сделок COLLECTION_HISTORY_ID имеет значение 0x7779, которое соответствует значению типа списка класса динамического списка экземпляров класса CObject и его наследников CList стандартной библиотеки. Это неправильно — то что идентификаторы объектов имели одинаковое значение.

Вот, скорее всего, неполный список идентификаторов объектов стандартной библиотеки и соответствующие им шестнадцатеричные значения:

Базовый класс           CObject
                        Type = 0
Коллекции данных    CArrayChar
                        Type = 0x77
Коллекции данных    CArrayShort
                        Type = 0x82
Коллекции данных    CArrayInt
                        Type = 0x82
Коллекции данных    CArrayLong
                        Type = 0x84
Коллекции данных    CArrayFloat
                        Type = 0x87
Коллекции данных    CArrayDouble
                        Type = 0x87
Коллекции данных    CArrayString
                        Type = 0x89
Коллекции данных    CArrayObj
                        Type = 0x7778
Коллекции данных    CList
                        Type = 0x7779
Графические объекты Базовый класс CChartObject
                        Type = 0x8888
Ценовые графики     CChart
                        Type = 0x1111

Как видим, тип объекта-списка совпадает с заданным для библиотеки идентификатором коллекции исторических ордеров и сделок.
Исправим эту коллизию данных в файле Defines.mqh, увеличив значения всех идентификаторов коллекций на 1:

//--- Идентификаторы списков коллекций
#define COLLECTION_HISTORY_ID          (0x777A)                   // Идентификатор списка исторической коллекции
#define COLLECTION_MARKET_ID           (0x777B)                   // Идентификатор списка рыночной коллекции
#define COLLECTION_EVENTS_ID           (0x777C)                   // Идентификатор списка коллекции событий
#define COLLECTION_ACCOUNT_ID          (0x777D)                   // Идентификатор списка коллекции аккаунтов
#define COLLECTION_SYMBOLS_ID          (0x777E)                   // Идентификатор списка коллекции символов
//--- Параметры данных для файловых операций

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

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

Поэтому, для разделения типов запросов мы введём понятие "тип запроса" и соответствующие типу идентификаторы:

//--- Параметры символов
#define CLR_DEFAULT                    (0xFF000000)               // Цвет по умолчанию
#define SYMBOLS_COMMON_TOTAL           (1000)                     // Общее количество рабочих символов
//--- Идентификаторы типов отложенных запросов
#define PENDING_REQUEST_ID_TYPE_ERR    (1)                        // Тип отложенного запроса, созданного по коду возврата сервера
#define PENDING_REQUEST_ID_TYPE_REQ    (2)                        // Тип отложенного запроса, созданного по запросу
//+------------------------------------------------------------------+

И в самом конце файла Defines.mqh впишем перечисление типов отложенных запросов:

//+------------------------------------------------------------------+
//| Тип отложенного запроса                                          |
//+------------------------------------------------------------------+
enum ENUM_PENDING_REQUEST_TYPE
  {
   PENDING_REQUEST_TYPE_ERROR=PENDING_REQUEST_ID_TYPE_ERR,  // Отложенный запрос, созданный по коду возврата или ошибке
   PENDING_REQUEST_TYPE_REQUEST=PENDING_REQUEST_ID_TYPE_REQ,// Отложенный запрос, созданный по запросу
  };
//+------------------------------------------------------------------+

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

Функции для вывода описания режима заливки ордера и типа экспирации:

//+------------------------------------------------------------------+
//| Возвращает описание режима заливки ордера                        |
//+------------------------------------------------------------------+
string OrderTypeFillingDescription(const ENUM_ORDER_TYPE_FILLING type)
  {
   return
     (
      type==ORDER_FILLING_FOK    ?  CMessage::Text(MSG_LIB_TEXT_REQUEST_ORDER_FILLING_FOK)   :
      type==ORDER_FILLING_IOC    ?  CMessage::Text(MSG_LIB_TEXT_REQUEST_ORDER_FILLING_IOK)   :
      type==ORDER_FILLING_RETURN ?  CMessage::Text(MSG_LIB_TEXT_REQUEST_ORDER_FILLING_RETURN): 
      type==WRONG_VALUE          ? "WRONG_VALUE"   :  EnumToString(type)
     );
  }
//+------------------------------------------------------------------+
//| Возвращает описание типа экспирации ордера                       |
//+------------------------------------------------------------------+
string OrderTypeTimeDescription(const ENUM_ORDER_TYPE_TIME type)
  {
   return
     (
      type==ORDER_TIME_GTC             ?  CMessage::Text(MSG_LIB_TEXT_REQUEST_ORDER_TIME_GTC)            :
      type==ORDER_TIME_DAY             ?  CMessage::Text(MSG_LIB_TEXT_REQUEST_ORDER_TIME_DAY)            :
      type==ORDER_TIME_SPECIFIED       ?  CMessage::Text(MSG_LIB_TEXT_REQUEST_ORDER_TIME_SPECIFIED)      :
      type==ORDER_TIME_SPECIFIED_DAY   ?  CMessage::Text(MSG_LIB_TEXT_REQUEST_ORDER_TIME_SPECIFIED_DAY)  :
      type==WRONG_VALUE                ? "WRONG_VALUE"   :  EnumToString(type)
     );
  }
//+------------------------------------------------------------------+

Функции для вывода описания структуры торгового запроса MqlTradeRequest:

//+------------------------------------------------------------------+
//| Выводит в журнал описание торгового запроса                      |
//+------------------------------------------------------------------+
void PrintRequestDescription(const MqlTradeRequest &request)
  {
   string datas=
     (
      " - "+RequestActionDescription(request)+"\n"+
      " - "+RequestMagicDescription(request)+"\n"+
      " - "+RequestOrderDescription(request)+"\n"+
      " - "+RequestSymbolDescription(request)+"\n"+
      " - "+RequestVolumeDescription(request)+"\n"+
      " - "+RequestPriceDescription(request)+"\n"+
      " - "+RequestStopLimitDescription(request)+"\n"+
      " - "+RequestStopLossDescription(request)+"\n"+
      " - "+RequestTakeProfitDescription(request)+"\n"+
      " - "+RequestDeviationDescription(request)+"\n"+
      " - "+RequestTypeDescription(request)+"\n"+
      " - "+RequestTypeFillingDescription(request)+"\n"+
      " - "+RequestTypeTimeDescription(request)+"\n"+
      " - "+RequestExpirationDescription(request)+"\n"+
      " - "+RequestCommentDescription(request)+"\n"+
      " - "+RequestPositionDescription(request)+"\n"+
      " - "+RequestPositionByDescription(request)
     );
   Print("================== ",CMessage::Text(MSG_LIB_TEXT_REQUEST_DATAS)," ==================\n",datas,"\n");
  }
//+------------------------------------------------------------------+
//| Возвращает описание типа выполняемого действия                   |
//+------------------------------------------------------------------+
string RequestActionDescription(const MqlTradeRequest &request)
  {
   int code_descr=
     (
      request.action==TRADE_ACTION_DEAL      ?  MSG_LIB_TEXT_REQUEST_ACTION_DEAL       :
      request.action==TRADE_ACTION_PENDING   ?  MSG_LIB_TEXT_REQUEST_ACTION_PENDING    :
      request.action==TRADE_ACTION_SLTP      ?  MSG_LIB_TEXT_REQUEST_ACTION_SLTP       :
      request.action==TRADE_ACTION_MODIFY    ?  MSG_LIB_TEXT_REQUEST_ACTION_MODIFY     :
      request.action==TRADE_ACTION_REMOVE    ?  MSG_LIB_TEXT_REQUEST_ACTION_REMOVE     :
      request.action==TRADE_ACTION_CLOSE_BY  ?  MSG_LIB_TEXT_REQUEST_ACTION_CLOSE_BY   :
      MSG_LIB_TEXT_REQUEST_ACTION_UNCNOWN
     );
   return CMessage::Text(MSG_LIB_TEXT_REQUEST_ACTION)+": "+CMessage::Text(code_descr);
  }
//+------------------------------------------------------------------+
//| Возвращает описание значения магического номера                  |
//+------------------------------------------------------------------+
string RequestMagicDescription(const MqlTradeRequest &request)
  {
   return CMessage::Text(MSG_ORD_MAGIC)+": "+(string)request.magic;
  }
//+------------------------------------------------------------------+
//| Возвращает описание значения тикета ордера                       |
//+------------------------------------------------------------------+
string RequestOrderDescription(const MqlTradeRequest &request)
  {
   return CMessage::Text(MSG_LIB_TEXT_REQUEST_ORDER)+": "+(request.order>0 ? (string)request.order : CMessage::Text(MSG_LIB_PROP_NOT_SET));
  }
//+------------------------------------------------------------------+
//| Возвращает описание имени торгового инструмента                  |
//+------------------------------------------------------------------+
string RequestSymbolDescription(const MqlTradeRequest &request)
  {
   return CMessage::Text(MSG_LIB_TEXT_REQUEST_SYMBOL)+": "+request.symbol;
  }
//+------------------------------------------------------------------+
//| Возвращает описание объёма в запросе                             |
//+------------------------------------------------------------------+
string RequestVolumeDescription(const MqlTradeRequest &request)
  {
   int dg=(int)DigitsLots(request.symbol);
   int dgl=(dg==0 ? 1 : dg);
   return CMessage::Text(MSG_LIB_TEXT_REQUEST_VOLUME)+": "+(request.volume>0 ? DoubleToString(request.volume,dgl) : CMessage::Text(MSG_LIB_PROP_NOT_SET));
  }
//+------------------------------------------------------------------+
//| Возвращает описание значения цены в запросе                      |
//+------------------------------------------------------------------+
string RequestPriceDescription(const MqlTradeRequest &request)
  {
   return CMessage::Text(MSG_LIB_TEXT_REQUEST_PRICE)+": "+(request.price>0 ? DoubleToString(request.price,(int)SymbolInfoInteger(request.symbol,SYMBOL_DIGITS)) : CMessage::Text(MSG_LIB_PROP_NOT_SET));
  }
//+------------------------------------------------------------------+
//| Возвращает описание значения цены StopLimit-ордера запросе       |
//+------------------------------------------------------------------+
string RequestStopLimitDescription(const MqlTradeRequest &request)
  {
   return CMessage::Text(MSG_LIB_TEXT_REQUEST_STOPLIMIT)+": "+(request.stoplimit>0 ? DoubleToString(request.stoplimit,(int)SymbolInfoInteger(request.symbol,SYMBOL_DIGITS)) : CMessage::Text(MSG_LIB_PROP_NOT_SET));
  }
//+------------------------------------------------------------------+
//| Возвращает описание значения цены StopLoss-ордера запросе        |
//+------------------------------------------------------------------+
string RequestStopLossDescription(const MqlTradeRequest &request)
  {
   return CMessage::Text(MSG_LIB_TEXT_REQUEST_SL)+": "+(request.sl>0 ? DoubleToString(request.sl,(int)SymbolInfoInteger(request.symbol,SYMBOL_DIGITS)) : CMessage::Text(MSG_LIB_PROP_NOT_SET));
  }
//+------------------------------------------------------------------+
//| Возвращает описание значения цены TakeProfit-ордера запросе       |
//+------------------------------------------------------------------+
string RequestTakeProfitDescription(const MqlTradeRequest &request)
  {
   return CMessage::Text(MSG_LIB_TEXT_REQUEST_TP)+": "+(request.tp>0 ? DoubleToString(request.tp,(int)SymbolInfoInteger(request.symbol,SYMBOL_DIGITS)) : CMessage::Text(MSG_LIB_PROP_NOT_SET));
  }
//+------------------------------------------------------------------+
//| Возвращает описание значения размера отклонения в запросе        |
//+------------------------------------------------------------------+
string RequestDeviationDescription(const MqlTradeRequest &request)
  {
   return CMessage::Text(MSG_LIB_TEXT_REQUEST_DEVIATION)+": "+(string)request.deviation;
  }
//+------------------------------------------------------------------+
//| Возвращает описание типа ордера в запросе                        |
//+------------------------------------------------------------------+
string RequestTypeDescription(const MqlTradeRequest &request)
  {
   return CMessage::Text(MSG_LIB_TEXT_REQUEST_TYPE)+": "+OrderTypeDescription(request.type);
  }
//+------------------------------------------------------------------+
//| Возвращает описание режима заливки ордера в запросе              |
//+------------------------------------------------------------------+
string RequestTypeFillingDescription(const MqlTradeRequest &request)
  {
   return CMessage::Text(MSG_LIB_TEXT_REQUEST_TYPE_FILLING)+": "+OrderTypeFillingDescription(request.type_filling);
  }
//+------------------------------------------------------------------+
//| Возвращает описание типа срока действия ордера в запросе         |
//+------------------------------------------------------------------+
string RequestTypeTimeDescription(const MqlTradeRequest &request)
  {
   return CMessage::Text(MSG_LIB_TEXT_REQUEST_TYPE_TIME)+": "+OrderTypeTimeDescription(request.type_time);
  }
//+------------------------------------------------------------------+
//| Возвращает описание срока истечения ордера в запросе             |
//+------------------------------------------------------------------+
string RequestExpirationDescription(const MqlTradeRequest &request)
  {
   return CMessage::Text(MSG_LIB_TEXT_REQUEST_EXPIRATION)+": "+(request.expiration>0 ? TimeToString(request.expiration) : CMessage::Text(MSG_LIB_PROP_NOT_SET));
  }
//+------------------------------------------------------------------+
//| Возвращает описание комментария ордера в запросе                 |
//+------------------------------------------------------------------+
string RequestCommentDescription(const MqlTradeRequest &request)
  {
   return CMessage::Text(MSG_LIB_TEXT_REQUEST_COMMENT)+": "+(request.comment!="" && request.comment!=NULL ? "\""+request.comment+"\"" : CMessage::Text(MSG_LIB_PROP_NOT_SET));
  }
//+------------------------------------------------------------------+
//| Возвращает описание тикета позиции в запросе                     |
//+------------------------------------------------------------------+
string RequestPositionDescription(const MqlTradeRequest &request)
  {
   return CMessage::Text(MSG_LIB_TEXT_REQUEST_POSITION)+": "+(request.position>0 ? (string)request.position : CMessage::Text(MSG_LIB_PROP_NOT_SET));
  }
//+------------------------------------------------------------------+
//| Возвращает описание тикета встречной позиции в запросе           |
//+------------------------------------------------------------------+
string RequestPositionByDescription(const MqlTradeRequest &request)
  {
   return CMessage::Text(MSG_LIB_TEXT_REQUEST_POSITION_BY)+": "+(request.position_by>0 ? (string)request.position_by : CMessage::Text(MSG_LIB_PROP_NOT_SET));
  }
//+------------------------------------------------------------------+

Займёмся доработкой торгового класса по исправлению выявленных недочётов, и одновременно напишем всё необходимое для создания объекта-отложенного запроса на выставление отложенных ордеров..

Устраняем недочёты торгового класса и создаём отложенный запрос на выставление ордеров

Заметил интересную особенность некоторых символов, график которых строится по ценам Last. Иногда у них отсутствуют цены Ask и Bid, или одна из них. Чтобы в любом случае получить цену, пришлось сделать дополнительные методы (и изменить уже существующие) в классе объекта абстрактного символа CSymbol, которые проверяют тип построения графика, и если он строится по ценам Last, то проводится дополнительная проверка на присутствие цен Ask и Bid, и если они есть, то они и используются, иначе — используется цена Last.

В файле Symbol.mqh, в блоке методов упрощённого доступа к свойствам объекта-символа изменим тип метода, возвращающего время. Так как время возвращается в милисекундах, то и тип должен быть ulong вместо datetime.
Также объявим три дополнительных метода, о назначении которых говорилось выше:

//+------------------------------------------------------------------+
//| Методы упрощённого доступа к свойствам объекта-символа           |
//+------------------------------------------------------------------+
//--- Целочисленные свойства
   long              Status(void)                                 const { return this.GetProperty(SYMBOL_PROP_STATUS);                                      }
   int               IndexInMarketWatch(void)                     const { return (int)this.GetProperty(SYMBOL_PROP_INDEX_MW);                               }
   bool              IsCustom(void)                               const { return (bool)this.GetProperty(SYMBOL_PROP_CUSTOM);                                }
   color             ColorBackground(void)                        const { return (color)this.GetProperty(SYMBOL_PROP_BACKGROUND_COLOR);                     }
   ENUM_SYMBOL_CHART_MODE ChartMode(void)                         const { return (ENUM_SYMBOL_CHART_MODE)this.GetProperty(SYMBOL_PROP_CHART_MODE);          }
   bool              IsExist(void)                                const { return (bool)this.GetProperty(SYMBOL_PROP_EXIST);                                 }
   bool              IsExist(const string name)                   const { return this.SymbolExists(name);                                                   }
   bool              IsSelect(void)                               const { return (bool)this.GetProperty(SYMBOL_PROP_SELECT);                                }
   bool              IsVisible(void)                              const { return (bool)this.GetProperty(SYMBOL_PROP_VISIBLE);                               }
   long              SessionDeals(void)                           const { return this.GetProperty(SYMBOL_PROP_SESSION_DEALS);                               }
   long              SessionBuyOrders(void)                       const { return this.GetProperty(SYMBOL_PROP_SESSION_BUY_ORDERS);                          }
   long              SessionSellOrders(void)                      const { return this.GetProperty(SYMBOL_PROP_SESSION_SELL_ORDERS);                         }
   long              Volume(void)                                 const { return this.GetProperty(SYMBOL_PROP_VOLUME);                                      }
   long              VolumeHigh(void)                             const { return this.GetProperty(SYMBOL_PROP_VOLUMEHIGH);                                  }
   long              VolumeLow(void)                              const { return this.GetProperty(SYMBOL_PROP_VOLUMELOW);                                   }
   long              Time(void)                                   const { return (datetime)this.GetProperty(SYMBOL_PROP_TIME);                              }
   int               Digits(void)                                 const { return (int)this.GetProperty(SYMBOL_PROP_DIGITS);                                 }
   int               DigitsLot(void)                              const { return (int)this.GetProperty(SYMBOL_PROP_DIGITS_LOTS);                            }
   int               Spread(void)                                 const { return (int)this.GetProperty(SYMBOL_PROP_SPREAD);                                 }
   bool              IsSpreadFloat(void)                          const { return (bool)this.GetProperty(SYMBOL_PROP_SPREAD_FLOAT);                          }
   int               TicksBookdepth(void)                         const { return (int)this.GetProperty(SYMBOL_PROP_TICKS_BOOKDEPTH);                        }
   ENUM_SYMBOL_CALC_MODE TradeCalcMode(void)                      const { return (ENUM_SYMBOL_CALC_MODE)this.GetProperty(SYMBOL_PROP_TRADE_CALC_MODE);      }
   ENUM_SYMBOL_TRADE_MODE TradeMode(void)                         const { return (ENUM_SYMBOL_TRADE_MODE)this.GetProperty(SYMBOL_PROP_TRADE_MODE);          }
   datetime          StartTime(void)                              const { return (datetime)this.GetProperty(SYMBOL_PROP_START_TIME);                        }
   datetime          ExpirationTime(void)                         const { return (datetime)this.GetProperty(SYMBOL_PROP_EXPIRATION_TIME);                   }
   int               TradeStopLevel(void)                         const { return (int)this.GetProperty(SYMBOL_PROP_TRADE_STOPS_LEVEL);                      }
   int               TradeFreezeLevel(void)                       const { return (int)this.GetProperty(SYMBOL_PROP_TRADE_FREEZE_LEVEL);                     }
   ENUM_SYMBOL_TRADE_EXECUTION TradeExecutionMode(void)           const { return (ENUM_SYMBOL_TRADE_EXECUTION)this.GetProperty(SYMBOL_PROP_TRADE_EXEMODE);  }
   ENUM_SYMBOL_SWAP_MODE SwapMode(void)                           const { return (ENUM_SYMBOL_SWAP_MODE)this.GetProperty(SYMBOL_PROP_SWAP_MODE);            }
   ENUM_DAY_OF_WEEK  SwapRollover3Days(void)                      const { return (ENUM_DAY_OF_WEEK)this.GetProperty(SYMBOL_PROP_SWAP_ROLLOVER3DAYS);        }
   bool              IsMarginHedgedUseLeg(void)                   const { return (bool)this.GetProperty(SYMBOL_PROP_MARGIN_HEDGED_USE_LEG);                 }
   int               ExpirationModeFlags(void)                    const { return (int)this.GetProperty(SYMBOL_PROP_EXPIRATION_MODE);                        }
   int               FillingModeFlags(void)                       const { return (int)this.GetProperty(SYMBOL_PROP_FILLING_MODE);                           }
   int               OrderModeFlags(void)                         const { return (int)this.GetProperty(SYMBOL_PROP_ORDER_MODE);                             }
   ENUM_SYMBOL_ORDER_GTC_MODE OrderModeGTC(void)                  const { return (ENUM_SYMBOL_ORDER_GTC_MODE)this.GetProperty(SYMBOL_PROP_ORDER_GTC_MODE);  }
   ENUM_SYMBOL_OPTION_MODE OptionMode(void)                       const { return (ENUM_SYMBOL_OPTION_MODE)this.GetProperty(SYMBOL_PROP_OPTION_MODE);        }
   ENUM_SYMBOL_OPTION_RIGHT OptionRight(void)                     const { return (ENUM_SYMBOL_OPTION_RIGHT)this.GetProperty(SYMBOL_PROP_OPTION_RIGHT);      }
//--- Вещественные свойства
   double            Bid(void)                                    const { return this.GetProperty(SYMBOL_PROP_BID);                                         }
   double            BidHigh(void)                                const { return this.GetProperty(SYMBOL_PROP_BIDHIGH);                                     }
   double            BidLow(void)                                 const { return this.GetProperty(SYMBOL_PROP_BIDLOW);                                      }
   double            Ask(void)                                    const { return this.GetProperty(SYMBOL_PROP_ASK);                                         }
   double            AskHigh(void)                                const { return this.GetProperty(SYMBOL_PROP_ASKHIGH);                                     }
   double            AskLow(void)                                 const { return this.GetProperty(SYMBOL_PROP_ASKLOW);                                      }
   double            Last(void)                                   const { return this.GetProperty(SYMBOL_PROP_LAST);                                        }
   double            LastHigh(void)                               const { return this.GetProperty(SYMBOL_PROP_LASTHIGH);                                    }
   double            LastLow(void)                                const { return this.GetProperty(SYMBOL_PROP_LASTLOW);                                     }
   double            VolumeReal(void)                             const { return this.GetProperty(SYMBOL_PROP_VOLUME_REAL);                                 }
   double            VolumeHighReal(void)                         const { return this.GetProperty(SYMBOL_PROP_VOLUMEHIGH_REAL);                             }
   double            VolumeLowReal(void)                          const { return this.GetProperty(SYMBOL_PROP_VOLUMELOW_REAL);                              }
   double            OptionStrike(void)                           const { return this.GetProperty(SYMBOL_PROP_OPTION_STRIKE);                               }
   double            Point(void)                                  const { return this.GetProperty(SYMBOL_PROP_POINT);                                       }
   double            TradeTickValue(void)                         const { return this.GetProperty(SYMBOL_PROP_TRADE_TICK_VALUE);                            }
   double            TradeTickValueProfit(void)                   const { return this.GetProperty(SYMBOL_PROP_TRADE_TICK_VALUE_PROFIT);                     }
   double            TradeTickValueLoss(void)                     const { return this.GetProperty(SYMBOL_PROP_TRADE_TICK_VALUE_LOSS);                       }
   double            TradeTickSize(void)                          const { return this.GetProperty(SYMBOL_PROP_TRADE_TICK_SIZE);                             }
   double            TradeContractSize(void)                      const { return this.GetProperty(SYMBOL_PROP_TRADE_CONTRACT_SIZE);                         }
   double            TradeAccuredInterest(void)                   const { return this.GetProperty(SYMBOL_PROP_TRADE_ACCRUED_INTEREST);                      }
   double            TradeFaceValue(void)                         const { return this.GetProperty(SYMBOL_PROP_TRADE_FACE_VALUE);                            }
   double            TradeLiquidityRate(void)                     const { return this.GetProperty(SYMBOL_PROP_TRADE_LIQUIDITY_RATE);                        }
   double            LotsMin(void)                                const { return this.GetProperty(SYMBOL_PROP_VOLUME_MIN);                                  }
   double            LotsMax(void)                                const { return this.GetProperty(SYMBOL_PROP_VOLUME_MAX);                                  }
   double            LotsStep(void)                               const { return this.GetProperty(SYMBOL_PROP_VOLUME_STEP);                                 }
   double            VolumeLimit(void)                            const { return this.GetProperty(SYMBOL_PROP_VOLUME_LIMIT);                                }
   double            SwapLong(void)                               const { return this.GetProperty(SYMBOL_PROP_SWAP_LONG);                                   }
   double            SwapShort(void)                              const { return this.GetProperty(SYMBOL_PROP_SWAP_SHORT);                                  }
   double            MarginInitial(void)                          const { return this.GetProperty(SYMBOL_PROP_MARGIN_INITIAL);                              }
   double            MarginMaintenance(void)                      const { return this.GetProperty(SYMBOL_PROP_MARGIN_MAINTENANCE);                          }
   double            MarginLongInitial(void)                      const { return this.GetProperty(SYMBOL_PROP_MARGIN_LONG_INITIAL);                         }
   double            MarginBuyStopInitial(void)                   const { return this.GetProperty(SYMBOL_PROP_MARGIN_BUY_STOP_INITIAL);                     }
   double            MarginBuyLimitInitial(void)                  const { return this.GetProperty(SYMBOL_PROP_MARGIN_BUY_LIMIT_INITIAL);                    }
   double            MarginBuyStopLimitInitial(void)              const { return this.GetProperty(SYMBOL_PROP_MARGIN_BUY_STOPLIMIT_INITIAL);                }
   double            MarginLongMaintenance(void)                  const { return this.GetProperty(SYMBOL_PROP_MARGIN_LONG_MAINTENANCE);                     }
   double            MarginBuyStopMaintenance(void)               const { return this.GetProperty(SYMBOL_PROP_MARGIN_BUY_STOP_MAINTENANCE);                 }
   double            MarginBuyLimitMaintenance(void)              const { return this.GetProperty(SYMBOL_PROP_MARGIN_BUY_LIMIT_MAINTENANCE);                }
   double            MarginBuyStopLimitMaintenance(void)          const { return this.GetProperty(SYMBOL_PROP_MARGIN_BUY_STOPLIMIT_MAINTENANCE);            }
   double            MarginShortInitial(void)                     const { return this.GetProperty(SYMBOL_PROP_MARGIN_SHORT_INITIAL);                        }
   double            MarginSellStopInitial(void)                  const { return this.GetProperty(SYMBOL_PROP_MARGIN_SELL_STOP_INITIAL);                    }
   double            MarginSellLimitInitial(void)                 const { return this.GetProperty(SYMBOL_PROP_MARGIN_SELL_LIMIT_INITIAL);                   }
   double            MarginSellStopLimitInitial(void)             const { return this.GetProperty(SYMBOL_PROP_MARGIN_SELL_STOPLIMIT_INITIAL);               }
   double            MarginShortMaintenance(void)                 const { return this.GetProperty(SYMBOL_PROP_MARGIN_SHORT_MAINTENANCE);                    }
   double            MarginSellStopMaintenance(void)              const { return this.GetProperty(SYMBOL_PROP_MARGIN_SELL_STOP_MAINTENANCE);                }
   double            MarginSellLimitMaintenance(void)             const { return this.GetProperty(SYMBOL_PROP_MARGIN_SELL_LIMIT_MAINTENANCE);               }
   double            MarginSellStopLimitMaintenance(void)         const { return this.GetProperty(SYMBOL_PROP_MARGIN_SELL_STOPLIMIT_MAINTENANCE);           }
   double            SessionVolume(void)                          const { return this.GetProperty(SYMBOL_PROP_SESSION_VOLUME);                              }
   double            SessionTurnover(void)                        const { return this.GetProperty(SYMBOL_PROP_SESSION_TURNOVER);                            }
   double            SessionInterest(void)                        const { return this.GetProperty(SYMBOL_PROP_SESSION_INTEREST);                            }
   double            SessionBuyOrdersVolume(void)                 const { return this.GetProperty(SYMBOL_PROP_SESSION_BUY_ORDERS_VOLUME);                   }
   double            SessionSellOrdersVolume(void)                const { return this.GetProperty(SYMBOL_PROP_SESSION_SELL_ORDERS_VOLUME);                  }
   double            SessionOpen(void)                            const { return this.GetProperty(SYMBOL_PROP_SESSION_OPEN);                                }
   double            SessionClose(void)                           const { return this.GetProperty(SYMBOL_PROP_SESSION_CLOSE);                               }
   double            SessionAW(void)                              const { return this.GetProperty(SYMBOL_PROP_SESSION_AW);                                  }
   double            SessionPriceSettlement(void)                 const { return this.GetProperty(SYMBOL_PROP_SESSION_PRICE_SETTLEMENT);                    }
   double            SessionPriceLimitMin(void)                   const { return this.GetProperty(SYMBOL_PROP_SESSION_PRICE_LIMIT_MIN);                     }
   double            SessionPriceLimitMax(void)                   const { return this.GetProperty(SYMBOL_PROP_SESSION_PRICE_LIMIT_MAX);                     }
   double            MarginHedged(void)                           const { return this.GetProperty(SYMBOL_PROP_MARGIN_HEDGED);                               }
   double            NormalizedPrice(const double price)          const;
   double            NormalizedLot(const double volume)           const;
   double            BidLast(void)                                const;
   double            BidLastHigh(void)                            const;
   double            BidLastLow(void)                             const;
   double            AskLast(void)                                const;
   double            AskLastHigh(void)                            const;
   double            AskLastLow(void)                             const;
//--- Строковые свойства
   string            Name(void)                                   const { return this.GetProperty(SYMBOL_PROP_NAME);                                        }
   string            Basis(void)                                  const { return this.GetProperty(SYMBOL_PROP_BASIS);                                       }
   string            CurrencyBase(void)                           const { return this.GetProperty(SYMBOL_PROP_CURRENCY_BASE);                               }
   string            CurrencyProfit(void)                         const { return this.GetProperty(SYMBOL_PROP_CURRENCY_PROFIT);                             }
   string            CurrencyMargin(void)                         const { return this.GetProperty(SYMBOL_PROP_CURRENCY_MARGIN);                             }
   string            Bank(void)                                   const { return this.GetProperty(SYMBOL_PROP_BANK);                                        }
   string            Description(void)                            const { return this.GetProperty(SYMBOL_PROP_DESCRIPTION);                                 }
   string            Formula(void)                                const { return this.GetProperty(SYMBOL_PROP_FORMULA);                                     }
   string            ISIN(void)                                   const { return this.GetProperty(SYMBOL_PROP_ISIN);                                        }
   string            Page(void)                                   const { return this.GetProperty(SYMBOL_PROP_PAGE);                                        }
   string            Path(void)                                   const { return this.GetProperty(SYMBOL_PROP_PATH);                                        }
   string            Category(void)                               const { return this.GetProperty(SYMBOL_PROP_CATEGORY);                                    }
   string            Exchange(void)                               const { return this.GetProperty(SYMBOL_PROP_EXCHANGE);                                    }
   
//+------------------------------------------------------------------+

Три таких же метода для цены Bid у нас уже были сделаны ранее, но в них не было проверки наличия цен. Внесём в них изменения:

//+------------------------------------------------------------------+
//| Возвращает цену Bid или Last                                     |
//| в зависимости от метода построения графика и доступности цены    |
//+------------------------------------------------------------------+
double CSymbol::BidLast(void) const
  {
   return
     (
      this.ChartMode()==SYMBOL_CHART_MODE_BID ? this.GetProperty(SYMBOL_PROP_BID)  : 
     (this.GetProperty(SYMBOL_PROP_BID)==0    ? this.GetProperty(SYMBOL_PROP_LAST) : this.GetProperty(SYMBOL_PROP_BID))
     );
  }  
//+------------------------------------------------------------------+
//| Возвращает максимальную цену за день Bid или Last                |
//| в зависимости от метода построения графика и доступности цены    |
//+------------------------------------------------------------------+
double CSymbol::BidLastHigh(void) const
  {
   return
     (
      this.ChartMode()==SYMBOL_CHART_MODE_BID   ? this.GetProperty(SYMBOL_PROP_BIDHIGH)   : 
     (this.GetProperty(SYMBOL_PROP_BIDHIGH)==0  ? this.GetProperty(SYMBOL_PROP_LASTHIGH)  : this.GetProperty(SYMBOL_PROP_BIDHIGH))
     );
  }  
//+------------------------------------------------------------------+
//| Возвращает минимальную цену за день Bid или Last                 |
//| в зависимости от метода построения графика и доступности цены    |
//+------------------------------------------------------------------+
double CSymbol::BidLastLow(void) const
  {
   return
     (
      this.ChartMode()==SYMBOL_CHART_MODE_BID   ? this.GetProperty(SYMBOL_PROP_BIDLOW)  : 
     (this.GetProperty(SYMBOL_PROP_BIDLOW)==0   ? this.GetProperty(SYMBOL_PROP_LASTLOW) : this.GetProperty(SYMBOL_PROP_BIDLOW))
     );
  }
//+------------------------------------------------------------------+

Здесь всё, как и говорилось выше — проверяем тип постороения графика, и если график строится по ценам Bid, то возвращаем соответствующие цены Bid, если же график строится по ценам Last, то делаем дополнительную проверку на нулевую цену Bid возвращаемого свойства символа. Если она равна нулю, то используем соответствующую цену Last, иначе — соответствующую цену Bid.

Напишем точно такую же реализацию для методов, возвращающих цены Ask:

//+------------------------------------------------------------------+
//| Возвращает цену Ask или Last                                     |
//| в зависимости от метода построения графика и доступности цены    |
//+------------------------------------------------------------------+
double CSymbol::AskLast(void) const
  {
   return
     (
      this.ChartMode()==SYMBOL_CHART_MODE_BID ? this.GetProperty(SYMBOL_PROP_ASK)  : 
     (this.GetProperty(SYMBOL_PROP_ASK)==0    ? this.GetProperty(SYMBOL_PROP_LAST) : this.GetProperty(SYMBOL_PROP_ASK))
     );
  }  
//+------------------------------------------------------------------+
//| Возвращает максимальную цену за день Ask или Last                |
//| в зависимости от метода построения графика и доступности цены    |
//+------------------------------------------------------------------+
double CSymbol::AskLastHigh(void) const
  {
   return
     (
      this.ChartMode()==SYMBOL_CHART_MODE_BID   ? this.GetProperty(SYMBOL_PROP_ASKHIGH)   : 
     (this.GetProperty(SYMBOL_PROP_ASKHIGH)==0  ? this.GetProperty(SYMBOL_PROP_LASTHIGH)  : this.GetProperty(SYMBOL_PROP_ASKHIGH))
     );
  }  
//+------------------------------------------------------------------+
//| Возвращает минимальную цену за день Ask или Last                 |
//| в зависимости от метода построения графика и доступности цены    |
//+------------------------------------------------------------------+
double CSymbol::AskLastLow(void) const
  {
   return
     (
      this.ChartMode()==SYMBOL_CHART_MODE_BID   ? this.GetProperty(SYMBOL_PROP_ASKLOW)  : 
     (this.GetProperty(SYMBOL_PROP_ASKLOW)==0   ? this.GetProperty(SYMBOL_PROP_LASTLOW) : this.GetProperty(SYMBOL_PROP_ASKLOW))
     );
  }
//+------------------------------------------------------------------+

Теперь именно эти методы будем использовать в библиотеке при расчёте уровней цен для получения цен Ask или Last и Bid или Last.

При выводе сообщений о торговых событиях в журнал у нас сейчас выводятся дополнительные обозначения для идентификаторов магика и двух групп при условии, что в значении магического номера хранятся дополнительные данные (это мы реализовали в прошлой статье). Но если в магическом номере содержится ещё и идентификатор отложенного запроса, то его мы не выводили в журнал. Поправим это упущение. Просто добавим пару строчек в каждый из классов событий в файлах EventModify.mqh, EventOrderPlaced.mqh, EventOrderRemoved.mqh, EventPositionClose.mqh и EventPositionOpen.mqh в их методы краткого описания события.
Заменим одну такую строку в каждом из файлов:

string magic=(this.Magic()!=0 ? ", "+CMessage::Text(MSG_ORD_MAGIC)+" "+(string)this.Magic()+magic_id+group_id1+group_id2 : "");

на две такие:

string pend_req_id=(this.GetPendReqID()>0 ? ", ID: "+(string)this.GetPendReqID() : "");
string magic=(this.Magic()!=0 ? ", "+CMessage::Text(MSG_ORD_MAGIC)+" "+(string)this.Magic()+magic_id+group_id1+group_id2+pend_req_id : "");

Тем самым мы добавили описание идентификатора отложенного события (если таковой есть) к описанию магика.

В публичную секцию класса торгового объекта символа CTradeObj в файле TradeObj.mqh добавим методы, возвращающие описания полей структуры торгового запроса MqlTradeRequest:

//--- Возвращает описание (1) Тип выполненного действия, (2) магик, (3) тикет ордера, (4) объём,
//--- цены (5) открытия, (6) StopLimit-ордера, (7) StopLoss, (8) TakeProfit, (9) отклонение,
//--- тип (10) ордера, (11) исполнения, (12) времени действия, (13) срок истечения ордера,
//--- (14) комментарий, (15) тикет позиции, (16) тикет встречной позиции
   string                     GetRequestActionDescription(void)                  const { return RequestActionDescription(this.m_request);       }
   string                     GetRequestMagicDescription(void)                   const { return RequestMagicDescription(this.m_request);        }
   string                     GetRequestOrderDescription(void)                   const { return RequestOrderDescription(this.m_request);        }
   string                     GetRequestSymbolDescription(void)                  const { return RequestSymbolDescription(this.m_request);       }
   string                     GetRequestVolumeDescription(void)                  const { return RequestVolumeDescription(this.m_request);       }
   string                     GetRequestPriceDescription(void)                   const { return RequestPriceDescription(this.m_request);        }
   string                     GetRequestStopLimitDescription(void)               const { return RequestStopLimitDescription(this.m_request);    }
   string                     GetRequestStopLossDescription(void)                const { return RequestStopLossDescription(this.m_request);     }
   string                     GetRequestTakeProfitDescription(void)              const { return RequestTakeProfitDescription(this.m_request);   }
   string                     GetRequestDeviationDescription(void)               const { return RequestDeviationDescription(this.m_request);    }
   string                     GetRequestTypeDescription(void)                    const { return RequestTypeDescription(this.m_request);         }
   string                     GetRequestTypeFillingDescription(void)             const { return RequestTypeFillingDescription(this.m_request);  }
   string                     GetRequestTypeTimeDescription(void)                const { return RequestTypeTimeDescription(this.m_request);     }
   string                     GetRequestExpirationDescription(void)              const { return RequestExpirationDescription(this.m_request);   }
   string                     GetRequestCommentDescription(void)                 const { return RequestCommentDescription(this.m_request);      }
   string                     GetRequestPositionDescription(void)                const { return RequestPositionDescription(this.m_request);     }
   string                     GetRequestPositionByDescription(void)              const { return RequestPositionByDescription(this.m_request);   }

//--- Открывает позицию

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

По какой-то причине я ранее упустил из виду передачу типа заливки ордера в методы открытия позиции.
Добавим этот параметр в объявлении метода открытия позиций класса:

//--- Открывает позицию
   bool                       OpenPosition(const ENUM_POSITION_TYPE type,
                                           const double volume,
                                           const double sl=0,
                                           const double tp=0,
                                           const ulong magic=ULONG_MAX,
                                           const string comment=NULL,
                                           const ulong deviation=ULONG_MAX,
                                           const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE);

и в реализацию метода:

//+------------------------------------------------------------------+
//| Открывает позицию                                                |
//+------------------------------------------------------------------+
bool CTradeObj::OpenPosition(const ENUM_POSITION_TYPE type,
                             const double volume,
                             const double sl=0,
                             const double tp=0,
                             const ulong magic=ULONG_MAX,
                             const string comment=NULL,
                             const ulong deviation=ULONG_MAX,
                             const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE)
  {

Так как в будущем у нас будет организована возможность торговли отложенными запросами, то мы ввели понятие типа отложенного запроса.

В файле торгового класса Trading.mqh в классе отложенного запроса CPendingReq
добавим в приватную секцию переменную-член класса для хранения типа отложенного запроса:

//+------------------------------------------------------------------+
//| Класс объекта-отложенного запроса                                |
//+------------------------------------------------------------------+
class CPendingReq : public CObject
  {
private:
   MqlTradeRequest      m_request;                       // Структура торгового запроса
   uchar                m_id;                            // Идентификатор торгового запроса
   int                  m_type;                          // Тип отложенного запроса
   int                  m_retcode;                       // Результат, на основании которого создан запрос
   double               m_price_create;                  // Цена при создании запроса
   ulong                m_time_create;                   // Время создания запроса
   ulong                m_time_activate;                 // Время активации очередной попытки
   ulong                m_waiting_msc;                   // Продолжительность ожидания между запросами
   uchar                m_current_attempt;               // Номер текущей попытки
   uchar                m_total_attempts;                // Количество попыток

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

public:
//--- Возвращает (1) структуру запроса, (2) цену при создании запроса,
//--- (3) время создания запроса, (4) время текущей попытки,
//--- (5) продолжительность ожидания между запросами, (6) номер текущей попытки,
//--- (7) количество попыток, (8) идентификатор запроса
   MqlTradeRequest      MqlRequest(void)                       const { return this.m_request;         }
   double               PriceCreate(void)                      const { return this.m_price_create;    }
   ulong                TimeCreate(void)                       const { return this.m_time_create;     }
   ulong                TimeActivate(void)                     const { return this.m_time_activate;   }
   ulong                WaitingMSC(void)                       const { return this.m_waiting_msc;     }
   uchar                CurrentAttempt(void)                   const { return this.m_current_attempt; }
   uchar                TotalAttempts(void)                    const { return this.m_total_attempts;  }
   uchar                ID(void)                               const { return this.m_id;              }
   int                  Retcode(void)                          const { return this.m_retcode;         }
//--- Устанавливает (1) цену при создании запроса, (2) время создания запроса,
//--- (3) время текущей попытки, (4) продолжительность ожидания между запросами,
//--- (5) номер текущей попытки, (6) количество попыток, (7) идентификатор запроса
   void                 SetPriceCreate(const double price)           { this.m_price_create=price;     }
   void                 SetTimeCreate(const ulong time)              { this.m_time_create=time;       }
   void                 SetTimeActivate(const ulong time)            { this.m_time_activate=time;     }
   void                 SetWaitingMSC(const ulong miliseconds)       { this.m_waiting_msc=miliseconds;}
   void                 SetCurrentAttempt(const uchar number)        { this.m_current_attempt=number; }
   void                 SetTotalAttempts(const uchar number)         { this.m_total_attempts=number;  }
   void                 SetID(const uchar id)                        { this.m_id=id;                  }
   
//--- Возвращает описание (1) структуры запроса, (2) цены при создании запроса,
//--- (3) времени создания запроса, (4) времени текущей попытки,
//--- (5) продолжительности ожидания между запросами, (6) номера текущей попытки,
//--- (7) количества попыток, (8) идентификатора запроса
   string               MqlRequestDescription(void)            const { return RequestActionDescription(this.m_request); }
   string               TypeDescription(void)                  const
                          { 
                           return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_TYPE) +
                             (this.Type()==PENDING_REQUEST_ID_TYPE_ERR           ? 
                              CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_BY_ERROR) : 
                              CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_BY_REQUEST)
                             );
                          }
   string               PriceCreateDescription(void)           const 
                          {
                           return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_PRICE_CREATE)+": "+
                                  ::DoubleToString(this.PriceCreate(),(int)::SymbolInfoInteger(this.m_request.symbol,SYMBOL_DIGITS));
                          }
   string               TimeCreateDescription(void)            const { return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_TIME_CREATE)+TimeMSCtoString(this.TimeCreate());    }
   string               TimeActivateDescription(void)          const { return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_TIME_ACTIVATE)+TimeMSCtoString(this.TimeActivate());}
   string               WaitingMSCDescription(void)            const { return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_WAITING)+(string)this.WaitingMSC();                 }
   string               CurrentAttemptDescription(void)        const 
                          {
                           return
                             (CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_CURRENT_ATTEMPT)+
                              (this.CurrentAttempt()==0 ? CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_WAITING_ONSET) : (string)this.CurrentAttempt())
                             );
                          }
   string               TotalAttemptsDescription(void)         const { return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_TOTAL_ATTEMPTS)+(string)this.TotalAttempts();       }
   string               IdDescription(void)                    const { return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_ID)+(string)this.ID();                              }
   string               RetcodeDescription(void)               const { return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_RETCODE)+(string)this.Retcode();                    }
   string               ReasonDescription(void)                const { return CMessage::Text(this.m_retcode);  }

//--- Возвращает тип запроса
   virtual int          Type(void)                             const { return this.m_type;   }
//--- Выводит в журнал данные запроса
   void                 Print(void);
//--- Конструкторы
                        CPendingReq(void){;}
                        CPendingReq(const uchar id,const double price,const ulong time,const MqlTradeRequest &request,const int retcode);
  };
//+------------------------------------------------------------------+

Методы, возвращающие описания свойств объекта отложенного запроса просто создают составное описание из заголовка, описывающего свойство, и его значения. Метод Type(), возвращающий тип отложенного запроса, сделан виртуальным, так как у базового объекта CObject, наследником которого является объект отложенного запроса, такой виртуальный метод уже определён. И для реализации возврата типа объекта-наследника метод нужно переопределить в наследнике, что мы и сделали — теперь этот метод возвращает значение переменной m_type, определённой в классе отложенного запроса.

В конструкторе класса установим значение для типа отложенного запроса:

//+------------------------------------------------------------------+
//| Параметрический конструктор                                      |
//+------------------------------------------------------------------+
CPendingReq::CPendingReq(const uchar id,const double price,const ulong time,const MqlTradeRequest &request,const int retcode) : m_price_create(price),
                                                                                                                                m_time_create(time),
                                                                                                                                m_id(id),
                                                                                                                                m_retcode(retcode)
  {
   this.CopyRequest(request);
   this.m_type=(retcode>0 ? PENDING_REQUEST_ID_TYPE_ERR : PENDING_REQUEST_ID_TYPE_REQ);
  }
//+------------------------------------------------------------------+

Так как отложенные запросы у нас будут создаваться по двум причинам: по коду возарата сервера и по запросу от программы, то для определения типа отложенного запроса достаточно знать код ответа сервера. Именно это тут мы и делаем: если значение кода возврата больше ноля (сервер вернул ошибку) — значит этот запрос был создан по коду возврата сервера, иначе — если код нулевой, то объект отложенного запроса создан по запросу из программы.

Доработаем виртуальный метод Compare() объекта отложенного запроса.
Ранее он у нас всегда сравнивал объекты только по одному свойству — по идентификатору запроса:

//+------------------------------------------------------------------+
//| Сравнивает объекты CPendingReq между собой по идентификаторам    |
//+------------------------------------------------------------------+
int CPendingReq::Compare(const CObject *node,const int mode=0) const
  {
   const CPendingReq *compared_req=node;
   return(this.ID()>compared_req.ID() ? 1 : this.ID()<compared_req.ID() ? -1 : 0);
   return 0;
  }
//+------------------------------------------------------------------+

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

//+------------------------------------------------------------------+
//| Сравнивает объекты CPendingReq между собой по свойствам          |
//+------------------------------------------------------------------+
int CPendingReq::Compare(const CObject *node,const int mode=0) const
  {
   const CPendingReq *compared_req=node;
   return
     (
      //--- Сравнение по идентификатору
      mode==0  ?  
      (this.ID()>compared_req.ID() ? 1 : this.ID()<compared_req.ID() ? -1 : 0)   :
      //--- Сравнение по типу
      (this.Type()>compared_req.Type() ? 1 : this.Type()<compared_req.Type() ? -1 : 0)
     );
  }
//+------------------------------------------------------------------+

Здесь происходит выбор сравниваемых свойств от значения переменной mode. При нулевом значении объекты сравниваются по идентификаторам, при отличном от нуля — по типам объектов.

Также за пределами тела класса напишем метод, выводящий в журнал полное описание всех свойств объекта-отложенного запроса:

//+------------------------------------------------------------------+
//| Выводит в журнал данные запроса                                  |
//+------------------------------------------------------------------+
void CPendingReq::Print(void)
  {
   string action=" - "+RequestActionDescription(this.m_request)+"\n";
   string symbol="",order="",volume="",price="",stoplimit="",sl="",tp="",deviation="",type="",type_filling="";
   string type_time="",expiration="",position="",position_by="",magic="",comment="",request_data="";
   string type_req=" - "+this.TypeDescription()+"\n";
   
   if(this.m_request.action==TRADE_ACTION_DEAL)
     {
      symbol=" - "+RequestSymbolDescription(this.m_request)+"\n";
      volume=" - "+RequestVolumeDescription(this.m_request)+"\n";
      price=" - "+RequestPriceDescription(this.m_request)+"\n";
      sl=" - "+RequestStopLossDescription(this.m_request)+"\n";
      tp=" - "+RequestTakeProfitDescription(this.m_request)+"\n";
      deviation=" - "+RequestDeviationDescription(this.m_request)+"\n";
      type=" - "+RequestTypeDescription(this.m_request)+"\n";
      type_filling=" - "+RequestTypeFillingDescription(this.m_request)+"\n";
      magic=" - "+RequestMagicDescription(this.m_request)+"\n";
      comment=" - "+RequestCommentDescription(this.m_request)+"\n";
      request_data=
        ("================== "+
         CMessage::Text(MSG_LIB_TEXT_REQUEST_DATAS)+" ==================\n"+
         action+symbol+volume+price+sl+tp+deviation+type+type_filling+magic+comment+
         " ==================\n"
        );
     }
   else if(this.m_request.action==TRADE_ACTION_SLTP)
     {
      symbol=" - "+RequestSymbolDescription(this.m_request)+"\n";
      sl=" - "+RequestStopLossDescription(this.m_request)+"\n";
      tp=" - "+RequestTakeProfitDescription(this.m_request)+"\n";
      position=" - "+RequestPositionDescription(this.m_request)+"\n";
      request_data=
        ("================== "+
         CMessage::Text(MSG_LIB_TEXT_REQUEST_DATAS)+" ==================\n"+
         action+symbol+sl+tp+position+
         " ==================\n"
        );
     }
   else if(this.m_request.action==TRADE_ACTION_PENDING)
     {
      symbol=" - "+RequestSymbolDescription(this.m_request)+"\n";
      volume=" - "+RequestVolumeDescription(this.m_request)+"\n";
      price=" - "+RequestPriceDescription(this.m_request)+"\n";
      stoplimit=" - "+RequestStopLimitDescription(this.m_request)+"\n";
      sl=" - "+RequestStopLossDescription(this.m_request)+"\n";
      tp=" - "+RequestTakeProfitDescription(this.m_request)+"\n";
      type=" - "+RequestTypeDescription(this.m_request)+"\n";
      type_filling=" - "+RequestTypeFillingDescription(this.m_request)+"\n";
      type_time=" - "+RequestTypeTimeDescription(this.m_request)+"\n";
      expiration=" - "+RequestExpirationDescription(this.m_request)+"\n";
      magic=" - "+RequestMagicDescription(this.m_request)+"\n";
      comment=" - "+RequestCommentDescription(this.m_request)+"\n";
      request_data=
        ("================== "+
         CMessage::Text(MSG_LIB_TEXT_REQUEST_DATAS)+" ==================\n"+
         action+symbol+volume+price+stoplimit+sl+tp+type+type_filling+type_time+expiration+magic+comment+
         " ==================\n"
        );
     }
   else if(this.m_request.action==TRADE_ACTION_MODIFY)
     {
      order=" - "+RequestOrderDescription(this.m_request)+"\n";
      price=" - "+RequestPriceDescription(this.m_request)+"\n";
      sl=" - "+RequestStopLossDescription(this.m_request)+"\n";
      tp=" - "+RequestTakeProfitDescription(this.m_request)+"\n";
      type_time=" - "+RequestTypeTimeDescription(this.m_request)+"\n";
      expiration=" - "+RequestExpirationDescription(this.m_request)+"\n";
      request_data=
        ("================== "+
         CMessage::Text(MSG_LIB_TEXT_REQUEST_DATAS)+" ==================\n"+
         action+order+price+sl+tp+type_time+expiration+
         " ==================\n"
        );
     }
   else if(this.m_request.action==TRADE_ACTION_REMOVE)
     {
      order=" - "+RequestOrderDescription(this.m_request)+"\n";
      request_data=
        ("================== "+
         CMessage::Text(MSG_LIB_TEXT_REQUEST_DATAS)+" ==================\n"+
         action+order+
         " ==================\n"
        );
     }
   else if(this.m_request.action==TRADE_ACTION_CLOSE_BY)
     {
      position=" - "+RequestPositionDescription(this.m_request)+"\n";
      position_by=" - "+RequestPositionByDescription(this.m_request)+"\n";
      magic=" - "+RequestMagicDescription(this.m_request)+"\n";
      request_data=
        ("================== "+
         CMessage::Text(MSG_LIB_TEXT_REQUEST_DATAS)+" ==================\n"+
         action+position+position_by+magic+
         " ==================\n"
        );
     }
   string datas=
     (
      " - "+this.TypeDescription()+"\n"+
      " - "+this.IdDescription()+"\n"+
      " - "+this.RetcodeDescription()+" \""+this.ReasonDescription()+"\"\n"+
      " - "+this.TimeCreateDescription()+"\n"+
      " - "+this.PriceCreateDescription()+"\n"+
      " - "+this.TimeActivateDescription()+"\n"+
      " - "+this.WaitingMSCDescription()+" ("+TimeToString(this.WaitingMSC()/1000,TIME_MINUTES|TIME_SECONDS)+")"+"\n"+
      " - "+this.CurrentAttemptDescription()+"\n"+
      " - "+this.TotalAttemptsDescription()+"\n"
     );
   ::Print("================== ",CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_DATAS)," ==================\n",datas,request_data);
  }
//+------------------------------------------------------------------+

В методе в строковые переменные собираются описания всех свойств объекта, включая описания полей структуры торгового запроса, входящей в состав объекта. Количество выводимых данных зависит от типа выполняемого действия в торговом запросе — так как при разных действиях используется разное количество полей структуры торгового запроса. Поэтому проверяется значение поля action и выводятся только соответствующие ему поля. Сначала выводится описание переменных объекта, затем — описание полей структуры запроса. Таким образом, полностью все свойства объекта-отложенного запроса выводятся в журнал в соответствии с типом торгового действия запроса (action).

Ранее мы добавили в метод открытия позиции в классе CTradeObj дополнительное свойство — тип заливки ордера.
Теперь добавим это же свойство и в определение приватного метода открытия позиции в классе CTrading:

//--- (1) Открывает позицию, (2) устанавливает отложенный ордер
   template<typename SL,typename TP> 
   bool                 OpenPosition(const ENUM_POSITION_TYPE type,
                                    const double volume,
                                    const string symbol,
                                    const ulong magic=ULONG_MAX,
                                    const SL sl=0,
                                    const TP tp=0,
                                    const string comment=NULL,
                                    const ulong deviation=ULONG_MAX,
                                    const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE);

И такие же свойства добавим в определение публичных методов открытия позиции Buy и Sell:

//--- Открывает позицию (1) Buy, (2) Sell
   template<typename SL,typename TP> 
   bool                 OpenBuy(const double volume,
                                const string symbol,
                                const ulong magic=ULONG_MAX,
                                const SL sl=0,
                                const TP tp=0,
                                const string comment=NULL,
                                const ulong deviation=ULONG_MAX,
                                const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE);
   
   template<typename SL,typename TP> 
   bool                 OpenSell(const double volume,
                                 const string symbol,
                                 const ulong magic=ULONG_MAX,
                                 const SL sl=0,
                                 const TP tp=0,
                                 const string comment=NULL,
                                 const ulong deviation=ULONG_MAX,
                                 const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE);

Соответственно, в реализации этих методов за пределами тела класса так же добавим эти параметры:

//+------------------------------------------------------------------+
//| Открывает позицию                                                |
//+------------------------------------------------------------------+
template<typename SL,typename TP> 
bool CTrading::OpenPosition(const ENUM_POSITION_TYPE type,
                            const double volume,
                            const string symbol,
                            const ulong magic=ULONG_MAX,
                            const SL sl=0,
                            const TP tp=0,
                            const string comment=NULL,
                            const ulong deviation=ULONG_MAX,
                            const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE)
  {
//+------------------------------------------------------------------+
//| Открывает позицию Buy                                            |
//+------------------------------------------------------------------+
template<typename SL,typename TP> 
bool CTrading::OpenBuy(const double volume,
                       const string symbol,
                       const ulong magic=ULONG_MAX,
                       const SL sl=0,
                       const TP tp=0,
                       const string comment=NULL,
                       const ulong deviation=ULONG_MAX,
                       const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE)
  {
//--- Возвращаем результат отправки торгового запроса из метода OpenPosition()
   return this.OpenPosition(POSITION_TYPE_BUY,volume,symbol,magic,sl,tp,comment,deviation,type_filling);
  }
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Открывает позицию Sell                                           |
//+------------------------------------------------------------------+
template<typename SL,typename TP> 
bool CTrading::OpenSell(const double volume,
                        const string symbol,
                        const ulong magic=ULONG_MAX,
                        const SL sl=0,
                        const TP tp=0,
                        const string comment=NULL,
                        const ulong deviation=ULONG_MAX,
                        const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE)
  {
//--- Возвращаем результат отправки торгового запроса из метода OpenPosition()
   return this.OpenPosition(POSITION_TYPE_SELL,volume,symbol,magic,sl,tp,comment,deviation,type_filling);
  }
//+------------------------------------------------------------------+

В приватной секции класса объявим метод, возвращающий индекс объекта-запроса в списке по идентификатору:

//--- Ищет первый свободный идентификатор отложенного запроса
   int                  GetFreeID(void);
//--- Возвращает индекс объекта-запроса в списке по идентификатору
   int                  GetIndexPendingRequestByID(const uchar id);

public:

Реализация таймера класса претерпела небольшие изменения.
Полный код реализации таймера с описанием логики в комментариях:

//+------------------------------------------------------------------+
//| Таймер                                                           |
//+------------------------------------------------------------------+
void CTrading::OnTimer(void)
  {
   //--- В цикле по списку отложенных запросов
   int total=this.m_list_request.Total();
   for(int i=total-1;i>WRONG_VALUE;i--)
     {
      //--- получаем очередной объект-запрос
      CPendingReq *req_obj=this.m_list_request.At(i);
      if(req_obj==NULL)
         continue;
      
      //--- получаем структуру запроса и из неё объект-символ, на котором должна быть проведена торговая операция
      MqlTradeRequest request=req_obj.MqlRequest();
      CSymbol *symbol_obj=this.m_symbols.GetSymbolObjByName(request.symbol);
      if(symbol_obj==NULL || !symbol_obj.RefreshRates())
         continue;
      
      //--- если текущая попытка превысила установленное количество торговых попыток,
      //--- или текущее время больше времени ожидания всех попыток
      //--- удаляем текущий объект-запрос и переходим к следующему
      if(req_obj.CurrentAttempt()>req_obj.TotalAttempts() || req_obj.CurrentAttempt()>=UCHAR_MAX || 
         (long)symbol_obj.Time()>long(req_obj.TimeCreate()+req_obj.WaitingMSC()*req_obj.TotalAttempts()))
        {
         if(this.m_log_level>LOG_LEVEL_NO_MSG)
           {
            ::Print(CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_DELETED));
            req_obj.Print();
           }
         this.m_list_request.Delete(i);
         continue;
        }
      
      //--- Получаем идентификатор отложенного запроса
      uchar id=this.GetPendReqID((uint)request.magic);
      //--- Получаем список ордеров/позиций, содержащий ордер/позицию с таким идентификатором отложенного запроса
      CArrayObj *list=this.m_market.GetList(ORDER_PROP_PEND_REQ_ID,id,EQUAL);
      if(::CheckPointer(list)==POINTER_INVALID)
         continue;
      //--- Если есть такой ордер/позиция - запрос отработан: удаляем его и переходим к следующему
      if(list.Total()>0)
        {
         this.m_list_request.Delete(i);
         continue;
        }

      //--- Устанавливаем время активации запроса в объекте-запросе
      req_obj.SetTimeActivate(req_obj.TimeCreate()+req_obj.WaitingMSC()*(req_obj.CurrentAttempt()+1));
      
      //--- Если текущее время меньше времени активации запроса,
      //--- значит время запроса не наступило - идём к следующему запросу в списке
      if((long)symbol_obj.Time()<(long)req_obj.TimeActivate())
         continue;
      
      //--- Устанавливаем номер попытки в объекте-запросе
      req_obj.SetCurrentAttempt(uchar(req_obj.CurrentAttempt()+1));
      
      //--- Выволим в журнал номер торговой попытки
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(CMessage::Text(MSG_LIB_TEXT_RE_TRY_N)+(string)req_obj.CurrentAttempt());
      
      //--- В зависимости от типа выполняемого действия в торговом запросе 
      switch(request.action)
        {
         //--- Открытие позиции
         case TRADE_ACTION_DEAL :
            this.OpenPosition((ENUM_POSITION_TYPE)request.type,request.volume,request.symbol,request.magic,request.sl,request.tp,request.comment,request.deviation,request.type_filling);
            break;
         //--- Установка отложенного ордера
         case TRADE_ACTION_PENDING :
            this.PlaceOrder(request.type,request.volume,request.symbol,request.price,request.stoplimit,request.sl,request.tp,request.magic,request.comment,request.expiration,request.type_time,request.type_filling);
            break;
         //---
         default:
            break;
        }
     }
  }
//+------------------------------------------------------------------+

Надеюсь, здесь всё понятно расписано для самостоятельного изучения.

В методе корректировки ошибок RequestErrorsCorrecting() добавим корректировку типа экспирации при получении ошибки "неправильная дата истечения ордера" (лишь часть кода с исправлениями):

//--- Указан неподдерживаемый тип исполнения ордера по остатку
   if(this.IsPresentErorCode(10030))
      request.type_filling=symbol_obj.GetCorrectTypeFilling();
//--- Неверная дата истечения ордера в запросе -
   if(this.IsPresentErorCode(10022))
     {
      //--- Установим корректный тип истечения ордера
      request.type_time=symbol_obj.GetCorrectTypeExpiration();
      //--- если тип истечения не поддерживается как указываемый датой истечения, и задана дата истечения, то сбросим дату истечения
      if(!symbol_obj.IsExpirationModeSpecified() && request.expiration>0)
         request.expiration=0;
     }
//--- Просмотрим список оставшихся ошибок и скорректируем параметры торгового запроса

Ранее мы добавили в объект-символ новые методы для получения цен Ask и поправили методы для получения цен Bid. Теперь нам нужно заменить все вхождения строк "Ask()" и "Bid()" во всём листинге на "AskLast()" и "BidLast()" соответственно. Для этого удобно воспользоваться функцией поиска с заменой (Ctrl+H) и, пройдясь поиском по всему коду, заменить все нужные строки. Таким образом, мы везде, где нам требуется использование цен Ask и Bid объекта-символа, будем использовать автоматический выбор подходящих цен.

Например, метод установки цены торгового запроса теперь будет таким с заменёнными ценами:

//+------------------------------------------------------------------+
//| Устанавливает цены торгового запроса                             |
//+------------------------------------------------------------------+
template <typename PS,typename SL,typename TP,typename PL> 
bool CTrading::SetPrices(const ENUM_ORDER_TYPE action,const PS price,const SL sl,const TP tp,const PL limit,const string source_method,CSymbol *symbol_obj)
  {
//--- Обнуляем цены
   ::ZeroMemory(this.m_request);
//--- Обновляем все данные по символу
   if(!symbol_obj.RefreshRates())
     {
      this.AddErrorCodeToList(10021);  // Отсутствуют котировки для обработки запроса
      return false;
     }
//--- Цена открытия/установки
   if(price>0)
     {
      //--- тип (double) параметра цены - нормализуем цену до Digits(), так как передана цена
      if(typename(price)=="double")
         this.m_request.price=::NormalizeDouble(price,symbol_obj.Digits());
      //--- тип (int) параметра цены - передана дистанция
      else if(typename(price)=="int" || typename(price)=="uint" || typename(price)=="long" || typename(price)=="ulong")
        {
         //--- Рассчитываем цену установки ордера
         switch((int)action)
           {
            //--- Отложенный ордер
            case ORDER_TYPE_BUY_LIMIT       :   this.m_request.price=::NormalizeDouble(symbol_obj.AskLast()-price*symbol_obj.Point(),symbol_obj.Digits()); break;
            case ORDER_TYPE_BUY_STOP        :
            case ORDER_TYPE_BUY_STOP_LIMIT  :   this.m_request.price=::NormalizeDouble(symbol_obj.AskLast()+price*symbol_obj.Point(),symbol_obj.Digits()); break;
            
            case ORDER_TYPE_SELL_LIMIT      :   this.m_request.price=::NormalizeDouble(symbol_obj.BidLast()+price*symbol_obj.Point(),symbol_obj.Digits()); break;
            case ORDER_TYPE_SELL_STOP       :
            case ORDER_TYPE_SELL_STOP_LIMIT :   this.m_request.price=::NormalizeDouble(symbol_obj.BidLast()-price*symbol_obj.Point(),symbol_obj.Digits()); break;
            //--- По умолчанию - текущие цены открытия позиции
            default  :  this.m_request.price=
              (
               this.DirectionByActionType((ENUM_ACTION_TYPE)action)==ORDER_TYPE_BUY ? ::NormalizeDouble(symbol_obj.AskLast(),symbol_obj.Digits()) : 
               ::NormalizeDouble(symbol_obj.BidLast(),symbol_obj.Digits())
              ); break;
           }
        }
      //--- неподдерживаемые типы цены - выводим сообщение и возвращаем false
      else
        {
         if(this.m_log_level>LOG_LEVEL_NO_MSG)
            ::Print(source_method,CMessage::Text(MSG_LIB_TEXT_UNSUPPORTED_PR_TYPE));
         return false;
        }
     }
   //--- Если цена не указана - используем текущие цены
   else
     {
      this.m_request.price=
        (
         this.DirectionByActionType((ENUM_ACTION_TYPE)action)==ORDER_TYPE_BUY ? 
         ::NormalizeDouble(symbol_obj.AskLast(),symbol_obj.Digits())          : 
         ::NormalizeDouble(symbol_obj.BidLast(),symbol_obj.Digits())
        );
     }
   
//--- Цена или дистанция установки StopLimit-ордера
   if(limit>0)
     {
      //--- тип (double) параметра цены limit-ордера - нормализуем цену до Digits(), так как передана цена
      if(typename(limit)=="double")
         this.m_request.stoplimit=::NormalizeDouble(limit,symbol_obj.Digits());
      //--- тип (int) параметра цены limit-ордера - передана дистанция
      else if(typename(limit)=="int" || typename(limit)=="uint" || typename(limit)=="long" || typename(limit)=="ulong")
        {
         //--- Рассчитываем цену установки limit-ордера
         if(this.DirectionByActionType((ENUM_ACTION_TYPE)action)==ORDER_TYPE_BUY)
            this.m_request.stoplimit=::NormalizeDouble(this.m_request.price-limit*symbol_obj.Point(),symbol_obj.Digits());
         else
            this.m_request.stoplimit=::NormalizeDouble(this.m_request.price+limit*symbol_obj.Point(),symbol_obj.Digits());
        }
      //--- неподдерживаемые типы цены limit-ордера - выводим сообщение и возвращаем false
      else
        {
         if(this.m_log_level>LOG_LEVEL_NO_MSG)
            ::Print(source_method,CMessage::Text(MSG_LIB_TEXT_UNSUPPORTED_PL_TYPE));
         return false;
        }
     }  
     
//--- Цена установки ордера, от которой рассчитывать цены стоп-приказов
   double price_open=
     (
      (action==ORDER_TYPE_BUY_STOP_LIMIT || action==ORDER_TYPE_SELL_STOP_LIMIT) && limit>0 ? this.m_request.stoplimit : this.m_request.price
     );
     
//--- StopLoss
   if(sl>0)
     {
      //--- тип (double) параметра StopLoss - нормализуем цену до Digits(), так как передана цена
      if(typename(sl)=="double")
         this.m_request.sl=::NormalizeDouble(sl,symbol_obj.Digits());
      //--- тип (int) параметра StopLoss - рассчитываем дистанцию установки
      else if(typename(sl)=="int" || typename(sl)=="uint" || typename(sl)=="long" || typename(sl)=="ulong")
        {
         //--- Рассчитываем цену установки StopLoss
         if(this.DirectionByActionType((ENUM_ACTION_TYPE)action)==ORDER_TYPE_BUY)
            this.m_request.sl=::NormalizeDouble(price_open-sl*symbol_obj.Point(),symbol_obj.Digits());
         else
            this.m_request.sl=::NormalizeDouble(price_open+sl*symbol_obj.Point(),symbol_obj.Digits());
        }
      //--- неподдерживаемые типы StopLoss - выводим сообщение и возвращаем false
      else
        {
         if(this.m_log_level>LOG_LEVEL_NO_MSG)
            ::Print(source_method,CMessage::Text(MSG_LIB_TEXT_UNSUPPORTED_SL_TYPE));
         return false;
        }
     }
     
//--- TakeProfit
   if(tp>0)
     {
      //--- тип (double) параметра TakeProfit - нормализуем цену до Digits(), так как передана цена
      if(typename(tp)=="double")
         this.m_request.tp=::NormalizeDouble(tp,symbol_obj.Digits());
      //--- тип (int) параметра TakeProfit - рассчитываем дистанцию установки
      else if(typename(tp)=="int" || typename(tp)=="uint" || typename(tp)=="long" || typename(tp)=="ulong")
        {
         if(this.DirectionByActionType((ENUM_ACTION_TYPE)action)==ORDER_TYPE_BUY)
            this.m_request.tp=::NormalizeDouble(price_open+tp*symbol_obj.Point(),symbol_obj.Digits());
         else
            this.m_request.tp=::NormalizeDouble(price_open-tp*symbol_obj.Point(),symbol_obj.Digits());
        }
      //--- неподдерживаемые типы TakeProfit - выводим сообщение и возвращаем false
      else
        {
         if(this.m_log_level>LOG_LEVEL_NO_MSG)
            ::Print(source_method,CMessage::Text(MSG_LIB_TEXT_UNSUPPORTED_TP_TYPE));
         return false;
        }
     }
      
//--- Все цены записаны
   return true;
  }
//+------------------------------------------------------------------+

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

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

//+------------------------------------------------------------------+
//| Устанавливает отложенный ордер                                   |
//+------------------------------------------------------------------+
template<typename PS,typename PL,typename SL,typename TP>
bool CTrading::PlaceOrder(const ENUM_ORDER_TYPE order_type,
                          const double volume,
                          const string symbol,
                          const PS price_stop,
                          const PL price_limit=0,
                          const SL sl=0,
                          const TP tp=0,
                          const ulong magic=ULONG_MAX,
                          const string comment=NULL,
                          const datetime expiration=0,
                          const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE,
                          const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE)
  {
   bool res=true;
   this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_NO_ERROR;
   ENUM_ACTION_TYPE action=(ENUM_ACTION_TYPE)order_type;
//--- Получаем объект-символ по имени символа
   CSymbol *symbol_obj=this.m_symbols.GetSymbolObjByName(symbol);
   if(symbol_obj==NULL)
     {
      this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_INTERNAL_ERR;
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_SYM_OBJ));
      return false;
     }
//--- Получаем торговый объект из объекта-символа
   CTradeObj *trade_obj=symbol_obj.GetTradeObj();
   if(trade_obj==NULL)
     {
      this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_INTERNAL_ERR;
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_TRADE_OBJ));
      return false; 
     }
//--- Устанавливаем цены
//--- Если установить не удалось - записываем флаг "внутренняя ошибка" устанавливаем код ошибки в структуру возврата,
//--- выводим сообщение в журнал и возвращаем false
   if(!this.SetPrices(order_type,price_stop,sl,tp,price_limit,DFUN,symbol_obj))
     {
      this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_INTERNAL_ERR;
      trade_obj.SetResultRetcode(10021);
      trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode()));
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(10021));   // Отсутствуют котировки для обработки запроса
      return false;
     }
     
//--- Если есть ограничения по разрешённости торговли, не хватает средств,
//--- есть ограничения по уровню StopLevel - воспроизводим звук ошибки и уходим
   this.m_request.volume=volume;
   this.m_request.type_filling=type_filling;
   this.m_request.type_time=type_time;
   this.m_request.expiration=expiration;
   ENUM_ERROR_CODE_PROCESSING_METHOD method=this.CheckErrors(this.m_request.volume,
                                                             this.m_request.price,
                                                             action,
                                                             order_type,
                                                             symbol_obj,
                                                             trade_obj,
                                                             DFUN,
                                                             this.m_request.stoplimit,
                                                             this.m_request.sl,
                                                             this.m_request.tp);
   if(method!=ERROR_CODE_PROCESSING_METHOD_OK)
     {
      //--- Если полный запрет торговли
      if(method==ERROR_CODE_PROCESSING_METHOD_DISABLE)
        {
         trade_obj.SetResultRetcode(MSG_LIB_TEXT_TRADING_DISABLE);
         trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode()));
         if(this.m_log_level>LOG_LEVEL_NO_MSG)
            ::Print(CMessage::Text(MSG_LIB_TEXT_TRADING_DISABLE));
         if(this.IsUseSounds())
            trade_obj.PlaySoundError(action,order_type);
         return false;
        }
      //--- Если результат проверки "прервать торговую операцию" - устанавливаем код последней ошибки в структуру возврата,
      //--- выводим сообщение в журнал, воспроизводим звук ошибки и уходим
      if(method==ERROR_CODE_PROCESSING_METHOD_EXIT)
        {
         int code=this.m_list_errors.At(this.m_list_errors.Total()-1);
         if(code!=NULL)
           {
            trade_obj.SetResultRetcode(code);
            trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode()));
           }
         if(this.m_log_level>LOG_LEVEL_NO_MSG)
            ::Print(CMessage::Text(MSG_LIB_TEXT_TRADING_OPERATION_ABORTED));
         if(this.IsUseSounds())
            trade_obj.PlaySoundError(action,order_type);
         return false;
        }
      //--- Если результат проверки "ожидание" - устанавливаем код последней ошибки в структуру возврата И выводим сообщение в журнал
      if(method==ERROR_CODE_PROCESSING_METHOD_WAIT)
        {
         int code=this.m_list_errors.At(this.m_list_errors.Total()-1);
         if(code!=NULL)
           {
            trade_obj.SetResultRetcode(code);
            trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode()));
           }
         if(this.m_log_level>LOG_LEVEL_NO_MSG)
            ::Print(CMessage::Text(MSG_LIB_TEXT_CREATE_PENDING_REQUEST));
         //--- Временно, вместо создания отложенного запроса, ожидаем требуемое время (возвращается результатом метода CheckErrors())
         ::Sleep(method);           
         symbol_obj.Refresh();
        }
      //--- Если результат проверки "создать отложенный запрос" - временно ничего не делаем
      if(this.m_err_handling_behavior==ERROR_HANDLING_BEHAVIOR_PENDING_REQUEST)
        {
         if(this.m_log_level>LOG_LEVEL_NO_MSG)
            ::Print(CMessage::Text(MSG_LIB_TEXT_CREATE_PENDING_REQUEST));
        }
     }

//--- В цикле по количеству попыток
   for(int i=0;i<this.m_total_try;i++)
     {                
      //--- Отсылаем запрос
      res=trade_obj.SetOrder(order_type,
                             this.m_request.volume,
                             this.m_request.price,
                             this.m_request.sl,
                             this.m_request.tp,
                             this.m_request.stoplimit,
                             magic,
                             comment,
                             this.m_request.expiration,
                             this.m_request.type_time,
                             this.m_request.type_filling);
      //--- Если запрос выполнен успешно или асинхронный режим отправки ордеров - проиграем звук успеха,
      //--- установленный торговому объекту символа для данного типа торговой операции и вернём true
      if(res || trade_obj.IsAsyncMode())
        {
         if(this.IsUseSounds())
            trade_obj.PlaySoundSuccess(action,order_type);
         return true;
        }
      //--- Если запрос не выполнен - выведем сообщение и проиграем звук ошибки, установленный торговому объекту символа для данного типа торговой операции
      else
        {
         if(this.m_log_level>LOG_LEVEL_NO_MSG)
            ::Print(CMessage::Text(MSG_LIB_TEXT_TRY_N),string(i+1),". ",CMessage::Text(MSG_LIB_SYS_ERROR),": ",CMessage::Text(trade_obj.GetResultRetcode()));
         if(this.IsUseSounds())
            trade_obj.PlaySoundError(action,order_type);
         
         method=this.ResultProccessingMethod(trade_obj.GetResultRetcode());
         //--- Если в результате отправки запроса получили "Запретить торговлю экспертом" - взводим флаг запрета и прерываем цикл попыток
         if(method==ERROR_CODE_PROCESSING_METHOD_DISABLE)
           {
            this.SetTradingDisableFlag(true);
            break;
           }
         //--- Если в результате отправки запроса получили "Выйти из торгового метода" - прерываем цикл попыток
         if(method==ERROR_CODE_PROCESSING_METHOD_EXIT)
           {
            break;
           }
         //--- Если в результате отправки запроса получили "Скорректировать параметры и повторить" -
         //--- корректируем параметры и идём на следующую итерацию
         if(method==ERROR_CODE_PROCESSING_METHOD_CORRECT)
           {
            this.RequestErrorsCorrecting(this.m_request,order_type,trade_obj.SpreadMultiplier(),symbol_obj,trade_obj);
            continue;
           }
         //--- Если в результате отправки запроса получили "Обновить данные и повторить" -
         //--- обновляем данные и идём на следующую итерацию
         if(method==ERROR_CODE_PROCESSING_METHOD_REFRESH)
           {
            symbol_obj.Refresh();
            continue;
           }
         //--- Если в результате отправки запроса получили "Подождать и повторить"
         //--- или "Создать отложенный запрос" - создаём отложенный запрос и завершаем цикл
         if(method>ERROR_CODE_PROCESSING_METHOD_REFRESH)
           {
            //--- Если в магике торгового запроса нет идентификатора отложенного запроса
            if(this.GetPendReqID((uint)magic)==0)
              {
               //--- Время ожидания в милисекундах:
               //--- для метода обработки "Подождать и повторить" - значение ожидания соответствует значению method,
               //--- для метода обработки "Создать отложенный запрос" - пока будет нулевое время ожидания
               ulong wait=(method>ERROR_CODE_PROCESSING_METHOD_PENDING ? method : 0);
               //--- Ищем наименьший из возможных идентификаторов, и если найти не удалось,
               //--- или произошла ошибка обновления текущих данных символа - возвращаем false
               int id=this.GetFreeID();
               if(id<1 || !symbol_obj.RefreshRates())
                  return false;
               //--- Записываем в магик идентификатор запроса, в структуру запроса наименование символа,
               //--- устанавливаем тип торговой операции и тип ордера, комментарий и тип позиции, тип заполнения и экспирации
               uint mn=(magic==ULONG_MAX ? (uint)trade_obj.GetMagic() : (uint)magic);
               this.SetPendReqID((uchar)id,mn);
               this.m_request.magic=mn;
               this.m_request.symbol=symbol_obj.Name();
               this.m_request.action=TRADE_ACTION_PENDING;
               this.m_request.type=order_type;
               this.m_request.comment=(comment==NULL ? trade_obj.GetComment() : comment);
               this.m_request.type_time=(type_time>WRONG_VALUE ? type_time : trade_obj.GetTypeExpiration());
               this.m_request.type_filling=(type_filling>WRONG_VALUE ? type_filling : trade_obj.GetTypeFilling());
               //--- В отложенный запрос передаём количество торговых попыток
               uchar attempts=(this.m_total_try < 1 ? 1 : this.m_total_try);
               this.CreatePendingRequest((uchar)id,attempts,wait,this.m_request,trade_obj.GetResultRetcode(),symbol_obj);
               break;
              }
           }
        }
     }
//--- Возвращаем результат отправки торгового запроса в торговом объекте символа
   return res;
  }
//+------------------------------------------------------------------+

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

Для упрощения отладки — чтобы можно было видеть результат создания отложенного запроса, в методе создания объекта-отложенного запроса добавим вывод свойств вновь созданного запроса в журнал:

//+------------------------------------------------------------------+
//| Создаёт отложенный запрос                                        |
//+------------------------------------------------------------------+
bool CTrading::CreatePendingRequest(const uchar id,const uchar attempts,const ulong wait,const MqlTradeRequest &request,const int retcode,CSymbol *symbol_obj)
  {
   //--- Создаём новый объект-отложенный запрос
   CPendingReq *req_obj=new CPendingReq(id,symbol_obj.BidLast(),symbol_obj.Time(),request,retcode);
   if(req_obj==NULL)
     {
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_FAILING_CREATE_PENDING_REQ));
      return false;
     }
   //--- Если не удалось добавить запрос в список - выводим об этом сообщение,
   //--- удаляем созданный объект и возвращаем false
   if(!this.m_list_request.Add(req_obj))
     {
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_FAILING_CREATE_PENDING_REQ));
      delete req_obj;
      return false;
     }
   //--- Заполняем поля успешно созданного объекта переданными в метод значениями
   req_obj.SetTimeActivate(symbol_obj.Time()+wait);
   req_obj.SetWaitingMSC(wait);
   req_obj.SetCurrentAttempt(0);
   req_obj.SetTotalAttempts(attempts);
   
   if(this.m_log_level>LOG_LEVEL_NO_MSG)
     {
      ::Print(CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_CREATED)," #",req_obj.ID(),":");
      req_obj.Print();
     }
   
   return true;
  }
//+------------------------------------------------------------------+

В самом конце листинга торгового класса напишем реализацию метода, возвращающего индекс объекта-запроса в списке по идентификатору:

//+------------------------------------------------------------------+
//| Возвращает индекс объекта-запроса в списке по идентификатору     |
//+------------------------------------------------------------------+
int CTrading::GetIndexPendingRequestByID(const uchar id)
  {
   CPendingReq *req=new CPendingReq();
   if(req==NULL)
      return WRONG_VALUE;
   req.SetID(id);
   this.m_list_request.Sort();
   int index=this.m_list_request.Search(req);
   delete req;
   return index;
  }
//+------------------------------------------------------------------+

В метод передаётся искомый идентификатор, создаётся временный объект-запрос и устанавливается для него переданный в метод идентификатор.

Затем списку, содержащему объекты-запросы устанавливается флаг сортированного списка. По умолчанию режим сортировки равен нулю, и в таком режиме в виртуальном методе Compare() класса CPendingReq у нас как раз организовано сравнение объектов по идентификатору. Поэтому мы спокойно можем воспользоваться методом поиска объекта Search() в динамическом массиве указателей на объекты. Метод возвращает индекс найденного объекта в списке или -1, если объект не найден. Перед выходом из метода удаляем временный объект-запрос, и возвращаем полученный индекс найденного объекта, или -1.

Всё, что нам осталось сделать, это добавить в класс базового объекта библиотеки CEngine в определения его методов отправки торговых запросов дополнительный параметр с указанием типа заливки ордеров.

//--- Открывает позицию (1) Buy, (2) Sell
   template<typename SL,typename TP>
   bool                 OpenBuy(const double volume,
                                 const string symbol,
                                 const ulong magic=ULONG_MAX,
                                 SL sl=0,
                                 TP tp=0,
                                 const string comment=NULL,
                                 const ulong deviation=ULONG_MAX,
                                 const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE);
   template<typename SL,typename TP>
   bool                 OpenSell(const double volume,
                                 const string symbol,
                                 const ulong magic=ULONG_MAX,
                                 SL sl=0,
                                 TP tp=0,
                                 const string comment=NULL,
                                 const ulong deviation=ULONG_MAX,
                                 const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE);
//--- Устанавливает отложенный ордер (1) BuyStop, (2) BuyLimit, (3) BuyStopLimit
   template<typename PR,typename SL,typename TP>
   bool                 PlaceBuyStop(const double volume,
                                     const string symbol,
                                     const PR price,
                                     const SL sl=0,
                                     const TP tp=0,
                                     const ulong magic=ULONG_MAX,
                                     const string comment=NULL,
                                     const datetime expiration=0,
                                     const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE,
                                     const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE);
   template<typename PR,typename SL,typename TP>
   bool                 PlaceBuyLimit(const double volume,
                                     const string symbol,
                                     const PR price,
                                     const SL sl=0,
                                     const TP tp=0,
                                     const ulong magic=ULONG_MAX,
                                     const string comment=NULL,
                                     const datetime expiration=0,
                                     const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE,
                                     const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE);
   template<typename PR,typename PL,typename SL,typename TP>
   bool                 PlaceBuyStopLimit(const double volume,
                                     const string symbol,
                                     const PR price_stop,
                                     const PL price_limit,
                                     const SL sl=0,
                                     const TP tp=0,
                                     const ulong magic=ULONG_MAX,
                                     const string comment=NULL,
                                     const datetime expiration=0,
                                     const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE,
                                     const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE);
//--- Устанавливает отложенный ордер (1) SellStop, (2) SellLimit, (3) SellStopLimit
   template<typename PR,typename SL,typename TP>
   bool                 PlaceSellStop(const double volume,
                                     const string symbol,
                                     const PR price,
                                     const SL sl=0,
                                     const TP tp=0,
                                     const ulong magic=ULONG_MAX,
                                     const string comment=NULL,
                                     const datetime expiration=0,
                                     const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE,
                                     const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE);
   template<typename PR,typename SL,typename TP>
   bool                 PlaceSellLimit(const double volume,
                                     const string symbol,
                                     const PR price,
                                     const SL sl=0,
                                     const TP tp=0,
                                     const ulong magic=ULONG_MAX,
                                     const string comment=NULL,
                                     const datetime expiration=0,
                                     const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE,
                                     const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE);
   template<typename PR,typename PL,typename SL,typename TP>
   bool                 PlaceSellStopLimit(const double volume,
                                     const string symbol,
                                     const PR price_stop,
                                     const PL price_limit,
                                     const SL sl=0,
                                     const TP tp=0,
                                     const ulong magic=ULONG_MAX,
                                     const string comment=NULL,
                                     const datetime expiration=0,
                                     const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE,
                                     const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE);

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

Допишем те же параметры и в коды реализации этих торговых методов:

//+------------------------------------------------------------------+
//| Открывает позицию Buy                                            |
//+------------------------------------------------------------------+
template<typename SL,typename TP>
bool CEngine::OpenBuy(const double volume,
                      const string symbol,
                      const ulong magic=ULONG_MAX,
                      SL sl=0,TP tp=0,
                      const string comment=NULL,
                      const ulong deviation=ULONG_MAX,
                      const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE)
  {
   return this.m_trading.OpenBuy(volume,symbol,magic,sl,tp,comment,deviation,type_filling);
  }
//+------------------------------------------------------------------+
//| Открывает позицию Sell                                           |
//+------------------------------------------------------------------+
template<typename SL,typename TP>
bool CEngine::OpenSell(const double volume,
                       const string symbol,
                       const ulong magic=ULONG_MAX,
                       SL sl=0,TP tp=0,
                       const string comment=NULL,
                       const ulong deviation=ULONG_MAX,
                       const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE)
  {
   return this.m_trading.OpenSell(volume,symbol,magic,sl,tp,comment,deviation,type_filling);
  }
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Устанавливает отложенный ордер BuyStop                           |
//+------------------------------------------------------------------+
template<typename PR,typename SL,typename TP>
bool CEngine::PlaceBuyStop(const double volume,
                           const string symbol,
                           const PR price,
                           const SL sl=0,
                           const TP tp=0,
                           const ulong magic=WRONG_VALUE,
                           const string comment=NULL,
                           const datetime expiration=0,
                           const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE,
                           const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE)
  {
   return this.m_trading.PlaceBuyStop(volume,symbol,price,sl,tp,magic,comment,expiration,type_time,type_filling);
  }
//+------------------------------------------------------------------+
//| Устанавливает отложенный ордер BuyLimit                          |
//+------------------------------------------------------------------+
template<typename PR,typename SL,typename TP>
bool CEngine::PlaceBuyLimit(const double volume,
                            const string symbol,
                            const PR price,
                            const SL sl=0,
                            const TP tp=0,
                            const ulong magic=WRONG_VALUE,
                            const string comment=NULL,
                            const datetime expiration=0,
                            const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE,
                            const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE)
  {
   return this.m_trading.PlaceBuyLimit(volume,symbol,price,sl,tp,magic,comment,expiration,type_time,type_filling);
  }
//+------------------------------------------------------------------+
//| Устанавливает отложенный ордер BuyStopLimit                      |
//+------------------------------------------------------------------+
template<typename PR,typename PL,typename SL,typename TP>
bool CEngine::PlaceBuyStopLimit(const double volume,
                                const string symbol,
                                const PR price_stop,
                                const PL price_limit,
                                const SL sl=0,
                                const TP tp=0,
                                const ulong magic=WRONG_VALUE,
                                const string comment=NULL,
                                const datetime expiration=0,
                                const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE,
                                const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE)
  {
   return this.m_trading.PlaceBuyStopLimit(volume,symbol,price_stop,price_limit,sl,tp,magic,comment,expiration,type_time,type_filling);
  }
//+------------------------------------------------------------------+
//| Устанавливает отложенный ордер SellStop                          |
//+------------------------------------------------------------------+
template<typename PR,typename SL,typename TP>
bool CEngine::PlaceSellStop(const double volume,
                            const string symbol,
                            const PR price,
                            const SL sl=0,
                            const TP tp=0,
                            const ulong magic=WRONG_VALUE,
                            const string comment=NULL,
                            const datetime expiration=0,
                            const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE,
                            const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE)
  {
   return this.m_trading.PlaceSellStop(volume,symbol,price,sl,tp,magic,comment,expiration,type_time,type_filling);
  }
//+------------------------------------------------------------------+
//| Устанавливает отложенный ордер SellLimit                         |
//+------------------------------------------------------------------+
template<typename PR,typename SL,typename TP>
bool CEngine::PlaceSellLimit(const double volume,
                             const string symbol,
                             const PR price,
                             const SL sl=0,
                             const TP tp=0,
                             const ulong magic=WRONG_VALUE,
                             const string comment=NULL,
                             const datetime expiration=0,
                             const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE,
                             const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE)
  {
   return this.m_trading.PlaceSellLimit(volume,symbol,price,sl,tp,magic,comment,expiration,type_time,type_filling);
  }
//+------------------------------------------------------------------+
//| Устанавливает отложенный ордер SellStopLimit                     |
//+------------------------------------------------------------------+
template<typename PR,typename PL,typename SL,typename TP>
bool CEngine::PlaceSellStopLimit(const double volume,
                                 const string symbol,
                                 const PR price_stop,
                                 const PL price_limit,
                                 const SL sl=0,
                                 const TP tp=0,
                                 const ulong magic=WRONG_VALUE,
                                 const string comment=NULL,
                                 const datetime expiration=0,
                                 const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE,
                                 const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE)
  {
   return this.m_trading.PlaceSellStopLimit(volume,symbol,price_stop,price_limit,sl,tp,magic,comment,expiration,type_time,type_filling);
  }
//+------------------------------------------------------------------+

На момент текущей реализации это все необходимые доработки и изменения.

Тестирование

Для тестирования отложенных запросов на выставление отложенных ордеров возьмём советник из прошлой статьи и сохраним его в новой папке \MQL5\Experts\TestDoEasy\Part27\ под новым именем TestDoEasyPart27.mq5.

В советнике, в функции инициализации библиотеки добавим установку корректных значений типов заливки ордеров и типов их экспирации для всех торговых объектов всех используемых в советнике символов:

//+------------------------------------------------------------------+
//| Инициализация библиотеки DoEasy                                  |
//+------------------------------------------------------------------+
void OnInitDoEasy()
  {
//--- Проверка на выбор работы с полным списком
   used_symbols_mode=InpModeUsedSymbols;
   if((ENUM_SYMBOLS_MODE)used_symbols_mode==SYMBOLS_MODE_ALL)
     {
      int total=SymbolsTotal(false);
      string ru_n="\nКоличество символов на сервере "+(string)total+".\nМаксимальное количество: "+(string)SYMBOLS_COMMON_TOTAL+" символов.";
      string en_n="\nThe number of symbols on server "+(string)total+".\nMaximal number: "+(string)SYMBOLS_COMMON_TOTAL+" symbols.";
      string caption=TextByLanguage("Внимание!","Attention!");
      string ru="Выбран режим работы с полным списком.\nВ этом режиме первичная подготовка списка коллекции символов может занять длительное время."+ru_n+"\nПродолжить?\n\"Нет\" - работа с текущим символом \""+Symbol()+"\"";
      string en="Full list mode selected.\nIn this mode, the initial preparation of the collection symbols list may take a long time."+en_n+"\nContinue?\n\"No\" - working with the current symbol \""+Symbol()+"\"";
      string message=TextByLanguage(ru,en);
      int flags=(MB_YESNO | MB_ICONWARNING | MB_DEFBUTTON2);
      int mb_res=MessageBox(message,caption,flags);
      switch(mb_res)
        {
         case IDNO : 
           used_symbols_mode=SYMBOLS_MODE_CURRENT; 
           break;
         default:
           break;
        }
     }
//--- Заполнение массива используемых символов
   used_symbols=InpUsedSymbols;
   CreateUsedSymbolsArray((ENUM_SYMBOLS_MODE)used_symbols_mode,used_symbols,array_used_symbols);

//--- Установка типа используемого списка символов в коллекции символов
   engine.SetUsedSymbols(array_used_symbols);
//--- Отображение выбранного режима работы с коллекцией объектов-символов
   Print(engine.ModeSymbolsListDescription(),TextByLanguage(". Количество используемых символов: ",". The number of symbols used: "),engine.GetSymbolsCollectionTotal());
   
//--- Создание тестовых файлов ресурсов
   engine.CreateFile(FILE_TYPE_WAV,"sound_array_coin_01",TextByLanguage("Звук упавшей монетки 1","The sound of a falling coin 1"),sound_array_coin_01);
   engine.CreateFile(FILE_TYPE_WAV,"sound_array_coin_02",TextByLanguage("Звук упавших монеток","Sound fallen coins"),sound_array_coin_02);
   engine.CreateFile(FILE_TYPE_WAV,"sound_array_coin_03",TextByLanguage("Звук монеток","Sound of coins"),sound_array_coin_03);
   engine.CreateFile(FILE_TYPE_WAV,"sound_array_coin_04",TextByLanguage("Звук упавшей монетки 2","The sound of a falling coin 2"),sound_array_coin_04);
   engine.CreateFile(FILE_TYPE_WAV,"sound_array_click_01",TextByLanguage("Звук щелчка по кнопке 1","Click on the button sound 1"),sound_array_click_01);
   engine.CreateFile(FILE_TYPE_WAV,"sound_array_click_02",TextByLanguage("Звук щелчка по кнопке 2","Click on the button sound 1"),sound_array_click_02);
   engine.CreateFile(FILE_TYPE_WAV,"sound_array_click_03",TextByLanguage("Звук щелчка по кнопке 3","Click on the button sound 1"),sound_array_click_03);
   engine.CreateFile(FILE_TYPE_WAV,"sound_array_cash_machine_01",TextByLanguage("Звук кассового аппарата","The sound of the cash machine"),sound_array_cash_machine_01);
   engine.CreateFile(FILE_TYPE_BMP,"img_array_spot_green",TextByLanguage("Изображение \"Зелёный светодиод\"","Image \"Green Spot lamp\""),img_array_spot_green);
   engine.CreateFile(FILE_TYPE_BMP,"img_array_spot_red",TextByLanguage("Изображение \"Красный светодиод\"","Image \"Red Spot lamp\""),img_array_spot_red);

//--- Передача в торговый класс всех имеющихся коллекций
   engine.TradingOnInit();

//--- Установка магика по умолчанию для всех используемых инструментов
   engine.TradingSetMagic(engine.SetCompositeMagicNumber(magic_number));
//--- Установка синхронной передачи приказов для всех используемых символов
   engine.TradingSetAsyncMode(false);
//--- Установка количества торговых попыток при ошибке
   engine.TradingSetTotalTry(InpTotalAttempts);
//--- Установка корректных типов истечения и заливки ордера всем торговым объектам
   engine.TradingSetCorrectTypeExpiration();
   engine.TradingSetCorrectTypeFilling();

//--- Установка стандартных звуков торговым объектам всех используемых символов
   engine.SetSoundsStandart();
//--- Установка общего флага использования звуков
   engine.SetUseSounds(InpUseSounds);
//--- Установка множителя спреда торговым объектам символов в коллекции символов
   engine.SetSpreadMultiplier(InpSpreadMultiplier);
      
//--- Установка контрольных значений для символов
   //--- Получаем список всех символов коллекции
   CArrayObj *list=engine.GetListAllUsedSymbols();
   if(list!=NULL && list.Total()!=0)
     {
      //--- В цикле по списку устанавливаем нужные значения для отслеживаемых свойств символов
      //--- По умолчанию всем свойствам установлены значения LONG_MAX, что означает "Не отслеживать данное свойство" 
      //--- Включить или выключить (задать величину меньше LONG_MAX или наоборот - установить значение LONG_MAX) можно в любое время в любом месте программы
      /*
      for(int i=0;i<list.Total();i++)
        {
         CSymbol* symbol=list.At(i);
         if(symbol==NULL)
            continue;
         //--- Установка контроля увеличения цены символа на 100 пунктов
         symbol.SetControlBidInc(100000*symbol.Point());
         //--- Установка контроля уменьшения цены символа на 100 пунктов
         symbol.SetControlBidDec(100000*symbol.Point());
         //--- Установка контроля увеличения спреда символа на 40 пунктов
         symbol.SetControlSpreadInc(400);
         //--- Установка контроля уменьшения спреда символа на 40 пунктов
         symbol.SetControlSpreadDec(400);
         //--- Установка контроля размера спреда по значению 40 пунктов
         symbol.SetControlSpreadLevel(400);
        }
      */
     }
//--- Установка контрольных значений для текущего аккаунта
   CAccount* account=engine.GetAccountCurrent();
   if(account!=NULL)
     {
      //--- Установка контроля увеличения значения прибыли на 10
      account.SetControlledValueINC(ACCOUNT_PROP_PROFIT,10.0);
      //--- Установка контроля увеличения значения средств на 15
      account.SetControlledValueINC(ACCOUNT_PROP_EQUITY,15.0);
      //--- Установка контрольного уровня прибыли на 20
      account.SetControlledValueLEVEL(ACCOUNT_PROP_PROFIT,20.0);
     }
  }
//+------------------------------------------------------------------+

И, как ни странно, это все изменения в советнике. Всё остальное уже сегодня нами сделано в кодах библиотеки.

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

Проверим.

Скомпилируем и запустим советник. Отключим любым способом интернет и дождёмся такого значка в правом нижнем углу терминала:


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

Затем подключаем интернет, тем самым восстанавливая связь с торговым сервером:


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

2019.12.05 16:38:32.591 CTrading::PlaceOrder<uint,int,uint,uint>: Invalid request:
2019.12.05 16:38:32.591 No connection with the trade server
2019.12.05 16:38:32.591 Correction of trade request parameters ...
2019.12.05 16:38:32.610 Trading attempt #1. Error: No connection with the trade server
2019.12.05 16:38:32.610 Pending request created #1:
2019.12.05 16:38:32.610 ================== Pending trade request's parameters ==================
2019.12.05 16:38:32.610  - Pending request type: Pending request that was created as a result of the server code
2019.12.05 16:38:32.610  - Trade request ID: 1
2019.12.05 16:38:32.610  - Return code based on which the request was created: 10031 "No connection with the trade server"
2019.12.05 16:38:32.610  - Request creation time: 2019.12.05 11:37:39.054
2019.12.05 16:38:32.610  - Price at time of request create: : 1.10913
2019.12.05 16:38:32.610  - Request activation time: 2019.12.05 11:37:59.054
2019.12.05 16:38:32.610  - Waiting time between trading attempts: 20000 (00:00:20)
2019.12.05 16:38:32.610  - Current trading attempt: Waiting for the onset time of the first trading attempt
2019.12.05 16:38:32.610  - Total trade attempts: 5
2019.12.05 16:38:32.610 ================== Trade request's parameters ==================
2019.12.05 16:38:32.610  - Trade operation type: Place pending order
2019.12.05 16:38:32.610  - Trade symbol: EURUSD
2019.12.05 16:38:32.610  - Requested volume for a deal in lots: 0.10
2019.12.05 16:38:32.610  - Price: 1.10963
2019.12.05 16:38:32.610  - StopLimit level of the order: Value not set
2019.12.05 16:38:32.610  - Stop Loss level of the order: 1.11113
2019.12.05 16:38:32.610  - Take Profit level of the order: 1.10813
2019.12.05 16:38:32.610  - Order type: Pending order Sell Limit
2019.12.05 16:38:32.610  - Order execution type: The order is executed exclusively in the specified volume, otherwise it is canceled (FOK)
2019.12.05 16:38:32.610  - Order expiration type: Good till cancel order
2019.12.05 16:38:32.610  - Order expiration time: Value not set
2019.12.05 16:38:32.610  - Magic number: 24379515
2019.12.05 16:38:32.610  - Order comment: "Pending order SellLimit"
2019.12.05 16:38:32.610  ==================
2019.12.05 16:38:32.610 
2019.12.05 16:38:45.185 Retry trading attempt #1
2019.12.05 16:38:45.185 CTrading::PlaceOrder<double,double,double,double>: Invalid request:
2019.12.05 16:38:45.185 Trading is prohibited for the current account
2019.12.05 16:38:45.185 Correction of trade request parameters ...
2019.12.05 16:38:45.185 Trading operation aborted
2019.12.05 16:38:45.512 Retry trading attempt #2
2019.12.05 16:38:45.512 CTrading::PlaceOrder<double,double,double,double>: Invalid request:
2019.12.05 16:38:45.512 Trading is prohibited for the current account
2019.12.05 16:38:45.512 Correction of trade request parameters ...
2019.12.05 16:38:45.512 Trading operation aborted
2019.12.05 16:38:45.852 Retry trading attempt #3
2019.12.05 16:38:46.405 - Pending order placed: 2019.12.05 11:38:45.933 -
2019.12.05 16:38:46.405 EURUSD Placed 0.10 Pending order Sell Limit #491179168 at price 1.10963, sl 1.11113, tp 1.10813, Magic number 24379515 (123), G1: 4, G2: 7, ID: 1
2019.12.05 16:38:46.472 OnDoEasyEvent: Pending order placed

Что мы тут видим:

Сначала получаем ошибку от торгового сервера "Нет соединения с торговым сервером".
Затем получаем сообщение о создании отложенного торгового запроса с идентификатором #1, в котором прописаны все параметры объекта и параметры структуры запроса в этом объекте.
После чего выполняются две повторные торговые попытки #1 и #2, отсылаемые из отложенного торгового запроса, на что возвращается ошибка отсутствия разрешения торговли на счёте (после восстановления связи не успело включиться разрешение для торговли на счёте).
И с третьей попытки, отсылаемой из объекта-отложенного торгового запроса, отложенный ордер всё же установить удалось.

В описании магического номера выставленного отложенного ордера у нас присутствует магический номер 24379515, затем идентификатор магика, установленный в параметрах советника (123), идентификатор первой группы "G1: 4", идентификатор второй группы "G2: 7" и идентификатор отложенного запроса "ID: 1"

Пожалуйста, имейте в виду:

Настоятельно обращаю ваше внимание на то, что результат торгового класса с отложенными запросами, описанные в данной статье и тестовый советник, прилагаемый к статье, ни в коем случае не стоит пытаться использовать в своих наработках для реальной торговли!
Данная статья, её материалы и результат являются лишь продолжением тестовой концепции отложенных запросов, и в данном виде не являются законченным продуктом для использования на реальных счетах
— только для использования в демо-режиме или тестере.

Что дальше

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

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

К содержанию

Статьи этой серии:

Часть 1. Концепция, организация данных
Часть 2. Коллекция исторических ордеров и сделок
Часть 3. Коллекция рыночных ордеров и позиций, организация поиска
Часть 4. Торговые события. Концепция
Часть 5. Классы и коллекция торговых событий. Отправка событий в программу
Часть 6. События на счёте с типом неттинг
Часть 7. События срабатывания StopLimit-ордеров, подготовка функционала для регистрации событий модификации ордеров и позиций
Часть 8. События модификации ордеров и позиций
Часть 9. Совместимость с MQL4 — Подготовка данных
Часть 10. Совместимость с MQL4 — События открытия позиций и активации отложенных ордеров
Часть 11. Совместимость с MQL4 — События закрытия позиций
Часть 12. Класс объекта "аккаунт", коллекция объектов-аккаунтов
Часть 13. События объекта "аккаунт"
Часть 14. Объект "Символ"
Часть 15. Коллекция объектов-символов
Часть 16. События коллекции символов
Часть 17. Интерактивность объектов библиотеки
Часть 18. Интерактивность объекта-аккаунт и любых других объектов библиотеки
Часть 19. Класс сообщений библиотеки
Часть 20. Создание и хранение ресурсов программы
Часть 21. Торговые классы — Базовый кроссплатформенный торговый объект
Часть 22. Торговые классы — Основной торговый класс, контроль ограничений
Часть 23. Торговые классы — Основной торговый класс, контроль допустимых параметров
Часть 24. Торговые классы — Основной торговый класс, автоматическая коррекция ошибочных параметров
Часть 25. Торговые классы — Основной торговый класс, обработка ошибок, возвращаемых торговым сервером
Часть 26. Работа с отложенными торговыми запросами - первая реализация