English 中文 Español Deutsch 日本語 Português
Библиотека для простого и быстрого создания программ для MetaTrader (Часть XXX): Отложенные торговые запросы - управление объектами-запросами

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

MetaTrader 5Примеры | 3 января 2020, 13:51
3 540 8
Artyom Trishkin
Artyom Trishkin

Содержание


Концепция

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

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

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

Помимо класса управления объектами отложенных запросов мы создадим маленький класс для организации паузы — чтобы не использовать в будущем функцию Sleep(), которая останавливает выполнение программы на время задержки, и объект-пауза позволит нам не зависеть от тиков, чтобы в выходные можно было тестировать код, требующий какого-либо ожидания — пауза будет контролироваться в таймере.

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

Исходя из вышенаписанного, сегодня нам предстоит сделать класс объекта-паузы (его будем использовать в последующих статьях), провести некоторую оптимизацию готового кода по переносу повторяющихся методов разных классов в класс базового объекта, и создать класс управления отложенными запросами. И, естественно, мы доработаем классы объектов отложенных запросов: нам необходимо будет прописать в них дополнительные свойства, которые позволят сравнивать состояния двух взаимосвязанных объектов — текущее состояние свойств ордера и состояние соответствующих свойств объекта-запроса. Это нужно для регистрации факта завершения работы отложенного запроса — чтобы вовремя его удалить из списка активных запросов, находящихся в ожидании наступления времени отсылки запроса на сервер.

И, как уже стало обычным, в первую очередь пропишем в файле Datas.mqh индексы новых сообщений:

   MSG_LIB_TEXT_PEND_REQUEST_STATUS_REMOVE,           // Отложенный запрос на удаление отложенного ордера
   MSG_LIB_TEXT_PEND_REQUEST_STATUS_MODIFY,           // Отложенный запрос на модификацию параметров отложенного ордера
   
   MSG_LIB_TEXT_PEND_REQUEST_ACTUAL_VOLUME,           // Фактический объем
   MSG_LIB_TEXT_PEND_REQUEST_ACTUAL_PRICE,            // Фактическая цена установки ордера
   MSG_LIB_TEXT_PEND_REQUEST_ACTUAL_STOPLIMIT,        // Фактическая цена установки StopLimit-ордера
   MSG_LIB_TEXT_PEND_REQUEST_ACTUAL_SL,               // Фактическая цена установки StopLoss-ордера
   MSG_LIB_TEXT_PEND_REQUEST_ACTUAL_TP,               // Фактическая цена установки TakeProfit-ордера
   MSG_LIB_TEXT_PEND_REQUEST_ACTUAL_TYPE_FILLING,     // Фактический тип заливки ордера
   MSG_LIB_TEXT_PEND_REQUEST_ACTUAL_TYPE_TIME,        // Фактический тип экспирации ордера
   MSG_LIB_TEXT_PEND_REQUEST_ACTUAL_EXPIRATION,       // Фактическое время жизни ордера
   
  };
//+------------------------------------------------------------------+

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

   {"Отложенный запрос на удаление отложенного ордера","Pending request to remove a pending order"},
   {"Отложенный запрос на модификацию параметров отложенного ордера","Pending request to modify pending order parameters"},
   
   {"Фактический объем","Actual volume"},
   {"Фактическая цена установки ордера","Actual order placement price"},
   {"Фактическая цена установки StopLimit-ордера","Actual StopLimit-order placement price"},
   {"Фактическая цена установки StopLoss-ордера","Actual StopLoss-order placement price"},
   {"Фактическая цена установки TakeProfit-ордера","Actual TakeProfit-order placement price"},   
   {"Фактический тип заливки ордера","Actual order filling type"},
   {"Фактический тип экспирации ордера","Actual of order expiration type"},
   {"Фактическое время жизни ордера","Actual of order lifetime"},
   
  };
//+---------------------------------------------------------------------+


Объект-пауза

В папке сервисных классов и функций библиотеки \MQL5\Include\DoEasy\Services\ создадим новый класс CPause в файле Pause.mqh, и сразу же пропишем в него всё необходимое:

//+------------------------------------------------------------------+
//|                                                        Pause.mqh |
//|                        Copyright 2019, MetaQuotes Software Corp. |
//|                             https://mql5.com/ru/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2019, MetaQuotes Software Corp."
#property link      "https://mql5.com/ru/users/artmedia70"
#property version   "1.00"
#property strict    // Нужно для mql4
//+------------------------------------------------------------------+
//| Включаемые файлы                                                 |
//+------------------------------------------------------------------+
#include "DELib.mqh"
//+------------------------------------------------------------------+
//| Класс "Пауза"                                                    |
//+------------------------------------------------------------------+
class CPause
  {
private:
   ulong             m_start;                               // Начало отсчёта
   ulong             m_time_begin;                          // Время начала отсчёта паузы
   ulong             m_wait_msc;                            // Пауза в милисекундах
public:
//--- Устанавливает новое (1) время начала отсчёта, (2) паузу в милисекундах
   void              SetTimeBegin(const ulong time)         { this.m_time_begin=time; this.m_start=::GetTickCount();    }
   void              SetWaitingMSC(const ulong pause)       { this.m_wait_msc=pause;                                    }

//--- Возвращает (1) прошедшее время от начала отсчёта в милисекундах, (2) флаг завершения ожидания
//--- (3) время начала отсчёта паузы, (4) установленный размер паузы в милисекундах
   ulong             Passed(void)                     const { return ::GetTickCount()-this.m_start;                     }
   bool              IsCompleted(void)                const { return this.Passed()>this.m_wait_msc;                     }
   ulong             TimeBegin(void)                  const { return this.m_time_begin;                                 }
   ulong             TimeWait(void)                   const { return this.m_wait_msc;                                   }

//--- Возвращает описание (1) прошедшего времени от начала отсчёта в милисекундах,
//--- (2) времени начала отсчёта паузы, (3) установленного размера паузы в милисекундах
   string            PassedDescription(void)          const { return ::TimeToString(this.Passed()/1000,TIME_SECONDS);   }
   string            TimeBeginDescription(void)       const { return ::TimeMSCtoString(this.m_time_begin);              }
   string            WaitingMSCDescription(void)      const { return (string)this.m_wait_msc;                           }
   string            WaitingSECDescription(void)      const { return ::TimeToString(this.m_wait_msc/1000,TIME_SECONDS); }

//--- Конструктор
                     CPause(void) : m_start(::GetTickCount()){;}
  };
//+------------------------------------------------------------------+

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

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

  • Переменная m_start служит для указания контрольного числа в момент создания объекта-паузы, или для записи в неё нового контрольного числа начала нового отсчёта в ранее созданном объекте-паузе.
  • Переменная m_time_begin хранит заданное время старта отсчёта паузы, и служит лишь для вывода информационных сообщений.
  • Переменная m_wait_msc содержит число милисекунд паузы — по прошествии этого количества милисекунд считается что пауза завершена.

Методы класса, думаю, в пояснениях особо не нуждаются.

  • Метод SetTimeBegin(), устанавливающий новое время начала нового отсчёта, сначала записывает в переменную m_time_begin переданное в него время, а затем устанавливает новое контрольное число начала отсчёта в переменную m_start. Контрольным числом начала любого отсчёта в классе является результат, возвращаемый функцией GetTickCount().
  • Метод Passed(), возвращающий количество прошедших милисекунд паузы, по сути возвращает разницу в милисекундах между текущим замером контрольного времени и числом, установленным в m_start при запуске отсчёта паузы.

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

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

//+------------------------------------------------------------------+
//|                                                        DELib.mqh |
//|                        Copyright 2019, MetaQuotes Software Corp. |
//|                             https://mql5.com/ru/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2019, MetaQuotes Software Corp."
#property link      "https://mql5.com/ru/users/artmedia70"
#property strict  // Нужно для mql4
//+------------------------------------------------------------------+
//| Включаемые файлы                                                 |
//+------------------------------------------------------------------+
#include "..\Defines.mqh"
#include "Message.mqh"
#include "TimerCounter.mqh"
#include "Pause.mqh"
//+------------------------------------------------------------------+


Немного оптимизации кода

Итак, мы решили, что пора перенести повторяющиеся от класса к классу методы в базовый объект всех объектов библиотеки, и унаследовать эти классы от базового объекта (если это ещё не сделано).

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

