English Русский 中文 Deutsch 日本語 Português
Biblioteca para el desarrollo rápido y sencillo de programas para MetaTrader (Parte XIII): Eventos del objeto "cuenta"

Biblioteca para el desarrollo rápido y sencillo de programas para MetaTrader (Parte XIII): Eventos del objeto "cuenta"

MetaTrader 5Ejemplos | 3 septiembre 2019, 08:32
1 070 0
Artyom Trishkin
Artyom Trishkin

Contenido


Concepto de los eventos de cuenta

Anteriormente, para monitorear los eventos de las órdenes y posiciones (Parte IVParte XI), creamos una clase aparte, que se ocupa precisamente de monitorear los eventos de las órdenes y posiciones, así como de enviar los datos sobre sus cambios al objeto principal de la biblioteca CEngine.


Para monitorear los eventos de la cuenta, vamos a elegir otro camino: dado que tenemos la posibilidad de monitorear los eventos solo de una única cuenta comercial (aquella a la que se encuentra ahora conectada el terminal), creemos que tener una clase aparte para monitorear eventos de una sola cuenta comercial sería excesivo, así que vamos a crear los métodos para trabajar directamente en la clase de colección de cuentas.
Para comprobar si ha tenido lugar algún un cambio en las propiedades de la cuenta comercial, vamos a comparar el estado actual de las propiedades de la cuenta comercial con su anterior estado, y si encontramos un cambio, enviaremos el evento al gráfico del programa de control.
Ya hemos creado cierta parte de la funcionalidad para monitorear la cuenta en el artículo anterior, al crear la colección de objetos de cuenta. En este artículo, vamos a terminar con la funcionalidad preparada, para lograr su plena operatividad.


Métodos de trabajo con los eventos de cuenta

Vamos a comenzar con la creación de las enumeraciones y las macrosustituciones necesarias en el archivo Defines.mqh. Ya que hemos obtenido respusta de los desarrolladores sobre las dimensiones reales en bytes reservadas para las propiedades de tipo string de la cuenta, las estableceremos con rigor en el código de la clase CAccount y, por consiguiente, ya no existirá necesidad de usar una macrosustitución que establezca el tamaño de las matrices uchar para guardar las propiedades de tipo string del objeto de cuenta. Eliminamos la macrosustitución del listado de Defines.mqh

#define UCHAR_ARRAY_SIZE            (64)                       // Size of uchar arrays for storing string properties

Asimismo, hemos decidido añadir todas las enumeraciones y macrosustituciones necesarias para trabajar con los objetos de cuenta al final del archivo, después de los datos para trabajar con los eventos comerciales. El motivo de ello es que vamos a enviar al programa los códigos de evento no solo de las cuentas, sino también de los eventos comerciales que ya hemos implementado y que son enviados al programa. Por consiguiente, los valores de los códigos de evento de la cuenta comenzarán por el valor numérico del último código del evento comercial +1. Si, por lo que sea, aumenta el número de eventos comerciales, para no tener que reescribir los valores numéricos de los códigos de evento de la cuenta, vamos a declarar una macrosustitución en la que se registrará el número total calculado de todos los eventos comerciales. Y exactamente de la misma forma vamos a establecer una macrosustitución con el valor del número total de eventos de la cuenta, por si vamos a implementar algún evento más (por ejemplo, los eventos de símbolo); en ese caso, los códigos numéricos de estos nuevos eventos comenzarán ya a partir del número total de eventos de la cuenta +1.

Al final de la lista de posibles eventos comerciales en la cuenta, añadimos una macrosustitución en la que se indicará el valor correspondiente al valor numérico del último evento comercial +1:

//+------------------------------------------------------------------+
//| List of possible trading events on the account                   |
//+------------------------------------------------------------------+
enum ENUM_TRADE_EVENT
  {
   TRADE_EVENT_NO_EVENT = 0,                                // No trading event
   TRADE_EVENT_PENDING_ORDER_PLASED,                        // Pending order placed
   TRADE_EVENT_PENDING_ORDER_REMOVED,                       // Pending order removed
//--- enumeration members matching the ENUM_DEAL_TYPE enumeration members
//--- (constant order below should not be changed, no constants should be added/deleted)
   TRADE_EVENT_ACCOUNT_CREDIT = DEAL_TYPE_CREDIT,           // Accruing credit (3)
   TRADE_EVENT_ACCOUNT_CHARGE,                              // Additional charges
   TRADE_EVENT_ACCOUNT_CORRECTION,                          // Correcting entry
   TRADE_EVENT_ACCOUNT_BONUS,                               // Accruing bonuses
   TRADE_EVENT_ACCOUNT_COMISSION,                           // Additional commissions
   TRADE_EVENT_ACCOUNT_COMISSION_DAILY,                     // Commission charged at the end of a trading day
   TRADE_EVENT_ACCOUNT_COMISSION_MONTHLY,                   // Commission charged at the end of a trading month
   TRADE_EVENT_ACCOUNT_COMISSION_AGENT_DAILY,               // Agent commission charged at the end of a trading day
   TRADE_EVENT_ACCOUNT_COMISSION_AGENT_MONTHLY,             // Agent commission charged at the end of a month
   TRADE_EVENT_ACCOUNT_INTEREST,                            // Accrued interest on free funds
   TRADE_EVENT_BUY_CANCELLED,                               // Canceled buy deal
   TRADE_EVENT_SELL_CANCELLED,                              // Canceled sell deal
   TRADE_EVENT_DIVIDENT,                                    // Accruing dividends
   TRADE_EVENT_DIVIDENT_FRANKED,                            // Accruing franked dividends
   TRADE_EVENT_TAX                        = DEAL_TAX,       // Tax
//--- constants related to the DEAL_TYPE_BALANCE deal type from the DEAL_TYPE_BALANCE enumeration
   TRADE_EVENT_ACCOUNT_BALANCE_REFILL     = DEAL_TAX+1,     // Replenishing account balance
   TRADE_EVENT_ACCOUNT_BALANCE_WITHDRAWAL = DEAL_TAX+2,     // Withdrawing funds from an account
//--- Remaining possible trading events//--- (constant order below can be changed, constants can be added/deleted)
   TRADE_EVENT_PENDING_ORDER_ACTIVATED    = DEAL_TAX+3,     // Pending order activated by price
   TRADE_EVENT_PENDING_ORDER_ACTIVATED_PARTIAL,             // Pending order partially activated by price
   TRADE_EVENT_POSITION_OPENED,                             // Position opened
   TRADE_EVENT_POSITION_OPENED_PARTIAL,                     // Position opened partially
   TRADE_EVENT_POSITION_CLOSED,                             // Position closed
   TRADE_EVENT_POSITION_CLOSED_BY_POS,                      // Position closed by an opposite one
   TRADE_EVENT_POSITION_CLOSED_BY_SL,                       // Position closed by StopLoss
   TRADE_EVENT_POSITION_CLOSED_BY_TP,                       // Position closed by TakeProfit
   TRADE_EVENT_POSITION_REVERSED_BY_MARKET,                 // Position reversal by a new deal (netting)
   TRADE_EVENT_POSITION_REVERSED_BY_PENDING,                // Position reversal by activating a pending order (netting)
   TRADE_EVENT_POSITION_REVERSED_BY_MARKET_PARTIAL,         // Position reversal by partial market order execution (netting)
   TRADE_EVENT_POSITION_REVERSED_BY_PENDING_PARTIAL,        // Position reversal by partial pending order activation (netting)
   TRADE_EVENT_POSITION_VOLUME_ADD_BY_MARKET,               // Added volume to a position by a new deal (netting)
   TRADE_EVENT_POSITION_VOLUME_ADD_BY_MARKET_PARTIAL,       // Added volume to a position by partial activation of an order (netting)
   TRADE_EVENT_POSITION_VOLUME_ADD_BY_PENDING,              // Added volume to a position by activating a pending order (netting)
   TRADE_EVENT_POSITION_VOLUME_ADD_BY_PENDING_PARTIAL,      // Added volume to a position by partial activation of a pending order (netting)
   TRADE_EVENT_POSITION_CLOSED_PARTIAL,                     // Position closed partially
   TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_POS,              // Position closed partially by an opposite one
   TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_SL,               // Position closed partially by StopLoss
   TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_TP,               // Position closed partially by TakeProfit
   TRADE_EVENT_TRIGGERED_STOP_LIMIT_ORDER,                  // StopLimit order activation
   TRADE_EVENT_MODIFY_ORDER_PRICE,                          // Changing order price
   TRADE_EVENT_MODIFY_ORDER_PRICE_STOP_LOSS,                // Changing order and StopLoss price 
   TRADE_EVENT_MODIFY_ORDER_PRICE_TAKE_PROFIT,              // Changing order and TakeProfit price
   TRADE_EVENT_MODIFY_ORDER_PRICE_STOP_LOSS_TAKE_PROFIT,    // Changing order, StopLoss and TakeProfit price
   TRADE_EVENT_MODIFY_ORDER_STOP_LOSS_TAKE_PROFIT,          // Changing order's StopLoss and TakeProfit price
   TRADE_EVENT_MODIFY_ORDER_STOP_LOSS,                      // Changing order Stop Loss
   TRADE_EVENT_MODIFY_ORDER_TAKE_PROFIT,                    // Changing order Take Profit
   TRADE_EVENT_MODIFY_POSITION_STOP_LOSS_TAKE_PROFIT,       // Changing position StopLoss and TakeProfit
   TRADE_EVENT_MODIFY_POSITION_STOP_LOSS,                   // Changing position StopLoss
   TRADE_EVENT_MODIFY_POSITION_TAKE_PROFIT,                 // Changing position TakeProfit
  };
#define TRADE_EVENTS_NEXT_CODE      (TRADE_EVENT_MODIFY_POSITION_TAKE_PROFIT+1)// The code of the next event after the last trading event code
//+------------------------------------------------------------------+

Precisamente a partir de este código comenzarán los códigos de eventos de la cuenta.

Hemos escrito los datos para trabajar con la cuenta (las propiedades de tipo entero, real y string de la cuenta, así como los posibles criterios de clasificación de las cuentas) en el artículo anterior, pero los ubicamos antes de los datos para trabajar con los eventos de la cuenta. Ahora, vamos a trasladarlos al final, escribiendo los datos adicionales para trabajar con los eventos de la cuenta: la lista de banderas de eventos de la cuenta y la lista de posibles eventos de la cuenta:

//+------------------------------------------------------------------+
//| Data for working with accounts                                   |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| List of account event flags                                      |
//+------------------------------------------------------------------+
enum ENUM_ACCOUNT_EVENT_FLAGS
  {
   ACCOUNT_EVENT_FLAG_NO_EVENT            =  0,             // No event
   ACCOUNT_EVENT_FLAG_LEVERAGE            =  1,             // Changing the leverage
   ACCOUNT_EVENT_FLAG_LIMIT_ORDERS        =  2,             // Changing the maximum allowed number of active pending orders
   ACCOUNT_EVENT_FLAG_TRADE_ALLOWED       =  4,             // Changing permission to trade for the account
   ACCOUNT_EVENT_FLAG_TRADE_EXPERT        =  8,             // Changing permission for auto trading for the account
   ACCOUNT_EVENT_FLAG_BALANCE             =  16,            // The balance exceeds the specified change value +/-
   ACCOUNT_EVENT_FLAG_EQUITY              =  32,            // The equity exceeds the specified change value +/-
   ACCOUNT_EVENT_FLAG_PROFIT              =  64,            // The profit exceeds the specified change value +/-
   ACCOUNT_EVENT_FLAG_CREDIT              =  128,           // Changing the credit in a deposit currency
   ACCOUNT_EVENT_FLAG_MARGIN              =  256,           // The reserved margin on an account in the deposit currency exceeds the specified change value +/-
   ACCOUNT_EVENT_FLAG_MARGIN_FREE         =  512,           // The free funds available for opening a position in a deposit currency exceed the specified change value +/-
   ACCOUNT_EVENT_FLAG_MARGIN_LEVEL        =  1024,          // The margin level on an account in % exceeds the specified change value +/-
   ACCOUNT_EVENT_FLAG_MARGIN_INITIAL      =  2048,          // The funds reserved on an account to ensure a guarantee amount for all pending orders exceed the specified change value +/-
   ACCOUNT_EVENT_FLAG_MARGIN_MAINTENANCE  =  4096,          // The funds reserved on an account to ensure a minimum amount for all open positions exceed the specified change value +/-
   ACCOUNT_EVENT_FLAG_MARGIN_SO_CALL      =  8192,          // Changing the Margin Call level
   ACCOUNT_EVENT_FLAG_MARGIN_SO_SO        =  16384,         // Changing the Stop Out level
   ACCOUNT_EVENT_FLAG_ASSETS              =  32768,         // The current assets on an account exceed the specified change value +/-
   ACCOUNT_EVENT_FLAG_LIABILITIES         =  65536,         // The current liabilities on an account exceed the specified change value +/-
   ACCOUNT_EVENT_FLAG_COMISSION_BLOCKED   =  131072,        // The current sum of blocked commissions on an account exceeds the specified change value +/-
  };
//+------------------------------------------------------------------+
//| List of possible account events                                  |
//+------------------------------------------------------------------+
enum ENUM_ACCOUNT_EVENT
  {
   ACCOUNT_EVENT_NO_EVENT = TRADE_EVENTS_NEXT_CODE,         // No event
   ACCOUNT_EVENT_LEVERAGE_INC,                              // Increasing the leverage
   ACCOUNT_EVENT_LEVERAGE_DEC,                              // Decreasing the leverage
   ACCOUNT_EVENT_LIMIT_ORDERS_INC,                          // Increasing the maximum allowed number of active pending orders
   ACCOUNT_EVENT_LIMIT_ORDERS_DEC,                          // Decreasing the maximum allowed number of active pending orders
   ACCOUNT_EVENT_TRADE_ALLOWED_ON,                          // Enabling trading for the account
   ACCOUNT_EVENT_TRADE_ALLOWED_OFF,                         // Disabling trading for the account
   ACCOUNT_EVENT_TRADE_EXPERT_ON,                           // Enabling auto trading for the account
   ACCOUNT_EVENT_TRADE_EXPERT_OFF,                          // Disabling auto trading for the account
   ACCOUNT_EVENT_BALANCE_INC,                               // The balance exceeds the specified value
   ACCOUNT_EVENT_BALANCE_DEC,                               // The balance falls below the specified value
   ACCOUNT_EVENT_EQUITY_INC,                                // The equity exceeds the specified value
   ACCOUNT_EVENT_EQUITY_DEC,                                // The equity falls below the specified value
   ACCOUNT_EVENT_PROFIT_INC,                                // The profit exceeds the specified value
   ACCOUNT_EVENT_PROFIT_DEC,                                // The profit falls below the specified value
   ACCOUNT_EVENT_CREDIT_INC,                                // The credit exceeds the specified value
   ACCOUNT_EVENT_CREDIT_DEC,                                // The credit falls below the specified value
   ACCOUNT_EVENT_MARGIN_INC,                                // Increasing the reserved margin on an account in the deposit currency
   ACCOUNT_EVENT_MARGIN_DEC,                                // Decreasing the reserved margin on an account in the deposit currency
   ACCOUNT_EVENT_MARGIN_FREE_INC,                           // Increasing the free funds available for opening a position in a deposit currency
   ACCOUNT_EVENT_MARGIN_FREE_DEC,                           // Decreasing the free funds available for opening a position in a deposit currency
   ACCOUNT_EVENT_MARGIN_LEVEL_INC,                          // Increasing the margin level on an account in %
   ACCOUNT_EVENT_MARGIN_LEVEL_DEC,                          // Decreasing the margin level on an account in %
   ACCOUNT_EVENT_MARGIN_INITIAL_INC,                        // Increasing the funds reserved on an account to ensure a guarantee amount for all pending orders
   ACCOUNT_EVENT_MARGIN_INITIAL_DEC,                        // Decreasing the funds reserved on an account to ensure a guarantee amount for all pending orders
   ACCOUNT_EVENT_MARGIN_MAINTENANCE_INC,                    // Increasing the funds reserved on an account to ensure a minimum amount for all open positions
   ACCOUNT_EVENT_MARGIN_MAINTENANCE_DEC,                    // Decreasing the funds reserved on an account to ensure a minimum amount for all open positions
   ACCOUNT_EVENT_MARGIN_SO_CALL_INC,                        // Increasing the Margin Call level
   ACCOUNT_EVENT_MARGIN_SO_CALL_DEC,                        // Decreasing the Margin Call level
   ACCOUNT_EVENT_MARGIN_SO_SO_INC,                          // Increasing the Stop Out level
   ACCOUNT_EVENT_MARGIN_SO_SO_DEC,                          // Decreasing the Stop Out level
   ACCOUNT_EVENT_ASSETS_INC,                                // Increasing the current asset size on the account
   ACCOUNT_EVENT_ASSETS_DEC,                                // Decreasing the current asset size on the account
   ACCOUNT_EVENT_LIABILITIES_INC,                           // Increasing the current liabilities on the account
   ACCOUNT_EVENT_LIABILITIES_DEC,                           // Decreasing the current liabilities on the account
   ACCOUNT_EVENT_COMISSION_BLOCKED_INC,                     // Increasing the current sum of blocked commissions on an account
   ACCOUNT_EVENT_COMISSION_BLOCKED_DEC,                     // Decreasing the current sum of blocked commissions on an account<
  };
