Biblioteca para el desarrollo rápido y sencillo de programas para MetaTrader (Parte XVII): Interactividad de los objetos de la biblioteca.

12 noviembre 2019, 15:58
Artyom Trishkin
0
506

Contenido


En el artículo anterior creamos el objeto básico de todos los objetos de la biblioteca, y ahora, cualquier objeto heredado del básico obtiene la funcionalidad de eventos: resulta muy sencillo monitorear los eventos que suceden en las propiedades de la clase heredera del objeto básico.
Hoy vamos a dar un paso más, asiganando a este objeto la funcionalidad necesaria, lo que significará que podremos decidir qué propiedades principalmente se controlarán desde el exterior para su posible cambio, así como la magnitud del cambio controlado y la magnitud del nivel controlado del valor de la propiedad de objeto. De esta forma, a todos los objetos de la biblioteca se le añadirá una funcionalidad que permite al usuario interactuar de forma activa con los objetos de la biblioteca.
Por ejemplo, para abrir una posición, queremos tener en cuenta el spread y el nivel de precio. Podremos establecer fácilmente el tamaño controlable del spread y monitorear el cruzamiento de cualquier nivel por parte del precio, después de lo cual se abrirá una posición. Bastará con establecer de forma automática el spread por debajo del cual será posible comerciar, y un valor de nivel de precio cuyo cruzamiento conllevará el envío de un evento de objeto de símbolo al programa sobre el permiso de comercio según el spread y el cruzamiento del nivel controlado por parte del precio.

Y, lo que no es menos importante, nos libraremos de la necesidad de usar las banderas de evento (lo cual establece una limitación sobre el número de posibles eventos para el seguimiento y obliga a guardar las listas de enumeración de todos los posibles tipos de evento para cada objeto). Ahora, el número de posibles eventos se corresponderá con el número de propiedades del objeto: de tipo entero y de tipo real. Aquellas propiedades que no tenemos que monitorear se inicializarán con el valor LONG_MAX, y por consiguiente, no participarán en la búsqueda de eventos de objeto.

Dado que los objetos de la biblioteca se guardan en sus colecciones, realizaremos la actualización de las propiedades de los objetos en la colección en el temporizador de la biblioteca con la ayuda de los métodos Refresh() de las colecciones, donde, a su vez, se llaman los métodos Refresh() de los objetos que se guardan en la lista de colección. Bien, si realizamos el seguimiento (en el método Refresh() del objeto básico) del cambio de las propiedades del objeto heredero para ver si han cambiado en las magnitudes establecidas, lograremos crear un sencillo modelo para cada uno de los objetos de la biblioteca. Y cada uno de los objetos enviará una lista con sus eventos al objeto principal de la biblioteca CEngine.
De esta forma, un programa construido usando como base la biblioteca, siempre conocerá todos los eventos sucedidos en cualquiera de los objetos de cualquier colección. Y nosotros siempre podremos establecer y modificar programáticamente para cada objeto de cualquier colección el tamaño de la magnitud controlable para cada propiedad.
Y todo ello con ayuda de la clase simple del objeto básico de todos los objetos de la biblioteca.

Métodos de control de eventos del objeto básico de la biblioteca

El trabajo con los eventos de los objetos básicos de la biblioteca se organizará de la forma siguiente: antes, para determinar los eventos de una clase concreta, hacíamos para ella nuestros propios métodos de control de eventos, creábamos banderas de los eventos y las enumeraciones de los posibles eventos de los objetos. Ahora, cuando el control de los eventos de las clases herederas esté organizado en su clase básica única, deberemos implementar el control universal de eventos, ya sean, por ejemplo, eventos de símbolo o eventos de cuenta, o bien eventos de cualquier otra clase que se cree en el futuro. Por eso, aquí nos vendrá bien el control del cambio del estado de las propiedades de tipo entero y real del objeto: su lista para cada clase heredera es única y representará un identificador de evento. Asimismo, necesitaremos tener en cuenta la dirección del cambio de las propiedades (el aumento o disminución del valor de la propiedad); lo denominaremos motivo del evento y la magnitud en la que ha cambiado la propiedad del objeto. Registraremos el indicador del evento, su motivo y la magnitud del cambio en la clase simple del evento básico del objeto; asimismo, guardaremos en la lista los eventos que suceden simultáneamente.
Antes determinamos que, para enviar eventos al programa, íbamos a usar un evento con parámetros rigurosamente establecidos (el identificador del evento, el valor long, el valor double y el valor string del evento), y enviar en el parámetro long la hora del evento en milisegundos. Ahora, debido a que ha cambiado el concepto de determinación de eventos, vamos a necesitar definir con exactitud el evento de varios parámetros:

  1. Identificador de evento: propiedad del objeto que ha sido modificada. Cada objeto tiene sus propiedades únicas, y no hay necesidad de disponer de un programa que no sabe nada sobre el objeto en el que ha cambiado una propiedad y cuál de estas propiedades ha cambiado (de tipo entero o real), por eso, no existe la posibilidad de definirla según el identificador de evento.
  2. Motivo del evento: aumento o disminución del valor de la propiedad, o bien el cruzamiento del nivel controlado. Este valor tampoco nos da la posibilidad de determinar un evento con exactitud. No obstante, según el identificador del evento y su motivo, podemos ya determinar que cierta propiedad de cierto objeto o bien ha aumentado, o bien ha disminuido, o bien se ha cruzado la magnitud controlada establecida. Por eso, para identificar un evento con precisión, necesitamos indicar el identificador de la clase en cuyo objeto ha tenido lugar el evento.
    A modo de semejante identificador servirá sin lugar a dudas el identificador de la lista de colección, ya que este indica con exactitud la pertenencia de un objeto a determinada clase: un símbolo, una cuenta, o cualquier otro creado en el futuro objeto de la colección. Por eso, será necesario enviar además al evento:
  3. El identificador de la colección: entonces, los tres identificadores enumerados permitirán determinar el evento de forma unívoca.
  4. La propiedad de línea del evento: denominación del objeto en el que ha sucedido el evento.