//+------------------------------------------------------------------+
//| Класс базового объекта для всех объектов библиотеки              |
//+------------------------------------------------------------------+
#define  CONTROLS_TOTAL    (10)
class CBaseObj : public CObject
  {
private:
   int               m_long_prop_total;
   int               m_double_prop_total;
   //--- Заполняет массив свойств объекта
   template<typename T> bool  FillPropertySettings(const int index,T &array[][CONTROLS_TOTAL],T &array_prev[][CONTROLS_TOTAL],int &event_id);
protected:
   CArrayObj         m_list_events_base;                       // Список базовых событий объекта
   CArrayObj         m_list_events;                            // Список событий объекта
   ENUM_LOG_LEVEL    m_log_level;                              // Уровень логирования
   MqlTick           m_tick;                                   // Структура тика для получения котировочных данных
   double            m_hash_sum;                               // Хэш-сумма данных объекта
   double            m_hash_sum_prev;                          // Хэш-сумма данных объекта на прошлой проверке
   int               m_digits_currency;                        // Число знаков после запятой валюты счёта
   int               m_global_error;                           // Код глобальной ошибки
   long              m_chart_id;                               // Идентификатор графика управляющей программы
   bool              m_is_event;                               // Флаг события объекта
   int               m_event_code;                             // Код события объекта
   int               m_event_id;                               // Идентификатор события (равен значению свойства объекта)
   string            m_name;                                   // Наименование объекта
   string            m_folder_name;                            // Имя папки хранения объектов-наследников CBaseObj
   bool              m_first_start;                            // Флаг первого запуска
   int               m_type;                                   // Тип объекта (соответствует идентификаторам коллекций)

//--- Данные для хранения, контроля и возврата отслеживаемых свойств:
//--- [Индекс свойства][0] Контролируемая величина прироста значения свойства
//--- [Индекс свойства][1] Контролируемая величина уменьшения значения свойства
//--- [Индекс свойства][2] Контрольный уровень значения свойства
//--- [Индекс свойства][3] Значение свойства
//--- [Индекс свойства][4] Величина изменения значения свойства
//--- [Индекс свойства][5] Флаг изменения значения свойства больше, чем на величину прироста
//--- [Индекс свойства][6] Флаг изменения значения свойства больше, чем на величину уменьшения
//--- [Индекс свойства][7] Флаг увеличения значения свойства больше контрольного уровня
//--- [Индекс свойства][8] Флаг уменьшения значения свойства меньше контрольного уровня
//--- [Индекс свойства][9] Флаг равенства значения свойства контрольному уровню
   long              m_long_prop_event[][CONTROLS_TOTAL];         // Массив для хранения величин целочисленных свойств объекта и контролируемых значений изменения свойств
   double            m_double_prop_event[][CONTROLS_TOTAL];       // Массив для хранения величин вещественных свойств объекта и контролируемых значений изменения свойств
   long              m_long_prop_event_prev[][CONTROLS_TOTAL];    // Массив для хранения величин контролируемых целочисленных свойств объекта на прошлой проверке
   double            m_double_prop_event_prev[][CONTROLS_TOTAL];  // Массив для хранения величин контролируемых вещественных свойств объекта на прошлой проверке

//--- Возвращает (1) время в милисекундах, (2) милисекунды из значения времени из MqlTick
   long              TickTime(void)                            const { return #ifdef __MQL5__ this.m_tick.time_msc #else this.m_tick.time*1000 #endif ;  }
   ushort            MSCfromTime(const long time_msc)          const { return #ifdef __MQL5__ ushort(this.TickTime()%1000) #else 0 #endif ;              }
//--- возвращает факт наличия кода события в событии объекта
   bool              IsPresentEventFlag(const int change_code) const { return (this.m_event_code & change_code)==change_code; }
//--- Возвращает число знаков после запятой валюты счёта
   int               DigitsCurrency(void)                      const { return this.m_digits_currency; }
//--- Возвращает количество знаков после запятой в double-значении
   int               GetDigits(const double value)             const;

//--- Устанавливает размер массива (1) целочисленных, (2) вещественных контролируемых свойств объекта
   bool              SetControlDataArraySizeLong(const int size);
   bool              SetControlDataArraySizeDouble(const int size);
//--- Проверяет размер массива свойств объекта
   bool              CheckControlDataArraySize(bool check_long=true);
   
//--- Проверяет список изменений свойств объекта и создаёт событие
   void              CheckEvents(void);
   
//--- (1) Упаковывает ushort-число в переданное long-число
   long              UshortToLong(const ushort ushort_value,const uchar to_byte,long &long_value);
   
protected:
//--- (1) преобразует ushort-значение в заданный байт long-числа
   long              UshortToByte(const ushort value,const uchar to_byte)  const;
   
public:
//--- Устанавливает величину контролируемого (1) приращения, (2) уменьшения, (3) контрольный уровень значения свойства объекта
   template<typename T> void  SetControlledValueINC(const int property,const T value);
   template<typename T> void  SetControlledValueDEC(const int property,const T value);
   template<typename T> void  SetControlledValueLEVEL(const int property,const T value);

//--- (1) Устанавливает, (2) возвращает уровень логирования ошибок
   void              SetLogLevel(const ENUM_LOG_LEVEL level)                  { this.m_log_level=level;                                               }
   ENUM_LOG_LEVEL    GetLogLevel(void)                                  const { return this.m_log_level;                                              }
//--- Возвращает установленную величину контролируемого приращения (1) целочисленных, (2) вещественных свойств объекта
   long              GetControlledLongValueINC(const int property)      const { return this.m_long_prop_event[property][0];                           }
   double            GetControlledDoubleValueINC(const int property)    const { return this.m_double_prop_event[property-this.m_long_prop_total][0];  }
//--- Возвращает установленную величину контролируемого уменьшения (1) целочисленных, (2) вещественных свойств объекта
   long              GetControlledLongValueDEC(const int property)      const { return this.m_long_prop_event[property][1];                           }
   double            GetControlledDoubleValueDEC(const int property)    const { return this.m_double_prop_event[property-this.m_long_prop_total][1];  }
//--- Возвращает установленный контрольный уровень (1) целочисленных, (2) вещественных свойств объекта
   long              GetControlledLongValueLEVEL(const int property)    const { return this.m_long_prop_event[property][2];                           }
   double            GetControlledDoubleValueLEVEL(const int property)  const { return this.m_double_prop_event[property-this.m_long_prop_total][2];  }

//--- Возвращает текущее значение (1) целочисленного, (2) вещественного свойства объекта
   long              GetPropLongValue(const int property)               const { return this.m_long_prop_event[property][3];                           }
   double            GetPropDoubleValue(const int property)             const { return this.m_double_prop_event[property-this.m_long_prop_total][3];  }
//--- Возвращает величину изменения контролируемого (1) целочисленного, (2) вещественного свойства объекта
   long              GetPropLongChangedValue(const int property)        const { return this.m_long_prop_event[property][4];                           }
   double            GetPropDoubleChangedValue(const int property)      const { return this.m_double_prop_event[property-this.m_long_prop_total][4];  }
//--- Возвращает Флаг изменения значения (1) целочисленного, (2) вещественного свойства больше, чем на величину прироста
   long              GetPropLongFlagINC(const int property)             const { return this.m_long_prop_event[property][5];                           }
   double            GetPropDoubleFlagINC(const int property)           const { return this.m_double_prop_event[property-this.m_long_prop_total][5];  }
//--- Возвращает Флаг изменения значения (1) целочисленного, (2) вещественного свойства больше, чем на величину уменьшения
   long              GetPropLongFlagDEC(const int property)             const { return this.m_long_prop_event[property][6];                           }
   double            GetPropDoubleFlagDEC(const int property)           const { return this.m_double_prop_event[property-this.m_long_prop_total][6];  }
//--- Возвращает Флаг увеличения значения (1) целочисленного, (2) вещественного свойства больше контрольного уровня
   long              GetPropLongFlagMORE(const int property)            const { return this.m_long_prop_event[property][7];                           }
   double            GetPropDoubleFlagMORE(const int property)          const { return this.m_double_prop_event[property-this.m_long_prop_total][7];  }
//--- Возвращает Флаг уменьшения значения (1) целочисленного, (2) вещественного свойства меньше контрольного уровня
   long              GetPropLongFlagLESS(const int property)            const { return this.m_long_prop_event[property][8];                           }
   double            GetPropDoubleFlagLESS(const int property)          const { return this.m_double_prop_event[property-this.m_long_prop_total][8];  }
//--- Возвращает Флаг равенства значений (1) целочисленного, (2) вещественного свойства и контрольного уровня
   long              GetPropLongFlagEQUAL(const int property)           const { return this.m_long_prop_event[property][9];                           }
   double            GetPropDoubleFlagEQUAL(const int property)         const { return this.m_double_prop_event[property-this.m_long_prop_total][9];  }

//--- Сбрасывает переменные (1) отслеживаемых, (2) контролируемых данных объекта (переназначить можно в наследниках)
   void              ResetChangesParams(void);
   virtual void      ResetControlsParams(void);
//--- Добавляет (1) событие объекта в список, (2) причину события объекта в список
   bool              EventAdd(const ushort event_id,const long lparam,const double dparam,const string sparam);
   bool              EventBaseAdd(const int event_id,const ENUM_BASE_EVENT_REASON reason,const double value);
//--- Устанавливает/Возвращает флаг произошедшего события в данных объекта
   void              SetEvent(const bool flag)                       { this.m_is_event=flag;                   }
   bool              IsEvent(void)                             const { return this.m_is_event;                 }
//--- Возвращает (1) список событий, (2) код события объекта, (3) код глобальной ошибки
   CArrayObj        *GetListEvents(void)                             { return &this.m_list_events;             }
   int               GetEventCode(void)                        const { return this.m_event_code;               }
   int               GetError(void)                            const { return this.m_global_error;             }
//--- Возвращает (1) объект-событие, (2) базовое событие по его номеру в списке
   CEventBaseObj    *GetEvent(const int shift=WRONG_VALUE,const bool check_out=true);
   CBaseEvent       *GetEventBase(const int index);
//--- Возвращает количество (1) событий объекта
   int               GetEventsTotal(void)                      const { return this.m_list_events.Total();      }
//--- (1) Устанавливает, (2) возвращает идентификатор графика управляющей программы
   void              SetChartID(const long id)                       { this.m_chart_id=id;                     }
   long              GetChartID(void)                          const { return this.m_chart_id;                 }
//--- (1) Устанавливает имя подпапки, (2) возвращает имя папки для хранения файлов объектов-наследников
   void              SetSubFolderName(const string name)             { this.m_folder_name=DIRECTORY+name;      }
   string            GetFolderName(void)                       const { return this.m_folder_name;              }
//--- Возвращает наименование объекта
   string            GetName(void)                             const { return this.m_name;                     }
//--- Обновляет данные объекта для поиска изменений в данных объекта (Вызов из наследников: CBaseObj::Refresh())
   virtual void      Refresh(void);
//--- Возвращает тип объекта
   virtual int       Type(void)                                const { return this.m_type;                     }
//--- Возвращает описание события объекта
   string            EventDescription(const int property,
                                      const ENUM_BASE_EVENT_REASON reason,
                                      const int source,
                                      const string value,
                                      const string property_descr,
                                      const int digits);

//--- Расположение данных в int-значении магика
      //-----------------------------------------------------------
      //  bit   32|31       24|23       16|15        8|7         0|
      //-----------------------------------------------------------
      //  byte    |     3     |     2     |     1     |     0     |
      //-----------------------------------------------------------
      //  data    |   uchar   |   uchar   |         ushort        |
      //-----------------------------------------------------------
      //  descr   |pend req id| id2 | id1 |          magic        |
      //-----------------------------------------------------------
      
//--- Устанавливает идентификатор (1) первой группы, (2) второй группы, (3) отложенного запроса в значение магика
   void              SetGroupID1(const uchar group,uint &magic)            { magic &=0xFFF0FFFF; magic |= uint(this.ConvToXX(group,0)<<16);  }
   void              SetGroupID2(const uchar group,uint &magic)            { magic &=0xFF0FFFFF; magic |= uint(this.ConvToXX(group,1)<<16);  }
   void              SetPendReqID(const uchar id,uint &magic)              { magic &=0x00FFFFFF; magic |= (uint)id<<24;                      }
//--- Конветирует значение 0 - 15 в нужные биты uchar-числа (0 - младшие, 1 - старшие)
   uchar             ConvToXX(const uchar number,const uchar index)  const { return((number>15 ? 15 : number)<<(4*(index>1 ? 1 : index)));   }
//--- Возвращает (1) заданный магический номер, идентификатор (2) первой группы, (3) второй группы, (4) отложенного запроса из значения магика
   ushort            GetMagicID(const uint magic)                    const { return ushort(magic & 0xFFFF);                                  }
   uchar             GetGroupID1(const uint magic)                   const { return uchar(magic>>16) & 0x0F;                                 }
   uchar             GetGroupID2(const uint magic)                   const { return uchar((magic>>16) & 0xF0)>>4;                            }
   uchar             GetPendReqID(const uint magic)                  const { return uchar(magic>>24) & 0xFF;                                 }

//--- Конструктор
                     CBaseObj();
  };
//+------------------------------------------------------------------+

Защищённая переменная (доступная наследникам, но не программам), хранящая значения уровня логирования для каждого из объектов-наследников базового объекта, в которых требуется вывод информации в журнал. В неё прописывается уровень логирования из перечисления ENUM_LOG_LEVEL, записанного в Defines.mqh:

//+------------------------------------------------------------------+
//|  Уровень логирования                                             |
//+------------------------------------------------------------------+
enum ENUM_LOG_LEVEL
  {
   LOG_LEVEL_NO_MSG,                                        // Логирование торговли отключено
   LOG_LEVEL_ERROR_MSG,                                     // Только торговые ошибки
   LOG_LEVEL_ALL_MSG                                        // Полное логирование
  };
//+------------------------------------------------------------------+

Установка и возврат значений данной переменной осуществляется публичными методами SetLogLevel() и GetLogLevel() соответственно.

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

Базовый торговый объект символа в файле \MQL5\Include\DoEasy\Objects\Trade\TradeObj.mqh.

Внесём необходимые доработки:
подключим к нему файл базового объекта библиотеки и сделаем его родительским классом класс объекта CBaseObj:

//+------------------------------------------------------------------+
//|                                                     TradeObj.mqh |
//|                        Copyright 2019, MetaQuotes Software Corp. |
//|                             https://mql5.com/ru/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2019, MetaQuotes Software Corp."
#property link      "https://mql5.com/ru/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Включаемые файлы                                                 |
//+------------------------------------------------------------------+
#include "..\..\Services\DELib.mqh"
#include "..\..\Objects\BaseObj.mqh"
//+------------------------------------------------------------------+
//| Класс торгового объекта                                          |
//+------------------------------------------------------------------+
class CTradeObj : public CBaseObj
  {

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

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

   SActions                   m_datas;
   MqlTick                    m_tick;                                            // Структура тика для получения цен
   MqlTradeRequest            m_request;                                         // Структура торгового запроса
   MqlTradeResult             m_result;                                          // Структура результата исполнения торгового запроса
   ENUM_SYMBOL_CHART_MODE     m_chart_mode;                                      // Тип цены для построения баров
   ENUM_ACCOUNT_MARGIN_MODE   m_margin_mode;                                     // Режим расчёта маржи
   ENUM_ORDER_TYPE_FILLING    m_type_filling;                                    // Политика исполнения
   ENUM_ORDER_TYPE_TIME       m_type_time;                                       // Тип ордера по истечению
   int                        m_symbol_expiration_flags;                         // Флаги режимов истечения ордера для символа торгового объекта
   ulong                      m_magic;                                           // Магик
   string                     m_symbol;                                          // Символ
   string                     m_comment;                                         // Комментарий
   ulong                      m_deviation;                                       // Размер проскальзывания в пунктах
   double                     m_volume;                                          // Объём
   datetime                   m_expiration;                                      // Срок истечения ордера (для ордеров типа ORDER_TIME_SPECIFIED)
   bool                       m_async_mode;                                      // Флаг асинхронной отправки торгового запроса
   ENUM_LOG_LEVEL             m_log_level;                                       // Уровень логирования
   int                        m_stop_limit;                                      // Дистанция установки StopLimit ордера в пунктах
   bool                       m_use_sound;                                       // Флаг использования звуков торговых событий объекта
   uint                       m_multiplier;                                      // Множитель спреда для корректировки уровней относительно StopLevel

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

//--- (1) Возвращает режим расчёта маржи (2) флаг счёта хедж
   ENUM_ACCOUNT_MARGIN_MODE   GetMarginMode(void)                                const { return this.m_margin_mode;           }
   bool                       IsHedge(void) const { return this.GetMarginMode()==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING;          }
//--- (1) Устанавливает, (2) возвращает уровень логирования ошибок
   void                       SetLogLevel(const ENUM_LOG_LEVEL level)                  { this.m_log_level=level;              }
   ENUM_LOG_LEVEL             GetLogLevel(void)                                  const { return this.m_log_level;             }
//--- (1) Устанавливает, (2) возвращает политику исполнения
   void                       SetTypeFilling(const ENUM_ORDER_TYPE_FILLING type)       { this.m_type_filling=type;            }
   ENUM_ORDER_TYPE_FILLING    GetTypeFilling(void)                               const { return this.m_type_filling;          }

Из конструктора класса, из его списка инициализации удалим инициализацию уровня логирования:

//+------------------------------------------------------------------+
//| Конструктор                                                      |
//+------------------------------------------------------------------+
CTradeObj::CTradeObj(void) : m_magic(0),
                             m_deviation(5),
                             m_stop_limit(0),
                             m_expiration(0),
                             m_async_mode(false),
                             m_type_filling(ORDER_FILLING_FOK),
                             m_type_time(ORDER_TIME_GTC),
                             m_comment(::MQLInfoString(MQL_PROGRAM_NAME)+" by DoEasy"),
                             m_log_level(LOG_LEVEL_ERROR_MSG)

И пропишем инициализацию уровня логирования в теле метода:

//+------------------------------------------------------------------+
//| Конструктор                                                      |
//+------------------------------------------------------------------+
CTradeObj::CTradeObj(void) : m_magic(0),
                             m_deviation(5),
                             m_stop_limit(0),
                             m_expiration(0),
                             m_async_mode(false),
                             m_type_filling(ORDER_FILLING_FOK),
                             m_type_time(ORDER_TIME_GTC),
                             m_comment(::MQLInfoString(MQL_PROGRAM_NAME)+" by DoEasy")

  {
   //--- Режим расчёта маржи
   this.m_margin_mode=
     (
      #ifdef __MQL5__ (ENUM_ACCOUNT_MARGIN_MODE)::AccountInfoInteger(ACCOUNT_MARGIN_MODE)
      #else /* MQL4 */ ACCOUNT_MARGIN_MODE_RETAIL_HEDGING #endif 
     );
   //--- Множитель спреда
   this.m_multiplier=1;
   //--- Установка звуков и флагов использования звуков по умолчанию
   this.m_use_sound=false;
   this.m_log_level=LOG_LEVEL_ERROR_MSG;
   this.InitSounds();
  }
//+------------------------------------------------------------------+


Объект базового абстрактного ордера в файле \MQL5\Include\DoEasy\Objects\Orders\Order.mqh.

Внесём необходимые доработки:
подключим к нему файл базового объекта библиотеки и сделаем его родительским классом класс объекта CBaseObj:

//+------------------------------------------------------------------+
//|                                                        Order.mqh |
//|                        Copyright 2019, MetaQuotes Software Corp. |
//|                             https://mql5.com/ru/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2019, MetaQuotes Software Corp."
#property link      "https://mql5.com/ru/users/artmedia70"
#property version   "1.00"
#property strict    // Нужно для mql4
//+------------------------------------------------------------------+
//| Включаемые файлы                                                 |
//+------------------------------------------------------------------+
#include <Object.mqh>
#include "..\..\Services\DELib.mqh"
#include "..\..\Objects\BaseObj.mqh"
//+------------------------------------------------------------------+
//| Класс абстрактного ордера                                        |
//+------------------------------------------------------------------+
class COrder : public CBaseObj
  {

Из приватной секции класса удалим методы получения идентификаторов, записанных в магике ордера:
(табличку с напоминанием о расположении значений идентификаторов в значении магика оставим)

//+------------------------------------------------------------------+
//| Класс абстрактного ордера                                        |
//+------------------------------------------------------------------+
class COrder : public CObject
  {
private:
   ulong             m_ticket;                                    // Тикет выбранного ордера/сделки (MQL5)
   long              m_long_prop[ORDER_PROP_INTEGER_TOTAL];       // Целочисленные свойства
   double            m_double_prop[ORDER_PROP_DOUBLE_TOTAL];      // Вещественные свойства
   string            m_string_prop[ORDER_PROP_STRING_TOTAL];      // Строковые свойства

//--- Возвращает индекс массива, по которому фактически расположено (1) double-свойство и (2) string-свойство ордера
   int               IndexProp(ENUM_ORDER_PROP_DOUBLE property)      const { return(int)property-ORDER_PROP_INTEGER_TOTAL;                         }
   int               IndexProp(ENUM_ORDER_PROP_STRING property)      const { return(int)property-ORDER_PROP_INTEGER_TOTAL-ORDER_PROP_DOUBLE_TOTAL; }

//--- Расположение данных в int-значении магика                    
      //-----------------------------------------------------------
      //  bit   32|31       24|23       16|15        8|7         0|
      //-----------------------------------------------------------
      //  byte    |     3     |     2     |     1     |     0     |
      //-----------------------------------------------------------
      //  data    |   uchar   |   uchar   |         ushort        |
      //-----------------------------------------------------------
      //  descr   |pend req id| id2 | id1 |          magic        |
      //-----------------------------------------------------------
//--- Возвращает (1) заданный магический номер, идентификатор (2) первой группы, (3) второй группы, (4) отложенного запроса из значения магика
   ushort            GetMagicID(void)                                const { return ushort(this.Magic() & 0xFFFF);                                 }
   uchar             GetGroupID1(void)                               const { return uchar(this.Magic()>>16) & 0x0F;                                }
   uchar             GetGroupID2(void)                               const { return uchar((this.Magic()>>16) & 0xF0)>>4;                           }
   uchar             GetPendReqID(void)                              const { return uchar(this.Magic()>>24) & 0xFF;                                }

public:

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

//+------------------------------------------------------------------+
//| Закрытый параметрический конструктор                             |
//+------------------------------------------------------------------+
COrder::COrder(ENUM_ORDER_STATUS order_status,const ulong ticket)
  {
//--- Сохранение целочисленных свойств
   this.m_ticket=ticket;
   this.m_long_prop[ORDER_PROP_STATUS]                               = order_status;
   this.m_long_prop[ORDER_PROP_MAGIC]                                = this.OrderMagicNumber();
   this.m_long_prop[ORDER_PROP_TICKET]                               = this.OrderTicket();
   this.m_long_prop[ORDER_PROP_TIME_EXP]                             = this.OrderExpiration();
   this.m_long_prop[ORDER_PROP_TYPE_FILLING]                         = this.OrderTypeFilling();
   this.m_long_prop[ORDER_PROP_TYPE_TIME]                            = this.OrderTypeTime();
   this.m_long_prop[ORDER_PROP_TYPE]                                 = this.OrderType();
   this.m_long_prop[ORDER_PROP_STATE]                                = this.OrderState();
   this.m_long_prop[ORDER_PROP_DIRECTION]                            = this.OrderTypeByDirection();
   this.m_long_prop[ORDER_PROP_POSITION_ID]                          = this.OrderPositionID();
   this.m_long_prop[ORDER_PROP_REASON]                               = this.OrderReason();
   this.m_long_prop[ORDER_PROP_DEAL_ORDER_TICKET]                    = this.DealOrderTicket();
   this.m_long_prop[ORDER_PROP_DEAL_ENTRY]                           = this.DealEntry();
   this.m_long_prop[ORDER_PROP_POSITION_BY_ID]                       = this.OrderPositionByID();
   this.m_long_prop[ORDER_PROP_TIME_OPEN]                            = this.OrderOpenTimeMSC();
   this.m_long_prop[ORDER_PROP_TIME_CLOSE]                           = this.OrderCloseTimeMSC();
   this.m_long_prop[ORDER_PROP_TIME_UPDATE]                          = this.PositionTimeUpdateMSC();
   
//--- Сохранение вещественных свойств
   this.m_double_prop[this.IndexProp(ORDER_PROP_PRICE_OPEN)]         = this.OrderOpenPrice();
   this.m_double_prop[this.IndexProp(ORDER_PROP_PRICE_CLOSE)]        = this.OrderClosePrice();
   this.m_double_prop[this.IndexProp(ORDER_PROP_PROFIT)]             = this.OrderProfit();
   this.m_double_prop[this.IndexProp(ORDER_PROP_COMMISSION)]         = this.OrderCommission();
   this.m_double_prop[this.IndexProp(ORDER_PROP_SWAP)]               = this.OrderSwap();
   this.m_double_prop[this.IndexProp(ORDER_PROP_VOLUME)]             = this.OrderVolume();
   this.m_double_prop[this.IndexProp(ORDER_PROP_SL)]                 = this.OrderStopLoss();
   this.m_double_prop[this.IndexProp(ORDER_PROP_TP)]                 = this.OrderTakeProfit();
   this.m_double_prop[this.IndexProp(ORDER_PROP_VOLUME_CURRENT)]     = this.OrderVolumeCurrent();
   this.m_double_prop[this.IndexProp(ORDER_PROP_PRICE_STOP_LIMIT)]   = this.OrderPriceStopLimit();
   
//--- Сохранение строковых свойств
   this.m_string_prop[this.IndexProp(ORDER_PROP_SYMBOL)]             = this.OrderSymbol();
   this.m_string_prop[this.IndexProp(ORDER_PROP_COMMENT)]            = this.OrderComment();
   this.m_string_prop[this.IndexProp(ORDER_PROP_EXT_ID)]             = this.OrderExternalID();
   
//--- Сохранение дополнительных целочисленных свойств
   this.m_long_prop[ORDER_PROP_PROFIT_PT]                            = this.ProfitInPoints();
   this.m_long_prop[ORDER_PROP_TICKET_FROM]                          = this.OrderTicketFrom();
   this.m_long_prop[ORDER_PROP_TICKET_TO]                            = this.OrderTicketTo();
   this.m_long_prop[ORDER_PROP_CLOSE_BY_SL]                          = this.OrderCloseByStopLoss();
   this.m_long_prop[ORDER_PROP_CLOSE_BY_TP]                          = this.OrderCloseByTakeProfit();
   this.m_long_prop[ORDER_PROP_MAGIC_ID]                             = this.GetMagicID((uint)this.GetProperty(ORDER_PROP_MAGIC));
   this.m_long_prop[ORDER_PROP_GROUP_ID1]                            = this.GetGroupID1((uint)this.GetProperty(ORDER_PROP_MAGIC));
   this.m_long_prop[ORDER_PROP_GROUP_ID2]                            = this.GetGroupID2((uint)this.GetProperty(ORDER_PROP_MAGIC));
   this.m_long_prop[ORDER_PROP_PEND_REQ_ID]                          = this.GetPendReqID((uint)this.GetProperty(ORDER_PROP_MAGIC));
   
//--- Сохранение дополнительных вещественных свойств
   this.m_double_prop[this.IndexProp(ORDER_PROP_PROFIT_FULL)]        = this.ProfitFull();
   
//--- Сохранение дополнительных строковых свойств
   this.m_string_prop[this.IndexProp(ORDER_PROP_COMMENT_EXT)]        = "";
  }
//+------------------------------------------------------------------+


Основной торговый класс в файле \MQL5\Include\DoEasy\Trading.mqh.

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

//+------------------------------------------------------------------+
//| Торговый класс                                                   |
//+------------------------------------------------------------------+
class CTrading : public CBaseObj
  {
protected:
   CAccount            *m_account;                       // Указатель на объект-текущий аккаунт
   CSymbolsCollection  *m_symbols;                       // Указатель на список коллекции символов
   CMarketCollection   *m_market;                        // Указатель на список коллекции рыночных ордеров и позиций
   CHistoryCollection  *m_history;                       // Указатель на список коллекции исторических ордеров и сделок
   CEventsCollection   *m_events;                        // Указатель на список коллекции событий
   CArrayObj            m_list_request;                  // Список отложенных запросов
private:

Из приватной секции удалим переменную для хранения уровня логирования:

private:
   CArrayInt            m_list_errors;                   // Список ошибок
   bool                 m_is_trade_disable;              // Флаг запрета торговли
   bool                 m_use_sound;                     // Флаг использования звуков торговых событий объекта
   uchar                m_total_try;                     // Количество торговых попыток
   ENUM_LOG_LEVEL       m_log_level;                     // Уровень логирования
   MqlTradeRequest      m_request;                       // Цены торгового запроса
   ENUM_TRADE_REQUEST_ERR_FLAGS m_error_reason_flags;    // Флаги причин ошибок в торговом методе
   ENUM_ERROR_HANDLING_BEHAVIOR m_err_handling_behavior; // Поведение при обработке ошибок

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

public:
//--- Возвращает себя
   CTrading            *GetObject(void)    { return &this;   }
//--- Конструктор
                        CTrading();
//--- Таймер
   virtual void         OnTimer(void);
//--- Получение указателей на списки (вызывать метод обязательно в OnInit() программы, так как список коллекции символов создаётся там)

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

//--- Создаёт отложенный запрос
   bool                 CreatePendingRequest(const ENUM_PEND_REQ_STATUS status,
                                             const uchar id,
                                             const uchar attempts,
                                             const ulong wait,
                                             const MqlTradeRequest &request,
                                             const int retcode,
                                             CSymbol *symbol_obj,
                                             COrder *order);

//--- Расположение данных в int-значении магика
      //-----------------------------------------------------------
      //  bit   32|31       24|23       16|15        8|7         0|
      //-----------------------------------------------------------
      //  byte    |     3     |     2     |     1     |     0     |
      //-----------------------------------------------------------
      //  data    |   uchar   |   uchar   |         ushort        |
      //-----------------------------------------------------------
      //  descr   |pend req id| id2 | id1 |          magic        |
      //-----------------------------------------------------------
      
//--- Устанавливает идентификатор (1) первой группы, (2) второй группы, (3) отложенного запроса в значение магика
   void              SetGroupID1(const uchar group,uint &magic)            { magic &=0xFFF0FFFF; magic |= uint(ConvToXX(group,0)<<16);    }
   void              SetGroupID2(const uchar group,uint &magic)            { magic &=0xFF0FFFFF; magic |= uint(ConvToXX(group,1)<<16);    }
   void              SetPendReqID(const uchar id,uint &magic)              { magic &=0x00FFFFFF; magic |= (uint)id<<24;                   }
//--- Конветирует значение 0 - 15 в нужные биты uchar-числа (0 - младшие, 1 - старшие)
   uchar             ConvToXX(const uchar number,const uchar index)  const { return((number>15 ? 15 : number)<<(4*(index>1 ? 1 : index)));}
//--- Возвращает (1) заданный магический номер, идентификатор (2) первой группы, (3) второй группы, (4) отложенного запроса из значения магика
   ushort            GetMagicID(const uint magic)                    const { return ushort(magic & 0xFFFF);                               }
   uchar             GetGroupID1(const uint magic)                   const { return uchar(magic>>16) & 0x0F;                              }
   uchar             GetGroupID2(const uint magic)                   const { return uchar((magic>>16) & 0xF0)>>4;                         }
   uchar             GetPendReqID(const uint magic)                  const { return uchar(magic>>24) & 0xFF;                              }

  };
//+------------------------------------------------------------------+

Из реализации таймера класса удалим полностью всё, оставив его пустым:

//+------------------------------------------------------------------+
//| Таймер                                                           |
//+------------------------------------------------------------------+
void CTrading::OnTimer(void)
  {
  }
//+------------------------------------------------------------------+

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

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

//--- Если закрытие, или не удаление/модификация
   if(action_type<ACTION_TYPE_CLOSE_BY || action_type==ACTION_TYPE_CLOSE)
     {
      //--- Если разрешены только операции закрытия позиций - записываем код ошибки в список и возвращаем false - нет смысла проверять далее
      if(symbol_obj.TradeMode()==SYMBOL_TRADE_MODE_CLOSEONLY)
        {
         this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_ERROR_IN_LIST;
         this.AddErrorCodeToList(MSG_SYM_TRADE_MODE_CLOSEONLY);
         return false;
        }
      //--- Проверка минимального объёма
      if(volume>0)
        {
         if(volume<symbol_obj.LotsMin())
           {
            //--- Объём в запросе меньше минимально-допустимого.
            //--- добавляем код ошибки в список
            this.m_error_reason_flags |=TRADE_REQUEST_ERR_FLAG_ERROR_IN_LIST;
            this.AddErrorCodeToList(MSG_LIB_TEXT_REQ_VOL_LESS_MIN_VOLUME);
            //--- Если поведение советника при торговой ошибке "прервать торговую операцию", то
            //--- возвращаем false - нет смысла проверять далее
            if(this.m_err_handling_behavior==ERROR_HANDLING_BEHAVIOR_BREAK)
               return false;
            //--- Если поведение советника при торговой ошибке указано как
            //--- "скорректировать параметры" или "создать отложенный запрос", то
            //--- записываем в результат false
            else res &=false;
           }
         //--- Проверка максимального объёма
         else if(volume>symbol_obj.LotsMax())
           {
            //--- Объём в запросе больше максимально-допустимого.
            //--- добавляем код ошибки в список
            this.m_error_reason_flags |=TRADE_REQUEST_ERR_FLAG_ERROR_IN_LIST;
            this.AddErrorCodeToList(MSG_LIB_TEXT_REQ_VOL_MORE_MAX_VOLUME);
            //--- Если поведение советника при торговой ошибке "прервать торговую операцию", то
            //--- возвращаем false - нет смысла проверять далее
            if(this.m_err_handling_behavior==ERROR_HANDLING_BEHAVIOR_BREAK)
               return false;
            //--- Если поведение советника при торговой ошибке указано как
            //--- "скорректировать параметры" или "создать отложенный запрос", то
            //--- записываем в результат false
            else res &=false;
           }
         //--- Проверка минимальной градации объема
         double step=symbol_obj.LotsStep();
         if(fabs((int)round(volume/step)*step-volume)>0.0000001)
           {
            //--- Объём в запросе не кратен минимальной градации шага изменения лота
            //--- добавляем код ошибки в список
            this.m_error_reason_flags |=TRADE_REQUEST_ERR_FLAG_ERROR_IN_LIST;
            this.AddErrorCodeToList(MSG_LIB_TEXT_INVALID_VOLUME_STEP);
            //--- Если поведение советника при торговой ошибке "прервать торговую операцию", то
            //--- возвращаем false - нет смысла проверять далее
            if(this.m_err_handling_behavior==ERROR_HANDLING_BEHAVIOR_BREAK)
               return false;
            //--- Если поведение советника при торговой ошибке указано как
            //--- "скорректировать параметры" или "создать отложенный запрос", то
            //--- записываем в результат false
            else res &=false;
           }
        }
     }

//--- Если открытие позиции

Далее:

Во всех торговых методах там, где требуется создание объекта отложенного запроса, в метод передаётся объект-ордер. А там, где такого объекта нет (в методах открытия позиции и установки отложенных ордеров) — передаём в метод NULL.

В методе открытия позиции передаём NULL последним параметром:

      //--- Если результат проверки "ожидание" - устанавливаем код последней ошибки в структуру возврата,
      //--- создаём отложенный запрос и возвращаем false (OpenPosition)
      if(method>ERROR_CODE_PROCESSING_METHOD_REFRESH)
        {
         //--- Если в магике торгового запроса нет идентификатора отложенного запроса
         if(this.GetPendReqID((uint)magic)==0)
           {
            //--- Проиграем звук ошибки
            if(this.IsUseSounds())
               trade_obj.PlaySoundError(action,order_type);
            //--- устанавливаем код последней ошибки в структуру возврата
            int code=this.m_list_errors.At(this.m_list_errors.Total()-1);
            if(code!=NULL)
              {
               if(code==MSG_LIB_TEXT_TERMINAL_NOT_TRADE_ENABLED || code==MSG_LIB_TEXT_EA_NOT_TRADE_ENABLED)
                  code=10027;
               trade_obj.SetResultRetcode(code);
               trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode()));
              }
            //--- Время ожидания в милисекундах:
            //--- для метода обработки "Подождать и повторить" - значение ожидания соответствует значению method,
            ulong wait=method;
            //--- Ищем наименьший из возможных идентификаторов, и если найти не удалось,
            //--- или произошла ошибка обновления текущих данных символа - возвращаем 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.action=TRADE_ACTION_DEAL;
            this.m_request.symbol=symbol_obj.Name();
            this.m_request.type=order_type;
            //--- Устанавливаем количество торговых попыток и создаём отложенный запрос
            uchar attempts=(this.m_total_try < 1 ? 1 : this.m_total_try);
            this.CreatePendingRequest(PEND_REQ_STATUS_OPEN,(uchar)id,attempts,wait,this.m_request,trade_obj.GetResultRetcode(),symbol_obj,NULL);
           }
         return false;
        }

И в методе установки отложенного ордера точно так же передаём NULL в качестве объекта-ордера в метод создания отложенного запроса.

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

      //--- Если результат проверки "ожидание" - устанавливаем код последней ошибки в структуру возврата,
      //--- создаём отложенный запрос и возвращаем false (ModifyPosition)
      if(method>ERROR_CODE_PROCESSING_METHOD_REFRESH)
        {
         //--- Если объекта-отложенного запроса с тикетом позиции нет в списке
         if(this.GetIndexPendingRequestByPosition(ticket)==WRONG_VALUE)
           {
            //--- Проиграем звук ошибки
            if(this.IsUseSounds())
               trade_obj.PlaySoundError(action,order_type,(sl<0 ? false : true),(tp<0 ? false : true));
            //--- устанавливаем код последней ошибки в структуру возврата
            int code=this.m_list_errors.At(this.m_list_errors.Total()-1);
            if(code!=NULL)
              {
               if(code==MSG_LIB_TEXT_TERMINAL_NOT_TRADE_ENABLED || code==MSG_LIB_TEXT_EA_NOT_TRADE_ENABLED)
                  code=10027;
               trade_obj.SetResultRetcode(code);
               trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode()));
              }
            //--- Время ожидания в милисекундах:
            //--- для метода обработки "Подождать и повторить" - значение ожидания соответствует значению method,
            ulong wait=method;
            //--- Ищем наименьший из возможных идентификаторов, и если найти не удалось,
            //--- или произошла ошибка обновления текущих данных символа - возвращаем false
            int id=this.GetFreeID();
            if(id<1 || !symbol_obj.RefreshRates())
               return false;
            //--- Записываем тип проводимой операции, символ и тикет модифицируемой позиции в структуру запроса
            this.m_request.action=TRADE_ACTION_SLTP;
            this.m_request.symbol=symbol_obj.Name();
            this.m_request.position=ticket;
            this.m_request.type=order_type;
            this.m_request.volume=order.Volume();
            //--- Устанавливаем количество торговых попыток и создаём отложенный запрос
            uchar attempts=(this.m_total_try < 1 ? 1 : this.m_total_try);
            this.CreatePendingRequest(PEND_REQ_STATUS_SLTP,(uchar)id,attempts,wait,this.m_request,trade_obj.GetResultRetcode(),symbol_obj,order);
           }
         return false;
        }

И в остальных методах, в которых известен ордер или позиция, передаём объект-ордер в метод создания отложенного запроса.

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

//--- Записываем отклонение и комментарий в структуру запроса
   this.m_request.deviation=(deviation==ULONG_MAX ? trade_obj.GetDeviation() : deviation);
   this.m_request.comment=(comment==NULL ? trade_obj.GetComment() : comment);
   this.m_request.volume=(volume==WRONG_VALUE || volume>order.Volume() ? order.Volume() : symbol_obj.NormalizedLot(volume));
//--- Если есть ограничения по разрешённости торговли, объёму
//--- есть ограничения по уровню FreezeLevel - воспроизводим звук ошибки и уходим
   ENUM_ERROR_CODE_PROCESSING_METHOD method=this.CheckErrors(volume,0,action,order_type,symbol_obj,trade_obj,DFUN,0,0,0,ticket);
   if(method!=ERROR_CODE_PROCESSING_METHOD_OK)
     {

Здесь мы записываем в структуру торгового запроса объём таким образом: если в метод передано значение -1 или значение объёма, переданного в метод, больше объёма закрываемой позиции, то записываем в структуру полный объём позиции (полное закрытие). Иначе — в структуру записываем нормализованный объём, переданный в метод (он может прийти некорректным, например, при делении объёма 0.05 на 2 при частичном закрытии позиции, в метод придёт объём 0.025, что вызовет ошибку)

В методе модификации отложенного ордера ModifyOrder()
впишем дополнительно в структуру торгового запроса идентификатор магика и объём ордера:

//--- Записываем магик, объём, тип заливки, дату и тип экспирации в структуру запроса
   this.m_request.magic=order.GetMagicID((uint)order.Magic());
   this.m_request.volume=order.Volume();
   this.m_request.type_filling=(type_filling>WRONG_VALUE ? type_filling : order.TypeFilling());
   this.m_request.expiration=(expiration>WRONG_VALUE ? expiration : order.TimeExpiration());
   this.m_request.type_time=(type_time>WRONG_VALUE ? type_time : order.TypeTime());

//--- Если есть ограничения по разрешённости торговли,
//--- есть ограничения по уровням StopLevel или FreezeLevel - воспроизводим звук ошибки и уходим
   ENUM_ERROR_CODE_PROCESSING_METHOD method=this.CheckErrors(0,this.m_request.price,action,order_type,symbol_obj,trade_obj,DFUN,this.m_request.stoplimit,this.m_request.sl,this.m_request.tp,ticket);
   if(method!=ERROR_CODE_PROCESSING_METHOD_OK)
     {

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

Метод создания отложенного запроса претерпел небольшие изменения:

//+------------------------------------------------------------------+
//| Создаёт отложенный запрос                                        |
//+------------------------------------------------------------------+
bool CTrading::CreatePendingRequest(const ENUM_PEND_REQ_STATUS status,
                                    const uchar id,
                                    const uchar attempts,
                                    const ulong wait,
                                    const MqlTradeRequest &request,
                                    const int retcode,
                                    CSymbol *symbol_obj,
                                    COrder *order)
  {
   //--- Создаём новый объект-отложенный запрос в зависимости от статуса запроса
   CPendRequest *req_obj=NULL;
   switch(status)
     {
      case PEND_REQ_STATUS_OPEN     : req_obj=new CPendReqOpen(id,symbol_obj.BidLast(),symbol_obj.Time(),request,retcode);    break;
      case PEND_REQ_STATUS_CLOSE    : req_obj=new CPendReqClose(id,symbol_obj.BidLast(),symbol_obj.Time(),request,retcode);   break;
      case PEND_REQ_STATUS_SLTP     : req_obj=new CPendReqSLTP(id,symbol_obj.BidLast(),symbol_obj.Time(),request,retcode);    break;
      case PEND_REQ_STATUS_PLACE    : req_obj=new CPendReqPlace(id,symbol_obj.BidLast(),symbol_obj.Time(),request,retcode);   break;
      case PEND_REQ_STATUS_REMOVE   : req_obj=new CPendReqRemove(id,symbol_obj.BidLast(),symbol_obj.Time(),request,retcode);  break;
      case PEND_REQ_STATUS_MODIFY   : req_obj=new CPendReqModify(id,symbol_obj.BidLast(),symbol_obj.Time(),request,retcode);  break;
      default: req_obj=NULL;
        break;
     }
   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(order!=NULL)
     {
      req_obj.SetActualVolume(order.Volume());
      req_obj.SetActualPrice(order.PriceOpen());
      req_obj.SetActualStopLimit(order.PriceStopLimit());
      req_obj.SetActualSL(order.StopLoss());
      req_obj.SetActualTP(order.TakeProfit());
      req_obj.SetActualTypeFilling(order.TypeFilling());
      req_obj.SetActualTypeTime(order.TypeTime());
      req_obj.SetActualExpiration(order.TimeExpiration());
     }
   else
     {
      req_obj.SetActualVolume(request.volume);
      req_obj.SetActualPrice(request.price);
      req_obj.SetActualStopLimit(request.stoplimit);
      req_obj.SetActualSL(request.sl);
      req_obj.SetActualTP(request.tp);
      req_obj.SetActualTypeFilling(request.type_filling);
      req_obj.SetActualTypeTime(request.type_time);
      req_obj.SetActualExpiration(request.expiration);
     }
   //--- Выводим краткое описание созданного отложенного запроса
   if(this.m_log_level>LOG_LEVEL_NO_MSG)
     {
      ::Print(CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_CREATED)," #",req_obj.ID(),":");
      req_obj.PrintShort();
     }
   //--- успешно
   return true;
  }
//+------------------------------------------------------------------+

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

Вот в методе создания отложенного торгового запроса мы и проверяем что передано в качестве объекта-ордера. Если передан не NULL (объект-ордер существует), то в объекте-отложенном запросе устанавливаются первоначальные параметры из объекта-ордера. Иначе — параметры устанавливаются из структуры торгового запроса.

Это все изменения основного торгового класса.

Теперь займёмся доработкой класса объекта отложенного запроса.

У нас появились новые свойства объекта-отложенного запроса. Запишем их в файл Defines.mqh.

В целочисленные свойства отложенного запроса добавим новые свойства, и изменим количество целочисленных свойств с 19 на 22:

//+------------------------------------------------------------------+
//| Целочисленные свойства отложенного торгового запроса             |
//+------------------------------------------------------------------+
enum ENUM_PEND_REQ_PROP_INTEGER
  {
   PEND_REQ_PROP_STATUS = 0,                                // Статус торгового запроса (из перечисления ENUM_PEND_REQ_STATUS)
   PEND_REQ_PROP_TYPE,                                      // Тип торгового запроса (из перечисления ENUM_PEND_REQ_TYPE)
   PEND_REQ_PROP_ID,                                        // Идентификатор торгового запроса
   PEND_REQ_PROP_RETCODE,                                   // Результат, на основании которого создан запрос
   PEND_REQ_PROP_TIME_CREATE,                               // Время создания запроса
   PEND_REQ_PROP_TIME_ACTIVATE,                             // Время активации очередной попытки
   PEND_REQ_PROP_WAITING,                                   // Продолжительность ожидания между запросами
   PEND_REQ_PROP_CURRENT_ATTEMPT,                           // Номер текущей попытки
   PEND_REQ_PROP_TOTAL,                                     // Количество попыток
   PEND_REQ_PROP_ACTUAL_TYPE_FILLING,                       // Фактический тип заливки ордера
   PEND_REQ_PROP_ACTUAL_TYPE_TIME,                          // Фактический тип экспирации ордера
   PEND_REQ_PROP_ACTUAL_EXPIRATION,                         // Фактическое время жизни ордера
   //--- MqlTradeRequest
   PEND_REQ_PROP_MQL_REQ_ACTION,                            // Тип выполняемого действия в структуре запроса
   PEND_REQ_PROP_MQL_REQ_TYPE,                              // Тип ордера в структуре запроса
   PEND_REQ_PROP_MQL_REQ_MAGIC,                             // Штамп эксперта (идентификатор magic number) в структуре запроса
   PEND_REQ_PROP_MQL_REQ_ORDER,                             // Тикет ордера в структуре запроса
   PEND_REQ_PROP_MQL_REQ_POSITION,                          // Тикет позиции в структуре запроса
   PEND_REQ_PROP_MQL_REQ_POSITION_BY,                       // Тикет встречной позиции в структуре запроса
   PEND_REQ_PROP_MQL_REQ_DEVIATION,                         // Максимально приемлемое отклонение от запрашиваемой цены в структуре запроса
   PEND_REQ_PROP_MQL_REQ_EXPIRATION,                        // Срок истечения ордера (для ордеров типа ORDER_TIME_SPECIFIED) в структуре запроса
   PEND_REQ_PROP_MQL_REQ_TYPE_FILLING,                      // Тип ордера по исполнению в структуре запроса
   PEND_REQ_PROP_MQL_REQ_TYPE_TIME,                         // Тип ордера по времени действия в структуре запроса
  }; 
#define PEND_REQ_PROP_INTEGER_TOTAL (22)                    // Общее количество целочисленных свойств события
#define PEND_REQ_PROP_INTEGER_SKIP  (0)                     // Количество неиспользуемых в сортировке свойств запроса
//+------------------------------------------------------------------+

В список вещественных свойств добавим новые свойства и изменим их количество с 6 до 11:

//+------------------------------------------------------------------+
//| Вещественные свойства отложенного торгового запроса              |
//+------------------------------------------------------------------+
enum ENUM_PEND_REQ_PROP_DOUBLE
  {
   PEND_REQ_PROP_PRICE_CREATE = PEND_REQ_PROP_INTEGER_TOTAL,// Цена при создании запроса
   PEND_REQ_PROP_ACTUAL_VOLUME,                             // Фактический объем
   PEND_REQ_PROP_ACTUAL_PRICE,                              // Фактическая цена установки ордера
   PEND_REQ_PROP_ACTUAL_STOPLIMIT,                          // Фактическая цена установки stoplimit-ордера
   PEND_REQ_PROP_ACTUAL_SL,                                 // Фактическая цена установки stoploss-ордера
   PEND_REQ_PROP_ACTUAL_TP,                                 // Фактическая цена установки takeprofit-ордера
   //--- MqlTradeRequest
   PEND_REQ_PROP_MQL_REQ_VOLUME,                            // Запрашиваемый объем сделки в лотах в структуре запроса
   PEND_REQ_PROP_MQL_REQ_PRICE,                             // Цена в структуре запроса
   PEND_REQ_PROP_MQL_REQ_STOPLIMIT,                         // Уровень StopLimit ордера в структуре запроса
   PEND_REQ_PROP_MQL_REQ_SL,                                // Уровень Stop Loss ордера в структуре запроса
   PEND_REQ_PROP_MQL_REQ_TP,                                // Уровень Take Profit ордера в структуре запроса
  };
#define PEND_REQ_PROP_DOUBLE_TOTAL  (11)                    // Общее количество вещественных свойств события
#define PEND_REQ_PROP_DOUBLE_SKIP   (0)                     // Количество неиспользуемых в сортировке свойств события
//+------------------------------------------------------------------+

В список возможных критериев сортировки добавим сортировку по новым свойствам:

//+------------------------------------------------------------------+
//| Возможные критерии сортировки отложенных запросов                |
//+------------------------------------------------------------------+
#define FIRST_PREQ_DBL_PROP         (PEND_REQ_PROP_INTEGER_TOTAL-PEND_REQ_PROP_INTEGER_SKIP)
#define FIRST_PREQ_STR_PROP         (PEND_REQ_PROP_INTEGER_TOTAL-PEND_REQ_PROP_INTEGER_SKIP+PEND_REQ_PROP_DOUBLE_TOTAL-PEND_REQ_PROP_DOUBLE_SKIP)
enum ENUM_SORT_PEND_REQ_MODE
  {
//--- Сортировка по целочисленным свойствам
   SORT_BY_PEND_REQ_STATUS = 0,                             // Сортировать по статусу торгового запроса (из перечисления ENUM_PEND_REQ_STATUS)
   SORT_BY_PEND_REQ_TYPE,                                   // Сортировать по типу торгового запроса (из перечисления ENUM_PEND_REQ_TYPE)
   SORT_BY_PEND_REQ_ID,                                     // Сортировать по идентификатору торгового запроса
   SORT_BY_PEND_REQ_RETCODE,                                // Сортировать по результату, на основании которого создан запрос
   SORT_BY_PEND_REQ_TIME_CREATE,                            // Сортировать по времени создания запроса
   SORT_BY_PEND_REQ_TIME_ACTIVATE,                          // Сортировать по времени активации очередной попытки
   SORT_BY_PEND_REQ_WAITING,                                // Сортировать по продолжительности ожидания между запросами
   SORT_BY_PEND_REQ_CURENT,                                 // Сортировать по номеру текущей попытки
   SORT_BY_PEND_REQ_TOTAL,                                  // Сортировать по количеству попыток
   SORT_BY_PEND_REQ_ACTUAL_TYPE_FILLING,                    // Сортировать по фактическому типу заливки ордера
   SORT_BY_PEND_REQ_ACTUAL_TYPE_TIME,                       // Сортировать по фактическому типу экспирации ордера
   SORT_BY_PEND_REQ_ACTUAL_EXPIRATION,                      // Сортировать по фактическому времени жизни ордера
   //--- MqlTradeRequest
   SORT_BY_PEND_REQ_MQL_REQ_ACTION,                         // Сортировать по типу выполняемого действия в структуре запроса
   SORT_BY_PEND_REQ_MQL_REQ_TYPE,                           // Сортировать по типу ордера в структуре запроса
   SORT_BY_PEND_REQ_MQL_REQ_MAGIC,                          // Сортировать по штампу эксперта (идентификатору magic number) в структуре запроса
   SORT_BY_PEND_REQ_MQL_REQ_ORDER,                          // Сортировать по тикету ордера в структуре запроса
   SORT_BY_PEND_REQ_MQL_REQ_POSITION,                       // Сортировать по тикету позиции в структуре запроса
   SORT_BY_PEND_REQ_MQL_REQ_POSITION_BY,                    // Сортировать по тикету встречной позиции в структуре запроса
   SORT_BY_PEND_REQ_MQL_REQ_DEVIATION,                      // Сортировать по максимально приемлемому отклонению от запрашиваемой цены в структуре запроса
   SORT_BY_PEND_REQ_MQL_REQ_EXPIRATION,                     // Сортировать по сроку истечения ордера (для ордеров типа ORDER_TIME_SPECIFIED) в структуре запроса
   SORT_BY_PEND_REQ_MQL_REQ_TYPE_FILLING,                   // Сортировать по типу ордера по исполнению в структуре запроса
   SORT_BY_PEND_REQ_MQL_REQ_TYPE_TIME,                      // Сортировать по типу ордера по времени действия в структуре запроса
//--- Сортировка по вещественным свойствам
   SORT_BY_PEND_REQ_PRICE_CREATE = FIRST_PREQ_DBL_PROP,     // Сортировать по цене при создании запроса
   SORT_BY_PEND_REQ_ACTUAL_VOLUME,                          // Сортировать по начальному объему
   SORT_BY_PEND_REQ_ACTUAL_PRICE,                           // Сортировать по фактической цене установки ордера
   SORT_BY_PEND_REQ_ACTUAL_STOPLIMIT,                       // Сортировать по фактической цене установки stoplimit-ордера
   SORT_BY_PEND_REQ_ACTUAL_SL,                              // Сортировать по фактической цене установки stoploss-ордера
   SORT_BY_PEND_REQ_ACTUAL_TP,                              // Сортировать по фактической цене установки takeprofit-ордера
   //--- MqlTradeRequest
   SORT_BY_PEND_REQ_MQL_REQ_VOLUME,                         // Сортировать по запрашиваемому объему сделки в лотах в структуре запроса
   SORT_BY_PEND_REQ_MQL_REQ_PRICE,                          // Сортировать по цене в структуре запроса
   SORT_BY_PEND_REQ_MQL_REQ_STOPLIMIT,                      // Сортировать по уровню StopLimit ордера в структуре запроса
   SORT_BY_PEND_REQ_MQL_REQ_SL,                             // Сортировать по уровню StopLoss ордера в структуре запроса
   SORT_BY_PEND_REQ_MQL_REQ_TP,                             // Сортировать по уровню TakeProfit ордера в структуре запроса
//--- Сортировка по строковым свойствам
   //--- MqlTradeRequest
   SORT_BY_PEND_REQ_MQL_SYMBOL = FIRST_PREQ_STR_PROP,       // Сортировать по имени торгового инструмента в структуре запроса
   SORT_BY_PEND_REQ_MQL_COMMENT                             // Сортировать по комментарию к ордеру в структуре запроса
  };
//+------------------------------------------------------------------+

Теперь к объектам.

Откроем файл класса базового объекта абстрактного отложенного запроса \MQL5\Include\DoEasy\Objects\PendRequest\PendRequest.mqh и внесём в него необходимые изменения.

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

//+------------------------------------------------------------------+
//| Класс абстрактного отложенного торгового запроса                 |
//+------------------------------------------------------------------+
class CPendRequest : public CObject
  {
private:
   MqlTradeRequest   m_request;                                         // Структура торгового запроса
   CPause            m_pause;                                           // Объект класса "Пауза"
//--- Копирует данные торгового запроса
   void              CopyRequest(const MqlTradeRequest &request);
//--- Возвращает индекс массива, по которому фактически расположено (1) double-свойство и (2) string-свойство запроса
   int               IndexProp(ENUM_PEND_REQ_PROP_DOUBLE property)const { return(int)property-PEND_REQ_PROP_INTEGER_TOTAL;                               }
   int               IndexProp(ENUM_PEND_REQ_PROP_STRING property)const { return(int)property-PEND_REQ_PROP_INTEGER_TOTAL-PEND_REQ_PROP_DOUBLE_TOTAL;    }
protected:
   int               m_digits;                                          // Количество знаков после запятой в котировке символа
   int               m_digits_lot;                                      // Количество знаков после запятой в значении лота символа
   bool              m_is_hedge;                                        // Флаг счёта с типом "хедж"
   long              m_long_prop[PEND_REQ_PROP_INTEGER_TOTAL];          // Целочисленные свойства запроса
   double            m_double_prop[PEND_REQ_PROP_DOUBLE_TOTAL];         // Вещественные свойства запроса
   string            m_string_prop[PEND_REQ_PROP_STRING_TOTAL];         // Строковые свойства запроса
//--- Защищённый параметрический конструктор
                     CPendRequest(const ENUM_PEND_REQ_STATUS status,
                                  const uchar id,
                                  const double price,
                                  const ulong time,
                                  const MqlTradeRequest &request,
                                  const int retcode);
//--- Возвращает (1) заданный в настройках магический номер, (2) флаг счёта с типом "хедж", (3) флаг равенства вещественного свойства значению
   ushort            GetMagicID(void)                                      const { return ushort(this.GetProperty(PEND_REQ_PROP_MQL_REQ_MAGIC) & 0xFFFF);}
   bool              IsHedge(void)                                         const { return this.m_is_hedge;                                               }
   bool              IsEqualByMode(const int mode,const double value)      const;
   bool              IsEqualByMode(const int mode,const long value)        const;
//--- Возвращает флаги завершения работы отложенного запроса по изменению каждого из параметров ордера/позиции
   bool              IsCompletedVolume(void)                               const;
   bool              IsCompletedPrice(void)                                const;
   bool              IsCompletedStopLimit(void)                            const;
   bool              IsCompletedStopLoss(void)                             const;
   bool              IsCompletedTakeProfit(void)                           const;
   bool              IsCompletedTypeFilling(void)                          const;
   bool              IsCompletedTypeTime(void)                             const;
   bool              IsCompletedExpiration(void)                           const;
public:

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

public:
//--- Конструктор по умолчанию
                     CPendRequest(){;}
//--- Устанавливает (1) целочисленное, (2) вещественное и (3) строковое свойство запроса
   void              SetProperty(ENUM_PEND_REQ_PROP_INTEGER property,long value) { this.m_long_prop[property]=value;                                     }
   void              SetProperty(ENUM_PEND_REQ_PROP_DOUBLE property,double value){ this.m_double_prop[this.IndexProp(property)]=value;                   }
   void              SetProperty(ENUM_PEND_REQ_PROP_STRING property,string value){ this.m_string_prop[this.IndexProp(property)]=value;                   }
//--- Возвращает из массива свойств (1) целочисленное, (2) вещественное и (3) строковое свойство запроса
   long              GetProperty(ENUM_PEND_REQ_PROP_INTEGER property)      const { return this.m_long_prop[property];                                    }
   double            GetProperty(ENUM_PEND_REQ_PROP_DOUBLE property)       const { return this.m_double_prop[this.IndexProp(property)];                  }
   string            GetProperty(ENUM_PEND_REQ_PROP_STRING property)       const { return this.m_string_prop[this.IndexProp(property)];                  }

//--- Возвращает флаг поддержания запросом данного свойства
   virtual bool      SupportProperty(ENUM_PEND_REQ_PROP_INTEGER property)        { return true; }
   virtual bool      SupportProperty(ENUM_PEND_REQ_PROP_DOUBLE property)         { return true; }
   virtual bool      SupportProperty(ENUM_PEND_REQ_PROP_STRING property)         { return true; }

//--- Возвращает флаг завершения работы отложенного запроса
   virtual bool      IsCompleted(void)                                     const { return false;}
//--- Возвращает себя
   CPendRequest     *GetObject(void)                                             { return &this;}

//--- Сравнивает объекты CPendRequest между собой по заданному свойству (для сортировки списков по указанному свойству объекта-запроса)
   virtual int       Compare(const CObject *node,const int mode=0) const;
//--- Сравнивает объекты CPendRequest между собой по всем свойствам (для поиска равных объектов-запросов)
   bool              IsEqual(CPendRequest* compared_obj);
//--- Возвращает из объекта-паузы (1) прошедшее количество милисекунд, (2) завершение времени ожидания,
//--- время в милисекундах (3) начала отсчёта паузы, (4) времени ожидания
   ulong             PausePassed(void)                                     const { return this.m_pause.Passed();                 }
   bool              PauseIsCompleted(void)                                const { return this.m_pause.IsCompleted();            }
   ulong             PauseTimeBegin(void)                                  const { return this.m_pause.TimeBegin();              }
   ulong             PauseTimeWait(void)                                   const { return this.m_pause.TimeWait();               }
//--- Возвращает описание (1) прошедшего количества секунд ожидания паузы,
//--- (2) времени начала отсчёта паузы, времени ожидания (3) в милисекундах, (4) в секундах
   string            PausePassedDescription(void)                          const { return this.m_pause.PassedDescription();      }
   string            PauseTimeBeginDescription(void)                       const { return this.m_pause.TimeBeginDescription();   }
   string            PauseWaitingMSCDescription(void)                      const { return this.m_pause.WaitingMSCDescription();  }
   string            PauseWaitingSecDescription(void)                      const { return this.m_pause.WaitingSECDescription();  }
   
//+------------------------------------------------------------------+

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

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

//+------------------------------------------------------------------+
//| Методы упрощённого доступа к свойствам объекта-запроса           |
//+------------------------------------------------------------------+
//--- Возвращает (1) структуру запроса, (2) статус, (3) тип запроса, (4) цену при создании запроса,
//--- (5) время создания запроса, (6) время активации очередной попытки,
//--- (7) продолжительность ожидания между запросами, (8) номер текущей попытки,
//--- (9) количество попыток, (10) идентификатор запроса
//--- (11) результат, на основании которого создан запрос,
//--- (12) тикет ордера, (13) тикет позиции, (14) тип торговой операции
   MqlTradeRequest      MqlRequest(void)                                   const { return this.m_request;                                                }
   ENUM_PEND_REQ_STATUS Status(void)                                       const { return (ENUM_PEND_REQ_STATUS)this.GetProperty(PEND_REQ_PROP_STATUS);  }
   ENUM_PEND_REQ_TYPE   TypeRequest(void)                                  const { return (ENUM_PEND_REQ_TYPE)this.GetProperty(PEND_REQ_PROP_TYPE);      }
   double               PriceCreate(void)                                  const { return this.GetProperty(PEND_REQ_PROP_PRICE_CREATE);                  }
   ulong                TimeCreate(void)                                   const { return this.GetProperty(PEND_REQ_PROP_TIME_CREATE);                   }
   ulong                TimeActivate(void)                                 const { return this.GetProperty(PEND_REQ_PROP_TIME_ACTIVATE);                 }
   ulong                WaitingMSC(void)                                   const { return this.GetProperty(PEND_REQ_PROP_WAITING);                       }
   uchar                CurrentAttempt(void)                               const { return (uchar)this.GetProperty(PEND_REQ_PROP_CURRENT_ATTEMPT);        }
   uchar                TotalAttempts(void)                                const { return (uchar)this.GetProperty(PEND_REQ_PROP_TOTAL);                  }
   uchar                ID(void)                                           const { return (uchar)this.GetProperty(PEND_REQ_PROP_ID);                     }
   int                  Retcode(void)                                      const { return (int)this.GetProperty(PEND_REQ_PROP_RETCODE);                  }
   ulong                Order(void)                                        const { return this.GetProperty(PEND_REQ_PROP_MQL_REQ_ORDER);                 }
   ulong                Position(void)                                     const { return this.GetProperty(PEND_REQ_PROP_MQL_REQ_POSITION);              }
   ENUM_TRADE_REQUEST_ACTIONS Action(void)                                 const { return (ENUM_TRADE_REQUEST_ACTIONS)this.GetProperty(PEND_REQ_PROP_MQL_REQ_ACTION);   }

//--- Возвращает фактический (1) объём, (2) цену установки ордера, (3) цену установки limit-ордера,
//--- (4) цену установки stoploss-ордера, (5) цену установки takeprofit-ордера, (6) тип заливки ордера,
//--- (7) тип экспирации ордера, (8) время жизни ордера
   double               ActualVolume(void)                                 const { return this.GetProperty(PEND_REQ_PROP_ACTUAL_VOLUME);                 }
   double               ActualPrice(void)                                  const { return this.GetProperty(PEND_REQ_PROP_ACTUAL_PRICE);                  }
   double               ActualStopLimit(void)                              const { return this.GetProperty(PEND_REQ_PROP_ACTUAL_STOPLIMIT);              }
   double               ActualSL(void)                                     const { return this.GetProperty(PEND_REQ_PROP_ACTUAL_SL);                     }
   double               ActualTP(void)                                     const { return this.GetProperty(PEND_REQ_PROP_ACTUAL_TP);                     }
   ENUM_ORDER_TYPE_FILLING ActualTypeFilling(void)                         const { return (ENUM_ORDER_TYPE_FILLING)this.GetProperty(PEND_REQ_PROP_ACTUAL_TYPE_FILLING); }
   ENUM_ORDER_TYPE_TIME ActualTypeTime(void)                               const { return (ENUM_ORDER_TYPE_TIME)this.GetProperty(PEND_REQ_PROP_ACTUAL_TYPE_TIME);       }
   datetime             ActualExpiration(void)                             const { return (datetime)this.GetProperty(PEND_REQ_PROP_ACTUAL_EXPIRATION);   }

Каждый раз при проверке очередного объекта-отложенного запроса, в эти его свойства будут записываться соответствующие свойства ордера/позиции, для работы с которым создан объект-запрос. Эти методы позволяют получить из объекта-запроса текущие свойства соответствующего ему ордера.

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

//--- Устанавливает (1) цену при создании запроса, (2) время создания запроса,
//--- (3) время текущей попытки, (4) продолжительность ожидания между запросами,
//--- (5) номер текущей попытки, (6) количество попыток, (7) идентификатор,
//--- (8) тикет ордера, (9) тикет позиции
   void                 SetPriceCreate(const double price)                       { this.SetProperty(PEND_REQ_PROP_PRICE_CREATE,price);                   }
   void                 SetTimeCreate(const ulong time)
                          {
                           this.SetProperty(PEND_REQ_PROP_TIME_CREATE,time);
                           this.m_pause.SetTimeBegin(time);
                          }
   void                 SetTimeActivate(const ulong time)                        { this.SetProperty(PEND_REQ_PROP_TIME_ACTIVATE,time);                   }
   void                 SetWaitingMSC(const ulong miliseconds)
                          { 
                           this.SetProperty(PEND_REQ_PROP_WAITING,miliseconds);
                           this.m_pause.SetWaitingMSC(miliseconds);
                          }

Теперь эти методы одновременно с установкой передаваемых в них значений в объект-отложенный запрос, будут устанавливать эти значения и в объект-паузу.

И в том же блоке напишем методы установки в объект-запрос фактических свойств объекта-ордера:

//--- Устанавливает фактический (1) объём, (2) цену установки ордера, (3) цену установки limit-ордера,
//--- (4) цену установки stoploss-ордера, (5) цену установки takeprofit-ордера, (6) тип заливки ордера,
//--- (7) тип экспирации ордера, (8) время жизни ордера
   void                 SetActualVolume(const double volume)                     { this.SetProperty(PEND_REQ_PROP_ACTUAL_VOLUME,volume);                 }
   void                 SetActualPrice(const double price)                       { this.SetProperty(PEND_REQ_PROP_ACTUAL_PRICE,price);                   }
   void                 SetActualStopLimit(const double price)                   { this.SetProperty(PEND_REQ_PROP_ACTUAL_STOPLIMIT,price);               }
   void                 SetActualSL(const double price)                          { this.SetProperty(PEND_REQ_PROP_ACTUAL_SL,price);                      }
   void                 SetActualTP(const double price)                          { this.SetProperty(PEND_REQ_PROP_ACTUAL_TP,price);                      }
   void                 SetActualTypeFilling(const ENUM_ORDER_TYPE_FILLING type) { this.SetProperty(PEND_REQ_PROP_ACTUAL_TYPE_FILLING,type);             }
   void                 SetActualTypeTime(const ENUM_ORDER_TYPE_TIME type)       { this.SetProperty(PEND_REQ_PROP_ACTUAL_TYPE_TIME,type);                }
   void                 SetActualExpiration(const datetime expiration)           { this.SetProperty(PEND_REQ_PROP_ACTUAL_EXPIRATION,expiration);         }

//+------------------------------------------------------------------+

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

//+------------------------------------------------------------------+
//| Описания свойств объекта-запроса                                 |
//+------------------------------------------------------------------+
//--- Возвращает описание (1) целочисленного, (2) вещественного и (3) строкового свойства запроса
   string               GetPropertyDescription(ENUM_PEND_REQ_PROP_INTEGER property);
   string               GetPropertyDescription(ENUM_PEND_REQ_PROP_DOUBLE property);
   string               GetPropertyDescription(ENUM_PEND_REQ_PROP_STRING property);

//--- Возвращает наименования параметров объекта-отложенного запроса
   string               StatusDescription(void)                const;
   string               TypeRequestDescription(void)           const;
   string               IDDescription(void)                    const;
   string               RetcodeDescription(void)               const;
   string               TimeCreateDescription(void)            const;
   string               TimeActivateDescription(void)          const;
   string               TimeWaitingDescription(void)           const;
   string               CurrentAttemptDescription(void)        const;
   string               TotalAttemptsDescription(void)         const;
   string               PriceCreateDescription(void)           const;
   
   string               TypeFillingActualDescription(void)     const;
   string               TypeTimeActualDescription(void)        const;
   string               ExpirationActualDescription(void)      const;
   string               VolumeActualDescription(void)          const;
   string               PriceActualDescription(void)           const;
   string               StopLimitActualDescription(void)       const;
   string               StopLossActualDescription(void)        const;
   string               TakeProfitActualDescription(void)      const;
   
//--- Возвращает наименования параметров структуры торгового запроса в объекте-запросе
   string               MqlReqActionDescription(void)          const;
   string               MqlReqMagicDescription(void)           const;
   string               MqlReqOrderDescription(void)           const;
   string               MqlReqSymbolDescription(void)          const;
   string               MqlReqVolumeDescription(void)          const;
   string               MqlReqPriceDescription(void)           const;
   string               MqlReqStopLimitDescription(void)       const;
   string               MqlReqStopLossDescription(void)        const;
   string               MqlReqTakeProfitDescription(void)      const;
   string               MqlReqDeviationDescription(void)       const;
   string               MqlReqTypeOrderDescription(void)       const;
   string               MqlReqTypeFillingDescription(void)     const;
   string               MqlReqTypeTimeDescription(void)        const;
   string               MqlReqExpirationDescription(void)      const;
   string               MqlReqCommentDescription(void)         const;
   string               MqlReqPositionDescription(void)        const;
   string               MqlReqPositionByDescription(void)      const;

//--- Выводит в журнал (1) описание свойств запроса (full_prop=true - все свойства, false - только поддерживаемые),
//--- (2) краткое сообщение о запросе, (3) краткое наименование запроса (2 и 3 - реализация в потомках класса)
   void                 Print(const bool full_prop=false);
   virtual void         PrintShort(void){;}
   virtual string       Header(void){return NULL;}
  };
//+------------------------------------------------------------------+

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

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

//+------------------------------------------------------------------+
//| Конструктор                                                      |
//+------------------------------------------------------------------+
CPendRequest::CPendRequest(const ENUM_PEND_REQ_STATUS status,
                           const uchar id,
                           const double price,
                           const ulong time,
                           const MqlTradeRequest &request,
                           const int retcode)
  {
   this.CopyRequest(request);
   this.m_is_hedge=#ifdef __MQL4__ true #else bool(::AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING) #endif;
   this.m_digits=(int)::SymbolInfoInteger(this.GetProperty(PEND_REQ_PROP_MQL_REQ_SYMBOL),SYMBOL_DIGITS);
   int dg=(int)DigitsLots(this.GetProperty(PEND_REQ_PROP_MQL_REQ_SYMBOL));
   this.m_digits_lot=(dg==0 ? 1 : dg);
   this.SetProperty(PEND_REQ_PROP_STATUS,status);
   this.SetProperty(PEND_REQ_PROP_ID,id);
   this.SetProperty(PEND_REQ_PROP_RETCODE,retcode);
   this.SetProperty(PEND_REQ_PROP_TYPE,this.GetProperty(PEND_REQ_PROP_RETCODE)>0 ? PEND_REQ_TYPE_ERROR : PEND_REQ_TYPE_REQUEST);
   this.SetProperty(PEND_REQ_PROP_TIME_CREATE,time);
   this.SetProperty(PEND_REQ_PROP_PRICE_CREATE,price);
   this.m_pause.SetTimeBegin(this.GetProperty(PEND_REQ_PROP_TIME_CREATE));
   this.m_pause.SetWaitingMSC(this.GetProperty(PEND_REQ_PROP_WAITING));
  }
//+------------------------------------------------------------------+

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

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

//+------------------------------------------------------------------+
//| Возвращает флаг равенства вещественного свойства значению        |
//+------------------------------------------------------------------+
bool CPendRequest::IsEqualByMode(const int mode,const double value) const
  {
   CPendRequest *req=new CPendRequest();
   if(req==NULL)
      return false;
   req.SetProperty((ENUM_PEND_REQ_PROP_DOUBLE)mode,value);
   bool res=!this.Compare(req,mode);
   delete req;
   return res;
  }
//+------------------------------------------------------------------+
//| Возвращает флаг равенства целочисленного свойства значению       |
//+------------------------------------------------------------------+
bool CPendRequest::IsEqualByMode(const int mode,const long value) const
  {
   CPendRequest *req=new CPendRequest();
   if(req==NULL)
      return false;
   req.SetProperty((ENUM_PEND_REQ_PROP_INTEGER)mode,value);
   bool res=!this.Compare(req,mode);
   delete req;
   return res;
  }
//+------------------------------------------------------------------+

В метод передаётся режим, по которому сравниваем свойство объекта и значение, переданное в метод.
Режимом является указание одного из значений свойства объекта из перечисления его вещественных (ENUM_PEND_REQ_PROP_DOUBLE) или целочисленных свойств (ENUM_PEND_REQ_PROP_INTEGER). По этому свойству и будет проводиться сравнение с переданным в метод значением.
В методе создаём новый временный объект-отложенный запрос и устанавливаем для его соответствующего свойства переданное в метод значение.
Метод Compare() базового объекта CObject стандартной библиотеки возвращает 0, что означает равенство. Но метод виртуальный, и реализуется в потомках класса. Здесь метод Compare() реализован так, что возвращает 1 если значение текущего объекта больше такого же значения у сравниваемого, -1 при значении текущего меньше значения сравниваемого, и 0 при равенстве.
Таким образом, если сравниваемые значения равны, то в переменную res записывается false.
Но мы туда записываем логическое отрицание результата проверки. Таким образом, если метод сравнения возвращает 0, то !false равно true. А если 1 или -1, то !true равно false.
После записи результата сравнения, временный объект удаляется и возвращается результат сравнения.

Методы, возвращающие флаги сравнения фактических значений свойств ордера и объекта-запроса по их типам:

//+------------------------------------------------------------------+
//| Возвращает флаг успешного изменения объёма                       |
//+------------------------------------------------------------------+
bool CPendRequest::IsCompletedVolume(void) const
  {
   return this.IsEqualByMode(PEND_REQ_PROP_ACTUAL_VOLUME,this.GetProperty(PEND_REQ_PROP_MQL_REQ_VOLUME));
  }
//+------------------------------------------------------------------+
//| Возвращает флаг успешной модификации цены установки              |
//+------------------------------------------------------------------+
bool CPendRequest::IsCompletedPrice(void) const
  {
   return this.IsEqualByMode(PEND_REQ_PROP_ACTUAL_PRICE,this.GetProperty(PEND_REQ_PROP_MQL_REQ_PRICE));
  }
//+------------------------------------------------------------------+
//| Возвращает флаг успешной модификации цены StopLimit-ордера       |
//+------------------------------------------------------------------+
bool CPendRequest::IsCompletedStopLimit(void) const
  {
   return this.IsEqualByMode(PEND_REQ_PROP_ACTUAL_STOPLIMIT,this.GetProperty(PEND_REQ_PROP_MQL_REQ_STOPLIMIT));
  }
//+------------------------------------------------------------------+
//| Возвращает флаг успешной модификации StopLoss-ордера             |
//+------------------------------------------------------------------+
bool CPendRequest::IsCompletedStopLoss(void) const
  {
   return this.IsEqualByMode(PEND_REQ_PROP_ACTUAL_SL,this.GetProperty(PEND_REQ_PROP_MQL_REQ_SL));
  }
//+------------------------------------------------------------------+
//| Возвращает флаг успешной модификации TakeProfit-ордера           |
//+------------------------------------------------------------------+
bool CPendRequest::IsCompletedTakeProfit(void) const
  {
   return this.IsEqualByMode(PEND_REQ_PROP_ACTUAL_TP,this.GetProperty(PEND_REQ_PROP_MQL_REQ_TP));
  }
//+------------------------------------------------------------------+
//| Возвращает флаг успешной модификации типа заливки ордера         |
//+------------------------------------------------------------------+
bool CPendRequest::IsCompletedTypeFilling(void) const
  {
   return this.IsEqualByMode(PEND_REQ_PROP_ACTUAL_TYPE_FILLING,this.GetProperty(PEND_REQ_PROP_MQL_REQ_TYPE_FILLING));
  }
//+------------------------------------------------------------------+
//| Возвращает флаг успешной модификации типа экспирации ордера      |
//+------------------------------------------------------------------+
bool CPendRequest::IsCompletedTypeTime(void) const
  {
   return this.IsEqualByMode(PEND_REQ_PROP_ACTUAL_TYPE_TIME,this.GetProperty(PEND_REQ_PROP_MQL_REQ_TYPE_TIME));
  }
//+------------------------------------------------------------------+
//| Возвращает флаг успешной модификации времени жизни ордера        |
//+------------------------------------------------------------------+
bool CPendRequest::IsCompletedExpiration(void) const
  {
   return this.IsEqualByMode(PEND_REQ_PROP_ACTUAL_EXPIRATION,this.GetProperty(PEND_REQ_PROP_MQL_REQ_EXPIRATION));
  }
//+------------------------------------------------------------------+

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

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

//+------------------------------------------------------------------+
//| Возвращает описание целочисленного свойства запроса              |
//+------------------------------------------------------------------+
string CPendRequest::GetPropertyDescription(ENUM_PEND_REQ_PROP_INTEGER property)
  {
   return
     (
      property==PEND_REQ_PROP_STATUS               ?  this.StatusDescription()            :
      property==PEND_REQ_PROP_TYPE                 ?  this.TypeRequestDescription()       :
      property==PEND_REQ_PROP_ID                   ?  this.IDDescription()                :
      property==PEND_REQ_PROP_RETCODE              ?  this.RetcodeDescription()           :
      property==PEND_REQ_PROP_TIME_CREATE          ?  this.TimeCreateDescription()        :
      property==PEND_REQ_PROP_TIME_ACTIVATE        ?  this.TimeActivateDescription()      :
      property==PEND_REQ_PROP_WAITING              ?  this.TimeWaitingDescription()       :
      property==PEND_REQ_PROP_CURRENT_ATTEMPT      ?  this.CurrentAttemptDescription()    :
      property==PEND_REQ_PROP_TOTAL                ?  this.TotalAttemptsDescription()     :
      property==PEND_REQ_PROP_ACTUAL_TYPE_FILLING  ?  this.TypeFillingActualDescription() :
      property==PEND_REQ_PROP_ACTUAL_TYPE_TIME     ?  this.TypeTimeActualDescription()    :
      property==PEND_REQ_PROP_ACTUAL_EXPIRATION    ?  this.ExpirationActualDescription()  :
      //--- MqlTradeRequest
      property==PEND_REQ_PROP_MQL_REQ_ACTION       ?  this.MqlReqActionDescription()      :
      property==PEND_REQ_PROP_MQL_REQ_TYPE         ?  this.MqlReqTypeOrderDescription()   :
      property==PEND_REQ_PROP_MQL_REQ_MAGIC        ?  this.MqlReqMagicDescription()       :
      property==PEND_REQ_PROP_MQL_REQ_ORDER        ?  this.MqlReqOrderDescription()       :
      property==PEND_REQ_PROP_MQL_REQ_POSITION     ?  this.MqlReqPositionDescription()    :
      property==PEND_REQ_PROP_MQL_REQ_POSITION_BY  ?  this.MqlReqPositionByDescription()  :
      property==PEND_REQ_PROP_MQL_REQ_DEVIATION    ?  this.MqlReqDeviationDescription()   :
      property==PEND_REQ_PROP_MQL_REQ_EXPIRATION   ?  this.MqlReqExpirationDescription()  :
      property==PEND_REQ_PROP_MQL_REQ_TYPE_FILLING ?  this.MqlReqTypeFillingDescription() :
      property==PEND_REQ_PROP_MQL_REQ_TYPE_TIME    ?  this.MqlReqTypeTimeDescription()    :
      ::EnumToString(property)
     );
  }
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Возвращает описание вещественного свойства запроса               |
//+------------------------------------------------------------------+
string CPendRequest::GetPropertyDescription(ENUM_PEND_REQ_PROP_DOUBLE property)
  {
   return
     (
      property==PEND_REQ_PROP_PRICE_CREATE      ?  this.PriceCreateDescription()          :
      property==PEND_REQ_PROP_ACTUAL_VOLUME     ?  this.VolumeActualDescription()         :
      property==PEND_REQ_PROP_ACTUAL_PRICE      ?  this.PriceActualDescription()          :
      property==PEND_REQ_PROP_ACTUAL_STOPLIMIT  ?  this.StopLimitActualDescription()      :
      property==PEND_REQ_PROP_ACTUAL_SL         ?  this.StopLossActualDescription()       :
      property==PEND_REQ_PROP_ACTUAL_TP         ?  this.TakeProfitActualDescription()     :
      //--- MqlTradeRequest
      property==PEND_REQ_PROP_MQL_REQ_VOLUME    ?  this.MqlReqVolumeDescription()         :
      property==PEND_REQ_PROP_MQL_REQ_PRICE     ?  this.MqlReqPriceDescription()          :
      property==PEND_REQ_PROP_MQL_REQ_STOPLIMIT ?  this.MqlReqStopLimitDescription()      :
      property==PEND_REQ_PROP_MQL_REQ_SL        ?  this.MqlReqStopLossDescription()       :
      property==PEND_REQ_PROP_MQL_REQ_TP        ?  this.MqlReqTakeProfitDescription()     :
      ::EnumToString(property)
     );
  }
//+------------------------------------------------------------------+

В методах проверяется переданное в него свойство объекта, и возвращается его описание при помощи соответствующих свойству методов.

Методы, возвращающие описания фактических свойств ордера, записанные в объекте-запросе:

//+------------------------------------------------------------------+
//| Возвращает описание фактического режима заливки ордера           |
//+------------------------------------------------------------------+
string CPendRequest::TypeFillingActualDescription(void) const
  {
   return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_ACTUAL_TYPE_FILLING)+": "+OrderTypeFillingDescription((ENUM_ORDER_TYPE_FILLING)this.GetProperty(PEND_REQ_PROP_ACTUAL_TYPE_FILLING));
  }
//+------------------------------------------------------------------+
//| Возвращает описание фактического типа срока действия ордера      |
//+------------------------------------------------------------------+
string CPendRequest::TypeTimeActualDescription(void) const
  {
   return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_ACTUAL_TYPE_TIME)+": "+OrderTypeTimeDescription((ENUM_ORDER_TYPE_TIME)this.GetProperty(PEND_REQ_PROP_ACTUAL_TYPE_TIME));
  }