#define ACCOUNT_EVENTS_NEXT_CODE       (ACCOUNT_EVENT_COMISSION_BLOCKED_DEC+1)   // The code of the next event after the last account event code
//+------------------------------------------------------------------+
//| Account integer properties                                       |
//+------------------------------------------------------------------+
enum ENUM_ACCOUNT_PROP_INTEGER
  {
   ACCOUNT_PROP_LOGIN,                                      // Account number
   ACCOUNT_PROP_TRADE_MODE,                                 // Trading account type
   ACCOUNT_PROP_LEVERAGE,                                   // Provided leverage
   ACCOUNT_PROP_LIMIT_ORDERS,                               // Maximum allowed number of active pending orders
   ACCOUNT_PROP_MARGIN_SO_MODE,                             // Mode of setting the minimum available margin level
   ACCOUNT_PROP_TRADE_ALLOWED,                              // Permission to trade for the current account from the server side
   ACCOUNT_PROP_TRADE_EXPERT,                               // Permission to trade for an EA from the server side
   ACCOUNT_PROP_MARGIN_MODE,                                // Margin calculation mode
   ACCOUNT_PROP_CURRENCY_DIGITS,                            // Number of digits for an account currency necessary for accurate display of trading results
   ACCOUNT_PROP_SERVER_TYPE                                 // Trade server type (MetaTrader 5, MetaTrader 4)
  };
#define ACCOUNT_PROP_INTEGER_TOTAL    (10)                  // Total number of account's integer properties
#define ACCOUNT_PROP_INTEGER_SKIP     (0)                   // Number of account's integer properties not used in sorting
//+------------------------------------------------------------------+
//| Account real properties                                          |
//+------------------------------------------------------------------+
enum ENUM_ACCOUNT_PROP_DOUBLE
  {
   ACCOUNT_PROP_BALANCE = ACCOUNT_PROP_INTEGER_TOTAL,       // Account balance in a deposit currency
   ACCOUNT_PROP_CREDIT,                                     // Credit in a deposit currency
   ACCOUNT_PROP_PROFIT,                                     // Current profit on an account in the account currency
   ACCOUNT_PROP_EQUITY,                                     // Equity on an account in the deposit currency
   ACCOUNT_PROP_MARGIN,                                     // Reserved margin on an account in a deposit currency
   ACCOUNT_PROP_MARGIN_FREE,                                // Free funds available for opening a position in a deposit currency
   ACCOUNT_PROP_MARGIN_LEVEL,                               // Margin level on an account in %
   ACCOUNT_PROP_MARGIN_SO_CALL,                             // Margin Call level
   ACCOUNT_PROP_MARGIN_SO_SO,                               // Stop Out level
   ACCOUNT_PROP_MARGIN_INITIAL,                             // Funds reserved on an account to ensure a guarantee amount for all pending orders 
   ACCOUNT_PROP_MARGIN_MAINTENANCE,                         // Funds reserved on an account to ensure a minimum amount for all open positions
   ACCOUNT_PROP_ASSETS,                                     // Current assets on an account
   ACCOUNT_PROP_LIABILITIES,                                // Current liabilities on an account
   ACCOUNT_PROP_COMMISSION_BLOCKED                          // Current sum of blocked commissions on an account
  };
#define ACCOUNT_PROP_DOUBLE_TOTAL     (14)                  // Total number of account's real properties
#define ACCOUNT_PROP_DOUBLE_SKIP      (0)                   // Number of account's real properties not used in sorting
//+------------------------------------------------------------------+
//| Account string properties                                        |
//+------------------------------------------------------------------+
enum ENUM_ACCOUNT_PROP_STRING
  {
   ACCOUNT_PROP_NAME = (ACCOUNT_PROP_INTEGER_TOTAL+ACCOUNT_PROP_DOUBLE_TOTAL), // Client name
   ACCOUNT_PROP_SERVER,                                     // Trade server name
   ACCOUNT_PROP_CURRENCY,                                   // Deposit currency
   ACCOUNT_PROP_COMPANY                                     // Name of a company serving an account
  };
#define ACCOUNT_PROP_STRING_TOTAL     (4)                   // Total number of account's string properties
#define ACCOUNT_PROP_STRING_SKIP      (0)                   // Number of account string properties not used in sorting
//+------------------------------------------------------------------+
//| Possible account sorting criteria                                |
//+------------------------------------------------------------------+
#define FIRST_ACC_DBL_PROP            (ACCOUNT_PROP_INTEGER_TOTAL-ACCOUNT_PROP_INTEGER_SKIP)
#define FIRST_ACC_STR_PROP            (ACCOUNT_PROP_INTEGER_TOTAL-ACCOUNT_PROP_INTEGER_SKIP+ACCOUNT_PROP_DOUBLE_TOTAL-ACCOUNT_PROP_DOUBLE_SKIP)
enum ENUM_SORT_ACCOUNT_MODE
  {
   SORT_BY_ACCOUNT_LOGIN               =  0,                      // Sort by account number
   SORT_BY_ACCOUNT_TRADE_MODE          =  1,                      // Sort by trading account type
   SORT_BY_ACCOUNT_LEVERAGE            =  2,                      // Sort by leverage
   SORT_BY_ACCOUNT_LIMIT_ORDERS        =  3,                      // Sort by maximum acceptable number of existing pending orders
   SORT_BY_ACCOUNT_MARGIN_SO_MODE      =  4,                      // Sort by mode for setting the minimum acceptable margin level
   SORT_BY_ACCOUNT_TRADE_ALLOWED       =  5,                      // Sort by permission to trade for the current account
   SORT_BY_ACCOUNT_TRADE_EXPERT        =  6,                      // Sort by permission to trade for an EA
   SORT_BY_ACCOUNT_MARGIN_MODE         =  7,                      // Sort by margin calculation mode
   SORT_BY_ACCOUNT_CURRENCY_DIGITS     =  8,                      // Sort by number of digits for an account currency
   SORT_BY_ACCOUNT_SERVER_TYPE         =  9,                      // Sort by trade server type (MetaTrader 5, MetaTrader 4)
    
   SORT_BY_ACCOUNT_BALANCE             =  FIRST_ACC_DBL_PROP,     // Sort by an account balance in the deposit currency
   SORT_BY_ACCOUNT_CREDIT              =  FIRST_ACC_DBL_PROP+1,   // Sort by credit in a deposit currency
   SORT_BY_ACCOUNT_PROFIT              =  FIRST_ACC_DBL_PROP+2,   // Sort by the current profit on an account in the deposit currency
   SORT_BY_ACCOUNT_EQUITY              =  FIRST_ACC_DBL_PROP+3,   // Sort by an account equity in the deposit currency
   SORT_BY_ACCOUNT_MARGIN              =  FIRST_ACC_DBL_PROP+4,   // Sort by an account reserved margin in the deposit currency
   SORT_BY_ACCOUNT_MARGIN_FREE         =  FIRST_ACC_DBL_PROP+5,   // Sort by account free funds available for opening a position in the deposit currency
   SORT_BY_ACCOUNT_MARGIN_LEVEL        =  FIRST_ACC_DBL_PROP+6,   // Sort by account margin level in %
   SORT_BY_ACCOUNT_MARGIN_SO_CALL      =  FIRST_ACC_DBL_PROP+7,   // Sort by margin level requiring depositing funds to an account (Margin Call)
   SORT_BY_ACCOUNT_MARGIN_SO_SO        =  FIRST_ACC_DBL_PROP+8,   // Sort by margin level, at which the most loss-making position is closed (Stop Out)
   SORT_BY_ACCOUNT_MARGIN_INITIAL      =  FIRST_ACC_DBL_PROP+9,   // Sort by funds reserved on an account to ensure a guarantee amount for all pending orders 
   SORT_BY_ACCOUNT_MARGIN_MAINTENANCE  =  FIRST_ACC_DBL_PROP+10,  // Sort by funds reserved on an account to ensure a minimum amount for all open positions
   SORT_BY_ACCOUNT_ASSETS              =  FIRST_ACC_DBL_PROP+11,  // Sort by the amount of the current assets on an account
   SORT_BY_ACCOUNT_LIABILITIES         =  FIRST_ACC_DBL_PROP+12,  // Sort by the current liabilities on an account
   SORT_BY_ACCOUNT_COMMISSION_BLOCKED  =  FIRST_ACC_DBL_PROP+13,  // Sort by the current amount of blocked commissions on an account

   SORT_BY_ACCOUNT_NAME                =  FIRST_ACC_STR_PROP,     // Sort by a client name
   SORT_BY_ACCOUNT_SERVER              =  FIRST_ACC_STR_PROP+1,   // Sort by a trade server name
   SORT_BY_ACCOUNT_CURRENCY            =  FIRST_ACC_STR_PROP+2,   // Sort by a deposit currency
   SORT_BY_ACCOUNT_COMPANY             =  FIRST_ACC_STR_PROP+3    // Sort by a name of a company serving an account
  };
//+------------------------------------------------------------------+

Dado que es posible que cambien de golpe varias propiedades de la cuenta, para no perder ni uno de los cambios en sus propiedades, vamos a trabajar, como siempre, con un conjunto de banderas de evento que se registrarán en la variable de código de evento, y a continuación, comprobaremos ya la presencia de una bandera concreta dentro de esta variable; luego, basándonos en las banderas disponibles en el código de evento, determinaremos qué ha sucedido en las propiedades de la cuenta. Guardaremos todos los eventos encontrados en una matriz, a la cual tendremos acceso en la clase CEngine y, por consiguiente, en el programa.

Como se podía ver, en la lista de propiedades de tipo entero de la cuenta se había añadido una propiedad más: el tipo de servidor comercial. Por consiguiente, se cambió el número total de propiedades de tipo entero de 9 a 10.
En esta propiedad de la cuenta guardaremos la pertenencia de una cuenta comercial a MetaTrader 5 o MetaTrader 4. El motivo de la adición de esta propiedad ha sido que en la lista con todas las cuentas a las que se ha conectado alguna vez un programa basado en la biblioteca, entran todas las cuentas comerciales, tanto para MetaTrader 5, como para MetaTrader 4, si iniciamos un programa basado en la biblioteca en ambos terminales. Así, para que podamos distinguir los tipos de servidores comerciales, hemos introducido esta propiedad, que estará presente en la descripción de la cuenta mostrada en el diario con el método PrintShort() de la clase CAccount. Asimismo, existirá la posibilidad de filtrar los objetos de cuenta según su pertenencia a las plataformas.

Vamos a pasar al archivo Acount.mqh, donde añadiremos la nueva propiedad en la sección privada de la estructura de datos de la cuenta y registraremos el tamaño exacto de las matrices para guardar las propiedades de tipo string de la cuenta:

//+------------------------------------------------------------------+
//| Account class                                                    |
//+------------------------------------------------------------------+
class CAccount : public CObject
  {
private:
   struct SData
     {
      //--- Account integer properties
      long           login;                        // ACCOUNT_LOGIN (Account number)
      int            trade_mode;                   // ACCOUNT_TRADE_MODE (Trading account type)
      long           leverage;                     // ACCOUNT_LEVERAGE (Leverage)
      int            limit_orders;                 // ACCOUNT_LIMIT_ORDERS (Maximum allowed number of active pending orders)
      int            margin_so_mode;               // ACCOUNT_MARGIN_SO_MODE (Mode of setting the minimum available margin level)
      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)
      int            margin_mode;                  // ACCOUNT_MARGIN_MODE (Margin calculation mode)
      int            currency_digits;              // ACCOUNT_CURRENCY_DIGITS (Number of digits for an account currency)
      int            server_type;                  // Trade server type (MetaTrader 5, MetaTrader 4)
      //--- Account real properties
      double         balance;                      // ACCOUNT_BALANCE (Account balance in the deposit currency)
      double         credit;                       // ACCOUNT_CREDIT (Credit in the deposit currency)
      double         profit;                       // ACCOUNT_PROFIT (Current profit on an account in the deposit currency)
      double         equity;                       // ACCOUNT_EQUITY (Equity on an account in the deposit currency)
      double         margin;                       // ACCOUNT_MARGIN (Reserved margin on an account in the deposit currency)
      double         margin_free;                  // ACCOUNT_MARGIN_FREE (Free funds available for opening a position in the deposit currency)
      double         margin_level;                 // ACCOUNT_MARGIN_LEVEL (Margin level on an account in %)
      double         margin_so_call;               // ACCOUNT_MARGIN_SO_CALL (Margin Call level)
      double         margin_so_so;                 // ACCOUNT_MARGIN_SO_SO (Stop Out level)
      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)
      //--- Account string properties
      uchar          name[128];                    // ACCOUNT_NAME (Client name)
      uchar          server[64];                   // ACCOUNT_SERVER (Trade server name)
      uchar          currency[32];                 // ACCOUNT_CURRENCY (Deposit currency)
      uchar          company[128];                 // ACCOUNT_COMPANY (Name of a company serving an account)
     };
   SData             m_struct_obj;                                      // Account object structure
   uchar             m_uchar_array[];                                   // uchar array of the account object structure

Añadimos a la sección pública de la clase otro método de acceso simplificado a las propiedades del objeto de cuenta que retorna el tipo de servidor comercial:

//+------------------------------------------------------------------+
//| Methods of a simplified access to the account object properties  |
//+------------------------------------------------------------------+
//--- Return the account's integer propertie
   ENUM_ACCOUNT_TRADE_MODE    TradeMode(void)                     const { return (ENUM_ACCOUNT_TRADE_MODE)this.GetProperty(ACCOUNT_PROP_TRADE_MODE);       }
   ENUM_ACCOUNT_STOPOUT_MODE  MarginSOMode(void)                  const { return (ENUM_ACCOUNT_STOPOUT_MODE)this.GetProperty(ACCOUNT_PROP_MARGIN_SO_MODE); }
   ENUM_ACCOUNT_MARGIN_MODE   MarginMode(void)                    const { return (ENUM_ACCOUNT_MARGIN_MODE)this.GetProperty(ACCOUNT_PROP_MARGIN_MODE);     }
   long              Login(void)                                  const { returnthis.GetProperty(ACCOUNT_PROP_LOGIN);                                      }
   long              Leverage(void)                               const { returnthis.GetProperty(ACCOUNT_PROP_LEVERAGE);                                   }
   long              LimitOrders(void)                            const { returnthis.GetProperty(ACCOUNT_PROP_LIMIT_ORDERS);                               }
   long              TradeAllowed(void)                           const { returnthis.GetProperty(ACCOUNT_PROP_TRADE_ALLOWED);                              }
   long              TradeExpert(void)                            const { returnthis.GetProperty(ACCOUNT_PROP_TRADE_EXPERT);                               }
   long              CurrencyDigits(void)                         const { returnthis.GetProperty(ACCOUNT_PROP_CURRENCY_DIGITS);                            }
   long              ServerType(void)                             const { returnthis.GetProperty(ACCOUNT_PROP_SERVER_TYPE);                                }

En los métodos de descripción de las propiedades de la cuenta, añadimos otro método que retorna la descripción del tipo de servidor comercial:

//+------------------------------------------------------------------+
//| Descriptions of the account object properties                    |
//+------------------------------------------------------------------+
//--- Return the description of the account's (1) integer, (2) real and (3) string properties
   string            GetPropertyDescription(ENUM_ACCOUNT_PROP_INTEGER property);
   string            GetPropertyDescription(ENUM_ACCOUNT_PROP_DOUBLE property);
   string            GetPropertyDescription(ENUM_ACCOUNT_PROP_STRING property);
//--- Return the trading account type name (demo, contest, real)
   string            TradeModeDescription(void)    const;
//--- Return the trade server type name (MetaTrader 5, MetaTrader 4)
   string            ServerTypeDescription(void)    const;
//--- Return the description of the mode for setting the minimum available margin level
   string            MarginSOModeDescription(void) const;
//--- Return the description of the margin calculation mode
   string            MarginModeDescription(void)   const;
//--- Display the description of the account properties in the journal (full_prop=true - all properties, false - supported ones only)
   void              Print(constbool full_prop=false);
//--- Display a short account description in the journal
   void              PrintShort(void);

En el constructor de la clase, rellenamos la propiedad que guarda el tipo de servidor comercial:

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

Aquí, solo tenemos que leer la denominación del terminal, y si se trata de MetaTrader 5, registramos el valor 5, de lo contrario, registramos 4.

Añadimos al método de creación de la estructura del objeto ObjectToStruct() el rellenado de una nueva propiedad:

//+------------------------------------------------------------------+
//| Create the account object structure                              |
//+------------------------------------------------------------------+
bool CAccount::ObjectToStruct(void)
  {
//--- Save the integer properties
   this.m_struct_obj.login=this.Login();
   this.m_struct_obj.trade_mode=this.TradeMode();
   this.m_struct_obj.leverage=this.Leverage();
   this.m_struct_obj.limit_orders=(int)this.LimitOrders();
   this.m_struct_obj.margin_so_mode=this.MarginSOMode();
   this.m_struct_obj.trade_allowed=this.TradeAllowed();
   this.m_struct_obj.trade_expert=this.TradeExpert();
   this.m_struct_obj.margin_mode=this.MarginMode();
   this.m_struct_obj.currency_digits=(int)this.CurrencyDigits();
   this.m_struct_obj.server_type=(int)this.ServerType();
//--- Save the real properties
   this.m_struct_obj.balance=this.Balance();
   this.m_struct_obj.credit=this.Credit();
   this.m_struct_obj.profit=this.Profit();
   this.m_struct_obj.equity=this.Equity();
   this.m_struct_obj.margin=this.Margin();
   this.m_struct_obj.margin_free=this.MarginFree();
   this.m_struct_obj.margin_level=this.MarginLevel();
   this.m_struct_obj.margin_so_call=this.MarginSOCall();
   this.m_struct_obj.margin_so_so=this.MarginSOSO();
   this.m_struct_obj.margin_initial=this.MarginInitial();
   this.m_struct_obj.margin_maintenance=this.MarginMaintenance();
   this.m_struct_obj.assets=this.Assets();
   this.m_struct_obj.liabilities=this.Liabilities();
   this.m_struct_obj.comission_blocked=this.ComissionBlocked();
//--- Save the string properties
   ::StringToCharArray(this.Name(),this.m_struct_obj.name);
   ::StringToCharArray(this.Server(),this.m_struct_obj.server);
   ::StringToCharArray(this.Currency(),this.m_struct_obj.currency);
   ::StringToCharArray(this.Company(),this.m_struct_obj.company);
   //--- Save the structure to the uchar array
   ::ResetLastError();
   if(!::StructToCharArray(this.m_struct_obj,this.m_uchar_array))
     {
      ::Print(DFUN,TextByLanguage("Не удалось сохранить структуру объекта в uchar-массив, ошибка ","Failed to save object structure to uchar array, error "),(string)::GetLastError());
      returnfalse;
     }
   returntrue;
  }
