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

Artyom Trishkin | 13 января, 2020

Содержание


Концепция

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

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

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

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

Чтобы объект-отложенный запрос мог работать как часть торговой логики (отсылка торговых приказов по условию), нам нужно добавить в этот объект дополнительные данные для хранения условий активации отложенного запроса и методы их контроля и обработки. Хранилищем таких данных у нас будет двумерный массив, в котором в первом измерении будет храниться номер условия (условий может быть сколь угодно), а во втором измерении — все данные условия, номер которого указан в первом измерении — тип источника условия (символ, аккаунт или событие), само условие (создадим перечисления для каждого из источников), метод сравнения (>,<,==,!=,>=,<=), контрольное значение отслеживаемого свойства и текущее его значение.

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

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


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

Начнём как обычно с добавления индексов новых сообщений библиотеки и текстов сообщений, соответствующих индексам.

В файле Datas.mqh впишем все необходимые индексы сообщений:

//--- CEvent
   MSG_EVN_EVENT,                                     // Событие
   MSG_EVN_TYPE,                                      // Тип события

...

//--- CAccount
   MSG_ACC_ACCOUNT,                                   // Аккаунт
   MSG_ACC_PROP_LOGIN,                                // Номер счёта

...

   MSG_LIB_TEXT_REQUEST,                              // Отложенный запрос #
   MSG_LIB_TEXT_REQUEST_ACTIVATED,                    // Активирован отложенный запрос: #
   MSG_LIB_TEXT_REQUEST_DATAS,                        // Параметры торгового запроса
   MSG_LIB_TEXT_PEND_REQUEST_DATAS,                   // Параметры отложенного торгового запроса
   MSG_LIB_TEXT_PEND_REQUEST_CREATED,                 // Создан отложенный запрос
   MSG_LIB_TEXT_PEND_REQUEST_DELETED,                 // Удалён в связи с окончанием времени его действия
   MSG_LIB_TEXT_PEND_REQUEST_EXECUTED,                // Удалён в связи с его исполнением
   MSG_LIB_TEXT_PEND_REQUEST_GETTING_FAILED,          // Не удалось получить объект-отложенный запрос из списка
   MSG_LIB_TEXT_PEND_REQUEST_FAILED_ADD_PARAMS,       // Не удалось добавить параметры активации запроса. Ошибка: 

   MSG_LIB_TEXT_PEND_REQUEST_PRICE_CREATE,            // Цена в момент создания запроса

...

   MSG_LIB_TEXT_PEND_REQUEST_ACTUAL_EXPIRATION,       // Фактическое время жизни ордера
   
   MSG_LIB_TEXT_PEND_REQUEST_NO_FREE_IDS,             // Нет свободных идентификаторов для создания отложенного запроса
   MSG_LIB_TEXT_PEND_REQUEST_ACTIVATION_TERMS,        // Условия активации
   MSG_LIB_TEXT_PEND_REQUEST_CRITERION,               // Критерий
   MSG_LIB_TEXT_PEND_REQUEST_ADD_CRITERIONS,          // Добавлены условия активации отложенного запроса
   
  };
//+------------------------------------------------------------------+

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

//--- CEvent
   {"Событие","Event"},
   {"Тип события","Event's type"},

...

//--- CAccount
   {"Аккаунт","Account"},
   {"Номер счёта","Account number"},

...

   {"Отложенный запрос #","Pending request #"},
   {"Активирован отложенный запрос: #","Pending request activated: #"},
   {"Параметры торгового запроса","Trade request parameters"},
   {"Параметры отложенного торгового запроса","Pending trade request parameters"},
   {"Создан отложенный запрос","Pending request created"},
   {"Удалён в связи с окончанием времени его действия","Deleted due to expiration"},
   {"Удалён в связи с его исполнением","Deleted due completed"},
   {"Не удалось получить объект-отложенный запрос из списка","Failed to get pending request object from list"},
   {"Не удалось добавить параметры активации запроса. Ошибка: ","Failed to add request activation parameters. Error: "},
   
   {"Цена в момент создания запроса","Price at time of request create"},

...

   {"Фактическое время жизни ордера","Actual of order lifetime"},
   
   {"Нет свободных идентификаторов для создания отложенного запроса","No free IDs to create a pending request"},
   {"Условия активации","Activation terms"},
   {"Критерий","Criterion"},
   {"Добавлены условия активации отложенного запроса","Pending request activation conditions added"},
   
  };
//+---------------------------------------------------------------------+

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

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

//+------------------------------------------------------------------+
//| Тип отложенного запроса                                          |
//+------------------------------------------------------------------+
enum ENUM_PEND_REQ_TYPE
  {
   PEND_REQ_TYPE_ERROR=PENDING_REQUEST_ID_TYPE_ERR,         // Отложенный запрос, созданный по коду возврата или ошибке
   PEND_REQ_TYPE_REQUEST=PENDING_REQUEST_ID_TYPE_REQ,       // Отложенный запрос, созданный по запросу
  };
//+------------------------------------------------------------------+
//| Источник активации отложенного запроса                           |
//+------------------------------------------------------------------+
enum ENUM_PEND_REQ_ACTIVATION_SOURCE
  {
   PEND_REQ_ACTIVATION_SOURCE_ACCOUNT,                      // Отложенный запрос активируется данными аккаунта
   PEND_REQ_ACTIVATION_SOURCE_SYMBOL,                       // Отложенный запрос активируется данными символа
   PEND_REQ_ACTIVATION_SOURCE_EVENT,                        // Отложенный запрос активируется данными торгового события
  };
