Biblioteca para criação simples e rápida de programas para MetaTrader (Parte XVII): interatividade de objetos de biblioteca

Artyom Trishkin | 12 dezembro, 2019

Sumário


No último artigo, criamos o objeto base de todos os objetos da biblioteca, por isso, agora qualquer objeto herdado do objeto base recebe a funcionalidade de evento, assim, é possível rastrear facilmente os eventos que ocorrem nas propriedades da classe-herdeiro do objeto base.
Hoje, vamos um pouco mais longe e forneceremos esse objeto e, portanto, qualquer outro objeto de biblioteca, definindo exatamente as propriedades cujas alterações serão controladas externamente, o tamanho da alteração controlada e o nível controlado da propriedade do objeto. Assim, a todos os objetos da biblioteca será adicionada uma funcionalidade que permitirá ao usuário interagir com os objetos da biblioteca.
Por exemplo, para abrir uma posição, gostaríamos de levar em consideração o tamanho do spread e o nível do preço. Podemos facilmente estabelecer o tamanho de spread controlado e rastrear o cruzamento do preço num determinado nível e, em seguida, abrir uma posição. Basta definir programaticamente o tamanho do spread, abaixo do qual é possível a negociação, bem como o nível de preço, em cuja interseção nosso programa receberá o evento do objeto-símbolo sobre um sinal indicando o tamanho do spread e o preço que cruza o nível controlado para negociação.

No entanto, é importante nos livrarmos da necessidade de usar sinalizadores de eventos (o que impõe um limite no número de eventos que podem ser rastreados e requer o armazenamento de listas-enumerações de todos os tipos possíveis de eventos para cada objeto). Agora, o número de eventos possíveis corresponderá ao número de propriedades do objeto — inteiras e reais. As propriedades que não precisam ser controladas serão inicializadas com o valor LONG_MAX, e, consequentemente, não devem ser envolvidas na pesquisa de eventos do objeto.

Como os objetos da biblioteca são armazenados em suas coleções, atualizamos as propriedades dos objetos na coleção no temporizador da biblioteca usando os métodos Refresh() das coleções, onde, por sua vez, são chamados os métodos Refresh() dos objetos armazenados na lista-coleções. Portanto, se rastrearmos as alterações nos valores definidos para as propriedades do objeto-herdeiro no objeto base, no seu método Refresh(), assim podemos criar um modelo de evento simples para cada um dos objetos da biblioteca. Cada um dos objetos enviará sua lista de eventos para o objeto principal da biblioteca CEngine.
Dessa maneria, programas criados com base na biblioteca sempre saberá sobre todos os eventos ocorridos em qualquer um dos objetos de qualquer coleção. Sempre, para cada objeto de qualquer coleção, podemos programaticamente definir e alterar o tamanho do valor controlado de qualquer propriedade.
Tudo isso com a ajuda de uma classe simples do objeto base de todos os objetos de biblioteca.

Métodos para controlar eventos do objeto base da biblioteca

Trabalharemos com o evento de objeto base de biblioteca, assim: anteriormente, para definir os eventos de uma classe concreta, criamos seus próprios métodos de controle de evento, bem como sinalizadores de eventos e enumerações de possíveis eventos de objeto. Agora que o controle de eventos das classes-herdeiros será feito em sua única classe base, precisamos criar um controle universal de eventos, por exemplo, para evento de símbolo ou evento de conta, ou eventos de qualquer outra classe que será criada no futuro. Por essa razão, neste caso, é apropriado o controle de alterações no estado das propriedades do objeto: reais e inteiros — cada classe-herdeiro tem uma lista única que será em si um identificador de evento. Também precisaremos considerar a direção da mudança nas propriedades (aumento/diminuição do valor da propriedade), vamos chamá-la de causa do evento, e o valor da alteração na propriedade do objeto. Registraremos o identificador, a causa e a magnitude da alteração do evento numa classe simples do evento base do objeto e armazenaremos numa lista eventos que ocorrem simultaneamente.
Anteriormente, decidimos que, para enviar eventos ao programa, íamos enviar um evento com parâmetros estritamente especificados (identificador de evento, valor long, valor double e valor de string do evento), e enviávamos o tempo do evento em milissegundos ao parâmetro long. Agora, como mudado o conceito de como determinar eventos, precisamos determinar com precisão o evento de acordo com vários de seus parâmetros:

  1. Identificador do evento — propriedade do evento alterado. Cada objeto tem suas próprias propriedades, e num programa que não saiba nada em qual objeto alterada a propriedade e quais as propriedades desse objeto alteradas (inteiro ou real), não é possível determinar isso exclusivamente a partir do identificador de evento.
  2. Causa do evento — aumento ou diminuição no valor da propriedade ou do cruzamento do nível controlado. Esse valor também não nos permite determinar com precisão o evento. Mas graças ao identificador do evento e à sua causa, já podemos determinar o aumento/diminuição/cruzamento na propriedade de um objeto com um valor controlado predeterminado. Por isso, para uma identificação precisa do evento, precisamos indicar o identificador da classe no objeto em que o evento acontece.
    Um identificador da lista de coleções (indica precisamente que o objeto pertence a uma determinada classe), um símbolo, uma conta ou um objeto de coleção criado no futuro pode servir como identificador. Portanto, para o evento também deve ser enviado:
  3. Identificador de coleção — assim todos os três identificadores acima permitirão identificar exclusivamente o evento.
  4. Propriedade de string do evento — nome do objeto em que acontecido o evento.

Assim, vemos que, para determinar o evento, precisamos obter três parâmetros inteiros, mas, também, a hora do evento, transferida através de um valor long. Enquanto nós temos apenas uma propriedade long do evento. Como devemos proceder? A solução é simples, num parâmetro long transferiremos de vez três eventos inteiros com tipo ushort. O tipo long possui oito bytes e o tipo ushort, dois bytes. Portanto, num contêiner long, podemos armazenar três números ushort escritos em 0,1 bytes, 2,3 bytes, 4,5 bytes de um número long, e ainda temos mais dois bytes 6 e 7 para transferir outro valor ushort, se necessário mais tarde.
Para determinar a hora do evento, basta transmitir apenas milissegundos de tempo em 0 e 1 bytes do parâmetro long.

Assim, ao obter o evento, do parâmetro long extraímos três valores ushort para que possamos definir a hora do evento e obter dados adicionais a fim de identificar ao certo o evento segundo o identificador de evento, transferido como parâmetro ushort custom_event_id para EventChartCustom(), e gerar o identificador exato do evento acontecido, com ajuda do identificador de evento e de dois valores obtidos adicionalmente de lparam .

Para determinar os eventos nas propriedades do objeto herdeiro no objeto pai (no objeto base de todos os objetos da biblioteca), no temporizador verificaremos o estado atual de cada uma das propriedades do objeto e o compararemos com o estado anterior dessa propriedade. Antes de tudo, verificaremos se definido o valor cuja magnitude deve ser comparada com o valor da alteração da propriedade. Se o valor marcado não estiver definido (LONG_MAX está definido para ele), essa propriedade será ignorada.

Como verificarmos listas de propriedades de objetos com tipos diferentes (long e double), para armazenar o estado atual e anterior das propriedades do objeto, decidi que é mais conveniente usar arrays bidimensionais em vez de uma estrutura. Na primeira dimensão do array, serão armazenados os índices de propriedade do objeto e na segunda, os valores da propriedade cujo índice inserido na primeira dimensão, o valor da alteração da propriedade, os valores controlados e os sinalizadores de evento dessa propriedade.

Deixe-me explicar por que é mais conveniente usar arryas do que estruturas:
Não sabemos antecipadamente qual o tipo da propriedade que verificaremos, mas podemos entender seu tipo no índice da propriedade (as propriedades double do objeto sempre estão localizadas após as propriedades long), o que significa que não precisamos duplicar os campos na estrutura para valores long e double do mesmo valor verificado da propriedade do objeto. Simplesmente vamos registrar no array do tipo necessário (em conformidade com o tipo de propriedade definida pelo seu índice) todos os dados requeridos para controlar o estado da propriedade do objeto com o tipo certo, e não será preciso selecionar o campo da estrutura para inserir o valor transferido (long ou double).
Assim que determinada a alteração em qualquer uma das propriedades do objeto, nós a adicionamos à lista de eventos básicos do objeto (como a pesquisa é realizada no objeto base, o evento será básico e não deverá ser confundido com o evento da classe herdeira, que será determinada pela lista de eventos básicos e que será criada a partir de eventos básicos cujos ponteiros estão armazenados nela).

Nos métodos Refresh() de cada classe herdeira do objeto base, o temporizador verifica as listas de alterações de propriedades (listas de eventos básicos) e, se esses eventos estiverem nas listas de objetos, cada evento será convertido num evento de biblioteca e enviado ao programa de controle.

E para complementar, precisaremos criar métodos que nos permitam estabelecer programaticamente valores controlados de alterações para qualquer propriedade de qualquer objeto de biblioteca criado com base no objeto base. Sengo assim, a qualquer momento, podemos alterar rapidamente as condições necessárias para gerar eventos a partir dos objetos dos quais precisamos.

O conjunto de medidas tomadas hoje para modificar o objeto básico da biblioteca permitirá que não pensemos mais na criação de um controle de eventos para todos os objetos criados posteriormente, pois usaremos apenas a funcionalidade preparada hoje.

Então, vamos começar.

Como agora trabalharemos com eventos no objeto base dos objetos de biblioteca, precisamos criar uma enumeração das causas do evento para identificação.
No arquivo \MQL5\Include\DoEasy\ Defines.mqh depois das opções para escolha de tempo
escrevemos a enumeração de possíveis causas de eventos do objeto base:

//+------------------------------------------------------------------+
//| Possible options of selecting by time                            |
//+------------------------------------------------------------------+
enum ENUM_SELECT_BY_TIME
  {
   SELECT_BY_TIME_OPEN,                                     // By open time (in milliseconds)
   SELECT_BY_TIME_CLOSE,                                    // By close time (in milliseconds)
  };
//+------------------------------------------------------------------+
//| Possible event reasons of the object library base object         |
//+------------------------------------------------------------------+
enum ENUM_BASE_EVENT_REASON
  {
   BASE_EVENT_REASON_INC,                                   // Increase in the object property value
   BASE_EVENT_REASON_DEC,                                   // Decrease in the object property value
   BASE_EVENT_REASON_MORE_THEN,                             // Object property value exceeds the control value
   BASE_EVENT_REASON_LESS_THEN,                             // Object property value is less than the control value
   BASE_EVENT_REASON_EQUALS                                 // Object property value is equal to the control value
  };
//+------------------------------------------------------------------+

Como não precisaremos de sinalizadores de eventos, em vez de listas de sinalizadores de eventos de símbolo
escreveremos a lista de possíveis eventos de símbolos na janela Observação do Mercado:

//+------------------------------------------------------------------+
//| Data for working with symbols                                    |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| List of possible symbol events in the Market Watch window        |
//+------------------------------------------------------------------+
enum ENUM_MW_EVENT
  {
   MARKET_WATCH_EVENT_NO_EVENT = ACCOUNT_EVENTS_NEXT_CODE,  // No event
   MARKET_WATCH_EVENT_SYMBOL_ADD,                           // Adding a symbol to the Market Watch window
   MARKET_WATCH_EVENT_SYMBOL_DEL,                           // Removing a symbol from the Market Watch window
   MARKET_WATCH_EVENT_SYMBOL_SORT,                          // Sorting symbols in the Market Watch window
  };
#define SYMBOL_EVENTS_NEXT_CODE  (MARKET_WATCH_EVENT_SYMBOL_SORT+1)  // The code of the next event after the last symbol event code
//+------------------------------------------------------------------+

E removemos a lista de possíveis eventos do símbolo por ser desnecessária:

//+------------------------------------------------------------------+
//| List of possible symbol events                                   |
//+------------------------------------------------------------------+
enum ENUM_SYMBOL_EVENT
  {
   SYMBOL_EVENT_NO_EVENT = ACCOUNT_EVENTS_NEXT_CODE,        // No event
   SYMBOL_EVENT_MW_ADD,                                     // Adding a symbol to the Market Watch window
   SYMBOL_EVENT_MW_DEL,                                     // Removing a symbol from the Market Watch window
   SYMBOL_EVENT_MW_SORT,                                    // Sorting symbols in the Market Watch window
   SYMBOL_EVENT_TRADE_DISABLE,                              // Disable order execution
   SYMBOL_EVENT_TRADE_LONGONLY,                             // Allow buy only
   SYMBOL_EVENT_TRADE_SHORTONLY,                            // Allow sell only
   SYMBOL_EVENT_TRADE_CLOSEONLY,                            // Enable close only
   SYMBOL_EVENT_TRADE_FULL,                                 // No trading limitations
   SYMBOL_EVENT_SESSION_DEALS_INC,                          // The increase in the number of deals in the current session exceeds the specified value
   SYMBOL_EVENT_SESSION_DEALS_DEC,                          // The decrease in the number of deals in the current session exceeds the specified value
   SYMBOL_EVENT_SESSION_BUY_ORDERS_INC,                     // The increase in the total number of buy orders currently exceeds the specified value
   SYMBOL_EVENT_SESSION_BUY_ORDERS_DEC,                     // The decrease in the total number of buy orders currently exceeds the specified value
   SYMBOL_EVENT_SESSION_SELL_ORDERS_INC,                    // The increase in the total number of sell orders currently exceeds the specified value
   SYMBOL_EVENT_SESSION_SELL_ORDERS_DEC,                    // The decrease in the total number of sell orders currently exceeds the specified value
   SYMBOL_EVENT_VOLUME_INC,                                 // Volume increase in the last deal exceeds the specified value
   SYMBOL_EVENT_VOLUME_DEC,                                 // Volume decrease in the last deal exceeds the specified value
   SYMBOL_EVENT_VOLUME_HIGH_DAY_INC,                        // The increase in the maximum volume per day exceeds the specified value
   SYMBOL_EVENT_VOLUME_HIGH_DAY_DEC,                        // The decrease in the maximum volume per day exceeds the specified value
   SYMBOL_EVENT_VOLUME_LOW_DAY_INC,                         // The increase in the minimum volume per day exceeds the specified value
   SYMBOL_EVENT_VOLUME_LOW_DAY_DEC,                         // The decrease in the minimum volume per day exceeds the specified value
   SYMBOL_EVENT_SPREAD_INC,                                 // The increase in a spread exceeds the specified change
   SYMBOL_EVENT_SPREAD_DEC,                                 // The decrease in a spread exceeds the specified change
   SYMBOL_EVENT_STOPLEVEL_INC,                              // The increase of a Stop order level exceeds the specified value
   SYMBOL_EVENT_STOPLEVEL_DEC,                              // The decrease of a Stop order level exceeds the specified value
   SYMBOL_EVENT_FREEZELEVEL_INC,                            // The increase in the freeze level exceeds the specified value
   SYMBOL_EVENT_FREEZELEVEL_DEC,                            // The decrease in the freeze level exceeds the specified value
   SYMBOL_EVENT_BID_LAST_INC,                               // The increase in the Bid or Last price exceeds the specified value
   SYMBOL_EVENT_BID_LAST_DEC,                               // The decrease in the Bid or Last price exceeds the specified value
   SYMBOL_EVENT_BID_LAST_HIGH_INC,                          // The increase in the maximum Bid or Last price per day exceeds the specified value
   SYMBOL_EVENT_BID_LAST_HIGH_DEC,                          // The decrease in the maximum Bid or Last price per day exceeds the specified value relative to the specified price
   SYMBOL_EVENT_BID_LAST_LOW_INC,                           // The increase in the minimum Bid or Last price per day exceeds the specified value relative to the specified price
   SYMBOL_EVENT_BID_LAST_LOW_DEC,                           // The decrease in the minimum Bid or Last price per day exceeds the specified value
   SYMBOL_EVENT_ASK_INC,                                    // The increase in the Ask price exceeds the specified value
   SYMBOL_EVENT_ASK_DEC,                                    // The decrease in the Ask price exceeds the specified value
   SYMBOL_EVENT_ASK_HIGH_INC,                               // The increase in the maximum Ask price per day exceeds the specified value
   SYMBOL_EVENT_ASK_HIGH_DEC,                               // The decrease in the maximum Ask price per day exceeds the specified value
   SYMBOL_EVENT_ASK_LOW_INC,                                // The increase in the minimum Ask price per day exceeds the specified value
   SYMBOL_EVENT_ASK_LOW_DEC,                                // The decrease in the minimum Ask price per day exceeds the specified value
   SYMBOL_EVENT_VOLUME_REAL_DAY_INC,                        // The increase in the real volume per day exceeds the specified value
   SYMBOL_EVENT_VOLUME_REAL_DAY_DEC,                        // The decrease in the real volume per day exceeds the specified value
   SYMBOL_EVENT_VOLUME_HIGH_REAL_DAY_INC,                   // The increase in the maximum real volume per day exceeds the specified value
   SYMBOL_EVENT_VOLUME_HIGH_REAL_DAY_DEC,                   // The decrease in the maximum real volume per day exceeds the specified value
   SYMBOL_EVENT_VOLUME_LOW_REAL_DAY_INC,                    // The increase in the minimum real volume per day exceeds the specified value
   SYMBOL_EVENT_VOLUME_LOW_REAL_DAY_DEC,                    // The decrease in the minimum real volume per day exceeds the specified value
   SYMBOL_EVENT_OPTION_STRIKE_INC,                          // The increase in the strike price exceeds the specified value
   SYMBOL_EVENT_OPTION_STRIKE_DEC,                          // The decrease in the strike price exceeds the specified value
   SYMBOL_EVENT_VOLUME_LIMIT_INC,                           // The increase in the maximum available total position volume and pending orders in one direction
   SYMBOL_EVENT_VOLUME_LIMIT_DEC,                           // The decrease in the maximum available total position volume and pending orders in one direction
   SYMBOL_EVENT_SWAP_LONG_INC,                              // The increase in the swap long
   SYMBOL_EVENT_SWAP_LONG_DEC,                              // The decrease in the swap long
   SYMBOL_EVENT_SWAP_SHORT_INC,                             // The increase in the swap short
   SYMBOL_EVENT_SWAP_SHORT_DEC,                             // The decrease in the swap short
   SYMBOL_EVENT_SESSION_VOLUME_INC,                         // The increase in the total volume of deals in the current session exceeds the specified value
   SYMBOL_EVENT_SESSION_VOLUME_DEC,                         // The decrease in the total volume of deals in the current session exceeds the specified value
   SYMBOL_EVENT_SESSION_TURNOVER_INC,                       // The increase in the total turnover in the current session exceeds the specified value
   SYMBOL_EVENT_SESSION_TURNOVER_DEC,                       // The decrease in the total turnover in the current session exceeds the specified value
   SYMBOL_EVENT_SESSION_INTEREST_INC,                       // The increase in the total volume of open positions in the current session exceeds the specified value
   SYMBOL_EVENT_SESSION_INTEREST_DEC,                       // The decrease in the total volume of open positions in the current session exceeds the specified value
   SYMBOL_EVENT_SESSION_BUY_ORD_VOLUME_INC,                 // The increase in the total volume of buy orders exceeds the specified value
   SYMBOL_EVENT_SESSION_BUY_ORD_VOLUME_DEC,                 // The decrease in the total volume of buy orders exceeds the specified value
   SYMBOL_EVENT_SESSION_SELL_ORD_VOLUME_INC,                // The increase in the total volume of sell orders exceeds the specified value
   SYMBOL_EVENT_SESSION_SELL_ORD_VOLUME_DEC,                // The decrease in the total volume of sell orders exceeds the specified value
   SYMBOL_EVENT_SESSION_OPEN_INC,                           // The increase in the session open price exceeds the specified value relative to the specified price
   SYMBOL_EVENT_SESSION_OPEN_DEC,                           // The decrease in the session open price exceeds the specified value relative to the specified price
   SYMBOL_EVENT_SESSION_CLOSE_INC,                          // The increase in the session close price exceeds the specified value relative to the specified price
   SYMBOL_EVENT_SESSION_CLOSE_DEC,                          // The decrease in the session close price exceeds the specified value relative to the specified price
   SYMBOL_EVENT_SESSION_AW_INC,                             // The increase in the average weighted session price exceeds the specified value
   SYMBOL_EVENT_SESSION_AW_DEC,                             // The decrease in the average weighted session price exceeds the specified value
  };
#define SYMBOL_EVENTS_NEXT_CODE       (SYMBOL_EVENT_SESSION_AW_DEC+1)   // The code of the next event after the last symbol event code
//+------------------------------------------------------------------+

O código do próximo evento foi mudado para o valor seguinte ao da constante MARKET_WATCH_EVENT_SYMBOL_SORT da enumeração ENUM_MW_EVENT.

Agora vamos implementar a funcionalidade planejada.

No arquivo do objeto base \MQL5\Include\DoEasy\Objects\BaseObj.mqh escrevemos a nova classe do evento base:

//+------------------------------------------------------------------+
//| Library object's base event class                                |
//+------------------------------------------------------------------+
class CBaseEvent : public CObject
  {
private:
   ENUM_BASE_EVENT_REASON  m_reason;  
   int                     m_event_id;
   double                  m_value;   
public:
   ENUM_BASE_EVENT_REASON  Reason(void)   const { return this.m_reason;    }
   int                     ID(void)       const { return this.m_event_id;  }
   double                  Value(void)    const { return this.m_value;     }
//--- Constructor
                           CBaseEvent(const int event_id,const ENUM_BASE_EVENT_REASON reason,const double value) : m_reason(reason),
                                                                                                                   m_event_id(event_id),
                                                                                                                   m_value(value){}
//--- Comparison method to search for identical event objects
   virtual int             Compare(const CObject *node,const int mode=0) const 
                             {   
                              const CBaseEvent *compared=node;
                              return
                                (
                                 this.Reason()>compared.Reason()  ?  1  :
                                 this.Reason()<compared.Reason()  ? -1  :
                                 this.ID()>compared.ID()          ?  1  :
                                 this.ID()<compared.ID()          ? -1  : 0
                                );
                             } 
  };
//+------------------------------------------------------------------+

Na seção privada da classe, existem variáveis para armazenamento das causas do evento, do ID do evento (corresponde ao valor do índice da propriedade alterada do objeto) e da magnitude da mudança na propriedade do evento.
A seção pública da classe contém métodos para retornar as variáveis-membro da classe mencionadas acima.
Aos parâmetros formais dentro do construtor da classe são transferidos os valores destas propriedades, e imediatamente na lista de inicialização às variáveis-membros de classe correspondentes são atribuídos os valores transferidos.
Também a classe tem o método para comparar dois objetos de classe para busca na lista de ponteiros dinâmicos para objetos, sobre a qual falamos mais de uma vez.

Como vamos armazenar a lista de propriedades controladas dos objetos em arrays de dois dimensões, adicionamos substituição de macros indicando o tamanho da segunda dimensão dos arrays, e na seção privada da classe declaramos dois variáveis, nas quais armazenaremos o número de propriedades inteiras e reais do objeto, que será herdado da classe em questão (afinal, a classe base não sabe nada sobre as quantidades de propriedades que possuem seus descendentes e essas quantidades precisarão ser especificadas explicitamente). E declaramos um método para preencher arrays de propriedades e para procurar alterações nas propriedades de objetos herdados.