//+------------------------------------------------------------------+

y añadimos al método de creación del objeto de la estructura StructToObject() la obtención de una propiedad de la estructura:

//+------------------------------------------------------------------+
//| Create the account object from the structure                     |
//+------------------------------------------------------------------+
void CAccount::StructToObject(void)
  {
//--- Save integer properties
   this.m_long_prop[ACCOUNT_PROP_LOGIN]                              = this.m_struct_obj.login;
   this.m_long_prop[ACCOUNT_PROP_TRADE_MODE]                         = this.m_struct_obj.trade_mode;
   this.m_long_prop[ACCOUNT_PROP_LEVERAGE]                           = this.m_struct_obj.leverage;
   this.m_long_prop[ACCOUNT_PROP_LIMIT_ORDERS]                       = this.m_struct_obj.limit_orders;
   this.m_long_prop[ACCOUNT_PROP_MARGIN_SO_MODE]                     = this.m_struct_obj.margin_so_mode;
   this.m_long_prop[ACCOUNT_PROP_TRADE_ALLOWED]                      = this.m_struct_obj.trade_allowed;
   this.m_long_prop[ACCOUNT_PROP_TRADE_EXPERT]                       = this.m_struct_obj.trade_expert;
   this.m_long_prop[ACCOUNT_PROP_MARGIN_MODE]                        = this.m_struct_obj.margin_mode;
   this.m_long_prop[ACCOUNT_PROP_CURRENCY_DIGITS]                    = this.m_struct_obj.currency_digits;
   this.m_long_prop[ACCOUNT_PROP_SERVER_TYPE]                        = this.m_struct_obj.server_type;
//--- Save real properties
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_BALANCE)]          = this.m_struct_obj.balance;
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_CREDIT)]           = this.m_struct_obj.credit;
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_PROFIT)]           = this.m_struct_obj.profit;
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_EQUITY)]           = this.m_struct_obj.equity;
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_MARGIN)]           = this.m_struct_obj.margin;
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_MARGIN_FREE)]      = this.m_struct_obj.margin_free;
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_MARGIN_LEVEL)]     = this.m_struct_obj.margin_level;
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_MARGIN_SO_CALL)]   = this.m_struct_obj.margin_so_call;
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_MARGIN_SO_SO)]     = this.m_struct_obj.margin_so_so;
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_MARGIN_INITIAL)]   = this.m_struct_obj.margin_initial;
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_MARGIN_MAINTENANCE)]=this.m_struct_obj.margin_maintenance;
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_ASSETS)]           = this.m_struct_obj.assets;
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_LIABILITIES)]      = this.m_struct_obj.liabilities;
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_COMMISSION_BLOCKED)]=this.m_struct_obj.comission_blocked;
//--- Save string properties
   this.m_string_prop[this.IndexProp(ACCOUNT_PROP_NAME)]             = ::CharArrayToString(this.m_struct_obj.name);
   this.m_string_prop[this.IndexProp(ACCOUNT_PROP_SERVER)]           = ::CharArrayToString(this.m_struct_obj.server);
   this.m_string_prop[this.IndexProp(ACCOUNT_PROP_CURRENCY)]         = ::CharArrayToString(this.m_struct_obj.currency);
   this.m_string_prop[this.IndexProp(ACCOUNT_PROP_COMPANY)]          = ::CharArrayToString(this.m_struct_obj.company);
  }
//+------------------------------------------------------------------+

Ahora, en el método de muestra de la descripción breve de las propiedades de la cuenta, añadimos la muestra del tipo de servidor comercial:

//+------------------------------------------------------------------+
//| 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=TextByLanguage("Счёт ","Account ")+(string)this.Login()+": "+this.Name()+" ("+this.Company()+" ";
   string values=::DoubleToString(this.Balance(),(int)this.CurrencyDigits())+" "+this.Currency()+", 1:"+(string)+this.Leverage()+mode+", "+this.TradeModeDescription()+" "+this.ServerTypeDescription()+")";
   ::Print(names,values);
  }
//+------------------------------------------------------------------+

Añadimos la descripción de una nueva propiedad al método que retorna la descripción de las propiedades de tipo entero de la cuenta:

//+------------------------------------------------------------------+
//| Display a description of an account integer property             |
//+------------------------------------------------------------------+
string CAccount::GetPropertyDescription(ENUM_ACCOUNT_PROP_INTEGER property)
  {
   return
     (
      property==ACCOUNT_PROP_LOGIN           ?  TextByLanguage("Номер счёта","Account number")+": "+(string)this.GetProperty(property)                                                    :
      property==ACCOUNT_PROP_TRADE_MODE      ?  TextByLanguage("Тип торгового счета","Account trade mode")+": "+this.TradeModeDescription()                                               :
      property==ACCOUNT_PROP_LEVERAGE        ?  TextByLanguage("Размер предоставленного плеча","Account leverage")+": "+(string)this.GetProperty(property) :
      property==ACCOUNT_PROP_LIMIT_ORDERS    ?  TextByLanguage("Максимально допустимое количество действующих отложенных ордеров","Maximum allowed number of active pending orders")+": "+
                                                   (string)this.GetProperty(property)                                                                                                     :
      property==ACCOUNT_PROP_MARGIN_SO_MODE  ?  TextByLanguage("Режим задания минимально допустимого уровня залоговых средств","Mode for setting the minimal allowed margin")+": "+
                                                   this.MarginSOModeDescription()                                                          :
      property==ACCOUNT_PROP_TRADE_ALLOWED   ?  TextByLanguage("Разрешенность торговли для текущего счета","Allowed trade for the current account")+": "+
                                                   (this.GetProperty(property) ? TextByLanguage("Да","Yes") : TextByLanguage("Нет","No"))                                                 :
      property==ACCOUNT_PROP_TRADE_EXPERT    ?  TextByLanguage("Разрешенность торговли для эксперта","Allowed trade for an Expert Advisor")+": "+
                                                   (this.GetProperty(property) ? TextByLanguage("Да","Yes") : TextByLanguage("Нет","No"))                                                 :
      property==ACCOUNT_PROP_MARGIN_MODE     ?  TextByLanguage("Режим расчета маржи","Margin calculation mode")+": "+
                                                   this.MarginModeDescription()                                                         :
      property==ACCOUNT_PROP_CURRENCY_DIGITS ?  TextByLanguage("Количество знаков после запятой для валюты счета","Number of decimal places in account currency")+": "+
                                                   (string)this.GetProperty(property)                                                                                                     :
      property==ACCOUNT_PROP_SERVER_TYPE     ?  TextByLanguage("Тип торгового сервера","Type of trading server")+": "+
                                                   (string)this.GetProperty(property)                                                                                                     :
      ""
     );
  }
//+------------------------------------------------------------------+

Implementamos el método que retorna la denominación del tipo de servidor comercial:

//+------------------------------------------------------------------+
//| Return the trading server type name                              |
//+------------------------------------------------------------------+
string CAccount::ServerTypeDescription(void) const
  {
   return(this.ServerType()==5 ? "MetaTrader5" : "MetaTrader4");
  }
//+------------------------------------------------------------------+

Ya hemos terminado las mejoras en la clase CAccount.

Ahora, vamos a introducir los cambios necesarios en la clase de colección de cuentas, dado que hemos decidido implementar el seguimiento de eventos desde la clase CAccountCollection. Registraremos todos los cambios ocurridos simultáneamente en una matriz int. Para ello, usaremos la clase preparada de la matriz dinámica de variables del tipo int o uint de la biblioteca estándar CArrayInt.

Para usarla, incluiremos el archivo de la clase en el archivo de la biblioteca AccountsCollection.mqh:

//+------------------------------------------------------------------+
//|                                           AccountsCollection.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\ArrayInt.mqh>
#include "ListObj.mqh"
#include "..\Services\Select.mqh"
#include "..\Objects\Accounts\Account.mqh"
//+------------------------------------------------------------------+
//| Account collection                                               |
//+------------------------------------------------------------------+
class CAccountsCollection : public CListObj
  {

Desviándonos un tanto del tema, vamos a decidir qué es lo que queremos obtener. Queremos conocer los momentos en los que han cambiado ciertas propiedades de la cuenta comercial. Alguna propiedad puede ser activada o desactivada, por ejemplo, permitir/prohibir el comercio en general en la cuenta, o solo permitir/prohibir el comercio con asesores. Debemos estar obligatoriamente al tanto del cambio de esta propiedad. Lo mismo sucede con el nivel de aparición de MarginCall o StopOut, su cambio puede influir en el éxito a la hora de soportar una reducción significativa, etcétera. Asimismo, necesitamos monitorear el crecimiento y la reducción de los fondos y el saldo para tomar esta u otra decisión en el asesor.

Si, por ejemplo, existe un cierto número de posiciones abiertas, y nosotros queremos cerrar alguna de ellas al superar una magnitud de fondos positiva o negativa, necesitaremos primero establecer un cierto umbral cuya superación generará un evento, y ya después tomar una decisión, dependiendo de la cual, se tratará de un aumento o una disminución por encima de la magnitud establecida. Esta lógica es aplicable al beneficio actual en la cuenta, la cuantía del saldo, el margen libre o la carga sobre el depósito.

De esta forma, para algunas propiedades deberemos tener nuestros propios valores umbral de aumento y disminución, así como el valor actual de la propiedad, mientras que para otros, deberemos tener solo un valor que puede o bien estar activado/desactivado, o bien modificarse/no modificarse.

Añadimos a la sección privada de la clase todas las variables de miembro de clase necesarias para guardar los valores de aumento/disminución monitoreados y sus valores actuales, eliminamos el método SavePrevValues(), innecesario ahora, y añadimos los métodos de inicialización de datos monitoreados en la cuenta e inicialización de datos controlados en la cuenta, el método que comprueba los cambios en la cuenta y retorna el código de cambio, el método que establece el tipo de evento y rellena la lista de eventos, y finalmente, el método que retorna la presencia de una bandera en el evento de la cuenta:

//--- Save the current data status values of the current account as previous ones
   void              SavePrevValues(void)
//+------------------------------------------------------------------+
//| Account collection                                               |
//+------------------------------------------------------------------+
class CAccountsCollection : public CListObj
  {
private:
   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 (Margin Call)
      double         margin_so_so;           // ACCOUNT_MARGIN_SO_SO (Stop Out)
      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)
     };
   MqlDataAccount    m_struct_curr_account;              // Account current data
   MqlDataAccount    m_struct_prev_account;              // Account previous data
   
   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 the folder storing the account objects
   int               m_index_current;                    // Index of an account object featuring the current account data
//--- Tracking account changes
   bool              m_is_account_event;                 // Event flag in the account data
   int               m_change_code;                      // Account change code
   //--- Leverage
   long              m_changed_leverage_value;           // Leverage change value
   bool              m_is_change_leverage_inc;           // Leverage increase flag
   bool              m_is_change_leverage_dec;           // Leverage decrease flag
   //--- Number of active pending orders
   int               m_changed_limit_orders_value;       // Change value of the maximum allowed number of active pending orders
   bool              m_is_change_limit_orders_inc;       // Increase flag of the maximum allowed number of active pending orders
   bool              m_is_change_limit_orders_dec;       // Decrease flag of the maximum allowed number of active pending orders
   //--- Trading on an account
   bool              m_is_change_trade_allowed_on;       // The flag allowing to trade for the current account from the server side
   bool              m_is_change_trade_allowed_off;      // The flag prohibiting trading for the current account from the server side
   //--- Auto trading on an account
   bool              m_is_change_trade_expert_on;        // The flag allowing to trade for an EA from the server side
   bool              m_is_change_trade_expert_off;       //The flag prohibiting trading for an EA from the server side
   //--- Balance
   double            m_control_balance_inc;              // Tracked balance growth value
   double            m_control_balance_dec;              // Tracked balance decrease value
   double            m_changed_balance_value;            // Balance change value
   bool              m_is_change_balance_inc;            // The flag of the balance change exceeding the growth value
   bool              m_is_change_balance_dec;            // The flag of the balance change exceeding the decrease value
   //--- Credit
   double            m_changed_credit_value;             // Credit change value
   bool              m_is_change_credit_inc;             // Credit increase flag
   bool              m_is_change_credit_dec;             // Credit decrease flag
   //--- Profit
   double            m_control_profit_inc;               // Tracked profit growth value
   double            m_control_profit_dec;               // Tracked profit decrease value
   double            m_changed_profit_value;             // Profit change value
   bool              m_is_change_profit_inc;             // The flag of the profit change exceeding the growth value
   bool              m_is_change_profit_dec;             // The flag of the profit change exceeding the decrease value
   //--- Funds (equity)
   double            m_control_equity_inc;               // Tracked funds growth value
   double            m_control_equity_dec;               // Tracked funds decrease value
   double            m_changed_equity_value;             // Funds change value
   bool              m_is_change_equity_inc;             // The flag of the funds change exceeding the growth value
   bool              m_is_change_equity_dec;             // The flag of the funds change exceeding the decrease value
   //--- Margin
   double            m_control_margin_inc;               // Tracked margin growth value
   double            m_control_margin_dec;               // Tracked margin decrease value
   double            m_changed_margin_value;             // Margin change value
   bool              m_is_change_margin_inc;             // The flag of the margin change exceeding the growth value
   bool              m_is_change_margin_dec;             // The flag of the margin change exceeding the decrease value
   //--- Free margin
   double            m_control_margin_free_inc;          // Tracked free margin growth value
   double            m_control_margin_free_dec;          // Tracked free margin decrease value<
   double            m_changed_margin_free_value;        // Free margin change value
   bool              m_is_change_margin_free_inc;        // The flag of the free margin change exceeding the growth value
   bool              m_is_change_margin_free_dec;        // The flag of the free margin change exceeding the decrease value
   //--- Margin level
   double            m_control_margin_level_inc;         // Tracked margin level growth value
   double            m_control_margin_level_dec;         // Tracked margin level decrease value
   double            m_changed_margin_level_value;       // Margin level change value
   bool              m_is_change_margin_level_inc;       // The flag of the margin level change exceeding the growth value
   bool              m_is_change_margin_level_dec;       // The flag of the margin level change exceeding the decrease value
   //--- Margin Call
   double            m_changed_margin_so_call_value;     // Margin Call level change value
   bool              m_is_change_margin_so_call_inc;     // Margin Call level increase value
   bool              m_is_change_margin_so_call_dec;     // Margin Call level decrease value
   //--- MarginStopOut
   double            m_changed_margin_so_so_value;       // Margin StopOut level change value
   bool              m_is_change_margin_so_so_inc;       // Margin StopOut level increase flag
   bool              m_is_change_margin_so_so_dec;       // Margin StopOut level decrease flag
   //--- Guarantee sum for pending orders
   double            m_control_margin_initial_inc;       // Tracked growth value of the reserved funds for providing the guarantee sum for pending orders
   double            m_control_margin_initial_dec;       // Tracked decrease value of the reserved funds for providing the guarantee sum for pending orders
   double            m_changed_margin_initial_value;     // The change value of the reserved funds for providing the guarantee sum for pending orders
   bool              m_is_change_margin_initial_inc;     // The flag of the reserved funds for providing the guarantee sum for pending orders exceeding the growth value
   bool              m_is_change_margin_initial_dec;     // The flag of the reserved funds for providing the guarantee sum for pending orders exceeding the decrease value
   //--- Guarantee sum for open positions
   double            m_control_margin_maintenance_inc;   // The tracked increase value of the funds reserved on an account to ensure a minimum amount for all open positions
   double            m_control_margin_maintenance_dec;   // The tracked decrease value of the funds reserved on an account to ensure a minimum amount for all open positions
   double            m_changed_margin_maintenance_value; // The change value of the funds reserved on an account to ensure a minimum amount for all open positions
   bool              m_is_change_margin_maintenance_inc; // The flag of the funds reserved on an account to ensure a minimum amount for all open positions exceeding the growth value
   bool              m_is_change_margin_maintenance_dec; // The flag of the funds reserved on an account to ensure a minimum amount for all open positions exceeding the decrease value
   //--- Assets
   double            m_control_assets_inc;               // Tracked assets growth value
   double            m_control_assets_dec;               // Tracked assets decrease value
   double            m_changed_assets_value;             // Assets change value
   bool              m_is_change_assets_inc;             // The flag of the assets change exceeding the growth value
   bool              m_is_change_assets_dec;             // The flag of the assets change exceeding the decrease value
   //--- Liabilities
   double            m_control_liabilities_inc;          // Tracked liabilities growth value
   double            m_control_liabilities_dec;          // Tracked liabilities decrease value
   double            m_changed_liabilities_value;        // Liabilities change values
   bool              m_is_change_liabilities_inc;        // The flag of the liabilities change exceeding the growth value
   bool              m_is_change_liabilities_dec;        // The flag of the liabilities change exceeding the decrease value
   //--- Blocked commissions
   double            m_control_comission_blocked_inc;    // Tracked blocked commissions growth value
   double            m_control_comission_blocked_dec;    // Tracked blocked commissions decrease value
   double            m_changed_comission_blocked_value;  // Blocked commissions changed value
   bool              m_is_change_comission_blocked_inc;  // The flag of the tracked commissions change exceeding the growth value
   bool              m_is_change_comission_blocked_dec;  // The flag of the tracked commissions change exceeding the decrease value
   
//--- Initialize the variables of (1) tracked, (2) controlled account data
   void              InitChangesParams(void); 
   void              InitControlsParams(void);
//--- Check account changes, return a change code
   int               SetChangeCode(void);
//--- Set an event type and fill in the event list
   void              SetTypeEvent(void);