//+------------------------------------------------------------------+
//| Целочисленные свойства отложенного торгового запроса             |
//+------------------------------------------------------------------+
enum ENUM_PEND_REQ_PROP_INTEGER
  {

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

//+------------------------------------------------------------------+
//| Возможные критерии активизации запросов по свойствам аккаунта    |
//+------------------------------------------------------------------+
enum ENUM_PEND_REQ_ACTIVATE_BY_ACCOUNT_PROP
  {
   //--- long
   PEND_REQ_ACTIVATE_BY_ACCOUNT_EMPTY                    =  MSG_LIB_PROP_NOT_SET,                     // Значение не задано
   PEND_REQ_ACTIVATE_BY_ACCOUNT_LEVERAGE                 =  MSG_ACC_PROP_LEVERAGE,                    // Активация по размеру предоставленного плеча
   PEND_REQ_ACTIVATE_BY_ACCOUNT_LIMIT_ORDERS             =  MSG_ACC_PROP_LIMIT_ORDERS,                // Активация по максимально допустимому количество действующих отложенных ордеров
   PEND_REQ_ACTIVATE_BY_ACCOUNT_TRADE_ALLOWED            =  MSG_ACC_PROP_TRADE_ALLOWED,               // Активация по разрешенности торговли для текущего счета со стороны сервера
   PEND_REQ_ACTIVATE_BY_ACCOUNT_TRADE_EXPERT             =  MSG_ACC_PROP_TRADE_EXPERT,                // Активация по разрешенности торговли для эксперта со стороны сервера
   //--- double
   PEND_REQ_ACTIVATE_BY_ACCOUNT_BALANCE                  =  MSG_ACC_PROP_BALANCE,                     // Активация по балансу счета в валюте депозита
   PEND_REQ_ACTIVATE_BY_ACCOUNT_CREDIT                   =  MSG_ACC_PROP_CREDIT,                      // Активация по размеру предоставленного кредита в валюте депозита
   PEND_REQ_ACTIVATE_BY_ACCOUNT_PROFIT                   =  MSG_ACC_PROP_PROFIT,                      // Активация по размеру текущей прибыли на счете в валюте депозита
   PEND_REQ_ACTIVATE_BY_ACCOUNT_EQUITY                   =  MSG_ACC_PROP_EQUITY,                      // Активация по значению собственных средств на счете в валюте депозита
   PEND_REQ_ACTIVATE_BY_ACCOUNT_MARGIN                   =  MSG_ACC_PROP_MARGIN,                      // Активация по размеру зарезервированных залоговых средств на счете в валюте депозита
   PEND_REQ_ACTIVATE_BY_ACCOUNT_MARGIN_FREE              =  MSG_ACC_PROP_MARGIN_FREE,                 // Активация по размеру свободных средств на счете в валюте депозита, доступных для открытия позиции
   PEND_REQ_ACTIVATE_BY_ACCOUNT_MARGIN_LEVEL             =  MSG_ACC_PROP_MARGIN_LEVEL,                // Активация по уровню залоговых средств на счете в процентах
   PEND_REQ_ACTIVATE_BY_ACCOUNT_MARGIN_INITIAL           =  MSG_ACC_PROP_MARGIN_INITIAL,              // Активация по размеру средств, зарезервированных на счёте, для обеспечения гарантийной суммы по всем отложенным ордерам
   PEND_REQ_ACTIVATE_BY_ACCOUNT_MARGIN_MAINTENANCE       =  MSG_ACC_PROP_MARGIN_MAINTENANCE,          // Активация по размеру средств, зарезервированных на счёте, для обеспечения минимальной суммы по всем открытым позициям
   PEND_REQ_ACTIVATE_BY_ACCOUNT_ASSETS                   =  MSG_ACC_PROP_ASSETS,                      // Активация по текущему размеру активов на счёте
   PEND_REQ_ACTIVATE_BY_ACCOUNT_LIABILITIES              =  MSG_ACC_PROP_LIABILITIES,                 // Активация по текущему размеру обязательств на счёте
   PEND_REQ_ACTIVATE_BY_ACCOUNT_COMMISSION_BLOCKED       =  MSG_ACC_PROP_COMMISSION_BLOCKED           // Активация по текущей сумме заблокированных комиссий по счёту
  };
//+------------------------------------------------------------------+
//| Возможные критерии активизации запросов по свойствам символа     |
//+------------------------------------------------------------------+
enum ENUM_PEND_REQ_ACTIVATE_BY_SYMBOL_PROP
  {
   PEND_REQ_ACTIVATE_BY_SYMBOL_EMPTY                     =  MSG_LIB_PROP_NOT_SET,                     // Значение не задано
   //--- double
   PEND_REQ_ACTIVATE_BY_SYMBOL_BID                       =  MSG_LIB_PROP_BID,                         // Активация по Bid - лучшее предложение на продажу
   PEND_REQ_ACTIVATE_BY_SYMBOL_ASK                       =  MSG_LIB_PROP_ASK,                         // Активация по Ask - лучшее предложение на покупку
   PEND_REQ_ACTIVATE_BY_SYMBOL_LAST                      =  MSG_LIB_PROP_LAST,                        // Активация по цене, по которой совершена последняя сделка
   //--- long
   PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_DEALS             =  MSG_SYM_PROP_SESSION_DEALS,               // Активация по количеству сделок в текущей сессии
   PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_BUY_ORDERS        =  MSG_SYM_PROP_SESSION_BUY_ORDERS,          // Активация по общему числу ордеров на покупку в текущий момент
   PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_SELL_ORDERS       =  MSG_SYM_PROP_SESSION_SELL_ORDERS,         // Активация по общему числу ордеров на продажу в текущий момент
   PEND_REQ_ACTIVATE_BY_SYMBOL_VOLUME                    =  MSG_SYM_PROP_VOLUME,                      // Активация по объему в последней сделке
   PEND_REQ_ACTIVATE_BY_SYMBOL_VOLUMEHIGH                =  MSG_SYM_PROP_VOLUMEHIGH,                  // Активация по максимальному Volume за день
   PEND_REQ_ACTIVATE_BY_SYMBOL_VOLUMELOW                 =  MSG_SYM_PROP_VOLUMELOW,                   // Активация по минимальному Volume за день
   PEND_REQ_ACTIVATE_BY_SYMBOL_TIME                      =  MSG_SYM_PROP_TIME,                        // Активация по времени последней котировки
   PEND_REQ_ACTIVATE_BY_SYMBOL_SPREAD                    =  MSG_SYM_PROP_SPREAD,                      // Активация по размеру спреда в пунктах
   PEND_REQ_ACTIVATE_BY_SYMBOL_START_TIME                =  MSG_SYM_PROP_START_TIME,                  // Активация по дате начала торгов по инструменту (обычно используется для фьючерсов)
   PEND_REQ_ACTIVATE_BY_SYMBOL_EXPIRATION_TIME           =  MSG_SYM_PROP_EXPIRATION_TIME,             // Активация по дате окончания торгов по инструменту (обычно используется для фьючерсов)
   PEND_REQ_ACTIVATE_BY_SYMBOL_TRADE_STOPS_LEVEL         =  MSG_SYM_PROP_TRADE_STOPS_LEVEL,           // Активация по минимальному отступу в пунктах от текущей цены закрытия для установки Stop ордеров
   PEND_REQ_ACTIVATE_BY_SYMBOL_TRADE_FREEZE_LEVEL        =  MSG_SYM_PROP_TRADE_FREEZE_LEVEL,          // Активация по дистанции заморозки торговых операций (в пунктах)
   //--- double
   PEND_REQ_ACTIVATE_BY_SYMBOL_BIDHIGH                   =  MSG_SYM_PROP_BIDHIGH,                     // Активация по максимальному Bid за день
   PEND_REQ_ACTIVATE_BY_SYMBOL_BIDLOW                    =  MSG_SYM_PROP_BIDLOW,                      // Активация по минимальному Bid за день
   PEND_REQ_ACTIVATE_BY_SYMBOL_ASKHIGH                   =  MSG_SYM_PROP_ASKHIGH,                     // Активация по максимальному Ask за день
   PEND_REQ_ACTIVATE_BY_SYMBOL_ASKLOW                    =  MSG_SYM_PROP_ASKLOW,                      // Активация по минимальному Ask за день
   PEND_REQ_ACTIVATE_BY_SYMBOL_LASTHIGH                  =  MSG_SYM_PROP_LASTHIGH,                    // Активация по максимальному Last за день
   PEND_REQ_ACTIVATE_BY_SYMBOL_LASTLOW                   =  MSG_SYM_PROP_LASTLOW,                     // Активация по минимальному Last за день
   PEND_REQ_ACTIVATE_BY_SYMBOL_VOLUME_REAL               =  MSG_SYM_PROP_VOLUME_REAL,                 // Активация по Volume за день
   PEND_REQ_ACTIVATE_BY_SYMBOL_VOLUMEHIGH_REAL           =  MSG_SYM_PROP_VOLUMEHIGH_REAL,             // Активация по максимальному Volume за день
   PEND_REQ_ACTIVATE_BY_SYMBOL_VOLUMELOW_REAL            =  MSG_SYM_PROP_VOLUMELOW_REAL,              // Активация по минимальному Volume за день
   PEND_REQ_ACTIVATE_BY_SYMBOL_OPTION_STRIKE             =  MSG_SYM_PROP_OPTION_STRIKE,               // Активация по цене исполнения опциона
   PEND_REQ_ACTIVATE_BY_SYMBOL_TRADE_ACCRUED_INTEREST    =  MSG_SYM_PROP_TRADE_ACCRUED_INTEREST,      // Активация по накопленному купонному доходу
   PEND_REQ_ACTIVATE_BY_SYMBOL_TRADE_FACE_VALUE          =  MSG_SYM_PROP_TRADE_FACE_VALUE,            // Активация по номинальной стоимости – начальная стоимость облигации, установленная эмитентом
   PEND_REQ_ACTIVATE_BY_SYMBOL_TRADE_LIQUIDITY_RATE      =  MSG_SYM_PROP_TRADE_LIQUIDITY_RATE,        // Активация по коэффициенту ликвидности – доля от стоимости актива, которую можно использовать в качестве залога
   PEND_REQ_ACTIVATE_BY_SYMBOL_SWAP_LONG                 =  MSG_SYM_PROP_SWAP_LONG,                   // Активация по значению свопа в покупку
   PEND_REQ_ACTIVATE_BY_SYMBOL_SWAP_SHORT                =  MSG_SYM_PROP_SWAP_SHORT,                  // Активация по значению свопа в продажу
   PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_VOLUME            =  MSG_SYM_PROP_SESSION_VOLUME,              // Активация по суммарному объёму сделок в текущую сессию
   PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_TURNOVER          =  MSG_SYM_PROP_SESSION_TURNOVER,            // Активация по суммарному обороту в текущую сессию
   PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_INTEREST          =  MSG_SYM_PROP_SESSION_INTEREST,            // Активация по суммарному объёму открытых позиций
   PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_BUY_ORDERS_VOLUME =  MSG_SYM_PROP_SESSION_BUY_ORDERS_VOLUME,   // Активация по общему объёму ордеров на покупку в текущий момент
   PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_SELL_ORDERS_VOLUME=  MSG_SYM_PROP_SESSION_SELL_ORDERS_VOLUME,  // Активация по общему объёму ордеров на продажу в текущий момент
   PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_OPEN              =  MSG_SYM_PROP_SESSION_OPEN,                // Активация по цене открытия сессии
   PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_CLOSE             =  MSG_SYM_PROP_SESSION_CLOSE,               // Активация по цене закрытия сессии
   PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_AW                =  MSG_SYM_PROP_SESSION_AW,                  // Активация по средневзвешенной цене сессии
   PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_PRICE_SETTLEMENT  =  MSG_SYM_PROP_SESSION_PRICE_SETTLEMENT,    // Активация по цене поставки на текущую сессию
   PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_PRICE_LIMIT_MIN   =  MSG_SYM_PROP_SESSION_PRICE_LIMIT_MIN,     // Активация по минимально допустимому значению цены на сессию 
   PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_PRICE_LIMIT_MAX   =  MSG_SYM_PROP_SESSION_PRICE_LIMIT_MAX,     // Активация по максимально допустимому значению цены на сессию
  };
//+------------------------------------------------------------------+
//| Возможные критерии активизации запросов по событиям              |
//+------------------------------------------------------------------+
enum ENUM_PEND_REQ_ACTIVATE_BY_EVENT
  {
   PEND_REQ_ACTIVATE_BY_EVENT_EMPTY                               =  MSG_LIB_PROP_NOT_SET,                        // Значение не задано
   PEND_REQ_ACTIVATE_BY_EVENT_POSITION_OPENED                     =  MSG_EVN_STATUS_MARKET_POSITION,              // Позиция открыта
   PEND_REQ_ACTIVATE_BY_EVENT_POSITION_CLOSED                     =  MSG_EVN_STATUS_HISTORY_POSITION,             // Позиция закрыта
   PEND_REQ_ACTIVATE_BY_EVENT_PENDING_ORDER_PLASED                =  MSG_EVN_PENDING_ORDER_PLASED,                // Отложенный ордер установлен
   PEND_REQ_ACTIVATE_BY_EVENT_PENDING_ORDER_REMOVED               =  MSG_EVN_PENDING_ORDER_REMOVED,               // Отложенный ордер удалён
   PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_CREDIT                      =  MSG_EVN_ACCOUNT_CREDIT,                      // Начисление кредита (3)
   PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_CHARGE                      =  MSG_EVN_ACCOUNT_CHARGE,                      // Дополнительные сборы
   PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_CORRECTION                  =  MSG_EVN_ACCOUNT_CORRECTION,                  // Корректирующая запись
   PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_BONUS                       =  MSG_EVN_ACCOUNT_BONUS,                       // Перечисление бонусов
   PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_COMISSION                   =  MSG_EVN_ACCOUNT_COMISSION,                   // Дополнительные комиссии
   PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_COMISSION_DAILY             =  MSG_EVN_ACCOUNT_COMISSION_DAILY,             // Комиссия, начисляемая в конце торгового дня
   PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_COMISSION_MONTHLY           =  MSG_EVN_ACCOUNT_COMISSION_MONTHLY,           // Комиссия, начисляемая в конце месяца
   PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_COMISSION_AGENT_DAILY       =  MSG_EVN_ACCOUNT_COMISSION_AGENT_DAILY,       // Агентская комиссия, начисляемая в конце торгового дня
   PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_COMISSION_AGENT_MONTHLY     =  MSG_EVN_ACCOUNT_COMISSION_AGENT_MONTHLY,     // Агентская комиссия, начисляемая в конце месяца
   PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_INTEREST                    =  MSG_EVN_ACCOUNT_INTEREST,                    // Начисления процентов на свободные средства
   PEND_REQ_ACTIVATE_BY_EVENT_BUY_CANCELLED                       =  MSG_EVN_BUY_CANCELLED,                       // Отмененная сделка покупки
   PEND_REQ_ACTIVATE_BY_EVENT_SELL_CANCELLED                      =  MSG_EVN_SELL_CANCELLED,                      // Отмененная сделка продажи
   PEND_REQ_ACTIVATE_BY_EVENT_DIVIDENT                            =  MSG_EVN_DIVIDENT,                            // Начисление дивиденда
   PEND_REQ_ACTIVATE_BY_EVENT_DIVIDENT_FRANKED                    =  MSG_EVN_DIVIDENT_FRANKED,                    // Начисление франкированного дивиденда
   PEND_REQ_ACTIVATE_BY_EVENT_TAX                                 =  MSG_EVN_TAX,                                 // Начисление налога
   PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_BALANCE_REFILL              =  MSG_EVN_BALANCE_REFILL,                      // Пополнение средств на балансе
   PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_BALANCE_WITHDRAWAL          =  MSG_EVN_BALANCE_WITHDRAWAL,                  // Снятие средств с баланса
   PEND_REQ_ACTIVATE_BY_EVENT_PENDING_ORDER_ACTIVATED             =  MSG_EVN_ACTIVATED_PENDING,                   // Отложенный ордер активирован ценой
   PEND_REQ_ACTIVATE_BY_EVENT_PENDING_ORDER_ACTIVATED_PARTIAL     =  MSG_EVN_ACTIVATED_PENDING_PARTIALLY,         // Отложенный ордер активирован ценой частично
   PEND_REQ_ACTIVATE_BY_EVENT_POSITION_OPENED_PARTIAL             =  MSG_EVN_POSITION_OPENED_PARTIALLY,           // Позиция открыта частично
   PEND_REQ_ACTIVATE_BY_EVENT_POSITION_CLOSED_PARTIAL             =  MSG_EVN_POSITION_CLOSED_PARTIALLY,           // Позиция закрыта частично
   PEND_REQ_ACTIVATE_BY_EVENT_POSITION_CLOSED_BY_POS              =  MSG_EVN_POSITION_CLOSED_BY_POS,              // Позиция закрыта встречной
   PEND_REQ_ACTIVATE_BY_EVENT_POSITION_CLOSED_PARTIAL_BY_POS      =  MSG_EVN_POSITION_CLOSED_PARTIALLY_BY_POS,    // Позиция закрыта частично встречной
   PEND_REQ_ACTIVATE_BY_EVENT_POSITION_CLOSED_BY_SL               =  MSG_EVN_POSITION_CLOSED_BY_SL,               // Позиция закрыта по StopLoss
   PEND_REQ_ACTIVATE_BY_EVENT_POSITION_CLOSED_BY_TP               =  MSG_EVN_POSITION_CLOSED_BY_TP,               // Позиция закрыта по TakeProfit
   PEND_REQ_ACTIVATE_BY_EVENT_POSITION_CLOSED_PARTIAL_BY_SL       =  MSG_EVN_POSITION_CLOSED_PARTIALLY_BY_SL,     // Позиция закрыта частично по StopLoss
   PEND_REQ_ACTIVATE_BY_EVENT_POSITION_CLOSED_PARTIAL_BY_TP       =  MSG_EVN_POSITION_CLOSED_PARTIALLY_BY_TP,     // Позиция закрыта частично по TakeProfit
   PEND_REQ_ACTIVATE_BY_EVENT_POSITION_REVERSED_BY_MARKET         =  MSG_EVN_POSITION_REVERSED_BY_MARKET,         // Разворот позиции новой сделкой (неттинг)
   PEND_REQ_ACTIVATE_BY_EVENT_POSITION_REVERSED_BY_PENDING        =  MSG_EVN_POSITION_REVERSED_BY_PENDING,        // Разворот позиции активацией отложенного ордера (неттинг)
   PEND_REQ_ACTIVATE_BY_EVENT_POSITION_REVERSED_BY_MARKET_PARTIAL =  MSG_EVN_POSITION_REVERSE_PARTIALLY,          // Разворот позиции частичным исполнением маркет-ордера (неттинг)
   PEND_REQ_ACTIVATE_BY_EVENT_POSITION_VOLUME_ADD_BY_MARKET       =  MSG_EVN_POSITION_VOLUME_ADD_BY_MARKET,       // Добавлен объём к позиции новой сделкой (неттинг)
   PEND_REQ_ACTIVATE_BY_EVENT_POSITION_VOLUME_ADD_BY_PENDING      =  MSG_EVN_POSITION_VOLUME_ADD_BY_PENDING,      // Добавлен объём к позиции активацией отложенного ордера (неттинг)
   PEND_REQ_ACTIVATE_BY_EVENT_MODIFY_ORDER_PRICE                  =  MSG_EVN_MODIFY_ORDER_PRICE,                  // Изменение цены установки ордера
   PEND_REQ_ACTIVATE_BY_EVENT_MODIFY_ORDER_PRICE_SL               =  MSG_EVN_MODIFY_ORDER_PRICE_SL,               // Изменение цены установки ордера и StopLoss 
   PEND_REQ_ACTIVATE_BY_EVENT_MODIFY_ORDER_PRICE_TP               =  MSG_EVN_MODIFY_ORDER_PRICE_TP,               // Изменение цены установки ордера и TakeProfit
   PEND_REQ_ACTIVATE_BY_EVENT_MODIFY_ORDER_PRICE_SL_TP            =  MSG_EVN_MODIFY_ORDER_PRICE_SL_TP,            // Изменение цены установки ордера, StopLoss и TakeProfit
   PEND_REQ_ACTIVATE_BY_EVENT_MODIFY_ORDER_SL_TP                  =  MSG_EVN_MODIFY_ORDER_SL_TP,                  // Изменение StopLoss и TakeProfit ордера
   PEND_REQ_ACTIVATE_BY_EVENT_MODIFY_ORDER_SL                     =  MSG_EVN_MODIFY_ORDER_SL,                     // Изменение StopLoss ордера
   PEND_REQ_ACTIVATE_BY_EVENT_MODIFY_ORDER_TP                     =  MSG_EVN_MODIFY_ORDER_TP,                     // Изменение TakeProfit ордера
   PEND_REQ_ACTIVATE_BY_EVENT_MODIFY_POSITION_SL_TP               =  MSG_EVN_MODIFY_POSITION_SL_TP,               // Изменение StopLoss и TakeProfit позиции
   PEND_REQ_ACTIVATE_BY_EVENT_MODIFY_POSITION_SL                  =  MSG_EVN_MODIFY_POSITION_SL,                  // Изменение StopLoss позиции
   PEND_REQ_ACTIVATE_BY_EVENT_MODIFY_POSITION_TP                  =  MSG_EVN_MODIFY_POSITION_TP,                  // Изменение TakeProfit позиции
   PEND_REQ_ACTIVATE_BY_EVENT_POSITION_VOL_ADD_BY_MARKET_PARTIAL  =  MSG_EVN_REASON_ADD_PARTIALLY,                // Добавлен объём к позиции частичным исполнением маркет-ордера (неттинг)
   PEND_REQ_ACTIVATE_BY_EVENT_POSITION_VOL_ADD_BY_PENDING_PARTIAL =  MSG_EVN_REASON_ADD_BY_PENDING_PARTIALLY,     // Добавлен объём к позиции частичной активацией отложенного ордера (неттинг)
   PEND_REQ_ACTIVATE_BY_EVENT_TRIGGERED_STOP_LIMIT_ORDER          =  MSG_EVN_REASON_STOPLIMIT_TRIGGERED,          // Срабатывание StopLimit ордера
   PEND_REQ_ACTIVATE_BY_EVENT_POSITION_REVERSED_BY_PENDING_PARTIAL=  MSG_EVN_REASON_REVERSE_BY_PENDING_PARTIALLY, // Разворот позиции частичной активацией отложенного ордера (неттинг)
  };
//+------------------------------------------------------------------+

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

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

В файл сервисных функций DELib.mqh впишем функцию, возвращающую описание типа сравнения:

//+------------------------------------------------------------------+
//| Возвращает описание типа сравнения                               |
//+------------------------------------------------------------------+
string ComparisonTypeDescription(const ENUM_COMPARER_TYPE type)
  {
   switch((int)type)
     {
      case EQUAL           :  return " == ";
      case MORE            :  return " > ";
      case LESS            :  return " < ";
      case EQUAL_OR_MORE   :  return " >= ";
      case EQUAL_OR_LESS   :  return " <= ";
      default              :  return " != ";
     }
  }
//+------------------------------------------------------------------+

Во многих файлах библиотеки были изменены наименования констант перечислений, в которых были прописаны строки "STOP_LOSS" и "TAKE_PROFIT". Теперь вхождения этих строк заменены на "SL" и "TP", соответственно.

Объект-отложенный запрос, создаваемый по запросу

Базовый объект абстрактного отложенного запроса теперь будет унаследован от базового объекта всех объектов библиотеки.
Подключим файл базового объекта всех объектов библиотеки к файлу класса CPendRequest и сделаем класс наследником базового объекта:

//+------------------------------------------------------------------+
//|                                                  PendRequest.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 CPendRequest : public CBaseObj
  {

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

//+------------------------------------------------------------------+
//| Класс абстрактного отложенного торгового запроса                 |
//+------------------------------------------------------------------+
class CPendRequest : public CBaseObj
  {
private:
   MqlTradeRequest   m_request;                                            // Структура торгового запроса
   CPause            m_pause;                                              // Объект класса "Пауза"

/* Данные активации отложенного запроса в массиве:
   В первом измерении находится номер критерия активации
   Во втором измерении находятся:

   m_activated_control[criterion number][0] - источник контролируемого свойства
   m_activated_control[criterion number][1] - контролируемое свойство
   m_activated_control[criterion number][2] - тип сравнения контролируемого свойства с фактической величиной (=,>,<,!=,>=,<=)
   m_activated_control[criterion number][3] - контрольное значение величины свойства для активации
   m_activated_control[criterion number][4] - фактическая величина свойства
*/
   double            m_activated_control[][5];                             // Массив контрольных значений критериев активации отложенного запроса
   
//--- Копирует данные торгового запроса
   void              CopyRequest(const MqlTradeRequest &request);

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

//--- Возвращает (1) магический номер, идентификатор (2) магика, (3) первой, (4) второй группы,
//--- (5) флаг счёта с типом "хедж", (6) флаг равенства вещественного свойства значению
   ulong             GetMagic(void)                                        const { return this.GetProperty(PEND_REQ_PROP_MQL_REQ_MAGIC);                             }
   ushort            GetMagicID(void)                                      const { return CBaseObj::GetMagicID((uint)this.GetProperty(PEND_REQ_PROP_MQL_REQ_MAGIC)); }
   uchar             GetGroupID1(void)                                     const { return CBaseObj::GetGroupID1((uint)this.GetProperty(PEND_REQ_PROP_MQL_REQ_MAGIC));}
   uchar             GetGroupID2(void)                                     const { return CBaseObj::GetGroupID2((uint)this.GetProperty(PEND_REQ_PROP_MQL_REQ_MAGIC));}
   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;
   
//--- Возвращает флаг успешности проверки контролируемого свойства объекта и соответствующего ему фактического свойства
   bool              IsComparisonCompleted(const uint index)               const;
//--- Сравнивает два значения источника данных по типу сравнения
   bool              IsCompared(const double actual_value,const double control_value,const ENUM_COMPARER_TYPE compare) const;

//--- Возвращает количество знаков после запятой контролируемого свойства
   int               DigitsControlledValue(const uint index)               const;

public:

Методы возврата идентификаторов магика и групп используют одноимённые методы родительского объекта CBaseObj, от которого мы теперь унаследовали объект базового абстрактного отложенного запроса.

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

//+------------------------------------------------------------------+
//| Методы упрощённого доступа к свойствам объекта-запроса           |
//+------------------------------------------------------------------+
//--- Возвращает (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) тикет позиции, (10) тип отложенного запроса
   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);
                          }
   void                 SetCurrentAttempt(const uchar number)                    { this.SetProperty(PEND_REQ_PROP_CURRENT_ATTEMPT,number);               }
   void                 SetTotalAttempts(const uchar number)                     { this.SetProperty(PEND_REQ_PROP_TOTAL,number);                         }
   void                 SetID(const uchar id)                                    { this.SetProperty(PEND_REQ_PROP_ID,id);                                }
   void                 SetOrder(const ulong ticket)                             { this.SetProperty(PEND_REQ_PROP_MQL_REQ_ORDER,ticket);                 }
   void                 SetPosition(const ulong ticket)                          { this.SetProperty(PEND_REQ_PROP_MQL_REQ_POSITION,ticket);              }
   void                 SetTypeRequest(const ENUM_PEND_REQ_TYPE type)            { this.SetProperty(PEND_REQ_PROP_TYPE,type);                            }
   
//--- Устанавливает фактический (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);         }

//--- Устанавливает в данные критерия срабатывания запроса по его индексу контролируемое свойство, метод сравнения, фактическое и в объекте значения свойств
//--- аккаунта, символа или торгового события (зависит от значения source) для активации отложенного запроса
   void                 SetNewActivationProperties(const ENUM_PEND_REQ_ACTIVATION_SOURCE source,
                                                   const int property,
                                                   const double control_value,
                                                   const ENUM_COMPARER_TYPE comparer_type,
                                                   const double actual_value);
   
//--- Устанавливает (1) контролируемое свойство, (2) тип сравнения, (3) значение величины в объекте,
//--- (4) фактической величины контролируемого свойства для активации отложенного запроса
   bool                 SetActivationProperty(const uint index,const ENUM_PEND_REQ_ACTIVATION_SOURCE source,const int property);
   bool                 SetActivationComparerType(const uint index,const ENUM_COMPARER_TYPE comparer_type);
   bool                 SetActivationControlValue(const uint index,const double value);
   bool                 SetActivationActualValue(const uint index,const double value);
   
//--- Возвращает (1) источник активации отложенного запроса, (2) контролируемое свойство, (3) тип сравнения,
//---  (4) значение величины в объекте,(5) фактической величины контролируемого свойства для активации отложенного запроса
   ENUM_PEND_REQ_ACTIVATION_SOURCE GetActivationSource(const uint index)         const;
   int                  GetActivationProperty(const uint index)                  const;
   ENUM_COMPARER_TYPE   GetActivationComparerType(const uint index)              const;
   double               GetActivationControlValue(const uint index)              const;
   double               GetActivationActualValue(const uint index)               const;
   
//--- Возвращает флаг успешности проверки всех контролируемых свойств объекта и соответствующих фактических свойств
   bool                 IsAllComparisonCompleted(void)  const;

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

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

//+------------------------------------------------------------------+
//| Описания свойств объекта-запроса                                 |
//+------------------------------------------------------------------+
//--- Возвращает описание (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);
   
//--- Возвращает описание (1) контролируемого свойства, (2), типа сравнения, (3) значения контролируемой величины свойства в объекте,
//--- (4) фактического значения контролируемого свойства для активации отложенного запроса, (5) общее количество условий активации
   string               GetActivationPropertyDescription(const uint index)       const;
   string               GetActivationComparerTypeDescription(const uint index)   const;
   string               GetActivationControlValueDescription(const uint index)   const;
   string               GetActivationActualValueDescription(const uint index)    const;
   uint                 GetActivationCriterionTotal(void)                        const { return ::ArrayRange(this.m_activated_control,0); }
   
//--- Возвращает наименования параметров объекта-отложенного запроса
   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) краткое сообщение о запросе, (4) краткое наименование запроса (3 и 4 - реализация в потомках класса)
   void                 Print(const bool full_prop=false);
   void                 PrintActivations(void);
   virtual void         PrintShort(void){;}
   virtual string       Header(void){return NULL;}
  };
//+------------------------------------------------------------------+

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

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

//+------------------------------------------------------------------+
//| Конструктор                                                      |
//+------------------------------------------------------------------+
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));
   ::ArrayResize(this.m_activated_control,0,10);
  }
//+------------------------------------------------------------------+

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

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