Bien, ya vemos que para determinar un evento, necesitamos obtener los tres parámetros de tipo entero, pero también obtener de alguna forma la hora del evento que se transmite de la misma forma mediante un valor long. Y nosotros solo tenemos un valor long del evento. ¿Qué podemos hacer? La salida es simple: transmitiremos en un mismo parámetro long las tres propiedades de tipo entero del evento, pero con el tipo ushort. El tipo long tiene ocho bytes, y el tipo ushort, dos. Esto significa que en un contenedor long podemos guardar tres números ushort, registrados en 0,1 bytes, 2,3 bytes, 4,5 bytes del número long, y aún nos quedarán dos bytes 6 y 7 para transmitir otro valor ushort si lo necesitáramos posteriormente.
Para determinar la hora del evento en el que ha sucedido, tendremos suficiente con transmitir solo los milisegundos de la hora en los bytes 0 y 1 del parámetro long.

  • La fecha y la hora del evento se podrán tomar de TimeCurrent() al obtener el evento y añadir a esta hora el número de milisegundosa transmitidos en los bytes cero y uno del valor long del evento.
  • El motivo del evento lo registraremos en los bytes dos y tres del parámetro long, mientras que
  • el identificador de la clase lo transmitiremos en los bytes 4 y 5 del parámetro long del evento.

De esta forma, al obtener un evento, extraeremos del parámetro long los tres valores ushort, y según ellos, podremos establecer la hora del evento y obtener datos adicionales para identificar exactamente el evento según el identificador del evento transmitido como parámetro ushort custom_event_id en EventChartCustom() y componer a partir del indentificador del evento y los dos valores obtenidos de forma adicional de lparam el identificador exacto del evento sucedido.

Para determinar los eventos, en las propiedades del objeto heredero en el objeto padre (en el objeto básico de todos los objetos de la biblioteca) comprobaremos en el temporizador el estado actual de cada una de las propiedades del objeto y lo compararemos con el estado pasado de esta propiedad. En primer lugar, comprobaremos si se ha establecido el valor cuya magnitud se debe comparar con la magnitud en la que ha cambiado el valor de la propiedad. Si el valor comprobado no ha sido establecido (se ha establecido para él LONG_MAX), esta propiedad será ignorada.

Debido a que vamos a comprobar listas de propiedades del objeto que tienen diferentes tipos: long y double, para guardar el estado pasado y el actual de las propiedades del objeto, hemos decidido que será más útil usar matrices bidimensionales, y no estructuras. En la primera dimensión de la matriz se guardarán los índices de la propiedades del objeto, y en la segunda, los valores de la propiedad cuyo índice se registra en la primera dimensión, la magnitud del cambio de la propiedad, los valores controlados y las banderas de los eventos de esta propiedad.

Vamos a aclarar por qué es más cómodo usar matrices, y no estructuras:
No podemos saber de antemano la propiedad de qué tipo vamos a comprobar, pero podemos comprobar su tipo a partir del índice la propiedad (las propiedades double del objeto siempre se ubican después de las propiedades long), esto significa que no tendremos que duplicar en la estructura los campos para los valores long y double del mismo valor comprobado de la propiedad del objeto. Simplemente registraremos en la matriz de tipo necesario (correspondiente al tipo de la propiedad determinado a partir del índice de la propiedad) todos los datos necesarios para controlar los estados de la propiedad del objeto con su tipo correcto, y no necesitaremos seleccionar en qué campo de la estructura registrar el valor transmitido, en long o en double.
En cuanto determinamos el cambio de cualquiera de las propiedades del objeto, lo añadimos a la lista de los eventos básicos del objeto(dado que la búsqueda se realiza en el objeto básico, el evento también será básico, y no deberemos confundirlo con el evento de la clase heredera, que en lo sucesivo se determinará según la lista de eventos básicos y se creará a partir de los eventos básicos cuyos punteros se guardan en esta lista).

En los métodos Refresh() de cada clase heredera se comprueban en el temporizador las listas de los cambios de las propiedades sucedidos (listas de eventos básicos), y si los objetos de estos eventos se encuentran en las listas, cada evento se transformará en un evento de la biblioteca y se enviará al programa de control.

Y para completar el cuadro, necesitaremos crear métodos que nos permitan establecer programáticamente las magnitudes controlables de los cambios para cualquier propiedad de cualquier objeto de la biblioteca creado sobre la base del objeto básico. De esta forma, podremos cambiar operativamente y en cualquier momento en el programa las condiciones necesarias de generación de eventos de los objetos necesarios.

