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

Artyom Trishkin | 4 noviembre, 2019

Contenido

Antes, al crear un objeto de cuenta y la colección de cuentas en la parte 12, y posteriormente para monitorear los eventos de la cuenta actual en la parte 13 de la descripción de la biblioteca, definimos un tipo de objetos nuevo para la biblioteca, que enviaba sus eventos al objeto básico Engine.


Los principios de seguimiento de los eventos de la cuenta se diferencian de los principios de seguimiento de los eventos comerciales que analizamos a partir del artículo 4: mientras que los eventos comerciales se determinan e introducen en la colección de eventos comerciales para obtener acceso completo a cualquiera de los eventos sucedidos anteriormente, los eventos de cuenta funcionan en tiempo real, "aquí y ahora", un evento se determina y se envía mediante EventChartCustom() al gráfico con el programa, además de enviarse al objeto básico de la biblioteca. A continuación, podemos obtener en nuestro programa desde el objeto básico de la biblioteca acceso a la lista de eventos sucedidos simultáneamente y procesar estos. Así se organizan los eventos del objeto de cuenta.

Y exactamente de la misma forma vamos a organizar el funcionamiento de los eventos en la colección de objetos de símbolo. La única diferencia reside en la calidad: nosotros monitoreamos los eventos de cuenta solo en la cuenta actual, a la que estamos conectados en este momento, mientras que los eventos de símbolo deberemos monitorearlos para cada una de las cuentas de la colección, no importa que solo haya un símbolo actual, dos, tres o todos los que haya en el servidor.

Y es aquí cunado comprendemos que prácticamente cada uno de los objetos contiene un cierto número de propiedades que se repiten de un objeto a otro. Y que registramos cada una de estas propiedades una y otra vez en cada nuevo objeto al desarrollarlo.

Esto nos empuja a tomar una decisión unívoca: crear un objeto básico a partir del cual se heredarán todos los objetos de la biblioteca. Ahora son heredados del objeto básico CObject de la biblioteca estándar. Vamos a crear otro objeto heredero de CObject y heredaremos ya de él todos los objetos de nuestra biblioteca. De esta forma, todas las propiedades comunes para cada objeto se podrán escribir una vez en el objeto básico, y todos los objetos herederos recibirán dichas propiedades.

Sin embargo, lo más interesante es que ahora se podrá crear un objeto de evento y registrarlo en el objeto básico, y todos nuestros objetos de la biblioteca dispondrán de la posibilidad de enviar sus eventos al programa. Eso es precisamente lo que necesitamos en el concepto de la biblioteca, que los objetos puedan anunciar al programa por sí mismos sobre su estado, mientras que la biblioteca o programa, basándose en los mensajes de los objetos, pueda procesar posteriormente estos mensajes, y, o bien tomar decisiones (programa), o bien realizar las acciones pertinentes en cuanto al procesamiento de los eventos de los objetos (biblioteca). Por consiguiente, esto aumentará la interactividad de la propia biblioteca, simplificando enormemente el desarrollo de programas para el susuario final, dado que todas las acciones de procesamiento de eventos de cualquier objeto serán asumidas por la biblioteca.

La estructura del objeto de evento de los objetos repetirá los datos necesarios para el envío con la función EventChartCustom(): el identificador del evento, el parámetro long, el parámetro double y el parámetro string del evento. De esta forma, se simplificará para nosotros el envío de eventos al programa, dado que al registrarse un evento del objeto, todos los datos que se deben enviar al programa sobre el evento sucedido se rellenan de inmediato en la clase del objeto en el que ha sucedido el evento, y solo tendremos que obtenerlo y reenviarlo al programa para su posterior procesamiento.

Vamos a terminar con la teoría y pasar al desarrollo propiamente dicho. En primer lugar, creamos el objeto de evento, después el objeto básico, y finalmente, implementamos el seguimiento de los eventos de la colección de símbolos y corregimos la clase de eventos de cuenta de acuerdo con el concepto creado. No vamos a modificar los eventos comerciales, ya que tienen otra estructura completamente distinta, y este concepto no casa con ella; además, los eventos comerciales tienen un aspecto finalizado, funcionan y envían sus resultados al programa.

Clase de objeto básico para todos los objetos de la biblioteca

Bien. Creamos en el directorio de la biblioteca \MQL5\Include\DoEasy\Objects\ la nueva clase CBaseObj en el archivo BaseObj.mqh. El objeto básico de la clase debe ser el objeto básico de la biblioteca estándar CObject. La clase no será grande, por eso mostraremos su listado completo, analizándola después por miembros y métodos.

Una pequeña observación: para no tener que crear un nuevo archivo para la clase de eventos de objeto, vamos a escribir esta clase antes de la clase básica de los objetos en este mismo archivo:
//+------------------------------------------------------------------+
//|                                                      BaseObj.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
#property strict    // Necessary for mql4
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include <Arrays\ArrayObj.mqh>
#include "..\Services\DELib.mqh"
//+------------------------------------------------------------------+
//| Base object event class for all library objects                  |
//+------------------------------------------------------------------+
class CEventBaseObj : public CObject
  {
private:
   long              m_time;
   long              m_chart_id;
   ushort            m_event_id;
   long              m_lparam;
   double            m_dparam;
   string            m_sparam;
public:
   void              Time(const long time)               { this.m_time=time;           }
   long              Time(void)                    const { return this.m_time;         }
   void              ChartID(const long chart_id)        { this.m_chart_id=chart_id;   }
   long              ChartID(void)                 const { return this.m_chart_id;     }
   void              ID(const ushort id)                 { this.m_event_id=id;         }
   ushort            ID(void)                      const { return this.m_event_id;     }
   void              LParam(const long lparam)           { this.m_lparam=lparam;       }
   long              LParam(void)                  const { return this.m_lparam;       }
   void              DParam(const double dparam)         { this.m_dparam=dparam;       }
   double            DParam(void)                  const { return this.m_dparam;       }
   void              SParam(const string sparam)         { this.m_sparam=sparam;       }
   string            SParam(void)                  const { return this.m_sparam;       }
public:
//--- Constructor
                     CEventBaseObj(const ushort event_id,const long lparam,const double dparam,const string sparam) : m_chart_id(::ChartID()) 
                       { 
                        this.m_event_id=event_id;
                        this.m_lparam=lparam;
                        this.m_dparam=dparam;
                        this.m_sparam=sparam;
                       }
//--- Comparison method to search for identical event objects
   virtual int       Compare(const CObject *node,const int mode=0) const 
                       {   
                        const CEventBaseObj *compared=node;
                        return
                          (
                           this.ID()>compared.ID()          ?  1  :
                           this.ID()<compared.ID()          ? -1  :
                           this.LParam()>compared.LParam()  ?  1  :
                           this.LParam()<compared.LParam()  ? -1  :
                           this.DParam()>compared.DParam()  ?  1  :
                           this.DParam()<compared.DParam()  ? -1  :
                           this.SParam()>compared.SParam()  ?  1  :
                           this.SParam()<compared.SParam()  ? -1  :  0
                          );
                       } 
  };