//--- return the flag presence in the account event
   bool              IsPresentEventFlag(constint change_code)   const { return (this.m_change_code & change_code)==change_code;               }
//--- Write the current account data to the account object properties
   void              SetAccountsParams(CAccount* account);

//--- Check the account object presence in the collection list
   bool              IsPresent(CAccount* account);
//--- Find and return the account object index with the current account data
   int               Index(void);
public:

El método SavePrevValues() ha resultado tener poco uso, dado que al determinar el cambio de las propiedades de la cuenta, necesitamos registrar de inmediato en la estructura que almacena los datos sobre el estado anterior el nuevo valor de la propiedad, y en este caso, además, debemos dejar el resto de propiedades como estaban en la anterior comprobación, hasta el momento de su cambio real. Y el método SavePrevValues() ha guardado de golpe todas las propiedades de la cuenta como pasadas, interfiriendo en el seguimiento de una magnitud concreta, establecida por defecto para cada una de las propiedades monitoreadas, que se podían establecer aparte: cada una con su método de establecimiento del tamaño de la magnitud monitoreada.

Escribimos el método de inicialización de datos monitoreados fuera del cuerpo de la clase:

//+------------------------------------------------------------------+
//| Initialize the variables of tracked account data                 |
//+------------------------------------------------------------------+
void CAccountsCollection::InitChangesParams(void)
  {
//--- List and code of changes
   this.m_list_changes.Clear();                    // Clear the change list
   this.m_list_changes.Sort();                     // Sort the change list
//--- Leverage
   this.m_changed_leverage_value=0;                // Leverage change value
   this.m_is_change_leverage_inc=false;            // Leverage increase flag
   this.m_is_change_leverage_dec=false;            // Leverage decrease flag
//--- Number of active pending orders
   this.m_changed_limit_orders_value=0;            // Change value of the maximum allowed number of active pending orders
   this.m_is_change_limit_orders_inc=false;        // Increase flag of the maximum allowed number of active pending orders
   this.m_is_change_limit_orders_dec=false;        // Decrease flag of the maximum allowed number of active pending orders
//--- Trading on an account
   this.m_is_change_trade_allowed_on=false;        // The flag allowing to trade for the current account from the server side
   this.m_is_change_trade_allowed_off=false;       // The flag prohibiting trading for the current account from the server side
//--- Auto trading on an account
   this.m_is_change_trade_expert_on=false;         // The flag allowing to trade for an EA from the server side
   this.m_is_change_trade_expert_off=false;        // The flag prohibiting trading for an EA from the server side
//--- Balance
   this.m_changed_balance_value=0;                 // Balance change value
   this.m_is_change_balance_inc=false;             // The flag of the balance change exceeding the growth value
   this.m_is_change_balance_dec=false;             // The flag of the balance change exceeding the decrease value
//--- Credit
   this.m_changed_credit_value=0;                  // Credit change value
   this.m_is_change_credit_inc=false;              // Credit increase flag
   this.m_is_change_credit_dec=false;              // Credit decrease flag
//--- Profit
   this.m_changed_profit_value=0;                  // Profit change value
   this.m_is_change_profit_inc=false;              // The flag of the profit change exceeding the growth value
   this.m_is_change_profit_dec=false;              // The flag of the profit change exceeding the decrease value
//--- Funds
   this.m_changed_equity_value=0;                  // Funds change value
   this.m_is_change_equity_inc=false;              // The flag of the funds change exceeding the growth value
   this.m_is_change_equity_dec=false;              // The flag of the funds change exceeding the decrease value
//--- Margin
   this.m_changed_margin_value=0;                  // Margin change value
   this.m_is_change_margin_inc=false;              // The flag of the margin change exceeding the growth value
   this.m_is_change_margin_dec=false;              // The flag of the margin change exceeding the decrease value
//--- Free margin
   this.m_changed_margin_free_value=0;             // Free margin change value
   this.m_is_change_margin_free_inc=false;         // The flag of the free margin change exceeding the growth value
   this.m_is_change_margin_free_dec=false;         // The flag of the free margin change exceeding the decrease value
//--- Margin level
   this.m_changed_margin_level_value=0;            // Margin level change value
   this.m_is_change_margin_level_inc=false;        // The flag of the margin level change exceeding the growth value
   this.m_is_change_margin_level_dec=false;        // The flag of the margin level change exceeding the decrease value
//--- Margin Call level
   this.m_changed_margin_so_call_value=0;          // Margin Call level change value
   this.m_is_change_margin_so_call_inc=false;      // Margin Call level increase flag
   this.m_is_change_margin_so_call_dec=false;      // Margin Call level decrease flag
//--- Margin StopOut level
   this.m_changed_margin_so_so_value=0;            // Margin StopOut level change value
   this.m_is_change_margin_so_so_inc=false;        // Margin StopOut level increase flag
   this.m_is_change_margin_so_so_dec=false;        // Margin StopOut level decrease flag
//--- Guarantee sum for pending orders
   this.m_changed_margin_initial_value=0;          // The change value of the reserved funds for providing the guarantee sum for pending orders
   this.m_is_change_margin_initial_inc=false;      // The flag of the reserved funds for providing the guarantee sum for pending orders exceeding the growth value
   this.m_is_change_margin_initial_dec=false;      // The flag of the reserved funds for providing the guarantee sum for pending orders exceeding the decrease value
//--- Guarantee sum for open positions
   this.m_changed_margin_maintenance_value=0;      // The change value of the funds reserved on an account to ensure a minimum amount for all open positions
   this.m_is_change_margin_maintenance_inc=false;  // The flag of the funds reserved on an account to ensure a minimum amount for all open positions exceeding the growth value
   this.m_is_change_margin_maintenance_dec=false;  // The flag of the funds reserved on an account to ensure a minimum amount for all open positions exceeding the decrease value
//--- Assets
   this.m_changed_assets_value=0;                  // Assets change value
   this.m_is_change_assets_inc=false;              // The flag of the assets change exceeding the growth value
   this.m_is_change_assets_dec=false;              // The flag of the assets change exceeding the decrease value
//--- Liabilities
   this.m_changed_liabilities_value=0;             // Liabilities change value
   this.m_is_change_liabilities_inc=false;         // The flag of the liabilities change exceeding the growth value
   this.m_is_change_liabilities_dec=false;         // The flag of the liabilities change exceeding the decrease value
//--- Blocked commissions
   this.m_changed_comission_blocked_value=0;       // Blocked commissions change value
   this.m_is_change_comission_blocked_inc=false;   // The flag of the tracked commissions change exceeding the growth value
   this.m_is_change_comission_blocked_dec=false;   // The flag of the tracked commissions change exceeding the decrease value
  }
//+------------------------------------------------------------------+

Método de inicialización de datos controlados:

//+------------------------------------------------------------------+
//| Initialize the variables of controlled account data              |
//+------------------------------------------------------------------+
void CAccountsCollection::InitControlsParams(void)
  {
//--- Balance
   this.m_control_balance_inc=50;                  // Tracked balance growth value
   this.m_control_balance_dec=50;                  // Tracked balance decrease value
//--- Profit
   this.m_control_profit_inc=20;                   // Tracked profit growth value
   this.m_control_profit_dec=20;                   // Tracked profit decrease value
//--- Funds (equity)
   this.m_control_equity_inc=15;                   // Tracked funds growth value
   this.m_control_equity_dec=15;                   // Tracked funds decrease value
//--- Margin
   this.m_control_margin_inc=1000;                 // Tracked margin growth value
   this.m_control_margin_dec=1000;                 // Tracked margin decrease value
//--- Free margin
   this.m_control_margin_free_inc=1000;            // Tracked free margin growth value
   this.m_control_margin_free_dec=1000;            // Tracked free margin decrease value
//--- Margin level
   this.m_control_margin_level_inc=10000;          // Tracked margin level growth value
   this.m_control_margin_level_dec=500;            // Tracked margin level decrease value
//--- Guarantee sum for pending orders
   this.m_control_margin_initial_inc=1000;         // Tracked growth value of the reserved funds for providing the guarantee sum for pending orders
   this.m_control_margin_initial_dec=1000;         // Tracked decrease value of the reserved funds for providing the guarantee sum for pending orders
//--- Guarantee sum for open positions
   this.m_control_margin_maintenance_inc=1000;     // The tracked increase value of the funds reserved on an account to ensure a minimum amount for all open positions
   this.m_control_margin_maintenance_dec=1000;     // The tracked decrease value of the funds reserved on an account to ensure a minimum amount for all open positions
//--- Assets
   this.m_control_assets_inc=1000;                 // Tracked assets growth value
   this.m_control_assets_dec=1000;                 // Tracked assets decrease value
//--- Liabilities
   this.m_control_liabilities_inc=1000;            // Tracked liabilities growth value
   this.m_control_liabilities_dec=1000;            // Tracked liabilities decrease value
//--- Blocked commissions
   this.m_control_comission_blocked_inc=1000;      // Tracked blocked commissions growth value
   this.m_control_comission_blocked_dec=1000;      // Tracked blocked commissions decrease value
  }
//+------------------------------------------------------------------+

En este método, simplemente inicializamos las variables con los valores por defecto. Cuando las propiedades superan los valores registrados en estas variables, se genera el evento de cuenta correspondiente. Estas propiedades se pueden establecer igualmente con la llamada de los métodos correspondientes para el establecimiento de las propiedades controladas de la cuenta.

Método que comprueba si han cambiado las propiedades de la cuenta, y que además rellena el código de evento con las banderas correspondientes al cambio:

//+------------------------------------------------------------------+
//| Check account changes, return the change code                    |
//+------------------------------------------------------------------+
int CAccountsCollection::SetChangeCode(void)
  {
   this.m_change_code=ACCOUNT_EVENT_FLAG_NO_EVENT;
   
   if(this.m_struct_curr_account.trade_allowed!=this.m_struct_prev_account.trade_allowed)
      this.m_change_code+=ACCOUNT_EVENT_FLAG_TRADE_ALLOWED;
   if(this.m_struct_curr_account.trade_expert!=this.m_struct_prev_account.trade_expert)
      this.m_change_code+=ACCOUNT_EVENT_FLAG_TRADE_EXPERT;
   if(this.m_struct_curr_account.leverage-this.m_struct_prev_account.leverage!=0)
      this.m_change_code+=ACCOUNT_EVENT_FLAG_LEVERAGE;
   if(this.m_struct_curr_account.limit_orders-this.m_struct_prev_account.limit_orders!=0)
      this.m_change_code+=ACCOUNT_EVENT_FLAG_LIMIT_ORDERS;
   if(this.m_struct_curr_account.balance-this.m_struct_prev_account.balance!=0)
      this.m_change_code+=ACCOUNT_EVENT_FLAG_BALANCE;
   if(this.m_struct_curr_account.credit-this.m_struct_prev_account.credit!=0)
      this.m_change_code+=ACCOUNT_EVENT_FLAG_CREDIT;
   if(this.m_struct_curr_account.profit-this.m_struct_prev_account.profit!=0)
      this.m_change_code+=ACCOUNT_EVENT_FLAG_PROFIT;
   if(this.m_struct_curr_account.equity-this.m_struct_prev_account.equity!=0)
      this.m_change_code+=ACCOUNT_EVENT_FLAG_EQUITY;
   if(this.m_struct_curr_account.margin-this.m_struct_prev_account.margin!=0)
      this.m_change_code+=ACCOUNT_EVENT_FLAG_MARGIN;
   if(this.m_struct_curr_account.margin_free-this.m_struct_prev_account.margin_free!=0)
      this.m_change_code+=ACCOUNT_EVENT_FLAG_MARGIN_FREE;
   if(this.m_struct_curr_account.margin_level-this.m_struct_prev_account.margin_level!=0)
      this.m_change_code+=ACCOUNT_EVENT_FLAG_MARGIN_LEVEL;
   if(this.m_struct_curr_account.margin_so_call-this.m_struct_prev_account.margin_so_call!=0)
      this.m_change_code+=ACCOUNT_EVENT_FLAG_MARGIN_SO_CALL;
   if(this.m_struct_curr_account.margin_so_so-this.m_struct_prev_account.margin_so_so!=0)
      this.m_change_code+=ACCOUNT_EVENT_FLAG_MARGIN_SO_SO;
   if(this.m_struct_curr_account.margin_initial-this.m_struct_prev_account.margin_initial!=0)
      this.m_change_code+=ACCOUNT_EVENT_FLAG_MARGIN_INITIAL;
   if(this.m_struct_curr_account.margin_maintenance-this.m_struct_prev_account.margin_maintenance!=0)
      this.m_change_code+=ACCOUNT_EVENT_FLAG_MARGIN_MAINTENANCE;
   if(this.m_struct_curr_account.assets-this.m_struct_prev_account.assets!=0)
      this.m_change_code+=ACCOUNT_EVENT_FLAG_ASSETS;
   if(this.m_struct_curr_account.liabilities-this.m_struct_prev_account.liabilities!=0)
      this.m_change_code+=ACCOUNT_EVENT_FLAG_LIABILITIES;
   if(this.m_struct_curr_account.comission_blocked-this.m_struct_prev_account.comission_blocked!=0)
      this.m_change_code+=ACCOUNT_EVENT_FLAG_COMISSION_BLOCKED;
//---
   returnthis.m_change_code;
  }
//+------------------------------------------------------------------+

En el método, primero reseteamos el código de evento, y después comparamos los valores de los parámetros controlados de la cuenta en la estructura de datos actuales y la estructura de datos pasados. Si los datos no son iguales, añadimos al código del evento la bandera correspondiente.

Método que establece el tipo de evento y añade el evento a la lista de cambios de la cuenta:

//+------------------------------------------------------------------+
//| Set the account object event type                                |
//+------------------------------------------------------------------+
void CAccountsCollection::SetTypeEvent(void)
  {
   this.InitChangesParams();
   ENUM_ACCOUNT_EVENT event=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=ACCOUNT_EVENT_TRADE_ALLOWED_OFF;
         this.m_struct_prev_account.trade_allowed=this.m_struct_curr_account.trade_allowed;
        }
      else
        {
         this.m_is_change_trade_allowed_on=true;
         event=ACCOUNT_EVENT_TRADE_ALLOWED_ON;
         this.m_struct_prev_account.trade_allowed=this.m_struct_curr_account.trade_allowed;
        }
      if(this.m_list_changes.Search(event)==WRONG_VALUE)
         this.m_list_changes.Add(event);
     }
//--- Changing permission for auto trading for the account
   if(this.IsPresentEventFlag(ACCOUNT_EVENT_FLAG_TRADE_EXPERT))
     {
      if(!this.m_struct_curr_account.trade_expert)
        {
         this.m_is_change_trade_expert_off=true;
         event=ACCOUNT_EVENT_TRADE_EXPERT_OFF;
         this.m_struct_prev_account.trade_expert=false;
        }
      else
        {
         this.m_is_change_trade_expert_on=true;
         event=ACCOUNT_EVENT_TRADE_EXPERT_ON;
         this.m_struct_prev_account.trade_expert=true;
        }
      if(this.m_list_changes.Search(event)==WRONG_VALUE)
         this.m_list_changes.Add(event);
     }
//--- Change the leverage
   if(this.IsPresentEventFlag(ACCOUNT_EVENT_FLAG_LEVERAGE))
     {
      this.m_changed_leverage_value=this.m_struct_curr_account.leverage-this.m_struct_prev_account.leverage;
      if(this.m_changed_leverage_value>0)
        {
         this.m_is_change_leverage_inc=true;
         event=ACCOUNT_EVENT_LEVERAGE_INC;
        }
      else
        {
         this.m_is_change_leverage_dec=true;
         event=ACCOUNT_EVENT_LEVERAGE_DEC;
        }
      if(this.m_list_changes.Search(event)==WRONG_VALUE && this.m_list_changes.Add(event))
         this.m_struct_prev_account.leverage=this.m_struct_curr_account.leverage;
     }
//--- Changing the maximum allowed number of active pending orders
   if(this.IsPresentEventFlag(ACCOUNT_EVENT_FLAG_LIMIT_ORDERS))
     {
      this.m_changed_limit_orders_value=this.m_struct_curr_account.limit_orders-this.m_struct_prev_account.limit_orders;
      if(this.m_changed_limit_orders_value>0)
        {
         this.m_is_change_limit_orders_inc=true;
         event=ACCOUNT_EVENT_LIMIT_ORDERS_INC;
        }
      else
        {
         this.m_is_change_limit_orders_dec=true;
         event=ACCOUNT_EVENT_LIMIT_ORDERS_DEC;
        }
      if(this.m_list_changes.Search(event)==WRONG_VALUE && this.m_list_changes.Add(event))
         this.m_struct_prev_account.limit_orders=this.m_struct_curr_account.limit_orders;
     }
//--- Changing the credit in a deposit currency
   if(this.IsPresentEventFlag(ACCOUNT_EVENT_FLAG_CREDIT))
     {
      this.m_changed_credit_value=this.m_struct_curr_account.credit-this.m_struct_prev_account.credit;
      if(this.m_changed_credit_value>0)
        {
         this.m_is_change_credit_inc=true;
         event=ACCOUNT_EVENT_CREDIT_INC;
        }
      else
        {
         this.m_is_change_credit_dec=true;
         event=ACCOUNT_EVENT_CREDIT_DEC;
        }
      if(this.m_list_changes.Search(event)==WRONG_VALUE && this.m_list_changes.Add(event))
         this.m_struct_prev_account.credit=this.m_struct_curr_account.credit;
     }
//--- Changing the Margin Call level
   if(this.IsPresentEventFlag(ACCOUNT_EVENT_FLAG_MARGIN_SO_CALL))
     {
      this.m_changed_margin_so_call_value=this.m_struct_curr_account.margin_so_call-this.m_struct_prev_account.margin_so_call;
      if(this.m_changed_margin_so_call_value>0)
        {
         this.m_is_change_margin_so_call_inc=true;
         event=ACCOUNT_EVENT_MARGIN_SO_CALL_INC;
        }
      else
        {
         this.m_is_change_margin_so_call_dec=true;
         event=ACCOUNT_EVENT_MARGIN_SO_CALL_DEC;
        }
      if(this.m_list_changes.Search(event)==WRONG_VALUE && this.m_list_changes.Add(event))
         this.m_struct_prev_account.margin_so_call=this.m_struct_curr_account.margin_so_call;
     }