El complejo de todas las actividades que hemos realizado para mejorar el objeto básico de la biblioteca nos permitirá no tener que pensar en crear un control de eventos para todos los objetos creados como consecuencia, bastará con usar la funcionalidad que hemos creado.

Bien, procedamos.

Dado que ahora vamos a trabajar con los eventos en el objeto básico de todos los objetos de la biblioteca, para identificar los eventos, debemos crear una enumeración con los motivos del evento.
En el archivo \MQL5\Include\DoEasy\ Defines.mqh, después de las variantes de selección por tiempo
añadimos la enumeración de los posibles motivos de los eventos del objeto básico:

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

Dado que ahora no vamos a necesitar las banderas de evento, en lugar de las listas con las banderas de evento del símbolo
añadimos la lista de posibles eventos de los símbolos en la ventana de "Observación de 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
//+------------------------------------------------------------------+

Mientras que la lista de posibles eventos del símbolo será simplemente eliminada por innecesaria:

//+------------------------------------------------------------------+
//| 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
//+------------------------------------------------------------------+

El código del próximo evento, naturalmente, ha sido modificado en el valor que sigue al valor de la constante MARKET_WATCH_EVENT_SYMBOL_SORT de la enumeración ENUM_MW_EVENT.

Ahora, vamos a analizar la implementación de la funcionalidad diseñada.

Añadimos al archivo del objeto básico \MQL5\Include\DoEasy\Objects\BaseObj.mqh la nueva clase del evento básico:

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

En la sección privada de la clase se ubican las variables para guardar los motivos del evento, el identificador del evento (coincide con el valor del índice de la propiedad del objeto modificada) y la magnitud del cambio de la propiedad del evento.
En la sección pública de la clase se ubican los métodos para retornar las variables de miembro de clase enumeradas anteriormente.
En los parámetros formales del constructor de clase, se transmiten los valores de estas propiedades, y en la lista de inicialización se asignan de inmediato los valores transmitidos a las variables de miembro de clase correspondientes.
Asimismo, la clase tiene un método para comparar dos objetos de clase para buscar en la lista de punteros dinámicos a los objetos, que ya hemos analizado másde una vez.

Dado que vamos a guardar la lista de propiedades controlables de los objetos en matrices bidimensionales, vamos a añadir una macrosustitución que indique el tamaño de la segunda dimensión de las matrices, y en la sección privada de la clase declararemos dos variables en las que vamos a guardar el número de propiedades de tipo entero y real del objeto que se heredará de esta clase (dado que la clase básica no sabe nada sobre el número de propiedades que tienen sus herederos, y este número se deberá indicar explícitamente). Y declaramos el método para rellenar las matrices de propiedades y la búsqueda de cambios en las propiedades de los objetos herederos.

//+------------------------------------------------------------------+
//| 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:

Declaramos en la sección protegida de la clase la lista para guardar los punteros a los ejemplares de los eventos básicos del objeto, la variable para guardar el identificador del evento, la bandera del primer inicio y la variable para guardar el tipo del objeto heredero.
Asimismo, hemos añadido cuatro matrices bidimensionales para guardar las propiedades y el control de su cambio (las propiedades de tipo entero y real actuales y pasadas del objeto heredero) y el método que retorna solo los milisegundos guardados en la hora del evento (para MQL4, retornamos 0, y para MQL5, el resto de la división por 1000 del valor long de la hora).
Dado que la clase básica no sabe nada sobre el número de propiedades de los objetos herederos, y debemos establecer el tamaño de las clases herederas (donde sea conocido), declaramos los métodos para la instalación y la comprobación del tamaño de las matrices:

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:


En este mismo lugar, en la sección privada de la clase, se han declarado los métodos para establecer y retornar las propiedades controlables y sus magnitudes, así como los métodos para establecer los números ushort en los bytes indicados del contenedor long según el índice. (Índice 0 => bytes 0-1, Índice 1 => bytes 2-3, Índice 2 => bytes 4-5)

Declaramos en la sección pública de la clase el reseteo de los valores de las propiedades modificables y los valores de las propiedades controlables del objeto, el método para añadir el evento básico a la lista, el método para obtener el objeto básico de la lista según el índice, así como el método que retorna el número de eventos básicos en la lista, el método virtual que retorna el tipo de objeto y el método que retorna la descripción de línea del evento básico:

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

Ahora, vamos a analizar brevemente todos los métodos declarados más arriba.

Constructor de la clase:

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

Aquí: dado que no tenemos los códigos de los eventos que hemos recopilado anteriormente a partir de las banderas, donde el valor cero indica la ausencia de evento, vamos a necesitar sustituir en la lista de inicialización de la clase la iniciacialización del código de evento por una distinta a cero (dado que cero indica el evento de la primera propiedad en la enumeración de propiedades de tipo entero del objeto, y un número superior a cero indica los siguientes eventos de la lista de propiedades del objeto; en la clase padre no sabemos cuántos son), así que estableceremos para el código de evento el valor -1.
Vamos a inicializar el nombre del objeto con la denominación de la clase (el nombre se reasigna en las herederas); inicializamos el número de propiedades de tipo entero y real del objeto heredero con valores cero y establecemos la bandera del primer inicio.
Establecemos en el cuerpo de la clase el tamaño de las matrices de las propiedades de tipo entero y real en la dimensión cero, limpiamos la lista de eventos básicos y establecemos para la misma la bandera de lista clasificada.