//+------------------------------------------------------------------+
//| Base object class for all library objects                        |
//+------------------------------------------------------------------+
class CBaseObj : public CObject
  {
private:

protected:
   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
   string            m_name;                                   // Object name
   string            m_folder_name;                            // Name of the folder storing CBaseObj descendant objects 

//--- Return time in milliseconds from MqlTick
   long              TickTime(void)                            const { return #ifdef __MQL5__ this.m_tick.time_msc #else this.m_tick.time*1000 #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;
//--- Initialize the variables of (1) tracked, (2) controlled object data (implementation in the descendants)
   virtual void      InitChangesParams(void);
   virtual void      InitControlsParams(void);
//--- (1) Check the object change, return the change code, (2) set the event type and fill in the list of events (implementation in the descendants)
   virtual int       SetEventCode(void);
   virtual void      SetTypeEvent(void);
public:
//--- Add the event object to the list
   bool              EventAdd(const ushort event_id,const long lparam,const double dparam,const string sparam);
//--- 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 the event object by its number in the list
   CEventBaseObj    *GetEvent(const int shift=WRONG_VALUE,const bool check_out=true);
//--- Return the number of 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 (implementation in the descendants)
   virtual void      Refresh(void);
   
//--- Constructor
                     CBaseObj();
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CBaseObj::CBaseObj() : m_global_error(ERR_SUCCESS),
                       m_hash_sum(0),m_hash_sum_prev(0),
                       m_is_event(false),m_event_code(0),
                       m_chart_id(::ChartID()),
                       m_folder_name(DIRECTORY),
                       m_name("")
  {
   ::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();
  }
//+------------------------------------------------------------------+
//| Add the event object to the list                                 |
//+------------------------------------------------------------------+
bool CBaseObj::EventAdd(const ushort event_id,const long lparam,const double dparam,const string sparam)
  {
   CEventBaseObj *event=new CEventBaseObj(event_id,lparam,dparam,sparam);
   if(event==NULL)
      return false;
   this.m_list_events.Sort();
   if(this.m_list_events.Search(event)>WRONG_VALUE)
     {
      delete event;
      return false;
     }
   return this.m_list_events.Add(event);
  }
//+------------------------------------------------------------------+
//| Return the object event by its index in the list                 |
//+------------------------------------------------------------------+
CEventBaseObj *CBaseObj::GetEvent(const int shift=WRONG_VALUE,const bool check_out=true)
  {
   int total=this.m_list_events.Total();
   if(total==0 || (!check_out && shift>total-1))
      return NULL;   
   int index=(shift<=0 ? total-1 : shift>total-1 ? 0 : total-shift-1);
   CEventBaseObj *event=this.m_list_events.At(index);
   return(event!=NULL ? event : NULL);
  }
//+------------------------------------------------------------------+
//| Return the number of decimal places in the 'double' value        |
//+------------------------------------------------------------------+
int CBaseObj::GetDigits(const double value) const
  {
   string val_str=(string)value;
   int len=::StringLen(val_str);
   int n=len-::StringFind(val_str,".",0)-1;
   if(::StringSubstr(val_str,len-1,1)=="0")
      n--;
   return n;
  }
//+------------------------------------------------------------------+

Vamos a analizar la clase de los eventos del objeto básico.

En la clase de los eventos del objeto básico, en la sección privada, hemos declarado las variables de miembro de clase para guardar todas las propiedades del evento:

private:
   long              m_time;
   long              m_chart_id;
   ushort            m_event_id;
   long              m_lparam;
   double            m_dparam;
   string            m_sparam;

La hora del evento, el identificador del gráfico al que se envía el evento, el identificador del evento, el valor long, el valor double y el valor string del evento, que se transmitirán al gráfico al programa de control.
Transmitimos de inmediato al constructor de clase los valores para la mayoría de estas variables:

//--- Constructor
                     CEventBaseObj(const ushort event_id,const long lparam,const double dparam,const string sparam) : m_chart_id(::ChartID()) 
                       { 
                        this.m_event_id=event_id;
                        this.m_lparam=lparam;
                        this.m_dparam=dparam;
                        this.m_sparam=sparam;
                       }

donde se les asignan los valores transmitidos.
También en la lista de inicialización de la clase, se le asigna a la variable de identificador el valor de la ID del gráfico actual.

Método de comparación de dos clases de evento de objetos:

//--- Comparison method to search for identical event objects
   virtual int       Compare(const CObject *node,const int mode=0) const 
                       {   
                        const CEventBaseObj *compared=node;
                        return
                          (
                           this.ID()>compared.ID()          ?  1  :
                           this.ID()<compared.ID()          ? -1  :
                           this.LParam()>compared.LParam()  ?  1  :
                           this.LParam()<compared.LParam()  ? -1  :
                           this.DParam()>compared.DParam()  ?  1  :
                           this.DParam()<compared.DParam()  ? -1  :
                           this.SParam()>compared.SParam()  ?  1  :
                           this.SParam()<compared.SParam()  ? -1  :  0  
                          );
                       } 

realiza una comparación elemento por elemento de todos los campos de las dos clases: la actual y la transmitida al método según el puntero. Si todos los campos son iguales entre sí, el método retornará 0, lo cual resulta imprescindible para realizar la búsqueda del mismo objeto exacto en la lista dinámica de punteros de la biblioteca estándar, ya que estos objetos se guardan en la lista CArrayObj, y para su método Search(), pensado para la búsqueda de objetos idénticos en la lista:

//+------------------------------------------------------------------+
//| Search of position of element in a sorted array                  |
//+------------------------------------------------------------------+
int CArrayObj::Search(const CObject *element) const
  {
   int pos;
//--- check
   if(m_data_total==0 || !CheckPointer(element) || m_sort_mode==-1)
      return(-1);
//--- search
   pos=QuickSearch(element);
   if(m_data[pos].Compare(element,m_sort_mode)==0)
      return(pos);
//--- not found
   return(-1);
  }
//+------------------------------------------------------------------+

es necesario que el objeto tenga el método virtual Compare(), que retorna 0 si conciden todas las propiedades de dos objetos)

En la sección pública de la clase, declaramos los métodos para establecer y retornar todas las propiedades de un objeto:

public:
   void              Time(const long time)               { this.m_time=time;           }
   long              Time(void)                    const { return this.m_time;         }
   void              ChartID(const long chart_id)        { this.m_chart_id=chart_id;   }
   long              ChartID(void)                 const { return this.m_chart_id;     }
   void              ID(const ushort id)                 { this.m_event_id=id;         }
   ushort            ID(void)                      const { return this.m_event_id;     }
   void              LParam(const long lparam)           { this.m_lparam=lparam;       }
   long              LParam(void)                  const { return this.m_lparam;       }
   void              DParam(const double dparam)         { this.m_dparam=dparam;       }
   double            DParam(void)                  const { return this.m_dparam;       }
   void              SParam(const string sparam)         { this.m_sparam=sparam;       }
   string            SParam(void)                  const { return this.m_sparam;       }

Aquí todo esta claro, así que no haremos mayores aclaraciones. Esta es la clase completa del evento de objeto.

Vamos a analizar la clase del objeto básico para todos los objetos de la biblioteca.

En la sección protegida de la clase, hemos declarado las variables de miembro de clase que ya conocemos de objetos anteriores:

protected:
   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
   string            m_name;                                   // Object name
   string            m_folder_name;                            // Name of the folder storing CBaseObj descendant objects 

Todas estas propiedades ya las creamos en los objetos de la biblioteca, además de analizarlas en diferentes partes de la descripción de la biblioteca que se corresponden con los objetos estudiados. Ahora, las hemos ubicado en una sola clase, en el objeto básico de todos los objetos de la biblioteca CBaseObj, quitando al mismo tiempo de las clases de los objetos herederos la determinación de estos miembros de clase (para no tener que describir los cambios aquí, el lector podrá mirar los cambios en los archivos adjuntos).

Vamos a echar un vistazo a los métodos ubicados en la sección privada de la clase:

//--- Return time in milliseconds from MqlTick
   long              TickTime(void)                            const { return #ifdef __MQL5__ this.m_tick.time_msc #else this.m_tick.time*1000 #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;
//--- Initialize the variables of (1) tracked, (2) controlled object data (implementation in the descendants)
   virtual void      InitChangesParams(void);
   virtual void      InitControlsParams(void);
//--- (1) Check the object change, return the change code, (2) set the event type and fill in the list of events (implementation in the descendants)
   virtual int       SetEventCode(void);
   virtual void      SetTypeEvent(void);

Ya hemos desarrollado todos estos métodos en los artículos anteriores, por lo que no vamos a volver a describirlos aquí. Solo diremos que ninguno de los métodos virtuales ejecuta nada aquí, por lo que será necesario implementarlos en las clases herederas del objeto básico.

En la sección pública de la clase, declaramos los métodos:

public:
//--- Add the event object to the list
   bool              EventAdd(const ushort event_id,const long lparam,const double dparam,const string sparam);
//--- 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 the event object by its number in the list
   CEventBaseObj    *GetEvent(const int shift=WRONG_VALUE,const bool check_out=true);
//--- Return the number of 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 (implementation in the descendants)
   virtual void      Refresh(void);

Asignamos los valores iniciales a las variables de miembro de clase en la lista de inicialización del constructor de 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(0),
                       m_chart_id(::ChartID()),          
                       m_folder_name(DIRECTORY),         
                       m_name("")                        
  {
   ::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();
  }
//+------------------------------------------------------------------+

A continuación, se resetea la estructura del tick, se asigna el valor del número de dígitos tras la coma para la divisa de la cuenta, se limpia la lista de eventos y se establece la bandera de lista clasificada para la lista de eventos de objeto.

Método de adición de eventos a la lista:

//+------------------------------------------------------------------+
//| Add the event object to the list                                 |
//+------------------------------------------------------------------+
bool CBaseObj::EventAdd(const ushort event_id,const long lparam,const double dparam,const string sparam)
  {
   CEventBaseObj *event=new CEventBaseObj(event_id,lparam,dparam,sparam);
   if(event==NULL)
      return false;
   this.m_list_events.Sort();
   if(this.m_list_events.Search(event)>WRONG_VALUE)
     {
      delete event;
      return false;
     }
   return this.m_list_events.Add(event);
  }
//+------------------------------------------------------------------+

Transmitimos al método el identificador del evento y los valores long, double y string de las porpiedades del evento. A continuación, creamos un nuevo evento con estos parámetros. Si en la lista existe exactamente el mismo evento, el objeto de evento se elimina y el método retorna false, de lo contrario, el método retorna el resultado de la adición del objeto de evento a la lista.

Método que retorna el objeto de evento según su índice en la lista:

//+------------------------------------------------------------------+
//| Return the object event by its index in the list                 |
//+------------------------------------------------------------------+
CEventBaseObj *CBaseObj::GetEvent(const int shift=WRONG_VALUE,const bool check_out=true)
  {
   int total=this.m_list_events.Total();
   if(total==0 || (!check_out && shift>total-1))
      return NULL;   
   int index=(shift<=0 ? total-1 : shift>total-1 ? 0 : total-shift-1);
   CEventBaseObj *event=this.m_list_events.At(index);
   return(event!=NULL ? event : NULL);
  }
//+------------------------------------------------------------------+

Ya hemos visto este método con anterioridad. Aquí solo se ha añadido la bandera de necesidad de comprobación y corrección del índice cuando su valor se sale de los límites de la lista. Por defecto, al método se le transmite el índice -1 y la comprobación de la salida del índice fuera de los límites de la lista. En este caso, el método retornará el último objeto de evento de la lista. Para obtener un objeto según su índice, debemos transmitir al método el índice requerido y la bandera de control de salida fuera de la lista = false. En este caso, se retornará el objeto (si el índice se encuentra dentro de la lista), o bien NULL, si el índice se encuentra fuera de la misma.

El método que retorna el número de dígitos tras la coma en un valor double, también lo hemos analizado anteriormente:

//+------------------------------------------------------------------+
//| Return the number of decimal places in the 'double' value        |
//+------------------------------------------------------------------+
int CBaseObj::GetDigits(const double value) const
  {
   string val_str=(string)value;
   int len=::StringLen(val_str);
   int n=len-::StringFind(val_str,".",0)-1;
   if(::StringSubstr(val_str,len-1,1)=="0")
      n--;
   return n;
  }
//+------------------------------------------------------------------+

El nuevo objeto básico está listo.

Ahora, solo necesitamos sustituir en cada uno de los objetos básicos de la biblioteca la clase básica CObject por CBaseObj. En nuestro caso, dichos objetos serán:

el objeto de clase CAccount:

//+------------------------------------------------------------------+
//| Account class                                                    |
//+------------------------------------------------------------------+
class CAccount : public CBaseObj
  {

el objeto de clase CSymbol:

//+------------------------------------------------------------------+
//| Abstract symbol class                                            |
//+------------------------------------------------------------------+
class CSymbol : public CBaseObj
  {

También asignaremos a las clases de colección las propiedades generales de los objetos:

colección de eventos comerciales:

//+------------------------------------------------------------------+
//| Collection of account trading events                             |
//+------------------------------------------------------------------+
class CEventsCollection : public CBaseObj
  {

colección de cuentas:

//+------------------------------------------------------------------+
//| Account collection                                               |
//+------------------------------------------------------------------+
class CAccountsCollection : public CBaseObj
  {

colección de símbolos:

//+------------------------------------------------------------------+
//| Symbol collection                                                |
//+------------------------------------------------------------------+
class CSymbolsCollection : public CBaseObj
  {

Ahora, todos los objetos creados sobre la base de CBaseObj tienen el mismo conjunto de propiedades, y al crear nuevos objetos, no será necesario escribir en cada objeto los datos referentes a las propiedades. Además, ahora será posible añadir las propiedades que queramos, resultando estas iguales para todos los objetos creados usando como base este objeto básico. Y lo que es más interesante: cada uno de estos objetos dispone ahora de una herramienta para trabajar con eventos. Y el evento de cada uno de los objetos tiene el mismo conjunto de parámetros que la función para el envío de eventos al gráfico del programa EventChartCustom(). De esta forma, hemos simplificado sustancialmente el posterior desarrollo de nuevos objetos y la mejora de los ya preparados.


Ahora, ya podemos comenzar a crear el evento de colección de símbolos.

Eventos de la colección de símbolos

Como siempre, comenzamos definiendo las constantes y enumeraciones. Abrimos el archivo Defines.mqh y le añadimos los datos necesarios para monitorear los eventos de símbolo.

Dado que para trabajar con los símbolos es necesario que estén presentes en la ventana de "Observación de mercado", y su número en ella es limitado,
añadimos a los parámetros de los símbolos una línea con una macrosustitución que indica el número máximo posible de símbolos que se encuentran simultáneamente en la ventana de "Observación de mercado":

//--- Symbol parameters
#define CLR_DEFAULT                    (0xFF000000)               // Default color
#define SYMBOLS_COMMON_TOTAL           (1000)                     // Total number of working symbols

Trasladamos del apartado de datos para el trabajo con los símbolos la enumeración de los modos de trabajo con los símbolos

//+------------------------------------------------------------------+
//| Data for working with symbols                                    |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Modes of working with symbols                                    |
//+------------------------------------------------------------------+
enum ENUM_SYMBOLS_MODE
  {
   SYMBOLS_MODE_CURRENT,                                    // Work with the current symbol only
   SYMBOLS_MODE_DEFINES,                                    // Work with the specified symbol list
   SYMBOLS_MODE_MARKET_WATCH,                               // Work with the Market Watch window symbols
   SYMBOLS_MODE_ALL                                         // Work with the full symbol list
  };
//+------------------------------------------------------------------+

en el archivo Datas.mqh:

//+------------------------------------------------------------------+
//|                                                        Datas.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/en/users/artmedia70"
//+------------------------------------------------------------------+
//| Enumerations                                                     |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Modes of working with symbols                                    |
//+------------------------------------------------------------------+
enum ENUM_SYMBOLS_MODE
  {
   SYMBOLS_MODE_CURRENT,                                    // Work with the current symbol only
   SYMBOLS_MODE_DEFINES,                                    // Work with the specified symbol list
   SYMBOLS_MODE_MARKET_WATCH,                               // Work with the Market Watch window symbols
   SYMBOLS_MODE_ALL                                         // Work with the full symbol list
  };
//+------------------------------------------------------------------+
//| Data sets                                                        |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Major Forex symbols                                              |
//+------------------------------------------------------------------+
string DataSymbolsFXMajors[]=
  {

El motivo de la existencia de esta solución es que estos datos son necesarios no solo para la biblioteca, sino también para los programas que funcionan usando como base la biblioteca. Y esta enumeración se relaciona con los datos generales del programa, más que con los de la biblioteca. Por ejemplo, deberemos usarlos junto con muchas otras enumeraciones como parámetros de entrada de los programas, y esto significa que también tendremos que traducirlo al lenguaje de comunicación necesario (organizaremos este proceso más tarde). Por eso, vamos a dejarlo en Datas.mqh.

En lugar de la enumeración trasladada desde Defines.mqh, añadimos una enumeración con la lista de las banderas de los eventos de símbolo y la enumeración con la lista de posibles eventos de símbolo:

//+------------------------------------------------------------------+
//| Data for working with symbols                                    |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| List of symbol event flags                                       |
//+------------------------------------------------------------------+
enum ENUM_SYMBOL_EVENT_FLAGS
  {
   SYMBOL_EVENT_FLAG_NO_EVENT                =  0,          // No event
   SYMBOL_EVENT_FLAG_TRADE_MODE              =  1,          // Change order execution permissions
   SYMBOL_EVENT_FLAG_SESSION_DEALS           =  2,          // Change the number of deals in the current session
   SYMBOL_EVENT_FLAG_SESSION_BUY_ORDERS      =  4,          // Change the total number of the current buy orders
   SYMBOL_EVENT_FLAG_SESSION_SELL_ORDERS     =  8,          // Change the total number of the current sell orders
   SYMBOL_EVENT_FLAG_VOLUME                  =  16,         // Change in the last deal volume exceeds the specified value in +/-
   SYMBOL_EVENT_FLAG_VOLUME_HIGH_DAY         =  32,         // Change of the maximum volume per day exceeds the specified value in +/-
   SYMBOL_EVENT_FLAG_VOLUME_LOW_DAY          =  64,         // Change of the minimum volume per day exceeds the specified value in +/-
   SYMBOL_EVENT_FLAG_SPREAD                  =  128,        // Change of a spread exceeds the specified change value in +/-
   SYMBOL_EVENT_FLAG_STOPLEVEL               =  256,        // Change of a Stop order level exceeds the specified value in +/-
   SYMBOL_EVENT_FLAG_FREEZELEVEL             =  512,        // Change of the freeze level exceeds the specified value in +/-
   SYMBOL_EVENT_FLAG_BID_LAST                =  1024,       // Change of the Bid or Last price exceeds the specified value in +/-
   SYMBOL_EVENT_FLAG_BID_LAST_HIGH           =  2048,       // Change of the maximum Bid or Last price per day exceeds the specified value in +/-
   SYMBOL_EVENT_FLAG_BID_LAST_LOW            =  4096,       // Change of the minimum Bid or Last price per day exceeds the specified value in +/-
   SYMBOL_EVENT_FLAG_ASK                     =  8192,       // Change of the Ask price exceeds the specified value in +/-
   SYMBOL_EVENT_FLAG_ASK_HIGH                =  16384,      // Change of the maximum Ask price per day exceeds the specified value in +/-
   SYMBOL_EVENT_FLAG_ASK_LOW                 =  32768,      // Change of the minimum Ask price per day exceeds the specified value in +/-
   SYMBOL_EVENT_FLAG_VOLUME_REAL_DAY         =  65536,      // Change of the real volume per day exceeds the specified value in +/-
   SYMBOL_EVENT_FLAG_VOLUME_HIGH_REAL_DAY    =  131072,     // Change of the maximum real volume per day exceeds the specified value in +/-
   SYMBOL_EVENT_FLAG_VOLUME_LOW_REAL_DAY     =  262144,     // Change of the minimum real volume per day exceeds the specified value in +/-
   SYMBOL_EVENT_FLAG_OPTION_STRIKE           =  524288,     // Change of the strike price exceeds the specified value in +/-
   SYMBOL_EVENT_FLAG_VOLUME_LIMIT            =  1048576,    // Change of the maximum available total position volume and pending orders in one direction
   SYMBOL_EVENT_FLAG_SWAP_LONG               =  2097152,    // Change swap long
   SYMBOL_EVENT_FLAG_SWAP_SHORT              =  4194304,    // Change swap short
   SYMBOL_EVENT_FLAG_SESSION_VOLUME          =  8388608,    // Change of the total volume of deals in the current session exceeds the specified value in +/-
   SYMBOL_EVENT_FLAG_SESSION_TURNOVER        =  16777216,   // Change of the total turnover in the current session exceeds the specified value in +/-
   SYMBOL_EVENT_FLAG_SESSION_INTEREST        =  33554432,   // Change of the total volume of open positions in the current session exceeds the specified value in +/-
   SYMBOL_EVENT_FLAG_SESSION_BUY_ORD_VOLUME  =  67108864,   // Change of the total volume of buy orders exceeds the specified value in +/-
   SYMBOL_EVENT_FLAG_SESSION_SELL_ORD_VOLUME =  134217728// Change of the total volume of sell orders exceeds the specified value in +/-
   SYMBOL_EVENT_FLAG_SESSION_OPEN            =  268435456// Change of the session open price exceeds the specified value in +/-
   SYMBOL_EVENT_FLAG_SESSION_CLOSE           =  536870912// Change of the session close price exceeds the specified value in +/-
   SYMBOL_EVENT_FLAG_SESSION_AW              =  1073741824  // Change of the average weighted session price exceeds the specified value in +/-
  };
//+------------------------------------------------------------------+
//| 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
//+------------------------------------------------------------------+

Aquí todo sucede igual que en las enumeraciones de las banderas y los posibles eventos de la cuenta y los eventos comerciales. Ya hemos analizado las banderas de eventos y los identificadores de eventos en la cuarta parte de la descripción de la biblioteca.

Para clasificar los símbolos según su ubicación en la ventana de "Observación de mercado", vamos añadir otra propiedad de tipo de entero del símbolo:

//+------------------------------------------------------------------+
//| Symbol integer properties                                        |
//+------------------------------------------------------------------+
enum ENUM_SYMBOL_PROP_INTEGER
  {
   SYMBOL_PROP_STATUS = 0,                                  // Symbol status
   SYMBOL_PROP_INDEX_MW,                                    // Symbol index in the Market Watch window
   SYMBOL_PROP_CUSTOM,                                      // Custom symbol flag
   SYMBOL_PROP_CHART_MODE,                                  // The price type used for generating bars – Bid or Last (from the ENUM_SYMBOL_CHART_MODE enumeration)
   SYMBOL_PROP_EXIST,                                       // Flag indicating that the symbol under this name exists
   SYMBOL_PROP_SELECT,                                      // The indication that the symbol is selected in Market Watch
   SYMBOL_PROP_VISIBLE,                                     // The indication that the symbol is displayed in Market Watch
   SYMBOL_PROP_SESSION_DEALS,                               // The number of deals in the current session 
   SYMBOL_PROP_SESSION_BUY_ORDERS,                          // The total number of Buy orders at the moment
   SYMBOL_PROP_SESSION_SELL_ORDERS,                         // The total number of Sell orders at the moment
   SYMBOL_PROP_VOLUME,                                      // Last deal volume
   SYMBOL_PROP_VOLUMEHIGH,                                  // Maximum volume within a day
   SYMBOL_PROP_VOLUMELOW,                                   // Minimum volume within a day
   SYMBOL_PROP_TIME,                                        // Latest quote time
   SYMBOL_PROP_DIGITS,                                      // Number of decimal places
   SYMBOL_PROP_DIGITS_LOTS,                                 // Number of decimal places for a lot
   SYMBOL_PROP_SPREAD,                                      // Spread in points
   SYMBOL_PROP_SPREAD_FLOAT,                                // Floating spread flag
   SYMBOL_PROP_TICKS_BOOKDEPTH,                             // Maximum number of orders displayed in the Depth of Market
   SYMBOL_PROP_TRADE_CALC_MODE,                             // Contract price calculation method (from the ENUM_SYMBOL_CALC_MODE enumeration)
   SYMBOL_PROP_TRADE_MODE,                                  // Order execution type (from the ENUM_SYMBOL_TRADE_MODE enumeration)
   SYMBOL_PROP_START_TIME,                                  // Symbol trading start date (usually used for futures)
   SYMBOL_PROP_EXPIRATION_TIME,                             // Symbol trading end date (usually used for futures)
   SYMBOL_PROP_TRADE_STOPS_LEVEL,                           // Minimum distance in points from the current close price for setting Stop orders
   SYMBOL_PROP_TRADE_FREEZE_LEVEL,                          // Freeze distance for trading operations (in points)
   SYMBOL_PROP_TRADE_EXEMODE,                               // Deal execution mode (from the ENUM_SYMBOL_TRADE_EXECUTION enumeration)
   SYMBOL_PROP_SWAP_MODE,                                   // Swap calculation model (from the ENUM_SYMBOL_SWAP_MODE enumeration)
   SYMBOL_PROP_SWAP_ROLLOVER3DAYS,                          // Triple-day swap (from the ENUM_DAY_OF_WEEK enumeration)
   SYMBOL_PROP_MARGIN_HEDGED_USE_LEG,                       // Calculating hedging margin using the larger leg (Buy or Sell)
   SYMBOL_PROP_EXPIRATION_MODE,                             // Flags of allowed order expiration modes
   SYMBOL_PROP_FILLING_MODE,                                // Flags of allowed order filling modes
   SYMBOL_PROP_ORDER_MODE,                                  // Flags of allowed order types
   SYMBOL_PROP_ORDER_GTC_MODE,                              // Expiration of Stop Loss and Take Profit orders if SYMBOL_EXPIRATION_MODE=SYMBOL_EXPIRATION_GTC (from the ENUM_SYMBOL_ORDER_GTC_MODE enumeration)
   SYMBOL_PROP_OPTION_MODE,                                 // Option type (from the ENUM_SYMBOL_OPTION_MODE enumeration)
   SYMBOL_PROP_OPTION_RIGHT,                                // Option right (Call/Put) (from the ENUM_SYMBOL_OPTION_RIGHT enumeration)
   //--- skipped property
   SYMBOL_PROP_BACKGROUND_COLOR                             // The color of the background used for the symbol in Market Watch
  }; 
#define SYMBOL_PROP_INTEGER_TOTAL    (36)                   // Total number of integer properties
#define SYMBOL_PROP_INTEGER_SKIP     (1)                    // Number of symbol integer properties not used in sorting
//+------------------------------------------------------------------+

Y dado que hemos añadido una nueva propiedad, deberemos aumentar el número total de propiedades de tipo entero a 36 , en lugar de las anteriores 35.

Finalmente, añadimos a la lista de posibles criterios de clasificación de los símbolos esta nueva propiedad:

//+------------------------------------------------------------------+
//| Possible symbol sorting criteria                                 |
//+------------------------------------------------------------------+
#define FIRST_SYM_DBL_PROP          (SYMBOL_PROP_INTEGER_TOTAL-SYMBOL_PROP_INTEGER_SKIP)
#define FIRST_SYM_STR_PROP          (SYMBOL_PROP_INTEGER_TOTAL-SYMBOL_PROP_INTEGER_SKIP+SYMBOL_PROP_DOUBLE_TOTAL-SYMBOL_PROP_DOUBLE_SKIP)
enum ENUM_SORT_SYMBOLS_MODE
  {
//--- Sort by integer properties
   SORT_BY_SYMBOL_STATUS = 0,                               // Sort by symbol status
   SORT_BY_SYMBOL_INDEX_MW,                                 // Sort by index in the Market Watch window
   SORT_BY_SYMBOL_CUSTOM,                                   // Sort by custom symbol property
   SORT_BY_SYMBOL_CHART_MODE,                               // Sort by price type for constructing bars – Bid or Last (from the ENUM_SYMBOL_CHART_MODE enumeration)
   SORT_BY_SYMBOL_EXIST,                                    // Sort by the flag that a symbol with such a name exists
   SORT_BY_SYMBOL_SELECT,                                   // Sort by the flag indicating that a symbol is selected in Market Watch
   SORT_BY_SYMBOL_VISIBLE,                                  // Sort by the flag indicating that a selected symbol is displayed in Market Watch
   SORT_BY_SYMBOL_SESSION_DEALS,                            // Sort by the number of deals in the current session 
   SORT_BY_SYMBOL_SESSION_BUY_ORDERS,                       // Sort by the total number of current buy orders
   SORT_BY_SYMBOL_SESSION_SELL_ORDERS,                      // Sort by the total number of current sell orders
   SORT_BY_SYMBOL_VOLUME,                                   // Sort by last deal volume
   SORT_BY_SYMBOL_VOLUMEHIGH,                               // Sort by maximum volume for a day
   SORT_BY_SYMBOL_VOLUMELOW,                                // Sort by minimum volume for a day
   SORT_BY_SYMBOL_TIME,                                     // Sort by the last quote time
   SORT_BY_SYMBOL_DIGITS,                                   // Sort by a number of decimal places
   SORT_BY_SYMBOL_DIGITS_LOT,                               // Sort by a number of decimal places in a lot
   SORT_BY_SYMBOL_SPREAD,                                   // Sort by spread in points
   SORT_BY_SYMBOL_SPREAD_FLOAT,                             // Sort by floating spread
   SORT_BY_SYMBOL_TICKS_BOOKDEPTH,                          // Sort by a maximum number of requests displayed in the market depth
   SORT_BY_SYMBOL_TRADE_CALC_MODE,                          // Sort by contract price calculation method (from the ENUM_SYMBOL_CALC_MODE enumeration)
   SORT_BY_SYMBOL_TRADE_MODE,                               // Sort by order execution type (from the ENUM_SYMBOL_TRADE_MODE enumeration)
   SORT_BY_SYMBOL_START_TIME,                               // Sort by an instrument trading start date (usually used for futures)
   SORT_BY_SYMBOL_EXPIRATION_TIME,                          // Sort by an instrument trading end date (usually used for futures)
   SORT_BY_SYMBOL_TRADE_STOPS_LEVEL,                        // Sort by the minimum indent from the current close price (in points) for setting Stop orders
   SORT_BY_SYMBOL_TRADE_FREEZE_LEVEL,                       // Sort by trade operation freeze distance (in points)
   SORT_BY_SYMBOL_TRADE_EXEMODE,                            // Sort by trade execution mode (from the ENUM_SYMBOL_TRADE_EXECUTION enumeration)
   SORT_BY_SYMBOL_SWAP_MODE,                                // Sort by swap calculation model (from the ENUM_SYMBOL_SWAP_MODE enumeration)
   SORT_BY_SYMBOL_SWAP_ROLLOVER3DAYS,                       // Sort by week day for accruing a triple swap (from the ENUM_DAY_OF_WEEK enumeration)
   SORT_BY_SYMBOL_MARGIN_HEDGED_USE_LEG,                    // Sort by the calculation mode of a hedged margin using the larger leg (Buy or Sell)
   SORT_BY_SYMBOL_EXPIRATION_MODE,                          // Sort by flags of allowed order expiration modes
   SORT_BY_SYMBOL_FILLING_MODE,                             // Sort by flags of allowed order filling modes
   SORT_BY_SYMBOL_ORDER_MODE,                               // Sort by flags of allowed order types
   SORT_BY_SYMBOL_ORDER_GTC_MODE,                           // Sort by StopLoss and TakeProfit orders lifetime
   SORT_BY_SYMBOL_OPTION_MODE,                              // Sort by option type (from the ENUM_SYMBOL_OPTION_MODE enumeration)
   SORT_BY_SYMBOL_OPTION_RIGHT,                             // Sort by option right (Call/Put) (from the ENUM_SYMBOL_OPTION_RIGHT enumeration)
//--- Sort by real properties
   SORT_BY_SYMBOL_BID = FIRST_SYM_DBL_PROP,                 // Sort by Bid
   SORT_BY_SYMBOL_BIDHIGH,                                  // Sort by maximum Bid for a day
   SORT_BY_SYMBOL_BIDLOW,                                   // Sort by minimum Bid for a day
   SORT_BY_SYMBOL_ASK,                                      // Sort by Ask
   SORT_BY_SYMBOL_ASKHIGH,                                  // Sort by maximum Ask for a day
   SORT_BY_SYMBOL_ASKLOW,                                   // Sort by minimum Ask for a day
   SORT_BY_SYMBOL_LAST,                                     // Sort by the last deal price
   SORT_BY_SYMBOL_LASTHIGH,                                 // Sort by maximum Last for a day
   SORT_BY_SYMBOL_LASTLOW,                                  // Sort by minimum Last for a day
   SORT_BY_SYMBOL_VOLUME_REAL,                              // Sort by Volume for a day
   SORT_BY_SYMBOL_VOLUMEHIGH_REAL,                          // Sort by maximum Volume for a day
   SORT_BY_SYMBOL_VOLUMELOW_REAL,                           // Sort by minimum Volume for a day
   SORT_BY_SYMBOL_OPTION_STRIKE,                            // Sort by an option execution price
   SORT_BY_SYMBOL_POINT,                                    // Sort by a single point value
   SORT_BY_SYMBOL_TRADE_TICK_VALUE,                         // Sort by SYMBOL_TRADE_TICK_VALUE_PROFIT value
   SORT_BY_SYMBOL_TRADE_TICK_VALUE_PROFIT,                  // Sort by a calculated tick price for a profitable position
   SORT_BY_SYMBOL_TRADE_TICK_VALUE_LOSS,                    // Sort by a calculated tick price for a loss-making position
   SORT_BY_SYMBOL_TRADE_TICK_SIZE,                          // Sort by a minimum price change
   SORT_BY_SYMBOL_TRADE_CONTRACT_SIZE,                      // Sort by a trading contract size
   SORT_BY_SYMBOL_TRADE_ACCRUED_INTEREST,                   // Sort by accrued interest
   SORT_BY_SYMBOL_TRADE_FACE_VALUE,                         // Sort by face value
   SORT_BY_SYMBOL_TRADE_LIQUIDITY_RATE,                     // Sort by liquidity rate
   SORT_BY_SYMBOL_VOLUME_MIN,                               // Sort by a minimum volume for performing a deal
   SORT_BY_SYMBOL_VOLUME_MAX,                               // Sort by a maximum volume for performing a deal
   SORT_BY_SYMBOL_VOLUME_STEP,                              // Sort by a minimum volume change step for deal execution
   SORT_BY_SYMBOL_VOLUME_LIMIT,                             // Sort by a maximum allowed aggregate volume of an open position and pending orders in one direction
   SORT_BY_SYMBOL_SWAP_LONG,                                // Sort by a long swap value
   SORT_BY_SYMBOL_SWAP_SHORT,                               // Sort by a short swap value
   SORT_BY_SYMBOL_MARGIN_INITIAL,                           // Sort by an initial margin
   SORT_BY_SYMBOL_MARGIN_MAINTENANCE,                       // Sort by a maintenance margin for an instrument
   SORT_BY_SYMBOL_MARGIN_LONG_INITIAL,                      // Sort by initial margin requirement applicable to Long orders
   SORT_BY_SYMBOL_MARGIN_BUY_STOP_INITIAL,                  // Sort by initial margin requirement applicable to BuyStop orders
   SORT_BY_SYMBOL_MARGIN_BUY_LIMIT_INITIAL,                 // Sort by initial margin requirement applicable to BuyLimit orders
   SORT_BY_SYMBOL_MARGIN_BUY_STOPLIMIT_INITIAL,             // Sort by initial margin requirement applicable to BuyStopLimit orders
   SORT_BY_SYMBOL_MARGIN_LONG_MAINTENANCE,                  // Sort by maintenance margin requirement applicable to Long orders
   SORT_BY_SYMBOL_MARGIN_BUY_STOP_MAINTENANCE,              // Sort by maintenance margin requirement applicable to BuyStop orders
   SORT_BY_SYMBOL_MARGIN_BUY_LIMIT_MAINTENANCE,             // Sort by maintenance margin requirement applicable to BuyLimit orders
   SORT_BY_SYMBOL_MARGIN_BUY_STOPLIMIT_MAINTENANCE,         // Sort by maintenance margin requirement applicable to BuyStopLimit orders
   SORT_BY_SYMBOL_MARGIN_SHORT_INITIAL,                     // Sort by initial margin requirement applicable to Short orders
   SORT_BY_SYMBOL_MARGIN_SELL_STOP_INITIAL,                 // Sort by initial margin requirement applicable to SellStop orders
   SORT_BY_SYMBOL_MARGIN_SELL_LIMIT_INITIAL,                // Sort by initial margin requirement applicable to SellLimit orders
   SORT_BY_SYMBOL_MARGIN_SELL_STOPLIMIT_INITIAL,            // Sort by initial margin requirement applicable to SellStopLimit orders
   SORT_BY_SYMBOL_MARGIN_SHORT_MAINTENANCE,                 // Sort by maintenance margin requirement applicable to Short orders
   SORT_BY_SYMBOL_MARGIN_SELL_STOP_MAINTENANCE,             // Sort by maintenance margin requirement applicable to SellStop orders
   SORT_BY_SYMBOL_MARGIN_SELL_LIMIT_MAINTENANCE,            // Sort by maintenance margin requirement applicable to SellLimit orders
   SORT_BY_SYMBOL_MARGIN_SELL_STOPLIMIT_MAINTENANCE,        // Sort by maintenance margin requirement applicable to SellStopLimit orders
   SORT_BY_SYMBOL_SESSION_VOLUME,                           // Sort by summary volume of the current session deals
   SORT_BY_SYMBOL_SESSION_TURNOVER,                         // Sort by the summary turnover of the current session
   SORT_BY_SYMBOL_SESSION_INTEREST,                         // Sort by the summary open interest
   SORT_BY_SYMBOL_SESSION_BUY_ORDERS_VOLUME,                // Sort by the current volume of Buy orders
   SORT_BY_SYMBOL_SESSION_SELL_ORDERS_VOLUME,               // Sort by the current volume of Sell orders
   SORT_BY_SYMBOL_SESSION_OPEN,                             // Sort by a session Open price
   SORT_BY_SYMBOL_SESSION_CLOSE,                            // Sort by a session Close price
   SORT_BY_SYMBOL_SESSION_AW,                               // Sort by an average weighted price of the current session
   SORT_BY_SYMBOL_SESSION_PRICE_SETTLEMENT,                 // Sort by a settlement price of the current session
   SORT_BY_SYMBOL_SESSION_PRICE_LIMIT_MIN,                  // Sort by a minimum price of the current session 
   SORT_BY_SYMBOL_SESSION_PRICE_LIMIT_MAX,                  // Sort by a maximum price of the current session
   SORT_BY_SYMBOL_MARGIN_HEDGED,                            // Sort by a contract size or a margin value per one lot of hedged positions
//--- Sort by string properties
   SORT_BY_SYMBOL_NAME = FIRST_SYM_STR_PROP,                // Sort by a symbol name
   SORT_BY_SYMBOL_BASIS,                                    // Sort by an underlying asset of a derivative
   SORT_BY_SYMBOL_CURRENCY_BASE,                            // Sort by a base currency of a symbol
   SORT_BY_SYMBOL_CURRENCY_PROFIT,                          // Sort by a profit currency
   SORT_BY_SYMBOL_CURRENCY_MARGIN,                          // Sort by a margin currency
   SORT_BY_SYMBOL_BANK,                                     // Sort by a feeder of the current quote
   SORT_BY_SYMBOL_DESCRIPTION,                              // Sort by a symbol string description
   SORT_BY_SYMBOL_FORMULA,                                  // Sort by the formula used for custom symbol pricing
   SORT_BY_SYMBOL_ISIN,                                     // Sort by the name of a symbol in the ISIN system
   SORT_BY_SYMBOL_PAGE,                                     // Sort by an address of the web page containing symbol information
   SORT_BY_SYMBOL_PATH                                      // Sort by a path in the symbol tree
  };
//+------------------------------------------------------------------+

Ya hemos terminado con el archivo Defines.mqh.

Ahora debemos mejorar la clase del objeto de símbolo. Dado que vamos a monitorear ciertas propiedades del símbolo para ver si han cambiado, necesitaremos comparar el valor actual con el anterior, ya sea en magnitudes absolutas, ya sea comprobando si se ha superado un cierto valor umbral. Para ello, deberemos crear una estructura de propiedades del símbolo y comparar los campos de la estructura de los valores actuales con los campos de la estructura de los valores pasados. Ya discutimos la definición de los eventos de objeto al implementar el seguimiento de eventos de la cuenta. Aquí haremos lo mismo, salvo que ahora tendremos ya preparados en el objeto básico los métodos para guardar y enviar eventos al programa.

Incluimos en la clase CSymbol el archivo del objeto básico, creamos en la sección privada de la clase la estructura de las propiedades del símbolo investigadas y declaramos las dos variables de esta estructua para el guardado del estado actual y pasado de las propiedades del símbolo:

//+------------------------------------------------------------------+
//|                                                       Symbol.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
#property strict    // Necessary for mql4
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "..\BaseObj.mqh"
//+------------------------------------------------------------------+
//| Abstract symbol class                                            |
//+------------------------------------------------------------------+
class CSymbol : public CBaseObj
  {
private:
   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
//---

A continuación, declaramos las variables de miembro de clase para guardar las magnitudes controladas establecidas del cambio de las propiedades, las magnitudes del cambio sucedido y las banderas de presencia de los eventos investigados:

   //--- Execution
   bool              m_is_change_trade_mode;                   // Flag of changing trading mode for a symbol
   //--- 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 growth 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 growth value
   bool              m_is_change_session_buy_ord_dec;          // Flag of a change in the number of Buy orders being less than the growth value
   //--- Sell orders of the current session
   long              m_control_session_sell_ord_inc;           // Controlled value of the growth 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 growth 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 growth 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 growth value
   bool              m_is_change_volume_dec;                   // Flag of the volume change in the last deal being less than the growth value
   //--- Maximum volume within a day
   long              m_control_volume_high_day_inc;            // Controlled value of the maximum volume growth 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 growth 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 growth 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 growth 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 growth 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 growth 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 growth 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 growth 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 growth 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 growth 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 growth
   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 growth 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 growth 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 growth 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 growth 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 growth 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 growth
   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 growth 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 growth 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 growth 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 growth 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 growth 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 growth 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 growth 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 growth 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 growth 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 growth 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 growth 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 growth
   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 growth 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 growth 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 growth 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 growth 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 growth 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 growth 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 growth 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 growth
   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 growth 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 growth
   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 growth 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 growth
   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 growth 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 growth
   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 growth 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 growth
   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 growth value
   bool              m_is_change_session_aw_dec;               // Flag of the average weighted session price change value exceeding the decrease value

Declaramos en la sección privada de la clase los métodos (ya declarados en la clase básica CBaseObj) de inicialización de las variables de las propiedades monitoreadas y controladas del símbolo, el método de comprobación del cambio de las propiedades que retorna el código del evento y el método que establece el tipo de evento según su código y que registra un evento en la lista de eventos:

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

Los implementamos de inmediato fuera del cuerpo de la clase.
Método de inicialización de las propiedades investigadas de un símbolo:

//+------------------------------------------------------------------+
//| Initialize the variables of tracked symbol data                  |
//+------------------------------------------------------------------+
void CSymbol::InitChangesParams(void)
  {
//--- List and code of changes
   this.m_list_events.Clear();                           // Clear the change list
   this.m_list_events.Sort();                            // Sort the change list
//--- Execution
   this.m_is_change_trade_mode=false;                    // Flag of changing trading mode for a symbol
//--- Current session deals
   this.m_changed_session_deals_value=0;                 // Value of change in the number of deals
   this.m_is_change_session_deals_inc=false;             // Flag of a change in the number of deals exceeding the growth value
   this.m_is_change_session_deals_dec=false;             // Flag of a change in the number of deals exceeding the decrease value
//--- Buy orders of the current session
   this.m_changed_session_buy_ord_value=0;               // Buy orders change value
   this.m_is_change_session_buy_ord_inc=false;           // Flag of a change in the number of Buy orders exceeding the growth value
   this.m_is_change_session_buy_ord_dec=false;           // Flag of a change in the number of Buy orders exceeding the decrease value
//--- Sell orders of the current session
   this.m_changed_session_sell_ord_value=0;              // Sell orders change value
   this.m_is_change_session_sell_ord_inc=false;          // Flag of a change in the number of Sell orders exceeding the growth value
   this.m_is_change_session_sell_ord_dec=false;          // Flag of a change in the number of Sell orders exceeding the decrease value
//--- Volume of the last deal
   this.m_changed_volume_value=0;                        // Value of the volume change in the last deal
   this.m_is_change_volume_inc=false;                    // Flag of the volume change in the last deal exceeding the growth value
   this.m_is_change_volume_dec=false;                    // Flag of the volume change in the last deal exceeding the decrease value
//--- Maximum volume within a day
   this.m_changed_volume_high_day_value=0;               // Maximum volume change value within a day
   this.m_is_change_volume_high_day_inc=false;           // Flag of the maximum day volume exceeding the growth value
   this.m_is_change_volume_high_day_dec=false;           // Flag of the maximum day volume exceeding the decrease value
//--- Minimum volume within a day
   this.m_changed_volume_low_day_value=0;                // Minimum volume change value within a day
   this.m_is_change_volume_low_day_inc=false;            // Flag of the minimum day volume exceeding the growth value
   this.m_is_change_volume_low_day_dec=false;            // Flag of the minimum day volume exceeding the decrease value
//--- Spread
   this.m_changed_spread_value=0;                        // Spread change value in points
   this.m_is_change_spread_inc=false;                    // Flag of spread change in points exceeding the growth value
   this.m_is_change_spread_dec=false;                    // Flag of spread change in points exceeding the decrease value
//--- StopLevel
   this.m_changed_stops_level_value=0;                   // StopLevel change value in points
   this.m_is_change_stops_level_inc=false;               // Flag of StopLevel change in points exceeding the growth value
   this.m_is_change_stops_level_dec=false;               // Flag of StopLevel change in points exceeding the decrease value
//--- Freeze distance
   this.m_changed_freeze_level_value=0;                  // FreezeLevel change value in points
   this.m_is_change_freeze_level_inc=false;              // Flag of FreezeLevel change in points exceeding the growth value
   this.m_is_change_freeze_level_dec=false;              // Flag of FreezeLevel change in points exceeding the decrease value
   
//--- Bid/Last
   this.m_changed_bid_last_value=0;                      // Bid or Last price change value
   this.m_is_change_bid_last_inc=false;                  // Flag of Bid or Last price change exceeding the growth value
   this.m_is_change_bid_last_dec=false;                  // Flag of Bid or Last price change exceeding the decrease value
//--- Maximum Bid/Last of the day
   this.m_changed_bid_last_high_value=0;                 // Maximum Bid or Last change value for the day
   this.m_is_change_bid_last_high_inc=false;             // Flag of the maximum Bid or Last price change for the day exceeding the growth value
   this.m_is_change_bid_last_high_dec=false;             // Flag of the maximum Bid or Last price change for the day exceeding the decrease value
//--- Minimum Bid/Last of the day
   this.m_changed_bid_last_low_value=0;                  // Minimum Bid or Last change value for the day
   this.m_is_change_bid_last_low_inc=false;              // Flag of the minimum Bid or Last price change for the day exceeding the growth value
   this.m_is_change_bid_last_low_dec=false;              // Flag of the minimum Bid or Last price change for the day exceeding the decrease value
//--- Ask
   this.m_changed_ask_value=0;                           // Ask price change value
   this.m_is_change_ask_inc=false;                       // Flag of the Ask price change exceeding the growth value
   this.m_is_change_ask_dec=false;                       // Flag of the Ask price change exceeding the decrease value
//--- Maximum Ask price for the day
   this.m_changed_ask_high_value=0;                      // Maximum Ask price change value for the day
   this.m_is_change_ask_high_inc=false;                  // Flag of the maximum day Ask exceeding the growth value
   this.m_is_change_ask_high_dec=false;                  // Flag of the maximum day Ask exceeding the decrease value
//--- Minimum Ask price for the day
   this.m_changed_ask_low_value=0;                       // Minimum Ask price change value for the day
   this.m_is_change_ask_low_inc=false;                   // Flag of the minimum Ask volume exceeding the growth value
   this.m_is_change_ask_low_dec=false;                   // Flag of the minimum Ask volume exceeding the decrease value
//--- Real Volume for the day
   this.m_changed_volume_real_value=0;                   // Real volume change value of the day
   this.m_is_change_volume_real_inc=false;               // Flag of the real volume change for the day exceeding the growth value
   this.m_is_change_volume_real_dec=false;               // Flag of the real volume change for the day exceeding the decrease value
//--- Maximum real volume for the day
   this.m_changed_volume_high_real_day_value=0;          // Maximum real volume change value of the day
   this.m_is_change_volume_high_real_day_inc=false;      // Flag of the maximum real volume change for the day exceeding the growth value
   this.m_is_change_volume_high_real_day_dec=false;      // Flag of the maximum real volume change for the day exceeding the decrease value
//--- Minimum real volume for the day
   this.m_changed_volume_low_real_day_value=0;           // Minimum real volume change value of the day
   this.m_is_change_volume_low_real_day_inc=false;       // Flag of the minimum real volume change for the day exceeding the growth value
   this.m_is_change_volume_low_real_day_dec=false;       // Flag of the minimum real volume change for the day exceeding the decrease value
//--- Strike price
   this.m_changed_option_strike_value=0;                 // Strike price change value
   this.m_is_change_option_strike_inc=false;             // Flag of the strike price change exceeding the growth value
   this.m_is_change_option_strike_dec=false;             // Flag of the strike price change exceeding the decrease value
//--- Total volume of positions and orders
   this.m_changed_volume_limit_value=0;                  // Minimum total volume change value
   this.m_is_change_volume_limit_inc=false;              // Flag of the minimum total volume increase
   this.m_is_change_volume_limit_dec=false;              // Flag of the minimum total volume decrease
//---  Swap long
   this.m_changed_swap_long_value=0;                     // Swap long change value
   this.m_is_change_swap_long_inc=false;                 // Flag of the swap long increase
   this.m_is_change_swap_long_dec=false;                 // Flag of the swap long decrease
//---  Swap short
   this.m_changed_swap_short_value=0;                    // Swap short change value
   this.m_is_change_swap_short_inc=false;                // Flag of the swap short increase
   this.m_is_change_swap_short_dec=false;                // Flag of the swap short decrease
//--- The total volume of deals in the current session
   this.m_changed_session_volume_value=0;                // The total deal volume change value in the current session
   this.m_is_change_session_volume_inc=false;            // Flag of total trade volume change in the current session exceeding the growth value
   this.m_is_change_session_volume_dec=false;            // Flag of total trade volume change in the current session exceeding the decrease value
//--- The total turnover in the current session
   this.m_changed_session_turnover_value=0;              // Total turnover change value in the current session
   this.m_is_change_session_turnover_inc=false;          // Flag of total turnover change in the current session exceeding the growth value
   this.m_is_change_session_turnover_dec=false;          // Flag of total turnover change in the current session exceeding the decrease value
//--- The total volume of open positions
   this.m_changed_session_interest_value=0;              // Change value of the open positions total volume in the current session
   this.m_is_change_session_interest_inc=false;          // Flag of total open positions' volume change in the current session exceeding the growth value
   this.m_is_change_session_interest_dec=false;          // Flag of total open positions' volume change in the current session exceeding the decrease value
//--- The total volume of Buy orders at the moment
   this.m_changed_session_buy_ord_volume_value=0;        // Change value of the current total buy order volume
   this.m_is_change_session_buy_ord_volume_inc=false;    // Flag of changing the current total buy orders volume exceeding the growth value
   this.m_is_change_session_buy_ord_volume_dec=false;    // Flag of changing the current total buy orders volume exceeding the decrease value
//--- The total volume of Sell orders at the moment
   this.m_changed_session_sell_ord_volume_value=0;       // Change value of the current total sell order volume
   this.m_is_change_session_sell_ord_volume_inc=false;   // Flag of changing the current total sell orders volume exceeding the growth value
   this.m_is_change_session_sell_ord_volume_dec=false;   // Flag of changing the current total sell orders volume exceeding the decrease value
//--- Session open price
   this.m_changed_session_open_value=0;                  // Session open price change value
   this.m_is_change_session_open_inc=false;              // Flag of the session open price change exceeding the growth value
   this.m_is_change_session_open_dec=false;              // Flag of the session open price change exceeding the decrease value
//--- Session close price
   this.m_changed_session_close_value=0;                 // Session close price change value
   this.m_is_change_session_close_inc=false;             // Flag of the session close price change exceeding the growth value
   this.m_is_change_session_close_dec=false;             // Flag of the session close price change exceeding the decrease value
//--- The average weighted session price
   this.m_changed_session_aw_value=0;                    // The average weighted session price change value
   this.m_is_change_session_aw_inc=false;                // Flag of the average weighted session price change value exceeding the growth value
   this.m_is_change_session_aw_dec=false;                // Flag of the average weighted session price change value exceeding the decrease value
  }
//+------------------------------------------------------------------+

En el método, simplemente se asignan los valores iniciales a las variables de las propiedades investigadas del símbolo declaradas en la sección privada de la clase.

Método de inicialización de los valores controlados de las propiedades del símbolo:

//+------------------------------------------------------------------+
//| Initialize the variables of controlled symbol data               |
//+------------------------------------------------------------------+
void CSymbol::InitControlsParams(void)
  {
//--- Current session deals
   this.m_control_session_deals_inc=10;                  // Controlled value of the growth of the number of deals
   this.m_control_session_deals_dec=10;                  // Controlled value of the decrease in the number of deals
//--- Buy orders of the current session
   this.m_control_session_buy_ord_inc=10;                // Controlled value of the growth of the number of Buy orders
   this.m_control_session_buy_ord_dec=10;                // Controlled value of the decrease in the number of Buy orders
//--- Sell orders of the current session
   this.m_control_session_sell_ord_inc=10;               // Controlled value of the growth of the number of Sell orders
   this.m_control_session_sell_ord_dec=10;               // Controlled value of the decrease in the number of Sell orders
//--- Volume of the last deal
   this.m_control_volume_inc=10;                         // Controlled value of the volume growth in the last deal
   this.m_control_volume_dec=10;                         // Controlled value of the volume decrease in the last deal
//--- Maximum volume within a day
   this.m_control_volume_high_day_inc=50;                // Controlled value of the maximum volume growth for a day
   this.m_control_volume_high_day_dec=50;                // Controlled value of the maximum volume decrease for a day
//--- Minimum volume within a day
   this.m_control_volume_low_day_inc=50;                 // Controlled value of the minimum volume growth for a day
   this.m_control_volume_low_day_dec=50;                 // Controlled value of the minimum volume decrease for a day
//--- Spread
   this.m_control_spread_inc=2;                          // Controlled spread growth value in points
   this.m_control_spread_dec=2;                          // Controlled spread decrease value in points
//--- StopLevel
   this.m_control_stops_level_inc=2;                     // Controlled StopLevel growth value in points
   this.m_control_stops_level_dec=2;                     // Controlled StopLevel decrease value in points
//--- Freeze distance
   this.m_control_freeze_level_inc=2;                    // Controlled FreezeLevel growth value in points
   this.m_control_freeze_level_dec=2;                    // Controlled FreezeLevel decrease value in points
   
//--- Bid/Last
   this.m_control_bid_last_inc=DBL_MAX;                  // Controlled value of Bid or Last price growth
   this.m_control_bid_last_dec=DBL_MAX;                  // Controlled value of Bid or Last price decrease
//--- Maximum Bid/Last of the day
   this.m_control_bid_last_high_inc=DBL_MAX;             // Controlled growth value of the maximum Bid or Last price of the day
   this.m_control_bid_last_high_dec=DBL_MAX;             // Controlled decrease value of the maximum Bid or Last price of the day
//--- Minimum Bid/Last of the day
   this.m_control_bid_last_low_inc=DBL_MAX;              // Controlled growth value of the minimum Bid or Last price of the day
   this.m_control_bid_last_low_dec=DBL_MAX;              // Controlled decrease value of the minimum Bid or Last price of the day
//--- Ask
   this.m_control_ask_inc=DBL_MAX;                       // Controlled value of the Ask price growth
   this.m_control_ask_dec=DBL_MAX;                       // Controlled value of the Ask price decrease
//--- Maximum Ask price for the day
   this.m_control_ask_high_inc=DBL_MAX;                  // Controlled growth value of the maximum Ask price of the day
   this.m_control_ask_high_dec=DBL_MAX;                  // Controlled decrease value of the maximum Ask price of the day
//--- Minimum Ask price for the day
   this.m_control_ask_low_inc=DBL_MAX;                   // Controlled growth value of the minimum Ask price of the day
   this.m_control_ask_low_dec=DBL_MAX;                   // Controlled decrease value of the minimum Ask price of the day
//--- Real Volume for the day
   this.m_control_volume_real_inc=50;                    // Controlled value of the real volume growth of the day
   this.m_control_volume_real_dec=50;                    // Controlled value of the real volume decrease of the day
//--- Maximum real volume for the day
   this.m_control_volume_high_real_day_inc=20;           // Controlled value of the maximum real volume growth of the day
   this.m_control_volume_high_real_day_dec=20;           // Controlled value of the maximum real volume decrease of the day
//--- Minimum real volume for the day
   this.m_control_volume_low_real_day_inc=10;            // Controlled value of the minimum real volume growth of the day
   this.m_control_volume_low_real_day_dec=10;            // Controlled value of the minimum real volume decrease of the day
//--- Strike price
   this.m_control_option_strike_inc=0;                   // Controlled value of the strike price growth
   this.m_control_option_strike_dec=0;                   // Controlled value of the strike price decrease
//--- The total volume of deals in the current session
   this.m_control_session_volume_inc=10;                 // Controlled value of the total trade volume growth in the current session
   this.m_control_session_volume_dec=10;                 // Controlled value of the total trade volume decrease in the current session
//--- The total turnover in the current session
   this.m_control_session_turnover_inc=1000;             // Controlled value of the total turnover growth in the current session
   this.m_control_session_turnover_dec=500;              // Controlled value of the total turnover decrease in the current session
//--- The total volume of open positions
   this.m_control_session_interest_inc=50;               // Controlled value of the total open position volume growth in the current session
   this.m_control_session_interest_dec=20;               // Controlled value of the total open position volume decrease in the current session
//--- The total volume of Buy orders at the moment
   this.m_control_session_buy_ord_volume_inc=50;         // Controlled value of the current total buy order volume growth
   this.m_control_session_buy_ord_volume_dec=20;         // Controlled value of the current total buy order volume decrease
//--- The total volume of Sell orders at the moment
   this.m_control_session_sell_ord_volume_inc=50;        // Controlled value of the current total sell order volume growth
   this.m_control_session_sell_ord_volume_dec=20;        // Controlled value of the current total sell order volume decrease
//--- Session open price
   this.m_control_session_open_inc=0;                    // Controlled value of the session open price growth
   this.m_control_session_open_dec=0;                    // Controlled value of the session open price decrease
//--- Session close price
   this.m_control_session_close_inc=0;                   // Controlled value of the session close price growth
   this.m_control_session_close_dec=0;                   // Controlled value of the session close price decrease
//--- The average weighted session price
   this.m_control_session_aw_inc=0;                      // Controlled value of the average weighted session price growth
   this.m_control_session_aw_dec=0;                      // Controlled value of the average weighted session price decrease
  }
//+------------------------------------------------------------------+

En el método, simplemente se asignan los valores iniciales a las variables de las propiedades controladas del símbolo declaradas en la sección privada de la clase. Cuando las propiedades superan los valores inscritos en estas variables, se genera el evento de cuenta correspondiente. Para desactivar el control de una propiedad, deberemos asignar a su variable el valor DBL_MAX. Para monitorear el cambio de cualquier magnitud, debemos asignar 0 a la variable.

Método que comprueba el cambio sucedido en las propiedades del símbolo y luego retorna el código del cambio sucedido:

//+------------------------------------------------------------------+
//| Check symbol changes, return a change code                       |
//+------------------------------------------------------------------+
int CSymbol::SetEventCode(void)
  {
   this.m_event_code=SYMBOL_EVENT_FLAG_NO_EVENT;

   if(this.m_struct_curr_symbol.trade_mode!=this.m_struct_prev_symbol.trade_mode)
      this.m_event_code+=SYMBOL_EVENT_FLAG_TRADE_MODE;
   if(this.m_struct_curr_symbol.session_deals!=this.m_struct_prev_symbol.session_deals)
      this.m_event_code+=SYMBOL_EVENT_FLAG_SESSION_DEALS;
   if(this.m_struct_curr_symbol.session_buy_orders!=this.m_struct_prev_symbol.session_buy_orders)
      this.m_event_code+=SYMBOL_EVENT_FLAG_SESSION_BUY_ORDERS;
   if(this.m_struct_curr_symbol.session_sell_orders!=this.m_struct_prev_symbol.session_sell_orders)
      this.m_event_code+=SYMBOL_EVENT_FLAG_SESSION_SELL_ORDERS;
   if(this.m_struct_curr_symbol.volume!=this.m_struct_prev_symbol.volume)
      this.m_event_code+=SYMBOL_EVENT_FLAG_VOLUME;
   if(this.m_struct_curr_symbol.volume_high_day!=this.m_struct_prev_symbol.volume_high_day)
      this.m_event_code+=SYMBOL_EVENT_FLAG_VOLUME_HIGH_DAY;
   if(this.m_struct_curr_symbol.volume_low_day!=this.m_struct_prev_symbol.volume_low_day)
      this.m_event_code+=SYMBOL_EVENT_FLAG_VOLUME_LOW_DAY;
   if(this.m_struct_curr_symbol.spread!=this.m_struct_prev_symbol.spread)
      this.m_event_code+=SYMBOL_EVENT_FLAG_SPREAD;
   if(this.m_struct_curr_symbol.stops_level!=this.m_struct_prev_symbol.stops_level)
      this.m_event_code+=SYMBOL_EVENT_FLAG_STOPLEVEL;
   if(this.m_struct_curr_symbol.freeze_level!=this.m_struct_prev_symbol.freeze_level)
      this.m_event_code+=SYMBOL_EVENT_FLAG_FREEZELEVEL;

   if(this.m_struct_curr_symbol.bid_last!=this.m_struct_prev_symbol.bid_last)
      this.m_event_code+=SYMBOL_EVENT_FLAG_BID_LAST;
   if(this.m_struct_curr_symbol.bid_last_high!=this.m_struct_prev_symbol.bid_last_high)
      this.m_event_code+=SYMBOL_EVENT_FLAG_BID_LAST_HIGH;
   if(this.m_struct_curr_symbol.bid_last_low!=this.m_struct_prev_symbol.bid_last_low)
      this.m_event_code+=SYMBOL_EVENT_FLAG_BID_LAST_LOW;
   if(this.m_struct_curr_symbol.ask!=this.m_struct_prev_symbol.ask)
      this.m_event_code+=SYMBOL_EVENT_FLAG_ASK;
   if(this.m_struct_curr_symbol.ask_high!=this.m_struct_prev_symbol.ask_high)
      this.m_event_code+=SYMBOL_EVENT_FLAG_ASK_HIGH;
   if(this.m_struct_curr_symbol.ask_low!=this.m_struct_prev_symbol.ask_low)
      this.m_event_code+=SYMBOL_EVENT_FLAG_ASK_LOW;
   if(this.m_struct_curr_symbol.volume_real_day!=this.m_struct_prev_symbol.volume_real_day)
      this.m_event_code+=SYMBOL_EVENT_FLAG_VOLUME_REAL_DAY;
   if(this.m_struct_curr_symbol.volume_high_real_day!=this.m_struct_prev_symbol.volume_high_real_day)
      this.m_event_code+=SYMBOL_EVENT_FLAG_VOLUME_HIGH_REAL_DAY;
   if(this.m_struct_curr_symbol.volume_low_real_day!=this.m_struct_prev_symbol.volume_low_real_day)
      this.m_event_code+=SYMBOL_EVENT_FLAG_VOLUME_LOW_REAL_DAY;
   if(this.m_struct_curr_symbol.option_strike!=this.m_struct_prev_symbol.option_strike)
      this.m_event_code+=SYMBOL_EVENT_FLAG_OPTION_STRIKE;
   if(this.m_struct_curr_symbol.volume_limit!=this.m_struct_prev_symbol.volume_limit)
      this.m_event_code+=SYMBOL_EVENT_FLAG_VOLUME_LIMIT;
   if(this.m_struct_curr_symbol.swap_long!=this.m_struct_prev_symbol.swap_long)
      this.m_event_code+=SYMBOL_EVENT_FLAG_SWAP_LONG;
   if(this.m_struct_curr_symbol.swap_short!=this.m_struct_prev_symbol.swap_short)
      this.m_event_code+=SYMBOL_EVENT_FLAG_SWAP_SHORT;
   if(this.m_struct_curr_symbol.session_volume!=this.m_struct_prev_symbol.session_volume)
      this.m_event_code+=SYMBOL_EVENT_FLAG_SESSION_VOLUME;
   if(this.m_struct_curr_symbol.session_turnover!=this.m_struct_prev_symbol.session_turnover)
      this.m_event_code+=SYMBOL_EVENT_FLAG_SESSION_TURNOVER;
   if(this.m_struct_curr_symbol.session_interest!=this.m_struct_prev_symbol.session_interest)
      this.m_event_code+=SYMBOL_EVENT_FLAG_SESSION_INTEREST;
   if(this.m_struct_curr_symbol.session_buy_ord_volume!=this.m_struct_prev_symbol.session_buy_ord_volume)
      this.m_event_code+=SYMBOL_EVENT_FLAG_SESSION_BUY_ORD_VOLUME;
   if(this.m_struct_curr_symbol.session_sell_ord_volume!=this.m_struct_prev_symbol.session_sell_ord_volume)
      this.m_event_code+=SYMBOL_EVENT_FLAG_SESSION_SELL_ORD_VOLUME;
   if(this.m_struct_curr_symbol.session_open!=this.m_struct_prev_symbol.session_open)
      this.m_event_code+=SYMBOL_EVENT_FLAG_SESSION_OPEN;
   if(this.m_struct_curr_symbol.session_close!=this.m_struct_prev_symbol.session_close)
      this.m_event_code+=SYMBOL_EVENT_FLAG_SESSION_CLOSE;
   if(this.m_struct_curr_symbol.session_aw!=this.m_struct_prev_symbol.session_aw)
      this.m_event_code+=SYMBOL_EVENT_FLAG_SESSION_AW;
//---
   return this.m_event_code;
  }
//+------------------------------------------------------------------+

En primer lugar, se resetea el código de evento en el método, luego se comparan los valores de los parámetros del símbolo controlados en la estructura de datos actuales y la estructura de datos pasados. Si los datos no son iguales, añadimos al código de evento la bandera correspondiente.

Método que establece el tipo de evento y registra el evento sucedido en la lista de eventos:

//+------------------------------------------------------------------+
//| Set a symbol object event type                                   |
//+------------------------------------------------------------------+
void CSymbol::SetTypeEvent(void)
  {
   this.InitChangesParams();
   ENUM_SYMBOL_EVENT event_id=SYMBOL_EVENT_NO_EVENT;
//--- Change of trading modes on a symbol
   if(this.IsPresentEventFlag(SYMBOL_EVENT_FLAG_TRADE_MODE))
     {
      event_id=
        (
         this.TradeMode()==SYMBOL_TRADE_MODE_DISABLED  ? SYMBOL_EVENT_TRADE_DISABLE    :
         this.TradeMode()==SYMBOL_TRADE_MODE_LONGONLY  ? SYMBOL_EVENT_TRADE_LONGONLY   :
         this.TradeMode()==SYMBOL_TRADE_MODE_SHORTONLY ? SYMBOL_EVENT_TRADE_SHORTONLY  :
         this.TradeMode()==SYMBOL_TRADE_MODE_CLOSEONLY ? SYMBOL_EVENT_TRADE_CLOSEONLY  :
         SYMBOL_EVENT_TRADE_FULL
        );
      this.m_is_change_trade_mode=true;
      if(this.EventAdd(event_id,this.TickTime(),this.TradeMode(),this.Name()))
         this.m_struct_prev_symbol.trade_mode=this.m_struct_curr_symbol.trade_mode;
     }
//--- Change of the number of deals in the current session
   if(this.IsPresentEventFlag(SYMBOL_EVENT_FLAG_SESSION_DEALS))
     {
      this.m_changed_session_deals_value=this.m_struct_curr_symbol.session_deals-this.m_struct_prev_symbol.session_deals;
      if(this.m_changed_session_deals_value>this.m_control_session_deals_inc)
        {
         this.m_is_change_session_deals_inc=true;
         event_id=SYMBOL_EVENT_SESSION_DEALS_INC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_session_deals_value,this.Name()))
            this.m_struct_prev_symbol.session_deals=this.m_struct_curr_symbol.session_deals;
        }
      else if(this.m_changed_session_deals_value<-this.m_control_session_deals_dec)
        {
         this.m_is_change_session_deals_dec=true;
         event_id=SYMBOL_EVENT_SESSION_DEALS_DEC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_session_deals_value,this.Name()))
            this.m_struct_prev_symbol.session_deals=this.m_struct_curr_symbol.session_deals;
        }
     }
//--- Change of the total number of the current buy orders
   if(this.IsPresentEventFlag(SYMBOL_EVENT_FLAG_SESSION_BUY_ORDERS))
     {
      this.m_changed_session_buy_ord_value=this.m_struct_curr_symbol.session_buy_orders-this.m_struct_prev_symbol.session_buy_orders;
      if(this.m_changed_session_buy_ord_value>this.m_control_session_buy_ord_inc)
        {
         this.m_is_change_session_buy_ord_inc=true;
         event_id=SYMBOL_EVENT_SESSION_BUY_ORDERS_INC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_session_buy_ord_value,this.Name()))
            this.m_struct_prev_symbol.session_buy_orders=this.m_struct_curr_symbol.session_buy_orders;
        }
      else if(this.m_changed_session_buy_ord_value<-this.m_control_session_buy_ord_dec)
        {
         this.m_is_change_session_buy_ord_dec=true;
         event_id=SYMBOL_EVENT_SESSION_BUY_ORDERS_DEC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_session_buy_ord_value,this.Name()))
            this.m_struct_prev_symbol.session_buy_orders=this.m_struct_curr_symbol.session_buy_orders;
        }
     }
//--- Change of the total number of the current sell orders
   if(this.IsPresentEventFlag(SYMBOL_EVENT_FLAG_SESSION_SELL_ORDERS))
     {
      this.m_changed_session_sell_ord_value=this.m_struct_curr_symbol.session_sell_orders-this.m_struct_prev_symbol.session_sell_orders;
      if(this.m_changed_session_sell_ord_value>this.m_control_session_sell_ord_inc)
        {
         this.m_is_change_session_sell_ord_inc=true;
         event_id=SYMBOL_EVENT_SESSION_SELL_ORDERS_INC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_session_sell_ord_value,this.Name()))
            this.m_struct_prev_symbol.session_sell_orders=this.m_struct_curr_symbol.session_sell_orders;
        }
      else if(this.m_changed_session_sell_ord_value<-this.m_control_session_sell_ord_dec)
        {
         this.m_is_change_session_sell_ord_dec=true;
         event_id=SYMBOL_EVENT_SESSION_SELL_ORDERS_DEC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_session_sell_ord_value,this.Name()))
            this.m_struct_prev_symbol.session_sell_orders=this.m_struct_curr_symbol.session_sell_orders;
        }
     }
