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

6 февраля 2020, 11:15
Artyom Trishkin
0
1 219

Содержание

Концепция

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

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

В данный момент у нас уже есть возможность создать дополнительные типы отложенных ордеров. Например, для MQL4 можно создать StopLimit-ордер. Но его созданием, вернее — созданием объекта-StopLimit-ордера для MQL4, я планирую заняться при наличии достаточного функционала библиотеки.
В итоге можно будет создать совсем новые типы отложенных ордеров, таких как, например, BuyTime, SellTime, BuyTimeStop, SellTimeStop, и т.д. ордеры.
Для создания полноценных пользовательских ордеров пока не хватает некоторых графических построений. И, соответственно, мы вернёмся к этой теме уже при наличии такого функционала в библиотеке.

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

Реализация

Для вывода описаний отложенных ордеров у нас, как оказалось, нет функции, которая просто выведет его название. Но есть функция OrderTypeDescription(), выводящая его описание+название. Нужно просто из результата возврата данной функции убрать текст описания, оставив только название ордера.
В файле сервисных функций \MQL5\Include\DoEasy\Services\DELib.mqh доработаем функцию, возвращающую наименование ордера:

//+------------------------------------------------------------------+
//| Возвращает наименование ордера                                   |
//+------------------------------------------------------------------+
string OrderTypeDescription(const ENUM_ORDER_TYPE type,bool as_order=true,bool prefix_for_market_order=true,bool descr=true)
  {
   string pref=
     (
      !prefix_for_market_order ? "" :
      #ifdef __MQL5__ CMessage::Text(MSG_ORD_MARKET) 
      #else/*__MQL4__*/(as_order ? CMessage::Text(MSG_ORD_MARKET) : CMessage::Text(MSG_ORD_POSITION)) #endif 
     );
   return
     (
      type==ORDER_TYPE_BUY_LIMIT       ?  (descr ? CMessage::Text(MSG_ORD_PENDING) : "")+" Buy Limit"       :
      type==ORDER_TYPE_BUY_STOP        ?  (descr ? CMessage::Text(MSG_ORD_PENDING) : "")+" Buy Stop"        :
      type==ORDER_TYPE_SELL_LIMIT      ?  (descr ? CMessage::Text(MSG_ORD_PENDING) : "")+" Sell Limit"      :
      type==ORDER_TYPE_SELL_STOP       ?  (descr ? CMessage::Text(MSG_ORD_PENDING) : "")+" Sell Stop"       :
   #ifdef __MQL5__
      type==ORDER_TYPE_BUY_STOP_LIMIT  ?  (descr ? CMessage::Text(MSG_ORD_PENDING) : "")+" Buy Stop Limit"  :
      type==ORDER_TYPE_SELL_STOP_LIMIT ?  (descr ? CMessage::Text(MSG_ORD_PENDING) : "")+" Sell Stop Limit" :
      type==ORDER_TYPE_CLOSE_BY        ?  CMessage::Text(MSG_ORD_CLOSE_BY)                                  :  
   #else 
      type==ORDER_TYPE_BALANCE         ?  CMessage::Text(MSG_LIB_PROP_BALANCE)                              :
      type==ORDER_TYPE_CREDIT          ?  CMessage::Text(MSG_LIB_PROP_CREDIT)                               :
   #endif 
      type==ORDER_TYPE_BUY             ?  pref+" Buy"                                                       :
      type==ORDER_TYPE_SELL            ?  pref+" Sell"                                                      :  
      CMessage::Text(MSG_ORD_UNKNOWN_TYPE)
     );
  }
//+------------------------------------------------------------------+

Функция всегда возвращает перед типом отложенного ордера текст "отложенный ордер".
Чтобы функция могла выводить просто описание типа ордера без этого предварительного текста, добавлен флаг, указывающий на необходимость вывода текста "отложенный ордер". Таким образом, если флаг снят (значение равно false), то предварительный текст "отложенный ордер" выводиться не будет.

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

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

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

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

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

Изменения в классе объекта-отложенного запроса на изменения уровней StopLoss и/или TakeProfit позиции:

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

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

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

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

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

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

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

В файле TradingControl.mqh класса управления торговлей CTradingControl, в его публичной секции впишем объявление методов создания отложенного запроса на удаление отложенного ордера, на модификацию StopLoss/TakeProfit позиции и на модификацию параметров отложенного ордера:

//--- Создаёт отложенный запрос (1) на полное и частичное закрытие позиции, (2) на закрытие позиции встречной, (3) на удаление ордера
   int                  CreatePReqClose(const ulong ticket,const double volume=WRONG_VALUE,const string comment=NULL,const ulong deviation=ULONG_MAX);
   int                  CreatePReqCloseBy(const ulong ticket,const ulong ticket_by);
   int                  CreatePreqDelete(const ulong ticket);

//--- Создаёт отложенный запрос на модификацию (1) стоп-приказов позиции, (2) ордера
   template<typename SL,typename TP> 
   int                  CreatePReqModifyPosition(const ulong ticket,const SL sl=WRONG_VALUE,const TP tp=WRONG_VALUE);
   template<typename PS,typename PL,typename SL,typename TP>
   int                  CreatePReqModifyOrder(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                 SetNewActivationProperties(const uchar id,
                                                   const ENUM_PEND_REQ_ACTIVATION_SOURCE source,
                                                   const int property,
                                                   const double control_value,
                                                   const ENUM_COMPARER_TYPE comparer_type,
                                                   const double actual_value);
  };
//+------------------------------------------------------------------+

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

//+------------------------------------------------------------------+
//| Создаёт отложенный запрос на удаление отложенного ордера         |
//+------------------------------------------------------------------+
int CTradingControl::CreatePreqDelete(const ulong ticket)
  {
//--- Если установлен флаг глобального запрета торговли - выходим с возвратом WRONG_VALUE
   if(this.IsTradingDisable())
     {
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_TRADING_DISABLE));
      return WRONG_VALUE;
     }
//--- Устанавливаем флаг ошибки как "нет ошибок"
   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 WRONG_VALUE;
     }
   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 WRONG_VALUE;
     }
//--- получаем торговый объект из объекта-символа
   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 WRONG_VALUE;
     }
//--- Обновляем котировки по символу
   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 WRONG_VALUE;
     }
     
//--- Ищем наименьший из возможных идентификаторов, и если найти не удалось - возвращаем WRONG_VALUE
   int id=this.GetFreeID();
   if(id<1)
     {
      //--- Нет свободных идентификаторов для создания отложенного запроса
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_NO_FREE_IDS));
      return WRONG_VALUE;
     }

   //--- Устанавливаем тип торговой операции, символ и тикет удаляемого ордера в структуру запроса
   this.m_request.action=TRADE_ACTION_REMOVE;
   this.m_request.symbol=symbol_obj.Name();
   this.m_request.order=ticket;
   this.m_request.type=order_type;
   this.m_request.volume=order.Volume();
   this.m_request.price=order.PriceOpen();
//--- В результате создания отложенного торгового запроса возвращаем либо его идентификатор, либо -1 при неудаче
   if(this.CreatePendingRequest(PEND_REQ_STATUS_REMOVE,(uchar)id,1,ulong(END_TIME-(ulong)::TimeCurrent()),this.m_request,0,symbol_obj,order))
      return id;
   return WRONG_VALUE;
  }
//+------------------------------------------------------------------+
//| Создаёт отложенный запрос на модификацию стоп-приказов позиции   |
//+------------------------------------------------------------------+
template<typename SL,typename TP> 
int CTradingControl::CreatePReqModifyPosition(const ulong ticket,const SL sl=WRONG_VALUE,const TP tp=WRONG_VALUE)
  {
//--- Если установлен флаг глобального запрета торговли - выходим с возвратом WRONG_VALUE
   if(this.IsTradingDisable())
     {
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_TRADING_DISABLE));
      return WRONG_VALUE;
     }
//--- Устанавливаем флаг ошибки как "нет ошибок"
   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 WRONG_VALUE;
     }
   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 WRONG_VALUE;
     }
//--- Получаем торговый объект из объекта-символа
   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 WRONG_VALUE;
     }
//--- Устанавливаем цены
//--- Если установить не удалось - записываем флаг "внутренняя ошибка" устанавливаем код ошибки в структуру возврата,
//--- выводим сообщение в журнал и возвращаем 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 WRONG_VALUE;
     }
//--- Ищем наименьший из возможных идентификаторов, и если найти не удалось - возвращаем false
   int id=this.GetFreeID();
   if(id<1)
      return WRONG_VALUE;
            
//--- Записываем тип проводимой операции, символ и тикет модифицируемой позиции в структуру запроса
   this.m_request.action=TRADE_ACTION_SLTP;
   this.m_request.symbol=symbol_obj.Name();
   this.m_request.position=ticket;
   this.m_request.type=order_type;
   this.m_request.volume=order.Volume();
//--- В результате создания отложенного торгового запроса возвращаем либо его идентификатор, либо -1 при неудаче
   if(this.CreatePendingRequest(PEND_REQ_STATUS_SLTP,(uchar)id,1,ulong(END_TIME-(ulong)::TimeCurrent()),this.m_request,0,symbol_obj,order))
      return id;
   return WRONG_VALUE;
  }
//+------------------------------------------------------------------+
//| Создаёт отложенный запрос на модификацию отложенного ордера      |
//+------------------------------------------------------------------+
template<typename PS,typename PL,typename SL,typename TP>
int CTradingControl::CreatePReqModifyOrder(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)
  {
//--- Если установлен флаг глобального запрета торговли - выходим с возвратом WRONG_VALUE
   if(this.IsTradingDisable())
     {
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_TRADING_DISABLE));
      return WRONG_VALUE;
     }
//--- Устанавливаем флаг ошибки как "нет ошибок"
   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;
     }
//--- Ищем наименьший из возможных идентификаторов, и если найти не удалось - возвращаем false
   int id=this.GetFreeID();
   if(id<1)
      return WRONG_VALUE;
            