Antes, el método virtual Refresh() simplemente estaba declarado, y su implementación se delegaba en las clases herederas. Ahora, vamos a implementar este método para la clase del objeto básico; en dicho método podremos monitorear los cambios de las propiedades de los objetos herederos, así como crear (al determinar un evento) eventos básicos y añadirlos a la lista de eventos básicos para procesarlos posteriormente y crear los eventos de los objetos para enviarlos al 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;
     }
  }
//+------------------------------------------------------------------+

Hemos registrado en este mismo lugar todas las acciones en los comentarios al código, además de todo lo se hace en el método: se limpian preliminarmente las listas de eventos y se llaman los métodos para rellenar las matrices de las propiedades de tipo entero y real del objeto heredero, con comprobación de sus cambios.
Si se trata del primer inicio, el estado actual de las matrices de las propiedades se copia en el estado pasado (para que no haya diferencia entre ellos, y por consiguiente, no se dé el registro de eventos), se resetea la bandera de primer inicio y se limpia la lista de eventos básicos, que posiblemente ya se había creado al llamar al método FillPropertySettings().

Implementación del método que rellena las matrices de las propiedades del objeto heredero y controla sus cambios:

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

Aquí también hemos registrado todas las acciones en los comentarios al código. Lo único que queremos aclarar se relaciona con la selección del desplazamiento del índice de la propiedad double del objeto parta obtener el identificador del evento. Dado que, en nuestro caso, as propiedades de tipo real de todos los objetos se ubican después de las propiedades de tipo entero, el comienzo de la primera propiedad de tipo real es igual al número de propiedades de tipo entero (si el número de propiedades long es igual a tres, la primera propiedad de tipo real tendrá el índice 3 (0,1,2,  3)). En las matrices, el cálculo comienza, naturalmente, desde cero. Por eso, si trabajamos con propiedades double, deberemos añadir al índice de la matriz el número de propiedades de tipo entero del objeto.

Métodos de establecimiento del tamaño de las matrices de las propiedades de tipo entero y real de los objetos herederos:

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

Los métodos retornan el resultado del cambio del tamaño de las matrices a la magnitud transmitida al método.

Conviene destacar una peculiaridad en el cambio de tamaño de las matrices multidimensionales de MQL4. La función ArrayResize() en MQL4 retorna el volumen total de todas las dimensiones de la matriz. En MQL5, el tamaño de la primera dimensión que cambia. Por ejemplo, si la segunda dimensión tiene un tamaño igual a 2, al modificar el tamaño de la primera dimensión de la matriz hasta 10, la función retornará 20, lo cual resulta ilógico (nosotros solo cambiamos el tamaño de la primera dimesión). En MQL5, entre tanto, la función retorna el valor correcto; para el ejemplo descrito más arriba, retornará 10, como era de esperar.

Por eso, hemos creado en los métodos un divisor por el que debemos dividir en MQL4 el valor retornado por la función : es decir, por el valor de la segunda división.

Método de comprobación de la matriz de propiedades de tipo entero o real del objeto heredero:

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

Transmitimos al método la bandera que indica el tamaño de la matriz comprobada.
Si tenemos true, se comprobará la matriz de las propiedades long, si tenemos false, la matriz de las propiedades double.

Si el tamaño de la matriz comprobada no ha sido establecido, se creará el texto del mensaje, el mensaje se mostrará en el diario y se retornará false. Si el tamaño de la matriz ya ha sido establecido, se retornará true.

Métodos de reseteo de los valores controlados y las magnitudes de los datos investigados de las propiedades del 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);
  }
//+------------------------------------------------------------------+

En los métodos de los dos ciclos por las matrices de las propiedades de tipo entero y real del objeto heredero se establecen los valores iniciadores en las celdas necesarias de la segunda dimensión de las matrices. Las celdas iniciadoras se han registrado en los comentarios al código.

Método que añade un evento básico a la lista de eventos básicos del 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);
  }
//+------------------------------------------------------------------+

Transmitimos al método el identificador del evento, el motivo del evento y la magnitud en la que se ha cambiado la propiedad del objeto heredero.

A continuación, creamos un nuevo evento básico, y si ya existe exactamente el mismo evento en la lista de eventos básicos, este evento será eliminado y se retornará false , el evento no ha sido añadido. De lo contario, se retornará el resultado de la adición del nuevo evento a la lista de eventos básicos del objeto.

Método que retorna un evento básico según su índice en la lista de eventos básicos del 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);
  }
//+------------------------------------------------------------------+

Transmitimos al método el índice del evento necesario, después, si la lista tiene un valor cero o el índice se sale de los límites de la lista de eventos básicos, retornaremos NULL, de lo contrario, obtendremos un evento de la lista según el índice y retornaremos el puntero al objeto obtenido.

Para la clase del objeto básico, será necesario crear métodos con cuya ayuda podamos establecer de forma operativa las magnitudes necesarias del cambio de propiedades cuya superación conlleva la generación de un evento. Y también los métodos para establecer los valores para las propiedades de los objetos herederos y retornar las banderas sobre los eventos de control de los objetos que se hayan sucedido. Dado que la clase básica no sabe nada sobre las propiedades de sus herederos, deberemos crear métodos universales que permitan introducir cambios en la propiedad necesaria del objeto heredero. Dado que podemos y vamos a indicar el número de propiedades de tipo entero y real para cada una de las clases herederas, no resultará difícil determinar para qué propiedad establecemos el valor, solo tenemos que comprobar el índice de la propiedad modificada. Si el índice es inferior al número de propiedades de tipo entero, los cambios se introducirán en la propiedad de tipo entero del objeto, de lo contrario, en la de tipo real.

