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

17 декабря 2019, 14:45
Artyom Trishkin
0
1 840

Содержание

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

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

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

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

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

   MSG_ORD_TIME_EXP,                                  // Дата экспирации
   MSG_ORD_TYPE_FILLING,                              // Тип исполнения по остатку
   MSG_ORD_TYPE_TIME,                                 // Время жизни ордера
   MSG_ORD_TYPE,                                      // Тип

...

   MSG_LIB_TEXT_PEND_REQUEST_CREATED,                 // Создан отложенный запрос
   MSG_LIB_TEXT_PEND_REQUEST_DELETED,                 // Отложенный запрос удалён в связи с окончанием времени его действия
   MSG_LIB_TEXT_PEND_REQUEST_EXECUTED,                // Отложенный запрос удалён в связи с его исполнением

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

   {"Дата экспирации","Date of expiration"},
   {"Тип исполнения по остатку","Order filling type"},
   {"Время жизни ордера","Order lifetime"},
   {"Тип","Type"},

...

   {"Создан отложенный запрос","Pending request created"},
   {"Отложенный запрос удалён в связи с окончанием времени его действия","Pending request deleted due to expiration"},
   {"Отложенный запрос удалён в связи с его исполнением","Pending request deleted due to execution"},

Так как мы добавляем ещё два свойства к объекту абстрактного ордера, то внесём изменения по части его свойств в файле Defines.mqh.

В блок целочисленных свойств ордера впишем два новых свойства и увеличим общее количество целочисленных свойств с 24 до 26:

//+------------------------------------------------------------------+
//| Целочисленные свойства ордера, сделки, позиции                   |
//+------------------------------------------------------------------+
enum ENUM_ORDER_PROP_INTEGER
  {
   ORDER_PROP_TICKET = 0,                                   // Тикет ордера
   ORDER_PROP_MAGIC,                                        // Реальный магик ордера
   ORDER_PROP_TIME_OPEN,                                    // Время открытия в милисекундах (MQL5 Время сделки)
   ORDER_PROP_TIME_CLOSE,                                   // Время закрытия в милисекундах (MQL5 Время исполнения или снятия - ORDER_TIME_DONE)
   ORDER_PROP_TIME_EXP,                                     // Дата экспирации ордера (для отложенных ордеров)
   ORDER_PROP_TYPE_FILLING,                                 // Тип исполнения по остатку
   ORDER_PROP_TYPE_TIME,                                    // Время жизни ордера
   ORDER_PROP_STATUS,                                       // Статус ордера (из перечисления ENUM_ORDER_STATUS)
   ORDER_PROP_TYPE,                                         // Тип ордера/сделки
   ORDER_PROP_REASON,                                       // Причина или источник сделки/ордера/позиции
   ORDER_PROP_STATE,                                        // Состояние ордера (из перечисления ENUM_ORDER_STATE)
   ORDER_PROP_POSITION_ID,                                  // Идентификатор позиции
   ORDER_PROP_POSITION_BY_ID,                               // Идентификатор встречной позиции
   ORDER_PROP_DEAL_ORDER_TICKET,                            // Тикет ордера, на основании которого выполнена сделка
   ORDER_PROP_DEAL_ENTRY,                                   // Направление сделки – IN, OUT или IN/OUT
   ORDER_PROP_TIME_UPDATE,                                  // Время изменения позиции  в милисекундах
   ORDER_PROP_TICKET_FROM,                                  // Тикет родительского ордера
   ORDER_PROP_TICKET_TO,                                    // Тикет дочернего ордера
   ORDER_PROP_PROFIT_PT,                                    // Профит в пунктах
   ORDER_PROP_CLOSE_BY_SL,                                  // Признак закрытия по StopLoss
   ORDER_PROP_CLOSE_BY_TP,                                  // Признак закрытия по TakeProfit
   ORDER_PROP_MAGIC_ID,                                     // Идентификатор "Магик" ордера
   ORDER_PROP_GROUP_ID1,                                    // Идентификатор 1 группы ордеров/позиций
   ORDER_PROP_GROUP_ID2,                                    // Идентификатор 2 группы ордеров/позиций
   ORDER_PROP_PEND_REQ_ID,                                  // Идентификатор отложенного запроса
   ORDER_PROP_DIRECTION,                                    // Тип по направлению (Buy, Sell)
  }; 
#define ORDER_PROP_INTEGER_TOTAL    (26)                    // Общее количество целочисленных свойств
#define ORDER_PROP_INTEGER_SKIP     (0)                     // Количество неиспользуемых в сортировке свойств ордера
//+------------------------------------------------------------------+

Добавим возможность сортировки ордеров по этим двум свойствам:

//+------------------------------------------------------------------+
//| Возможные критерии сортировки ордеров и сделок                   |
//+------------------------------------------------------------------+
#define FIRST_ORD_DBL_PROP          (ORDER_PROP_INTEGER_TOTAL-ORDER_PROP_INTEGER_SKIP)
#define FIRST_ORD_STR_PROP          (ORDER_PROP_INTEGER_TOTAL+ORDER_PROP_DOUBLE_TOTAL-ORDER_PROP_INTEGER_SKIP)
enum ENUM_SORT_ORDERS_MODE
  {
//--- Сортировка по целочисленным свойствам
   SORT_BY_ORDER_TICKET = 0,                                // Сортировать по тикету ордера
   SORT_BY_ORDER_MAGIC,                                     // Сортировать по магику ордера
   SORT_BY_ORDER_TIME_OPEN,                                 // Сортировать по времени открытия ордера в милисекундах
   SORT_BY_ORDER_TIME_CLOSE,                                // Сортировать по времени закрытия ордера в милисекундах
   SORT_BY_ORDER_TIME_EXP,                                  // Сортировать по дате экспирации ордера
   SORT_BY_ORDER_TYPE_FILLING,                              // Сортировать по типу исполнения по остатку
   SORT_BY_ORDER_TYPE_TIME,                                 // Сортировать по времени жизни ордера
   SORT_BY_ORDER_STATUS,                                    // Сортировать по статусу ордера (маркет-ордер/отложенный ордер/сделка/балансовая,кредитная операция)
   SORT_BY_ORDER_TYPE,                                      // Сортировать по типу ордера
   SORT_BY_ORDER_REASON,                                    // Сортировать по причине/источнику сделки/ордера/позиции
   SORT_BY_ORDER_STATE,                                     // Сортировать по состоянию ордера
   SORT_BY_ORDER_POSITION_ID,                               // Сортировать по идентификатору позиции
   SORT_BY_ORDER_POSITION_BY_ID,                            // Сортировать по идентификатору встречной позиции
   SORT_BY_ORDER_DEAL_ORDER,                                // Сортировать по ордеру, на основание которого выполнена сделка
   SORT_BY_ORDER_DEAL_ENTRY,                                // Сортировать по направлению сделки – IN, OUT или IN/OUT
   SORT_BY_ORDER_TIME_UPDATE,                               // Сортировать по времени изменения позиции в секундах
   SORT_BY_ORDER_TICKET_FROM,                               // Сортировать по тикету родительского ордера
   SORT_BY_ORDER_TICKET_TO,                                 // Сортировать по тикету дочернего ордера
   SORT_BY_ORDER_PROFIT_PT,                                 // Сортировать по профиту ордера в пунктах
   SORT_BY_ORDER_CLOSE_BY_SL,                               // Сортировать по признаку закрытия ордера по StopLoss
   SORT_BY_ORDER_CLOSE_BY_TP,                               // Сортировать по признаку закрытия ордера по TakeProfit
   SORT_BY_ORDER_MAGIC_ID,                                  // Сортировать по идентификатору "магик" ордера/позиции
   SORT_BY_ORDER_GROUP_ID1,                                 // Сортировать по идентификатору 1 группы ордеров/позиций
   SORT_BY_ORDER_GROUP_ID2,                                 // Сортировать по идентификатору 2 группы ордеров/позиций
   SORT_BY_ORDER_PEND_REQ_ID,                               // Сортировать по идентификатору отложенного запроса
   SORT_BY_ORDER_DIRECTION,                                 // Сортировать по направлению (Buy, Sell)
//--- Сортировка по вещественным свойствам
   SORT_BY_ORDER_PRICE_OPEN = FIRST_ORD_DBL_PROP,           // Сортировать по цене открытия
   SORT_BY_ORDER_PRICE_CLOSE,                               // Сортировать по цене закрытия
   SORT_BY_ORDER_SL,                                        // Сортировать по цене StopLoss
   SORT_BY_ORDER_TP,                                        // Сортировать по цене TaleProfit
   SORT_BY_ORDER_PROFIT,                                    // Сортировать по профиту
   SORT_BY_ORDER_COMMISSION,                                // Сортировать по комиссии
   SORT_BY_ORDER_SWAP,                                      // Сортировать по свопу
   SORT_BY_ORDER_VOLUME,                                    // Сортировать по объёму
   SORT_BY_ORDER_VOLUME_CURRENT,                            // Сортировать по невыполненному объему
   SORT_BY_ORDER_PROFIT_FULL,                               // Сортировать по критерию профит+комиссия+своп
   SORT_BY_ORDER_PRICE_STOP_LIMIT,                          // Сортировать по цене постановки Limit ордера при срабатывании StopLimit ордера
//--- Сортировка по строковым свойствам
   SORT_BY_ORDER_SYMBOL = FIRST_ORD_STR_PROP,               // Сортировать по символу
   SORT_BY_ORDER_COMMENT,                                   // Сортировать по комментарию
   SORT_BY_ORDER_COMMENT_EXT,                               // Сортировать по пользовательскому комментарию
   SORT_BY_ORDER_EXT_ID                                     // Сортировать по идентификатору ордера во внешней торговой системе
  };
//+------------------------------------------------------------------+

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

//+------------------------------------------------------------------+
//| Поведение эксперта при обработке ошибок                          |
//+------------------------------------------------------------------+
enum ENUM_ERROR_HANDLING_BEHAVIOR
  {
   ERROR_HANDLING_BEHAVIOR_BREAK,                           // Прервать торговую попытку
   ERROR_HANDLING_BEHAVIOR_CORRECT,                         // Скорректировать ошибочные параметры
   ERROR_HANDLING_BEHAVIOR_PENDING_REQUEST,                 // Создать отложенный запрос
  };
//+------------------------------------------------------------------+

Теперь это перечисление выглядит так:

//+------------------------------------------------------------------+
//| Поведение эксперта при обработке ошибок                          |
//+------------------------------------------------------------------+
enum ENUM_ERROR_HANDLING_BEHAVIOR
  {
   ERROR_HANDLING_BEHAVIOR_BREAK,                           // Прервать торговую попытку
   ERROR_HANDLING_BEHAVIOR_CORRECT,                         // Скорректировать ошибочные параметры
  };
//+------------------------------------------------------------------+

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

//+------------------------------------------------------------------+
//| Методы обработки ошибок и кодов возврата сервера                 |
//+------------------------------------------------------------------+
enum ENUM_ERROR_CODE_PROCESSING_METHOD
  {
   ERROR_CODE_PROCESSING_METHOD_OK,                         // Ошибки нет
   ERROR_CODE_PROCESSING_METHOD_DISABLE,                    // Запретить торговлю экспертом
   ERROR_CODE_PROCESSING_METHOD_EXIT,                       // Выйти из торгового метода
   ERROR_CODE_PROCESSING_METHOD_CORRECT,                    // Скорректировать параметры торгового запроса и повторить
   ERROR_CODE_PROCESSING_METHOD_REFRESH,                    // Обновить данные и повторить
   ERROR_CODE_PROCESSING_METHOD_PENDING,                    // Создать отложенный запрос
   ERROR_CODE_PROCESSING_METHOD_WAIT,                       // Подождать и повторить
  };
//+------------------------------------------------------------------+

Теперь это перечисление выглядит так:

//+------------------------------------------------------------------+
//| Методы обработки ошибок и кодов возврата сервера                 |
//+------------------------------------------------------------------+
enum ENUM_ERROR_CODE_PROCESSING_METHOD
  {
   ERROR_CODE_PROCESSING_METHOD_OK,                         // Ошибки нет
   ERROR_CODE_PROCESSING_METHOD_DISABLE,                    // Запретить торговлю экспертом
   ERROR_CODE_PROCESSING_METHOD_EXIT,                       // Выйти из торгового метода
   ERROR_CODE_PROCESSING_METHOD_CORRECT,                    // Скорректировать параметры торгового запроса и повторить
   ERROR_CODE_PROCESSING_METHOD_REFRESH,                    // Обновить данные и повторить
   ERROR_CODE_PROCESSING_METHOD_WAIT,                       // Подождать и повторить
  };
//+------------------------------------------------------------------+

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

//+------------------------------------------------------------------+
//| Возможные критерии сортировки отложенных запросов                |
//+------------------------------------------------------------------+
enum ENUM_SORT_PEND_REQ_MODE
  {
   SORT_BY_PEND_REQ_ID = 0,                                 // Сортировать по идентификатору
   SORT_BY_PEND_REQ_TYPE,                                   // Сортировать по типу
   SORT_BY_PEND_REQ_TICKET,                                 // Сортировать по тикету
  };
//+------------------------------------------------------------------+

Теперь непосредственно в классе абстрактного ордера COrder в файле Order.mqh впишем все необходимые изменения.

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

//+------------------------------------------------------------------+
//| Методы упрощённого доступа к свойствам объекта-ордера            |
//+------------------------------------------------------------------+
//--- Возвращает (1) тикет, (2) тикет родительского ордера, (3) тикет дочернего ордера, (4) магик, (5) причину выставления ордера,
//--- (6) идентификатор позиции, (7) идентификатор встречной позиции, (8) идентификатор первой группы, (9) идентификатор второй группы,
//--- (10) идентификатор отложенного запроса, (11) идентификатор магического номера, (12) тип, (13) флаг закрытия по StopLoss,
//--- (14) флаг закрытия по TakeProfit (15) время открытия, (16) время закрытия,
//--- (17) дату экспирации, (18) состояние, (19) статус (20) тип ордера по направлению, (21) тип исполнения по остатку, (22) время жизни ордера
   long              Ticket(void)                                       const { return this.GetProperty(ORDER_PROP_TICKET);                     }
   long              TicketFrom(void)                                   const { return this.GetProperty(ORDER_PROP_TICKET_FROM);                }
   long              TicketTo(void)                                     const { return this.GetProperty(ORDER_PROP_TICKET_TO);                  }
   long              Magic(void)                                        const { return this.GetProperty(ORDER_PROP_MAGIC);                      }
   long              Reason(void)                                       const { return this.GetProperty(ORDER_PROP_REASON);                     }
   long              PositionID(void)                                   const { return this.GetProperty(ORDER_PROP_POSITION_ID);                }
   long              PositionByID(void)                                 const { return this.GetProperty(ORDER_PROP_POSITION_BY_ID);             }
   long              MagicID(void)                                      const { return this.GetProperty(ORDER_PROP_MAGIC_ID);                   }
   long              GroupID1(void)                                     const { return this.GetProperty(ORDER_PROP_GROUP_ID1);                  }
   long              GroupID2(void)                                     const { return this.GetProperty(ORDER_PROP_GROUP_ID2);                  }
   long              PendReqID(void)                                    const { return this.GetProperty(ORDER_PROP_PEND_REQ_ID);                }
   long              TypeOrder(void)                                    const { return this.GetProperty(ORDER_PROP_TYPE);                       }
   bool              IsCloseByStopLoss(void)                            const { return (bool)this.GetProperty(ORDER_PROP_CLOSE_BY_SL);          }
   bool              IsCloseByTakeProfit(void)                          const { return (bool)this.GetProperty(ORDER_PROP_CLOSE_BY_TP);          }
   long              TimeOpen(void)                                     const { return this.GetProperty(ORDER_PROP_TIME_OPEN);                  }
   long              TimeClose(void)                                    const { return this.GetProperty(ORDER_PROP_TIME_CLOSE);                 }
   datetime          TimeExpiration(void)                               const { return (datetime)this.GetProperty(ORDER_PROP_TIME_EXP);         }
   ENUM_ORDER_STATE  State(void)                                        const { return (ENUM_ORDER_STATE)this.GetProperty(ORDER_PROP_STATE);    }
   ENUM_ORDER_STATUS Status(void)                                       const { return (ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS);  }
   ENUM_ORDER_TYPE   TypeByDirection(void)                              const { return (ENUM_ORDER_TYPE)this.GetProperty(ORDER_PROP_DIRECTION); }
   ENUM_ORDER_TYPE_FILLING TypeFilling(void)                            const { return (ENUM_ORDER_TYPE_FILLING)this.GetProperty(ORDER_PROP_TYPE_FILLING);  }
   ENUM_ORDER_TYPE_TIME TypeTime(void)                                  const { return (ENUM_ORDER_TYPE_TIME)this.GetProperty(ORDER_PROP_TYPE_TIME);        }

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

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

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

Здесь просто в соответствующие значения массива свойств ордера вписываются значения, возвращаемые методами получения этих свойств OrderTypeFilling() и OrderTypeTime().

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