//+------------------------------------------------------------------+
//| Возвращает описание фактического срока истечения ордера          |
//+------------------------------------------------------------------+
string CPendRequest::ExpirationActualDescription(void) const
  {
   return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_ACTUAL_EXPIRATION)+": "+
          (this.GetProperty(PEND_REQ_PROP_ACTUAL_EXPIRATION)>0 ? 
           ::TimeToString(this.GetProperty(PEND_REQ_PROP_ACTUAL_EXPIRATION)) : 
           CMessage::Text(MSG_LIB_PROP_NOT_SET));
  }
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Возвращает описание значения фактического объёма ордера          |
//+------------------------------------------------------------------+
string CPendRequest::VolumeActualDescription(void) const
  {
   return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_ACTUAL_VOLUME)+": "+::DoubleToString(this.GetProperty(PEND_REQ_PROP_ACTUAL_VOLUME),this.m_digits_lot);
  }
//+------------------------------------------------------------------+
//| Возвращает описание значения фактической цены ордера             |
//+------------------------------------------------------------------+
string CPendRequest::PriceActualDescription(void) const
  {
   return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_ACTUAL_PRICE)+": "+::DoubleToString(this.GetProperty(PEND_REQ_PROP_ACTUAL_PRICE),this.m_digits);
  }