Implementación de los métodos de establecimiento de las propiedades controlables de los objetos herederos:

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

Vamos a analizar el último método: transmitimos al método la propiedad en cuyo valor se debe añadir el valor de plantilla T value. Si el índice de la propiedad es superior al número de propiedades de tipo entero del objeto heredero, añadiremos el valor T en la celda necesaria de la matriz de propiedades de tipo entero de la matriz, de lo contrario, calcularemos el índice según el cual se guarda en la matriz de propiedades de tipo entero esta propiedad (el índice de la propiedad double siempre es mayor que el índice de esta misma propiedad en la matriz en la magnitud del número de propiedades de tipo entero del objeto), y añadiremos el valor T a la celda necesaria de la matriz de propiedades de tipo entero del objeto. Las celdas necesarias de la segunda dimensión de la matriz para cada uno de los métodos está enumeradas antes de la lista de métodos.

Método que transforma un valor ushort en un valor long desplazado en el número necesario de bytes para su posterior empaquetado en un contenedor 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);
  }
//+------------------------------------------------------------------+

Imaginamos un número long de ocho bytes, dividido en celdas de dos bytes (a cada una de estas celdas se le asigna su propio í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

En él podemos ubicar cuatro números ushort. Deberemos desplazar cada número posterior a la izquierda 16 bits * índice (1 byte = 8 bit), y a continuación añadir el valor obtenido al número long. De esta forma, obtendremos un contenedor long empaquetado de varios valores ushort.

Transmitimos al método el número ushort y el índice según el cual debemos guardar el valor ushort en el contenedor long.
Asimismo, comprobamos el índice, y si es superior a 3
, se mostrará un mensaje sobre el índice erróneo y se retornará 0.
Si el índice es correcto, el número ushort se desplazará a la izquierda 16 bits * índice (en un byte hay 8 bits, y debemos desplazar un número ushort de dos bytes),
retornando desde el método el resultado del desplazamiento.

Método que empaqueta un valor ushort desplazado en el número necesario de bytes en el contenedor 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));
  }
//+------------------------------------------------------------------+

Transmitimos al método el número ushort que debemos empaquetar en el contenedor long transmitido al método a través del enlace y el índice del byte en los que debemos ubicar el valor ushort en el contenedor long.
Al igual que sucede con el método descrito más arriba, se comprueba el índice, y después, si el índice se comprueba con éxito,
añadimos al número long el número ushort desplazado en el número necesario de bytes con el método UshortToByte() con la ayuda del "O" a nivel de bits,
y el resultado se retorna al programa que realiza la llamada
.

Método que retorna la descripción de línea del evento del objeto heredero:

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



Dado que la clase del objeto básico no sabe nada sobre sus herederos, para describir un evento en la clase heredera deberemos indicar al objeto heredero en el que ha sucedido el evento.
Para ello, transmitimos al método

  • la propiedad del objeto en el que se ha registrado el evento, 
  • el motivo del evento — el aumento/disminución del valor de la propiedad en una magnitud establecida o el cruzamiento del nivel establecido por el valor de la propiedad,
  • el origen del evento — el identificador de la colección en cuyo objeto ha sucedido el evento,
  • el valor en el que ha cambiado la propiedad del objeto
  • la descripción de texto de la propiedad del objeto heredero (disponible en el heredero) y
  • el número de decimales tras la coma en la representación digital de la propiedad modificada (también disponible en el heredero).

Todos los pasos de la creación de la descripción del objeto heredero han sido comentados en el código, y esperamos que sean comprensibles para el análisis independiente.

Con esto, podemos dar por finalizado los cambios de la clase del objeto básico (en el posterior desarrollo de la biblioteca y la creación de nuevas colecciones, se añadirán al último método nuevos identificadores de colección para crear la descripción del evento correcta).

Colocando la clase de símbolo y la colección de símbolos en el nuevo camino

Hoy vamos a vamos a transferir el funcionamiento de la clase de símbolo y la colección de símbolos desde el trabajo con los nuevos eventos del objeto básico. Para ello, vamos a introducir algunos cambios en las clases de símbolo y de colección de símbolos.
Abrimos el archivo \MQL5\Include\DoEasy\Objects\Symbols\Symbol.mqh y comenzamos a introducir los cambios.

Dado que ahora todos los eventos de los objetos herederos del objeto básico se determinan en la clase padre, ya no es necesario controlar el cambio de las propiedades del objeto en la clase heredera. Y esto significa que ahora no es necesaria una estructura de datos de las propiedades del objeto analizadas.
Vamos a eliminar la estrutura y los dos objetos con el tipo de esta estructura de la clase del objeto de símbolo:

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

Eliminamos todas las variables de miembro de la clase para guardar las propiedades controladas y cambiadas del objeto de símbolo; ahora, todos estos datos se guardan en las matrices de la clase del objeto básico:

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

Eliminamos por innecesarios los metodos destacados:

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

Y declaramos el método de comprobación del cambio de las propiedades del símbolo y la creación de eventos en lugar del método virtual para establecer el codigo de cambio de la propiedad del símbolo:

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