//+------------------------------------------------------------------+
//| Возвращает описание целочисленного свойства ордера               |
//+------------------------------------------------------------------+
string COrder::GetPropertyDescription(ENUM_ORDER_PROP_INTEGER property)
  {
   return
     (
   //--- Общие свойства
      property==ORDER_PROP_MAGIC             ?  CMessage::Text(MSG_ORD_MAGIC)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==ORDER_PROP_TICKET            ?  CMessage::Text(MSG_ORD_TICKET)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          " #"+(string)this.GetProperty(property)
         )  :
      property==ORDER_PROP_TICKET_FROM       ?  CMessage::Text(MSG_ORD_TICKET_FROM)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          " #"+(string)this.GetProperty(property)
         )  :
      property==ORDER_PROP_TICKET_TO         ?  CMessage::Text(MSG_ORD_TICKET_TO)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          " #"+(string)this.GetProperty(property)
         )  :
      property==ORDER_PROP_TIME_EXP          ?  CMessage::Text(MSG_ORD_TIME_EXP)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          (this.GetProperty(property)==0     ?  CMessage::Text(MSG_LIB_PROP_NOT_SET)            :
          ": "+::TimeToString(this.GetProperty(property),TIME_DATE|TIME_MINUTES|TIME_SECONDS))
         )  :
      property==ORDER_PROP_TYPE_FILLING      ?  CMessage::Text(MSG_ORD_TYPE_FILLING)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+OrderTypeFillingDescription((ENUM_ORDER_TYPE_FILLING)this.GetProperty(property))
         )  :
      property==ORDER_PROP_TYPE_TIME         ?  CMessage::Text(MSG_ORD_TYPE_TIME)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+OrderTypeTimeDescription((ENUM_ORDER_TYPE_TIME)this.GetProperty(property))
         )  :
      property==ORDER_PROP_TYPE              ?  CMessage::Text(MSG_ORD_TYPE)+": "+this.TypeDescription()   :
      property==ORDER_PROP_DIRECTION         ?  CMessage::Text(MSG_ORD_TYPE_BY_DIRECTION)+": "+this.DirectionDescription() :
      
      property==ORDER_PROP_REASON            ?  CMessage::Text(MSG_ORD_REASON)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+this.GetReasonDescription(this.GetProperty(property))
         )  :
      property==ORDER_PROP_POSITION_ID       ?  CMessage::Text(MSG_ORD_POSITION_ID)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": #"+(string)this.GetProperty(property)
         )  :
      property==ORDER_PROP_DEAL_ORDER_TICKET ?  CMessage::Text(MSG_ORD_DEAL_ORDER_TICKET)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": #"+(string)this.GetProperty(property)
         )  :
      property==ORDER_PROP_DEAL_ENTRY        ?  CMessage::Text(MSG_ORD_DEAL_ENTRY)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+this.GetEntryDescription(this.GetProperty(property))
         )  :
      property==ORDER_PROP_POSITION_BY_ID    ?  CMessage::Text(MSG_ORD_POSITION_BY_ID)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==ORDER_PROP_TIME_OPEN         ?  CMessage::Text(MSG_ORD_TIME_OPEN)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+TimeMSCtoString(this.GetProperty(property))+" ("+(string)this.GetProperty(property)+")"
         )  :
      property==ORDER_PROP_TIME_CLOSE        ?  CMessage::Text(MSG_ORD_TIME_CLOSE)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+TimeMSCtoString(this.GetProperty(property))+" ("+(string)this.GetProperty(property)+")"
         )  :
      property==ORDER_PROP_TIME_UPDATE       ?  CMessage::Text(MSG_ORD_TIME_UPDATE)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.GetProperty(property)!=0 ? TimeMSCtoString(this.GetProperty(property))+" ("+(string)this.GetProperty(property)+")" : "0")
         )  :
      property==ORDER_PROP_STATE             ?  CMessage::Text(MSG_ORD_STATE)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": \""+this.StateDescription()+"\""
         )  :
   //--- Дополнительное свойство
      property==ORDER_PROP_STATUS            ?  CMessage::Text(MSG_ORD_STATUS)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": \""+this.StatusDescription()+"\""
         )  :
      property==ORDER_PROP_PROFIT_PT         ?  (
                                                 this.Status()==ORDER_STATUS_MARKET_PENDING ? 
                                                 CMessage::Text(MSG_ORD_DISTANCE_PT)  : 
                                                 CMessage::Text(MSG_ORD_PROFIT_PT)
                                                )+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==ORDER_PROP_CLOSE_BY_SL       ?  CMessage::Text(MSG_LIB_PROP_CLOSE_BY_SL)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.GetProperty(property)   ?  CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==ORDER_PROP_CLOSE_BY_TP       ?  CMessage::Text(MSG_LIB_PROP_CLOSE_BY_TP)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.GetProperty(property)   ?  CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==ORDER_PROP_MAGIC_ID          ?  CMessage::Text(MSG_ORD_MAGIC_ID)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==ORDER_PROP_GROUP_ID1         ?  CMessage::Text(MSG_ORD_GROUP_ID1)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==ORDER_PROP_GROUP_ID2         ?  CMessage::Text(MSG_ORD_GROUP_ID2)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==ORDER_PROP_PEND_REQ_ID       ?  CMessage::Text(MSG_ORD_PEND_REQ_ID)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      ""
     );
  }
//+------------------------------------------------------------------+

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

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

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

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

В файле класса торговых событий EventsCollection.mqh добавим публичный метод GetObject():

//+------------------------------------------------------------------+
//| Коллекция торговых событий счёта                                 |
//+------------------------------------------------------------------+
class CEventsCollection : public CBaseObj
  {
private:
   CListObj          m_list_trade_events;             // Список событий
   bool              m_is_hedge;                      // Флаг хедж-счёта
   int               m_trade_event_code;              // Код торгового события
   ENUM_TRADE_EVENT  m_trade_event;                   // Торговое событие на счёте
   CEvent            m_event_instance;                // Объект-событие для поиска по свойству
   ulong             m_position_id;                   // Идентификатор позиции (MQL4)
   ENUM_ORDER_TYPE   m_type_first;                    // Тип открывающего ордера (MQL4)
   
//--- Создаёт торговое событие в зависимости от (1) статуса и (2) типа изменения ордера
   void              CreateNewEvent(COrder* order,CArrayObj* list_history,CArrayObj* list_market,CArrayObj* list_control);
   void              CreateNewEvent(COrderControl* order);
//--- Создаёт событие для (1) хеджевого счёта, (2) неттингового счёта
   void              NewDealEventHedge(COrder* deal,CArrayObj* list_history,CArrayObj* list_market);
   void              NewDealEventNetto(COrder* deal,CArrayObj* list_history,CArrayObj* list_market);
//--- Выбирает из списка и возвращает список (1) рыночных отложенных ордеров, (2) открытых позиций
   CArrayObj*        GetListMarketPendings(CArrayObj* list);
   CArrayObj*        GetListPositions(CArrayObj* list);
//--- Выбирает из списка и возвращает список исторических (1) закрытых позиций,
//--- (2) удалённых отложенных ордеров, (3) сделок, (4) всех закрывающих ордеров 
   CArrayObj*        GetListHistoryPositions(CArrayObj* list);
   CArrayObj*        GetListHistoryPendings(CArrayObj* list);
   CArrayObj*        GetListDeals(CArrayObj* list);
   CArrayObj*        GetListCloseByOrders(CArrayObj* list);
//--- Возвращает список (1) всех ордеров позиции по её идентификатору, (2) всех сделок позиции по её идентификатору
//--- (3) всех сделок на вход в рынок по идентификатору позиции, (4) всех сделок на выход из рынка по идентификатору позиции,
//--- (5) всех сделок на разворот позиции по идентификатору позиции
   CArrayObj*        GetListAllOrdersByPosID(CArrayObj* list,const ulong position_id);
   CArrayObj*        GetListAllDealsByPosID(CArrayObj* list,const ulong position_id);
   CArrayObj*        GetListAllDealsInByPosID(CArrayObj* list,const ulong position_id);
   CArrayObj*        GetListAllDealsOutByPosID(CArrayObj* list,const ulong position_id);
   CArrayObj*        GetListAllDealsInOutByPosID(CArrayObj* list,const ulong position_id);
//--- Возвращает суммарный объём всех сделок (1) IN, (2) OUT позиции по её идентификатору
   double            SummaryVolumeDealsInByPosID(CArrayObj* list,const ulong position_id);
   double            SummaryVolumeDealsOutByPosID(CArrayObj* list,const ulong position_id);
//--- Возвращает (1) первый, (2) последний и (3) закрывающий ордер из списка всех ордеров позиции,
//--- (4) ордер по тикету, (5) рыночную позицию по идентификатору,
//--- (6) последнюю и (7) предпоследнюю сделку InOut по идентификатору позиции
   COrder*           GetFirstOrderFromList(CArrayObj* list,const ulong position_id);
   COrder*           GetLastOrderFromList(CArrayObj* list,const ulong position_id);
   COrder*           GetCloseByOrderFromList(CArrayObj* list,const ulong position_id);
   COrder*           GetHistoryOrderByTicket(CArrayObj* list,const ulong order_ticket);
   COrder*           GetPositionByID(CArrayObj* list,const ulong position_id);
//--- Возвращает (1) контрольный ордер по тикету, (2) тип открывающего ордера по тикету позиции (MQL4)
   COrderControl*    GetOrderControlByTicket(CArrayObj* list,const ulong ticket);
   ENUM_ORDER_TYPE   GetTypeFirst(CArrayObj* list,const ulong ticket);
//--- Возвращает флаг наличия объекта-события в списке событий
   bool              IsPresentEventInList(CEvent* compared_event);
//--- Обработчик события изменения существующего ордера/позиции
   void              OnChangeEvent(CArrayObj* list_changes,const int index);

public:
//--- Возвращает себя
   CEventsCollection*GetObject(void)                                                                     { return &this;                                                         }
//--- Выбирает события из коллекции со временем в диапазоне от begin_time до end_time
   CArrayObj        *GetListByTime(const datetime begin_time=0,const datetime end_time=0);
//--- Возвращает полный список-коллекцию событий "как есть"
   CArrayObj        *GetList(void)                                                                       { return &this.m_list_trade_events;                                     }
//--- Возвращает список по выбранному (1) целочисленному, (2) вещественному и (3) строковому свойству, удовлетворяющему сравниваемому критерию
   CArrayObj        *GetList(ENUM_EVENT_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode=EQUAL)  { return CSelect::ByEventProperty(this.GetList(),property,value,mode);  }
   CArrayObj        *GetList(ENUM_EVENT_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByEventProperty(this.GetList(),property,value,mode);  }
   CArrayObj        *GetList(ENUM_EVENT_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByEventProperty(this.GetList(),property,value,mode);  }
//--- Обновляет список событий
   void              Refresh(CArrayObj* list_history,
                             CArrayObj* list_market,
                             CArrayObj* list_changes,
                             CArrayObj* list_control,
                             const bool is_history_event,
                             const bool is_market_event,
                             const int  new_history_orders,
                             const int  new_market_pendings,
                             const int  new_market_positions,
                             const int  new_deals,
                             const double changed_volume);
//--- Возвращает (1) последнее торговое событие на счёте, (2) объект базового события по индексу, (3) количество новых событий
   ENUM_TRADE_EVENT  GetLastTradeEvent(void)                const { return this.m_trade_event;                 }
   CEventBaseObj    *GetTradeEventByIndex(const int index)        { return this.GetEvent(index,false);         }
   int               GetTradeEventsTotal(void)              const { return this.m_list_events.Total();         }
//--- Сбрасывает последнее торговое событие
   void              ResetLastTradeEvent(void)                    { this.m_trade_event=TRADE_EVENT_NO_EVENT;   }
//--- Конструктор
                     CEventsCollection(void);
  };
//+------------------------------------------------------------------+

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

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

Ранее код выглядел так:

//+------------------------------------------------------------------+
//| Открывает позицию                                                |
//+------------------------------------------------------------------+
bool CTradeObj::OpenPosition(const ENUM_POSITION_TYPE type,
                             const double volume,
                             const double sl=0,
                             const double tp=0,
                             const ulong magic=ULONG_MAX,
                             const string comment=NULL,
                             const ulong deviation=ULONG_MAX,
                             const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE)
  {
   ::ResetLastError();
   //--- Если не удалось получить текущие цены - записываем код ошибки, описание ошибки, выводим сообщение в журнал и возвращаем false
   if(!::SymbolInfoTick(this.m_symbol,this.m_tick))
     {
      this.m_result.retcode=::GetLastError();
      this.m_result.comment=CMessage::Text(this.m_result.retcode);
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_NOT_GET_PRICE),CMessage::Text(this.m_result.retcode));
      return false;
     }
   //--- Очищаем структуры
   ::ZeroMemory(this.m_request);
   ::ZeroMemory(this.m_result);
   //--- Заполняем структуру запроса
   this.m_request.action   =  TRADE_ACTION_DEAL;
   this.m_request.symbol   =  this.m_symbol;
   this.m_request.magic    =  (magic==ULONG_MAX ? this.m_magic : magic);
   this.m_request.type     =  (ENUM_ORDER_TYPE)type;
   this.m_request.price    =  (type==POSITION_TYPE_BUY ? this.m_tick.ask : this.m_tick.bid);
   this.m_request.volume   =  volume;
   this.m_request.sl       =  sl;
   this.m_request.tp       =  tp;
   this.m_request.deviation=  (deviation==ULONG_MAX ? this.m_deviation : deviation);
   this.m_request.comment  =  (comment==NULL ? this.m_comment : comment);
   //--- Возвращаем результат отсылки запроса на сервер
#ifdef __MQL5__
   return(!this.m_async_mode ? ::OrderSend(this.m_request,this.m_result) : ::OrderSendAsync(this.m_request,this.m_result));
#else 
   ::ResetLastError();
   int ticket=::OrderSend(m_request.symbol,m_request.type,m_request.volume,m_request.price,(int)m_request.deviation,m_request.sl,m_request.tp,m_request.comment,(int)m_request.magic,m_request.expiration,clrNONE);
   ::SymbolInfoTick(this.m_symbol,this.m_tick);
   if(ticket!=WRONG_VALUE)
     {
      this.m_result.retcode=::GetLastError();
      this.m_result.ask=this.m_tick.ask;
      this.m_result.bid=this.m_tick.bid;
      this.m_result.deal=ticket;
      this.m_result.price=(::OrderSelect(ticket,SELECT_BY_TICKET) ? ::OrderOpenPrice() : this.m_request.price);
      this.m_result.volume=(::OrderSelect(ticket,SELECT_BY_TICKET) ? ::OrderLots() : this.m_request.volume);
      this.m_result.comment=CMessage::Text(this.m_result.retcode);
      return true;
     }
   else
     {
      this.m_result.retcode=::GetLastError();
      this.m_result.ask=this.m_tick.ask;
      this.m_result.bid=this.m_tick.bid;
      this.m_result.comment=CMessage::Text(this.m_result.retcode);
      return false;
     }
#endif 
  }
//+------------------------------------------------------------------+

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

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

//+------------------------------------------------------------------+
//| Открывает позицию                                                |
//+------------------------------------------------------------------+
bool CTradeObj::OpenPosition(const ENUM_POSITION_TYPE type,
                             const double volume,
                             const double sl=0,
                             const double tp=0,
                             const ulong magic=ULONG_MAX,
                             const string comment=NULL,
                             const ulong deviation=ULONG_MAX,
                             const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE)
  {
   ::ResetLastError();
   //--- Если не удалось получить текущие цены - записываем код ошибки, описание ошибки, выводим сообщение в журнал и возвращаем false
   if(!::SymbolInfoTick(this.m_symbol,this.m_tick))
     {
      this.m_result.retcode=::GetLastError();
      this.m_result.comment=CMessage::Text(this.m_result.retcode);
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_NOT_GET_PRICE),CMessage::Text(this.m_result.retcode));
      return false;
     }
   //--- Очищаем структуры
   ::ZeroMemory(this.m_request);
   ::ZeroMemory(this.m_result);
   //--- Заполняем структуру запроса
   this.m_request.action   =  TRADE_ACTION_DEAL;
   this.m_request.symbol   =  this.m_symbol;
   this.m_request.magic    =  (magic==ULONG_MAX ? this.m_magic : magic);
   this.m_request.type     =  (ENUM_ORDER_TYPE)type;
   this.m_request.price    =  (type==POSITION_TYPE_BUY ? this.m_tick.ask : this.m_tick.bid);
   this.m_request.volume   =  volume;
   this.m_request.sl       =  sl;
   this.m_request.tp       =  tp;
   this.m_request.deviation=  (deviation==ULONG_MAX ? this.m_deviation : deviation);
   this.m_request.comment  =  (comment==NULL ? this.m_comment : comment);
   //--- Возвращаем результат отсылки запроса на сервер
#ifdef __MQL5__
   return(!this.m_async_mode ? ::OrderSend(this.m_request,this.m_result) : ::OrderSendAsync(this.m_request,this.m_result));
#else 
   ::ResetLastError();
   int ticket=::OrderSend(m_request.symbol,m_request.type,m_request.volume,m_request.price,(int)m_request.deviation,m_request.sl,m_request.tp,m_request.comment,(int)m_request.magic,m_request.expiration,clrNONE);
   this.m_result.retcode=::GetLastError();
   ::SymbolInfoTick(this.m_symbol,this.m_tick);
   this.m_result.ask=this.m_tick.ask;
   this.m_result.bid=this.m_tick.bid;
   this.m_result.comment=CMessage::Text(this.m_result.retcode);
   if(ticket!=WRONG_VALUE)
     {
      this.m_result.deal=ticket;
      this.m_result.price=(::OrderSelect(ticket,SELECT_BY_TICKET) ? ::OrderOpenPrice() : this.m_request.price);
      this.m_result.volume=(::OrderSelect(ticket,SELECT_BY_TICKET) ? ::OrderLots() : this.m_request.volume);
      return true;
     }
   else
     {
      return false;
     }
#endif 
  }
//+------------------------------------------------------------------+

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

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

Отложенные запросы на закрытие/удаление/модификацию позиций и отложенных ордеров


В файле основного торгового класса Trading.mqh первым делом подключим класс коллекции торговых событий счёта:

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

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