//+------------------------------------------------------------------+
//| Выводит в журнал свойства отложенного запроса                    |
//+------------------------------------------------------------------+
void CPendRequest::Print(const bool full_prop=false)
  {
   int header_code=
     (
      this.GetProperty(PEND_REQ_PROP_STATUS)==PEND_REQ_STATUS_OPEN   ? MSG_LIB_TEXT_PEND_REQUEST_STATUS_OPEN   :
      this.GetProperty(PEND_REQ_PROP_STATUS)==PEND_REQ_STATUS_CLOSE  ? MSG_LIB_TEXT_PEND_REQUEST_STATUS_CLOSE  :
      this.GetProperty(PEND_REQ_PROP_STATUS)==PEND_REQ_STATUS_SLTP   ? MSG_LIB_TEXT_PEND_REQUEST_STATUS_SLTP   :
      this.GetProperty(PEND_REQ_PROP_STATUS)==PEND_REQ_STATUS_PLACE  ? MSG_LIB_TEXT_PEND_REQUEST_STATUS_PLACE  :
      this.GetProperty(PEND_REQ_PROP_STATUS)==PEND_REQ_STATUS_REMOVE ? MSG_LIB_TEXT_PEND_REQUEST_STATUS_REMOVE :
      this.GetProperty(PEND_REQ_PROP_STATUS)==PEND_REQ_STATUS_MODIFY ? MSG_LIB_TEXT_PEND_REQUEST_STATUS_MODIFY :
      WRONG_VALUE
     );
   ::Print("============= \"",CMessage::Text(header_code),"\" =============");
   int beg=0, end=PEND_REQ_PROP_INTEGER_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_PEND_REQ_PROP_INTEGER prop=(ENUM_PEND_REQ_PROP_INTEGER)i;
      if(!full_prop && !this.SupportProperty(prop)) continue;
      ::Print(this.GetPropertyDescription(prop));
     }
   ::Print("------");
   beg=end; end+=PEND_REQ_PROP_DOUBLE_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_PEND_REQ_PROP_DOUBLE prop=(ENUM_PEND_REQ_PROP_DOUBLE)i;
      if(!full_prop && !this.SupportProperty(prop)) continue;
      ::Print(this.GetPropertyDescription(prop));
     }
   ::Print("------");
   beg=end; end+=PEND_REQ_PROP_STRING_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_PEND_REQ_PROP_STRING prop=(ENUM_PEND_REQ_PROP_STRING)i;
      if(!full_prop && !this.SupportProperty(prop)) continue;
      ::Print(this.GetPropertyDescription(prop));
     }
   this.PrintActivations();
   ::Print("================== ",CMessage::Text(MSG_LIB_PARAMS_LIST_END),": \"",CMessage::Text(header_code),"\" ==================\n");
  }
//+------------------------------------------------------------------+

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

//+------------------------------------------------------------------+
//| Выводит в журнал параметры активации запроса                     |
//+------------------------------------------------------------------+
void CPendRequest::PrintActivations(void)
  {
   //--- Получаем размер первого измерения массива данных условий активации,
   //--- и если размер больше нуля - выводим в журнал все записанные в массиве данные
   int range=::ArrayRange(this.m_activated_control,0);
   if(range>0)
     {
      ::Print("--- ",CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_ACTIVATION_TERMS)," ---");
      for(int i=0;i<range;i++)
        {
         ENUM_PEND_REQ_ACTIVATION_SOURCE source=(ENUM_PEND_REQ_ACTIVATION_SOURCE)this.m_activated_control[i][0];
         string type=
           (
            source==PEND_REQ_ACTIVATION_SOURCE_ACCOUNT   ?  CMessage::Text(MSG_ACC_ACCOUNT)     :
            source==PEND_REQ_ACTIVATION_SOURCE_SYMBOL    ?  CMessage::Text(MSG_LIB_PROP_SYMBOL) :
            source==PEND_REQ_ACTIVATION_SOURCE_EVENT     ?  CMessage::Text(MSG_EVN_EVENT)       :
            ""
           );
         ::Print(" - ",CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_CRITERION)," #",string(i+1),". ",type,": ",this.GetActivationPropertyDescription(i));
        }
     }
   ::Print("");
  }
//+------------------------------------------------------------------+

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

//+------------------------------------------------------------------+
//| Устанавливает контролируемое свойство, значения                  |
//| и метод сравнения для активации отложенного запроса              |
//+------------------------------------------------------------------+
void CPendRequest::SetNewActivationProperties(const ENUM_PEND_REQ_ACTIVATION_SOURCE source,
                                              const int property,
                                              const double control_value,
                                              const ENUM_COMPARER_TYPE comparer_type,
                                              const double actual_value)
  {
   int range=::ArrayRange(this.m_activated_control,0);
   if(::ArrayResize(this.m_activated_control,range+1,10)==WRONG_VALUE)
     {
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_FAILED_ADD_PARAMS));
      return;
     }
   this.m_activated_control[range][0]=source;
   this.m_activated_control[range][1]=property;
   this.m_activated_control[range][2]=comparer_type;
   this.m_activated_control[range][3]=control_value;
   this.m_activated_control[range][4]=actual_value;
  }
//+---------------------------------------------------------------------+

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

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

//+---------------------------------------------------------------------+
//| Устанавливает контролируемое свойство для активации запроса         |
//+---------------------------------------------------------------------+
bool CPendRequest::SetActivationProperty(const uint index,const ENUM_PEND_REQ_ACTIVATION_SOURCE source,const int property)
  {
   int range=::ArrayRange(this.m_activated_control,0);
   if((int)index>range-1 || range==0)
     {
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(4002));
      return false;
     }
   this.m_activated_control[index][0]=source;
   this.m_activated_control[index][1]=property;
   return true;
  }
//+------------------------------------------------------------------+
//| Устанавливает тип сравнения свойства объекта                     |
//| с фактическим для активации отложенного запроса                  |
//+------------------------------------------------------------------+
bool CPendRequest::SetActivationComparerType(const uint index,const ENUM_COMPARER_TYPE comparer_type)
  {
   int range=::ArrayRange(this.m_activated_control,0);
   if((int)index>range-1 || range==0)
     {
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(4002));
      return false;
     }
   this.m_activated_control[index][2]=comparer_type;
   return true;
  }
//+------------------------------------------------------------------+
//| Устанавливает значение контролируемой величины                   |
//| свойства для активации отложенного запроса                       |
//+------------------------------------------------------------------+
bool CPendRequest::SetActivationControlValue(const uint index,const double value)
  {
   int range=::ArrayRange(this.m_activated_control,0);
   if((int)index>range-1 || range==0)
     {
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(4002));
      return false;
     }
   this.m_activated_control[index][3]=value;
   return true;
  }
//+------------------------------------------------------------------+
//| Устанавливает значение фактической величины                      |
//| контролируемого свойства в объекте-запросе                       |
//+------------------------------------------------------------------+
bool CPendRequest::SetActivationActualValue(const uint index,const double value)
  {
   int range=::ArrayRange(this.m_activated_control,0);
   if((int)index>range-1 || range==0)
     {
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(4002));
      return false;
     }
   this.m_activated_control[index][4]=value;
   return true;
  }
//+------------------------------------------------------------------+

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

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

Методы, возвращающие параметры условий активации:

//+------------------------------------------------------------------+
//| Возвращает источник активации отложенного запроса                |
//+------------------------------------------------------------------+
ENUM_PEND_REQ_ACTIVATION_SOURCE CPendRequest::GetActivationSource(const uint index) const
  {
   int range=::ArrayRange(this.m_activated_control,0);
   if((int)index>range-1 || range==0)
     {
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(4002));
      return WRONG_VALUE;
     }
   return (ENUM_PEND_REQ_ACTIVATION_SOURCE)this.m_activated_control[index][0];
  }
//+------------------------------------------------------------------+
//| Возвращает контролируемое свойство для активации запроса         |
//+------------------------------------------------------------------+
int CPendRequest::GetActivationProperty(const uint index) const
  {
   int range=::ArrayRange(this.m_activated_control,0);
   if((int)index>range-1 || range==0)
     {
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(4002));
      return WRONG_VALUE;
     }
   return (int)this.m_activated_control[index][1];
  }
//+------------------------------------------------------------------+
//| Возвращает тип сравнения свойства объекта                        |
//| с фактическим для активации отложенного запроса                  |
//+------------------------------------------------------------------+
ENUM_COMPARER_TYPE CPendRequest::GetActivationComparerType(const uint index) const
  {
   int range=::ArrayRange(this.m_activated_control,0);
   if((int)index>range-1 || range==0)
     {
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(4002));
      return WRONG_VALUE;
     }
   return (ENUM_COMPARER_TYPE)this.m_activated_control[index][2];
  }
//+------------------------------------------------------------------+
//| Возвращает значение контролируемой величины                      |
//| свойства для активации отложенного запроса                       |
//+------------------------------------------------------------------+
double CPendRequest::GetActivationControlValue(const uint index) const
  {
   int range=::ArrayRange(this.m_activated_control,0);
   if((int)index>range-1 || range==0)
     {
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(4002));
      return EMPTY_VALUE;
     }
   return this.m_activated_control[index][3];
  }
//+------------------------------------------------------------------+
//| Возвращает значение фактической величины                         |
//| контролируемого свойства в объекте-запросе                       |
//+------------------------------------------------------------------+
double CPendRequest::GetActivationActualValue(const uint index) const
  {
   int range=::ArrayRange(this.m_activated_control,0);
   if((int)index>range-1 || range==0)
     {
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(4002));
      return EMPTY_VALUE;
     }
   return this.m_activated_control[index][4];
  }
//+------------------------------------------------------------------+

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

Метод сравнения двух величин по заданному типу сравнения:

//+------------------------------------------------------------------+
//| Сравнивает два значения источника данных по типу сравнения       |
//+------------------------------------------------------------------+
bool CPendRequest::IsCompared(const double actual_value,const double control_value,const ENUM_COMPARER_TYPE compare) const
  {
   switch((int)compare)
     {
      case EQUAL           :  return(actual_value<control_value || actual_value>control_value ? false : true);
      case NO_EQUAL        :  return(actual_value<control_value || actual_value>control_value ? true : false);
      case MORE            :  return(actual_value>control_value ? true  : false);
      case LESS            :  return(actual_value<control_value ? true  : false);
      case EQUAL_OR_MORE   :  return(actual_value<control_value ? false : true);
      case EQUAL_OR_LESS   :  return(actual_value>control_value ? false : true);
      default              :  break;
     }
   return false;
  }
//+------------------------------------------------------------------+

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

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

//+------------------------------------------------------------------+
//| Возвращает флаг успешности проверки контролируемого свойства     |
//| объекта и соответствующего ему фактического свойства             |
//+------------------------------------------------------------------+
bool CPendRequest::IsComparisonCompleted(const uint index) const
  {
//--- Если контролируемое свойство не задано - возвращаем false
   if(this.m_activated_control[index][1]==MSG_LIB_PROP_NOT_SET)
      return false;
//--- Возвращаем результат заданного сравнения величины контролируемого свойства с его фактическим значением
   ENUM_COMPARER_TYPE comparer=(ENUM_COMPARER_TYPE)this.m_activated_control[index][2];
   return this.IsCompared(this.m_activated_control[index][4],this.m_activated_control[index][3],comparer);
  }
//+------------------------------------------------------------------+

Метод возвращает флаг срабатывания одного из условий активации отложенного запроса. Во входном параметре метода передаётся индекс проверяемого условия в массиве данных условий активации. Сравнение производится рассмотренным выше методом IsCompared() и возвращается результат сравнения.

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

//+------------------------------------------------------------------+
//| Возвращает флаг успешности проверки всех контролируемых свойств  |
//| объекта и соответствующих им фактических свойств                 |
//+------------------------------------------------------------------+
bool CPendRequest::IsAllComparisonCompleted(void) const
  {
   bool res=true;
   int range=::ArrayRange(this.m_activated_control,0);
   if(range==0)
      return false;
   for(int i=0;i<range;i++)
      res &=this.IsComparisonCompleted(i);
   return res;
  }
//+-------------------------------------------------------------------+

Это универсальный метод, позволяющий проверить любой объект-отложенный запрос на наступление момента его активации.
Здесь в цикле по первому измерению массива данных условий активации при помощи метода IsComparisonCompleted() в результат проверки (переменную res) добавляется флаг успешности проверки соответствующего индексу цикла контролируемого свойства. По завершению цикла возвращается результат проверки всех условий. Если хоть одно из условий не выполнено или массив данных имеет нулевой размер в первом измерении, то результатом будет false.

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

//+-------------------------------------------------------------------+
//|Возвращает количество знаков после запятой контролируемого свойства|
//+-------------------------------------------------------------------+
int CPendRequest::DigitsControlledValue(const uint index) const
  {
   int dg=0;
   //--- В зависимости от источника условия активации проверяем условия активации
   //--- и записываем в результат требуемое количество знаков после запятой
   switch((int)this.m_activated_control[index][0])
     {
      //--- Аккаунт. Если условие активации - целочисленное значение, то 0,
      //--- если вещественное - то количество знаков после запятой в текущей валюте
      case PEND_REQ_ACTIVATION_SOURCE_ACCOUNT      :
         dg=(this.m_activated_control[index][1]<PEND_REQ_ACTIVATE_BY_ACCOUNT_BALANCE  ?  0 : this.m_digits_currency);
        break;
      //--- Символ. В зависимости от условия, записываем либо количество знаков после запятой в котировке символа,
      //--- либо количество знаков после запятой в текущей валюте, либо количество знаков после запятой в значении лота, либо ноль
      case PEND_REQ_ACTIVATION_SOURCE_SYMBOL       :
         //--- digits
         if(
           (this.m_activated_control[index][1]<PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_DEALS &&
            this.m_activated_control[index][1]>PEND_REQ_ACTIVATE_BY_SYMBOL_EMPTY)                     ||
            this.m_activated_control[index][1]==PEND_REQ_ACTIVATE_BY_SYMBOL_OPTION_STRIKE             ||
            this.m_activated_control[index][1]>PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_SELL_ORDERS_VOLUME ||
           (this.m_activated_control[index][1]>PEND_REQ_ACTIVATE_BY_SYMBOL_TRADE_FREEZE_LEVEL &&
            this.m_activated_control[index][1]<PEND_REQ_ACTIVATE_BY_SYMBOL_VOLUME_REAL)
           ) dg=this.m_digits;
         //--- не digits
         else if(
            this.m_activated_control[index][1]>PEND_REQ_ACTIVATE_BY_SYMBOL_LASTLOW)
           {
            //--- digits currency
            if(
               (this.m_activated_control[index][1]>PEND_REQ_ACTIVATE_BY_SYMBOL_OPTION_STRIKE    && 
                this.m_activated_control[index][1]<PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_VOLUME)  ||
               this.m_activated_control[index][1]==PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_TURNOVER
              ) dg=this.m_digits_currency;
            //--- digits lots
            else
               dg=(this.m_digits_lot==0 ? 1 : this.m_digits_lot);
           }
         //--- 0
         else
            dg=0;
        break;
      //--- По умолчанию ноль
      default:
        break;
     }
   return dg;
  }
//+------------------------------------------------------------------+

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

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

//+------------------------------------------------------------------+
//| Возвращает описание контролируемого свойства по индексу          |
//+------------------------------------------------------------------+
string CPendRequest::GetActivationPropertyDescription(const uint index) const
  {
   //--- Получаем источник активации и в зависимости от источника создаём текст описания типа сравнения с контролируемым значением
   ENUM_PEND_REQ_ACTIVATION_SOURCE source=(ENUM_PEND_REQ_ACTIVATION_SOURCE)this.m_activated_control[index][0];
   string value=
     (
      source==PEND_REQ_ACTIVATION_SOURCE_EVENT     ?  "" : 
      (
       this.m_activated_control[index][1]==MSG_LIB_PROP_NOT_SET ? ""  :
       this.GetActivationComparerTypeDescription(index)+this.GetActivationControlValueDescription(index)
      )
     );
   //--- Возвращаем описание условия активации + тип сравнения + контролируемое значение
   return
     (
      source==PEND_REQ_ACTIVATION_SOURCE_ACCOUNT   ?  CMessage::Text((ENUM_PEND_REQ_ACTIVATE_BY_ACCOUNT_PROP)this.m_activated_control[index][1])+value   :
      source==PEND_REQ_ACTIVATION_SOURCE_SYMBOL    ?  CMessage::Text((ENUM_PEND_REQ_ACTIVATE_BY_SYMBOL_PROP)this.m_activated_control[index][1])+value    :
      source==PEND_REQ_ACTIVATION_SOURCE_EVENT     ?  CMessage::Text((ENUM_PEND_REQ_ACTIVATE_BY_EVENT)this.m_activated_control[index][1])+value          :
      ""
     );
  }
//+------------------------------------------------------------------+

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

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

//+------------------------------------------------------------------+
//| Возвращает описание типа сравнения                               |
//+------------------------------------------------------------------+
string CPendRequest::GetActivationComparerTypeDescription(const uint index) const
  {
   return ComparisonTypeDescription((ENUM_COMPARER_TYPE)this.m_activated_control[index][2]);
  }
//+------------------------------------------------------------------+

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

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

//+------------------------------------------------------------------+
//| Возвращает описание значения контролируемого свойства в объекте  |
//+------------------------------------------------------------------+
string CPendRequest::GetActivationControlValueDescription(const uint index) const
  {
   return
     (
      this.m_activated_control[index][3]!=EMPTY_VALUE ? 
      (
       this.m_activated_control[index][0]==PEND_REQ_ACTIVATION_SOURCE_SYMBOL &&
       this.m_activated_control[index][1]==PEND_REQ_ACTIVATE_BY_SYMBOL_TIME   ? 
       ::TimeToString((ulong)this.m_activated_control[index][3])              :
       ::DoubleToString(this.m_activated_control[index][3],this.DigitsControlledValue(index))
      )  :  ""
     );
  }
//+------------------------------------------------------------------+

В метод передаётся индекс условия.
Проверяется значение контролируемого свойства, записанное в массив по указанному индексу, и если оно не равно "пустому значению" ( EMPTY_VALUE), то проверяется условие и его тип. Если в итоге проверяется время символа, то возвращается текстовое описание времени,
иначе — текстовое описание целочисленного или вещественного значения с корректным количеством знаков после запятой.

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

//+------------------------------------------------------------------+
//|Возвращает описание фактического значения контролируемого свойства|
//+------------------------------------------------------------------+
string CPendRequest::GetActivationActualValueDescription(const uint index) const
  {
   return
     (
      this.m_activated_control[index][4]!=EMPTY_VALUE ? 
      (
       this.m_activated_control[index][0]==PEND_REQ_ACTIVATION_SOURCE_SYMBOL &&
       this.m_activated_control[index][1]==PEND_REQ_ACTIVATE_BY_SYMBOL_TIME   ? 
       ::TimeToString((ulong)this.m_activated_control[index][4])              :
       ::DoubleToString(this.m_activated_control[index][4],this.DigitsControlledValue(index))
      )  :  ""
     );
  }
//+------------------------------------------------------------------+

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

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

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

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