//+------------------------------------------------------------------+
//| Base object class for all library objects                        |
//+------------------------------------------------------------------+
#define  CONTROLS_TOTAL    (10)
class CBaseObj : public CObject
  {
private:
   int               m_long_prop_total;  
   int               m_double_prop_total;
   //--- Fill in the object property array
   template<typename T> bool  FillPropertySettings(const int index,T &array[][CONTROLS_TOTAL],T &array_prev[][CONTROLS_TOTAL],int &event_id);
protected:


Na seção protegida da classe declaramos uma lista para armazenar ponteiros para instâncias dos do objeto, uma variável para armazenar o ID do evento, o sinalizador da primeira inicialização e uma variável para armazenar o tipo de objeto herdado.
Também adicionamos quatro arrays bidimensionais para armazenar propriedades e controlar suas alterações (as propriedades inteiras e reais - atuais e anteriores - do objeto herdeiro) e o método que retorna apenas milissegundos armazenados na hora do evento (para MQL4, retornamos 0 e para MQL5, o restante da divisão por 1000 valores de tempo long).
Como a classe base não sabe nada sobre o número de propriedades de objetos herdeiros e os tamanhos precisam ser definidos a partir de classes herdeiras (onde são conhecidos), declararemos métodos de definição e verificação de tamanhos de array:

protected:
   CArrayObj         m_list_events_base;                       // Object base event list
   CArrayObj         m_list_events;                            // Object event list
   MqlTick           m_tick;                                   // Tick structure for receiving quote data
   double            m_hash_sum;                               // Object data hash sum
   double            m_hash_sum_prev;                          // Object data hash sum during the previous check
   int               m_digits_currency;                        // Number of decimal places in an account currency
   int               m_global_error;                           // Global error code
   long              m_chart_id;                               // Control program chart ID
   bool              m_is_event;                               // Object event flag
   int               m_event_code;                             // Object event code
   int               m_event_id;                               // Event ID (equal to the object property value)
   string            m_name;                                   // Object name
   string            m_folder_name;                            // Name of the folder storing CBaseObj descendant objects 
   bool              m_first_start;                            // First launch flag
   int               m_type;                                   // Object type (corresponds to the collection IDs)
//--- Data in the array cells
//--- Data for storing, controlling and returning tracked properties:
//--- [Property index][0] Controlled property increase value
//--- [Property index][1] Controlled property decrease value
//--- [Property index][2] Controlled property value level
//--- [Property index][3] Property value
//--- [Property index][4] Property value change
//--- [Property index][5] Flag of a property change exceeding the increase value
//--- [Property index][6] Flag of a property change exceeding the decrease value
//--- [Property index][7] Flag of a property increase exceeding the control level
//--- [Property index][8] Flag of a property decrease being less than the control level
//--- [Property index][9] Flag of a property value being equal to the control level
   long              m_long_prop_event[][CONTROLS_TOTAL];         // The array for storing object's integer properties values and controlled property change values
   double            m_double_prop_event[][CONTROLS_TOTAL];       // The array for storing object's real properties values and controlled property change values
   long              m_long_prop_event_prev[][CONTROLS_TOTAL];    // The array for storing object's controlled integer properties values during the previous check
   double            m_double_prop_event_prev[][CONTROLS_TOTAL];  // The array for storing object's controlled real properties values during the previous check

//--- Return (1) time in milliseconds, (2) milliseconds from the MqlTick time value
   long              TickTime(void)                            const { return #ifdef __MQL5__ this.m_tick.time_msc #else this.m_tick.time*1000 #endif ;  }
   ushort            MSCfromTime(const long time_msc)          const { return #ifdef __MQL5__ ushort(this.TickTime()%1000) #else 0 #endif ;              }
//--- return the flag of the event code presence in the event object
   bool              IsPresentEventFlag(const int change_code) const { return (this.m_event_code & change_code)==change_code; }
//--- Return the number of decimal places of the account currency
   int               DigitsCurrency(void)                      const { return this.m_digits_currency; }
//--- Returns the number of decimal places in the 'double' value
   int               GetDigits(const double value)             const;

//--- Set the size of the array of controlled (1) integer and (2) real object properties
   bool              SetControlDataArraySizeLong(const int size);    
   bool              SetControlDataArraySizeDouble(const int size);
//--- Check the array size of object properties
   bool              CheckControlDataArraySize(bool check_long=true);

//--- Set the (1) controlled value and (2) object property change value
   template<typename T> void  SetControlledValue(const int property,const T value);
   template<typename T> void  SetControlledChangedValue(const int property,const T value);

//--- Set the value of the pbject property controlled (1) increase, (2) decrease, (3) control level
   template<typename T> void  SetControlledValueINC(const int property,const T value);
   template<typename T> void  SetControlledValueDEC(const int property,const T value);
   template<typename T> void  SetControlledValueLEVEL(const int property,const T value);

//--- Set the flag of a property change exceeding the (1) increase and (2) decrease values
   template<typename T> void  SetControlledFlagINC(const int property,const T value);
   template<typename T> void  SetControlledFlagDEC(const int property,const T value);
//--- Set the flag of a property change (1) exceeding, (2) being less than the control level, (3) being equal to the level
   template<typename T> void  SetControlledFlagMORE(const int property,const T value);
   template<typename T> void  SetControlledFlagLESS(const int property,const T value);
   template<typename T> void  SetControlledFlagEQUAL(const int property,const T value);

//--- Return the set value of the controlled (1) integer and (2) real object properties increase
   long              GetControlledValueLongINC(const int property)         const { return this.m_long_prop_event[property][0];                           }
   double            GetControlledValueDoubleINC(const int property)       const { return this.m_double_prop_event[property-this.m_long_prop_total][0];  }
//--- Return the set value of the controlled (1) integer and (2) real object properties decrease
   long              GetControlledValueLongDEC(const int property)         const { return this.m_long_prop_event[property][1];                           }
   double            GetControlledValueDoubleDEC(const int property)       const { return this.m_double_prop_event[property-this.m_long_prop_total][1];  }
//--- Return the control level of object's (1) integer and (2) real properties
   long              GetControlledValueLongLEVEL(const int property)       const { return this.m_long_prop_event[property][2];                           }
   double            GetControlledValueDoubleLEVEL(const int property)     const { return this.m_double_prop_event[property-this.m_long_prop_total][2];  }
//--- Return the value of the object (1) integer and (2) real property
   long              GetControlledValueLong(const int property)            const { return this.m_long_prop_event[property][3];                           }
   double            GetControlledValueDouble(const int property)          const { return this.m_double_prop_event[property-this.m_long_prop_total][3];  }
//--- Return the change value of the controlled (1) integer and (2) real object property
   long              GetControlledChangedValueLong(const int property)     const { return this.m_long_prop_event[property][4];                           }
   double            GetControlledChangedValueDouble(const int property)   const { return this.m_double_prop_event[property-this.m_long_prop_total][4];  }

//--- Return the flag of an (1) integer and (2) real property value change exceeding the increase value
   long              GetControlledFlagLongINC(const int property)          const { return this.m_long_prop_event[property][5];                           }
   double            GetControlledFlagDoubleINC(const int property)        const { return this.m_double_prop_event[property-this.m_long_prop_total][5];  }
//--- Return the flag of an (1) integer and (2) real property value change exceeding the decrease value
   long              GetControlledFlagLongDEC(const int property)          const { return this.m_long_prop_event[property][6];                           }
   double            GetControlledFlagDoubleDEC(const int property)        const { return this.m_double_prop_event[property-this.m_long_prop_total][6];  }
//--- Return the flag of an (1) integer and (2) real property value increase exceeding the control level
   long              GetControlledFlagLongMORE(const int property)         const { return this.m_long_prop_event[property][7];                           }
   double            GetControlledFlagDoubleMORE(const int property)       const { return this.m_double_prop_event[property-this.m_long_prop_total][7];  }
//--- Return the flag of an (1) integer and (2) real property value decrease being less than the control level
   long              GetControlledFlagLongLESS(const int property)         const { return this.m_long_prop_event[property][8];                           }
   double            GetControlledFlagDoubleLESS(const int property)       const { return this.m_double_prop_event[property-this.m_long_prop_total][8];  }
//--- Return the flag of an (1) integer and (2) real property being equal to the control level
   long              GetControlledFlagLongEQUAL(const int property)        const { return this.m_long_prop_event[property][9];                           }
   double            GetControlledFlagDoubleEQUAL(const int property)      const { return this.m_double_prop_event[property-this.m_long_prop_total][9];  }
   
//--- (1) Pack a 'ushort' number to a passed 'long' number
//--- (2) convert a 'ushort' value to a specified 'long' number byte
   long              UshortToLong(const ushort ushort_value,const uchar index,long &long_value);
   long              UshortToByte(const ushort value,const uchar index) const;
public:

Aqui, na seção privada da classe, são declarados métodos para definir e retornar propriedades controladas e seus valores, bem como métodos para empacotamento do número ushort em bytes especificados de um contêiner long segundo o índice. (Índice 0 => bytes 0-1, Índice 1 => bytes 2-3, Índice 2 => bytes 4-5)

Na seção pública da classe, declaramos os métodos para redefinir valores de propriedades mutáveis e valores das propriedades controladas do objeto, método para adicionar um evento base à lista, método de obter o objeto base de uma lista por índice, método que retorna o número de na lista, método virtual retornando o tipo de um objeto e método que retorna a descrição de string do evento base:

public:
//--- Reset the variables of (1) tracked and (2) controlled object data (can be reset in the descendants)
   void              ResetChangesParams(void); 
   virtual void      ResetControlsParams(void);
//--- Add the (1) object event and (2) the object event reason to the list
   bool              EventAdd(const ushort event_id,const long lparam,const double dparam,const string sparam);
   bool              EventBaseAdd(const int event_id,const ENUM_BASE_EVENT_REASON reason,const double value);
//--- Return the occurred event flag to the object data
   bool              IsEvent(void)                             const { return this.m_is_event;                 }
//--- Return (1) the list of events, (2) the object event code and (3) the global error code
   CArrayObj        *GetListEvents(void)                             { return &this.m_list_events;             }
   int               GetEventCode(void)                        const { return this.m_event_code;               }
   int               GetError(void)                            const { return this.m_global_error;             }
//--- Return (1) an event object and (2) a base event by its number in the list
   CEventBaseObj    *GetEvent(const int shift=WRONG_VALUE,const bool check_out=true);
   CBaseEvent       *GetEventBase(const int index);
//--- Return the number of (1) object events
   int               GetEventsTotal(void)                      const { return this.m_list_events.Total();      }
//--- (1) Set and (2) return the chart ID of the control program
   void              SetChartID(const long id)                       { this.m_chart_id=id;                     }
   long              GetChartID(void)                          const { return this.m_chart_id;                 }
//--- (1) Set the sub-folder name, (2) return the folder name for storing descendant object files
   void              SetSubFolderName(const string name)             { this.m_folder_name=DIRECTORY+name;      }
   string            GetFolderName(void)                       const { return this.m_folder_name;              }
//--- Return the object name
   string            GetName(void)                             const { return this.m_name;                     }
//--- Update the object data to search for changes (Calling from the descendants: CBaseObj::Refresh())
   virtual void      Refresh(void);
//--- Return an object type
   virtual int       Type(void)                                const { return this.m_type;                     }
//--- Return an object event description
   string            EventDescription(const int property,
                                      const ENUM_BASE_EVENT_REASON reason,
                                      const int source,
                                      const string value,
                                      const string property_descr,
                                      const int digits);
//--- Constructor
                     CBaseObj();
  };
//+------------------------------------------------------------------+

Agora, revisaremos brevemente todos os métodos declarados acima.

Construtor de classe:

//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CBaseObj::CBaseObj() : m_global_error(ERR_SUCCESS),
                       m_hash_sum(0),m_hash_sum_prev(0),
                       m_is_event(false),m_event_code(WRONG_VALUE),
                       m_chart_id(::ChartID()),
                       m_folder_name(DIRECTORY),
                       m_name(__FUNCTION__),
                       m_long_prop_total(0),
                       m_double_prop_total(0),
                       m_first_start(true)
  {
   ::ArrayResize(this.m_long_prop_event,0,100);       
   ::ArrayResize(this.m_double_prop_event,0,100);     
   ::ArrayResize(this.m_long_prop_event_prev,0,100);  
   ::ArrayResize(this.m_double_prop_event_prev,0,100);
   ::ZeroMemory(this.m_tick);
   this.m_digits_currency=(#ifdef __MQL5__ (int)::AccountInfoInteger(ACCOUNT_CURRENCY_DIGITS) #else 2 #endif);
   this.m_list_events.Clear();
   this.m_list_events.Sort();
   this.m_list_events_base.Clear();
   this.m_list_events_base.Sort();
  }
//+------------------------------------------------------------------+

Neste caso, como agora não temos códigos dos eventos que coletamos anteriormente a partir de sinalizadores e nos quais o valor zero indicava a ausência de evento, precisamos substituir a inicialização do código de evento na lista de inicialização da classe por uma diferente de zero (já que zero indica um evento da primeira propriedade na enumeração das propriedades inteiras de objeto, mais de zero indica os seguintes eventos na lista de propriedades de objeto e quantos deles não são conhecidos na classe pai), definimos o valor do código do evento como -1.
Inicializamos o nome do objeto com o nome da classe (o nome é reatribuído nos herdeiros), iniciamos o número de propriedades de inteiras e reais do objeto herdeiro com valores nulos e definimos o sinalizador da primeira inicialização.
No corpo da classe definimos os tamanhos de arrays de propriedades inteiras e reais como zero, limpamos a lista de eventos básicos e definimos o sinalizador da lista classificada.

A implementação do método virtual Refresh() da classe, que declaramos anteriormente, foi atribuída às classes herdeiras. Agora, criaremos uma implementação desse método para a classe do objeto base, assim, nela, rastrearemos as alterações nas propriedades dos objetos herdeiros e, ao definir um evento, criaremos eventos básicos e os adicionaremos à lista de eventos básicos para processamento subsequente e criação de eventos de objetos para enviá-los ao programa:

//+------------------------------------------------------------------+
//| Update the object data to search changes in them                 |
//| Call from descendants: CBaseObj::Refresh()                       |
//+------------------------------------------------------------------+
void CBaseObj::Refresh(void)
  {
//--- Check the size of the arrays, Exit if it is zero
   if(!this.CheckControlDataArraySize() || !this.CheckControlDataArraySize(false))
      return;
//--- Reset the event flag and clear all lists
   this.m_is_event=false;
   this.m_list_events.Clear();
   this.m_list_events.Sort();
   this.m_list_events_base.Clear();
   this.m_list_events_base.Sort();
//--- Fill in the array of integer properties and control their changes
   for(int i=0;i<this.m_long_prop_total;i++)
      if(!this.FillPropertySettings(i,this.m_long_prop_event,this.m_long_prop_event_prev,this.m_event_id))
         continue;
//--- Fill in the array of real properties and control their changes
   for(int i=0;i<this.m_double_prop_total;i++)
      if(!this.FillPropertySettings(i,this.m_double_prop_event,this.m_double_prop_event_prev,this.m_event_id))
         continue;
//--- First launch
   if(this.m_first_start)
     {
      ::ArrayCopy(this.m_long_prop_event_prev,this.m_long_prop_event);
      ::ArrayCopy(this.m_double_prop_event_prev,this.m_double_prop_event);
      this.m_hash_sum_prev=this.m_hash_sum;
      this.m_first_start=false;
      this.m_is_event=false;
      this.m_list_events_base.Clear();
      this.m_list_events_base.Sort();
      return;
     }
  }
//+------------------------------------------------------------------+

Aqui, todas as ações estão escritas nos comentários do código e tudo o que é feito no método — são limpas preliminarmente as listas de eventos, são chamados os métodos para preencher os arrays de propriedades inteiras e reais do objeto herdeiro e são verificadas suas alterações.
Se esta for a primeira inicialização, o estado atual dos arrays de propriedades é copiado para o estado anterior (para que não haja diferença entre eles e, portanto, não haja log de eventos), é redefinido o sinalizador da primeira inicialização e é limpa a lista de eventos básicos que já podem ter sido criados ao chamar métodos FillPropertySettings().

Implementação do método que preenche os arrays de propriedades do objeto herdeiro e que controla suas alterações:

//+------------------------------------------------------------------+
//| Fill in the object property array                                |
//+------------------------------------------------------------------+
template<typename T> bool CBaseObj::FillPropertySettings(const int index,T &array[][CONTROLS_TOTAL],T &array_prev[][CONTROLS_TOTAL],int &event_id)
  {
   //--- Data in the array cells
   //--- [Property index][0] Controlled property increase value
   //--- [Property index][1] Controlled property decrease value
   //--- [Property index][2] Controlled property value level
   //--- [Property index][3] Property value
   //--- [Property index][4] Property value change
   //--- [Property index][5] Flag of a property change exceeding the increase value
   //--- [Property index][6] Flag of a property change exceeding the decrease value
   //--- [Property index][7] Flag of a property increase exceeding the control level
   //--- [Property index][8] Flag of a property decrease being less than the control level
   //--- [Property index][9] Flag of a property value being equal to the control level
   
   //--- If controlled values are not set, exit with 'false'
   if(this.m_first_start)
      return false;
   //--- Set the shift of the 'double' property index and the event ID
   event_id=index+(typename(T)=="double" ? this.m_long_prop_total : 0);
   //--- Reset all event flags
   for(int j=5;j<CONTROLS_TOTAL;j++)
      array[index][j]=false;
   //--- Property change value
   T value=array[index][3]-array_prev[index][3];
   array[index][4]=value;
   //--- If the controlled property increase value is set
   if(array[index][0]<LONG_MAX)
     {
      //--- If the property change value exceeds the controlled increase value - there is an event,
      //--- add the event to the list, set the flag and save the new property value size
      if(value>0 && value>array[index][0])
        {
         if(this.EventBaseAdd(event_id,BASE_EVENT_REASON_INC,value))
           {
            array[index][5]=true;
            array_prev[index][4]=value;
           }
        }
     }
   //--- If the controlled property decrease value is set
   if(array[index][1]<LONG_MAX)
     {
      //--- If the property change value exceeds the controlled decrease value - there is an event,
      //--- add the event to the list, set the flag and save the new property value size
      if(value<0 && fabs(value)>array[index][1])
        {
         if(this.EventBaseAdd(event_id,BASE_EVENT_REASON_DEC,value))
           {
            array[index][6]=true;
            array_prev[index][4]=value;
           }
        }
     }
   //--- If the controlled level value is set
   if(array[index][2]<LONG_MAX)
     {
      value=array[index][3]-array[index][2];
      //--- If a property value exceeds the control level, there is an event
      //--- add the event to the list and set the flag
      if(value>0 && array_prev[index][3]<=array[index][2])
        {
         if(this.EventBaseAdd(event_id,BASE_EVENT_REASON_MORE_THEN,array[index][2]))
            array[index][7]=true;
        }
      //--- If a property value is less than the control level, there is an event,
      //--- add the event to the list and set the flag
      else if(value<0 && array_prev[index][3]>=array[index][2])
        {
         if(this.EventBaseAdd(event_id,BASE_EVENT_REASON_LESS_THEN,array[index][2]))
            array[index][8]=true;
        }
      //--- If a property value is equal to the control level, there is an event,
      //--- add the event to the list and set the flag
      else if(value==0 && array_prev[index][3]!=array[index][2])
        {
         if(this.EventBaseAdd(event_id,BASE_EVENT_REASON_EQUALS,array[index][2]))
            array[index][9]=true;
        }
     }
   //--- Save the current property value as a previous one
   array_prev[index][3]=array[index][3];
   return true;
  }
//+------------------------------------------------------------------+

Aqui, todas as ações estão explicitadas nos comentários do código. A única coisa que quero explicar é a seleção do deslocamento do índice da propriedade double do objeto para obter o ID do evento. Como temos as propriedades reais de todos os objetos após as propriedades inteiras, o início da primeira propriedade real é igual ao número de propriedades inteiras (se o número de propriedades long for três, a primeira propriedade do material terá o índice 3 (0,1,2, 3)). Em nossos arrays, a contagem regressiva começa, naturalmente, do zero. Portanto, no caso de trabalhar com propriedades double, nós precisamos adicionar ao índice do array o número de propriedades inteiras do objeto.

Métodos para definir os tamanhos arrays de propriedades inteiras e reais de objetos herdeiros:

//+------------------------------------------------------------------+
//| Set the size of the arrays of the object integer properties      |
//+------------------------------------------------------------------+
bool CBaseObj::SetControlDataArraySizeLong(const int size)
  {
   int x=(#ifdef __MQL4__ CONTROLS_TOTAL #else 1 #endif );
   this.m_long_prop_total=::ArrayResize(this.m_long_prop_event,size,100)/x;
   return((::ArrayResize(this.m_long_prop_event_prev,size,100)/x)==size && this.m_long_prop_total==size ? true : false);
  }
//+------------------------------------------------------------------+
//| Set the size of the arrays of the object real properties         |
//+------------------------------------------------------------------+
bool CBaseObj::SetControlDataArraySizeDouble(const int size)
  {
   int x=(#ifdef __MQL4__ CONTROLS_TOTAL #else 1 #endif );
   this.m_double_prop_total=::ArrayResize(this.m_double_prop_event,size,100)/x;
   return((::ArrayResize(this.m_double_prop_event_prev,size,100)/x)==size && this.m_double_prop_total==size ? true : false);
  }
//+------------------------------------------------------------------+

Os métodos retornam o resultado do redimensionamento de arrays pelo valor passado ao método.

Ressalto uma particularidade de redimensionar um array multidimensional em MQL4. A função ArrayResize() em MQL4 retorna o tamanho total de todas as dimensões do array. Em MQL5, é o tamanho da primeira dimensão que muda. Por exemplo, se nossa segunda dimensão tiver um tamanho igual a dois, ao redimensionar a primeira dimensão do array para 10, a função retornará 20, o que não é lógico (apenas redimensionamos a primeira dimensão). Mas em MQL5, a função retorna o valor correto, assim, para o exemplo acima, retornará 10, conforme o esperado.

Por isso, nos métodos foi criado um divisor pelo qual em MQL4 é preciso dividir o valor retornado pela função pelo tamanho da segunda dimensão.

Método para verificar o tamanho de um array de propriedades inteiras ou reais de um objeto herdeiro:

//+------------------------------------------------------------------+
//| Check the array size of object properties                        |
//+------------------------------------------------------------------+
bool CBaseObj::CheckControlDataArraySize(bool check_long=true)
  {
   string txt1="";
   string txt2="";
   string txt3="";
   string txt4="";
   bool res=true;
   if(check_long)
     {
      if(this.m_long_prop_total==0)
        {
         txt1=TextByLanguage("Массив данных контролируемых integer-свойств имеет нулевой размер","Controlled integer properties data array has zero size");
         txt2=TextByLanguage("Необходимо сначала установить размер массива равным количеству integer-свойств объекта","You should first set size of array equal to number of object integer properties");
         txt3=TextByLanguage("Для этого используйте метод CBaseObj::SetControlDataArraySizeLong()","To do this, use CBaseObj::SetControlDataArraySizeLong() method");
         txt4=TextByLanguage("со значением количества integer-свойств объекта в параметре \"size\"","with value of number of integer properties of object in \"size\" parameter");
         res=false;
        }
     }
   else
     {
      if(this.m_double_prop_total==0)
        {
         txt1=TextByLanguage("Массив данных контролируемых double-свойств имеет нулевой размер","Controlled double properties data array has zero size");
         txt2=TextByLanguage("Необходимо сначала установить размер массива равным количеству double-свойств объекта","You should first set size of array equal to number of object double properties");
         txt3=TextByLanguage("Для этого используйте метод CBaseObj::SetControlDataArraySizeDouble()","To do this, use CBaseObj::SetControlDataArraySizeDouble() method");
         txt4=TextByLanguage("со значением количества double-свойств объекта в параметре \"size\"","with value of number of double properties of object in \"size\" parameter");
         res=false;
        }
     }
   if(res)
      return true;
   #ifdef __MQL5__ 
      ::Print(DFUN,"\n",txt1,"\n",txt2,"\n",txt3,"\n",txt4);
   #else 
      ::Print(DFUN);
      ::Print(txt1);
      ::Print(txt2);
      ::Print(txt3);
      ::Print(txt4);
   #endif 
   this.m_global_error=ERR_ZEROSIZE_ARRAY;
   return false;
  }
//+------------------------------------------------------------------+

Para o método é transferido o sinalizador indicando o tamanho da matriz verificada.
Se true , é verificada um array de propriedades long, se false , um array de propriedades double.

Se o tamanho do array a ser verificado não estiver definido, é gerada uma mensagem que será exibida no log e será retornado false. Se o tamanho do array já estiver definido, será retornado true.

Métodos para redefinir os valores monitorados e alterar os valores dos dados monitorados das propriedades do objeto:

//+------------------------------------------------------------------+
//| Reset the variables of controlled object data values             |
//+------------------------------------------------------------------+
void CBaseObj::ResetControlsParams(void)
  {
   if(!this.CheckControlDataArraySize(true) || !this.CheckControlDataArraySize(false))
      return;
//--- Data in the array cells
//--- [Property index][0] Controlled property increase value
//--- [Property index][1] Controlled property decrease value
//--- [Property index][2] Controlled property value level
   for(int i=this.m_long_prop_total-1;i>WRONG_VALUE;i--)
      for(int j=0; j<3; j++)
         this.m_long_prop_event[i][j]=LONG_MAX;
   for(int i=this.m_double_prop_total-1;i>WRONG_VALUE;i--)
      for(int j=0; j<3; j++)
         this.m_double_prop_event[i][j]=(double)LONG_MAX;
  }
//+------------------------------------------------------------------+
//| Reset the variables of tracked object data                       |
//+------------------------------------------------------------------+
void CBaseObj::ResetChangesParams(void)
  {
   if(!this.CheckControlDataArraySize(true) || !this.CheckControlDataArraySize(false))
      return;
   this.m_list_events.Clear();
   this.m_list_events.Sort();
   this.m_list_events_base.Clear();
   this.m_list_events_base.Sort();
//--- Data in the array cells
//--- [Property index][3] Property value
//--- [Property index][4] Property value change
//--- [Property index][5] Flag of a property change exceeding the increase value
//--- [Property index][6] Flag of a property change exceeding the decrease value
//--- [Property index][7] Flag of a property increase exceeding the control level
//--- [Property index][8] Flag of a property decrease being less than the control level
//--- [Property index][9] Flag of a property value being equal to the controlled value
   for(int i=this.m_long_prop_total-1;i>WRONG_VALUE;i--)
      for(int j=3; j<CONTROLS_TOTAL; j++)
         this.m_long_prop_event[i][j]=(j<5 ? LONG_MAX : 0);
   for(int i=this.m_double_prop_total-1;i>WRONG_VALUE;i--)
      for(int j=3; j<CONTROLS_TOTAL; j++)
         this.m_double_prop_event[i][j]=(j<5 ? (double)LONG_MAX : 0);
  }
//+------------------------------------------------------------------+

Nos métodos de dois ciclos, segundo os arrays de propriedades inteiras e reais do objeto herdeiro, são definidos os valores de inicialização para as células necessárias da segunda dimensão de arrays. As células inicializadas são explicitadas nos comentários do código.

Método que adiciona o evento base à lista de do objeto:

//+------------------------------------------------------------------+
//| Add the object base event to the list                            |
//+------------------------------------------------------------------+
bool CBaseObj::EventBaseAdd(const int event_id,const ENUM_BASE_EVENT_REASON reason,const double value)
  {
   CBaseEvent* event=new CBaseEvent(event_id,reason,value);
   if(event==NULL)
      return false;
   this.m_list_events_base.Sort();
   if(this.m_list_events_base.Search(event)>WRONG_VALUE)
     {
      delete event;
      return false;
     }
   return this.m_list_events_base.Add(event);
  }
//+------------------------------------------------------------------+

Para o método é transferido o identificador do evento, o motivo do evento e o tamanho da alteração da propriedade do objeto herdeiro.

Depois, é criado um novo evento base, e se exatamente o mesmo evento já estiver na lista de eventos básicos, esse evento será excluído e será retornado false indicando que o evento não foi adicionado. Caso contrário, será retornado o resultado da adição do novo evento à lista de eventos básicos do objeto.

Método que retorna o evento base por seu índice na lista de do objeto:

//+------------------------------------------------------------------+
//| Return a base event by its index in the list                     |
//+------------------------------------------------------------------+
CBaseEvent *CBaseObj::GetEventBase(const int index)
  {
   int total=this.m_list_events_base.Total();
   if(total==0 || index<0 || index>total-1)
      return NULL;
   CBaseEvent *event=this.m_list_events_base.At(index);
   return(event!=NULL ? event : NULL);
  }

Para o método é transferido o índice do evento necessário, e se a lista tiver tamanho zero ou o índice ficar fora da lista de eventos básicos, será retornado NULL, caso contrário, obtemos o evento da lista por índice e retornamos o ponteiro para o objeto recebido.

Para a classe do objeto base, é necessário criar métodos pelos quais possamos definir rapidamente os valores necessários para a alteração nas propriedades, valores esses que excedidos leva à geração de eventos. E métodos para definir novos valores de propriedades de objetos herdeiros e para retornar sinalizadores sobre eventos 'controlados' de objetos. Como a classe base não sabe nada sobre as propriedades de seus descendentes, é necessário criar métodos universais que permitam fazer alterações na propriedade desejada do objeto herdeiro. Como indicaremos o número de propriedades inteiras e reais para cada uma das classes sucessoras, não será difícil determinar para qual propriedade estaremos definindo o valor, por tanto, será necessário apenas verificar o índice da propriedade alterável. Se o índice for menor que o número de propriedades inteiras, serão feitas alterações na propriedade inteira do objeto, caso contrário, na propriedade real.

Implementação de métodos para definir as propriedades controladas de objetos herdeiros:

//+------------------------------------------------------------------+
//| Methods of setting controlled parameters                         |
//+------------------------------------------------------------------+
//--- Data for storing, controlling and returning tracked properties:
//--- [Property index][0] Controlled property increase value
//--- [Property index][1] Controlled property decrease value
//--- [Property index][2] Controlled property value level
//--- [Property index][3] Property value
//--- [Property index][4] Property value change
//--- [Property index][5] Flag of a property change exceeding the increase value
//--- [Property index][6] Flag of a property change exceeding the decrease value
//--- [Property index][7] Flag of a property increase exceeding the control level
//--- [Property index][8] Flag of a property decrease being less than the control level
//--- [Property index][9] Flag of a property value being equal to the control level
//+------------------------------------------------------------------+
//| Set the value of the controlled increase of object properties    |
//+------------------------------------------------------------------+
template<typename T> void CBaseObj::SetControlledValueINC(const int property,const T value)
  {
   if(property<this.m_long_prop_total)
      this.m_long_prop_event[property][0]=(long)value;
   else
      this.m_double_prop_event[property-this.m_long_prop_total][0]=(double)value;
  }  
//+------------------------------------------------------------------+
//| Set the value of the controlled decrease of object properties    |
//+------------------------------------------------------------------+
template<typename T> void CBaseObj::SetControlledValueDEC(const int property,const T value)
  {
   if(property<this.m_long_prop_total)
      this.m_long_prop_event[property][1]=(long)value;
   else
      this.m_double_prop_event[property-this.m_long_prop_total][1]=(double)value;
  }  
//+------------------------------------------------------------------+
//| Set the control level of object properties                       |
//+------------------------------------------------------------------+
template<typename T> void CBaseObj::SetControlledValueLEVEL(const int property,const T value)
  {
   if(property<this.m_long_prop_total)
      this.m_long_prop_event[property][2]=(long)value;
   else
      this.m_double_prop_event[property-this.m_long_prop_total][2]=(double)value;
  }  
//+------------------------------------------------------------------+
//| Set the object property value                                    |
//+------------------------------------------------------------------+
template<typename T> void CBaseObj::SetControlledValue(const int property,const T value)
  {
   if(property<this.m_long_prop_total)
      this.m_long_prop_event[property][3]=(long)value;
   else
      this.m_double_prop_event[property-this.m_long_prop_total][3]=(double)value;
  }
//+------------------------------------------------------------------+
//| Set the object property change value			     |
//+------------------------------------------------------------------+
template<typename T> void CBaseObj::SetControlledChangedValue(const int property,const T value)
  {
   if(property<this.m_long_prop_total)
      this.m_long_prop_event[property][4]=(long)value;
   else
      this.m_double_prop_event[property-this.m_long_prop_total][4]=(double)value;
  }
//+------------------------------------------------------------------+
//| Set the flag of the property value change                        |
//| exceeding the increase value                                     |
//+------------------------------------------------------------------+
template<typename T> void CBaseObj::SetControlledFlagINC(const int property,const T value)
  {
   if(property<this.m_long_prop_total)
      this.m_long_prop_event[property][5]=(long)value;
   else
      this.m_double_prop_event[property-this.m_long_prop_total][5]=(double)value;
  }
//+------------------------------------------------------------------+
//| Set the flag of the property value change                        |
//| exceeding the decrease value                                     |
//+------------------------------------------------------------------+
template<typename T> void CBaseObj::SetControlledFlagDEC(const int property,const T value)
  {
   if(property<this.m_long_prop_total)
      this.m_long_prop_event[property][6]=(long)value;
   else
      this.m_double_prop_event[property-this.m_long_prop_total][6]=(double)value;
  }
//+------------------------------------------------------------------+
//| Set the flag of the property value increase                      |
//| exceeding the control level                                      |
//+------------------------------------------------------------------+
template<typename T> void CBaseObj::SetControlledFlagMORE(const int property,const T value)
  {
   if(property<this.m_long_prop_total)
      this.m_long_prop_event[property][7]=(long)value;
   else
      this.m_double_prop_event[property-this.m_long_prop_total][7]=(double)value;
  }
//+------------------------------------------------------------------+
//| Set the flag of the property value decrease                      |
//| being less than the control level                                |
//+------------------------------------------------------------------+
template<typename T> void CBaseObj::SetControlledFlagLESS(const int property,const T value)
  {
   if(property<this.m_long_prop_total)
      this.m_long_prop_event[property][8]=(long)value;
   else
      this.m_double_prop_event[property-this.m_long_prop_total][8]=(double)value;
  }
//+------------------------------------------------------------------+
//| Set the flag of the property value being equal to the            |
//| control level                                                    |
//+------------------------------------------------------------------+
template<typename T> void CBaseObj::SetControlledFlagEQUAL(const int property,const T value)
  {
   if(property<this.m_long_prop_total)
      this.m_long_prop_event[property][9]=(long)value;
   else
      this.m_double_prop_event[property-this.m_long_prop_total][9]=(double)value;
  }
//+------------------------------------------------------------------+

Consideremos o último método: para o método é transferida a propriedade em cujo valor é necessário inserir o valor padrão T value. Se o índice de propriedade for menor que o número de propriedades inteiras do objeto herdeiro, inserimos o T value na célula desejada no array de propriedades inteiras do objeto, caso contrário, calculamos o índice pelo qual essa propriedade é armazenada no array de propriedades reais (o índice de uma propriedade double é sempre - o número de propriedades inteiras do objeto - maior que o índice da mesma propriedade no array) e inserimos o T value na célula desejada no array de propriedades reais do objeto. As células necessárias da segunda dimensão do aray para cada um dos métodos são listadas antes da lista de métodos.

Método que converte um valor ushort num valor long deslocado um número de bytes necessário para empacotamento subsequente num contêiner long:

//+------------------------------------------------------------------+
//| Convert a 'ushort' value to a specified 'long' number byte       |
//+------------------------------------------------------------------+
long CBaseObj::UshortToByte(const ushort value,const uchar index) const
  {
   if(index>3)
     {
      ::Print(DFUN,TextByLanguage("Ошибка. Значение \"index\" должно быть в пределах 0 - 3","Error. \"index\" value should be between 0 - 3"));
      return 0;
     }
   return(long)value<<(16*index);
  }
//+------------------------------------------------------------------+

Imagine um número long de oito bytes, dividido em células de dois bytes (cada uma dessas células tem seu próprio índice):

Bytes 6-7 (índice 3)
 Bytes 4-5 (índice 2) Bytes 2-3 (índice 1) 
Bytes 0-1 (índice 0) 
ushort 4
ushort 3
ushort 2
ushort 1

Podemos colocar quatro números ushort nela. Devemos deslocar cada número subsequente para a esquerda 16 bits * índice (1 byte = 8 bits) e, em seguida, adicionar o valor resultante ao número long. Dessa forma, obtemos alguns valores ushort compactados num contêiner long.

Para o método são transferidos um número ushort e o índice no qual é necessário armazenar o valor ushort num contêiner long.
O índice é verificado e, se for mais de 3
, será exibida uma mensagem de índice inválido e será retornado 0.
Se o índice for válido, o número ushort será deslocado para a esquerda 16 bits * índice (num byte há 8 bits, e é necessário deslocar um número de dois bytes),
e o resultado do deslocamento será retornado do método.

Método que compacta o valor ushort deslocado um número necessário de bytes num contêiner long:

//+------------------------------------------------------------------+
//| Pack a 'ushort' number to a passed 'long' number                 |
//+------------------------------------------------------------------+
long CBaseObj::UshortToLong(const ushort ushort_value,const uchar index,long &long_value)
  {
   if(index>3)
     {
      ::Print(DFUN,TextByLanguage("Ошибка. Значение \"index\" должно быть в пределах 0 - 3","Error. \"index\" value should be between 0 - 3"));
      return 0;
     }
   return(long_value |= UshortToByte(ushort_value,index));
  }
//+------------------------------------------------------------------+

Para o método são transferidos um número ushort, que é necessário armazenar num contêiner long transferido para o método pela referência, e o índice de bytes no qual é necessário armazenar o valor ushort no contêiner long.
Igual ao método acima, é verificado o índice e, após verificação bem-sucedida do índice,
ao número long é adicionado o número ushort deslocado o número de bytes necessário pelo método UshortToByte()com ajuda do bit a bit "OR",
e o resultado será retornado ao programa de chamada.
.

Método que retorna uma descrição de string do evento do objeto herdeiro:

//+------------------------------------------------------------------+
//| Return an object event description                               |
//+------------------------------------------------------------------+
string CBaseObj::EventDescription(const int property,
                                  const ENUM_BASE_EVENT_REASON reason,
                                  const int source,
                                  const string value,
                                  const string property_descr,
                                  const int digits)
  {
//--- Depending on the collection ID, create th object type description
   string type=
     (
      this.Type()==COLLECTION_SYMBOLS_ID ? TextByLanguage("символа: ","symbol property: ")   :
      this.Type()==COLLECTION_ACCOUNT_ID ? TextByLanguage("аккаунта: ","account property: ")   :
      ""
     );
//--- Depending on the property type, create the property change value description
   string level=
     (
      property<this.m_long_prop_total ? 
      ::DoubleToString(this.GetControlledValueLongLEVEL(property),digits) : 
      ::DoubleToString(this.GetControlledValueDoubleLEVEL(property),digits)
     );
//--- Depending on the event reason, create the event description text
   string res=
     (
      reason==BASE_EVENT_REASON_INC       ?  TextByLanguage("Значение свойства ","Value of the ")+type+property_descr+TextByLanguage(" увеличено на "," increased by ")+value       :
      reason==BASE_EVENT_REASON_DEC       ?  TextByLanguage("Значение свойства ","Value of the ")+type+property_descr+TextByLanguage(" уменьшено на "," decreased by ")+value       :
      reason==BASE_EVENT_REASON_MORE_THEN ?  TextByLanguage("Значение свойства ","Value of the ")+type+property_descr+TextByLanguage(" стало больше "," became more than ")+level   :
      reason==BASE_EVENT_REASON_LESS_THEN ?  TextByLanguage("Значение свойства ","Value of the ")+type+property_descr+TextByLanguage(" стало меньше "," became less than ")+level   :
      reason==BASE_EVENT_REASON_EQUALS    ?  TextByLanguage("Значение свойства ","Value of the ")+type+property_descr+TextByLanguage(" равно "," is equal to ")+level               :
      TextByLanguage("Неизвестное событие ","Unknown ")+type
     );
//--- Return the object name+created event description text
   return this.m_name+": "+res;
  }
//+------------------------------------------------------------------+

Como a classe do objeto base não sabe nada sobre seus descendentes, para descrever o evento na classe herdeira, precisamos apontar para o objeto descendente no qual o evento ocorre.
Para fazer isso, para o método é transferido

Todas as etapas para criar uma descrição do evento do objeto herdeiro são comentadas no código, espero que sejam claras.

Isso conclui as alterações necessárias na classe de objeto base (durante o desenvolvimento da biblioteca e a criação de novas coleções, novos identificadores de coleção serão adicionados ao último método para criar uma descrição correta dos eventos).

Renovando a classe de símbolos e a coleção de símbolos

Hoje, revisaremos a operação da classe de símbolos e da coleção de símbolos tendo os novos eventos de objeto base em mente. Para fazer isso, fazemos alterações nas classes de símbolos e nas coleções de símbolos.
Abrimos o arquivo \MQL5\Include\DoEasy\Objects\Symbols\Symbol.mqh e começamos a fazer alterações.

Como agora todos os eventos dos objetos herdeiros do objeto base estão definidos na classe pai, não há necessidade de controlar a alteração de propriedades do objeto na classe herdeira. Isso significa que agora não é mais necessária a estrutura de dados das propriedades controladas do objeto.
Da classe do objeto-símbolo excluímos a estrutura e dois objetos com o tipo dessa estrutura:

   struct MqlDataSymbol
     {
      //--- Symbol integer properties
      ENUM_SYMBOL_TRADE_MODE trade_mode;     // SYMBOL_TRADE_MODE Order filling modes
      long session_deals;                    // SYMBOL_SESSION_DEALS The number of deals in the current session 
      long session_buy_orders;               // SYMBOL_SESSION_BUY_ORDERS The total number of current buy orders
      long session_sell_orders;              // SYMBOL_SESSION_SELL_ORDERS The total number of current sell orders
      long volume;                           // SYMBOL_VOLUME Last deal volume
      long volume_high_day;                  // SYMBOL_VOLUMEHIGH Maximum volume within a day
      long volume_low_day;                   // SYMBOL_VOLUMELOW Minimum volume within a day
      int spread;                            // SYMBOL_SPREAD Spread in points
      int stops_level;                       // SYMBOL_TRADE_STOPS_LEVEL Minimum distance in points from the current close price for setting Stop orders
      int freeze_level;                      // SYMBOL_TRADE_FREEZE_LEVEL Freeze distance for trading operations (in points)
      
      //--- Symbol real properties
      double bid_last;                       // SYMBOL_BID/SYMBOL_LAST Bid - the best sell offer/Last deal price
      double bid_last_high;                  // SYMBOL_BIDHIGH/SYMBOL_LASTHIGH Maximum Bid within the day/Maximum Last per day
      double bid_last_low;                   // SYMBOL_BIDLOW/SYMBOL_LASTLOW Minimum Bid within the day/Minimum Last per day
      double ask;                            // SYMBOL_ASK Ask - nest buy offer
      double ask_high;                       // SYMBOL_ASKHIGH Maximum Ask of the day
      double ask_low;                        // SYMBOL_ASKLOW Minimum Ask of the day
      double volume_real_day;                // SYMBOL_VOLUME_REAL Real Volume of the day
      double volume_high_real_day;           // SYMBOL_VOLUMEHIGH_REAL Maximum real Volume of the day
      double volume_low_real_day;            // SYMBOL_VOLUMELOW_REAL Minimum real Volume of the day
      double option_strike;                  // SYMBOL_OPTION_STRIKE Strike price
      double volume_limit;                   // SYMBOL_VOLUME_LIMIT Maximum permissible total volume for a position and pending orders in one direction
      double swap_long;                      // SYMBOL_SWAP_LONG Long swap value
      double swap_short;                     // SYMBOL_SWAP_SHORT Short swap value
      double session_volume;                 // SYMBOL_SESSION_VOLUME The total volume of deals in the current session
      double session_turnover;               // SYMBOL_SESSION_TURNOVER The total turnover in the current session
      double session_interest;               // SYMBOL_SESSION_INTEREST The total volume of open positions
      double session_buy_ord_volume;         // SYMBOL_SESSION_BUY_ORDERS_VOLUME The total volume of Buy orders at the moment
      double session_sell_ord_volume;        // SYMBOL_SESSION_SELL_ORDERS_VOLUME The total volume of Sell orders at the moment
      double session_open;                   // SYMBOL_SESSION_OPEN Session open price
      double session_close;                  // SYMBOL_SESSION_CLOSE Session close price
      double session_aw;                     // SYMBOL_SESSION_AW The average weighted price of the session
     };
   MqlDataSymbol    m_struct_curr_symbol;    // Current symbol data
   MqlDataSymbol    m_struct_prev_symbol;    // Previous symbol data
//---

Excluímos todas as variáveis-membro da classe para armazenar as propriedades controladas e alteradas do objeto-símbolo, assim, agora todos esses dados são armazenados em arrays da classe do objeto base:

   //--- Current session deals
   long              m_control_session_deals_inc;              // Controlled value of the growth of the number of deals
   long              m_control_session_deals_dec;              // Controlled value of the decrease in the number of deals
   long              m_changed_session_deals_value;            // Value of change in the number of deals
   bool              m_is_change_session_deals_inc;            // Flag of a change in the number of deals exceeding the growth value
   bool              m_is_change_session_deals_dec;            // Flag of a change in the number of deals exceeding the decrease value
   //--- Buy orders of the current session
   long              m_control_session_buy_ord_inc;            // Controlled value of the increase of the number of Buy orders
   long              m_control_session_buy_ord_dec;            // Controlled value of the decrease in the number of Buy orders
   long              m_changed_session_buy_ord_value;          // Buy orders change value
   bool              m_is_change_session_buy_ord_inc;          // Flag of a change in the number of Buy orders exceeding the increase value
   bool              m_is_change_session_buy_ord_dec;          // Flag of a change in the number of Buy orders being less than the increase value
   //--- Sell orders of the current session
   long              m_control_session_sell_ord_inc;           // Controlled value of the increase of the number of Sell orders
   long              m_control_session_sell_ord_dec;           // Controlled value of the decrease in the number of Sell orders
   long              m_changed_session_sell_ord_value;         // Sell orders change value
   bool              m_is_change_session_sell_ord_inc;         // Flag of a change in the number of Sell orders exceeding the increase value
   bool              m_is_change_session_sell_ord_dec;         // Flag of a change in the number of Sell orders exceeding the decrease value
   //--- Volume of the last deal
   long              m_control_volume_inc;                     // Controlled value of the volume increase in the last deal
   long              m_control_volume_dec;                     // Controlled value of the volume decrease in the last deal
   long              m_changed_volume_value;                   // Value of the volume change in the last deal
   bool              m_is_change_volume_inc;                   // Flag of the volume change in the last deal exceeding the increase value
   bool              m_is_change_volume_dec;                   // Flag of the volume change in the last deal being less than the increase value
   //--- Maximum volume within a day
   long              m_control_volume_high_day_inc;            // Controlled value of the maximum volume increase for a day
   long              m_control_volume_high_day_dec;            // Controlled value of the maximum volume decrease for a day
   long              m_changed_volume_high_day_value;          // Maximum volume change value within a day
   bool              m_is_change_volume_high_day_inc;          // Flag of the maximum day volume exceeding the increase value
   bool              m_is_change_volume_high_day_dec;          // Flag of the maximum day volume exceeding the decrease value
   //--- Minimum volume within a day
   long              m_control_volume_low_day_inc;             // Controlled value of the minimum volume increase for a day
   long              m_control_volume_low_day_dec;             // Controlled value of the minimum volume decrease for a day
   long              m_changed_volume_low_day_value;           // Minimum volume change value within a day
   bool              m_is_change_volume_low_day_inc;           // Flag of the minimum day volume exceeding the increase value
   bool              m_is_change_volume_low_day_dec;           // Flag of the minimum day volume exceeding the decrease value
   //--- Spread
   int               m_control_spread_inc;                     // Controlled spread increase value in points
   int               m_control_spread_dec;                     // Controlled spread decrease value in points
   int               m_changed_spread_value;                   // Spread change value in points
   bool              m_is_change_spread_inc;                   // Flag of spread change in points exceeding the increase value
   bool              m_is_change_spread_dec;                   // Flag of spread change in points exceeding the decrease value
   //--- StopLevel
   int               m_control_stops_level_inc;                // Controlled StopLevel increase value in points
   int               m_control_stops_level_dec;                // Controlled StopLevel decrease value in points
   int               m_changed_stops_level_value;              // StopLevel change value in points
   bool              m_is_change_stops_level_inc;              // Flag of StopLevel change in points exceeding the increase value
   bool              m_is_change_stops_level_dec;              // Flag of StopLevel change in points exceeding the decrease value
   //--- Freeze distance
   int               m_control_freeze_level_inc;               // Controlled FreezeLevel increase value in points
   int               m_control_freeze_level_dec;               // Controlled FreezeLevel decrease value in points
   int               m_changed_freeze_level_value;             // FreezeLevel change value in points
   bool              m_is_change_freeze_level_inc;             // Flag of FreezeLevel change in points exceeding the increase value
   bool              m_is_change_freeze_level_dec;             // Flag of FreezeLevel change in points exceeding the decrease value
   
   //--- Bid/Last
   double            m_control_bid_last_inc;                   // Controlled value of Bid or Last price increase
   double            m_control_bid_last_dec;                   // Controlled value of Bid or Last price decrease
   double            m_changed_bid_last_value;                 // Bid or Last price change value
   bool              m_is_change_bid_last_inc;                 // Flag of Bid or Last price change exceeding the increase value
   bool              m_is_change_bid_last_dec;                 // Flag of Bid or Last price change exceeding the decrease value
   //--- Maximum Bid/Last of the day
   double            m_control_bid_last_high_inc;              // Controlled increase value of the maximum Bid or Last price of the day
   double            m_control_bid_last_high_dec;              // Controlled decrease value of the maximum Bid or Last price of the day
   double            m_changed_bid_last_high_value;            // Maximum Bid or Last change value for the day
   bool              m_is_change_bid_last_high_inc;            // Flag of the maximum Bid or Last price change for the day exceeding the increase value
   bool              m_is_change_bid_last_high_dec;            // Flag of the maximum Bid or Last price change for the day exceeding the decrease value
   //--- Minimum Bid/Last of the day
   double            m_control_bid_last_low_inc;               // Controlled increase value of the minimum Bid or Last price of the day
   double            m_control_bid_last_low_dec;               // Controlled decrease value of the minimum Bid or Last price of the day
   double            m_changed_bid_last_low_value;             // Minimum Bid or Last change value for the day
   bool              m_is_change_bid_last_low_inc;             // Flag of the minimum Bid or Last price change for the day exceeding the increase value
   bool              m_is_change_bid_last_low_dec;             // Flag of the minimum Bid or Last price change for the day exceeding the decrease value
   //--- Ask
   double            m_control_ask_inc;                        // Controlled value of the Ask price increase
   double            m_control_ask_dec;                        // Controlled value of the Ask price decrease
   double            m_changed_ask_value;                      // Ask price change value
   bool              m_is_change_ask_inc;                      // Flag of the Ask price change exceeding the increase value
   bool              m_is_change_ask_dec;                      // Flag of the Ask price change exceeding the decrease value
   //--- Maximum Ask price for the day
   double            m_control_ask_high_inc;                   // Controlled increase value of the maximum Ask price of the day
   double            m_control_ask_high_dec;                   // Controlled decrease value of the maximum Ask price of the day
   double            m_changed_ask_high_value;                 // Maximum Ask price change value for the day
   bool              m_is_change_ask_high_inc;                 // Flag of the maximum Ask price change for the day exceeding the increase value
   bool              m_is_change_ask_high_dec;                 // Flag of the maximum Ask price change for the day exceeding the decrease value
   //--- Minimum Ask price for the day
   double            m_control_ask_low_inc;                    // Controlled increase value of the minimum Ask price of the day
   double            m_control_ask_low_dec;                    // Controlled decrease value of the minimum Ask price of the day
   double            m_changed_ask_low_value;                  // Minimum Ask price change value for the day
   bool              m_is_change_ask_low_inc;                  // Flag of the minimum Ask price change for the day exceeding the increase value
   bool              m_is_change_ask_low_dec;                  // Flag of the minimum Ask price change for the day exceeding the decrease value
   //--- Real Volume for the day
   double            m_control_volume_real_inc;                // Controlled value of the real volume increase of the day
   double            m_control_volume_real_dec;                // Controlled value of the real volume decrease of the day
   double            m_changed_volume_real_value;              // Real volume change value of the day
   bool              m_is_change_volume_real_inc;              // Flag of the real volume change for the day exceeding the increase value
   bool              m_is_change_volume_real_dec;              // Flag of the real volume change for the day exceeding the decrease value
   //--- Maximum real volume for the day
   double            m_control_volume_high_real_day_inc;       // Controlled value of the maximum real volume increase of the day
   double            m_control_volume_high_real_day_dec;       // Controlled value of the maximum real volume decrease of the day
   double            m_changed_volume_high_real_day_value;     // Maximum real volume change value of the day
   bool              m_is_change_volume_high_real_day_inc;     // Flag of the maximum real volume change for the day exceeding the increase value
   bool              m_is_change_volume_high_real_day_dec;     // Flag of the maximum real volume change for the day exceeding the decrease value
   //--- Minimum real volume for the day
   double            m_control_volume_low_real_day_inc;        // Controlled value of the minimum real volume increase of the day
   double            m_control_volume_low_real_day_dec;        // Controlled value of the minimum real volume decrease of the day
   double            m_changed_volume_low_real_day_value;      // Minimum real volume change value of the day
   bool              m_is_change_volume_low_real_day_inc;      // Flag of the minimum real volume change for the day exceeding the increase value
   bool              m_is_change_volume_low_real_day_dec;      // Flag of the minimum real volume change for the day exceeding the decrease value
   //--- Strike price
   double            m_control_option_strike_inc;              // Controlled value of the strike price increase
   double            m_control_option_strike_dec;              // Controlled value of the strike price decrease
   double            m_changed_option_strike_value;            // Strike price change value
   bool              m_is_change_option_strike_inc;            // Flag of the strike price change exceeding the increase value
   bool              m_is_change_option_strike_dec;            // Flag of the strike price change exceeding the decrease value
   //--- Total volume of positions and orders
   double            m_changed_volume_limit_value;             // Minimum total volume change value
   bool              m_is_change_volume_limit_inc;             // Flag of the minimum total volume increase
   bool              m_is_change_volume_limit_dec;             // Flag of the minimum total volume decrease
   //---  Swap long
   double            m_changed_swap_long_value;                // Swap long change value
   bool              m_is_change_swap_long_inc;                // Flag of the swap long increase
   bool              m_is_change_swap_long_dec;                // Flag of the swap long decrease
   //---  Swap short
   double            m_changed_swap_short_value;               // Swap short change value
   bool              m_is_change_swap_short_inc;               // Flag of the swap short increase
   bool              m_is_change_swap_short_dec;               // Flag of the swap short decrease
   //--- The total volume of deals in the current session
   double            m_control_session_volume_inc;             // Controlled value of the total trade volume increase in the current session
   double            m_control_session_volume_dec;             // Controlled value of the total trade volume decrease in the current session
   double            m_changed_session_volume_value;           // The total deal volume change value in the current session
   bool              m_is_change_session_volume_inc;           // Flag of total trade volume change in the current session exceeding the increase value
   bool              m_is_change_session_volume_dec;           // Flag of total trade volume change in the current session exceeding the decrease value
   //--- The total turnover in the current session
   double            m_control_session_turnover_inc;           // Controlled value of the total turnover increase in the current session
   double            m_control_session_turnover_dec;           // Controlled value of the total turnover decrease in the current session
   double            m_changed_session_turnover_value;         // Total turnover change value in the current session
   bool              m_is_change_session_turnover_inc;         // Flag of total turnover change in the current session exceeding the increase value
   bool              m_is_change_session_turnover_dec;         // Flag of total turnover change in the current session exceeding the decrease value
   //--- The total volume of open positions
   double            m_control_session_interest_inc;           // Controlled value of the total open position volume increase in the current session
   double            m_control_session_interest_dec;           // Controlled value of the total open position volume decrease in the current session
   double            m_changed_session_interest_value;         // Change value of the open positions total volume in the current session
   bool              m_is_change_session_interest_inc;         // Flag of total open positions' volume change in the current session exceeding the increase value
   bool              m_is_change_session_interest_dec;         // Flag of total open positions' volume change in the current session exceeding the decrease value
   //--- The total volume of Buy orders at the moment
   double            m_control_session_buy_ord_volume_inc;     // Controlled value of the current total buy order volume increase
   double            m_control_session_buy_ord_volume_dec;     // Controlled value of the current total buy order volume decrease
   double            m_changed_session_buy_ord_volume_value;   // Change value of the current total buy order volume
   bool              m_is_change_session_buy_ord_volume_inc;   // Flag of changing the current total buy orders volume exceeding the increase value
   bool              m_is_change_session_buy_ord_volume_dec;   // Flag of changing the current total buy orders volume exceeding the decrease value
   //--- The total volume of Sell orders at the moment
   double            m_control_session_sell_ord_volume_inc;    // Controlled value of the current total sell order volume increase
   double            m_control_session_sell_ord_volume_dec;    // Controlled value of the current total sell order volume decrease
   double            m_changed_session_sell_ord_volume_value;  // Change value of the current total sell order volume
   bool              m_is_change_session_sell_ord_volume_inc;  // Flag of changing the current total sell orders volume exceeding the increase value
   bool              m_is_change_session_sell_ord_volume_dec;  // Flag of changing the current total sell orders volume exceeding the decrease value
   //--- Session open price
   double            m_control_session_open_inc;               // Controlled value of the session open price increase
   double            m_control_session_open_dec;               // Controlled value of the session open price decrease
   double            m_changed_session_open_value;             // Session open price change value
   bool              m_is_change_session_open_inc;             // Flag of the session open price change exceeding the increase value
   bool              m_is_change_session_open_dec;             // Flag of the session open price change exceeding the decrease value
   //--- Session close price
   double            m_control_session_close_inc;              // Controlled value of the session close price increase
   double            m_control_session_close_dec;              // Controlled value of the session close price decrease
   double            m_changed_session_close_value;            // Session close price change value
   bool              m_is_change_session_close_inc;            // Flag of the session close price change exceeding the increase value
   bool              m_is_change_session_close_dec;            // Flag of the session close price change exceeding the decrease value
   //--- The average weighted session price
   double            m_control_session_aw_inc;                 // Controlled value of the average weighted session price increase
   double            m_control_session_aw_dec;                 // Controlled value of the average weighted session price decrease
   double            m_changed_session_aw_value;               // The average weighted session price change value
   bool              m_is_change_session_aw_inc;               // Flag of the average weighted session price change value exceeding the increase value
   bool              m_is_change_session_aw_dec;               // Flag of the average weighted session price change value exceeding the decrease value

Removemos os métodos realçados, por serem desnecessários:

//--- Initialize the variables of (1) tracked, (2) controlled symbol data
   virtual void      InitChangesParams(void);
   virtual void      InitControlsParams(void);
//--- Check symbol changes, return a change code
   virtual int       SetEventCode(void);
//--- Set an event type and fill in the event list
   virtual void      SetTypeEvent(void);

//--- Return description of symbol events
   string            EventDescription(const ENUM_SYMBOL_EVENT event);


Em vez do método virtual para definir o código que altera as propriedades do símbolo, declaramos um método para verificar alterações nas propriedades do símbolo e para criar um evento:

//--- Initialize the variables of controlled symbol data
   virtual void      InitControlsParams(void);
//--- Check the list of symbol property changes and create an event
   void              CheckEvents(void);

Na seção pública da classe, inserimos as declarações dos métodos que estabelecem os valores monitorados e que retornam os valores de controle estabelecidos das propriedades controladas, os valores da alteração nas propriedades e os sinalizadores:

public:
//--- Set the change value of the controlled symbol property
   template<typename T> void  SetControlChangedValue(const int property,const T value);
//--- Set the value of the controlled symbol property (1) increase, (2) decrease and (3) control level
   template<typename T> void  SetControlPropertyINC(const int property,const T value);
   template<typename T> void  SetControlPropertyDEC(const int property,const T value);
   template<typename T> void  SetControlPropertyLEVEL(const int property,const T value);
//--- Set the flag of a symbol property change exceeding the (1) increase and (2) decrease values
   template<typename T> void  SetControlFlagINC(const int property,const T value);
   template<typename T> void  SetControlFlagDEC(const int property,const T value);
   
//--- Return the set value of the (1) integer and (2) real symbol property controlled increase
   long              GetControlParameterINC(const ENUM_SYMBOL_PROP_INTEGER property)   const { return this.GetControlledValueLongINC(property);             }
   double            GetControlParameterINC(const ENUM_SYMBOL_PROP_DOUBLE property)    const { return this.GetControlledValueDoubleINC(property);           }
//--- Return the set value of the (1) integer and (2) real symbol property controlled decrease
   long              GetControlParameterDEC(const ENUM_SYMBOL_PROP_INTEGER property)   const { return this.GetControlledValueLongDEC(property);             }
   double            GetControlParameterDEC(const ENUM_SYMBOL_PROP_DOUBLE property)    const { return this.GetControlledValueDoubleDEC(property);           }
//--- Return the flag of an (1) integer and (2) real symbol property value change exceeding the increase value
   long              GetControlFlagINC(const ENUM_SYMBOL_PROP_INTEGER property)        const { return this.GetControlledFlagLongINC(property);              }
   double            GetControlFlagINC(const ENUM_SYMBOL_PROP_DOUBLE property)         const { return this.GetControlledFlagDoubleINC(property);            }
//--- Return the flag of an (1) integer and (2) real symbol property value change exceeding the decrease value
   bool              GetControlFlagDEC(const ENUM_SYMBOL_PROP_INTEGER property)        const { return (bool)this.GetControlledFlagLongDEC(property);        }
   bool              GetControlFlagDEC(const ENUM_SYMBOL_PROP_DOUBLE property)         const { return (bool)this.GetControlledFlagDoubleDEC(property);      }
//--- Return the change value of the controlled (1) integer and (2) real object property
   long              GetControlChangedValue(const ENUM_SYMBOL_PROP_INTEGER property)   const { return this.GetControlledChangedValueLong(property);         }
   double            GetControlChangedValue(const ENUM_SYMBOL_PROP_DOUBLE property)    const { return this.GetControlledChangedValueDouble(property);       }
   
//+------------------------------------------------------------------+

Cada método é feito como dois métodos sobrecarregados que chamam os métodos do objeto base correspondentes ao tipo da propriedade definida/verificada do objeto-símbolo.

Anteriormente, escrevemos métodos de acesso simplificado a algumas propriedades do objeto-símbolo. Vamos adicionar ai métodos para definir níveis de controle de valores de propriedade e declarar métodos de definição/obtenção para Bid/Last e parâmetros relacionados (anteriormente, era selecionado automaticamente Bid ou Last, dependendo dos preços do gráfico. Agora precisamos criar métodos para trabalhar com esses dados):

//+------------------------------------------------------------------+
//| Get and set the parameters of tracked property changes           |
//+------------------------------------------------------------------+
   //--- Execution
   //--- Flag of changing the trading mode for a symbol
   bool              IsChangedTradeMode(void)                              const { return this.m_is_change_trade_mode;                                      } 
   //--- Current session deals
   //--- setting the controlled value of (1) growth, (2) decrease and (3) control level of the number of deals during the current session
   //--- getting (3) the number of deals change value during the current session,
   //--- getting the flag of the number of deals change during the current session exceeding the (4) increase, (5) decrease value
   void              SetControlSessionDealsInc(const long value)                 { this.SetControlPropertyINC(SYMBOL_PROP_SESSION_DEALS,(long)::fabs(value));        }
   void              SetControlSessionDealsDec(const long value)                 { this.SetControlPropertyDEC(SYMBOL_PROP_SESSION_DEALS,(long)::fabs(value));        }
   void              SetControlSessionDealsLevel(const long value)               { this.SetControlPropertyLEVEL(SYMBOL_PROP_SESSION_DEALS,(long)::fabs(value));      }
   long              GetValueChangedSessionDeals(void)                     const { return this.GetControlChangedValue(SYMBOL_PROP_SESSION_DEALS);                    }
   bool              IsIncreasedSessionDeals(void)                         const { return (bool)this.GetControlFlagINC(SYMBOL_PROP_SESSION_DEALS);                   }
   bool              IsDecreasedSessionDeals(void)                         const { return (bool)this.GetControlFlagDEC(SYMBOL_PROP_SESSION_DEALS);                   }
   //--- Buy orders of the current session
   //--- setting the controlled value of (1) growth, (2) decrease and (3) control level of the current number of Buy orders
   //--- getting (4) the current number of Buy orders change value,
   //--- getting the flag of the current Buy orders' number change exceeding the (5) growth, (6) decrease value
   void              SetControlSessionBuyOrdInc(const long value)                { this.SetControlPropertyINC(SYMBOL_PROP_SESSION_BUY_ORDERS,(long)::fabs(value));   }
   void              SetControlSessionBuyOrdDec(const long value)                { this.SetControlPropertyDEC(SYMBOL_PROP_SESSION_BUY_ORDERS,(long)::fabs(value));   }
   void              SetControlSessionBuyOrdLevel(const long value)              { this.SetControlPropertyLEVEL(SYMBOL_PROP_SESSION_BUY_ORDERS,(long)::fabs(value)); }
   long              GetValueChangedSessionBuyOrders(void)                 const { return this.GetControlChangedValue(SYMBOL_PROP_SESSION_BUY_ORDERS);               }
   bool              IsIncreasedSessionBuyOrders(void)                     const { return (bool)this.GetControlFlagINC(SYMBOL_PROP_SESSION_BUY_ORDERS);              }
   bool              IsDecreasedSessionBuyOrders(void)                     const { return (bool)this.GetControlFlagDEC(SYMBOL_PROP_SESSION_BUY_ORDERS);              }
   //--- Sell orders of the current session
   //--- setting the controlled value of (1) growth, (2) decrease and (3) control level of the current number of Sell orders
   //--- getting (4) the current number of Sell orders change value,
   //--- getting the flag of the current Sell orders' number change exceeding the (5) growth, (6) decrease value
   void              SetControlSessionSellOrdInc(const long value)               { this.SetControlPropertyINC(SYMBOL_PROP_SESSION_SELL_ORDERS,(long)::fabs(value));  }
   void              SetControlSessionSellOrdDec(const long value)               { this.SetControlPropertyDEC(SYMBOL_PROP_SESSION_SELL_ORDERS,(long)::fabs(value));  }
   void              SetControlSessionSellOrdLevel(const long value)             { this.SetControlPropertyLEVEL(SYMBOL_PROP_SESSION_SELL_ORDERS,(long)::fabs(value));}
   long              GetValueChangedSessionSellOrders(void)                const { return this.GetControlChangedValue(SYMBOL_PROP_SESSION_SELL_ORDERS);              }
   bool              IsIncreasedSessionSellOrders(void)                    const { return (bool)this.GetControlFlagINC(SYMBOL_PROP_SESSION_SELL_ORDERS);             }
   bool              IsDecreasedSessionSellOrders(void)                    const { return (bool)this.GetControlFlagDEC(SYMBOL_PROP_SESSION_SELL_ORDERS);             }
   //--- Volume of the last deal
   //--- setting the last deal volume controlled (1) increase, (2) decrease and (3) control level
   //--- getting (4) volume change values in the last deal,
   //--- getting the flag of the volume change in the last deal exceeding the (5) growth, (6) decrease value
   void              SetControlVolumeInc(const long value)                       { this.SetControlPropertyINC(SYMBOL_PROP_VOLUME,(long)::fabs(value));               }
   void              SetControlVolumeDec(const long value)                       { this.SetControlPropertyDEC(SYMBOL_PROP_VOLUME,(long)::fabs(value));               }
   void              SetControlVolumeLevel(const long value)                     { this.SetControlPropertyLEVEL(SYMBOL_PROP_VOLUME,(long)::fabs(value));             }
   long              GetValueChangedVolume(void)                           const { return this.GetControlChangedValue(SYMBOL_PROP_VOLUME);                           }
   bool              IsIncreasedVolume(void)                               const { return (bool)this.GetControlFlagINC(SYMBOL_PROP_VOLUME);                          }
   bool              IsDecreasedVolume(void)                               const { return (bool)this.GetControlFlagDEC(SYMBOL_PROP_VOLUME);                          }
   //--- Maximum volume within a day
   //--- setting the maximum day volume controlled (1) increase, (2) decrease and (3) control level
   //--- getting (4) the maximum volume change value within a day,
   //--- getting the flag of the maximum day volume change exceeding the (5) growth, (6) decrease value
   void              SetControlVolumeHighInc(const long value)                   { this.SetControlPropertyINC(SYMBOL_PROP_VOLUMEHIGH,(long)::fabs(value));           }
   void              SetControlVolumeHighDec(const long value)                   { this.SetControlPropertyDEC(SYMBOL_PROP_VOLUMEHIGH,(long)::fabs(value));           }
   void              SetControlVolumeHighLevel(const long value)                 { this.SetControlPropertyLEVEL(SYMBOL_PROP_VOLUMEHIGH,(long)::fabs(value));         }
   long              GetValueChangedVolumeHigh(void)                       const { return this.GetControlChangedValue(SYMBOL_PROP_VOLUMEHIGH);                       }
   bool              IsIncreasedVolumeHigh(void)                           const { return (bool)this.GetControlFlagINC(SYMBOL_PROP_VOLUMEHIGH);                      }
   bool              IsDecreasedVolumeHigh(void)                           const { return (bool)this.GetControlFlagDEC(SYMBOL_PROP_VOLUMEHIGH);                      }
   //--- Minimum volume within a day
   //--- setting the minimum day volume controlled (1) increase, (2) decrease and (3) control level
   //--- getting (4) the minimum volume change value within a day,
   //--- getting the flag of the minimum day volume change exceeding the (5) growth, (6) decrease value
   void              SetControlVolumeLowInc(const long value)                    { this.SetControlPropertyINC(SYMBOL_PROP_VOLUMELOW,(long)::fabs(value));            }
   void              SetControlVolumeLowDec(const long value)                    { this.SetControlPropertyDEC(SYMBOL_PROP_VOLUMELOW,(long)::fabs(value));            }
   void              SetControlVolumeLowLevel(const long value)                  { this.SetControlPropertyLEVEL(SYMBOL_PROP_VOLUMELOW,(long)::fabs(value));          }
   long              GetValueChangedVolumeLow(void)                        const { return this.GetControlChangedValue(SYMBOL_PROP_VOLUMELOW);                        }
   bool              IsIncreasedVolumeLow(void)                            const { return (bool)this.GetControlFlagINC(SYMBOL_PROP_VOLUMELOW);                       }
   bool              IsDecreasedVolumeLow(void)                            const { return (bool)this.GetControlFlagDEC(SYMBOL_PROP_VOLUMELOW);                       }
   //--- Spread
   //--- setting the controlled spread (1) increase, (2) decrease value and (3) control level in points
   //--- getting (4) spread change value in points,
   //--- getting the flag of the spread change in points exceeding the (5) growth, (6) decrease value
   void              SetControlSpreadInc(const int value)                        { this.SetControlPropertyINC(SYMBOL_PROP_SPREAD,(long)::fabs(value));               }
   void              SetControlSpreadDec(const int value)                        { this.SetControlPropertyDEC(SYMBOL_PROP_SPREAD,(long)::fabs(value));               }
   void              SetControlSpreadLevel(const int value)                      { this.SetControlPropertyLEVEL(SYMBOL_PROP_SPREAD,(long)::fabs(value));             }
   int               GetValueChangedSpread(void)                           const { return (int)this.GetControlChangedValue(SYMBOL_PROP_SPREAD);                      }
   bool              IsIncreasedSpread(void)                               const { return (bool)this.GetControlFlagINC(SYMBOL_PROP_SPREAD);                          }
   bool              IsDecreasedSpread(void)                               const { return (bool)this.GetControlFlagDEC(SYMBOL_PROP_SPREAD);                          }
   //--- StopLevel
   //--- setting the controlled StopLevel (1) increase, (2) decrease value and (3) control level in points
   //--- getting (4) StopLevel change value in points,
   //--- getting the flag of StopLevel change in points exceeding the (5) growth, (6) decrease value
   void              SetControlStopLevelInc(const int value)                     { this.SetControlPropertyINC(SYMBOL_PROP_TRADE_STOPS_LEVEL,(long)::fabs(value));    }
   void              SetControlStopLevelDec(const int value)                     { this.SetControlPropertyDEC(SYMBOL_PROP_TRADE_STOPS_LEVEL,(long)::fabs(value));    }
   void              SetControlStopLevelLevel(const int value)                   { this.SetControlPropertyLEVEL(SYMBOL_PROP_TRADE_STOPS_LEVEL,(long)::fabs(value));  }
   int               GetValueChangedStopLevel(void)                        const { return (int)this.GetControlChangedValue(SYMBOL_PROP_TRADE_STOPS_LEVEL);           }
   bool              IsIncreasedStopLevel(void)                            const { return (bool)this.GetControlFlagINC(SYMBOL_PROP_TRADE_STOPS_LEVEL);               }
   bool              IsDecreasedStopLevel(void)                            const { return (bool)this.GetControlFlagDEC(SYMBOL_PROP_TRADE_STOPS_LEVEL);               }
   //--- Freeze distance
   //--- setting the controlled FreezeLevel (1) increase, (2) decrease value and (3) control level in points
   //--- getting (4) FreezeLevel change value in points,
   //--- getting the flag of FreezeLevel change in points exceeding the (5) growth, (6) decrease value
   void              SetControlFreezeLevelInc(const int value)                   { this.SetControlPropertyINC(SYMBOL_PROP_TRADE_FREEZE_LEVEL,(long)::fabs(value));   }
   void              SetControlFreezeLevelDec(const int value)                   { this.SetControlPropertyDEC(SYMBOL_PROP_TRADE_FREEZE_LEVEL,(long)::fabs(value));   }
   void              SetControlFreezeLevelLevel(const int value)                 { this.SetControlPropertyLEVEL(SYMBOL_PROP_TRADE_FREEZE_LEVEL,(long)::fabs(value)); }
   int               GetValueChangedFreezeLevel(void)                      const { return (int)this.GetControlChangedValue(SYMBOL_PROP_TRADE_FREEZE_LEVEL);          }
   bool              IsIncreasedFreezeLevel(void)                          const { return (bool)this.GetControlFlagINC(SYMBOL_PROP_TRADE_FREEZE_LEVEL);              }
   bool              IsDecreasedFreezeLevel(void)                          const { return (bool)this.GetControlFlagDEC(SYMBOL_PROP_TRADE_FREEZE_LEVEL);              }
   
   //--- Bid
   //--- setting the controlled Bid price (1) increase, (2) decrease value and (3) control level in points
   //--- getting (4) Bid or Last price change value,
   //--- getting the flag of the Bid or Last price change exceeding the (5) growth, (6) decrease value
   void              SetControlBidInc(const double value)                        { this.SetControlPropertyINC(SYMBOL_PROP_BID,::fabs(value));                        }
   void              SetControlBidDec(const double value)                        { this.SetControlPropertyDEC(SYMBOL_PROP_BID,::fabs(value));                        }
   void              SetControlBidLevel(const double value)                      { this.SetControlPropertyLEVEL(SYMBOL_PROP_BID,::fabs(value));                      }
   double            GetValueChangedBid(void)                              const { return this.GetControlChangedValue(SYMBOL_PROP_BID);                              }
   bool              IsIncreasedBid(void)                                  const { return (bool)this.GetControlFlagINC(SYMBOL_PROP_BID);                             }
   bool              IsDecreasedBid(void)                                  const { return (bool)this.GetControlFlagDEC(SYMBOL_PROP_BID);                             }
   //--- The highest Bid price of the day
   //--- setting the controlled maximum Bid price (1) increase, (2) decrease value and (3) control level in points
   //--- getting the (4) maximum Bid or Last price change value,
   //--- getting the flag of the maximum Bid or Last price change exceeding the (5) growth, (6) decrease value
   void              SetControlBidHighInc(const double value)                    { this.SetControlPropertyINC(SYMBOL_PROP_BIDHIGH,::fabs(value));                    }
   void              SetControlBidHighDec(const double value)                    { this.SetControlPropertyDEC(SYMBOL_PROP_BIDHIGH,::fabs(value));                    }
   void              SetControlBidHighLevel(const double value)                  { this.SetControlPropertyLEVEL(SYMBOL_PROP_BIDHIGH,::fabs(value));                  }
   double            GetValueChangedBidHigh(void)                          const { return this.GetControlChangedValue(SYMBOL_PROP_BIDHIGH);                          }
   bool              IsIncreasedBidHigh(void)                              const { return (bool)this.GetControlFlagINC(SYMBOL_PROP_BIDHIGH);                         }
   bool              IsDecreasedBidHigh(void)                              const { return (bool)this.GetControlFlagDEC(SYMBOL_PROP_BIDHIGH);                         }
   //--- The lowest Bid price of the day
   //--- setting the controlled minimum Bid price (1) increase, (2) decrease value and (3) control level in points
   //--- getting the (4) minimum Bid or Last price change value,
   //--- getting the flag of the minimum Bid or Last price change exceeding the (5) growth, (6) decrease value
   void              SetControlBidLowInc(const double value)                     { this.SetControlPropertyINC(SYMBOL_PROP_BIDLOW,::fabs(value));                     }
   void              SetControlBidLowDec(const double value)                     { this.SetControlPropertyDEC(SYMBOL_PROP_BIDLOW,::fabs(value));                     }
   void              SetControlBidLowLevel(const double value)                   { this.SetControlPropertyLEVEL(SYMBOL_PROP_BIDLOW,::fabs(value));                   }
   double            GetValueChangedBidLow(void)                           const { return this.GetControlChangedValue(SYMBOL_PROP_BIDLOW);                           }
   bool              IsIncreasedBidLow(void)                               const { return (bool)this.GetControlFlagINC(SYMBOL_PROP_BIDLOW);                          }
   bool              IsDecreasedBidLow(void)                               const { return (bool)this.GetControlFlagDEC(SYMBOL_PROP_BIDLOW);                          }
   
   //--- Last
   //--- setting the controlled Last price (1) increase, (2) decrease value and (3) control level in points
   //--- getting (4) Bid or Last price change value,
   //--- getting the flag of the Bid or Last price change exceeding the (5) growth, (6) decrease value
   void              SetControlLastInc(const double value)                       { this.SetControlPropertyINC(SYMBOL_PROP_LAST,::fabs(value));                       }
   void              SetControlLastDec(const double value)                       { this.SetControlPropertyDEC(SYMBOL_PROP_LAST,::fabs(value));                       }
   void              SetControlLastLevel(const double value)                     { this.SetControlPropertyLEVEL(SYMBOL_PROP_LAST,::fabs(value));                     }
   double            GetValueChangedLast(void)                             const { return this.GetControlChangedValue(SYMBOL_PROP_LAST);                             }
   bool              IsIncreasedLast(void)                                 const { return (bool)this.GetControlFlagINC(SYMBOL_PROP_LAST);                            }
   bool              IsDecreasedLast(void)                                 const { return (bool)this.GetControlFlagDEC(SYMBOL_PROP_LAST);                            }
   //--- The highest Last price of the day
   //--- setting the controlled maximum Last price (1) increase, (2) decrease value and (3) control level in points
   //--- getting the (4) maximum Bid or Last price change value,
   //--- getting the flag of the maximum Bid or Last price change exceeding the (5) growth, (6) decrease value
   void              SetControlLastHighInc(const double value)                   { this.SetControlPropertyINC(SYMBOL_PROP_LASTHIGH,::fabs(value));                   }
   void              SetControlLastHighDec(const double value)                   { this.SetControlPropertyDEC(SYMBOL_PROP_LASTHIGH,::fabs(value));                   }
   void              SetControlLastHighLevel(const double value)                 { this.SetControlPropertyLEVEL(SYMBOL_PROP_LASTHIGH,::fabs(value));                 }
   double            GetValueChangedLastHigh(void)                         const { return this.GetControlChangedValue(SYMBOL_PROP_LASTHIGH);                         }
   bool              IsIncreasedLastHigh(void)                             const { return (bool)this.GetControlFlagINC(SYMBOL_PROP_LASTHIGH);                        }
   bool              IsDecreasedLastHigh(void)                             const { return (bool)this.GetControlFlagDEC(SYMBOL_PROP_LASTHIGH);                        }
   //--- The lowest Last price of the day
   //--- setting the controlled minimum Last price (1) increase, (2) decrease value and (3) control level in points
   //--- getting the (4) minimum Bid or Last price change value,
   //--- getting the flag of the minimum Bid or Last price change exceeding the (5) growth, (6) decrease value
   void              SetControlLastLowInc(const double value)                    { this.SetControlPropertyINC(SYMBOL_PROP_LASTLOW,::fabs(value));                    }
   void              SetControlLastLowDec(const double value)                    { this.SetControlPropertyDEC(SYMBOL_PROP_LASTLOW,::fabs(value));                    }
   void              SetControlLastLowLevel(const double value)                  { this.SetControlPropertyLEVEL(SYMBOL_PROP_LASTLOW,::fabs(value));                  }
   double            GetValueChangedLastLow(void)                          const { return this.GetControlChangedValue(SYMBOL_PROP_LASTLOW);                          }
   bool              IsIncreasedLastLow(void)                              const { return (bool)this.GetControlFlagINC(SYMBOL_PROP_LASTLOW);                         }
   bool              IsDecreasedLastLow(void)                              const { return (bool)this.GetControlFlagDEC(SYMBOL_PROP_LASTLOW);                         }
   
   //--- Bid/Last
   //--- setting the controlled Bid or Last price (1) increase, (2) decrease value and (3) control level in points
   //--- getting (4) Bid or Last price change value,
   //--- getting the flag of the Bid or Last price change exceeding the (5) growth, (6) decrease value
   void              SetControlBidLastInc(const double value);
   void              SetControlBidLastDec(const double value);
   void              SetControlBidLastLevel(const double value);
   double            GetValueChangedBidLast(void)                          const;
   bool              IsIncreasedBidLast(void)                              const;
   bool              IsDecreasedBidLast(void)                              const;
   //--- Maximum Bid/Last of the day
   //--- setting the controlled maximum Bid or Last price (1) increase, (2) decrease value and (3) control level in points
   //--- getting the (4) maximum Bid or Last price change value,
   //--- getting the flag of the maximum Bid or Last price change exceeding the (5) growth, (6) decrease value
   void              SetControlBidLastHighInc(const double value);
   void              SetControlBidLastHighDec(const double value);
   void              SetControlBidLastHighLevel(const double value);
   double            GetValueChangedBidLastHigh(void)                      const;
   bool              IsIncreasedBidLastHigh(void)                          const;
   bool              IsDecreasedBidLastHigh(void)                          const;
   //--- Minimum Bid/Last of the day
   //--- setting the controlled minimum Bid or Last price (1) increase, (2) decrease value and (3) control level in points
   //--- getting the (4) minimum Bid or Last price change value,
   //--- getting the flag of the minimum Bid or Last price change exceeding the (5) growth, (6) decrease value
   void              SetControlBidLastLowInc(const double value);
   void              SetControlBidLastLowDec(const double value);
   void              SetControlBidLastLowLevev(const double value);
   double            GetValueChangedBidLastLow(void)                       const;
   bool              IsIncreasedBidLastLow(void)                           const;
   bool              IsDecreasedBidLastLow(void)                           const;
   
   //--- Ask
   //--- setting the controlled Ask price (1) increase, (2) decrease value and (3) control level in points
   //--- getting (4) Ask price change value,
   //--- getting the flag of the Ask price change exceeding the (5) growth, (6) decrease value
   void              SetControlAskInc(const double value)                        { this.SetControlPropertyINC(SYMBOL_PROP_ASK,::fabs(value));                        }
   void              SetControlAskDec(const double value)                        { this.SetControlPropertyDEC(SYMBOL_PROP_ASK,::fabs(value));                        }
   void              SetControlAskLevel(const double value)                      { this.SetControlPropertyLEVEL(SYMBOL_PROP_ASK,::fabs(value));                      }
   double            GetValueChangedAsk(void)                              const { return this.GetControlChangedValue(SYMBOL_PROP_ASK);                              }
   bool              IsIncreasedAsk(void)                                  const { return (bool)this.GetControlFlagINC(SYMBOL_PROP_ASK);                             }
   bool              IsDecreasedAsk(void)                                  const { return (bool)this.GetControlFlagDEC(SYMBOL_PROP_ASK);                             }
   //--- Maximum Ask price for the day
   //--- setting the maximum day Ask controlled (1) increase, (2) decrease and (3) control level
   //--- getting (4) the maximum Ask change value within a day,
   //--- getting the flag of the maximum day Ask change exceeding the (5) growth, (6) decrease value
   void              SetControlAskHighInc(const double value)                    { this.SetControlPropertyINC(SYMBOL_PROP_ASKHIGH,::fabs(value));                    }
   void              SetControlAskHighDec(const double value)                    { this.SetControlPropertyDEC(SYMBOL_PROP_ASKHIGH,::fabs(value));                    }
   void              SetControlAskHighLevel(const double value)                  { this.SetControlPropertyLEVEL(SYMBOL_PROP_ASKHIGH,::fabs(value));                  }
   double            GetValueChangedAskHigh(void)                          const { return this.GetControlChangedValue(SYMBOL_PROP_ASKHIGH);                          }
   bool              IsIncreasedAskHigh(void)                              const { return (bool)this.GetControlFlagINC(SYMBOL_PROP_ASKHIGH);                         }
   bool              IsDecreasedAskHigh(void)                              const { return (bool)this.GetControlFlagDEC(SYMBOL_PROP_ASKHIGH);                         }
   //--- Minimum Ask price for the day
   //--- setting the minimum day Ask controlled (1) increase, (2) decrease and (3) control level
   //--- getting (4) the minimum Ask change value within a day,
   //--- getting the flag of the minimum day Ask change exceeding the (5) growth, (6) decrease value
   void              SetControlAskLowInc(const double value)                     { this.SetControlPropertyINC(SYMBOL_PROP_ASKLOW,::fabs(value));                     }
   void              SetControlAskLowDec(const double value)                     { this.SetControlPropertyDEC(SYMBOL_PROP_ASKLOW,::fabs(value));                     }
   void              SetControlAskLowLevel(const double value)                   { this.SetControlPropertyLEVEL(SYMBOL_PROP_ASKLOW,::fabs(value));                   }
   double            GetValueChangedAskLow(void)                           const { return this.GetControlChangedValue(SYMBOL_PROP_ASKLOW);                           }
   bool              IsIncreasedAskLow(void)                               const { return (bool)this.GetControlFlagINC(SYMBOL_PROP_ASKLOW);                          }
   bool              IsDecreasedAskLow(void)                               const { return (bool)this.GetControlFlagDEC(SYMBOL_PROP_ASKLOW);                          }
   //--- Real Volume for the day
   //--- setting the real day volume controlled (1) increase, (2) decrease and (3) control level
   //--- getting (4) the change value of the real day volume,
   //--- getting the flag of the real day volume change exceeding the (5) growth, (6) decrease value
   void              SetControlVolumeRealInc(const double value)                 { this.SetControlPropertyINC(SYMBOL_PROP_VOLUME_REAL,::fabs(value));                }
   void              SetControlVolumeRealDec(const double value)                 { this.SetControlPropertyDEC(SYMBOL_PROP_VOLUME_REAL,::fabs(value));                }
   void              SetControlVolumeRealLevel(const double value)               { this.SetControlPropertyLEVEL(SYMBOL_PROP_VOLUME_REAL,::fabs(value));              }
   double            GetValueChangedVolumeReal(void)                       const { return this.GetControlChangedValue(SYMBOL_PROP_VOLUME_REAL);                      }
   bool              IsIncreasedVolumeReal(void)                           const { return (bool)this.GetControlFlagINC(SYMBOL_PROP_VOLUME_REAL);                     }
   bool              IsDecreasedVolumeReal(void)                           const { return (bool)this.GetControlFlagDEC(SYMBOL_PROP_VOLUME_REAL);                     }
   //--- Maximum real volume for the day
   //--- setting the maximum real day volume controlled (1) increase, (2) decrease and (3) control level
   //--- getting (4) the change value of the maximum real day volume,
   //--- getting the flag of the maximum real day volume change exceeding the (5) growth, (6) decrease value
   void              SetControlVolumeHighRealInc(const double value)             { this.SetControlPropertyINC(SYMBOL_PROP_VOLUMEHIGH_REAL,::fabs(value));            }
   void              SetControlVolumeHighRealDec(const double value)             { this.SetControlPropertyDEC(SYMBOL_PROP_VOLUMEHIGH_REAL,::fabs(value));            }
   void              SetControlVolumeHighRealLevel(const double value)           { this.SetControlPropertyLEVEL(SYMBOL_PROP_VOLUMEHIGH_REAL,::fabs(value));          }
   double            GetValueChangedVolumeHighReal(void)                   const { return this.GetControlChangedValue(SYMBOL_PROP_VOLUMEHIGH_REAL);                  }
   bool              IsIncreasedVolumeHighReal(void)                       const { return (bool)this.GetControlFlagINC(SYMBOL_PROP_VOLUMEHIGH_REAL);                 }
   bool              IsDecreasedVolumeHighReal(void)                       const { return (bool)this.GetControlFlagDEC(SYMBOL_PROP_VOLUMEHIGH_REAL);                 }
   //--- Minimum real volume for the day
   //--- setting the minimum real day volume controlled (1) increase, (2) decrease and (3) control level
   //--- getting (4) the change value of the minimum real day volume,
   //--- getting the flag of the minimum real day volume change exceeding the (5) growth, (6) decrease value
   void              SetControlVolumeLowRealInc(const double value)              { this.SetControlPropertyINC(SYMBOL_PROP_VOLUMELOW_REAL,::fabs(value));             }
   void              SetControlVolumeLowRealDec(const double value)              { this.SetControlPropertyDEC(SYMBOL_PROP_VOLUMELOW_REAL,::fabs(value));             }
   void              SetControlVolumeLowRealLevel(const double value)            { this.SetControlPropertyLEVEL(SYMBOL_PROP_VOLUMELOW_REAL,::fabs(value));           }
   double            GetValueChangedVolumeLowReal(void)                    const { return this.GetControlChangedValue(SYMBOL_PROP_VOLUMELOW_REAL);                   }
   bool              IsIncreasedVolumeLowReal(void)                        const { return (bool)this.GetControlFlagINC(SYMBOL_PROP_VOLUMELOW_REAL);                  }
   bool              IsDecreasedVolumeLowReal(void)                        const { return (bool)this.GetControlFlagDEC(SYMBOL_PROP_VOLUMELOW_REAL);                  }
   //--- Strike price
   //--- setting the controlled strike price (1) increase, (2) decrease value and (3) control level in points
   //--- getting (4) the change value of the strike price,
   //--- getting the flag of the strike price change exceeding the (5) growth, (6) decrease value
   void              SetControlOptionStrikeInc(const double value)               { this.SetControlPropertyINC(SYMBOL_PROP_OPTION_STRIKE,::fabs(value));              }
   void              SetControlOptionStrikeDec(const double value)               { this.SetControlPropertyDEC(SYMBOL_PROP_OPTION_STRIKE,::fabs(value));              }
   void              SetControlOptionStrikeLevel(const double value)             { this.SetControlPropertyLEVEL(SYMBOL_PROP_OPTION_STRIKE,::fabs(value));            }
   double            GetValueChangedOptionStrike(void)                     const { return this.GetControlChangedValue(SYMBOL_PROP_OPTION_STRIKE);                    } 
   bool              IsIncreasedOptionStrike(void)                         const { return (bool)this.GetControlFlagINC(SYMBOL_PROP_OPTION_STRIKE);                   }
   bool              IsDecreasedOptionStrike(void)                         const { return (bool)this.GetControlFlagDEC(SYMBOL_PROP_OPTION_STRIKE);                   }
   //--- Maximum allowed total volume of unidirectional positions and orders
   //--- (1) Setting the control level
   //--- (2) getting the change value of the maximum allowed total volume of unidirectional positions and orders,
   //--- getting the flag of (3) increasing, (4) decreasing the maximum allowed total volume of unidirectional positions and orders
   void              SetControlVolumeLimitLevel(const double value)              { this.SetControlPropertyLEVEL(SYMBOL_PROP_VOLUME_LIMIT,::fabs(value));             }
   double            GetValueChangedVolumeLimit(void)                      const { return this.GetControlChangedValue(SYMBOL_PROP_VOLUME_LIMIT);                     }
   bool              IsIncreasedVolumeLimit(void)                          const { return (bool)this.GetControlFlagINC(SYMBOL_PROP_VOLUME_LIMIT);                    }
   bool              IsDecreasedVolumeLimit(void)                          const { return (bool)this.GetControlFlagDEC(SYMBOL_PROP_VOLUME_LIMIT);                    }
   //---  Swap long
   //--- (1) Setting the control level
   //--- (2) getting the swap long change value,
   //--- getting the flag of (3) increasing, (4) decreasing the swap long
   void              SetControlSwapLongLevel(const double value)                 { this.SetControlPropertyLEVEL(SYMBOL_PROP_SWAP_LONG,::fabs(value));                }
   double            GetValueChangedSwapLong(void)                         const { return this.GetControlChangedValue(SYMBOL_PROP_SWAP_LONG);                        }
   bool              IsIncreasedSwapLong(void)                             const { return (bool)this.GetControlFlagINC(SYMBOL_PROP_SWAP_LONG);                       }
   bool              IsDecreasedSwapLong(void)                             const { return (bool)this.GetControlFlagDEC(SYMBOL_PROP_SWAP_LONG);                       }
   //---  Swap short
   //--- (1) Setting the control level
   //--- (2) getting the swap short change value,
   //--- getting the flag of (3) increasing, (4) decreasing the swap short
   void              SetControlSwapShortLevel(const double value)                { this.SetControlPropertyLEVEL(SYMBOL_PROP_SWAP_SHORT,::fabs(value));               }
   double            GetValueChangedSwapShort(void)                        const { return this.GetControlChangedValue(SYMBOL_PROP_SWAP_SHORT);                       }
   bool              IsIncreasedSwapShort(void)                            const { return (bool)this.GetControlFlagINC(SYMBOL_PROP_SWAP_SHORT);                      }
   bool              IsDecreasedSwapShort(void)                            const { return (bool)this.GetControlFlagDEC(SYMBOL_PROP_SWAP_SHORT);                      }
   //--- The total volume of deals in the current session
   //--- setting the controlled value of (1) growth, (2) decrease and (3) control level of the total volume of deals during the current session
   //--- getting (4) the total deal volume change value in the current session,
   //--- getting the flag of the total deal volume change during the current session exceeding the (5) growth, (6) decrease value
   void              SetControlSessionVolumeInc(const double value)              { this.SetControlPropertyINC(SYMBOL_PROP_SESSION_VOLUME,::fabs(value));             }
   void              SetControlSessionVolumeDec(const double value)              { this.SetControlPropertyDEC(SYMBOL_PROP_SESSION_VOLUME,::fabs(value));             }
   void              SetControlSessionVolumeLevel(const double value)            { this.SetControlPropertyLEVEL(SYMBOL_PROP_SESSION_VOLUME,::fabs(value));           }
   double            GetValueChangedSessionVolume(void)                    const { return this.GetControlChangedValue(SYMBOL_PROP_SESSION_VOLUME);                   }
   bool              IsIncreasedSessionVolume(void)                        const { return (bool)this.GetControlFlagINC(SYMBOL_PROP_SESSION_VOLUME);                  }
   bool              IsDecreasedSessionVolume(void)                        const { return (bool)this.GetControlFlagDEC(SYMBOL_PROP_SESSION_VOLUME);                  }
   //--- The total turnover in the current session
   //--- setting the controlled value of (1) growth, (2) decrease and (3) control level of the total turnover during the current session
   //--- getting (4) the total turnover change value in the current session,
   //--- getting the flag of the total turnover change during the current session exceeding the (5) growth, (6) decrease value
   void              SetControlSessionTurnoverInc(const double value)            { this.SetControlPropertyINC(SYMBOL_PROP_SESSION_TURNOVER,::fabs(value));           }
   void              SetControlSessionTurnoverDec(const double value)            { this.SetControlPropertyDEC(SYMBOL_PROP_SESSION_TURNOVER,::fabs(value));           }
   void              SetControlSessionTurnoverLevel(const double value)          { this.SetControlPropertyLEVEL(SYMBOL_PROP_SESSION_TURNOVER,::fabs(value));         }
   double            GetValueChangedSessionTurnover(void)                  const { return this.GetControlChangedValue(SYMBOL_PROP_SESSION_TURNOVER);                 }
   bool              IsIncreasedSessionTurnover(void)                      const { return (bool)this.GetControlFlagINC(SYMBOL_PROP_SESSION_TURNOVER);                }
   bool              IsDecreasedSessionTurnover(void)                      const { return (bool)this.GetControlFlagDEC(SYMBOL_PROP_SESSION_TURNOVER);                }
   //--- The total volume of open positions
   //--- setting the controlled value of (1) growth, (2) decrease and (3) control level of the total volume of open positions during the current session
   //--- getting (4) the change value of the open positions total volume in the current session,
   //--- getting the flag of the open positions total volume change during the current session exceeding the (5) growth, (6) decrease value
   void              SetControlSessionInterestInc(const double value)            { this.SetControlPropertyINC(SYMBOL_PROP_SESSION_INTEREST,::fabs(value));           }
   void              SetControlSessionInterestDec(const double value)            { this.SetControlPropertyDEC(SYMBOL_PROP_SESSION_INTEREST,::fabs(value));           }
   void              SetControlSessionInterestLevel(const double value)          { this.SetControlPropertyLEVEL(SYMBOL_PROP_SESSION_INTEREST,::fabs(value));         }
   double            GetValueChangedSessionInterest(void)                  const { return this.GetControlChangedValue(SYMBOL_PROP_SESSION_INTEREST);                 }
   bool              IsIncreasedSessionInterest(void)                      const { return (bool)this.GetControlFlagINC(SYMBOL_PROP_SESSION_INTEREST);                }
   bool              IsDecreasedSessionInterest(void)                      const { return (bool)this.GetControlFlagDEC(SYMBOL_PROP_SESSION_INTEREST);                }
   //--- The total volume of Buy orders at the moment
   //--- setting the controlled value of (1) growth, (2) decrease and (3) control level of the current total buy order volume
   //--- getting (4) the change value of the current total buy order volume,
   //--- getting the flag of the current total buy orders' volume change exceeding the (5) growth, (6) decrease value
   void              SetControlSessionBuyOrdVolumeInc(const double value)        { this.SetControlPropertyINC(SYMBOL_PROP_SESSION_BUY_ORDERS_VOLUME,::fabs(value));  }
   void              SetControlSessionBuyOrdVolumeDec(const double value)        { this.SetControlPropertyDEC(SYMBOL_PROP_SESSION_BUY_ORDERS_VOLUME,::fabs(value));  }
   void              SetControlSessionBuyOrdVolumeLevel(const double value)      { this.SetControlPropertyLEVEL(SYMBOL_PROP_SESSION_BUY_ORDERS_VOLUME,::fabs(value));}
   double            GetValueChangedSessionBuyOrdVolume(void)              const { return this.GetControlChangedValue(SYMBOL_PROP_SESSION_BUY_ORDERS_VOLUME);        }
   bool              IsIncreasedSessionBuyOrdVolume(void)                  const { return (bool)this.GetControlFlagINC(SYMBOL_PROP_SESSION_BUY_ORDERS_VOLUME);       }
   bool              IsDecreasedSessionBuyOrdVolume(void)                  const { return (bool)this.GetControlFlagDEC(SYMBOL_PROP_SESSION_BUY_ORDERS_VOLUME);       }
   //--- The total volume of Sell orders at the moment
   //--- setting the controlled value of (1) growth, (2) decrease and (3) control level of the current total sell order volume
   //--- getting (4) the change value of the current total sell order volume,
   //--- getting the flag of the current total sell orders' volume change exceeding the (5) growth, (6) decrease value
   void              SetControlSessionSellOrdVolumeInc(const double value)       { this.SetControlPropertyINC(SYMBOL_PROP_SESSION_SELL_ORDERS_VOLUME,::fabs(value)); }
   void              SetControlSessionSellOrdVolumeDec(const double value)       { this.SetControlPropertyDEC(SYMBOL_PROP_SESSION_SELL_ORDERS_VOLUME,::fabs(value)); }
   void              SetControlSessionSellOrdVolumeLevel(const double value)     { this.SetControlPropertyLEVEL(SYMBOL_PROP_SESSION_SELL_ORDERS_VOLUME,::fabs(value));}
   double            GetValueChangedSessionSellOrdVolume(void)             const { return this.GetControlChangedValue(SYMBOL_PROP_SESSION_SELL_ORDERS_VOLUME);       }
   bool              IsIncreasedSessionSellOrdVolume(void)                 const { return (bool)this.GetControlFlagINC(SYMBOL_PROP_SESSION_SELL_ORDERS_VOLUME);      }
   bool              IsDecreasedSessionSellOrdVolume(void)                 const { return (bool)this.GetControlFlagDEC(SYMBOL_PROP_SESSION_SELL_ORDERS_VOLUME);      }
   //--- Session open price
   //--- setting the controlled session open price (1) increase, (2) decrease and (3) control value
   //--- getting (4) the change value of the session open price,
   //--- getting the flag of the session open price change exceeding the (5) growth, (6) decrease value
   void              SetControlSessionPriceOpenInc(const double value)           { this.SetControlPropertyINC(SYMBOL_PROP_SESSION_OPEN,::fabs(value));               }
   void              SetControlSessionPriceOpenDec(const double value)           { this.SetControlPropertyDEC(SYMBOL_PROP_SESSION_OPEN,::fabs(value));               }
   void              SetControlSessionPriceOpenLevel(const double value)         { this.SetControlPropertyLEVEL(SYMBOL_PROP_SESSION_OPEN,::fabs(value));             }
   double            GetValueChangedSessionPriceOpen(void)                 const { return this.GetControlChangedValue(SYMBOL_PROP_SESSION_OPEN);                     }
   bool              IsIncreasedSessionPriceOpen(void)                     const { return (bool)this.GetControlFlagINC(SYMBOL_PROP_SESSION_OPEN);                    }
   bool              IsDecreasedSessionPriceOpen(void)                     const { return (bool)this.GetControlFlagDEC(SYMBOL_PROP_SESSION_OPEN);                    }
   //--- Session close price
   //--- setting the controlled session close price (1) increase, (2) decrease and (3) control value
   //--- getting (4) the change value of the session close price,
   //--- getting the flag of the session close price change exceeding the (5) growth, (6) decrease value
   void              SetControlSessionPriceCloseInc(const double value)          { this.SetControlPropertyINC(SYMBOL_PROP_SESSION_CLOSE,::fabs(value));              }
   void              SetControlSessionPriceCloseDec(const double value)          { this.SetControlPropertyDEC(SYMBOL_PROP_SESSION_CLOSE,::fabs(value));              }
   void              SetControlSessionPriceCloseLevel(const double value)        { this.SetControlPropertyLEVEL(SYMBOL_PROP_SESSION_CLOSE,::fabs(value));            }
   double            GetValueChangedSessionPriceClose(void)                const { return this.GetControlChangedValue(SYMBOL_PROP_SESSION_CLOSE);                    }
   bool              IsIncreasedSessionPriceClose(void)                    const { return (bool)this.GetControlFlagINC(SYMBOL_PROP_SESSION_CLOSE);                   }
   bool              IsDecreasedSessionPriceClose(void)                    const { return (bool)this.GetControlFlagDEC(SYMBOL_PROP_SESSION_CLOSE);                   }
   //--- The average weighted session price
   //--- setting the controlled session average weighted price (1) increase, (2) decrease and (3) control value
   //--- getting (4) the change value of the average weighted session price,
   //--- getting the flag of the average weighted session price change exceeding the (5) growth, (6) decrease value
   void              SetControlSessionPriceAWInc(const double value)             { this.SetControlPropertyINC(SYMBOL_PROP_SESSION_AW,::fabs(value));                 }
   void              SetControlSessionPriceAWDec(const double value)             { this.SetControlPropertyDEC(SYMBOL_PROP_SESSION_AW,::fabs(value));                 }
   void              SetControlSessionPriceAWLevel(const double value)           { this.SetControlPropertyLEVEL(SYMBOL_PROP_SESSION_AW,::fabs(value));               }
   double            GetValueChangedSessionPriceAW(void)                   const { return this.GetControlChangedValue(SYMBOL_PROP_SESSION_AW);                       }
   bool              IsIncreasedSessionPriceAW(void)                       const { return (bool)this.GetControlFlagINC(SYMBOL_PROP_SESSION_AW);                      }
   bool              IsDecreasedSessionPriceAW(void)                       const { return (bool)this.GetControlFlagDEC(SYMBOL_PROP_SESSION_AW);                      }
//---

Vejamos a implementação de métodos declarados e de alterações nos existentes.

Faremos pequenas alterações no construtor da classe:

//+------------------------------------------------------------------+
//| Closed parametric constructor                                    |
//+------------------------------------------------------------------+
CSymbol::CSymbol(ENUM_SYMBOL_STATUS symbol_status,const string name,const int index)
  {
   this.m_name=name;
   this.m_type=COLLECTION_SYMBOLS_ID;
   if(!this.Exist())
     {
      ::Print(DFUN_ERR_LINE,"\"",this.m_name,"\"",": ",TextByLanguage("Ошибка. Такого символа нет на сервере","Error. No such symbol on the server"));
      this.m_global_error=ERR_MARKET_UNKNOWN_SYMBOL;
     }
   bool select=::SymbolInfoInteger(this.m_name,SYMBOL_SELECT);
   ::ResetLastError();
   if(!select)
     {
      if(!this.SetToMarketWatch())
        {
         this.m_global_error=::GetLastError();
         ::Print(DFUN_ERR_LINE,"\"",this.m_name,"\": ",TextByLanguage("Не удалось поместить в обзор рынка. Ошибка: ","Failed to put in market watch. Error: "),this.m_global_error);
        }
     }
   ::ResetLastError();
   if(!::SymbolInfoTick(this.m_name,this.m_tick))
     {
      this.m_global_error=::GetLastError();
      ::Print(DFUN_ERR_LINE,"\"",this.m_name,"\": ",TextByLanguage("Не удалось получить текущие цены. Ошибка: ","Could not get current prices. Error: "),this.m_global_error);
     }
//--- Initialize control data
   this.SetControlDataArraySizeLong(SYMBOL_PROP_INTEGER_TOTAL); 
   this.SetControlDataArraySizeDouble(SYMBOL_PROP_DOUBLE_TOTAL);
   this.ResetChangesParams();                                   
   this.ResetControlsParams();                                  
   
//--- Initialize symbol data
   this.Reset();
   this.InitMarginRates();
#ifdef __MQL5__
   ::ResetLastError();
   if(!this.MarginRates())
     {
      this.m_global_error=::GetLastError();
      ::Print(DFUN_ERR_LINE,this.Name(),": ",TextByLanguage("Не удалось получить коэффициенты взимания маржи. Ошибка: ","Failed to get margin rates. Error: "),this.m_global_error);
      return;
     }
#endif 
   
//--- Save integer properties
   this.m_long_prop[SYMBOL_PROP_STATUS]                                             = symbol_status;
   this.m_long_prop[SYMBOL_PROP_INDEX_MW]                                           = index;
   this.m_long_prop[SYMBOL_PROP_VOLUME]                                             = (long)this.m_tick.volume;
   this.m_long_prop[SYMBOL_PROP_SELECT]                                             = ::SymbolInfoInteger(this.m_name,SYMBOL_SELECT);
   this.m_long_prop[SYMBOL_PROP_VISIBLE]                                            = ::SymbolInfoInteger(this.m_name,SYMBOL_VISIBLE);
   this.m_long_prop[SYMBOL_PROP_SESSION_DEALS]                                      = ::SymbolInfoInteger(this.m_name,SYMBOL_SESSION_DEALS);
   this.m_long_prop[SYMBOL_PROP_SESSION_BUY_ORDERS]                                 = ::SymbolInfoInteger(this.m_name,SYMBOL_SESSION_BUY_ORDERS);
   this.m_long_prop[SYMBOL_PROP_SESSION_SELL_ORDERS]                                = ::SymbolInfoInteger(this.m_name,SYMBOL_SESSION_SELL_ORDERS);
   this.m_long_prop[SYMBOL_PROP_VOLUMEHIGH]                                         = ::SymbolInfoInteger(this.m_name,SYMBOL_VOLUMEHIGH);
   this.m_long_prop[SYMBOL_PROP_VOLUMELOW]                                          = ::SymbolInfoInteger(this.m_name,SYMBOL_VOLUMELOW);
   this.m_long_prop[SYMBOL_PROP_DIGITS]                                             = ::SymbolInfoInteger(this.m_name,SYMBOL_DIGITS);
   this.m_long_prop[SYMBOL_PROP_SPREAD]                                             = ::SymbolInfoInteger(this.m_name,SYMBOL_SPREAD);
   this.m_long_prop[SYMBOL_PROP_SPREAD_FLOAT]                                       = ::SymbolInfoInteger(this.m_name,SYMBOL_SPREAD_FLOAT);
   this.m_long_prop[SYMBOL_PROP_TICKS_BOOKDEPTH]                                    = ::SymbolInfoInteger(this.m_name,SYMBOL_TICKS_BOOKDEPTH);
   this.m_long_prop[SYMBOL_PROP_TRADE_MODE]                                         = ::SymbolInfoInteger(this.m_name,SYMBOL_TRADE_MODE);
   this.m_long_prop[SYMBOL_PROP_START_TIME]                                         = ::SymbolInfoInteger(this.m_name,SYMBOL_START_TIME);
   this.m_long_prop[SYMBOL_PROP_EXPIRATION_TIME]                                    = ::SymbolInfoInteger(this.m_name,SYMBOL_EXPIRATION_TIME);
   this.m_long_prop[SYMBOL_PROP_TRADE_STOPS_LEVEL]                                  = ::SymbolInfoInteger(this.m_name,SYMBOL_TRADE_STOPS_LEVEL);
   this.m_long_prop[SYMBOL_PROP_TRADE_FREEZE_LEVEL]                                 = ::SymbolInfoInteger(this.m_name,SYMBOL_TRADE_FREEZE_LEVEL);
   this.m_long_prop[SYMBOL_PROP_TRADE_EXEMODE]                                      = ::SymbolInfoInteger(this.m_name,SYMBOL_TRADE_EXEMODE);
   this.m_long_prop[SYMBOL_PROP_SWAP_ROLLOVER3DAYS]                                 = ::SymbolInfoInteger(this.m_name,SYMBOL_SWAP_ROLLOVER3DAYS);
   this.m_long_prop[SYMBOL_PROP_TIME]                                               = this.TickTime();
   this.m_long_prop[SYMBOL_PROP_EXIST]                                              = this.SymbolExists();
   this.m_long_prop[SYMBOL_PROP_CUSTOM]                                             = this.SymbolCustom();
   this.m_long_prop[SYMBOL_PROP_MARGIN_HEDGED_USE_LEG]                              = this.SymbolMarginHedgedUseLEG();
   this.m_long_prop[SYMBOL_PROP_ORDER_MODE]                                         = this.SymbolOrderMode();
   this.m_long_prop[SYMBOL_PROP_FILLING_MODE]                                       = this.SymbolOrderFillingMode();
   this.m_long_prop[SYMBOL_PROP_EXPIRATION_MODE]                                    = this.SymbolExpirationMode();
   this.m_long_prop[SYMBOL_PROP_ORDER_GTC_MODE]                                     = this.SymbolOrderGTCMode();
   this.m_long_prop[SYMBOL_PROP_OPTION_MODE]                                        = this.SymbolOptionMode();
   this.m_long_prop[SYMBOL_PROP_OPTION_RIGHT]                                       = this.SymbolOptionRight();
   this.m_long_prop[SYMBOL_PROP_BACKGROUND_COLOR]                                   = this.SymbolBackgroundColor();
   this.m_long_prop[SYMBOL_PROP_CHART_MODE]                                         = this.SymbolChartMode();
   this.m_long_prop[SYMBOL_PROP_TRADE_CALC_MODE]                                    = this.SymbolCalcMode();
   this.m_long_prop[SYMBOL_PROP_SWAP_MODE]                                          = this.SymbolSwapMode();
//--- Save real properties
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_ASKHIGH)]                          = ::SymbolInfoDouble(this.m_name,SYMBOL_ASKHIGH);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_ASKLOW)]                           = ::SymbolInfoDouble(this.m_name,SYMBOL_ASKLOW);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_LASTHIGH)]                         = ::SymbolInfoDouble(this.m_name,SYMBOL_LASTHIGH);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_LASTLOW)]                          = ::SymbolInfoDouble(this.m_name,SYMBOL_LASTLOW);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_POINT)]                            = ::SymbolInfoDouble(this.m_name,SYMBOL_POINT);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_TICK_VALUE)]                 = ::SymbolInfoDouble(this.m_name,SYMBOL_TRADE_TICK_VALUE);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_TICK_VALUE_PROFIT)]          = ::SymbolInfoDouble(this.m_name,SYMBOL_TRADE_TICK_VALUE_PROFIT);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_TICK_VALUE_LOSS)]            = ::SymbolInfoDouble(this.m_name,SYMBOL_TRADE_TICK_VALUE_LOSS);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_TICK_SIZE)]                  = ::SymbolInfoDouble(this.m_name,SYMBOL_TRADE_TICK_SIZE);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_CONTRACT_SIZE)]              = ::SymbolInfoDouble(this.m_name,SYMBOL_TRADE_CONTRACT_SIZE);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_VOLUME_MIN)]                       = ::SymbolInfoDouble(this.m_name,SYMBOL_VOLUME_MIN);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_VOLUME_MAX)]                       = ::SymbolInfoDouble(this.m_name,SYMBOL_VOLUME_MAX);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_VOLUME_STEP)]                      = ::SymbolInfoDouble(this.m_name,SYMBOL_VOLUME_STEP);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_VOLUME_LIMIT)]                     = ::SymbolInfoDouble(this.m_name,SYMBOL_VOLUME_LIMIT);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SWAP_LONG)]                        = ::SymbolInfoDouble(this.m_name,SYMBOL_SWAP_LONG);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SWAP_SHORT)]                       = ::SymbolInfoDouble(this.m_name,SYMBOL_SWAP_SHORT);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_INITIAL)]                   = ::SymbolInfoDouble(this.m_name,SYMBOL_MARGIN_INITIAL);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_MAINTENANCE)]               = ::SymbolInfoDouble(this.m_name,SYMBOL_MARGIN_MAINTENANCE);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_VOLUME)]                   = ::SymbolInfoDouble(this.m_name,SYMBOL_SESSION_VOLUME);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_TURNOVER)]                 = ::SymbolInfoDouble(this.m_name,SYMBOL_SESSION_TURNOVER);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_INTEREST)]                 = ::SymbolInfoDouble(this.m_name,SYMBOL_SESSION_INTEREST);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_BUY_ORDERS_VOLUME)]        = ::SymbolInfoDouble(this.m_name,SYMBOL_SESSION_BUY_ORDERS_VOLUME);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_SELL_ORDERS_VOLUME)]       = ::SymbolInfoDouble(this.m_name,SYMBOL_SESSION_SELL_ORDERS_VOLUME);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_OPEN)]                     = ::SymbolInfoDouble(this.m_name,SYMBOL_SESSION_OPEN);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_CLOSE)]                    = ::SymbolInfoDouble(this.m_name,SYMBOL_SESSION_CLOSE);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_AW)]                       = ::SymbolInfoDouble(this.m_name,SYMBOL_SESSION_AW);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_PRICE_SETTLEMENT)]         = ::SymbolInfoDouble(this.m_name,SYMBOL_SESSION_PRICE_SETTLEMENT);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_PRICE_LIMIT_MIN)]          = ::SymbolInfoDouble(this.m_name,SYMBOL_SESSION_PRICE_LIMIT_MIN);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_PRICE_LIMIT_MAX)]          = ::SymbolInfoDouble(this.m_name,SYMBOL_SESSION_PRICE_LIMIT_MAX);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_BID)]                              = this.m_tick.bid;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_ASK)]                              = this.m_tick.ask;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_LAST)]                             = this.m_tick.last;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_BIDHIGH)]                          = this.SymbolBidHigh();
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_BIDLOW)]                           = this.SymbolBidLow();
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_VOLUME_REAL)]                      = this.SymbolVolumeReal();
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_VOLUMEHIGH_REAL)]                  = this.SymbolVolumeHighReal();
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_VOLUMELOW_REAL)]                   = this.SymbolVolumeLowReal();
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_OPTION_STRIKE)]                    = this.SymbolOptionStrike();
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_ACCRUED_INTEREST)]           = this.SymbolTradeAccruedInterest();
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_FACE_VALUE)]                 = this.SymbolTradeFaceValue();
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_LIQUIDITY_RATE)]             = this.SymbolTradeLiquidityRate();
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_HEDGED)]                    = this.SymbolMarginHedged();
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_LONG_INITIAL)]              = this.m_margin_rate.Long.Initial;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_BUY_STOP_INITIAL)]          = this.m_margin_rate.BuyStop.Initial;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_BUY_LIMIT_INITIAL)]         = this.m_margin_rate.BuyLimit.Initial;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_BUY_STOPLIMIT_INITIAL)]     = this.m_margin_rate.BuyStopLimit.Initial;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_LONG_MAINTENANCE)]          = this.m_margin_rate.Long.Maintenance;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_BUY_STOP_MAINTENANCE)]      = this.m_margin_rate.BuyStop.Maintenance;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_BUY_LIMIT_MAINTENANCE)]     = this.m_margin_rate.BuyLimit.Maintenance;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_BUY_STOPLIMIT_MAINTENANCE)] = this.m_margin_rate.BuyStopLimit.Maintenance;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_SHORT_INITIAL)]             = this.m_margin_rate.Short.Initial;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_SELL_STOP_INITIAL)]         = this.m_margin_rate.SellStop.Initial;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_SELL_LIMIT_INITIAL)]        = this.m_margin_rate.SellLimit.Initial;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_SELL_STOPLIMIT_INITIAL)]    = this.m_margin_rate.SellStopLimit.Initial;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_SHORT_MAINTENANCE)]         = this.m_margin_rate.Short.Maintenance;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_SELL_STOP_MAINTENANCE)]     = this.m_margin_rate.SellStop.Maintenance;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_SELL_LIMIT_MAINTENANCE)]    = this.m_margin_rate.SellLimit.Maintenance;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_SELL_STOPLIMIT_MAINTENANCE)]= this.m_margin_rate.SellStopLimit.Maintenance;
//--- Save string properties
   this.m_string_prop[this.IndexProp(SYMBOL_PROP_NAME)]                             = this.m_name;
   this.m_string_prop[this.IndexProp(SYMBOL_PROP_CURRENCY_BASE)]                    = ::SymbolInfoString(this.m_name,SYMBOL_CURRENCY_BASE);
   this.m_string_prop[this.IndexProp(SYMBOL_PROP_CURRENCY_PROFIT)]                  = ::SymbolInfoString(this.m_name,SYMBOL_CURRENCY_PROFIT);
   this.m_string_prop[this.IndexProp(SYMBOL_PROP_CURRENCY_MARGIN)]                  = ::SymbolInfoString(this.m_name,SYMBOL_CURRENCY_MARGIN);
   this.m_string_prop[this.IndexProp(SYMBOL_PROP_DESCRIPTION)]                      = ::SymbolInfoString(this.m_name,SYMBOL_DESCRIPTION);
   this.m_string_prop[this.IndexProp(SYMBOL_PROP_PATH)]                             = ::SymbolInfoString(this.m_name,SYMBOL_PATH);
   this.m_string_prop[this.IndexProp(SYMBOL_PROP_BASIS)]                            = this.SymbolBasis();
   this.m_string_prop[this.IndexProp(SYMBOL_PROP_BANK)]                             = this.SymbolBank();
   this.m_string_prop[this.IndexProp(SYMBOL_PROP_ISIN)]                             = this.SymbolISIN();
   this.m_string_prop[this.IndexProp(SYMBOL_PROP_FORMULA)]                          = this.SymbolFormula();
   this.m_string_prop[this.IndexProp(SYMBOL_PROP_PAGE)]                             = this.SymbolPage();