//+------------------------------------------------------------------+
//| Возвращает описание значения фактической цены StopLimit-ордера   |
//+------------------------------------------------------------------+
string CPendRequest::StopLimitActualDescription(void) const
  {
   return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_ACTUAL_STOPLIMIT)+": "+::DoubleToString(this.GetProperty(PEND_REQ_PROP_ACTUAL_STOPLIMIT),this.m_digits);
  }
//+------------------------------------------------------------------+
//| Возвращает описание значения фактической цены StopLoss-ордера    |
//+------------------------------------------------------------------+
string CPendRequest::StopLossActualDescription(void) const
  {
   return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_ACTUAL_SL)+": "+::DoubleToString(this.GetProperty(PEND_REQ_PROP_ACTUAL_SL),this.m_digits);
  }
//+------------------------------------------------------------------+
//| Возвращает описание значения фактической цены TaleProfit-ордера  |
//+------------------------------------------------------------------+
string CPendRequest::TakeProfitActualDescription(void) const
  {
   return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_ACTUAL_TP)+": "+::DoubleToString(this.GetProperty(PEND_REQ_PROP_ACTUAL_TP),this.m_digits);
  }
//+------------------------------------------------------------------+

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

Это все изменения базового объекта абстрактного отложенного запроса.

Теперь внесём изменения в классы-наследники базового объекта-запроса.