В файлы объектов-наследников базового объекта абстрактного отложенного запроса PendReqOpen.mqh, PendReqClose.mqh, PendReqSLTP.mqh, PendReqPlace.mqh, PendReqRemove.mqh и PendReqModify.mqh внесём такие изменения (на примере класса CPendReqOpen):
//+------------------------------------------------------------------+
//| Возвращает истину, если ордер поддерживает переданное            |
//| целочисленное свойство, возвращает ложь в противном случае       |
//+------------------------------------------------------------------+
bool CPendReqOpen::SupportProperty(ENUM_PEND_REQ_PROP_INTEGER property)
  {
   if(
      (this.GetProperty(PEND_REQ_PROP_TYPE)==PEND_REQ_TYPE_REQUEST   &&
       (property==PEND_REQ_PROP_RETCODE                              ||
        property==PEND_REQ_PROP_TIME_ACTIVATE                        ||
        property==PEND_REQ_PROP_WAITING                              ||
        property==PEND_REQ_PROP_CURRENT_ATTEMPT                        
       )                                                               
      ||
      property==PEND_REQ_PROP_MQL_REQ_ORDER                          ||
      property==PEND_REQ_PROP_MQL_REQ_POSITION                       ||
      property==PEND_REQ_PROP_MQL_REQ_POSITION_BY                    ||
      property==PEND_REQ_PROP_MQL_REQ_EXPIRATION                     ||
      property==PEND_REQ_PROP_MQL_REQ_TYPE_TIME
     ) return false;
   return true;
  }
//+------------------------------------------------------------------+

Здесь проверяется что это объект, созданный по запросу, и если да, то исключаются свойства, озвученные выше.

//+------------------------------------------------------------------+
//| Выводит в журнал краткое сообщение с данными запроса             |
//+------------------------------------------------------------------+
void CPendReqOpen::PrintShort(void)
  {
   string params=this.GetProperty(PEND_REQ_PROP_MQL_REQ_SYMBOL)+" "+::DoubleToString(this.GetProperty(PEND_REQ_PROP_MQL_REQ_VOLUME),this.m_digits_lot)+" "+
                 OrderTypeDescription((ENUM_ORDER_TYPE)this.GetProperty(PEND_REQ_PROP_MQL_REQ_TYPE));
   string price=CMessage::Text(MSG_LIB_TEXT_REQUEST_PRICE)+" "+::DoubleToString(this.GetProperty(PEND_REQ_PROP_MQL_REQ_PRICE),this.m_digits);
   string sl=this.GetProperty(PEND_REQ_PROP_MQL_REQ_SL)>0 ? ", "+CMessage::Text(MSG_LIB_TEXT_REQUEST_SL)+" "+::DoubleToString(this.GetProperty(PEND_REQ_PROP_MQL_REQ_SL),this.m_digits) : "";
   string tp=this.GetProperty(PEND_REQ_PROP_MQL_REQ_TP)>0 ? ", "+CMessage::Text(MSG_LIB_TEXT_REQUEST_TP)+" "+::DoubleToString(this.GetProperty(PEND_REQ_PROP_MQL_REQ_TP),this.m_digits) : "";
   string time=this.IDDescription()+", "+CMessage::Text(MSG_LIB_TEXT_CREATED)+" "+TimeMSCtoString(this.GetProperty(PEND_REQ_PROP_TIME_CREATE));
   string attempts=CMessage::Text(MSG_LIB_TEXT_ATTEMPTS)+" "+(string)this.GetProperty(PEND_REQ_PROP_TOTAL);
   string wait=CMessage::Text(MSG_LIB_TEXT_WAIT)+" "+::TimeToString(this.GetProperty(PEND_REQ_PROP_WAITING)/1000,TIME_SECONDS);
   string end=CMessage::Text(MSG_LIB_TEXT_END)+" "+
              TimeMSCtoString(this.GetProperty(PEND_REQ_PROP_TIME_CREATE)+this.GetProperty(PEND_REQ_PROP_WAITING)*this.GetProperty(PEND_REQ_PROP_TOTAL));
   //---
   string message=CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_STATUS_OPEN)+": "+
   "\n- "+params+", "+price+sl+tp+
   "\n- "+time+", "+attempts+", "+wait+", "+end;
   ::Print(message);
   this.PrintActivations();
  }
//+------------------------------------------------------------------+

Здесь мы убрали после строки "+end" добавление кода переноса на новую строку (+"\n"), и после строки ::Print(message); добавили вызов метода, выводящего список условий активации. Если массив с условиями имеет нулевой размер (в объектах, созданных по коду ошибки), то метод PrintActivations() ничего не распечатывает кроме кода переноса на новую строку ("\n"). В противном случае метод выводит полный список всех условий, записанных в массиве данных.
В некоторые эти классы были внесены незначительные "косметические" исправления, касающиеся только вывода в журнал, поэтому здесь на них останавливаться не будем — всё есть в прилагаемых к статье файлах, и можно ознакомиться с изменениями самостоятельно.

Теперь займёмся торговыми классами.
В основном торговом классе CTrading перенесём из приватной секции в защищённую три переменные-члены класса и метод GetFreeID():

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

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

//+------------------------------------------------------------------+
//| Торговый класс                                                   |
//+------------------------------------------------------------------+
class CTrading : public CBaseObj
  {
protected:
   CAccount            *m_account;                       // Указатель на объект-текущий аккаунт
   CSymbolsCollection  *m_symbols;                       // Указатель на список коллекции символов
   CMarketCollection   *m_market;                        // Указатель на список коллекции рыночных ордеров и позиций
   CHistoryCollection  *m_history;                       // Указатель на список коллекции исторических ордеров и сделок
   CEventsCollection   *m_events;                        // Указатель на список коллекции событий
   CArrayObj            m_list_request;                  // Список отложенных запросов
   uchar                m_total_try;                     // Количество торговых попыток
   MqlTradeRequest      m_request;                       // Структура торгового запроса
   ENUM_TRADE_REQUEST_ERR_FLAGS m_error_reason_flags;    // Флаги причин ошибок в торговом методе
//--- Ищет первый свободный идентификатор отложенного запроса
   int                  GetFreeID(void);
//--- Возвращает флаг наличия рыночного ордера/позиции с идентификатором отложенного запроса
   bool                 IsPresentOrderByID(const uchar id);
private:

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

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

//--- Создаёт отложенный запрос
   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);

//--- Возвращает (1) указатель на объект-запрос по его идентификатору в списке,
//--- (2) уровень логирования торгового объекта символа
   CPendRequest        *GetPendRequestByID(const uchar id);
   ENUM_LOG_LEVEL       GetTradeObjLogLevel(const string symbol_name);

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

За пределами тела класса напишем реализацию этих методов.

Реализация метода, возвращающего уровень логирования торгового объекта символа:

//+------------------------------------------------------------------+
//| Возвращает уровень логирования торгового объекта символа         |
//+------------------------------------------------------------------+
ENUM_LOG_LEVEL CTrading::GetTradeObjLogLevel(const string symbol_name)
  {
   CTradeObj *trade_obj=this.GetTradeObjBySymbol(symbol_name,DFUN);
   return(trade_obj!=NULL ? trade_obj.GetLogLevel() : LOG_LEVEL_NO_MSG);
  }
//+------------------------------------------------------------------+

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

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

//+------------------------------------------------------------------+
//| Возвращает указатель на объект-запрос по идентификатору в списке |
//+------------------------------------------------------------------+
CPendRequest* CTrading::GetPendRequestByID(const uchar id)
  {
   int index=this.GetIndexPendingRequestByID(id);
   if(index==WRONG_VALUE)
      return NULL;
   return this.m_list_request.At(index);
  }
//+------------------------------------------------------------------+

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

Реализация метода, возвращающего флаг наличия рыночного ордера/позиции с идентификатором отложенного запроса:

//+------------------------------------------------------------------+
//| Возвращает флаг наличия рыночного ордера/позиции                 |
//| с идентификатором отложенного запроса                            |
//+------------------------------------------------------------------+
bool CTrading::IsPresentOrderByID(const uchar id)
  {
   CArrayObj *list=this.m_market.GetList(ORDER_PROP_PEND_REQ_ID,id,EQUAL);
   return(list==NULL ? false : list.Total()!=0);
  }
//+------------------------------------------------------------------+

В метод передаётся идентификатор отложенного запроса. Далее получаем список раночных ордеров/позиций, отфильтрованный по идентификатору отложенного запроса и его значению. Если список не получен или пуст (нет ордеров/позиций с искомым идентификатором), то возвращаем false, иначе — возвращаем true.

Дополним ещё одной проверкой метод, возвращающий номер свободного идентификатора:

//+------------------------------------------------------------------+
//| Ищет первый свободный идентификатор отложенного запроса          |
//+------------------------------------------------------------------+
int CTrading::GetFreeID(void)
  {
   int id=WRONG_VALUE;
   CPendRequest *element=new CPendRequest();
   if(element==NULL)
      return 0;
   for(int i=1;i<256;i++)
     {
      element.SetID((uchar)i);
      this.m_list_request.Sort(SORT_BY_PEND_REQ_ID);
      if(this.m_list_request.Search(element)==WRONG_VALUE)
        {
         if(this.IsPresentOrderByID((uchar)i))
            continue;
         id=i;
         break;
        }
     }
   delete element;
   return id;
  }
//+------------------------------------------------------------------+

Для чего нужна ещё одна проверка на то, что присутствует в рынке ордер/позиция с таким идентификатором? Дело в том, что если у нас есть активированный отложенный запрос, и по нему, например, открыта позиция, то этот запрос удаляется из списков отложенных запросов, и его идентификатор становится доступным для использования при создании новых отложенных запросов.
При создании нового отложенного запроса будет создан отложенный запрос с таким же идентификатором, по которому в данный момент есть открытая позиция. При наступлении условий активации этого нового отложенного запроса, проверяется наличие позиции с таким же идентификатором (а она есть — открыта по прошлому такому же идентификатору), и новый отложенный запрос просто удаляется — считается, что раз позиция с таким ID существует, значит запрос отработан. Т.е., запрос не пошлёт торговый приказ на сервер, а просто будет удалён.

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

На этом доработка основного торгового класса завершена.

Теперь доработаем класс управления торговлей CTradingControl — класс-наследник основного торгового класса CTrading.

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

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

Внесём в тело класса все необходимые дополнения, а далее разберём их:

//+------------------------------------------------------------------+
//| Класс управления отложенными торговыми запросами                 |
//+------------------------------------------------------------------+
class CTradingControl : public CTrading
  {
private:
//--- Устнанавливает в объект-отложенный запрос фактические данные ордера/позиции
   void                 SetOrderActualProperties(CPendRequest *req_obj,const COrder *order);
//--- Обработчик отложенных запросов, созданных (1) по коду ошибки, (2) по запросу
   void                 OnPReqByErrCodeHandler(CPendRequest *req_obj,const int index);
   void                 OnPReqByRequestHandler(CPendRequest *req_obj,const int index);
//--- Проверка актуальности отложенного запроса (сработал или нет)
   bool                 CheckPReqRelevance(CPendRequest *req_obj,const MqlTradeRequest &request,const int index);
//--- Обновляет актуальные значения контролируемых свойств в объектах-отложенных запросах,
   void                 RefreshControlActualDatas(CPendRequest *req_obj,const CSymbol *symbol);
//--- Возвращает для контроля активации актуальные данные (1) аккаунта, (2) символа, (3) событий
   double               GetActualDataAccount(const int property);
   double               GetActualDataSymbol(const int property,const CSymbol *symbol);
   double               GetActualDataEvent(const int property);
   
public:
//--- Возвращает себя
   CTradingControl     *GetObject(void)            { return &this;   }
//--- Таймер
   virtual void         OnTimer(void);
//--- Конструктор
                        CTradingControl();
//--- (1) Создаёт отложенный запрос (1) на открытие позиции, (2) на установку отложенного ордера
   template<typename SL,typename TP> 
   int                  OpenPositionPending(const ENUM_POSITION_TYPE type,
                                           const double volume,
                                           const string symbol,
                                           const ulong magic=ULONG_MAX,
                                           const SL sl=0,
                                           const TP tp=0,
                                           const uchar group_id1=0,
                                           const uchar group_id2=0,
                                           const string comment=NULL,
                                           const ulong deviation=ULONG_MAX,
                                           const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE);
   template<typename PS,typename PL,typename SL,typename TP>
   int                  PlaceOrderPending( const ENUM_ORDER_TYPE order_type,
                                           const double volume,
                                           const string symbol,
                                           const PS price_stop,
                                           const PL price_limit=0,
                                           const SL sl=0,
                                           const TP tp=0,
                                           const ulong magic=ULONG_MAX,
                                           const string comment=NULL,
                                           const datetime expiration=0,
                                           const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE,
                                           const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE);
//--- Устанавливает критерии активации отложенного запроса
   bool                 SetNewActivationProperties(const uchar id,
                                                   const ENUM_PEND_REQ_ACTIVATION_SOURCE source,
                                                   const int property,
                                                   const double control_value,
                                                   const ENUM_COMPARER_TYPE comparer_type,
                                                   const double actual_value);
  };
//+------------------------------------------------------------------+

Так как теперь у нас появилась необходимость устанавливать в объект-отложенный запрос данные контролируемых параметров, а ранее для отслеживания срабатывания отложенного запроса мы также вписывали в объект-запрос актуальные данные ордера, которому соответствует данный запрос, то во избежание путаницы, переименуем метод установки актуальных данных ордера в объект-запрос из SetActualProperties() в SetOrderActualProperties().

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

//+------------------------------------------------------------------+
//| Создаёт отложенный запрос на открытие позиции                    |
//+------------------------------------------------------------------+
template<typename SL,typename TP> 
int CTradingControl::OpenPositionPending(const ENUM_POSITION_TYPE type,
                                         const double volume,
                                         const string symbol,
                                         const ulong magic=ULONG_MAX,
                                         const SL sl=0,
                                         const TP tp=0,
                                         const uchar group_id1=0,
                                         const uchar group_id2=0,
                                         const string comment=NULL,
                                         const ulong deviation=ULONG_MAX,
                                         const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE)
  {
//--- Если установлен флаг глобального запрета торговли - выходим
   if(this.IsTradingDisable())
     {
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_TRADING_DISABLE));
      return false;
     }
//--- Устанавливаем результат торгового запроса как true и флаг ошибки как "нет ошибок"
   bool res=true;
   this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_NO_ERROR;
   ENUM_ORDER_TYPE order_type=(ENUM_ORDER_TYPE)type;
   ENUM_ACTION_TYPE action=(ENUM_ACTION_TYPE)order_type;
//--- Получаем объект-символ по имени символа.
   CSymbol *symbol_obj=this.m_symbols.GetSymbolObjByName(symbol);
//--- Если получить не удалось - записываем флаг "внутренняя ошибка", выводим сообщение в журнал и возвращаем false
   if(symbol_obj==NULL)
     {
      this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_INTERNAL_ERR;
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_SYM_OBJ));
      return false;
     }
//--- получаем торговый объект из объекта-символа
   CTradeObj *trade_obj=symbol_obj.GetTradeObj();
//--- Если получить не удалось - записываем флаг "внутренняя ошибка", выводим сообщение в журнал и возвращаем false
   if(trade_obj==NULL)
     {
      this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_INTERNAL_ERR;
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_TRADE_OBJ));
      return false;
     }
//--- Устанавливаем цены
//--- Если установить не удалось - записываем флаг "внутренняя ошибка" устанавливаем код ошибки в структуру возврата,
//--- выводим сообщение в журнал и возвращаем false
   if(!this.SetPrices(order_type,0,sl,tp,0,DFUN,symbol_obj))
     {
      this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_INTERNAL_ERR;
      trade_obj.SetResultRetcode(10021);
      trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode()));
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(10021));   // Отсутствуют котировки для обработки запроса
      return false;
     }
   //--- Ищем наименьший из возможных идентификаторов, и если найти не удалось - возвращаем false
   int id=this.GetFreeID();
   if(id<1)
     {
      //--- Нет свободных идентификаторов для создания отложенного запроса
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_NO_FREE_IDS));
      return false;
     }

//--- Записываем объём, отклонение, комментарий и тип заливки в структуру запроса
   this.m_request.volume=volume;
   this.m_request.deviation=(deviation==ULONG_MAX ? trade_obj.GetDeviation() : deviation);
   this.m_request.comment=(comment==NULL ? trade_obj.GetComment() : comment);
   this.m_request.type_filling=(type_filling>WRONG_VALUE ? type_filling : trade_obj.GetTypeFilling());
//--- Записываем в магик идентификатор объекта-отложенного запроса, идентификаторы групп в значение магика,
//--- и заполняем остальные незаполненные поля структуры торгового запроса
   uint mn=(magic==ULONG_MAX ? (uint)trade_obj.GetMagic() : (uint)magic);
   this.SetPendReqID((uchar)id,mn);
   if(group_id1>0)
      this.SetGroupID1(group_id1,mn);
   if(group_id2>0)
      this.SetGroupID2(group_id2,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;
//--- В результате создания отложенного торгового запроса возвращаем либо его идентификатор, либо -1 при неудаче
   if(this.CreatePendingRequest(PEND_REQ_STATUS_OPEN,(uchar)id,1,ulong(END_TIME-(ulong)::TimeCurrent()),this.m_request,0,symbol_obj,NULL))
      return id;
   return WRONG_VALUE;
  }
//+------------------------------------------------------------------+

Метод представляет собой урезанную версию метода для открытия позиции из статьи 26 (и последующих статей) с созданием отложенного запроса в случае получения ошибки от торгового сервера. Достаточно подробно прокомментирован, и здесь останавливаться на его разборе подробно не будем.
В метод передаются все необходимые данные для открытия позиции, заполняются поля структуры торгового запроса и отправляются в метод создания отложенного запроса.
В итоге, при удачном создании отложенного запроса, возвращается идентификатор вновь созданного отложенного запроса, в противном случае возвращается -1.
В качестве задержки между повторными попытками здесь используется рассчитанное максимально-возможное время ожидания как разница между максимально-возможным временем в терминале и текущим временем. Таким образом время действия отложенного запроса будет максимально-возможным (до 31.12.3000 г.).

Метод, устанавливающий критерии активации отложенного запроса:

//+------------------------------------------------------------------+
//| Устанавливает критерии активации отложенного запроса             |
//+------------------------------------------------------------------+
bool CTradingControl::SetNewActivationProperties(const uchar id,
                                                const ENUM_PEND_REQ_ACTIVATION_SOURCE source,
                                                const int property,
                                                const double control_value,
                                                const ENUM_COMPARER_TYPE comparer_type,
                                                const double actual_value)
  {
   CPendRequest *req_obj=this.GetPendRequestByID(id);
   if(req_obj==NULL)
     {
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_GETTING_FAILED));
      return false;
     }
   req_obj.SetNewActivationProperties(source,property,control_value,comparer_type,actual_value);
   return true;
  }
//+------------------------------------------------------------------+

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

Метод проверки актуальности отложенного запроса:

//+------------------------------------------------------------------+
//| Проверка актуальности отложенного запроса                        |
//+------------------------------------------------------------------+
bool CTradingControl::CheckPReqRelevance(CPendRequest *req_obj,const MqlTradeRequest &request,const int index)
  {
   //--- Если это открытие позиции или установка отложенного ордера
   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)
         return false;
      //--- Если есть такой ордер/позиция - запрос отработан: удаляем его и переходим к следующему (уходим из метода во внешний цикл)
      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(index);
         return false;
        }
     }
   //--- Иначе: полное и частичное закрытие позиции, удаление ордера, модификация параметров ордера и стоп приказов позиции
   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)
            return false;
         //--- Если в рынке нет такой позиции - запрос отработан: удаляем его и переходим к следующему (уходим из метода во внешний цикл)
         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(index);
            return false;
           }
         //--- Иначе, если позиция ещё существует, то это частичное закрытие
         else
           {
            //--- Получаем список всех торговых событий аккаунта
            list=this.m_events.GetList();
            if(list==NULL)

               return false;
            //--- В цикле с конца списка торговых событий аккаунта
            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.SetOrderActualProperties(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(index);
                        break;
                       }
                    }
                 }
              }
            //--- Если в цикле по списку торговых событий был удалён отработанный объект-отложенный запрос - переходим к следующему (уходим из метода во внешний цикл)
            if(::CheckPointer(req_obj)==POINTER_INVALID)
               return false;
           }
        }
      //--- Если это модификация стоп-приказов позиции
      if(req_obj.Action()==TRADE_ACTION_SLTP)
        {
         //--- Получаем список всех торговых событий аккаунта
         list=this.m_events.GetList();
         if(list==NULL)
            return false;
         //--- В цикле с конца списка торговых событий аккаунта
         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_TP)
              {
               //--- Если тикет позиции в торговом событии совпадает с тикетом в отложенном торговом запросе
               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.SetOrderActualProperties(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(index);
                     break;
                    }
                 }
              }
           }
         //--- Если в цикле по списку торговых событий был удалён отработанный объект-отложенный запрос - переходим к следующему (уходим из метода во внешний цикл)
         if(::CheckPointer(req_obj)==POINTER_INVALID)
            return false;
        }
      //--- Если это удаление отложенного ордера
      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)
            return false;
         //--- Оставляем в списке только один ордер с нужным тикетом
         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(index);
            return false;
           }
        }
      //--- Если это модификация отложенного ордера
      if(req_obj.Action()==TRADE_ACTION_MODIFY)
        {
         //--- Получаем список всех торговых событий аккаунта
         list=this.m_events.GetList();
         if(list==NULL)
            return false;
         //--- В цикле с конца списка торговых событий аккаунта
         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_SL_TP)
              {
               //--- Если тикет ордера в торговом событии совпадает с тикетом в отложенном торговом запросе
               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.SetOrderActualProperties(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(index);
                     break;
                    }
                 }
              }
           }
        }
     }
   //--- Если после проверки отработки запроса объект-отложенный запрос удалён - выходим (уходим из метода во внешний цикл)
   return(::CheckPointer(req_obj)==POINTER_INVALID ? false : true);
  }
//+------------------------------------------------------------------+

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

Обработчик отложенных запросов, созданных по коду ошибки:

//+------------------------------------------------------------------+
//| Обработчик отложенных запросов, созданных по коду ошибки         |
//+------------------------------------------------------------------+
void CTradingControl::OnPReqByErrCodeHandler(CPendRequest *req_obj,const int index)
  {
   //--- получаем структуру запроса и из неё объект-символ, на котором должна быть проведена торговая операция
   MqlTradeRequest request=req_obj.MqlRequest();
   CSymbol *symbol_obj=this.m_symbols.GetSymbolObjByName(request.symbol);
   if(symbol_obj==NULL || !symbol_obj.RefreshRates())
      return;
   
   //--- Устанавливаем флаг запрета торговли со стороны терминала одновременно с двух свойств
   //--- (кнопка Авто-торговля в терминале и галочка "Разрешить автоматическую торговлю" в настройках советника)
   //--- Если любое из двух свойств имеет значение false, то флаг будет иметь значение false
   bool terminal_trade_allowed=::TerminalInfoInteger(TERMINAL_TRADE_ALLOWED);
   terminal_trade_allowed &=::MQLInfoInteger(MQL_TRADE_ALLOWED);
   //--- если ошибка - запрет торговли со стороны терминала, и её причина устранена
   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()+1)))
     {
      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(index);
      return;
     }
   //--- Проверяем актуальность отложенного запроса и уходим во внешний цикл если запрос отработан или произошла ошибка
   if(!this.CheckPReqRelevance(req_obj,request,index))
      return;
   
   //--- Устанавливаем время активации запроса в объекте-запросе
   req_obj.SetTimeActivate(req_obj.TimeCreate()+req_obj.WaitingMSC()*(req_obj.CurrentAttempt()+1));
   
   //--- Если текущее время меньше времени активации запроса,
   //--- значит время запроса не наступило - идём к следующему запросу в списке (уходим из метода во внешний цикл)
   if((long)symbol_obj.Time()<(long)req_obj.TimeActivate())
      return;
   
   //--- Устанавливаем номер попытки в объекте-запросе
   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;
     }  
  }
//+------------------------------------------------------------------+

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

Обработчик отложенных запросов, созданных по запросу:

//+------------------------------------------------------------------+
//| Обработчик отложенных запросов, созданных по запросу             |
//+------------------------------------------------------------------+
void CTradingControl::OnPReqByRequestHandler(CPendRequest *req_obj,const int index)
  {
   //--- получаем структуру запроса и из неё объект-символ, на котором должна быть проведена торговая операция
   MqlTradeRequest request=req_obj.MqlRequest();
   CSymbol *symbol_obj=this.m_symbols.GetSymbolObjByName(request.symbol);
   if(symbol_obj==NULL || !symbol_obj.RefreshRates())
      return;
   //--- Проверяем актуальность отложенного запроса и уходим во внешний цикл если запрос отработан или произошла ошибка
   if(!this.CheckPReqRelevance(req_obj,request,index))
      return;

   //--- Обновляем актуальные данные условий активации запросов
   this.RefreshControlActualDatas(req_obj,symbol_obj);
   
   //--- Если все условия срабатывания отложенного запроса исполнены
   if(req_obj.IsAllComparisonCompleted())
     {
      //--- Устанавливаем номер попытки в объекте-запросе
      req_obj.SetCurrentAttempt(uchar(req_obj.CurrentAttempt()+1));
      //--- Выводим в журнал сообщение о срабатывании запроса
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
        {
         ::Print(CMessage::Text(MSG_LIB_TEXT_REQUEST_ACTIVATED)+(string)req_obj.ID()+":");
         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;
        }  
     }
  }
//+------------------------------------------------------------------+

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

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

//+------------------------------------------------------------------+
//| Обновляет актуальные значения контролируемых свойств             |
//| в объектах-отложенных запросах                                   |
//+------------------------------------------------------------------+
void CTradingControl::RefreshControlActualDatas(CPendRequest *req_obj,const CSymbol *symbol)
  {
//--- Если объект-запрос имеет тип запроса, созданного по коду ошибки - уходим
   if(req_obj.GetProperty(PEND_REQ_PROP_TYPE)==PEND_REQ_TYPE_ERROR)
      return;
   double res=EMPTY_VALUE;
//--- В цикле по всем условиям активации объекта-запроса
   uint total=req_obj.GetActivationCriterionTotal();
   for(uint i=0;i<total;i++)
     {
      //--- получаем источник активации
      ENUM_PEND_REQ_ACTIVATION_SOURCE source=req_obj.GetActivationSource(i);
      //--- получаем текущее значение контролируемого свойства
      double value=req_obj.GetActivationActualValue(i),actual=EMPTY_VALUE;
      //--- В зависимости от источника активации
      //--- записываем текущее значение контролируемого свойства в массив данных условий активации
      switch((int)source)
        {
         //--- Аккаунт
         case PEND_REQ_ACTIVATION_SOURCE_ACCOUNT   :  
            actual=this.GetActualDataAccount(req_obj.GetActivationProperty(i));
            req_obj.SetActivationActualValue(i,(actual!=EMPTY_VALUE ? actual : value));
           break;
         //--- Символ
         case PEND_REQ_ACTIVATION_SOURCE_SYMBOL    :
            actual=this.GetActualDataSymbol(req_obj.GetActivationProperty(i),symbol);
            req_obj.SetActivationActualValue(i,(actual!=EMPTY_VALUE ? actual : value));
           break;
         //--- Событие
         case PEND_REQ_ACTIVATION_SOURCE_EVENT     :
            actual=this.GetActualDataEvent(req_obj.GetActivationProperty(i));
            req_obj.SetActivationActualValue(i,(actual!=EMPTY_VALUE ? actual : value));
           break;
         //--- По умолчанию EMPTY_VALUE
         default:
           break;
        }
     }
  }
//+------------------------------------------------------------------+

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

Метод, возвращающий актуальные данные аккаунта:

//+------------------------------------------------------------------+
//| Возвращает актуальные данные аккаунта для контроля активации     |
//+------------------------------------------------------------------+
double CTradingControl::GetActualDataAccount(const int property)
  {
   switch(property)
    {
     case PEND_REQ_ACTIVATE_BY_ACCOUNT_LEVERAGE             : return (double)this.m_account.Leverage();
     case PEND_REQ_ACTIVATE_BY_ACCOUNT_LIMIT_ORDERS         : return (double)this.m_account.LimitOrders();
     case PEND_REQ_ACTIVATE_BY_ACCOUNT_TRADE_ALLOWED        : return (double)this.m_account.TradeAllowed();
     case PEND_REQ_ACTIVATE_BY_ACCOUNT_TRADE_EXPERT         : return (double)this.m_account.TradeExpert();
     case PEND_REQ_ACTIVATE_BY_ACCOUNT_BALANCE              : return this.m_account.Balance();
     case PEND_REQ_ACTIVATE_BY_ACCOUNT_CREDIT               : return this.m_account.Credit();
     case PEND_REQ_ACTIVATE_BY_ACCOUNT_PROFIT               : return this.m_account.Profit();
     case PEND_REQ_ACTIVATE_BY_ACCOUNT_EQUITY               : return this.m_account.Equity();
     case PEND_REQ_ACTIVATE_BY_ACCOUNT_MARGIN               : return this.m_account.Margin();
     case PEND_REQ_ACTIVATE_BY_ACCOUNT_MARGIN_FREE          : return this.m_account.MarginFree();
     case PEND_REQ_ACTIVATE_BY_ACCOUNT_MARGIN_LEVEL         : return this.m_account.MarginLevel();
     case PEND_REQ_ACTIVATE_BY_ACCOUNT_MARGIN_INITIAL       : return this.m_account.MarginInitial();
     case PEND_REQ_ACTIVATE_BY_ACCOUNT_MARGIN_MAINTENANCE   : return this.m_account.MarginMaintenance();
     case PEND_REQ_ACTIVATE_BY_ACCOUNT_ASSETS               : return this.m_account.Assets();
     case PEND_REQ_ACTIVATE_BY_ACCOUNT_LIABILITIES          : return this.m_account.Liabilities();
     case PEND_REQ_ACTIVATE_BY_ACCOUNT_COMMISSION_BLOCKED   : return this.m_account.ComissionBlocked();
     default: return EMPTY_VALUE;
    }
  }
//+------------------------------------------------------------------+

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

Метод, возвращающий актуальные данные символа:

//+------------------------------------------------------------------+
//| Возвращает актуальные данные символа для контроля активации      |
//+------------------------------------------------------------------+
double CTradingControl::GetActualDataSymbol(const int property,const CSymbol *symbol)
  {
   switch(property)
    {
     case PEND_REQ_ACTIVATE_BY_SYMBOL_BID                         : return symbol.Bid();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_ASK                         : return symbol.Ask();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_LAST                        : return symbol.Last();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_DEALS               : return (double)symbol.SessionDeals();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_BUY_ORDERS          : return (double)symbol.SessionBuyOrders();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_SELL_ORDERS         : return (double)symbol.SessionSellOrders();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_VOLUME                      : return (double)symbol.Volume();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_VOLUMEHIGH                  : return (double)symbol.VolumeHigh();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_VOLUMELOW                   : return (double)symbol.VolumeLow();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_TIME                        : return (double)symbol.Time()/1000;
     case PEND_REQ_ACTIVATE_BY_SYMBOL_SPREAD                      : return symbol.Spread();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_START_TIME                  : return (double)symbol.StartTime();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_EXPIRATION_TIME             : return (double)symbol.ExpirationTime();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_TRADE_STOPS_LEVEL           : return symbol.TradeStopLevel();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_TRADE_FREEZE_LEVEL          : return symbol.TradeFreezeLevel();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_BIDHIGH                     : return symbol.BidHigh();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_BIDLOW                      : return symbol.BidLow();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_ASKHIGH                     : return symbol.AskHigh();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_ASKLOW                      : return symbol.AskLow();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_LASTHIGH                    : return symbol.LastHigh();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_LASTLOW                     : return symbol.LastLow();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_VOLUME_REAL                 : return symbol.VolumeReal();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_VOLUMEHIGH_REAL             : return symbol.VolumeHighReal();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_VOLUMELOW_REAL              : return symbol.VolumeLowReal();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_OPTION_STRIKE               : return symbol.OptionStrike();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_TRADE_ACCRUED_INTEREST      : return symbol.TradeAccuredInterest();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_TRADE_FACE_VALUE            : return symbol.TradeFaceValue();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_TRADE_LIQUIDITY_RATE        : return symbol.TradeLiquidityRate();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_SWAP_LONG                   : return symbol.SwapLong();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_SWAP_SHORT                  : return symbol.SwapShort();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_VOLUME              : return symbol.SessionVolume();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_TURNOVER            : return symbol.SessionTurnover();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_INTEREST            : return symbol.SessionInterest();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_BUY_ORDERS_VOLUME   : return symbol.SessionBuyOrdersVolume();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_SELL_ORDERS_VOLUME  : return symbol.SessionSellOrdersVolume();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_OPEN                : return symbol.SessionOpen();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_CLOSE               : return symbol.SessionClose();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_AW                  : return symbol.SessionAW();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_PRICE_SETTLEMENT    : return symbol.SessionPriceSettlement();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_PRICE_LIMIT_MIN     : return symbol.SessionPriceLimitMin();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_PRICE_LIMIT_MAX     : return symbol.SessionPriceLimitMax();
     default: return EMPTY_VALUE;
    }
  }
//+------------------------------------------------------------------+

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

Метод, возвращающий актуальные данные события:

//+------------------------------------------------------------------+
//| Возвращает актуальные данные событий для контроля активации      |
//+------------------------------------------------------------------+
double CTradingControl::GetActualDataEvent(const int property)
  {
   if(this.m_events.IsEvent())
     {
      ENUM_TRADE_EVENT event=this.m_events.GetLastTradeEvent();
      switch(property)
       {
        case PEND_REQ_ACTIVATE_BY_EVENT_POSITION_OPENED                       : return event==TRADE_EVENT_POSITION_OPENED;
        case PEND_REQ_ACTIVATE_BY_EVENT_POSITION_CLOSED                       : return event==TRADE_EVENT_POSITION_CLOSED;
        case PEND_REQ_ACTIVATE_BY_EVENT_PENDING_ORDER_PLASED                  : return event==TRADE_EVENT_PENDING_ORDER_PLASED;
        case PEND_REQ_ACTIVATE_BY_EVENT_PENDING_ORDER_REMOVED                 : return event==TRADE_EVENT_PENDING_ORDER_REMOVED;
        case PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_CREDIT                        : return event==TRADE_EVENT_ACCOUNT_CREDIT;
        case PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_CHARGE                        : return event==TRADE_EVENT_ACCOUNT_CHARGE;
        case PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_CORRECTION                    : return event==TRADE_EVENT_ACCOUNT_CORRECTION;
        case PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_BONUS                         : return event==TRADE_EVENT_ACCOUNT_BONUS;
        case PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_COMISSION                     : return event==TRADE_EVENT_ACCOUNT_COMISSION;
        case PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_COMISSION_DAILY               : return event==TRADE_EVENT_ACCOUNT_COMISSION_DAILY;
        case PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_COMISSION_MONTHLY             : return event==TRADE_EVENT_ACCOUNT_COMISSION_MONTHLY;
        case PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_COMISSION_AGENT_DAILY         : return event==TRADE_EVENT_ACCOUNT_COMISSION_AGENT_DAILY;
        case PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_COMISSION_AGENT_MONTHLY       : return event==TRADE_EVENT_ACCOUNT_COMISSION_AGENT_MONTHLY;
        case PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_INTEREST                      : return event==TRADE_EVENT_ACCOUNT_INTEREST;
        case PEND_REQ_ACTIVATE_BY_EVENT_BUY_CANCELLED                         : return event==TRADE_EVENT_BUY_CANCELLED;
        case PEND_REQ_ACTIVATE_BY_EVENT_SELL_CANCELLED                        : return event==TRADE_EVENT_SELL_CANCELLED;
        case PEND_REQ_ACTIVATE_BY_EVENT_DIVIDENT                              : return event==TRADE_EVENT_DIVIDENT;
        case PEND_REQ_ACTIVATE_BY_EVENT_DIVIDENT_FRANKED                      : return event==TRADE_EVENT_DIVIDENT_FRANKED;
        case PEND_REQ_ACTIVATE_BY_EVENT_TAX                                   : return event==TRADE_EVENT_TAX;
        case PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_BALANCE_REFILL                : return event==TRADE_EVENT_ACCOUNT_BALANCE_REFILL;
        case PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_BALANCE_WITHDRAWAL            : return event==TRADE_EVENT_ACCOUNT_BALANCE_WITHDRAWAL;
        case PEND_REQ_ACTIVATE_BY_EVENT_PENDING_ORDER_ACTIVATED               : return event==TRADE_EVENT_PENDING_ORDER_ACTIVATED;
        case PEND_REQ_ACTIVATE_BY_EVENT_PENDING_ORDER_ACTIVATED_PARTIAL       : return event==TRADE_EVENT_PENDING_ORDER_ACTIVATED_PARTIAL;
        case PEND_REQ_ACTIVATE_BY_EVENT_POSITION_OPENED_PARTIAL               : return event==TRADE_EVENT_POSITION_OPENED_PARTIAL;
        case PEND_REQ_ACTIVATE_BY_EVENT_POSITION_CLOSED_PARTIAL               : return event==TRADE_EVENT_POSITION_CLOSED_PARTIAL;
        case PEND_REQ_ACTIVATE_BY_EVENT_POSITION_CLOSED_BY_POS                : return event==TRADE_EVENT_POSITION_CLOSED_BY_POS;
        case PEND_REQ_ACTIVATE_BY_EVENT_POSITION_CLOSED_PARTIAL_BY_POS        : return event==TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_POS;
        case PEND_REQ_ACTIVATE_BY_EVENT_POSITION_CLOSED_BY_SL                 : return event==TRADE_EVENT_POSITION_CLOSED_BY_SL;
        case PEND_REQ_ACTIVATE_BY_EVENT_POSITION_CLOSED_BY_TP                 : return event==TRADE_EVENT_POSITION_CLOSED_BY_TP;
        case PEND_REQ_ACTIVATE_BY_EVENT_POSITION_CLOSED_PARTIAL_BY_SL         : return event==TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_SL;
        case PEND_REQ_ACTIVATE_BY_EVENT_POSITION_CLOSED_PARTIAL_BY_TP         : return event==TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_TP;
        case PEND_REQ_ACTIVATE_BY_EVENT_POSITION_REVERSED_BY_MARKET           : return event==TRADE_EVENT_POSITION_REVERSED_BY_MARKET;
        case PEND_REQ_ACTIVATE_BY_EVENT_POSITION_REVERSED_BY_PENDING          : return event==TRADE_EVENT_POSITION_REVERSED_BY_PENDING;
        case PEND_REQ_ACTIVATE_BY_EVENT_POSITION_REVERSED_BY_MARKET_PARTIAL   : return event==TRADE_EVENT_POSITION_REVERSED_BY_MARKET_PARTIAL;
        case PEND_REQ_ACTIVATE_BY_EVENT_POSITION_VOLUME_ADD_BY_MARKET         : return event==TRADE_EVENT_POSITION_VOLUME_ADD_BY_MARKET;
        case PEND_REQ_ACTIVATE_BY_EVENT_POSITION_VOLUME_ADD_BY_PENDING        : return event==TRADE_EVENT_POSITION_VOLUME_ADD_BY_PENDING;
        case PEND_REQ_ACTIVATE_BY_EVENT_MODIFY_ORDER_PRICE                    : return event==TRADE_EVENT_MODIFY_ORDER_PRICE;
        case PEND_REQ_ACTIVATE_BY_EVENT_MODIFY_ORDER_PRICE_SL                 : return event==TRADE_EVENT_MODIFY_ORDER_PRICE_SL;
        case PEND_REQ_ACTIVATE_BY_EVENT_MODIFY_ORDER_PRICE_TP                 : return event==TRADE_EVENT_MODIFY_ORDER_PRICE_TP;
        case PEND_REQ_ACTIVATE_BY_EVENT_MODIFY_ORDER_PRICE_SL_TP              : return event==TRADE_EVENT_MODIFY_ORDER_PRICE_SL_TP;
        case PEND_REQ_ACTIVATE_BY_EVENT_MODIFY_ORDER_SL_TP                    : return event==TRADE_EVENT_MODIFY_ORDER_SL_TP;
        case PEND_REQ_ACTIVATE_BY_EVENT_MODIFY_ORDER_SL                       : return event==TRADE_EVENT_MODIFY_ORDER_SL;
        case PEND_REQ_ACTIVATE_BY_EVENT_MODIFY_ORDER_TP                       : return event==TRADE_EVENT_MODIFY_ORDER_TP;
        case PEND_REQ_ACTIVATE_BY_EVENT_MODIFY_POSITION_SL_TP                 : return event==TRADE_EVENT_MODIFY_POSITION_SL_TP;
        case PEND_REQ_ACTIVATE_BY_EVENT_MODIFY_POSITION_SL                    : return event==TRADE_EVENT_MODIFY_POSITION_SL;
        case PEND_REQ_ACTIVATE_BY_EVENT_MODIFY_POSITION_TP                    : return event==TRADE_EVENT_MODIFY_POSITION_TP;
        case PEND_REQ_ACTIVATE_BY_EVENT_POSITION_VOL_ADD_BY_MARKET_PARTIAL    : return event==TRADE_EVENT_POSITION_VOLUME_ADD_BY_MARKET_PARTIAL;
        case PEND_REQ_ACTIVATE_BY_EVENT_POSITION_VOL_ADD_BY_PENDING_PARTIAL   : return event==TRADE_EVENT_POSITION_VOLUME_ADD_BY_PENDING_PARTIAL;
        case PEND_REQ_ACTIVATE_BY_EVENT_TRIGGERED_STOP_LIMIT_ORDER            : return event==TRADE_EVENT_TRIGGERED_STOP_LIMIT_ORDER;
        case PEND_REQ_ACTIVATE_BY_EVENT_POSITION_REVERSED_BY_PENDING_PARTIAL  : return event==TRADE_EVENT_POSITION_REVERSED_BY_PENDING_PARTIAL;
        default: return EMPTY_VALUE;
       }
     }
   return EMPTY_VALUE;
  }
//+------------------------------------------------------------------+

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

Таймер класса теперь стал намного компактнее:

//+------------------------------------------------------------------+
//| Таймер                                                           |
//+------------------------------------------------------------------+
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;
      //--- Если объект-запрос создан по коду ошибки - обрабатываем обработчиком объектов, созданных по коду ошибки
      if(req_obj.TypeRequest()==PEND_REQ_TYPE_ERROR)
         this.OnPReqByErrCodeHandler(req_obj,i);
      //--- Иначе - это объект, созданный по запросу - обрабатываем обработчиком объектов, созданных по запросу
      else
         this.OnPReqByRequestHandler(req_obj,i);
     }
  }
//+------------------------------------------------------------------+

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

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

Внесём дополнения в публичную секцию класса основного объекта библиотеки CEngine.

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

   void                 TradingSetTotalTry(const uchar attempts)                       { this.m_trading.SetTotalTry(attempts);                     }
   
//--- Возвращает уровень логирования торгового объекта символа торгового класса
   ENUM_LOG_LEVEL       TradingGetLogLevel(const string symbol_name)                   { return this.m_trading.GetTradeObjLogLevel(symbol_name);   }
   
//--- Устанавливает стандартные звуки (symbol==NULL) торговому объекту символа, (symbol!=NULL) торговым объектам всех символов

Метод возвращает результат работы метода класса управления торговлей GetTradeObjLogLevel().

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

//--- Удаляет отложенный ордер
   bool                 DeleteOrder(const ulong ticket);
   
//--- Создаёт отложенный запрос (1) на открытие позиции Buy, (2) на открытие позиции Sell
   template<typename SL,typename TP> 
   int                  OpenBuyPending(const double volume,
                                       const string symbol,
                                       const ulong magic=ULONG_MAX,
                                       const SL sl=0,
                                       const TP tp=0,
                                       const uchar group_id1=0,
                                       const uchar group_id2=0,
                                       const string comment=NULL,
                                       const ulong deviation=ULONG_MAX,
                                       const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE);
   template<typename SL,typename TP> 
   int                  OpenSellPending(const double volume,
                                       const string symbol,
                                       const ulong magic=ULONG_MAX,
                                       const SL sl=0,
                                       const TP tp=0,
                                       const uchar group_id1=0,
                                       const uchar group_id2=0,
                                       const string comment=NULL,
                                       const ulong deviation=ULONG_MAX,
                                       const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE);
                                    
//--- Устанавливает критерии активации отложенного запроса
   bool                 SetNewActivationProperties(const uchar id,
                                                   const ENUM_PEND_REQ_ACTIVATION_SOURCE source,
                                                   const int property,
                                                   const double control_value,
                                                   const ENUM_COMPARER_TYPE comparer_type,
                                                   const double actual_value);
                                                
//--- Возвращает указатель на объект-запрос по его идентификатору в списке
   CPendRequest        *GetPendRequestByID(const uchar id)              { return this.m_trading.GetPendRequestByID(id);       }

//--- Возвращает (1) милисекунды, (2) причину, (3) источник события из его long-значения

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

Реализация метода создания отложенного запроса на открытие позиции Buy:

//+------------------------------------------------------------------+
//| Создаёт отложенный запрос на открытие позиции Buy                |
//+------------------------------------------------------------------+
template<typename SL,typename TP> 
int CEngine::OpenBuyPending(const double volume,
                            const string symbol,
                            const ulong magic=ULONG_MAX,
                            const SL sl=0,
                            const TP tp=0,
                            const uchar group_id1=0,
                            const uchar group_id2=0,
                            const string comment=NULL,
                            const ulong deviation=ULONG_MAX,
                            const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE)
  {
   return this.m_trading.OpenPositionPending(POSITION_TYPE_BUY,volume,symbol,magic,sl,tp,group_id1,group_id2,comment,deviation,type_filling);
  }
//+------------------------------------------------------------------+

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

Реализация метода создания отложенного запроса на открытие позиции Sell:

//+------------------------------------------------------------------+
//| Создаёт отложенный запрос на открытие позиции Sell               |
//+------------------------------------------------------------------+
template<typename SL,typename TP> 
int CEngine::OpenSellPending(const double volume,
                            const string symbol,
                            const ulong magic=ULONG_MAX,
                            const SL sl=0,
                            const TP tp=0,
                            const uchar group_id1=0,
                            const uchar group_id2=0,
                            const string comment=NULL,
                            const ulong deviation=ULONG_MAX,
                            const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE)
  {
   return this.m_trading.OpenPositionPending(POSITION_TYPE_SELL,volume,symbol,magic,sl,tp,group_id1,group_id2,comment,deviation,type_filling);
  }
//+------------------------------------------------------------------+

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

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

//+------------------------------------------------------------------+
//| Устанавливает критерии активации отложенного запроса             |
//+------------------------------------------------------------------+
bool CEngine::SetNewActivationProperties(const uchar id,
                                         const ENUM_PEND_REQ_ACTIVATION_SOURCE source,
                                         const int property,
                                         const double control_value,
                                         const ENUM_COMPARER_TYPE comparer_type,
                                         const double actual_value)
  {
   return this.m_trading.SetNewActivationProperties(id,source,property,control_value,comparer_type,actual_value);
  }
//+------------------------------------------------------------------+

Метод вызывает рассмотренный выше метод добавления нового условия активации в объект-отложенный запрос класса управления торговлей. И это все доработки библиотеки на сегодня.

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

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

Для проверки работы отложенных запросов по условиям мы сделаем дополнительные кнопки к торговой панели тестового советника. Кнопки будут обозначены как " P" — условие по цене, и как "T" — условие по времени. Отложенные запросы будут создаваться при нажатии на кнопку "Buy" или " Sell" при условии, что нажата одна из кнопок "P" или "T", или обе сразу. Если нажаты обе сразу, то это означает, что у отложенного запроса два условия активации — по значению цены и времени.

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

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

Если включена кнопка "T", то к текущему времени будет добавлено время, рассчитанное как текущее время + время указанного количества баров текущего таймфрейма, и это время будет записано как контрольное для срабатывания отложенного запроса — когда текущее время станет равно или больше рассчитанного, отложенный запрос сработает.

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

Для открытия позиции Sell, контролируемая цена будет рассчитываться как текущая цена + указанное количество пунктов в настройках. Т.е. для срабатывания отложенного запроса текущая цена должна стать выше той, которая была на момент создания отложенного запроса (на момент нажатия кнопки " Sell").

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

//--- input variables
input    ushort            InpMagic             =  123;  // Magic number
input    double            InpLots              =  0.1;  // Lots
input    uint              InpStopLoss          =  150;  // StopLoss in points
input    uint              InpTakeProfit        =  150;  // TakeProfit in points
input    uint              InpDistance          =  50;   // Pending orders distance (points)
input    uint              InpDistanceSL        =  50;   // StopLimit orders distance (points)
input    uint              InpDistancePReq      =  50;   // Distance for Pending Request's activate (points)
input    uint              InpBarsDelayPReq     =  5;    // Bars delay for Pending Request's activate (current timeframe)
input    uint              InpSlippage          =  5;    // Slippage in points
input    uint              InpSpreadMultiplier  =  1;    // Spread multiplier for adjusting stop-orders by StopLevel
input    uchar             InpTotalAttempts     =  5;    // Number of trading attempts
sinput   double            InpWithdrawal        =  10;   // Withdrawal funds (in tester)
sinput   uint              InpButtShiftX        =  0;    // Buttons X shift 
sinput   uint              InpButtShiftY        =  10;   // Buttons Y shift 
input    uint              InpTrailingStop      =  50;   // Trailing Stop (points)
input    uint              InpTrailingStep      =  20;   // Trailing Step (points)
input    uint              InpTrailingStart     =  0;    // Trailing Start (points)
input    uint              InpStopLossModify    =  20;   // StopLoss for modification (points)
input    uint              InpTakeProfitModify  =  60;   // TakeProfit for modification (points)
sinput   ENUM_SYMBOLS_MODE InpModeUsedSymbols   =  SYMBOLS_MODE_CURRENT;   // Mode of used symbols list
sinput   string            InpUsedSymbols       =  "EURUSD,AUDUSD,EURAUD,EURCAD,EURGBP,EURJPY,EURUSD,GBPUSD,NZDUSD,USDCAD,USDJPY";  // List of used symbols (comma - separator)
sinput   bool              InpUseSounds         =  true; // Use sounds

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

//--- global variables
CEngine        engine;
SDataButt      butt_data[TOTAL_BUTT];
string         prefix;
double         lot;
double         withdrawal=(InpWithdrawal<0.1 ? 0.1 : InpWithdrawal);
ushort         magic_number;
uint           stoploss;
uint           takeprofit;
uint           distance_pending;
uint           distance_stoplimit;
uint           distance_pending_request;
uint           bars_delay_pending_request;
uint           slippage;
bool           trailing_on;
bool           pending_buy;
bool           pending_buy_limit;
bool           pending_buy_stop;
bool           pending_buy_stoplimit;
bool           pending_close_buy;
bool           pending_close_buy2;
bool           pending_close_buy_by_sell;
bool           pending_sell;
bool           pending_sell_limit;
bool           pending_sell_stop;
bool           pending_sell_stoplimit;
bool           pending_close_sell;
bool           pending_close_sell2;
bool           pending_close_sell_by_buy;
double         trailing_stop;
double         trailing_step;
uint           trailing_start;
uint           stoploss_to_modify;
uint           takeprofit_to_modify;
int            used_symbols_mode;
string         used_symbols;
string         array_used_symbols[];
bool           testing;
uchar          group1;
uchar          group2;
//+------------------------------------------------------------------+

В обработчике OnInit() советника присвоим переменным корректные значения входных параметров и сбросим состояния кнопок отложенных запросов:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Вызов данной функции выводит в журнал список констант перечисления, 
//--- заданного в файле DELib.mqh в строках 22 и 25, для проверки корректности констант
   //EnumNumbersTest();

//--- Установка глобальных переменных советника
   prefix=MQLInfoString(MQL_PROGRAM_NAME)+"_";
   testing=engine.IsTester();
   for(int i=0;i<TOTAL_BUTT;i++)
     {
      butt_data[i].name=prefix+EnumToString((ENUM_BUTTONS)i);
      butt_data[i].text=EnumToButtText((ENUM_BUTTONS)i);
     }
   lot=NormalizeLot(Symbol(),fmax(InpLots,MinimumLots(Symbol())*2.0));
   magic_number=InpMagic;
   stoploss=InpStopLoss;
   takeprofit=InpTakeProfit;
   distance_pending=InpDistance;
   distance_stoplimit=InpDistanceSL;
   slippage=InpSlippage;
   trailing_stop=InpTrailingStop*Point();
   trailing_step=InpTrailingStep*Point();
   trailing_start=InpTrailingStart;
   stoploss_to_modify=InpStopLossModify;
   takeprofit_to_modify=InpTakeProfitModify;
   distance_pending_request=(InpDistancePReq<5 ? 5 : InpDistancePReq);
   bars_delay_pending_request=(InpBarsDelayPReq<1 ? 1 : InpBarsDelayPReq);
//--- Инициализация случайных номеров групп
   group1=0;
   group2=0;
   srand(GetTickCount());
   
//--- Инициализация библиотеки DoEasy
   OnInitDoEasy();
   
//--- Проверка и удаление неудалённых графических объектов советника
   if(IsPresentObects(prefix))
      ObjectsDeleteAll(0,prefix);

//--- Создание панели кнопок
   if(!CreateButtons(InpButtShiftX,InpButtShiftY))
      return INIT_FAILED;
//--- Установка состояния кнопки активизации трейлингов
   ButtonState(butt_data[TOTAL_BUTT-1].name,trailing_on);
//--- Сброс состояний кнопок активизации работы отложенными запросами
   for(int i=0;i<14;i++)
     {
      ButtonState(butt_data[i].name+"_PRICE",false);
      ButtonState(butt_data[i].name+"_TIME",false);
     }

//--- Проверка воспроизведения стандартного звука по макроподстановке и пользовательского звука по описанию
   engine.PlaySoundByDescription(SND_OK);
   Sleep(600);
   engine.PlaySoundByDescription(TextByLanguage("Звук упавшей монетки 2","The sound of a falling coin 2"));

//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+

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

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

//+------------------------------------------------------------------+
//| Создаёт панель кнопок                                            |
//+------------------------------------------------------------------+
bool CreateButtons(const int shift_x=20,const int shift_y=0)
  {
   int h=18,w=82,offset=2,wpt=14;
   int cx=offset+shift_x+wpt*2+2,cy=offset+shift_y+(h+1)*(TOTAL_BUTT/2)+3*h+1;
   int x=cx,y=cy;
   int shift=0;
   for(int i=0;i<TOTAL_BUTT;i++)
     {
      x=x+(i==7 ? w+2 : 0);
      if(i==TOTAL_BUTT-6) x=cx;
      y=(cy-(i-(i>6 ? 7 : 0))*(h+1));
      if(!ButtonCreate(butt_data[i].name,x,y,(i<TOTAL_BUTT-6 ? w : w*2+2),h,butt_data[i].text,(i<4 ? clrGreen : i>6 && i<11 ? clrRed : clrBlue)))
        {
         Alert(TextByLanguage("Не удалось создать кнопку \"","Could not create button \""),butt_data[i].text);
         return false;
        }
     }
   
   h=18; offset=2;
   cx=offset+shift_x; cy=offset+shift_y+(h+1)*(TOTAL_BUTT/2)+3*h+1;
   x=cx; y=cy;
   shift=0;
   for(int i=0;i<14;i++)
     {
      y=(cy-(i-(i>6 ? 7 : 0))*(h+1));
      if(!ButtonCreate(butt_data[i].name+"_PRICE",((i>6 && i<11) || i>10 ? x+wpt*2+w*2+5 : x),y,wpt,h,"P",(i<4 ? clrGreen : i>6 && i<11 ? clrChocolate : clrBlue)))
        {
         Alert(TextByLanguage("Не удалось создать кнопку \"","Could not create button \""),butt_data[i].text+" \"P\"");
         return false;
        }
      if(!ButtonCreate(butt_data[i].name+"_TIME",((i>6 && i<11) || i>10 ? x+wpt*2+w*2+5+wpt+1 : x+wpt+1),y,wpt,h,"T",(i<4 ? clrGreen : i>6 && i<11 ? clrChocolate : clrBlue)))
        {
         Alert(TextByLanguage("Не удалось создать кнопку \"","Could not create button \""),butt_data[i].text+" \"T\"");
         return false;
        }
     }
   ChartRedraw(0);
   return true;
  }