//--- Записываем магик, объём, тип заливки, дату и тип экспирации в структуру запроса
   this.m_request.magic=order.GetMagicID((uint)order.Magic());
   this.m_request.volume=order.Volume();
   this.m_request.type_filling=(type_filling>WRONG_VALUE ? type_filling : order.TypeFilling());
   this.m_request.expiration=(expiration>WRONG_VALUE ? expiration : order.TimeExpiration());
   this.m_request.type_time=(type_time>WRONG_VALUE ? type_time : order.TypeTime());

   //--- Устанавливаем тип торговой операции, символ и тикет модифицируемого ордера в структуру запроса
   this.m_request.action=TRADE_ACTION_MODIFY;
   this.m_request.symbol=symbol_obj.Name();
   this.m_request.order=ticket;
   this.m_request.type=order_type;
//--- В результате создания отложенного торгового запроса возвращаем либо его идентификатор, либо -1 при неудаче
   if(this.CreatePendingRequest(PEND_REQ_STATUS_MODIFY,(uchar)id,1,ulong(END_TIME-(ulong)::TimeCurrent()),this.m_request,0,symbol_obj,order))
      return id;
   return WRONG_VALUE;
  }
//+------------------------------------------------------------------+

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

Теперь добавим доступ из программы к созданным методам. Для этого пропишем вызов этих методов из методов класса основного объекта библиотеки.
Впишем в класс CEngine объявление методов создания отложенных запросов на удаление отложенного ордера, на модификацию StopLoss/TakeProfit позиции и на модификацию параметров отложенного ордера:

//--- Создаёт отложенный запрос на закрытие позиции (1) полностью, (2) частично, (3) встречной, (4) на удаление ордера
   int                  ClosePositionPending(const ulong ticket,const string comment=NULL,const ulong deviation=ULONG_MAX);
   int                  ClosePositionPartiallyPending(const ulong ticket,const double volume,const string comment=NULL,const ulong deviation=ULONG_MAX);
   int                  ClosePositionByPending(const ulong ticket,const ulong ticket_by);
   int                  DeleteOrderPending(const ulong ticket);

//--- Создаёт отложенный запрос на модификацию (1 позиции), (2) ордера
   template<typename SL,typename TP> 
   int                  ModifyPositionPending(const ulong ticket,const SL sl=WRONG_VALUE,const TP tp=WRONG_VALUE,const string comment=NULL);
   template<typename PR,typename SL,typename TP,typename PL>
   int                  ModifyOrderPending(const ulong ticket,
                                           const PR price=WRONG_VALUE,
                                           const SL sl=WRONG_VALUE,
                                           const TP tp=WRONG_VALUE,
                                           const PL stoplimit=WRONG_VALUE,
                                           datetime expiration=WRONG_VALUE,
                                           const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE,
                                           const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE);
                                   

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

//+------------------------------------------------------------------+
//| Создаёт отложенный запрос на удаление отложенного ордера         |
//+------------------------------------------------------------------+
int CEngine::DeleteOrderPending(const ulong ticket)
  {
   return this.m_trading.CreatePreqDelete(ticket);
  }
//+------------------------------------------------------------------+
//| Создаёт отложенный запрос на модификацию позиции                 |
//+------------------------------------------------------------------+
template<typename SL,typename TP>
int CEngine::ModifyPositionPending(const ulong ticket,const SL sl=WRONG_VALUE,const TP tp=WRONG_VALUE,const string comment=NULL)
  {
   return this.m_trading.CreatePReqModifyPosition(ticket,sl,tp);
  }
//+------------------------------------------------------------------+
//| Создаёт отложенный запрос на модификацию ордера                  |
//+------------------------------------------------------------------+
template<typename PR,typename SL,typename TP,typename PL>
int CEngine::ModifyOrderPending(const ulong ticket,
                                const PR price=WRONG_VALUE,
                                const SL sl=WRONG_VALUE,
                                const TP tp=WRONG_VALUE,
                                const PL stoplimit=WRONG_VALUE,
                                datetime expiration=WRONG_VALUE,
                                const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE,
                                const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE)
  {
   return this.m_trading.CreatePReqModifyOrder(ticket,price,sl,tp,stoplimit,expiration,type_time,type_filling);
  }
//+------------------------------------------------------------------+

Методы просто возвращают результат вызова соответствующих методов создания отложенных запросов класса CTradingControl, написанных нами выше.

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

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

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

Для этого советника мы так же, как и для прошлых советников по тестированию работы с отложенными запросами, создадим кнопки активизации режимов работы при помощи отложенных запросов — для кнопок удаления всех отложенных ордеров (Delete pending), закрытия всех позиций (Close all), и кнопок установки StopLoss и TakeProfit ордерам и позициям, не имеющих этих стоп-уровней (Set StopLoss и Set TakeProfit).

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

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

//+------------------------------------------------------------------+
//|                                             TestDoEasyPart34.mq5 |
//|                        Copyright 2018, 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"
//--- includes
#include <DoEasy\Engine.mqh>
//--- enums
enum ENUM_BUTTONS
  {
   BUTT_BUY,
   BUTT_BUY_LIMIT,
   BUTT_BUY_STOP,
   BUTT_BUY_STOP_LIMIT,
   BUTT_CLOSE_BUY,
   BUTT_CLOSE_BUY2,
   BUTT_CLOSE_BUY_BY_SELL,
   BUTT_SELL,
   BUTT_SELL_LIMIT,
   BUTT_SELL_STOP,
   BUTT_SELL_STOP_LIMIT,
   BUTT_CLOSE_SELL,
   BUTT_CLOSE_SELL2,
   BUTT_CLOSE_SELL_BY_BUY,
   BUTT_DELETE_PENDING,
   BUTT_CLOSE_ALL,
   BUTT_SET_STOP_LOSS,
   BUTT_SET_TAKE_PROFIT,
   BUTT_PROFIT_WITHDRAWAL,
   BUTT_TRAILING_ALL
  };
#define TOTAL_BUTT   (20)

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

//--- global variables
CEngine        engine;
SDataButt      butt_data[TOTAL_BUTT];
string         prefix;
double         lot;
double         withdrawal=(InpWithdrawal<0.1 ? 0.1 : InpWithdrawal);
ushort         magic_number;
uint           stoploss;
uint           takeprofit;
uint           distance_pending;
uint           distance_stoplimit;
uint           distance_pending_request;
uint           bars_delay_pending_request;
uint           slippage;
bool           trailing_on;
bool           pressed_pending_buy;
bool           pressed_pending_buy_limit;
bool           pressed_pending_buy_stop;
bool           pressed_pending_buy_stoplimit;
bool           pressed_pending_close_buy;
bool           pressed_pending_close_buy2;
bool           pressed_pending_close_buy_by_sell;
bool           pressed_pending_sell;
bool           pressed_pending_sell_limit;
bool           pressed_pending_sell_stop;
bool           pressed_pending_sell_stoplimit;
bool           pressed_pending_close_sell;
bool           pressed_pending_close_sell2;
bool           pressed_pending_close_sell_by_buy;
bool           pressed_pending_delete_all;
bool           pressed_pending_close_all;
bool           pressed_pending_sl;
bool           pressed_pending_tp;
double         trailing_stop;
double         trailing_step;
uint           trailing_start;
uint           stoploss_to_modify;
uint           takeprofit_to_modify;
int            used_symbols_mode;
string         used_symbols;
string         array_used_symbols[];
bool           testing;
uchar          group1;
uchar          group2;
double         g_point;
int            g_digits;
//+------------------------------------------------------------------+

В обработчике OnInit() советника присвоим значения Point и Digits текущего символа соответствующим переменным:

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

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

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

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

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

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

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

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

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

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