Откроем файл класса объекта отложенного запроса на открытие позиции \MQL5\Include\DoEasy\Objects\PendRequest\PendReqOpen.mqh и внесём правки.

Впишем определение виртуального метода, возвращающего краткое наименование запроса:

//+------------------------------------------------------------------+
//| Отложенный запрос на открытие позиции                            |
//+------------------------------------------------------------------+
class CPendReqOpen : public CPendRequest
  {
public:
//--- Конструктор
                     CPendReqOpen(const uchar id,
                                  const double price,
                                  const ulong time,
                                  const MqlTradeRequest &request,
                                  const int retcode) : CPendRequest(PEND_REQ_STATUS_OPEN,id,price,time,request,retcode) {}
                                  
//--- Поддерживаемые свойства сделки (1) вещественные, (2) целочисленные
   virtual bool      SupportProperty(ENUM_PEND_REQ_PROP_INTEGER property);
   virtual bool      SupportProperty(ENUM_PEND_REQ_PROP_DOUBLE property);
   virtual bool      SupportProperty(ENUM_PEND_REQ_PROP_STRING property);
//--- Выводит в журнал краткое сообщениес данными запроса, (2) краткое наименование запроса
   virtual void      PrintShort(void);
   virtual string    Header(void);
  };
//+------------------------------------------------------------------+