//--- Volume change in the last deal
   if(this.IsPresentEventFlag(SYMBOL_EVENT_FLAG_VOLUME))
     {
      this.m_changed_volume_value=this.m_struct_curr_symbol.volume-this.m_struct_prev_symbol.volume;
      if(this.m_changed_volume_value>this.m_control_volume_inc)
        {
         this.m_is_change_volume_inc=true;
         event_id=SYMBOL_EVENT_VOLUME_INC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_volume_value,this.Name()))
            this.m_struct_prev_symbol.volume=this.m_struct_curr_symbol.volume;
        }
      else if(this.m_changed_volume_value<-this.m_control_volume_dec)
        {
         this.m_is_change_volume_dec=true;
         event_id=SYMBOL_EVENT_VOLUME_DEC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_volume_value,this.Name()))
            this.m_struct_prev_symbol.volume=this.m_struct_curr_symbol.volume;
        }
     }
//--- Maximum volume change for a day
   if(this.IsPresentEventFlag(SYMBOL_EVENT_FLAG_VOLUME_HIGH_DAY))
     {
      this.m_changed_volume_high_day_value=this.m_struct_curr_symbol.volume_high_day-this.m_struct_prev_symbol.volume_high_day;
      if(this.m_changed_volume_high_day_value>this.m_control_volume_high_day_inc)
        {
         this.m_is_change_volume_high_day_inc=true;
         event_id=SYMBOL_EVENT_VOLUME_HIGH_DAY_INC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_volume_high_day_value,this.Name()))
            this.m_struct_prev_symbol.volume_high_day=this.m_struct_curr_symbol.volume_high_day;
        }
      else if(this.m_changed_volume_high_day_value<-this.m_control_volume_high_day_dec)
        {
         this.m_is_change_volume_high_day_dec=true;
         event_id=SYMBOL_EVENT_VOLUME_HIGH_DAY_DEC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_volume_high_day_value,this.Name()))
            this.m_struct_prev_symbol.volume_high_day=this.m_struct_curr_symbol.volume_high_day;
        }
     }
//--- Minimum volume change for a day
   if(this.IsPresentEventFlag(SYMBOL_EVENT_FLAG_VOLUME_LOW_DAY))
     {
      this.m_changed_volume_low_day_value=this.m_struct_curr_symbol.volume_low_day-this.m_struct_prev_symbol.volume_low_day;
      if(this.m_changed_volume_low_day_value>this.m_control_volume_low_day_inc)
        {
         this.m_is_change_volume_low_day_inc=true;
         event_id=SYMBOL_EVENT_VOLUME_LOW_DAY_INC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_volume_low_day_value,this.Name()))
            this.m_struct_prev_symbol.volume_low_day=this.m_struct_curr_symbol.volume_low_day;
        }
      else if(this.m_changed_volume_low_day_value<-this.m_control_volume_low_day_dec)
        {
         this.m_is_change_volume_low_day_dec=true;
         event_id=SYMBOL_EVENT_VOLUME_LOW_DAY_DEC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_volume_low_day_value,this.Name()))
            this.m_struct_prev_symbol.volume_low_day=this.m_struct_curr_symbol.volume_low_day;
        }
     }
//--- Spread value change in points
   if(this.IsPresentEventFlag(SYMBOL_EVENT_FLAG_SPREAD))
     {
      this.m_changed_spread_value=this.m_struct_curr_symbol.spread-this.m_struct_prev_symbol.spread;
      if(this.m_changed_spread_value>this.m_control_spread_inc)
        {
         this.m_is_change_spread_inc=true;
         event_id=SYMBOL_EVENT_SPREAD_INC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_spread_value,this.Name()))
            this.m_struct_prev_symbol.spread=this.m_struct_curr_symbol.spread;
        }
      else if(this.m_changed_spread_value<-this.m_control_spread_dec)
        {
         this.m_is_change_spread_dec=true;
         event_id=SYMBOL_EVENT_SPREAD_DEC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_spread_value,this.Name()))
            this.m_struct_prev_symbol.spread=this.m_struct_curr_symbol.spread;
        }
     }
//--- StopLevel change in points
   if(this.IsPresentEventFlag(SYMBOL_EVENT_FLAG_STOPLEVEL))
     {
      this.m_changed_stops_level_value=this.m_struct_curr_symbol.stops_level-this.m_struct_prev_symbol.stops_level;
      if(this.m_changed_stops_level_value>this.m_control_stops_level_inc)
        {
         this.m_is_change_stops_level_inc=true;
         event_id=SYMBOL_EVENT_STOPLEVEL_INC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_stops_level_value,this.Name()))
            this.m_struct_prev_symbol.stops_level=this.m_struct_curr_symbol.stops_level;
        }
      else if(this.m_changed_stops_level_value<-this.m_control_stops_level_dec)
        {
         this.m_is_change_stops_level_dec=true;
         event_id=SYMBOL_EVENT_STOPLEVEL_DEC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_stops_level_value,this.Name()))
            this.m_struct_prev_symbol.stops_level=this.m_struct_curr_symbol.stops_level;
        }
     }
//--- FreezeLevel change in points
   if(this.IsPresentEventFlag(SYMBOL_EVENT_FLAG_FREEZELEVEL))
     {
      this.m_changed_freeze_level_value=this.m_struct_curr_symbol.freeze_level-this.m_struct_prev_symbol.freeze_level;
      if(this.m_changed_freeze_level_value>this.m_control_freeze_level_inc)
        {
         this.m_is_change_freeze_level_inc=true;
         event_id=SYMBOL_EVENT_FREEZELEVEL_INC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_freeze_level_value,this.Name()))
            this.m_struct_prev_symbol.freeze_level=this.m_struct_curr_symbol.freeze_level;
        }
      else if(this.m_changed_freeze_level_value<-this.m_control_freeze_level_dec)
        {
         this.m_is_change_freeze_level_dec=true;
         event_id=SYMBOL_EVENT_FREEZELEVEL_DEC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_freeze_level_value,this.Name()))
            this.m_struct_prev_symbol.freeze_level=this.m_struct_curr_symbol.freeze_level;
        }
     }
//--- Bid/Last price change
   if(this.IsPresentEventFlag(SYMBOL_EVENT_FLAG_BID_LAST))
     {
      this.m_changed_bid_last_value=this.m_struct_curr_symbol.bid_last-this.m_struct_prev_symbol.bid_last;
      if(this.m_changed_bid_last_value>this.m_control_bid_last_inc)
        {
         this.m_is_change_bid_last_inc=true;
         event_id=SYMBOL_EVENT_BID_LAST_INC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_bid_last_value,this.Name()))
            this.m_struct_prev_symbol.bid_last=this.m_struct_curr_symbol.bid_last;
        }
      else if(this.m_changed_bid_last_value<-this.m_control_bid_last_dec)
        {
         this.m_is_change_bid_last_dec=true;
         event_id=SYMBOL_EVENT_BID_LAST_DEC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_bid_last_value,this.Name()))
            this.m_struct_prev_symbol.bid_last=this.m_struct_curr_symbol.bid_last;
        }
     }
//--- Maximum Bid/Last change for a day
   if(this.IsPresentEventFlag(SYMBOL_EVENT_FLAG_BID_LAST_HIGH))
     {
      this.m_changed_bid_last_high_value=this.m_struct_curr_symbol.bid_last_high-this.m_struct_prev_symbol.bid_last_high;
      if(this.m_changed_bid_last_high_value>this.m_control_bid_last_high_inc)
        {
         this.m_is_change_bid_last_high_inc=true;
         event_id=SYMBOL_EVENT_BID_LAST_HIGH_INC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_bid_last_high_value,this.Name()))
            this.m_struct_prev_symbol.bid_last_high=this.m_struct_curr_symbol.bid_last_high;
        }
      else if(this.m_changed_bid_last_high_value<-this.m_control_bid_last_high_dec)
        {
         this.m_is_change_bid_last_high_dec=true;
         event_id=SYMBOL_EVENT_BID_LAST_HIGH_DEC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_bid_last_high_value,this.Name()))
            this.m_struct_prev_symbol.bid_last_high=this.m_struct_curr_symbol.bid_last_high;
        }
     }
//--- Minimum Bid/Last change for a day
   if(this.IsPresentEventFlag(SYMBOL_EVENT_FLAG_BID_LAST_LOW))
     {
      this.m_changed_bid_last_low_value=this.m_struct_curr_symbol.bid_last_low-this.m_struct_prev_symbol.bid_last_low;
      if(this.m_changed_bid_last_low_value>this.m_control_bid_last_low_inc)
        {
         this.m_is_change_bid_last_low_inc=true;
         event_id=SYMBOL_EVENT_BID_LAST_LOW_INC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_bid_last_low_value,this.Name()))
            this.m_struct_prev_symbol.bid_last_low=this.m_struct_curr_symbol.bid_last_low;
        }
      else if(this.m_changed_bid_last_low_value<-this.m_control_bid_last_low_dec)
        {
         this.m_is_change_bid_last_low_dec=true;
         event_id=SYMBOL_EVENT_BID_LAST_LOW_DEC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_bid_last_low_value,this.Name()))
            this.m_struct_prev_symbol.bid_last_low=this.m_struct_curr_symbol.bid_last_low;
        }
     }
//--- Ask price change
   if(this.IsPresentEventFlag(SYMBOL_EVENT_FLAG_ASK))
     {
      this.m_changed_ask_value=this.m_struct_curr_symbol.ask-this.m_struct_prev_symbol.ask;
      if(this.m_changed_ask_value>this.m_control_ask_inc)
        {
         this.m_is_change_ask_inc=true;
         event_id=SYMBOL_EVENT_ASK_INC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_ask_value,this.Name()))
            this.m_struct_prev_symbol.ask=this.m_struct_curr_symbol.ask;
        }
      else if(this.m_changed_ask_value<-this.m_control_ask_dec)
        {
         this.m_is_change_ask_dec=true;
         event_id=SYMBOL_EVENT_ASK_DEC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_ask_value,this.Name()))
            this.m_struct_prev_symbol.ask=this.m_struct_curr_symbol.ask;
        }
     }
//--- Maximum As change for a day
   if(this.IsPresentEventFlag(SYMBOL_EVENT_FLAG_ASK_HIGH))
     {
      this.m_changed_ask_high_value=this.m_struct_curr_symbol.ask_high-this.m_struct_prev_symbol.ask_high;
      if(this.m_changed_ask_high_value>this.m_control_ask_high_inc)
        {
         this.m_is_change_ask_high_inc=true;
         event_id=SYMBOL_EVENT_ASK_HIGH_INC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_ask_high_value,this.Name()))
            this.m_struct_prev_symbol.ask_high=this.m_struct_curr_symbol.ask_high;
        }
      else if(this.m_changed_ask_high_value<-this.m_control_ask_high_dec)
        {
         this.m_is_change_ask_high_dec=true;
         event_id=SYMBOL_EVENT_ASK_HIGH_DEC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_ask_high_value,this.Name()))
            this.m_struct_prev_symbol.ask_high=this.m_struct_curr_symbol.ask_high;
        }
     }
//--- Minimum Ask change for a day
   if(this.IsPresentEventFlag(SYMBOL_EVENT_FLAG_ASK_LOW))
     {
      this.m_changed_ask_low_value=this.m_struct_curr_symbol.ask_low-this.m_struct_prev_symbol.ask_low;
      if(this.m_changed_ask_low_value>this.m_control_ask_low_inc)
        {
         this.m_is_change_ask_low_inc=true;
         event_id=SYMBOL_EVENT_ASK_LOW_INC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_ask_low_value,this.Name()))
            this.m_struct_prev_symbol.ask_low=this.m_struct_curr_symbol.ask_low;
        }
      else if(this.m_changed_ask_low_value<-this.m_control_ask_low_dec)
        {
         this.m_is_change_ask_low_dec=true;
         event_id=SYMBOL_EVENT_ASK_LOW_DEC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_ask_low_value,this.Name()))
            this.m_struct_prev_symbol.ask_low=this.m_struct_curr_symbol.ask_low;
        }
     }
//--- Real volume change for a day
   if(this.IsPresentEventFlag(SYMBOL_EVENT_FLAG_VOLUME_REAL_DAY))
     {
      this.m_changed_volume_real_value=this.m_struct_curr_symbol.volume_real_day-this.m_struct_prev_symbol.volume_real_day;
      if(this.m_changed_volume_real_value>this.m_control_volume_real_inc)
        {
         this.m_is_change_volume_real_inc=true;
         event_id=SYMBOL_EVENT_VOLUME_REAL_DAY_INC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_volume_real_value,this.Name()))
            this.m_struct_prev_symbol.volume_real_day=this.m_struct_curr_symbol.volume_real_day;
        }
      else if(this.m_changed_volume_real_value<-this.m_control_volume_real_dec)
        {
         this.m_is_change_volume_real_dec=true;
         event_id=SYMBOL_EVENT_VOLUME_REAL_DAY_DEC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_volume_real_value,this.Name()))
            this.m_struct_prev_symbol.volume_real_day=this.m_struct_curr_symbol.volume_real_day;
        }
     }
//--- Maximum real volume change for a day
   if(this.IsPresentEventFlag(SYMBOL_EVENT_FLAG_VOLUME_HIGH_REAL_DAY))
     {
      this.m_changed_volume_high_real_day_value=this.m_struct_curr_symbol.volume_high_real_day-this.m_struct_prev_symbol.volume_high_real_day;
      if(this.m_changed_volume_high_real_day_value>this.m_control_volume_high_real_day_inc)
        {
         this.m_is_change_volume_high_real_day_inc=true;
         event_id=SYMBOL_EVENT_VOLUME_HIGH_REAL_DAY_INC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_volume_high_real_day_value,this.Name()))
            this.m_struct_prev_symbol.volume_high_real_day=this.m_struct_curr_symbol.volume_high_real_day;
        }
      else if(this.m_changed_volume_high_real_day_value<-this.m_control_volume_high_real_day_dec)
        {
         this.m_is_change_volume_high_real_day_dec=true;
         event_id=SYMBOL_EVENT_VOLUME_HIGH_REAL_DAY_DEC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_volume_high_real_day_value,this.Name()))
            this.m_struct_prev_symbol.volume_high_real_day=this.m_struct_curr_symbol.volume_high_real_day;
        }
     }
//--- Minimum real volume change for a day
   if(this.IsPresentEventFlag(SYMBOL_EVENT_FLAG_VOLUME_LOW_REAL_DAY))
     {
      this.m_changed_volume_low_real_day_value=this.m_struct_curr_symbol.volume_low_real_day-this.m_struct_prev_symbol.volume_low_real_day;
      if(this.m_changed_volume_low_real_day_value>this.m_control_volume_low_real_day_inc)
        {
         this.m_is_change_volume_low_real_day_inc=true;
         event_id=SYMBOL_EVENT_VOLUME_LOW_REAL_DAY_INC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_volume_low_real_day_value,this.Name()))
            this.m_struct_prev_symbol.volume_low_real_day=this.m_struct_curr_symbol.volume_low_real_day;
        }
      else if(this.m_changed_volume_low_real_day_value<-this.m_control_volume_low_real_day_dec)
        {
         this.m_is_change_volume_low_real_day_dec=true;
         event_id=SYMBOL_EVENT_VOLUME_LOW_REAL_DAY_DEC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_volume_low_real_day_value,this.Name()))
            this.m_struct_prev_symbol.volume_low_real_day=this.m_struct_curr_symbol.volume_low_real_day;
        }
     }
//--- Strike price change
   if(this.IsPresentEventFlag(SYMBOL_EVENT_FLAG_OPTION_STRIKE))
     {
      this.m_changed_option_strike_value=this.m_struct_curr_symbol.option_strike-this.m_struct_prev_symbol.option_strike;
      if(this.m_changed_option_strike_value>this.m_control_option_strike_inc)
        {
         this.m_is_change_option_strike_inc=true;
         event_id=SYMBOL_EVENT_OPTION_STRIKE_INC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_option_strike_value,this.Name()))
            this.m_struct_prev_symbol.option_strike=this.m_struct_curr_symbol.option_strike;
        }
      else if(this.m_changed_option_strike_value<-this.m_control_option_strike_dec)
        {
         this.m_is_change_option_strike_dec=true;
         event_id=SYMBOL_EVENT_OPTION_STRIKE_DEC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_option_strike_value,this.Name()))
            this.m_struct_prev_symbol.option_strike=this.m_struct_curr_symbol.option_strike;
        }
     }
//--- Change of the maximum available total position volume and pending orders in one direction
   if(this.IsPresentEventFlag(SYMBOL_EVENT_FLAG_VOLUME_LIMIT))
     {
      this.m_changed_volume_limit_value=this.m_struct_curr_symbol.volume_limit-this.m_struct_prev_symbol.volume_limit;
      if(this.m_changed_volume_limit_value>0)
        {
         this.m_is_change_volume_limit_inc=true;
         event_id=SYMBOL_EVENT_VOLUME_LIMIT_INC;
        }
      else
        {
         this.m_is_change_volume_limit_dec=true;
         event_id=SYMBOL_EVENT_VOLUME_LIMIT_DEC;
        }
      if(this.EventAdd(event_id,this.TickTime(),this.m_changed_volume_limit_value,this.Name()))
         this.m_struct_prev_symbol.volume_limit=this.m_struct_curr_symbol.volume_limit;
     }
//--- Swap long change
   if(this.IsPresentEventFlag(SYMBOL_EVENT_FLAG_SWAP_LONG))
     {
      this.m_changed_swap_long_value=this.m_struct_curr_symbol.swap_long-this.m_struct_prev_symbol.swap_long;
      if(this.m_changed_swap_long_value>0)
        {
         this.m_is_change_swap_long_inc=true;
         event_id=SYMBOL_EVENT_SWAP_LONG_INC;
        }
      else
        {
         this.m_is_change_swap_long_dec=true;
         event_id=SYMBOL_EVENT_SWAP_LONG_DEC;
        }
      if(this.EventAdd(event_id,this.TickTime(),this.m_changed_swap_long_value,this.Name()))
         this.m_struct_prev_symbol.swap_long=this.m_struct_curr_symbol.swap_long;
     }
//--- Swap short change
   if(this.IsPresentEventFlag(SYMBOL_EVENT_FLAG_SWAP_SHORT))
     {
      this.m_changed_swap_short_value=this.m_struct_curr_symbol.swap_short-this.m_struct_prev_symbol.swap_short;
      if(this.m_changed_swap_short_value>0)
        {
         this.m_is_change_swap_short_inc=true;
         event_id=SYMBOL_EVENT_SWAP_SHORT_INC;
        }
      else
        {
         this.m_is_change_swap_short_dec=true;
         event_id=SYMBOL_EVENT_SWAP_SHORT_DEC;
        }
      if(this.EventAdd(event_id,this.TickTime(),this.m_changed_swap_short_value,this.Name()))
         this.m_struct_prev_symbol.swap_short=this.m_struct_curr_symbol.swap_short;
     }
//--- Change of the total volume of deals during the current session
   if(this.IsPresentEventFlag(SYMBOL_EVENT_FLAG_SESSION_VOLUME))
     {
      this.m_changed_session_volume_value=this.m_struct_curr_symbol.session_volume-this.m_struct_prev_symbol.session_volume;
      if(this.m_changed_session_volume_value>this.m_control_session_volume_inc)
        {
         this.m_is_change_session_volume_inc=true;
         event_id=SYMBOL_EVENT_SESSION_VOLUME_INC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_session_volume_value,this.Name()))
            this.m_struct_prev_symbol.session_volume=this.m_struct_curr_symbol.session_volume;
        }
      else if(this.m_changed_session_volume_value<-this.m_control_session_volume_dec)
        {
         this.m_is_change_session_volume_dec=true;
         event_id=SYMBOL_EVENT_SESSION_VOLUME_DEC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_session_volume_value,this.Name()))
            this.m_struct_prev_symbol.session_volume=this.m_struct_curr_symbol.session_volume;
        }
     }
//--- Change of the total turnover during the current session
   if(this.IsPresentEventFlag(SYMBOL_EVENT_FLAG_SESSION_TURNOVER))
     {
      this.m_changed_session_turnover_value=this.m_struct_curr_symbol.session_turnover-this.m_struct_prev_symbol.session_turnover;
      if(this.m_changed_session_turnover_value>this.m_control_session_turnover_inc)
        {
         this.m_is_change_session_turnover_inc=true;
         event_id=SYMBOL_EVENT_SESSION_TURNOVER_INC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_session_turnover_value,this.Name()))
            this.m_struct_prev_symbol.session_turnover=this.m_struct_curr_symbol.session_turnover;
        }
      else if(this.m_changed_session_turnover_value<-this.m_control_session_turnover_dec)
        {
         this.m_is_change_session_turnover_dec=true;
         event_id=SYMBOL_EVENT_SESSION_TURNOVER_DEC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_session_turnover_value,this.Name()))
            this.m_struct_prev_symbol.session_turnover=this.m_struct_curr_symbol.session_turnover;
        }
     }
//--- Change of the total volume of open positions during the current session
   if(this.IsPresentEventFlag(SYMBOL_EVENT_FLAG_SESSION_INTEREST))
     {
      this.m_changed_session_interest_value=this.m_struct_curr_symbol.session_interest-this.m_struct_prev_symbol.session_interest;
      if(this.m_changed_session_interest_value>this.m_control_session_interest_inc)
        {
         this.m_is_change_session_interest_inc=true;
         event_id=SYMBOL_EVENT_SESSION_INTEREST_INC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_session_interest_value,this.Name()))
            this.m_struct_prev_symbol.session_interest=this.m_struct_curr_symbol.session_interest;
        }
      else if(this.m_changed_session_interest_value<-this.m_control_session_interest_dec)
        {
         this.m_is_change_session_interest_dec=true;
         event_id=SYMBOL_EVENT_SESSION_INTEREST_DEC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_session_interest_value,this.Name()))
            this.m_struct_prev_symbol.session_interest=this.m_struct_curr_symbol.session_interest;
        }
     }