//--- Save additional integer properties
   this.m_long_prop[SYMBOL_PROP_DIGITS_LOTS]                                        = this.SymbolDigitsLot();
//---
   if(!select)
      this.RemoveFromMarketWatch();
  }
//+------------------------------------------------------------------------------------------------------------+
//|Compare CSymbol objects by all possible properties (for sorting lists by a specified symbol object property)|
//+------------------------------------------------------------------------------------------------------------+

Para identificar com precisão eventos de objetos na classe de objeto base, atribuímos o identificador de coleção de símbolos ao tipo de objeto-símbolo e definimos tamanhos de arrays inteiras e de dados reais para rastrear eventos nas propriedades do objeto-símbolo pelo objeto pai. Em seguida, inicializamos os parâmetros mutáveis e de controle em arrays de propriedades inteiras e reais.

Também foi alterado o método Refresh() do objeto-símbolo:

//+------------------------------------------------------------------+
//| Update all symbol data                                           |
//+------------------------------------------------------------------+
void CSymbol::Refresh(void)
  {
//--- Update quote data
   if(!this.RefreshRates())
      return;
#ifdef __MQL5__
   ::ResetLastError();
   if(!this.MarginRates())
     {
      this.m_global_error=::GetLastError();
      return;
     }
#endif 
//--- Initialize event data
   this.m_is_event=false;

   this.m_hash_sum=0;
//--- Update integer properties
   this.m_long_prop[SYMBOL_PROP_SELECT]                                             = ::SymbolInfoInteger(this.m_name,SYMBOL_SELECT);
   this.m_long_prop[SYMBOL_PROP_VISIBLE]                                            = ::SymbolInfoInteger(this.m_name,SYMBOL_VISIBLE);
   this.m_long_prop[SYMBOL_PROP_SESSION_DEALS]                                      = ::SymbolInfoInteger(this.m_name,SYMBOL_SESSION_DEALS);
   this.m_long_prop[SYMBOL_PROP_SESSION_BUY_ORDERS]                                 = ::SymbolInfoInteger(this.m_name,SYMBOL_SESSION_BUY_ORDERS);
   this.m_long_prop[SYMBOL_PROP_SESSION_SELL_ORDERS]                                = ::SymbolInfoInteger(this.m_name,SYMBOL_SESSION_SELL_ORDERS);
   this.m_long_prop[SYMBOL_PROP_VOLUMEHIGH]                                         = ::SymbolInfoInteger(this.m_name,SYMBOL_VOLUMEHIGH);
   this.m_long_prop[SYMBOL_PROP_VOLUMELOW]                                          = ::SymbolInfoInteger(this.m_name,SYMBOL_VOLUMELOW);
   this.m_long_prop[SYMBOL_PROP_SPREAD]                                             = ::SymbolInfoInteger(this.m_name,SYMBOL_SPREAD);
   this.m_long_prop[SYMBOL_PROP_TICKS_BOOKDEPTH]                                    = ::SymbolInfoInteger(this.m_name,SYMBOL_TICKS_BOOKDEPTH);
   this.m_long_prop[SYMBOL_PROP_START_TIME]                                         = ::SymbolInfoInteger(this.m_name,SYMBOL_START_TIME);
   this.m_long_prop[SYMBOL_PROP_EXPIRATION_TIME]                                    = ::SymbolInfoInteger(this.m_name,SYMBOL_EXPIRATION_TIME);
   this.m_long_prop[SYMBOL_PROP_TRADE_STOPS_LEVEL]                                  = ::SymbolInfoInteger(this.m_name,SYMBOL_TRADE_STOPS_LEVEL);
   this.m_long_prop[SYMBOL_PROP_TRADE_FREEZE_LEVEL]                                 = ::SymbolInfoInteger(this.m_name,SYMBOL_TRADE_FREEZE_LEVEL);
   this.m_long_prop[SYMBOL_PROP_BACKGROUND_COLOR]                                   = this.SymbolBackgroundColor();
   
//--- Update real properties
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_TICK_VALUE)]                 = ::SymbolInfoDouble(this.m_name,SYMBOL_TRADE_TICK_VALUE);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_TICK_VALUE_PROFIT)]          = ::SymbolInfoDouble(this.m_name,SYMBOL_TRADE_TICK_VALUE_PROFIT);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_TICK_VALUE_LOSS)]            = ::SymbolInfoDouble(this.m_name,SYMBOL_TRADE_TICK_VALUE_LOSS);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_TICK_SIZE)]                  = ::SymbolInfoDouble(this.m_name,SYMBOL_TRADE_TICK_SIZE);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_CONTRACT_SIZE)]              = ::SymbolInfoDouble(this.m_name,SYMBOL_TRADE_CONTRACT_SIZE);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_VOLUME_MIN)]                       = ::SymbolInfoDouble(this.m_name,SYMBOL_VOLUME_MIN);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_VOLUME_MAX)]                       = ::SymbolInfoDouble(this.m_name,SYMBOL_VOLUME_MAX);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_VOLUME_STEP)]                      = ::SymbolInfoDouble(this.m_name,SYMBOL_VOLUME_STEP);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_VOLUME_LIMIT)]                     = ::SymbolInfoDouble(this.m_name,SYMBOL_VOLUME_LIMIT);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SWAP_LONG)]                        = ::SymbolInfoDouble(this.m_name,SYMBOL_SWAP_LONG);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SWAP_SHORT)]                       = ::SymbolInfoDouble(this.m_name,SYMBOL_SWAP_SHORT);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_INITIAL)]                   = ::SymbolInfoDouble(this.m_name,SYMBOL_MARGIN_INITIAL);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_MAINTENANCE)]               = ::SymbolInfoDouble(this.m_name,SYMBOL_MARGIN_MAINTENANCE);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_VOLUME)]                   = ::SymbolInfoDouble(this.m_name,SYMBOL_SESSION_VOLUME);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_TURNOVER)]                 = ::SymbolInfoDouble(this.m_name,SYMBOL_SESSION_TURNOVER);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_INTEREST)]                 = ::SymbolInfoDouble(this.m_name,SYMBOL_SESSION_INTEREST);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_BUY_ORDERS_VOLUME)]        = ::SymbolInfoDouble(this.m_name,SYMBOL_SESSION_BUY_ORDERS_VOLUME);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_SELL_ORDERS_VOLUME)]       = ::SymbolInfoDouble(this.m_name,SYMBOL_SESSION_SELL_ORDERS_VOLUME);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_OPEN)]                     = ::SymbolInfoDouble(this.m_name,SYMBOL_SESSION_OPEN);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_CLOSE)]                    = ::SymbolInfoDouble(this.m_name,SYMBOL_SESSION_CLOSE);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_AW)]                       = ::SymbolInfoDouble(this.m_name,SYMBOL_SESSION_AW);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_PRICE_SETTLEMENT)]         = ::SymbolInfoDouble(this.m_name,SYMBOL_SESSION_PRICE_SETTLEMENT);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_PRICE_LIMIT_MIN)]          = ::SymbolInfoDouble(this.m_name,SYMBOL_SESSION_PRICE_LIMIT_MIN);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_PRICE_LIMIT_MAX)]          = ::SymbolInfoDouble(this.m_name,SYMBOL_SESSION_PRICE_LIMIT_MAX);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_VOLUME_REAL)]                      = this.SymbolVolumeReal();
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_VOLUMEHIGH_REAL)]                  = this.SymbolVolumeHighReal();
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_VOLUMELOW_REAL)]                   = this.SymbolVolumeLowReal();
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_OPTION_STRIKE)]                    = this.SymbolOptionStrike();
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_ACCRUED_INTEREST)]           = this.SymbolTradeAccruedInterest();
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_FACE_VALUE)]                 = this.SymbolTradeFaceValue();
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_LIQUIDITY_RATE)]             = this.SymbolTradeLiquidityRate();
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_HEDGED)]                    = this.SymbolMarginHedged();
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_LONG_INITIAL)]              = this.m_margin_rate.Long.Initial;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_BUY_STOP_INITIAL)]          = this.m_margin_rate.BuyStop.Initial;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_BUY_LIMIT_INITIAL)]         = this.m_margin_rate.BuyLimit.Initial;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_BUY_STOPLIMIT_INITIAL)]     = this.m_margin_rate.BuyStopLimit.Initial;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_LONG_MAINTENANCE)]          = this.m_margin_rate.Long.Maintenance;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_BUY_STOP_MAINTENANCE)]      = this.m_margin_rate.BuyStop.Maintenance;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_BUY_LIMIT_MAINTENANCE)]     = this.m_margin_rate.BuyLimit.Maintenance;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_BUY_STOPLIMIT_MAINTENANCE)] = this.m_margin_rate.BuyStopLimit.Maintenance;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_SHORT_INITIAL)]             = this.m_margin_rate.Short.Initial;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_SELL_STOP_INITIAL)]         = this.m_margin_rate.SellStop.Initial;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_SELL_LIMIT_INITIAL)]        = this.m_margin_rate.SellLimit.Initial;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_SELL_STOPLIMIT_INITIAL)]    = this.m_margin_rate.SellStopLimit.Initial;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_SHORT_MAINTENANCE)]         = this.m_margin_rate.Short.Maintenance;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_SELL_STOP_MAINTENANCE)]     = this.m_margin_rate.SellStop.Maintenance;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_SELL_LIMIT_MAINTENANCE)]    = this.m_margin_rate.SellLimit.Maintenance;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_SELL_STOPLIMIT_MAINTENANCE)]= this.m_margin_rate.SellStopLimit.Maintenance;
   