//+------------------------------------------------------------------+
//| Обработка нажатий кнопок                                         |
//+------------------------------------------------------------------+
void PressButtonEvents(const string button_name)
  {
   bool comp_magic=true;   // Временная переменная для выбора использования составного магика со случайными идентификаторами групп
   string comment="";
   //--- Преобразуем имя кнопки в её строковый идентификатор
   string button=StringSubstr(button_name,StringLen(prefix));
   //--- Случайные номера групп 1 и 2 в диапазоне 0 - 15
   group1=(uchar)Rand();
   group2=(uchar)Rand();
   uint magic=(comp_magic ? engine.SetCompositeMagicNumber(magic_number,group1,group2) : magic_number);
   //--- Если кнопка в нажатом состоянии
   if(ButtonState(button_name))
     {
      //--- Если нажата кнопка BUTT_BUY: Открыть позицию Buy
      if(button==EnumToString(BUTT_BUY))
        {
         //--- Если не нажаты кнопки создания отложенного запроса - открываем позицию Buy 
         if(!pressed_pending_buy)
            engine.OpenBuy(lot,Symbol(),magic,stoploss,takeprofit);   // Нет комментария - будет установлен комментарий по умолчанию
         //--- Иначе - создаём отложенный запрос на открытие позиции Buy
         else
           {
            int id=engine.OpenBuyPending(lot,Symbol(),magic,stoploss,takeprofit);
            if(id>0)
              {
               //--- устанавливаем цену и время активации отложенного запроса и устанавливаем параметры активации
               double ask=SymbolInfoDouble(NULL,SYMBOL_ASK);
               double price_activation=NormalizeDouble(ask-distance_pending_request*g_point,g_digits);
               ulong  time_activation=TimeCurrent()+bars_delay_pending_request*PeriodSeconds();
               SetPReqCriterion((uchar)id,price_activation,time_activation,BUTT_BUY,EQUAL_OR_LESS,ask,TimeCurrent());
              }
           }
        }
      //--- Если нажата кнопка BUTT_BUY_LIMIT: Выставить BuyLimit
      else if(button==EnumToString(BUTT_BUY_LIMIT))
        {
         //--- Если не нажаты кнопки создания отложенного запроса - устанавливаем ордер BuyLimit
         if(!pressed_pending_buy_limit)
            engine.PlaceBuyLimit(lot,Symbol(),distance_pending,stoploss,takeprofit,magic,TextByLanguage("Отложенный BuyLimit","Pending order BuyLimit"));
         //--- Иначе - создаём отложенный запрос на установку ордера BuyLimit со значением дистанции установки
         //--- и задаём условия в зависимости от активных кнопок
         else
           {
            int id=engine.PlaceBuyLimitPending(lot,Symbol(),distance_pending,stoploss,takeprofit,magic);
            if(id>0)
              {
               //--- устанавливаем цену и время активации отложенного запроса и устанавливаем параметры активации
               double ask=SymbolInfoDouble(NULL,SYMBOL_ASK);
               double price_activation=NormalizeDouble(ask-distance_pending_request*g_point,g_digits);
               ulong  time_activation=TimeCurrent()+bars_delay_pending_request*PeriodSeconds();
               SetPReqCriterion((uchar)id,price_activation,time_activation,BUTT_BUY_LIMIT,EQUAL_OR_LESS,ask,TimeCurrent());
              }
           }
        }
      //--- Если нажата кнопка BUTT_BUY_STOP: Выставить BuyStop
      else if(button==EnumToString(BUTT_BUY_STOP))
        {
         //--- Если не нажаты кнопки создания отложенного запроса - устанавливаем ордер BuyStop
         if(!pressed_pending_buy_stop)
            engine.PlaceBuyStop(lot,Symbol(),distance_pending,stoploss,takeprofit,magic,TextByLanguage("Отложенный BuyStop","Pending order BuyStop"));
         //--- Иначе - создаём отложенный запрос на установку ордера BuyStop со значением дистанции установки
         //--- и задаём условия в зависимости от активных кнопок
         else
           {
            int id=engine.PlaceBuyStopPending(lot,Symbol(),distance_pending,stoploss,takeprofit,magic);
            if(id>0)
              {
               //--- устанавливаем цену и время активации отложенного запроса и устанавливаем параметры активации
               double ask=SymbolInfoDouble(NULL,SYMBOL_ASK);
               double price_activation=NormalizeDouble(ask-distance_pending_request*g_point,g_digits);
               ulong  time_activation=TimeCurrent()+bars_delay_pending_request*PeriodSeconds();
               SetPReqCriterion((uchar)id,price_activation,time_activation,BUTT_BUY_STOP,EQUAL_OR_LESS,ask,TimeCurrent());
              }
           }
        }
      //--- Если нажата кнопка BUTT_BUY_STOP_LIMIT: Выставить BuyStopLimit
      else if(button==EnumToString(BUTT_BUY_STOP_LIMIT))
        {
         //--- Если не нажаты кнопки создания отложенного запроса - устанавливаем ордер BuyStopLimit
         if(!pressed_pending_buy_stoplimit)
            engine.PlaceBuyStopLimit(lot,Symbol(),distance_pending,distance_stoplimit,stoploss,takeprofit,magic,TextByLanguage("Отложенный BuyStopLimit","Pending order BuyStopLimit"));
         //--- Иначе - создаём отложенный запрос на установку ордера BuyStopLimit со значениями дистанций установки
         //--- и задаём условия в зависимости от активных кнопок
         else
           {
            int id=engine.PlaceBuyStopLimitPending(lot,Symbol(),distance_pending,distance_stoplimit,stoploss,takeprofit,magic);
            if(id>0)
              {
               //--- устанавливаем цену и время активации отложенного запроса и устанавливаем параметры активации
               double ask=SymbolInfoDouble(NULL,SYMBOL_ASK);
               double price_activation=NormalizeDouble(ask-distance_pending_request*g_point,g_digits);
               ulong  time_activation=TimeCurrent()+bars_delay_pending_request*PeriodSeconds();
               SetPReqCriterion((uchar)id,price_activation,time_activation,BUTT_BUY_STOP_LIMIT,EQUAL_OR_LESS,ask,TimeCurrent());
              }
           }
        }
      //--- Если нажата кнопка BUTT_SELL: Открыть позицию Sell
      else if(button==EnumToString(BUTT_SELL))
        {
         //--- Если не нажаты кнопки создания отложенного запроса - открываем позицию Sell
         if(!pressed_pending_sell)
            engine.OpenSell(lot,Symbol(),magic,stoploss,takeprofit);  // Нет комментария - будет установлен комментарий по умолчанию
         //--- Иначе - создаём отложенный запрос на открытие позиции Sell
         else
           {
            int id=engine.OpenSellPending(lot,Symbol(),magic,stoploss,takeprofit);
            if(id>0)
              {
               //--- устанавливаем цену и время активации отложенного запроса и устанавливаем параметры активации
               double bid=SymbolInfoDouble(NULL,SYMBOL_BID);
               double price_activation=NormalizeDouble(bid+distance_pending_request*g_point,g_digits);
               ulong  time_activation=TimeCurrent()+bars_delay_pending_request*PeriodSeconds();
               SetPReqCriterion((uchar)id,price_activation,time_activation,BUTT_SELL,EQUAL_OR_MORE,bid,TimeCurrent());
              }
           }
        }
      //--- Если нажата кнопка BUTT_SELL_LIMIT: Выставить SellLimit
      else if(button==EnumToString(BUTT_SELL_LIMIT))
        {
         //--- Если не нажаты кнопки создания отложенного запроса - устанавливаем ордер SellLimit
         if(!pressed_pending_sell_limit)
            engine.PlaceSellLimit(lot,Symbol(),distance_pending,stoploss,takeprofit,magic,TextByLanguage("Отложенный SellLimit","Pending order SellLimit"));
         //--- Иначе - создаём отложенный запрос на установку ордера SellLimit со значением дистанции установки
         //--- и задаём условия в зависимости от активных кнопок
         else
           {
            int id=engine.PlaceSellLimitPending(lot,Symbol(),distance_pending,stoploss,takeprofit,magic);
            if(id>0)
              {
               //--- устанавливаем цену и время активации отложенного запроса и устанавливаем параметры активации
               double bid=SymbolInfoDouble(NULL,SYMBOL_BID);
               double price_activation=NormalizeDouble(bid+distance_pending_request*g_point,g_digits);
               ulong  time_activation=TimeCurrent()+bars_delay_pending_request*PeriodSeconds();
               SetPReqCriterion((uchar)id,price_activation,time_activation,BUTT_SELL_LIMIT,EQUAL_OR_MORE,bid,TimeCurrent());
              }
           }
        }
      //--- Если нажата кнопка BUTT_SELL_STOP: Выставить SellStop
      else if(button==EnumToString(BUTT_SELL_STOP))
        {
         //--- Если не нажаты кнопки создания отложенного запроса - устанавливаем ордер SellStop
         if(!pressed_pending_sell_stop)
            engine.PlaceSellStop(lot,Symbol(),distance_pending,stoploss,takeprofit,magic,TextByLanguage("Отложенный SellStop","Pending order SellStop"));
         //--- Иначе - создаём отложенный запрос на установку ордера SellStop со значением дистанции установки
         //--- и задаём условия в зависимости от активных кнопок
         else
           {
            int id=engine.PlaceSellStopPending(lot,Symbol(),distance_pending,stoploss,takeprofit,magic);
            if(id>0)
              {
               //--- устанавливаем цену и время активации отложенного запроса и устанавливаем параметры активации
               double bid=SymbolInfoDouble(NULL,SYMBOL_BID);
               double price_activation=NormalizeDouble(bid+distance_pending_request*g_point,g_digits);
               ulong  time_activation=TimeCurrent()+bars_delay_pending_request*PeriodSeconds();
               SetPReqCriterion((uchar)id,price_activation,time_activation,BUTT_SELL_STOP,EQUAL_OR_MORE,bid,TimeCurrent());
              }
           }
        }
      //--- Если нажата кнопка BUTT_SELL_STOP_LIMIT: Выставить SellStopLimit
      else if(button==EnumToString(BUTT_SELL_STOP_LIMIT))
        {
         //--- Если не нажаты кнопки создания отложенного запроса - устанавливаем ордер SellStopLimit
         if(!pressed_pending_sell_stoplimit)
            engine.PlaceSellStopLimit(lot,Symbol(),distance_pending,distance_stoplimit,stoploss,takeprofit,magic,TextByLanguage("Отложенный SellStopLimit","Pending order SellStopLimit"));
         //--- Иначе - создаём отложенный запрос на установку ордера SellStopLimit со значениями дистанций установки
         //--- и задаём условия в зависимости от активных кнопок
         else
           {
            int id=engine.PlaceSellStopLimitPending(lot,Symbol(),distance_pending,distance_stoplimit,stoploss,takeprofit,magic);
            if(id>0)
              {
               //--- устанавливаем цену и время активации отложенного запроса и устанавливаем параметры активации
               double bid=SymbolInfoDouble(NULL,SYMBOL_BID);
               double price_activation=NormalizeDouble(bid+distance_pending_request*g_point,g_digits);
               ulong  time_activation=TimeCurrent()+bars_delay_pending_request*PeriodSeconds();
               SetPReqCriterion((uchar)id,price_activation,time_activation,BUTT_SELL_STOP_LIMIT,EQUAL_OR_MORE,bid,TimeCurrent());
              }
           }
        }
      //--- Если нажата кнопка BUTT_CLOSE_BUY: Закрыть Buy с максимальной прибылью
      else if(button==EnumToString(BUTT_CLOSE_BUY))
        {
         //--- Получаем список всех открытых позиций
         CArrayObj* list=engine.GetListMarketPosition();
         //--- Выбираем из списка только позиции Buy и только по текущему символу
         list=CSelect::ByOrderProperty(list,ORDER_PROP_SYMBOL,Symbol(),EQUAL);
         list=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_BUY,EQUAL);
         //--- Сортируем список по прибыли с учётом комиссии и свопа
         list.Sort(SORT_BY_ORDER_PROFIT_FULL);
         //--- Получаем индекс позиции Buy с наибольшей прибылью
         int index=CSelect::FindOrderMax(list,ORDER_PROP_PROFIT_FULL);
         if(index>WRONG_VALUE)
           {
            //--- Получаем объект-позицию Buy и закрываем позицию по тикету
            COrder* position=list.At(index);
            if(position!=NULL)
              {
               //--- Если не нажаты кнопки создания отложенного запроса - закрываем позицию
               if(!pressed_pending_close_buy)
                  engine.ClosePosition((ulong)position.Ticket());
               //--- Иначе - создаём отложенный запрос на закрытие позиции по тикету
               //--- и задаём условия в зависимости от активных кнопок
               else
                 {
                  int id=engine.ClosePositionPending(position.Ticket());
                  if(id>0)
                    {
                     //--- устанавливаем цену и время активации отложенного запроса и устанавливаем параметры активации
                     double bid=SymbolInfoDouble(NULL,SYMBOL_BID);
                     double price_activation=NormalizeDouble(bid+distance_pending_request*g_point,g_digits);
                     ulong  time_activation=TimeCurrent()+bars_delay_pending_request*PeriodSeconds();
                     SetPReqCriterion((uchar)id,price_activation,time_activation,BUTT_CLOSE_BUY,EQUAL_OR_MORE,bid,TimeCurrent());
                    }
                 }
              }
           }
        }
      //--- Если нажата кнопка BUTT_CLOSE_BUY2: Закрыть половину Buy с максимальной прибылью
      else if(button==EnumToString(BUTT_CLOSE_BUY2))
        {
         //--- Получаем список всех открытых позиций
         CArrayObj* list=engine.GetListMarketPosition();
         //--- Выбираем из списка только позиции Buy и только по текущему символу
         list=CSelect::ByOrderProperty(list,ORDER_PROP_SYMBOL,Symbol(),EQUAL);
         list=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_BUY,EQUAL);
         //--- Сортируем список по прибыли с учётом комиссии и свопа
         list.Sort(SORT_BY_ORDER_PROFIT_FULL);
         //--- Получаем индекс позиции Buy с наибольшей прибылью
         int index=CSelect::FindOrderMax(list,ORDER_PROP_PROFIT_FULL);
         if(index>WRONG_VALUE)
           {
            //--- Получаем объект-позицию Buy и закрываем позицию по тикету
            COrder* position=list.At(index);
            if(position!=NULL)
              {
               //--- Если не нажаты кнопки создания отложенного запроса - закрываем позицию по тикету
               if(!pressed_pending_close_buy2)
                  engine.ClosePositionPartially((ulong)position.Ticket(),position.Volume()/2.0);
               //--- Иначе - создаём отложенный запрос на частичное закрытие позиции по тикету
               //--- и задаём условия в зависимости от активных кнопок
               else
                 {
                  int id=engine.ClosePositionPartiallyPending(position.Ticket(),position.Volume()/2.0);
                  if(id>0)
                    {
                     //--- устанавливаем цену и время активации отложенного запроса и устанавливаем параметры активации
                     double bid=SymbolInfoDouble(NULL,SYMBOL_BID);
                     double price_activation=NormalizeDouble(bid+distance_pending_request*g_point,g_digits);
                     ulong  time_activation=TimeCurrent()+bars_delay_pending_request*PeriodSeconds();
                     SetPReqCriterion((uchar)id,price_activation,time_activation,BUTT_CLOSE_BUY2,EQUAL_OR_MORE,bid,TimeCurrent());
                    }
                 }
              }
           }
        }
      //--- Если нажата кнопка BUTT_CLOSE_BUY_BY_SELL: Закрыть Buy с максимальной прибылью встречной Sell с максимальной прибылью
      else if(button==EnumToString(BUTT_CLOSE_BUY_BY_SELL))
        {
         //--- Если счёт хеджевый
         if(engine.IsHedge())
           {
            CArrayObj *list_buy=NULL, *list_sell=NULL;
            //--- Получаем список всех открытых позиций
            CArrayObj* list=engine.GetListMarketPosition();
            if(list==NULL)
               return;
            //--- Выбираем из списка только позиции по текущему символу
            list=CSelect::ByOrderProperty(list,ORDER_PROP_SYMBOL,Symbol(),EQUAL);
            
            //--- Выбираем из списка только позиции Buy
            list_buy=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_BUY,EQUAL);
            if(list_buy==NULL)
               return;
            //--- Сортируем список по прибыли с учётом комиссии и свопа
            list_buy.Sort(SORT_BY_ORDER_PROFIT_FULL);
            //--- Получаем индекс позиции Buy с наибольшей прибылью
            int index_buy=CSelect::FindOrderMax(list_buy,ORDER_PROP_PROFIT_FULL);
            
            //--- Выбираем из списка только позиции Sell
            list_sell=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_SELL,EQUAL);
            if(list_sell==NULL)
               return;
            //--- Сортируем список по прибыли с учётом комиссии и свопа
            list_sell.Sort(SORT_BY_ORDER_PROFIT_FULL);
            //--- Получаем индекс позиции Sell с наибольшей прибылью
            int index_sell=CSelect::FindOrderMax(list_sell,ORDER_PROP_PROFIT_FULL);
            if(index_buy>WRONG_VALUE && index_sell>WRONG_VALUE)
              {
               //--- Выбираем позицию Buy с наибольшей прибылью
               COrder* position_buy=list_buy.At(index_buy);
               //--- Выбираем позицию Sell с наибольшей прибылью
               COrder* position_sell=list_sell.At(index_sell);
               if(position_buy!=NULL && position_sell!=NULL)
                 {
                  //--- Если не нажаты кнопки создания отложенного запроса - закрываем позиции по тикету
                  if(!pressed_pending_close_buy_by_sell)
                     engine.ClosePositionBy((ulong)position_buy.Ticket(),(ulong)position_sell.Ticket());
                  //--- Иначе - создаём отложенный запрос на закрытие позиции Buy встречной позицией Sell
                  //--- и задаём условия в зависимости от активных кнопок
                  else
                    {
                     int id=engine.ClosePositionByPending(position_buy.Ticket(),position_sell.Ticket());
                     if(id>0)
                       {
                        //--- устанавливаем цену и время активации отложенного запроса и устанавливаем параметры активации
                        double bid=SymbolInfoDouble(NULL,SYMBOL_BID);
                        double price_activation=NormalizeDouble(bid+distance_pending_request*g_point,g_digits);
                        ulong  time_activation=TimeCurrent()+bars_delay_pending_request*PeriodSeconds();
                        SetPReqCriterion((uchar)id,price_activation,time_activation,BUTT_CLOSE_BUY_BY_SELL,EQUAL_OR_MORE,bid,TimeCurrent());
                       }
                    }
                 }
              }
           }
        }
        
      //--- Если нажата кнопка BUTT_CLOSE_SELL: Закрыть Sell с максимальной прибылью
      else if(button==EnumToString(BUTT_CLOSE_SELL))
        {
         //--- Получаем список всех открытых позиций
         CArrayObj* list=engine.GetListMarketPosition();
         //--- Выбираем из списка только позиции Sell и только по текущему символу
         list=CSelect::ByOrderProperty(list,ORDER_PROP_SYMBOL,Symbol(),EQUAL);
         list=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_SELL,EQUAL);
         //--- Сортируем список по прибыли с учётом комиссии и свопа
         list.Sort(SORT_BY_ORDER_PROFIT_FULL);
         //--- Получаем индекс позиции Sell с наибольшей прибылью
         int index=CSelect::FindOrderMax(list,ORDER_PROP_PROFIT_FULL);
         if(index>WRONG_VALUE)
           {
            //--- Получаем объект-позицию Sell и закрываем позицию по тикету
            COrder* position=list.At(index);
            if(position!=NULL)
              {
               //--- Если не нажаты кнопки создания отложенного запроса - закрываем позицию
               if(!pressed_pending_close_sell)
                  engine.ClosePosition((ulong)position.Ticket());
               //--- Иначе - создаём отложенный запрос на закрытие позиции по тикету
               //--- и задаём условия в зависимости от активных кнопок
               else
                 {
                  int id=engine.ClosePositionPending(position.Ticket());
                  if(id>0)
                    {
                     //--- устанавливаем цену и время активации отложенного запроса и устанавливаем параметры активации
                     double ask=SymbolInfoDouble(NULL,SYMBOL_ASK);
                     double price_activation=NormalizeDouble(ask-distance_pending_request*g_point,g_digits);
                     ulong  time_activation=TimeCurrent()+bars_delay_pending_request*PeriodSeconds();
                     SetPReqCriterion((uchar)id,price_activation,time_activation,BUTT_CLOSE_SELL,EQUAL_OR_LESS,ask,TimeCurrent());
                    }
                 }
              }
           }
        }
      //--- Если нажата кнопка BUTT_CLOSE_SELL2: Закрыть половину Sell с максимальной прибылью
      else if(button==EnumToString(BUTT_CLOSE_SELL2))
        {
         //--- Получаем список всех открытых позиций
         CArrayObj* list=engine.GetListMarketPosition();
         //--- Выбираем из списка только позиции Sell и только по текущему символу
         list=CSelect::ByOrderProperty(list,ORDER_PROP_SYMBOL,Symbol(),EQUAL);
         list=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_SELL,EQUAL);
         //--- Сортируем список по прибыли с учётом комиссии и свопа
         list.Sort(SORT_BY_ORDER_PROFIT_FULL);
         //--- Получаем индекс позиции Sell с наибольшей прибылью
         int index=CSelect::FindOrderMax(list,ORDER_PROP_PROFIT_FULL);
         if(index>WRONG_VALUE)
           {
            //--- Получаем объект-позицию Sell и закрываем позицию по тикету
            COrder* position=list.At(index);
            if(position!=NULL)
              {
               //--- Если не нажаты кнопки создания отложенного запроса - закрываем позицию по тикету
               if(!pressed_pending_close_sell2)
                  engine.ClosePositionPartially((ulong)position.Ticket(),position.Volume()/2.0);
               //--- Иначе - создаём отложенный запрос на частичное закрытие позиции по тикету
               //--- и задаём условия в зависимости от активных кнопок
               else
                 {
                  int id=engine.ClosePositionPartiallyPending(position.Ticket(),position.Volume()/2.0);
                  if(id>0)
                    {
                     //--- устанавливаем цену и время активации отложенного запроса и устанавливаем параметры активации
                     double ask=SymbolInfoDouble(NULL,SYMBOL_ASK);
                     double price_activation=NormalizeDouble(ask-distance_pending_request*g_point,g_digits);
                     ulong  time_activation=TimeCurrent()+bars_delay_pending_request*PeriodSeconds();
                     SetPReqCriterion((uchar)id,price_activation,time_activation,BUTT_CLOSE_SELL2,EQUAL_OR_LESS,ask,TimeCurrent());
                    }
                 }
              }
           }
        }
      //--- Если нажата кнопка BUTT_CLOSE_SELL_BY_BUY: Закрыть Sell с максимальной прибылью встречной Buy с максимальной прибылью
      else if(button==EnumToString(BUTT_CLOSE_SELL_BY_BUY))
        {
         //--- Если счёт хеджевый
         if(engine.IsHedge())
           {
            CArrayObj *list_buy=NULL, *list_sell=NULL;
            //--- Получаем список всех открытых позиций
            CArrayObj* list=engine.GetListMarketPosition();
            if(list==NULL)
               return;
            //--- Выбираем из списка только позиции по текущему символу
            list=CSelect::ByOrderProperty(list,ORDER_PROP_SYMBOL,Symbol(),EQUAL);
            
            //--- Выбираем из списка только позиции Sell
            list_sell=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_SELL,EQUAL);
            if(list_sell==NULL)
               return;
            //--- Сортируем список по прибыли с учётом комиссии и свопа
            list_sell.Sort(SORT_BY_ORDER_PROFIT_FULL);
            //--- Получаем индекс позиции Sell с наибольшей прибылью
            int index_sell=CSelect::FindOrderMax(list_sell,ORDER_PROP_PROFIT_FULL);
            
            //--- Выбираем из списка только позиции Buy
            list_buy=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_BUY,EQUAL);
            if(list_buy==NULL)
               return;
            //--- Сортируем список по прибыли с учётом комиссии и свопа
            list_buy.Sort(SORT_BY_ORDER_PROFIT_FULL);
            //--- Получаем индекс позиции Buy с наибольшей прибылью
            int index_buy=CSelect::FindOrderMax(list_buy,ORDER_PROP_PROFIT_FULL);
            if(index_sell>WRONG_VALUE && index_buy>WRONG_VALUE)
              {
               //--- Выбираем позицию Sell с наибольшей прибылью
               COrder* position_sell=list_sell.At(index_sell);
               //--- Выбираем позицию Buy с наибольшей прибылью
               COrder* position_buy=list_buy.At(index_buy);
               if(position_sell!=NULL && position_buy!=NULL)
                 {
                  //--- Если не нажаты кнопки создания отложенного запроса - закрываем позиции по тикету
                  if(!pressed_pending_close_sell_by_buy)
                     engine.ClosePositionBy((ulong)position_sell.Ticket(),(ulong)position_buy.Ticket());
                  //--- Иначе - создаём отложенный запрос на закрытие позиции Sell встречной позицией Buy
                  //--- и задаём условия в зависимости от активных кнопок
                  else
                    {
                     int id=engine.ClosePositionByPending(position_sell.Ticket(),position_buy.Ticket());
                     if(id>0)
                       {
                        //--- устанавливаем цену и время активации отложенного запроса и устанавливаем параметры активации
                        double ask=SymbolInfoDouble(NULL,SYMBOL_ASK);
                        double price_activation=NormalizeDouble(ask-distance_pending_request*g_point,g_digits);
                        ulong  time_activation=TimeCurrent()+bars_delay_pending_request*PeriodSeconds();
                        SetPReqCriterion((uchar)id,price_activation,time_activation,BUTT_CLOSE_SELL_BY_BUY,EQUAL_OR_LESS,ask,TimeCurrent());
                       }
                    }
                 }
              }
           }
        }
      //--- Если нажата кнопка BUTT_CLOSE_ALL: Закрыть все позиции, начиная от позиции с наименьшим профитом
      else if(button==EnumToString(BUTT_CLOSE_ALL))
        {
         //--- Получаем список всех открытых позиций
         CArrayObj* list=engine.GetListMarketPosition();
         //--- Выбираем из списка только позиции по текущему символу
         list=CSelect::ByOrderProperty(list,ORDER_PROP_SYMBOL,Symbol(),EQUAL);
         if(list!=NULL)
           {
            //--- Сортируем список по прибыли с учётом комиссии и свопа
            list.Sort(SORT_BY_ORDER_PROFIT_FULL);
            int total=list.Total();
            //--- В цикле от позиции с наименьшей прибылью
            for(int i=0;i<total;i++)
              {
               COrder* position=list.At(i);
               if(position==NULL)
                  continue;
               //--- Если не нажаты кнопки создания отложенного запроса - закрываем каждую позицию по её тикету
               if(!pressed_pending_close_all)
                  engine.ClosePosition((ulong)position.Ticket());
               //--- Иначе - создаём отложенный запрос на закрытие каждой позиции
               //--- и задаём условия в зависимости от активных кнопок
               else
                 {
                  int id=engine.ClosePositionPending(position.Ticket());
                  if(id>0)
                    {
                     //--- устанавливаем цену и время активации отложенного запроса и устанавливаем параметры активации
                     double price=SymbolInfoDouble(NULL,SYMBOL_BID);
                     double price_activation=NormalizeDouble(position.PriceOpen()+distance_pending_request*g_point,g_digits);
                     ENUM_COMPARER_TYPE comparer=EQUAL_OR_MORE;
                     if(position.TypeOrder()==POSITION_TYPE_SELL)
                       {
                        price=SymbolInfoDouble(NULL,SYMBOL_ASK);
                        price_activation=NormalizeDouble(position.PriceOpen()-distance_pending_request*g_point,g_digits);
                        comparer=EQUAL_OR_LESS;
                       }
                     ulong  time_activation=TimeCurrent()+bars_delay_pending_request*PeriodSeconds();
                     SetPReqCriterion((uchar)id,price_activation,time_activation,BUTT_CLOSE_ALL,comparer,price,TimeCurrent());
                    }
                 }
              }
           }
        }
      //--- Если нажата кнопка BUTT_DELETE_PENDING: Удалить отложенные ордера, начиная с самого раннего
      else if(button==EnumToString(BUTT_DELETE_PENDING))
        {
         //--- Получаем список всех ордеров
         CArrayObj* list=engine.GetListMarketPendings();
         //--- Выбираем из списка только ордера по текущему символу
         list=CSelect::ByOrderProperty(list,ORDER_PROP_SYMBOL,Symbol(),EQUAL);
         if(list!=NULL)
           {
            //--- Сортируем список по времени установки
            list.Sort(SORT_BY_ORDER_TIME_OPEN);
            int total=list.Total();
            //--- В цикле от ордера с наибольшим временем
            for(int i=total-1;i>=0;i--)
              {
               COrder* order=list.At(i);
               if(order==NULL)
                  continue;
               //--- Если не нажаты кнопки создания отложенного запроса - удаляем каждый ордер по его тикету
               if(!pressed_pending_delete_all)
                  engine.DeleteOrder((ulong)order.Ticket());
               //--- Иначе - создаём отложенный запрос на удаление каждого ордера
               //--- и задаём условия в зависимости от активных кнопок
               else
                 {
                  int id=engine.DeleteOrderPending(order.Ticket());
                  if(id>0)
                    {
                     //--- устанавливаем цену и время активации отложенного запроса и устанавливаем параметры активации
                     double price=SymbolInfoDouble(NULL,SYMBOL_ASK);
                     double price_activation=NormalizeDouble(order.PriceOpen()+(distance_pending+distance_pending_request)*g_point,g_digits);
                     ENUM_COMPARER_TYPE comparer=EQUAL_OR_MORE;
                     if(order.TypeByDirection()==ORDER_TYPE_SELL)
                       {
                        price=SymbolInfoDouble(NULL,SYMBOL_BID);
                        price_activation=NormalizeDouble(order.PriceOpen()-(distance_pending+distance_pending_request)*g_point,g_digits);
                        comparer=EQUAL_OR_LESS;
                       }
                     ulong  time_activation=TimeCurrent()+bars_delay_pending_request*PeriodSeconds();
                     SetPReqCriterion((uchar)id,price_activation,time_activation,BUTT_DELETE_PENDING,comparer,price,TimeCurrent());
                    }
                 }
              }
           }
        }
               
      //--- Если нажата кнопка BUTT_SET_STOP_LOSS: Установить StopLoss всем ордерам и позициям, где его нету
      if(button==EnumToString(BUTT_SET_STOP_LOSS))
        {
         SetStopLoss();
        }
      //--- Если нажата кнопка BUTT_SET_TAKE_PROFIT: Установить TakeProfit всем ордерам и позициям, где его нету
      if(button==EnumToString(BUTT_SET_TAKE_PROFIT))
        {
         SetTakeProfit();
        }
      //--- Если нажата кнопка BUTT_PROFIT_WITHDRAWAL: Вывести средства со счёта
      if(button==EnumToString(BUTT_PROFIT_WITHDRAWAL))
        {
         //--- Если программа запущена в тестере
         if(MQLInfoInteger(MQL_TESTER))
           {
            //--- Эмулируем вывод средств
            TesterWithdrawal(withdrawal);
           }
        }

      //--- Подождём 1/10 секунды
      Sleep(100);
      //--- "Отожмём" кнопку (если это не кнопка трейлинга и не кнопки активации работы отложенными запросами)
      if(button!=EnumToString(BUTT_TRAILING_ALL) && StringFind(button,"_PRICE")<0 && StringFind(button,"_TIME")<0)
         ButtonState(button_name,false);
      //--- Если нажата кнопка BUTT_TRAILING_ALL или кнопки включения работы отложенными запросами
      else
        {
         //--- Поставим цвет активной кнопки для кнопки включения трейлинга
         if(button==EnumToString(BUTT_TRAILING_ALL))
           {
            ButtonState(button_name,true);
            trailing_on=true;
           }
         
         //--- Покупки
         //--- Поставим цвет активной кнопки для кнопки активации работы отложенными запросами для открытия Buy по цене или времени
         if(button==EnumToString(BUTT_BUY)+"_PRICE" || button==EnumToString(BUTT_BUY)+"_TIME")
           {
            ButtonState(button_name,true);
            pressed_pending_buy=true;
           }
         //--- Поставим цвет активной кнопки для кнопки активации работы отложенными запросами для установки BuyLimit по цене или времени
         if(button==EnumToString(BUTT_BUY_LIMIT)+"_PRICE" || button==EnumToString(BUTT_BUY_LIMIT)+"_TIME")
           {
            ButtonState(button_name,true);
            pressed_pending_buy_limit=true;
           }
         //--- Поставим цвет активной кнопки для кнопки активации работы отложенными запросами для установки BuyStop по цене или времени
         if(button==EnumToString(BUTT_BUY_STOP)+"_PRICE" || button==EnumToString(BUTT_BUY_STOP)+"_TIME")
           {
            ButtonState(button_name,true);
            pressed_pending_buy_stop=true;
           }
         //--- Поставим цвет активной кнопки для кнопки активации работы отложенными запросами для установки BuyStopLimit по цене или времени
         if(button==EnumToString(BUTT_BUY_STOP_LIMIT)+"_PRICE" || button==EnumToString(BUTT_BUY_STOP_LIMIT)+"_TIME")
           {
            ButtonState(button_name,true);
            pressed_pending_buy_stoplimit=true;
           }
         //--- Поставим цвет активной кнопки для кнопки активации работы отложенными запросами для закрытия Buy по цене или времени
         if(button==EnumToString(BUTT_CLOSE_BUY)+"_PRICE" || button==EnumToString(BUTT_CLOSE_BUY)+"_TIME")
           {
            ButtonState(button_name,true);
            pressed_pending_close_buy=true;
           }
         //--- Поставим цвет активной кнопки для кнопки активации работы отложенными запросами для закрытия 1/2 Buy по цене или времени
         if(button==EnumToString(BUTT_CLOSE_BUY2)+"_PRICE" || button==EnumToString(BUTT_CLOSE_BUY2)+"_TIME")
           {
            ButtonState(button_name,true);
            pressed_pending_close_buy2=true;
           }
         //--- Поставим цвет активной кнопки для кнопки активации работы отложенными запросами для закрытия Buy встречной Sell по цене или времени
         if(button==EnumToString(BUTT_CLOSE_BUY_BY_SELL)+"_PRICE" || button==EnumToString(BUTT_CLOSE_BUY_BY_SELL)+"_TIME")
           {
            ButtonState(button_name,true);
            pressed_pending_close_buy_by_sell=true;
           }
         
         //--- Продажи
         //--- Поставим цвет активной кнопки для кнопки активации работы отложенными запросами для открытия Sell по цене или времени
         if(button==EnumToString(BUTT_SELL)+"_PRICE" || button==EnumToString(BUTT_SELL)+"_TIME")
           {
            ButtonState(button_name,true);
            pressed_pending_sell=true;
           }
         //--- Поставим цвет активной кнопки для кнопки активации работы отложенными запросами для установки SellLimit по цене или времени
         if(button==EnumToString(BUTT_SELL_LIMIT)+"_PRICE" || button==EnumToString(BUTT_SELL_LIMIT)+"_TIME")
           {
            ButtonState(button_name,true);
            pressed_pending_sell_limit=true;
           }
         //--- Поставим цвет активной кнопки для кнопки активации работы отложенными запросами для установки SellStop по цене или времени
         if(button==EnumToString(BUTT_SELL_STOP)+"_PRICE" || button==EnumToString(BUTT_SELL_STOP)+"_TIME")
           {
            ButtonState(button_name,true);
            pressed_pending_sell_stop=true;
           }
         //--- Поставим цвет активной кнопки для кнопки активации работы отложенными запросами для установки SellStopLimit по цене или времени
         if(button==EnumToString(BUTT_SELL_STOP_LIMIT)+"_PRICE" || button==EnumToString(BUTT_SELL_STOP_LIMIT)+"_TIME")
           {
            ButtonState(button_name,true);
            pressed_pending_sell_stoplimit=true;
           }
         //--- Поставим цвет активной кнопки для кнопки активации работы отложенными запросами для закрытия Sell по цене или времени
         if(button==EnumToString(BUTT_CLOSE_SELL)+"_PRICE" || button==EnumToString(BUTT_CLOSE_SELL)+"_TIME")
           {
            ButtonState(button_name,true);
            pressed_pending_close_sell=true;
           }
         //--- Поставим цвет активной кнопки для кнопки активации работы отложенными запросами для закрытия 1/2 Sell по цене или времени
         if(button==EnumToString(BUTT_CLOSE_SELL2)+"_PRICE" || button==EnumToString(BUTT_CLOSE_SELL2)+"_TIME")
           {
            ButtonState(button_name,true);
            pressed_pending_close_sell2=true;
           }
         //--- Поставим цвет активной кнопки для кнопки активации работы отложенными запросами для закрытия Sell встречной Buy по цене или времени
         if(button==EnumToString(BUTT_CLOSE_SELL_BY_BUY)+"_PRICE" || button==EnumToString(BUTT_CLOSE_SELL_BY_BUY)+"_TIME")
           {
            ButtonState(button_name,true);
            pressed_pending_close_sell_by_buy=true;
           }
         //--- Поставим цвет активной кнопки для кнопки активации работы отложенными запросами для удаления ордеров по цене или времени
         if(button==EnumToString(BUTT_DELETE_PENDING)+"_PRICE" || button==EnumToString(BUTT_DELETE_PENDING)+"_TIME")
           {
            ButtonState(button_name,true);
            pressed_pending_delete_all=true;
           }
         //--- Поставим цвет активной кнопки для кнопки активации работы отложенными запросами для закрытия позиций по цене или времени
         if(button==EnumToString(BUTT_CLOSE_ALL)+"_PRICE" || button==EnumToString(BUTT_CLOSE_ALL)+"_TIME")
           {
            ButtonState(button_name,true);
            pressed_pending_close_all=true;
           }
         //--- Поставим цвет активной кнопки для кнопки активации работы отложенными запросами для установки StopLoss по цене или времени
         if(button==EnumToString(BUTT_SET_STOP_LOSS)+"_PRICE" || button==EnumToString(BUTT_SET_STOP_LOSS)+"_TIME")
           {
            ButtonState(button_name,true);
            pressed_pending_sl=true;
           }
         //--- Поставим цвет активной кнопки для кнопки активации работы отложенными запросами для установки TakeProfit по цене или времени
         if(button==EnumToString(BUTT_SET_TAKE_PROFIT)+"_PRICE" || button==EnumToString(BUTT_SET_TAKE_PROFIT)+"_TIME")
           {
            ButtonState(button_name,true);
            pressed_pending_tp=true;
           }
        }
      //--- перерисуем чарт
      ChartRedraw();
     }
   //--- Вернём цвет неактивным кнопкам
   else 
     {
      //--- кнопка трейлинга
      if(button==EnumToString(BUTT_TRAILING_ALL))
        {
         ButtonState(button_name,false);
         trailing_on=false;
        }
      
      //--- Покупки
      //--- кнопка активации работы отложенными запросами для открытия Buy по цене
      if(button==EnumToString(BUTT_BUY)+"_PRICE")
        {
         ButtonState(button_name,false);
         pressed_pending_buy=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_BUY)+"_TIME"));
        }
      //--- кнопка активации работы отложенными запросами для открытия Buy по времени
      if(button==EnumToString(BUTT_BUY)+"_TIME")
        {
         ButtonState(button_name,false);
         pressed_pending_buy=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_BUY)+"_PRICE"));
        }
      
      //--- кнопка активации работы отложенными запросами для установки BuyLimit по цене
      if(button==EnumToString(BUTT_BUY_LIMIT)+"_PRICE")
        {
         ButtonState(button_name,false);
         pressed_pending_buy_limit=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_BUY_LIMIT)+"_TIME"));
        }
      //--- кнопка активации работы отложенными запросами для установки BuyLimit по времени
      if(button==EnumToString(BUTT_BUY_LIMIT)+"_TIME")
        {
         ButtonState(button_name,false);
         pressed_pending_buy_limit=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_BUY_LIMIT)+"_PRICE"));
        }
      
      //--- кнопка активации работы отложенными запросами для установки BuyStop по цене
      if(button==EnumToString(BUTT_BUY_STOP)+"_PRICE")
        {
         ButtonState(button_name,false);
         pressed_pending_buy_stop=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_BUY_STOP)+"_TIME"));
        }
      //--- кнопка активации работы отложенными запросами для установки BuyStop по времени
      if(button==EnumToString(BUTT_BUY_STOP)+"_TIME")
        {
         ButtonState(button_name,false);
         pressed_pending_buy_stop=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_BUY_STOP)+"_PRICE"));
        }
      
      //--- кнопка активации работы отложенными запросами для установки BuyStopLimit по цене
      if(button==EnumToString(BUTT_BUY_STOP_LIMIT)+"_PRICE")
        {
         ButtonState(button_name,false);
         pressed_pending_buy_stoplimit=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_BUY_STOP_LIMIT)+"_TIME"));
        }
      //--- кнопка активации работы отложенными запросами для установки BuyStopLimit по времени
      if(button==EnumToString(BUTT_BUY_STOP_LIMIT)+"_TIME")
        {
         ButtonState(button_name,false);
         pressed_pending_buy_stoplimit=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_BUY_STOP_LIMIT)+"_PRICE"));
        }
      
      //--- кнопка активации работы отложенными запросами для закрытия Buy по цене
      if(button==EnumToString(BUTT_CLOSE_BUY)+"_PRICE")
        {
         ButtonState(button_name,false);
         pressed_pending_close_buy=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_BUY)+"_TIME"));
        }
      //--- кнопка активации работы отложенными запросами для закрытия Buy по времени
      if(button==EnumToString(BUTT_CLOSE_BUY)+"_TIME")
        {
         ButtonState(button_name,false);
         pressed_pending_close_buy=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_BUY)+"_PRICE"));
        }
      
      //--- кнопка активации работы отложенными запросами для закрытия 1/2 Buy по цене
      if(button==EnumToString(BUTT_CLOSE_BUY2)+"_PRICE")
        {
         ButtonState(button_name,false);
         pressed_pending_close_buy2=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_BUY2)+"_TIME"));
        }
      //--- кнопка активации работы отложенными запросами для закрытия 1/2 Buy по времени
      if(button==EnumToString(BUTT_CLOSE_BUY2)+"_TIME")
        {
         ButtonState(button_name,false);
         pressed_pending_close_buy2=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_BUY2)+"_PRICE"));
        }
      
      //--- кнопка активации работы отложенными запросами для закрытия Buy встречной Sell по цене
      if(button==EnumToString(BUTT_CLOSE_BUY_BY_SELL)+"_PRICE")
        {
         ButtonState(button_name,false);
         pressed_pending_close_buy_by_sell=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_BUY_BY_SELL)+"_TIME"));
        }
      //--- кнопка активации работы отложенными запросами для закрытия Buy встречной Sell по времени
      if(button==EnumToString(BUTT_CLOSE_BUY_BY_SELL)+"_TIME")
        {
         ButtonState(button_name,false);
         pressed_pending_close_buy_by_sell=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_BUY_BY_SELL)+"_PRICE"));
        }

      //--- Продажи
      //--- кнопка активации работы отложенными запросами для открытия Sell по цене
      if(button==EnumToString(BUTT_SELL)+"_PRICE")
        {
         ButtonState(button_name,false);
         pressed_pending_sell=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_SELL)+"_TIME"));
        }
      //--- кнопка активации работы отложенными запросами для открытия Sell по времени
      if(button==EnumToString(BUTT_SELL)+"_TIME")
        {
         ButtonState(button_name,false);
         pressed_pending_sell=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_SELL)+"_PRICE"));
        }
      
      //--- кнопка активации работы отложенными запросами для установки SellLimit по цене
      if(button==EnumToString(BUTT_SELL_LIMIT)+"_PRICE")
        {
         ButtonState(button_name,false);
         pressed_pending_sell_limit=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_SELL_LIMIT)+"_TIME"));
        }
      //--- кнопка активации работы отложенными запросами для установки SellLimit по времени
      if(button==EnumToString(BUTT_SELL_LIMIT)+"_TIME")
        {
         ButtonState(button_name,false);
         pressed_pending_sell_limit=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_SELL_LIMIT)+"_PRICE"));
        }
      
      //--- кнопка активации работы отложенными запросами для установки SellStop по цене
      if(button==EnumToString(BUTT_SELL_STOP)+"_PRICE")
        {
         ButtonState(button_name,false);
         pressed_pending_sell_stop=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_SELL_STOP)+"_TIME"));
        }
      //--- кнопка активации работы отложенными запросами для установки SellStop по времени
      if(button==EnumToString(BUTT_SELL_STOP)+"_TIME")
        {
         ButtonState(button_name,false);
         pressed_pending_sell_stop=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_SELL_STOP)+"_PRICE"));
        }
      
      //--- кнопка активации работы отложенными запросами для установки SellStopLimit по цене
      if(button==EnumToString(BUTT_SELL_STOP_LIMIT)+"_PRICE")
        {
         ButtonState(button_name,false);
         pressed_pending_sell_stoplimit=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_SELL_STOP_LIMIT)+"_TIME"));
        }
      //--- кнопка активации работы отложенными запросами для установки SellStopLimit по времени
      if(button==EnumToString(BUTT_SELL_STOP_LIMIT)+"_TIME")
        {
         ButtonState(button_name,false);
         pressed_pending_sell_stoplimit=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_SELL_STOP_LIMIT)+"_PRICE"));
        }
      
      //--- кнопка активации работы отложенными запросами для закрытия Sell по цене
      if(button==EnumToString(BUTT_CLOSE_SELL)+"_PRICE")
        {
         ButtonState(button_name,false);
         pressed_pending_close_sell=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_SELL)+"_TIME"));
        }
      //--- кнопка активации работы отложенными запросами для закрытия Sell по времени
      if(button==EnumToString(BUTT_CLOSE_SELL)+"_TIME")
        {
         ButtonState(button_name,false);
         pressed_pending_close_sell=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_SELL)+"_PRICE"));
        }
      
      //--- кнопка активации работы отложенными запросами для закрытия 1/2 Sell по цене
      if(button==EnumToString(BUTT_CLOSE_SELL2)+"_PRICE")
        {
         ButtonState(button_name,false);
         pressed_pending_close_sell2=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_SELL2)+"_TIME"));
        }
      //--- кнопка активации работы отложенными запросами для закрытия 1/2 Sell по времени
      if(button==EnumToString(BUTT_CLOSE_SELL2)+"_TIME")
        {
         ButtonState(button_name,false);
         pressed_pending_close_sell2=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_SELL2)+"_PRICE"));
        }
      
      //--- кнопка активации работы отложенными запросами для закрытия Sell встречной Buy по цене
      if(button==EnumToString(BUTT_CLOSE_SELL_BY_BUY)+"_PRICE")
        {
         ButtonState(button_name,false);
         pressed_pending_close_sell_by_buy=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_SELL_BY_BUY)+"_TIME"));
        }
      //--- кнопка активации работы отложенными запросами для закрытия Sell встречной Buy по времени
      if(button==EnumToString(BUTT_CLOSE_SELL_BY_BUY)+"_TIME")
        {
         ButtonState(button_name,false);
         pressed_pending_close_sell_by_buy=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_SELL_BY_BUY)+"_PRICE"));
        }
      
      //--- кнопка активации работы отложенными запросами для удаления ордеров по цене
      if(button==EnumToString(BUTT_DELETE_PENDING)+"_PRICE")
        {
         ButtonState(button_name,false);
         pressed_pending_delete_all=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_DELETE_PENDING)+"_TIME"));
        }
      //--- кнопка активации работы отложенными запросами для удаления ордеров по времени
      if(button==EnumToString(BUTT_DELETE_PENDING)+"_TIME")
        {
         ButtonState(button_name,false);
         pressed_pending_delete_all=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_DELETE_PENDING)+"_PRICE"));
        }
      
      //--- кнопка активации работы отложенными запросами для закрытия позиций по цене
      if(button==EnumToString(BUTT_CLOSE_ALL)+"_PRICE")
        {
         ButtonState(button_name,false);
         pressed_pending_close_all=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_ALL)+"_TIME"));
        }
      //--- кнопка активации работы отложенными запросами для закрытия позиций по времени
      if(button==EnumToString(BUTT_CLOSE_ALL)+"_TIME")
        {
         ButtonState(button_name,false);
         pressed_pending_close_all=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_ALL)+"_PRICE"));
        }
      
      //--- кнопка активации работы отложенными запросами для установки StopLoss по цене
      if(button==EnumToString(BUTT_SET_STOP_LOSS)+"_PRICE")
        {
         ButtonState(button_name,false);
         pressed_pending_sl=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_SET_STOP_LOSS)+"_TIME"));
        }
      //--- кнопка активации работы отложенными запросами для установки StopLoss по времени
      if(button==EnumToString(BUTT_SET_STOP_LOSS)+"_TIME")
        {
         ButtonState(button_name,false);
         pressed_pending_sl=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_SET_STOP_LOSS)+"_PRICE"));
        }
      
      //--- кнопка активации работы отложенными запросами для установки TakeProfit по цене
      if(button==EnumToString(BUTT_SET_TAKE_PROFIT)+"_PRICE")
        {
         ButtonState(button_name,false);
         pressed_pending_tp=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_SET_TAKE_PROFIT)+"_TIME"));
        }
      //--- кнопка активации работы отложенными запросами для установки TakeProfit по времени
      if(button==EnumToString(BUTT_SET_TAKE_PROFIT)+"_TIME")
        {
         ButtonState(button_name,false);
         pressed_pending_tp=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_SET_TAKE_PROFIT)+"_PRICE"));
        }
      //--- перерисуем чарт
      ChartRedraw();
     }
  }