//--- Change of the current total volume of buy orders
   if(this.IsPresentEventFlag(SYMBOL_EVENT_FLAG_SESSION_BUY_ORD_VOLUME))
     {
      this.m_changed_session_buy_ord_volume_value=this.m_struct_curr_symbol.session_buy_ord_volume-this.m_struct_prev_symbol.session_buy_ord_volume;
      if(this.m_changed_session_buy_ord_volume_value>this.m_control_session_buy_ord_volume_inc)
        {
         this.m_is_change_session_buy_ord_volume_inc=true;
         event_id=SYMBOL_EVENT_SESSION_BUY_ORD_VOLUME_INC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_session_buy_ord_volume_value,this.Name()))
            this.m_struct_prev_symbol.session_buy_ord_volume=this.m_struct_curr_symbol.session_buy_ord_volume;
        }
      else if(this.m_changed_session_buy_ord_volume_value<-this.m_control_session_buy_ord_volume_dec)
        {
         this.m_is_change_session_buy_ord_volume_dec=true;
         event_id=SYMBOL_EVENT_SESSION_BUY_ORD_VOLUME_DEC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_session_buy_ord_volume_value,this.Name()))
            this.m_struct_prev_symbol.session_buy_ord_volume=this.m_struct_curr_symbol.session_buy_ord_volume;
        }
     }
//--- Change of the current total volume of sell orders
   if(this.IsPresentEventFlag(SYMBOL_EVENT_FLAG_SESSION_SELL_ORD_VOLUME))
     {
      this.m_changed_session_sell_ord_volume_value=this.m_struct_curr_symbol.session_sell_ord_volume-this.m_struct_prev_symbol.session_sell_ord_volume;
      if(this.m_changed_session_sell_ord_volume_value>this.m_control_session_sell_ord_volume_inc)
        {
         this.m_is_change_session_sell_ord_volume_inc=true;
         event_id=SYMBOL_EVENT_SESSION_SELL_ORD_VOLUME_INC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_session_sell_ord_volume_value,this.Name()))
            this.m_struct_prev_symbol.session_sell_ord_volume=this.m_struct_curr_symbol.session_sell_ord_volume;
        }
      else if(this.m_changed_session_sell_ord_volume_value<-this.m_control_session_sell_ord_volume_dec)
        {
         this.m_is_change_session_sell_ord_volume_dec=true;
         event_id=SYMBOL_EVENT_SESSION_SELL_ORD_VOLUME_DEC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_session_sell_ord_volume_value,this.Name()))
            this.m_struct_prev_symbol.session_sell_ord_volume=this.m_struct_curr_symbol.session_sell_ord_volume;
        }
     }
//--- Session open price change
   if(this.IsPresentEventFlag(SYMBOL_EVENT_FLAG_SESSION_OPEN))
     {
      this.m_changed_session_open_value=this.m_struct_curr_symbol.session_open-this.m_struct_prev_symbol.session_open;
      if(this.m_changed_session_open_value>this.m_control_session_open_inc)
        {
         this.m_is_change_session_open_inc=true;
         event_id=SYMBOL_EVENT_SESSION_OPEN_INC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_session_open_value,this.Name()))
            this.m_struct_prev_symbol.session_open=this.m_struct_curr_symbol.session_open;
        }
      else if(this.m_changed_session_open_value<-this.m_control_session_open_dec)
        {
         this.m_is_change_session_open_dec=true;
         event_id=SYMBOL_EVENT_SESSION_OPEN_DEC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_session_open_value,this.Name()))
            this.m_struct_prev_symbol.session_open=this.m_struct_curr_symbol.session_open;
        }
     }
//--- Session close price change
   if(this.IsPresentEventFlag(SYMBOL_EVENT_FLAG_SESSION_CLOSE))
     {
      this.m_changed_session_close_value=this.m_struct_curr_symbol.session_close-this.m_struct_prev_symbol.session_close;
      if(this.m_changed_session_close_value>this.m_control_session_close_inc)
        {
         this.m_is_change_session_close_inc=true;
         event_id=SYMBOL_EVENT_SESSION_CLOSE_INC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_session_close_value,this.Name()))
            this.m_struct_prev_symbol.session_close=this.m_struct_curr_symbol.session_close;
        }
      else if(this.m_changed_session_close_value<-this.m_control_session_close_dec)
        {
         this.m_is_change_session_close_dec=true;
         event_id=SYMBOL_EVENT_SESSION_CLOSE_DEC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_session_close_value,this.Name()))
            this.m_struct_prev_symbol.session_close=this.m_struct_curr_symbol.session_close;
        }
     }
//--- Average weighted session price change
   if(this.IsPresentEventFlag(SYMBOL_EVENT_FLAG_SESSION_AW))
     {
      this.m_changed_session_aw_value=this.m_struct_curr_symbol.session_aw-this.m_struct_prev_symbol.session_aw;
      if(this.m_changed_session_aw_value>this.m_control_session_aw_inc)
        {
         this.m_is_change_session_aw_inc=true;
         event_id=SYMBOL_EVENT_SESSION_AW_INC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_session_aw_value,this.Name()))
            this.m_struct_prev_symbol.session_aw=this.m_struct_curr_symbol.session_aw;
        }
      else if(this.m_changed_session_aw_value<-this.m_control_session_aw_dec)
        {
         this.m_is_change_session_aw_dec=true;
         event_id=SYMBOL_EVENT_SESSION_AW_DEC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_session_aw_value,this.Name()))
            this.m_struct_prev_symbol.session_aw=this.m_struct_curr_symbol.session_aw;
        }
     }
  }
//+------------------------------------------------------------------+

El método es bastante voluminoso, pero solo debido a los bloques de comprobaciones idénticas del cambio en las propiedades del símbolo investigadas. Ya analizamos un método análogo en la parte 13, al monitorear los cambios de la cuenta. Aquí la lógica es similar, salvo que ahora toda nuestra funcionalidad de guardado de eventos se ubica en el objeto básico CBaseObj.
Vamos a usar como ejemplo el cambio del precio medio ponderado de la sesión.
Al inicio del método, reseteamos los valores de las propiedades investigadas con la ayuda del método InitChangesParams() y establecemos el estado del evento como "No hay eventos".

Para registrar el evento de disminución del valor de una propiedad, realizaremos todas las comprobaciones exactamente de la misma forma, pero comprobando si el valor ha disminuido en más que la magnitud controlada.
Enviamos al método de adición de un evento a la lista EventAdd() los siguientes datos:
  1. el identificador del evento (event_id, rellenado al comprobar el evento)
  2. la hora actual en milisegundos (método TickTime() de la clase básica CBaseObj)
  3. el valor calculado en el que ha cambiado la propiedad del símbolo (m_changed_session_aw_value)
  4. el nombre del objeto (en este caso, el nombre del símbolo)

En el constructor protegido de la clase, introducimos igualmente un pequeño cambio: para rellenar la nueva propiedad del objeto de símbolo "índice del símbolo en la ventana de Observación de mercado", deberemos transmitir este índice al escanear los símbolos en la observación de mercado. Transmitiremos el índice directamente al constructor de clase:

protected:
//--- Protected parametric constructor
                     CSymbol(ENUM_SYMBOL_STATUS symbol_status,const string name,const int index);

Asimismo, añadimos en la sección protegida de la clase un método más, encargado de retornar el número de dígitos tras la coma según el método de cálculo de los swaps:

//--- Get and return integer properties of a selected symbol from its parameters
   bool              SymbolExists(const string name)     const;
   long              SymbolExists(void)                  const;
   long              SymbolCustom(void)                  const;
   long              SymbolChartMode(void)               const;
   long              SymbolMarginHedgedUseLEG(void)      const;
   long              SymbolOrderFillingMode(void)        const;
   long              SymbolOrderMode(void)               const;
   long              SymbolExpirationMode(void)          const;
   long              SymbolOrderGTCMode(void)            const;
   long              SymbolOptionMode(void)              const;
   long              SymbolOptionRight(void)             const;
   long              SymbolBackgroundColor(void)         const;
   long              SymbolCalcMode(void)                const;
   long              SymbolSwapMode(void)                const;
   long              SymbolDigitsLot(void);
   int               SymbolDigitsBySwap(void);
//--- Get and return real properties of a selected symbol from its parameters

Lo que ocurre es que los swaps se pueden calcular en dinero, puntos y tanto por ciento. Y para cada uno de estos tipos de cálculo de swap, resulta imprescindible retornar el valor correspondiente del número de dígitos tras la coma. Este método precisamente los retorna. Vamos a escribirlo fuera del cuerpo de la clase:

//+------------------------------------------------------------------+
//| Return the number of decimal places                              |
//| depending on the swap calculation method                         |
//+------------------------------------------------------------------+
int CSymbol::SymbolDigitsBySwap(void)
  {
   return
     (
      this.SwapMode()==SYMBOL_SWAP_MODE_POINTS           || 
      this.SwapMode()==SYMBOL_SWAP_MODE_REOPEN_CURRENT   || 
      this.SwapMode()==SYMBOL_SWAP_MODE_REOPEN_BID       ?  this.Digits() :
      this.SwapMode()==SYMBOL_SWAP_MODE_CURRENCY_SYMBOL  || 
      this.SwapMode()==SYMBOL_SWAP_MODE_CURRENCY_MARGIN  || 
      this.SwapMode()==SYMBOL_SWAP_MODE_CURRENCY_DEPOSIT ?  this.DigitsCurrency():
      this.SwapMode()==SYMBOL_SWAP_MODE_INTEREST_CURRENT || 
      this.SwapMode()==SYMBOL_SWAP_MODE_INTEREST_OPEN    ?  1  :  0
     );
  }  
//+------------------------------------------------------------------+

Hacemos el método Refresh() virtual, dado que ha sido definido así en la clase básica de todos los objetos CBaseObj. Asimismo, cambiaremos el tipo de método de actualización de los datos de cotización RefreshRates() de void a bool, dado que ahora, al inicio del método Refresh() se llamará el método RefreshRates(), y si no hemos logrado obtener los datos en él, el método retornará false, por lo que también se realizará de inmediato la salida del método Refresh().
Además, añadimos la definición del método que retorna la descripción del evento de símbolo:

//--- Update all symbol data
   virtual void      Refresh(void);
//--- Update quote data by a symbol
   bool              RefreshRates(void);
//--- Return description of symbol events
   string            EventDescription(const ENUM_SYMBOL_EVENT event);

en la sección pública de la clase, en el apartado de los métodos de acceso simplificado, en los métodos que retornan las propiedades de tipo entero del símbolo,
añadimos el método de retorno del índice del símbolo en la venta de "Observación de mercado":

//+------------------------------------------------------------------+
//| Methods of a simplified access to the order object properties    |
//+------------------------------------------------------------------+
//--- Integer properties
   long              Status(void)                                 const { return this.GetProperty(SYMBOL_PROP_STATUS);                                      }
   int               IndexInMarketWatch(void)                     const { return (int)this.GetProperty(SYMBOL_PROP_INDEX_MW);                               }
   bool              IsCustom(void)                               const { return (bool)this.GetProperty(SYMBOL_PROP_CUSTOM);                                }
   color             ColorBackground(void)                        const { return (color)this.GetProperty(SYMBOL_PROP_BACKGROUND_COLOR);                     }
   ENUM_SYMBOL_CHART_MODE ChartMode(void)                         const { return (ENUM_SYMBOL_CHART_MODE)this.GetProperty(SYMBOL_PROP_CHART_MODE);          }
   bool              IsExist(void)                                const { return (bool)this.GetProperty(SYMBOL_PROP_EXIST);                                 }
   bool              IsExist(const string name)                   const { return this.SymbolExists(name);                                                   }
   bool              IsSelect(void)                               const { return (bool)this.GetProperty(SYMBOL_PROP_SELECT);                                }
   bool              IsVisible(void)                              const { return (bool)this.GetProperty(SYMBOL_PROP_VISIBLE);                               }
   long              SessionDeals(void)                           const { return this.GetProperty(SYMBOL_PROP_SESSION_DEALS);                               }
   long              SessionBuyOrders(void)                       const { return this.GetProperty(SYMBOL_PROP_SESSION_BUY_ORDERS);                          }
   long              SessionSellOrders(void)                      const { return this.GetProperty(SYMBOL_PROP_SESSION_SELL_ORDERS);                         }
   long              Volume(void)                                 const { return this.GetProperty(SYMBOL_PROP_VOLUME);                                      }
   long              VolumeHigh(void)                             const { return this.GetProperty(SYMBOL_PROP_VOLUMEHIGH);                                  }
   long              VolumeLow(void)                              const { return this.GetProperty(SYMBOL_PROP_VOLUMELOW);                                   }
   datetime          Time(void)                                   const { return (datetime)this.GetProperty(SYMBOL_PROP_TIME);                              }
   int               Digits(void)                                 const { return (int)this.GetProperty(SYMBOL_PROP_DIGITS);                                 }
   int               DigitsLot(void)                              const { return (int)this.GetProperty(SYMBOL_PROP_DIGITS_LOTS);                            }
   int               Spread(void)                                 const { return (int)this.GetProperty(SYMBOL_PROP_SPREAD);                                 }
   bool              IsSpreadFloat(void)                          const { return (bool)this.GetProperty(SYMBOL_PROP_SPREAD_FLOAT);                          }
   int               TicksBookdepth(void)                         const { return (int)this.GetProperty(SYMBOL_PROP_TICKS_BOOKDEPTH);                        }
   ENUM_SYMBOL_CALC_MODE TradeCalcMode(void)                      const { return (ENUM_SYMBOL_CALC_MODE)this.GetProperty(SYMBOL_PROP_TRADE_CALC_MODE);      }
   ENUM_SYMBOL_TRADE_MODE TradeMode(void)                         const { return (ENUM_SYMBOL_TRADE_MODE)this.GetProperty(SYMBOL_PROP_TRADE_MODE);          }
   datetime          StartTime(void)                              const { return (datetime)this.GetProperty(SYMBOL_PROP_START_TIME);                        }
   datetime          ExpirationTime(void)                         const { return (datetime)this.GetProperty(SYMBOL_PROP_EXPIRATION_TIME);                   }
   int               TradeStopLevel(void)                         const { return (int)this.GetProperty(SYMBOL_PROP_TRADE_STOPS_LEVEL);                      }
   int               TradeFreezeLevel(void)                       const { return (int)this.GetProperty(SYMBOL_PROP_TRADE_FREEZE_LEVEL);                     }
   ENUM_SYMBOL_TRADE_EXECUTION TradeExecutionMode(void)           const { return (ENUM_SYMBOL_TRADE_EXECUTION)this.GetProperty(SYMBOL_PROP_TRADE_EXEMODE);  }
   ENUM_SYMBOL_SWAP_MODE SwapMode(void)                           const { return (ENUM_SYMBOL_SWAP_MODE)this.GetProperty(SYMBOL_PROP_SWAP_MODE);            }
   ENUM_DAY_OF_WEEK  SwapRollover3Days(void)                      const { return (ENUM_DAY_OF_WEEK)this.GetProperty(SYMBOL_PROP_SWAP_ROLLOVER3DAYS);        }
   bool              IsMarginHedgedUseLeg(void)                   const { return (bool)this.GetProperty(SYMBOL_PROP_MARGIN_HEDGED_USE_LEG);                 }
   int               ExpirationModeFlags(void)                    const { return (int)this.GetProperty(SYMBOL_PROP_EXPIRATION_MODE);                        }
   int               FillingModeFlags(void)                       const { return (int)this.GetProperty(SYMBOL_PROP_FILLING_MODE);                           }
   int               OrderModeFlags(void)                         const { return (int)this.GetProperty(SYMBOL_PROP_ORDER_MODE);                             }
   ENUM_SYMBOL_ORDER_GTC_MODE OrderModeGTC(void)                  const { return (ENUM_SYMBOL_ORDER_GTC_MODE)this.GetProperty(SYMBOL_PROP_ORDER_GTC_MODE);  }
   ENUM_SYMBOL_OPTION_MODE OptionMode(void)                       const { return (ENUM_SYMBOL_OPTION_MODE)this.GetProperty(SYMBOL_PROP_OPTION_MODE);        }
   ENUM_SYMBOL_OPTION_RIGHT OptionRight(void)                     const { return (ENUM_SYMBOL_OPTION_RIGHT)this.GetProperty(SYMBOL_PROP_OPTION_RIGHT);      }
//--- Real properties

Y en los métodos que retornan las propiedades de tipo real
añadimos el método que retorna el precio Bid o Last, el método que retorna el precio Bid o Last máximo en el día y el método que retorna el precio Bid o Last mínimo en el día:

//--- Real properties
   double            Bid(void)                                    const { return this.GetProperty(SYMBOL_PROP_BID);                                         }
   double            BidHigh(void)                                const { return this.GetProperty(SYMBOL_PROP_BIDHIGH);                                     }
   double            BidLow(void)                                 const { return this.GetProperty(SYMBOL_PROP_BIDLOW);                                      }
   double            Ask(void)                                    const { return this.GetProperty(SYMBOL_PROP_ASK);                                         }
   double            AskHigh(void)                                const { return this.GetProperty(SYMBOL_PROP_ASKHIGH);                                     }
   double            AskLow(void)                                 const { return this.GetProperty(SYMBOL_PROP_ASKLOW);                                      }
   double            Last(void)                                   const { return this.GetProperty(SYMBOL_PROP_LAST);                                        }
   double            LastHigh(void)                               const { return this.GetProperty(SYMBOL_PROP_LASTHIGH);                                    }
   double            LastLow(void)                                const { return this.GetProperty(SYMBOL_PROP_LASTLOW);                                     }
   double            VolumeReal(void)                             const { return this.GetProperty(SYMBOL_PROP_VOLUME_REAL);                                 }
   double            VolumeHighReal(void)                         const { return this.GetProperty(SYMBOL_PROP_VOLUMEHIGH_REAL);                             }
   double            VolumeLowReal(void)                          const { return this.GetProperty(SYMBOL_PROP_VOLUMELOW_REAL);                              }
   double            OptionStrike(void)                           const { return this.GetProperty(SYMBOL_PROP_OPTION_STRIKE);                               }
   double            Point(void)                                  const { return this.GetProperty(SYMBOL_PROP_POINT);                                       }
   double            TradeTickValue(void)                         const { return this.GetProperty(SYMBOL_PROP_TRADE_TICK_VALUE);                            }
   double            TradeTickValueProfit(void)                   const { return this.GetProperty(SYMBOL_PROP_TRADE_TICK_VALUE_PROFIT);                     }
   double            TradeTickValueLoss(void)                     const { return this.GetProperty(SYMBOL_PROP_TRADE_TICK_VALUE_LOSS);                       }
   double            TradeTickSize(void)                          const { return this.GetProperty(SYMBOL_PROP_TRADE_TICK_SIZE);                             }
   double            TradeContractSize(void)                      const { return this.GetProperty(SYMBOL_PROP_TRADE_CONTRACT_SIZE);                         }
   double            TradeAccuredInterest(void)                   const { return this.GetProperty(SYMBOL_PROP_TRADE_ACCRUED_INTEREST);                      }
   double            TradeFaceValue(void)                         const { return this.GetProperty(SYMBOL_PROP_TRADE_FACE_VALUE);                            }
   double            TradeLiquidityRate(void)                     const { return this.GetProperty(SYMBOL_PROP_TRADE_LIQUIDITY_RATE);                        }
   double            LotsMin(void)                                const { return this.GetProperty(SYMBOL_PROP_VOLUME_MIN);                                  }
   double            LotsMax(void)                                const { return this.GetProperty(SYMBOL_PROP_VOLUME_MAX);                                  }
   double            LotsStep(void)                               const { return this.GetProperty(SYMBOL_PROP_VOLUME_STEP);                                 }
   double            VolumeLimit(void)                            const { return this.GetProperty(SYMBOL_PROP_VOLUME_LIMIT);                                }
   double            SwapLong(void)                               const { return this.GetProperty(SYMBOL_PROP_SWAP_LONG);                                   }
   double            SwapShort(void)                              const { return this.GetProperty(SYMBOL_PROP_SWAP_SHORT);                                  }
   double            MarginInitial(void)                          const { return this.GetProperty(SYMBOL_PROP_MARGIN_INITIAL);                              }
   double            MarginMaintenance(void)                      const { return this.GetProperty(SYMBOL_PROP_MARGIN_MAINTENANCE);                          }
   double            MarginLongInitial(void)                      const { return this.GetProperty(SYMBOL_PROP_MARGIN_LONG_INITIAL);                         }
   double            MarginBuyStopInitial(void)                   const { return this.GetProperty(SYMBOL_PROP_MARGIN_BUY_STOP_INITIAL);                     }
   double            MarginBuyLimitInitial(void)                  const { return this.GetProperty(SYMBOL_PROP_MARGIN_BUY_LIMIT_INITIAL);                    }
   double            MarginBuyStopLimitInitial(void)              const { return this.GetProperty(SYMBOL_PROP_MARGIN_BUY_STOPLIMIT_INITIAL);                }
   double            MarginLongMaintenance(void)                  const { return this.GetProperty(SYMBOL_PROP_MARGIN_LONG_MAINTENANCE);                     }
   double            MarginBuyStopMaintenance(void)               const { return this.GetProperty(SYMBOL_PROP_MARGIN_BUY_STOP_MAINTENANCE);                 }
   double            MarginBuyLimitMaintenance(void)              const { return this.GetProperty(SYMBOL_PROP_MARGIN_BUY_LIMIT_MAINTENANCE);                }
   double            MarginBuyStopLimitMaintenance(void)          const { return this.GetProperty(SYMBOL_PROP_MARGIN_BUY_STOPLIMIT_MAINTENANCE);            }
   double            MarginShortInitial(void)                     const { return this.GetProperty(SYMBOL_PROP_MARGIN_SHORT_INITIAL);                        }
   double            MarginSellStopInitial(void)                  const { return this.GetProperty(SYMBOL_PROP_MARGIN_SELL_STOP_INITIAL);                    }
   double            MarginSellLimitInitial(void)                 const { return this.GetProperty(SYMBOL_PROP_MARGIN_SELL_LIMIT_INITIAL);                   }
   double            MarginSellStopLimitInitial(void)             const { return this.GetProperty(SYMBOL_PROP_MARGIN_SELL_STOPLIMIT_INITIAL);               }
   double            MarginShortMaintenance(void)                 const { return this.GetProperty(SYMBOL_PROP_MARGIN_SHORT_MAINTENANCE);                    }
   double            MarginSellStopMaintenance(void)              const { return this.GetProperty(SYMBOL_PROP_MARGIN_SELL_STOP_MAINTENANCE);                }
   double            MarginSellLimitMaintenance(void)             const { return this.GetProperty(SYMBOL_PROP_MARGIN_SELL_LIMIT_MAINTENANCE);               }
   double            MarginSellStopLimitMaintenance(void)         const { return this.GetProperty(SYMBOL_PROP_MARGIN_SELL_STOPLIMIT_MAINTENANCE);           }
   double            SessionVolume(void)                          const { return this.GetProperty(SYMBOL_PROP_SESSION_VOLUME);                              }
   double            SessionTurnover(void)                        const { return this.GetProperty(SYMBOL_PROP_SESSION_TURNOVER);                            }
   double            SessionInterest(void)                        const { return this.GetProperty(SYMBOL_PROP_SESSION_INTEREST);                            }
   double            SessionBuyOrdersVolume(void)                 const { return this.GetProperty(SYMBOL_PROP_SESSION_BUY_ORDERS_VOLUME);                   }
   double            SessionSellOrdersVolume(void)                const { return this.GetProperty(SYMBOL_PROP_SESSION_SELL_ORDERS_VOLUME);                  }
   double            SessionOpen(void)                            const { return this.GetProperty(SYMBOL_PROP_SESSION_OPEN);                                }
   double            SessionClose(void)                           const { return this.GetProperty(SYMBOL_PROP_SESSION_CLOSE);                               }
   double            SessionAW(void)                              const { return this.GetProperty(SYMBOL_PROP_SESSION_AW);                                  }
   double            SessionPriceSettlement(void)                 const { return this.GetProperty(SYMBOL_PROP_SESSION_PRICE_SETTLEMENT);                    }
   double            SessionPriceLimitMin(void)                   const { return this.GetProperty(SYMBOL_PROP_SESSION_PRICE_LIMIT_MIN);                     }
   double            SessionPriceLimitMax(void)                   const { return this.GetProperty(SYMBOL_PROP_SESSION_PRICE_LIMIT_MAX);                     }
   double            MarginHedged(void)                           const { return this.GetProperty(SYMBOL_PROP_MARGIN_HEDGED);                               }
   double            NormalizedPrice(const double price)          const;
   double            BidLast(void)                                const;
   double            BidLastHigh(void)                            const;
   double            BidLastLow(void                            const;
//--- String properties

Los implementamos fuera del cuerpo de la clase:

//+------------------------------------------------------------------+
//| Return Bid or Last price                                         |
//| depending on the chart construction method                       |
//+------------------------------------------------------------------+
double CSymbol::BidLast(void) const
  {
   return(this.ChartMode()==SYMBOL_CHART_MODE_BID ? this.GetProperty(SYMBOL_PROP_BID) : this.GetProperty(SYMBOL_PROP_LAST));
  }  
//+------------------------------------------------------------------+
//| Return maximum Bid or Last price for a day                       |
//| depending on the chart construction method                       |
//+------------------------------------------------------------------+
double CSymbol::BidLastHigh(void) const
  {
   return(this.ChartMode()==SYMBOL_CHART_MODE_BID ? this.GetProperty(SYMBOL_PROP_BIDHIGH) : this.GetProperty(SYMBOL_PROP_LASTHIGH));
  }  
//+------------------------------------------------------------------+
//| Return minimum Bid or Last price for a day                       |
//| depending on the chart construction method                       |
//+------------------------------------------------------------------+
double CSymbol::BidLastLow(void) const
  {
   return(this.ChartMode()==SYMBOL_CHART_MODE_BID ? this.GetProperty(SYMBOL_PROP_BIDLOW) : this.GetProperty(SYMBOL_PROP_LASTLOW));
  }
//+------------------------------------------------------------------+

Aquí todo es sencillo: dependiendo de los precios sobre los que se construye el gráfico de precio, se retorna la propiedad correspondiente del símbolo, o bien con los precios Bid, o bien con los precios Last.

También en la sección pública, escribimos los métodos para esteblecer las propiedades controladas y los métodos que retornan las magnitudes de cambio de las propiedades y las banderas de evento del símbolo:

//+------------------------------------------------------------------+
//| Get and set the parameters of tracked 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 in 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) growth, (5) decrease value
   void              SetControlSessionDealsInc(const long value)                 { this.m_control_session_deals_inc=::fabs(value);           }
   void              SetControlSessionDealsDec(const long value)                 { this.m_control_session_deals_dec=::fabs(value);           }
   long              GetValueChangedSessionDeals(void)                     const { return this.m_changed_session_deals_value;                }
   bool              IsIncreaseSessionDeals(void)                          const { return this.m_is_change_session_deals_inc;                }
   bool              IsDecreaseSessionDeals(void)                          const { return this.m_is_change_session_deals_dec;                }
   //--- Buy orders of the current session
   //--- setting the controlled value of (1) growth, (2) decrease in the current number of Buy orders
   //--- getting (3) the current number of Buy orders change value,
   //--- getting the flag of the current Buy orders' number change exceeding the (4) growth, (5) decrease value
   void              SetControlSessionBuyOrdInc(const long value)                { this.m_control_session_buy_ord_inc=::fabs(value);         }
   void              SetControlSessionBuyOrdDec(const long value)                { this.m_control_session_buy_ord_dec=::fabs(value);         }
   long              GetValueChangedSessionBuyOrders(void)                 const { return this.m_changed_session_buy_ord_value;              }
   bool              IsIncreaseSessionBuyOrders(void)                      const { return this.m_is_change_session_buy_ord_inc;              }
   bool              IsDecreaseSessionBuyOrders(void)                      const { return this.m_is_change_session_buy_ord_dec;              }
   //--- Sell orders of the current session
   //--- setting the controlled value of (1) growth, (2) decrease in the current number of Sell orders
   //--- getting (3) the current number of Sell orders change value,
   //--- getting the flag of the current Sell orders' number change exceeding the (4) growth, (5) decrease value
   void              SetControlSessionSellOrdInc(const long value)               { this.m_control_session_sell_ord_inc=::fabs(value);        }
   void              SetControlSessionSellOrdDec(const long value)               { this.m_control_session_sell_ord_dec=::fabs(value);        }
   long              GetValueChangedSessionSellOrders(void)                const { return this.m_changed_session_sell_ord_value;             }
   bool              IsIncreaseSessionSellOrders(void)                     const { return this.m_is_change_session_sell_ord_inc;             }
   bool              IsDecreaseSessionSellOrders(void)                     const { return this.m_is_change_session_sell_ord_dec;             }
   //--- Volume of the last deal
   //--- setting the last deal volume controlled (1) growth, (2) decrease value
   //--- getting (3) volume change values in the last deal,
   //--- getting the flag of the volume change in the last deal exceeding the (4) growth, (5) decrease value
   void              SetControlVolumeInc(const long value)                       { this.m_control_volume_inc=::fabs(value);                  }
   void              SetControlVolumeDec(const long value)                       { this.m_control_volume_dec=::fabs(value);                  }
   long              GetValueChangedVolume(void)                           const { return this.m_changed_volume_value;                       }
   bool              IsIncreaseVolume(void)                                const { return this.m_is_change_volume_inc;                       }
   bool              IsDecreaseVolume(void)                                const { return this.m_is_change_volume_dec;                       }
   //--- Maximum volume within a day
   //--- setting the maximum day volume controlled (1) growth, (2) decrease value
   //--- getting (3) the maximum volume change value within a day,
   //--- getting the flag of the maximum day volume change exceeding the (4) growth, (5) decrease value
   void              SetControlVolumeHighInc(const long value)                   { this.m_control_volume_high_day_inc=::fabs(value);         }
   void              SetControlVolumeHighDec(const long value)                   { this.m_control_volume_high_day_dec=::fabs(value);         }
   long              GetValueChangedVolumeHigh(void)                       const { return this.m_changed_volume_high_day_value;              }
   bool              IsIncreaseVolumeHigh(void)                            const { return this.m_is_change_volume_high_day_inc;              }
   bool              IsDecreaseVolumeHigh(void)                            const { return this.m_is_change_volume_high_day_dec;              }
   //--- Minimum volume within a day
   //--- setting the minimum day volume controlled (1) growth, (2) decrease value
   //--- getting (3) the minimum volume change value within a day,
   //--- getting the flag of the minimum day volume change exceeding the (4) growth, (5) decrease value
   void              SetControlVolumeLowInc(const long value)                    { this.m_control_volume_low_day_inc=::fabs(value);          }
   void              SetControlVolumeLowDec(const long value)                    { this.m_control_volume_low_day_dec=::fabs(value);          }
   long              GetValueChangedVolumeLow(void)                        const { return this.m_changed_volume_low_day_value;               }
   bool              IsIncreaseVolumeLow(void)                             const { return this.m_is_change_volume_low_day_inc;               }
   bool              IsDecreaseVolumeLow(void)                             const { return this.m_is_change_volume_low_day_dec;               }
   //--- Spread
   //--- setting the controlled spread decrease (1) growth, (2) decrease value in points
   //--- getting (3) spread change value in points,
   //--- getting the flag of the spread change in points exceeding the (4) growth, (5) decrease value
   void              SetControlSpreadInc(const int value)                        { this.m_control_spread_inc=::fabs(value);                  }
   void              SetControlSpreadDec(const int value)                        { this.m_control_spread_dec=::fabs(value);                  }
   int               GetValueChangedSpread(void)                           const { return this.m_changed_spread_value;                       }
   bool              IsIncreaseSpread(void)                                const { return this.m_is_change_spread_inc;                       }
   bool              IsDecreaseSpread(void)                                const { return this.m_is_change_spread_dec;                       }
   //--- StopLevel
   //--- setting the controlled StopLevel decrease (1) growth, (2) decrease value in points
   //--- getting (3) StopLevel change value in points,
   //--- getting the flag of StopLevel change in points exceeding the (4) growth, (5) decrease value
   void              SetControlStopLevelInc(const int value)                     { this.m_control_stops_level_inc=::fabs(value);             }
   void              SetControlStopLevelDec(const int value)                     { this.m_control_stops_level_dec=::fabs(value);             }
   int               GetValueChangedStopLevel(void)                        const { return this.m_changed_stops_level_value;                  }
   bool              IsIncreaseStopLevel(void)                             const { return this.m_is_change_stops_level_inc;                  }
   bool              IsDecreaseStopLevel(void)                             const { return this.m_is_change_stops_level_dec;                  }
   //--- Freeze distance
   //--- setting the controlled FreezeLevel decrease (1) growth, (2) decrease value in points
   //--- getting (3) FreezeLevel change value in points,
   //--- getting the flag of FreezeLevel change in points exceeding the (4) growth, (5) decrease value
   void              SetControlFreezeLevelInc(const int value)                   { this.m_control_freeze_level_inc=::fabs(value);            }
   void              SetControlFreezeLevelDec(const int value)                   { this.m_control_freeze_level_dec=::fabs(value);            }
   int               GetValueChangedFreezeLevel(void)                      const { return this.m_changed_freeze_level_value;                 }
   bool              IsIncreaseFreezeLevel(void)                           const { return this.m_is_change_freeze_level_inc;                 }
   bool              IsDecreaseFreezeLevel(void)                           const { return this.m_is_change_freeze_level_dec;                 }
   
   //--- Bid/Last
   //--- setting the Bid or Last price controlled (1) growth, (2) decrease value
   //--- getting (3) Bid or Last price change value,
   //--- getting the flag of the Bid or Last price change exceeding the (4) growth, (5) decrease value
   void              SetControlBidLastInc(const double value)                    { this.m_control_bid_last_inc=::fabs(value);                }
   void              SetControlBidLastDec(const double value)                    { this.m_control_bid_last_dec=::fabs(value);                }
   double            GetValueChangedBidLast(void)                          const { return this.m_changed_bid_last_value;                     }
   bool              IsIncreaseBidLast(void)                               const { return this.m_is_change_bid_last_inc;                     }
   bool              IsDecreaseBidLast(void)                               const { return this.m_is_change_bid_last_dec;                     }
   //--- Maximum Bid/Last of the day
   //--- setting the maximum Bid or Last price controlled (1) growth, (2) decrease value
   //--- getting the (3) maximum Bid or Last price change value,
   //--- getting the flag of the maximum Bid or Last price change exceeding the (4) growth, (5) decrease value
   void              SetControlBidLastHighInc(const double value)                { this.m_control_bid_last_high_inc=::fabs(value);           }
   void              SetControlBidLastHighDec(const double value)                { this.m_control_bid_last_high_dec=::fabs(value);           }
   double            GetValueChangedBidLastHigh(void)                      const { return this.m_changed_bid_last_high_value;                }
   bool              IsIncreaseBidLastHigh(void)                           const { return this.m_is_change_bid_last_high_inc;                }
   bool              IsDecreaseBidLastHigh(void)                           const { return this.m_is_change_bid_last_high_dec;                }
   //--- Minimum Bid/Last of the day
   //--- setting the minimum Bid or Last price controlled (1) growth, (2) decrease value
   //--- getting the (3) minimum Bid or Last price change value,
   //--- getting the flag of the minimum Bid or Last price change exceeding the (4) growth, (5) decrease value
   void              SetControlBidLastLowInc(const double value)                 { this.m_control_bid_last_low_inc=::fabs(value);            }
   void              SetControlBidLastLowDec(const double value)                 { this.m_control_bid_last_low_dec=::fabs(value);            }
   double            GetValueChangedBidLastLow(void)                       const { return this.m_changed_bid_last_low_value;                 }
   bool              IsIncreaseBidLastLow(void)                            const { return this.m_is_change_bid_last_low_inc;                 }
   bool              IsDecreaseBidLastLow(void)                            const { return this.m_is_change_bid_last_low_dec;                 }
   //--- Ask
   //--- setting the Ask price controlled (1) growth, (2) decrease value
   //--- getting (3) Ask price change value,
   //--- getting the flag of the Ask price change exceeding the (4) growth, (5) decrease value
   void              SetControlAskInc(const double value)                        { this.m_control_ask_inc=::fabs(value);                     }
   void              SetControlAskDec(const double value)                        { this.m_control_ask_dec=::fabs(value);                     }
   double            GetValueChangedAsk(void)                              const { return this.m_changed_ask_value;                          }
   bool              IsIncreaseAsk(void)                                   const { return this.m_is_change_ask_inc;                          }
   bool              IsDecreaseAsk(void)                                   const { return this.m_is_change_ask_dec;                          }
   //--- Maximum Ask price for the day
   //--- setting the maximum day Ask controlled (1) growth, (2) decrease value
   //--- getting (3) the maximum Ask change value within a day,
   //--- getting the flag of the maximum day Ask change exceeding the (4) growth, (5) decrease value
   void              SetControlAskHighInc(const double value)                    { this.m_control_ask_high_inc=::fabs(value);                }
   void              SetControlAskHighDec(const double value)                    { this.m_control_ask_high_dec=::fabs(value);                }
   double            GetValueChangedAskHigh(void)                          const { return this.m_changed_ask_high_value;                     }
   bool              IsIncreaseAskHigh(void)                               const { return this.m_is_change_ask_high_inc;                     }
   bool              IsDecreaseAskHigh(void)                               const { return this.m_is_change_ask_high_dec;                     }
   //--- Minimum Ask price for the day
   //--- setting the minimum day Ask controlled (1) growth, (2) decrease value
   //--- getting (3) the minimum Ask change value within a day,
   //--- getting the flag of the minimum day Ask change exceeding the (4) growth, (5) decrease value
   void              SetControlAskLowInc(const double value)                     { this.m_control_ask_low_inc=::fabs(value);                 }
   void              SetControlAskLowDec(const double value)                     { this.m_control_ask_low_dec=::fabs(value);                 }
   double            GetValueChangedAskLow(void)                           const { return this.m_changed_ask_low_value;                      }
   bool              IsIncreaseAskLow(void)                                const { return this.m_is_change_ask_low_inc;                      }
   bool              IsDecreaseAskLow(void)                                const { return this.m_is_change_ask_low_dec;                      }
   //--- Real Volume for the day
   //--- setting the real day volume controlled (1) growth, (2) decrease value
   //--- getting (3) the change value of the real day volume,
   //--- getting the flag of the real day volume change exceeding the (4) growth, (5) decrease value
   void              SetControlVolumeRealInc(const double value)                 { this.m_control_volume_real_inc=::fabs(value);             }
   void              SetControlVolumeRealDec(const double value)                 { this.m_control_volume_real_dec=::fabs(value);             }
   double            GetValueChangedVolumeReal(void)                       const { return this.m_changed_volume_real_value;                  }
   bool              IsIncreaseVolumeReal(void)                            const { return this.m_is_change_volume_real_inc;                  }
   bool              IsDecreaseVolumeReal(void)                            const { return this.m_is_change_volume_real_dec;                  }
   //--- Maximum real volume for the day
   //--- setting the maximum real day volume controlled (1) growth, (2) decrease value
   //--- getting (3) the change value of the maximum real day volume,
   //--- getting the flag of the maximum real day volume change exceeding the (4) growth, (5) decrease value
   void              SetControlVolumeHighRealInc(const double value)             { this.m_control_volume_high_real_day_inc=::fabs(value);    }
   void              SetControlVolumeHighRealDec(const double value)             { this.m_control_volume_high_real_day_dec=::fabs(value);    }
   double            GetValueChangedVolumeHighReal(void)                   const { return this.m_changed_volume_high_real_day_value;         }
   bool              IsIncreaseVolumeHighReal(void)                        const { return this.m_is_change_volume_high_real_day_inc;         }
   bool              IsDecreaseVolumeHighReal(void)                        const { return this.m_is_change_volume_high_real_day_dec;         }
   //--- Minimum real volume for the day
   //--- setting the minimum real day volume controlled (1) growth, (2) decrease value
   //--- getting (3) the change value of the minimum real day volume,
   //--- getting the flag of the minimum real day volume change exceeding the (4) growth, (5) decrease value
   void              SetControlVolumeLowRealInc(const double value)              { this.m_control_volume_low_real_day_inc=::fabs(value);     }
   void              SetControlVolumeLowRealDec(const double value)              { this.m_control_volume_low_real_day_dec=::fabs(value);     }
   double            GetValueChangedVolumeLowReal(void)                    const { return this.m_changed_volume_low_real_day_value;          }
   bool              IsIncreaseVolumeLowReal(void)                         const { return this.m_is_change_volume_low_real_day_inc;          }
   bool              IsDecreaseVolumeLowReal(void)                         const { return this.m_is_change_volume_low_real_day_dec;          }
   //--- Strike price
   //--- setting the strike price controlled (1) growth, (2) decrease value
   //--- getting (3) the change value of the strike price,
   //--- getting the flag of the strike price change exceeding the (4) growth, (5) decrease value
   void              SetControlOptionStrikeInc(const double value)               { this.m_control_option_strike_inc=::fabs(value);           }
   void              SetControlOptionStrikeDec(const double value)               { this.m_control_option_strike_dec=::fabs(value);           }
   double            GetValueChangedOptionStrike(void)                     const { return this.m_changed_option_strike_value;                } 
   bool              IsIncreaseOptionStrike(void)                          const { return this.m_is_change_option_strike_inc;                }
   bool              IsDecreaseOptionStrike(void)                          const { return this.m_is_change_option_strike_dec;                }
   //--- Maximum allowed total volume of unidirectional positions and orders
   //--- (1) getting the change value of the maximum allowed total volume of unidirectional positions and orders,
   //--- getting the flag of (2) increasing, (3) decreasing the maximum allowed total volume of unidirectional positions and orders
   double            GetValueChangedVolumeLimit(void)                      const { return this.m_changed_volume_limit_value;                 }
   bool              IsIncreaseVolumeLimit(void)                           const { return this.m_is_change_volume_limit_inc;                 }
   bool              IsDecreaseVolumeLimit(void)                           const { return this.m_is_change_volume_limit_dec;                 }
   //---  Swap long
   //--- (1) getting the swap long change value,
   //--- getting the flag of (2) increasing, (3) decreasing the swap long
   double            GetValueChangedSwapLong(void)                         const { return this.m_changed_swap_long_value;                    }
   bool              IsIncreaseSwapLong(void)                              const { return this.m_is_change_swap_long_inc;                    }
   bool              IsDecreaseSwapLong(void)                              const { return this.m_is_change_swap_long_dec;                    }
   //---  Swap short
   //--- (1) getting the swap short change value,
   //--- getting the flag of (2) increasing, (3) decreasing the swap short
   double            GetValueChangedSwapShort(void)                        const { return this.m_changed_swap_short_value;                   }
   bool              IsIncreaseSwapShort(void)                             const { return this.m_is_change_swap_short_inc;                   }
   bool              IsDecreaseSwapShort(void)                             const { return this.m_is_change_swap_short_dec;                   }
   //--- The total volume of deals in the current session
   //--- setting the controlled value of (1) growth, (2) decrease in the total volume of deals during the current session
   //--- getting (3) 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 (4) growth, (5) decrease value
   void              SetControlSessionVolumeInc(const double value)              { this.m_control_session_volume_inc=::fabs(value);          }
   void              SetControlSessionVolumeDec(const double value)              { this.m_control_session_volume_dec=::fabs(value);          }
   double            GetValueChangedSessionVolume(void)                    const { return this.m_changed_session_volume_value;               }
   bool              IsIncreaseSessionVolume(void)                         const { return this.m_is_change_session_volume_inc;               }
   bool              IsDecreaseSessionVolume(void)                         const { return this.m_is_change_session_volume_dec;               }
   //--- The total turnover in the current session
   //--- setting the controlled value of the turnover (1) growth, (2) decrease during the current session
   //--- getting (3) the total turnover change value in the current session,
   //--- getting the flag of the total turnover change during the current session exceeding the (4) growth, (5) decrease value
   void              SetControlSessionTurnoverInc(const double value)            { this.m_control_session_turnover_inc=::fabs(value);        }
   void              SetControlSessionTurnoverDec(const double value)            { this.m_control_session_turnover_dec=::fabs(value);        }
   double            GetValueChangedSessionTurnover(void)                  const { return this.m_changed_session_turnover_value;             }
   bool              IsIncreaseSessionTurnover(void)                       const { return this.m_is_change_session_turnover_inc;             }
   bool              IsDecreaseSessionTurnover(void)                       const { return this.m_is_change_session_turnover_dec;             }
   //--- The total volume of open positions
   //--- setting the controlled value of (1) growth, (2) decrease in the total volume of open positions during the current session
   //--- getting (3) 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 (4) growth, (5) decrease value
   void              SetControlSessionInterestInc(const double value)            { this.m_control_session_interest_inc=::fabs(value);        }
   void              SetControlSessionInterestDec(const double value)            { this.m_control_session_interest_dec=::fabs(value);        }
   double            GetValueChangedSessionInterest(void)                  const { return this.m_changed_session_interest_value;             }
   bool              IsIncreaseSessionInterest(void)                       const { return this.m_is_change_session_interest_inc;             }
   bool              IsDecreaseSessionInterest(void)                       const { return this.m_is_change_session_interest_dec;             }
   //--- The total volume of Buy orders at the moment
   //--- setting the controlled value of (1) growth, (2) decrease in the current total buy order volume
   //--- getting (3) the change value of the current total buy order volume,
   //--- getting the flag of the current total buy orders' volume change exceeding the (4) growth, (5) decrease value
   void              SetControlSessionBuyOrdVolumeInc(const double value)        { this.m_control_session_buy_ord_volume_inc=::fabs(value);  }
   void              SetControlSessionBuyOrdVolumeDec(const double value)        { this.m_control_session_buy_ord_volume_dec=::fabs(value);  }
   double            GetValueChangedSessionBuyOrdVolume(void)              const { return this.m_changed_session_buy_ord_volume_value;       }
   bool              IsIncreaseSessionBuyOrdVolume(void)                   const { return this.m_is_change_session_buy_ord_volume_inc;       }
   bool              IsDecreaseSessionBuyOrdVolume(void)                   const { return this.m_is_change_session_buy_ord_volume_dec;       }
   //--- The total volume of Sell orders at the moment
   //--- setting the controlled value of (1) growth, (2) decrease in the current total sell order volume
   //--- getting (3) the change value of the current total sell order volume,
   //--- getting the flag of the current total sell orders' volume change exceeding the (4) growth, (5) decrease value
   void              SetControlSessionSellOrdVolumeInc(const double value)       { this.m_control_session_sell_ord_volume_inc=::fabs(value); }
   void              SetControlSessionSellOrdVolumeDec(const double value)       { this.m_control_session_sell_ord_volume_dec=::fabs(value); }
   double            GetValueChangedSessionSellOrdVolume(void)             const { return this.m_changed_session_sell_ord_volume_value;      }
   bool              IsIncreaseSessionSellOrdVolume(void)                  const { return this.m_is_change_session_sell_ord_volume_inc;      }
   bool              IsDecreaseSessionSellOrdVolume(void)                  const { return this.m_is_change_session_sell_ord_volume_dec;      }
   //--- Session open price
   //--- setting the session open price controlled (1) growth, (2) decrease value
   //--- getting (3) the change value of the session open price,
   //--- getting the flag of the session open price change exceeding the (4) growth, (5) decrease value
   void              SetControlSessionPriceOpenInc(const double value)           { this.m_control_session_open_inc=::fabs(value);            }
   void              SetControlSessionPriceOpenDec(const double value)           { this.m_control_session_open_dec=::fabs(value);            }
   double            GetValueChangedSessionPriceOpen(void)                 const { return this.m_changed_session_open_value;                 }
   bool              IsIncreaseSessionPriceOpen(void)                      const { return this.m_is_change_session_open_inc;                 }
   bool              IsDecreaseSessionPriceOpen(void)                      const { return this.m_is_change_session_open_dec;                 }
   //--- Session close price
   //--- setting the session close price controlled (1) growth, (2) decrease value
   //--- getting (3) the change value of the session close price,
   //--- getting the flag of the session close price change exceeding the (4) growth, (5) decrease value
   void              SetControlSessionPriceCloseInc(const double value)          { this.m_control_session_close_inc=::fabs(value);           }
   void              SetControlSessionPriceCloseDec(const double value)          { this.m_control_session_close_dec=::fabs(value);           }
   double            GetValueChangedSessionPriceClose(void)                const { return this.m_changed_session_close_value;                }
   bool              IsIncreaseSessionPriceClose(void)                     const { return this.m_is_change_session_close_inc;                }
   bool              IsDecreaseSessionPriceClose(void)                     const { return this.m_is_change_session_close_dec;                }
   //--- The average weighted session price
   //--- setting the average weighted session price controlled (1) growth, (2) decrease value
   //--- getting (3) the change value of the average weighted session price,
   //--- getting the flag of the average weighted session price change exceeding the (4) growth, (5) decrease value
   void              SetControlSessionPriceAWInc(const double value)             { this.m_control_session_aw_inc=::fabs(value);              }
   void              SetControlSessionPriceAWDec(const double value)             { this.m_control_session_aw_dec=::fabs(value);              }
   double            GetValueChangedSessionPriceAW(void)                   const { return this.m_changed_session_aw_value;                   }
   bool              IsIncreaseSessionPriceAW(void)                        const { return this.m_is_change_session_aw_inc;                   }
   bool              IsDecreaseSessionPriceAW(void)                        const { return this.m_is_change_session_aw_dec;                   }
//---

Aquí: para cada una de las propiedades controladas existen métodos para establecer la magnitud controlada del cambio de propiedad, superada la cual se formará un evento cuya bandera se puede obtener con la ayuda del método que retorna la bandera de evento, mientras que la magnitud del cambio se obtiene con la ayuda del método que retorna esta magnitud. Ya hemos analizado métodos similares al implementar el seguimiento de los eventos de cuenta en la parte 13 de la descripción de la biblioteca.

Hemos introducido algunos cambios en el constructor de clase:

//+------------------------------------------------------------------+
//| Closed parametric constructor                                    |
//+------------------------------------------------------------------+
CSymbol::CSymbol(ENUM_SYMBOL_STATUS symbol_status,const string name,const int index)
  {
   this.m_name=name;
   if(!this.Exist())
     {
      ::Print(DFUN_ERR_LINE,"\"",this.m_name,"\"",": ",TextByLanguage("Ошибка. Такого символа нет на сервере","Error. There is 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 the 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 data
   this.Reset();
   this.InitMarginRates();
   ::ZeroMemory(this.m_struct_prev_symbol);
   this.m_struct_prev_symbol.trade_mode=WRONG_VALUE;
   this.InitChangesParams();
   this.InitControlsParams();
#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();
  }
//+------------------------------------------------------------------+

Ahora, al constructor de clase se le transmite el índice del símbolo en la ventana de "Observación de mercado", se asigna el nombre del símbolo a la denominación del objetose resetea la estructura de los datos pasados del símbolo y se registra en el campo de la estructura de los datos pasados del símbolo trade_mode el valor WRONG_VALUE. Determinaremos el primer inicio en función de la presencia o ausencia de este valor en el campo de modo de comercio del símbolo; despues inicializaremos las variables de las propiedades modificadas y controladas del símbolo, y en la propiedad del símbolo "índice en la ventana de Observación de mercado", registraremos el índice transmitido al constructor.

Dado que tenemos la variable m_name en la clase básica CBaseObj, en la que registraremos la denomiación del objeto (en este caso, el nombre del símbolo), será necesario eliminar de la clase CSymbol la variable m_symbol_name, y cambiar todas sus entradas por m_name (en nuestro caso, la asignación del nombre del símbolo se ejecuta en el constructor de la clase).
Seleccionamos cualquiera de las entradas del texto " this.m_symbol_name" en el listado de la clase CSymbol y pulsamos la combinación de teclas Ctrl+H. Se abrirá la ventana de búsqueda y sustitución, en cuyo campo de valor buscado se encontrará ya la entrada introducida por nosotros en el texto. En el campo de edición, introducimos this.m_name y sustituimos en el listado todas las entradas encontradas this.m_symbol_name por this.m_name.
Asimismo, debemos eliminar el resto de variables de miembros de clase, que ahora se encuentran en el objeto básico CBaseObj. Su entrada en CSymbol provocará durante la compilación una advertencia sobre la duplicación de la variable. Por cierto, en los archivos adjuntos al artículo ya se han eliminado todas estas variables: podemos descargar los archivos y familiarizarnos con su contenido.

En el método que retorna la descripción de una propiedad de tipo entero, añadimos la descripción de la nueva propiedad del símbolo "índice en la ventana de Observación de mercado":

//+------------------------------------------------------------------+
//| Return the description of the symbol integer property            |
//+------------------------------------------------------------------+
string CSymbol::GetPropertyDescription(ENUM_SYMBOL_PROP_INTEGER property)
  {
   return
     (
      property==SYMBOL_PROP_STATUS              ?  TextByLanguage("Статус","Status")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+(string)this.GetProperty(property)
         )  :
      property==SYMBOL_PROP_INDEX_MW            ?  TextByLanguage("Индекс в окне \"Обзор рынка\"","Index in the \"Market Watch window\"")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+this.GetStatusDescription()
         )  :
      property==SYMBOL_PROP_CUSTOM              ?  TextByLanguage("Пользовательский символ","Custom symbol")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+(this.GetProperty(property)   ?  TextByLanguage("Да","Yes") : TextByLanguage("Нет","No"))
         )  :
      property==SYMBOL_PROP_CHART_MODE          ?  TextByLanguage("Тип цены для построения баров","Price type used for generating symbols bars")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+this.GetChartModeDescription()
         )  :
      property==SYMBOL_PROP_EXIST               ?  TextByLanguage("Символ с таким именем существует","Symbol with this name exists")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+(this.GetProperty(property)   ?  TextByLanguage("Да","Yes") : TextByLanguage("Нет","No"))
         )  :
      property==SYMBOL_PROP_SELECT  ?  TextByLanguage("Символ выбран в Market Watch","Symbol selected in Market Watch")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+(this.GetProperty(property)   ?  TextByLanguage("Да","Yes") : TextByLanguage("Нет","No"))
         )  :
      property==SYMBOL_PROP_VISIBLE ?  TextByLanguage("Символ отображается в Market Watch","Symbol visible in Market Watch")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+(this.GetProperty(property)   ?  TextByLanguage("Да","Yes") : TextByLanguage("Нет","No"))
         )  :
      property==SYMBOL_PROP_SESSION_DEALS       ?  TextByLanguage("Количество сделок в текущей сессии","Number of deals in the current session")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+ #ifdef __MQL5__(string)this.GetProperty(property) #else TextByLanguage("Свойство не поддерживается в MQL4","Property not supported in MQL4") #endif 
         )  :
      property==SYMBOL_PROP_SESSION_BUY_ORDERS  ?  TextByLanguage("Общее число ордеров на покупку в текущий момент","Number of Buy orders at the moment")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+ #ifdef __MQL5__(string)this.GetProperty(property) #else TextByLanguage("Свойство не поддерживается в MQL4","Property not supported in MQL4") #endif 
         )  :
      property==SYMBOL_PROP_SESSION_SELL_ORDERS ?  TextByLanguage("Общее число ордеров на продажу в текущий момент","Number of Sell orders at the moment")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+ #ifdef __MQL5__(string)this.GetProperty(property) #else TextByLanguage("Свойство не поддерживается в MQL4","Property not supported in MQL4") #endif 
         )  :
      property==SYMBOL_PROP_VOLUME              ?  TextByLanguage("Объем в последней сделке","Volume of the last deal")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+ #ifdef __MQL5__(string)this.GetProperty(property) #else TextByLanguage("Свойство не поддерживается в MQL4","Property not supported in MQL4") #endif 
         )  :
      property==SYMBOL_PROP_VOLUMEHIGH          ?  TextByLanguage("Максимальный объём за день","Maximal day volume")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+ #ifdef __MQL5__(string)this.GetProperty(property) #else TextByLanguage("Свойство не поддерживается в MQL4","Property not supported in MQL4") #endif 
         )  :
      property==SYMBOL_PROP_VOLUMELOW           ?  TextByLanguage("Минимальный объём за день","Minimal day volume")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+ #ifdef __MQL5__(string)this.GetProperty(property) #else TextByLanguage("Свойство не поддерживается в MQL4","Property not supported in MQL4") #endif 
         )  :
      property==SYMBOL_PROP_TIME                ?  TextByLanguage("Время последней котировки","Time of the last quote")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+(this.GetProperty(property)==0 ? TextByLanguage("(Ещё не было тиков)","(No ticks yet)") : TimeMSCtoString(this.GetProperty(property)))
         )  :
      property==SYMBOL_PROP_DIGITS              ?  TextByLanguage("Количество знаков после запятой","Digits after decimal point")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+(string)this.GetProperty(property)
         )  :
      property==SYMBOL_PROP_DIGITS_LOTS         ?  TextByLanguage("Количество знаков после запятой в значении лота","Digits after decimal point in the value of the lot")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+(string)this.GetProperty(property)
         )  :
      property==SYMBOL_PROP_SPREAD              ?  TextByLanguage("Размер спреда в пунктах","Spread value in points")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+(string)this.GetProperty(property)
         )  :
      property==SYMBOL_PROP_SPREAD_FLOAT        ?  TextByLanguage("Плавающий спред","Spread is floating")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+(this.GetProperty(property)   ?  TextByLanguage("Да","Yes") : TextByLanguage("Нет","No"))
         )  :
      property==SYMBOL_PROP_TICKS_BOOKDEPTH     ?  TextByLanguage("Максимальное количество показываемых заявок в стакане","Maximal number of requests shown in Depth of Market")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+ #ifdef __MQL5__(string)this.GetProperty(property) #else TextByLanguage("Свойство не поддерживается в MQL4","Property not supported in MQL4") #endif 
         )  :
      property==SYMBOL_PROP_TRADE_CALC_MODE     ?  TextByLanguage("Способ вычисления стоимости контракта","Contract price calculation mode")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+this.GetCalcModeDescription()
         )  :
      property==SYMBOL_PROP_TRADE_MODE ?  TextByLanguage("Тип исполнения ордеров","Order execution type")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+this.GetTradeModeDescription()
         )  :
      property==SYMBOL_PROP_START_TIME          ?  TextByLanguage("Дата начала торгов по инструменту","Date of symbol trade beginning")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
         (this.GetProperty(property)==0  ?  TextByLanguage(": (Отсутствует)",": (Not set)") : ": "+TimeMSCtoString(this.GetProperty(property)*1000))
         )  :
      property==SYMBOL_PROP_EXPIRATION_TIME     ?  TextByLanguage("Дата окончания торгов по инструменту","Date of symbol trade end")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
         (this.GetProperty(property)==0  ?  TextByLanguage(": (Отсутствует)",": (Not set)") : ": "+TimeMSCtoString(this.GetProperty(property)*1000))
         )  :
      property==SYMBOL_PROP_TRADE_STOPS_LEVEL   ?  TextByLanguage("Минимальный отступ от цены закрытия для установки Stop ордеров","Minimal indention from close price to place Stop orders")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+(string)this.GetProperty(property)
         )  :
      property==SYMBOL_PROP_TRADE_FREEZE_LEVEL  ?  TextByLanguage("Дистанция заморозки торговых операций","Distance to freeze trade operations in points")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+(string)this.GetProperty(property)
         )  :
      property==SYMBOL_PROP_TRADE_EXEMODE       ?  TextByLanguage("Режим заключения сделок","Deal execution mode")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+this.GetTradeExecDescription()
         )  :
      property==SYMBOL_PROP_SWAP_MODE           ?  TextByLanguage("Модель расчета свопа","Swap calculation model")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+this.GetSwapModeDescription()
         )  :
      property==SYMBOL_PROP_SWAP_ROLLOVER3DAYS  ?  TextByLanguage("День недели для начисления тройного свопа","Day of week to charge 3 days swap rollover")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+DayOfWeekDescription(this.SwapRollover3Days())
         )  :
      property==SYMBOL_PROP_MARGIN_HEDGED_USE_LEG  ?  TextByLanguage("Расчет хеджированной маржи по наибольшей стороне","Calculating hedging margin using the larger leg")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+(this.GetProperty(property)   ?  TextByLanguage("Да","Yes") : TextByLanguage("Нет","No"))
         )  :
      property==SYMBOL_PROP_EXPIRATION_MODE     ?  TextByLanguage("Флаги разрешенных режимов истечения ордера","Flags of allowed order expiration modes")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+this.GetExpirationModeFlagsDescription()
         )  :
      property==SYMBOL_PROP_FILLING_MODE        ?  TextByLanguage("Флаги разрешенных режимов заливки ордера","Flags of allowed order filling modes")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+this.GetFillingModeFlagsDescription()
         )  :
      property==SYMBOL_PROP_ORDER_MODE          ?  TextByLanguage("Флаги разрешённых типов ордеров","Flags of allowed order types")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+this.GetOrderModeFlagsDescription()
         )  :
      property==SYMBOL_PROP_ORDER_GTC_MODE      ?  TextByLanguage("Срок действия StopLoss и TakeProfit ордеров","Expiration of Stop Loss and Take Profit orders")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+this.GetOrderGTCModeDescription()
         )  :
      property==SYMBOL_PROP_OPTION_MODE         ?  TextByLanguage("Тип опциона","Option type")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+this.GetOptionTypeDescription()
         )  :
      property==SYMBOL_PROP_OPTION_RIGHT        ?  TextByLanguage("Право опциона","Option right")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+this.GetOptionRightDescription()
         )  :
      property==SYMBOL_PROP_BACKGROUND_COLOR    ?  TextByLanguage("Цвет фона символа в Market Watch","Background color of symbol in Market Watch")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
         #ifdef __MQL5__
         (this.GetProperty(property)==CLR_DEFAULT || this.GetProperty(property)==CLR_NONE ?  TextByLanguage(": (Отсутствует)",": (Not set)") : ": "+::ColorToString((color)this.GetProperty(property),true))
         #else TextByLanguage(": Свойство не поддерживается в MQL4","Property not supported in MQL4") #endif 
         )  :
      ""
     );
  }
//+------------------------------------------------------------------+

Eliminamos del listado de implementación de los métodos de la clase CSymbol el segundo formulario del método Exist() y el método que retorna el número de dígitos tras la coma en el valor de la cifra:

//+------------------------------------------------------------------+
bool CSymbol::Exist(const string name) const
  {
   int total=::SymbolsTotal(false);
   for(int i=0;i<total;i++)
      if(::SymbolName(i,false)==name)
         return true;
   return false;
  }
//+------------------------------------------------------------------+
//| Return the number of decimal places in the 'double' value        |
//+------------------------------------------------------------------+
int CSymbol::GetDigits(const double value) const
  {
   string val_str=(string)value;
   int len=::StringLen(val_str);
   int n=len-::StringFind(val_str,".",0)-1;
   if(::StringSubstr(val_str,len-1,1)=="0")
      n--;
   return n;
  }
//+------------------------------------------------------------------+

Estos métodos sobran aquí. En esta clase no es necesario Exist() con el parámetro de entrada, por lo que ha sido trasladado al archivo de funciones de servicio DELib.mqh, donde debería estar, mientras que GetDigits() ahora se ubica en la clase básica CBaseObj.

El método Refresh() de la clase CSymbol se inicia en el temporizador y actualiza todos los datos del símbolo. En este mismo método realizaremos la búsqueda de los cambios en las propiedades del símbolo. Sin embargo, tenemos otro método más, RefreshRates(), que también se inicia desde el temporizador, pero con una alta frecuencia de actualización; en este método se actualizan solo los datos de cotización del símbolo. Si implementamos la búsqueda de los cambios en las propiedades de los símbolos en ambos métodos, esto provocará una ralentazación en los mensajes sobre los eventos.
La solución para esta situación podría ser la siguiente: el método RefreshRates() actualizará los datos de cotización y retornará la bandera sobre su correcta obtención. Este método se llamará de la misma forma que antes: desde su temporizador, pero también desde el método Refresh(), como complemento. De esta forma, se llamarán ambos métodos, como se hacía antes: cada uno en su propio temporizador, mientras que la búsqueda de cambios en las propiedades de los símbolos se dará solo en el método Refresh().

Vamos a introducir los cambios necesarios en los métodos RefreshRates() y Refresh():

//+------------------------------------------------------------------+
//| Update quote data by a symbol                                    |
//+------------------------------------------------------------------+
bool CSymbol::RefreshRates(void)
  {
//--- Get quote data
   ::ResetLastError();
   if(!::SymbolInfoTick(this.m_name,this.m_tick))
     {
      this.m_global_error=::GetLastError();
      return false;
     }
//--- Update integer properties
   this.m_long_prop[SYMBOL_PROP_VOLUME]                                             = (long)this.m_tick.volume;
   this.m_long_prop[SYMBOL_PROP_TIME]                                               = this.TickTime();
//--- Update real properties
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_ASK)]                              = this.m_tick.ask;
   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_BID)]                              = this.m_tick.bid;
   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_LAST)]                             = this.m_tick.last;
   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);
   return true;
  }  
//+------------------------------------------------------------------+

Aquí, primero obtenemos los datos de cotización del símbolo, y si no hemos logrado obtener los datos, registramos el código de error y salimos del método retornando false. Si hemos conseguido obtener los datos, rellenamos las propiedades necesarias del símbolo y retornamos true.

//+------------------------------------------------------------------+
//| 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 
//--- Prepare event data
   this.m_is_event=false;
   ::ZeroMemory(this.m_struct_curr_symbol);
   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 current symbol data structure
   this.m_struct_curr_symbol.ask                                                    = this.Ask();
   this.m_struct_curr_symbol.ask_high                                               = this.AskHigh();
   this.m_struct_curr_symbol.ask_low                                                = this.AskLow();
   this.m_struct_curr_symbol.bid_last                                               = (this.ChartMode()==SYMBOL_CHART_MODE_BID ? this.Bid() : this.Last());
   this.m_struct_curr_symbol.bid_last_high                                          = (this.ChartMode()==SYMBOL_CHART_MODE_BID ? this.BidHigh() : this.LastHigh());
   this.m_struct_curr_symbol.bid_last_low                                           = (this.ChartMode()==SYMBOL_CHART_MODE_BID ? this.BidLow() : this.LastLow());
   this.m_struct_curr_symbol.volume                                                 = this.Volume();
   this.m_struct_curr_symbol.session_deals                                          = this.SessionDeals();
   this.m_struct_curr_symbol.session_buy_orders                                     = this.SessionBuyOrders();
   this.m_struct_curr_symbol.session_sell_orders                                    = this.SessionSellOrders();
   this.m_struct_curr_symbol.volume_high_day                                        = this.VolumeHigh();
   this.m_struct_curr_symbol.volume_low_day                                         = this.VolumeLow();
   this.m_struct_curr_symbol.spread                                                 = this.Spread();
   this.m_struct_curr_symbol.stops_level                                            = this.TradeStopLevel();
   this.m_struct_curr_symbol.freeze_level                                           = this.TradeFreezeLevel();
   this.m_struct_curr_symbol.volume_limit                                           = this.VolumeLimit();
   this.m_struct_curr_symbol.swap_long                                              = this.SwapLong();
   this.m_struct_curr_symbol.swap_short                                             = this.SwapShort();
   this.m_struct_curr_symbol.session_volume                                         = this.SessionVolume();
   this.m_struct_curr_symbol.session_turnover                                       = this.SessionTurnover();
   this.m_struct_curr_symbol.session_interest                                       = this.SessionInterest();
   this.m_struct_curr_symbol.session_buy_ord_volume                                 = this.SessionBuyOrdersVolume();
   this.m_struct_curr_symbol.session_sell_ord_volume                                = this.SessionSellOrdersVolume();
   this.m_struct_curr_symbol.session_open                                           = this.SessionOpen();
   this.m_struct_curr_symbol.session_close                                          = this.SessionClose();
   this.m_struct_curr_symbol.session_aw                                             = this.SessionAW();
   this.m_struct_curr_symbol.volume_real_day                                        = this.VolumeReal();
   this.m_struct_curr_symbol.volume_high_real_day                                   = this.VolumeHighReal();
   this.m_struct_curr_symbol.volume_low_real_day                                    = this.VolumeLowReal();
   this.m_struct_curr_symbol.option_strike                                          = this.OptionStrike();
   this.m_struct_curr_symbol.trade_mode                                             = this.TradeMode();