//--- Changing the Stop Out level
   if(this.IsPresentEventFlag(ACCOUNT_EVENT_FLAG_MARGIN_SO_SO))
     {
      this.m_changed_margin_so_so_value=this.m_struct_curr_account.margin_so_so-this.m_struct_prev_account.margin_so_so;
      if(this.m_changed_margin_so_so_value>0)
        {
         this.m_is_change_margin_so_so_inc=true;
         event=ACCOUNT_EVENT_MARGIN_SO_SO_INC;
        }
      else
        {
         this.m_is_change_margin_so_so_dec=true;
         event=ACCOUNT_EVENT_MARGIN_SO_SO_DEC;
        }
      if(this.m_list_changes.Search(event)==WRONG_VALUE && this.m_list_changes.Add(event))
         this.m_struct_prev_account.margin_so_so=this.m_struct_curr_account.margin_so_so;
     }
//--- The balance exceeds the specified change value +/-
   if(this.IsPresentEventFlag(ACCOUNT_EVENT_FLAG_BALANCE))
     {
      this.m_changed_balance_value=this.m_struct_curr_account.balance-this.m_struct_prev_account.balance;
      if(this.m_changed_balance_value>this.m_control_balance_inc)
        {
         this.m_is_change_balance_inc=true;
         event=ACCOUNT_EVENT_BALANCE_INC;
         if(this.m_list_changes.Search(event)==WRONG_VALUE && this.m_list_changes.Add(event))
            this.m_struct_prev_account.balance=this.m_struct_curr_account.balance;
        }
      elseif(this.m_changed_balance_value<-this.m_control_balance_dec)
        {
         this.m_is_change_balance_dec=true;
         event=ACCOUNT_EVENT_BALANCE_DEC;
         if(this.m_list_changes.Search(event)==WRONG_VALUE && this.m_list_changes.Add(event))
            this.m_struct_prev_account.balance=this.m_struct_curr_account.balance;
        }
     }
//--- The profit exceeds the specified change value +/-
   if(this.IsPresentEventFlag(ACCOUNT_EVENT_FLAG_PROFIT))
     {
      this.m_changed_profit_value=this.m_struct_curr_account.profit-this.m_struct_prev_account.profit;
      if(this.m_changed_profit_value>this.m_control_profit_inc)
        {
         this.m_is_change_profit_inc=true;
         event=ACCOUNT_EVENT_PROFIT_INC;
         if(this.m_list_changes.Search(event)==WRONG_VALUE && this.m_list_changes.Add(event))
            this.m_struct_prev_account.profit=this.m_struct_curr_account.profit;
        }
      elseif(this.m_changed_profit_value<-this.m_control_profit_dec)
        {
         this.m_is_change_profit_dec=true;
         event=ACCOUNT_EVENT_PROFIT_DEC;
         if(this.m_list_changes.Search(event)==WRONG_VALUE && this.m_list_changes.Add(event))
            this.m_struct_prev_account.profit=this.m_struct_curr_account.profit;
        }
     }
//--- The equity exceeds the specified change value +/-
   if(this.IsPresentEventFlag(ACCOUNT_EVENT_FLAG_EQUITY))
     {
      this.m_changed_equity_value=this.m_struct_curr_account.equity-this.m_struct_prev_account.equity;
      if(this.m_changed_equity_value>this.m_control_equity_inc)
        {
         this.m_is_change_equity_inc=true;
         event=ACCOUNT_EVENT_EQUITY_INC;
         if(this.m_list_changes.Search(event)==WRONG_VALUE && this.m_list_changes.Add(event))
            this.m_struct_prev_account.equity=this.m_struct_curr_account.equity;
        }
      elseif(this.m_changed_equity_value<-this.m_control_equity_dec)
        {
         this.m_is_change_equity_dec=true;
         event=ACCOUNT_EVENT_EQUITY_DEC;
         if(this.m_list_changes.Search(event)==WRONG_VALUE && this.m_list_changes.Add(event))
            this.m_struct_prev_account.equity=this.m_struct_curr_account.equity;
        }
     }
//--- The reserved margin on an account in the deposit currency change exceeds the specified value +/-
   if(this.IsPresentEventFlag(ACCOUNT_EVENT_FLAG_MARGIN))
     {
      this.m_changed_margin_value=this.m_struct_curr_account.margin-this.m_struct_prev_account.margin;
      if(this.m_changed_margin_value>this.m_control_margin_inc)
        {
         this.m_is_change_margin_inc=true;
         event=ACCOUNT_EVENT_MARGIN_INC;
         if(this.m_list_changes.Search(event)==WRONG_VALUE && this.m_list_changes.Add(event))
            this.m_struct_prev_account.margin=this.m_struct_curr_account.margin;
        }
      elseif(this.m_changed_margin_value<-this.m_control_margin_dec)
        {
         this.m_is_change_margin_dec=true;
         event=ACCOUNT_EVENT_MARGIN_DEC;
         if(this.m_list_changes.Search(event)==WRONG_VALUE && this.m_list_changes.Add(event))
            this.m_struct_prev_account.margin=this.m_struct_curr_account.margin;
        }
     }
//--- The free funds available for opening a position in a deposit currency exceed the specified change value +/-
   if(this.IsPresentEventFlag(ACCOUNT_EVENT_FLAG_MARGIN_FREE))
     {
      this.m_changed_margin_free_value=this.m_struct_curr_account.margin_free-this.m_struct_prev_account.margin_free;
      if(this.m_changed_margin_free_value>this.m_control_margin_free_inc)
        {
         this.m_is_change_margin_free_inc=true;
         event=ACCOUNT_EVENT_MARGIN_FREE_INC;
         if(this.m_list_changes.Search(event)==WRONG_VALUE && this.m_list_changes.Add(event))
            this.m_struct_prev_account.margin_free=this.m_struct_curr_account.margin_free;
        }
      elseif(this.m_changed_margin_free_value<-this.m_control_margin_free_dec)
        {
         this.m_is_change_margin_free_dec=true;
         event=ACCOUNT_EVENT_MARGIN_FREE_DEC;
         if(this.m_list_changes.Search(event)==WRONG_VALUE && this.m_list_changes.Add(event))
            this.m_struct_prev_account.margin_free=this.m_struct_curr_account.margin_free;
        }
     }
//--- The margin level on an account in % exceeds the specified change value +/-
   if(this.IsPresentEventFlag(ACCOUNT_EVENT_FLAG_MARGIN_LEVEL))
     {
      this.m_changed_margin_level_value=this.m_struct_curr_account.margin_level-this.m_struct_prev_account.margin_level;
      if(this.m_changed_margin_level_value>this.m_control_margin_level_inc)
        {
         this.m_is_change_margin_level_inc=true;
         event=ACCOUNT_EVENT_MARGIN_LEVEL_INC;
         if(this.m_list_changes.Search(event)==WRONG_VALUE && this.m_list_changes.Add(event))
            this.m_struct_prev_account.margin_level=this.m_struct_curr_account.margin_level;
        }
      elseif(this.m_changed_margin_level_value<-this.m_control_margin_level_dec)
        {
         this.m_is_change_margin_level_dec=true;
         event=ACCOUNT_EVENT_MARGIN_LEVEL_DEC;
         if(this.m_list_changes.Search(event)==WRONG_VALUE && this.m_list_changes.Add(event))
            this.m_struct_prev_account.margin_level=this.m_struct_curr_account.margin_level;
        }
     }
//--- The funds reserved on an account to ensure a guarantee amount for all pending orders exceed the specified change value +/-
   if(this.IsPresentEventFlag(ACCOUNT_EVENT_FLAG_MARGIN_INITIAL))
     {
      this.m_changed_margin_initial_value=this.m_struct_curr_account.margin_initial-this.m_struct_prev_account.margin_initial;
      if(this.m_changed_margin_initial_value>this.m_control_margin_initial_inc)
        {
         this.m_is_change_margin_initial_inc=true;
         event=ACCOUNT_EVENT_MARGIN_INITIAL_INC;
         if(this.m_list_changes.Search(event)==WRONG_VALUE && this.m_list_changes.Add(event))
            this.m_struct_prev_account.margin_initial=this.m_struct_curr_account.margin_initial;
        }
      elseif(this.m_changed_margin_initial_value<-this.m_control_margin_initial_dec)
        {
         this.m_is_change_margin_initial_dec=true;
         event=ACCOUNT_EVENT_MARGIN_INITIAL_DEC;
         if(this.m_list_changes.Search(event)==WRONG_VALUE && this.m_list_changes.Add(event))
            this.m_struct_prev_account.margin_initial=this.m_struct_curr_account.margin_initial;
        }
     }
//--- The funds reserved on an account to ensure a minimum amount for all open positions exceed the specified change value +/-
   if(this.IsPresentEventFlag(ACCOUNT_EVENT_FLAG_MARGIN_MAINTENANCE))
     {
      this.m_changed_margin_maintenance_value=this.m_struct_curr_account.margin_maintenance-this.m_struct_prev_account.margin_maintenance;
      if(this.m_changed_margin_maintenance_value>this.m_control_margin_maintenance_inc)
        {
         this.m_is_change_margin_maintenance_inc=true;
         event=ACCOUNT_EVENT_MARGIN_MAINTENANCE_INC;
         if(this.m_list_changes.Search(event)==WRONG_VALUE && this.m_list_changes.Add(event))
            this.m_struct_prev_account.margin_maintenance=this.m_struct_curr_account.margin_maintenance;
        }
      elseif(this.m_changed_margin_maintenance_value<-this.m_control_margin_maintenance_dec)
        {
         this.m_is_change_margin_maintenance_dec=true;
         event=ACCOUNT_EVENT_MARGIN_MAINTENANCE_DEC;
         if(this.m_list_changes.Search(event)==WRONG_VALUE && this.m_list_changes.Add(event))
            this.m_struct_prev_account.margin_maintenance=this.m_struct_curr_account.margin_maintenance;
        }
     }
//--- The current assets on an account exceed the specified change value +/-
   if(this.IsPresentEventFlag(ACCOUNT_EVENT_FLAG_ASSETS))
     {
      this.m_changed_assets_value=this.m_struct_curr_account.assets-this.m_struct_prev_account.assets;
      if(this.m_changed_assets_value>this.m_control_assets_inc)
        {
         this.m_is_change_assets_inc=true;
         event=ACCOUNT_EVENT_ASSETS_INC;
         if(this.m_list_changes.Search(event)==WRONG_VALUE && this.m_list_changes.Add(event))
            this.m_struct_prev_account.assets=this.m_struct_curr_account.assets;
        }
      elseif(this.m_changed_assets_value<-this.m_control_assets_dec)
        {
         this.m_is_change_assets_dec=true;
         event=ACCOUNT_EVENT_ASSETS_DEC;
         if(this.m_list_changes.Search(event)==WRONG_VALUE && this.m_list_changes.Add(event))
            this.m_struct_prev_account.assets=this.m_struct_curr_account.assets;
        }
     }
//--- The current liabilities on an account exceed the specified change value +/-
   if(this.IsPresentEventFlag(ACCOUNT_EVENT_FLAG_LIABILITIES))
     {
      this.m_changed_liabilities_value=this.m_struct_curr_account.liabilities-this.m_struct_prev_account.liabilities;
      if(this.m_changed_liabilities_value>this.m_control_liabilities_inc)
        {
         this.m_is_change_liabilities_inc=true;
         event=ACCOUNT_EVENT_LIABILITIES_INC;
         if(this.m_list_changes.Search(event)==WRONG_VALUE && this.m_list_changes.Add(event))
            this.m_struct_prev_account.liabilities=this.m_struct_curr_account.liabilities;
        }
      elseif(this.m_changed_liabilities_value<-this.m_control_liabilities_dec)
        {
         this.m_is_change_liabilities_dec=true;
         event=ACCOUNT_EVENT_LIABILITIES_DEC;
         if(this.m_list_changes.Search(event)==WRONG_VALUE && this.m_list_changes.Add(event))
            this.m_struct_prev_account.liabilities=this.m_struct_curr_account.liabilities;
        }
     }
//--- The current sum of blocked commissions on an account exceeds the specified change value +/-
   if(this.IsPresentEventFlag(ACCOUNT_EVENT_FLAG_COMISSION_BLOCKED))
     {
      this.m_changed_comission_blocked_value=this.m_struct_curr_account.comission_blocked-this.m_struct_prev_account.comission_blocked;
      if(this.m_changed_comission_blocked_value>this.m_control_comission_blocked_inc)
        {
         this.m_is_change_comission_blocked_inc=true;
         event=ACCOUNT_EVENT_COMISSION_BLOCKED_INC;
         if(this.m_list_changes.Search(event)==WRONG_VALUE && this.m_list_changes.Add(event))
            this.m_struct_prev_account.comission_blocked=this.m_struct_curr_account.comission_blocked;
        }
      elseif(this.m_changed_comission_blocked_value<-this.m_control_comission_blocked_dec)
        {
         this.m_is_change_comission_blocked_dec=true;
         event=ACCOUNT_EVENT_COMISSION_BLOCKED_DEC;
         if(this.m_list_changes.Search(event)==WRONG_VALUE && this.m_list_changes.Add(event))
            this.m_struct_prev_account.comission_blocked=this.m_struct_curr_account.comission_blocked;
        }
     }
  }
//+------------------------------------------------------------------+

En el método hay dos tipos de lógica de definición de eventos:

  1. el seguimiento simple del permiso/prohibición o cambio/permanencia de una propiedad,
  2. el seguimiento de un cambio superior a la magnitud establecida, ya sea un aumento del valor, o una disminución del mismo.

Como el método es bastante voluminoso, analizaremos su funcionamiento usando como ejemplo dos los tipos de definición de los eventos de la cuenta:

Primero, reseteamos todas las banderas y los datos sobre los cambios y establecemos como cero el tipo de evento .
a continuación, para el primer tipo de lógica (usando como ejemplo el permiso del comercio en la cuenta de trading):

  • comprobamos la presencia de la bandera de cambio del permiso de comercio en la cuenta en el código del evento
  • si en este momento el comercio no está permitido, significa que se acaba de desactivar el permiso
    • establecemos la bandera de prohibición del comercio en la cuenta
    • establecemos el evento "el comercio en la cuenta está prohibido"
    • guardamos en la estructura de datos pasados el estado actual de esta propiedad de la cuenta, para realizar comprobaciones posteriores
  • de lo contrario, si en este momento el comercio está permitido
    • establecemos la bandera de permiso del comercio en la cuenta
    • establecemos el evento "el comercio en la cuenta está permitido"
    • guardamos en la estructura de datos pasados el estado actual de esta propiedad de la cuenta, para realizar comprobaciones posteriores
  • si este evento aún no existe en la lista de cambios
  • añadimos el evento a la lista

Para el segundo tipo de lógica (usando como ejemplo el cambio de la suma de comisiones bloquedas):

  • comprobamos la presencia de la bandera de cambio de la suma de comisiones bloquedas
  • calculamos la magnitud del cambio de la suma de comisiones bloqueadas
  • si la magnitud del cambio es superior a la magnitud controlada de aumento
    • establecemos la bandera de aumento de la suma de comisiones bloqueadas
    • establecemos el evento "la suma de comisiones bloqueadas ha aumentado en más del valor establecido"
    • si este evento aún no existe en la lista de cambios y el evento se ha añadido a la lista con éxito
      • guardamos en la estructura de datos pasados el estado actual de esta propiedad de la cuenta, para realizar comprobaciones posteriores
  • de lo contrario, si la magnitud de cambio es superior a la magnitud controlada de disminución
    • establecemos la bandera de aumento de la suma de comisiones bloqueadas
    • establecemos el evento "la suma de comisiones bloqueadas ha disminuido en más del valor establecido"
    • si este evento aún no existe en la lista de cambios y el evento se ha añadido a la lista con éxito
      • guardamos en la estructura de datos pasados el estado actual de esta propiedad de la cuenta, para realizar comprobaciones posteriores

En la sección pública de a clase, añadimos los métodos que retornan el código de evento de la cuenta, la lista de eventos de la cuenta, el evento de cuenta según su índice en la lista; así como los métodos que establecen y retornan el símbolo, el método que retorna el identificador del gráfico del programa de control y el método que retorna la descripción del evento de cuenta. Además, añadimos los métodos de obtención y establecimiento de los parámetros de los cambios monitoreados:

public:
//--- Return the full account collection list "as is"
   CArrayObj        *GetList(void)                                                                          { return &this.m_list_accounts;                                         }
//--- Return the list by selected (1) integer, (2) real and (3) string properties meeting the compared criterion
   CArrayObj        *GetList(ENUM_ACCOUNT_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode=EQUAL)   { return CSelect::ByAccountProperty(this.GetList(),property,value,mode);}
   CArrayObj        *GetList(ENUM_ACCOUNT_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode=EQUAL)  { return CSelect::ByAccountProperty(this.GetList(),property,value,mode);}
   CArrayObj        *GetList(ENUM_ACCOUNT_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode=EQUAL)  { return CSelect::ByAccountProperty(this.GetList(),property,value,mode);}
//--- Return the (1) current account object index, (2) the flag of the event occurred in the account data, (3) event type
   int               IndexCurrentAccount(void)                                                        const { return this.m_index_current;                                           }
   bool              IsAccountEvent(void)                                                             const { return this.m_is_account_event;                                        }
//--- Return the (1) object account event code, (2) event list, (3) account event by its number in the list
   int               GetEventCode(void)                                                               const { return this.m_change_code;                                             }
   CArrayInt        *GetListChanges(void)                                                                   { return &this.m_list_changes;                          }
   ENUM_ACCOUNT_EVENT GetEvent(constint shift=WRONG_VALUE);