//--- Fill in the symbol current data
   for(int i=0;i<SYMBOL_PROP_INTEGER_TOTAL;i++)
      this.m_long_prop_event[i][3]=this.m_long_prop[i];
   for(int i=0;i<SYMBOL_PROP_DOUBLE_TOTAL;i++)
      this.m_double_prop_event[i][3]=this.m_double_prop[i];
   
   CBaseObj::Refresh();
   this.CheckEvents();
  }
//+------------------------------------------------------------------+


Neste caso, como nos livramos da necessidade de criar estruturas para armazenar os estados atuais e anteriores das propriedades do símbolo, aqui foi excluído o preenchimento da estrutura de dados do estado atual do símbolo e, em vez disso, realizamos o preenchimento de arrays de propriedades inteiras e reais no objeto base.
Em seguida, quando os arrays estiverem preenchidos, precisamos chamar o método Refresh() do objeto base CBaseObj, no qual são procuradas as alterações ocorridas e é criada uma lista de eventos básicos do objeto herdeiro.
Depois que a lista de na classe pai é criada (se houver critérios para gerar eventos), nós verificamos eventos básicos usando o método CheckEvents() e, se houver, criamos uma lista de eventos de símbolos.

Implementando o método de verificação de eventos:

//+------------------------------------------------------------------+
//| Check the list of symbol property changes and create an event    |
//+------------------------------------------------------------------+
void CSymbol::CheckEvents(void)
  {
   int total=this.m_list_events_base.Total();
   if(total==0)
      return;
  for(int i=0;i<total;i++)
     {
      CBaseEvent *event=this.GetEventBase(i);
      if(event==NULL)
         continue;
      long lvalue=0;
      this.UshortToLong(this.MSCfromTime(this.TickTime()),0,lvalue);
      this.UshortToLong(event.Reason(),1,lvalue);
      this.UshortToLong(COLLECTION_SYMBOLS_ID,2,lvalue);
      if(this.EventAdd((ushort)event.ID(),lvalue,event.Value(),this.Name()))
         this.m_is_event=true;
     }
  }  