//--- Hash sum calculation
   this.m_hash_sum+=(double)this.m_struct_curr_symbol.volume;
   this.m_hash_sum+=(double)this.m_struct_curr_symbol.session_deals;
   this.m_hash_sum+=(double)this.m_struct_curr_symbol.session_buy_orders;
   this.m_hash_sum+=(double)this.m_struct_curr_symbol.session_sell_orders;
   this.m_hash_sum+=(double)this.m_struct_curr_symbol.volume_high_day;
   this.m_hash_sum+=(double)this.m_struct_curr_symbol.volume_low_day;
   this.m_hash_sum+=(double)this.m_struct_curr_symbol.spread;
   this.m_hash_sum+=(double)this.m_struct_curr_symbol.stops_level;
   this.m_hash_sum+=(double)this.m_struct_curr_symbol.freeze_level;
   this.m_hash_sum+=this.m_struct_curr_symbol.ask;
   this.m_hash_sum+=this.m_struct_curr_symbol.ask_high;
   this.m_hash_sum+=this.m_struct_curr_symbol.ask_low;
   this.m_hash_sum+=this.m_struct_curr_symbol.bid_last;
   this.m_hash_sum+=this.m_struct_curr_symbol.bid_last_high;
   this.m_hash_sum+=this.m_struct_curr_symbol.bid_last_low;
   this.m_hash_sum+=this.m_struct_curr_symbol.volume_limit;
   this.m_hash_sum+=this.m_struct_curr_symbol.swap_long;
   this.m_hash_sum+=this.m_struct_curr_symbol.swap_short;
   this.m_hash_sum+=this.m_struct_curr_symbol.session_volume;
   this.m_hash_sum+=this.m_struct_curr_symbol.session_turnover;
   this.m_hash_sum+=this.m_struct_curr_symbol.session_interest;
   this.m_hash_sum+=this.m_struct_curr_symbol.session_buy_ord_volume;
   this.m_hash_sum+=this.m_struct_curr_symbol.session_sell_ord_volume;
   this.m_hash_sum+=this.m_struct_curr_symbol.session_open;
   this.m_hash_sum+=this.m_struct_curr_symbol.session_close;
   this.m_hash_sum+=this.m_struct_curr_symbol.session_aw;
   this.m_hash_sum+=this.m_struct_curr_symbol.volume_real_day;
   this.m_hash_sum+=this.m_struct_curr_symbol.volume_high_real_day;
   this.m_hash_sum+=this.m_struct_curr_symbol.volume_low_real_day;
   this.m_hash_sum+=this.m_struct_curr_symbol.option_strike;
   this.m_hash_sum+=this.m_struct_curr_symbol.trade_mode;

//--- First launch
   if(this.m_struct_prev_symbol.trade_mode==WRONG_VALUE)
     {
      this.m_struct_prev_symbol=this.m_struct_curr_symbol;
      this.m_hash_sum_prev=this.m_hash_sum;
      return;
     }
//--- If symbol's hash sum changed
   if(this.m_hash_sum!=this.m_hash_sum_prev)
     {
      this.m_event_code=this.SetEventCode();
      this.SetTypeEvent();
      CEventBaseObj *event=this.GetEvent(WRONG_VALUE,false);
      if(event!=NULL)
        {
         ENUM_SYMBOL_EVENT event_id=(ENUM_SYMBOL_EVENT)event.ID();
         if(event_id!=SYMBOL_EVENT_NO_EVENT)
           {
            this.m_is_event=true;
           }
        }
      this.m_hash_sum_prev=this.m_hash_sum;
     }
  }
//+------------------------------------------------------------------+

Aquí: en primer lugar, llamamos al método RefreshRates(), y si no hemos logrado obtener los datos de cotización, ya no tendrá sentido obtener el resto de datos, por lo que salimos del método.
A continuación, reseteamos la bandera de evento, la estructura del estado actual de las propiedades del símbolo y la suma hash actual. Después, rellenamos todas las propiedades del símbolo con los datos actuales y registramos estos datos en la estructura del estado actual de las propiedades del símbolo. Tras rellenar la estructura, calculamos la suma hash actual.
Si se trata del primer inicio (la propiedadad del símbolo SYMBOL_TRADE_MODE se ha establecido con el valor WRONG_VALUE), guardamos los valores de la estructura en la estructura del estado pasado , registramos la suma hash actual en la suma hash anterior y salimos del método.
Si no se trata del primer inicio, deberemos comprobar si el valor de la suma hash actual y la pasada son distintos.
Si la suma hash ha cambiado,
significará que ha habido algún cambio en las propiedades del símbolo, así que llamamos al método de establecimiento del código de evento, al método de obtención de un evento del código de eventos y de registro de un evento en la lista de eventos sucedidos, obtenemos el último evento de los eventos recién añadidos de la lista, comprobamos el tipo de evento, y si no se trata de "No hay evento", activamos la bandera de evento. Finalmente, guardamos la suma hash actual como pasada para las comprobaciones posteriores.

Con esto, podemos considerar por finalizada la mejora de la clase CSymbol para la búsqueda de eventos y el trabajo con el nuevo objeto básico.

La clase de objeto de símbolo está preparada. Ahora, cada símbolo podrá monitorear sus eventos y ubicarlos en su propia lista de eventos. Nosotros usamos una colección de símbolos, y por consiguiente, debemos iterar en un ciclo por todos los símbolos de la colección y obetener de cada uno de ellos una lista con sus eventos. Y todos estos eventos deberemos ubicarlos en la lista de eventos de la colección (ahora la colección también tiene una lista así, dado que está ubicada en el objeto básico CBaseObj). Solo queda interrogar a la lista de eventos y determinar si ha sucedido un evento o varios de ellos al mismo tiempo para cada uno de los símbolos de la colección.

En nuestro caso, también es posible trabajar con la ventana de "Observación de mercado", para lo cual implementamos también el seguimiento de eventos tales como la adición de un símbolo a la observación de mercado, la eliminación de un símbolo de la observación de mercado y la clasificación de símbolos en la ventana de observación de mercado. Para ello, deberemos guardar una plantilla de la ventana de "Observación de mercado" y la suma hash de los símbolos ubicados en ella. Cuando cambie la suma hash, comprenderemos que ha sucedido un evento en la observación del mercado, y solo quedará determinar qué evento ha sucedido, usando para ello el método de comparación del estado actual en la ventana de Observación de mercado con su plantilla y enviando el evento identificado al programa de control.

Bien. Abrimos el archivo de clase de la colección de símbolos SymbolsCollection.mqh e introducimos los cambios necesarios:

//+------------------------------------------------------------------+
//|                                            SymbolsCollection.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include <Arrays\ArrayString.mqh>
#include "ListObj.mqh"
#include "..\Services\Select.mqh"
#include "..\Objects\Symbols\Symbol.mqh"
#include "..\Objects\Symbols\SymbolFX.mqh"
#include "..\Objects\Symbols\SymbolFXMajor.mqh"
#include "..\Objects\Symbols\SymbolFXMinor.mqh"
#include "..\Objects\Symbols\SymbolFXExotic.mqh"
#include "..\Objects\Symbols\SymbolFXRub.mqh"
#include "..\Objects\Symbols\SymbolMetall.mqh"
#include "..\Objects\Symbols\SymbolIndex.mqh"
#include "..\Objects\Symbols\SymbolIndicative.mqh"
#include "..\Objects\Symbols\SymbolCrypto.mqh"
#include "..\Objects\Symbols\SymbolCommodity.mqh"
#include "..\Objects\Symbols\SymbolExchange.mqh"
#include "..\Objects\Symbols\SymbolFutures.mqh"
#include "..\Objects\Symbols\SymbolCFD.mqh"
#include "..\Objects\Symbols\SymbolStocks.mqh"
#include "..\Objects\Symbols\SymbolBonds.mqh"
#include "..\Objects\Symbols\SymbolOption.mqh"
#include "..\Objects\Symbols\SymbolCollateral.mqh"
#include "..\Objects\Symbols\SymbolCustom.mqh"
#include "..\Objects\Symbols\SymbolCommon.mqh"
//+------------------------------------------------------------------+
//| Symbol collection                                                |
//+------------------------------------------------------------------+
class CSymbolsCollection : public CBaseObj
  {
private:
   CListObj          m_list_all_symbols;                          // The list of all symbol objects
   CArrayString      m_list_names;                                // Symbol name control list
   ENUM_SYMBOLS_MODE m_mode_list;                                 // Mode of working with symbol lists
   ENUM_SYMBOL_EVENT m_last_event;                                // The last event
   string            m_array_symbols[];                           // The array of used symbols passed from the program
   int               m_delta_symbol;                              // Difference in the number of symbols compared to the previous check
   int               m_total_symbols;                             // Number of symbols in the Market Watch window
   int               m_total_symbol_prev;                         // Number of symbols in the Market Watch window during the previous check
//--- Return the flag of a symbol object presence by its name in the (1) list of all symbols, (2) Market Watch window, (3) control list
   bool              IsPresentSymbolInList(const string symbol_name);
   bool              IsPresentSymbolInMW(const string symbol_name);
   bool              IsPresentSymbolInControlList(const string symbol_name);
//--- Create the symbol object and place it to the list
   bool              CreateNewSymbol(const ENUM_SYMBOL_STATUS symbol_status,const string name,const int index);
//--- Return the (1) type of a used symbol list (Market watch/Server),
//--- (2) the number of visible symbols, (3) symbol index in the Market Watch window
   ENUM_SYMBOLS_MODE TypeSymbolsList(const string &symbol_used_array[]);
   int               SymbolsTotalVisible(void)                    const;
   int               SymbolIndexInMW(const string name)           const;
   
//--- Define a symbol affiliation with a group by name and return it
   ENUM_SYMBOL_STATUS SymbolStatus(const string symbol_name)      const;
//--- Return a symbol affiliation with a category by custom criteria
   ENUM_SYMBOL_STATUS StatusByCustomPredefined(const string symbol_name)  const;
//--- Return a symbol affiliation with categories by margin calculation
   ENUM_SYMBOL_STATUS StatusByCalcMode(const string symbol_name)  const;
//--- Return a symbol affiliation with pre-defined (1) majors, (2) minors, (3) exotics, (4) RUB,
//--- (5) indicatives, (6) metals, (7) commodities, (8) indices, (9) cryptocurrency, (10) options
   bool              IsPredefinedFXMajor(const string name)       const;
   bool              IsPredefinedFXMinor(const string name)       const;
   bool              IsPredefinedFXExotic(const string name)      const;
   bool              IsPredefinedFXRUB(const string name)         const;
   bool              IsPredefinedIndicative(const string name)    const;
   bool              IsPredefinedMetall(const string name)        const;
   bool              IsPredefinedCommodity(const string name)     const;
   bool              IsPredefinedIndex(const string name)         const;
   bool              IsPredefinedCrypto(const string name)        const;
   bool              IsPredefinedOption(const string name)        const;

public:
//--- Return the full collection list 'as is'
   CArrayObj        *GetList(void)                                                                          { return &this.m_list_all_symbols;                                      }
//--- Return the list by selected (1) integer, (2) real and (3) string properties meeting the compared criterion
   CArrayObj        *GetList(ENUM_SYMBOL_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode=EQUAL)    { return CSelect::BySymbolProperty(this.GetList(),property,value,mode); }
   CArrayObj        *GetList(ENUM_SYMBOL_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode=EQUAL)   { return CSelect::BySymbolProperty(this.GetList(),property,value,mode); }
   CArrayObj        *GetList(ENUM_SYMBOL_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode=EQUAL)   { return CSelect::BySymbolProperty(this.GetList(),property,value,mode); }
//--- Return the (1) symbol object, (2) the symbol object index from the list by a name
   CSymbol          *GetSymbolByName(const string name);
   int               GetSymbolIndexByName(const string name);
//--- Return the number of new symbols in the Market Watch window
   int               NewSymbols(void)    const                                                              { return this.m_delta_symbol;                                           }
//--- Return (1) the mode of working with symbol lists, (2) the event flag and (3) the event of one of the collection symbols
   ENUM_SYMBOLS_MODE ModeSymbolsList(void)                        const                                     { return this.m_mode_list;                                              }
   bool              IsEvent(void)                                const                                     { return this.m_is_event;                                               }
   int               GetLastEventsCode(void)                      const                                     { return this.m_event_code;                                             }
   ENUM_SYMBOL_EVENT GetLastEvent(void)                           const                                     { return this.m_last_event;                                             }
//--- Return the number of symbols in the collection
   int               GetSymbolsCollectionTotal(void)              const                                     { return this.m_list_all_symbols.Total();                               }

//--- Constructor
                     CSymbolsCollection();
   
//--- (1) Set the list of used symbols, (2) creating the symbol list (Market Watch or the complete list)
   bool              SetUsedSymbols(const string &symbol_used_array[]);
   bool              CreateSymbolsList(const bool flag);
//--- Save names of used Market Watch symbols
   void              CopySymbolsNames(void);
//--- Update (1) all, (2) quote data of the collection symbols
   virtual void      Refresh(void);
   void              RefreshRates(void);
//--- Working with the events of the (1) collection symbol list, (2) market watch window
   void              SymbolsEventsControl(void);
   void              MarketWatchEventsControl(const bool send_events=true);
//--- Return the description of the (1) Market Watch window event, (2) mode of working with symbols
   string            EventDescription(const ENUM_SYMBOL_EVENT event);
   string            ModeSymbolsListDescription(void);
  };
//+------------------------------------------------------------------+

Vamos a usar el archivo CArrayString, incluido desde la biblioteca estándar, para crear la plantilla de la ventana de "Observación de mercado". En esta lista, vamos a guardar una copia del conjunto de símbolos de la observación de mercado y comparar el estado actual de la lista de símbolos en la ventana con el estado actual de la lista de símbolos en la plantilla. Ante los otros cambios, reaccionaremos creando los eventos de la ventana de "Observación de mercado".

En la variable m_last_event, registraremos el último evento sucedido con cualquiera de los símbolos en la colección. De esta forma, en la variable se guardará el último evento sucedido en uno de los símbolos usados.
La matriz m_array_symbols transmite la matriz de símbolos utilizados en el programa desde el programa de control a la clase de colección de símbolos.
En las variables de miembro de clase m_total_symbols y m_total_symbols_prev, guardaremos el número actual y pasado de símbolos en la ventana de "Observación de mercado". La comparación de estas variables nos permitirá conocer los eventos de adición o eliminación de símbolos en la observación de mercado.

Los métodos privados de clase IsPresentSymbolInMW() y IsPresentSymbolInControlList() retornan las banderas de presencia de un símbolos según su nombre en la ventana de "Observación de mercado" y la lista de plantilla de la ventana de observación de mercado, respectivamente. Los métodos SymbolsTotalVisible() y SymbolIndexInMW(), a su vez, retornan el número de símbolos visibles en la ventana de "Observación de mercado" y el índice del símbolo en la lista de esta ventana, respectivamente.

En la sección pública de la clase, añadimos los métodos:
IsEvent() — retorna la bandera de presencia de cualquier evento en la colección de símbolos o en la ventana de "Observación de mercado",
GetLastEventsCode() — retorna el código del último evento en la colección de símbolos o en la ventana de "Observación de mercado",
GetLastEvent() — retorna el último evento en la colección de símbolos o en la ventana de "Observación de mercado",
GetSymbolsCollectionTotal() — retorna el número total de símbolos en la colección,
CreateSymbolsList() — crea la lista de colección al trabajar con la ventana de "Observación de mercado" o con la lista completa de símbolos en el servidor (no más de 1000),
CopySymbolsNames() — crea la plantilla de símbolos de la ventana de "Observación de mercado" a partir de la lista con todos los símbolos de la colección,
Refresh() — ahora el método se declara como virtual, dado que se declara virtual en la clase del objeto básico (y la colección de objetos ahora se crea usando como base la clase de objeto básico CBaseObj),
SymbolsEventsControl() — método de trabajo con la lista de colección de símbolos para determinar los eventos de colección de símbolos,
MarketWatchEventsControl() — método de trabajo con la ventana de "Observación de mercado" para determinar los eventos en la ventana de observación de mercado,
EventDescription() — retorna la descripción de un evento de la colección de símbolos,
ModeSymbolsListDescription() — retorna la descripción del modo de trabajo con los símbolos.

Vamos a echar un vistazo a la implementación de los métodos.
Inicializamos en la lista de inicialización del constructor de clase las variables para trabajar con la ventana de "Observación de mercado", y también sustituimos en el cuerpo de la clase la clasificación de la lista de símbolos de la colección con clasificación según el nombre por la clasificación según el índice en la ventana de "Observación de mercado", además de limpiar la lista de la plantilla de la ventana de observación de mercado.

//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CSymbolsCollection::CSymbolsCollection(void) : m_total_symbol_prev(0),
                                               m_delta_symbol(0),
                                               m_mode_list(SYMBOLS_MODE_CURRENT)
  {
   this.m_list_all_symbols.Sort(SORT_BY_SYMBOL_INDEX_MW);
   this.m_list_all_symbols.Clear();
   this.m_list_all_symbols.Type(COLLECTION_SYMBOLS_ID);
   this.m_list_names.Clear();
  }
//+------------------------------------------------------------------+

Dado que en la ventana de "Observación de mercado", el símbolo puede encontrarse, pero no representarse (propiedad del símbolo SYMBOL_VISIBLE, algunos símbolos (normalmente, se trata de cursos cruzados, imprescindibles para calcular los requisitos de margen y el beneficio en la divisa del depósito) se seleccionan automáticamente, aunque en este caso pueden no representarse en Market Watch), para saber el número de símbolos solo visibles, deberemos calcular en el ciclo por los símbolos de la ventana solo aquellos símbolos que tienen establecida esta propiedad.
El método SymbolsTotalVisible() retorna el número de símbolos visibles en la ventana de "Observación de mercado":

//+------------------------------------------------------------------+
//| Return the number of visible symbols in the Market Watch window  |
//+------------------------------------------------------------------+
int CSymbolsCollection::SymbolsTotalVisible(void) const
  {
   int total=::SymbolsTotal(true),n=0;
   for(int i=0;i<total;i++)
     {
      if(!::SymbolInfoInteger(::SymbolName(i,true),SYMBOL_VISIBLE))
         continue;
      n++;
     }
   return n;
  }
//+------------------------------------------------------------------+

Método que retorna el índice del símbolo en la lista de la ventana de "Observación de mercado":

//+------------------------------------------------------------------+
//| Return symbol index in the Market Watch window                   |
//+------------------------------------------------------------------+
int CSymbolsCollection::SymbolIndexInMW(const string name) const
  {
   int total=::SymbolsTotal(true);
   for(int i=0;i<total;i++)
     {
      if(!::SymbolInfoInteger(::SymbolName(i,true),SYMBOL_VISIBLE))
         continue;
      if(SymbolName(i,true)==name)
         return i;
     }
   return WRONG_VALUE;
  }
//+------------------------------------------------------------------+

Al trabajar con la lista de símbolos de la ventana de "Observación de mercado", tenemos que conocer el índice de cada uno de los símbolos en la lista de la ventana, para poder sincronizar la ubicación de los símbolos en la lista de símbolos de la colección con la ubicación de los símbolos en la ventana de observación de mercado. Por ejemplo, esto podría ser necesario para crear una ventana propia para la lista de símbolos, totalmente sincronizada con la ventana del terminal. El índice será una de las propiedades del símbolo, lo que permitirá clasificar la lista según esta propiedad. Además, el índice se transmitirá al constructor de clase del símbolo asbtracto.

Método que retorna la bandera de presencia de un símbolo visible en la ventana de "Observación de mercado":

//+------------------------------------------------------------------+
//| Return the visible symbol object presence flag                   |
//| by its name in the Market Watch window                           |
//+------------------------------------------------------------------+
bool CSymbolsCollection::IsPresentSymbolInMW(const string symbol_name)
  {
   int total=SymbolsTotal(true);
   for(int i=0;i<total;i++)
     {
      string name=::SymbolName(i,true);
      if(!::SymbolInfoInteger(name,SYMBOL_VISIBLE))
         continue;
      if(name==symbol_name)
         return true;
     }
   return false;
  }
//+------------------------------------------------------------------+

Transmitimos al método el nombre del símbolo, después iteramos con el símbolo invisible en un ciclo por la lista completa de símbolosseleccionados en la ventana de observación de mercado, comparamos el nombre del siguiente símbolo con el transmitido al método, y si los nombres coinciden, retornamos true. Si el nombre no ha sido encontrado en la lista, retornamos false.

Método que retorna la bandera de presencia de un símbolo en la lista de plantilla de la ventana de "Observación de mercado":

//+------------------------------------------------------------------+
//| Return the flag of a symbol object presence in the control list  |
//+------------------------------------------------------------------+
bool CSymbolsCollection::IsPresentSymbolInControlList(const string symbol_name)
  {
   int total=this.m_list_names.Total();
   for(int i=0;i<total;i++)
     {
      string name=this.m_list_names.At(i);
      if(name==NULL)
         continue;
      if(name==symbol_name)
         return true;
     }
   return false;
  }
//+------------------------------------------------------------------+

Transmitimos al método el nombre del símbolo buscado. Obtenemos el siguiente símbolo en el ciclo por la lista de plantilla de la ventana de observación de mercado, y si su nombre coincide con el transmitido al método, retornamos true, de lo contrario, false.

Método para crear una lista al trabajar con la ventana de "Observación de mercado" o con la lista completa de símbolos en el servidor:

//+------------------------------------------------------------------+
//| Creating the symbol list (Market Watch or the complete list)     |
//+------------------------------------------------------------------+
bool CSymbolsCollection::CreateSymbolsList(const bool flag)
  {
   bool res=true;
   int total=::SymbolsTotal(flag);
   for(int i=0;i<total && i<SYMBOLS_COMMON_TOTAL;i++)
     {
      string name=::SymbolName(i,flag);
      if(flag && !::SymbolInfoInteger(name,SYMBOL_VISIBLE))
         continue;
      ENUM_SYMBOL_STATUS status=this.SymbolStatus(name);
      bool add=this.CreateNewSymbol(status,name,i);
      res &=add;
      if(!add) 
         continue;
     }
   return res;
  }
//+------------------------------------------------------------------+

Transmitimos al método la bandera que establece el modo de búsqueda: true — trabajar con los símbolos seleccionados en la ventana de "Observación de mercado", false — trabajar con la lista completa de símbolos disponibles en el servidor. Obtenemos el número total de símbolos dependiendo de la bandera, o bien en la ventana de observación de mercado, o bien en el servidor, y luego, en un ciclo por una lista con un tamaño no superior al de la magnitud establecida por la constante SYMBOLS_COMMON_TOTAL en el archivo Defines.mqh (1000 símbolos), obtenemos el nombre del siguiente símbolo de la lista, comprobamos su propiedad de visibilidad al trabajar con la ventana de observación de mercado, y si el símbolo es invisible, lo omitimos.
A continuación, obtenemos el estado del objeto de símbolo según su nombre y añadimos el símbolo a la lista de completa de símbolos de la colección con la ayuda del método CreateNewSymbol(), que ya analizamos en el artículo anterior. (Hay que destacar que el método ha cambiado un poco, debido a la adición de una nueva propiedad del símbolo: su índice en la lista de símbolos de la ventana de "Observación de mercado", ahora, al objeto de símbolo se la transmite también el índice).
Introducimos en la variable que retorna el resultado del funcionamiento del método los resultados de la adición de cada símbolo en la lista completa de símbolos de la colección
, y retornamos desde el método el valor final de esta variable al finalizar el ciclo completo del procesamiento de símbolos.

Vamos a analizar el método mejorado para la creación de un objeto de símbolo y su ubicación en la lista:

//+------------------------------------------------------------------+
//| Create a symbol object and place it to the list                  |
//+------------------------------------------------------------------+
bool CSymbolsCollection::CreateNewSymbol(const ENUM_SYMBOL_STATUS symbol_status,const string name,const int index)
  {
   if(this.IsPresentSymbolInList(name))
     {
      return true;
     }
   if(#ifdef __MQL5__ !::SymbolInfoInteger(name,SYMBOL_EXIST) #else !Exist(name) #endif )
     {
      string t1=TextByLanguage("Ошибка входных данных: нет символа ","Input error: no ");
      string t2=TextByLanguage(" на сервере"," symbol on the server");
      ::Print(DFUN,t1,name,t2);
      this.m_global_error=ERR_MARKET_UNKNOWN_SYMBOL;
      return false;
     }
   CSymbol *symbol=NULL;
   switch(symbol_status)
     {
      case SYMBOL_STATUS_FX         :  symbol=new CSymbolFX(name,index);         break;   // Forex symbol
      case SYMBOL_STATUS_FX_MAJOR   :  symbol=new CSymbolFXMajor(name,index);    break;   // Major Forex symbol
      case SYMBOL_STATUS_FX_MINOR   :  symbol=new CSymbolFXMinor(name,index);    break;   // Minor Forex symbol
      case SYMBOL_STATUS_FX_EXOTIC  :  symbol=new CSymbolFXExotic(name,index);   break;   // Exotic Forex symbol
      case SYMBOL_STATUS_FX_RUB     :  symbol=new CSymbolFXRub(name,index);      break;   // Forex symbol/RUR
      case SYMBOL_STATUS_METAL      :  symbol=new CSymbolMetall(name,index);     break;   // Metal
      case SYMBOL_STATUS_INDEX      :  symbol=new CSymbolIndex(name,index);      break;   // Index
      case SYMBOL_STATUS_INDICATIVE :  symbol=new CSymbolIndicative(name,index); break;   // Indicative
      case SYMBOL_STATUS_CRYPTO     :  symbol=new CSymbolCrypto(name,index);     break;   // Cryptocurrency symbol
      case SYMBOL_STATUS_COMMODITY  :  symbol=new CSymbolCommodity(name,index);  break;   // Commodity
      case SYMBOL_STATUS_EXCHANGE   :  symbol=new CSymbolExchange(name,index);   break;   // Exchange symbol
      case SYMBOL_STATUS_FUTURES    :  symbol=new CSymbolFutures(name,index);    break;   // Futures
      case SYMBOL_STATUS_CFD        :  symbol=new CSymbolCFD(name,index);        break;   // CFD
      case SYMBOL_STATUS_STOCKS     :  symbol=new CSymbolStocks(name,index);     break;   // Stock
      case SYMBOL_STATUS_BONDS      :  symbol=new CSymbolBonds(name,index);      break;   // Bond
      case SYMBOL_STATUS_OPTION     :  symbol=new CSymbolOption(name,index);     break;   // Option
      case SYMBOL_STATUS_COLLATERAL :  symbol=new CSymbolCollateral(name,index); break;   // Non-tradable asset
      case SYMBOL_STATUS_CUSTOM     :  symbol=new CSymbolCustom(name,index);     break;   // Custom symbol
      default                       :  symbol=new CSymbolCommon(name,index);     break;   // The rest
     }
   if(symbol==NULL)
     {
      ::Print(DFUN,TextByLanguage("Не удалось создать объект-символ ","Failed to create symbol object "),name);
      return false;
     }
   if(!this.m_list_all_symbols.Add(symbol))
     {
      string t1=TextByLanguage("Не удалось добавить символ ","Failed to add ");
      string t2=TextByLanguage(" в список"," symbol to the list");
      ::Print(DFUN,t1,name,t2);
      delete symbol;
      return false;
     }
   return true;
  }
//+------------------------------------------------------------------+

Como podemos ver por el listado, aquí transmitimos el índice al método de forma adicional, enviando también este índice a cada uno de los constructores de las clases de los objetos de símbolo, junto con el nombre del símbolo. Por consiguiente, será necesario mejorar cada una de las clases herederas de la clase de símbolo abstracto CSymbol.
Vamos a estudiar esta mejora usando como ejemplo la clase CSymbolFX:

//+------------------------------------------------------------------+
//|                                                     SymbolFX.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "Symbol.mqh"
//+------------------------------------------------------------------+
//| Forex symbol                                                     |
//+------------------------------------------------------------------+
class CSymbolFX : public CSymbol
  {
public:
//--- Constructor
                     CSymbolFX(const string name,const int index) : CSymbol(SYMBOL_STATUS_FX,name,index) {}
//--- Supported integer properties of a symbol
   virtual bool      SupportProperty(ENUM_SYMBOL_PROP_INTEGER property);
//--- Supported real properties of a symbol
   virtual bool      SupportProperty(ENUM_SYMBOL_PROP_DOUBLE property);
//--- Supported string properties of a symbol
   virtual bool      SupportProperty(ENUM_SYMBOL_PROP_STRING property);
//--- Display a short symbol description in the journal
   virtual void      PrintShort(void);
  };
//+------------------------------------------------------------------+

Aquí, además del nombre del símbolo, transmitimos al constructor de la clase su índice. Asimismo, transmitimos este índice al constructor de la clase padre CSymbol en la lista de inicialización.
Esto es todo lo que debemos cambiar en todas las clases herederas del símbolo abstracto. Ya hemos introducido en los archivos adjuntos al final del artículo todos los cambios en las clases de los objetos de símbolo.

Método mejorado para esteblecer la lista de símbolos usados en la colección:

//+------------------------------------------------------------------+
//| Set the list of used symbols                                     |
//+------------------------------------------------------------------+
bool CSymbolsCollection::SetUsedSymbols(const string &symbol_used_array[])
  {
   ::ArrayCopy(this.m_array_symbols,symbol_used_array);
   this.m_mode_list=this.TypeSymbolsList(this.m_array_symbols);
   this.m_list_all_symbols.Clear();
   this.m_list_all_symbols.Sort(SORT_BY_SYMBOL_INDEX_MW);
   //--- Use only the current symbol
   if(this.m_mode_list==SYMBOLS_MODE_CURRENT)
     {
      string name=::Symbol();
      ENUM_SYMBOL_STATUS status=this.SymbolStatus(name);
      return this.CreateNewSymbol(status,name,this.SymbolIndexInMW(name));
     }
   else
     {
      bool res=true;
      //--- Use the pre-defined symbol list
      if(this.m_mode_list==SYMBOLS_MODE_DEFINES)
        {
         int total=::ArraySize(this.m_array_symbols);
         for(int i=0;i<total;i++)
           {
            string name=this.m_array_symbols[i];
            ENUM_SYMBOL_STATUS status=this.SymbolStatus(name);
            bool add=this.CreateNewSymbol(status,name,this.SymbolIndexInMW(name));
            res &=add;
            if(!add) 
               continue;
           }
         return res;
        }
      //--- Use the full list of the server symbols
      else if(this.m_mode_list==SYMBOLS_MODE_ALL)
        {
         return this.CreateSymbolsList(false);
        }
      //--- Use the symbol list from the Market Watch window
      else if(this.m_mode_list==SYMBOLS_MODE_MARKET_WATCH)
        {
         this.MarketWatchEventsControl(false);
         return true;
        }
     }
   return false;
  }
//+------------------------------------------------------------------+

Aquí: copiamos directamente la matriz de símbolos usados transmitida desde el programa de control en nuestra propia matriz. Guardamos el modo de trabajo con los símbolos y establecemos la clasificación de la lista completa de símbolos en el modo de clasificación según el índice. Ahora, transmitimos al método de creación de un nuevo objeto de símbolo, además del nombre del símbolo, su índice.
Si usamos la lista completa de todos los símbolos en el servidor, llamaremos al método de construcción de la lista de símbolos de la colección con la bandera = false, lo que indica la construcción de la lista completa de símbolos en el servidor,
y al usar la lista de la ventana de "Observación de mercado", llamaremos al método de trabajo con la ventana de observación de mercado con la bandera = false, lo que no indicará que el método tenga prohibido trabajar con eventos, sino solo la necesidad de crear una lista y recordar sus datos.

Métodos de trabajo con los eventos de todos los símbolos de la colección:

//+------------------------------------------------------------------+
//| 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;
         ENUM_SYMBOL_EVENT event_id=(ENUM_SYMBOL_EVENT)event.ID();
         if(event_id==SYMBOL_EVENT_NO_EVENT)
            continue;
         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());
           }
        }
     }
  }
//+------------------------------------------------------------------+

Este método funciona el temporizador. Primero se inicializan los datos:
después se resetea la bandera de evento en la colección de símbolos,   
se limpia la lista de eventos en la colección de símbolos y   
se establece para la lista la bandera de lista clasificada.
A continuación, obtenemos el siguiente símbolo en un ciclo por todos los símbolos que se encuentran en la lista de símbolos de la colección, actualizamos todos sus datos y comprobamos la presencia de la bandera de evento establecida para el símbolo. Si el evento no existe, pasamos a la comprobación del siguiente símbolo en la colección.
Si en el símbolo se ha establecido la bandera de evento, establecemos la bandera de evento para toda la colección (la presencia de un evento en aunque sea uno de los símbolos conlleva la presencia de un evento para toda la colección). Obtenemos la lista de todos los eventos del símbolo actual, establecemos el código del último evento de la colección igual al código del evento del símbolo actual de la lista, y en el caso de que se dé un evento, se actualizará en el símbolo siguiente el valor guardado en la variable, que almacena el código del último evento de la colección; asimismo obtenemos en un ciclo por la lista de todos los eventos del símbolo actual el siguiente evento y
guardamos el identificador de evento como el último evento en la colección. Exactamente de la misma forma actualizará el evento del símbolo siguiente el último evento de la colección de símbolos.
A continuación, creamos un evento de colección con los parámetros establecidos para él a partir del evento del símbolo( el identificador del evento del símbolo, el parámetro long, el parámetro double y el parámetro string del evento), y guardamos el evento del símbolo en la lista de eventos de la colección de símbolos.
Si guardamos con éxito el evento en la lista de eventos de la colección, este evento se enviará al gráfico del programa con la ayuda de la función EventChartCustom(), con los mismos parámetros de evento para el posterior desarrollo del evento en el programa que realiza la llamada.