Añadimos a la sección pública de la clase la declaración de los métodos que establecen las magnitudes monitoreadas y retornan los valores de control establecidos de las propiedades monitoreadas, las magnitudes de cambio de las propiedades y las banderas:

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 se ejecuta como dos métodos sobrecargados que llaman a los métodos del objeto básico, y que se corresponden con el tipo de la propiedad establecida/ comprobada del objeto de símbolo.

Anteriormente, ya escribimos los métodos de acceso simplificado a ciertas propiedades del objeto de símbolo. Vamos a añadir a ese mismo lugar los métodos para establecer las magnitudes de los niveles de control de los valores de las propiedades y a declarar los métodos para establecer/obtener los datos para Bid/Last y los parámetros relacionados con ellos (antes, se seleccionaba de forma automática qué usar, Bid o Last, dependiendo de los precios según los cuales se construye el gráfico. Ahora, es necesario crear los métodos para trabajar con estos datos):

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

Vamos a analizar la implementación de los metodos declarados y el cambio de los ya existentes.

Introducimos algunos pequeños cambios en el constructor de la clase:

//+------------------------------------------------------------------+
//| 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 con precisión los eventos de los objetos en la clase del objeto básico, asignamos al tipo del objeto de símbolo el indentificador de la colección de símbolos y establecemos el tamaño de las matrices de las propiedades de tipo entero y real de los datos para monitorear los eventos en las propiedades de los objetos de símbolo con el objeto padre básico. A continuación, inicializamos los parámetros modificables y de control en las matrices de las propiedades de tipo entero y real.

Asimismo, hemos modificado el método Refresh() del objeto de 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();
  }
//+------------------------------------------------------------------+

Aquí: dado que nos hemos librado de la necesidad de crear estructuras para guardar el estado actual y pasado de las propiedades del símbolo, aquí hemos eliminado el rellenado de la estructura de datos del estado actual del símbolo, y en lugar de ello, hemos organizado el rellenado de las matrices de las propiedades de tipo entero y real en el objeto básico.
A continuación, cuando las matrices han sido rellenadas, necesitaremos llamar al método Refresh() del objeto básico CBaseObj, en el que se realiza la búsqueda de los cambios sucedidos y se crea la lista de eventos básicos del objeto heredero.
Después de que la lista de eventos básicos en la clase padre haya sido creada (si se dan los criterios de generación de eventos), comprobamos los eventos básicos con el método CheckEvents(), y si estos existen, creamos una lista con los eventos del símbolo.

Implementamos el método de comprobación 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;
     }
  }  
//+------------------------------------------------------------------+

Aqui: Si la lista de eventos basicos está vacía, salimos.
Obtenemos en el ciclo por la lista de eventos básicos el siguiente evento, y si el evento ha sido obtenido, creamos el evento del símbolo:

  • obtenemos solo los milisegundos de la hora actual en milisegundos y los añadimos a los primeros bytes del parámetro long del evento
  • obtenemos el motivo del evento (aumento/disminución/mayor/menor al nivel) y lo añadimos a los segundos dos bytes del parámetro long del evento
  • añadimos el identificador de la colección de símbolos a los terceros dos bytes del párametro long del evento
  • añadimos el evento de símbolo a la lista de eventos de símbolo y establecemos la bandera de presencia de un evento en el símbolo

Método de inicialización de las variables de los datos controlables del símbolo:

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

Simplemente llama al método que resetea las variables de los valores controlables de los datos del objeto que ya hemos analizado anteriormente.

Métodos para establecer las magnitudes controlables y las banderas de los cambios sucedidos, así como los métodos de obtención del tamaño del los cambios sucedidos y las banderas:

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

Ya hemos analizado métodos semejantes al mejorar la clase del objeto básico: aquí se llaman precisamente estos métodos que hemos estudiado, dependiendo de la propiedad necesaria del objeto de símbolo.

Con esto, podemos considerar finalizada la clase del objeto básico.

Ahora solo queda mejorar un poco la clase de la colección de símbolos.
Abrimos el archivo \MQL5\Include\DoEasy\Collections\ SymbolsCollection.mqh e introducimos los cambios necesarios.

Dado que ya no es necesario crear para cada objeto nuestras propias enumeraciones con sus eventos,
establecemos para la variable "último evento del símbolo" y para el método GetLastEvent() los tipos int, en lugar del tipo ENUM_SYMBOL_EVENT anterior:

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

Dado que ahora todos los eventos del símbolo (y de cualquier otro objeto heredero) se procesan en la clase del objeto básico,
renombramos el método EventDescription() como EventMWDescription(), y transmitimos al método la variable con el tipo de enumeración de eventos de la ventana de "Observación de mercado":

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

Debido a que han cambiado las denominaciones de las enumeraciones,
el método de trabajo con la ventana de "Observación de mercado" ha sufrido algunos cambios ( se han modificado las denominaciones de las enumeraciones y el tipo de la variable de evento):

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

Exactamente de la misma forma se ha modificado el tipo de la variable de evento en el método de trabajo con la lista de eventos de la colección 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());
           }
        }
     }
  }
//+------------------------------------------------------------------+

En el método que retorna la descripción de línea del evento de la ventana de observación de mercado también se han modificado las denominaciones de las constantes de la enumeraciones con los 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)
     );
  }