//+------------------------------------------------------------------+

Neste caso, se a lista de eventos básicos estiver vazia , saímos.
No ciclo da lista de eventos básicos obtemos o próximo evento e, se o evento for recebido, criamos um evento de símbolo:

Método para inicializar as variáveis dos dados controlados do símbolo:

//+------------------------------------------------------------------+
//| Initialize the variables of controlled symbol data               |
//+------------------------------------------------------------------+
void CSymbol::InitControlsParams(void)
  {
   this.ResetControlsParams();
  }
//+------------------------------------------------------------------+

Simplesmente chama o método para redefinir as variáveis de valores controlados dos dados do objeto, que consideramos acima.

Métodos para definir valores controlados e sinalizadores das alterações ocorridas e métodos para obter o tamanho das alterações ocorridas e sinalizadores:

//+------------------------------------------------------------------+
//| Set the value of the controlled property increase                |
//+------------------------------------------------------------------+
template<typename T> void CSymbol::SetControlPropertyINC(const int property,const T value)
  {
   if(property<SYMBOL_PROP_INTEGER_TOTAL)
      this.SetControlledValueINC(property,(long)value);
   else
      this.SetControlledValueINC(property,(double)value);
  }  
//+------------------------------------------------------------------+
//| Set the value of the controlled property decrease                |
//+------------------------------------------------------------------+
template<typename T> void CSymbol::SetControlPropertyDEC(const int property,const T value)
  {
   if(property<SYMBOL_PROP_INTEGER_TOTAL)
      this.SetControlledValueDEC(property,(long)value);
   else
      this.SetControlledValueDEC(property,(double)value);
  }