//--- (1) Set and (2) return the current symbol
   void              SetSymbol(conststring symbol)                                                          { this.m_symbol=symbol;                                 }
   string            GetSymbol(void)                                                                  const { return this.m_symbol;                                 }
//--- Set the control program chart ID
   void              SetChartID(constlong id)                                                               { this.m_chart_id=id;                                   }

//--- Constructor, destructor
                     CAccountsCollection();
                    ~CAccountsCollection();
//--- Add the account object to the list
   bool              AddToList(CAccount* account);
//--- (1) Save account objects from the list to the files
//--- (2) Save account objects from the files to the list
   bool              SaveObjects(void);
   bool              LoadObjects(void);
//--- Return the account event description
   string            EventDescription(const ENUM_ACCOUNT_EVENT event);
//--- Update the current account data
   void              Refresh(void);
   
//--- Get and set the parameters of tracked changes
   //--- Leverage:
   //--- (1) Leverage change value, (2) Leverage increase flag, (3) Leverage decrease flag
   long              GetValueChangedLeverage(void)                                                    const { return this.m_changed_leverage_value;                  }
   bool              IsIncreaseLeverage(void)                                                         const { return this.m_is_change_leverage_inc;                  }
   bool              IsDecreaseLeverage(void)                                                         const { return this.m_is_change_leverage_dec;                  }
   //--- Number of active pending orders:
   //--- (1) Change value, (2) Increase flag, (3) Decrease flag
   int               GetValueChangedLimitOrders(void)                                                 const { return this.m_changed_limit_orders_value;              }
   bool              IsIncreaseLimitOrders(void)                                                      const { return this.m_is_change_limit_orders_inc;              }
   bool              IsDecreaseLimitOrders(void)                                                      const { return this.m_is_change_limit_orders_dec;              }
   //--- Trading on an account:
   //--- (1) The flag allowing to trade for the current account, (2) The flag prohibiting trading for the current account from the server side
   bool              IsOnTradeAllowed(void)                                                           const { return this.m_is_change_trade_allowed_on;              }
   bool              IsOffTradeAllowed(void)                                                          const { return this.m_is_change_trade_allowed_off;             }
   //--- Auto trading on an account:
   //--- (1) The flag allowing to trade for an EA, (2) The flag prohibiting trading for an EA from the server side
   bool              IsOnTradeExpert(void)                                                            const { return this.m_is_change_trade_expert_on;               }
   bool              IsOffTradeExpert(void)                                                           const { return this.m_is_change_trade_expert_off;              }
   //--- Balance:
   //--- setting the tracked value of the balance (1) growth, (2) decrease
   //--- getting (3) the balance change value,
   //--- getting the flag of the balance change exceeding the (4) growth value, (5) decrease value
   void              SetControlBalanceInc(const double value)                                               { this.m_control_balance_inc=::fabs(value);              }
   void              SetControlBalanceDec(const double value)                                               { this.m_control_balance_dec=::fabs(value);              }
   double            GetValueChangedBalance(void)                                                     const { return this.m_changed_balance_value;                   }
   bool              IsIncreaseBalance(void)                                                          const { return this.m_is_change_balance_inc;                   }
   bool              IsDecreaseBalance(void)                                                          const { return this.m_is_change_balance_dec;                   }
   //--- Credit:
   //--- getting (1) the credit change value, (2) credit increase flag, (3) decrease flag
   double            GetValueChangedCredit(void)                                                      const { return this.m_changed_credit_value;                    }
   bool              IsIncreaseCredit(void)                                                           const { return this.m_is_change_credit_inc;                    }
   bool              IsDecreaseCredit(void)                                                           const { return this.m_is_change_credit_dec;                    }
   //--- Profit:
   //--- setting the tracked profit (1) growth, (2) decrease value
   //--- getting the (3) profit change value,
   //--- getting the flag of the profit change exceeding the (4) growth, (5) decrease value
   void              SetControlProfitInc(const double value)                                                { this.m_control_profit_inc=::fabs(value);               }
   void              SetControlProfitDec(const double value)                                                { this.m_control_profit_dec=::fabs(value);               }
   double            GetValueChangedProfit(void)                                                      const { return this.m_changed_profit_value;                    }
   bool              IsIncreaseProfit(void)                                                           const { return this.m_is_change_profit_inc;                    }
   bool              IsDecreaseProfit(void)                                                           const { return this.m_is_change_profit_dec;                    }
   //--- Equity:
   //--- setting the tracked equity (1) growth, (2) decrease value
   //--- getting the (3) equity change value,
   //--- getting the flag of the equity change exceeding the (4) growth, (5) decrease value
   void              SetControlEquityInc(const double value)                                                { this.m_control_equity_inc=::fabs(value);               }
   void              SetControlEquityDec(const double value)                                                { this.m_control_equity_dec=::fabs(value);               }
   double            GetValueChangedEquity(void)                                                      const { return this.m_changed_equity_value;                    }
   bool              IsIncreaseEquity(void)                                                           const { return this.m_is_change_equity_inc;                    }
   bool              IsDecreaseEquity(void)                                                           const { return this.m_is_change_equity_dec;                    }
   //--- Margin:
   //--- setting the tracked margin (1) growth, (2) decrease value
   //--- getting the (3) margin change value,
   //--- getting the flag of the margin change exceeding the (4) growth, (5) decrease value
   void              SetControlMarginInc(const double value)                                                { this.m_control_margin_inc=::fabs(value);               }
   void              SetControlMarginDec(const double value)                                                { this.m_control_margin_dec=::fabs(value);               }
   double            GetValueChangedMargin(void)                                                      const { return this.m_changed_margin_value;                    }
   bool              IsIncreaseMargin(void)                                                           const { return this.m_is_change_margin_inc;                    }
   bool              IsDecreaseMargin(void)                                                           const { return this.m_is_change_margin_dec;                    }
   //--- Free margin:
   //--- setting the tracked free margin (1) growth, (2) decrease value
   //--- getting the (3) free margin change value,
   //--- getting the flag of the free margin change exceeding the (4) growth, (5) decrease value
   void              SetControlMarginFreeInc(const double value)                                            { this.m_control_margin_free_inc=::fabs(value);          }
   void              SetControlMarginFreeDec(const double value)                                            { this.m_control_margin_free_dec=::fabs(value);          }
   double            GetValueChangedMarginFree(void)                                                  const { return this.m_changed_margin_free_value;               }
   bool              IsIncreaseMarginFree(void)                                                       const { return this.m_is_change_margin_free_inc;               }
   bool              IsDecreaseMarginFree(void)                                                       const { return this.m_is_change_margin_free_dec;               }
   //--- Margin level:
   //--- setting the tracked margin level (1) growth, (2) decrease value
   //--- getting the (3) margin level change value,
   //--- getting the flag of the margin level change exceeding the (4) growth, (5) decrease value
   void              SetControlMarginLevelInc(const double value)                                           { this.m_control_margin_level_inc=::fabs(value);         }
   void              SetControlMarginLevelDec(const double value)                                           { this.m_control_margin_level_dec=::fabs(value);         }
   double            GetValueChangedMarginLevel(void)                                                 const { return this.m_changed_margin_level_value;              }
   bool              IsIncreaseMarginLevel(void)                                                      const { return this.m_is_change_margin_level_inc;              }
   bool              IsDecreaseMarginLevel(void)                                                      const { return this.m_is_change_margin_level_dec;              }
   //--- Margin Call:
   //--- getting (1) Margin Call change value, (2) increase flag, (3) decrease flag
   double            GetValueChangedMarginCall(void)                                                  const { return this.m_changed_margin_so_call_value;            }
   bool              IsIncreaseMarginCall(void)                                                       const { return this.m_is_change_margin_so_call_inc;            }
   bool              IsDecreaseMarginCall(void)                                                       const { return this.m_is_change_margin_so_call_dec;            }
   //--- Margin StopOut:
   //--- getting (1) Margin StopOut change value, (2) increase flag, (3) decrease flag
   double            GetValueChangedMarginStopOut(void)                                               const { return this.m_changed_margin_so_so_value;              }
   bool              IsIncreaseMarginStopOut(void)                                                    const { return this.m_is_change_margin_so_so_inc;              }
   bool              IsDecreasMarginStopOute(void)                                                    const { return this.m_is_change_margin_so_so_dec;              }
   //--- Guarantee sum for pending orders:
   //--- setting the tracked value of the reserved funds for providing the guarantee sum for pending orders (1) growth, (2) decrease
   //--- getting the change value of the (3) reserved funds for providing the guarantee sum,
   //--- getting the flag of the reserved funds for providing the guarantee sum for pending orders exceeding the (4) growth, (5) decrease value
   void              SetControlMarginInitialInc(const double value)                                         { this.m_control_margin_initial_inc=::fabs(value);       }
   void              SetControlMarginInitialDec(const double value)                                         { this.m_control_margin_initial_dec=::fabs(value);       }
   double            GetValueChangedMarginInitial(void)                                               const { return this.m_changed_margin_initial_value;            }
   bool              IsIncreaseMarginInitial(void)                                                    const { return this.m_is_change_margin_initial_inc;            }
   bool              IsDecreaseMarginInitial(void)                                                    const { return this.m_is_change_margin_initial_dec;            }
   //--- Guarantee sum for open positions:
   //--- setting the tracked value of the funds reserved on an account to ensure a minimum amount for all open positions (1) growth, (2) decrease
   //--- getting the change value of the (3) funds reserved on an account to ensure a minimum amount for all open positions,
   //--- getting the flag of the funds reserved on an account to ensure a minimum amount for all open positions exceeding the (4) growth, (5) decrease value
   void              SetControlMarginMaintenanceInc(const double value)                                     { this.m_control_margin_maintenance_inc=::fabs(value);   }
   void              SetControlMarginMaintenanceDec(const double value)                                     { this.m_control_margin_maintenance_dec=::fabs(value);   }
   double            GetValueChangedMarginMaintenance(void)                                           const { return this.m_changed_margin_maintenance_value;        }
   bool              IsIncreaseMarginMaintenance(void)                                                const { return this.m_is_change_margin_maintenance_inc;        }
   bool              IsDecreaseMarginMaintenance(void)                                                const { return this.m_is_change_margin_maintenance_dec;        }
   //--- Assets:
   //--- setting the tracked value of the assets (1) growth, (2) decrease
   //--- getting (3) the assets change value,
   //--- getting the flag of the change exceeding the (4) growth, (5) decrease value
   void              SetControlAssetsInc(const double value)                                                { this.m_control_assets_inc=::fabs(value);               }
   void              SetControlAssetsDec(const double value)                                                { this.m_control_assets_dec=::fabs(value);               }
   double            GetValueChangedAssets(void)                                                      const { return this.m_changed_assets_value;                    }
   bool              IsIncreaseAssets(void)                                                           const { return this.m_is_change_assets_inc;                    }
   bool              IsDecreaseAssets(void)                                                           const { return this.m_is_change_assets_dec;                    }
   //--- Liabilities:
   //--- setting the tracked value of the liabilities (1) growth, (2) decrease
   //--- getting (3) the liabilities change value,
   //--- getting the flag of the liabilities change exceeding the (4) growth, (5) decrease value
   void              SetControlLiabilitiesInc(const double value)                                           { this.m_control_liabilities_inc=::fabs(value);          }
   void              SetControlLiabilitiesDec(const double value)                                           { this.m_control_liabilities_dec=::fabs(value);          }
   double            GetValueChangedLiabilities(void)                                                 const { return this.m_changed_liabilities_value;               }
   bool              IsIncreaseLiabilities(void)                                                      const { return this.m_is_change_liabilities_inc;               }
   bool              IsDecreaseLiabilities(void)                                                      const { return this.m_is_change_liabilities_dec;               }
   //--- Blocked commissions:
   //--- setting the tracked blocked commissions (1) growth, (2) decrease value
   //--- getting (3) the blocked commissions change value,
   //--- getting the flag of the tracked commissions change exceeding the (4) growth, (5) decrease value
   void              SetControlComissionBlockedInc(const double value)                                      { this.m_control_comission_blocked_inc=::fabs(value);    }
   void              SetControlComissionBlockedDec(const double value)                                      { this.m_control_comission_blocked_dec=::fabs(value);    }
   double            GetValueChangedComissionBlocked(void)                                            const { return this.m_changed_comission_blocked_value;         }
   bool              IsIncreaseComissionBlocked(void)                                                 const { return this.m_is_change_comission_blocked_inc;         }
   bool              IsDecreaseComissionBlocked(void)                                                 const { return this.m_is_change_comission_blocked_dec;         }
  };
//+------------------------------------------------------------------+

Vamos a implementar fuera del cuerpo de la clase el método que retorna un evento de la cuenta según su número en la lista.

//+------------------------------------------------------------------+
//| Return the account event by its number in the list               |
//+------------------------------------------------------------------+
ENUM_ACCOUNT_EVENT CAccountsCollection::GetEvent(constint shift=WRONG_VALUE)
  {
   int total=this.m_list_changes.Total();
   if(total==0)
      return ACCOUNT_EVENT_NO_EVENT;   
   int index=(shift<0 || shift>total-1 ? total-1 : total-shift-1);
   int event=this.m_list_changes.At(index);
   return ENUM_ACCOUNT_EVENT(event!=NULL ? event : ACCOUNT_EVENT_NO_EVENT);
  }
//+------------------------------------------------------------------+

Los eventos en la lista de cambios de las propiedades de la cuenta se ubican según su orden de aparición: el primero de todos se encuentra en el índice 0, y el último, en el índice (tamaño_de_la_lista-1). No necesitamos que el usuario pueda obtener el evento buscado como en las series temporales: en el índice cero debe encontrarse el último evento de todos. Para ello, en el método se ha implementado el cálculo del índice: index = (tamaño_de_la_lista - número_del_evento_buscado-1). Con estos cálculos, si transmitimos 0, se retornará el último elemento de la lista; si retornamos 1, el penúltimo; si transmitimos una cifra que supere el tamaño de la lista, se retornará el último evento.

Bien, transmitimos al método el índice del evento buscado.
Primero, comprobamos el número de eventos en la lista, y si no los hay, retornamos la ausencia de evento.
A continuación, comprobamos el índice del evento buscado, y si el valor transmitido es inferior a cero o excede el tamaño de la matriz, el índice se referirá al último evento en la lista, de lo contrario, calcularemos el índice del evento en la lista de acuerdo con la siguiente regla: si al método se ha transmitido 0, significará que queremos transmitir el último evento (como en la serie temporal), si es 1, el penúltimo, etcétera. O bien, si es necesario obtener el último evento, podemos transmitir el valor -1 como parámetro de entrada del índice.
A continuación, obtenemos el valor de la lista según el índice calculado y lo retornamos.
Si no hemos logrado obtener el evento, se retornará NULL, y esto significará que, antes de usar el resultado del funcionamiento del método, deberemos comprobar si es válido.

Implementamos el método que retorna la descripción del evento de cuenta:

//+------------------------------------------------------------------+
//| Return an account event description                              |
//+------------------------------------------------------------------+
string CAccountsCollection::EventDescription(const ENUM_ACCOUNT_EVENT event)
  {
   int total=this.m_list_accounts.Total();
   if(total==0)
      return(DFUN+TextByLanguage("Ошибка. Список изменений пуст","Error. List of changes is empty"));
   CAccount* account=this.m_list_accounts.At(this.m_index_current);
   if(account==NULL)
      return(DFUN+TextByLanguage("Ошибка. Не удалось получить данные аккаунта","Error. Failed to get account data"));
   constint dg=(account.MarginSOMode()==ACCOUNT_STOPOUT_MODE_MONEY ? (int)account.CurrencyDigits() : 2);
   conststring curency=" "+account.Currency();
   conststring mode_lev=(account.IsPercentsForSOLevels() ? "%" : " "+curency);   return
     (
      event==ACCOUNT_EVENT_NO_EVENT                ? TextByLanguage("Нет события","No event")                                                                                                                                                                     :
      event==ACCOUNT_EVENT_TRADE_ALLOWED_ON        ? TextByLanguage("Торговля на счёте разрешена","Trading on account allowed now")                                                                                                                        :
      event==ACCOUNT_EVENT_TRADE_ALLOWED_OFF       ? TextByLanguage("Торговля на счёте запрещена","Trading on account prohibited now")                                                                                                                     :
      event==ACCOUNT_EVENT_TRADE_EXPERT_ON         ? TextByLanguage("Автоторговля на счёте разрешена","Autotrading on account allowed now")                                                                                                                :
      event==ACCOUNT_EVENT_TRADE_EXPERT_OFF        ? TextByLanguage("Автоторговля на счёте запрещена","Autotrade on account prohibited now")                                                                                                               :
      event==ACCOUNT_EVENT_LEVERAGE_INC            ?
         TextByLanguage("Плечо увеличено на ","Leverage increased by ")+(string)this.GetValueChangedLeverage()+" (1:"+(string)account.Leverage()+")"                                                                                                              :
      event==ACCOUNT_EVENT_LEVERAGE_DEC            ?
         TextByLanguage("Плечо уменьшено на ","Leverage decreased by ")+(string)this.GetValueChangedLeverage()+" (1:"+(string)account.Leverage()+")"                                                                                                              :
      event==ACCOUNT_EVENT_LIMIT_ORDERS_INC        ?
         TextByLanguage("Максимально допустимое количество действующих отложенных ордеров увеличено на","Maximum allowable number of active pending orders increased by ")+(string)this.GetValueChangedLimitOrders()+" ("+(string)account.LimitOrders()+")"       :
      event==ACCOUNT_EVENT_LIMIT_ORDERS_DEC        ?
         TextByLanguage("Максимально допустимое количество действующих отложенных ордеров уменьшено на","Maximum allowable number of active pending orders decreased by ")+(string)this.GetValueChangedLimitOrders()+" ("+(string)account.LimitOrders()+")"       :
      event==ACCOUNT_EVENT_BALANCE_INC             ?
         TextByLanguage("Баланс счёта увеличен на ","Account balance increased by ")+::DoubleToString(this.GetValueChangedBalance(),dg)+curency+" ("+::DoubleToString(account.Balance(),dg)+curency+")"                                                           :
      event==ACCOUNT_EVENT_BALANCE_DEC             ?
         TextByLanguage("Баланс счёта уменьшен на ","Account balance decreased by ")+::DoubleToString(this.GetValueChangedBalance(),dg)+curency+" ("+::DoubleToString(account.Balance(),dg)+curency+")"                                                           :
      event==ACCOUNT_EVENT_EQUITY_INC              ?
         TextByLanguage("Средства увеличены на ","Equity increased by ")+::DoubleToString(this.GetValueChangedEquity(),dg)+curency+" ("+::DoubleToString(account.Equity(),dg)+curency+")"                                                                         :
      event==ACCOUNT_EVENT_EQUITY_DEC              ?
         TextByLanguage("Средства уменьшены на ","Equity decreased by ")+::DoubleToString(this.GetValueChangedEquity(),dg)+curency+" ("+::DoubleToString(account.Equity(),dg)+curency+")"                                                                         :
      event==ACCOUNT_EVENT_PROFIT_INC              ?
         TextByLanguage("Текущая прибыль счёта увеличена на ","Account current profit increased by ")+::DoubleToString(this.GetValueChangedProfit(),dg)+curency+" ("+::DoubleToString(account.Profit(),dg)+curency+")"                                            :
      event==ACCOUNT_EVENT_PROFIT_DEC              ?
         TextByLanguage("Текущая прибыль счёта уменьшена на ","Account current profit decreased by ")+::DoubleToString(this.GetValueChangedProfit(),dg)+curency+" ("+::DoubleToString(account.Profit(),dg)+curency+")"                                            :
      event==ACCOUNT_EVENT_CREDIT_INC              ?
         TextByLanguage("Предоставленный кредит увеличен на ","Credit increased by ")+::DoubleToString(this.GetValueChangedCredit(),dg)+curency+" ("+::DoubleToString(account.Credit(),dg)+curency+")"                                                            :
      event==ACCOUNT_EVENT_CREDIT_DEC              ?
         TextByLanguage("Предоставленный кредит уменьшен на ","Credit decreased by ")+::DoubleToString(this.GetValueChangedCredit(),dg)+curency+" ("+::DoubleToString(account.Credit(),dg)+curency+")"                                                            :
      event==ACCOUNT_EVENT_MARGIN_INC              ?
         TextByLanguage("Залоговые средства увеличены на ","Margin increased by ")+::DoubleToString(this.GetValueChangedMargin(),dg)+curency+" ("+::DoubleToString(account.Margin(),dg)+curency+")"                                                               :
      event==ACCOUNT_EVENT_MARGIN_DEC              ?
         TextByLanguage("Залоговые средства уменьшены на ","Margin decreased by ")+::DoubleToString(this.GetValueChangedMargin(),dg)+curency+" ("+::DoubleToString(account.Margin(),dg)+curency+")"                                                               :
      event==ACCOUNT_EVENT_MARGIN_FREE_INC         ?
         TextByLanguage("Свободные средства увеличены на ","Free margin increased by ")+::DoubleToString(this.GetValueChangedMarginFree(),dg)+curency+" ("+::DoubleToString(account.MarginFree(),dg)+curency+")"                                                  :
      event==ACCOUNT_EVENT_MARGIN_FREE_DEC         ?
         TextByLanguage("Свободные средства уменьшены на ","Free margin decreased by ")+::DoubleToString(this.GetValueChangedMarginFree(),dg)+curency+" ("+::DoubleToString(account.MarginFree(),dg)+curency+")"                                                  :
      event==ACCOUNT_EVENT_MARGIN_LEVEL_INC        ?
         TextByLanguage("Уровень залоговых средств увеличен на ","Margin level increased by ")+::DoubleToString(this.GetValueChangedMarginLevel(),dg)+"%"+" ("+::DoubleToString(account.MarginLevel(),dg)+"%)"                                                    :
      event==ACCOUNT_EVENT_MARGIN_LEVEL_DEC        ?
         TextByLanguage("Уровень залоговых средств уменьшен на ","Margin level decreased by ")+::DoubleToString(this.GetValueChangedMarginLevel(),dg)+"%"+" ("+::DoubleToString(account.MarginLevel(),dg)+"%)"                                                    :
      event==ACCOUNT_EVENT_MARGIN_INITIAL_INC      ?
         TextByLanguage("Гарантийная сумма по отложенным ордерам увеличена на ","Guarantee sum for pending orders increased by ")+::DoubleToString(this.GetValueChangedMarginInitial(),dg)+curency+" ("+::DoubleToString(account.MarginInitial(),dg)+curency+")" :
      event==ACCOUNT_EVENT_MARGIN_INITIAL_DEC      ?
         TextByLanguage("Гарантийная сумма по отложенным ордерам уменьшена на ","Guarantee sum for pending orders decreased by ")+::DoubleToString(this.GetValueChangedMarginInitial(),dg)+curency+" ("+::DoubleToString(account.MarginInitial(),dg)+curency+")" :
      event==ACCOUNT_EVENT_MARGIN_MAINTENANCE_INC  ?
         TextByLanguage("Гарантийная сумма по позициям увеличена на ","Guarantee sum for positions increased by ")+::DoubleToString(this.GetValueChangedMarginMaintenance(),dg)+curency+" ("+::DoubleToString(account.MarginMaintenance(),dg)+curency+")"        :
      event==ACCOUNT_EVENT_MARGIN_MAINTENANCE_DEC  ?
         TextByLanguage("Гарантийная сумма по позициям уменьшена на ","Guarantee sum for positions decreased by ")+::DoubleToString(this.GetValueChangedMarginMaintenance(),dg)+curency+" ("+::DoubleToString(account.MarginMaintenance(),dg)+curency+")"        :
      event==ACCOUNT_EVENT_MARGIN_SO_CALL_INC      ?
         TextByLanguage("Увеличен уровень Margin Call на ","Increased Margin Call level by ")+::DoubleToString(this.GetValueChangedMarginCall(),dg)+mode_lev+" ("+::DoubleToString(account.MarginSOCall(),dg)+mode_lev+")"                                        :
      event==ACCOUNT_EVENT_MARGIN_SO_CALL_DEC      ?
         TextByLanguage("Уменьшен уровень Margin Call на ","Decreased Margin Call level by ")+::DoubleToString(this.GetValueChangedMarginCall(),dg)+mode_lev+" ("+::DoubleToString(account.MarginSOCall(),dg)+mode_lev+")"                                        :
      event==ACCOUNT_EVENT_MARGIN_SO_SO_INC        ?
         TextByLanguage("Увеличен уровень Stop Out на ","Increased Margin Stop Out level by ")+::DoubleToString(this.GetValueChangedMarginStopOut(),dg)+mode_lev+" ("+::DoubleToString(account.MarginSOSO(),dg)+mode_lev+")"                                      :
      event==ACCOUNT_EVENT_MARGIN_SO_SO_DEC        ?
         TextByLanguage("Уменьшен уровень Stop Out на ","Decreased Margin Stop Out level by ")+::DoubleToString(this.GetValueChangedMarginStopOut(),dg)+mode_lev+" ("+::DoubleToString(account.MarginSOSO(),dg)+mode_lev+")"                                      :
      event==ACCOUNT_EVENT_ASSETS_INC              ?
         TextByLanguage("Размер активов увеличен на ","Assets increased by ")+::DoubleToString(this.GetValueChangedAssets(),dg)+" ("+::DoubleToString(account.Assets(),dg)+")"                                                                                    :
      event==ACCOUNT_EVENT_ASSETS_DEC              ?
         TextByLanguage("Размер активов уменьшен на ","Assets decreased by ")+::DoubleToString(this.GetValueChangedAssets(),dg)+" ("+::DoubleToString(account.Assets(),dg)+")"                                                                                    :
      event==ACCOUNT_EVENT_LIABILITIES_INC         ?
         TextByLanguage("Размер обязательств увеличен на ","Liabilities increased by ")+::DoubleToString(this.GetValueChangedLiabilities(),dg)+" ("+::DoubleToString(account.Liabilities(),dg)+")"                                                                :
      event==ACCOUNT_EVENT_LIABILITIES_DEC         ?
         TextByLanguage("Размер обязательств уменьшен на ","Liabilities decreased by ")+::DoubleToString(this.GetValueChangedLiabilities(),dg)+" ("+::DoubleToString(account.Liabilities(),dg)+")"                                                                :
      event==ACCOUNT_EVENT_COMISSION_BLOCKED_INC   ?
         TextByLanguage("Размер заблокированных комиссий увеличен на ","Blocked commissions increased by ")+::DoubleToString(this.GetValueChangedComissionBlocked(),dg)+" ("+::DoubleToString(account.ComissionBlocked(),dg)+")"                                   :
      event==ACCOUNT_EVENT_COMISSION_BLOCKED_DEC   ?
         TextByLanguage("Размер заблокированных комиссий уменьшен на ","Blocked commissions decreased by ")+::DoubleToString(this.GetValueChangedComissionBlocked(),dg)+" ("+::DoubleToString(account.ComissionBlocked(),dg)+")"                                   :
      ::EnumToString(event)
     );
  }
//+------------------------------------------------------------------+

Aquí: transmitimos al método el evento de cuenta cuya descripción debemos obtener. Comprobamos el tamaño de la lista de los objetos de cuenta, y si está vacío, retornamos la descripción del archivo. Dado que podemos monitorear solo los eventos de la cuenta actual, obtenemos el objeto de cuenta actual de la lista de cuentas según el índice del objeto de cuenta actual. Si no hemos logrado obtener el objeto, retornamos igualmente la descripción del error.
A continuación, obtenemos las propiedades de la cuenta necesarias para mostrar correctamente la descripción del evento, comprobamos el evento y retornamos su descripción.

Inicializamos en la lista de inicialización del constructor las variables del símbolo y del indentificador del gráfico del programa de control con los valores por defecto, el símbolo actual y el gráfico actual; limpiamos la estructura del tick, que necesitaremos para determinar la hora del evento, e inicializamos los parámetros variables y controlables de la cuenta:

//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CAccountsCollection::CAccountsCollection(void) : m_folder_name(DIRECTORY+"Accounts"),
                                                 m_is_account_event(false),
                                                 m_symbol(::Symbol()),  
                                                 m_chart_id(::ChartID())
  {
   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
   ::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();
  }
//+------------------------------------------------------------------+

Y, finalmente, añadimos al método de actualización de la cuenta el seguimiento de los cambios de sus propiedades:

//+------------------------------------------------------------------+
//| Update the current account data                                  |
//+------------------------------------------------------------------+
void CAccountsCollection::Refresh(void)
  {
   this.m_is_account_event=false;
   if(this.m_index_current==WRONG_VALUE)
      return;
   CAccount* account=this.m_list_accounts.At(this.m_index_current);
   if(account==NULL)
      return;
   ::ZeroMemory(this.m_struct_curr_account);
   this.SetAccountsParams(account);
   
//--- First launch
   if(!this.m_struct_prev_account.login)
     {
      this.m_struct_prev_account=this.m_struct_curr_account;
      return;
     }
//--- f the account hash sum changed
   if(this.m_struct_curr_account.hash_sum!=this.m_struct_prev_account.hash_sum)
     {
      this.m_change_code=this.SetChangeCode();
      this.SetTypeEvent();
      int total=this.m_list_changes.Total();
      if(total>0)
        {
         this.m_is_account_event=true;
         for(int i=0;i<total;i++)
           {
            ENUM_ACCOUNT_EVENT event=this.GetEvent(i);
            if(event==NULL || !::SymbolInfoTick(this.m_symbol,this.m_tick))
               continue;
            string sparam=TimeMSCtoString(this.m_tick.time_msc)+": "+this.EventDescription(event);
            Print(sparam);
            ::EventChartCustom(this.m_chart_id,(ushort)event,this.m_tick.time_msc,(double)i,sparam);
           }
        }
      this.m_struct_prev_account.hash_sum=this.m_struct_curr_account.hash_sum;
     }
  }
//+------------------------------------------------------------------+
En primer lugar, reseteamos la bandera de evento de la cuenta. Dado que hemos eliminado el método SavePrevValues(), en lugar de él, añadimos la línea que estaba registrada en el método eliminado: el guardado de la estructura de los datos actuales en la estructura de los datos pasados en el primer inicio. Al cambiar la suma hash, comprobamos y establecemos el código de evento y el tipo de evento sucedido.

En el método de establecimiento del tipo de evento, introducimos en la lista de cambios todos los eventos que han sucedido simultáneamente en las propiedades de la cuenta comercial. Por eso, en primer lugar, comprobamos el tamaño de la lista de cambios, y si no está vacío, establecemos la bandera del cambio ocurrido, y acto seguido, obtenemos el evento en el ciclo por los datos de la lista, estableciendo posteriormente su descripción de tipo string, que consta de la hora en milisegundos y la descripción del evento. Después, de forma temporal y solo para la comprobación (más tarde sustituiremos esto, y todos los mensajes de sistema de la biblioteca para la muestra en el diario se darán solo al permitir el logueo), imprimimos en el diario la descripción del evento, y finalmente, enviamos el evento al programa de control con la ayuda de EventChartCustom().

En los parámetros de la función EventChartCustom(), transmitimos:

  • transmitimos el evento a event_id,
  • a lparam, la hora del evento en segundos
  • a dparam, el índice del evento en la lista de cambios de la cuenta sucedidos de forma simulatánea
  • a sparam, la descripción de tipo string del evento

Al final del todo del método, debemos guardar necesariamente la suma hash actual como pasada, para la comprobación posterior.

Con esto, ya hemos terminado las mejoras en la clase CAccountsCollection.

Ahora, vamos a pasar a la clase CEngine . Aquí todo se controla y comienza con ella, por lo que le añadiremos todo lo necesario para trabajar con los eventos de la clase.

Añadimos en la sección privada de la clase las variables para guardar la bandera del evento de cambio de las propiedades de la cuenta y para guardar el último evento ocurrido en la cuenta. En la sección pública, añadimos los métodos que retornan la lista de eventos de la cuenta ocurridos simultáneamente, y el último evento de la cuenta:

//+------------------------------------------------------------------+
//| 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
   CArrayObj            m_list_counters;                 // List of timer counters
   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
   ENUM_TRADE_EVENT     m_last_trade_event;              // Account last trading event
   ENUM_ACCOUNT_EVENT   m_last_account_event;            // Last event in account properties
//--- Return the counter index by id
   int                  CounterIndex(constint id) const;
//--- Return (1) the first launch flag, (2) the flag presence in a trading event
   bool                 IsFirstStart(void);
//--- Working with (1) order, deal and position, as well as (2) account events
   void                 TradeEventsControl(void);
   void                 AccountEventsControl(void);
//--- Return the last (1) market pending order, (2) market order, (3) last position, (4) position by ticket
   COrder*              GetLastMarketPending(void);
   COrder*              GetLastMarketOrder(void);
   COrder*              GetLastPosition(void);
   COrder*              GetPosition(constulong ticket);
//--- Return the last (1) removed pending order, (2) historical market order, (3) historical order (market or pending) by its ticket
   COrder*              GetLastHistoryPending(void);
   COrder*              GetLastHistoryOrder(void);
   COrder*              GetHistoryOrder(constulong ticket);
//--- Return the (1) first and the (2) last historical market orders from the list of all position orders, (3) the last deal
   COrder*              GetFirstOrderPosition(constulong position_id);
   COrder*              GetLastOrderPosition(constulong position_id);
   COrder*              GetLastDeal(void);
public:
   //--- Return the list of market (1) positions, (2) pending orders and (3) market orders
   CArrayObj*           GetListMarketPosition(void);
   CArrayObj*           GetListMarketPendings(void);
   CArrayObj*           GetListMarketOrders(void);
   //--- Return the list of historical (1) orders, (2) removed pending orders, (3) deals, (4) all position market orders by its id
   CArrayObj*           GetListHistoryOrders(void);
   CArrayObj*           GetListHistoryPendings(void);
   CArrayObj*           GetListDeals(void);
   CArrayObj*           GetListAllOrdersByPosID(constulong position_id);
//--- Return the list of (1) accounts, (2) account events
   CArrayObj*           GetListAllAccounts(void)                        { returnthis.m_accounts.GetList();          }
   CArrayInt*           GetListAccountEvents(void)                      { returnthis.m_accounts.GetListChanges();   }
//--- Return the list of order, deal and position events
   CArrayObj*           GetListAllOrdersEvents(void)                    { returnthis.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 { returnthis.m_last_trade_event;            }
   ENUM_ACCOUNT_EVENT   LastAccountEvent(void)                    const { returnthis.m_last_account_event;          }
   bool                 IsHedge(void)                             const { returnthis.m_is_hedge;                    }
   bool                 IsTester(void)                            const { returnthis.m_is_tester;                   }
//--- Create the timer counter
   void                 CreateCounter(constint id,constulong frequency,constulong pause);
//--- Timer
   void                 OnTimer(void);
//--- Constructor/Destructor
                        CEngine();
                       ~CEngine();
  };
//+------------------------------------------------------------------+

Añadimos la inicialización del último evento de la cuenta en el constructor de la clase, en su lista de inicialización:

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

Terminamos de implementar el método AccountEventsControl(), que antes solo se encargaba de llamar al método Refresh() de la clase de colección de cuentas:

//+------------------------------------------------------------------+
//| Check account events                                             |
//+------------------------------------------------------------------+
void CEngine::AccountEventsControl(void)
  {
//--- Check account property changes and set the flag of the account change events
   this.m_accounts.Refresh();
   this.m_is_account_event=this.m_accounts.IsAccountEvent();
//--- If there are account property changes
   if(this.m_is_account_event)
     {
      //--- Get the last event of the account property change
      this.m_last_account_event=this.m_accounts.GetEvent();
     }
  }
