Biblioteca para criação simples e rápida de programas para MetaTrader (Parte XXXI): ordens de negociação pendentes, abertura de posições por condições

Artyom Trishkin | 7 abril, 2020

Sumário


Ideia

Ao projetar a funcionalidade da biblioteca, foi planejada uma ideia de negociação que usava ordens pendentes e que abrangia duas maneiras de trabalhar: processamento de erros do servidor de negociação e envio habitual de ordens de negociação de acordo com condições definidas programaticamente. Nos artigos anteriores, começando com o artigo 26, criamos passo a passo o processamento de erros do servidor de negociação com a ajuda de ordens pendentes que permitiam processar o envio repetido de ordens de negociação nos casos em que, para processamento do erro, era necessário e envio repetido da ordem para o servidor após depurar parâmetros incorretos e esperar um pouco.

A partir deste artigo, criaremos um recurso que nos permitirá negociar através de ordens pendentes de acordo com uma determinada condição.
Em resumo, o que obteremos com isso? Este recurso da biblioteca nos permitirá criar por conta própria as condições que, ao serem atendidas, desencadearão o envio da ordem de negociação para o servidor.
Por exemplo:

  1. Abrir uma posição Buy, caso atinjamos/ultrapassemos uma determinada hora ou caso o preço caia abaixo de um valor predeterminado (duas condições com base nos valores das propriedades do símbolo).
  2. Fechar parcialmente uma posição, caso ultrapassemos o lucro definido (uma condição com base no valor da propriedade de conta).
  3. Abrir uma posição oposta, caso seja registrado um evento de fechamento de posição por stop-loss (uma condição com base no valor da propriedade de evento de conta).

Estes são apenas três exemplos simples. Porém, as condições e suas combinações podem ser bastantes. Nesta fase, desenvolveremos o controle de alterações das propriedades de conta, de símbolo e de eventos que ocorrem na conta atual. As condições dessas três listas podem ser definidas usando qualquer uma de suas combinações.
Comecemos com a mais simples, isto é, com o controle de alterações nos valores das propriedades de símbolo e de conta. Mais para frente, trataremos do controle dos eventos de conta e da reação a eles.

Para que o objeto-ordem pendente possa funcionar como parte da lógica de negociação (envio de ordens de negociação por condição), precisamos adicionar a este objeto dados adicionais para armazenar condições de ativação de ordem pendente e seus métodos de controle e processamento. O repositório desses dados será uma matriz bidimensional, na qual a primeira dimensão armazenará o número da condição (não há limite para esse número) enquanto a segunda conterá todos os dados de uma condição cujo número será especificado na primeira dimensão: o tipo de fonte da condição (símbolo, conta ou evento), a condição em si (criamos uma enumeração para cada uma das fontes), o método de comparação (>,<,==,!=,>=,<=), o valor de controle da propriedade monitorada e seu valor atual.

O controle das condições definidas nos objetos-ordens pendentes será realizado no temporizador da classe de gerenciamento de ordens pendentes. A partir dele serão enviadas para o servidor as ordens pendentes "que estão aguardando sua hora", imediatamente após registrado o fato de serem atendidas todas as condições gravadas no objeto-ordem pendente.

Hoje, neste artigo, faremos e verificaremos nossa negociação com a ajuda de ordens pendentes — abertura de posições por condição. As duas condições monitoradas no EA de teste serão o preço e o tempo. A condições podem ser definidas tanto separadamente (quer pelo preço que pelo tempo) quanto em conjunto (pelo valor do preço e do tempo).


Preparação de dados

Começamos como de costume, isto é, adicionando os índices das novas mensagens da biblioteca, bem como os textos das mensagens que correspondem aos índices.

No arquivo Datas.mqh inserimos todos os índices de mensagens necessários:

//--- CEvent
   MSG_EVN_EVENT,                                     // Event
   MSG_EVN_TYPE,                                      // Event type

...

//--- CAccount
   MSG_ACC_ACCOUNT,                                   // Account
   MSG_ACC_PROP_LOGIN,                                // Account number

...

   MSG_LIB_TEXT_REQUEST,                              // Pending request #
   MSG_LIB_TEXT_REQUEST_ACTIVATED,                    // Pending request activated: #
   MSG_LIB_TEXT_REQUEST_DATAS,                        // Trading request parameters
   MSG_LIB_TEXT_PEND_REQUEST_DATAS,                   // Pending trading request parameters
   MSG_LIB_TEXT_PEND_REQUEST_CREATED,                 // Pending request created
   MSG_LIB_TEXT_PEND_REQUEST_DELETED,                 // Removed due to expiration
   MSG_LIB_TEXT_PEND_REQUEST_EXECUTED,                // Removed due to execution
   MSG_LIB_TEXT_PEND_REQUEST_GETTING_FAILED,          // Failed to obtain a pending request object from the list
   MSG_LIB_TEXT_PEND_REQUEST_FAILED_ADD_PARAMS,       // Failed to add request activation parameters. Error: 

   MSG_LIB_TEXT_PEND_REQUEST_PRICE_CREATE,            // Price at the moment of request generation

...

   MSG_LIB_TEXT_PEND_REQUEST_ACTUAL_EXPIRATION,       // Actual order lifetime
   
   MSG_LIB_TEXT_PEND_REQUEST_NO_FREE_IDS,             // No free IDs to create a pending request
   MSG_LIB_TEXT_PEND_REQUEST_ACTIVATION_TERMS,        // Activation conditions
   MSG_LIB_TEXT_PEND_REQUEST_CRITERION,               // Criterion
   MSG_LIB_TEXT_PEND_REQUEST_ADD_CRITERIONS,          // Added pending request activation conditions
   
  };
//+------------------------------------------------------------------+

e os textos que correspondem aos índices recém-adicionados:

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

...

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

...

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

...

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

Como as condições controladas vindo de fontes completamente diferentes (neste caso, essas condições são conta, símbolo e eventos da conta; mais para frente, podemos adicionar mais alguma fonte) serão processadas com um objeto-ordem pendente, para monitorar que uma condição de ativação de ordem pendente seja acionada, precisamos especificar a fonte de dados cujos parâmetros estamos rastreando. Afinal, ao rastrear os parâmetros da conta e do símbolo, eles podem ter os mesmos índices de propriedades, mas serão propriedades completamente diferentes. Para que não haja confusão, indicaremos a fonte de dados na qual são rastreados os valores de suas propriedades.

No arquivo Defines.mqh, inserimos a enumeração de fontes de ativação de ordens pendentes:

//+------------------------------------------------------------------+
//| Pending request type                                             |
//+------------------------------------------------------------------+
enum ENUM_PEND_REQ_TYPE
  {
   PEND_REQ_TYPE_ERROR=PENDING_REQUEST_ID_TYPE_ERR,         // Pending request created based on the return code or error
   PEND_REQ_TYPE_REQUEST=PENDING_REQUEST_ID_TYPE_REQ,       // Pending request created by request
  };
//+------------------------------------------------------------------+
//| Pending request activation source                                |
//+------------------------------------------------------------------+
enum ENUM_PEND_REQ_ACTIVATION_SOURCE
  {
   PEND_REQ_ACTIVATION_SOURCE_ACCOUNT,                      // Pending request activated by account data
   PEND_REQ_ACTIVATION_SOURCE_SYMBOL,                       // Pending request activated by symbol data
   PEND_REQ_ACTIVATION_SOURCE_EVENT,                        // Pending request activated by trading event data
  };