И его реализацию:

//+------------------------------------------------------------------+
//| Возвращает краткое наименование запроса                          |
//+------------------------------------------------------------------+
string CPendReqOpen::Header(void)
  {
   return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_STATUS_OPEN)+", ID #"+(string)this.GetProperty(PEND_REQ_PROP_ID);
  }
//+------------------------------------------------------------------+

Класс отложенного запроса на модификацию стоп-приказов позиции (файл PendReqSLTP.mqh):

//+------------------------------------------------------------------+
//| Отложенный запрос на мидификацию стоп-приказов позиции           |
//+------------------------------------------------------------------+
class CPendReqSLTP : public CPendRequest
  {
public:
//--- Конструктор
                     CPendReqSLTP(const uchar id,
                                  const double price,
                                  const ulong time,
                                  const MqlTradeRequest &request,
                                  const int retcode) : CPendRequest(PEND_REQ_STATUS_SLTP,id,price,time,request,retcode) {}
                                  
//--- Поддерживаемые свойства сделки (1) вещественные, (2) целочисленные
   virtual bool      SupportProperty(ENUM_PEND_REQ_PROP_INTEGER property);
   virtual bool      SupportProperty(ENUM_PEND_REQ_PROP_DOUBLE property);
   virtual bool      SupportProperty(ENUM_PEND_REQ_PROP_STRING property);
//--- Возвращает флаг завершения работы отложенного запроса
   virtual bool      IsCompleted(void) const;
//--- Выводит в журнал краткое сообщениес данными запроса, (2) краткое наименование запроса
   virtual void      PrintShort(void);
   virtual string    Header(void);
  };
//+------------------------------------------------------------------+

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

//+------------------------------------------------------------------+
//| Возвращает истину, если ордер поддерживает переданное            |
//| целочисленное свойство, возвращает ложь в противном случае       |
//+------------------------------------------------------------------+
bool CPendReqSLTP::SupportProperty(ENUM_PEND_REQ_PROP_INTEGER property)
  {
   if(property==PEND_REQ_PROP_MQL_REQ_POSITION_BY  ||
      property==PEND_REQ_PROP_MQL_REQ_ORDER        ||
      property==PEND_REQ_PROP_MQL_REQ_EXPIRATION   ||
      property==PEND_REQ_PROP_MQL_REQ_DEVIATION    ||
      property==PEND_REQ_PROP_MQL_REQ_TYPE_FILLING ||
      property==PEND_REQ_PROP_MQL_REQ_TYPE_TIME    ||
      property==PEND_REQ_PROP_ACTUAL_EXPIRATION    ||
      property==PEND_REQ_PROP_ACTUAL_TYPE_TIME     ||
      property==PEND_REQ_PROP_ACTUAL_TYPE_FILLING
      
     ) return false;
   return true;
  }
//+------------------------------------------------------------------+
//| Возвращает истину, если ордер поддерживает переданное            |
//| вещественное свойство, возвращает ложь в противном случае        |
//+------------------------------------------------------------------+
bool CPendReqSLTP::SupportProperty(ENUM_PEND_REQ_PROP_DOUBLE property)
  {
   if(property==PEND_REQ_PROP_PRICE_CREATE         ||
      property==PEND_REQ_PROP_ACTUAL_SL            ||
      property==PEND_REQ_PROP_ACTUAL_TP            ||
      property==PEND_REQ_PROP_MQL_REQ_SL           ||
      property==PEND_REQ_PROP_MQL_REQ_TP
     ) return true;
   return false;
  }
//+------------------------------------------------------------------+

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

//+------------------------------------------------------------------+
//| Возвращает флаг завершения работы отложенного запроса            |
//+------------------------------------------------------------------+
bool CPendReqSLTP::IsCompleted(void) const
  {
   bool res=true;
   res &= this.IsCompletedStopLoss();
   res &= this.IsCompletedTakeProfit();
   return res;
  }
//+------------------------------------------------------------------+

Здесь: в переменную res добавляем результаты проверки завершения модификации StopLoss и TakeProfit. Если хоть одна из проверок вернёт false, то общий результат будет false. Итоговый результат возвращаем из метода.

Метод, возвращающий краткое наименование запроса:

//+------------------------------------------------------------------+
//| Возвращает краткое наименование запроса                          |
//+------------------------------------------------------------------+
string CPendReqSLTP::Header(void)
  {
   return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_STATUS_SLTP)+", ID #"+(string)this.GetProperty(PEND_REQ_PROP_ID);
  }
//+------------------------------------------------------------------+

Класс отложенного запроса на закрытие позиции (файл PendReqClose.mqh):

//+------------------------------------------------------------------+
//| Отложенный запрос на закрытие позиции                            |
//+------------------------------------------------------------------+
class CPendReqClose : public CPendRequest
  {
public:
//--- Конструктор
                     CPendReqClose(const uchar id,
                                   const double price,
                                   const ulong time,
                                   const MqlTradeRequest &request,
                                   const int retcode) : CPendRequest(PEND_REQ_STATUS_CLOSE,id,price,time,request,retcode) {}
                                  
//--- Поддерживаемые свойства сделки (1) вещественные, (2) целочисленные
   virtual bool      SupportProperty(ENUM_PEND_REQ_PROP_INTEGER property);
   virtual bool      SupportProperty(ENUM_PEND_REQ_PROP_DOUBLE property);
   virtual bool      SupportProperty(ENUM_PEND_REQ_PROP_STRING property);
//--- Возвращает флаг завершения работы отложенного запроса
   virtual bool      IsCompleted(void) const;
//--- Выводит в журнал краткое сообщениес данными запроса, (2) краткое наименование запроса
   virtual void      PrintShort(void);
   virtual string    Header(void);
  };
//+------------------------------------------------------------------+

Метод, возвращающий флаг завершения работы запроса:

//+------------------------------------------------------------------+
//| Возвращает флаг завершения работы отложенного запроса            |
//+------------------------------------------------------------------+
bool CPendReqClose::IsCompleted(void) const
  {
   return this.IsCompletedVolume();
  }
//+------------------------------------------------------------------+

Метод, возвращающий краткое наименование запроса:

//+------------------------------------------------------------------+
//| Возвращает краткое наименование запроса                          |
//+------------------------------------------------------------------+
string CPendReqClose::Header(void)
  {
   return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_STATUS_CLOSE)+", ID #"+(string)this.GetProperty(PEND_REQ_PROP_ID);
  }
//+------------------------------------------------------------------+

Класс отложенного запроса на установку отложенного ордера (файл PendReqPlace.mqh):

//+------------------------------------------------------------------+
//| Отложенный запрос на установку отложенного ордера                |
//+------------------------------------------------------------------+
class CPendReqPlace : public CPendRequest
  {
public:
//--- Конструктор
                     CPendReqPlace(const uchar id,
                                   const double price,
                                   const ulong time,
                                   const MqlTradeRequest &request,
                                   const int retcode) : CPendRequest(PEND_REQ_STATUS_PLACE,id,price,time,request,retcode) {}
                                  
//--- Поддерживаемые свойства сделки (1) вещественные, (2) целочисленные
   virtual bool      SupportProperty(ENUM_PEND_REQ_PROP_INTEGER property);
   virtual bool      SupportProperty(ENUM_PEND_REQ_PROP_DOUBLE property);
   virtual bool      SupportProperty(ENUM_PEND_REQ_PROP_STRING property);
//--- Выводит в журнал краткое сообщениес данными запроса, (2) краткое наименование запроса
   virtual void      PrintShort(void);
   virtual string    Header(void);
  };
//+------------------------------------------------------------------+

Метод, возвращающий краткое наименование запроса:

//+------------------------------------------------------------------+
//| Возвращает краткое наименование запроса                          |
//+------------------------------------------------------------------+
string CPendReqPlace::Header(void)
  {
   return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_STATUS_PLACE)+", ID #"+(string)this.GetProperty(PEND_REQ_PROP_ID);
  }
//+------------------------------------------------------------------+

Класс отложенного запроса на изменение параметров отложенного ордера (файл PendReqModify.mqh):

//+------------------------------------------------------------------+
//| Отложенный запрос на мидификацию параметров отложенного ордера   |
//+------------------------------------------------------------------+
class CPendReqModify : public CPendRequest
  {
public:
//--- Конструктор
                     CPendReqModify(const uchar id,
                                    const double price,
                                    const ulong time,
                                    const MqlTradeRequest &request,
                                    const int retcode) : CPendRequest(PEND_REQ_STATUS_MODIFY,id,price,time,request,retcode) {}
                                  
//--- Поддерживаемые свойства сделки (1) вещественные, (2) целочисленные
   virtual bool      SupportProperty(ENUM_PEND_REQ_PROP_INTEGER property);
   virtual bool      SupportProperty(ENUM_PEND_REQ_PROP_DOUBLE property);
   virtual bool      SupportProperty(ENUM_PEND_REQ_PROP_STRING property);
//--- Возвращает флаг завершения работы отложенного запроса
   virtual bool      IsCompleted(void) const;
//--- Выводит в журнал краткое сообщениес данными запроса, (2) краткое наименование запроса
   virtual void      PrintShort(void);
   virtual string    Header(void);
  };
//+------------------------------------------------------------------+

Метод, возвращающий флаг завершения работы запроса:

//+------------------------------------------------------------------+
//| Возвращает флаг завершения работы отложенного запроса            |
//+------------------------------------------------------------------+
bool CPendReqModify::IsCompleted(void) const
  {
   bool res=true;
   res &= this.IsCompletedPrice();
   res &= this.IsCompletedStopLimit();
   res &= this.IsCompletedStopLoss();
   res &= this.IsCompletedTakeProfit();
   res &= this.IsCompletedTypeFilling();
   res &= this.IsCompletedTypeTime();
   res &= this.IsCompletedExpiration();
   return res;
  }
//+------------------------------------------------------------------+

Метод, возвращающий краткое наименование запроса:

//+------------------------------------------------------------------+
//| Возвращает краткое наименование запроса                          |
//+------------------------------------------------------------------+
string CPendReqModify::Header(void)
  {
   return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_STATUS_MODIFY)+", ID #"+(string)this.GetProperty(PEND_REQ_PROP_ID);
  }
//+------------------------------------------------------------------+

Класс отложенного запроса на удаление отложенного ордера (файл PendReqRemove.mqh):

//+------------------------------------------------------------------+
//| Отложенный запрос на удаление отложенного ордера                 |
//+------------------------------------------------------------------+
class CPendReqRemove : public CPendRequest
  {
public:
//--- Конструктор
                     CPendReqRemove(const uchar id,
                                    const double price,
                                    const ulong time,
                                    const MqlTradeRequest &request,
                                    const int retcode) : CPendRequest(PEND_REQ_STATUS_REMOVE,id,price,time,request,retcode) {}
                                  
//--- Поддерживаемые свойства сделки (1) вещественные, (2) целочисленные
   virtual bool      SupportProperty(ENUM_PEND_REQ_PROP_INTEGER property);
   virtual bool      SupportProperty(ENUM_PEND_REQ_PROP_DOUBLE property);
   virtual bool      SupportProperty(ENUM_PEND_REQ_PROP_STRING property);
//--- Выводит в журнал краткое сообщениес данными запроса, (2) краткое наименование запроса
   virtual void      PrintShort(void);
   virtual string    Header(void);
  };
//+------------------------------------------------------------------+

Метод, возвращающий краткое наименование запроса:

//+------------------------------------------------------------------+
//| Возвращает краткое наименование запроса                          |
//+------------------------------------------------------------------+
string CPendReqRemove::Header(void)
  {
   return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_STATUS_REMOVE)+", ID #"+(string)this.GetProperty(PEND_REQ_PROP_ID);
  }