De esta forma, tras obtener la lista de eventos de cada uno de los símbolos que se encuentran en la lista de colección, iteramos por la lista de eventos de cada símbolo, e introducimos todos estos eventos en la lista de eventos de colección. Al finalizar el funcionamiento del ciclo, en la lista de eventos de la colección se encontrarán todos los eventos de todos los símbolos de la colección. Para cada uno de los eventos de cada símbolo de la colección se creará un evento de usuario, que será enviado al gráfico del programa de control.

Para reconocer los eventos en la ventana de observación de mercado, vamos a necesitar calcular la suma hash de todos los símbolos que se encuentran en esta. Cualquier cambio en la misma nos indicará que ha sucedido un evento. Lo primero que se nos ocurre es recurrir al cálculo simple del número de símbolos en la ventana, pero... La adición o eliminación de un símbolo aumentará o reducirá el tamaño de la lista, pero la clasificación de los símbolos con el ratón no cambiará el número de símbolos. Esto significa que el número de símbolos en la ventana de "Observación de mercado" no es adecuado para calcular la suma hash.
Nosotros procederemos de la forma siguiente: cada símbolo que se guarda en la lista, su denominación, se puede representar como una cifra (código del símbolo) compuesta de la suma de valores uchar (letras) de los que consta la denominación del símbolo, añadiendo a este valor el índice del símbolo en la lista de la ventana de observación de mercado. Y la suma hash será la suma de todos los códigos de los símbolos.

Implementación del método de trabajo con los eventos de la ventana de "Observación de mercado":

//+------------------------------------------------------------------+
//| 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;
      ENUM_SYMBOL_EVENT event_id=
        (
         this.m_total_symbols>this.m_total_symbol_prev ? SYMBOL_EVENT_MW_ADD :
         this.m_total_symbols<this.m_total_symbol_prev ? SYMBOL_EVENT_MW_DEL :
         SYMBOL_EVENT_MW_SORT
        );
      //--- Adding a symbol to the Market Watch window
      if(event_id==SYMBOL_EVENT_MW_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==SYMBOL_EVENT_MW_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==SYMBOL_EVENT_MW_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;
     }
  }
//+------------------------------------------------------------------+

Para no explicar todas las ramificaciones del método, hemos destacado por bloques el código en el listado; cada uno de dichos bloques viene acompañado de comentarios detallados. Esperamos que todo resulte comprensible al lector. Cualquier pregunta podrá ser expuesta en los comentarios al artículo.

Al trabajar con la ventana de "Observación de mercado" para monitorear los eventos, no basta con operar solo con la suma hash. Y es que para saber lo que ha sucedido antes de un evento, deberemos tener una copia de la lista de símbolos en la ventana de observación de mercado (plantilla de la observación de mercado), y en función de esta copia, siempre podremos saber, por ejemplo, qué símbolo ha sido eliminado. No existe posibilidad de conocer el nombre del símbolo eliminado si no disponemos de una plantilla de la ventana de observación de mercado.

Método de creación de la plantilla de la ventana de "Observación de mercado":

//+------------------------------------------------------------------+
//| Save names of used Market Watch symbols                          |
//+------------------------------------------------------------------+
void CSymbolsCollection::CopySymbolsNames(void)
  {
   this.m_list_names.Clear();
   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;
      this.m_list_names.Add(symbol.Name());
     }
  }
//+------------------------------------------------------------------+

Aquí: limpiamos la lista de los nombres de los símbolos, obtenemos el símbolo siguiente en el ciclo por la lista de símbolos de la colección y lo añadimos a la lista de nombres de los símbolos.
Al finalizar el ciclo, tendremos una lista con los nombres de todos los símbolos que se encuentran en la lista de la colección de símbolos.

Método que retorna un objeto de símbolo según su nombre:

//+------------------------------------------------------------------+
//| Return an object symbol from the list by a name                  |
//+------------------------------------------------------------------+
CSymbol *CSymbolsCollection::GetSymbolByName(const string name)
  {
   CArrayObj *list=this.GetList(SYMBOL_PROP_NAME,name,EQUAL);
   if(list==NULL || list.Total()==0)
      return NULL;
   CSymbol *symbol=list.At(0);
   return(symbol!=NULL ? symbol : NULL);
  }
//+------------------------------------------------------------------+

Transmitimos el nombre del símbolo al método. A continuación, con la ayuda del método de obtención de la lista de objetos GetList(), creamos una nueva lista según la propiedad "Nombre del símbolo", en la que se deberá encontrar un único objeto de símbolo, con la condición de que su nombre coincida con el nombre transmitido al método.
Obtenemos de esta lista el objeto de símbolo y lo retornamos si la búsqueda finaliza con éxito, o bien retornamos NULL, si no existe un símbolo con este nombre en la lista de la colección de símbolos.

Método que retorna el índice de un símbolo en la lista de colección de símbolos:

//+------------------------------------------------------------------+
//| Return the symbol object index from the list by a name           |
//+------------------------------------------------------------------+
int CSymbolsCollection::GetSymbolIndexByName(const string name)
  {
   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;
      if(symbol.Name()==name)
         return i;
     }
   return WRONG_VALUE;
  }
//+------------------------------------------------------------------+

Aquí: transmitimos al método el nombre del símbolo buscado. A continuación, obtenemos en el ciclo por todos los símbolos que se encuentran en la lista de símbolos el siguiente objeto de símbolo de la lista. Si su nombre coincide con el buscado, retornamos el índice del ciclo. De lo contrario, retornamos -1.

Método que retorna la descripción de un evento en la ventana de "Observación de mercado":

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

Transmitimos el evento al método, y partiendo del evento que ha sido transmitido, retornamos su descripción de texto.

Método que retorna la descripción del modo de trabajo con los símbolos:

//+------------------------------------------------------------------+
//| Return a description of the mode of working with symbols         |
//+------------------------------------------------------------------+
string CSymbolsCollection::ModeSymbolsListDescription(void)
  {
   return
     (
      this.m_mode_list==SYMBOLS_MODE_CURRENT       ? TextByLanguage("Работа только с текущим символом","Work only with current symbol")                                : 
      this.m_mode_list==SYMBOLS_MODE_DEFINES       ? TextByLanguage("Работа с предопределённым списком символов","Work with predefined list of symbols")                 :
      this.m_mode_list==SYMBOLS_MODE_MARKET_WATCH  ? TextByLanguage("Работа с символами из окна \"Обзор рынка\"","Working with symbols from \"Market Watch\" window")  :
      TextByLanguage("Работа с полным списком всех доступных символов","Work with full list of all available symbols")
     );
  }
//+------------------------------------------------------------------+

Aquí, comprobamos el valor de la variable m_mode_list y retornamos la descripción de texto del modo de trabajo de acuerdo con el valor de esta variable.

Con esto, hemos terminado de crear la clase de eventos de símbolo.

Antes de poner en marcha la clase de eventos de símbolo, debemos recordar que en la clase de eventos de cuenta podemos organizar el seguimiento de eventos de la misma forma, ya que ahora tiene el objeto básico CBaseObj; ambos están basados en CBaseObj, y esto significa que ambos pueden utilizar la funcionalidad preparada para buscar eventos. Todos los siguientes objetos que sean herederos de CBaseObj estarán equipados con las mismas propiedades que permiten monitorear los eventos de estos objetos.

Mejorando la clase de eventos de cuenta

Abrimos el archivo de clase de cuenta Account.mqh e introducimos los cambios necesarios.
Sustituimos la inclusión del archivo Object.mqh por la inclusión de BaseObj.mqh:

//+------------------------------------------------------------------+
//|                                                      Account.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "..\BaseObj.mqh"
#include "..\..\Services\DELib.mqh"
//+------------------------------------------------------------------+
//| Account class                                                    |
//+------------------------------------------------------------------+
class CAccount : public CBaseObj
  {
private:

Y ahora, en lugar de la clase CObject, convertiremos a CBaseObj en la clase básica.

Dado que ahora tenemos la posibilidad de asignar una denominación a un objeto heredado de CBaseObj, aprovecharemos esta posibilidad y estableceremos una denominación para el objeto de cuenta.
Añadimos al final del constructor de la clase CAccount una línea que establece el nombre del objeto de cuenta:

//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CAccount::CAccount(void)
  {
//--- Save integer properties
   this.m_long_prop[ACCOUNT_PROP_LOGIN]                              = ::AccountInfoInteger(ACCOUNT_LOGIN);
   this.m_long_prop[ACCOUNT_PROP_TRADE_MODE]                         = ::AccountInfoInteger(ACCOUNT_TRADE_MODE);
   this.m_long_prop[ACCOUNT_PROP_LEVERAGE]                           = ::AccountInfoInteger(ACCOUNT_LEVERAGE);
   this.m_long_prop[ACCOUNT_PROP_LIMIT_ORDERS]                       = ::AccountInfoInteger(ACCOUNT_LIMIT_ORDERS);
   this.m_long_prop[ACCOUNT_PROP_MARGIN_SO_MODE]                     = ::AccountInfoInteger(ACCOUNT_MARGIN_SO_MODE);
   this.m_long_prop[ACCOUNT_PROP_TRADE_ALLOWED]                      = ::AccountInfoInteger(ACCOUNT_TRADE_ALLOWED);
   this.m_long_prop[ACCOUNT_PROP_TRADE_EXPERT]                       = ::AccountInfoInteger(ACCOUNT_TRADE_EXPERT);
   this.m_long_prop[ACCOUNT_PROP_MARGIN_MODE]                        = #ifdef __MQL5__::AccountInfoInteger(ACCOUNT_MARGIN_MODE) #else ACCOUNT_MARGIN_MODE_RETAIL_HEDGING #endif ;
   this.m_long_prop[ACCOUNT_PROP_CURRENCY_DIGITS]                    = #ifdef __MQL5__::AccountInfoInteger(ACCOUNT_CURRENCY_DIGITS) #else 2 #endif ;
   this.m_long_prop[ACCOUNT_PROP_SERVER_TYPE]                        = (::TerminalInfoString(TERMINAL_NAME)=="MetaTrader 5" ? 5 : 4);
   
//--- Save real properties
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_BALANCE)]          = ::AccountInfoDouble(ACCOUNT_BALANCE);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_CREDIT)]           = ::AccountInfoDouble(ACCOUNT_CREDIT);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_PROFIT)]           = ::AccountInfoDouble(ACCOUNT_PROFIT);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_EQUITY)]           = ::AccountInfoDouble(ACCOUNT_EQUITY);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_MARGIN)]           = ::AccountInfoDouble(ACCOUNT_MARGIN);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_MARGIN_FREE)]      = ::AccountInfoDouble(ACCOUNT_MARGIN_FREE);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_MARGIN_LEVEL)]     = ::AccountInfoDouble(ACCOUNT_MARGIN_LEVEL);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_MARGIN_SO_CALL)]   = ::AccountInfoDouble(ACCOUNT_MARGIN_SO_CALL);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_MARGIN_SO_SO)]     = ::AccountInfoDouble(ACCOUNT_MARGIN_SO_SO);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_MARGIN_INITIAL)]   = ::AccountInfoDouble(ACCOUNT_MARGIN_INITIAL);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_MARGIN_MAINTENANCE)]=::AccountInfoDouble(ACCOUNT_MARGIN_MAINTENANCE);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_ASSETS)]           = ::AccountInfoDouble(ACCOUNT_ASSETS);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_LIABILITIES)]      = ::AccountInfoDouble(ACCOUNT_LIABILITIES);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_COMMISSION_BLOCKED)]=::AccountInfoDouble(ACCOUNT_COMMISSION_BLOCKED);
   
//--- Save string properties
   this.m_string_prop[this.IndexProp(ACCOUNT_PROP_NAME)]             = ::AccountInfoString(ACCOUNT_NAME);
   this.m_string_prop[this.IndexProp(ACCOUNT_PROP_SERVER)]           = ::AccountInfoString(ACCOUNT_SERVER);
   this.m_string_prop[this.IndexProp(ACCOUNT_PROP_CURRENCY)]         = ::AccountInfoString(ACCOUNT_CURRENCY);
   this.m_string_prop[this.IndexProp(ACCOUNT_PROP_COMPANY)]          = ::AccountInfoString(ACCOUNT_COMPANY);

//--- Account object name
   this.m_name=TextByLanguage("Счёт ","Account ")+(string)this.Login()+": "+this.Name()+" ("+this.Company()+")";
  }
//+-------------------------------------------------------------------+

Como podemos ver, el nombre del objeto constará del texto "Cuenta comercial", el número de cuenta, el nombre del cliente y la denominación de la compañía que da servicio a la cuenta.
Por ejemplo, al conectarnos a una de las cuentas comerciales en MetaQuotes-Demo con el nombre del autor, el nombre del objeto de cuenta será el siguiente: "Account 8550475: Artyom Trishkin (MetaQuotes Software Corp.)"

Incluimos en el método de muestra de la denominación breve de la cuenta comercial el valor de la variable names (antes se establecía de la misma forma que hemos establecido el nombre del objeto de cuenta):

//+------------------------------------------------------------------+
//| Display a short account description in the journal               |
//+------------------------------------------------------------------+
void CAccount::PrintShort(void)
  {
   string mode=(this.MarginMode()==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING ? ", Hedge" : this.MarginMode()==ACCOUNT_MARGIN_MODE_EXCHANGE ? ", Exhange" : "");
   string names=this.m_name+" ";
   string values=::DoubleToString(this.Balance(),(int)this.CurrencyDigits())+" "+this.Currency()+", 1:"+(string)+this.Leverage()+mode+", "+this.TradeModeDescription()+" "+this.ServerTypeDescription();
   ::Print(names,values);
  }
//+------------------------------------------------------------------+

Estas son todas las mejoras de la clase CAccount.

Ahora vamos a mejorar la clase de la colección de cuenta. Abrimos el archivo AccountsCollection.mqh e introducimos los cambios necesarios.
Hacemos objeto básico de la clase de colección de cuentas la clase CBaseObj:

//+------------------------------------------------------------------+
//| Account collection                                               |
//+------------------------------------------------------------------+
class CAccountsCollection : public CBaseObj
  {
private:

Dado que ahora la clase se ha heredado del objeto básico, en el que ya se ha registrado la funcionalidad para monitorear los eventos del objeto, eliminamos de la clase de la colección de cuentas las variables y métodos que han resultado duplicados.
Eliminamos de la estructura de datos de la cuenta el campo de la suma hash:

   struct MqlDataAccount
     {
      double         hash_sum;               // Account data hash sum
      //--- Account integer properties
      long           login;                  // ACCOUNT_LOGIN (Account number)
      long           leverage;               // ACCOUNT_LEVERAGE (Leverage)
      int            limit_orders;           // ACCOUNT_LIMIT_ORDERS (Maximum allowed number of active pending orders)
      bool           trade_allowed;          // ACCOUNT_TRADE_ALLOWED (Permission to trade for the current account from the server side)
      bool           trade_expert;           // ACCOUNT_TRADE_EXPERT (Permission to trade for an EA from the server side)
      //--- Account real properties
      double         balance;                // ACCOUNT_BALANCE (Account balance in a deposit currency)
      double         credit;                 // ACCOUNT_CREDIT (Credit in a deposit currency)
      double         profit;                 // ACCOUNT_PROFIT (Current profit on an account in the account currency)
      double         equity;                 // ACCOUNT_EQUITY (Equity on an account in the deposit currency)
      double         margin;                 // ACCOUNT_MARGIN (Reserved margin on an account in a deposit currency)
      double         margin_free;            // ACCOUNT_MARGIN_FREE (Free funds available for opening a position in a deposit currency)
      double         margin_level;           // ACCOUNT_MARGIN_LEVEL (Margin level on an account in %)
      double         margin_so_call;         // ACCOUNT_MARGIN_SO_CALL (MarginCall)
      double         margin_so_so;           // ACCOUNT_MARGIN_SO_SO (StopOut)
      double         margin_initial;         // ACCOUNT_MARGIN_INITIAL (Funds reserved on an account to ensure a guarantee amount for all pending orders)
      double         margin_maintenance;     // ACCOUNT_MARGIN_MAINTENANCE (Funds reserved on an account to ensure a minimum amount for all open positions)
      double         assets;                 // ACCOUNT_ASSETS (Current assets on an account)
      double         liabilities;            // ACCOUNT_LIABILITIES (Current liabilities on an account)
      double         comission_blocked;      // ACCOUNT_COMMISSION_BLOCKED (Current sum of blocked commissions on an account)
     };

Eliminamos las variables de miembro privadas de la clase:

   MqlTick           m_tick;                             // Tick structure
   string            m_symbol;                           // Current symbol
   long              m_chart_id;                         // Control program chart ID
   CListObj          m_list_accounts;                    // Account object list
   CArrayInt         m_list_changes;                     // Account change list
   string            m_folder_name;                      // Name of a folder account objects are stored
   int               m_index_current;                    // Index of an account object featuring the current account data
//--- Tracking account changes
   bool              m_is_account_event;                 // Account data event flag
   int               m_change_code;                      // Account change code

Renombramos el método SetChangeCode() como SetEventCode(), para que los nombres de los métodos con el mismo tipo sean los mismos en clases diferentes.
Hacemos el método SetTypeEvent() virtual, dado que ya ha sido declarado en la clase CBaseObj, y debe implementarse en los herederos.
Eliminamos el método IsPresentEventFlag() de la clase, ya ha sido implementado en CBaseObj.

Asimismo, cribamos un poco los métodos duplicados en la sección pública de la clase.
Eliminamos los métodos GetEventCode(), GetListChanges() y SetChartID(), y hacemos el método ENUM_ACCOUNT_EVENT GetEvent(const int shift=WRONG_VALUE) de la forma siguiente:

ENUM_ACCOUNT_EVENT GetEventID(const int shift=WRONG_VALUE,const bool check_out=true);

Y analizamos de inmediato su implementación fuera del cuerpo de la clase:

//+------------------------------------------------------------------+
//| Return the account event by its number in the list               |
//+------------------------------------------------------------------+
ENUM_ACCOUNT_EVENT CAccountsCollection::GetEventID(const int shift=WRONG_VALUE,const bool check_out=true)
  {
   CEventBaseObj *event=this.GetEvent(shift,check_out);
   if(event==NULL)
      return ACCOUNT_EVENT_NO_EVENT;
   return (ENUM_ACCOUNT_EVENT)event.ID();
  }
//+------------------------------------------------------------------+

Transmitimos al método el índice del evento buscado (-1 para seleccionar el último) y la bandera de control de la salida del índice fuera del tamaño límite de la lista de eventos.
Obtenemos el objeto de evento con la ayuda del método del objeto básico CBaseObj GetEvent(), analizado por nosotros al inicio del artículo. Si no ha habido evento, retornamos "No hay evento", de lo contrario, retornamos el identificador del evento.

Eliminamos en la lista de inicialización en el constructor de la clase la inicialización de todos los parámetros excepto el establecimiento del símbolo y establecemos el nombre de la subcarpeta para guardar los archivos de los objetos de cuenta:

//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CAccountsCollection::CAccountsCollection(void) : m_symbol(::Symbol())
  {
   this.m_list_accounts.Clear();
   this.m_list_accounts.Sort(SORT_BY_ACCOUNT_LOGIN);
   this.m_list_accounts.Type(COLLECTION_ACCOUNT_ID);
   ::ZeroMemory(this.m_struct_prev_account);
   ::ZeroMemory(this.m_tick);
   this.InitChangesParams();
   this.InitControlsParams();
//--- Create the folder for storing account files
   this.SetSubFolderName("Accounts");
   ::ResetLastError();
   if(!::FolderCreate(this.m_folder_name,FILE_COMMON))
      ::Print(DFUN,TextByLanguage("Не удалось создать папку хранения файлов. Ошибка ","Could not create file storage folder. Error "),::GetLastError());
//--- Create the current account object and add it to the list
   CAccount* account=new CAccount();
   if(account!=NULL)
     {
      if(!this.AddToList(account))
        {
         ::Print(DFUN_ERR_LINE,TextByLanguage("Ошибка. Не удалось добавить текущий объект-аккаунт в список-коллекцию.","Error. Failed to add current account object to collection list."));
         delete account;
        }
      else
         account.PrintShort();
     }
   else
      ::Print(DFUN,TextByLanguage("Ошибка. Не удалось создать объект-аккаунт с данными текущего счёта.","Error. Failed to create an account object with current account data."));

//--- Download account objects from the files to the collection
   this.LoadObjects();
//--- Save the current account index
   this.m_index_current=this.Index();
  }
//+------------------------------------------------------------------+

Hacemos el método de actualización de los datos de la cuenta Refresh() virtual, dado que se declara en la clase CBaseObj y se implementa en sus herederos.

En la implementación del método se han introducido ciertos cambios:

//+------------------------------------------------------------------+
//| Update the current account data                                  |
//+------------------------------------------------------------------+
void CAccountsCollection::Refresh(void)
  {
   ::ResetLastError();
   if(!::SymbolInfoTick(::Symbol(),this.m_tick))
     {
      this.m_global_error=::GetLastError();
      return;
     }
   if(this.m_index_current==WRONG_VALUE)
      return;
   CAccount* account=this.m_list_accounts.At(this.m_index_current);
   if(account==NULL)
      return;
//--- Prepare event data
   this.m_is_event=false;
   ::ZeroMemory(this.m_struct_curr_account);
   this.m_hash_sum=0;
   this.SetAccountsParams(account);

//--- First launch
   if(!this.m_struct_prev_account.login)
     {
      this.m_struct_prev_account=this.m_struct_curr_account;
      this.m_hash_sum_prev=this.m_hash_sum;
      return;
     }
//--- If the account hash sum changed
   if(this.m_hash_sum!=this.m_hash_sum_prev)
     {
      this.m_list_events.Clear();
      this.m_event_code=this.SetEventCode();
      this.SetTypeEvent();
      int total=this.m_list_events.Total();
      if(total>0)
        {
         this.m_is_event=true;
         for(int i=0;i<total;i++)
           {
            CEventBaseObj *event=this.GetEvent(i,false);
            if(event==NULL)
               continue;
            ENUM_ACCOUNT_EVENT event_id=(ENUM_ACCOUNT_EVENT)event.ID();
            if(event_id==ACCOUNT_EVENT_NO_EVENT)
               continue;
            long lparam=event.LParam();
            double dparam=event.DParam();
            string sparam=event.SParam();
            ::EventChartCustom(this.m_chart_id,(ushort)event_id,lparam,dparam,sparam);
           }
        }
      this.m_hash_sum_prev=this.m_hash_sum;
     }
  }
//+------------------------------------------------------------------+

Vamos a analizar solo los cambios introducidos.
Primero, obtenemos los datos de cotización del símbolo (para determinar la hora en milisegundos), y si no hemos logrado obtenerlos, salimos del método.

Reseteamos la bandera de evento de la cuenta y el valor de la suma hash actual. En el primer inicio, guardamos la suma hash actual como pasada.

Cuando cambia la suma hash, limpiamos la lista de eventos de la cuenta y realizamos exactamente las mismas acciones para obtener los eventos de la lista de eventos de la cuenta, que ya realizamos al obtener la lista de eventos de los símbolos, y que analizamos anteriormente.

Ahora, para cualquier objeto heredado de CBaseObj, las acciones destinadas a obtener sus eventos serán idénticas. Por eso, será mejor que nos familiaricemos de nuevo con su obtención, que ya hemos analizado en este artículo, para que todo resulte comprensible en artículos posteriores y no tengamos que regresar al análisis de las acciones realizadas al obtener la lista de eventos de un objeto.

Sustituimos en el objeto de cuenta y la estructura de datos de la cuenta en el método de guardado de las propiedades de la cuenta la variable de clase CBaseObj de la suma hash y guardamos el nombre del objeto:

//+------------------------------------------------------------------+
//| Write the current account data to the account object properties  |
//+------------------------------------------------------------------+
void CAccountsCollection::SetAccountsParams(CAccount *account)
  {
   if(account==NULL)
      return;
//--- Name
   this.m_name=account.GetName();
//--- Account number
   this.m_struct_curr_account.login=account.Login();
//--- Leverage
   account.SetProperty(ACCOUNT_PROP_LEVERAGE,::AccountInfoInteger(ACCOUNT_LEVERAGE));
   this.m_struct_curr_account.leverage=account.Leverage();
   this.m_hash_sum+=(double)this.m_struct_curr_account.leverage;
//--- Maximum allowed number of active pending orders
   account.SetProperty(ACCOUNT_PROP_LIMIT_ORDERS,::AccountInfoInteger(ACCOUNT_LIMIT_ORDERS));
   this.m_struct_curr_account.limit_orders=(int)account.LimitOrders();
   this.m_hash_sum+=(double)this.m_struct_curr_account.limit_orders;
//--- Permission to trade for the current account from the server side
   account.SetProperty(ACCOUNT_PROP_TRADE_ALLOWED,::AccountInfoInteger(ACCOUNT_TRADE_ALLOWED));
   this.m_struct_curr_account.trade_allowed=account.TradeAllowed();
   this.m_hash_sum+=(double)this.m_struct_curr_account.trade_allowed;
//--- Permission to trade for an EA from the server side
   account.SetProperty(ACCOUNT_PROP_TRADE_EXPERT,::AccountInfoInteger(ACCOUNT_TRADE_EXPERT));
   this.m_struct_curr_account.trade_expert=account.TradeExpert();
   this.m_hash_sum+=(double)this.m_struct_curr_account.trade_expert;
//--- Account balance in a deposit currency
   account.SetProperty(ACCOUNT_PROP_BALANCE,::AccountInfoDouble(ACCOUNT_BALANCE));
   this.m_struct_curr_account.balance=account.Balance();
   this.m_hash_sum+=(double)this.m_struct_curr_account.balance;
//--- Credit in a deposit currency
   account.SetProperty(ACCOUNT_PROP_CREDIT,::AccountInfoDouble(ACCOUNT_CREDIT));
   this.m_struct_curr_account.credit=account.Credit();
   this.m_hash_sum+=(double)this.m_struct_curr_account.credit;
//--- Current profit on an account in the account currency
   account.SetProperty(ACCOUNT_PROP_PROFIT,::AccountInfoDouble(ACCOUNT_PROFIT));
   this.m_struct_curr_account.profit=account.Profit();
   this.m_hash_sum+=(double)this.m_struct_curr_account.profit;
//--- Equity on an account in the deposit currency
   account.SetProperty(ACCOUNT_PROP_EQUITY,::AccountInfoDouble(ACCOUNT_EQUITY));
   this.m_struct_curr_account.equity=account.Equity();
   this.m_hash_sum+=(double)this.m_struct_curr_account.equity;
//--- Reserved margin on an account in the deposit currency
   account.SetProperty(ACCOUNT_PROP_MARGIN,::AccountInfoDouble(ACCOUNT_MARGIN));
   this.m_struct_curr_account.margin=account.Margin();
   this.m_hash_sum+=(double)this.m_struct_curr_account.margin;
//--- Free funds available for opening a position on an account in the deposit currency
   account.SetProperty(ACCOUNT_PROP_MARGIN_FREE,::AccountInfoDouble(ACCOUNT_MARGIN_FREE));
   this.m_struct_curr_account.margin_free=account.MarginFree();
   this.m_hash_sum+=(double)this.m_struct_curr_account.margin_free;
//--- Margin level on an account in %
   account.SetProperty(ACCOUNT_PROP_MARGIN_LEVEL,::AccountInfoDouble(ACCOUNT_MARGIN_LEVEL));
   this.m_struct_curr_account.margin_level=account.MarginLevel();
   this.m_hash_sum+=(double)this.m_struct_curr_account.margin_level;
//--- Margin Call level
   account.SetProperty(ACCOUNT_PROP_MARGIN_SO_CALL,::AccountInfoDouble(ACCOUNT_MARGIN_SO_CALL));
   this.m_struct_curr_account.margin_so_call=account.MarginSOCall();
   this.m_hash_sum+=(double)this.m_struct_curr_account.margin_so_call;
//--- Stop Out level
   account.SetProperty(ACCOUNT_PROP_MARGIN_SO_SO,::AccountInfoDouble(ACCOUNT_MARGIN_SO_SO));
   this.m_struct_curr_account.margin_so_so=account.MarginSOSO();
   this.m_hash_sum+=(double)this.m_struct_curr_account.margin_so_so;
//--- Funds reserved on an account to ensure a guarantee amount for all pending orders
   account.SetProperty(ACCOUNT_PROP_MARGIN_INITIAL,::AccountInfoDouble(ACCOUNT_MARGIN_INITIAL));
   this.m_struct_curr_account.margin_initial=account.MarginInitial();
   this.m_hash_sum+=(double)this.m_struct_curr_account.margin_initial;
//--- Funds reserved on an account to ensure a minimum amount for all open positions
   account.SetProperty(ACCOUNT_PROP_MARGIN_MAINTENANCE,::AccountInfoDouble(ACCOUNT_MARGIN_MAINTENANCE));
   this.m_struct_curr_account.margin_maintenance=account.MarginMaintenance();
   this.m_hash_sum+=(double)this.m_struct_curr_account.margin_maintenance;
//--- Current assets on an account
   account.SetProperty(ACCOUNT_PROP_ASSETS,::AccountInfoDouble(ACCOUNT_ASSETS));
   this.m_struct_curr_account.assets=account.Assets();
   this.m_hash_sum+=(double)this.m_struct_curr_account.assets;
//--- Current liabilities on an account
   account.SetProperty(ACCOUNT_PROP_LIABILITIES,::AccountInfoDouble(ACCOUNT_LIABILITIES));
   this.m_struct_curr_account.liabilities=account.Liabilities();
   this.m_hash_sum+=(double)this.m_struct_curr_account.liabilities;
//--- Current sum of blocked commissions on an account
   account.SetProperty(ACCOUNT_PROP_COMMISSION_BLOCKED,::AccountInfoDouble(ACCOUNT_COMMISSION_BLOCKED));
   this.m_struct_curr_account.comission_blocked=account.ComissionBlocked();
   this.m_hash_sum+=(double)this.m_struct_curr_account.comission_blocked;
  }
//+------------------------------------------------------------------+

Hemos mejorado y modificado de la misma forma el método SetTypeEvent() de la clase de colección de cuentas, debido a que ahora es posible determinar los eventos de cualquier objeto. El método es voluminoso, pero todas las acciones para determinar los tipos de evento de la cuenta son del mismo tipo, y ya han sido analizadas aquí al estudiar cómo se determinan los tipos de evento de los símbolos. Por eso, solo vamos a mostrar un ejemplo sobre, la definición del evento que permite el comercio en la cuenta comercial. Podrá encontrar el listado completo del método en los archivos adjuntos al artículo:

//+------------------------------------------------------------------+
//| Set the account object event type                                |
//+------------------------------------------------------------------+
void CAccountsCollection::SetTypeEvent(void)
  {
   this.InitChangesParams();
   ENUM_ACCOUNT_EVENT event_id=ACCOUNT_EVENT_NO_EVENT;
//--- Changing permission to trade for the account
   if(this.IsPresentEventFlag(ACCOUNT_EVENT_FLAG_TRADE_ALLOWED))
     {
      if(!this.m_struct_curr_account.trade_allowed)
        {
         this.m_is_change_trade_allowed_off=true;
         event_id=ACCOUNT_EVENT_TRADE_ALLOWED_OFF;
         if(this.EventAdd(event_id,this.TickTime(),this.m_is_change_trade_allowed_off,this.m_name))
            this.m_struct_prev_account.trade_allowed=this.m_struct_curr_account.trade_allowed;
        }
      else
        {
         this.m_is_change_trade_allowed_on=true;
         event_id=ACCOUNT_EVENT_TRADE_ALLOWED_ON;
         if(this.EventAdd(event_id,this.TickTime(),this.m_is_change_trade_allowed_on,this.m_name))
            this.m_struct_prev_account.trade_allowed=this.m_struct_curr_account.trade_allowed;
        }
     }
//--- Changing permission for auto trading for the account

Con esto, podemos considerar finalizados la modificación y mejora de la clase de colección de cuentas, por lo que vamos a activar la clase actualizada de evento de símbolo y la clase de cuenta.

Como recordaremos, el control parte desde la clase CEngine, por lo que todos los datos se envían a esta. La clase de evento de símbolo y la clase de cuenta no son una excepción.

Activando la clase de eventos de símbolo y la clase actualizada de la cuenta para el trabajo

Abrimos el archivo Engine.mqh e introducimos los cambios y correcciones necesarias.

En la sección privada de la clase, declaramos la bandera de evento de símbolo y el valor del último evento en el símbolo:

//+------------------------------------------------------------------+
//| Library basis class                                              |
//+------------------------------------------------------------------+
class CEngine : public CObject
  {
private:
   CHistoryCollection   m_history;                       // Collection of historical orders and deals
   CMarketCollection    m_market;                        // Collection of market orders and deals
   CEventsCollection    m_events;                        // Event collection
   CAccountsCollection  m_accounts;                      // Account collection
   CSymbolsCollection   m_symbols;                       // Symbol collection
   CArrayObj            m_list_counters;                 // List of timer counters
   int                  m_global_error;                  // Global error code
   bool                 m_first_start;                   // First launch flag
   bool                 m_is_hedge;                      // Hedge account flag
   bool                 m_is_tester;                     // Flag of working in the tester
   bool                 m_is_market_trade_event;         // Account trading event flag
   bool                 m_is_history_trade_event;        // Account history trading event flag
   bool                 m_is_account_event;              // Account change event flag
   bool                 m_is_symbol_event;               // Symbol change event flag
   ENUM_TRADE_EVENT     m_last_trade_event;              // Last account trading event
   ENUM_ACCOUNT_EVENT   m_last_account_event;            // Last event in the account properties
   ENUM_SYMBOL_EVENT    m_last_symbol_event;             // Last event in the symbol properties
//--- Return the counter index by id

En la sección pública de la clase, declaramos el método que retorna la descripción del último evento comercial:

//--- Return the list of historical (1) orders, (2) removed pending orders, (3) deals,
//--- (4) all market orders of a position by its ID, (5) description of the last trading event
   CArrayObj           *GetListHistoryOrders(void);
   CArrayObj           *GetListHistoryPendings(void);
   CArrayObj           *GetListDeals(void);
   CArrayObj           *GetListAllOrdersByPosID(const ulong position_id);
   string               GetLastTradeEventDescription(void);

y los nuevos métodos necesarios para trabajar con los eventos de símbolo, además de cambiar el método que retorna la bandera de evento de cuenta:

//--- Return the list of (1) used symbols, (2) symbol events, (3) the last symbol change event
//--- (4) the current symbol, (5) symbol event description, (6) Market Watch event description
   CArrayObj           *GetListAllUsedSymbols(void)                     { return this.m_symbols.GetList();                    }
   CArrayObj           *GetListSymbolsEvents(void)                      { return this.m_symbols.GetListEvents();              }
   ENUM_SYMBOL_EVENT    GetLastSymbolsEvent()                           { return this.m_symbols.GetLastEvent();               }
   CSymbol             *GetSymbolCurrent(void);
   string               GetSymbolEventDescription(ENUM_SYMBOL_EVENT event);
   string               GetMWEventDescription(ENUM_SYMBOL_EVENT event)  { return this.m_symbols.EventDescription(event);      }
   string               ModeSymbolsListDescription(void)                { return this.m_symbols.ModeSymbolsListDescription(); }
   
//--- Return the list of order, deal and position events
   CArrayObj           *GetListAllOrdersEvents(void)                    { return this.m_events.GetList();                     }
//--- Reset the last trading event
   void                 ResetLastTradeEvent(void)                       { this.m_events.ResetLastTradeEvent(); }
//--- Return the (1) last trading event, (2) the last event in the account properties, (3) hedging account flag, (4) flag of working in the tester
   ENUM_TRADE_EVENT     LastTradeEvent(void)                      const { return this.m_last_trade_event;                     }
   ENUM_ACCOUNT_EVENT   LastAccountEvent(void)                    const { return this.m_last_account_event;                   }
   ENUM_SYMBOL_EVENT    LastSymbolsEvent(void)                    const { return this.m_last_symbol_event;                    }
//--- Return the (1) hedge account, (2) working in the tester, (3) account event and (4) symbol event flag
   bool                 IsHedge(void)                             const { return this.m_is_hedge;                             }
   bool                 IsTester(void)                            const { return this.m_is_tester;                            }
   bool                 IsAccountsEvent(void)                     const { return this.m_accounts.IsEvent();                   }
   bool                 IsSymbolsEvent(void)                      const { return this.m_symbols.IsEvent();                    }
//--- Return the (1) symbol object by name, as well as the code of the last event of (2) an account and (3) a symbol
   CSymbol             *GetSymbolObjByName(const string name)           { return this.m_symbols.GetSymbolByName(name);        }
   int                  GetAccountEventsCode(void)                const { return this.m_accounts.GetEventCode();              }
   int                  GetSymbolsEventsCode(void)                const { return this.m_symbols.GetLastEventsCode();          }
//--- Return the number of (1) symbols, (2) events in the symbol collection
   int                  GetSymbolsCollectionTotal(void)           const { return this.m_symbols.GetSymbolsCollectionTotal();  }
   int                  GetSymbolsCollectionEventsTotal(void)     const { return this.m_symbols.GetEventsTotal();             }

En la lista de inicialización del constructor de la clase, añadimos la inicialización del último evento en la colección de símbolos:

//+------------------------------------------------------------------+
//| 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(SYMBOL_EVENT_NO_EVENT),
                     m_global_error(ERR_SUCCESS)
  {
   this.m_is_hedge=#ifdef __MQL4__ true #else bool(::AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING) #endif;
   this.m_is_tester=::MQLInfoInteger(MQL_TESTER);
   
   this.m_list_counters.Sort();
   this.m_list_counters.Clear();
   this.CreateCounter(COLLECTION_ORD_COUNTER_ID,COLLECTION_ORD_COUNTER_STEP,COLLECTION_ORD_PAUSE);
   this.CreateCounter(COLLECTION_ACC_COUNTER_ID,COLLECTION_ACC_COUNTER_STEP,COLLECTION_ACC_PAUSE);
   
   this.CreateCounter(COLLECTION_SYM_COUNTER_ID1,COLLECTION_SYM_COUNTER_STEP1,COLLECTION_SYM_PAUSE1);
   this.CreateCounter(COLLECTION_SYM_COUNTER_ID2,COLLECTION_SYM_COUNTER_STEP2,COLLECTION_SYM_PAUSE2);
   
   ::ResetLastError();
   #ifdef __MQL5__
      if(!::EventSetMillisecondTimer(TIMER_FREQUENCY))
        {
         ::Print(DFUN_ERR_LINE,"Не удалось создать таймер. Ошибка: ","Could not create timer. Error: ",(string)::GetLastError());
         this.m_global_error=::GetLastError();
        }
   //---__MQL4__
   #else 
      if(!this.IsTester() && !::EventSetMillisecondTimer(TIMER_FREQUENCY))
        {
         ::Print(DFUN_ERR_LINE,"Не удалось создать таймер. Ошибка: ","Could not create timer. Error: ",(string)::GetLastError());
         this.m_global_error=::GetLastError();
        }
   #endif 
  }
//+------------------------------------------------------------------+

Introducimos la correcciones en el manejador del temporizador de la clase, en los bloques de procesamiento temporizador1 y temporizador2 de la colección de símbolos:

//+------------------------------------------------------------------+
//| CEngine timer                                                    |
//+------------------------------------------------------------------+
void CEngine::OnTimer(void)
  {
//--- Timer of the collections of historical orders and deals, as well as of market orders and positions
   int index=this.CounterIndex(COLLECTION_ORD_COUNTER_ID);
   if(index>WRONG_VALUE)
     {
      CTimerCounter* counter=this.m_list_counters.At(index);
      if(counter!=NULL)
        {
         //--- If this is not a tester
         if(!this.IsTester())
           {
            //--- If unpaused, work with the order, deal and position collections events
            if(counter.IsTimeDone())
               this.TradeEventsControl();
           }
         //--- If this is a tester, work with collection events by tick
         else
            this.TradeEventsControl();
        }
     }
//--- Account collection timer
   index=this.CounterIndex(COLLECTION_ACC_COUNTER_ID);
   if(index>WRONG_VALUE)
     {
      CTimerCounter* counter=this.m_list_counters.At(index);
      if(counter!=NULL)
        {
         //--- If this is not a tester
         if(!this.IsTester())
           {
            //--- If unpaused, work with the account collection events
            if(counter.IsTimeDone())
               this.AccountEventsControl();
           }
         //--- If this is a tester, work with collection events by tick
         else
            this.AccountEventsControl();
        }
     }
     
//--- Timer 1 of the symbol collection (updating symbol quote data in the collection)
   index=this.CounterIndex(COLLECTION_SYM_COUNTER_ID1);
   if(index>WRONG_VALUE)
     {
      CTimerCounter* counter=this.m_list_counters.At(index);
      if(counter!=NULL)
        {
         //--- If this is not a tester
         if(!this.IsTester())
           {
            //--- If the pause is over, update quote data of all symbols in the collection
            if(counter.IsTimeDone())
               this.m_symbols.RefreshRates();
           }
         //--- In case of a tester, update quote data of all collection symbols by tick
         else
            this.m_symbols.RefreshRates();
        }
     }
//--- Timer 2 of the symbol collection (updating all data of all symbols in the collection and tracking symbl and symbol search events in the market watch window)
   index=this.CounterIndex(COLLECTION_SYM_COUNTER_ID2);
   if(index>WRONG_VALUE)
     {
      CTimerCounter* counter=this.m_list_counters.At(index);
      if(counter!=NULL)
        {
         //--- If this is not a tester
         if(!this.IsTester())
           {
            //--- If the pause is over
            if(counter.IsTimeDone())
              {
               //--- update data and work with events of all symbols in the collection
               this.SymbolEventsControl();
               //--- When working with the market watch list, check the market watch window events
               if(this.m_symbols.ModeSymbolsList()==SYMBOLS_MODE_MARKET_WATCH)
                  this.MarketWatchEventsControl();
              }
           }
         //--- If this is a tester, work with events of all symbols in the collection by tick
         else
            this.SymbolEventsControl();
        }
     }
  }
//+------------------------------------------------------------------+

Aquí, cuando finaliza el contador del temporizador1, debemos simplemente actualizar los datos de cotización de todos los símbolos de la colección, por eso, llamamos al método RefreshRates() de la colección de símbolos.
Cuando finaliza el contador del temporizador2, tenemos que actualizar por completo todos los símbolos de la colección de símbolos y monitorear los eventos sucedidos tanto de los símbolos de la colección, como de la lista de símbolos en la ventana de "Observación de mercado", por eso, llamamos a los métodos de la clase CEngine SymbolEventsControl(), y solo al trabajar en el simulador, MarketWatchEventsControl().

Implementación del método de trabajo con los eventos de la colección de símbolos:

//+------------------------------------------------------------------+
//| Working with symbol collection events                            |
//+------------------------------------------------------------------+
void CEngine::SymbolEventsControl(void)
  {
   this.m_symbols.SymbolsEventsControl();
   this.m_is_symbol_event=this.m_symbols.IsEvent();
//--- If there are changes in symbol properties
   if(this.m_is_symbol_event)
     {
      //--- Get the last event of the symbol property change
      this.m_last_symbol_event=this.m_symbols.GetLastEvent();
     }
  }
//+------------------------------------------------------------------+

Aquí: llamamos al método de la colección de símbolos SymbolsEventsControl(), analizado en este artículo al estudiar la clase de eventos de la colección de símbolos. Cuando este método termina de funcionar, la bandera de evento se activará en la clase de la colección de símbolos, con la condición de que un evento haya sido registrado en cualquiera de los símbolos de la colección; luego registraremos el estado de esta bandera con la ayuda del método IsEvent() de la clase del objeto básico CBaseObj en la variable de bandera del evento de la colección de símbolos m_is_symbol_event, cuyo valor se puede monitorear en el programa que realiza la llamada. Si se ha registrado un evento en la colección de símbolos, registramos el último evento en la variable m_last_symbol_event, cuyo valor también se puede monitorear en el programa que realiza la llamada.

Implementación del método de trabajo con los eventos de la ventana de "Observación de mercado":

//+------------------------------------------------------------------+
//| Working with symbol list events in the market watch window       |
//+------------------------------------------------------------------+
void CEngine::MarketWatchEventsControl(void)
  {
   if(this.IsTester())
      return;
   this.m_symbols.MarketWatchEventsControl();
  }
//+------------------------------------------------------------------+

Aquí, si se trata del simulador, salimos, de lo contrario, llamamos al método MarketWatchEventsControl() de la clase de colección de símbolos para procesar los eventos de la ventana de observación de mercado, que analizamos antes al estudiar el seguimiento de los eventos de la clase de colección de símbolos.

Implementación del método que retorna la descripción del último evento comercial:

//+------------------------------------------------------------------+
//| Return the description of the last trading event                 |
//+------------------------------------------------------------------+
string CEngine::GetLastTradeEventDescription(void)
  {
   CArrayObj *list=this.m_events.GetList();
   if(list!=NULL)
     {
      if(list.Total()==0)
         return TextByLanguage("С момента последнего запуска ЕА торговых событий не было","There have been no trade events since the last launch of EA");
      CEvent *event=list.At(list.Total()-1);
      if(event!=NULL)
         return event.TypeEventDescription();
     }
   return DFUN_ERR_LINE+TextByLanguage("Не удалось получить описание последнего торгового события","Failed to get the description of the last trading event");
  }
//+------------------------------------------------------------------+

Aquí, obtenemos la lista completa de eventos comerciales en la cuenta. Si hemos obtenido la lista, pero su valor es cero, retornamos un mensaje indicando que no hemos tenido eventos comerciales, de lo contrario, obtenemos de la lista el último evento y retornamos su descripción. En el caso contrario, retornamos un mensaje sobre el fallo al obtener el evento comercial.

Método que retorna la descripción del último evento en la colección de símbolos:

//+------------------------------------------------------------------+
//| Return the symbol event description                              |
//+------------------------------------------------------------------+
string CEngine::GetSymbolEventDescription(ENUM_SYMBOL_EVENT event)
  {
   CArrayObj *list=this.m_symbols.GetList();
   if(list!=NULL)
     {
      if(list.Total()==0)
         return TextByLanguage("С момента последнего запуска ЕА не было никаких событий символов","There have been no events of symbols since the last launch of EA");
      CSymbol *symbol=list.At(list.Total()-1);
      if(symbol!=NULL)
         return symbol.EventDescription(event);
     }
   return DFUN_ERR_LINE+TextByLanguage("Не удалось получить описание события символа","Failed to get symbol's event description");
  }
//+------------------------------------------------------------------+

El método funciona de forma idéntica al método de retorno de la descripción del último evento comercial que acabamos de analizar.

Ya hemos terminado las mejoras de la clase CEngine, y lo tenemos todo listo para simular los eventos de símbolo, la clase de cuenta actualizada y los eventos de cuenta.

Hemos introducido en las clases analizadas algunos cambios que no se reflejan en el artículo, dado que se trata solo de cambios, por ejemplo, relacionados con las denominaciones de algunos métodos, para que los métodos del mismo tipo y de diferentes clases tengan los mismos nombres dentro de lo posible. Estas pequeñas mejoras no serán mencionadas en el código de la biblioteca, ya que esta se encuentra en constante modificación: el lector siempre podrá familiarizarse con los cambios en los archivos adjuntos a los artículos.

Simulando los eventos de símbolo y cuenta

Para poner a prueba la colección, vamos a tomar el asesor de prueba del artículo anterior, guardarlo con el nombre \MQL5\Experts\TestDoEasy\ Part16\TestDoEasyPart16.mq5 e introducir en el mismo los cambios necesarios.

Añadimos a la lista de variables globales la variable para guardar el modo de trabajo con las listas de símbolos:

//--- global variables
CEngine        engine;
#ifdef __MQL5__
CTrade         trade;
#endif 
SDataButt      butt_data[TOTAL_BUTT];
string         prefix;
double         lot;
double         withdrawal=(InpWithdrawal<0.1 ? 0.1 : InpWithdrawal);
ulong          magic_number;
uint           stoploss;
uint           takeprofit;
uint           distance_pending;
uint           distance_stoplimit;
uint           slippage;
bool           trailing_on;
double         trailing_stop;
double         trailing_step;
uint           trailing_start;
uint           stoploss_to_modify;
uint           takeprofit_to_modify;
int            used_symbols_mode;
string         used_symbols;
string         array_used_symbols[];

Dado que al elegir el modo de trabajo con los símbolos "Trabajo con la lista completa de símbolos en el servidor", el primer inicio puede resultar bastante prolongado (porque la colección de símbolos debe recopilar todos los datos sobre todos los símbolos disponibles), vamos a necesitar advertir de ello al usuario. No tiene sentido hacer esto en la propia biblioteca, puesto que esta solo debe procesar lo que le pide el usuario, por eso, implementaremos la advertencia en el manejador OnInit() del programa.

Lo haremos así: si en los ajustes del asesor se selecciona el modo de trabajo con la lista completa de símbolos disponibles en el servidor, el programa mostrará la ventana estándar de la función MessageBox() con la advertencia

proponiendo seleccionar "Sí" para cargar la lista completa de símbolos, o "No" para trabajar solo con el símbolo actual. El usuario solo tiene que elegir: pulsar "Sí" y esperar a que se cree la colección de todos los símbolos disponibles, o bien pulsar "No" y trabajar con el actual.

Implementamos la amplia comprobación, mostrando al usuario la pregunta en el manejador OnInit() del asesor:

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

Aquí, asignamos a la variable global used_symbols_mode el valor del modo de trabajo con los símbolos seleccionado por el usuario en los ajustes del asesor.
Si se ha seleccionado el modo de trabajo con la lista completa
, creamos el texto del mensaje de la ventana de advertencia y mostramos esta ventana en la pantalla. A continuación, comprobamos qué botón ha pulsado el usuario. Si se trata del botón "No", asignamos a la variable used_symbols_mode el valor del modo de trabajo con el símbolo actual.
En el resto de los casos (botón "Sí" o Esc), dejamos el modo de trabajo con la lista completa de símbolos disponibles.

Acto seguido, creamos una matriz con los símbolos usados (enviamos a la función de creación de la matriz la variable used_symbols_mode):

//--- Fill in the array of used symbols
   used_symbols=InpUsedSymbols;
   CreateUsedSymbolsArray((ENUM_SYMBOLS_MODE)used_symbols_mode,used_symbols,array_used_symbols);

establecemos en la biblioteca el tipo de lista utilizado (modo de trabajo con los símbolos) y mostramos en el diario un mensaje sobre el modo usado de trabajo con los símbolos:

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

Eliminamos el bloque de código de comprobación rápida de la colección de símbolos del manejador OnInit(), ya que no resulta necesario en este asesor de prueba:

//--- Fast check of the symbol object collection
   CArrayObj *list=engine.GetListAllUsedSymbols();
   CSymbol *symbol=NULL;
   if(list!=NULL)
     {
      int total=list.Total();
      for(int i=0;i<total;i++)
        {
         symbol=list.At(i);
         if(symbol==NULL)
            continue;
         symbol.Refresh();
         symbol.RefreshRates();
         symbol.PrintShort();
         if(InpModeUsedSymbols<SYMBOLS_MODE_MARKET_WATCH)
            symbol.Print();
        }
     }

Añadimos al manejador OnTick() una variable para guardar el último evento en la colección de símbolos y escribimos (o modificamos, para los eventos de cuenta) el bloque de procesamiento de los eventos de cuenta y de los eventos de la colección de símbolos:

//+------------------------------------------------------------------+
//| 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))
     {
      engine.OnTimer();
      PressButtonsControl();
     }
//--- If the last trading event changed
   if(engine.LastTradeEvent()!=last_trade_event)
     {
      last_trade_event=engine.LastTradeEvent();
      Comment("\nLast trade event: ",engine.GetLastTradeEventDescription());
      engine.ResetLastTradeEvent();
     }
//--- If there is an account event
   if(engine.IsAccountsEvent())
     {
      //--- the last account event
      last_account_event=engine.LastAccountEvent();
      //--- If this is a tester
      if(MQLInfoInteger(MQL_TESTER))
        {
         //--- Get the list of all account events occurred simultaneously
         CArrayObj* list=engine.GetListAccountEvents();
         if(list!=NULL)
           {
            //--- Get the next event in a loop
            int total=list.Total();
            for(int i=0;i<total;i++)
              {
               //--- take an event from the list
               CEventBaseObj *event=list.At(i);
               if(event==NULL)
                  continue;
               //--- Send an event to the event handler
               long lparam=event.LParam();
               double dparam=event.DParam();
               string sparam=event.SParam();
               OnDoEasyEvent(CHARTEVENT_CUSTOM+event.ID(),lparam,dparam,sparam);
              }
           }
        }
     }
//--- If there is a symbol collection event
   if(engine.IsSymbolsEvent())
     {
      //--- the last event in the symbol collection
      last_symbol_event=engine.LastSymbolsEvent();
      //--- If this is a tester
      if(MQLInfoInteger(MQL_TESTER))
        {
         //--- Get the list of all symbol events occurred simultaneously
         CArrayObj* list=engine.GetListSymbolsEvents();
         if(list!=NULL)
           {
            //--- Get the next event in a loop
            int total=list.Total();
            for(int i=0;i<total;i++)
              {
               //--- take an event from the list
               CEventBaseObj *event=list.At(i);
               if(event==NULL)
                  continue;
               //--- Send an event to the event handler
               long lparam=event.LParam();
               double dparam=event.DParam();
               string sparam=event.SParam();
               OnDoEasyEvent(CHARTEVENT_CUSTOM+event.ID(),lparam,dparam,sparam);
              }
           }
        }
     }
//--- If the trailing flag is set
   if(trailing_on)
     {
      TrailingPositions();
      TrailingOrders();
     }
  }
//+------------------------------------------------------------------+

Aquí todo es simple: todos los cambios necesarios en cuanto al procesamiento de los eventos de cuenta y los eventos de la colección de símbolos han sido comentados en el listado.

Como podemos observar, ahora el procesamiento de eventos es absolutamente idéntico para cualquier objeto, ya sea de una cuenta, o de una colección de símbolos: todo se reduce a obtener la lista de eventos y enviar cada evento sucesivo de la lista al manejador OnDoEasyEvent() del asesor que procesa los eventos de la biblioteca. Esto es posible gracias a los cambios realizados en la herencia de los objetos de la biblioteca del objeto básico CBaseObj, en el que se ha implementado el procesamiento de los eventos de los objetos herederos.

Añadimos al manejador del asesor OnDoEasyEvent() el código de procesamiento de los 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);
   int digits=Digits();
//--- Handling trading events
   if(idx>TRADE_EVENT_NO_EVENT && idx<TRADE_EVENTS_NEXT_CODE)
     {
      event=EnumToString((ENUM_TRADE_EVENT)ushort(idx));
      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 growth 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 
                 }
              }
           }
        }
     }
     
//--- Handling symbol events
   else if(idx>SYMBOL_EVENT_NO_EVENT && idx<SYMBOL_EVENTS_NEXT_CODE)
     {
      string name="";
      //--- Market Watch window event
      if(idx<SYMBOL_EVENT_TRADE_DISABLE)
        {
         string descr=engine.GetMWEventDescription((ENUM_SYMBOL_EVENT)idx);
         name=(idx==SYMBOL_EVENT_MW_SORT ? "" : ": "+sparam);
         Print(TimeMSCtoString(lparam)," ",descr,name);
        }
      //--- Symbol event
      else
        {
         CSymbol *symbol=engine.GetSymbolObjByName(sparam);
         if(symbol!=NULL)
           {
            string descr=": "+symbol.EventDescription((ENUM_SYMBOL_EVENT)ushort(idx));
            Print(TimeMSCtoString(lparam)," ",sparam,descr);
           }
        }
     }
  }
//+------------------------------------------------------------------+

Aquí solo hay dos opciones: el procesamiento de los eventos de la ventana "Observación de mercado" y el procesamiento de los eventos de los símbolos de la colección.

Para los eventos de la ventana de observación de mercado
creamos el mensaje necesario
y lo mostramos en el diario,
y para los eventos de los símbolos
obtenemos de la lista el símbolo según el nombre
del parámetro de línea de evento sparam, obtenemos del objeto de símbolo la descripción de línea de su evento, lo añadimos al texto creado y mostramos el texto en el diario.
No vamos a realizar ninguna acción adicional para las simulaciones.

Aquí se terminan los cambios para el asesor de prueba.
Podrá ver el listado completo del asesor en los archivos adjuntos al artículo.

Si iniciamos el asesor en una cuenta demo, pasado cierto tiempo, podremos ver en el diario varias entradas sobre los cambios en las propiedades de los símbolos. Por ejemplo, al iniciar el asesor en la víspera de la apertura de una sesión comercial el lunes, en el diario se comenzarán a mostrar de forma activa entradas sobre el cambio en el spread de diferentes símbolos.
Como ejemplo, para solo cuatro símbolos en la ventana de "Observación de mercado" se han mostrado durante la última hora multitud de mensajes sobre el cambio en el spread de los símbolos:

2019.07.15 04:02:24.167 TestDoEasyPart16 (EURUSD,H4)    Working with symbols from the "Market Watch" window. The number of symbols used: 4
2019.07.15 04:02:25.762 TestDoEasyPart16 (EURUSD,H4)    2019.07.15 00:02:27.316 GBPUSD: Spread value in points decreased by -7 (351)
2019.07.15 04:02:31.259 TestDoEasyPart16 (EURUSD,H4)    2019.07.15 00:02:32.676 USDCHF: Spread value in points increased by 4 (287)
2019.07.15 04:02:33.761 TestDoEasyPart16 (EURUSD,H4)    2019.07.15 00:02:35.218 USDCHF: Spread value in points decreased by -4 (283)
2019.07.15 04:02:46.261 TestDoEasyPart16 (EURUSD,H4)    2019.07.15 00:02:47.680 USDCHF: Spread value in points increased by 4 (287)
2019.07.15 04:02:48.761 TestDoEasyPart16 (EURUSD,H4)    2019.07.15 00:02:50.222 USDCHF: Spread value in points decreased by -4 (283)
2019.07.15 04:02:53.760 TestDoEasyPart16 (EURUSD,H4)    2019.07.15 00:02:55.305 USDCHF: Spread value in points increased by 4 (287)
2019.07.15 04:02:56.760 TestDoEasyPart16 (EURUSD,H4)    2019.07.15 00:02:58.221 USDCHF: Spread value in points decreased by -4 (283)
2019.07.15 04:03:01.261 TestDoEasyPart16 (EURUSD,H4)    2019.07.15 00:03:02.683 USDCHF: Spread value in points increased by 4 (287)
2019.07.15 04:03:03.760 TestDoEasyPart16 (EURUSD,H4)    2019.07.15 00:03:05.226 USDCHF: Spread value in points decreased by -4 (283)
2019.07.15 04:03:16.260 TestDoEasyPart16 (EURUSD,H4)    2019.07.15 00:03:17.673 USDCHF: Spread value in points increased by 4 (287)
2019.07.15 04:03:18.789 TestDoEasyPart16 (EURUSD,H4)    2019.07.15 00:03:20.219 USDCHF: Spread value in points decreased by -4 (283)
2019.07.15 04:03:30.832 TestDoEasyPart16 (EURUSD,H4)    2019.07.15 00:03:32.686 USDCHF: Spread value in points increased by 4 (287)
2019.07.15 04:03:33.819 TestDoEasyPart16 (EURUSD,H4)    2019.07.15 00:03:35.219 USDCHF: Spread value in points decreased by -4 (283)
2019.07.15 04:03:38.820 TestDoEasyPart16 (EURUSD,H4)    2019.07.15 00:03:39.926 USDCHF: Spread value in points increased by 4 (287)
2019.07.15 04:03:41.821 TestDoEasyPart16 (EURUSD,H4)    2019.07.15 00:03:43.221 USDCHF: Spread value in points decreased by -4 (283)
2019.07.15 04:03:45.820 TestDoEasyPart16 (EURUSD,H4)    2019.07.15 00:03:47.673 USDCHF: Spread value in points increased by 4 (287)
2019.07.15 04:03:48.836 TestDoEasyPart16 (EURUSD,H4)    2019.07.15 00:03:50.234 USDCHF: Spread value in points decreased by -4 (283)
2019.07.15 04:03:50.865 TestDoEasyPart16 (EURUSD,H4)    2019.07.15 00:03:52.598 USDCHF: Spread value in points increased by 51 (334)
2019.07.15 04:03:58.867 TestDoEasyPart16 (EURUSD,H4)    2019.07.15 00:04:00.450 EURUSD: Spread value in points decreased by -42 (50)
2019.07.15 04:03:58.868 TestDoEasyPart16 (EURUSD,H4)    2019.07.15 00:04:00.430 USDCHF: Spread value in points decreased by -96 (238)
2019.07.15 04:03:59.417 TestDoEasyPart16 (EURUSD,H4)    2019.07.15 00:04:00.934 USDCHF: Spread value in points increased by 22 (260)
2019.07.15 04:03:59.912 TestDoEasyPart16 (EURUSD,H4)    2019.07.15 00:04:01.431 USDCHF: Spread value in points decreased by -5 (255)
2019.07.15 04:04:35.445 TestDoEasyPart16 (EURUSD,H4)    2019.07.15 00:04:36.984 GBPUSD: Spread value in points decreased by -112 (239)
2019.07.15 04:04:35.445 TestDoEasyPart16 (EURUSD,H4)    2019.07.15 00:04:36.985 EURUSD: Spread value in points decreased by -7 (43)
2019.07.15 04:04:35.445 TestDoEasyPart16 (EURUSD,H4)    2019.07.15 00:04:36.984 USDCHF: Spread value in points decreased by -127 (128)
2019.07.15 04:04:58.460 TestDoEasyPart16 (EURUSD,H4)    2019.07.15 00:05:00.102 GBPUSD: Spread value in points decreased by -207 (32)
2019.07.15 04:04:58.959 TestDoEasyPart16 (EURUSD,H4)    2019.07.15 00:05:00.696 EURUSD: Spread value in points decreased by -4 (39)
2019.07.15 04:05:01.006 TestDoEasyPart16 (EURUSD,H4)    2019.07.15 00:05:02.697 EURUSD: Spread value in points increased by 3 (42)
2019.07.15 04:05:02.037 TestDoEasyPart16 (EURUSD,H4)    2019.07.15 00:05:03.686 EURUSD: Spread value in points decreased by -32 (10)

... hemos omitido muchas líneas ...

2019.07.15 04:55:09.780 TestDoEasyPart16 (EURUSD,H4)    2019.07.15 00:55:11.578 GBPUSD: Spread value in points decreased by -3 (29)
2019.07.15 04:55:09.780 TestDoEasyPart16 (EURUSD,H4)    2019.07.15 00:55:11.478 USDCHF: Spread value in points increased by 4 (32)
2019.07.15 04:55:10.482 TestDoEasyPart16 (EURUSD,H4)    2019.07.15 00:55:11.681 USDCHF: Spread value in points decreased by -3 (29)
2019.07.15 04:55:11.623 TestDoEasyPart16 (EURUSD,H4)    2019.07.15 00:55:13.477 USDCHF: Spread value in points increased by 3 (32)
2019.07.15 04:55:12.111 TestDoEasyPart16 (EURUSD,H4)    2019.07.15 00:55:13.884 USDCHF: Spread value in points decreased by -5 (27)
2019.07.15 04:55:13.626 TestDoEasyPart16 (EURUSD,H4)    2019.07.15 00:55:15.275 USDCHF: Spread value in points increased by 4 (31)
2019.07.15 04:55:19.628 TestDoEasyPart16 (EURUSD,H4)    2019.07.15 00:55:21.381 USDCHF: Spread value in points decreased by -3 (28)
2019.07.15 04:55:20.126 TestDoEasyPart16 (EURUSD,H4)    2019.07.15 00:55:21.882 USDCHF: Spread value in points increased by 3 (31)
2019.07.15 04:55:28.659 TestDoEasyPart16 (EURUSD,H4)    2019.07.15 00:55:30.292 EURUSD: Spread value in points increased by 3 (20)
2019.07.15 04:55:33.690 TestDoEasyPart16 (EURUSD,H4)    2019.07.15 00:55:35.298 EURUSD: Spread value in points decreased by -3 (17)
2019.07.15 04:55:53.298 TestDoEasyPart16 (EURUSD,H4)    2019.07.15 00:55:55.137 EURUSD: Spread value in points increased by 3 (20)
2019.07.15 04:55:53.826 TestDoEasyPart16 (EURUSD,H4)    2019.07.15 00:55:55.643 EURUSD: Spread value in points decreased by -3 (17)
2019.07.15 04:55:54.906 TestDoEasyPart16 (EURUSD,H4)    2019.07.15 00:55:56.632 USDCHF: Spread value in points decreased by -3 (28)
2019.07.15 04:55:55.912 TestDoEasyPart16 (EURUSD,H4)    2019.07.15 00:55:57.536 USDCHF: Spread value in points increased by 4 (32)
2019.07.15 04:55:56.907 TestDoEasyPart16 (EURUSD,H4)    2019.07.15 00:55:58.636 USDCHF: Spread value in points decreased by -4 (28)
2019.07.15 04:55:57.434 TestDoEasyPart16 (EURUSD,H4)    2019.07.15 00:55:58.832 USDCHF: Spread value in points increased by 4 (32)
2019.07.15 04:55:59.949 TestDoEasyPart16 (EURUSD,H4)    2019.07.15 00:56:01.538 USDCHF: Spread value in points decreased by -3 (29)

Vamos a iniciar el asesor con dos símbolos en el simulador, así veremos qué entradas nos muestra.

En los ajustes del simulador, para el parámetro de entrada del asesor Mode of used symbols list, seleccionamos de la lista desplegable "Trabajar con la lista de símbolos establecida", y en el parámetro List of used symbols (comma - separator) introducimos los dos símbolos separados por una coma: EURUSD,GBPUSD e iniciamos el test visual del asesor:


En el diario se muestran entradas sobre los eventos de ambos símbolos, concretamente, sobre los cambios en los spreads de los símbolos usados. Cuando las propiedades de la cuenta cambian (aquí aumenta el beneficio actual), en el diario se muestran entradas sobre ello, cerrándose asimismo las posiciones rentables.

¿Qué es lo próximo?

En el siguiente artículo, implementaremos un cómodo acceso desde el programa al cambio de valores de las propiedades controladas y monitoreadas de los objetos, basado en 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