//+------------------------------------------------------------------+

Aquí todo es muy sencillo: primero actualizamos los datos de la cuenta, y si hay algún cambio en las propiedades de la cuenta, simplemente registramos el último evento en la variable. El resto de datos sobre el evento los extraeremos en el programa de control, que funciona usando la biblioteca como base.

Poniendo a prueba los eventos de cuenta

Para simular los eventos de la cuenta, en principio, podemos usar el asesor del artículo anterior, dado que la propia biblioteca encuentra todos los cambios de las propiedades de la cuenta, enviando posteriormenete un mensaje sobre ello a los eventos del gráfico e imprimiendo en el diario la descripción del evento de la cuenta ocurrido.
Vamos a probar a salir un tanto del sandbox de la biblioteca y procesar ciertos eventos de la cuenta en el programa, por ejemplo, el aumento del tamaño de los propios fondos.

En efecto, por el momento, la biblioteca es un "programa en sí", y el acceso a sus capacidades desde el exterior está fuertemente restringido. Pero esto será así solo hasta que nos ocupemos de la recopilación y el procesamiento de los datos necesarios: la biblioteca imprime en el diario distintos eventos solo para las comprobaciones de prueba destinadas a verificar el correcto funcionamiento de las clases creadas y los datos recopilados, así como de los eventos monitoreados por ella. A continuación, vamos a organizar un acceso cómodo y sencillo a cualquier dato disponible de la biblioteca, lo cual facilitará la posibilidad de obtener dicha información desde el programa.

Para poder obtener los datos sobre los cambios de las propiedades de la cuenta ahora, introduciremos algunas correcciones en la clase CEngine: vamos a necesitar obtener acceso al objeto de la cuenta actual y a los eventos de la cuenta actual.
Para ello, añadimos los métodos necesarios a la sección pública de la clase CEngine, en el archivo Engine.mqh:

public:
   //--- Return the list of market (1) positions, (2) pending orders and (3) market orders
   CArrayObj*           GetListMarketPosition(void);
   CArrayObj*           GetListMarketPendings(void);
   CArrayObj*           GetListMarketOrders(void);
   //--- Return the list of historical (1) orders, (2) removed pending orders, (3) deals, (4) all position market orders by its id
   CArrayObj*           GetListHistoryOrders(void);
   CArrayObj*           GetListHistoryPendings(void);
   CArrayObj*           GetListDeals(void);
   CArrayObj*           GetListAllOrdersByPosID(constulong position_id);
//--- Return the list of (1) accounts, (2) account events, (3) account change event by its index in the list
//--- (4) the current account, (5) event description
   CArrayObj*           GetListAllAccounts(void)                        { return this.m_accounts.GetList();          }
   CArrayInt*           GetListAccountEvents(void)                      { return this.m_accounts.GetListChanges();   }
   ENUM_ACCOUNT_EVENT   GetAccountEventByIndex(const int index)         { return this.m_accounts.GetEvent(index);    }
   CAccount*            GetAccountCurrent(void);
   string               GetAccountEventDescription(ENUM_ACCOUNT_EVENT event);
  

Aquí, hemos añadido el método de obtención de un evento de la cuenta según su índice en la lista de cambios, el método de obtención del objeto de cuenta actual y el método de obtención de la descripción del evento de cuenta.

El método de obtención de un evento de la cuenta según su índice simplemente retorna el resultado del funcionamiento del método GetEvent() de la clase de colección de cuentas (ya lo hemos analizado con anterioridad).

Añadimos al final del listado la implementación del método de obtención del objeto de cuenta actual:

//+------------------------------------------------------------------+
//| Return the current account                                       |
//+------------------------------------------------------------------+
CAccount* CEngine::GetAccountCurrent(void)
  {
   int index=this.m_accounts.IndexCurrentAccount();
   if(index==WRONG_VALUE)
      return NULL;
   CArrayObj* list=this.m_accounts.GetList();   
   return(list!=NULL ? (list.At(index)!=NULL ? list.At(index) : NULL) : NULL);  
  }
//+------------------------------------------------------------------+

Primero, obtenemos el índice de la cuenta actual desde la clase de colección de cuentas. Si no hemos obtenido el índice, retornamos NULL. A continuación, obtenemos la lista completa de cuentas y retornamos la cuenta actual necesaria según su índice en la lista de cuentas. Si se da un error al obtener la lista o cuenta, retornamos NULL.

E implementamos el método que retorna la descripción del evento de cuenta:

//+------------------------------------------------------------------+
//| Return the account event description                             |
//+------------------------------------------------------------------+
string CEngine::GetAccountEventDescription(ENUM_ACCOUNT_EVENT event)
  {
   return this.m_accounts.EventDescription(event);
  }
//+------------------------------------------------------------------+

El método retorna el resultado del funcionamiento del método de la clase de colección de cuentas que retorna la descripción del evento de cuenta.

Para la simulación, vamos a tomar el asesor del artículo anterior \MQL5\Experts\TestDoEasy\Part12\TestDoEasyPart12_2.mq5 y guardarlo con el nombre TestDoEasyPart13.mq5 en la nueva carpeta \MQL5\Experts\TestDoEasy\Part13.

Eliminamos de inmediato el parámetro de entrada

inputbool     InpFullProperties    =  false;// Show full accounts properties

y eliminamos del procesador OnInit() la comprobación rápida de la colección de cuentas:

//--- Fast check of the account object collection   
   CArrayObj* list=engine.GetListAllAccounts();
   if(list!=NULL)
     {
      int total=list.Total();
      if(total>0)
         Print("\n",TextByLanguage("=========== Список сохранённых аккаунтов ===========","=========== List of saved accounts ==========="));
      for(int i=0;i<total;i++)
        {
         CAccount* account=list.At(i);
         if(account==NULL)
            continue;
         Sleep(100);
         if(InpFullProperties)
            account.Print();
         else
            account.PrintShort();
        }
     }
//---

Información a tener en cuenta:
dado que hemos cambiado la estructura del objeto de cuenta (hemos cambiado las dimensiones de las matrices uchar para guardar las propiedades de tipo string de la cuenta y hemos añadido otra propiedad de tipo entero más), los archivos de los objetos de cuenta guardados anteriormente ahora no se cargarán correctamente. Si se encuentran en la carpeta general de los terminales en el directorio \Files\DoEasy\Accounts\, deberemos eliminarlos antes de iniciar esta asesor de prueba: los crearemos de nuevo al alternar entre una cuenta comercial y otra, con un tamaño nuevo para la estructura de objetos.

El procesador OnInit() ahora tiene el aspecto siguiente:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Calling the function displays the list of enumeration constants in the journal 
//--- (the list is set in the strings 22 and 25 of the DELib.mqh file) for checking the constants validity
   //EnumNumbersTest();
//--- Set EA global variables
   prefix=MQLInfoString(MQL_PROGRAM_NAME)+"_";
   for(int i=0;i<TOTAL_BUTT;i++)
     {
      butt_data[i].name=prefix+EnumToString((ENUM_BUTTONS)i);
      butt_data[i].text=EnumToButtText((ENUM_BUTTONS)i);
     }
   lot=NormalizeLot(Symbol(),fmax(InpLots,MinimumLots(Symbol())*2.0));
   magic_number=InpMagic;
   stoploss=InpStopLoss;
   takeprofit=InpTakeProfit;
   distance_pending=InpDistance;
   distance_stoplimit=InpDistanceSL;
   slippage=InpSlippage;
   trailing_stop=InpTrailingStop*Point();
   trailing_step=InpTrailingStep*Point();
   trailing_start=InpTrailingStart;
   stoploss_to_modify=InpStopLossModify;
   takeprofit_to_modify=InpTakeProfitModify;

//--- Check and remove remaining EA graphical objects
   if(IsPresentObects(prefix))
      ObjectsDeleteAll(0,prefix);

//--- Create the button panel
   if(!CreateButtons(InpButtShiftX,InpButtShiftY))
      returnINIT_FAILED;
//--- Set trailing activation button status
   ButtonState(butt_data[TOTAL_BUTT-1].name,trailing_on);

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

Para procesar todos los eventos que llegan de la biblioteca al programa, y para no desordenar OnChartEvent() estándar, vamos a crear un manejador aparte: OnDoEasyEvent(), y ya en él, analizaremos todos los eventos entrantes. Y nos encontramos con otra ventaja más: el código será ahora más legible, y lo que es no menos importante, podremos procesar los eventos en el simulador, cosa que precisamente necesitamos en estos momentos. Bien, íbamos a procesar el evento de la cuenta "aumento de los fondos propios por encima del valor de crecimiento establecido", y para ello, resulta mucho más rápido y sencillo hacer las comprobaciones en el simulador.

Vayamos por orden.
Añadimos al manejador OnTick() la funcionalidad necesaria para procesar los eventos de la cuenta:

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//--- Initialize the last events
   static ENUM_TRADE_EVENT last_trade_event=WRONG_VALUE;
   static ENUM_ACCOUNT_EVENT last_account_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: ",EnumToString(last_trade_event));
     }
//--- If the last account event changed
   if(engine.LastAccountEvent()!=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
         CArrayInt* list=engine.GetListAccountEvents();
         if(list!=NULL)
           {
            //--- Get the next event in a loop
            int total=list.Total();
            for(int i=0;i<total;i++)
              {
               ENUM_ACCOUNT_EVENT event=(ENUM_ACCOUNT_EVENT)list.At(i);
               if(event==NULL)
                  continue;
               string sparam=engine.GetAccountEventDescription(event);
               long lparam=TimeCurrent()*1000;
               double dparam=(double)i;
               //--- Send an event to the event handler
               OnDoEasyEvent(CHARTEVENT_CUSTOM+event,lparam,dparam,sparam);
              }
           }
        }
      Comment("\nLast account event: ",EnumToString(last_account_event));
     }
//--- If the trailing flag is set
   if(trailing_on)
     {
      TrailingPositions();
      TrailingOrders();
     }
  }
//+------------------------------------------------------------------+

Aquí: hemos introducido una nueva variable que guarda el tipo del último evento de la cuenta. Comprobamos su estado actual respecto al tipo del último evento retornado por la clase CAccountsCollection. Si el estado ha cambiado, significa que ha habido un evento de cuenta. A continuación, solo para el simulador, obtenemos el siguiente evento en el ciclo por la lista de eventos de la cuenta ocurridos simultáneamente, y lo enviamos al manejador de eventos de la biblioteca. En los comentarios del listado, se han escrito todas las acciones sobre la obtención de eventos y su envío al manejador. La única diferencia del funcionamiento del simulador reside en que aquí no podemos obtener acceso a los datos sobre la hora del evento en milisegundos. Por eso, simplemente enviamos la hora actual * 1000.

Ahora, completamos el manejador de eventos OnChartEvent():

//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
   if(MQLInfoInteger(MQL_TESTER))
      return;
   if(id==CHARTEVENT_OBJECT_CLICK && StringFind(sparam,"BUTT_")>0)
     {
      PressButtonEvents(sparam);
     }
//--- DoEasy library event
   if(id>=CHARTEVENT_CUSTOM)
     {
      OnDoEasyEvent(id,lparam,dparam,sparam);
     } 
  }
//+------------------------------------------------------------------+

Aquí, hemos añadido la llamada del manejador de eventos de la biblioteca, en el caso de que el identificador del evento indique un evento que ha llegado desde la biblioteca.

Y, finalmente, escribimos el manejador de eventos de la biblioteca:

//+------------------------------------------------------------------+
//| 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_EVENTS_NEXT_CODE)
     {
      event=EnumToString((ENUM_TRADE_EVENT)ushort(idx));
      digits=(int)SymbolInfoInteger(sparam,SYMBOL_DIGITS);
     }
//--- Handling account events
   else if(idx<ACCOUNT_EVENTS_NEXT_CODE)
     {
      event=EnumToString((ENUM_ACCOUNT_EVENT)ushort(idx));
      digits=0;
      
      //--- if this is an equity increase
      if((ENUM_ACCOUNT_EVENT)idx==ACCOUNT_EVENT_EQUITY_INC)        
        {
         Print(DFUN,sparam);
         //--- Close a position with the highest profit 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();
         //--- 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 
              }
           }
        }
     }
  }
//+------------------------------------------------------------------+

Dado que al enviar un evento al gráfico del programa de control, se añade al código de evento un valor de CHARTEVENT_CUSTOM igual a 1000, para obtener el código verdadero, necesitaremos restar del código de evento recibido el valor de CHARTEVENT_CUSTOM. Si el evento, su valor numérico, se encuentra en el límite que va de TRADE_EVENTS_NEXT_CODE a ACCOUNT_EVENTS_NEXT_CODE-1, esto significará que ha llegado un evento de cambio de las propiedades de la cuenta. Dado que, según nuestra idea, queremos cerrar la posición más rentable cuando la cuantía de los fondos en superior a la establecida en los ajustes (y hemos establecido por defecto un valor de crecimiento de 15), comprobamos el evento "aumento del tamaño de los fondos por encima de la magnitud establecida", y después, imprimimos en el diario la descripción del evento y cerramos la posición más rentable: todo está escrito en los comentarios al código.

Si ahora iniciamos sin más el asesor en el gráfico, pasado cierto tiempo, podremos obtener en el diario una entrada sobre la prohibición del comercio en la cuenta de trading, y después, sobre el permiso del mismo:

2019.06.1010:56:33.8772019.06.1006:55:29.279: Trading on the account is prohibited now
2019.06.1011:08:56.5492019.06.1007:08:51.900: Trading on the account is allowed now

En MetaQuotes-Demo, esta desactivación/activación del permiso de comercio se puede observar varias veces al día, lo que nos da la posibilidad de comprobar el funcionamiento de la biblioteca en cuanto a la definición de estos eventos en la cuenta demo.

Ahora, iniciamos el asesor en el simulador y abrimos muchas posiciones, para captar rápidamente el evento de aumento de fondos y el procesamiento que hemos escrito para este evento, es decir, el cierre de la posición más rentable:


Podemos ver que cuando los fondos aumentan por encima de la magnitud establecida, la posición más rentable se cierra automáticamene. Y en el diario, se muestra un mensaje sobre el evento de cuenta que estamos monitoreando.

¿Qué es lo próximo?

En el siguiente artículo, comenzaremos a implmentar el trabajo con símbolos. Planeamos implementar los objetos de símbolo, la colección de objetos de símbolo y los eventos de símbolo.

Más abajo se adjuntan todos los archivos de la versión actual de la biblioteca y los archivos del asesor de prueba. El lector podrá descargar y poner a prueba todo por sí mismo.
Si tiene cualquier duda, observación o sugerencia, podrá formularla en los comentarios al artículo.

Volver al contenido

Artículos de esta serie:

Parte 1. Concepto, organización de datos.
Parte 2. Colección de ó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 concepto de los eventos comerciales. Envío de eventos al programa.
Parte 6. Eventos en las cuentas de compensación.
Parte 7. Eventos de activación de órdenes StopLimit, preparación de la funcionalidad para 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 posición.
Parte 12. Implementando la clase de objeto "cuenta" y la colección de objetos de cuenta.


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

Archivos adjuntos |
MQL5.zip (129.92 KB)
MQL4.zip (127.28 KB)
Gestión de la optimización (Parte 2): Creando los objetos clave y la lógica de la aplicación Gestión de la optimización (Parte 2): Creando los objetos clave y la lógica de la aplicación
Es la continuación del artículo anterior que describe la creación de la interfaz gráfica para gestionar la optimización. Aquí, vamos a considerar la lógica del funcionamiento de la extensión creada. Vamos a crear un envoltorio para el terminal MetaTrader 5 con el fin de iniciarlo como un proceso controlado usando C#. Además, vamos a analizar el trabajo con los archivos de configuración y archivos de los ajustes. La lógica del programa será dividida en dos partes: en la primera estarán descritos los métodos que se invocan después de pulsar algún botón, la segunda parte se encargará del inicio y de la gestión de la optimización.
Biblioteca para el desarrollo rápido y sencillo de programas para MetaTrader (Parte XII): Implementando la clase de objeto "cuenta" y la colección de objetos de cuenta Biblioteca para el desarrollo rápido y sencillo de programas para MetaTrader (Parte XII): Implementando la clase de objeto "cuenta" y la colección de objetos de cuenta
En el artículo anterior, definimos los eventos de cierre de posiciones para MQL4 en la biblioteca y eliminamos las propiedades de las órdenes que nos resultaban innecesarias. En el presente artículo, analizaremos la creación del objeto "Cuenta", crearemos una colección de objetos de cuenta y prepararemos la funcionalidad de para monitorear los eventos de las cuentas.
Estudio de las figuras técnicas de Merrill Estudio de las figuras técnicas de Merrill
En el presente artículo, vamos a analizar el modelo de las figuras técnicas de Merrill, e intentaremos averiguar hasta qué punto estos patrones técnicos son útiles hoy en día. Para este propósito, crearemos una herramienta para testearlos y aplicaremos este modelo a diferentes tipos de datos, a saber: precio de cierre, sus máximos y mínimos, indicadores del tipo oscilatorio.
Biblioteca para el desarrollo rápido y sencillo de programas para MetaTrader (Parte XI) Compatibilidad con MQL4 - Eventos de cierre de posición Biblioteca para el desarrollo rápido y sencillo de programas para MetaTrader (Parte XI) Compatibilidad con MQL4 - Eventos de cierre de posición
Continuamos creando la gran biblioteca multiplataforma cuyo objetivo es simplificar la escritura de programas para las plataformas MetaTrader 5 y MetaTrader 4. En la décima parte, continuamos trabajando con la compatibilidad de la biblioteca con MQL4 e implementamos la definición de los eventos de apertura de posición y activación de órdenes pendientes. En el presente artículo, vamos a implementar la defición de los eventos de cierre de posición, eliminando al mismo tiempo las propiedades innecesarias de las órdenes.