//+------------------------------------------------------------------+
//| Integer properties of a pending trading request                  |
//+------------------------------------------------------------------+
enum ENUM_PEND_REQ_PROP_INTEGER
  {

E aqui adicionamos a enumeração dos possíveis critérios pelos quais serão ativadas as ordens pendentes.
Para os critérios de ativação com base nas propriedades de conta, de símbolo e de eventos, usaremos enumerações diferentes:

//+------------------------------------------------------------------+
//| Possible criteria for activating requests by account properties  |
//+------------------------------------------------------------------+
enum ENUM_PEND_REQ_ACTIVATE_BY_ACCOUNT_PROP
  {
   //--- long
   PEND_REQ_ACTIVATE_BY_ACCOUNT_EMPTY                    =  MSG_LIB_PROP_NOT_SET,                     // Value not set
   PEND_REQ_ACTIVATE_BY_ACCOUNT_LEVERAGE                 =  MSG_ACC_PROP_LEVERAGE,                    // Activate by a provided leverage
   PEND_REQ_ACTIVATE_BY_ACCOUNT_LIMIT_ORDERS             =  MSG_ACC_PROP_LIMIT_ORDERS,                // Activate by a maximum allowed number of active pending orders
   PEND_REQ_ACTIVATE_BY_ACCOUNT_TRADE_ALLOWED            =  MSG_ACC_PROP_TRADE_ALLOWED,               // Activate by the permission to trade for the current account from the server side
   PEND_REQ_ACTIVATE_BY_ACCOUNT_TRADE_EXPERT             =  MSG_ACC_PROP_TRADE_EXPERT,                // Activate by the permission to trade for an EA from the server side
   //--- double
   PEND_REQ_ACTIVATE_BY_ACCOUNT_BALANCE                  =  MSG_ACC_PROP_BALANCE,                     // Activate by an account balance in the deposit currency
   PEND_REQ_ACTIVATE_BY_ACCOUNT_CREDIT                   =  MSG_ACC_PROP_CREDIT,                      // Activate by credit in a deposit currency
   PEND_REQ_ACTIVATE_BY_ACCOUNT_PROFIT                   =  MSG_ACC_PROP_PROFIT,                      // Activate by the current profit on the account in the deposit currency
   PEND_REQ_ACTIVATE_BY_ACCOUNT_EQUITY                   =  MSG_ACC_PROP_EQUITY,                      // Sort by an account equity in the deposit currency
   PEND_REQ_ACTIVATE_BY_ACCOUNT_MARGIN                   =  MSG_ACC_PROP_MARGIN,                      // Activate by an account reserved margin in the deposit currency
   PEND_REQ_ACTIVATE_BY_ACCOUNT_MARGIN_FREE              =  MSG_ACC_PROP_MARGIN_FREE,                 // Activate by account free funds available for opening a position in the deposit currency
   PEND_REQ_ACTIVATE_BY_ACCOUNT_MARGIN_LEVEL             =  MSG_ACC_PROP_MARGIN_LEVEL,                // Activate by account margin level in %
   PEND_REQ_ACTIVATE_BY_ACCOUNT_MARGIN_INITIAL           =  MSG_ACC_PROP_MARGIN_INITIAL,              // Activate by funds reserved on an account to ensure a guarantee amount for all pending orders
   PEND_REQ_ACTIVATE_BY_ACCOUNT_MARGIN_MAINTENANCE       =  MSG_ACC_PROP_MARGIN_MAINTENANCE,          // Activate by funds reserved on an account to ensure a minimum amount for all open positions
   PEND_REQ_ACTIVATE_BY_ACCOUNT_ASSETS                   =  MSG_ACC_PROP_ASSETS,                      // Activate by the current assets on the account
   PEND_REQ_ACTIVATE_BY_ACCOUNT_LIABILITIES              =  MSG_ACC_PROP_LIABILITIES,                 // Activate by the current liabilities on the account
   PEND_REQ_ACTIVATE_BY_ACCOUNT_COMMISSION_BLOCKED       =  MSG_ACC_PROP_COMMISSION_BLOCKED           // Activate by the current amount of blocked commissions on the account
  };
//+------------------------------------------------------------------+
//| Possible criteria for activating requests by symbol properties   |
//+------------------------------------------------------------------+
enum ENUM_PEND_REQ_ACTIVATE_BY_SYMBOL_PROP
  {
   PEND_REQ_ACTIVATE_BY_SYMBOL_EMPTY                     =  MSG_LIB_PROP_NOT_SET,                     // Value not set
   //--- double
   PEND_REQ_ACTIVATE_BY_SYMBOL_BID                       =  MSG_LIB_PROP_BID,                         // Activate by Bid - the best price at which a symbol can be sold
   PEND_REQ_ACTIVATE_BY_SYMBOL_ASK                       =  MSG_LIB_PROP_ASK,                         // Activate by Ask - best price, at which an instrument can be bought
   PEND_REQ_ACTIVATE_BY_SYMBOL_LAST                      =  MSG_LIB_PROP_LAST,                        // Activate by the last deal price
   //--- long
   PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_DEALS             =  MSG_SYM_PROP_SESSION_DEALS,               // Activate by number of deals in the current session
   PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_BUY_ORDERS        =  MSG_SYM_PROP_SESSION_BUY_ORDERS,          // Activate by number of Buy orders at the moment
   PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_SELL_ORDERS       =  MSG_SYM_PROP_SESSION_SELL_ORDERS,         // Activate by number of Sell orders at the moment
   PEND_REQ_ACTIVATE_BY_SYMBOL_VOLUME                    =  MSG_SYM_PROP_VOLUME,                      // Activate by the last deal volume
   PEND_REQ_ACTIVATE_BY_SYMBOL_VOLUMEHIGH                =  MSG_SYM_PROP_VOLUMEHIGH,                  // Activate by maximum Volume per day
   PEND_REQ_ACTIVATE_BY_SYMBOL_VOLUMELOW                 =  MSG_SYM_PROP_VOLUMELOW,                   // Activate by minimum Volume per day
   PEND_REQ_ACTIVATE_BY_SYMBOL_TIME                      =  MSG_SYM_PROP_TIME,                        // Activate by the last quote time
   PEND_REQ_ACTIVATE_BY_SYMBOL_SPREAD                    =  MSG_SYM_PROP_SPREAD,                      // Activate by spread in points
   PEND_REQ_ACTIVATE_BY_SYMBOL_START_TIME                =  MSG_SYM_PROP_START_TIME,                  // Activate by an instrument trading start date (usually used for futures)
   PEND_REQ_ACTIVATE_BY_SYMBOL_EXPIRATION_TIME           =  MSG_SYM_PROP_EXPIRATION_TIME,             // Activate by an instrument trading completion date (usually used for futures)
   PEND_REQ_ACTIVATE_BY_SYMBOL_TRADE_STOPS_LEVEL         =  MSG_SYM_PROP_TRADE_STOPS_LEVEL,           // Activate by the minimum indent from the current close price (in points) for setting Stop orders
   PEND_REQ_ACTIVATE_BY_SYMBOL_TRADE_FREEZE_LEVEL        =  MSG_SYM_PROP_TRADE_FREEZE_LEVEL,          // Activate by trade operation freeze distance (in points)
   //--- double
   PEND_REQ_ACTIVATE_BY_SYMBOL_BIDHIGH                   =  MSG_SYM_PROP_BIDHIGH,                     // Activate by a maximum Bid of the day
   PEND_REQ_ACTIVATE_BY_SYMBOL_BIDLOW                    =  MSG_SYM_PROP_BIDLOW,                      // Activate by a minimum Bid of the day
   PEND_REQ_ACTIVATE_BY_SYMBOL_ASKHIGH                   =  MSG_SYM_PROP_ASKHIGH,                     // Activate by a maximum Ask of the day
   PEND_REQ_ACTIVATE_BY_SYMBOL_ASKLOW                    =  MSG_SYM_PROP_ASKLOW,                      // Activate by a minimum Ask of the day
   PEND_REQ_ACTIVATE_BY_SYMBOL_LASTHIGH                  =  MSG_SYM_PROP_LASTHIGH,                    // Activate by the maximum Last of the day
   PEND_REQ_ACTIVATE_BY_SYMBOL_LASTLOW                   =  MSG_SYM_PROP_LASTLOW,                     // Activate by the minimum Last of the day
   PEND_REQ_ACTIVATE_BY_SYMBOL_VOLUME_REAL               =  MSG_SYM_PROP_VOLUME_REAL,                 // Activate by Volume of the day
   PEND_REQ_ACTIVATE_BY_SYMBOL_VOLUMEHIGH_REAL           =  MSG_SYM_PROP_VOLUMEHIGH_REAL,             // Activate by a maximum Volume of the day
   PEND_REQ_ACTIVATE_BY_SYMBOL_VOLUMELOW_REAL            =  MSG_SYM_PROP_VOLUMELOW_REAL,              // Activate by a minimum Volume of the day
   PEND_REQ_ACTIVATE_BY_SYMBOL_OPTION_STRIKE             =  MSG_SYM_PROP_OPTION_STRIKE,               // Activate by an option execution price
   PEND_REQ_ACTIVATE_BY_SYMBOL_TRADE_ACCRUED_INTEREST    =  MSG_SYM_PROP_TRADE_ACCRUED_INTEREST,      // Activate by an accrued interest
   PEND_REQ_ACTIVATE_BY_SYMBOL_TRADE_FACE_VALUE          =  MSG_SYM_PROP_TRADE_FACE_VALUE,            // Activate by a face value – initial bond value set by an issuer
   PEND_REQ_ACTIVATE_BY_SYMBOL_TRADE_LIQUIDITY_RATE      =  MSG_SYM_PROP_TRADE_LIQUIDITY_RATE,        // Activate by a liquidity rate – the share of an asset that can be used for a margin
   PEND_REQ_ACTIVATE_BY_SYMBOL_SWAP_LONG                 =  MSG_SYM_PROP_SWAP_LONG,                   // Activate by a long swap value
   PEND_REQ_ACTIVATE_BY_SYMBOL_SWAP_SHORT                =  MSG_SYM_PROP_SWAP_SHORT,                  // Activate by a short swap value
   PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_VOLUME            =  MSG_SYM_PROP_SESSION_VOLUME,              // Activate by a summary volume of the current session deals
   PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_TURNOVER          =  MSG_SYM_PROP_SESSION_TURNOVER,            // Activate by a summary turnover of the current session
   PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_INTEREST          =  MSG_SYM_PROP_SESSION_INTEREST,            // Activate by a summary open interest
   PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_BUY_ORDERS_VOLUME =  MSG_SYM_PROP_SESSION_BUY_ORDERS_VOLUME,   // Activate by the current volume of Buy orders
   PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_SELL_ORDERS_VOLUME=  MSG_SYM_PROP_SESSION_SELL_ORDERS_VOLUME,  // Activate by the current volume of Sell orders
   PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_OPEN              =  MSG_SYM_PROP_SESSION_OPEN,                // Activate by an open price of the current session
   PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_CLOSE             =  MSG_SYM_PROP_SESSION_CLOSE,               // Activate by a close price of the current session
   PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_AW                =  MSG_SYM_PROP_SESSION_AW,                  // Activate by an average weighted session price
   PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_PRICE_SETTLEMENT  =  MSG_SYM_PROP_SESSION_PRICE_SETTLEMENT,    // Activate by a settlement price of the current session
   PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_PRICE_LIMIT_MIN   =  MSG_SYM_PROP_SESSION_PRICE_LIMIT_MIN,     // Activate by a minimum session price 
   PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_PRICE_LIMIT_MAX   =  MSG_SYM_PROP_SESSION_PRICE_LIMIT_MAX,     // Activate by a maximum session price
  };
//+------------------------------------------------------------------+
//| Possible criteria for activating requests by events              |
//+------------------------------------------------------------------+
enum ENUM_PEND_REQ_ACTIVATE_BY_EVENT
  {
   PEND_REQ_ACTIVATE_BY_EVENT_EMPTY                               =  MSG_LIB_PROP_NOT_SET,                        // Value not set
   PEND_REQ_ACTIVATE_BY_EVENT_POSITION_OPENED                     =  MSG_EVN_STATUS_MARKET_POSITION,              // Position opened
   PEND_REQ_ACTIVATE_BY_EVENT_POSITION_CLOSED                     =  MSG_EVN_STATUS_HISTORY_POSITION,             // Position closed
   PEND_REQ_ACTIVATE_BY_EVENT_PENDING_ORDER_PLASED                =  MSG_EVN_PENDING_ORDER_PLASED,                // Pending order placed
   PEND_REQ_ACTIVATE_BY_EVENT_PENDING_ORDER_REMOVED               =  MSG_EVN_PENDING_ORDER_REMOVED,               // Pending order removed
   PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_CREDIT                      =  MSG_EVN_ACCOUNT_CREDIT,                      // Accruing credit (3)
   PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_CHARGE                      =  MSG_EVN_ACCOUNT_CHARGE,                      // Additional charges
   PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_CORRECTION                  =  MSG_EVN_ACCOUNT_CORRECTION,                  // Correcting entry
   PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_BONUS                       =  MSG_EVN_ACCOUNT_BONUS,                       // Charging bonuses
   PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_COMISSION                   =  MSG_EVN_ACCOUNT_COMISSION,                   // Additional commissions
   PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_COMISSION_DAILY             =  MSG_EVN_ACCOUNT_COMISSION_DAILY,             // Commission charged at the end of a day
   PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_COMISSION_MONTHLY           =  MSG_EVN_ACCOUNT_COMISSION_MONTHLY,           // Commission charged at the end of a trading month
   PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_COMISSION_AGENT_DAILY       =  MSG_EVN_ACCOUNT_COMISSION_AGENT_DAILY,       // Agent commission charged at the end of a trading day
   PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_COMISSION_AGENT_MONTHLY     =  MSG_EVN_ACCOUNT_COMISSION_AGENT_MONTHLY,     // Agent commission charged at the end of a month
   PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_INTEREST                    =  MSG_EVN_ACCOUNT_INTEREST,                    // Accruing interest on free funds
   PEND_REQ_ACTIVATE_BY_EVENT_BUY_CANCELLED                       =  MSG_EVN_BUY_CANCELLED,                       // Canceled buy deal
   PEND_REQ_ACTIVATE_BY_EVENT_SELL_CANCELLED                      =  MSG_EVN_SELL_CANCELLED,                      // Canceled sell deal
   PEND_REQ_ACTIVATE_BY_EVENT_DIVIDENT                            =  MSG_EVN_DIVIDENT,                            // Accruing dividends
   PEND_REQ_ACTIVATE_BY_EVENT_DIVIDENT_FRANKED                    =  MSG_EVN_DIVIDENT_FRANKED,                    // Accrual of franked dividend
   PEND_REQ_ACTIVATE_BY_EVENT_TAX                                 =  MSG_EVN_TAX,                                 // Tax accrual
   PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_BALANCE_REFILL              =  MSG_EVN_BALANCE_REFILL,                      // Replenishing account balance
   PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_BALANCE_WITHDRAWAL          =  MSG_EVN_BALANCE_WITHDRAWAL,                  // Withdrawing funds from an account
   PEND_REQ_ACTIVATE_BY_EVENT_PENDING_ORDER_ACTIVATED             =  MSG_EVN_ACTIVATED_PENDING,                   // Pending order activated by price
   PEND_REQ_ACTIVATE_BY_EVENT_PENDING_ORDER_ACTIVATED_PARTIAL     =  MSG_EVN_ACTIVATED_PENDING_PARTIALLY,         // Pending order partially activated by price
   PEND_REQ_ACTIVATE_BY_EVENT_POSITION_OPENED_PARTIAL             =  MSG_EVN_POSITION_OPENED_PARTIALLY,           // Position opened partially
   PEND_REQ_ACTIVATE_BY_EVENT_POSITION_CLOSED_PARTIAL             =  MSG_EVN_POSITION_CLOSED_PARTIALLY,           // Position closed partially
   PEND_REQ_ACTIVATE_BY_EVENT_POSITION_CLOSED_BY_POS              =  MSG_EVN_POSITION_CLOSED_BY_POS,              // Position closed by an opposite one
   PEND_REQ_ACTIVATE_BY_EVENT_POSITION_CLOSED_PARTIAL_BY_POS      =  MSG_EVN_POSITION_CLOSED_PARTIALLY_BY_POS,    // Position partially closed by an opposite one
   PEND_REQ_ACTIVATE_BY_EVENT_POSITION_CLOSED_BY_SL               =  MSG_EVN_POSITION_CLOSED_BY_SL,               // Position closed by StopLoss
   PEND_REQ_ACTIVATE_BY_EVENT_POSITION_CLOSED_BY_TP               =  MSG_EVN_POSITION_CLOSED_BY_TP,               // Position closed by TakeProfit
   PEND_REQ_ACTIVATE_BY_EVENT_POSITION_CLOSED_PARTIAL_BY_SL       =  MSG_EVN_POSITION_CLOSED_PARTIALLY_BY_SL,     // Position closed partially by StopLoss
   PEND_REQ_ACTIVATE_BY_EVENT_POSITION_CLOSED_PARTIAL_BY_TP       =  MSG_EVN_POSITION_CLOSED_PARTIALLY_BY_TP,     // Position closed partially by TakeProfit
   PEND_REQ_ACTIVATE_BY_EVENT_POSITION_REVERSED_BY_MARKET         =  MSG_EVN_POSITION_REVERSED_BY_MARKET,         // Position reversal by a new deal (netting)
   PEND_REQ_ACTIVATE_BY_EVENT_POSITION_REVERSED_BY_PENDING        =  MSG_EVN_POSITION_REVERSED_BY_PENDING,        // Position reversal by activating a pending order (netting)
   PEND_REQ_ACTIVATE_BY_EVENT_POSITION_REVERSED_BY_MARKET_PARTIAL =  MSG_EVN_POSITION_REVERSE_PARTIALLY,          // Position reversal by partial market order execution (netting)
   PEND_REQ_ACTIVATE_BY_EVENT_POSITION_VOLUME_ADD_BY_MARKET       =  MSG_EVN_POSITION_VOLUME_ADD_BY_MARKET,       // Added volume to a position by a new deal (netting)
   PEND_REQ_ACTIVATE_BY_EVENT_POSITION_VOLUME_ADD_BY_PENDING      =  MSG_EVN_POSITION_VOLUME_ADD_BY_PENDING,      // Added volume to a position by activating a pending order (netting)
   PEND_REQ_ACTIVATE_BY_EVENT_MODIFY_ORDER_PRICE                  =  MSG_EVN_MODIFY_ORDER_PRICE,                  // Order price change
   PEND_REQ_ACTIVATE_BY_EVENT_MODIFY_ORDER_PRICE_SL               =  MSG_EVN_MODIFY_ORDER_PRICE_SL,               // Changing order and StopLoss price 
   PEND_REQ_ACTIVATE_BY_EVENT_MODIFY_ORDER_PRICE_TP               =  MSG_EVN_MODIFY_ORDER_PRICE_TP,               // Order and TakeProfit price change
   PEND_REQ_ACTIVATE_BY_EVENT_MODIFY_ORDER_PRICE_SL_TP            =  MSG_EVN_MODIFY_ORDER_PRICE_SL_TP,            // Changing order, StopLoss and TakeProfit price
   PEND_REQ_ACTIVATE_BY_EVENT_MODIFY_ORDER_SL_TP                  =  MSG_EVN_MODIFY_ORDER_SL_TP,                  // Changing order's StopLoss and TakeProfit price
   PEND_REQ_ACTIVATE_BY_EVENT_MODIFY_ORDER_SL                     =  MSG_EVN_MODIFY_ORDER_SL,                     // Modify StopLoss order
   PEND_REQ_ACTIVATE_BY_EVENT_MODIFY_ORDER_TP                     =  MSG_EVN_MODIFY_ORDER_TP,                     // Modify TakeProfit order
   PEND_REQ_ACTIVATE_BY_EVENT_MODIFY_POSITION_SL_TP               =  MSG_EVN_MODIFY_POSITION_SL_TP,               // Change position's StopLoss and TakeProfit
   PEND_REQ_ACTIVATE_BY_EVENT_MODIFY_POSITION_SL                  =  MSG_EVN_MODIFY_POSITION_SL,                  // Modify position's StopLoss
   PEND_REQ_ACTIVATE_BY_EVENT_MODIFY_POSITION_TP                  =  MSG_EVN_MODIFY_POSITION_TP,                  // Modify position's TakeProfit
   PEND_REQ_ACTIVATE_BY_EVENT_POSITION_VOL_ADD_BY_MARKET_PARTIAL  =  MSG_EVN_REASON_ADD_PARTIALLY,                // Added volume to a position by partial execution of a market order (netting)
   PEND_REQ_ACTIVATE_BY_EVENT_POSITION_VOL_ADD_BY_PENDING_PARTIAL =  MSG_EVN_REASON_ADD_BY_PENDING_PARTIALLY,     // Added volume to a position by partial activation of a pending order (netting)
   PEND_REQ_ACTIVATE_BY_EVENT_TRIGGERED_STOP_LIMIT_ORDER          =  MSG_EVN_REASON_STOPLIMIT_TRIGGERED,          // StopLimit order activation
   PEND_REQ_ACTIVATE_BY_EVENT_POSITION_REVERSED_BY_PENDING_PARTIAL=  MSG_EVN_REASON_REVERSE_BY_PENDING_PARTIALLY, // Position reversal by activating a pending order (netting)
  };
//+------------------------------------------------------------------+

Os valores das constantes das enumerações são iguais aos valores das constantes das mensagens de texto das propriedades de símbolo, de conta e de evento. Ao exibir mensagens no log, nos métodos de descrição de condições de ativação de ordem isso irá nos permitir utilizar o índice da constante para exibição da mensagem, em vez de termos de identificar adicionalmente que a constante descrita pertence à propriedade de símbolo, de conta ou de evento.

Com a ajuda de três enumerações de condições de ativação que são diferentes, no final, podemos definir qualquer combinação de constantes a partir dessas três enumerações, para gerar o critério de ativação de ordem pendente necessário.

No arquivo de funções de serviço DELib.mqh, inserimos uma função que retorna uma descrição do tipo de comparação:

//+------------------------------------------------------------------+
//| Return the comparison type description                           |
//+------------------------------------------------------------------+
string ComparisonTypeDescription(const ENUM_COMPARER_TYPE type)
  {
   switch((int)type)
     {
      case EQUAL           :  return " == ";
      case MORE            :  return " > ";
      case LESS            :  return " < ";
      case EQUAL_OR_MORE   :  return " >= ";
      case EQUAL_OR_LESS   :  return " <= ";
      default              :  return " != ";
     }
  }
//+------------------------------------------------------------------+

Em muitos arquivos de biblioteca, foram alterados os nomes das constantes das enumerações em que foram registradas as strings "STOP_LOSS" e "TAKE_PROFIT". Agora, as ocorrências destas linhas são substituídas por "SL" e "TP", respectivamente.

Objeto-ordem pendente criado por solicitação

O objeto base de ordem pendente abstrata agora será herdado do objeto base de todos os objetos da biblioteca.
Ao arquivo da classe CPendRequest anexamos o arquivo do objeto base de todos os objetos da biblioteca e fazermos com que a classe se torne numa herdeira do objeto base:

//+------------------------------------------------------------------+
//|                                                  PendRequest.mqh |
//|                        Copyright 2019, MetaQuotes Software Corp. |
//|                             https://mql5.com/pt/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2019, MetaQuotes Software Corp."
#property link      "https://mql5.com/pt/users/artmedia70"
#property version   "1.00"
#property strict    // Necessary for mql4
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include <Object.mqh>
#include "..\..\Services\DELib.mqh"
#include "..\..\Objects\BaseObj.mqh"
//+------------------------------------------------------------------+
//| Abstract pending trading request class                           |
//+------------------------------------------------------------------+
class CPendRequest : public CBaseObj
  {

Na seção privada da classe declaramos uma matriz para armazenar dados com base nas propriedades rastreadas dos critérios de ativação de ordem pendente:

//+------------------------------------------------------------------+
//| Abstract pending trading request class                           |
//+------------------------------------------------------------------+
class CPendRequest : public CBaseObj
  {
private:
   MqlTradeRequest   m_request;                                            // Trade request structure
   CPause            m_pause;                                              // Pause class object

/* Data on a pending request activation in the array:
   The first dimension contains the activation criteria number
   The second one features:

   m_activated_control[criterion number][0] - controlled property source
   m_activated_control[criterion number][1] - controlled property
   m_activated_control[criterion number][2] - type of comparing a controlled property with an actual value (=,>,<,!=,>=,<=)
   m_activated_control[criterion number][3] - property reference value for activation
   m_activated_control[criterion number][4] - actual property value
*/
   double            m_activated_control[][5];                             // Array of reference values of the pending request activation criterion
   
//--- Copy trading request data
   void              CopyRequest(const MqlTradeRequest &request);

Nesta mesma seção privada, escrevemos os métodos que retornam o magic, o identificador de magic, definido nas configurações do EA, e os identificadores do primeiro e segundo grupos.
Além disso, declaramos um método para retornar um sinalizador indicando que a verificação da propriedade controlada com seu valor real foi bem-sucedida,
um método para comparar dois valores de uma propriedade controlada e um método que retorna o número de casas decimais da propriedade monitorada a fim de enviar corretamente os valores para o log:

//--- Return (1) the magic number, ID of the (2) magic number, (3) the first group, (4) the second group,
//--- (5) hedging account flag, (6) flag indicating the real property is equal to the value
   ulong             GetMagic(void)                                        const { return this.GetProperty(PEND_REQ_PROP_MQL_REQ_MAGIC);                             }
   ushort            GetMagicID(void)                                      const { return CBaseObj::GetMagicID((uint)this.GetProperty(PEND_REQ_PROP_MQL_REQ_MAGIC)); }
   uchar             GetGroupID1(void)                                     const { return CBaseObj::GetGroupID1((uint)this.GetProperty(PEND_REQ_PROP_MQL_REQ_MAGIC));}
   uchar             GetGroupID2(void)                                     const { return CBaseObj::GetGroupID2((uint)this.GetProperty(PEND_REQ_PROP_MQL_REQ_MAGIC));}
   bool              IsHedge(void)                                         const { return this.m_is_hedge;                                                           }
   bool              IsEqualByMode(const int mode,const double value)      const;
   bool              IsEqualByMode(const int mode,const long value)        const;
//--- Return the flags indicating the pending request has completed changing each of the order/position parameters
   bool              IsCompletedVolume(void)                               const;
   bool              IsCompletedPrice(void)                                const;
   bool              IsCompletedStopLimit(void)                            const;
   bool              IsCompletedStopLoss(void)                             const;
   bool              IsCompletedTakeProfit(void)                           const;
   bool              IsCompletedTypeFilling(void)                          const;
   bool              IsCompletedTypeTime(void)                             const;
   bool              IsCompletedExpiration(void)                           const;
   
//--- Return the flag of a successful check of a controlled object property and the appropriate actual property
   bool              IsComparisonCompleted(const uint index)               const;
//--- Compare two data source values by a comparison type
   bool              IsCompared(const double actual_value,const double control_value,const ENUM_COMPARER_TYPE compare) const;

//--- Return the number of decimal places of a controlled property
   int               DigitsControlledValue(const uint index)               const;

public:

Os métodos de retorno de identificadores de magic e de grupos usam os mesmos métodos do objeto pai CBaseObj, dos quais agora herdamos o objeto da ordem pendente abstrata base.

Na seção pública da classe no bloco de métodos para acesso simplificado às propriedades do objeto de solicitação adicionamos declarações de todos os métodos públicos necessários que consideraremos mais adiante:

//+------------------------------------------------------------------+
//| Methods of a simplified access to the request object properties  |
//+------------------------------------------------------------------+
//--- Return (1) request structure, (2) status, (3) type, (4) price at the moment of the request generation,
//--- (5) request generation time, (6) next attempt activation time,
//--- (7) waiting time between requests, (8) current attempt index,
//--- (9) number of attempts, (10) request ID
//--- (11) result a request is based on,
//--- (12) order ticket, (13) position ticket, (14) trading operation type
   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);   }

//--- Return the actual (1) volume, (2) order, (3) limit order,
//--- (4) stoploss order and (5) takeprofit order prices, (6) order filling type,
//--- (7) order expiration type and (8) order lifetime
   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);   }

//--- Set (1) the price when creating a request, (2) request creation time,
//--- (3) current attempt time, (4) waiting time between requests,
//--- (5) current attempt index, (6) number of attempts, (7) ID,
//--- (8) order ticket, (9) position ticket, (10) pending request type
   void                 SetPriceCreate(const double price)                       { this.SetProperty(PEND_REQ_PROP_PRICE_CREATE,price);                   }
   void                 SetTimeCreate(const ulong time)
                          {
                           this.SetProperty(PEND_REQ_PROP_TIME_CREATE,time);
                           this.m_pause.SetTimeBegin(time);
                          }
   void                 SetTimeActivate(const ulong time)                        { this.SetProperty(PEND_REQ_PROP_TIME_ACTIVATE,time);                   }
   void                 SetWaitingMSC(const ulong miliseconds)
                          { 
                           this.SetProperty(PEND_REQ_PROP_WAITING,miliseconds);
                           this.m_pause.SetWaitingMSC(miliseconds);
                          }
   void                 SetCurrentAttempt(const uchar number)                    { this.SetProperty(PEND_REQ_PROP_CURRENT_ATTEMPT,number);               }
   void                 SetTotalAttempts(const uchar number)                     { this.SetProperty(PEND_REQ_PROP_TOTAL,number);                         }
   void                 SetID(const uchar id)                                    { this.SetProperty(PEND_REQ_PROP_ID,id);                                }
   void                 SetOrder(const ulong ticket)                             { this.SetProperty(PEND_REQ_PROP_MQL_REQ_ORDER,ticket);                 }
   void                 SetPosition(const ulong ticket)                          { this.SetProperty(PEND_REQ_PROP_MQL_REQ_POSITION,ticket);              }
   void                 SetTypeRequest(const ENUM_PEND_REQ_TYPE type)            { this.SetProperty(PEND_REQ_PROP_TYPE,type);                            }
   
//--- Set the actual (1) volume, (2) order, (3) limit order,
//--- (4) stoploss order and (5) takeprofit order prices, (6) order filling type,
//--- (7) order expiration type and (8) order lifetime
   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);         }

//--- Set a controlled property and a comparison method for a request activation criteria data by its index - both the actual one and the one in the object of
//--- account, symbol or trading event property value (depends on 'source' value) for activating a pending request
   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);
   
//--- Set a (1) controlled property, (2) comparison type, (3) object value and
//--- (4) actual controlled property value for activating a pending request
   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);
   
//--- Return (1) a pending request activation source, (2) controlled property, (3) comparison type,
//---  (4) object value,(5) actual controlled property value for activating a pending request
   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;
   
//--- Return the flag of a successful check of all controlled object properties and the appropriate actual properties
   bool                 IsAllComparisonCompleted(void)  const;


O método SetTypeRequest() define, para a propriedade "tipo de ordem pendente", o tipo transferido ao método. O tipo pode ser “ordem pendente criada por código de erro” ou “ordem pendente criada por solicitação”. O tipo de ordem pendente é definido automaticamente no construtor da classe no objeto, dependendo do valor do parâmetro "código de erro". Se o código for zero, será porque se trata de uma ordem pendente criada por solicitação do programa. Portanto, este método não é usado em nenhum lugar agora, e é criado caso precisemos alterar rapidamente de fora o tipo de ordem pendente.