//+------------------------------------------------------------------+
//| Класс объекта-отложенного запроса                                |
//+------------------------------------------------------------------+
class CPendingReq : public CObject
  {
private:
   MqlTradeRequest      m_request;                       // Структура торгового запроса
   uchar                m_id;                            // Идентификатор торгового запроса
   int                  m_type;                          // Тип отложенного запроса
   int                  m_retcode;                       // Результат, на основании которого создан запрос
   double               m_price_create;                  // Цена при создании запроса
   ulong                m_time_create;                   // Время создания запроса
   ulong                m_time_activate;                 // Время активации очередной попытки
   ulong                m_waiting_msc;                   // Продолжительность ожидания между запросами
   uchar                m_current_attempt;               // Номер текущей попытки
   uchar                m_total_attempts;                // Количество попыток
//--- Копирует данные торгового запроса
   void                 CopyRequest(const MqlTradeRequest &request)  { this.m_request=request;        }
//--- Сравнивает объекты CPendingReq между собой по идентификаторам
   virtual int          Compare(const CObject *node,const int mode=0) const;

public:
//--- Возвращает (1) структуру запроса, (2) цену при создании запроса,
//--- (3) время создания запроса, (4) время активации очередной попытки,
//--- (5) продолжительность ожидания между запросами, (6) номер текущей попытки,
//--- (7) количество попыток, (8) идентификатор запроса
//--- (9) результат, на основании которого создан запрос,
//--- (10) тикет ордера, (11) тикет позиции, (12) тип торговой операции
   MqlTradeRequest      MqlRequest(void)                       const { return this.m_request;         }
   double               PriceCreate(void)                      const { return this.m_price_create;    }
   ulong                TimeCreate(void)                       const { return this.m_time_create;     }
   ulong                TimeActivate(void)                     const { return this.m_time_activate;   }
   ulong                WaitingMSC(void)                       const { return this.m_waiting_msc;     }
   uchar                CurrentAttempt(void)                   const { return this.m_current_attempt; }
   uchar                TotalAttempts(void)                    const { return this.m_total_attempts;  }
   uchar                ID(void)                               const { return this.m_id;              }
   int                  Retcode(void)                          const { return this.m_retcode;         }
   ulong                Order(void)                            const { return this.m_request.order;   }
   ulong                Position(void)                         const { return this.m_request.position;}
   ENUM_TRADE_REQUEST_ACTIONS Action(void)                     const { return this.m_request.action;  }

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

//--- Устанавливает (1) цену при создании запроса, (2) время создания запроса,
//--- (3) время текущей попытки, (4) продолжительность ожидания между запросами,
//--- (5) номер текущей попытки, (6) количество попыток, (7) идентификатор,
//--- (8) тикет ордера, (9) тикет позиции
   void                 SetPriceCreate(const double price)           { this.m_price_create=price;     }
   void                 SetTimeCreate(const ulong time)              { this.m_time_create=time;       }
   void                 SetTimeActivate(const ulong time)            { this.m_time_activate=time;     }
   void                 SetWaitingMSC(const ulong miliseconds)       { this.m_waiting_msc=miliseconds;}
   void                 SetCurrentAttempt(const uchar number)        { this.m_current_attempt=number; }
   void                 SetTotalAttempts(const uchar number)         { this.m_total_attempts=number;  }
   void                 SetID(const uchar id)                        { this.m_id=id;                  }
   void                 SetOrder(const ulong ticket)                 { this.m_request.order=ticket;   }
   void                 SetPosition(const ulong ticket)              { this.m_request.position=ticket;}
   

Метод Compare() для сравнения объектов класса CPendingReq между собой по заданным свойствам претерпел изменения.
Ранее он был таким:

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

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

//+------------------------------------------------------------------+
//| Сравнивает объекты CPendingReq между собой по свойствам          |
//+------------------------------------------------------------------+
int CPendingReq::Compare(const CObject *node,const int mode=0) const
  {
   const CPendingReq *compared_req=node;
   return
     (
      //--- Сравнение по идентификатору
      mode==SORT_BY_PEND_REQ_ID        ?  
      (this.ID()>compared_req.ID()     ? 1 : this.ID()<compared_req.ID() ? -1 : 0)     :
      //--- Сравнение по типу
      mode==SORT_BY_PEND_REQ_TYPE      ?  
      (this.Type()>compared_req.Type() ? 1 : this.Type()<compared_req.Type() ? -1 : 0) :
      //--- Сравнение по тикету
      (
       //--- модификация sl, tp позиции, открытие/закрытие позиции, или закрытие встречной
       this.m_request.action==TRADE_ACTION_SLTP    || this.m_request.action==TRADE_ACTION_DEAL     || this.m_request.action==TRADE_ACTION_CLOSE_BY ?
       (this.m_request.position>compared_req.m_request.position ? 1 : this.m_request.position<compared_req.m_request.position ? -1 : 0)            :
       //--- модификация параметров, установка/удаление отложенного ордера
       this.m_request.action==TRADE_ACTION_MODIFY  || this.m_request.action==TRADE_ACTION_PENDING  || this.m_request.action==TRADE_ACTION_REMOVE   ?
       (this.m_request.order>compared_req.m_request.order ? 1 : this.m_request.order<compared_req.m_request.order ? -1 : 0)                        : 
       //--- иначе
       0
      )
     );
  }
//+------------------------------------------------------------------+
  • Если режим сравнения, переданный в метод, равен значению константы SORT_BY_PEND_REQ_ID из перечисления ENUM_SORT_PEND_REQ_MODE, значит, сравнение производится по идентификатору объекта отложенного запроса;
  • Если режим сравнения равен SORT_BY_PEND_REQ_TYPE, значит, сравнение производится по типу объекта отложенного запроса;
  • Третий режим сравнения (SORT_BY_PEND_REQ_TICKET) производится следующим образом:
    - проверяем тип торговой операции, и
    • если это модификация стоп-приказов позиции, открытие, закрытие позиции, или закрытие встречным, то сравнение производится по значению полей position структуры торгового запроса — в них хранятся тикеты позиций;
    • если это модификация параметров, установка или удаление отложенного ордера, то сравнение производится по значению полей order структуры торгового запроса — в них хранятся тикеты ордеров;
    • иначе возвращается ноль.

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

//+------------------------------------------------------------------+
//| Торговый класс                                                   |
//+------------------------------------------------------------------+
class CTrading
  {
private:
   CAccount            *m_account;                       // Указатель на объект-текущий аккаунт
   CSymbolsCollection  *m_symbols;                       // Указатель на список коллекции символов
   CMarketCollection   *m_market;                        // Указатель на список коллекции рыночных ордеров и позиций
   CHistoryCollection  *m_history;                       // Указатель на список коллекции исторических ордеров и сделок
   CEventsCollection   *m_events;                        // Указатель на список коллекции событий
   CArrayObj            m_list_request;                  // Список отложенных запросов
   CArrayInt            m_list_errors;                   // Список ошибок
   bool                 m_is_trade_disable;              // Флаг запрета торговли
   bool                 m_use_sound;                     // Флаг использования звуков торговых событий объекта
   uchar                m_total_try;                     // Количество торговых попыток
   ENUM_LOG_LEVEL       m_log_level;                     // Уровень логирования
   MqlTradeRequest      m_request;                       // Цены торгового запроса
   ENUM_TRADE_REQUEST_ERR_FLAGS m_error_reason_flags;    // Флаги причин ошибок в торговом методе
   ENUM_ERROR_HANDLING_BEHAVIOR m_err_handling_behavior; // Поведение при обработке ошибок
   

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

//--- Возвращает индекс объекта-запроса в списке по (1) идентификатору,
//--- (2) тикету ордера, (3) тикету позиции в запросе
   int                  GetIndexPendingRequestByID(const uchar id);
   int                  GetIndexPendingRequestByOrder(const ulong ticket);
   int                  GetIndexPendingRequestByPosition(const ulong ticket);

public:

За пределами тела класса напишем их реализацию:

//+------------------------------------------------------------------+
//| Возвращает индекс объекта-запроса в списке по тикету ордера      |
//+------------------------------------------------------------------+
int CTrading::GetIndexPendingRequestByOrder(const ulong ticket)
  {
   CPendingReq *req=new CPendingReq();
   if(req==NULL)
      return WRONG_VALUE;
   req.SetOrder(ticket);
   this.m_list_request.Sort(SORT_BY_PEND_REQ_TICKET);
   int index=this.m_list_request.Search(req);
   delete req;
   return index;
  }
//+------------------------------------------------------------------+
//| Возвращает индекс объекта-запроса в списке по тикету позиции     |
//+------------------------------------------------------------------+
int CTrading::GetIndexPendingRequestByPosition(const ulong ticket)
  {
   CPendingReq *req=new CPendingReq();
   if(req==NULL)
      return WRONG_VALUE;
   req.SetPosition(ticket);
   this.m_list_request.Sort(SORT_BY_PEND_REQ_TICKET);
   int index=this.m_list_request.Search(req);
   delete req;
   return index;
  }
//+------------------------------------------------------------------+

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

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

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

public:
//--- Конструктор
                        CTrading();
//--- Таймер
   void                 OnTimer(void);
//--- Получение указателей на списки (вызывать метод обязательно в OnInit() программы, так как список коллекции символов создаётся там)
   void                 OnInit(CAccount *account,CSymbolsCollection *symbols,CMarketCollection *market,CHistoryCollection *history,CEventsCollection *events)
                          {
                           this.m_account=account;
                           this.m_symbols=symbols;
                           this.m_market=market;
                           this.m_history=history;
                           this.m_events=events;
                          }

В метод проверки ограничений и ошибок добавим передачу ещё одного параметра — тикета ордера или позиции:

//--- Проверка ограничений и ошибок
   ENUM_ERROR_CODE_PROCESSING_METHOD CheckErrors(const double volume,
                                                 const double price,
                                                 const ENUM_ACTION_TYPE action,
                                                 const ENUM_ORDER_TYPE order_type,
                                                 CSymbol *symbol_obj,
                                                 CTradeObj *trade_obj,
                                                 const string source_method,
                                                 const double limit=0,
                                                 double sl=0,
                                                 double tp=0,
                                                 ulong ticket=0);

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

Посмотрим реализацию дополненного метода:

//+------------------------------------------------------------------+
//| Проверка ограничений и ошибок                                    |
//+------------------------------------------------------------------+
ENUM_ERROR_CODE_PROCESSING_METHOD CTrading::CheckErrors(const double volume,
                                                        const double price,
                                                        const ENUM_ACTION_TYPE action,
                                                        const ENUM_ORDER_TYPE order_type,
                                                        CSymbol *symbol_obj,
                                                        CTradeObj *trade_obj,
                                                        const string source_method,
                                                        const double limit=0,
                                                        double sl=0,
                                                        double tp=0,
                                                        ulong ticket=0)
  {
//--- Проверка установленного ранее флага запрета торговли для эксперта
   if(this.IsTradingDisable())
     {
      this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_FATAL_ERROR;
      this.AddErrorCodeToList(MSG_LIB_TEXT_TRADING_DISABLE);
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(source_method,CMessage::Text(MSG_LIB_TEXT_TRADING_DISABLE));
      return ERROR_CODE_PROCESSING_METHOD_DISABLE;
     }
//--- результат проведения всех проверок и флаги ошибок
   this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_NO_ERROR;
   bool res=true;
//--- Очищаем список ошибок
   this.m_list_errors.Clear();
   this.m_list_errors.Sort();
//--- Проверка ограничений для торговли
   res &=this.CheckTradeConstraints(volume,action,symbol_obj,source_method,sl,tp);
//--- Проверка достаточности средств для открытия позиций/установки ордеров
   if(action<ACTION_TYPE_CLOSE_BY)
      res &=this.CheckMoneyFree(volume,price,order_type,symbol_obj,source_method);
//--- Проверка значений параметров по уровням StopLevel и FreezeLevel
   res &=this.CheckLevels(action,order_type,price,limit,sl,tp,symbol_obj,source_method);
   
//--- Если есть ограничения и ошибки, выводим заголовок и список ошибок
   if(!res)
     {
      //--- Объявляем индекс отложенного запроса
      int index=WRONG_VALUE;
      //--- Запрос отклонён до отправки на сервер по причине:
      int total=this.m_list_errors.Total();
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
        {
         //--- если торговая операция - закрытие встречным или открытие/закрытие позиции - индекс получаем по тикету позиции
         if(action==ACTION_TYPE_CLOSE_BY || action<ACTION_TYPE_BUY_LIMIT)
            index=this.GetIndexPendingRequestByPosition(ticket);
         //--- если торговая операция - закрытие позиции, удаление ордера или модификация ...
         else if(action>ACTION_TYPE_CLOSE_BY)
           {
            //--- если это позиция -  индекс получаем по тикету позиции
            if(order_type<ORDER_TYPE_BUY_LIMIT)
               index=this.GetIndexPendingRequestByPosition(ticket);
            //--- иначе - индекс получаем по тикету ордера
            else
               index=this.GetIndexPendingRequestByOrder(ticket);
           }
         //--- иначе, если торговая операция - установка отложенного ордера - индекс получаем по тикету ордера
         else index=this.GetIndexPendingRequestByOrder(ticket);
                                                            
         //--- если индекс не получен - нет такого объекта отложенного запроса - выводим сообщения об ошибках
         if(index==WRONG_VALUE)
           {
            //--- Для MQL5 сначала выводится заголовок списка, затем список ошибок
            #ifdef __MQL5__
            ::Print(source_method,CMessage::Text(this.m_err_handling_behavior==ERROR_HANDLING_BEHAVIOR_BREAK ? MSG_LIB_TEXT_REQUEST_REJECTED_DUE : MSG_LIB_TEXT_INVALID_REQUEST));
            for(int i=0;i<total;i++)
               ::Print((total>1 ? string(i+1)+". " : ""),CMessage::Text(m_list_errors.At(i)));
            //--- Для MQL4, так как в журнал выводится всё в обратном порядке, то сначала выводится список ошибок в обратном цикле, а затем заголовок списка
            #else    
            for(int i=total-1;i>WRONG_VALUE;i--)
               ::Print((total>1 ? string(i+1)+". " : ""),CMessage::Text(m_list_errors.At(i)));
            ::Print(source_method,CMessage::Text(this.m_err_handling_behavior==ERROR_HANDLING_BEHAVIOR_BREAK ? MSG_LIB_TEXT_REQUEST_REJECTED_DUE : MSG_LIB_TEXT_INVALID_REQUEST));
            #endif 
           }
        }
      //--- Если действие при ошибке "прервать торговую операцию"
      if(this.m_err_handling_behavior==ERROR_HANDLING_BEHAVIOR_BREAK)
         return ERROR_CODE_PROCESSING_METHOD_EXIT;
      //--- Если действие при ошибке "скорректировать параметры"
      if(this.m_err_handling_behavior==ERROR_HANDLING_BEHAVIOR_CORRECT)
        {
         if(this.m_log_level>LOG_LEVEL_NO_MSG && index==WRONG_VALUE)
            ::Print(CMessage::Text(MSG_LIB_TEXT_CORRECTED_TRADE_REQUEST));
         //--- Возвращаем результат попытки скорректировать параметры запроса
         return this.RequestErrorsCorrecting(this.m_request,order_type,trade_obj.SpreadMultiplier(),symbol_obj,trade_obj);
        }
     }
//--- Нет ограничений и ошибок
   trade_obj.SetResultRetcode(0);
   trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode()));
   return ERROR_CODE_PROCESSING_METHOD_OK;
  }
//+------------------------------------------------------------------+

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

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

//+------------------------------------------------------------------+
//| Таймер                                                           |
//+------------------------------------------------------------------+
void CTrading::OnTimer(void)
  {
   //--- В цикле по списку отложенных запросов
   int total=this.m_list_request.Total();
   for(int i=total-1;i>WRONG_VALUE;i--)
     {
      //--- получаем очередной объект-запрос
      CPendingReq *req_obj=this.m_list_request.At(i);
      if(req_obj==NULL)
         continue;
      
      //--- получаем структуру запроса и из неё объект-символ, на котором должна быть проведена торговая операция
      MqlTradeRequest request=req_obj.MqlRequest();
      CSymbol *symbol_obj=this.m_symbols.GetSymbolObjByName(request.symbol);
      if(symbol_obj==NULL || !symbol_obj.RefreshRates())
         continue;
      
      //--- если текущая попытка превысила установленное количество торговых попыток,
      //--- или текущее время больше времени ожидания всех попыток
      //--- удаляем текущий объект-запрос и переходим к следующему
      if(req_obj.CurrentAttempt()>req_obj.TotalAttempts() || req_obj.CurrentAttempt()>=UCHAR_MAX || 
         (long)symbol_obj.Time()>long(req_obj.TimeCreate()+req_obj.WaitingMSC()*req_obj.TotalAttempts()))
        {
         if(this.m_log_level>LOG_LEVEL_NO_MSG)
            ::Print(req_obj.IdDescription(),": ",CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_EXECUTED));
         this.m_list_request.Delete(i);
         continue;
        }
      
      //--- Если это открытие позиции или установка отложенного ордера
      if((req_obj.Action()==TRADE_ACTION_DEAL && req_obj.Position()==0) || req_obj.Action()==TRADE_ACTION_PENDING)
        {
         //--- Получаем идентификатор отложенного запроса
         uchar id=this.GetPendReqID((uint)request.magic);
         //--- Получаем список ордеров/позиций, содержащий ордер/позицию с таким идентификатором отложенного запроса
         CArrayObj *list=this.m_market.GetList(ORDER_PROP_PEND_REQ_ID,id,EQUAL);
         if(::CheckPointer(list)==POINTER_INVALID)
            continue;
         //--- Если есть такой ордер/позиция - запрос отработан: удаляем его и переходим к следующему
         if(list.Total()>0)
           {
            if(this.m_log_level>LOG_LEVEL_NO_MSG)
               ::Print(req_obj.IdDescription(),": ",CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_EXECUTED));
            this.m_list_request.Delete(i);
            continue;
           }
        }
      //--- Иначе: полное и частичное закрытие позиции, удаление ордера, модификация параметров ордера и стоп приказов позиции
      else
        {
         CArrayObj *list=NULL;
         //--- если это закрытие позиции, в том числе и встречным
         if((req_obj.Action()==TRADE_ACTION_DEAL && req_obj.Position()>0) || req_obj.Action()==TRADE_ACTION_CLOSE_BY)
           {
            //--- Получаем из списка открытых позиций позицию с нужным тикетом
            list=this.m_market.GetList(ORDER_PROP_TICKET,req_obj.Position(),EQUAL);
            if(::CheckPointer(list)==POINTER_INVALID)
               continue;
            //--- Если в рынке нет такой позиции - запрос отработан: удаляем его и переходим к следующему
            if(list.Total()==0)
              {
               if(this.m_log_level>LOG_LEVEL_NO_MSG)
                  ::Print(req_obj.IdDescription(),": ",CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_EXECUTED));
               this.m_list_request.Delete(i);
               continue;
              }
            //--- Иначе, если позиция ещё существует, то это частичное закрытие
            else
              {
               //--- Получаем список всех торговых событий аккаунта
               list=this.m_events.GetList();
               if(list==NULL)
                  continue;
               //--- В цикле с конца списка торговых событий аккаунта
               int events_total=list.Total();
               for(int j=events_total-1; j>WRONG_VALUE; j--)
                 {
                  //--- получаем очередное торговое событие
                  CEvent *event=list.At(j);
                  if(event==NULL)
                     continue;
                  //--- Если это событие - частичное закрытие, или при закрытии встречным было частичное закрытие
                  if(event.TypeEvent()==TRADE_EVENT_POSITION_CLOSED_PARTIAL || event.TypeEvent()==TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_POS)
                    {
                     //--- Если тикет позиции в торговом событии совпадает с тикетом в отложенном торговом запросе -
                     //--- запрос отработан: удаляем его и прерываем цикл по списку торговых событий аккаунта
                     if(event.TicketFirstOrderPosition()==req_obj.Position())
                       {
                        if(this.m_log_level>LOG_LEVEL_NO_MSG)
                           ::Print(req_obj.IdDescription(),": ",CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_EXECUTED));
                        this.m_list_request.Delete(i);
                        break;
                       }
                    }
                 }
               //--- Если в цикле по списку торговых событий был удалён отработанный объект-отложенный запрос - переходим к следующему
               if(::CheckPointer(req_obj)==POINTER_INVALID)
                  continue;
              }
           }
         //--- Если это модификация стоп-приказов позиции
         if(req_obj.Action()==TRADE_ACTION_SLTP)
           {
            //--- Получаем список всех торговых событий аккаунта
            list=this.m_events.GetList();
            if(list==NULL)
               continue;
            //--- В цикле с конца списка торговых событий аккаунта
            int events_total=list.Total();
            for(int j=events_total-1; j>WRONG_VALUE; j--)
              {
               //--- получаем очередное торговое событие
               CEvent *event=list.At(j);
               if(event==NULL)
                  continue;
               //--- Если это событие - любое изменение стоп-приказов позиции
               if(event.TypeEvent()>TRADE_EVENT_MODIFY_ORDER_TAKE_PROFIT)
                 {
                  //--- Если тикет позиции в торговом событии совпадает с тикетом в отложенном торговом запросе -
                  //--- запрос отработан: удаляем его и прерываем цикл по списку торговых событий аккаунта
                  if(event.TicketFirstOrderPosition()==req_obj.Position())
                    {
                     if(this.m_log_level>LOG_LEVEL_NO_MSG)
                        ::Print(req_obj.IdDescription(),": ",CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_EXECUTED));
                     this.m_list_request.Delete(i);
                     break;
                    }
                 }
              }
            //--- Если в цикле по списку торговых событий был удалён отработанный объект-отложенный запрос - переходим к следующему
            if(::CheckPointer(req_obj)==POINTER_INVALID)
               continue;
           }
         //--- Если это удаление отложенного ордера
         if(req_obj.Action()==TRADE_ACTION_REMOVE)
           {
            //--- Получаем список удалённых отложенных ордеров
            list=this.m_history.GetList(ORDER_PROP_STATUS,ORDER_STATUS_HISTORY_PENDING,EQUAL);
            if(::CheckPointer(list)==POINTER_INVALID)
               continue;
            //--- Оставляем в списке только один ордер с нужным тикетом
            list=CSelect::ByOrderProperty(list,ORDER_PROP_TICKET,req_obj.Order(),EQUAL);
            //--- Если есть такой ордер - запрос отработан: удаляем его и переходим к следующему
            if(list.Total()>0)
              {
               if(this.m_log_level>LOG_LEVEL_NO_MSG)
                  ::Print(req_obj.IdDescription(),": ",CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_EXECUTED));
               this.m_list_request.Delete(i);
               continue;
              }
           }
         //--- Если это модификация отложенного ордера
         if(req_obj.Action()==TRADE_ACTION_MODIFY)
           {
            //--- Получаем список всех торговых событий аккаунта
            list=this.m_events.GetList();
            if(list==NULL)
               continue;
            //--- В цикле с конца списка торговых событий аккаунта
            int events_total=list.Total();
            for(int j=events_total-1; j>WRONG_VALUE; j--)
              {
               //--- получаем очередное торговое событие
               CEvent *event=list.At(j);
               if(event==NULL)
                  continue;
               //--- Если это событие - любое изменение модифицируемых параметров отложенного ордера
               if(event.TypeEvent()>TRADE_EVENT_TRIGGERED_STOP_LIMIT_ORDER && event.TypeEvent()<TRADE_EVENT_MODIFY_POSITION_STOP_LOSS_TAKE_PROFIT)
                 {
                  //--- Если тикет ордера в торговом событии совпадает с тикетом в отложенном торговом запросе -
                  //--- запрос отработан: удаляем его и прерываем цикл по списку торговых событий аккаунта
                  if(event.TicketOrderEvent()==req_obj.Order())
                    {
                     if(this.m_log_level>LOG_LEVEL_NO_MSG)
                        ::Print(req_obj.IdDescription(),": ",CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_EXECUTED));
                     this.m_list_request.Delete(i);
                     break;
                    }
                 }
              }
           }
        }
      //--- Если после проверки отработки запроса объект-отложенный запрос удалён - выходим
      if(::CheckPointer(req_obj)==POINTER_INVALID)
         return;
      //--- Устанавливаем время активации запроса в объекте-запросе
      req_obj.SetTimeActivate(req_obj.TimeCreate()+req_obj.WaitingMSC()*(req_obj.CurrentAttempt()+1));
      
      //--- Если текущее время меньше времени активации запроса,
      //--- значит время запроса не наступило - идём к следующему запросу в списке
      if((long)symbol_obj.Time()<(long)req_obj.TimeActivate())
         continue;
      
      //--- Устанавливаем номер попытки в объекте-запросе
      req_obj.SetCurrentAttempt(uchar(req_obj.CurrentAttempt()+1));
      
      //--- Выводим в журнал номер торговой попытки
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(CMessage::Text(MSG_LIB_TEXT_RE_TRY_N)+(string)req_obj.CurrentAttempt());
      
      //--- В зависимости от типа выполняемого действия в торговом запросе 
      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 :
            Print(DFUN,RequestActionDescription(req_obj.MqlRequest()));
            this.DeleteOrder(request.order);
            break;
         //---
         default:
            break;
        }
     }
  }