//+------------------------------------------------------------------+

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

//+------------------------------------------------------------------+
//| Устанавливает состояние кнопки                                   |
//+------------------------------------------------------------------+
void ButtonState(const string name,const bool state)
  {
   ObjectSetInteger(0,name,OBJPROP_STATE,state);
//--- Кнопка активации трейлинга
   if(name==butt_data[TOTAL_BUTT-1].name)
     {
      if(state)
         ObjectSetInteger(0,name,OBJPROP_BGCOLOR,C'220,255,240');
      else
         ObjectSetInteger(0,name,OBJPROP_BGCOLOR,C'240,240,240');
     }
//--- Кнопки активации работы отложенными запросами
   if(StringFind(name,"_PRICE")>0 || StringFind(name,"_TIME")>0)
     {
      if(state)
         ObjectSetInteger(0,name,OBJPROP_BGCOLOR,C'255,220,90');
      else
         ObjectSetInteger(0,name,OBJPROP_BGCOLOR,C'240,240,240');
     }
  }
//+------------------------------------------------------------------+

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

//+------------------------------------------------------------------+
//| Обработка нажатий кнопок                                         |
//+------------------------------------------------------------------+
void PressButtonEvents(const string button_name)
  {
   bool comp_magic=true;   // Временная переменная для выбора использования составного магика со случайными идентификаторами групп
   string comment="";
   //--- Преобразуем имя кнопки в её строковый идентификатор
   string button=StringSubstr(button_name,StringLen(prefix));
   //--- Случайные номера групп 1 и 2 в диапазоне 0 - 15
   group1=(uchar)Rand();
   group2=(uchar)Rand();
   uint magic=(comp_magic ? engine.SetCompositeMagicNumber(magic_number,group1,group2) : magic_number);
   //--- Если кнопка в нажатом состоянии
   if(ButtonState(button_name))
     {
      //--- Если нажата кнопка BUTT_BUY: Открыть позицию Buy
      if(button==EnumToString(BUTT_BUY))
        {
         //--- Если не нажаты кнопки создания отложенного запроса - открываем позицию Buy 
         if(!pending_buy)
            engine.OpenBuy(lot,Symbol(),magic,stoploss,takeprofit);   // Нет комментария - будет установлен комментарий по умолчанию
         //--- Иначе - создаём отложенный запрос на открытие позиции Buy
         else
           {
            int id=engine.OpenBuyPending(lot,Symbol(),magic,stoploss,takeprofit);
            if(id>0)
              {
               //--- Если выбран критерий по цене
               if(ButtonState(prefix+EnumToString(BUTT_BUY)+"_PRICE"))
                 {
                  double ask=SymbolInfoDouble(NULL,SYMBOL_ASK);
                  double control_value=NormalizeDouble(ask-distance_pending_request*SymbolInfoDouble(NULL,SYMBOL_POINT),(int)SymbolInfoInteger(NULL,SYMBOL_DIGITS));
                  engine.SetNewActivationProperties((uchar)id,PEND_REQ_ACTIVATION_SOURCE_SYMBOL,PEND_REQ_ACTIVATE_BY_SYMBOL_ASK,control_value,EQUAL_OR_LESS,ask);
                 }
               //--- Если выбран критерий по времени
               if(ButtonState(prefix+EnumToString(BUTT_BUY)+"_TIME"))
                 {
                  ulong control_time=TimeCurrent()+bars_delay_pending_request*PeriodSeconds();
                  engine.SetNewActivationProperties((uchar)id,PEND_REQ_ACTIVATION_SOURCE_SYMBOL,PEND_REQ_ACTIVATE_BY_SYMBOL_TIME,control_time,EQUAL_OR_MORE,TimeCurrent());
                 }
              }
            CPendRequest *req_obj=engine.GetPendRequestByID((uchar)id);
            if(req_obj==NULL)
               return;
            if(engine.TradingGetLogLevel(Symbol())>LOG_LEVEL_NO_MSG)
              {
               ::Print(CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_ADD_CRITERIONS)," #",req_obj.ID(),":");
               req_obj.PrintActivations();
              }
           }
        }
      //--- Если нажата кнопка BUTT_BUY_LIMIT: Выставить BuyLimit
      else if(button==EnumToString(BUTT_BUY_LIMIT))
        {
         //--- Если не нажаты кнопки создания отложенного запроса - устанавливаем ордер BuyLimit
         if(!pending_buy_limit)
            engine.PlaceBuyLimit(lot,Symbol(),distance_pending,stoploss,takeprofit,magic,TextByLanguage("Отложенный BuyLimit","Pending order BuyLimit"));
         //--- Иначе - в данной редакции ничего не делаем
         else
           {
            
           }
        }
      //--- Если нажата кнопка BUTT_BUY_STOP: Выставить BuyStop
      else if(button==EnumToString(BUTT_BUY_STOP))
        {
         //--- Если не нажаты кнопки создания отложенного запроса - устанавливаем ордер BuyStop
         if(!pending_buy_stop)
            engine.PlaceBuyStop(lot,Symbol(),distance_pending,stoploss,takeprofit,magic,TextByLanguage("Отложенный BuyStop","Pending order BuyStop"));
         //--- Иначе - в данной редакции ничего не делаем
         else
           {
            
           }
        }
      //--- Если нажата кнопка BUTT_BUY_STOP_LIMIT: Выставить BuyStopLimit
      else if(button==EnumToString(BUTT_BUY_STOP_LIMIT))
        {
         //--- Если не нажаты кнопки создания отложенного запроса - устанавливаем ордер BuyStopLimit
         if(!pending_buy_stoplimit)
            engine.PlaceBuyStopLimit(lot,Symbol(),distance_pending,distance_stoplimit,stoploss,takeprofit,magic,TextByLanguage("Отложенный BuyStopLimit","Pending order BuyStopLimit"));
         //--- Иначе - в данной редакции ничего не делаем
         else
           {
            
           }
        }
      //--- Если нажата кнопка BUTT_SELL: Открыть позицию Sell
      else if(button==EnumToString(BUTT_SELL))
        {
         //--- Если не нажаты кнопки создания отложенного запроса - открываем позицию Sell
         if(!pending_sell)
            engine.OpenSell(lot,Symbol(),magic,stoploss,takeprofit);  // Нет комментария - будет установлен комментарий по умолчанию
         //--- Иначе - создаём отложенный запрос на открытие позиции Sell
         else
           {
            int id=engine.OpenSellPending(lot,Symbol(),magic,stoploss,takeprofit);
            if(id>0)
              {
               //--- Если выбран критерий по цене
               if(ButtonState(prefix+EnumToString(BUTT_SELL)+"_PRICE"))
                 {
                  double bid=SymbolInfoDouble(NULL,SYMBOL_BID);
                  double control_value=NormalizeDouble(bid+distance_pending_request*SymbolInfoDouble(NULL,SYMBOL_POINT),(int)SymbolInfoInteger(NULL,SYMBOL_DIGITS));
                  engine.SetNewActivationProperties((uchar)id,PEND_REQ_ACTIVATION_SOURCE_SYMBOL,PEND_REQ_ACTIVATE_BY_SYMBOL_BID,control_value,EQUAL_OR_MORE,bid);
                 }
               //--- Если выбран критерий по времени
               if(ButtonState(prefix+EnumToString(BUTT_SELL)+"_TIME"))
                 {
                  ulong control_time=TimeCurrent()+bars_delay_pending_request*PeriodSeconds();
                  engine.SetNewActivationProperties((uchar)id,PEND_REQ_ACTIVATION_SOURCE_SYMBOL,PEND_REQ_ACTIVATE_BY_SYMBOL_TIME,control_time,EQUAL_OR_MORE,TimeCurrent());
                 }
              }
            CPendRequest *req_obj=engine.GetPendRequestByID((uchar)id);
            if(req_obj==NULL)
               return;
            if(engine.TradingGetLogLevel(Symbol())>LOG_LEVEL_NO_MSG)
              {
               ::Print(CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_ADD_CRITERIONS)," #",req_obj.ID(),":");
               req_obj.PrintActivations();
              }
           }
        }
      //--- Если нажата кнопка BUTT_SELL_LIMIT: Выставить SellLimit
      else if(button==EnumToString(BUTT_SELL_LIMIT))
        {
         //--- Если не нажаты кнопки создания отложенного запроса - устанавливаем ордер SellLimit
         if(!pending_sell_limit)
            engine.PlaceSellLimit(lot,Symbol(),distance_pending,stoploss,takeprofit,magic,TextByLanguage("Отложенный SellLimit","Pending order SellLimit"));
         //--- Иначе - в данной редакции ничего не делаем
         else
           {
            
           }
        }
      //--- Если нажата кнопка BUTT_SELL_STOP: Выставить SellStop
      else if(button==EnumToString(BUTT_SELL_STOP))
        {
         //--- Если не нажаты кнопки создания отложенного запроса - устанавливаем ордер SellStop
         if(!pending_sell_stop)
            engine.PlaceSellStop(lot,Symbol(),distance_pending,stoploss,takeprofit,magic,TextByLanguage("Отложенный SellStop","Pending order SellStop"));
         //--- Иначе - в данной редакции ничего не делаем
         else
           {
            
           }
        }
      //--- Если нажата кнопка BUTT_SELL_STOP_LIMIT: Выставить SellStopLimit
      else if(button==EnumToString(BUTT_SELL_STOP_LIMIT))
        {
         //--- Если не нажаты кнопки создания отложенного запроса - устанавливаем ордер SellStopLimit
         if(!pending_sell_stoplimit)
            engine.PlaceSellStopLimit(lot,Symbol(),distance_pending,distance_stoplimit,stoploss,takeprofit,magic,TextByLanguage("Отложенный SellStopLimit","Pending order SellStopLimit"));
         //--- Иначе - в данной редакции ничего не делаем
         else
           {
            
           }
        }
      //--- Если нажата кнопка BUTT_CLOSE_BUY: Закрыть Buy с максимальной прибылью
      else if(button==EnumToString(BUTT_CLOSE_BUY))
        {
         //--- Получаем список всех открытых позиций
         CArrayObj* list=engine.GetListMarketPosition();
         //--- Выбираем из списка только позиции Buy и только по текущему символу
         list=CSelect::ByOrderProperty(list,ORDER_PROP_SYMBOL,Symbol(),EQUAL);
         list=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_BUY,EQUAL);
         //--- Сортируем список по прибыли с учётом комиссии и свопа
         list.Sort(SORT_BY_ORDER_PROFIT_FULL);
         //--- Получаем индекс позиции Buy с наибольшей прибылью
         int index=CSelect::FindOrderMax(list,ORDER_PROP_PROFIT_FULL);
         if(index>WRONG_VALUE)
           {
            //--- Получаем объект-позицию Buy и закрываем позицию по тикету
            COrder* position=list.At(index);
            if(position!=NULL)
               engine.ClosePosition((ulong)position.Ticket());
           }
        }
      //--- Если нажата кнопка BUTT_CLOSE_BUY2: Закрыть половину Buy с максимальной прибылью
      else if(button==EnumToString(BUTT_CLOSE_BUY2))
        {
         //--- Получаем список всех открытых позиций
         CArrayObj* list=engine.GetListMarketPosition();
         //--- Выбираем из списка только позиции Buy и только по текущему символу
         list=CSelect::ByOrderProperty(list,ORDER_PROP_SYMBOL,Symbol(),EQUAL);
         list=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_BUY,EQUAL);
         //--- Сортируем список по прибыли с учётом комиссии и свопа
         list.Sort(SORT_BY_ORDER_PROFIT_FULL);
         //--- Получаем индекс позиции Buy с наибольшей прибылью
         int index=CSelect::FindOrderMax(list,ORDER_PROP_PROFIT_FULL);
         if(index>WRONG_VALUE)
           {
            COrder* position=list.At(index);
            //--- Закрываем частично позицию Buy
            if(position!=NULL)
               engine.ClosePositionPartially((ulong)position.Ticket(),position.Volume()/2.0);
           }
        }
      //--- Если нажата кнопка BUTT_CLOSE_BUY_BY_SELL: Закрыть Buy с максимальной прибылью встречной Sell с максимальной прибылью
      else if(button==EnumToString(BUTT_CLOSE_BUY_BY_SELL))
        {
         //--- Если счёт хеджевый
         if(engine.IsHedge())
           {
            CArrayObj *list_buy=NULL, *list_sell=NULL;
            //--- Получаем список всех открытых позиций
            CArrayObj* list=engine.GetListMarketPosition();
            if(list==NULL)
               return;
            //--- Выбираем из списка только позиции по текущему символу
            list=CSelect::ByOrderProperty(list,ORDER_PROP_SYMBOL,Symbol(),EQUAL);
            
            //--- Выбираем из списка только позиции Buy
            list_buy=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_BUY,EQUAL);
            if(list_buy==NULL)
               return;
            //--- Сортируем список по прибыли с учётом комиссии и свопа
            list_buy.Sort(SORT_BY_ORDER_PROFIT_FULL);
            //--- Получаем индекс позиции Buy с наибольшей прибылью
            int index_buy=CSelect::FindOrderMax(list_buy,ORDER_PROP_PROFIT_FULL);
            
            //--- Выбираем из списка только позиции Sell
            list_sell=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_SELL,EQUAL);
            if(list_sell==NULL)
               return;
            //--- Сортируем список по прибыли с учётом комиссии и свопа
            list_sell.Sort(SORT_BY_ORDER_PROFIT_FULL);
            //--- Получаем индекс позиции Sell с наибольшей прибылью
            int index_sell=CSelect::FindOrderMax(list_sell,ORDER_PROP_PROFIT_FULL);
            if(index_buy>WRONG_VALUE && index_sell>WRONG_VALUE)
              {
               //--- Выбираем позицию Buy с наибольшей прибылью
               COrder* position_buy=list_buy.At(index_buy);
               //--- Выбираем позицию Sell с наибольшей прибылью
               COrder* position_sell=list_sell.At(index_sell);
               //--- Закрываем позицию Buy встречной позицией Sell
               if(position_buy!=NULL && position_sell!=NULL)
                  engine.ClosePositionBy((ulong)position_buy.Ticket(),(ulong)position_sell.Ticket());
              }
           }
        }
        
      //--- Если нажата кнопка BUTT_CLOSE_SELL: Закрыть Sell с максимальной прибылью
      else if(button==EnumToString(BUTT_CLOSE_SELL))
        {
         //--- Получаем список всех открытых позиций
         CArrayObj* list=engine.GetListMarketPosition();
         //--- Выбираем из списка только позиции Sell и только по текущему символу
         list=CSelect::ByOrderProperty(list,ORDER_PROP_SYMBOL,Symbol(),EQUAL);
         list=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_SELL,EQUAL);
         //--- Сортируем список по прибыли с учётом комиссии и свопа
         list.Sort(SORT_BY_ORDER_PROFIT_FULL);
         //--- Получаем индекс позиции Sell с наибольшей прибылью
         int index=CSelect::FindOrderMax(list,ORDER_PROP_PROFIT_FULL);
         if(index>WRONG_VALUE)
           {
            //--- Получаем объект-позицию Sell и закрываем позицию по тикету
            COrder* position=list.At(index);
            if(position!=NULL)
               engine.ClosePosition((ulong)position.Ticket());
           }
        }
      //--- Если нажата кнопка BUTT_CLOSE_SELL2: Закрыть половину Sell с максимальной прибылью
      else if(button==EnumToString(BUTT_CLOSE_SELL2))
        {
         //--- Получаем список всех открытых позиций
         CArrayObj* list=engine.GetListMarketPosition();
         //--- Выбираем из списка только позиции Sell и только по текущему символу
         list=CSelect::ByOrderProperty(list,ORDER_PROP_SYMBOL,Symbol(),EQUAL);
         list=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_SELL,EQUAL);
         //--- Сортируем список по прибыли с учётом комиссии и свопа
         list.Sort(SORT_BY_ORDER_PROFIT_FULL);
         //--- Получаем индекс позиции Sell с наибольшей прибылью
         int index=CSelect::FindOrderMax(list,ORDER_PROP_PROFIT_FULL);
         if(index>WRONG_VALUE)
           {
            COrder* position=list.At(index);
            //--- Закрываем частично позицию Sell
            if(position!=NULL)
               engine.ClosePositionPartially((ulong)position.Ticket(),position.Volume()/2.0);
           }
        }
      //--- Если нажата кнопка BUTT_CLOSE_SELL_BY_BUY: Закрыть Sell с максимальной прибылью встречной Buy с максимальной прибылью
      else if(button==EnumToString(BUTT_CLOSE_SELL_BY_BUY))
        {
         //--- Если счёт хеджевый
         if(engine.IsHedge())
           {
            CArrayObj *list_buy=NULL, *list_sell=NULL;
            //--- Получаем список всех открытых позиций
            CArrayObj* list=engine.GetListMarketPosition();
            if(list==NULL)
               return;
            //--- Выбираем из списка только позиции по текущему символу
            list=CSelect::ByOrderProperty(list,ORDER_PROP_SYMBOL,Symbol(),EQUAL);
            
            //--- Выбираем из списка только позиции Sell
            list_sell=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_SELL,EQUAL);
            if(list_sell==NULL)
               return;
            //--- Сортируем список по прибыли с учётом комиссии и свопа
            list_sell.Sort(SORT_BY_ORDER_PROFIT_FULL);
            //--- Получаем индекс позиции Sell с наибольшей прибылью
            int index_sell=CSelect::FindOrderMax(list_sell,ORDER_PROP_PROFIT_FULL);
            
            //--- Выбираем из списка только позиции Buy
            list_buy=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_BUY,EQUAL);
            if(list_buy==NULL)
               return;
            //--- Сортируем список по прибыли с учётом комиссии и свопа
            list_buy.Sort(SORT_BY_ORDER_PROFIT_FULL);
            //--- Получаем индекс позиции Buy с наибольшей прибылью
            int index_buy=CSelect::FindOrderMax(list_buy,ORDER_PROP_PROFIT_FULL);
            if(index_sell>WRONG_VALUE && index_buy>WRONG_VALUE)
              {
               //--- Выбираем позицию Sell с наибольшей прибылью
               COrder* position_sell=list_sell.At(index_sell);
               //--- Выбираем позицию Buy с наибольшей прибылью
               COrder* position_buy=list_buy.At(index_buy);
               //--- Закрываем позицию Sell встречной позицией Buy
               if(position_sell!=NULL && position_buy!=NULL)
                  engine.ClosePositionBy((ulong)position_sell.Ticket(),(ulong)position_buy.Ticket());
              }
           }
        }
      //--- Если нажата кнопка BUTT_CLOSE_ALL: Закрыть все позиции, начиная от позиции с наименьшим профитом
      else if(button==EnumToString(BUTT_CLOSE_ALL))
        {
         //--- Получаем список всех открытых позиций
         CArrayObj* list=engine.GetListMarketPosition();
         //--- Выбираем из списка только позиции по текущему символу
         list=CSelect::ByOrderProperty(list,ORDER_PROP_SYMBOL,Symbol(),EQUAL);
         if(list!=NULL)
           {
            //--- Сортируем список по прибыли с учётом комиссии и свопа
            list.Sort(SORT_BY_ORDER_PROFIT_FULL);
            int total=list.Total();
            //--- В цикле от позиции с наименьшей прибылью
            for(int i=0;i<total;i++)
              {
               COrder* position=list.At(i);
               if(position==NULL)
                  continue;
               //--- закрываем каждую позицию по её тикету
               engine.ClosePosition((ulong)position.Ticket());
              }
           }
        }
      //--- Если нажата кнопка BUTT_DELETE_PENDING: Удалить отложенные ордера, начиная с самого раннего
      else if(button==EnumToString(BUTT_DELETE_PENDING))
        {
         //--- Получаем список всех ордеров
         CArrayObj* list=engine.GetListMarketPendings();
         //--- Выбираем из списка только ордера по текущему символу
         list=CSelect::ByOrderProperty(list,ORDER_PROP_SYMBOL,Symbol(),EQUAL);
         if(list!=NULL)
           {
            //--- Сортируем список по времени установки
            list.Sort(SORT_BY_ORDER_TIME_OPEN);
            int total=list.Total();
            //--- В цикле от ордера с наибольшим временем
            for(int i=total-1;i>=0;i--)
              {
               COrder* order=list.At(i);
               if(order==NULL)
                  continue;
               //--- удаяем ордер по его тикету
               engine.DeleteOrder((ulong)order.Ticket());
              }
           }
        }
      //--- Если нажата кнопка BUTT_PROFIT_WITHDRAWAL: Вывести средства со счёта
      if(button==EnumToString(BUTT_PROFIT_WITHDRAWAL))
        {
         //--- Если программа запущена в тестере
         if(MQLInfoInteger(MQL_TESTER))
           {
            //--- Эмулируем вывод средств
            TesterWithdrawal(withdrawal);
           }
        }
      //--- Если нажата кнопка BUTT_SET_STOP_LOSS: Установить StopLoss всем ордерам и позициям, где его нету
      if(button==EnumToString(BUTT_SET_STOP_LOSS))
        {
         SetStopLoss();
        }
      //--- Если нажата кнопка BUTT_SET_TAKE_PROFIT: Установить TakeProfit всем ордерам и позициям, где его нету
      if(button==EnumToString(BUTT_SET_TAKE_PROFIT))
        {
         SetTakeProfit();
        }
      //--- Подождём 1/10 секунды
      Sleep(100);
      //--- "Отожмём" кнопку (если это не кнопка трейлинга и не кнопки активации работы отложенными запросами)
      if(button!=EnumToString(BUTT_TRAILING_ALL) && StringFind(button,"_PRICE")<0 && StringFind(button,"_TIME")<0)
         ButtonState(button_name,false);
      //--- Если нажата кнопка BUTT_TRAILING_ALL или кнопки включения работы отложенными запросами
      else
        {
         //--- Поставим цвет активной кнопки для кнопки включения трейлинга
         if(button==EnumToString(BUTT_TRAILING_ALL))
           {
            ButtonState(button_name,true);
            trailing_on=true;
           }
         
         //--- Покупки
         //--- Поставим цвет активной кнопки для кнопки активации работы отложенными запросами для открытия Buy по цене или времени
         if(button==EnumToString(BUTT_BUY)+"_PRICE" || button==EnumToString(BUTT_BUY)+"_TIME")
           {
            ButtonState(button_name,true);
            pending_buy=true;
           }
         //--- Поставим цвет активной кнопки для кнопки активации работы отложенными запросами для установки BuyLimit по цене или времени
         if(button==EnumToString(BUTT_BUY_LIMIT)+"_PRICE" || button==EnumToString(BUTT_BUY_LIMIT)+"_TIME")
           {
            ButtonState(button_name,true);
            pending_buy_limit=true;
           }
         //--- Поставим цвет активной кнопки для кнопки активации работы отложенными запросами для установки BuyStop по цене или времени
         if(button==EnumToString(BUTT_BUY_STOP)+"_PRICE" || button==EnumToString(BUTT_BUY_STOP)+"_TIME")
           {
            ButtonState(button_name,true);
            pending_buy_stop=true;
           }
         //--- Поставим цвет активной кнопки для кнопки активации работы отложенными запросами для установки BuyStopLimit по цене или времени
         if(button==EnumToString(BUTT_BUY_STOP_LIMIT)+"_PRICE" || button==EnumToString(BUTT_BUY_STOP_LIMIT)+"_TIME")
           {
            ButtonState(button_name,true);
            pending_buy_stoplimit=true;
           }
         //--- Поставим цвет активной кнопки для кнопки активации работы отложенными запросами для закрытия Buy по цене или времени
         if(button==EnumToString(BUTT_CLOSE_BUY)+"_PRICE" || button==EnumToString(BUTT_CLOSE_BUY)+"_TIME")
           {
            ButtonState(button_name,true);
            pending_close_buy=true;
           }
         //--- Поставим цвет активной кнопки для кнопки активации работы отложенными запросами для закрытия 1/2 Buy по цене или времени
         if(button==EnumToString(BUTT_CLOSE_BUY2)+"_PRICE" || button==EnumToString(BUTT_CLOSE_BUY2)+"_TIME")
           {
            ButtonState(button_name,true);
            pending_close_buy2=true;
           }
         //--- Поставим цвет активной кнопки для кнопки активации работы отложенными запросами для закрытия Buy встречной Sell по цене или времени
         if(button==EnumToString(BUTT_CLOSE_BUY_BY_SELL)+"_PRICE" || button==EnumToString(BUTT_CLOSE_BUY_BY_SELL)+"_TIME")
           {
            ButtonState(button_name,true);
            pending_close_buy_by_sell=true;
           }
         
         //--- Продажи
         //--- Поставим цвет активной кнопки для кнопки активации работы отложенными запросами для открытия Sell по цене или времени
         if(button==EnumToString(BUTT_SELL)+"_PRICE" || button==EnumToString(BUTT_SELL)+"_TIME")
           {
            ButtonState(button_name,true);
            pending_sell=true;
           }
         //--- Поставим цвет активной кнопки для кнопки активации работы отложенными запросами для установки SellLimit по цене или времени
         if(button==EnumToString(BUTT_SELL_LIMIT)+"_PRICE" || button==EnumToString(BUTT_SELL_LIMIT)+"_TIME")
           {
            ButtonState(button_name,true);
            pending_sell_limit=true;
           }
         //--- Поставим цвет активной кнопки для кнопки активации работы отложенными запросами для установки SellStop по цене или времени
         if(button==EnumToString(BUTT_SELL_STOP)+"_PRICE" || button==EnumToString(BUTT_SELL_STOP)+"_TIME")
           {
            ButtonState(button_name,true);
            pending_sell_stop=true;

           }
         //--- Поставим цвет активной кнопки для кнопки активации работы отложенными запросами для установки SellStopLimit по цене или времени
         if(button==EnumToString(BUTT_SELL_STOP_LIMIT)+"_PRICE" || button==EnumToString(BUTT_SELL_STOP_LIMIT)+"_TIME")
           {
            ButtonState(button_name,true);
            pending_sell_stoplimit=true;
           }
         //--- Поставим цвет активной кнопки для кнопки активации работы отложенными запросами для закрытия Sell по цене или времени
         if(button==EnumToString(BUTT_CLOSE_SELL)+"_PRICE" || button==EnumToString(BUTT_CLOSE_SELL)+"_TIME")
           {
            ButtonState(button_name,true);
            pending_close_sell=true;
           }
         //--- Поставим цвет активной кнопки для кнопки активации работы отложенными запросами для закрытия 1/2 Sell по цене или времени
         if(button==EnumToString(BUTT_CLOSE_SELL2)+"_PRICE" || button==EnumToString(BUTT_CLOSE_SELL2)+"_TIME")
           {
            ButtonState(button_name,true);
            pending_close_sell2=true;
           }
         //--- Поставим цвет активной кнопки для кнопки активации работы отложенными запросами для закрытия Sell встречной Buy по цене или времени
         if(button==EnumToString(BUTT_CLOSE_SELL_BY_BUY)+"_PRICE" || button==EnumToString(BUTT_CLOSE_SELL_BY_BUY)+"_TIME")
           {
            ButtonState(button_name,true);
            pending_close_sell_by_buy=true;
           }
        }
      //--- перерисуем чарт
      ChartRedraw();
     }
   //--- Вернём цвет неактивным кнопкам
   else 
     {
      //--- кнопка трейлинга
      if(button==EnumToString(BUTT_TRAILING_ALL))
        {
         ButtonState(button_name,false);
         trailing_on=false;
        }
      
      //--- Покупки
      //--- кнопка активации работы отложенными запросами для открытия Buy по цене
      if(button==EnumToString(BUTT_BUY)+"_PRICE")
        {
         ButtonState(button_name,false);
         pending_buy=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_BUY)+"_TIME"));
        }
      //--- кнопка активации работы отложенными запросами для открытия Buy по времени
      if(button==EnumToString(BUTT_BUY)+"_TIME")
        {
         ButtonState(button_name,false);
         pending_buy=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_BUY)+"_PRICE"));
        }
      
      //--- кнопка активации работы отложенными запросами для установки BuyLimit по цене
      if(button==EnumToString(BUTT_BUY_LIMIT)+"_PRICE")
        {
         ButtonState(button_name,false);
         pending_buy_limit=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_BUY_LIMIT)+"_TIME"));
        }
      //--- кнопка активации работы отложенными запросами для установки BuyLimit по времени
      if(button==EnumToString(BUTT_BUY_LIMIT)+"_TIME")
        {
         ButtonState(button_name,false);
         pending_buy_limit=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_BUY_LIMIT)+"_PRICE"));
        }
      
      //--- кнопка активации работы отложенными запросами для установки BuyStop по цене
      if(button==EnumToString(BUTT_BUY_STOP)+"_PRICE")
        {
         ButtonState(button_name,false);
         pending_buy_stop=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_BUY_STOP)+"_TIME"));
        }
      //--- кнопка активации работы отложенными запросами для установки BuyStop по времени
      if(button==EnumToString(BUTT_BUY_STOP)+"_TIME")
        {
         ButtonState(button_name,false);
         pending_buy_stop=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_BUY_STOP)+"_PRICE"));
        }
      
      //--- кнопка активации работы отложенными запросами для установки BuyStopLimit по цене
      if(button==EnumToString(BUTT_BUY_STOP_LIMIT)+"_PRICE")
        {
         ButtonState(button_name,false);
         pending_buy_stoplimit=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_BUY_STOP_LIMIT)+"_TIME"));
        }
      //--- кнопка активации работы отложенными запросами для установки BuyStopLimit по времени
      if(button==EnumToString(BUTT_BUY_STOP_LIMIT)+"_TIME")
        {
         ButtonState(button_name,false);
         pending_buy_stoplimit=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_BUY_STOP_LIMIT)+"_PRICE"));
        }
      
      //--- кнопка активации работы отложенными запросами для закрытия Buy по цене
      if(button==EnumToString(BUTT_CLOSE_BUY)+"_PRICE")
        {
         ButtonState(button_name,false);
         pending_close_buy=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_BUY)+"_TIME"));
        }
      //--- кнопка активации работы отложенными запросами для закрытия Buy по времени
      if(button==EnumToString(BUTT_CLOSE_BUY)+"_TIME")
        {
         ButtonState(button_name,false);
         pending_close_buy=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_BUY)+"_PRICE"));
        }
      
      //--- кнопка активации работы отложенными запросами для закрытия 1/2 Buy по цене
      if(button==EnumToString(BUTT_CLOSE_BUY2)+"_PRICE")
        {
         ButtonState(button_name,false);
         pending_close_buy2=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_BUY2)+"_TIME"));
        }
      //--- кнопка активации работы отложенными запросами для закрытия 1/2 Buy по времени
      if(button==EnumToString(BUTT_CLOSE_BUY2)+"_TIME")
        {
         ButtonState(button_name,false);
         pending_close_buy2=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_BUY2)+"_PRICE"));
        }
      
      //--- кнопка активации работы отложенными запросами для закрытия Buy встречной Sell по цене
      if(button==EnumToString(BUTT_CLOSE_BUY_BY_SELL)+"_PRICE")
        {
         ButtonState(button_name,false);
         pending_close_buy_by_sell=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_BUY_BY_SELL)+"_TIME"));
        }
      //--- кнопка активации работы отложенными запросами для закрытия Buy встречной Sell по времени
      if(button==EnumToString(BUTT_CLOSE_BUY_BY_SELL)+"_TIME")
        {
         ButtonState(button_name,false);
         pending_close_buy_by_sell=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_BUY_BY_SELL)+"_PRICE"));
        }

      //--- Продажи
      //--- кнопка активации работы отложенными запросами для открытия Sell по цене
      if(button==EnumToString(BUTT_SELL)+"_PRICE")
        {
         ButtonState(button_name,false);
         pending_sell=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_SELL)+"_TIME"));
        }
      //--- кнопка активации работы отложенными запросами для открытия Sell по времени
      if(button==EnumToString(BUTT_SELL)+"_TIME")
        {
         ButtonState(button_name,false);
         pending_sell=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_SELL)+"_PRICE"));
        }
      
      //--- кнопка активации работы отложенными запросами для установки SellLimit по цене
      if(button==EnumToString(BUTT_SELL_LIMIT)+"_PRICE")
        {
         ButtonState(button_name,false);
         pending_sell_limit=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_SELL_LIMIT)+"_TIME"));
        }
      //--- кнопка активации работы отложенными запросами для установки SellLimit по времени
      if(button==EnumToString(BUTT_SELL_LIMIT)+"_TIME")
        {
         ButtonState(button_name,false);
         pending_sell_limit=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_SELL_LIMIT)+"_PRICE"));
        }
      
      //--- кнопка активации работы отложенными запросами для установки SellStop по цене
      if(button==EnumToString(BUTT_SELL_STOP)+"_PRICE")
        {
         ButtonState(button_name,false);
         pending_sell_stop=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_SELL_STOP)+"_TIME"));
        }
      //--- кнопка активации работы отложенными запросами для установки SellStop по времени
      if(button==EnumToString(BUTT_SELL_STOP)+"_TIME")
        {
         ButtonState(button_name,false);
         pending_sell_stop=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_SELL_STOP)+"_PRICE"));
        }
      
      //--- кнопка активации работы отложенными запросами для установки SellStopLimit по цене
      if(button==EnumToString(BUTT_SELL_STOP_LIMIT)+"_PRICE")
        {
         ButtonState(button_name,false);
         pending_sell_stoplimit=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_SELL_STOP_LIMIT)+"_TIME"));
        }
      //--- кнопка активации работы отложенными запросами для установки SellStopLimit по времени
      if(button==EnumToString(BUTT_SELL_STOP_LIMIT)+"_TIME")
        {
         ButtonState(button_name,false);
         pending_sell_stoplimit=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_SELL_STOP_LIMIT)+"_PRICE"));
        }
      
      //--- кнопка активации работы отложенными запросами для закрытия Sell по цене
      if(button==EnumToString(BUTT_CLOSE_SELL)+"_PRICE")
        {
         ButtonState(button_name,false);
         pending_close_sell=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_SELL)+"_TIME"));
        }
      //--- кнопка активации работы отложенными запросами для закрытия Sell по времени
      if(button==EnumToString(BUTT_CLOSE_SELL)+"_TIME")
        {
         ButtonState(button_name,false);
         pending_close_sell=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_SELL)+"_PRICE"));
        }
      
      //--- кнопка активации работы отложенными запросами для закрытия 1/2 Sell по цене
      if(button==EnumToString(BUTT_CLOSE_SELL2)+"_PRICE")
        {
         ButtonState(button_name,false);
         pending_close_sell2=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_SELL2)+"_TIME"));
        }
      //--- кнопка активации работы отложенными запросами для закрытия 1/2 Sell по времени
      if(button==EnumToString(BUTT_CLOSE_SELL2)+"_TIME")
        {
         ButtonState(button_name,false);
         pending_close_sell2=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_SELL2)+"_PRICE"));
        }
      
      //--- кнопка активации работы отложенными запросами для закрытия Sell встречной Buy по цене
      if(button==EnumToString(BUTT_CLOSE_SELL_BY_BUY)+"_PRICE")
        {
         ButtonState(button_name,false);
         pending_close_sell_by_buy=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_SELL_BY_BUY)+"_TIME"));
        }
      //--- кнопка активации работы отложенными запросами для закрытия Sell встречной Buy по времени
      if(button==EnumToString(BUTT_CLOSE_SELL_BY_BUY)+"_TIME")
        {
         ButtonState(button_name,false);
         pending_close_sell_by_buy=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_SELL_BY_BUY)+"_PRICE"));
        }
      //--- перерисуем чарт
      ChartRedraw();
     }
  }
//+------------------------------------------------------------------+

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

Скомпилируем советник. По умолчанию отступ цены для отложенного запроса равен 50 пунктов, а задержка в барах равна пяти барам. Оставим эти настройки без изменений и запустим советник в тестере стратегий.
Включим кнопки активации отложенных запросов на открытие позиции Buy по цене и времени, после чего дождёмся срабатывания отложенных запросов.
Затем включим кнопку активации отложенных запросов на открытие позиции Sell только по времени и так же дождёмся срабатывания отложенного запроса:


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

Что дальше

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

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

К содержанию

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

Часть 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. Работа с отложенными торговыми запросами - классы объектов-запросов
Часть 30. Работа с отложенными запросами - управление объектами-запросами