//+------------------------------------------------------------------+
//| Set the value of the controlled property level                   |
//+------------------------------------------------------------------+
template<typename T> void CSymbol::SetControlPropertyLEVEL(const int property,const T value)
  {
   if(property<SYMBOL_PROP_INTEGER_TOTAL)
      this.SetControlledValueLEVEL(property,(long)value);
   else
      this.SetControlledValueLEVEL(property,(double)value);
  }
//+------------------------------------------------------------------+
//| Set the flag of the symbol property value change                 |
//| exceeding the increase value                                     |
//+------------------------------------------------------------------+
template<typename T> void CSymbol::SetControlFlagINC(const int property,const T value)
  {
   if(property<SYMBOL_PROP_INTEGER_TOTAL)
      this.SetControlledFlagINC(property,(long)value);
   else
      this.SetControlledFlagINC(property,(double)value);
  }  
//+------------------------------------------------------------------+
//| Set the flag of the symbol property value change                 |
//| exceeding the decrease value                                     |
//+------------------------------------------------------------------+
template<typename T> void CSymbol::SetControlFlagDEC(const int property,const T value)
  {
   if(property<SYMBOL_PROP_INTEGER_TOTAL)
      this.SetControlledFlagDEC(property,(long)value);
   else
      this.SetControlledFlagDEC(property,(double)value);
  }