//+------------------------------------------------------------------+

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

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

//+------------------------------------------------------------------+
//| Возвращает метод обработки ошибки                                |
//+------------------------------------------------------------------+
ENUM_ERROR_CODE_PROCESSING_METHOD CTrading::ResultProccessingMethod(const uint result_code)
  {
   switch(result_code)
     {
   #ifdef __MQL4__
      //--- Недопустимая операция, нарушающая функционирование сервера
      case 9   :
      //--- Счет заблокирован
      case 64  :
      //--- Неправильный номер счета
      case 65  :  return ERROR_CODE_PROCESSING_METHOD_DISABLE;
      
      //--- Нет ошибки, но результат неизвестен
      case 1   :
      //--- Общая ошибка
      case 2   :
      //--- Старая версия клиентского терминала
      case 5   :
      //--- Недостаточно прав
      case 7   :
      //--- Рынок закрыт
      case 132 :
      //--- Торговля запрещена
      case 133 :
      //--- Ордер заблокирован и уже обрабатывается
      case 139 :
      //--- Разрешена только покупка
      case 140 :
      //--- Количество открытых и отложенных ордеров достигло предела, установленного брокером
      case 148 :
      //--- Попытка открыть противоположный ордер в случае, если хеджирование запрещено
      case 149 :
      //--- Попытка закрыть позицию по инструменту в противоречии с правилом FIFO
      case 150 :  return ERROR_CODE_PROCESSING_METHOD_EXIT;
      
      //--- Неправильные параметры торгового запроса
      case 3   :
      //--- Неправильная цена
      case 129 :
      //--- Неправильные стопы
      case 130 :
      //--- Неправильный объем
      case 131 :
      //--- Недостаточно денег для совершения операции
      case 134 :
      //--- Использование даты истечения ордера запрещено брокером
      case 147 :  return ERROR_CODE_PROCESSING_METHOD_CORRECT;
      
      //--- Торговый сервер занят
      case 4   :  return (ENUM_ERROR_CODE_PROCESSING_METHOD)5000;    // ERROR_CODE_PROCESSING_METHOD_WAIT; Ждём 5 секунд
      //--- Нет связи с торговым сервером
      case 6   :  return (ENUM_ERROR_CODE_PROCESSING_METHOD)20000;   // ERROR_CODE_PROCESSING_METHOD_WAIT; Ждём 20 секунд
      //--- Слишком частые запросы
      case 8   :  return (ENUM_ERROR_CODE_PROCESSING_METHOD)10000;   // ERROR_CODE_PROCESSING_METHOD_WAIT; Ждём 10 секунд
      //--- Нет цен
      case 136 :  return (ENUM_ERROR_CODE_PROCESSING_METHOD)5000;    // ERROR_CODE_PROCESSING_METHOD_WAIT; Ждём 5 секунд
      //--- Брокер занят
      case 137 :  return (ENUM_ERROR_CODE_PROCESSING_METHOD)15000;   // ERROR_CODE_PROCESSING_METHOD_WAIT; Ждём 15 секунд
      //--- Слишком много запросов
      case 141 :  return (ENUM_ERROR_CODE_PROCESSING_METHOD)10000;   // ERROR_CODE_PROCESSING_METHOD_WAIT; Ждём 10 секунд
      //--- Модификация запрещена, так как ордер слишком близок к рынку
      case 145 :  return (ENUM_ERROR_CODE_PROCESSING_METHOD)10000;   // ERROR_CODE_PROCESSING_METHOD_WAIT; Ждём 10 секунд
      //--- Подсистема торговли занята
      case 146 :  return (ENUM_ERROR_CODE_PROCESSING_METHOD)5000;    // ERROR_CODE_PROCESSING_METHOD_WAIT; Ждём 5 секунд
      
      //--- Истек срок ожидания совершения сделки
      case 128 :
      //--- Цена изменилась
      case 135 :
      //--- Новые цены
      case 138 :  return ERROR_CODE_PROCESSING_METHOD_REFRESH;

   //--- MQL5
   #else
      //--- Автотрейдинг запрещен сервером
      case 10026  :  return ERROR_CODE_PROCESSING_METHOD_DISABLE;
      
      //--- Запрос отменен трейдером
      case 10007  :
      //--- Ошибка обработки запроса
      case 10011  :
      //--- Запрос отменен по истечению времени
      case 10012  :
      //--- Торговля запрещена
      case 10017 :
      //--- Рынок закрыт
      case 10018  :
      //--- Состояние ордера изменилось
      case 10023  :
      //--- В запросе нет изменений
      case 10025  :
      //--- Запрос заблокирован для обработки
      case 10028  :
      //--- Операция разрешена только для реальных счетов
      case 10032  :
      //--- Достигнут лимит на количество отложенных ордеров
      case 10033  :
      //--- Достигнут лимит на объем ордеров и позиций для данного символа
      case 10034  :
      //--- Неверный или запрещённый тип ордера
      case 10035  :
      //--- Позиция с указанным идентификатором уже закрыта
      case 10036  :
      //--- Для указанной позиции уже есть ордер на закрытие
      case 10039  :
      //--- Достигнут лимит на количество открытых позиций
      case 10040  :
      //--- Запрос на активацию отложенного ордера отклонен, а сам ордер отменен
      case 10041  :
      //--- Запрос отклонен, так как на символе установлено правило "Разрешены только длинные позиции"
      case 10042  :
      //--- Запрос отклонен, так как на символе установлено правило "Разрешены только короткие позиции"
      case 10043  :
      //--- Запрос отклонен, так как на символе установлено правило "Разрешено только закрывать существующие позиции"
      case 10044  :
      //--- Запрос отклонен, так как для торгового счета установлено правило "Разрешено закрывать существующие позиции только по правилу FIFO"
      case 10045  :  return ERROR_CODE_PROCESSING_METHOD_EXIT;

      //--- Реквота
      case 10004  :
      //--- Цены изменились
      case 10020  :  return ERROR_CODE_PROCESSING_METHOD_REFRESH;

      //--- Неправильный запрос
      case 10013  :
      //--- Неправильный объем в запросе
      case 10014  :
      //--- Неправильная цена в запросе
      case 10015  :
      //--- Неправильные стопы в запросе
      case 10016  :
      //--- Нет достаточных денежных средств для выполнения запроса
      case 10019  :
      //--- Неверная дата истечения ордера в запросе
      case 10022  :
      //--- Указан неподдерживаемый тип исполнения ордера по остатку
      case 10030  :
      //--- Закрываемый объем превышает текущий объем позиции
      case 10038  :  return ERROR_CODE_PROCESSING_METHOD_CORRECT;

      //--- Запрос отклонен
      case 10006  :  return (ENUM_ERROR_CODE_PROCESSING_METHOD)15000;   // ERROR_CODE_PROCESSING_METHOD_WAIT; Ждём 15 секунд
      //--- Отсутствуют котировки для обработки запроса
      case 10021  :  return (ENUM_ERROR_CODE_PROCESSING_METHOD)5000;    // ERROR_CODE_PROCESSING_METHOD_WAIT; Ждём 5 секунд
      //--- Слишком частые запросы
      case 10024  :  return (ENUM_ERROR_CODE_PROCESSING_METHOD)10000;   // ERROR_CODE_PROCESSING_METHOD_WAIT; Ждём 10 секунд
      //--- Автотрейдинг запрещен клиентским терминалом
      case 10027  :  return (ENUM_ERROR_CODE_PROCESSING_METHOD)20000;   // ERROR_CODE_PROCESSING_METHOD_WAIT; Ждём 20 секунд
      //--- Ордер или позиция заморожены
      case 10029  :  return (ENUM_ERROR_CODE_PROCESSING_METHOD)10000;   // ERROR_CODE_PROCESSING_METHOD_WAIT; Ждём 10 секунд
      //--- Нет соединения с торговым сервером
      case 10031  :  return (ENUM_ERROR_CODE_PROCESSING_METHOD)20000;   // ERROR_CODE_PROCESSING_METHOD_WAIT; Ждём 20 секунд

      //--- Ордер размещен
      case 10008  :
      //--- Заявка выполнена
      case 10009  :
      //--- Заявка выполнена частично
      case 10010  :
   #endif 
      //--- "OK"
      default:
        break;
     }
   return ERROR_CODE_PROCESSING_METHOD_OK;
  }
//+------------------------------------------------------------------+

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

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

//+------------------------------------------------------------------+
//| Корректировка ошибок                                             |
//+------------------------------------------------------------------+
ENUM_ERROR_CODE_PROCESSING_METHOD CTrading::RequestErrorsCorrecting(MqlTradeRequest &request,
                                                                    const ENUM_ORDER_TYPE order_type,
                                                                    const uint spread_multiplier,
                                                                    CSymbol *symbol_obj,
                                                                    CTradeObj *trade_obj)
  {
//--- Если список ошибок пуст - нет ошибок, возвращаем успешность
   int total=this.m_list_errors.Total();
   if(total==0)
      return ERROR_CODE_PROCESSING_METHOD_OK;

//--- Для текущего счёта запрещена торговля
//--- записываем в объект базового торгового класса код ошибки и возвращаем "выход из торгового метода"
   if(this.IsPresentErorCode(MSG_LIB_TEXT_ACCOUNT_NOT_TRADE_ENABLED))
     {
      trade_obj.SetResultRetcode(MSG_LIB_TEXT_ACCOUNT_NOT_TRADE_ENABLED);
      trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode()));
      return ERROR_CODE_PROCESSING_METHOD_EXIT;
     }
//--- Для советников на текущем счёте запрещена торговля на стороне торгового сервера
//--- записываем в объект базового торгового класса код ошибки и возвращаем "выход из торгового метода"
   if(this.IsPresentErorCode(MSG_LIB_TEXT_ACCOUNT_EA_NOT_TRADE_ENABLED))
     {
      trade_obj.SetResultRetcode(MSG_LIB_TEXT_ACCOUNT_EA_NOT_TRADE_ENABLED);
      trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode()));
      return ERROR_CODE_PROCESSING_METHOD_EXIT;
     }
//--- В терминале нет разрешения на проведение торговых операций
//--- записываем в объект базового торгового класса код ошибки и возвращаем "выход из торгового метода"
   if(this.IsPresentErorCode(MSG_LIB_TEXT_TERMINAL_NOT_TRADE_ENABLED))
     {
      trade_obj.SetResultRetcode(MSG_LIB_TEXT_TERMINAL_NOT_TRADE_ENABLED);
      trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode()));
      return ERROR_CODE_PROCESSING_METHOD_EXIT;
     }
//--- Для советника нет разрешения на проведение торговых пераций
//--- записываем в объект базового торгового класса код ошибки и возвращаем "выход из торгового метода"
   if(this.IsPresentErorCode(MSG_LIB_TEXT_EA_NOT_TRADE_ENABLED))
     {
      trade_obj.SetResultRetcode(MSG_LIB_TEXT_EA_NOT_TRADE_ENABLED);
      trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode()));
      return ERROR_CODE_PROCESSING_METHOD_EXIT;
     }
//--- Торговля по символу запрещена

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

//--- Просмотрим список оставшихся ошибок и скорректируем параметры торгового запроса
   for(int i=0;i<total;i++)
     {
      int err=this.m_list_errors.At(i);
      if(err==NULL)
         continue;
      switch(err)
        {
         //--- Неправильный объём и запрет стоп-уровней скорректируем в торговом запросе
         case MSG_LIB_TEXT_REQ_VOL_LESS_MIN_VOLUME    :
         case MSG_LIB_TEXT_REQ_VOL_MORE_MAX_VOLUME    :
         case MSG_LIB_TEXT_INVALID_VOLUME_STEP        :  request.volume=symbol_obj.NormalizedLot(request.volume);                      break;
         case MSG_SYM_SL_ORDER_DISABLED               :  request.sl=0;                                                                 break;
         case MSG_SYM_TP_ORDER_DISABLED               :  request.tp=0;                                                                 break;
         
         //--- Если не получится подобрать допустимый лот позиции, то вернём "прервать торговую попытку", так как денег нет даже на минимальный лот
         case MSG_LIB_TEXT_NOT_ENOUTH_MONEY_FOR       :  request.volume=this.CorrectVolume(request.price,order_type,symbol_obj,DFUN);
                                                         if(request.volume==0)
                                                           {
                                                            trade_obj.SetResultRetcode(MSG_LIB_TEXT_NOT_POSSIBILITY_CORRECT_LOT);
                                                            trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode()));
                                                            return ERROR_CODE_PROCESSING_METHOD_EXIT;                                                                                      break;
                                                           }
         //--- Отсутствуют котировки для обработки запроса
         case 10021                                   :  trade_obj.SetResultRetcode(10021);
                                                         trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode()));
                                                         return (ENUM_ERROR_CODE_PROCESSING_METHOD)5000;    // ERROR_CODE_PROCESSING_METHOD_WAIT - ждём 5 секунд
         //--- Нет соединения с торговым сервером
         case 10031                                   :  trade_obj.SetResultRetcode(10031);
                                                         trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode()));
                                                         return (ENUM_ERROR_CODE_PROCESSING_METHOD)20000;   // ERROR_CODE_PROCESSING_METHOD_WAIT - ждём 20 секунд
                                                      
         //--- Близость к уровню срабатывания ордера обрабатываем пятисекундным ожиданием - цена может выйти из зоны заморозки за это время
         case MSG_LIB_TEXT_SL_LESS_FREEZE_LEVEL       :
         case MSG_LIB_TEXT_TP_LESS_FREEZE_LEVEL       :
         case MSG_LIB_TEXT_PR_LESS_FREEZE_LEVEL       :  return (ENUM_ERROR_CODE_PROCESSING_METHOD)5000;    // ERROR_CODE_PROCESSING_METHOD_WAIT - ждём 5 секунд
        
         //--- В терминале нет разрешения на проведение торговых операций
         case MSG_LIB_TEXT_TERMINAL_NOT_TRADE_ENABLED :
         //--- Для советника нет разрешения на проведение торговых пераций
         case MSG_LIB_TEXT_EA_NOT_TRADE_ENABLED       :  return (ENUM_ERROR_CODE_PROCESSING_METHOD)20000;   // ERROR_CODE_PROCESSING_METHOD_WAIT - ждём 20 секунд
        
         default:
           break;
        }
     }
//--- Ошибок нет - возвращаем ОК
   trade_obj.SetResultRetcode(0);
   trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode()));
   return ERROR_CODE_PROCESSING_METHOD_OK;
  }
//+------------------------------------------------------------------+

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

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

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