//+------------------------------------------------------------------+

В функциях установки StopLoss и TakeProfit всем ордерам и позициям тоже добавим блоки кода для создания отложенных запросов на установку StopLoss/TakeProfit:

//+------------------------------------------------------------------+
//| Установка StopLoss всем ордерам и позициям                       |
//+------------------------------------------------------------------+
void SetStopLoss(void)
  {
   if(stoploss_to_modify==0)
      return;
//--- Установка StopLoss всем позициям, где его нету
   //--- Получаем список всех позиций
   CArrayObj* list=engine.GetListMarketPosition();
   //--- Выбираем из списка только позиции по текущему символу
   list=CSelect::ByOrderProperty(list,ORDER_PROP_SYMBOL,Symbol(),EQUAL);
   //--- выбираем из списка позиции с нулевым StopLoss
   list=CSelect::ByOrderProperty(list,ORDER_PROP_SL,0,EQUAL);
   if(list==NULL)
      return;
   int total=list.Total();
   for(int i=total-1;i>=0;i--)
     {
      COrder* position=list.At(i);
      if(position==NULL)
         continue;
      double sl=CorrectStopLoss(position.Symbol(),position.TypeByDirection(),0,stoploss_to_modify);
      //--- Если не нажаты кнопки создания отложенного запроса - устанавливаем StopLoss каждой позиции по её тикету
      if(!pressed_pending_sl)
         engine.ModifyPosition((ulong)position.Ticket(),sl,-1);
      //--- Иначе - создаём отложенный запрос на установку StopLoss каждой позиции
      //--- и задаём условия в зависимости от активных кнопок
      else
        {
         int id=engine.ModifyPositionPending(position.Ticket(),sl,-1);
         if(id>0)
           {
            //--- устанавливаем цену и время активации отложенного запроса и устанавливаем параметры активации
            double price=SymbolInfoDouble(NULL,SYMBOL_BID);
            double price_activation=NormalizeDouble(position.PriceOpen()+distance_pending_request*g_point,g_digits);
            ENUM_COMPARER_TYPE comparer=EQUAL_OR_MORE;
            if(position.TypeByDirection()==ORDER_TYPE_SELL)
              {
               price=SymbolInfoDouble(NULL,SYMBOL_ASK);
               price_activation=NormalizeDouble(position.PriceOpen()-distance_pending_request*g_point,g_digits);
               comparer=EQUAL_OR_LESS;
              }
            ulong  time_activation=TimeCurrent()+bars_delay_pending_request*PeriodSeconds();
            SetPReqCriterion((uchar)id,price_activation,time_activation,BUTT_SET_STOP_LOSS,comparer,price,TimeCurrent());
           }
        }
     }
//--- Установка StopLoss всем отложенным ордерам, где его нету
   //--- Получаем список всех ордеров
   list=engine.GetListMarketPendings();
   //--- Выбираем из списка только позиции по текущему символу
   list=CSelect::ByOrderProperty(list,ORDER_PROP_SYMBOL,Symbol(),EQUAL);
   //--- выбираем из списка ордера с нулевым StopLoss
   list=CSelect::ByOrderProperty(list,ORDER_PROP_SL,0,EQUAL);
   if(list==NULL)
      return;
   total=list.Total();
   for(int i=total-1;i>=0;i--)
     {
      COrder* order=list.At(i);
      if(order==NULL)
         continue;
      double sl=CorrectStopLoss(order.Symbol(),(ENUM_ORDER_TYPE)order.TypeOrder(),order.PriceOpen(),stoploss_to_modify);
      //--- Если не нажаты кнопки создания отложенного запроса - устанавливаем StopLoss каждому ордеру по его тикету
      if(!pressed_pending_sl)
         engine.ModifyOrder((ulong)order.Ticket(),-1,sl,-1,-1);
      //--- Иначе - создаём отложенный запрос на установку StopLoss каждому ордеру
      //--- и задаём условия в зависимости от активных кнопок
      else
        {
         int id=engine.ModifyOrderPending(order.Ticket(),-1,sl,-1,-1);
         if(id>0)
           {
            //--- устанавливаем цену и время активации отложенного запроса и устанавливаем параметры активации
            double price=SymbolInfoDouble(NULL,SYMBOL_ASK);
            double price_activation=NormalizeDouble(order.PriceOpen()+(distance_pending+distance_pending_request)*g_point,g_digits);
            ENUM_COMPARER_TYPE comparer=EQUAL_OR_MORE;
            if(order.TypeByDirection()==ORDER_TYPE_SELL)
              {
               price=SymbolInfoDouble(NULL,SYMBOL_BID);
               price_activation=NormalizeDouble(order.PriceOpen()-(distance_pending+distance_pending_request)*g_point,g_digits);
               comparer=EQUAL_OR_LESS;
              }
            ulong  time_activation=TimeCurrent()+bars_delay_pending_request*PeriodSeconds();
            SetPReqCriterion((uchar)id,price_activation,time_activation,BUTT_SET_STOP_LOSS,comparer,price,TimeCurrent());
           }
        }
     }
  }