//+------------------------------------------------------------------+
//| Set the change value of the controlled symbol property           |
//+------------------------------------------------------------------+
template<typename T> void CSymbol::SetControlChangedValue(const int property,const T value)
  {
   if(property<SYMBOL_PROP_INTEGER_TOTAL)
      this.SetControlledChangedValue(property,(long)value);
   else
      this.SetControlledChangedValue(property,(double)value);
  }
//+------------------------------------------------------------------+
//| Set the Bid or Last price controlled increase                    |
//+------------------------------------------------------------------+
void CSymbol::SetControlBidLastInc(const double value)
  {
   this.SetControlPropertyINC((this.ChartMode()==SYMBOL_CHART_MODE_BID ? SYMBOL_PROP_BID : SYMBOL_PROP_LAST),::fabs(value));
  }
//+------------------------------------------------------------------+
//|Set the Bid or Last price controlled decrease                     |
//+------------------------------------------------------------------+
void CSymbol::SetControlBidLastDec(const double value)
  {
   this.SetControlPropertyDEC((this.ChartMode()==SYMBOL_CHART_MODE_BID ? SYMBOL_PROP_BID : SYMBOL_PROP_LAST),::fabs(value));
  }
//+------------------------------------------------------------------+
//| Set the Bid or Last price control level                          |
//+------------------------------------------------------------------+
void CSymbol::SetControlBidLastLevel(const double value)
  {
   this.SetControlPropertyLEVEL((this.ChartMode()==SYMBOL_CHART_MODE_BID ? SYMBOL_PROP_BID : SYMBOL_PROP_LAST),::fabs(value));
  }
//+------------------------------------------------------------------+
//| Return the Bid or Last price change value                        |
//+------------------------------------------------------------------+
double CSymbol::GetValueChangedBidLast(void) const
  {
   return(this.ChartMode()==SYMBOL_CHART_MODE_BID ? this.GetControlChangedValue(SYMBOL_PROP_BID) : this.GetControlChangedValue(SYMBOL_PROP_LAST));
  }
//+------------------------------------------------------------------+
//| Return the flag of the Bid or Last price change                  |
//| exceeding the increase value                                     |
//+------------------------------------------------------------------+
bool CSymbol::IsIncreasedBidLast(void) const
  {
   return(this.ChartMode()==SYMBOL_CHART_MODE_BID ? (bool)this.GetControlFlagINC(SYMBOL_PROP_BID) : (bool)this.GetControlFlagINC(SYMBOL_PROP_LAST));
  }
//+------------------------------------------------------------------+
//| Return the flag of the Bid or Last price change                  |
//| exceeding the decrease value                                     |
//+------------------------------------------------------------------+
bool CSymbol::IsDecreasedBidLast(void) const
  {
   return(this.ChartMode()==SYMBOL_CHART_MODE_BID ? (bool)this.GetControlFlagDEC(SYMBOL_PROP_BID) : (bool)this.GetControlFlagDEC(SYMBOL_PROP_LAST));
  }
//+------------------------------------------------------------------+
//| Set the controlled increase value                                |
//| of the maximum Bid or Last price                                 |
//+------------------------------------------------------------------+
void CSymbol::SetControlBidLastHighInc(const double value)
  {
   this.SetControlPropertyINC((this.ChartMode()==SYMBOL_CHART_MODE_BID ? SYMBOL_PROP_BIDHIGH : SYMBOL_PROP_LASTHIGH),::fabs(value));
  }
//+------------------------------------------------------------------+
//| Set the controlled decrease value                                |
//| of the maximum Bid or Last price                                 |
//+------------------------------------------------------------------+
void CSymbol::SetControlBidLastHighDec(const double value)
  {
   this.SetControlPropertyDEC((this.ChartMode()==SYMBOL_CHART_MODE_BID ? SYMBOL_PROP_BIDHIGH : SYMBOL_PROP_LASTHIGH),::fabs(value));
  }
//+------------------------------------------------------------------+
//| Set the maximum Bid or Last price control level                  |
//+------------------------------------------------------------------+
void CSymbol::SetControlBidLastHighLevel(const double value)
  {
   this.SetControlPropertyLEVEL((this.ChartMode()==SYMBOL_CHART_MODE_BID ? SYMBOL_PROP_BIDHIGH : SYMBOL_PROP_LASTHIGH),::fabs(value));
  }
//+------------------------------------------------------------------+
//| Return the maximum Bid or Last price change value                |
//+------------------------------------------------------------------+
double CSymbol::GetValueChangedBidLastHigh(void) const
  {
   return(this.ChartMode()==SYMBOL_CHART_MODE_BID ? this.GetControlChangedValue(SYMBOL_PROP_BIDHIGH) : this.GetControlChangedValue(SYMBOL_PROP_LASTHIGH));
  }
//+------------------------------------------------------------------+
//| Return the flag of a change of the maximum                       |
//| Bid or Last price exceeding the increase value                   |
//+------------------------------------------------------------------+
bool CSymbol::IsIncreasedBidLastHigh(void) const
  {
   return(this.ChartMode()==SYMBOL_CHART_MODE_BID ? (bool)this.GetControlFlagINC(SYMBOL_PROP_BIDHIGH) : (bool)this.GetControlFlagINC(SYMBOL_PROP_LASTHIGH));
  }
//+------------------------------------------------------------------+
//| Return the flag of a change of the maximum                       |
//| Bid or Last price exceeding the decrease value                   |
//+------------------------------------------------------------------+
bool CSymbol::IsDecreasedBidLastHigh(void) const
  {
   return(this.ChartMode()==SYMBOL_CHART_MODE_BID ? (bool)this.GetControlFlagDEC(SYMBOL_PROP_BIDHIGH) : (bool)this.GetControlFlagDEC(SYMBOL_PROP_LASTHIGH));
  }
//+------------------------------------------------------------------+
//| Set the controlled increase value                                |
//| of the minimum Bid or Last price                                 |
//+------------------------------------------------------------------+
void CSymbol::SetControlBidLastLowInc(const double value)
  {
   this.SetControlPropertyINC((this.ChartMode()==SYMBOL_CHART_MODE_BID ? SYMBOL_PROP_BIDLOW : SYMBOL_PROP_LASTLOW),::fabs(value));
  }
//+------------------------------------------------------------------+
//| Set the controlled decrease value                                |
//| of the minimum Bid or Last price                                 |
//+------------------------------------------------------------------+
void CSymbol::SetControlBidLastLowDec(const double value)
  {
   this.SetControlPropertyDEC((this.ChartMode()==SYMBOL_CHART_MODE_BID ? SYMBOL_PROP_BIDLOW : SYMBOL_PROP_LASTLOW),::fabs(value));
  }
//+------------------------------------------------------------------+
//| Set the minimum Bid or Last price control level                  |
//+------------------------------------------------------------------+
void CSymbol::SetControlBidLastLowLevev(const double value)
  {
   this.SetControlPropertyLEVEL((this.ChartMode()==SYMBOL_CHART_MODE_BID ? SYMBOL_PROP_BIDLOW : SYMBOL_PROP_LASTLOW),::fabs(value));
  }
//+------------------------------------------------------------------+
//| Return the minimum Bid or Last price change value                |
//+------------------------------------------------------------------+
double CSymbol::GetValueChangedBidLastLow(void) const
  {
   return(this.ChartMode()==SYMBOL_CHART_MODE_BID ? this.GetControlChangedValue(SYMBOL_PROP_BIDLOW) : this.GetControlChangedValue(SYMBOL_PROP_LASTLOW));
  }
//+------------------------------------------------------------------+
//| Return the flag of a change of the minimum                       |
//| Bid or Last price exceeding the increase value                   |
//+------------------------------------------------------------------+
bool CSymbol::IsIncreasedBidLastLow(void) const
  {
   return(this.ChartMode()==SYMBOL_CHART_MODE_BID ? (bool)this.GetControlFlagINC(SYMBOL_PROP_BIDLOW) : (bool)this.GetControlFlagINC(SYMBOL_PROP_LASTLOW));
  }
//+------------------------------------------------------------------+
//| Return the flag of a change of the minimum                       |
//| Bid or Last price exceeding the decrease value                   |
//+------------------------------------------------------------------+
bool CSymbol::IsDecreasedBidLastLow(void) const
  {
   return(this.ChartMode()==SYMBOL_CHART_MODE_BID ? (bool)this.GetControlFlagDEC(SYMBOL_PROP_BIDLOW) : (bool)this.GetControlFlagDEC(SYMBOL_PROP_LASTLOW));
  }
//+------------------------------------------------------------------+

Vimos métodos semelhantes ao finalizar a classe do objeto base, e os métodos que acabamos ver exatamente os que chamamos aqui dependendo da propriedade requerida do objeto-símbolo.

Assim fica concluída a modificação da classe do objeto-símbolo.

Agora resta modificar ligeiramente a classe de coleção de símbolos.
Abrimos o arquivo \MQL5\Include\DoEasy\Collections\ SymbolsCollection.mqh e fazemos as alterações necessárias:

Como não precisamos mais criar enumerações de eventos separadas para cada objeto, definimos o tipo 'int' para a variável "último evento do símbolo" e o método GetLastEvent() em vez do método anterior ENUM_SYMBOL_EVENT:

int m_last_event; // The last event
int GetLastEvent(void) const { return this.m_last_event; }

Como todos os eventos do símbolo (assim como os eventos de qualquer objeto herdeiro) agora são manipulados na classe do objeto base,
renomeamos o método EventDescription() para EventMWDescription() e transferimos a variável com o tipo de enumeração dos eventos da janela Observação do Mercado para o método:

//--- Return the description of the (1) Market Watch window event, (2) mode of working with symbols
   string            EventMWDescription(const ENUM_MW_EVENT event);
   string            ModeSymbolsListDescription(void);

Como os nomes da enumeração foram alterados,
o método para trabalhar com a janela Observação do Mercado passou por pequenas alterações (os nomes da enumeração e o tipo de variável de evento foram alterados):