Ao bloco de métodos que retornam descrições das propriedades do objeto-ordem, inserimos declarações de métodos que retornam descrições de propriedades controladas:

//+------------------------------------------------------------------+
//| Descriptions of request object properties                        |
//+------------------------------------------------------------------+
//--- Get description of a request (1) integer, (2) real and (3) string property
   string               GetPropertyDescription(ENUM_PEND_REQ_PROP_INTEGER property);
   string               GetPropertyDescription(ENUM_PEND_REQ_PROP_DOUBLE property);
   string               GetPropertyDescription(ENUM_PEND_REQ_PROP_STRING property);
   
//--- Return the description of a (1) controlled property, (2) comparison type, (3) controlled property value in the object,
//--- (4) actual controlled property value for activating a pending request, (5) total number of activation conditions
   string               GetActivationPropertyDescription(const uint index)       const;
   string               GetActivationComparerTypeDescription(const uint index)   const;
   string               GetActivationControlValueDescription(const uint index)   const;
   string               GetActivationActualValueDescription(const uint index)    const;
   uint                 GetActivationCriterionTotal(void)                        const { return ::ArrayRange(this.m_activated_control,0); }
   
//--- Return the names of pending request object parameters
   string               StatusDescription(void)                const;
   string               TypeRequestDescription(void)           const;
   string               IDDescription(void)                    const;
   string               RetcodeDescription(void)               const;
   string               TimeCreateDescription(void)            const;
   string               TimeActivateDescription(void)          const;
   string               TimeWaitingDescription(void)           const;
   string               CurrentAttemptDescription(void)        const;
   string               TotalAttemptsDescription(void)         const;
   string               PriceCreateDescription(void)           const;
   
   string               TypeFillingActualDescription(void)     const;
   string               TypeTimeActualDescription(void)        const;
   string               ExpirationActualDescription(void)      const;
   string               VolumeActualDescription(void)          const;
   string               PriceActualDescription(void)           const;
   string               StopLimitActualDescription(void)       const;
   string               StopLossActualDescription(void)        const;
   string               TakeProfitActualDescription(void)      const;
   
//--- Return the names of trading request structures parameters in the request object
   string               MqlReqActionDescription(void)          const;
   string               MqlReqMagicDescription(void)           const;
   string               MqlReqOrderDescription(void)           const;
   string               MqlReqSymbolDescription(void)          const;
   string               MqlReqVolumeDescription(void)          const;
   string               MqlReqPriceDescription(void)           const;
   string               MqlReqStopLimitDescription(void)       const;
   string               MqlReqStopLossDescription(void)        const;
   string               MqlReqTakeProfitDescription(void)      const;
   string               MqlReqDeviationDescription(void)       const;
   string               MqlReqTypeOrderDescription(void)       const;
   string               MqlReqTypeFillingDescription(void)     const;
   string               MqlReqTypeTimeDescription(void)        const;
   string               MqlReqExpirationDescription(void)      const;
   string               MqlReqCommentDescription(void)         const;
   string               MqlReqPositionDescription(void)        const;
   string               MqlReqPositionByDescription(void)      const;

//--- Display (1) description of request properties (full_prop=true - all properties, false - only supported ones),
//--- (2) request activation parameters, (3) short message about the request, (4) short request name (3 and 4 - implementation in the class descendants)
   void                 Print(const bool full_prop=false);
   void                 PrintActivations(void);
   virtual void         PrintShort(void){;}
   virtual string       Header(void){return NULL;}
  };
//+------------------------------------------------------------------+

O método GetActivationCriterionTotal() retorna o tamanho da primeira dimensão da matriz de dados de condições de ativação, em outras palavras, retorna o número de condições de ativação no objeto-ordem pendente.

No construtor da classe, para a matriz de dados de condições de ativação definimos zero como tamanho na primeira dimensão:

//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
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);
  }
//+------------------------------------------------------------------+

O tamanho da matriz de dados das condições de ativação mudará automaticamente quando for adicionada a seguinte condição de ativação.

No método que exibe a lista completa dos dados do objeto-ordem pedente, após exibir todas as suas propriedades, adicionamos uma lista de condições de ativação:

//+------------------------------------------------------------------+
//| Display the pending request properties in the journal            |
//+------------------------------------------------------------------+
void CPendRequest::Print(const bool full_prop=false)
  {
   int header_code=
     (
      this.GetProperty(PEND_REQ_PROP_STATUS)==PEND_REQ_STATUS_OPEN   ? MSG_LIB_TEXT_PEND_REQUEST_STATUS_OPEN   :
      this.GetProperty(PEND_REQ_PROP_STATUS)==PEND_REQ_STATUS_CLOSE  ? MSG_LIB_TEXT_PEND_REQUEST_STATUS_CLOSE  :
      this.GetProperty(PEND_REQ_PROP_STATUS)==PEND_REQ_STATUS_SLTP   ? MSG_LIB_TEXT_PEND_REQUEST_STATUS_SLTP   :
      this.GetProperty(PEND_REQ_PROP_STATUS)==PEND_REQ_STATUS_PLACE  ? MSG_LIB_TEXT_PEND_REQUEST_STATUS_PLACE  :
      this.GetProperty(PEND_REQ_PROP_STATUS)==PEND_REQ_STATUS_REMOVE ? MSG_LIB_TEXT_PEND_REQUEST_STATUS_REMOVE :
      this.GetProperty(PEND_REQ_PROP_STATUS)==PEND_REQ_STATUS_MODIFY ? MSG_LIB_TEXT_PEND_REQUEST_STATUS_MODIFY :
      WRONG_VALUE
     );
   ::Print("============= \"",CMessage::Text(header_code),"\" =============");
   int beg=0, end=PEND_REQ_PROP_INTEGER_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_PEND_REQ_PROP_INTEGER prop=(ENUM_PEND_REQ_PROP_INTEGER)i;
      if(!full_prop && !this.SupportProperty(prop)) continue;
      ::Print(this.GetPropertyDescription(prop));
     }
   ::Print("------");
   beg=end; end+=PEND_REQ_PROP_DOUBLE_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_PEND_REQ_PROP_DOUBLE prop=(ENUM_PEND_REQ_PROP_DOUBLE)i;
      if(!full_prop && !this.SupportProperty(prop)) continue;
      ::Print(this.GetPropertyDescription(prop));
     }
   ::Print("------");
   beg=end; end+=PEND_REQ_PROP_STRING_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_PEND_REQ_PROP_STRING prop=(ENUM_PEND_REQ_PROP_STRING)i;
      if(!full_prop && !this.SupportProperty(prop)) continue;
      ::Print(this.GetPropertyDescription(prop));
     }
   this.PrintActivations();
   ::Print("================== ",CMessage::Text(MSG_LIB_PARAMS_LIST_END),": \"",CMessage::Text(header_code),"\" ==================\n");
  }
//+------------------------------------------------------------------+

Implementação de um método que exibe no log dados sobre condições de ativação de ordem pendente:

//+------------------------------------------------------------------+
//| Display request activation parameters in the journal             |
//+------------------------------------------------------------------+
void CPendRequest::PrintActivations(void)
  {
   //--- Get the size of the activation conditions data array's first dimension,
   //--- if it exceeds zero, send all data written in the data array to the journal
   int range=::ArrayRange(this.m_activated_control,0);
   if(range>0)
     {
      ::Print("--- ",CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_ACTIVATION_TERMS)," ---");
      for(int i=0;i<range;i++)
        {
         ENUM_PEND_REQ_ACTIVATION_SOURCE source=(ENUM_PEND_REQ_ACTIVATION_SOURCE)this.m_activated_control[i][0];
         string type=
           (
            source==PEND_REQ_ACTIVATION_SOURCE_ACCOUNT   ?  CMessage::Text(MSG_ACC_ACCOUNT)     :
            source==PEND_REQ_ACTIVATION_SOURCE_SYMBOL    ?  CMessage::Text(MSG_LIB_PROP_SYMBOL) :
            source==PEND_REQ_ACTIVATION_SOURCE_EVENT     ?  CMessage::Text(MSG_EVN_EVENT)       :
            ""
           );
         ::Print(" - ",CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_CRITERION)," #",string(i+1),". ",type,": ",this.GetActivationPropertyDescription(i));
        }
     }
   ::Print("");
  }
//+------------------------------------------------------------------+

Método para criar uma nova condição de ativação de ordem pendente na matriz de dados de condições de ativação:

//+------------------------------------------------------------------+
//| Set a controlled property, values                                |
//| and comparison method for activating a pending request           |
//+------------------------------------------------------------------+
void CPendRequest::SetNewActivationProperties(const ENUM_PEND_REQ_ACTIVATION_SOURCE source,
                                              const int property,
                                              const double control_value,
                                              const ENUM_COMPARER_TYPE comparer_type,
                                              const double actual_value)
  {
   int range=::ArrayRange(this.m_activated_control,0);
   if(::ArrayResize(this.m_activated_control,range+1,10)==WRONG_VALUE)
     {
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_FAILED_ADD_PARAMS));
      return;
     }
   this.m_activated_control[range][0]=source;
   this.m_activated_control[range][1]=property;
   this.m_activated_control[range][2]=comparer_type;
   this.m_activated_control[range][3]=control_value;
   this.m_activated_control[range][4]=actual_value;
  }
//+---------------------------------------------------------------------+

Ao método são transferidos a fonte de dados de ativação, a condição de ativação, os valores da condição de ativação controlados e reais, bem como o método de comparação.
O tamanho da matriz de dados de condições de ativação aumenta em 1 e são preenchidos todos os dados necessários na matriz usando os valores transferidos nos parâmetros de entrada do método. Este método deve ser usado apenas para adicionar uma nova condição de ativação.

Para corrigir as condições de ativação que já existem no objeto-ordem, são usados os seguintes métodos:

//+---------------------------------------------------------------------+
//| Set a controlled property to activate a request                     |
//+---------------------------------------------------------------------+
bool CPendRequest::SetActivationProperty(const uint index,const ENUM_PEND_REQ_ACTIVATION_SOURCE source,const int property)
  {
   int range=::ArrayRange(this.m_activated_control,0);
   if((int)index>range-1 || range==0)
     {
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(4002));
      return false;
     }
   this.m_activated_control[index][0]=source;
   this.m_activated_control[index][1]=property;
   return true;
  }
//+------------------------------------------------------------------+
//| Set the object property comparison type                          |
//| with the actual one for a pending request activation             |
//+------------------------------------------------------------------+
bool CPendRequest::SetActivationComparerType(const uint index,const ENUM_COMPARER_TYPE comparer_type)
  {
   int range=::ArrayRange(this.m_activated_control,0);
   if((int)index>range-1 || range==0)
     {
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(4002));
      return false;
     }
   this.m_activated_control[index][2]=comparer_type;
   return true;
  }
//+------------------------------------------------------------------+
//| Set the controlled property                                      |
//| value for activating a pending request                           |
//+------------------------------------------------------------------+
bool CPendRequest::SetActivationControlValue(const uint index,const double value)
  {
   int range=::ArrayRange(this.m_activated_control,0);
   if((int)index>range-1 || range==0)
     {
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(4002));
      return false;
     }
   this.m_activated_control[index][3]=value;
   return true;
  }
//+------------------------------------------------------------------+
//| Set the actual value                                             |
//| of a controlled property in the request object                   |
//+------------------------------------------------------------------+
bool CPendRequest::SetActivationActualValue(const uint index,const double value)
  {
   int range=::ArrayRange(this.m_activated_control,0);
   if((int)index>range-1 || range==0)
     {
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(4002));
      return false;
     }
   this.m_activated_control[index][4]=value;
   return true;
  }
//+------------------------------------------------------------------+

Ao método para definir a propriedade controlada SetActivationProperty() é transferido o índice e dois parâmetros de condição, isto é, a fonte da condição (símbolo, conta ou evento) e a própria condição de ativação (a partir das enumerações correspondentes discutidas acima), já que a condição de ativação consiste em dois parâmetros, nomeadamente em fonte e em tipo de alteração de propriedade. Aos outros métodos de configuração de valores de ativação são transferidos apenas o índice e o valor.

O índice é o número da condição de ativação. Se for uma condição, o índice deverá ser zero. Se houver duas condições, o índice deverá ser 0 ou 1, dependendo da conta que queremos alterar, e assim por diante. Ao passar um índice que excede o tamanho da primeira dimensão da matriz, é exibida uma entrada de log sobre esse erro de índice, e é retornado false.

Métodos que retornam parâmetros de condição de ativação:

//+------------------------------------------------------------------+
//| Return a pending request activation source                       |
//+------------------------------------------------------------------+
ENUM_PEND_REQ_ACTIVATION_SOURCE CPendRequest::GetActivationSource(const uint index) const
  {
   int range=::ArrayRange(this.m_activated_control,0);
   if((int)index>range-1 || range==0)
     {
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(4002));
      return WRONG_VALUE;
     }
   return (ENUM_PEND_REQ_ACTIVATION_SOURCE)this.m_activated_control[index][0];
  }
//+------------------------------------------------------------------+
//| Return a controlled property to activate a request               |
//+------------------------------------------------------------------+
int CPendRequest::GetActivationProperty(const uint index) const
  {
   int range=::ArrayRange(this.m_activated_control,0);
   if((int)index>range-1 || range==0)
     {
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(4002));
      return WRONG_VALUE;
     }
   return (int)this.m_activated_control[index][1];
  }
//+------------------------------------------------------------------+
//| Return the object property comparison type                       |
//| with the actual one for a pending request activation             |
//+------------------------------------------------------------------+
ENUM_COMPARER_TYPE CPendRequest::GetActivationComparerType(const uint index) const
  {
   int range=::ArrayRange(this.m_activated_control,0);
   if((int)index>range-1 || range==0)
     {
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(4002));
      return WRONG_VALUE;
     }
   return (ENUM_COMPARER_TYPE)this.m_activated_control[index][2];
  }
//+------------------------------------------------------------------+
//| Return the controlled property                                   |
//| value for activating a pending request                           |
//+------------------------------------------------------------------+
double CPendRequest::GetActivationControlValue(const uint index) const
  {
   int range=::ArrayRange(this.m_activated_control,0);
   if((int)index>range-1 || range==0)
     {
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(4002));
      return EMPTY_VALUE;
     }
   return this.m_activated_control[index][3];
  }
//+------------------------------------------------------------------+
//| Return the actual value                                          |
//| of a controlled property in the request object                   |
//+------------------------------------------------------------------+
double CPendRequest::GetActivationActualValue(const uint index) const
  {
   int range=::ArrayRange(this.m_activated_control,0);
   if((int)index>range-1 || range==0)
     {
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(4002));
      return EMPTY_VALUE;
     }
   return this.m_activated_control[index][4];
  }
//+------------------------------------------------------------------+

Aqui, tudo é semelhante à configuração das propriedades de ativação, exceto que todas as propriedades de condições são retornadas uma de cada vez, por esse motivo, em cada método basta passar o índice da condição de ativação solicitada. No caso de transferência de um índice inválido, é retornado -1 e é exibida uma entrada de log sobre o erro de índice, enquanto para métodos retornando valores inteiros e valores reais é devolvido EMPTY_VALUE.

Método para comparar duas quantidades com base num determinado tipo de comparação:

//+------------------------------------------------------------------+
//| Compare two data source values by a comparison type              |
//+------------------------------------------------------------------+
bool CPendRequest::IsCompared(const double actual_value,const double control_value,const ENUM_COMPARER_TYPE compare) const
  {
   switch((int)compare)
     {
      case EQUAL           :  return(actual_value<control_value || actual_value>control_value ? false : true);
      case NO_EQUAL        :  return(actual_value<control_value || actual_value>control_value ? true : false);
      case MORE            :  return(actual_value>control_value ? true  : false);
      case LESS            :  return(actual_value<control_value ? true  : false);
      case EQUAL_OR_MORE   :  return(actual_value<control_value ? false : true);
      case EQUAL_OR_LESS   :  return(actual_value>control_value ? false : true);
      default              :  break;
     }
   return false;
  }
//+------------------------------------------------------------------+

Ao método é transferido o valor atual da propriedade comparada, o valor de controle com o qual é comparado o valor atual e o tipo de comparação.
Dependendo do tipo de comparação, são cotejados os valores do valor atual da propriedade com seu valor de controle, e é retornado o resultado da comparação.

Método que retorna um sinalizador indicando que as condições de ativação com base no índice na matriz de dados de condições de ativação foram comparadas com êxito:

//+-----------------------------------------------------------------------+
//| Return the flag of a successful check of a controlled object property |
//| and the appropriate real property                                     |
//+-----------------------------------------------------------------------+
bool CPendRequest::IsComparisonCompleted(const uint index) const
  {
//--- If the controlled property is not set, return 'false'
   if(this.m_activated_control[index][1]==MSG_LIB_PROP_NOT_SET)
      return false;
//--- Return the result of the specified comparison of a controlled property value with a real one
   ENUM_COMPARER_TYPE comparer=(ENUM_COMPARER_TYPE)this.m_activated_control[index][2];
   return this.IsCompared(this.m_activated_control[index][4],this.m_activated_control[index][3],comparer);
  }
//+------------------------------------------------------------------+

O método retorna um sinalizador para acionar uma das condições de ativação de ordem pendente. No parâmetro de entrada do método, é transferido o índice da condição a ser verificada na matriz de dados das condições de ativação. O método IsCompared(), considerado acima, realiza a comparação, e é retornado o resultado.

Método que retorna um sinalizador indicando que a verificação das condições de ativação criadas para o objeto-ordem foi bem-sucedida:

//+------------------------------------------------------------------+
//| Return the flag of successful check of all controlled properties |
//| of the object and the appropriate actual properties              |
//+------------------------------------------------------------------+
bool CPendRequest::IsAllComparisonCompleted(void) const
  {
   bool res=true;
   int range=::ArrayRange(this.m_activated_control,0);
   if(range==0)
      return false;
   for(int i=0;i<range;i++)
      res &=this.IsComparisonCompleted(i);
   return res;
  }
//+-------------------------------------------------------------------+

Este é um método universal que permite verificar qualquer objeto-ordem pendente durante sua ativação.
Aqui no ciclo para a primeira dimensão da matriz de condições de ativação, com a ajuda do método IsComparisonCompleted(), ao resultado da verificação (à variável res) é adicionado um sinalizador indicando que se verificou corretamente o índice em questão do ciclo da propriedade controlada. Após a conclusão do ciclo, é retornado o resultado da verificação de todas as condições. Se pelo menos uma das condições não for atendida ou se na primeira dimensão a matriz de dados tiver tamanho zero, o resultado será false.

Método que retorna o número de casas decimais para exibir corretamente no log uma descrição da condição de ativação:

//+-------------------------------------------------------------------+
//|Return the number of decimal places of a controlled property       |
//+-------------------------------------------------------------------+
int CPendRequest::DigitsControlledValue(const uint index) const
  {
   int dg=0;
   //--- Depending on the activation condition source, check the activation conditions
   //--- and write the required number of decimal places to the result
   switch((int)this.m_activated_control[index][0])
     {
      //--- Account. If an activation condition is an integer value, then 0,
      //--- if it is a real value, then the number of decimal places in the current currency
      case PEND_REQ_ACTIVATION_SOURCE_ACCOUNT      :
         dg=(this.m_activated_control[index][1]<PEND_REQ_ACTIVATE_BY_ACCOUNT_BALANCE  ?  0 : this.m_digits_currency);
        break;
      //--- Symbol. Depending on a condition, write either a number of decimal places in a symbol quote,
      //--- or a number of decimal places in the current currency, or a number of decimal places in the lot value, or zero
      case PEND_REQ_ACTIVATION_SOURCE_SYMBOL       :
         //--- digits
         if(
           (this.m_activated_control[index][1]<PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_DEALS &&
            this.m_activated_control[index][1]>PEND_REQ_ACTIVATE_BY_SYMBOL_EMPTY)                     ||
            this.m_activated_control[index][1]==PEND_REQ_ACTIVATE_BY_SYMBOL_OPTION_STRIKE             ||
            this.m_activated_control[index][1]>PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_SELL_ORDERS_VOLUME ||
           (this.m_activated_control[index][1]>PEND_REQ_ACTIVATE_BY_SYMBOL_TRADE_FREEZE_LEVEL &&
            this.m_activated_control[index][1]<PEND_REQ_ACTIVATE_BY_SYMBOL_VOLUME_REAL)
           ) dg=this.m_digits;
         //--- не digits
         else if(
            this.m_activated_control[index][1]>PEND_REQ_ACTIVATE_BY_SYMBOL_LASTLOW)
           {
            //--- digits currency
            if(
               (this.m_activated_control[index][1]>PEND_REQ_ACTIVATE_BY_SYMBOL_OPTION_STRIKE    && 
                this.m_activated_control[index][1]<PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_VOLUME)  ||
               this.m_activated_control[index][1]==PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_TURNOVER
              ) dg=this.m_digits_currency;
            //--- digits lots
            else
               dg=(this.m_digits_lot==0 ? 1 : this.m_digits_lot);
           }
         //--- 0
         else
            dg=0;
        break;
      //--- Default is zero
      default:
        break;
     }
   return dg;
  }
//+------------------------------------------------------------------+

No método é verificada a fonte de ativação, enquanto a condição de ativação é validada dependendo da fonte. Dependendo da condição de ativação, é retornado o número de casas decimais no valor do símbolo, o número de casas decimais no valor da moeda da conta atual, o número de casas decimais no valor do lote do símbolo ou zero.

Método que retorna uma descrição de texto sobre a propriedade controlada:

//+------------------------------------------------------------------+
//| Return the controlled property description by index              |
//+------------------------------------------------------------------+
string CPendRequest::GetActivationPropertyDescription(const uint index) const
  {
   //--- Get the activation source and, depending on that source, create a description text for a type of comparison with the reference value
   ENUM_PEND_REQ_ACTIVATION_SOURCE source=(ENUM_PEND_REQ_ACTIVATION_SOURCE)this.m_activated_control[index][0];
   string value=
     (
      source==PEND_REQ_ACTIVATION_SOURCE_EVENT     ?  "" : 
      (
       this.m_activated_control[index][1]==MSG_LIB_PROP_NOT_SET ? ""  :
       this.GetActivationComparerTypeDescription(index)+this.GetActivationControlValueDescription(index)
      )
     );
   //--- Return the activation conditions description + comparison type + controlled value
   return
     (
      source==PEND_REQ_ACTIVATION_SOURCE_ACCOUNT   ?  CMessage::Text((ENUM_PEND_REQ_ACTIVATE_BY_ACCOUNT_PROP)this.m_activated_control[index][1])+value   :
      source==PEND_REQ_ACTIVATION_SOURCE_SYMBOL    ?  CMessage::Text((ENUM_PEND_REQ_ACTIVATE_BY_SYMBOL_PROP)this.m_activated_control[index][1])+value    :
      source==PEND_REQ_ACTIVATION_SOURCE_EVENT     ?  CMessage::Text((ENUM_PEND_REQ_ACTIVATE_BY_EVENT)this.m_activated_control[index][1])+value          :
      ""
     );
  }
//+------------------------------------------------------------------+

Ao método é transferido o índice da condição contido na matriz de dados de condições de ativação. Com base no índice, obtemos a fonte de ativação, e, dependendo dela, obtemos as descrições de texto restantes das quais compomos o texto final e o retornamos.

Método retornando a descrição do tipo de comparação:

//+------------------------------------------------------------------+
//| Return the comparison type description                           |
//+------------------------------------------------------------------+
string CPendRequest::GetActivationComparerTypeDescription(const uint index) const
  {
   return ComparisonTypeDescription((ENUM_COMPARER_TYPE)this.m_activated_control[index][2]);
  }
//+------------------------------------------------------------------+

Ele simplesmente retorna uma descrição do tipo de comparação registrado na matriz de dados de acordo com o índice da condição de ativação que o parâmetro transfere ao método.

Método que retorna uma descrição do valor da propriedade controlada no objeto-ordem:

//+------------------------------------------------------------------+
//| Return a controlled property value description in an object      |
//+------------------------------------------------------------------+
string CPendRequest::GetActivationControlValueDescription(const uint index) const
  {
   return
     (
      this.m_activated_control[index][3]!=EMPTY_VALUE ? 
      (
       this.m_activated_control[index][0]==PEND_REQ_ACTIVATION_SOURCE_SYMBOL &&
       this.m_activated_control[index][1]==PEND_REQ_ACTIVATE_BY_SYMBOL_TIME   ? 
       ::TimeToString((ulong)this.m_activated_control[index][3])              :
       ::DoubleToString(this.m_activated_control[index][3],this.DigitsControlledValue(index))
      )  :  ""
     );
  }
//+------------------------------------------------------------------+

O índice de condição é transferido ao método.
É verificado o valor da propriedade controlada gravado na matriz com base no índice, e se for igual a "valor vazio" ( EMPTY_VALUE), será verificada a condição e seu tipo. Se, afinal, for verificada a hora do símbolo, será retornada uma descrição da hora,
caso contrário, será retornada uma descrição do valor inteiro ou real com o número correto de casas decimais.

Método que retorna uma descrição do valor real da propriedade controlada no objeto-ordem:

//+------------------------------------------------------------------+
//|Return an actual controlled property value description            |
//+------------------------------------------------------------------+
string CPendRequest::GetActivationActualValueDescription(const uint index) const
  {
   return
     (
      this.m_activated_control[index][4]!=EMPTY_VALUE ? 
      (
       this.m_activated_control[index][0]==PEND_REQ_ACTIVATION_SOURCE_SYMBOL &&
       this.m_activated_control[index][1]==PEND_REQ_ACTIVATE_BY_SYMBOL_TIME   ? 
       ::TimeToString((ulong)this.m_activated_control[index][4])              :
       ::DoubleToString(this.m_activated_control[index][4],this.DigitsControlledValue(index))
      )  :  ""
     );
  }
//+------------------------------------------------------------------+

O método é idêntico ao anterior, exceto que os dados são obtidos no índice 4 na segunda dimensão da matriz de condições de ativação. Estas são todas as alterações no objeto base de uma ordem de negociação pendente abstrata.

Agora, faremos algumas modificações nas classes de objetos-herdeiros do objeto base de ordem abstrata.

Como agora temos implementado dois tipos de ordens pendentes — por código de erro e por solicitação —, o segundo tipo de objetos não implica a presença de algumas propriedades, como código de retorno do servidor (aqui sempre é zero), hora de ativação da ordem (nas ordens do segundo tipo esta hora pode ser definida como uma das condições de ativação da ordem, e, neste caso, estará localizada na matriz de condições de ativação da ordem de negociação pendente), tempo de espera (aqui não é utilizado) e número de tentativa atual (aqui é feita a tentativa, em seguida, funciona como envio padrão de ordem pendente e processamento com base no código de retorno do servidor).

Por isso, complementamos todos os objetos-herdeiros do objeto-ordem pendente base nos seus métodos que retornam que o objeto suporta propriedades inteiras, e inserimos uma chamada de método que registra uma lista de condições de ativação de ordem pendente no método PrintShort() de cada um dos objetos-herdeiros.

Nos arquivos de objetos-herdeiros do objeto base da ordem pendente abstrata PendReqOpen.mqh, PendReqClose.mqh, PendReqSLTP.mqh, PendReqPlace.mqh, PendReqRemove.mqh e PendReqModify.mqh fazemos essas alterações (usando a classe CPendReqOpen como exemplo):
//+------------------------------------------------------------------+
//| Return 'true' if an order supports a passed                      |
//| integer property, otherwise return 'false'                       |
//+------------------------------------------------------------------+
bool CPendReqOpen::SupportProperty(ENUM_PEND_REQ_PROP_INTEGER property)
  {
   if(
      (this.GetProperty(PEND_REQ_PROP_TYPE)==PEND_REQ_TYPE_REQUEST   &&
       (property==PEND_REQ_PROP_RETCODE                              ||
        property==PEND_REQ_PROP_TIME_ACTIVATE                        ||
        property==PEND_REQ_PROP_WAITING                              ||
        property==PEND_REQ_PROP_CURRENT_ATTEMPT                        
       )                                                               
      ||
      property==PEND_REQ_PROP_MQL_REQ_ORDER                          ||
      property==PEND_REQ_PROP_MQL_REQ_POSITION                       ||
      property==PEND_REQ_PROP_MQL_REQ_POSITION_BY                    ||
      property==PEND_REQ_PROP_MQL_REQ_EXPIRATION                     ||
      property==PEND_REQ_PROP_MQL_REQ_TYPE_TIME
     ) return false;
   return true;
  }
//+------------------------------------------------------------------+

Aqui é verificado que este é um objeto criado por solicitação e, nesse caso, são excluídas as propriedades declaradas acima.

//+------------------------------------------------------------------+
//| Display a brief message with request data in the journal         |
//+------------------------------------------------------------------+
void CPendReqOpen::PrintShort(void)
  {
   string params=this.GetProperty(PEND_REQ_PROP_MQL_REQ_SYMBOL)+" "+::DoubleToString(this.GetProperty(PEND_REQ_PROP_MQL_REQ_VOLUME),this.m_digits_lot)+" "+
                 OrderTypeDescription((ENUM_ORDER_TYPE)this.GetProperty(PEND_REQ_PROP_MQL_REQ_TYPE));
   string price=CMessage::Text(MSG_LIB_TEXT_REQUEST_PRICE)+" "+::DoubleToString(this.GetProperty(PEND_REQ_PROP_MQL_REQ_PRICE),this.m_digits);
   string sl=this.GetProperty(PEND_REQ_PROP_MQL_REQ_SL)>0 ? ", "+CMessage::Text(MSG_LIB_TEXT_REQUEST_SL)+" "+::DoubleToString(this.GetProperty(PEND_REQ_PROP_MQL_REQ_SL),this.m_digits) : "";
   string tp=this.GetProperty(PEND_REQ_PROP_MQL_REQ_TP)>0 ? ", "+CMessage::Text(MSG_LIB_TEXT_REQUEST_TP)+" "+::DoubleToString(this.GetProperty(PEND_REQ_PROP_MQL_REQ_TP),this.m_digits) : "";
   string time=this.IDDescription()+", "+CMessage::Text(MSG_LIB_TEXT_CREATED)+" "+TimeMSCtoString(this.GetProperty(PEND_REQ_PROP_TIME_CREATE));
   string attempts=CMessage::Text(MSG_LIB_TEXT_ATTEMPTS)+" "+(string)this.GetProperty(PEND_REQ_PROP_TOTAL);
   string wait=CMessage::Text(MSG_LIB_TEXT_WAIT)+" "+::TimeToString(this.GetProperty(PEND_REQ_PROP_WAITING)/1000,TIME_SECONDS);
   string end=CMessage::Text(MSG_LIB_TEXT_END)+" "+
              TimeMSCtoString(this.GetProperty(PEND_REQ_PROP_TIME_CREATE)+this.GetProperty(PEND_REQ_PROP_WAITING)*this.GetProperty(PEND_REQ_PROP_TOTAL));
   //---
   string message=CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_STATUS_OPEN)+": "+
   "\n- "+params+", "+price+sl+tp+
   "\n- "+time+", "+attempts+", "+wait+", "+end;
   ::Print(message);
   this.PrintActivations();
  }
//+------------------------------------------------------------------+

Aqui, após a linha "+end" removemos a adição do código de transferência para uma nova linha (+"\n"), e após a linha ::Print(message); adicionamos uma chamada a um método que exibe uma lista de condições de ativação. Se a matriz contendo condições tiver um tamanho zero (em objetos criados por código de erro), o método PrintActivations() não imprimirá nada, exceto o código de transferência para uma nova linha ("\n"). Caso contrário, o método exibe uma lista contendo todas as condições registradas na matriz de dados.
Em algumas destas classes foram feitas pequenas correções "cosméticas" referentes apenas à exibição no log, portanto, não vamos nos deter nelas, pois tudo está nos arquivos anexados ao artigo e você pode se familiarizar com as alterações por conta própria.

Agora tratemos das classes de negociação.
Na classe de negociação básica CTrading, da seção privada à seção protegida transferimos três variáveis-membro de classe e o método GetFreeID():

private:
   CArrayInt            m_list_errors;                   // Error list
   bool                 m_is_trade_disable;              // Flag disabling trading
   bool                 m_use_sound;                     // The flag of using sounds of the object trading events
   uchar                m_total_try;                     // Number of trading attempts
   MqlTradeRequest      m_request;                       // Trading request prices
   ENUM_TRADE_REQUEST_ERR_FLAGS m_error_reason_flags;    // Flags of error source in a trading method
   ENUM_ERROR_HANDLING_BEHAVIOR m_err_handling_behavior; // Behavior when handling error
   
//--- Add the error code to the list

Estes métodos e variáveis são necessários na classe-herdeira, por isso, para que ela possa ter acesso a eles, eles devem estar na seção protegida (na seção pública não são necessários, pois o acesso a eles de fora é proibido). E à seção privada da classe adicionamos um método que retorna um sinalizador para indicar a presença de ordem a mercado ou de posição com o identificador da ordem pendente.
Como resultado, a seção protegida da classe fica assim:

//+------------------------------------------------------------------+
//| Trading class                                                    |
//+------------------------------------------------------------------+
class CTrading : public CBaseObj
  {
protected:
   CAccount            *m_account;                       // Pointer to the current account object
   CSymbolsCollection  *m_symbols;                       // Pointer to the symbol collection list
   CMarketCollection   *m_market;                        // Pointer to the list of the collection of market orders and positions
   CHistoryCollection  *m_history;                       // Pointer to the list of the collection of historical orders and deals
   CEventsCollection   *m_events;                        // Pointer to the event collection list
   CArrayObj            m_list_request;                  // List of pending requests
   uchar                m_total_try;                     // Number of trading attempts
   MqlTradeRequest      m_request;                       // Trade request structure
   ENUM_TRADE_REQUEST_ERR_FLAGS m_error_reason_flags;    // Flags of error source in a trading method
//--- Look for the first free pending request ID
   int                  GetFreeID(void);
//--- Return the flag of a market order/position with a pending request ID
   bool                 IsPresentOrderByID(const uchar id);
private:

Neste caso, variáveis e métodos, transferidos da seção privada, bem como a definição do novo método são indicados a cores.

Na seção pública da classe inserimos a declaração do método, que retorna o ponteiro para o objeto-ordem de acordo com seu identificador na lista, bem como a adição do método que retorna o nível de registro do objeto do símbolo:

//--- Create a pending request
   bool                 CreatePendingRequest(const ENUM_PEND_REQ_STATUS status,
                                             const uchar id,
                                             const uchar attempts,
                                             const ulong wait,
                                             const MqlTradeRequest &request,
                                             const int retcode,
                                             CSymbol *symbol_obj,
                                             COrder *order);

//--- Return (1) the pointer to the request object by its ID in the list,
//--- (2) the logging level of a symbol trading object
   CPendRequest        *GetPendRequestByID(const uchar id);
   ENUM_LOG_LEVEL       GetTradeObjLogLevel(const string symbol_name);

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

Fora do corpo da classe, escrevemos a implementação destes métodos.

Implementação do método que retorna o nível de registro do objeto de negociação do símbolo:

//+------------------------------------------------------------------+
//| Return the logging level of a symbol trading object              |
//+------------------------------------------------------------------+
ENUM_LOG_LEVEL CTrading::GetTradeObjLogLevel(const string symbol_name)
  {
   CTradeObj *trade_obj=this.GetTradeObjBySymbol(symbol_name,DFUN);
   return(trade_obj!=NULL ? trade_obj.GetLogLevel() : LOG_LEVEL_NO_MSG);
  }
//+------------------------------------------------------------------+

Ao método é transferido o nome do símbolo, cujo nível de registro do objeto de negociação é necessário obter, obtemos o objeto de negociação a partir do objeto-símbolo, e se o objeto for recebido, retornamos o nível de registro do objeto em questão, caso contrário, retornamos a restrição de registro.

Implementação do método que retorna o ponteiro para o objeto-ordem de acordo com um identificador na lista:

//+------------------------------------------------------------------+
//| Return the pointer to the request object by its ID in the list   |
//+------------------------------------------------------------------+
CPendRequest* CTrading::GetPendRequestByID(const uchar id)
  {
   int index=this.GetIndexPendingRequestByID(id);
   if(index==WRONG_VALUE)
      return NULL;
   return this.m_list_request.At(index);
  }
//+------------------------------------------------------------------+

Ao método é transferido o identificador da ordem, em seguida, obtemos o índice do objeto-ordem pendente de acordo com seu identificador, e se o objeto não estiver na lista, retornamos NULL, caso contrário, o objeto de acordo com seu índice de lista.

Implementação do método que retorna a sinalizador de presença de uma ordem a mercado/posição com o identificador da ordem pendente:

//+------------------------------------------------------------------+
//| Return the flag of a market order/position                       |
//| with a pending request ID                                        |
//+------------------------------------------------------------------+
bool CTrading::IsPresentOrderByID(const uchar id)
  {
   CArrayObj *list=this.m_market.GetList(ORDER_PROP_PEND_REQ_ID,id,EQUAL);
   return(list==NULL ? false : list.Total()!=0);
  }
//+------------------------------------------------------------------+

Ao método é transferido o identificador da ordem pendente. Em seguida, obtemos a lista de ordens a mercado/posições, filtrada por identificador de ordem pendente e seu valor. Se a lista não for recebida ou estiver vazia (não tiver ordens/posições como identificador procurado), retornamos false, caso contrário, true.

Adicionamos mais uma verificação, o método que retorna o número de identificador livre:

//+------------------------------------------------------------------+
//| Look for the first free pending request ID                       |
//+------------------------------------------------------------------+
int CTrading::GetFreeID(void)
  {
   int id=WRONG_VALUE;
   CPendRequest *element=new CPendRequest();
   if(element==NULL)
      return 0;
   for(int i=1;i<256;i++)
     {
      element.SetID((uchar)i);
      this.m_list_request.Sort(SORT_BY_PEND_REQ_ID);
      if(this.m_list_request.Search(element)==WRONG_VALUE)
        {
         if(this.IsPresentOrderByID((uchar)i))
            continue;
         id=i;
         break;
        }
     }
   delete element;
   return id;
  }
//+------------------------------------------------------------------+

Para que é necessário verificar que no mercado existe uma ordem/posição com o mesmo identificador? Acontece que se, por exemplo, temos uma posição aberta de acordo com determinada ordem pendente ativada, esta ordem será removida da lista de ordens pendentes, e seu identificador ficará disponível quando criadas novas ordens pendentes.
Ao criar uma nova ordem pendente, será criada uma ordem pendente com o mesmo identificador pelo qual existe atualmente uma posição aberta. Se as condições de ativação desta nova ordem pendente forem atendidas, será verificada a presença de uma posição com o mesmo identificador, e será excluída a nova ordem pendente, pois será considerado que já existe uma posição com o mesmo identificador, o que indica que a ordem já foi usada. Ou seja, a ordem será simplesmente excluída e não enviará uma ordem de negociação para o servidor.

Para evitar essas situações existem duas saídas: identificar adicionalmente que se trata de uma ordem diferente com o mesmo identificador da posição aberta ou simplesmente verificar que essa posição existe com esse ID caso na lista não haja uma ordem pendente com o mesmo ID.
A segunda opção me pareceu menos dispendiosa, apesar de ser impossível usar um identificador com um ID livre até que seja fechada uma posição com o mesmo identificador, ou seja, a pesar de uma restrição estrita em 255 posições com diferentes identificadores de ordens pendentes.

Assim concluímos a modificação da classe de negociação base.

Agora modificamos a classe de gerenciamento de negociação CTradingControl — classe-herdeira da classe de negociação base CTrading.

Ao criar uma classe de gerenciamento de ordem pendente no último artigo, fizemos o processamento de objetos de ordens pendentes no temporizador da classe.
Como, nele, processamos apenas um tipo de ordem pendente (criada com base no código de retorno do servidor), bastava colocar todo o código de processamento no temporizador da classe.

Hoje, adicionaremos o processamento do segundo tipo de ordens pendentes, nomeadamente as criadas por solicitação do programa.
Por isso, precisaremos fazer dois manipuladores, o primeiro para ordens criadas pelo código de erro e o segundo para ordens geradas por solicitação.
Portanto, faremos o seguinte: criaremos dois manipuladores de objetos de ordem pendente separados pelo tipo de ordens processadas, e geraremos o mesmo código para os dois manipuladores num método separado. Assim, para processar os dois tipos de ordens pendentes, será suficiente verificarmos o tipo de ordem no temporizador e chamar o manipulador correspondente.

Faremos todas as adições necessárias ao corpo da classe e depois as analisaremos:

//+------------------------------------------------------------------+
//| Class for managing pending trading requests                      |
//+------------------------------------------------------------------+
class CTradingControl : public CTrading
  {
private:
//--- Set actual order/position data to a pending request object
   void                 SetOrderActualProperties(CPendRequest *req_obj,const COrder *order);
//--- Handler of pending requests created (1) by error code, (2) by request
   void                 OnPReqByErrCodeHandler(CPendRequest *req_obj,const int index);
   void                 OnPReqByRequestHandler(CPendRequest *req_obj,const int index);
//--- Check a pending request relevance (activated or not)
   bool                 CheckPReqRelevance(CPendRequest *req_obj,const MqlTradeRequest &request,const int index);
//--- Update relevant values of controlled properties in pending request objects,
   void                 RefreshControlActualDatas(CPendRequest *req_obj,const CSymbol *symbol);
//--- Return the relevant (1) account, (2) symbol, (3) event data to control activation
   double               GetActualDataAccount(const int property);
   double               GetActualDataSymbol(const int property,const CSymbol *symbol);
   double               GetActualDataEvent(const int property);
   
public:
//--- Return itself
   CTradingControl     *GetObject(void)            { return &this;   }
//--- Timer
   virtual void         OnTimer(void);
//--- Constructor
                        CTradingControl();
//--- (1) Create a pending request (1) to open a position, (2) to place a pending order
   template<typename SL,typename TP> 
   int                  OpenPositionPending(const ENUM_POSITION_TYPE type,
                                           const double volume,
                                           const string symbol,
                                           const ulong magic=ULONG_MAX,
                                           const SL sl=0,
                                           const TP tp=0,
                                           const uchar group_id1=0,
                                           const uchar group_id2=0,
                                           const string comment=NULL,
                                           const ulong deviation=ULONG_MAX,
                                           const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE);
   template<typename PS,typename PL,typename SL,typename TP>
   int                  PlaceOrderPending( const ENUM_ORDER_TYPE order_type,
                                           const double volume,
                                           const string symbol,
                                           const PS price_stop,
                                           const PL price_limit=0,
                                           const SL sl=0,
                                           const TP tp=0,
                                           const ulong magic=ULONG_MAX,
                                           const string comment=NULL,
                                           const datetime expiration=0,
                                           const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE,
                                           const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE);
//--- Set pending request activation criteria
   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);
  };