//+------------------------------------------------------------------+
//| Открывает позицию                                                |
//+------------------------------------------------------------------+
template<typename SL,typename TP> 
bool CTrading::OpenPosition(const ENUM_POSITION_TYPE type,
                            const double volume,
                            const string symbol,
                            const ulong magic=ULONG_MAX,
                            const SL sl=0,
                            const TP tp=0,
                            const string comment=NULL,
                            const ulong deviation=ULONG_MAX,
                            const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE)
  {
//--- Устанавливаем результат торгового запроса как 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;
     }

//--- Записываем объём, отклонение, комментарий и тип заливки в структуру запроса
   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());
//--- Получаем метод обработки ошибок из метода CheckErrors(), одновременно проверяя ошибки в параметрах запроса
   double pr=(type==POSITION_TYPE_BUY ? symbol_obj.AskLast() : symbol_obj.BidLast());
   ENUM_ERROR_CODE_PROCESSING_METHOD method=this.CheckErrors(this.m_request.volume,pr,action,order_type,symbol_obj,trade_obj,DFUN,0,this.m_request.sl,this.m_request.tp);
//--- Если есть ограничения по разрешённости торговли, не хватает средств,
//--- есть ограничения по уровням StopLevel или FreezeLevel ...
   if(method!=ERROR_CODE_PROCESSING_METHOD_OK)
     {
      //--- Если полный запрет торговли - устанавливаем код ошибки в структуру возврата,
      //--- выводим сообщение в журнал, воспроизводим звук ошибки и уходим
      if(method==ERROR_CODE_PROCESSING_METHOD_DISABLE)
        {
         trade_obj.SetResultRetcode(MSG_LIB_TEXT_TRADING_DISABLE);
         trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode()));
         if(this.m_log_level>LOG_LEVEL_NO_MSG)
            ::Print(CMessage::Text(MSG_LIB_TEXT_TRADING_DISABLE));
         if(this.IsUseSounds())
            trade_obj.PlaySoundError(action,order_type);
         return false;
        }
      //--- Если результат проверки "прервать торговую операцию" - устанавливаем код последней ошибки в структуру возврата,
      //--- выводим сообщение в журнал, воспроизводим звук ошибки и уходим
      if(method==ERROR_CODE_PROCESSING_METHOD_EXIT)
        {
         int code=this.m_list_errors.At(this.m_list_errors.Total()-1);
         if(code!=NULL)
           {
            trade_obj.SetResultRetcode(code);
            trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode()));
           }
         if(this.m_log_level>LOG_LEVEL_NO_MSG)
            ::Print(CMessage::Text(MSG_LIB_TEXT_TRADING_OPERATION_ABORTED));
         if(this.IsUseSounds())
            trade_obj.PlaySoundError(action,order_type);
         return false;
        }
      //--- Если результат проверки "ожидание" - устанавливаем код последней ошибки в структуру возврата,
      //--- создаём отложенный запрос и возвращаем false
      if(method>ERROR_CODE_PROCESSING_METHOD_REFRESH)
        {
         //--- Если в магике торгового запроса нет идентификатора отложенного запроса
         if(this.GetPendReqID((uint)magic)==0)
           {
            //--- Проиграем звук ошибки
            if(this.IsUseSounds())
               trade_obj.PlaySoundError(action,order_type);
            //--- устанавливаем код последней ошибки в структуру возврата
            int code=this.m_list_errors.At(this.m_list_errors.Total()-1);
            if(code!=NULL)
              {
               if(code==MSG_LIB_TEXT_TERMINAL_NOT_TRADE_ENABLED || code==MSG_LIB_TEXT_EA_NOT_TRADE_ENABLED)
                  code=10027;
               trade_obj.SetResultRetcode(code);
               trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode()));
              }
            //--- Время ожидания в милисекундах:
            //--- для метода обработки "Подождать и повторить" - значение ожидания соответствует значению method,
            ulong wait=method;
            //--- Ищем наименьший из возможных идентификаторов, и если найти не удалось,
            //--- или произошла ошибка обновления текущих данных символа - возвращаем false
            int id=this.GetFreeID();
            if(id<1 || !symbol_obj.RefreshRates())
               return false;
            //--- Записываем в магик идентификатор объекта-отложенного запроса и заполняем остальные незаполненные поля структуры торгового запроса
            uint mn=(magic==ULONG_MAX ? (uint)trade_obj.GetMagic() : (uint)magic);
            this.SetPendReqID((uchar)id,mn);
            this.m_request.magic=mn;
            this.m_request.action=TRADE_ACTION_DEAL;
            this.m_request.symbol=symbol_obj.Name();
            this.m_request.type=order_type;
            //--- Устанавливаем количество торговых попыток и создаём отложенный запрос
            uchar attempts=(this.m_total_try < 1 ? 1 : this.m_total_try);
            this.CreatePendingRequest((uchar)id,attempts,wait,this.m_request,trade_obj.GetResultRetcode(),symbol_obj);
           }
         return false;
        }
     }
   
//--- В цикле по количеству попыток
   for(int i=0;i<this.m_total_try;i++)
     {                
      //--- Отсылаем запрос
      res=trade_obj.OpenPosition(type,this.m_request.volume,this.m_request.sl,this.m_request.tp,magic,comment,deviation,type_filling);
      //--- Если запрос выполнен успешно или асинхронный режим отправки ордеров - проиграем звук успеха,
      //--- установленный торговому объекту символа для данного типа торговой операции и вернём true
      if(res || trade_obj.IsAsyncMode())
        {
         if(this.IsUseSounds())
            trade_obj.PlaySoundSuccess(action,order_type);
         return true;
        }
      //--- Если запрос не выполнен - выведем сообщение и проиграем звук ошибки, установленный торговому объекту символа для данного типа торговой операции
      else
        {
         if(this.m_log_level>LOG_LEVEL_NO_MSG)
            ::Print(CMessage::Text(MSG_LIB_TEXT_TRY_N),string(i+1),". ",CMessage::Text(MSG_LIB_SYS_ERROR),": ",CMessage::Text(trade_obj.GetResultRetcode()));
         if(this.IsUseSounds())
            trade_obj.PlaySoundError(action,order_type);
         
         //--- Получаем метод обработки ошибки
         method=this.ResultProccessingMethod(trade_obj.GetResultRetcode());
         //--- Если в результате отправки запроса получили "Запретить торговлю экспертом" - взводим флаг запрета и прерываем цикл попыток
         if(method==ERROR_CODE_PROCESSING_METHOD_DISABLE)
           {
            this.SetTradingDisableFlag(true);
            break;
           }
         //--- Если в результате отправки запроса получили "Выйти из торгового метода" - прерываем цикл попыток
         if(method==ERROR_CODE_PROCESSING_METHOD_EXIT)
           {
            break;
           }
         //--- Если в результате отправки запроса получили "Скорректировать параметры и повторить" -
         //--- корректируем параметры и идём на следующую итерацию
         if(method==ERROR_CODE_PROCESSING_METHOD_CORRECT)
           {
            this.RequestErrorsCorrecting(this.m_request,order_type,trade_obj.SpreadMultiplier(),symbol_obj,trade_obj);
            continue;
           }
         //--- Если в результате отправки запроса получили "Обновить данные и повторить" -
         //--- обновляем данные и идём на следующую итерацию
         if(method==ERROR_CODE_PROCESSING_METHOD_REFRESH)
           {
            symbol_obj.Refresh();
            continue;
           }
         //--- Если в результате отправки запроса получили "Подождать и повторить" - создаём отложенный запрос и завершаем цикл
         if(method>ERROR_CODE_PROCESSING_METHOD_REFRESH)
           {
            //--- Если в магике торгового запроса нет идентификатора отложенного запроса
            if(this.GetPendReqID((uint)magic)==0)
              {
               //--- Время ожидания в милисекундах:
               //--- для метода обработки "Подождать и повторить" - значение ожидания соответствует значению method,
               //--- для метода обработки "Создать отложенный запрос" - пока будет нулевое время ожидания
               ulong wait=method;
               //--- Ищем наименьший из возможных идентификаторов, и если найти не удалось,
               //--- или произошла ошибка обновления текущих данных символа - возвращаем false
               int id=this.GetFreeID();
               if(id<1 || !symbol_obj.RefreshRates())
                  return false;
               //--- Записываем в магик идентификатор объекта-отложенного запроса и заполняем остальные незаполненные поля структуры торгового запроса
               uint mn=(magic==ULONG_MAX ? (uint)trade_obj.GetMagic() : (uint)magic);
               this.SetPendReqID((uchar)id,mn);
               this.m_request.magic=mn;
               this.m_request.action=TRADE_ACTION_DEAL;
               this.m_request.symbol=symbol_obj.Name();
               this.m_request.type=order_type;
               //--- Устанавливаем количество торговых попыток, создаём отложенный запрос и прерываем цикл
               uchar attempts=(this.m_total_try < 1 ? 1 : this.m_total_try);
               this.CreatePendingRequest((uchar)id,attempts,wait,this.m_request,trade_obj.GetResultRetcode(),symbol_obj);
               break;
              }
           }
        }
     }
//--- Возвращаем результат отправки торгового запроса в торговом объекте символа
   return res;
  }
//+------------------------------------------------------------------+

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

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

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

Метод модификации стоп-приказов открытой позиции:

//+------------------------------------------------------------------+
//| Модифицирует позицию                                             |
//+------------------------------------------------------------------+
template<typename SL,typename TP> 
bool CTrading::ModifyPosition(const ulong ticket,const SL sl=WRONG_VALUE,const TP tp=WRONG_VALUE)
  {
   bool res=true;
   this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_NO_ERROR;
   ENUM_ACTION_TYPE action=ACTION_TYPE_MODIFY;
//--- Получаем объект-ордер по тикету
   COrder *order=this.GetOrderObjByTicket(ticket);
   if(order==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_ORD_OBJ));
      return false;
     }
   ENUM_ORDER_TYPE order_type=(ENUM_ORDER_TYPE)order.TypeOrder();
//--- Получаем объект-символ по тикету позиции
   CSymbol *symbol_obj=this.GetSymbolObjByPosition(ticket,DFUN);
   if(symbol_obj==NULL)
     {
      this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_INTERNAL_ERR;
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_SYM_OBJ));
      return false;
     }
//--- Получаем торговый объект из объекта-символа
   CTradeObj *trade_obj=symbol_obj.GetTradeObj();
   if(trade_obj==NULL)
     {
      this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_INTERNAL_ERR;
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_TRADE_OBJ));
      return false;
     }
//--- Устанавливаем цены
//--- Если установить не удалось - записываем флаг "внутренняя ошибка" устанавливаем код ошибки в структуру возврата,
//--- выводим сообщение в журнал и возвращаем false
   if(!this.SetPrices(order_type,0,(sl==WRONG_VALUE ? order.StopLoss() : sl),(tp==WRONG_VALUE ? order.TakeProfit() : 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;
     }
     
//--- Если есть ограничения по разрешённости торговли, есть ограничения по уровням StopLevel или FreezeLevel - воспроизводим звук ошибки и уходим
   ENUM_ERROR_CODE_PROCESSING_METHOD method=this.CheckErrors(0,0,action,order_type,symbol_obj,trade_obj,DFUN,0,this.m_request.sl,this.m_request.tp,ticket);
   if(method!=ERROR_CODE_PROCESSING_METHOD_OK)
     {
      //--- Если полный запрет торговли
      if(method==ERROR_CODE_PROCESSING_METHOD_DISABLE)
        {
         trade_obj.SetResultRetcode(MSG_LIB_TEXT_TRADING_DISABLE);
         trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode()));
         if(this.m_log_level>LOG_LEVEL_NO_MSG)
            ::Print(CMessage::Text(MSG_LIB_TEXT_TRADING_DISABLE));
         if(this.IsUseSounds())
            trade_obj.PlaySoundError(action,order_type,(sl<0 ? false : true),(tp<0 ? false : true));
         return false;
        }
      //--- Если результат проверки "прервать торговую операцию" - устанавливаем код последней ошибки в структуру возврата,
      //--- выводим сообщение в журнал, воспроизводим звук ошибки и уходим
      if(method==ERROR_CODE_PROCESSING_METHOD_EXIT)
        {
         int code=this.m_list_errors.At(this.m_list_errors.Total()-1);
         if(code!=NULL)
           {
            trade_obj.SetResultRetcode(code);
            trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode()));
           }
         if(this.m_log_level>LOG_LEVEL_NO_MSG)
            ::Print(CMessage::Text(MSG_LIB_TEXT_TRADING_OPERATION_ABORTED));
         if(this.IsUseSounds() && trade_obj.GetResultRetcode()!=10025)
            trade_obj.PlaySoundError(action,order_type,(sl<0 ? false : true),(tp<0 ? false : true));
         return false;
        }
      //--- Если результат проверки "ожидание" - устанавливаем код последней ошибки в структуру возврата,
      //--- создаём отложенный запрос и возвращаем false
      if(method>ERROR_CODE_PROCESSING_METHOD_REFRESH)
        {
         //--- Если объекта-отложенного запроса с тикетом позиции нет в списке
         if(this.GetIndexPendingRequestByPosition(ticket)==WRONG_VALUE)
           {
            //--- Проиграем звук ошибки
            if(this.IsUseSounds())
               trade_obj.PlaySoundError(action,order_type,(sl<0 ? false : true),(tp<0 ? false : true));
            //--- устанавливаем код последней ошибки в структуру возврата
            int code=this.m_list_errors.At(this.m_list_errors.Total()-1);
            if(code!=NULL)
              {
               if(code==MSG_LIB_TEXT_TERMINAL_NOT_TRADE_ENABLED || code==MSG_LIB_TEXT_EA_NOT_TRADE_ENABLED)
                  code=10027;
               trade_obj.SetResultRetcode(code);
               trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode()));
              }
            //--- Время ожидания в милисекундах:
            //--- для метода обработки "Подождать и повторить" - значение ожидания соответствует значению method,
            //--- для метода обработки "Создать отложенный запрос" - пока будет нулевое время ожидания
            ulong wait=method;
            //--- Ищем наименьший из возможных идентификаторов, и если найти не удалось,
            //--- или произошла ошибка обновления текущих данных символа - возвращаем false
            int id=this.GetFreeID();
            if(id<1 || !symbol_obj.RefreshRates())
               return false;
            //--- Записываем тип проводимой операции, символ и тикет модифицируемой позиции в структуру запроса
            this.m_request.action=TRADE_ACTION_SLTP;
            this.m_request.symbol=symbol_obj.Name();
            this.m_request.position=ticket;
            //--- Устанавливаем количество торговых попыток и создаём отложенный запрос
            uchar attempts=(this.m_total_try < 1 ? 1 : this.m_total_try);
            this.CreatePendingRequest((uchar)id,attempts,wait,this.m_request,trade_obj.GetResultRetcode(),symbol_obj);
           }
         return false;
        }
     }

//--- В цикле по количеству попыток
   for(int i=0;i<this.m_total_try;i++)
     {                
      //--- Отсылаем запрос
      res=trade_obj.ModifyPosition(ticket,this.m_request.sl,this.m_request.tp);
      //--- Если запрос выполнен успешно или асинхронный режим отправки ордеров - проиграем звук успеха,
      //--- установленный торговому объекту символа для данного типа торговой операции и вернём true
      if(res || trade_obj.IsAsyncMode())
        {
         if(this.IsUseSounds())
            trade_obj.PlaySoundSuccess(action,(int)order.TypeOrder(),(sl<0 ? false : true),(tp<0 ? false : true));
         return true;
        }
      //--- Если запрос не выполнен - выведем сообщение и проиграем звук ошибки, установленный торговому объекту символа для данного типа торговой операции
      else
        {
         if(this.m_log_level>LOG_LEVEL_NO_MSG)
            ::Print(CMessage::Text(MSG_LIB_TEXT_TRY_N),string(i+1),". ",CMessage::Text(MSG_LIB_SYS_ERROR),": ",CMessage::Text(trade_obj.GetResultRetcode()));
         if(this.IsUseSounds() && trade_obj.GetResultRetcode()!=10025)
            trade_obj.PlaySoundError(action,(int)order.TypeOrder(),(sl<0 ? false : true),(tp<0 ? false : true));
         
         method=this.ResultProccessingMethod(trade_obj.GetResultRetcode());
         //--- Если в результате отправки запроса получили "Запретить торговлю экспертом" - взводим флаг запрета и прерываем цикл попыток
         if(method==ERROR_CODE_PROCESSING_METHOD_DISABLE)
           {
            this.SetTradingDisableFlag(true);
            break;
           }
         //--- Если в результате отправки запроса получили "Выйти из торгового метода" -
         //--- устанавливаем код последней ошибки в структуру возврата и прерываем цикл попыток
         if(method==ERROR_CODE_PROCESSING_METHOD_EXIT)
           {
            break;
           }
         //--- Если в результате отправки запроса получили "Скорректировать параметры и повторить" -
         //--- корректируем параметры и идём на следующую итерацию
         if(method==ERROR_CODE_PROCESSING_METHOD_CORRECT)
           {
            this.RequestErrorsCorrecting(this.m_request,order_type,trade_obj.SpreadMultiplier(),symbol_obj,trade_obj);
            continue;
           }
         //--- Если в результате отправки запроса получили "Обновить данные и повторить" -
         //--- обновляем данные и идём на следующую итерацию
         if(method==ERROR_CODE_PROCESSING_METHOD_REFRESH)
           {
            symbol_obj.Refresh();
            continue;
           }
         //--- Если результат проверки "ожидание" - устанавливаем код последней ошибки в структуру возврата,
         //--- создаём отложенный запрос и прерываем цикл
         if(method>ERROR_CODE_PROCESSING_METHOD_REFRESH)
           {
            //--- Если объекта-отложенного запроса с тикетом ордера нет в списке
            if(this.GetIndexPendingRequestByPosition(ticket)==WRONG_VALUE)
              {
               //--- Время ожидания в милисекундах:
               //--- для метода обработки "Подождать и повторить" - значение ожидания соответствует значению method,
               //--- для метода обработки "Создать отложенный запрос" - пока будет нулевое время ожидания
               ulong wait=method;
               //--- Ищем наименьший из возможных идентификаторов, и если найти не удалось,
               //--- или произошла ошибка обновления текущих данных символа - возвращаем false
               int id=this.GetFreeID();
               if(id<1 || !symbol_obj.RefreshRates())
                  return false;
               //--- Записываем тип проводимой операции, символ и тикет модифицируемой позиции в структуру запроса
               this.m_request.action=TRADE_ACTION_SLTP;
               this.m_request.symbol=symbol_obj.Name();
               this.m_request.position=ticket;
               //--- Устанавливаем количество торговых попыток, создаём отложенный запрос и прерываем цикл
               uchar attempts=(this.m_total_try < 1 ? 1 : this.m_total_try);
               this.CreatePendingRequest((uchar)id,attempts,wait,this.m_request,trade_obj.GetResultRetcode(),symbol_obj);
               break;
              }
           }
        }
     }