//+------------------------------------------------------------------+

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

На этом доработка классов-наследников базового класса объекта абстрактного отложенного торгового запроса завершена.


Класс управления объектами-отложенными запросами

Теперь займёмся наконец классом управления объектами-отложенными запросами.

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

Создадим новый класс CTradingControl в файле \MQL5\Include\DoEasy\TradingControl.mqh. Базовым классом при создании этого класса укажем CTrading, и не забываем сразу же после создания класса подключить к нему файл Trading.mqh:

//+------------------------------------------------------------------+
//|                                               PendReqControl.mqh |
//|                        Copyright 2019, MetaQuotes Software Corp. |
//|                             https://mql5.com/ru/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2019, MetaQuotes Software Corp."
#property link      "https://mql5.com/ru/users/artmedia70"
#property version   "1.00"
#property strict    // Нужно для mql4
//+------------------------------------------------------------------+
//| Включаемые файлы                                                 |
//+------------------------------------------------------------------+
#include "Trading.mqh"
//+------------------------------------------------------------------+
//| Класс управления отложенными торговыми запросами                 |
//+------------------------------------------------------------------+
class CTradingControl : public CTrading
  {
private:
//--- Устнанавливает в объект-отложенный запрос фактические данные ордера/позиции
   void                 SetActualProperties(CPendRequest *req_obj,const COrder *order);
public:
//--- Возвращает себя
   CTradingControl     *GetObject(void)            { return &this;   }
//--- Таймер
   virtual void         OnTimer(void);
//--- Конструктор
                        CTradingControl();
  };
//+------------------------------------------------------------------+

Что мы тут видим:
класс имеет приватный метод SetActualProperties(), устанавливающий в объект-отложенный запрос фактические данные ордера/позиции.
В публичной секции метод GetObject() возвращает указатель на весь объект управления отложенными запросами, а виртуальный метод OnTimer() является таймером класса управления отложенными запросами, где всё и будет работать.

В конструкторе класса очищаем список отложенных запросов и устанавливаем ему флаг сортированного списка:

//+------------------------------------------------------------------+
//| Конструктор                                                      |
//+------------------------------------------------------------------+
CTradingControl::CTradingControl()
  {
   this.m_list_request.Clear();
   this.m_list_request.Sort();
  }
//+------------------------------------------------------------------+

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

За пределами тела класса напишем реализацию метода установки фактических данных ордера в объект-запрос:

//+------------------------------------------------------------------+
//| Устнанавливает в объект-отложенный запрос данные ордера/позиции  |
//+------------------------------------------------------------------+
void CTradingControl::SetActualProperties(CPendRequest *req_obj,const COrder *order)
  {
   req_obj.SetActualExpiration(order.TimeExpiration());
   req_obj.SetActualPrice(order.PriceOpen());
   req_obj.SetActualSL(order.StopLoss());
   req_obj.SetActualStopLimit(order.PriceStopLimit());
   req_obj.SetActualTP(order.TakeProfit());
   req_obj.SetActualTypeFilling(order.TypeFilling());
   req_obj.SetActualTypeTime(order.TypeTime());
   req_obj.SetActualVolume(order.Volume());
  }
//+------------------------------------------------------------------+

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

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

Если же запрос ещё не отработан, и наступило время его активации, то на сервер отправляется записанный в отложенном запросе торговый приказ.


В методе организована проверка разрешения на проведение торговых операций со стороны терминала — кнопка "Авто-торговля" и галочка в настройках советника "Разрешить авто-торговлю". И если отложенный торговый запрос создан по коду ошибки 10027 — запрет автоторговли со стороны терминала, то при устранении причины ошибки пользователем (включение кнопки "Авто-торговля" или установка в настройках советника галочки "Разрешить авто-торговлю") этот отложенный запрос сразу же обрабатывается — устанавливается новое время активации отложенного запроса так, чтобы на следующем тике оно наступило — ведь пользователь исправил ошибку, и уже не нужно ожидать, а сразу же стоит отправить приказ на сервер во-избежание последующих реквот.

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

Таймер класса управления отложенными запросами:

//+------------------------------------------------------------------+
//| Таймер                                                           |
//+------------------------------------------------------------------+
void CTradingControl::OnTimer(void)
  {
   //--- В цикле по списку отложенных запросов
   int total=this.m_list_request.Total();
   for(int i=total-1;i>WRONG_VALUE;i--)
     {
      //--- получаем очередной объект-запрос
      CPendRequest *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;
      
      //--- Устанавливаем флаг запрета торговли со стороны терминала одновременно с двух свойств
      //--- (кнопка Авто-торговля в терминале и галочка "Разрешить автоматическую торговлю" в настройках советника)
      //--- Если любое из двух свойств имеет значение false, то флаг будет иметь значение false
      bool terminal_trade_allowed=::TerminalInfoInteger(TERMINAL_TRADE_ALLOWED);
      terminal_trade_allowed &=::MQLInfoInteger(MQL_TRADE_ALLOWED);
      //--- Если объект-запрос создан по коду ошибки
      if(req_obj.TypeRequest()==PEND_REQ_TYPE_ERROR)
        {
         //--- если ошибка - запрет торговли со стороны терминала, и её причина устранена
         if(req_obj.Retcode()==10027 && terminal_trade_allowed)
           {
            //--- если текущая попытка ещё не превысила установленное количество торговых попыток
            if(req_obj.CurrentAttempt()<req_obj.TotalAttempts()+1)
              {
               //--- Установим время создания запроса равным времени его создания минус время ожидания - т.е., отослать запрос немедленно
               //--- И уменьшим номер текущей попытки, так как при последующем запросе номер попытки будет увеличен, и если это последняя попытка,
               //--- то она уже исполнена не будет. Но это исправление причины ошибки пользователем, а значит - нужно дать ещё время на последнюю попытку
               req_obj.SetTimeCreate(req_obj.TimeCreate()-req_obj.WaitingMSC());
               req_obj.SetCurrentAttempt(uchar(req_obj.CurrentAttempt()>0 ? req_obj.CurrentAttempt()-1 : 0));
              }
           }
        }

      //--- если текущая попытка превысила установленное количество торговых попыток,
      //--- или текущее время больше времени ожидания всех попыток
      //--- удаляем текущий объект-запрос и переходим к следующему
      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(req_obj.Header(),": ",CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_DELETED));
         this.m_list_request.Delete(i);
         continue;
        }
      
      //--- Если это открытие позиции или установка отложенного ордера
      if((req_obj.Action()==TRADE_ACTION_DEAL && req_obj.Position()==0) || req_obj.Action()==TRADE_ACTION_PENDING)
        {
         //--- Получаем идентификатор отложенного запроса
         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)
           {
            if(this.m_log_level>LOG_LEVEL_NO_MSG)
               ::Print(req_obj.Header(),": ",CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_EXECUTED));
            this.m_list_request.Delete(i);
            continue;
           }
        }
      //--- Иначе: полное и частичное закрытие позиции, удаление ордера, модификация параметров ордера и стоп приказов позиции
      else
        {
         CArrayObj *list=NULL;
         //--- если это закрытие позиции, в том числе и встречным
         if((req_obj.Action()==TRADE_ACTION_DEAL && req_obj.Position()>0) || req_obj.Action()==TRADE_ACTION_CLOSE_BY)
           {
            //--- Получаем из списка открытых позиций позицию с нужным тикетом
            list=this.m_market.GetList(ORDER_PROP_TICKET,req_obj.Position(),EQUAL);
            if(::CheckPointer(list)==POINTER_INVALID)
               continue;
            //--- Если в рынке нет такой позиции - запрос отработан: удаляем его и переходим к следующему
            if(list.Total()==0)
              {
               if(this.m_log_level>LOG_LEVEL_NO_MSG)
                  ::Print(req_obj.Header(),": ",CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_EXECUTED));
               this.m_list_request.Delete(i);
               continue;
              }
            //--- Иначе, если позиция ещё существует, то это частичное закрытие
            else
              {
               //--- Получаем список всех торговых событий аккаунта
               list=this.m_events.GetList();
               if(list==NULL)
                  continue;
               //--- В цикле с конца списка торговых событий аккаунта
               int events_total=list.Total();
               for(int j=events_total-1; j>WRONG_VALUE; j--)
                 {
                  //--- получаем очередное торговое событие
                  CEvent *event=list.At(j);
                  if(event==NULL)
                     continue;
                  //--- Если это событие - частичное закрытие, или при закрытии встречным было частичное закрытие
                  if(event.TypeEvent()==TRADE_EVENT_POSITION_CLOSED_PARTIAL || event.TypeEvent()==TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_POS)
                    {
                     //--- Если тикет позиции в торговом событии совпадает с тикетом в отложенном торговом запросе
                     if(event.PositionID()==req_obj.Position())
                       {
                        //--- Получаем объект-позицию из списка рыночных позиций
                        CArrayObj *list_orders=this.m_market.GetList(ORDER_PROP_POSITION_ID,req_obj.Position(),EQUAL);
                        if(list_orders==NULL || list_orders.Total()==0)
                           break;
                        COrder *order=list_orders.At(list_orders.Total()-1);
                        if(order==NULL)
                           break;
                        //--- Устанавливаем фактические данные позиции в объект-отложенный запрос
                        this.SetActualProperties(req_obj,order);
                        //--- Если (исполненный объём заявки + неисполненный объём заявки) равен запрашиваемому объёму в отложенном запросе -
                        //--- запрос отработан: удаляем его и прерываем цикл по списку торговых событий аккаунта
                        if(req_obj.GetProperty(PEND_REQ_PROP_MQL_REQ_VOLUME)==event.VolumeOrderExecuted()+event.VolumeOrderCurrent())
                          {
                           if(this.m_log_level>LOG_LEVEL_NO_MSG)
                              ::Print(req_obj.Header(),": ",CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_EXECUTED));
                           this.m_list_request.Delete(i);
                           break;
                          }
                       }
                    }
                 }
               //--- Если в цикле по списку торговых событий был удалён отработанный объект-отложенный запрос - переходим к следующему
               if(::CheckPointer(req_obj)==POINTER_INVALID)
                  continue;
              }
           }
         //--- Если это модификация стоп-приказов позиции
         if(req_obj.Action()==TRADE_ACTION_SLTP)
           {
            //--- Получаем список всех торговых событий аккаунта
            list=this.m_events.GetList();
            if(list==NULL)
               continue;
            //--- В цикле с конца списка торговых событий аккаунта
            int events_total=list.Total();
            for(int j=events_total-1; j>WRONG_VALUE; j--)
              {
               //--- получаем очередное торговое событие
               CEvent *event=list.At(j);
               if(event==NULL)
                  continue;
               //--- Если это событие - любое изменение стоп-приказов позиции
               if(event.TypeEvent()>TRADE_EVENT_MODIFY_ORDER_TAKE_PROFIT)
                 {
                  //--- Если тикет позиции в торговом событии совпадает с тикетом в отложенном торговом запросе
                  if(event.PositionID()==req_obj.Position())
                    {
                     //--- Получаем объект-позицию из списка рыночных позиций
                     CArrayObj *list_orders=this.m_market.GetList(ORDER_PROP_POSITION_ID,req_obj.Position(),EQUAL);
                     if(list_orders==NULL || list_orders.Total()==0)
                        break;
                     COrder *order=list_orders.At(list_orders.Total()-1);
                     if(order==NULL)
                        break;
                     //--- Устанавливаем фактические данные позиции в объект-отложенный запрос
                     this.SetActualProperties(req_obj,order);
                     //--- Если все модификации отработаны -
                     //--- запрос отработан: удаляем его и прерываем цикл по списку торговых событий аккаунта
                     if(req_obj.IsCompleted())
                       {
                        if(this.m_log_level>LOG_LEVEL_NO_MSG)
                           ::Print(req_obj.Header(),": ",CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_EXECUTED));
                        this.m_list_request.Delete(i);
                        break;
                       }
                    }
                 }
              }
            //--- Если в цикле по списку торговых событий был удалён отработанный объект-отложенный запрос - переходим к следующему
            if(::CheckPointer(req_obj)==POINTER_INVALID)
               continue;
           }
         //--- Если это удаление отложенного ордера
         if(req_obj.Action()==TRADE_ACTION_REMOVE)
           {
            //--- Получаем список удалённых отложенных ордеров из исторического списка
            list=this.m_history.GetList(ORDER_PROP_STATUS,ORDER_STATUS_HISTORY_PENDING,EQUAL);
            if(::CheckPointer(list)==POINTER_INVALID)
               continue;
            //--- Оставляем в списке только один ордер с нужным тикетом
            list=CSelect::ByOrderProperty(list,ORDER_PROP_TICKET,req_obj.Order(),EQUAL);
            //--- Если есть такой ордер - запрос отработан: удаляем его и переходим к следующему
            if(list.Total()>0)
              {
               if(this.m_log_level>LOG_LEVEL_NO_MSG)
                  ::Print(req_obj.Header(),": ",CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_EXECUTED));
               this.m_list_request.Delete(i);
               continue;
              }
           }
         //--- Если это модификация отложенного ордера
         if(req_obj.Action()==TRADE_ACTION_MODIFY)
           {
            //--- Получаем список всех торговых событий аккаунта
            list=this.m_events.GetList();
            if(list==NULL)
               continue;
            //--- В цикле с конца списка торговых событий аккаунта
            int events_total=list.Total();
            for(int j=events_total-1; j>WRONG_VALUE; j--)
              {
               //--- получаем очередное торговое событие
               CEvent *event=list.At(j);
               if(event==NULL)
                  continue;
               //--- Если это событие - любое изменение модифицируемых параметров отложенного ордера
               if(event.TypeEvent()>TRADE_EVENT_TRIGGERED_STOP_LIMIT_ORDER && event.TypeEvent()<TRADE_EVENT_MODIFY_POSITION_STOP_LOSS_TAKE_PROFIT)
                 {
                  //--- Если тикет ордера в торговом событии совпадает с тикетом в отложенном торговом запросе
                  if(event.TicketOrderEvent()==req_obj.Order())
                    {
                     //--- Получаем объект-ордер из списка
                     CArrayObj *list_orders=this.m_market.GetList(ORDER_PROP_TICKET,req_obj.Order(),EQUAL);
                     if(list_orders==NULL || list_orders.Total()==0)
                        break;
                     COrder *order=list_orders.At(0);
                     if(order==NULL)
                        break;
                     //--- Устанавливаем фактические данные ордера в объект-отложенный запрос
                     this.SetActualProperties(req_obj,order);
                     //--- Если все модификации отработаны -
                     //--- запрос отработан: удаляем его и прерываем цикл по списку торговых событий аккаунта
                     if(req_obj.IsCompleted())
                       {
                        if(this.m_log_level>LOG_LEVEL_NO_MSG)
                           ::Print(req_obj.Header(),": ",CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_EXECUTED));
                        this.m_list_request.Delete(i);
                        break;
                       }
                    }
                 }
              }
           }
        }
      
      //--- Если после проверки отработки запроса объект-отложенный запрос удалён - выходим
      if(::CheckPointer(req_obj)==POINTER_INVALID)
         return;
      //--- Устанавливаем время активации запроса в объекте-запросе
      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()+":");
         req_obj.PrintShort();
        }
      
      //--- В зависимости от типа выполняемого действия в торговом запросе 
      switch(request.action)
        {
         //--- Открытие/закрытие позиции
         case TRADE_ACTION_DEAL :
            //--- Если тикета нет в структуре запроса - это открытие позиции
            if(request.position==0)
               this.OpenPosition((ENUM_POSITION_TYPE)request.type,request.volume,request.symbol,request.magic,request.sl,request.tp,request.comment,request.deviation,request.type_filling);
            //--- Если тикет есть в структуре запроса - это закрытие позиции
            else
               this.ClosePosition(request.position,request.volume,request.comment,request.deviation);
            break;
         //--- Модификация StopLoss/TakeProfit позиции
         case TRADE_ACTION_SLTP :
            this.ModifyPosition(request.position,request.sl,request.tp);
            break;
         //--- Закрытие встречным
         case TRADE_ACTION_CLOSE_BY :
            this.ClosePositionBy(request.position,request.position_by);
            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;
         //--- Модификация отложенного ордера
         case TRADE_ACTION_MODIFY :
            this.ModifyOrder(request.order,request.price,request.sl,request.tp,request.stoplimit,request.expiration,request.type_time,request.type_filling);
            break;
         //--- Удаление отложенного ордера
         case TRADE_ACTION_REMOVE :
            this.DeleteOrder(request.order);
            break;
         //---
         default:
            break;
        }
     }
  }
//+------------------------------------------------------------------+

На сегодня это всё, что нужно было сделать в классе управления отложенными торговыми запросами. Если что-либо не понятно в работе таймера класса, то все вопросы можно озвучить в обсуждении к статье.

Внесём изменения в класс базового объекта библиотеки CEngine.

Так как теперь вместо торгового класса будем использовать его наследника — класс управления отложенными запросами, то
вместо подключения к листингу файла торгового класса:

//+------------------------------------------------------------------+
//|                                                       Engine.mqh |
//|                        Copyright 2019, MetaQuotes Software Corp. |
//|                             https://mql5.com/ru/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2019, MetaQuotes Software Corp."
#property link      "https://mql5.com/ru/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Включаемые файлы                                                 |
//+------------------------------------------------------------------+
#include "Services\TimerCounter.mqh"
#include "Collections\HistoryCollection.mqh"
#include "Collections\MarketCollection.mqh"
#include "Collections\EventsCollection.mqh"
#include "Collections\AccountsCollection.mqh"
#include "Collections\SymbolsCollection.mqh"
#include "Collections\ResourceCollection.mqh"
#include "Trading.mqh"
//+------------------------------------------------------------------+

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

//+------------------------------------------------------------------+
//|                                                       Engine.mqh |
//|                        Copyright 2019, MetaQuotes Software Corp. |
//|                             https://mql5.com/ru/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2019, MetaQuotes Software Corp."
#property link      "https://mql5.com/ru/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Включаемые файлы                                                 |
//+------------------------------------------------------------------+
#include "Services\TimerCounter.mqh"
#include "Collections\HistoryCollection.mqh"
#include "Collections\MarketCollection.mqh"
#include "Collections\EventsCollection.mqh"
#include "Collections\AccountsCollection.mqh"
#include "Collections\SymbolsCollection.mqh"
#include "Collections\ResourceCollection.mqh"
#include "TradingControl.mqh"
//+------------------------------------------------------------------+