//+------------------------------------------------------------------+
//| Установка TakeProfit всем ордерам и позициям                     |
//+------------------------------------------------------------------+
void SetTakeProfit(void)
  {
   if(takeprofit_to_modify==0)
      return;
//--- Установка TakeProfit всем позициям, где его нету
   //--- Получаем список всех позиций
   CArrayObj* list=engine.GetListMarketPosition();
   //--- Выбираем из списка только позиции по текущему символу
   list=CSelect::ByOrderProperty(list,ORDER_PROP_SYMBOL,Symbol(),EQUAL);
   //--- выбираем из списка позиции с нулевым TakeProfit
   list=CSelect::ByOrderProperty(list,ORDER_PROP_TP,0,EQUAL);
   if(list==NULL)
      return;
   int total=list.Total();
   for(int i=total-1;i>=0;i--)
     {
      COrder* position=list.At(i);
      if(position==NULL)
         continue;
      double tp=CorrectTakeProfit(position.Symbol(),position.TypeByDirection(),0,takeprofit_to_modify);
      //--- Если не нажаты кнопки создания отложенного запроса - устанавливаем TakeProfit каждой позиции по её тикету
      if(!pressed_pending_tp)
         engine.ModifyPosition((ulong)position.Ticket(),-1,tp);
      //--- Иначе - создаём отложенный запрос на установку TakeProfit каждой позиции
      //--- и задаём условия в зависимости от активных кнопок
      else
        {
         int id=engine.ModifyPositionPending(position.Ticket(),-1,tp);
         if(id>0)
           {
            //--- устанавливаем цену и время активации отложенного запроса и устанавливаем параметры активации
            double price=SymbolInfoDouble(NULL,SYMBOL_BID);
            double price_activation=NormalizeDouble(position.PriceOpen()+distance_pending_request*g_point,g_digits);
            ENUM_COMPARER_TYPE comparer=EQUAL_OR_MORE;
            if(position.TypeByDirection()==ORDER_TYPE_SELL)
              {
               price=SymbolInfoDouble(NULL,SYMBOL_ASK);
               price_activation=NormalizeDouble(position.PriceOpen()-distance_pending_request*g_point,g_digits);
               comparer=EQUAL_OR_LESS;
              }
            ulong  time_activation=TimeCurrent()+bars_delay_pending_request*PeriodSeconds();
            SetPReqCriterion((uchar)id,price_activation,time_activation,BUTT_SET_TAKE_PROFIT,comparer,price,TimeCurrent());
           }
        }
     }
//--- Установка TakeProfit всем отложенным ордерам, где его нету
   //--- Получаем список всех ордеров
   list=engine.GetListMarketPendings();
   //--- Выбираем из списка только ордера по текущему символу
   list=CSelect::ByOrderProperty(list,ORDER_PROP_SYMBOL,Symbol(),EQUAL);
   //--- выбираем из списка ордера с нулевым TakeProfit
   list=CSelect::ByOrderProperty(list,ORDER_PROP_TP,0,EQUAL);
   if(list==NULL)
      return;
   total=list.Total();
   for(int i=total-1;i>=0;i--)
     {
      COrder* order=list.At(i);
      if(order==NULL)
         continue;
      double tp=CorrectTakeProfit(order.Symbol(),(ENUM_ORDER_TYPE)order.TypeOrder(),order.PriceOpen(),takeprofit_to_modify);
      //--- Если не нажаты кнопки создания отложенного запроса - устанавливаем TakeProfit каждому ордеру по его тикету
      if(!pressed_pending_sl)
         engine.ModifyOrder((ulong)order.Ticket(),-1,-1,tp,-1);
      //--- Иначе - создаём отложенный запрос на установку TakeProfit каждому ордеру
      //--- и задаём условия в зависимости от активных кнопок
      else
        {
         int id=engine.ModifyOrderPending(order.Ticket(),-1,-1,tp,-1);
         if(id>0)
           {
            //--- устанавливаем цену и время активации отложенного запроса и устанавливаем параметры активации
            double price=SymbolInfoDouble(NULL,SYMBOL_ASK);
            double price_activation=NormalizeDouble(order.PriceOpen()+(distance_pending+distance_pending_request)*g_point,g_digits);
            ENUM_COMPARER_TYPE comparer=EQUAL_OR_MORE;
            if(order.TypeByDirection()==ORDER_TYPE_SELL)
              {
               price=SymbolInfoDouble(NULL,SYMBOL_BID);
               price_activation=NormalizeDouble(order.PriceOpen()-(distance_pending+distance_pending_request)*g_point,g_digits);
               comparer=EQUAL_OR_LESS;
              }
            ulong  time_activation=TimeCurrent()+bars_delay_pending_request*PeriodSeconds();
            SetPReqCriterion((uchar)id,price_activation,time_activation,BUTT_SET_TAKE_PROFIT,comparer,price,TimeCurrent());
           }
        }
     }
  }
