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

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

MetaTrader 5Примеры | 24 января 2020, 09:14
4 308 3
Artyom Trishkin
Artyom Trishkin

Содержание

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

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

Концепция

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

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

Сделаем так: при создании отложенного запроса будем указывать дистанцию установки отложенного ордера. Эту дистанцию всегда можно узнать при помощи разницы текущей цены на момент создания отложенного запроса (в это свойство записывается текущая цена Ask или Bid, в зависимости от направления будущего ордера) и цены установки отложенного ордера (которая также записывается в свойства объекта-отложенного запроса). Т.е. при любом значении цены на момент активации отложенного запроса мы имеем возможность рассчитать новую цену установки отложенного ордера, или оставить её той, которая указывалась при создании отложенного запроса.

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


Реализиция

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

//+------------------------------------------------------------------+
//| Класс абстрактного отложенного торгового запроса                 |
//+------------------------------------------------------------------+
class CPendRequest : public CBaseObj
  {
private:
   MqlTradeRequest   m_request;                                            // Структура торгового запроса
   CPause            m_pause;                                              // Объект класса "Пауза"
   bool              m_follow;                                             // Флаг следования за ценой точки отсчёта дистанции установки отложенного ордера
/* Данные активации отложенного запроса в массиве:

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

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

//--- Возвращает количество знаков после запятой контролируемого свойства
   int               DigitsControlledValue(const uint index)               const;
//--- Устанавливает всем ценам ордера новое значение, изменённое на величину смещения (+/-)
   void              SetAllMqlPrices(const double shift);

public:

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

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

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

//--- Модифицирует цены ордера по значению текущей цены
   void                 CorrectMqlPricesByCurrentPrice(const double price);
                          
//--- Устанавливает цену (1) при создании запроса, (2) установки, (3) StopLoss, (4) TakeProfit, (5) stoplimit,
//--- (6) время создания запроса, (7) время текущей попытки, (8) продолжительность ожидания между запросами, (9) номер текущей попытки,
//---  (10) количество попыток,(11) идентификатор, (12) тикет ордера, (13) тикет позиции, (14) тип отложенного запроса
   void                 SetPriceCreate(const double price)           { this.SetProperty(PEND_REQ_PROP_PRICE_CREATE,price);                                              }
   void                 SetMqlPrice(const double price)              { this.SetProperty(PEND_REQ_PROP_MQL_REQ_PRICE,price); this.m_request.price=price;                 }
   void                 SetMqlSL(const double sl)                    { this.SetProperty(PEND_REQ_PROP_MQL_REQ_SL,sl); this.m_request.sl=sl;                             }
   void                 SetMqlTP(const double tp)                    { this.SetProperty(PEND_REQ_PROP_MQL_REQ_TP,tp); this.m_request.tp=tp;                             }
   void                 SetMqlStopLimit(const double stoplimit)      { this.SetProperty(PEND_REQ_PROP_MQL_REQ_STOPLIMIT,stoplimit); this.m_request.stoplimit=stoplimit; }
   void                 SetTimeCreate(const ulong time)
                          {
                           this.SetProperty(PEND_REQ_PROP_TIME_CREATE,time);
                           this.m_pause.SetTimeBegin(time);
                          }
   void                 SetTimeActivate(const ulong time)                        { this.SetProperty(PEND_REQ_PROP_TIME_ACTIVATE,time);                   }
   void                 SetWaitingMSC(const ulong miliseconds)
                          { 
                           this.SetProperty(PEND_REQ_PROP_WAITING,miliseconds);
                           this.m_pause.SetWaitingMSC(miliseconds);
                          }
   void                 SetCurrentAttempt(const uchar number)                    { this.SetProperty(PEND_REQ_PROP_CURRENT_ATTEMPT,number);               }
   void                 SetTotalAttempts(const uchar number)                     { this.SetProperty(PEND_REQ_PROP_TOTAL,number);                         }
   void                 SetID(const uchar id)                                    { this.SetProperty(PEND_REQ_PROP_ID,id);                                }
   void                 SetOrder(const ulong ticket)                             { this.SetProperty(PEND_REQ_PROP_MQL_REQ_ORDER,ticket);                 }
   void                 SetPosition(const ulong ticket)                          { this.SetProperty(PEND_REQ_PROP_MQL_REQ_POSITION,ticket);              }
   void                 SetTypeRequest(const ENUM_PEND_REQ_TYPE type)            { this.SetProperty(PEND_REQ_PROP_TYPE,type);                            }
   
//--- Устанавливает фактический (1) объём, (2) цену установки ордера, (3) цену установки limit-ордера,
//--- (4) цену установки stoploss-ордера, (5) цену установки takeprofit-ордера, (6) тип заливки ордера,
//--- (7) тип экспирации ордера, (8) время жизни ордера
   void                 SetActualVolume(const double volume)                     { this.SetProperty(PEND_REQ_PROP_ACTUAL_VOLUME,volume);                 }
   void                 SetActualPrice(const double price)                       { this.SetProperty(PEND_REQ_PROP_ACTUAL_PRICE,price);                   }
   void                 SetActualStopLimit(const double price)                   { this.SetProperty(PEND_REQ_PROP_ACTUAL_STOPLIMIT,price);               }
   void                 SetActualSL(const double price)                          { this.SetProperty(PEND_REQ_PROP_ACTUAL_SL,price);                      }
   void                 SetActualTP(const double price)                          { this.SetProperty(PEND_REQ_PROP_ACTUAL_TP,price);                      }
   void                 SetActualTypeFilling(const ENUM_ORDER_TYPE_FILLING type) { this.SetProperty(PEND_REQ_PROP_ACTUAL_TYPE_FILLING,type);             }
   void                 SetActualTypeTime(const ENUM_ORDER_TYPE_TIME type)       { this.SetProperty(PEND_REQ_PROP_ACTUAL_TYPE_TIME,type);                }
   void                 SetActualExpiration(const datetime expiration)           { this.SetProperty(PEND_REQ_PROP_ACTUAL_EXPIRATION,expiration);         }

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

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

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

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

//+------------------------------------------------------------------+
//| Устанавливает всем ценам ордера новое значение,                  |
//| изменённое на величину смещения (+/-)                            |
//+------------------------------------------------------------------+
void CPendRequest::SetAllMqlPrices(const double shift)
  {
   this.SetMqlPrice(this.GetProperty(PEND_REQ_PROP_MQL_REQ_PRICE)-shift);
   if(this.GetProperty(PEND_REQ_PROP_MQL_REQ_SL)!=0)
      this.SetMqlSL(this.GetProperty(PEND_REQ_PROP_MQL_REQ_SL)-shift);
   if(this.GetProperty(PEND_REQ_PROP_MQL_REQ_TP)!=0)
      this.SetMqlTP(this.GetProperty(PEND_REQ_PROP_MQL_REQ_TP)-shift);
   if(this.GetProperty(PEND_REQ_PROP_MQL_REQ_STOPLIMIT)!=0)
      this.SetMqlStopLimit(this.GetProperty(PEND_REQ_PROP_MQL_REQ_STOPLIMIT)-shift);
  }
//+------------------------------------------------------------------+

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

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

//+------------------------------------------------------------------+
//| Корректирует цены ордера по значению текущей цены                |
//+------------------------------------------------------------------+
void CPendRequest::CorrectMqlPricesByCurrentPrice(const double price)
  {
   ENUM_ORDER_TYPE type=this.m_request.type;
   if(!this.m_follow || (type<ORDER_TYPE_BUY_LIMIT && type>ORDER_TYPE_SELL_STOP_LIMIT))
      return;
   this.SetAllMqlPrices(this.PriceCreate()-price);
  }
//+------------------------------------------------------------------+

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

Теперь внесём дополнения и исправления в файл PendReqControl.mqh класса управления торговлей CPendReqControl.

Переименуем публичные методы создания отложенных запросов OpenPositionPending() и PlaceOrderPending() в CreatePReqPosition() и CreatePReqOrder() соответственно. Мне кажется, что такие названия методов правильнее отражают их суть — создание отложенного запроса.

Во входные параметры метода CreatePReqOrder() добавим передачу идентификаторов групп:

//--- (1) Создаёт отложенный запрос (1) на открытие позиции, (2) на установку отложенного ордера
   template<typename SL,typename TP> 
   int                  CreatePReqPosition(const ENUM_POSITION_TYPE type,
                                        const double volume,
                                        const string symbol,
                                        const ulong magic=ULONG_MAX,
                                        const SL sl=0,
                                        const TP tp=0,
                                        const uchar group_id1=0,
                                        const uchar group_id2=0,
                                        const string comment=NULL,
                                        const ulong deviation=ULONG_MAX,
                                        const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE);
   template<typename PS,typename PL,typename SL,typename TP>
   int                  CreatePReqOrder(const ENUM_ORDER_TYPE order_type,
                                        const double volume,
                                        const string symbol,
                                        const PS price_set,
                                        const PL price_limit=0,
                                        const SL sl=0,
                                        const TP tp=0,
                                        const ulong magic=ULONG_MAX,
                                        const uchar group_id1=0,
                                        const uchar group_id2=0,
                                        const string comment=NULL,
                                        const datetime expiration=0,
                                        const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE,
                                        const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE);
//--- Устанавливает критерии активации отложенного запроса

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

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

   //--- Обновляем актуальные данные условий активации запросов
   this.RefreshControlActualDatas(req_obj,symbol_obj);
   
   //--- Если все условия срабатывания отложенного запроса исполнены
   if(req_obj.IsAllComparisonCompleted())
     {
      //--- Устанавливаем номер попытки в объекте-запросе
      req_obj.SetCurrentAttempt(uchar(req_obj.CurrentAttempt()+1));
      //--- Корректируем цены для отложенного ордера относительно текущей цены и получаем заново запрос
      if(request.action==TRADE_ACTION_PENDING)
        {
         req_obj.CorrectMqlPricesByCurrentPrice(PositionTypeByOrderType(request.type)==POSITION_TYPE_BUY ? symbol_obj.AskLast() : symbol_obj.BidLast());
         request=req_obj.MqlRequest();
        }
      //--- Выводим в журнал сообщение о срабатывании запроса
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
        {
         ::Print(CMessage::Text(MSG_LIB_TEXT_REQUEST_ACTIVATED)+(string)req_obj.ID()+":");
         req_obj.PrintShort();
        }
      //--- В зависимости от типа выполняемого действия в торговом запросе 
      switch(request.action)
        {
         //--- Открытие/закрытие позиции
         case TRADE_ACTION_DEAL :
            //--- Если тикета нет в структуре запроса - это открытие позиции
            if(request.position==0)
               this.OpenPosition((ENUM_POSITION_TYPE)request.type,request.volume,request.symbol,request.magic,request.sl,request.tp,request.comment,request.deviation,request.type_filling);
            //--- Если тикет есть в структуре запроса - это закрытие позиции
            else
               this.ClosePosition(request.position,request.volume,request.comment,request.deviation);
            break;
         //--- Модификация StopLoss/TakeProfit позиции
         case TRADE_ACTION_SLTP :
            this.ModifyPosition(request.position,request.sl,request.tp);
            break;
         //--- Закрытие встречным
         case TRADE_ACTION_CLOSE_BY :
            this.ClosePositionBy(request.position,request.position_by);
            break;
         //---
         //--- Установка отложенного ордера
         case TRADE_ACTION_PENDING :
            this.PlaceOrder(request.type,request.volume,request.symbol,request.price,request.stoplimit,request.sl,request.tp,request.magic,request.comment,request.expiration,request.type_time,request.type_filling);
            break;
         //--- Модификация отложенного ордера
         case TRADE_ACTION_MODIFY :
            this.ModifyOrder(request.order,request.price,request.sl,request.tp,request.stoplimit,request.expiration,request.type_time,request.type_filling);
            break;
         //--- Удаление отложенного ордера
         case TRADE_ACTION_REMOVE :
            this.DeleteOrder(request.order);
            break;
         //---
         default:
            break;
        }  
     }
  }
//+------------------------------------------------------------------+

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

Немного подправим метод создания отложенного запроса на открытие позиции. При его создании методом copy-paste я допустил оплошность — метод должен возвращать целочисленное значение идентификатора отложенного запроса, а в данный момент он возвращает в ошибочных ситуациях false. Исправим на WRONG_VALUE:

//+------------------------------------------------------------------+
//| Создаёт отложенный запрос на открытие позиции                    |
//+------------------------------------------------------------------+
template<typename SL,typename TP> 
int CTradingControl::CreatePReqPosition(const ENUM_POSITION_TYPE type,
                                        const double volume,
                                        const string symbol,
                                        const ulong magic=ULONG_MAX,
                                        const SL sl=0,
                                        const TP tp=0,
                                        const uchar group_id1=0,
                                        const uchar group_id2=0,
                                        const string comment=NULL,
                                        const ulong deviation=ULONG_MAX,
                                        const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE)
  {
//--- Если установлен флаг глобального запрета торговли - выходим с возвратом 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_ORDER_TYPE order_type=(ENUM_ORDER_TYPE)type;
   ENUM_ACTION_TYPE action=(ENUM_ACTION_TYPE)order_type;
//--- Получаем объект-символ по имени символа.
   CSymbol *symbol_obj=this.m_symbols.GetSymbolObjByName(symbol);
//--- Если получить не удалось - записываем флаг "внутренняя ошибка", выводим сообщение в журнал и возвращаем WRONG_VALUE
   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();
//--- Если получить не удалось - записываем флаг "внутренняя ошибка", выводим сообщение в журнал и возвращаем WRONG_VALUE
   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;
     }
//--- Устанавливаем цены
//--- Если установить не удалось - записываем флаг "внутренняя ошибка" устанавливаем код ошибки в структуру возврата,
//--- выводим сообщение в журнал и возвращаем WRONG_VALUE
   if(!this.SetPrices(order_type,0,sl,tp,0,DFUN,symbol_obj))
     {
      this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_INTERNAL_ERR;
      trade_obj.SetResultRetcode(10021);
      trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode()));
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(10021));   // Отсутствуют котировки для обработки запроса
      return 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.volume=volume;
   this.m_request.deviation=(deviation==ULONG_MAX ? trade_obj.GetDeviation() : deviation);
   this.m_request.comment=(comment==NULL ? trade_obj.GetComment() : comment);
   this.m_request.type_filling=(type_filling>WRONG_VALUE ? type_filling : trade_obj.GetTypeFilling());
//--- Записываем в магик идентификатор объекта-отложенного запроса, идентификаторы групп в значение магика,
//--- и заполняем остальные незаполненные поля структуры торгового запроса
   uint mn=(magic==ULONG_MAX ? (uint)trade_obj.GetMagic() : (uint)magic);
   this.SetPendReqID((uchar)id,mn);
   if(group_id1>0)
      this.SetGroupID1(group_id1,mn);
   if(group_id2>0)
      this.SetGroupID2(group_id2,mn);
   this.m_request.magic=mn;
   this.m_request.action=TRADE_ACTION_DEAL;
   this.m_request.symbol=symbol_obj.Name();
   this.m_request.type=order_type;
//--- В результате создания отложенного торгового запроса возвращаем либо его идентификатор, либо -1 при неудаче
   if(this.CreatePendingRequest(PEND_REQ_STATUS_OPEN,(uchar)id,1,ulong(END_TIME-(ulong)::TimeCurrent()),this.m_request,0,symbol_obj,NULL))
      return id;
   return WRONG_VALUE;
  }
//+------------------------------------------------------------------+

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

//+------------------------------------------------------------------+
//| Создаёт отложенный запрос на установку отложенного ордера        |
//+------------------------------------------------------------------+
template<typename PS,typename PL,typename SL,typename TP>
int CTradingControl::CreatePReqOrder(const ENUM_ORDER_TYPE order_type,
                                     const double volume,
                                     const string symbol,
                                     const PS price_set,
                                     const PL price_limit=0,
                                     const SL sl=0,
                                     const TP tp=0,
                                     const ulong magic=ULONG_MAX,
                                     const uchar group_id1=0,
                                     const uchar group_id2=0,
                                     const string comment=NULL,
                                     const datetime expiration=0,
                                     const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE,
                                     const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE)
  {
//--- Если установлен флаг глобального запрета торговли - выходим с возвратом 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=(ENUM_ACTION_TYPE)order_type;
//--- Получаем объект-символ по имени символа
   CSymbol *symbol_obj=this.m_symbols.GetSymbolObjByName(symbol);
   if(symbol_obj==NULL)
     {
      this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_INTERNAL_ERR;
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_SYM_OBJ));
      return 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; 
     }
//--- Устанавливаем цены
//--- Если установить не удалось - записываем флаг "внутренняя ошибка" устанавливаем код ошибки в структуру возврата,
//--- выводим сообщение в журнал и возвращаем WRONG_VALUE
   if(!this.SetPrices(order_type,price_set,sl,tp,price_limit,DFUN,symbol_obj))
     {
      this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_INTERNAL_ERR;
      trade_obj.SetResultRetcode(10021);
      trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode()));
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(10021));   // Отсутствуют котировки для обработки запроса
      return 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.volume=volume;
   this.m_request.comment=(comment==NULL ? trade_obj.GetComment() : comment);
   this.m_request.type_time=(type_time>WRONG_VALUE ? type_time : trade_obj.GetTypeExpiration());
   this.m_request.type_filling=(type_filling>WRONG_VALUE ? type_filling : trade_obj.GetTypeFilling());
//--- Записываем в магик идентификатор запроса, в структуру запроса наименование символа,
//--- устанавливаем тип торговой операции и тип ордера
   uint mn=(magic==ULONG_MAX ? (uint)trade_obj.GetMagic() : (uint)magic);
   this.SetPendReqID((uchar)id,mn);
   if(group_id1>0)
      this.SetGroupID1(group_id1,mn);
   if(group_id2>0)
      this.SetGroupID2(group_id2,mn);
   this.m_request.magic=mn;
   this.m_request.symbol=symbol_obj.Name();
   this.m_request.action=TRADE_ACTION_PENDING;
   this.m_request.type=order_type;
//--- В результате создания отложенного торгового запроса возвращаем либо его идентификатор, либо -1 при неудаче
   if(this.CreatePendingRequest(PEND_REQ_STATUS_PLACE,(uchar)id,1,ulong(END_TIME-(ulong)::TimeCurrent()),this.m_request,0,symbol_obj,NULL))
      return id;
   return WRONG_VALUE;
  }
//+------------------------------------------------------------------+

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

При создании отложенного запроса нам необходимо записать в объект-отложенный запрос цену в момент его создания. Для разных типов ордеров нам нужно вписывать разные цены — для ордеров на покупку текущую цену Ask, а для ордеров на продажу — текущую цену Bid.
Для этого внесём правки в метод создания отложенного запроса CreatePendingRequest() в файле Trading.mqh класса основного торгового объекта CTrading:

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

Здесь мы при помощи функции определения типа позиции по типу ордера PositionTypeByOrderType() узнаём направление ордера, и если он на покупку, то используем цену Ask, если на продажу — используем цену Bid. И далее, при создании отложенного запроса передаём в метод его создания полученную цену.

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

//--- Создаёт отложенный запрос (1) на открытие позиции Buy, (2) на открытие позиции Sell
   template<typename SL,typename TP> 
   int                  OpenBuyPending(const double volume,
                                       const string symbol,
                                       const ulong magic=ULONG_MAX,
                                       const SL sl=0,
                                       const TP tp=0,
                                       const uchar group_id1=0,
                                       const uchar group_id2=0,
                                       const string comment=NULL,
                                       const ulong deviation=ULONG_MAX,
                                       const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE);
   template<typename SL,typename TP> 
   int                  OpenSellPending(const double volume,
                                       const string symbol,
                                       const ulong magic=ULONG_MAX,
                                       const SL sl=0,
                                       const TP tp=0,
                                       const uchar group_id1=0,
                                       const uchar group_id2=0,
                                       const string comment=NULL,
                                       const ulong deviation=ULONG_MAX,
                                       const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE);
                                    
//--- Создаёт отложенный запрос на установку ордера (1) BuyLimit, (2) BuyStop, (3) BuyStopLimit
   template<typename PS,typename SL,typename TP>
   int                  PlaceBuyLimitPending(const double volume,
                                             const string symbol,
                                             const PS price_set,
                                             const SL sl=0,
                                             const TP tp=0,
                                             const ulong magic=ULONG_MAX,
                                             const uchar group_id1=0,
                                             const uchar group_id2=0,
                                             const string comment=NULL,
                                             const datetime expiration=0,
                                             const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE,
                                             const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE);
   template<typename PS,typename SL,typename TP>
   int                  PlaceBuyStopPending( const double volume,
                                             const string symbol,
                                             const PS price_set,
                                             const SL sl=0,
                                             const TP tp=0,
                                             const ulong magic=ULONG_MAX,
                                             const uchar group_id1=0,
                                             const uchar group_id2=0,
                                             const string comment=NULL,
                                             const datetime expiration=0,
                                             const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE,
                                             const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE);
   template<typename PS,typename PL,typename SL,typename TP>
   int                  PlaceBuyStopLimitPending(const double volume,
                                             const string symbol,
                                             const PS price_stop,
                                             const PL price_limit,
                                             const SL sl=0,
                                             const TP tp=0,
                                             const ulong magic=ULONG_MAX,
                                             const uchar group_id1=0,
                                             const uchar group_id2=0,
                                             const string comment=NULL,
                                             const datetime expiration=0,
                                             const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE,
                                             const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE);

//--- Создаёт отложенный запрос на установку ордера (1) SellLimit, (2) SellStop, (3) SellStopLimit
   template<typename PS,typename SL,typename TP>
   int                  PlaceSellLimitPending(const double volume,
                                             const string symbol,
                                             const PS price_set,
                                             const SL sl=0,
                                             const TP tp=0,
                                             const ulong magic=ULONG_MAX,
                                             const uchar group_id1=0,
                                             const uchar group_id2=0,
                                             const string comment=NULL,
                                             const datetime expiration=0,
                                             const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE,
                                             const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE);
   template<typename PS,typename SL,typename TP>
   int                  PlaceSellStopPending(const double volume,
                                             const string symbol,
                                             const PS price_set,
                                             const SL sl=0,
                                             const TP tp=0,
                                             const ulong magic=ULONG_MAX,
                                             const uchar group_id1=0,
                                             const uchar group_id2=0,
                                             const string comment=NULL,
                                             const datetime expiration=0,
                                             const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE,
                                             const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE);
   template<typename PS,typename PL,typename SL,typename TP>
   int                  PlaceSellStopLimitPending(const double volume,
                                             const string symbol,
                                             const PS price_stop,
                                             const PL price_limit,
                                             const SL sl=0,
                                             const TP tp=0,
                                             const ulong magic=ULONG_MAX,
                                             const uchar group_id1=0,
                                             const uchar group_id2=0,
                                             const string comment=NULL,
                                             const datetime expiration=0,
                                             const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE,
                                             const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE);

//--- Устанавливает критерии активации отложенного запроса
   bool                 SetNewActivationProperties(const uchar id,
                                                   const ENUM_PEND_REQ_ACTIVATION_SOURCE source,
                                                   const int property,
                                                   const double control_value,
                                                   const ENUM_COMPARER_TYPE comparer_type,
                                                   const double actual_value);

А за пределами тела класса напишем реализацию всех этих методов, заодно исправив на новое наименование методы создания отложенных запросов на открытие позиций (мы их ранее переименовали на более подходящие названия):

//+------------------------------------------------------------------+
//| Создаёт отложенный запрос на открытие позиции Buy                |
//+------------------------------------------------------------------+
template<typename SL,typename TP> 
int CEngine::OpenBuyPending(const double volume,
                            const string symbol,
                            const ulong magic=ULONG_MAX,
                            const SL sl=0,
                            const TP tp=0,
                            const uchar group_id1=0,
                            const uchar group_id2=0,
                            const string comment=NULL,
                            const ulong deviation=ULONG_MAX,
                            const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE)
  {
   return this.m_trading.CreatePReqPosition(POSITION_TYPE_BUY,volume,symbol,magic,sl,tp,group_id1,group_id2,comment,deviation,type_filling);
  }
//+------------------------------------------------------------------+
//| Создаёт отложенный запрос на открытие позиции Sell               |
//+------------------------------------------------------------------+
template<typename SL,typename TP> 
int CEngine::OpenSellPending(const double volume,
                            const string symbol,
                            const ulong magic=ULONG_MAX,
                            const SL sl=0,
                            const TP tp=0,
                            const uchar group_id1=0,
                            const uchar group_id2=0,
                            const string comment=NULL,
                            const ulong deviation=ULONG_MAX,
                            const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE)
  {
   return this.m_trading.CreatePReqPosition(POSITION_TYPE_SELL,volume,symbol,magic,sl,tp,group_id1,group_id2,comment,deviation,type_filling);
  }
//+------------------------------------------------------------------+
//| Создаёт отложенный запрос на установку ордера BuyLimit           |
//+------------------------------------------------------------------+
template<typename PS,typename SL,typename TP>
int CEngine::PlaceBuyLimitPending(const double volume,
                                  const string symbol,
                                  const PS price_set,
                                  const SL sl=0,
                                  const TP tp=0,
                                  const ulong magic=ULONG_MAX,
                                  const uchar group_id1=0,
                                  const uchar group_id2=0,
                                  const string comment=NULL,
                                  const datetime expiration=0,
                                  const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE,
                                  const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE)
  {
   return this.m_trading.CreatePReqOrder(ORDER_TYPE_BUY_LIMIT,volume,symbol,price_set,0,sl,tp,magic,group_id1,group_id2,comment,expiration,type_time,type_filling);
  }
//+------------------------------------------------------------------+
//| Создаёт отложенный запрос на установку ордера BuyStop            |
//+------------------------------------------------------------------+
template<typename PS,typename SL,typename TP>
int CEngine::PlaceBuyStopPending(const double volume,
                                 const string symbol,
                                 const PS price_set,
                                 const SL sl=0,
                                 const TP tp=0,
                                 const ulong magic=ULONG_MAX,
                                 const uchar group_id1=0,
                                 const uchar group_id2=0,
                                 const string comment=NULL,
                                 const datetime expiration=0,
                                 const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE,
                                 const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE)
  {
   return this.m_trading.CreatePReqOrder(ORDER_TYPE_BUY_STOP,volume,symbol,price_set,0,sl,tp,magic,group_id1,group_id2,comment,expiration,type_time,type_filling);
  }
//+------------------------------------------------------------------+
//| Создаёт отложенный запрос на установку ордера BuyStopLimit       |
//+------------------------------------------------------------------+
template<typename PS,typename PL,typename SL,typename TP>
int CEngine::PlaceBuyStopLimitPending(const double volume,
                                      const string symbol,
                                      const PS price_stop,
                                      const PL price_limit,
                                      const SL sl=0,
                                      const TP tp=0,
                                      const ulong magic=ULONG_MAX,
                                      const uchar group_id1=0,
                                      const uchar group_id2=0,
                                      const string comment=NULL,
                                      const datetime expiration=0,
                                      const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE,
                                      const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE)
  {
   return
     (
      #ifdef __MQL4__ WRONG_VALUE #else 
      this.m_trading.CreatePReqOrder(ORDER_TYPE_BUY_STOP_LIMIT,volume,symbol,price_stop,price_limit,sl,tp,magic,group_id1,group_id2,comment,expiration,type_time,type_filling);
      #endif 
     ); 
  }
//+------------------------------------------------------------------+
//| Создаёт отложенный запрос на установку ордера SellLimit          |
//+------------------------------------------------------------------+
template<typename PS,typename SL,typename TP>
int CEngine::PlaceSellLimitPending(const double volume,
                                   const string symbol,
                                   const PS price_set,
                                   const SL sl=0,
                                   const TP tp=0,
                                   const ulong magic=ULONG_MAX,
                                   const uchar group_id1=0,
                                   const uchar group_id2=0,
                                   const string comment=NULL,
                                   const datetime expiration=0,
                                   const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE,
                                   const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE)
  {
   return this.m_trading.CreatePReqOrder(ORDER_TYPE_SELL_LIMIT,volume,symbol,price_set,0,sl,tp,magic,group_id1,group_id2,comment,expiration,type_time,type_filling);
  }
//+------------------------------------------------------------------+
//| Создаёт отложенный запрос на установку ордера SellStop           |
//+------------------------------------------------------------------+
template<typename PS,typename SL,typename TP>
int CEngine::PlaceSellStopPending(const double volume,
                                  const string symbol,
                                  const PS price_set,
                                  const SL sl=0,
                                  const TP tp=0,
                                  const ulong magic=ULONG_MAX,
                                  const uchar group_id1=0,
                                  const uchar group_id2=0,
                                  const string comment=NULL,
                                  const datetime expiration=0,
                                  const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE,
                                  const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE)
  {
   return this.m_trading.CreatePReqOrder(ORDER_TYPE_SELL_STOP,volume,symbol,price_set,0,sl,tp,magic,group_id1,group_id2,comment,expiration,type_time,type_filling);
  }
//+------------------------------------------------------------------+
//| Создаёт отложенный запрос на установку ордера SellStopLimit      |
//+------------------------------------------------------------------+
template<typename PS,typename PL,typename SL,typename TP>
int CEngine::PlaceSellStopLimitPending(const double volume,
                                       const string symbol,
                                       const PS price_stop,
                                       const PL price_limit,
                                       const SL sl=0,
                                       const TP tp=0,
                                       const ulong magic=ULONG_MAX,
                                       const uchar group_id1=0,
                                       const uchar group_id2=0,
                                       const string comment=NULL,
                                       const datetime expiration=0,
                                       const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE,
                                       const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE)
  {
   return
     (
      #ifdef __MQL4__ WRONG_VALUE #else 
      this.m_trading.CreatePReqOrder(ORDER_TYPE_SELL_STOP_LIMIT,volume,symbol,price_stop,price_limit,sl,tp,magic,group_id1,group_id2,comment,expiration,type_time,type_filling)
      #endif 
     );
  }
//+------------------------------------------------------------------+

Здесь методы создания отложенных запросов на установку отложенных ордеров возвращают результат работы метода создания отложенного запроса класса управления торговлей CTradingControl, в который передаются требуемые типы отложенного ордера, соответствующие методу, из которого осуществляется создание отложенного запроса. Для MQL4 возвращаем WRONG_VALUE — пока у нас не реализован класс объекта-отложенного StopLimit-ордера для MQL4.

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

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

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

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

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

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

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

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

Это все необходимые изменения тестового советника.

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


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

Что дальше

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

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

К содержанию

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

Часть 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. Работа с отложенными запросами - открытие позиций по условиям
Прикрепленные файлы |
MQL5.zip (3685.78 KB)
MQL4.zip (3685.78 KB)
Последние комментарии | Перейти к обсуждению на форуме трейдеров (3)
Anton Iaroshenko
Anton Iaroshenko | 18 апр. 2023 в 12:43

Скрипт не компилируется, находит 7 ошибок. Подробности в картинке

Artyom Trishkin
Artyom Trishkin | 18 апр. 2023 в 12:59
TopSeller #:

Скрипт не компилируется, находит 7 ошибок. Подробности в картинке

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

protected:

   переменная

private:

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

Anton Iaroshenko
Anton Iaroshenko | 18 апр. 2023 в 14:51
@Artyom Trishkin, извините я совсем не шарю в программировании. private поменял на protected, но ошибки остались. Не могли бы вы выложить рабочую версию скрипта, когда у вас будет время на это?
Мультивалютный мониторинг торговых сигналов (Часть 2): Реализация визуальной части приложения Мультивалютный мониторинг торговых сигналов (Часть 2): Реализация визуальной части приложения
В предыдущей статье был создан каркас приложения, на основе которого и будет строиться вся дальнейшая работа. Теперь последовательно приступим к его разработке - создадим визуальную часть приложения и настроим базовые взаимодействия элементов интерфейса.
Применение OLAP в трейдинге (Часть 3): анализ котировок в целях выработки торговых стратегий Применение OLAP в трейдинге (Часть 3): анализ котировок в целях выработки торговых стратегий
В данной статье мы продолжим рассматривать технологию OLAP в применении к трейдингу, расширяя функционал, представленный в первых двух статьях. На этот раз оперативному анализу подвергнутся котировки. Показано выдвижение и проверка гипотез о торговых стратегиях на основе агрегированных показателей истории. Представлены эксперты для исследований побаровых закономерностей и адаптивной торговли.
Библиотека для простого и быстрого создания программ для MetaTrader (Часть XXXIII): Отложенные торговые запросы - закрытие позиций по условиям Библиотека для простого и быстрого создания программ для MetaTrader (Часть XXXIII): Отложенные торговые запросы - закрытие позиций по условиям
Продолжаем работу над функционалом библиотеки для реализации торговли при помощи отложенных запросов. У нас уже реализована отправка торговых запросов по условию на открытие позиций и установку отложенных ордеров. Сегодня создадим возможность полного, частичного и встречного закрытия позиций по условию.
Работа с сетевыми функциями, или MySQL без DLL: Часть I - коннектор Работа с сетевыми функциями, или MySQL без DLL: Часть I - коннектор
Относительно недавно в MetaTrader 5 появились сетевые функции. Это открыло широкие возможности для программистов, которые разрабатывают продукты для Маркета, поскольку теперь можно реализовать то, чего раньше нельзя было сделать без динамических библиотек. В данной статье мы ознакомимся с ними на примере написания коннектора MySQL.