//--- Возвращаем результат отправки торгового запроса в торговом объекте символа
   return res;
  }
//+------------------------------------------------------------------+

Метод для закрытия позиции:

//+------------------------------------------------------------------+
//| Закрывает позицию полностью                                      |
//+------------------------------------------------------------------+
bool CTrading::ClosePosition(const ulong ticket,const double volume=WRONG_VALUE,const string comment=NULL,const ulong deviation=ULONG_MAX)
  {
   bool res=true;
   this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_NO_ERROR;
   ENUM_ACTION_TYPE action=ACTION_TYPE_CLOSE;
//--- Получаем объект-ордер по тикету
   COrder *order=this.GetOrderObjByTicket(ticket);
   if(order==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_ORD_OBJ));
      return false;
     }
   ENUM_ORDER_TYPE order_type=(ENUM_ORDER_TYPE)order.TypeOrder();
//--- Получаем объект-символ по тикету позиции
   CSymbol *symbol_obj=this.GetSymbolObjByPosition(ticket,DFUN);
   //--- Если не удалось получить объект-символ - выводим сообщение и возвращаем 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();
   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;
     }
//--- Обновляем котировки по символу
   if(!symbol_obj.RefreshRates())
     {
      trade_obj.SetResultRetcode(10021);
      trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode()));
      this.AddErrorCodeToList(10021);  // Отсутствуют котировки для обработки запроса
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(10021));
      return false;
     }
     
//--- Записываем отклонение и комментарий в структуру запроса
   this.m_request.deviation=(deviation==ULONG_MAX ? trade_obj.GetDeviation() : deviation);
   this.m_request.comment=(comment==NULL ? trade_obj.GetComment() : comment);
//--- Если есть ограничения по разрешённости торговли,
//--- есть ограничения по уровню FreezeLevel - воспроизводим звук ошибки и уходим
   ENUM_ERROR_CODE_PROCESSING_METHOD method=this.CheckErrors(0,0,action,order_type,symbol_obj,trade_obj,DFUN,0,0,0,ticket);
   if(method!=ERROR_CODE_PROCESSING_METHOD_OK)
     {
      //--- Если полный запрет торговли
      if(method==ERROR_CODE_PROCESSING_METHOD_DISABLE)
        {
         trade_obj.SetResultRetcode(MSG_LIB_TEXT_TRADING_DISABLE);
         trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode()));
         if(this.m_log_level>LOG_LEVEL_NO_MSG)
            ::Print(CMessage::Text(MSG_LIB_TEXT_TRADING_DISABLE));
         if(this.IsUseSounds())
            trade_obj.PlaySoundError(action,order_type);
         return false;
        }
      //--- Если результат проверки "прервать торговую операцию" - устанавливаем код последней ошибки в структуру возврата,
      //--- выводим сообщение в журнал, воспроизводим звук ошибки и уходим
      if(method==ERROR_CODE_PROCESSING_METHOD_EXIT)
        {
         int code=this.m_list_errors.At(this.m_list_errors.Total()-1);
         if(code!=NULL)
           {
            trade_obj.SetResultRetcode(code);
            trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode()));
           }
         if(this.m_log_level>LOG_LEVEL_NO_MSG)
            ::Print(CMessage::Text(MSG_LIB_TEXT_TRADING_OPERATION_ABORTED));
         if(this.IsUseSounds())
            trade_obj.PlaySoundError(action,order_type);
         return false;
        }
      //--- Если результат проверки "ожидание" - устанавливаем код последней ошибки в структуру возврата,
      //--- создаём отложенный запрос и возвращаем false
      if(method>ERROR_CODE_PROCESSING_METHOD_REFRESH)
        {
         //--- Если объекта-отложенного запроса с тикетом позиции нет в списке
         if(this.GetIndexPendingRequestByPosition(ticket)==WRONG_VALUE)
           {
            //--- Проиграем звук ошибки
            if(this.IsUseSounds())
               trade_obj.PlaySoundError(action,order_type);
            //--- устанавливаем код последней ошибки в структуру возврата
            int code=this.m_list_errors.At(this.m_list_errors.Total()-1);
            if(code!=NULL)
              {
               if(code==MSG_LIB_TEXT_TERMINAL_NOT_TRADE_ENABLED || code==MSG_LIB_TEXT_EA_NOT_TRADE_ENABLED)
                  code=10027;
               trade_obj.SetResultRetcode(code);
               trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode()));
              }
            //--- Время ожидания в милисекундах:
            //--- для метода обработки "Подождать и повторить" - значение ожидания соответствует значению method,
            ulong wait=method;
            //--- Ищем наименьший из возможных идентификаторов, и если найти не удалось,
            //--- или произошла ошибка обновления текущих данных символа - возвращаем false
            int id=this.GetFreeID();
            if(id<1 || !symbol_obj.RefreshRates())
               return false;
            //--- Записываем в структуру запроса тип торговой операции, символ, тикет и объём закрываемой позиции
            this.m_request.action=TRADE_ACTION_DEAL;
            this.m_request.symbol=symbol_obj.Name();
            this.m_request.position=ticket;
            this.m_request.volume=volume;
            //--- Устанавливаем количество торговых попыток и создаём отложенный запрос
            uchar attempts=(this.m_total_try < 1 ? 1 : this.m_total_try);
            this.CreatePendingRequest((uchar)id,attempts,wait,this.m_request,trade_obj.GetResultRetcode(),symbol_obj);
           }
         return false;
        }
     }

//--- В цикле по количеству попыток
   for(int i=0;i<this.m_total_try;i++)
     {                
      //--- Отсылаем запрос
      res=(volume==WRONG_VALUE ? trade_obj.ClosePosition(ticket,comment,deviation) : trade_obj.ClosePositionPartially(ticket,volume,comment,deviation));
      //--- Если запрос выполнен успешно или асинхронный режим отправки ордеров - проиграем звук успеха,
      //--- установленный торговому объекту символа для данного типа торговой операции и вернём true
      if(res || trade_obj.IsAsyncMode())
        {
         if(this.IsUseSounds())
            trade_obj.PlaySoundSuccess(action,(int)order.TypeOrder());
         return true;
        }
      //--- Если запрос не выполнен - выведем сообщение и проиграем звук ошибки, установленный торговому объекту символа для данного типа торговой операции
      else
        {
         if(this.m_log_level>LOG_LEVEL_NO_MSG)
            ::Print(CMessage::Text(MSG_LIB_TEXT_TRY_N),string(i+1),". ",CMessage::Text(MSG_LIB_SYS_ERROR),": ",CMessage::Text(trade_obj.GetResultRetcode()));
         if(this.IsUseSounds())
            trade_obj.PlaySoundError(action,(int)order.TypeOrder());
         
         method=this.ResultProccessingMethod(trade_obj.GetResultRetcode());
         //--- Если в результате отправки запроса получили "Запретить торговлю экспертом" - взводим флаг запрета и прерываем цикл попыток
         if(method==ERROR_CODE_PROCESSING_METHOD_DISABLE)
           {
            this.SetTradingDisableFlag(true);
            break;
           }
         //--- Если в результате отправки запроса получили "Выйти из торгового метода" - прерываем цикл попыток
         if(method==ERROR_CODE_PROCESSING_METHOD_EXIT)
           {
            break;
           }
         //--- Если в результате отправки запроса получили "Скорректировать параметры и повторить" -
         //--- корректируем параметры и идём на следующую итерацию
         if(method==ERROR_CODE_PROCESSING_METHOD_CORRECT)
           {
            this.RequestErrorsCorrecting(this.m_request,order_type,trade_obj.SpreadMultiplier(),symbol_obj,trade_obj);
            continue;
           }
         //--- Если в результате отправки запроса получили "Обновить данные и повторить" -
         //--- обновляем данные и идём на следующую итерацию
         if(method==ERROR_CODE_PROCESSING_METHOD_REFRESH)
           {
            symbol_obj.Refresh();
            continue;
           }
         //--- Если в результате отправки запроса получили "Подождать и повторить" -
         //--- создаём отложенный запрос и прерываем цикл
         if(method>ERROR_CODE_PROCESSING_METHOD_REFRESH)
           {
            //--- Если объекта-отложенного запроса с тикетом позиции нет в списке
            if(this.GetIndexPendingRequestByPosition(ticket)==WRONG_VALUE)
              {
               //--- Время ожидания в милисекундах:
               //--- для метода обработки "Подождать и повторить" - значение ожидания соответствует значению method,
               ulong wait=method;
               //--- Ищем наименьший из возможных идентификаторов, и если найти не удалось,
               //--- или произошла ошибка обновления текущих данных символа - возвращаем false
               int id=this.GetFreeID();
               if(id<1 || !symbol_obj.RefreshRates())
                  return false;
               //--- Записываем в структуру запроса тип торговой операции, символ, тикет и объём закрываемой позиции
               this.m_request.action=TRADE_ACTION_DEAL;
               this.m_request.symbol=symbol_obj.Name();
               this.m_request.position=ticket;
               this.m_request.volume=volume;
               //--- Устанавливаем количество торговых попыток, создаём отложенный запрос и прерываем цикл
               uchar attempts=(this.m_total_try < 1 ? 1 : this.m_total_try);
               this.CreatePendingRequest((uchar)id,attempts,wait,this.m_request,trade_obj.GetResultRetcode(),symbol_obj);
               break;
              }
           }
        }
     }
//--- Возвращаем результат отправки торгового запроса в торговом объекте символа
   return res;
  }
//+------------------------------------------------------------------+

Метод для закрытия позиции встречным ордером:

//+------------------------------------------------------------------+
//| Закрывает позицию встречной                                      |
//+------------------------------------------------------------------+
bool CTrading::ClosePositionBy(const ulong ticket,const ulong ticket_by)
  {
   bool res=true;
   this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_NO_ERROR;
   ENUM_ACTION_TYPE action=ACTION_TYPE_CLOSE_BY;
//--- Получаем объект-ордер по тикету
   COrder *order=this.GetOrderObjByTicket(ticket);
   if(order==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_ORD_OBJ));
      return false;
     }
   ENUM_ORDER_TYPE order_type=(ENUM_ORDER_TYPE)order.TypeOrder();
//--- Получаем объект-символ по тикету позиции
   CSymbol *symbol_obj=this.GetSymbolObjByPosition(ticket,DFUN);
   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_pos=this.GetTradeObjByPosition(ticket,DFUN);
   if(trade_obj_pos==NULL)
     {
      this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_INTERNAL_ERR;
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_TRADE_OBJ));
      return false;
     }
   if(!this.m_account.IsHedge())
     {
      trade_obj_pos.SetResultRetcode(MSG_ACC_UNABLE_CLOSE_BY);
      trade_obj_pos.SetResultComment(CMessage::Text(trade_obj_pos.GetResultRetcode()));
      return false;
     }
//--- проверим наличие встречной позиции
   if(!this.CheckPositionAvailablity(ticket_by,DFUN))
     {
      trade_obj_pos.SetResultRetcode(MSG_LIB_SYS_ERROR_POSITION_BY_ALREADY_CLOSED);
      trade_obj_pos.SetResultComment(CMessage::Text(trade_obj_pos.GetResultRetcode()));
      return false;
     }
//--- торговый объект встречной позиции
   CTradeObj *trade_obj_pos_by=this.GetTradeObjByPosition(ticket_by,DFUN);
   if(trade_obj_pos_by==NULL)
     {
      trade_obj_pos.SetResultRetcode(MSG_LIB_SYS_ERROR_FAILED_GET_TRADE_OBJ);
      trade_obj_pos.SetResultComment(CMessage::Text(trade_obj_pos.GetResultRetcode()));
      this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_INTERNAL_ERR;
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_TRADE_OBJ));
      return false;
     }
//--- Если символ закрываемой позиции не равен символу встречной позиции - сообщаем и уходим
   if(symbol_obj.Name()!=trade_obj_pos_by.GetSymbol())
     {
      trade_obj_pos.SetResultRetcode(MSG_LIB_TEXT_CLOSE_BY_SYMBOLS_UNEQUAL);
      trade_obj_pos.SetResultComment(CMessage::Text(trade_obj_pos.GetResultRetcode()));
      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_TEXT_CLOSE_BY_SYMBOLS_UNEQUAL));
      return false;
     }
//--- Обновляем котировки по символу
   if(!symbol_obj.RefreshRates())
     {
      trade_obj_pos.SetResultRetcode(10021);
      trade_obj_pos.SetResultComment(CMessage::Text(trade_obj_pos.GetResultRetcode()));
      this.AddErrorCodeToList(10021);  // Отсутствуют котировки для обработки запроса
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(10021));
      return false;
     }
     
//--- Если есть ограничения по разрешённости торговли, есть ограничения по уровню FreezeLevel - воспроизводим звук ошибки и уходим
   ENUM_ERROR_CODE_PROCESSING_METHOD method=this.CheckErrors(0,0,action,order_type,symbol_obj,trade_obj_pos,DFUN,0,0,0,ticket);
   if(method!=ERROR_CODE_PROCESSING_METHOD_OK)
     {
      //--- Если полный запрет торговли
      if(method==ERROR_CODE_PROCESSING_METHOD_DISABLE)
        {
         trade_obj_pos.SetResultRetcode(MSG_LIB_TEXT_TRADING_DISABLE);
         trade_obj_pos.SetResultComment(CMessage::Text(trade_obj_pos.GetResultRetcode()));
         if(this.m_log_level>LOG_LEVEL_NO_MSG)
            ::Print(CMessage::Text(MSG_LIB_TEXT_TRADING_DISABLE));
         if(this.IsUseSounds())
            trade_obj_pos.PlaySoundError(action,order_type);
         return false;
        }
      //--- Если результат проверки "прервать торговую операцию" - устанавливаем код последней ошибки в структуру возврата,
      //--- выводим сообщение в журнал, воспроизводим звук ошибки и уходим
      if(method==ERROR_CODE_PROCESSING_METHOD_EXIT)
        {
         int code=this.m_list_errors.At(this.m_list_errors.Total()-1);
         if(code!=NULL)
           {
            trade_obj_pos.SetResultRetcode(code);
            trade_obj_pos.SetResultComment(CMessage::Text(trade_obj_pos.GetResultRetcode()));
           }
         if(this.m_log_level>LOG_LEVEL_NO_MSG)
            ::Print(CMessage::Text(MSG_LIB_TEXT_TRADING_OPERATION_ABORTED));
         if(this.IsUseSounds())
            trade_obj_pos.PlaySoundError(action,order_type);
         return false;
        }
      //--- Если результат проверки "ожидание" - устанавливаем код последней ошибки в структуру возврата,
      //--- создаём отложенный запрос и возвращаем false
      if(method>ERROR_CODE_PROCESSING_METHOD_REFRESH)
        {
         //--- Если объекта-отложенного запроса с тикетом позиции нет в списке
         if(this.GetIndexPendingRequestByPosition(ticket)==WRONG_VALUE)
           {
            //--- Проиграем звук ошибки
            if(this.IsUseSounds())
               trade_obj_pos.PlaySoundError(action,order_type);
            //--- устанавливаем код последней ошибки в структуру возврата
            int code=this.m_list_errors.At(this.m_list_errors.Total()-1);
            if(code!=NULL)
              {
               if(code==MSG_LIB_TEXT_TERMINAL_NOT_TRADE_ENABLED || code==MSG_LIB_TEXT_EA_NOT_TRADE_ENABLED)
                  code=10027;
               trade_obj_pos.SetResultRetcode(code);
               trade_obj_pos.SetResultComment(CMessage::Text(trade_obj_pos.GetResultRetcode()));
              }
            //--- Время ожидания в милисекундах:
            //--- для метода обработки "Подождать и повторить" - значение ожидания соответствует значению method,
            ulong wait=method;
            //--- Ищем наименьший из возможных идентификаторов, и если найти не удалось,
            //--- или произошла ошибка обновления текущих данных символа - возвращаем false
            int id=this.GetFreeID();
            if(id<1 || !symbol_obj.RefreshRates())
               return false;
            //--- Записываем в структуру запроса тип торговой операции, символ и тикеты двух позиций
            this.m_request.action=TRADE_ACTION_CLOSE_BY;
            this.m_request.symbol=symbol_obj.Name();
            this.m_request.position=ticket;
            this.m_request.position_by=ticket_by;
            //--- Устанавливаем количество торговых попыток и создаём отложенный запрос
            uchar attempts=(this.m_total_try < 1 ? 1 : this.m_total_try);
            this.CreatePendingRequest((uchar)id,attempts,wait,this.m_request,trade_obj_pos.GetResultRetcode(),symbol_obj);
           }
         return false;
        }
     }

//--- В цикле по количеству попыток
   for(int i=0;i<this.m_total_try;i++)
     {                
      //--- Отсылаем запрос
      res=trade_obj_pos.ClosePositionBy(ticket,ticket_by);
      //--- Если запрос выполнен успешно - проиграем звук успеха, установленный торговому объекту символа для данного типа торговой операции
      if(res || trade_obj_pos.IsAsyncMode())
        {
         if(this.IsUseSounds())
            trade_obj_pos.PlaySoundSuccess(action,(int)order.TypeOrder());
         return true;
        }
      //--- Если запрос не выполнен - выведем сообщение и проиграем звук ошибки, установленный торговому объекту символа для данного типа торговой операции
      else
        {
         if(this.m_log_level>LOG_LEVEL_NO_MSG)
            ::Print(CMessage::Text(MSG_LIB_SYS_ERROR),": ",CMessage::Text(trade_obj_pos.GetResultRetcode()));
         if(this.IsUseSounds())
            trade_obj_pos.PlaySoundError(action,(int)order.TypeOrder());
         
         method=this.ResultProccessingMethod(trade_obj_pos.GetResultRetcode());
         //--- Если в результате отправки запроса получили "Запретить торговлю экспертом" - взводим флаг запрета и прерываем цикл попыток
         if(method==ERROR_CODE_PROCESSING_METHOD_DISABLE)
           {
            this.SetTradingDisableFlag(true);
            break;
           }
         //--- Если в результате отправки запроса получили "Выйти из торгового метода" - прерываем цикл попыток
         if(method==ERROR_CODE_PROCESSING_METHOD_EXIT)
           {
            break;
           }
         //--- Если в результате отправки запроса получили "Скорректировать параметры и повторить" -
         //--- корректируем параметры и идём на следующую итерацию
         if(method==ERROR_CODE_PROCESSING_METHOD_CORRECT)
           {
            this.RequestErrorsCorrecting(this.m_request,order_type,trade_obj_pos.SpreadMultiplier(),symbol_obj,trade_obj_pos);
            continue;
           }
         //--- Если в результате отправки запроса получили "Обновить данные и повторить" -
         //--- обновляем данные и идём на следующую итерацию
         if(method==ERROR_CODE_PROCESSING_METHOD_REFRESH)
           {
            symbol_obj.Refresh();
            continue;
           }
         //--- Если в результате отправки запроса получили "Подождать и повторить" -
         //--- создаём отложенный запрос и прерываем цикл
         if(method>ERROR_CODE_PROCESSING_METHOD_REFRESH)
           {
            //--- Если объекта-отложенного запроса с тикетом позиции нет в списке
            if(this.GetIndexPendingRequestByPosition(ticket)==WRONG_VALUE)
              {
               //--- Время ожидания в милисекундах:
               //--- для метода обработки "Подождать и повторить" - значение ожидания соответствует значению method,
               ulong wait=method;
               //--- Ищем наименьший из возможных идентификаторов, и если найти не удалось,
               //--- или произошла ошибка обновления текущих данных символа - возвращаем false
               int id=this.GetFreeID();
               if(id<1 || !symbol_obj.RefreshRates())
                  return false;
               //--- Записываем в структуру запроса тип торговой операции, символ и тикеты двух позиций
               this.m_request.action=TRADE_ACTION_CLOSE_BY;
               this.m_request.symbol=symbol_obj.Name();
               this.m_request.position=ticket;
               this.m_request.position_by=ticket_by;
               //--- Устанавливаем количество торговых попыток, создаём отложенный запрос и прерываем цикл
               uchar attempts=(this.m_total_try < 1 ? 1 : this.m_total_try);
               this.CreatePendingRequest((uchar)id,attempts,wait,this.m_request,trade_obj_pos.GetResultRetcode(),symbol_obj);
               break;
              }
           }
        }
     }
   //--- Возвращаем результат отправки торгового запроса в торговом объекте символа
   return res;
  }