//+------------------------------------------------------------------+

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

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

Далее откроем две позиции на покупку и установим отложенный ордер на покупку. После чего создадим отложенные запросы на удаление ордеров и закрытие позиций по времени.


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

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

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

Что дальше

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

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

К содержанию

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

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


Прикрепленные файлы |
MQL5.zip (3690.47 KB)
MQL4.zip (3690.48 KB)
Библиотека для простого и быстрого создания программ для MetaTrader (Часть XXXIII): Отложенные торговые запросы - закрытие позиций по условиям Библиотека для простого и быстрого создания программ для MetaTrader (Часть XXXIII): Отложенные торговые запросы - закрытие позиций по условиям

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

Мультивалютный мониторинг торговых сигналов (Часть 2): Реализация визуальной части приложения Мультивалютный мониторинг торговых сигналов (Часть 2): Реализация визуальной части приложения

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

Непрерывная скользящая оптимизация (Часть 4): Программа для управления оптимизацией (автооптимизатор) Непрерывная скользящая оптимизация (Часть 4): Программа для управления оптимизацией (автооптимизатор)

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

Прогнозирование временных рядов (Часть 1): метод эмпирической модовой декомпозиции (EMD) Прогнозирование временных рядов (Часть 1): метод эмпирической модовой декомпозиции (EMD)

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