//+------------------------------------------------------------------+

Ahora, vamos a mejorar la clase CEngine. Abrimos el archivo \MQL5\Include\DoEasy\Engine.mqh e introducimos algunos cambios en el mismo:

La variable que guarda el último evento en las propiedades del símbolo y el método que retorna el valor de esta variable también tendrán el tipo int:

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

Añadimos a la sección pública de la clase la declaración del método que extrae el número ushort del contenedor long según el índice de guardado establecido en el parámetro long del número ushort:

//--- Извлекает нужное ushort-число из упакованного long-значения
   ushort               LongToUshortFromByte(const long source_value,const uchar index) const;

Y en ese mismo sitio, escribimos tres métodos que retornan de inmediato los valores en milisegundos, el motivo y la fuente del evento del parámetro long del 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);         }

Dado que el valor cero constituye la primera propiedad de tipo entero de caulquier objeto, cambiamos en la lista de inicialización del constructor de la clase el valor de inicialización para la variable que guarda el último evento del símbolo, ahora, esta se inicializará con un 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)
  {

Implementación del método que extrae un número ushort de un contenedor long según el índice de los bytes de su ubicación en los componentes del contenedor 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);
  }
//+------------------------------------------------------------------+

Transmitimos al método el valor long del cual debemos extraer el número ushort y el índice de los bytes en los que se ubica el número (ya hemos visto anteriormente el recuadro de ubicación de los número ushort en el contenedor long); a continuación, comprobamos que el índice se haya indicado correctamente, y mostramos un mensaje sobre el error si el índice es incorrecto, retornando después 0.
Después, desplazamos los bits del número long 16 * index bits a la derecha, colocamos una máscara para "extinguir" los bits antiguos restantes y retornamos el número ushort extraído de esta forma.

Para trabajar en MQL4, necesitaremos indicar al compilador el error de tamaño cero de la matriz ERR_ZEROSIZE_ARRAY, que no conoce.
El error más adecuado para el tamaño cero de la matriz de los conocidos para el compilador MQL4 es el de "matriz incorrecta". Este vamos a indicar precisamente como alternativa al error de tamaño cero de la matriz.

Abrimos el archivo \MQL5\Include\DoEasy\ToMQL4.mqh y añadimos el código del error desconocido para el compilador MQL4:

//+------------------------------------------------------------------+
//|                                                       ToMQL4.mqh |
//|              Copyright 2017, Artem A. Trishkin, Skype artmedia70 |
//|                         https://www.mql5.com/ru/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2017, Artem A. Trishkin, Skype artmedia70"
#property link      "https://www.mql5.com/ru/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)
//+------------------------------------------------------------------+

Estos son todos los cambios que teníamos que hacer para iniciar el funcionamiento de los símbolos con la nueva funcionalidad de eventos ofrecida por el objeto CBaseObj a todos sus herederos.


Simulando la funcionalidad de eventos del objeto básico de todos los objetos de la biblioteca

Para simular la nueva funcionalidad de eventos del objeto básico, tomaremos el asesor del artículo anterior y lo guardaremos con el nuevo nombre TestDoEasyPart17.mq5 en la carpeta \MQL5\Experts\TestDoEasy\ Part17.

Vamos a simular el cambio de spread del símbolo actual en 4 puntos (aumento y disminución), ademas de controlar el tamaño del spread en 15 puntos. Para el precio Bid, controlaremos el aumento/disminución de su valor en +/- 10 puntos, y también monitorearemos el cruzamiento del nivel 1.13700 por parte del precio.

Para establecer los valores controlables anteriormente mencionados, bastará en este ejemplo con escribir en el manejador OnInit() las siguientes líneas:

//+------------------------------------------------------------------+
//| 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 ejemplo de establecimiento de los parámetros controlables del símbolo es de prueba, por eso, hemos indicado directamente los valores de control en OnInit().
Sin embargo, nada nos impide cambiar los valores monitoreados del símbolo partiendo de ciertos criterios actuales durante el trabajo: todos los métodos existen en el objeto básico, y bastará con obtener acceso a cualquiera de los objetos heredados de CBaseObj para tener a nuestra disposición los métodos para establecer los parámtros controlables y los métodos para obtener los parámetros modificados, y a continuación cambiar los parámatros controlables según la lógica implementada en el programa, ya sea de forma programática o desde el envoltorio gráfico de la biblioteca que se también se creará como resultado.

Eliminamos del manejador OnTick() del asesor la variable que guarda el último evento del símbolo; ahora, para monitorear los eventos de los símbolos tenemos otras herramientas, como la comparación simple del estado actual y el 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))
     {

Modificamos el manejador de eventos de la biblioteca en la parte del manejador de eventos de la colección 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 
                 }
              }
           }
        }
     }
  }
//+------------------------------------------------------------------+

Todos los cambios han sido comentados en el código, y se reducen a la obtención de la descripción del evento del objeto de símbolo y su muestra en el diario, dependiendo del motivo del evento. En el manejador que no es de prueba, en lugar de mostrar el mensaje en el diario, debemos escribir el manejador normal del evento.

Vamos a compilar el asesor e iniciarlo en el simulador:

Como podemos ver, al aumentar o disminuir el tamaño del spread por encima de las magnitudes establecidas, en el diario se muestra una entrada sobre ello. Exactamente lo mismo sucede con los cambios del precio Bid: un aumento o una disminución de su valor en más de 10 puntos se muestra en el diario con la entrada correspondiente. Y, finalmente, cuando el precio Bid cruza el nivel de control establecido, también se mostrará una entrada sobre ello en el diario.

De esta manera, hemos creado un objeto básico que permite monitorear los eventos de cualquier objeto heredero propio y enviar estos eventos al programa de control, donde se los puede monitorear y reaccionar a los mismos de acuerdo con la lógica implementada en el programa, así como establecer nuevos valores y niveles a monitorear, lo que permite controlar de forma flexible la lógica de funcionamiento del programa.

¿Qué es lo próximo?

En el siguiente artículo, implementaremos el funcionamiento del objeto de cuenta y sus eventos usando como base la funcionalidad de la clase del objeto básico CBaseObj.

Más abajo se adjuntan todos los archivos de la versión actual de la biblioteca y los archivos del asesor de prueba. Puede descargarlo todo y ponerlo a prueba por sí mismo.
Si tiene preguntas, observaciones o sugerencias, podrá concretarlas en los comentarios al artículo.

Volver al contenido

Artículos de esta serie:

Parte 1: Concepto y organización de datos
Parte 2: Colecciones de las órdenes y transacciones históricas
Parte 3: Colección de órdenes y posiciones de mercado, organización de la búsqueda
Parte 4: Eventos comerciales. Concepto
Parte 5: Clases y colección de eventos comerciales. Envío de eventos al programa.
Parte 6. Eventos en la cuenta con compensación
Parte 7. Eventos de activación de órdenes StopLimit, preparación de la funcionalidad para el registro de los eventos de modificación de órdenes y posiciones
Parte 8. Eventos de modificación de órdenes y posiciones
Parte 9. Compatibilidad con MQL4 - Preparando los datos
Parte 10. Compatibilidad con MQL4 - Eventos de apertura de posición y activación de órdenes pendientes
Parte 11. Compatibilidad con MQL4 - Eventos de cierre de posiciones
Parte 12. Implementando la clase de objeto "cuenta" y la colección de objetos de cuenta
Parte 13. Eventos del objeto "cuenta"
Parte 14. El objeto "Símbolo"
Parte 15. Colección de objetos de símbolo
Parte 16. Eventos de la colección de símbolos


Traducción del ruso hecha por MetaQuotes Software Corp.
Artículo original: https://www.mql5.com/ru/articles/7124

Archivos adjuntos |
MQL5.zip (218.23 KB)
MQL4.zip (218.22 KB)
Desarrollando el Oscilador de Promedio de Pivote (PMO): un nuevo indicador para la Media Móvil Acumulativa Desarrollando el Oscilador de Promedio de Pivote (PMO): un nuevo indicador para la Media Móvil Acumulativa

En este artículo, presentamos el Pivot Mean Oscillator (PMO) o Oscilador de Promedio de Pivote, una implementación de la Media Móvil Acumulativa (CMA) como indicador comercial para las plataformas MetaTrader. En particular, primero presentaremos el Pivot Mean (PM) o Promedio de Pivote, como un índice de normalización para las series temporales que calcula la fracción entre cualquier punto de datos y la CMA. Entonces, construimos el PMO como la diferencia entre las medias móviles aplicadas a las dos señales de PM. También hemos realizado algunos experimentos preliminares con el símbolo EURUSD para probar la eficacia del indicador presentado, dejando un amplio espacio para otras consideraciones y mejoras.

Biblioteca para el desarrollo rápido y sencillo de programas para MetaTrader (Parte XVI): Eventos de la colección de símbolos. Biblioteca para el desarrollo rápido y sencillo de programas para MetaTrader (Parte XVI): Eventos de la colección de símbolos.

En este artículo, vamos a crear la clase básica para todos los objetos de la biblioteca, encargada de añadir la funcionalidad de los eventos a todos sus herederos; asimismo, crearemos una clase para monitorear los eventos de la colección de símbolos basada en la nueva clase básica. Además, modificaremos las clases de la cuenta y los eventos de la cuenta para trabajar con la nueva funcionalidad del objeto básico.

Construimos un asesor usando módulos individuales Construimos un asesor usando módulos individuales

Durante el desarrollo de indicadores, asesores y scripts, el desarrollor se ve obligado a crear constantemente fragmentos de código terminados, que no tienen relación directa con la estrategia de trading. En el artículo vamos a analizar diferentes métodos para proyectar asesores usando los bloques individuales proyectados anteriormente: trailing, filtros, horarios, etcétera. Asimismo, hemos analizado las peculiaridades de este tipo de proyectos.

Biblioteca para el desarrollo rápido y sencillo de programas para MetaTrader (Parte XVIII): Interactividad del objeto de cuenta con cualquier otro objeto de la biblioteca Biblioteca para el desarrollo rápido y sencillo de programas para MetaTrader (Parte XVIII): Interactividad del objeto de cuenta con cualquier otro objeto de la biblioteca

En el presente artículo, hemos organizado el funcionamiento del objeto de cuenta en el nuevo objeto básico de todos los objetos de la biblioteca, hemos mejorado el objeto básico CBaseObj y hemos puesto a prueba el establecimiento de los parámetros monitoreados, así como la obtención de eventos para cualquier objeto de la biblioteca.