//+------------------------------------------------------------------+

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

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

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

Метод для модификации отложенного ордера:

//+------------------------------------------------------------------+
//| Модифицирует отложенный ордер                                    |
//+------------------------------------------------------------------+
template<typename PS,typename PL,typename SL,typename TP>
bool CTrading::ModifyOrder(const ulong ticket,
                           const PS price=WRONG_VALUE,
                           const SL sl=WRONG_VALUE,
                           const TP tp=WRONG_VALUE,
                           const PL limit=WRONG_VALUE,
                           datetime expiration=WRONG_VALUE,
                           const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE,
                           const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE)
  {
   bool res=true;
   this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_NO_ERROR;
   ENUM_ACTION_TYPE action=ACTION_TYPE_MODIFY;
//--- Получаем объект-ордер по тикету
   COrder *order=this.GetOrderObjByTicket(ticket);
   if(order==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_ORD_OBJ));
      return false;
     }
   ENUM_ORDER_TYPE order_type=(ENUM_ORDER_TYPE)order.TypeOrder();
//--- Получаем объект-символ по тикету ордера
   CSymbol *symbol_obj=this.GetSymbolObjByOrder(ticket,DFUN);
   if(symbol_obj==NULL)
     {
      this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_INTERNAL_ERR;
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_SYM_OBJ));
      return false;
     }
//--- получаем торговый объект из объекта-символа
   CTradeObj *trade_obj=symbol_obj.GetTradeObj();
   if(trade_obj==NULL)
     {
      this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_INTERNAL_ERR;
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_TRADE_OBJ));
      return false;
     }
//--- Устанавливаем цены
//--- Если установить не удалось - записываем флаг "внутренняя ошибка" устанавливаем код ошибки в структуру возврата,
//--- выводим сообщение в журнал и возвращаем false
   if(!this.SetPrices(order_type,
                      (price>0 ? price : order.PriceOpen()),
                      (sl>0 ? sl : sl<0 ? order.StopLoss() : 0),
                      (tp>0 ? tp : tp<0 ? order.TakeProfit() : 0),
                      (limit>0 ? limit : order.PriceStopLimit()),
                      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;
     }
     
//--- Записываем тип заливки, дату и тип экспирации в структуру запроса
   this.m_request.type_filling=(type_filling>WRONG_VALUE ? type_filling : order.TypeFilling());
   this.m_request.expiration=(expiration>WRONG_VALUE ? expiration : order.TimeExpiration());
   this.m_request.type_time=(type_time>WRONG_VALUE ? type_time : order.TypeTime());
//--- Если есть ограничения по разрешённости торговли,
//--- есть ограничения по уровням StopLevel или FreezeLevel - воспроизводим звук ошибки и уходим
   ENUM_ERROR_CODE_PROCESSING_METHOD method=this.CheckErrors(0,this.m_request.price,action,order_type,symbol_obj,trade_obj,DFUN,this.m_request.stoplimit,this.m_request.sl,this.m_request.tp,ticket);
   if(method!=ERROR_CODE_PROCESSING_METHOD_OK)
     {
      //--- Если полный запрет торговли
      if(method==ERROR_CODE_PROCESSING_METHOD_DISABLE)
        {
         trade_obj.SetResultRetcode(MSG_LIB_TEXT_TRADING_DISABLE);
         trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode()));
         if(this.m_log_level>LOG_LEVEL_NO_MSG)
            ::Print(CMessage::Text(MSG_LIB_TEXT_TRADING_DISABLE));
         if(this.IsUseSounds())
            trade_obj.PlaySoundError(action,order_type,(sl<0 ? false : true),(tp<0 ? false : true),(price>0 || limit>0 ? true : false));
         return false;
        }
      //--- Если результат проверки "прервать торговую операцию" - устанавливаем код последней ошибки в структуру возврата,
      //--- выводим сообщение в журнал, воспроизводим звук ошибки и уходим
      if(method==ERROR_CODE_PROCESSING_METHOD_EXIT)
        {
         int code=this.m_list_errors.At(this.m_list_errors.Total()-1);
         if(code!=NULL)
           {
            trade_obj.SetResultRetcode(code);
            trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode()));
           }
         if(this.m_log_level>LOG_LEVEL_NO_MSG)
            ::Print(CMessage::Text(MSG_LIB_TEXT_TRADING_OPERATION_ABORTED));
         if(this.IsUseSounds() && trade_obj.GetResultRetcode()!=10025)
            trade_obj.PlaySoundError(action,order_type,(sl<0 ? false : true),(tp<0 ? false : true),(price>0 || limit>0 ? true : false));
         return false;
        }
      //--- Если результат проверки "ожидание" - устанавливаем код последней ошибки в структуру возврата,
      //--- создаём отложенный запрос и возвращаем false
      if(method>ERROR_CODE_PROCESSING_METHOD_REFRESH)
        {
         //--- Если объекта-отложенного запроса с тикетом ордера нет в списке
         if(this.GetIndexPendingRequestByOrder(ticket)==WRONG_VALUE)
           {
            //--- Проиграем звук ошибки
            if(this.IsUseSounds())
               trade_obj.PlaySoundError(action,order_type,(sl<0 ? false : true),(tp<0 ? false : true),(price>0 || limit>0 ? true : false));
            //--- устанавливаем код последней ошибки в структуру возврата
            int code=this.m_list_errors.At(this.m_list_errors.Total()-1);
            if(code!=NULL)
              {
               if(code==MSG_LIB_TEXT_TERMINAL_NOT_TRADE_ENABLED || code==MSG_LIB_TEXT_EA_NOT_TRADE_ENABLED)
                  code=10027;
               trade_obj.SetResultRetcode(code);
               trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode()));
              }
            //--- Время ожидания в милисекундах:
            //--- для метода обработки "Подождать и повторить" - значение ожидания соответствует значению method,
            //--- для метода обработки "Создать отложенный запрос" - пока будет нулевое время ожидания
            ulong wait=method;
            //--- Ищем наименьший из возможных идентификаторов, и если найти не удалось,
            //--- или произошла ошибка обновления текущих данных символа - возвращаем false
            int id=this.GetFreeID();
            if(id<1 || !symbol_obj.RefreshRates())
               return false;
            //--- Устанавливаем тип торговой операции, символ и тикет модифицируемого ордера в структуру запроса
            this.m_request.action=TRADE_ACTION_MODIFY;
            this.m_request.symbol=symbol_obj.Name();
            this.m_request.order=ticket;
            //--- Устанавливаем количество торговых попыток и создаём отложенный запрос
            uchar attempts=(this.m_total_try < 1 ? 1 : this.m_total_try);
            this.CreatePendingRequest((uchar)id,attempts,wait,this.m_request,trade_obj.GetResultRetcode(),symbol_obj);
           }
         return false;
        }
     }

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

Метод для удаления отложенного ордера:

//+------------------------------------------------------------------+
//| Удаляет отложенный ордер                                         |
//+------------------------------------------------------------------+
bool CTrading::DeleteOrder(const ulong ticket)
  {
   bool res=true;
   this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_NO_ERROR;
   ENUM_ACTION_TYPE action=ACTION_TYPE_CLOSE;
//--- Получаем объект-ордер по тикету
   COrder *order=this.GetOrderObjByTicket(ticket);
   if(order==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_ORD_OBJ));
      return false;
     }
   ENUM_ORDER_TYPE order_type=(ENUM_ORDER_TYPE)order.TypeOrder();
//--- Получаем объект-символ по тикету ордера
   CSymbol *symbol_obj=this.GetSymbolObjByOrder(ticket,DFUN);
   if(symbol_obj==NULL)
     {
      this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_INTERNAL_ERR;
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_SYM_OBJ));
      return false;
     }
//--- получаем торговый объект из объекта-символа
   CTradeObj *trade_obj=symbol_obj.GetTradeObj();
   if(trade_obj==NULL)
     {
      this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_INTERNAL_ERR;
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_TRADE_OBJ));
      return false;
     }
//--- Обновляем котировки по символу
   if(!symbol_obj.RefreshRates())
     {
      trade_obj.SetResultRetcode(10021);
      trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode()));
      this.AddErrorCodeToList(10021);  // Отсутствуют котировки для обработки запроса
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(10021));
      return false;
     }
     
//--- Если есть ограничения по разрешённости торговли,
//--- есть ограничения по уровню FreezeLevel - воспроизводим звук ошибки и уходим
   ENUM_ERROR_CODE_PROCESSING_METHOD method=this.CheckErrors(0,0,action,order_type,symbol_obj,trade_obj,DFUN,0,0,0,ticket);
   if(method!=ERROR_CODE_PROCESSING_METHOD_OK)
     {
      //--- Если полный запрет торговли
      if(method==ERROR_CODE_PROCESSING_METHOD_DISABLE)
        {
         trade_obj.SetResultRetcode(MSG_LIB_TEXT_TRADING_DISABLE);
         trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode()));
         if(this.m_log_level>LOG_LEVEL_NO_MSG)
            ::Print(CMessage::Text(MSG_LIB_TEXT_TRADING_DISABLE));
         if(this.IsUseSounds())
            trade_obj.PlaySoundError(action,order_type);
         return false;
        }
      //--- Если результат проверки "прервать торговую операцию" - устанавливаем код последней ошибки в структуру возврата,
      //--- выводим сообщение в журнал, воспроизводим звук ошибки и уходим
      if(method==ERROR_CODE_PROCESSING_METHOD_EXIT)
        {
         int code=this.m_list_errors.At(this.m_list_errors.Total()-1);
         if(code!=NULL)
           {
            trade_obj.SetResultRetcode(code);
            trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode()));
           }
         if(this.m_log_level>LOG_LEVEL_NO_MSG)
            ::Print(CMessage::Text(MSG_LIB_TEXT_TRADING_OPERATION_ABORTED));
         if(this.IsUseSounds())
            trade_obj.PlaySoundError(action,order_type);
         return false;
        }
      //--- Если результат проверки "ожидание" - устанавливаем код последней ошибки в структуру возврата,
      //--- создаём отложенный запрос и возвращаем false
      if(method>ERROR_CODE_PROCESSING_METHOD_REFRESH)
        {
         //--- Если объекта-отложенного запроса с тикетом ордера нет в списке
         if(this.GetIndexPendingRequestByOrder(ticket)==WRONG_VALUE)
           {
            //--- Проиграем звук ошибки
            if(this.IsUseSounds())
               trade_obj.PlaySoundError(action,order_type);
            //--- устанавливаем код последней ошибки в структуру возврата
            int code=this.m_list_errors.At(this.m_list_errors.Total()-1);
            if(code!=NULL)
              {
               if(code==MSG_LIB_TEXT_TERMINAL_NOT_TRADE_ENABLED || code==MSG_LIB_TEXT_EA_NOT_TRADE_ENABLED)
                  code=10027;
               trade_obj.SetResultRetcode(code);
               trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode()));
              }
            //--- Время ожидания в милисекундах:
            //--- для метода обработки "Подождать и повторить" - значение ожидания соответствует значению method,
            //--- для метода обработки "Создать отложенный запрос" - пока будет нулевое время ожидания
            ulong wait=method;
            //--- Ищем наименьший из возможных идентификаторов, и если найти не удалось,
            //--- или произошла ошибка обновления текущих данных символа - возвращаем false
            int id=this.GetFreeID();
            if(id<1 || !symbol_obj.RefreshRates())
               return false;
            //--- Устанавливаем тип торговой операции, символ и тикет удаляемого ордера в структуру запроса
            this.m_request.action=TRADE_ACTION_REMOVE;
            this.m_request.symbol=symbol_obj.Name();
            this.m_request.order=ticket;
            //--- Устанавливаем количество торговых попыток и создаём отложенный запрос
            uchar attempts=(this.m_total_try < 1 ? 1 : this.m_total_try);
            this.CreatePendingRequest((uchar)id,attempts,wait,this.m_request,trade_obj.GetResultRetcode(),symbol_obj);
           }
         return false;
        }
     }

//--- В цикле по количеству попыток
   for(int i=0;i<this.m_total_try;i++)
     {                
      //--- Отсылаем запрос
      res=trade_obj.DeleteOrder(ticket);
      //--- Если запрос выполнен успешно или асинхронный режим отправки ордеров - проиграем звук успеха,
      //--- установленный торговому объекту символа для данного типа торговой операции и вернём true
      if(res || trade_obj.IsAsyncMode())
        {
         if(this.IsUseSounds())
            trade_obj.PlaySoundSuccess(action,(int)order.TypeOrder());
         return true;
        }
      //--- Если запрос не выполнен - выведем сообщение и проиграем звук ошибки, установленный торговому объекту символа для данного типа торговой операции
      else
        {
         if(this.m_log_level>LOG_LEVEL_NO_MSG)
            ::Print(CMessage::Text(MSG_LIB_TEXT_TRY_N),string(i+1),". ",CMessage::Text(MSG_LIB_SYS_ERROR),": ",CMessage::Text(trade_obj.GetResultRetcode()));
         if(this.IsUseSounds())
            trade_obj.PlaySoundError(action,(int)order.TypeOrder());
         
         //--- Получаем метод обработки ошибки
         method=this.ResultProccessingMethod(trade_obj.GetResultRetcode());
         //--- Если в результате отправки запроса получили "Запретить торговлю экспертом" - взводим флаг запрета и прерываем цикл попыток
         if(method==ERROR_CODE_PROCESSING_METHOD_DISABLE)
           {
            this.SetTradingDisableFlag(true);
            break;
           }
         //--- Если в результате отправки запроса получили "Выйти из торгового метода" - прерываем цикл попыток
         if(method==ERROR_CODE_PROCESSING_METHOD_EXIT)
           {
            break;
           }
         //--- Если в результате отправки запроса получили "Скорректировать параметры и повторить" -
         //--- корректируем параметры и идём на следующую итерацию
         if(method==ERROR_CODE_PROCESSING_METHOD_CORRECT)
           {
            this.RequestErrorsCorrecting(this.m_request,order_type,trade_obj.SpreadMultiplier(),symbol_obj,trade_obj);
            continue;
           }
         //--- Если в результате отправки запроса получили "Обновить данные и повторить" -
         //--- обновляем данные и идём на следующую итерацию
         if(method==ERROR_CODE_PROCESSING_METHOD_REFRESH)
           {
            symbol_obj.Refresh();
            continue;
           }
         //--- Если результат проверки "ожидание" - устанавливаем код последней ошибки в структуру возврата,
         //--- создаём отложенный запрос и прерываем цикл
         if(method>ERROR_CODE_PROCESSING_METHOD_REFRESH)
           {
            //--- Если объекта-отложенного запроса с тикетом ордера нет в списке
            if(this.GetIndexPendingRequestByOrder(ticket)==WRONG_VALUE)
              {
               //--- Время ожидания в милисекундах:
               //--- для метода обработки "Подождать и повторить" - значение ожидания соответствует значению method,
               //--- для метода обработки "Создать отложенный запрос" - пока будет нулевое время ожидания
               ulong wait=method;
               //--- Ищем наименьший из возможных идентификаторов, и если найти не удалось,
               //--- или произошла ошибка обновления текущих данных символа - возвращаем false
               int id=this.GetFreeID();
               if(id<1 || !symbol_obj.RefreshRates())
                  return false;
               //--- Устанавливаем тип торговой операции, символ и тикет удаляемого ордера в структуру запроса
               this.m_request.action=TRADE_ACTION_REMOVE;
               this.m_request.symbol=symbol_obj.Name();
               this.m_request.order=ticket;
               //--- Устанавливаем количество торговых попыток, создаём отложенный запрос и прерываем цикл
               uchar attempts=(this.m_total_try < 1 ? 1 : this.m_total_try);
               this.CreatePendingRequest((uchar)id,attempts,wait,this.m_request,trade_obj.GetResultRetcode(),symbol_obj);
               break;
              }
           }
        }
     }
//--- Возвращаем результат отправки торгового запроса в торговом объекте символа
   return res;
  }
//+------------------------------------------------------------------+

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

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

Так как в торговом классе метод OnInit() теперь принимает дополнительный параметр — указатель на объект класса-коллекции торговых событий, то его туда необходимо передать.
Делается это в классе базового объекта библиотеки CEngine.
Откроем файл Engine.mqh и внесём дополнение в его метод TradingOnInit():

//--- Передаёт в торговый класс указатели на все необходимые коллекции
   void                 TradingOnInit(void)
                          {
                           this.m_trading.OnInit(this.GetAccountCurrent(),m_symbols.GetObject(),m_market.GetObject(),m_history.GetObject(),m_events.GetObject());
                          }

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

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

Для тестирования работы объектов отложенных запросов нам достаточно использовать советник из прошлой статьи без каких-либо изменений.
Но для порядка мы его сохраним в новой папке \MQL5\Experts\TestDoEasy\Part28\ под новым именем TestDoEasyPart28.mq5.