А вместо объекта торгового класса

//+------------------------------------------------------------------+
//| Класс-основа библиотеки                                          |
//+------------------------------------------------------------------+
class CEngine
  {
private:
   CHistoryCollection   m_history;                       // Коллекция исторических ордеров и сделок
   CMarketCollection    m_market;                        // Коллекция рыночных ордеров и сделок
   CEventsCollection    m_events;                        // Коллекция событий
   CAccountsCollection  m_accounts;                      // Коллекция аккаунтов
   CSymbolsCollection   m_symbols;                       // Коллекция символов
   CResourceCollection  m_resource;                      // Список ресурсов
   CTrading             m_trading;                       // Объект торгового класса
   CArrayObj            m_list_counters;                 // Список счётчиков таймера

будем использовать объект класса управления торговыми запросами:

//+------------------------------------------------------------------+
//| Класс-основа библиотеки                                          |
//+------------------------------------------------------------------+
class CEngine
  {
private:
   CHistoryCollection   m_history;                       // Коллекция исторических ордеров и сделок
   CMarketCollection    m_market;                        // Коллекция рыночных ордеров и сделок
   CEventsCollection    m_events;                        // Коллекция событий
   CAccountsCollection  m_accounts;                      // Коллекция аккаунтов
   CSymbolsCollection   m_symbols;                       // Коллекция символов
   CResourceCollection  m_resource;                      // Список ресурсов
   CTradingControl      m_trading;                       // Объект управления торговлей
   CArrayObj            m_list_counters;                 // Список счётчиков таймера

И на сегодня это все изменения в библиотеке.


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

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

Скомпилируем советник и запустим его на графике демо-счёта.

Нам нужно проверить как отрабатывают объекты отложенных запросов при отключенной кнопке Авто-торговля в терминале.
В настройках советника установим дистанцию установки StopLoss и TakeProfit в значения 0.
За это отвечают два настраиваемых параметра:

  • StopLoss in points
  • TakeProfit in points.

А далее сделаем так:
сначала отключим автоторговлю кнопкой "Авто-торговля" в терминале, пытаемся отправить торговый запрос, например, на установку отложенного ордера SellLimit кнопкой с торговой панели советника.
Соответственно, получим ошибку запрета торговли со стороны терминала,
при этом создастся отложенный запрос
.
Далее сразу же нажимаем кнопку "Авто-торговля" чтобы исправить ошибку.
И по замыслу — тут же должен быть активирован только что созданный торговый запрос, который отошлёт торговый запрос на сервер,
и после его исполнения
объект-запрос будет удалён как исполненный:

2019.12.27 04:16:28.894 automated trading is disabled
2019.12.27 04:16:33.177 CTrading::PlaceOrder<uint,int,uint,uint>: Invalid request:
2019.12.27 04:16:33.177 There is no permission to conduct trading operations in the terminal (the "AutoTrading" button is disabled)
2019.12.27 04:16:33.177 Correction of trade request parameters ...
2019.12.27 04:16:33.178 Pending request created #1:
2019.12.27 04:16:33.178 Pending request to place a pending order: 
2019.12.27 04:16:33.178 - GBPUSD 0.10 Pending order Sell Limit, Price 1.30088
2019.12.27 04:16:33.178 - Pending request ID: #1, Created 2019.12.26 23:16:30.003, Attempts 5, Wait 00:00:20, End 2019.12.26 23:18:10.003
2019.12.27 04:16:33.178 
2019.12.27 04:16:37.397 automated trading is enabled
2019.12.27 04:16:37.472 Retry trading attempt #1:
2019.12.27 04:16:37.472 Pending request to place a pending order: 
2019.12.27 04:16:37.472 - GBPUSD 0.10 Pending order Sell Limit, Price 1.30088
2019.12.27 04:16:37.472 - Pending request ID: #1, Created 2019.12.26 23:16:10.003, Attempts 5, Wait 00:00:20, End 2019.12.26 23:17:50.003
2019.12.27 04:16:37.472 
2019.12.27 04:16:37.977 - Pending order placed: 2019.12.26 23:16:38.325 -
2019.12.27 04:16:37.977 GBPUSD Placed 0.10 Pending order Sell Limit #500442708 at price 1.30088, Magic number 26148987 (123), G1: 15, G2: 8, ID: 1
2019.12.27 04:16:37.979 OnDoEasyEvent: Pending order placed
2019.12.27 04:16:38.024 Pending request to place a pending order, ID #1: Deleted due completed

Теперь необходимо проверить возможность модификации стоп-приказов только что установленного отложенного ордера (он был установлен без них).
Опять отключим автоторговлю и нажмём кнопку "Set StopLoss" на торговой панели тестового советника.
Получим ошибку запрета проведения торговых операций со стороны терминала
и
будет создан отложенный запрос на изменение параметров отложенного ордера
.
После очередной торговой попытки, которую уже отсылает отложенный запрос,
разрешим в терминале автоторговлю
, и
отложенному ордеру будет выставлен StopLoss
,
а сам отложенный запрос удалён как отработавший:

2019.12.27 04:24:11.671 automated trading is disabled
2019.12.27 04:24:16.653 CTrading::ModifyOrder<int,int,double,int>: Invalid request:
2019.12.27 04:24:16.653 There is no permission to conduct trading operations in the terminal (the "AutoTrading" button is disabled)
2019.12.27 04:24:16.653 Correction of trade request parameters ...
2019.12.27 04:24:16.653 Pending request created #1:
2019.12.27 04:24:16.653 Pending request to modify pending order parameters: 
2019.12.27 04:24:16.653 - GBPUSD 0.10 Pending order Sell Limit #500442708, Price 1.30088, StopLoss order level 1.30208
2019.12.27 04:24:16.653 - Order execution type: The order is executed at an available volume, unfulfilled remains in the market (Return)
2019.12.27 04:24:16.653 - Order expiration type: Good till cancel order
2019.12.27 04:24:16.653 - Pending request ID: #1, Created 2019.12.26 23:24:00.270, Attempts 5, Wait 00:00:20, End 2019.12.26 23:25:40.270
2019.12.27 04:24:16.653 
2019.12.27 04:24:25.803 Retry trading attempt #1:
2019.12.27 04:24:25.803 Pending request to modify pending order parameters: 
2019.12.27 04:24:25.803 - GBPUSD 0.10 Pending order Sell Limit #500442708, Price 1.30088, StopLoss order level 1.30208
2019.12.27 04:24:25.803 - Order execution type: The order is executed at an available volume, unfulfilled remains in the market (Return)
2019.12.27 04:24:25.803 - Order expiration type: Good till cancel order
2019.12.27 04:24:25.803 - Pending request ID: #1, Created 2019.12.26 23:24:00.270, Attempts 5, Wait 00:00:20, End 2019.12.26 23:25:40.270
2019.12.27 04:24:25.803 
2019.12.27 04:24:29.770 automated trading is enabled
2019.12.27 04:24:30.022 Retry trading attempt #1:
2019.12.27 04:24:30.022 Pending request to modify pending order parameters: 
2019.12.27 04:24:30.022 - GBPUSD 0.10 Pending order Sell Limit #500442708, Price 1.30088, StopLoss order level 1.30208
2019.12.27 04:24:30.022 - Order execution type: The order is executed at an available volume, unfulfilled remains in the market (Return)
2019.12.27 04:24:30.022 - Order expiration type: Good till cancel order
2019.12.27 04:24:30.022 - Pending request ID: #1, Created 2019.12.26 23:23:40.270, Attempts 5, Wait 00:00:20, End 2019.12.26 23:25:20.270
2019.12.27 04:24:30.022 
2019.12.27 04:24:30.405 - Modified order StopLoss: 2019.12.26 23:16:38.325 -
2019.12.27 04:24:30.405 GBPUSD Pending order Sell Limit #500442708: Modified order StopLoss: [0.00000 --> 1.30208], Magic number 26148987 (123), G1: 15, G2: 8, ID: 1
2019.12.27 04:24:30.405 OnDoEasyEvent: Modified order StopLoss
2019.12.27 04:24:30.601 Pending request to modify pending order parameters, ID #1: Deleted due completed

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

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

2019.12.27 04:32:46.843 automated trading is disabled
2019.12.27 04:32:50.810 CTrading::ModifyOrder<int,int,int,double>: Invalid request:
2019.12.27 04:32:50.810 There is no permission to conduct trading operations in the terminal (the "AutoTrading" button is disabled)
2019.12.27 04:32:50.810 Correction of trade request parameters ...
2019.12.27 04:32:50.810 Pending request created #1:
2019.12.27 04:32:50.810 Pending request to modify pending order parameters: 
2019.12.27 04:32:50.810 - GBPUSD 0.10 Pending order Sell Limit #500442708, Price 1.30088, StopLoss order level 1.30208, TakeProfit order level 1.29888
2019.12.27 04:32:50.810 - Order execution type: The order is executed at an available volume, unfulfilled remains in the market (Return)
2019.12.27 04:32:50.810 - Order expiration type: Good till cancel order
2019.12.27 04:32:50.810 - Pending request ID: #1, Created 2019.12.26 23:32:47.943, Attempts 5, Wait 00:00:20, End 2019.12.26 23:34:27.943
2019.12.27 04:32:50.810 
2019.12.27 04:33:08.782 Retry trading attempt #1:
2019.12.27 04:33:08.782 Pending request to modify pending order parameters: 
2019.12.27 04:33:08.782 - GBPUSD 0.10 Pending order Sell Limit #500442708, Price 1.30088, StopLoss order level 1.30208, TakeProfit order level 1.29888
2019.12.27 04:33:08.782 - Order execution type: The order is executed at an available volume, unfulfilled remains in the market (Return)
2019.12.27 04:33:08.782 - Order expiration type: Good till cancel order
2019.12.27 04:33:08.782 - Pending request ID: #1, Created 2019.12.26 23:32:47.943, Attempts 5, Wait 00:00:20, End 2019.12.26 23:34:27.943
2019.12.27 04:33:08.782 
2019.12.27 04:33:29.984 Retry trading attempt #2:
2019.12.27 04:33:29.984 Pending request to modify pending order parameters: 
2019.12.27 04:33:29.984 - GBPUSD 0.10 Pending order Sell Limit #500442708, Price 1.30088, StopLoss order level 1.30208, TakeProfit order level 1.29888
2019.12.27 04:33:29.984 - Order execution type: The order is executed at an available volume, unfulfilled remains in the market (Return)
2019.12.27 04:33:29.984 - Order expiration type: Good till cancel order
2019.12.27 04:33:29.984 - Pending request ID: #1, Created 2019.12.26 23:32:47.943, Attempts 5, Wait 00:00:20, End 2019.12.26 23:34:27.943
2019.12.27 04:33:29.984 
2019.12.27 04:33:31.999 automated trading is enabled
2019.12.27 04:33:32.250 Retry trading attempt #2:
2019.12.27 04:33:32.250 Pending request to modify pending order parameters: 
2019.12.27 04:33:32.250 - GBPUSD 0.10 Pending order Sell Limit #500442708, Price 1.30088, StopLoss order level 1.30208, TakeProfit order level 1.29888
2019.12.27 04:33:32.250 - Order execution type: The order is executed at an available volume, unfulfilled remains in the market (Return)
2019.12.27 04:33:32.250 - Order expiration type: Good till cancel order
2019.12.27 04:33:32.250 - Pending request ID: #1, Created 2019.12.26 23:32:27.943, Attempts 5, Wait 00:00:20, End 2019.12.26 23:34:07.943
2019.12.27 04:33:32.250 
2019.12.27 04:33:32.352 - Modified order TakeProfit: 2019.12.26 23:24:26.509 -
2019.12.27 04:33:32.352 GBPUSD Pending order Sell Limit #500442708: Modified order TakeProfit: [0.00000 --> 1.29888], Magic number 26148987 (123), G1: 15, G2: 8, ID: 1
2019.12.27 04:33:32.352 OnDoEasyEvent: Modified order TakeProfit
2019.12.27 04:33:32.754 Pending request to modify pending order parameters, ID #1: Deleted due completed

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

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

Что дальше

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

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

К содержанию

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

Часть 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. Работа с отложенными торговыми запросами - первая реализация (открытие позиций)
Часть 27. Работа с отложенными торговыми запросами - выставление отложенных ордеров
Часть 28. Работа с отложенными торговыми запросами - закрытие, удаление, модификации
Часть 29. Работа с отложенными торговыми запросами - классы объектов-запросов
Прикрепленные файлы |
MQL5.zip (3664.97 KB)
MQL4.zip (3664.97 KB)
Последние комментарии | Перейти к обсуждению на форуме трейдеров (8)
Artyom Trishkin
Artyom Trishkin | 9 янв. 2020 в 13:11
Сергей Романов:

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

Я с предложением: может опубликуем репозиторий с вашей либой на гитхаб? Что это даст:

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

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

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

Здравствуйте. Спасибо за отзыв.

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

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

MQL_User
MQL_User | 5 авг. 2022 в 13:37

При компилировании файла TradingControl.mqh выпадают две ошибки:

'CTrading::OpenPosition<double,d…' - cannot access private member function    TradingControl.mqh    328    21
   see declaration of 'CTrading::OpenPosition<double,double>'    Trading.mqh    146    24
'CTrading::PlaceOrder<double,dou…' - cannot access private member function    TradingControl.mqh    344    18
   see declaration of 'CTrading::PlaceOrder<double,double,double,double>'    Trading.mqh    156    26

Эти методы находятся в приватной секции класса CTrading. Ошибки уходят, если их перенести в публичную секцию этого класса. Но в прикреплённых файлах (а как я понимаю, они рабочие) эти методы тоже в приватной секции класса CTrading, и тоже при компилировании файла TradingControl.mqh выпадают эти две ошибки.

Артём, а как же тогда у вас работает? Либо здесь ошибка, либо я чего-то недопонимаю.

P.S. Я скачал прикреплённые файлы к следующей части - 31, и там тоже эти методы находятся в приватной секции класса CTrading и тоже выпадают эти две ошибки при компилировании.

Artyom Trishkin
Artyom Trishkin | 5 авг. 2022 в 13:51
MQL_User #:

При компилировании файла TradingControl.mqh выпадают две ошибки:

'CTrading::OpenPosition<double,d…' - cannot access private member function    TradingControl.mqh    328    21
   see declaration of 'CTrading::OpenPosition<double,double>'    Trading.mqh    146    24
'CTrading::PlaceOrder<double,dou…' - cannot access private member function    TradingControl.mqh    344    18
   see declaration of 'CTrading::PlaceOrder<double,double,double,double>'    Trading.mqh    156    26

Эти методы находятся в приватной секции класса CTrading. Ошибки уходят, если их перенести в публичную секцию этого класса. Но в прикреплённых файлах (а как я понимаю, они рабочие) эти методы тоже в приватной секции класса CTrading, и тоже при компилировании файла TradingControl.mqh выпадают эти две ошибки.

Артём, а как же тогда у вас работает? Либо здесь ошибка, либо я чего-то недопонимаю.

P.S. Я скачал прикреплённые файлы к следующей части - 31, и там тоже эти методы находятся в приватной секции класса CTrading и тоже выпадают эти две ошибки при компилировании.

Это стало так после недавнего обновления. Попробуйте их в защищённой (protected) секции сделать - чтобы не было к ним доступа извне. Публичные - слишком нехорошо

MQL_User
MQL_User | 5 авг. 2022 в 14:39
Artyom Trishkin #:

Это стало так после недавнего обновления. Попробуйте их в защищённой (protected) секции сделать - чтобы не было к ним доступа извне. Публичные - слишком нехорошо

Хорошо, так и сделаю.

И ещё есть вопрос: есть методы в public-секции ClosePosition, PlaceBuyStop, PlaceBuyLimit, и т.д. Для них не критично, что они находятся в public-секции?

Artyom Trishkin
Artyom Trishkin | 5 авг. 2022 в 14:57
MQL_User #:

Хорошо, так и сделаю.

И ещё есть вопрос: есть методы в public-секции ClosePosition, PlaceBuyStop, PlaceBuyLimit, и т.д. Для них не критично, что они находятся в public-секции?

Она там и должны быть - это одни из методов для работы с ордерами и позициями

Непрерывная скользящая оптимизация (Часть 3): Способ адаптации робота к автооптимизатору Непрерывная скользящая оптимизация (Часть 3): Способ адаптации робота к автооптимизатору
Третья статья служит неким мостом между двумя предыдущими, в ней освещается механизм взаимодействия с DLL, написанной в первой статье, и объектами для выгрузки из второй статьи. Показывается процесс создания обертки для класса, который импортируется из DLL и формирует XML-файл с историей торгов, а также способ взаимодействии с данной оберткой.
Непрерывная скользящая оптимизация (Часть 2): Механизм создания отчета оптимизации для любого робота Непрерывная скользящая оптимизация (Часть 2): Механизм создания отчета оптимизации для любого робота
Если прошлая статья повествовала о создании DLL-библиотеки, которая будет использоваться в нашем автооптимизаторе и в роботе, то продолжение будет целиком посвящено языку MQL5.
Эконометрический подход к поиску рыночных закономерностей: автокорреляция, тепловые карты и диаграммы рассеяния Эконометрический подход к поиску рыночных закономерностей: автокорреляция, тепловые карты и диаграммы рассеяния
Расширенное исследование сезонных характеристик: автокорреляция тепловые карты и диаграммы рассеяния. Целью текущей статьи является показать, что "память рынка" имеет сезонный характер, который выражается через максимизацию корреляции приращений произвольного порядка.
Библиотека для простого и быстрого создания программ для MetaTrader (Часть XXIX): Отложенные торговые запросы - классы объектов-запросов Библиотека для простого и быстрого создания программ для MetaTrader (Часть XXIX): Отложенные торговые запросы - классы объектов-запросов
В прошлых статьях проверили концепцию отложенных торговых запросов. Отложенный запрос — это по сути обычный торговый приказ, но исполняемый по некоему условию. Сегодня создадим полноценные классы объектов-отложенных запросов — базовый объект-запрос и его наследников.