//+------------------------------------------------------------------+

Como agora podemos definir no objeto-ordem pendente os dados dos parâmetros controlados e antigamente ao monitorar a ativação da ordem pendente também podíamos inserir no objeto-ordem dados atuais da ordem que correspondia à solicitação em questão, para evitar confusão, renomeamos o método que serve para definir os dados atuais da ordem no objeto-ordem, isto é, alteramos de SetActualProperties() para SetOrderActualProperties().

Hoje, trabalharemos apenas com a abertura de posições usando ordens pendentes, portanto, deixaremos fora do escopo deste artigo o método de criação de ordem pendente para fazer uma ordem pendente, e
consideraremos o método de criação de solicitação pendente para abrir uma posição:

//+------------------------------------------------------------------+
//| Create a pending request for opening a position                  |
//+------------------------------------------------------------------+
template<typename SL,typename TP> 
int CTradingControl::OpenPositionPending(const ENUM_POSITION_TYPE type,
                                         const double volume,
                                         const string symbol,
                                         const ulong magic=ULONG_MAX,
                                         const SL sl=0,
                                         const TP tp=0,
                                         const uchar group_id1=0,
                                         const uchar group_id2=0,
                                         const string comment=NULL,
                                         const ulong deviation=ULONG_MAX,
                                         const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE)
  {
//--- Exit if the global trading ban flag is set
   if(this.IsTradingDisable())
     {
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_TRADING_DISABLE));
      return false;
     }
//--- Set the trading request result as 'true' and the error flag as "no errors"
   bool res=true;
   this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_NO_ERROR;
   ENUM_ORDER_TYPE order_type=(ENUM_ORDER_TYPE)type;
   ENUM_ACTION_TYPE action=(ENUM_ACTION_TYPE)order_type;
//--- Get a symbol object by a symbol name.
   CSymbol *symbol_obj=this.m_symbols.GetSymbolObjByName(symbol);
//--- If failed to get - write the "internal error" flag, display the message in the journal and return 'false'
   if(symbol_obj==NULL)
     {
      this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_INTERNAL_ERR;
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_SYM_OBJ));
      return false;
     }
//--- get a trading object from a symbol object
   CTradeObj *trade_obj=symbol_obj.GetTradeObj();
//--- If failed to get - write the "internal error" flag, display the message in the journal and return 'false'
   if(trade_obj==NULL)
     {
      this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_INTERNAL_ERR;
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_TRADE_OBJ));
      return false;
     }
//--- Set the prices
//--- If failed to set - write the "internal error" flag, set the error code in the return structure,
//--- display the message in the journal and return 'false'
   if(!this.SetPrices(order_type,0,sl,tp,0,DFUN,symbol_obj))
     {
      this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_INTERNAL_ERR;
      trade_obj.SetResultRetcode(10021);
      trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode()));
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(10021));   // No quotes to process the request
      return false;
     }
   //--- Look for the least of the possible IDs. If failed to find, return 'false'
   int id=this.GetFreeID();
   if(id<1)
     {
      //--- No free IDs to create a pending request
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_NO_FREE_IDS));
      return false;
     }

//--- Write the volume, deviation, comment and filling type to the request structure
   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());
//--- Write pending request object ID to the magic number, add group IDs to the magic number value
//--- and fill in the remaining unfilled trading request structure fields
   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;
//--- As a result of creating a pending trading request, return either its ID or -1 if unsuccessful
   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;
  }
//+------------------------------------------------------------------+

O método é uma versão reduzida do método para abrir uma posição do artigo 26 (e artigos posteriores) por meio da criação de uma solicitação pendente em caso de erro do servidor de negociação. Já foi comentado em detalhes, por isso não iremos nos aprofundar em sua análise.
Todos os dados necessários para abrir uma posição são transferidos para o método, os campos da estrutura da solicitação de negociação são preenchidos e enviados ao método de criação de solicitação pendente.
Como resultado, após a criação bem-sucedida de uma solicitação pendente, é retornado o identificador da solicitação pendente recém-criada, caso contrário, é retornado -1.
Como atraso entre novas tentativas, aqui é usado o tempo de espera máximo possível calculado como a diferença entre o tempo de espera máximo possível no terminal e o tempo atual. Assim, a duração de uma solicitação pendente será o máximo possível (até 31/12/3000).

Método que define os critérios para ativar uma solicitação pendente:

//+------------------------------------------------------------------+
//| Set pending request activation criteria                          |
//+------------------------------------------------------------------+
bool CTradingControl::SetNewActivationProperties(const uchar id,
                                                const ENUM_PEND_REQ_ACTIVATION_SOURCE source,
                                                const int property,
                                                const double control_value,
                                                const ENUM_COMPARER_TYPE comparer_type,
                                                const double actual_value)
  {
   CPendRequest *req_obj=this.GetPendRequestByID(id);
   if(req_obj==NULL)
     {
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_GETTING_FAILED));
      return false;
     }
   req_obj.SetNewActivationProperties(source,property,control_value,comparer_type,actual_value);
   return true;
  }
//+------------------------------------------------------------------+

Ao método são transferidos o identificador da ordem pendente, que precisa de uma nova condição de ativação, a fonte de ativação da solicitação (símbolo, conta ou evento), a condição de ativação, o valor controlado, o tipo de comparação e o valor real da propriedade monitorada para ativação da ordem.
Em seguida, obtemos o objeto-ordem pendente pelo identificador transferido ao método e criamos para ele uma nova condição de ativação com os parâmetros transferidos ao método.

Método para verificar a relevância de uma solicitação pendente:

//+------------------------------------------------------------------+
//| Checking the pending request relevance                           |
//+------------------------------------------------------------------+
bool CTradingControl::CheckPReqRelevance(CPendRequest *req_obj,const MqlTradeRequest &request,const int index)
  {
   //--- If this is a position opening or placing a pending order
   if((req_obj.Action()==TRADE_ACTION_DEAL && req_obj.Position()==0) || req_obj.Action()==TRADE_ACTION_PENDING)
     {
      //--- Get the pending request ID
      uchar id=this.GetPendReqID((uint)request.magic);
      //--- Get the list of orders/positions containing the order/position with the pending request ID
      CArrayObj *list=this.m_market.GetList(ORDER_PROP_PEND_REQ_ID,id,EQUAL);
      if(::CheckPointer(list)==POINTER_INVALID)
         return false;
      //--- If the order/position is present, the request is handled: remove it and proceed to the next (leave the method for the external loop)
      if(list.Total()>0)
        {
         if(this.m_log_level>LOG_LEVEL_NO_MSG)
            ::Print(req_obj.Header(),": ",CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_EXECUTED));
         this.m_list_request.Delete(index);
         return false;
        }
     }
   //--- Otherwise: full and partial position closure, removing an order, modifying order parameters and position stop orders
   else
     {
      CArrayObj *list=NULL;
      //--- if this is a position closure, including a closure by an opposite one
      if((req_obj.Action()==TRADE_ACTION_DEAL && req_obj.Position()>0) || req_obj.Action()==TRADE_ACTION_CLOSE_BY)
        {
         //--- Get a position with the necessary ticket from the list of open positions
         list=this.m_market.GetList(ORDER_PROP_TICKET,req_obj.Position(),EQUAL);
         if(::CheckPointer(list)==POINTER_INVALID)
            return false;
         //--- If the market has no such position, the request is handled: remove it and proceed to the next (leave the method for the external loop)
         if(list.Total()==0)
           {
            if(this.m_log_level>LOG_LEVEL_NO_MSG)
               ::Print(req_obj.Header(),": ",CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_EXECUTED));
            this.m_list_request.Delete(index);
            return false;
           }
         //--- Otherwise, if the position still exists, this is a partial closure
         else
           {
            //--- Get the list of all account trading events
            list=this.m_events.GetList();
            if(list==NULL)

               return false;
            //--- In the loop from the end of the account trading event list
            int events_total=list.Total();
            for(int j=events_total-1; j>WRONG_VALUE; j--)
              {
               //--- get the next trading event
               CEvent *event=list.At(j);
               if(event==NULL)
                  continue;
               //--- If this event is a partial closure or there was a partial closure when closing by an opposite one
               if(event.TypeEvent()==TRADE_EVENT_POSITION_CLOSED_PARTIAL || event.TypeEvent()==TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_POS)
                 {
                  //--- If a position ticket in a trading event coincides with the ticket in a pending trading request
                  if(event.PositionID()==req_obj.Position())
                    {
                     //--- Get a position object from the list of market positions
                     CArrayObj *list_orders=this.m_market.GetList(ORDER_PROP_POSITION_ID,req_obj.Position(),EQUAL);
                     if(list_orders==NULL || list_orders.Total()==0)
                        break;
                     COrder *order=list_orders.At(list_orders.Total()-1);
                     if(order==NULL)
                        break;
                     //--- Set actual position data to the pending request object
                     this.SetOrderActualProperties(req_obj,order);
                     //--- If (executed request volume + unexecuted request volume) is equal to the requested volume in a pending request -
                     //--- the request is handled: remove it and break the loop by the list of account trading events
                     if(req_obj.GetProperty(PEND_REQ_PROP_MQL_REQ_VOLUME)==event.VolumeOrderExecuted()+event.VolumeOrderCurrent())
                       {
                        if(this.m_log_level>LOG_LEVEL_NO_MSG)
                           ::Print(req_obj.Header(),": ",CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_EXECUTED));
                        this.m_list_request.Delete(index);
                        break;
                       }
                    }
                 }
              }
            //--- If a handled pending request object was removed by the trading event list in the loop, move on to the next one (leave the method for the external loop)
            if(::CheckPointer(req_obj)==POINTER_INVALID)
               return false;
           }
        }
      //--- If this is a modification of position stop orders
      if(req_obj.Action()==TRADE_ACTION_SLTP)
        {
         //--- Get the list of all account trading events
         list=this.m_events.GetList();
         if(list==NULL)
            return false;
         //--- In the loop from the end of the account trading event list
         int events_total=list.Total();
         for(int j=events_total-1; j>WRONG_VALUE; j--)
           {
            //--- get the next trading event
            CEvent *event=list.At(j);
            if(event==NULL)
               continue;
            //--- If this is a change of the position's stop orders
            if(event.TypeEvent()>TRADE_EVENT_MODIFY_ORDER_TP)
              {
               //--- If a position ticket in a trading event coincides with the ticket in a pending trading request
               if(event.PositionID()==req_obj.Position())
                 {
                  //--- Get a position object from the list of market positions
                  CArrayObj *list_orders=this.m_market.GetList(ORDER_PROP_POSITION_ID,req_obj.Position(),EQUAL);
                  if(list_orders==NULL || list_orders.Total()==0)
                     break;
                  COrder *order=list_orders.At(list_orders.Total()-1);
                  if(order==NULL)
                     break;
                  //--- Set actual position data to the pending request object
                  this.SetOrderActualProperties(req_obj,order);
                  //--- If all modifications have worked out -
                  //--- the request is handled: remove it and break the loop by the list of account trading events
                  if(req_obj.IsCompleted())
                    {
                     if(this.m_log_level>LOG_LEVEL_NO_MSG)
                        ::Print(req_obj.Header(),": ",CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_EXECUTED));
                     this.m_list_request.Delete(index);
                     break;
                    }
                 }
              }
           }
         //--- If a handled pending request object was removed by the trading event list in the loop, move on to the next one (leave the method for the external loop)
         if(::CheckPointer(req_obj)==POINTER_INVALID)
            return false;
        }
      //--- If this is a pending order removal
      if(req_obj.Action()==TRADE_ACTION_REMOVE)
        {
         //--- Get the list of removed pending orders from the historical list
         list=this.m_history.GetList(ORDER_PROP_STATUS,ORDER_STATUS_HISTORY_PENDING,EQUAL);
         if(::CheckPointer(list)==POINTER_INVALID)
            return false;
         //--- Leave a single order with the necessary ticket in the list
         list=CSelect::ByOrderProperty(list,ORDER_PROP_TICKET,req_obj.Order(),EQUAL);
         //--- If the order is present, the request is handled: remove it and proceed to the next (leave the method for the external loop)
         if(list.Total()>0)
           {
            if(this.m_log_level>LOG_LEVEL_NO_MSG)
               ::Print(req_obj.Header(),": ",CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_EXECUTED));
            this.m_list_request.Delete(index);
            return false;
           }
        }
      //--- If this is a pending order modification
      if(req_obj.Action()==TRADE_ACTION_MODIFY)
        {
         //--- Get the list of all account trading events
         list=this.m_events.GetList();
         if(list==NULL)
            return false;
         //--- In the loop from the end of the account trading event list
         int events_total=list.Total();
         for(int j=events_total-1; j>WRONG_VALUE; j--)
           {
            //--- get the next trading event
            CEvent *event=list.At(j);
            if(event==NULL)
               continue;
            //--- If this event involves any change of modified pending order parameters
            if(event.TypeEvent()>TRADE_EVENT_TRIGGERED_STOP_LIMIT_ORDER && event.TypeEvent()<TRADE_EVENT_MODIFY_POSITION_SL_TP)
              {
               //--- If an order ticket in a trading event coincides with the ticket in a pending trading request
               if(event.TicketOrderEvent()==req_obj.Order())
                 {
                  //--- Get an order object from the list
                  CArrayObj *list_orders=this.m_market.GetList(ORDER_PROP_TICKET,req_obj.Order(),EQUAL);
                  if(list_orders==NULL || list_orders.Total()==0)
                     break;
                  COrder *order=list_orders.At(0);
                  if(order==NULL)
                     break;
                  //--- Set actual order data to the pending request object
                  this.SetOrderActualProperties(req_obj,order);
                  //--- If all modifications have worked out -
                  //--- the request is handled: remove it and break the loop by the list of account trading events
                  if(req_obj.IsCompleted())
                    {
                     if(this.m_log_level>LOG_LEVEL_NO_MSG)
                        ::Print(req_obj.Header(),": ",CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_EXECUTED));
                     this.m_list_request.Delete(index);
                     break;
                    }
                 }
              }
           }
        }
     }
   //--- Exit if the pending request object has been removed after checking its operation (leave the method for the external loop)
   return(::CheckPointer(req_obj)==POINTER_INVALID ? false : true);
  }
//+------------------------------------------------------------------+

Este método verifica que ordem pendente é executada e a exclui quando isso é confirmado. Já revisamos este código como parte do código do temporizador da classe de controle de negociação. Agora, como dividimos o processamento de objetos-ordens pendentes em dois manipuladores com base no tipo de ordem pendente, e este código é o mesmo para eles dois, nos vamos colocá-lo num método separado e vamos chamá-lo em cada manipulador.

Manipulador de ordens pendentes criado pelo código de erro:

//+------------------------------------------------------------------+
//| Handler of pending requests created by error code                |
//+------------------------------------------------------------------+
void CTradingControl::OnPReqByErrCodeHandler(CPendRequest *req_obj,const int index)
  {
   //--- get the request structure and the symbol object a trading operation should be performed for
   MqlTradeRequest request=req_obj.MqlRequest();
   CSymbol *symbol_obj=this.m_symbols.GetSymbolObjByName(request.symbol);
   if(symbol_obj==NULL || !symbol_obj.RefreshRates())
      return;
   
   //--- Set the flag disabling trading in the terminal by two properties simultaneously
   //--- (the AutoTrading button in the terminal and the Allow Automated Trading option in the EA settings)
   //--- If any of the two properties is 'false', the flag is 'false' as well
   bool terminal_trade_allowed=::TerminalInfoInteger(TERMINAL_TRADE_ALLOWED);
   terminal_trade_allowed &=::MQLInfoInteger(MQL_TRADE_ALLOWED);
   //--- if the error has been caused by trading disabled on the terminal side and has been eliminated
   if(req_obj.Retcode()==10027 && terminal_trade_allowed)
     {
      //--- if the current attempt has not exceeded the defined number of trading attempts yet
      if(req_obj.CurrentAttempt()<req_obj.TotalAttempts()+1)
        {
         //--- Set the request creation time equal to its creation time minus waiting time, i.e. send the request immediately
         //--- Also, decrease the number of a successful attempt since during the next attempt, its number is increased, and if this is the last attempt,
         //--- it is not executed. However, this is related to fixing the error cause by a user, which means we need to give more time for the last attempt
         req_obj.SetTimeCreate(req_obj.TimeCreate()-req_obj.WaitingMSC());
         req_obj.SetCurrentAttempt(uchar(req_obj.CurrentAttempt()>0 ? req_obj.CurrentAttempt()-1 : 0));
        }
     }

   //--- if the current attempt exceeds the defined number of trading attempts,
   //--- or the current time exceeds the waiting time of all attempts
   //--- remove the current request object and proceed to the next (leave the method for the external loop)
   if(req_obj.CurrentAttempt()>req_obj.TotalAttempts() || req_obj.CurrentAttempt()>=UCHAR_MAX || 
      (long)symbol_obj.Time()>long(req_obj.TimeCreate()+req_obj.WaitingMSC()*(req_obj.TotalAttempts()+1)))
     {
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(req_obj.Header(),": ",CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_DELETED));
      this.m_list_request.Delete(index);
      return;
     }
   //--- Check the relevance of a pending request and exit to the external loop if the request is handled or an error occurs
   if(!this.CheckPReqRelevance(req_obj,request,index))
      return;
   
   //--- Set the request activation time in the request object
   req_obj.SetTimeActivate(req_obj.TimeCreate()+req_obj.WaitingMSC()*(req_obj.CurrentAttempt()+1));
   
   //--- If the current time is less than the request activation time,
   //--- this is not the request time - move on to the next request in the list (leave the method for the external loop)
   if((long)symbol_obj.Time()<(long)req_obj.TimeActivate())
      return;
   
   //--- Set the attempt number in the request object
   req_obj.SetCurrentAttempt(uchar(req_obj.CurrentAttempt()+1));
   
   //--- Display the number of a trading attempt in the journal

   if(this.m_log_level>LOG_LEVEL_NO_MSG)
     {
      ::Print(CMessage::Text(MSG_LIB_TEXT_RE_TRY_N)+(string)req_obj.CurrentAttempt()+":");
      req_obj.PrintShort();
     }
   
   //--- Depending on the type of action performed in the trading request 
   switch(request.action)
     {
      //--- Opening/closing a position
      case TRADE_ACTION_DEAL :
         //--- If no ticket is present in the request structure - this is opening a position
         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);
         //--- If the ticket is present in the request structure - this is a position closure
         else
            this.ClosePosition(request.position,request.volume,request.comment,request.deviation);
         break;
      //--- Modify StopLoss/TakeProfit position
      case TRADE_ACTION_SLTP :
         this.ModifyPosition(request.position,request.sl,request.tp);
         break;
      //--- Close by an opposite one
      case TRADE_ACTION_CLOSE_BY :
         this.ClosePositionBy(request.position,request.position_by);
         break;
      //---
      //--- Place a pending order
      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;
      //--- Modify a pending order
      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;
      //--- Remove a pending order
      case TRADE_ACTION_REMOVE :
         this.DeleteOrder(request.order);
         break;
      //---
      default:
         break;
     }  
  }
//+------------------------------------------------------------------+

Nós também já revisamos este código como parte de um temporizador de classe de gerenciamento de negociação, e difere apenas porque processamento de verificação de ordem agora é colocado na chamada para o método correspondente.

Manipulador para solicitações pendentes criadas por solicitação:

//+------------------------------------------------------------------+
//| The handler of pending requests created by request               |
//+------------------------------------------------------------------+
void CTradingControl::OnPReqByRequestHandler(CPendRequest *req_obj,const int index)
  {
   //--- get the request structure and the symbol object a trading operation should be performed for
   MqlTradeRequest request=req_obj.MqlRequest();
   CSymbol *symbol_obj=this.m_symbols.GetSymbolObjByName(request.symbol);
   if(symbol_obj==NULL || !symbol_obj.RefreshRates())
      return;
   //--- Check the relevance of a pending request and exit to the external loop if the request is handled or an error occurs
   if(!this.CheckPReqRelevance(req_obj,request,index))
      return;

   //--- Update relevant data on request activation conditions
   this.RefreshControlActualDatas(req_obj,symbol_obj);
   
   //--- If all pending request activation conditions are met
   if(req_obj.IsAllComparisonCompleted())
     {
      //--- Set the attempt number in the request object
      req_obj.SetCurrentAttempt(uchar(req_obj.CurrentAttempt()+1));
      //--- Display the request activation message in the journal
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
        {
         ::Print(CMessage::Text(MSG_LIB_TEXT_REQUEST_ACTIVATED)+(string)req_obj.ID()+":");
         req_obj.PrintShort();
        }
      //--- Depending on the type of action performed in the trading request 
      switch(request.action)
        {
         //--- Opening/closing a position
         case TRADE_ACTION_DEAL :
            //--- If no ticket is present in the request structure - this is opening a position
            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);
            //--- If the ticket is present in the request structure - this is a position closure
            else
               this.ClosePosition(request.position,request.volume,request.comment,request.deviation);
            break;
         //--- Modify StopLoss/TakeProfit position
         case TRADE_ACTION_SLTP :
            this.ModifyPosition(request.position,request.sl,request.tp);
            break;
         //--- Close by an opposite one
         case TRADE_ACTION_CLOSE_BY :
            this.ClosePositionBy(request.position,request.position_by);
            break;
         //---
         //--- Place a pending order
         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;
         //--- Modify a pending order
         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;
         //--- Remove a pending order
         case TRADE_ACTION_REMOVE :
            this.DeleteOrder(request.order);
            break;
         //---
         default:
            break;
        }  
     }
  }
//+------------------------------------------------------------------+

Esse método é um pouco mais simples que o anterior, pois requer apenas que sua ativação e envio de ordem de negociação sejam verificados (desencadeamento de condições de ativação de ordem pendente).
Nele, dessa mesma maneira, é chamado o método de verificação de ativação de solicitação, depois, é monitorado o desencadeamento das condições de ativação de ordem pendente, escritas nos seus parâmetros, e se confirmado que todas as condições são atendidas, é enviada a ordem de negociação ao servidor.

Método que atualiza os valores reais de propriedades controladas em objetos-solicitações pendentes:

//+------------------------------------------------------------------+
//| Update relevant values of controlled properties                  |
//| in pending request objects                                       |
//+------------------------------------------------------------------+
void CTradingControl::RefreshControlActualDatas(CPendRequest *req_obj,const CSymbol *symbol)
  {
//--- Exit if a request object has a request type based on an error code
   if(req_obj.GetProperty(PEND_REQ_PROP_TYPE)==PEND_REQ_TYPE_ERROR)
      return;
   double res=EMPTY_VALUE;
//--- In the loop by all request object activation conditions,
   uint total=req_obj.GetActivationCriterionTotal();
   for(uint i=0;i<total;i++)
     {
      //--- get the activation source
      ENUM_PEND_REQ_ACTIVATION_SOURCE source=req_obj.GetActivationSource(i);
      //--- receive the current value of a controlled property
      double value=req_obj.GetActivationActualValue(i),actual=EMPTY_VALUE;
      //--- Depending on the activation source,
      //--- write the current value of a controlled property to the activation conditions data array
      switch((int)source)
        {
         //--- Account
         case PEND_REQ_ACTIVATION_SOURCE_ACCOUNT   :  
            actual=this.GetActualDataAccount(req_obj.GetActivationProperty(i));
            req_obj.SetActivationActualValue(i,(actual!=EMPTY_VALUE ? actual : value));
           break;
         //--- Symbol
         case PEND_REQ_ACTIVATION_SOURCE_SYMBOL    :
            actual=this.GetActualDataSymbol(req_obj.GetActivationProperty(i),symbol);
            req_obj.SetActivationActualValue(i,(actual!=EMPTY_VALUE ? actual : value));
           break;
         //--- Event
         case PEND_REQ_ACTIVATION_SOURCE_EVENT     :
            actual=this.GetActualDataEvent(req_obj.GetActivationProperty(i));
            req_obj.SetActivationActualValue(i,(actual!=EMPTY_VALUE ? actual : value));
           break;
         //--- Default is EMPTY_VALUE
         default:
           break;
        }
     }
  }
//+------------------------------------------------------------------+

No método, obtemos o tamanho da matriz de dados das condições de ativação, no ciclo iteramos todas as condições, dependendo da fonte das condições de ativação, obtemos os dados reais (atuais) das coleções correspondentes e escrevemo-los de volta na matriz de dados das condições de ativação do objeto-ordem pendente.

Método que retorna os dados de conta atuais:

//+------------------------------------------------------------------+
//| Return the relevant account data to control activation           |
//+------------------------------------------------------------------+
double CTradingControl::GetActualDataAccount(const int property)
  {
   switch(property)
    {
     case PEND_REQ_ACTIVATE_BY_ACCOUNT_LEVERAGE             : return (double)this.m_account.Leverage();
     case PEND_REQ_ACTIVATE_BY_ACCOUNT_LIMIT_ORDERS         : return (double)this.m_account.LimitOrders();
     case PEND_REQ_ACTIVATE_BY_ACCOUNT_TRADE_ALLOWED        : return (double)this.m_account.TradeAllowed();
     case PEND_REQ_ACTIVATE_BY_ACCOUNT_TRADE_EXPERT         : return (double)this.m_account.TradeExpert();
     case PEND_REQ_ACTIVATE_BY_ACCOUNT_BALANCE              : return this.m_account.Balance();
     case PEND_REQ_ACTIVATE_BY_ACCOUNT_CREDIT               : return this.m_account.Credit();
     case PEND_REQ_ACTIVATE_BY_ACCOUNT_PROFIT               : return this.m_account.Profit();
     case PEND_REQ_ACTIVATE_BY_ACCOUNT_EQUITY               : return this.m_account.Equity();
     case PEND_REQ_ACTIVATE_BY_ACCOUNT_MARGIN               : return this.m_account.Margin();
     case PEND_REQ_ACTIVATE_BY_ACCOUNT_MARGIN_FREE          : return this.m_account.MarginFree();
     case PEND_REQ_ACTIVATE_BY_ACCOUNT_MARGIN_LEVEL         : return this.m_account.MarginLevel();
     case PEND_REQ_ACTIVATE_BY_ACCOUNT_MARGIN_INITIAL       : return this.m_account.MarginInitial();
     case PEND_REQ_ACTIVATE_BY_ACCOUNT_MARGIN_MAINTENANCE   : return this.m_account.MarginMaintenance();
     case PEND_REQ_ACTIVATE_BY_ACCOUNT_ASSETS               : return this.m_account.Assets();
     case PEND_REQ_ACTIVATE_BY_ACCOUNT_LIABILITIES          : return this.m_account.Liabilities();
     case PEND_REQ_ACTIVATE_BY_ACCOUNT_COMMISSION_BLOCKED   : return this.m_account.ComissionBlocked();
     default: return EMPTY_VALUE;
    }
  }
//+------------------------------------------------------------------+

Dependendo do tipo de condição, de acordo com a enumeração dos tipos de condições de conta, retornamos o valor da propriedade correspondente do objeto-conta.

Método que retorna os dados de símbolo atuais:

//+------------------------------------------------------------------+
//| Return the relevant symbol data to control activation            |
//+------------------------------------------------------------------+
double CTradingControl::GetActualDataSymbol(const int property,const CSymbol *symbol)
  {
   switch(property)
    {
     case PEND_REQ_ACTIVATE_BY_SYMBOL_BID                         : return symbol.Bid();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_ASK                         : return symbol.Ask();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_LAST                        : return symbol.Last();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_DEALS               : return (double)symbol.SessionDeals();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_BUY_ORDERS          : return (double)symbol.SessionBuyOrders();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_SELL_ORDERS         : return (double)symbol.SessionSellOrders();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_VOLUME                      : return (double)symbol.Volume();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_VOLUMEHIGH                  : return (double)symbol.VolumeHigh();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_VOLUMELOW                   : return (double)symbol.VolumeLow();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_TIME                        : return (double)symbol.Time()/1000;
     case PEND_REQ_ACTIVATE_BY_SYMBOL_SPREAD                      : return symbol.Spread();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_START_TIME                  : return (double)symbol.StartTime();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_EXPIRATION_TIME             : return (double)symbol.ExpirationTime();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_TRADE_STOPS_LEVEL           : return symbol.TradeStopLevel();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_TRADE_FREEZE_LEVEL          : return symbol.TradeFreezeLevel();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_BIDHIGH                     : return symbol.BidHigh();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_BIDLOW                      : return symbol.BidLow();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_ASKHIGH                     : return symbol.AskHigh();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_ASKLOW                      : return symbol.AskLow();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_LASTHIGH                    : return symbol.LastHigh();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_LASTLOW                     : return symbol.LastLow();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_VOLUME_REAL                 : return symbol.VolumeReal();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_VOLUMEHIGH_REAL             : return symbol.VolumeHighReal();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_VOLUMELOW_REAL              : return symbol.VolumeLowReal();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_OPTION_STRIKE               : return symbol.OptionStrike();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_TRADE_ACCRUED_INTEREST      : return symbol.TradeAccuredInterest();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_TRADE_FACE_VALUE            : return symbol.TradeFaceValue();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_TRADE_LIQUIDITY_RATE        : return symbol.TradeLiquidityRate();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_SWAP_LONG                   : return symbol.SwapLong();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_SWAP_SHORT                  : return symbol.SwapShort();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_VOLUME              : return symbol.SessionVolume();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_TURNOVER            : return symbol.SessionTurnover();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_INTEREST            : return symbol.SessionInterest();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_BUY_ORDERS_VOLUME   : return symbol.SessionBuyOrdersVolume();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_SELL_ORDERS_VOLUME  : return symbol.SessionSellOrdersVolume();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_OPEN                : return symbol.SessionOpen();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_CLOSE               : return symbol.SessionClose();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_AW                  : return symbol.SessionAW();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_PRICE_SETTLEMENT    : return symbol.SessionPriceSettlement();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_PRICE_LIMIT_MIN     : return symbol.SessionPriceLimitMin();
     case PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_PRICE_LIMIT_MAX     : return symbol.SessionPriceLimitMax();
     default: return EMPTY_VALUE;
    }
  }
//+------------------------------------------------------------------+

Dependendo do tipo de condição, de acordo com a enumeração dos tipos de condição do símbolo, retornamos o valor da propriedade correspondente do objeto-símbolo, o ponteiro para o qual é transferido ao método.

Método que retorna dados de evento atuais:

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

Dependendo do tipo de condição e se registrado um novo evento na conta num determinado momento, obtemos o último evento, e de acordo com a enumeração dos tipos de condições, retornamos o sinalizador de igualdade ao valor do último evento controlado no objeto-ordem pendente (retornamos o sinalizador de ocorrência de evento controlado).

Agora o temporizador de classe é mais compacto:

//+------------------------------------------------------------------+
//| Timer                                                            |
//+------------------------------------------------------------------+
void CTradingControl::OnTimer(void)
  {
   //--- In a loop by the list of pending requests
   int total=this.m_list_request.Total();
   for(int i=total-1;i>WRONG_VALUE;i--)
     {
      //--- receive the next request object
      CPendRequest *req_obj=this.m_list_request.At(i);
      if(req_obj==NULL)
         continue;
      //--- If a request object is created by an error code, use the handler of objects created by the error code
      if(req_obj.TypeRequest()==PEND_REQ_TYPE_ERROR)
         this.OnPReqByErrCodeHandler(req_obj,i);
      //--- Otherwise, this is an object created by request - use the handler of objects created by request
      else
         this.OnPReqByRequestHandler(req_obj,i);
     }
  }
//+------------------------------------------------------------------+

Agora, no temporizador de classe, verificamos apenas o tipo de objeto-ordem pendente obtido da lista de objetos-ordem pendente e, dependendo de seu tipo, chamamos o manipulador de ordem pendente correspondente, que estudamos acima.

Sem entrar em detalhes, estas são todas as modificações na classe de gerenciamento de negociação no momento.
De qualquer forma, você sempre pode ler o código completo nos arquivos anexados ao artigo.

Complementaremos a seção pública da classe do objeto base da biblioteca CEngine.

Para poder obter os valores do nível de registro dos objetos de negociação para enviar mensagens deles a partir de um programa criado com base na biblioteca, adicionamos um método para obter o nível de registro de objeto de negociação de acordo com um símbolo:

   void                 TradingSetTotalTry(const uchar attempts)                       { this.m_trading.SetTotalTry(attempts);                     }
   
//--- Return the logging level of a trading class symbol trading object
   ENUM_LOG_LEVEL       TradingGetLogLevel(const string symbol_name)                   { return this.m_trading.GetTradeObjLogLevel(symbol_name);   }
   
//--- Set standard sounds (symbol==NULL) for a symbol trading object, (symbol!=NULL) for trading objects of all symbols

O método retorna o resultado do funcionamento do método da classe de gerenciamento de negociação GetTradeObjLogLevel().

Declaramos métodos de criação de ordem pendente para abrir posições Buy, para abrir posições Sell, métodos para definir uma nova condição de ativação de ordem pendente, e escrevemos um método que retorna o ponteiro para um objeto-ordem pendente por identificador:

//--- Remove a pending order
   bool                 DeleteOrder(const ulong ticket);
   
//--- Create a pending request (1) to open Buy and (2) Sell positions
   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);
                                    
//--- Set pending request activation criteria
   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);
                                                
//--- Return the pointer to the request object by its ID in the list
   CPendRequest        *GetPendRequestByID(const uchar id)              { return this.m_trading.GetPendRequestByID(id);       }

//--- Return event (1) milliseconds, (2) reason and (3) source from its 'long' value

O método GetPendRequestByID(), que retorna um ponteiro para um objeto-ordem pendente, retorna o resultado do funcionamento do método de mesmo nome da classe de controle de negociação.

Implementação do método de criação de ordem pendente para abrir uma posição Buy:

//+------------------------------------------------------------------+
//| Create a pending request for opening a Buy position              |
//+------------------------------------------------------------------+
template<typename SL,typename TP> 
int CEngine::OpenBuyPending(const double volume,
                            const string symbol,
                            const ulong magic=ULONG_MAX,
                            const SL sl=0,
                            const TP tp=0,
                            const uchar group_id1=0,
                            const uchar group_id2=0,
                            const string comment=NULL,
                            const ulong deviation=ULONG_MAX,
                            const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE)
  {
   return this.m_trading.OpenPositionPending(POSITION_TYPE_BUY,volume,symbol,magic,sl,tp,group_id1,group_id2,comment,deviation,type_filling);
  }
//+------------------------------------------------------------------+

O método chama o método - discutido acima - para criar uma ordem pendente para abrir uma posição de uma classe de gerenciamento de negociação. Como tipo de posição a ser aberta, ao método transferimos uma constante POSITION_TYPE_BUY, e os restantes parâmetros da posição futura.

Implementação do método de criação de ordem pendente para abrir uma posição Sell:

//+------------------------------------------------------------------+
//| Create a pending request for opening a Sell position             |
//+------------------------------------------------------------------+
template<typename SL,typename TP> 
int CEngine::OpenSellPending(const double volume,
                            const string symbol,
                            const ulong magic=ULONG_MAX,
                            const SL sl=0,
                            const TP tp=0,
                            const uchar group_id1=0,
                            const uchar group_id2=0,
                            const string comment=NULL,
                            const ulong deviation=ULONG_MAX,
                            const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE)
  {
   return this.m_trading.OpenPositionPending(POSITION_TYPE_SELL,volume,symbol,magic,sl,tp,group_id1,group_id2,comment,deviation,type_filling);
  }
//+------------------------------------------------------------------+

O método chama o método - discutido acima - para criar uma ordem pendente para abrir uma posição de uma classe de gerenciamento de negociação. Como tipo de posição a ser aberta, ao método transferimos uma constante POSITION_TYPE_SELL, e os restantes parâmetros da posição futura.

Implementação do método para definir no objeto-ordem pendente uma nova condição de ativação:

//+------------------------------------------------------------------+
//| Set pending request activation criteria                          |
//+------------------------------------------------------------------+
bool CEngine::SetNewActivationProperties(const uchar id,
                                         const ENUM_PEND_REQ_ACTIVATION_SOURCE source,
                                         const int property,
                                         const double control_value,
                                         const ENUM_COMPARER_TYPE comparer_type,
                                         const double actual_value)
  {
   return this.m_trading.SetNewActivationProperties(id,source,property,control_value,comparer_type,actual_value);
  }
//+------------------------------------------------------------------+

O método chama o método - discutido acima - para adicionar uma nova condição de ativação no objeto-ordem pendente da classe de gerenciamento de negociação. Por hoje essas são todas as modificações na biblioteca.

Teste

Para testar a negociação usando ordens pendentes para abrir posições, usamos o EA do artigo anterior e o salvamos na nova pasta \MQL5\Experts\TestDoEasy\ Part31\ com o novo nome TestDoEasyPart31.mq5.

Para verificar o funcionamento de ordens pendentes de acordo com as condições definidas, criaremos botões adicionais no painel de negociação do EA de teste. Os botões serão marcados como " P" (condição segundo o preço) e como "T" (condição segundo a hora). As ordens pendentes serão criadas ao clicar no botão "Buy" ou " Sell" desde que seja pressionado um dos botões "P", "T", ou ambos. Se os dois forem pressionados ao mesmo tempo, significa que a ordem pendente possui duas condições de ativação, por preço e por hora.

Também adicionamos dois parâmetros de entrada, um indicando a distância em relação ao preço atual para definir o preço controlado e outro sendo o número de barras do período gráfico atual para definir a tempo de ativação da ordem.

Assim, se pressionado o botão Buy e ativado o botão "P" correspondente, abaixo do preço atual será traçada uma distância tendo em conta número de pontos definido nas configurações, este valor será definido como controle para acionar a ordem pendente: se o preço se tornar igual ou inferior ao calculado, será acionada a solicitação pendente.

Se for ativado o botão "T", ao tempo atual será adicionado uma hora calculada como hora atual + hora do número de barras definido para o período gráfico atual, e essa hora será registrada como controle para acionar a ordem pendente — quando o preço atual se tornar igual o superior so preço calculado, a ordem pendente será ativada.

Se ambos os botões "P" e "T" forem pressionados, para a ordem pendente ser ativada deverão ser atendidas ambas as condições simultaneamente.

Se for aberta uma posição Sell, o preço de controle será calculado como preço atual + número de pontos definido nas configurações. Quer dizer, para que seja acionada a ordem pendente, o preço atual deve se tornar superior ao preço que estava no momento em que a ordem pendente foi criada (quando foi pressionado o botão " Sell").

Ao bloco de parâmetros de entrada do EA adicionamos a distância de recuo do preço de controle para ativação da ordem no momento de criação da ordem e o número de barras de atraso para definir a hora de ativação da ordem pendente:

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

Ao bloco de variáveis globais do EA adicionamos as variáveis correspondentes para armazenar o recuo do preço de ativação e o atraso em barras para definir a hora de ativação da ordem pendente, bem como os sinalizadores de estado de botões de ordens pendentes:

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

No processador OnInit() do EA, às variáveis atribuímos valores corretos de parâmetros de entrada e redefinimos os estados dos botões de ordens pendentes:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Calling the function displays the list of enumeration constants in the journal 
//--- (the list is set in the strings 22 and 25 of the DELib.mqh file) for checking the constants validity
   //EnumNumbersTest();

//--- Set EA global variables
   prefix=MQLInfoString(MQL_PROGRAM_NAME)+"_";
   testing=engine.IsTester();
   for(int i=0;i<TOTAL_BUTT;i++)
     {
      butt_data[i].name=prefix+EnumToString((ENUM_BUTTONS)i);
      butt_data[i].text=EnumToButtText((ENUM_BUTTONS)i);
     }
   lot=NormalizeLot(Symbol(),fmax(InpLots,MinimumLots(Symbol())*2.0));
   magic_number=InpMagic;
   stoploss=InpStopLoss;
   takeprofit=InpTakeProfit;
   distance_pending=InpDistance;
   distance_stoplimit=InpDistanceSL;
   slippage=InpSlippage;
   trailing_stop=InpTrailingStop*Point();
   trailing_step=InpTrailingStep*Point();
   trailing_start=InpTrailingStart;
   stoploss_to_modify=InpStopLossModify;
   takeprofit_to_modify=InpTakeProfitModify;
   distance_pending_request=(InpDistancePReq<5 ? 5 : InpDistancePReq);
   bars_delay_pending_request=(InpBarsDelayPReq<1 ? 1 : InpBarsDelayPReq);
//--- Initialize random group numbers
   group1=0;
   group2=0;
   srand(GetTickCount());
   
//--- Initialize DoEasy library
   OnInitDoEasy();
   
//--- Check and remove remaining EA graphical objects
   if(IsPresentObects(prefix))
      ObjectsDeleteAll(0,prefix);

//--- Create the button panel
   if(!CreateButtons(InpButtShiftX,InpButtShiftY))
      return INIT_FAILED;
//--- Set trailing activation button status
   ButtonState(butt_data[TOTAL_BUTT-1].name,trailing_on);
//--- Reset states of the buttons for working using pending requests
   for(int i=0;i<14;i++)
     {
      ButtonState(butt_data[i].name+"_PRICE",false);
      ButtonState(butt_data[i].name+"_TIME",false);
     }

//--- Check playing a standard sound by macro substitution and a custom sound by description
   engine.PlaySoundByDescription(SND_OK);
   Sleep(600);
   engine.PlaySoundByDescription(TextByLanguage("Звук упавшей монетки 2","Falling coin 2"));

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

Para a distância do preço de ativação de ordem pendente, igualamos a distância de menos de cinco pontos a cinco pontos, já para o atraso em barras definimos um atraso mínimo de pelo menos uma barra do período gráfico atual.
Os botões para ativar o trabalho com ordens es pendentes são redefinidos para um estado inativo — no EA de teste, os botões são projetados apenas para verificação e não monitoraremos seu status para armazenamento.

Na função de criação de botões do painel adicionamos uma variável que armazena a largura dos novos botões e em outro ciclo criamos novos botões de ativação de negociação usando ordens pendentes:

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

Na função para definir o estado dos botões (cores de botão ativo) adicionamos configuração de cores para os botões de ordens pendentes ativos:

//+------------------------------------------------------------------+
//| Set the button status                                            |
//+------------------------------------------------------------------+
void ButtonState(const string name,const bool state)
  {
   ObjectSetInteger(0,name,OBJPROP_STATE,state);
//--- Trailing activation button
   if(name==butt_data[TOTAL_BUTT-1].name)
     {
      if(state)
         ObjectSetInteger(0,name,OBJPROP_BGCOLOR,C'220,255,240');
      else
         ObjectSetInteger(0,name,OBJPROP_BGCOLOR,C'240,240,240');
     }
//--- Buttons enabling pending requests
   if(StringFind(name,"_PRICE")>0 || StringFind(name,"_TIME")>0)
     {
      if(state)
         ObjectSetInteger(0,name,OBJPROP_BGCOLOR,C'255,220,90');
      else
         ObjectSetInteger(0,name,OBJPROP_BGCOLOR,C'240,240,240');
     }
  }
//+------------------------------------------------------------------+

À função de processamento de botões pressionados adicionamos códigos para processar pressionamentos de botão usando ordens pendentes:

//+------------------------------------------------------------------+
//| Handle pressing the buttons                                      |
//+------------------------------------------------------------------+
void PressButtonEvents(const string button_name)
  {
   bool comp_magic=true;   // Temporary variable selecting the composite magic number with random group IDs
   string comment="";
   //--- Convert button name into its string ID
   string button=StringSubstr(button_name,StringLen(prefix));
   //--- Random group 1 and 2 numbers within the range of 0 - 15
   group1=(uchar)Rand();
   group2=(uchar)Rand();
   uint magic=(comp_magic ? engine.SetCompositeMagicNumber(magic_number,group1,group2) : magic_number);
   //--- If the button is pressed
   if(ButtonState(button_name))
     {
      //--- If the BUTT_BUY button is pressed: Open Buy position
      if(button==EnumToString(BUTT_BUY))
        {
         //--- If the pending request creation buttons are not pressed, open Buy 
         if(!pending_buy)
            engine.OpenBuy(lot,Symbol(),magic,stoploss,takeprofit);   // No comment - the default comment is to be set
         //--- Otherwise, create a pending request for opening a Buy position
         else
           {
            int id=engine.OpenBuyPending(lot,Symbol(),magic,stoploss,takeprofit);
            if(id>0)
              {
               //--- If the price criterion is selected
               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 the time criterion is selected
               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();
              }
           }
        }
      //--- If the BUTT_BUY_LIMIT button is pressed: Place BuyLimit
      else if(button==EnumToString(BUTT_BUY_LIMIT))
        {
         //--- If the pending request creation buttons are not pressed, set BuyLimit
         if(!pending_buy_limit)
            engine.PlaceBuyLimit(lot,Symbol(),distance_pending,stoploss,takeprofit,magic,TextByLanguage("Отложенный BuyLimit","Pending BuyLimit order"));
         //--- Otherwise, do nothing in this version
         else
           {
            
           }
        }
      //--- If the BUTT_BUY_STOP button is pressed: Set BuyStop
      else if(button==EnumToString(BUTT_BUY_STOP))
        {
         //--- If the pending request creation buttons are not pressed, set BuyStop
         if(!pending_buy_stop)
            engine.PlaceBuyStop(lot,Symbol(),distance_pending,stoploss,takeprofit,magic,TextByLanguage("Отложенный BuyStop","Pending BuyStop order"));
         //--- Otherwise, do nothing in this version
         else
           {
            
           }
        }
      //--- If the BUTT_BUY_STOP_LIMIT button is pressed: Set BuyStopLimit
      else if(button==EnumToString(BUTT_BUY_STOP_LIMIT))
        {
         //--- If the pending request creation buttons are not pressed, set BuyStopLimit
         if(!pending_buy_stoplimit)
            engine.PlaceBuyStopLimit(lot,Symbol(),distance_pending,distance_stoplimit,stoploss,takeprofit,magic,TextByLanguage("Отложенный BuyStopLimit","Pending order BuyStopLimit"));
         //--- Otherwise, do nothing in this version
         else
           {
            
           }
        }
      //--- If the BUTT_SELL button is pressed: Open Sell position
      else if(button==EnumToString(BUTT_SELL))
        {
         //--- If the pending request creation buttons are not pressed, open Sell
         if(!pending_sell)
            engine.OpenSell(lot,Symbol(),magic,stoploss,takeprofit);  // No comment - the default comment is to be set
         //--- Otherwise, create a pending request for opening a Sell position
         else
           {
            int id=engine.OpenSellPending(lot,Symbol(),magic,stoploss,takeprofit);
            if(id>0)
              {
               //--- If the price criterion is selected
               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 the time criterion is selected
               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();
              }
           }
        }
      //--- If the BUTT_SELL_LIMIT button is pressed: Set SellLimit
      else if(button==EnumToString(BUTT_SELL_LIMIT))
        {
         //--- If the pending request creation buttons are not pressed, set SellLimit
         if(!pending_sell_limit)
            engine.PlaceSellLimit(lot,Symbol(),distance_pending,stoploss,takeprofit,magic,TextByLanguage("Отложенный SellLimit","Pending SellLimit order"));
         //--- Otherwise, do nothing in this version
         else
           {
            
           }
        }
      //--- If the BUTT_SELL_STOP button is pressed: Set SellStop
      else if(button==EnumToString(BUTT_SELL_STOP))
        {
         //--- If the pending request creation buttons are not pressed, set SellStop
         if(!pending_sell_stop)
            engine.PlaceSellStop(lot,Symbol(),distance_pending,stoploss,takeprofit,magic,TextByLanguage("Отложенный SellStop","Pending SellStop order"));
         //--- Otherwise, do nothing in this version
         else
           {
            
           }
        }
      //--- If the BUTT_SELL_STOP_LIMIT button is pressed: Set SellStopLimit
      else if(button==EnumToString(BUTT_SELL_STOP_LIMIT))
        {
         //--- If the pending request creation buttons are not pressed, set SellStopLimit
         if(!pending_sell_stoplimit)
            engine.PlaceSellStopLimit(lot,Symbol(),distance_pending,distance_stoplimit,stoploss,takeprofit,magic,TextByLanguage("Отложенный SellStopLimit","Pending SellStopLimit order"));
         //--- Otherwise, do nothing in this version
         else
           {
            
           }
        }
      //--- If the BUTT_CLOSE_BUY button is pressed: Close Buy with the maximum profit
      else if(button==EnumToString(BUTT_CLOSE_BUY))
        {
         //--- Get the list of all open positions
         CArrayObj* list=engine.GetListMarketPosition();
         //--- Select only Buy positions from the list and for the current symbol only
         list=CSelect::ByOrderProperty(list,ORDER_PROP_SYMBOL,Symbol(),EQUAL);
         list=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_BUY,EQUAL);
         //--- Sort the list by profit considering commission and swap
         list.Sort(SORT_BY_ORDER_PROFIT_FULL);
         //--- Get the index of the Buy position with the maximum profit
         int index=CSelect::FindOrderMax(list,ORDER_PROP_PROFIT_FULL);
         if(index>WRONG_VALUE)
           {
            //--- Get the Buy position object and close a position by ticket
            COrder* position=list.At(index);
            if(position!=NULL)
               engine.ClosePosition((ulong)position.Ticket());
           }
        }
      //--- If the BUTT_CLOSE_BUY2 button is pressed: Close the half of the Buy with the maximum profit
      else if(button==EnumToString(BUTT_CLOSE_BUY2))
        {
         //--- Get the list of all open positions
         CArrayObj* list=engine.GetListMarketPosition();
         //--- Select only Buy positions from the list and for the current symbol only
         list=CSelect::ByOrderProperty(list,ORDER_PROP_SYMBOL,Symbol(),EQUAL);
         list=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_BUY,EQUAL);
         //--- Sort the list by profit considering commission and swap
         list.Sort(SORT_BY_ORDER_PROFIT_FULL);
         //--- Get the index of the Buy position with the maximum profit
         int index=CSelect::FindOrderMax(list,ORDER_PROP_PROFIT_FULL);
         if(index>WRONG_VALUE)
           {
            COrder* position=list.At(index);
            //--- Close the Buy position partially
            if(position!=NULL)
               engine.ClosePositionPartially((ulong)position.Ticket(),position.Volume()/2.0);
           }
        }
      //--- If the BUTT_CLOSE_BUY_BY_SELL button is pressed: Close Buy with the maximum profit by the opposite Sell with the maximum profit
      else if(button==EnumToString(BUTT_CLOSE_BUY_BY_SELL))
        {
         //--- In case of a hedging account
         if(engine.IsHedge())
           {
            CArrayObj *list_buy=NULL, *list_sell=NULL;
            //--- Get the list of all open positions
            CArrayObj* list=engine.GetListMarketPosition();
            if(list==NULL)
               return;
            //--- Select only current symbol positions from the list
            list=CSelect::ByOrderProperty(list,ORDER_PROP_SYMBOL,Symbol(),EQUAL);
            
            //--- Select only Buy positions from the list
            list_buy=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_BUY,EQUAL);
            if(list_buy==NULL)
               return;
            //--- Sort the list by profit considering commission and swap
            list_buy.Sort(SORT_BY_ORDER_PROFIT_FULL);
            //--- Get the index of the Buy position with the maximum profit
            int index_buy=CSelect::FindOrderMax(list_buy,ORDER_PROP_PROFIT_FULL);
            
            //--- Select only Sell positions from the list
            list_sell=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_SELL,EQUAL);
            if(list_sell==NULL)
               return;
            //--- Sort the list by profit considering commission and swap
            list_sell.Sort(SORT_BY_ORDER_PROFIT_FULL);
            //--- Get the index of the Sell position with the maximum profit
            int index_sell=CSelect::FindOrderMax(list_sell,ORDER_PROP_PROFIT_FULL);
            if(index_buy>WRONG_VALUE && index_sell>WRONG_VALUE)
              {
               //--- Select the Buy position with the maximum profit
               COrder* position_buy=list_buy.At(index_buy);
               //--- Select the Sell position with the maximum profit
               COrder* position_sell=list_sell.At(index_sell);
               //--- Close the Buy position by the opposite Sell one
               if(position_buy!=NULL && position_sell!=NULL)
                  engine.ClosePositionBy((ulong)position_buy.Ticket(),(ulong)position_sell.Ticket());
              }
           }
        }
        
      //--- If the BUTT_CLOSE_SELL button is pressed: Close Sell with the maximum profit
      else if(button==EnumToString(BUTT_CLOSE_SELL))
        {
         //--- Get the list of all open positions
         CArrayObj* list=engine.GetListMarketPosition();
         //--- Select only Sell positions from the list and for the current symbol only
         list=CSelect::ByOrderProperty(list,ORDER_PROP_SYMBOL,Symbol(),EQUAL);
         list=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_SELL,EQUAL);
         //--- Sort the list by profit considering commission and swap
         list.Sort(SORT_BY_ORDER_PROFIT_FULL);
         //--- Get the index of the Sell position with the maximum profit
         int index=CSelect::FindOrderMax(list,ORDER_PROP_PROFIT_FULL);
         if(index>WRONG_VALUE)
           {
            //--- Get the Sell position object and close a position by ticket
            COrder* position=list.At(index);
            if(position!=NULL)
               engine.ClosePosition((ulong)position.Ticket());
           }
        }
      //--- If the BUTT_CLOSE_SELL2 button is pressed: Close the half of the Sell with the maximum profit
      else if(button==EnumToString(BUTT_CLOSE_SELL2))
        {
         //--- Get the list of all open positions
         CArrayObj* list=engine.GetListMarketPosition();
         //--- Select only Sell positions from the list and for the current symbol only
         list=CSelect::ByOrderProperty(list,ORDER_PROP_SYMBOL,Symbol(),EQUAL);
         list=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_SELL,EQUAL);
         //--- Sort the list by profit considering commission and swap
         list.Sort(SORT_BY_ORDER_PROFIT_FULL);
         //--- Get the index of the Sell position with the maximum profit
         int index=CSelect::FindOrderMax(list,ORDER_PROP_PROFIT_FULL);
         if(index>WRONG_VALUE)
           {
            COrder* position=list.At(index);
            //--- Close the Sell position partially
            if(position!=NULL)
               engine.ClosePositionPartially((ulong)position.Ticket(),position.Volume()/2.0);
           }
        }
      //--- If the BUTT_CLOSE_SELL_BY_BUY button is pressed: Close Sell with the maximum profit by the opposite Buy with the maximum profit
      else if(button==EnumToString(BUTT_CLOSE_SELL_BY_BUY))
        {
         //--- In case of a hedging account
         if(engine.IsHedge())
           {
            CArrayObj *list_buy=NULL, *list_sell=NULL;
            //--- Get the list of all open positions
            CArrayObj* list=engine.GetListMarketPosition();
            if(list==NULL)
               return;
            //--- Select only current symbol positions from the list
            list=CSelect::ByOrderProperty(list,ORDER_PROP_SYMBOL,Symbol(),EQUAL);
            
            //--- Select only Sell positions from the list
            list_sell=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_SELL,EQUAL);
            if(list_sell==NULL)
               return;
            //--- Sort the list by profit considering commission and swap
            list_sell.Sort(SORT_BY_ORDER_PROFIT_FULL);
            //--- Get the index of the Sell position with the maximum profit
            int index_sell=CSelect::FindOrderMax(list_sell,ORDER_PROP_PROFIT_FULL);
            
            //--- Select only Buy positions from the list
            list_buy=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_BUY,EQUAL);
            if(list_buy==NULL)
               return;
            //--- Sort the list by profit considering commission and swap
            list_buy.Sort(SORT_BY_ORDER_PROFIT_FULL);
            //--- Get the index of the Buy position with the maximum profit
            int index_buy=CSelect::FindOrderMax(list_buy,ORDER_PROP_PROFIT_FULL);
            if(index_sell>WRONG_VALUE && index_buy>WRONG_VALUE)
              {
               //--- Select the Sell position with the maximum profit
               COrder* position_sell=list_sell.At(index_sell);
               //--- Select the Buy position with the maximum profit
               COrder* position_buy=list_buy.At(index_buy);
               //--- Close the Sell position by the opposite Buy one
               if(position_sell!=NULL && position_buy!=NULL)
                  engine.ClosePositionBy((ulong)position_sell.Ticket(),(ulong)position_buy.Ticket());
              }
           }
        }
      //--- If the BUTT_CLOSE_ALL is pressed: Close all positions starting with the one with the least profit
      else if(button==EnumToString(BUTT_CLOSE_ALL))
        {
         //--- Get the list of all open positions
         CArrayObj* list=engine.GetListMarketPosition();
         //--- Select only current symbol positions from the list
         list=CSelect::ByOrderProperty(list,ORDER_PROP_SYMBOL,Symbol(),EQUAL);
         if(list!=NULL)
           {
            //--- Sort the list by profit considering commission and swap
            list.Sort(SORT_BY_ORDER_PROFIT_FULL);
            int total=list.Total();
            //--- In the loop from the position with the least profit
            for(int i=0;i<total;i++)
              {
               COrder* position=list.At(i);
               if(position==NULL)
                  continue;
               //--- close each position by its ticket
               engine.ClosePosition((ulong)position.Ticket());
              }
           }
        }
      //--- If the BUTT_DELETE_PENDING button is pressed: Remove pending orders starting from the oldest one
      else if(button==EnumToString(BUTT_DELETE_PENDING))
        {
         //--- Get the list of all orders
         CArrayObj* list=engine.GetListMarketPendings();
         //--- Select only current symbol orders from the list
         list=CSelect::ByOrderProperty(list,ORDER_PROP_SYMBOL,Symbol(),EQUAL);
         if(list!=NULL)
           {
            //--- Sort the list by placement time
            list.Sort(SORT_BY_ORDER_TIME_OPEN);
            int total=list.Total();
            //--- In a loop from an order with the longest time
            for(int i=total-1;i>=0;i--)
              {
               COrder* order=list.At(i);
               if(order==NULL)
                  continue;
               //--- delete the order by its ticket
               engine.DeleteOrder((ulong)order.Ticket());
              }
           }
        }
      //--- If the BUTT_PROFIT_WITHDRAWAL button is pressed: Withdraw funds from the account
      if(button==EnumToString(BUTT_PROFIT_WITHDRAWAL))
        {
         //--- If the program is launched in the tester
         if(MQLInfoInteger(MQL_TESTER))
           {
            //--- Emulate funds withdrawal
            TesterWithdrawal(withdrawal);
           }
        }
      //--- If the BUTT_SET_STOP_LOSS button is pressed: Place StopLoss to all orders and positions where it is not present
      if(button==EnumToString(BUTT_SET_STOP_LOSS))
        {
         SetStopLoss();
        }
      //--- If the BUTT_SET_TAKE_PROFIT button is pressed: Place TakeProfit to all orders and positions where it is not present
      if(button==EnumToString(BUTT_SET_TAKE_PROFIT))
        {
         SetTakeProfit();
        }
      //--- Wait for 1/10 of a second
      Sleep(100);
      //--- "Unpress" the button (if this is neither a trailing button, nor the buttons enabling pending requests)
      if(button!=EnumToString(BUTT_TRAILING_ALL) && StringFind(button,"_PRICE")<0 && StringFind(button,"_TIME")<0)
         ButtonState(button_name,false);
      //--- If the BUTT_TRAILING_ALL button or the buttons enabling pending requests are pressed
      else
        {
         //--- Set the active button color for the button enabling trailing
         if(button==EnumToString(BUTT_TRAILING_ALL))
           {
            ButtonState(button_name,true);
            trailing_on=true;
           }
         
         //--- Buying
         //--- Set the active button color for the button enabling pending requests for opening Buy by price or time
         if(button==EnumToString(BUTT_BUY)+"_PRICE" || button==EnumToString(BUTT_BUY)+"_TIME")
           {
            ButtonState(button_name,true);
            pending_buy=true;
           }
         //--- Set the active button color for the button enabling pending requests for placing BuyLimit by price or time
         if(button==EnumToString(BUTT_BUY_LIMIT)+"_PRICE" || button==EnumToString(BUTT_BUY_LIMIT)+"_TIME")
           {
            ButtonState(button_name,true);
            pending_buy_limit=true;
           }
         //--- Set the active button color for the button enabling pending requests for placing BuyStop by price or time
         if(button==EnumToString(BUTT_BUY_STOP)+"_PRICE" || button==EnumToString(BUTT_BUY_STOP)+"_TIME")
           {
            ButtonState(button_name,true);
            pending_buy_stop=true;
           }
         //--- Set the active button color for the button enabling pending requests for placing BuyStopLimit by price or time
         if(button==EnumToString(BUTT_BUY_STOP_LIMIT)+"_PRICE" || button==EnumToString(BUTT_BUY_STOP_LIMIT)+"_TIME")
           {
            ButtonState(button_name,true);
            pending_buy_stoplimit=true;
           }
         //--- Set the active button color for the button enabling pending requests for closing Buy by price or time
         if(button==EnumToString(BUTT_CLOSE_BUY)+"_PRICE" || button==EnumToString(BUTT_CLOSE_BUY)+"_TIME")
           {
            ButtonState(button_name,true);
            pending_close_buy=true;
           }
         //--- Set the active button color for the button enabling pending requests for closing 1/2 Buy by price or time
         if(button==EnumToString(BUTT_CLOSE_BUY2)+"_PRICE" || button==EnumToString(BUTT_CLOSE_BUY2)+"_TIME")
           {
            ButtonState(button_name,true);
            pending_close_buy2=true;
           }
         //--- Set the active button color for the button enabling pending requests for closing Buy by an opposite Sell by price or time
         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;
           }
         
         //--- Selling
         //--- Set the active button color for the button enabling pending requests for opening Sell by price or time
         if(button==EnumToString(BUTT_SELL)+"_PRICE" || button==EnumToString(BUTT_SELL)+"_TIME")
           {
            ButtonState(button_name,true);
            pending_sell=true;
           }
         //--- Set the active button color for the button enabling pending requests for placing SellLimit by price or time
         if(button==EnumToString(BUTT_SELL_LIMIT)+"_PRICE" || button==EnumToString(BUTT_SELL_LIMIT)+"_TIME")
           {
            ButtonState(button_name,true);
            pending_sell_limit=true;
           }
         //--- Set the active button color for the button enabling pending requests for placing SellStop by price or time
         if(button==EnumToString(BUTT_SELL_STOP)+"_PRICE" || button==EnumToString(BUTT_SELL_STOP)+"_TIME")
           {
            ButtonState(button_name,true);
            pending_sell_stop=true;

           }
         //--- Set the active button color for the button enabling pending requests for placing SellStopLimit by price or time
         if(button==EnumToString(BUTT_SELL_STOP_LIMIT)+"_PRICE" || button==EnumToString(BUTT_SELL_STOP_LIMIT)+"_TIME")
           {
            ButtonState(button_name,true);
            pending_sell_stoplimit=true;
           }
         //--- Set the active button color for the button enabling pending requests for closing Sell by price or time
         if(button==EnumToString(BUTT_CLOSE_SELL)+"_PRICE" || button==EnumToString(BUTT_CLOSE_SELL)+"_TIME")
           {
            ButtonState(button_name,true);
            pending_close_sell=true;
           }
         //--- Set the active button color for the button enabling pending requests for closing 1/2 Sell by price or time
         if(button==EnumToString(BUTT_CLOSE_SELL2)+"_PRICE" || button==EnumToString(BUTT_CLOSE_SELL2)+"_TIME")
           {
            ButtonState(button_name,true);
            pending_close_sell2=true;
           }
         //--- Set the active button color for the button enabling pending requests for closing Sell by an opposite Buy by price or time
         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;
           }
        }
      //--- re-draw the chart
      ChartRedraw();
     }
   //--- Return a color for the inactive buttons
   else 
     {
      //--- trailing button
      if(button==EnumToString(BUTT_TRAILING_ALL))
        {
         ButtonState(button_name,false);
         trailing_on=false;
        }
      
      //--- Buying
      //--- the button enabling pending requests for opening Buy by price
      if(button==EnumToString(BUTT_BUY)+"_PRICE")
        {
         ButtonState(button_name,false);
         pending_buy=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_BUY)+"_TIME"));
        }
      //--- the button enabling pending requests for opening Buy by time
      if(button==EnumToString(BUTT_BUY)+"_TIME")
        {
         ButtonState(button_name,false);
         pending_buy=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_BUY)+"_PRICE"));
        }
      
      //--- the button enabling pending requests for placing BuyLimit by price
      if(button==EnumToString(BUTT_BUY_LIMIT)+"_PRICE")
        {
         ButtonState(button_name,false);
         pending_buy_limit=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_BUY_LIMIT)+"_TIME"));
        }
      //--- the button enabling pending requests for placing BuyLimit by time
      if(button==EnumToString(BUTT_BUY_LIMIT)+"_TIME")
        {
         ButtonState(button_name,false);
         pending_buy_limit=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_BUY_LIMIT)+"_PRICE"));
        }
      
      //--- the button enabling pending requests for placing BuyStop by price
      if(button==EnumToString(BUTT_BUY_STOP)+"_PRICE")
        {
         ButtonState(button_name,false);
         pending_buy_stop=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_BUY_STOP)+"_TIME"));
        }
      //--- the button enabling pending requests for placing BuyStop by time
      if(button==EnumToString(BUTT_BUY_STOP)+"_TIME")
        {
         ButtonState(button_name,false);
         pending_buy_stop=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_BUY_STOP)+"_PRICE"));
        }
      
      //--- the button enabling pending requests for placing BuyStopLimit by price
      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"));
        }
      //--- the button enabling pending requests for placing BuyStopLimit by time
      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"));
        }
      
      //--- the button enabling pending requests for closing Buy by price
      if(button==EnumToString(BUTT_CLOSE_BUY)+"_PRICE")
        {
         ButtonState(button_name,false);
         pending_close_buy=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_BUY)+"_TIME"));
        }
      //--- the button enabling pending requests for closing Buy by time
      if(button==EnumToString(BUTT_CLOSE_BUY)+"_TIME")
        {
         ButtonState(button_name,false);
         pending_close_buy=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_BUY)+"_PRICE"));
        }
      
      //--- the button enabling pending requests for closing 1/2 Buy by price
      if(button==EnumToString(BUTT_CLOSE_BUY2)+"_PRICE")
        {
         ButtonState(button_name,false);
         pending_close_buy2=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_BUY2)+"_TIME"));
        }
      //--- the button enabling pending requests for closing 1/2 Buy by time
      if(button==EnumToString(BUTT_CLOSE_BUY2)+"_TIME")
        {
         ButtonState(button_name,false);
         pending_close_buy2=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_BUY2)+"_PRICE"));
        }
      
      //--- the button enabling pending requests for closing Buy by an opposite Sell by price
      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"));
        }
      //--- the button enabling pending requests for closing Buy by an opposite Sell by time
      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"));
        }

      //--- Selling
      //--- the button enabling pending requests for opening Sell by price
      if(button==EnumToString(BUTT_SELL)+"_PRICE")
        {
         ButtonState(button_name,false);
         pending_sell=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_SELL)+"_TIME"));
        }
      //--- the button enabling pending requests for opening Sell by time
      if(button==EnumToString(BUTT_SELL)+"_TIME")
        {
         ButtonState(button_name,false);
         pending_sell=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_SELL)+"_PRICE"));
        }
      
      //--- the button enabling pending requests for placing SellLimit by price
      if(button==EnumToString(BUTT_SELL_LIMIT)+"_PRICE")
        {
         ButtonState(button_name,false);
         pending_sell_limit=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_SELL_LIMIT)+"_TIME"));
        }
      //--- the button enabling pending requests for placing SellLimit by time
      if(button==EnumToString(BUTT_SELL_LIMIT)+"_TIME")
        {
         ButtonState(button_name,false);
         pending_sell_limit=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_SELL_LIMIT)+"_PRICE"));
        }
      
      //--- the button enabling pending requests for placing SellStop by price
      if(button==EnumToString(BUTT_SELL_STOP)+"_PRICE")
        {
         ButtonState(button_name,false);
         pending_sell_stop=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_SELL_STOP)+"_TIME"));
        }
      //--- the button enabling pending requests for placing SellStop by time
      if(button==EnumToString(BUTT_SELL_STOP)+"_TIME")
        {
         ButtonState(button_name,false);
         pending_sell_stop=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_SELL_STOP)+"_PRICE"));
        }
      
      //--- the button enabling pending requests for placing SellStopLimit by price
      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"));
        }
      //--- the button enabling pending requests for placing SellStopLimit by time
      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"));
        }
      
      //--- the button enabling pending requests for closing Sell by price
      if(button==EnumToString(BUTT_CLOSE_SELL)+"_PRICE")
        {
         ButtonState(button_name,false);
         pending_close_sell=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_SELL)+"_TIME"));
        }
      //--- the button enabling pending requests for closing Sell by time
      if(button==EnumToString(BUTT_CLOSE_SELL)+"_TIME")
        {
         ButtonState(button_name,false);
         pending_close_sell=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_SELL)+"_PRICE"));
        }
      
      //--- the button enabling pending requests for closing 1/2 Sell by price
      if(button==EnumToString(BUTT_CLOSE_SELL2)+"_PRICE")
        {
         ButtonState(button_name,false);
         pending_close_sell2=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_SELL2)+"_TIME"));
        }
      //--- the button enabling pending requests for closing 1/2 Sell by time
      if(button==EnumToString(BUTT_CLOSE_SELL2)+"_TIME")
        {
         ButtonState(button_name,false);
         pending_close_sell2=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_SELL2)+"_PRICE"));
        }
      
      //--- the button enabling pending requests for closing Sell by an opposite Buy by price
      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"));
        }
      //--- the button enabling pending requests for closing Sell by an opposite Buy by time
      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"));
        }
      //--- re-draw the chart
      ChartRedraw();
     }
  }
//+------------------------------------------------------------------+

Apesar da função ser bastante grande, o código é comentado em detalhes, e não precisa de explicações adicionais. De qualquer forma, na discussão do artigo podem ser feitas todas as perguntas.

Compilemos o EA. Por padrão, o recuo do preço para uma ordem pendente é de 50 pontos, enquanto o atraso nas barras é de cinco barras. Deixamos estas configurações inalteradas e iniciamos o EA no testador de estratégia.
Ativamos os botões de ativação - por preço e por hora - de ordens pendentes para abertura de uma posição Buy, após isso, esperamos que sejam acionadas as ordens pendentes.
Em seguida, ativamos o botão de ativação - desta vez, por hora - de ordens pendentes para abertura de posições Sell, e também aguardamos que seja ativada a ordem pendente:


Como podemos ver nas entradas do log, são criadas ordens pendentes para compra, e a elas são atribuídas condições para serem ativadas. Se o preço e a hora atingirem as condições especificadas, ambas as ordens pendentes serão ativadas e os objetos de ordens pendentes serão excluídos devido à sua ativação.
Em seguida, criamos uma ordem pendente para compra que é ativada após cinco barras, depois, a ordem é excluída como executada devido ao resultado da abertura da posição.

O que vem agora?

No próximo artigo, continuaremos a desenvolver o conceito de ordens de negociação pendentes e implementaremos a criação de ordens pendentes por condição.

Abaixo estão anexados todos os arquivos da versão atual da biblioteca e os arquivos do EA de teste. Você pode baixá-los e testar tudo sozinho.
Se você tiver perguntas, comentários e sugestões, poderá expressá-los nos comentários do artigo.

Complementos

Artigos desta série:

Parte 1. Conceito, gerenciamento de dados e primeiros resultados
Parte 2. Coleção do histórico de ordens e negócios
Parte 3. Coleção de ordens e posições de mercado, busca e ordenação
Parte 4. Eventos de Negociação. Conceito
Parte 5. Classes e coleções de eventos de negociação. Envio de eventos para o programa
Parte 6. Eventos da conta netting
Parte 7. Eventos de ativação da ordem stoplimit, preparação da funcionalidade para os eventos de modificação de ordens e posições
Parte 8. Eventos de modificação de ordens e posições
Parte 9. Compatibilidade com a MQL4 — preparação dos dados
Parte 10. Compatibilidade com a MQL4 — eventos de abertura de posição e ativação de ordens pendentes
Parte 11. Compatibilidade com a MQL4 — eventos de encerramento de posição
Parte 12. Implementação da classe de objeto "conta" e da coleção de objetos da conta
Parte 13. Eventos do objeto conta
Parte 14. O objeto símbolo
Parte 15. Coleção de objetos-símbolos
Parte 16. Eventos de coleção de símbolos
Parte 17. Interatividade de objetos de biblioteca
Parte 18. Interatividade do objeto-conta e quaisquer de outros objetos da biblioteca
Parte 19. Classe de mensagens de biblioteca
Parte 20. Criação e armazenamento de recursos de programas
Parte 21 Classes de negociação - objeto base de negociação multiplataforma
Parte 22. Classes de negociação - classe básica de negociação, controle de restrições
Parte 23. Classes de negociação - classe básica de negociação, controle de parâmetros válidos
Parte 24. Classes de negociação - classe básica de negociação, correção automática de parâmetros errados
Parte 25. Classes de negociação - classe básica de negociação, processamento de erros retornados pelo servidor de negociação
Parte 26. Trabalho com ordens pendentes, primeira implementação (abertura de posições)
Parte 27. Trabalho com ordens pendentes, posicionamento de ordens pendentes Parte 28. Trabalho com ordens pendentes de negociação - fechamento, exclusão, modificações
Parte 29. Trabalho com ordens de negociação pendentes, classes de objetos-ordens
Parte 30. Trabalho com ordens de negociação pendentes, gerenciamento de objetos-ordens