//+------------------------------------------------------------------+
//| Working with market watch window events                          |
//+------------------------------------------------------------------+
void CSymbolsCollection::MarketWatchEventsControl(const bool send_events=true)
  {
   ::ResetLastError();
//--- If no current prices are received, exit
   if(!::SymbolInfoTick(::Symbol(),this.m_tick))
     {
      this.m_global_error=::GetLastError();
      return;
     }
   uchar array[];
   int sum=0;
   this.m_hash_sum=0;
//--- Calculate the hash sum of all visible symbols in the Market Watch window
   this.m_total_symbols=this.SymbolsTotalVisible();
   //--- In the loop by all Market Watch window symbols
   int total_symbols=::SymbolsTotal(true);
   for(int i=0;i<total_symbols;i++)
     {
      //--- get a symbol name by index
      string name=::SymbolName(i,true);
      //--- skip if invisible
      if(!::SymbolInfoInteger(name,SYMBOL_VISIBLE))
         continue;
      //--- write symbol name (characters) codes to the uchar array
      ::StringToCharArray(name,array);
      //--- in a loop by the resulting array, sum up the values of all array cells creating the symbol code
      for(int j=::ArraySize(array)-1;j>WRONG_VALUE;j--)
         sum+=array[j];
      //--- add the symbol code and the loop index specifying the symbol index in the market watch list to the hash sum
      m_hash_sum+=i+sum;
     }
//--- If sending events is disabled, create the collection list and exit saving the current hash some as the previous one
   if(!send_events)
     {
      //--- Clear the list
      this.m_list_all_symbols.Clear();
      //--- Clear the collection list
      this.CreateSymbolsList(true);
      //--- Clear the market watch window snapshot
      this.CopySymbolsNames();
      //--- save the current hash some as the previous one
      this.m_hash_sum_prev=this.m_hash_sum;
      //--- save the current number of visible symbols as the previous one
      this.m_total_symbol_prev=this.m_total_symbols;
      return;
     }
   
//--- If the hash sum of symbols in the Market Watch window has changed
   if(this.m_hash_sum!=this.m_hash_sum_prev)
     {
      //--- Define the Market Watch window event
      this.m_delta_symbol=this.m_total_symbols-this.m_total_symbol_prev;
      ushort event_id=
        (ushort(
         this.m_total_symbols>this.m_total_symbol_prev ? MARKET_WATCH_EVENT_SYMBOL_ADD :
         this.m_total_symbols<this.m_total_symbol_prev ? MARKET_WATCH_EVENT_SYMBOL_DEL :
         MARKET_WATCH_EVENT_SYMBOL_SORT)
        );
      //--- Adding a symbol to the Market Watch window
      if(event_id==MARKET_WATCH_EVENT_SYMBOL_ADD)
        {
         string name="";
         //--- In the loop by all Market Watch window symbols
         int total=::SymbolsTotal(true), index=WRONG_VALUE;
         for(int i=0;i<total;i++)
           {
            //--- get the symbol name and check its "visibility". Skip it if invisible
            name=::SymbolName(i,true);
            if(!::SymbolInfoInteger(name,SYMBOL_VISIBLE))
               continue;
            //--- If there is no symbol in the collection symbol list yet
            if(!this.IsPresentSymbolInList(name))
              {
               //--- clear the collection list
               this.m_list_all_symbols.Clear();
               //--- recreate the collection list
               this.CreateSymbolsList(true);
               //--- create the symbol collection snapshot
               this.CopySymbolsNames();
               //--- get a new symbol index in the Market Watch window
               index=this.GetSymbolIndexByName(name);
               //--- If the "Adding a new symbol" event is successfully added to the event list 
               if(this.EventAdd(event_id,this.TickTime(),index,name))
                 {
                  //--- send the event to the chart:
                  //--- long value = event time in milliseconds, double value = symbol index, string value = added symbol name
                  ::EventChartCustom(this.m_chart_id,(ushort)event_id,this.TickTime(),index,name);
                 }
              }
           }
         //--- Save the new number of visible symbols in the market watch window
         this.m_total_symbols=this.SymbolsTotalVisible();
        }
      //--- Remove a symbol from the Market Watch window
      else if(event_id==MARKET_WATCH_EVENT_SYMBOL_DEL)
        {
         //--- clear the collection list 
         this.m_list_all_symbols.Clear();
         //--- recreate the collection list
         this.CreateSymbolsList(true);
         //--- In a loop by the market watch window snapshot
         int total=this.m_list_names.Total();
         for(int i=0; i<total;i++)
           {
            //--- get a symbol name 
            string name=this.m_list_names.At(i);
            if(name==NULL)
               continue;
            //--- if no symbol with such a name exists in the collection symbol list
            if(!this.IsPresentSymbolInList(name))
              {
               //--- If the "Removing a symbol" event is successfully added to the event list
               if(this.EventAdd(event_id,this.TickTime(),WRONG_VALUE,name))
                 {
                  //--- send the event to the chart:
                  //--- long value = event tine in milliseconds, double value = -1 for an absent symbol, string value = a removed symbol name
                  ::EventChartCustom(this.m_chart_id,(ushort)event_id,this.TickTime(),WRONG_VALUE,name);
                 }
              }
           }
         //--- Recreate the market watch snapshot
         this.CopySymbolsNames();
         //--- Save the new number of visible symbols in the market watch window
         this.m_total_symbols=this.SymbolsTotalVisible();
        }
      //--- Sorting symbols in the Market Watch window
      else if(event_id==MARKET_WATCH_EVENT_SYMBOL_SORT)
        {
         //--- clear the collection list 
         this.m_list_all_symbols.Clear();
         //--- set sorting of the collection list as sorting by index
         this.m_list_all_symbols.Sort(SORT_BY_SYMBOL_INDEX_MW);
         //--- recreate the collection list
         this.CreateSymbolsList(true);
         //--- get the current symbol index in the Market Watch window
         int index=this.GetSymbolIndexByName(Symbol());
         //--- send the event to the chart:
         //--- long value = event time in milliseconds, double value = current symbol index, string value = current symbol name
         ::EventChartCustom(this.m_chart_id,(ushort)event_id,this.TickTime(),index,::Symbol());
        }
      //--- save the current number of visible symbols as the previous one
      this.m_total_symbol_prev=this.m_total_symbols;
      //--- save the current hash some as the previous one
      this.m_hash_sum_prev=this.m_hash_sum;
     }
  }
//+------------------------------------------------------------------+

O tipo de variável de evento também foi alterado no método para trabalhar com a lista de eventos da coleção de símbolos:

//+------------------------------------------------------------------+
//| Working with the events of the collection symbol list            |
//+------------------------------------------------------------------+
void CSymbolsCollection::SymbolsEventsControl(void)
  {
   this.m_is_event=false;
   this.m_list_events.Clear();
   this.m_list_events.Sort();
   //--- The full update of all collection symbols
   int total=this.m_list_all_symbols.Total();
   for(int i=0;i<total;i++)
     {
      CSymbol *symbol=this.m_list_all_symbols.At(i);
      if(symbol==NULL)
         continue;
      symbol.Refresh();
      if(!symbol.IsEvent())
         continue;
      this.m_is_event=true;
      CArrayObj *list=symbol.GetListEvents();
      if(list==NULL)
         continue;
      this.m_event_code=symbol.GetEventCode();
      int n=list.Total();
      for(int j=0; j<n; j++)
        {
         CEventBaseObj *event=list.At(j);
         if(event==NULL)
            continue;
         ushort event_id=event.ID();
         this.m_last_event=event_id;
         if(this.EventAdd((ushort)event.ID(),event.LParam(),event.DParam(),event.SParam()))
           {
            ::EventChartCustom(this.m_chart_id,(ushort)event_id,event.LParam(),event.DParam(),event.SParam());
           }
        }
     }
  }
//+------------------------------------------------------------------+

No método que retorna uma descrição de string do evento da janela Observação do Mercado, também foram alterados os nomes de constantes de enumeração de eventos:

//+------------------------------------------------------------------+
//| Return the Market Watch window event description                 |
//+------------------------------------------------------------------+
string CSymbolsCollection::EventMWDescription(const ENUM_MW_EVENT event)
  {
   return
     (
      event==MARKET_WATCH_EVENT_SYMBOL_ADD   ?  TextByLanguage("В окно \"Обзор рынка\" добавлен символ","Added symbol to \"Market Watch\" window")                                     :
      event==MARKET_WATCH_EVENT_SYMBOL_DEL   ?  TextByLanguage("Из окна \"Обзор рынка\" удалён символ","Removed symbol from \"Market Watch\" window")                                       :
      event==MARKET_WATCH_EVENT_SYMBOL_SORT  ?  TextByLanguage("Изменено расположение символов в окне \"Обзор рынка\"","Changed arrangement of symbols in \"Market Watch\" window")  :
      EnumToString(event)
     );
  }
//+------------------------------------------------------------------+

Agora modificamos a classe CEngine. Abrimos o arquivo \MQL5\Include\DoEasy\Engine.mqh e fazemos as alterações necessárias:

A variável que armazena o último evento nas propriedades do símbolo e o método que retorna o valor da variável também serão do tipo int:

   int m_last_symbol_event;  // Last event in the symbol properties
   int LastSymbolsEvent(void) const { return this.m_last_symbol_event; }

Na seção pública da classe, adicionamos a declaração do método que recupera um número ushort do contêiner long segundo o índice de armazenamento especificado no parâmetro long do número ushort:

//--- Retrieve a necessary 'ushort' number from the packed 'long' value
   ushort               LongToUshortFromByte(const long source_value,const uchar index) const;

Além disso, escrevemos os três métodos retornando imediatamente milissegundos de evento, motivo e origem do parâmetro long do evento:

//--- Return event (1) milliseconds, (2) reason and (3) source from its 'long' value
   ushort               EventMSC(const long lparam)               const { return this.LongToUshortFromByte(lparam,0);         }
   ushort               EventReason(const long lparam)            const { return this.LongToUshortFromByte(lparam,1);         }
   ushort               EventSource(const long lparam)            const { return this.LongToUshortFromByte(lparam,2);         }

Como o valor zero é a primeira propriedade inteira de qualquer objeto, na lista de inicialização do seu construtor da classe alteramos o valor de inicialização da variável que armazena o último evento do símbolo, assim, agora ele será inicializado com um valor negativo:

//+------------------------------------------------------------------+
//| CEngine constructor                                              |
//+------------------------------------------------------------------+
CEngine::CEngine() : m_first_start(true),
                     m_last_trade_event(TRADE_EVENT_NO_EVENT),
                     m_last_account_event(ACCOUNT_EVENT_NO_EVENT),
                     m_last_symbol_event(WRONG_VALUE),
                     m_global_error(ERR_SUCCESS)
  {

Implementação do método de recuperação de um número ushort do contêiner long pelo índice de bytes de sua localização no contêiner long:

//+------------------------------------------------------------------+
//| Retrieve a necessary 'ushort' number from the packed 'long' value|
//+------------------------------------------------------------------+
ushort CEngine::LongToUshortFromByte(const long source_value,const uchar index) const
  {
   if(index>3)
     {
      ::Print(DFUN,TextByLanguage("Ошибка. Значение \"index\" должно быть в пределах 0 - 3","Error. \"index\" value should be between 0 - 3"));
      return 0;
     }
   long res=source_value>>(16*index);
   return ushort(res &=0xFFFF);
  }
//+------------------------------------------------------------------+

O método recebe um valor long do qual deve ser extraído um número de ushort e o índice de bytes em que está localizado o número (a tabela de localização dos números de ushort no contêiner long foi considerada acima). Em seguida, é verificada a validade da especificação do índice. Se o índice for inválido, será exibida uma mensagem de erro e será retornado 0.
Em seguida, deslocamos os bits de número long 16 * bits de índice para a direita, aplicamos uma máscara para "extinguir" os bits maiores restantes e retornamos o número ushort extraído dessa maneira.

Para trabalhar em MQL4, precisamos informar o compilador sobre o erro de tamanho zero do array ERR_ZEROSIZE_ARRAY.
O erro mais adequado para o tamanho zero do array daqueles conhecidos pelo compilador MQL4 é "array inválido". Vamos configurá-lo como uma alternativa ao erro de tamanho zero do array.

Abrimos o arquivo \MQL5\Include\DoEasy\ToMQL4.mqh e fazemos as alterações necessárias:

//+------------------------------------------------------------------+
//|                                                       ToMQL4.mqh |
//|              Copyright 2017, Artem A. Trishkin, Skype artmedia70 |
//|                         https://www.mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2017, Artem A. Trishkin, Skype artmedia70"
#property link      "https://www.mql5.com/en/users/artmedia70"
#property strict
#ifdef __MQL4__
//+------------------------------------------------------------------+
//| Error codes                                                      |
//+------------------------------------------------------------------+
#define ERR_SUCCESS                       (ERR_NO_ERROR)
#define ERR_MARKET_UNKNOWN_SYMBOL         (ERR_UNKNOWN_SYMBOL)
#define ERR_ZEROSIZE_ARRAY                (ERR_ARRAY_INVALID)
//+------------------------------------------------------------------+

Essas são todas as alterações que precisamos fazer para começar a trabalhar com símbolos tendo em mente a nova funcionalidade de evento fornecida pelo objeto CBaseObj a todos os seus herdeiros.


Teste da funcionalidade do evento do objeto base de todos os objetos da biblioteca

Para testar a nova funcionalidade de evento, pegamos no EA do último artigo e o salvamos com o novo nome TestDoEasyPart17.mq5 na pasta MQL5\Experts\TestDoEasy\Part17.

Testaremos a variação do spread atual em 4 pontos (aumento e diminuição), além de controlar o tamanho do spread em 15 pontos. Para o preço Bid, controlaremos seu aumento/redução em +/- 10 pontos e acompanharemos o cruzamento do preço no nível de 1.13700.

Para definir os valores monitorados mencionados acima, é suficiente para este exemplo no manipulador OnInit() especificar essas strings:

//+------------------------------------------------------------------+
//| 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)+"_";
   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;
   
//--- Check if working with the full list is selected
   used_symbols_mode=InpModeUsedSymbols;
   if((ENUM_SYMBOLS_MODE)used_symbols_mode==SYMBOLS_MODE_ALL)
     {
      int total=SymbolsTotal(false);
      string ru_n="\nКоличество символов на сервере "+(string)total+".\nМаксимальное количество: "+(string)SYMBOLS_COMMON_TOTAL+" символов.";
      string en_n="\nThe number of symbols on server "+(string)total+".\nMaximal number: "+(string)SYMBOLS_COMMON_TOTAL+" symbols.";
      string caption=TextByLanguage("Внимание!","Attention!");
      string ru="Выбран режим работы с полным списком.\nВ этом режиме первичная подготовка списка коллекции символов может занять длительное время."+ru_n+"\nПродолжить?\n\"Нет\" - работа с текущим символом \""+Symbol()+"\"";
      string en="Full list mode selected.\nIn this mode, the initial preparation of the collection symbols list may take a long time."+en_n+"\nContinue?\n\"No\" - working with the current symbol \""+Symbol()+"\"";
      string message=TextByLanguage(ru,en);
      int flags=(MB_YESNO | MB_ICONWARNING | MB_DEFBUTTON2);
      int mb_res=MessageBox(message,caption,flags);
      switch(mb_res)
        {
         case IDNO : 
           used_symbols_mode=SYMBOLS_MODE_CURRENT; 
           break;
         default:
           break;
        }
     }
//--- Fill in the array of used symbols
   used_symbols=InpUsedSymbols;
   CreateUsedSymbolsArray((ENUM_SYMBOLS_MODE)used_symbols_mode,used_symbols,array_used_symbols);

//--- Set the type of the used symbol list in the symbol collection
   engine.SetUsedSymbols(array_used_symbols);
//--- Displaying the selected mode of working with the symbol object collection
   Print(engine.ModeSymbolsListDescription(),TextByLanguage(". Количество используемых символов: ",". Number of symbols used: "),engine.GetSymbolsCollectionTotal());

//--- Set controlled values for the current symbol
   CSymbol* symbol=engine.GetSymbolCurrent();
   if(symbol!=NULL)
     {
      //--- Set control of the current symbol price increase by 10 points
      symbol.SetControlBidInc(10*Point());
      //--- Set control of the current symbol price decrease by 10 points
      symbol.SetControlBidDec(10*Point());
      //--- Set control of the current symbol spread increase by 4 points
      symbol.SetControlSpreadInc(4);
      //--- Set control of the current symbol spread decrease by 4 points
      symbol.SetControlSpreadDec(4);
      //--- Set control of the current spread by the value of 15 points
      symbol.SetControlSpreadLevel(15);
      //--- Set control of the price crossing the level of 1.13700
      symbol.SetControlBidLevel(1.13700);
     }

//--- 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);

//--- Set CTrade trading class parameters
#ifdef __MQL5__
   trade.SetDeviationInPoints(slippage);
   trade.SetExpertMagicNumber(magic_number);
   trade.SetTypeFillingBySymbol(Symbol());
   trade.SetMarginMode();
   trade.LogLevel(LOG_LEVEL_NO);
#endif 
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+

Este é um exemplo de teste de configuração de parâmetros de símbolos controlados, portanto, definimos imediatamente os valores de controle necessários em OnInit().
No entanto, nada nos impede de alterar rapidamente os valores dos símbolos controlados com base em alguns critérios atuais durante a operação, pois todos os métodos estão presentes no objeto base. Resta obter acesso a qualquer um dos objetos herdados do CBaseObj para obter os métodos para definir parâmetros controlados e métodos para receber parâmetros alterados, bem como alterar parâmetros controlados de acordo com a lógica incorporada no programa - programaticamente ou da shell gráfica da biblioteca a ser criada posteriormente.

No manipulador OnTick() do EA, removemos a variável que armazena o último evento de símbolo. Temos outras ferramentas para rastrear eventos de símbolos, em vez de uma simples comparação dos estados atual e anterior.

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//--- Initializing the last events
   static ENUM_TRADE_EVENT last_trade_event=WRONG_VALUE;
   static ENUM_ACCOUNT_EVENT last_account_event=WRONG_VALUE;
   static ENUM_SYMBOL_EVENT last_symbol_event=WRONG_VALUE;
//--- If working in the tester
   if(MQLInfoInteger(MQL_TESTER))
     {

Alteramos o manipulador de eventos da biblioteca em termos de manipulação de eventos da coleção de símbolos:

//+------------------------------------------------------------------+
//| Handling DoEasy library events                                   |
//+------------------------------------------------------------------+
void OnDoEasyEvent(const int id,
                   const long &lparam,
                   const double &dparam,
                   const string &sparam)
  {
   int idx=id-CHARTEVENT_CUSTOM;
   string event="::"+string(idx);
   
//--- Retrieve (1) event time milliseconds, (2) reason and (3) source from lparam, as well as (4) set the exact event time
   ushort msc=engine.EventMSC(lparam);
   ushort reason=engine.EventReason(lparam);
   ushort source=engine.EventSource(lparam);
   long time=TimeCurrent()*1000+msc;
      
//--- Handling market watch window events
   if(idx>MARKET_WATCH_EVENT_NO_EVENT && idx<SYMBOL_EVENTS_NEXT_CODE)
     {
      string name="";
      //--- Market Watch window event
      string descr=engine.GetMWEventDescription((ENUM_MW_EVENT)idx);
      name=(idx==MARKET_WATCH_EVENT_SYMBOL_SORT ? "" : ": "+sparam);
      Print(TimeMSCtoString(lparam)," ",descr,name);
     }
//--- Handling symbol events
   if(source==COLLECTION_SYMBOLS_ID)
     {
      CSymbol *symbol=engine.GetSymbolObjByName(sparam);
      if(symbol==NULL)
         return;
      //--- Number of decimal places in the event value - in case of a 'long' event, it is 0, otherwise - Digits() of a symbol
      int digits=(idx<SYMBOL_PROP_INTEGER_TOTAL ? 0 : symbol.Digits());
      //--- Event text description
      string id_descr=(idx<SYMBOL_PROP_INTEGER_TOTAL ? symbol.GetPropertyDescription((ENUM_SYMBOL_PROP_INTEGER)idx) : symbol.GetPropertyDescription((ENUM_SYMBOL_PROP_DOUBLE)idx));
      //--- Property change text value
      string value=DoubleToString(dparam,digits);
      //--- Check event reasons and display its description in the journal
      if(reason==BASE_EVENT_REASON_INC)
        {
         Print(symbol.EventDescription(idx,(ENUM_BASE_EVENT_REASON)reason,source,value,id_descr,digits));
        }
      if(reason==BASE_EVENT_REASON_DEC)
        {
         Print(symbol.EventDescription(idx,(ENUM_BASE_EVENT_REASON)reason,source,value,id_descr,digits));
        }
      if(reason==BASE_EVENT_REASON_MORE_THEN)
        {
         Print(symbol.EventDescription(idx,(ENUM_BASE_EVENT_REASON)reason,source,value,id_descr,digits));
        }
      if(reason==BASE_EVENT_REASON_LESS_THEN)
        {
         Print(symbol.EventDescription(idx,(ENUM_BASE_EVENT_REASON)reason,source,value,id_descr,digits));
        }
      if(reason==BASE_EVENT_REASON_EQUALS)
        {
         Print(symbol.EventDescription(idx,(ENUM_BASE_EVENT_REASON)reason,source,value,id_descr,digits));
        }
     }
      
//--- Handling trading events
   if(idx>TRADE_EVENT_NO_EVENT && idx<TRADE_EVENTS_NEXT_CODE)
     {
      event=EnumToString((ENUM_TRADE_EVENT)ushort(idx));
      int digits=(int)SymbolInfoInteger(sparam,SYMBOL_DIGITS);
     }
//--- Handling account events
   else if(idx>ACCOUNT_EVENT_NO_EVENT && idx<ACCOUNT_EVENTS_NEXT_CODE)
     {
      Print(TimeMSCtoString(lparam)," ",sparam,": ",engine.GetAccountEventDescription((ENUM_ACCOUNT_EVENT)idx));
      
      //--- if this is an equity increase
      if((ENUM_ACCOUNT_EVENT)idx==ACCOUNT_EVENT_EQUITY_INC)
        {
         //--- Close a position with the highest profit exceeding zero when the equity exceeds the value,
         //--- specified in the CAccountsCollection::InitControlsParams() method for
         //--- the m_control_equity_inc variable tracking the equity increase by 15 units (by default)
         //--- AccountCollection file, InitControlsParams() method, string 1199
         
         //--- Get the list of all open positions
         CArrayObj* list_positions=engine.GetListMarketPosition();
         //--- Select positions with the profit exceeding zero
         list_positions=CSelect::ByOrderProperty(list_positions,ORDER_PROP_PROFIT_FULL,0,MORE);
         if(list_positions!=NULL)
           {
            //--- Sort the list by profit considering commission and swap
            list_positions.Sort(SORT_BY_ORDER_PROFIT_FULL);
            //--- Get the position index with the highest profit
            int index=CSelect::FindOrderMax(list_positions,ORDER_PROP_PROFIT_FULL);
            if(index>WRONG_VALUE)
              {
               COrder* position=list_positions.At(index);
               if(position!=NULL)
                 {
                  //--- Get a ticket of a position with the highest profit and close the position by a ticket
                  #ifdef __MQL5__
                     trade.PositionClose(position.Ticket());
                  #else 
                     PositionClose(position.Ticket(),position.Volume());
                  #endif 
                 }
              }
           }
        }
     }
  }
//+------------------------------------------------------------------+

Todas as alterações são comentadas no código e são reduzidas apenas para obter uma descrição do evento do objeto-símbolo e enviá-lo para o log, dependendo da causa do evento. Num manipulador que não é de teste, em vez de exibir uma mensagem no log, precisamos escrever um manipulador de eventos normal.

Compilamos e iniciamos o EA no testador:

Como se pode ver, ao reduzir ou aumentar o tamanho do spread além dos valores de controle especificados, no log são exibidas as entradas sobre isso. Da mesma forma, no log são exibidas informações sobre alterações no preço Bid - aumento ou diminuição em mais de 10 pontos. E finalmente, quando o preço Bid cruza o nível de controle especificado, também é enviado um evento e no log aparece uma entrada sobre ele.

Assim, criamos um objeto básico que permite rastrear os eventos de qualquer um de seus objetos herdeiros e enviá-los ao programa de controle, em que é possível monitorá-los e reagir a eles de acordo com a lógica estabelecida no programa, além de definir novos valores e níveis rastreados, o que permite flexibilidade ao gerenciar a lógica do programa.

O que vem agora?

No próximo artigo, implementaremos o trabalho do objeto-conta e seus eventos com base na funcionalidade de evento da classe de objeto base CBaseObj.

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