Что мы сделаем для проверки работы отложенных запросов:
Отключим кнопку "Авто-торговля" в терминале и нажмём на торговой панели тестового эксперта кнопку открытия позиции. Получим ошибку отправки торгового запроса и записи в журнале об ошибке и о создании отложенного запроса.
Затем включим обратно кнопку "Авто-торговля" в терминале и дождёмся наступления времени повторной торговой попытки (для отсутствия разрешения на торговлю от терминала у нас установлено ожидание в 20 секунд). После наступления времени очередной попытки у нас будет открыта позиция, выведена об этом запись в журнал, и дополнительно — запись о том, что отложенный запрос выполнил свою работу, и был удалён:

2019.12.12 15:36:17.878 automated trading is disabled
2019.12.12 15:36:23.206 CTrading::OpenPosition<uint,uint>: Invalid request:
2019.12.12 15:36:23.206 There is no permission to conduct trading operations in the terminal (the "AutoTrading" button is disabled)
2019.12.12 15:36:23.206 Correction of trade request parameters ...
2019.12.12 15:36:23.207 Pending request created #1:
2019.12.12 15:36:23.207 ================== Pending trade request's parameters ==================
2019.12.12 15:36:23.207  - Pending request type: Pending request that was created as a result of the server code
2019.12.12 15:36:23.207  - Trade request ID: #1
2019.12.12 15:36:23.207  - Return code based on which the request was created: 10027 "Autotrading disabled by client terminal"
2019.12.12 15:36:23.207  - Request creation time: 2019.12.12 10:36:23.006
2019.12.12 15:36:23.207  - Price at time of request create: : 1.11353
2019.12.12 15:36:23.207  - Request activation time: 2019.12.12 10:36:43.006
2019.12.12 15:36:23.207  - Waiting time between trading attempts: 20000 (00:00:20)
2019.12.12 15:36:23.207  - Current trading attempt: Waiting for the onset time of the first trading attempt
2019.12.12 15:36:23.207  - Total trade attempts: 5
2019.12.12 15:36:23.207 ================== Trade request's parameters ==================
2019.12.12 15:36:23.207  - Trade operation type: Place market order
2019.12.12 15:36:23.207  - Trade symbol: EURUSD
2019.12.12 15:36:23.207  - Requested volume for a deal in lots: 0.10
2019.12.12 15:36:23.207  - Price: 1.11356
2019.12.12 15:36:23.207  - Stop Loss level of the order: 1.11206
2019.12.12 15:36:23.207  - Take Profit level of the order: 1.11506
2019.12.12 15:36:23.207  - Maximal deviation from the price: 5
2019.12.12 15:36:23.207  - Order type: Market-order Buy
2019.12.12 15:36:23.207  - Order execution type: The order is executed exclusively in the specified volume, otherwise it is canceled (FOK)
2019.12.12 15:36:23.207  - Magic number: 25821307
2019.12.12 15:36:23.207  - Order comment: "TestDoEasyPart28 by DoEasy"
2019.12.12 15:36:23.207 ==================
2019.12.12 15:36:23.207  
2019.12.12 15:36:26.845 automated trading is enabled
2019.12.12 15:36:43.536 Retry trading attempt #1
2019.12.12 15:36:43.883 - Position is open: 2019.12.12 10:36:43.797 -
2019.12.12 15:36:43.883 EURUSD Opened 0.10 Buy #494348590 [0.10 Market-order Buy #494348590] at price 1.11354, sl 1.11206, tp 1.11506, Magic number 25821307 (123), G1: 10, G2: 8, ID: 1
2019.12.12 15:36:43.883 OnDoEasyEvent: Position is open
2019.12.12 15:36:44.101 Trade request ID: #1: Pending request deleted due to execution

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

2019.12.12 15:37:02.347 automated trading is disabled
2019.12.12 15:37:06.566 CTrading::ClosePosition: Invalid request:
2019.12.12 15:37:06.566 There is no permission to conduct trading operations in the terminal (the "AutoTrading" button is disabled)
2019.12.12 15:37:06.566 Correction of trade request parameters ...
2019.12.12 15:37:06.566 Pending request created #1:
2019.12.12 15:37:06.566 ================== Pending trade request's parameters ==================
2019.12.12 15:37:06.566  - Pending request type: Pending request that was created as a result of the server code
2019.12.12 15:37:06.566  - Trade request ID: #1
2019.12.12 15:37:06.566  - Return code based on which the request was created: 10027 "Autotrading disabled by client terminal"
2019.12.12 15:37:06.566  - Request creation time: 2019.12.12 10:37:03.586
2019.12.12 15:37:06.566  - Price at time of request create: : 1.11351
2019.12.12 15:37:06.566  - Request activation time: 2019.12.12 10:37:23.586
2019.12.12 15:37:06.566  - Waiting time between trading attempts: 20000 (00:00:20)
2019.12.12 15:37:06.566  - Current trading attempt: Waiting for the onset time of the first trading attempt
2019.12.12 15:37:06.566  - Total trade attempts: 5
2019.12.12 15:37:06.566 ================== Trade request's parameters ==================
2019.12.12 15:37:06.566  - Trade operation type: Place market order
2019.12.12 15:37:06.566  - Trade symbol: EURUSD
2019.12.12 15:37:06.566  - Requested volume for a deal in lots: Value not set
2019.12.12 15:37:06.566  - Price: 1.11354
2019.12.12 15:37:06.566  - Stop Loss level of the order: 1.11206
2019.12.12 15:37:06.566  - Take Profit level of the order: 1.11506
2019.12.12 15:37:06.566  - Maximal deviation from the price: 5
2019.12.12 15:37:06.566  - Order type: Market-order Buy
2019.12.12 15:37:06.566  - Order execution type: The order is executed exclusively in the specified volume, otherwise it is canceled (FOK)
2019.12.12 15:37:06.566  - Magic number: 0
2019.12.12 15:37:06.566  - Order comment: "TestDoEasyPart28 by DoEasy"
2019.12.12 15:37:06.566 ==================
2019.12.12 15:37:06.566 
2019.12.12 15:37:11.611 automated trading is enabled
2019.12.12 15:37:25.196 Retry trading attempt #1
2019.12.12 15:37:25.679 - Position closed: 2019.12.12 10:36:43.797 -
2019.12.12 15:37:25.679 EURUSD Closed Buy #494348590 [0.10 Market-order Sell #494348941] at price 1.11349, sl 1.11206, tp 1.11506, Magic number 25821307 (123), G1: 10, G2: 8, ID: 1, Profit -0.50 USD
2019.12.12 15:37:25.679 OnDoEasyEvent: Position closed
2019.12.12 15:37:25.838 Trade request ID: #1: Pending request deleted due to execution

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

2019.12.12 17:47:16.774 automated trading is disabled
2019.12.12 17:47:20.297 CTrading::PlaceOrder<uint,int,uint,uint>: Invalid request:
2019.12.12 17:47:20.297 There is no permission to conduct trading operations in the terminal (the "AutoTrading" button is disabled)
2019.12.12 17:47:20.298 Correction of trade request parameters ...
2019.12.12 17:47:20.298 Pending request created #1:
2019.12.12 17:47:20.298 ================== Pending trade request's parameters ==================
2019.12.12 17:47:20.298  - Pending request type: Pending request that was created as a result of the server code
2019.12.12 17:47:20.298  - Trade request ID: #1
2019.12.12 17:47:20.298  - Return code based on which the request was created: 10027 "Autotrading disabled by client terminal"
2019.12.12 17:47:20.298  - Request creation time: 2019.12.12 12:47:18.719
2019.12.12 17:47:20.298  - Price at time of request create: : 1.11293
2019.12.12 17:47:20.298  - Request activation time: 2019.12.12 12:47:38.719
2019.12.12 17:47:20.298  - Waiting time between trading attempts: 20000 (00:00:20)
2019.12.12 17:47:20.298  - Current trading attempt: Waiting for the onset time of the first trading attempt
2019.12.12 17:47:20.298  - Total trade attempts: 5
2019.12.12 17:47:20.298 ================== Trade request's parameters ==================
2019.12.12 17:47:20.298  - Trade operation type: Place pending order
2019.12.12 17:47:20.298  - Trade symbol: EURUSD
2019.12.12 17:47:20.298  - Requested volume for a deal in lots: 0.10
2019.12.12 17:47:20.298  - Price: 1.11343
2019.12.12 17:47:20.298  - StopLimit level of the order: Value not set
2019.12.12 17:47:20.298  - Stop Loss level of the order: 1.11493
2019.12.12 17:47:20.298  - Take Profit level of the order: 1.11193
2019.12.12 17:47:20.298  - Order type: Pending order Sell Limit
2019.12.12 17:47:20.298  - Order execution type: The order is executed exclusively in the specified volume, otherwise it is canceled (FOK)
2019.12.12 17:47:20.298  - Order expiration type: Good till cancel order
2019.12.12 17:47:20.298  - Order expiration time: Value not set
2019.12.12 17:47:20.298  - Magic number: 27525243
2019.12.12 17:47:20.298  - Order comment: "Pending order SellLimit"
2019.12.12 17:47:20.298  ==================
2019.12.12 17:47:20.298 
2019.12.12 17:47:24.514 Pending request created #2:
2019.12.12 17:47:24.514 ================== Pending trade request's parameters ==================
2019.12.12 17:47:24.514  - Pending request type: Pending request that was created as a result of the server code
2019.12.12 17:47:24.514  - Trade request ID: #2
2019.12.12 17:47:24.514  - Return code based on which the request was created: 10027 "Autotrading disabled by client terminal"
2019.12.12 17:47:24.514  - Request creation time: 2019.12.12 12:47:23.638
2019.12.12 17:47:24.514  - Price at time of request create: : 1.11296
2019.12.12 17:47:24.514  - Request activation time: 2019.12.12 12:47:43.638
2019.12.12 17:47:24.514  - Waiting time between trading attempts: 20000 (00:00:20)
2019.12.12 17:47:24.514  - Current trading attempt: Waiting for the onset time of the first trading attempt
2019.12.12 17:47:24.514  - Total trade attempts: 5
2019.12.12 17:47:24.514 ================== Trade request's parameters ==================
2019.12.12 17:47:24.514  - Trade operation type: Place pending order
2019.12.12 17:47:24.514  - Trade symbol: EURUSD
2019.12.12 17:47:24.514  - Requested volume for a deal in lots: 0.10
2019.12.12 17:47:24.514  - Price: 1.11251
2019.12.12 17:47:24.514  - StopLimit level of the order: Value not set
2019.12.12 17:47:24.514  - Stop Loss level of the order: 1.11101
2019.12.12 17:47:24.514  - Take Profit level of the order: 1.11401
2019.12.12 17:47:24.514  - Order type: Pending order Buy Limit
2019.12.12 17:47:24.514  - Order execution type: The order is executed exclusively in the specified volume, otherwise it is canceled (FOK)
2019.12.12 17:47:24.514  - Order expiration type: Good till cancel order
2019.12.12 17:47:24.514  - Order expiration time: Value not set
2019.12.12 17:47:24.514  - Magic number: 39387259
2019.12.12 17:47:24.514  - Order comment: "Pending order BuyLimit"
2019.12.12 17:47:24.514  ==================
2019.12.12 17:47:24.514 
2019.12.12 17:47:29.167 automated trading is enabled
2019.12.12 17:47:45.796 Retry trading attempt #1
2019.12.12 17:47:46.045 Retry trading attempt #1
2019.12.12 17:47:46.256 - Pending order placed: 2019.12.12 12:47:46.319 -
2019.12.12 17:47:46.256 EURUSD Placed 0.10 Pending order Sell Limit #494419747 at price 1.11343, sl 1.11493, tp 1.11193, Magic number 27525243 (123), G1: 4, G2: 10, ID: 1
2019.12.12 17:47:46.256 - Pending order placed: 2019.12.12 12:47:46.162 -
2019.12.12 17:47:46.256 EURUSD Placed 0.10 Pending order Buy Limit #494419742 at price 1.11251, sl 1.11101, tp 1.11401, Magic number 39387259 (123), G1: 9, G2: 5, ID: 2
2019.12.12 17:47:46.256 OnDoEasyEvent: Pending order placed
2019.12.12 17:47:46.256 OnDoEasyEvent: Pending order placed
2019.12.12 17:47:46.505 Trade request ID: #2: Pending request deleted due to execution
2019.12.12 17:47:46.505 Trade request ID: #1: Pending request deleted due to execution

Теперь попробуем одновременно удалить оба отложенных ордера.
Отключим автоторговлю в терминале и нажмём кнопку удаления всех отложенных ордеров на торговой панели тестового советника (Delete pending). Получим сообщение об ошибке, а затем записи о создании отложенного запроса и его параметры для каждого из удаляемых ордеров.
Затем включим автоторговлю в терминале. Как только наступит время очередной попытки каждого из отложенных торговых запросов, будет выведено сообщение о номере попытки от каждого запроса, и сообщение об успешном удалении каждого ордера.
Затем каждый отложенный торговый запрос будет удалён, и об этом будут сообщения в журнале:

2019.12.12 17:47:57.310 automated trading is disabled
2019.12.12 17:48:03.105 CTrading::DeleteOrder: Invalid request:
2019.12.12 17:48:03.105 There is no permission to conduct trading operations in the terminal (the "AutoTrading" button is disabled)
2019.12.12 17:48:03.105 Correction of trade request parameters ...
2019.12.12 17:48:03.105 Pending request created #1:
2019.12.12 17:48:03.106 ================== Pending trade request's parameters ==================
2019.12.12 17:48:03.106  - Pending request type: Pending request that was created as a result of the server code
2019.12.12 17:48:03.106  - Trade request ID: #1
2019.12.12 17:48:03.106  - Return code based on which the request was created: 10027 "Autotrading disabled by client terminal"
2019.12.12 17:48:03.106  - Request creation time: 2019.12.12 12:48:00.305
2019.12.12 17:48:03.106  - Price at time of request create: : 1.11300
2019.12.12 17:48:03.106  - Request activation time: 2019.12.12 12:48:20.305
2019.12.12 17:48:03.106  - Waiting time between trading attempts: 20000 (00:00:20)
2019.12.12 17:48:03.106  - Current trading attempt: Waiting for the onset time of the first trading attempt
2019.12.12 17:48:03.106  - Total trade attempts: 5
2019.12.12 17:48:03.106 ================== Trade request's parameters ==================
2019.12.12 17:48:03.106  - Trade operation type: Delete the pending order placed previously
2019.12.12 17:48:03.106  - Order ticket: 494419747
2019.12.12 17:48:03.106  ==================
2019.12.12 17:48:03.106 
2019.12.12 17:48:03.106 CTrading::DeleteOrder: Invalid request:
2019.12.12 17:48:03.106 There is no permission to conduct trading operations in the terminal (the "AutoTrading" button is disabled)
2019.12.12 17:48:03.106 Correction of trade request parameters ...
2019.12.12 17:48:03.106 Pending request created #2:
2019.12.12 17:48:03.106 ================== Pending trade request's parameters ==================
2019.12.12 17:48:03.106  - Pending request type: Pending request that was created as a result of the server code
2019.12.12 17:48:03.106  - Trade request ID: #2
2019.12.12 17:48:03.106  - Return code based on which the request was created: 10027 "Autotrading disabled by client terminal"
2019.12.12 17:48:03.106  - Request creation time: 2019.12.12 12:48:00.305
2019.12.12 17:48:03.106  - Price at time of request create: : 1.11300
2019.12.12 17:48:03.106  - Request activation time: 2019.12.12 12:48:20.305
2019.12.12 17:48:03.106  - Waiting time between trading attempts: 20000 (00:00:20)
2019.12.12 17:48:03.106  - Current trading attempt: Waiting for the onset time of the first trading attempt
2019.12.12 17:48:03.106  - Total trade attempts: 5
2019.12.12 17:48:03.106 ================== Trade request's parameters ==================
2019.12.12 17:48:03.106  - Trade operation type: Delete the pending order placed previously
2019.12.12 17:48:03.106  - Order ticket: 494419742
2019.12.12 17:48:03.106  ==================
2019.12.12 17:48:03.106 
2019.12.12 17:48:09.073 automated trading is enabled
2019.12.12 17:48:24.428 Retry trading attempt #1
2019.12.12 17:48:24.428 CTrading::OnTimer: Trade operation type: Delete the pending order placed previously
2019.12.12 17:48:24.593 Retry trading attempt #1
2019.12.12 17:48:24.593 CTrading::OnTimer: Trade operation type: Delete the pending order placed previously
2019.12.12 17:48:24.771 - Pending order removed: 2019.12.12 12:47:46.319 -
2019.12.12 17:48:24.771 EURUSD Deleted 0.10 Pending order Sell Limit #494419747 at price 1.11343, sl 1.11493, tp 1.11193, Magic number 27525243 (123), G1: 4, G2: 10, ID: 1
2019.12.12 17:48:24.771 - Pending order removed: 2019.12.12 12:47:46.162 -
2019.12.12 17:48:24.771 EURUSD Deleted 0.10 Pending order Buy Limit #494419742 at price 1.11251, sl 1.11101, tp 1.11401, Magic number 39387259 (123), G1: 9, G2: 5, ID: 2
2019.12.12 17:48:24.771 OnDoEasyEvent: Pending order removed
2019.12.12 17:48:24.771 OnDoEasyEvent: Pending order removed
2019.12.12 17:48:25.024 Trade request ID: #2: Pending request deleted due to execution
2019.12.12 17:48:25.024 Trade request ID: #1: Pending request deleted due to execution

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

Это не последний и окончательный вариант объектов-отложенных запросов.
Это лишь проверка предложенной концепции для MQL5, и при этом на счёте с типом "хедж".

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

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

Что дальше

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

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

К содержанию

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

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

Прикрепленные файлы |
MQL5.zip (3640.49 KB)
MQL4.zip (3640.49 KB)
Библиотека для простого и быстрого создания программ для MetaTrader (Часть XXVII): Работа с торговыми запросами - выставление отложенных ордеров Библиотека для простого и быстрого создания программ для MetaTrader (Часть XXVII): Работа с торговыми запросами - выставление отложенных ордеров

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

Исследование сезонных характеристик финансовых временных рядов при помощи диаграмм Boxplot Исследование сезонных характеристик финансовых временных рядов при помощи диаграмм Boxplot

Исследование сезонных характеристик финансовых временных рядов при помощи диаграмм Boxplot. Каждый отдельный ящик с усами дает хорошее представление о том, как распределены значения в наборе данных. Boxplots не следует путать с графиком японских свечей, хотя они визуально похожи.

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

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

Непрерывная скользящая оптимизация (Часть 2): Механизм создания отчета оптимизации для любого робота Непрерывная скользящая оптимизация (Часть 2): Механизм создания отчета оптимизации для любого робота

Если прошлая статья повествовала о создании DLL-библиотеки, которая будет использоваться в нашем автооптимизаторе и в роботе, то продолжение будет целиком посвящено языку MQL5.