Библиотека для простого и быстрого создания программ для MetaTrader (Часть XIII): События объекта "аккаунт"

14 июня 2019, 12:39
Artyom Trishkin
0
1 646

Содержание


Концепция событий аккаунта

Ранее для отслеживания событий ордеров и позиций (Часть IVЧасть XI), мы создали отдельный класс, который занимается именно отслеживанием событий ордеров и позиций и отправляет данные об их изменениях в главный объект библиотеки CEngine.


Для отслеживания событий аккаунта, мы пойдём иным путём: так как у нас есть возможность отслеживать события только одного-единственного счёта — того, к которому на данный момент подключен терминал, то отдельный класс для отслеживания событий только одного счёта, думаю, будет излишним, и мы создадим методы работы с событиями прямо в классе-коллекции аккаунтов.
Для того, чтобы понять произошло ли какое-нибудь изменение в свойствах счёта, мы будем сравнивать текущее состояние свойств счёта с его прошлым состоянием, и при нахождении изменений, будем отправлять событие на график управляющей программы.
Некоторая часть функционала для отслеживания событий аккаунта уже была нами создана в прошлой статье при создании коллекции объектов-аккаунтов. В данной статье доработаем уже подготовленный функционал до завершённого рабочего состояния.


Методы работы с событиями аккаунта

Начнём работу с создания необходимых перечислений и макроподстановок в файле Defines.mqh. Так как был получен ответ от разработчиков о реальных размерах в байтах, выделяемых под строковые свойства аккаунта, то задавать их будем жёстко в коде класса CAccount и, соответственно, надобность в макроподстановке, задающей размер uchar-массивов для хранения строковых свойств объекта-аккаунта, исчезла. Удалим из листинга Defines.mqh макроподстановку

#define UCHAR_ARRAY_SIZE            (64)                       // Размер uchar-массивов для хранения string-свойств

Так же было решено вписать все необходимые перечисления и макроподстановки для работы с объектами-аккаунтами в самый конец файла — после данных для работы с торговыми событиями. Причиной тому послужило то, что в программу будем отправлять коды событий не только аккаунтов, но и коды торговых событий, которые у нас уже реализованы и отправляются в программу. Соответственно,числовые  значения кодов событий аккаунта у нас будут начинаться с числового значения самого последнего кода торгового события+1. Если вдруг у нас увеличится количество торговых событий, то чтобы не переписывать числовые значения кодов событий аккаунта, мы объявим макроподстановку, в которой будет прописываться рассчитываемое общее количество всех торговых событий. И точно так же зададим макроподстановку со значением общего количества событий аккаунта — на случай, если будем реализовывать ещё какие-либо события (например, события символов) — тогда числовые коды этих новых событий будут начинаться уже от общего количества событий аккаунта+1.

В конце списка возможных торговых событий на счёте впишем макроподстановку, в которой будет указано значение, соответствующее числовому значению последнего торгового события+1:

//+------------------------------------------------------------------+
//| Список возможных торговых событий на счёте                       |
//+------------------------------------------------------------------+
enum ENUM_TRADE_EVENT
  {
   TRADE_EVENT_NO_EVENT = 0,                                // Нет торгового события
   TRADE_EVENT_PENDING_ORDER_PLASED,                        // Отложенный ордер установлен
   TRADE_EVENT_PENDING_ORDER_REMOVED,                       // Отложенный ордер удалён
//--- члены перечисления, совпадающие с членами перечисления ENUM_DEAL_TYPE
//--- (порядок следования констант ниже менять нельзя, удалять и добавлять новые - нельзя)
   TRADE_EVENT_ACCOUNT_CREDIT = DEAL_TYPE_CREDIT,           // Начисление кредита (3)
   TRADE_EVENT_ACCOUNT_CHARGE,                              // Дополнительные сборы
   TRADE_EVENT_ACCOUNT_CORRECTION,                          // Корректирующая запись
   TRADE_EVENT_ACCOUNT_BONUS,                               // Перечисление бонусов
   TRADE_EVENT_ACCOUNT_COMISSION,                           // Дополнительные комиссии
   TRADE_EVENT_ACCOUNT_COMISSION_DAILY,                     // Комиссия, начисляемая в конце торгового дня
   TRADE_EVENT_ACCOUNT_COMISSION_MONTHLY,                   // Комиссия, начисляемая в конце месяца
   TRADE_EVENT_ACCOUNT_COMISSION_AGENT_DAILY,               // Агентская комиссия, начисляемая в конце торгового дня
   TRADE_EVENT_ACCOUNT_COMISSION_AGENT_MONTHLY,             // Агентская комиссия, начисляемая в конце месяца
   TRADE_EVENT_ACCOUNT_INTEREST,                            // Начисления процентов на свободные средства
   TRADE_EVENT_BUY_CANCELLED,                               // Отмененная сделка покупки
   TRADE_EVENT_SELL_CANCELLED,                              // Отмененная сделка продажи
   TRADE_EVENT_DIVIDENT,                                    // Начисление дивиденда
   TRADE_EVENT_DIVIDENT_FRANKED,                            // Начисление франкированного дивиденда
   TRADE_EVENT_TAX                        = DEAL_TAX,       // Начисление налога
//--- константы, относящиеся к типу сделки DEAL_TYPE_BALANCE из перечисления ENUM_DEAL_TYPE
   TRADE_EVENT_ACCOUNT_BALANCE_REFILL     = DEAL_TAX+1,     // Пополнение средств на балансе
   TRADE_EVENT_ACCOUNT_BALANCE_WITHDRAWAL = DEAL_TAX+2,     // Снятие средств с баланса
//--- Остальные возможные торговые события//--- (менять порядок следования констант ниже, удалять и добавлять новые - можно)
   TRADE_EVENT_PENDING_ORDER_ACTIVATED    = DEAL_TAX+3,     // Отложенный ордер активирован ценой
   TRADE_EVENT_PENDING_ORDER_ACTIVATED_PARTIAL,             // Отложенный ордер активирован ценой частично
   TRADE_EVENT_POSITION_OPENED,                             // Позиция открыта
   TRADE_EVENT_POSITION_OPENED_PARTIAL,                     // Позиция открыта частично
   TRADE_EVENT_POSITION_CLOSED,                             // Позиция закрыта
   TRADE_EVENT_POSITION_CLOSED_BY_POS,                      // Позиция закрыта встречной
   TRADE_EVENT_POSITION_CLOSED_BY_SL,                       // Позиция закрыта по StopLoss
   TRADE_EVENT_POSITION_CLOSED_BY_TP,                       // Позиция закрыта по TakeProfit
   TRADE_EVENT_POSITION_REVERSED_BY_MARKET,                 // Разворот позиции новой сделкой (неттинг)
   TRADE_EVENT_POSITION_REVERSED_BY_PENDING,                // Разворот позиции активацией отложенного ордера (неттинг)
   TRADE_EVENT_POSITION_REVERSED_BY_MARKET_PARTIAL,         // Разворот позиции частичным исполнением маркет-ордера (неттинг)
   TRADE_EVENT_POSITION_REVERSED_BY_PENDING_PARTIAL,        // Разворот позиции частичной активацией отложенного ордера (неттинг)
   TRADE_EVENT_POSITION_VOLUME_ADD_BY_MARKET,               // Добавлен объём к позиции новой сделкой (неттинг)
   TRADE_EVENT_POSITION_VOLUME_ADD_BY_MARKET_PARTIAL,       // Добавлен объём к позиции частичным исполнением маркет-ордера (неттинг)
   TRADE_EVENT_POSITION_VOLUME_ADD_BY_PENDING,              // Добавлен объём к позиции активацией отложенного ордера (неттинг)
   TRADE_EVENT_POSITION_VOLUME_ADD_BY_PENDING_PARTIAL,      // Добавлен объём к позиции частичной активацией отложенного ордера (неттинг)
   TRADE_EVENT_POSITION_CLOSED_PARTIAL,                     // Позиция закрыта частично
   TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_POS,              // Позиция закрыта частично встречной
   TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_SL,               // Позиция закрыта частично по StopLoss
   TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_TP,               // Позиция закрыта частично по TakeProfit
   TRADE_EVENT_TRIGGERED_STOP_LIMIT_ORDER,                  // Срабатывание StopLimit ордера
   TRADE_EVENT_MODIFY_ORDER_PRICE,                          // Изменение цены установки ордера
   TRADE_EVENT_MODIFY_ORDER_PRICE_STOP_LOSS,                // Изменение цены установки ордера и StopLoss 
   TRADE_EVENT_MODIFY_ORDER_PRICE_TAKE_PROFIT,              // Изменение цены установки ордера и TakeProfit
   TRADE_EVENT_MODIFY_ORDER_PRICE_STOP_LOSS_TAKE_PROFIT,    // Изменение цены установки ордера, StopLoss и TakeProfit
   TRADE_EVENT_MODIFY_ORDER_STOP_LOSS_TAKE_PROFIT,          // Изменение StopLoss и TakeProfit ордера
   TRADE_EVENT_MODIFY_ORDER_STOP_LOSS,                      // Изменение StopLoss ордера
   TRADE_EVENT_MODIFY_ORDER_TAKE_PROFIT,                    // Изменение TakeProfit ордера
   TRADE_EVENT_MODIFY_POSITION_STOP_LOSS_TAKE_PROFIT,       // Изменение StopLoss и TakeProfit позиции
   TRADE_EVENT_MODIFY_POSITION_STOP_LOSS,                   // Изменение StopLoss позиции
   TRADE_EVENT_MODIFY_POSITION_TAKE_PROFIT,                 // Изменение TakeProfit позиции
  };
#define TRADE_EVENTS_NEXT_CODE      (TRADE_EVENT_MODIFY_POSITION_TAKE_PROFIT+1)// Код следующего события после последнего кода торгового события
//+------------------------------------------------------------------+

Именно с этого кода будут начинаться коды событий аккаунта.

Данные для работы с аккаунтом — целочисленные, вещественные и строковые свойства аккаунта, а так же возможные критерии сортировки аккаунтов мы прописали в прошлой статье, но разместили их перед данными для работы с событиями счёта. Теперь же мы их перенесём в конец, и пропишем дополнительные данные для работы с событиями аккаунта — список флагов событий аккаунта и список возможных событий аккаунта:

//+------------------------------------------------------------------+
//| Данные для работы с аккаунтами                                   |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Список флагов событий аккаунта                                   |
//+------------------------------------------------------------------+
enum ENUM_ACCOUNT_EVENT_FLAGS
  {
   ACCOUNT_EVENT_FLAG_NO_EVENT            =  0,             // Нет события
   ACCOUNT_EVENT_FLAG_LEVERAGE            =  1,             // Изменение предоставленного плеча
   ACCOUNT_EVENT_FLAG_LIMIT_ORDERS        =  2,             // Изменение максимально допустимого количества действующих отложенных ордеров
   ACCOUNT_EVENT_FLAG_TRADE_ALLOWED       =  4,             // Изменение разрешения торговли на счёте
   ACCOUNT_EVENT_FLAG_TRADE_EXPERT        =  8,             // Изменение разрешения автоторговли на счёте
   ACCOUNT_EVENT_FLAG_BALANCE             =  16,            // Изменение баланса больше заданной величины в +/-
   ACCOUNT_EVENT_FLAG_EQUITY              =  32,            // Изменение собственных средств больше заданной величины в +/-
   ACCOUNT_EVENT_FLAG_PROFIT              =  64,            // Изменение профита больше заданной величины в +/-
   ACCOUNT_EVENT_FLAG_CREDIT              =  128,           // Изменение размера предоставленного кредита в валюте депозита
   ACCOUNT_EVENT_FLAG_MARGIN              =  256,           // Изменение размера зарезервированных залоговых средств на счете в валюте депозита больше заданной величины в +/-
   ACCOUNT_EVENT_FLAG_MARGIN_FREE         =  512,           // Изменение размера средств на счете в валюте депозита, доступных для открытия позиции больше заданной величины в +/-
   ACCOUNT_EVENT_FLAG_MARGIN_LEVEL        =  1024,          // Изменение уровня залоговых средств на счете в процентах больше заданной величины в +/-
   ACCOUNT_EVENT_FLAG_MARGIN_INITIAL      =  2048,          // Изменение размера средств, зарезервированных на счёте, для обеспечения гарантийной суммы по всем отложенным ордерам больше заданной величины в +/-
   ACCOUNT_EVENT_FLAG_MARGIN_MAINTENANCE  =  4096,          // Изменение размера средств, зарезервированных на счёте, для обеспечения минимальной суммы по всем открытым позициям больше заданной величины в +/-
   ACCOUNT_EVENT_FLAG_MARGIN_SO_CALL      =  8192,          // Изменение уровня залоговых средств, при котором происходит MarginCall
   ACCOUNT_EVENT_FLAG_MARGIN_SO_SO        =  16384,         // Изменение уровня залоговых средств, при достижении которого происходит StopOut
   ACCOUNT_EVENT_FLAG_ASSETS              =  32768,         // Изменение текущего размера активов на счёте больше заданной величины в +/-
   ACCOUNT_EVENT_FLAG_LIABILITIES         =  65536,         // Изменение текущего размера обязательств на счёте больше заданной величины в +/-
   ACCOUNT_EVENT_FLAG_COMISSION_BLOCKED   =  131072,        // Изменение текущей суммы заблокированных комиссий по счёту больше заданной величины в +/-
  };
//+------------------------------------------------------------------+
//| Список возможных событий аккаунта                                |
//+------------------------------------------------------------------+
enum ENUM_ACCOUNT_EVENT
  {
   ACCOUNT_EVENT_NO_EVENT = TRADE_EVENTS_NEXT_CODE,         // Нет события
   ACCOUNT_EVENT_LEVERAGE_INC,                              // Увеличение предоставленного плеча
   ACCOUNT_EVENT_LEVERAGE_DEC,                              // Уменьшение предоставленного плеча
   ACCOUNT_EVENT_LIMIT_ORDERS_INC,                          // Увеличение максимально допустимого количества действующих отложенных ордеров
   ACCOUNT_EVENT_LIMIT_ORDERS_DEC,                          // Уменьшение максимально допустимого количества действующих отложенных ордеров
   ACCOUNT_EVENT_TRADE_ALLOWED_ON,                          // Разрешение торговли на счёте
   ACCOUNT_EVENT_TRADE_ALLOWED_OFF,                         // Запрет торговли на счёте
   ACCOUNT_EVENT_TRADE_EXPERT_ON,                           // Разрешение автоторговли на счёте
   ACCOUNT_EVENT_TRADE_EXPERT_OFF,                          // Запрет автоторговли на счёте
   ACCOUNT_EVENT_BALANCE_INC,                               // Увеличение баланса больше заданной величины
   ACCOUNT_EVENT_BALANCE_DEC,                               // Уменьшение баланса больше заданной величины
   ACCOUNT_EVENT_EQUITY_INC,                                // Увеличение собственных средств больше заданной величины
   ACCOUNT_EVENT_EQUITY_DEC,                                // Уменьшение собственных средств больше заданной величины
   ACCOUNT_EVENT_PROFIT_INC,                                // Увеличение профита больше заданной величины
   ACCOUNT_EVENT_PROFIT_DEC,                                // Уменьшение профита больше заданной величины
   ACCOUNT_EVENT_CREDIT_INC,                                // Увеличение размера предоставленного кредита в валюте депозита
   ACCOUNT_EVENT_CREDIT_DEC,                                // Уменьшение размера предоставленного кредита в валюте депозита
   ACCOUNT_EVENT_MARGIN_INC,                                // Увеличение размера зарезервированных залоговых средств на счете  в валюте депозита
   ACCOUNT_EVENT_MARGIN_DEC,                                // Уменьшение размера зарезервированных залоговых средств на счете  в валюте депозита
   ACCOUNT_EVENT_MARGIN_FREE_INC,                           // Увеличение размера средств на счете в валюте депозита, доступных для открытия позиции
   ACCOUNT_EVENT_MARGIN_FREE_DEC,                           // Уменьшение размера средств на счете в валюте депозита, доступных для открытия позиции
   ACCOUNT_EVENT_MARGIN_LEVEL_INC,                          // Увеличение уровня залоговых средств на счете в процентах
   ACCOUNT_EVENT_MARGIN_LEVEL_DEC,                          // Уменьшение уровня залоговых средств на счете в процентах
   ACCOUNT_EVENT_MARGIN_INITIAL_INC,                        // Увеличение размера средств, зарезервированных на счёте, для обеспечения гарантийной суммы по всем отложенным ордерам
   ACCOUNT_EVENT_MARGIN_INITIAL_DEC,                        // Уменьшение размера средств, зарезервированных на счёте, для обеспечения гарантийной суммы по всем отложенным ордерам
   ACCOUNT_EVENT_MARGIN_MAINTENANCE_INC,                    // Увеличение размера средств, зарезервированных на счёте, для обеспечения минимальной суммы по всем открытым позициям
   ACCOUNT_EVENT_MARGIN_MAINTENANCE_DEC,                    // Уменьшение размера средств, зарезервированных на счёте, для обеспечения минимальной суммы по всем открытым позициям
   ACCOUNT_EVENT_MARGIN_SO_CALL_INC,                        // Увеличение уровня залоговых средств, при котором происходит MarginCall
   ACCOUNT_EVENT_MARGIN_SO_CALL_DEC,                        // Уменьшение уровня залоговых средств, при котором происходит MarginCall
   ACCOUNT_EVENT_MARGIN_SO_SO_INC,                          // Увеличение уровня залоговых средств, при достижении которого происходит StopOut
   ACCOUNT_EVENT_MARGIN_SO_SO_DEC,                          // Уменьшение уровня залоговых средств, при достижении которого происходит StopOut
   ACCOUNT_EVENT_ASSETS_INC,                                // Увеличение текущего размера активов на счёте
   ACCOUNT_EVENT_ASSETS_DEC,                                // Уменьшение текущего размера активов на счёте
   ACCOUNT_EVENT_LIABILITIES_INC,                           // Увеличение текущего размера обязательств на счёте
   ACCOUNT_EVENT_LIABILITIES_DEC,                           // Уменьшение текущего размера обязательств на счёте
   ACCOUNT_EVENT_COMISSION_BLOCKED_INC,                     // Увеличение текущей суммы заблокированных комиссий по счёту
   ACCOUNT_EVENT_COMISSION_BLOCKED_DEC,                     // Уменьшение текущей суммы заблокированных комиссий по счёту
  };
#define ACCOUNT_EVENTS_NEXT_CODE       (ACCOUNT_EVENT_COMISSION_BLOCKED_DEC+1)   // Код следующего событя после последнего кода события аккаунта
//+------------------------------------------------------------------+
//| Целочисленные свойства аккаунта                                  |
//+------------------------------------------------------------------+
enum ENUM_ACCOUNT_PROP_INTEGER
  {
   ACCOUNT_PROP_LOGIN,                                      // Номер счёта
   ACCOUNT_PROP_TRADE_MODE,                                 // Тип торгового счета
   ACCOUNT_PROP_LEVERAGE,                                   // Размер предоставленного плеча
   ACCOUNT_PROP_LIMIT_ORDERS,                               // Максимально допустимое количество действующих отложенных ордеров
   ACCOUNT_PROP_MARGIN_SO_MODE,                             // Режим задания минимально допустимого уровня залоговых средств
   ACCOUNT_PROP_TRADE_ALLOWED,                              // Разрешенность торговли для текущего счета со стороны сервера
   ACCOUNT_PROP_TRADE_EXPERT,                               // Разрешенность торговли для эксперта со стороны сервера
   ACCOUNT_PROP_MARGIN_MODE,                                // Режим расчета маржи
   ACCOUNT_PROP_CURRENCY_DIGITS,                            // Количество знаков после запятой для валюты счета, необходимых для точного отображения торговых результатов
   ACCOUNT_PROP_SERVER_TYPE                                 // Тип торгового сервера (MetaTrader5, MetaTrader4)
  };
#define ACCOUNT_PROP_INTEGER_TOTAL    (10)                  // Общее количество целочисленных свойств счетов
#define ACCOUNT_PROP_INTEGER_SKIP     (0)                   // Количество неиспользуемых в сортировке целочисленных свойств счетов
//+------------------------------------------------------------------+
//| Вещественные свойства аккаунта                                   |
//+------------------------------------------------------------------+
enum ENUM_ACCOUNT_PROP_DOUBLE
  {
   ACCOUNT_PROP_BALANCE = ACCOUNT_PROP_INTEGER_TOTAL,       // Баланс счета в валюте депозита
   ACCOUNT_PROP_CREDIT,                                     // Размер предоставленного кредита в валюте депозита
   ACCOUNT_PROP_PROFIT,                                     // Размер текущей прибыли на счете в валюте депозита
   ACCOUNT_PROP_EQUITY,                                     // Значение собственных средств на счете в валюте депозита
   ACCOUNT_PROP_MARGIN,                                     // Размер зарезервированных залоговых средств на счете  в валюте депозита
   ACCOUNT_PROP_MARGIN_FREE,                                // Размер свободных средств на счете  в валюте депозита, доступных для открытия позиции
   ACCOUNT_PROP_MARGIN_LEVEL,                               // Уровень залоговых средств на счете в процентах
   ACCOUNT_PROP_MARGIN_SO_CALL,                             // Уровень залоговых средств, при котором требуется пополнение счета (Margin Call)
   ACCOUNT_PROP_MARGIN_SO_SO,                               // Уровень залоговых средств, при достижении которого происходит принудительное закрытие самой убыточной позиции (Stop Out)
   ACCOUNT_PROP_MARGIN_INITIAL,                             // Размер средств, зарезервированных на счёте, для обеспечения гарантийной суммы по всем отложенным ордерам 
   ACCOUNT_PROP_MARGIN_MAINTENANCE,                         // Размер средств, зарезервированных на счёте, для обеспечения минимальной суммы по всем открытым позициям
   ACCOUNT_PROP_ASSETS,                                     // Текущий размер активов на счёте
   ACCOUNT_PROP_LIABILITIES,                                // Текущий размер обязательств на счёте
   ACCOUNT_PROP_COMMISSION_BLOCKED                          // Текущая сумма заблокированных комиссий по счёту
  };
#define ACCOUNT_PROP_DOUBLE_TOTAL     (14)                  // Общее количество вещественных свойств счетов
#define ACCOUNT_PROP_DOUBLE_SKIP      (0)                   // Количество неиспользуемых в сортировке вещественных свойств счетов
//+------------------------------------------------------------------+
//| Строковые свойства аккаунта                                      |
//+------------------------------------------------------------------+
enum ENUM_ACCOUNT_PROP_STRING
  {
   ACCOUNT_PROP_NAME = (ACCOUNT_PROP_INTEGER_TOTAL+ACCOUNT_PROP_DOUBLE_TOTAL), // Имя клиента
   ACCOUNT_PROP_SERVER,                                     // Имя торгового сервера
   ACCOUNT_PROP_CURRENCY,                                   // Валюта депозита
   ACCOUNT_PROP_COMPANY                                     // Имя компании, обслуживающей счет
  };
#define ACCOUNT_PROP_STRING_TOTAL     (4)                   // Общее количество строковых свойств счетов
#define ACCOUNT_PROP_STRING_SKIP      (0)                   // Количество неиспользуемых в сортировке строковых свойств счетов
//+------------------------------------------------------------------+
//| Возможные критерии сортировки аккаунтов                          |
//+------------------------------------------------------------------+
#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_TRADE_MODE          =  1,                      // Сортировать по типу торгового счета
   SORT_BY_ACCOUNT_LEVERAGE            =  2,                      // Сортировать по размеру предоставленного плеча
   SORT_BY_ACCOUNT_LIMIT_ORDERS        =  3,                      // Сортировать по максимально допустимому количеству действующих отложенных ордеров
   SORT_BY_ACCOUNT_MARGIN_SO_MODE      =  4,                      // Сортировать по режиму задания минимально допустимого уровня залоговых средств
   SORT_BY_ACCOUNT_TRADE_ALLOWED       =  5,                      // Сортировать по разрешенности торговли для текущего счета
   SORT_BY_ACCOUNT_TRADE_EXPERT        =  6,                      // Сортировать по разрешенности торговли для эксперта
   SORT_BY_ACCOUNT_MARGIN_MODE         =  7,                      // Сортировать по режиму расчета маржи
   SORT_BY_ACCOUNT_CURRENCY_DIGITS     =  8,                      // Сортировать по количеству знаков после запятой для валюты счета
   SORT_BY_ACCOUNT_SERVER_TYPE         =  9,                      // Сортировать по типу торгового сервера (MetaTrader5, MetaTrader4)
    
   SORT_BY_ACCOUNT_BALANCE             =  FIRST_ACC_DBL_PROP,     // Сортировать по балансу счета в валюте депозита
   SORT_BY_ACCOUNT_CREDIT              =  FIRST_ACC_DBL_PROP+1,   // Сортировать по размеру предоставленного кредита в валюте депозита
   SORT_BY_ACCOUNT_PROFIT              =  FIRST_ACC_DBL_PROP+2,   // Сортировать по размеру текущей прибыли на счете в валюте депозита
   SORT_BY_ACCOUNT_EQUITY              =  FIRST_ACC_DBL_PROP+3,   // Сортировать по значению собственных средств на счете в валюте депозита
   SORT_BY_ACCOUNT_MARGIN              =  FIRST_ACC_DBL_PROP+4,   // Сортировать по размеру зарезервированных залоговых средств на счете в валюте депозита
   SORT_BY_ACCOUNT_MARGIN_FREE         =  FIRST_ACC_DBL_PROP+5,   // Сортировать по размеру свободных средств на счете в валюте депозита, доступных для открытия позиции
   SORT_BY_ACCOUNT_MARGIN_LEVEL        =  FIRST_ACC_DBL_PROP+6,   // Сортировать по уровню залоговых средств на счете в процентах
   SORT_BY_ACCOUNT_MARGIN_SO_CALL      =  FIRST_ACC_DBL_PROP+7,   // Сортировать по уровню залоговых средств, при котором требуется пополнение счета (Margin Call)
   SORT_BY_ACCOUNT_MARGIN_SO_SO        =  FIRST_ACC_DBL_PROP+8,   // Сортировать по уровню залоговых средств, при достижении которого происходит принудительное закрытие самой убыточной позиции (Stop Out)
   SORT_BY_ACCOUNT_MARGIN_INITIAL      =  FIRST_ACC_DBL_PROP+9,   // Сортировать по размеру средств, зарезервированных на счёте, для обеспечения гарантийной суммы по всем отложенным ордерам 
   SORT_BY_ACCOUNT_MARGIN_MAINTENANCE  =  FIRST_ACC_DBL_PROP+10,  // Сортировать по размеру средств, зарезервированных на счёте, для обеспечения минимальной суммы по всем открытым позициям
   SORT_BY_ACCOUNT_ASSETS              =  FIRST_ACC_DBL_PROP+11,  // Сортировать по текущему размеру активов на счёте
   SORT_BY_ACCOUNT_LIABILITIES         =  FIRST_ACC_DBL_PROP+12,  // Сортировать по текущему размеру обязательств на счёте
   SORT_BY_ACCOUNT_COMMISSION_BLOCKED  =  FIRST_ACC_DBL_PROP+13,  // Сортировать по текущей сумме заблокированных комиссий по счёту

   SORT_BY_ACCOUNT_NAME                =  FIRST_ACC_STR_PROP,     // Сортировать по имени клиента
   SORT_BY_ACCOUNT_SERVER              =  FIRST_ACC_STR_PROP+1,   // Сортировать по имени торгового сервера
   SORT_BY_ACCOUNT_CURRENCY            =  FIRST_ACC_STR_PROP+2,   // Сортировать по валюте депозита
   SORT_BY_ACCOUNT_COMPANY             =  FIRST_ACC_STR_PROP+3    // Сортировать по имени компании, обслуживающей счет
  };
//+------------------------------------------------------------------+

Так как за один раз у нас вполне могут измениться сразу несколько свойств аккаунта, то чтобы не потерять ни одно из изменений его свойств, будем, как обычно, работать с набором флагов событий, которые будут записываться в переменную-код события, а далее уже будем проверять наличие конкретного флага в составе этой переменной и на основании имеющихся флагов в коде события определять что именно произошло в свойствах аккаунта. Все найденные события будем хранить в массиве, доступ к которому будет предоставлен в классе CEngine, а в последствии — и в программе.

Как можно было заметить, в списке целочисленных свойств аккаунта было добавлено ещё одно свойство — тип торгового сервера. Соответственно, было изменено общее количество целочисленных свойств — с 9 до 10.
В данном свойстве аккаунта будем хранить принадлежность счёта к MetaTrader 5 или MetaTrader 4. Причиной добавления данного свойства послужило то, что в список всех аккаунтов, к которым когда-либо было подключение программы на основе библиотеки, попадают все счета — как для MetaTrader 5, так и для MetaTrader 4 в случае, если запустить программу на основе библиотеки в обоих терминалах. Вот для того, чтобы можно было различать типы торговых серверов, мы и ввели данное свойство, которое будет присутствовать в описании аккаунта, выводимом в журнал методом PrintShort() класса CAccount, а так же будет возможность фильтрации объектов-аккаунтов по их принадлежности к платформам.

Перейдём к файлу Acount.mqh и в приватной секции в структуре данных аккаунта добавим новое свойство и впишем точные размеры массивов для хранения строковых свойств аккаунта:

//+------------------------------------------------------------------+
//| Класс аккаунта                                                   |
//+------------------------------------------------------------------+
class CAccount : public CObject
  {
private:
   struct SData
     {
      //--- Целочисленные свойства счёта
      long           login;                        // ACCOUNT_LOGIN (Номер счёта)
      int            trade_mode;                   // ACCOUNT_TRADE_MODE (Тип торгового счета)
      long           leverage;                     // ACCOUNT_LEVERAGE (Размер предоставленного плеча)
      int            limit_orders;                 // ACCOUNT_LIMIT_ORDERS (Максимально допустимое количество действующих отложенных ордеров)
      int            margin_so_mode;               // ACCOUNT_MARGIN_SO_MODE (Режим задания минимально допустимого уровня залоговых средств)
      bool           trade_allowed;                // ACCOUNT_TRADE_ALLOWED (Разрешенность торговли для текущего счета со стороны сервера)
      bool           trade_expert;                 // ACCOUNT_TRADE_EXPERT (Разрешенность торговли для эксперта со стороны сервера)
      int            margin_mode;                  // ACCOUNT_MARGIN_MODE (Режим расчета маржи)
      int            currency_digits;              // ACCOUNT_CURRENCY_DIGITS (Количество знаков после запятой для валюты счета)
      int            server_type;                  // Тип торгового сервера (MetaTrader5, MetaTrader4)
      //--- Вещественные свойства счёта
      double         balance;                      // ACCOUNT_BALANCE (Баланс счета в валюте депозита)
      double         credit;                       // ACCOUNT_CREDIT (Размер предоставленного кредита в валюте депозита)
      double         profit;                       // ACCOUNT_PROFIT (Размер текущей прибыли на счете в валюте депозита)
      double         equity;                       // ACCOUNT_EQUITY (Значение собственных средств на счете в валюте депозита)
      double         margin;                       // ACCOUNT_MARGIN (Размер зарезервированных залоговых средств на счете  в валюте депозита)
      double         margin_free;                  // ACCOUNT_MARGIN_FREE (Размер свободных средств на счете  в валюте депозита, доступных для открытия позиции)
      double         margin_level;                 // ACCOUNT_MARGIN_LEVEL (Уровень залоговых средств на счете в процентах)
      double         margin_so_call;               // ACCOUNT_MARGIN_SO_CALL (Уровень залоговых средств, при котором происходит MarginCall)
      double         margin_so_so;                 // ACCOUNT_MARGIN_SO_SO (Уровень залоговых средств, при достижении которого происходит StopOut)
      double         margin_initial;               // ACCOUNT_MARGIN_INITIAL (Размер средств, зарезервированных на счёте, для обеспечения гарантийной суммы по всем отложенным ордерам)
      double         margin_maintenance;           // ACCOUNT_MARGIN_MAINTENANCE (Размер средств, зарезервированных на счёте, для обеспечения минимальной суммы по всем открытым позициям)
      double         assets;                       // ACCOUNT_ASSETS (Текущий размер активов на счёте)
      double         liabilities;                  // ACCOUNT_LIABILITIES (Текущий размер обязательств на счёте)
      double         comission_blocked;            // ACCOUNT_COMMISSION_BLOCKED (Текущая сумма заблокированных комиссий по счёту)
      //--- Строковые свойства счёта
      uchar          name[128];                    // ACCOUNT_NAME (Имя клиента)
      uchar          server[64];                   // ACCOUNT_SERVER (Имя торгового сервера)
      uchar          currency[32];                 // ACCOUNT_CURRENCY (Валюта депозита)
      uchar          company[128];                 // ACCOUNT_COMPANY (Имя компании, обслуживающей счет)
     };
   SData             m_struct_obj;                                      // Структура объекта-аккаунта
   uchar             m_uchar_array[];                                   // uchar-массив структуры объекта-аккаунта

В публичной секции класса добавим ещё один метод упрощённого доступа к свойствам объекта-аккаунта, возвращающего тип торгового сервера:

//+------------------------------------------------------------------+
//| Методы упрощённого доступа к свойствам объекта-аккаунта          |
//+------------------------------------------------------------------+
//--- Возвращает целочисленные свойства аккаунта
   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);                                }

В методы описания свойств аккаунта добавим ещё один метод, возвращающий описание типа торгового сервера:

//+------------------------------------------------------------------+
//| Описания свойств объекта-аккаунта                                |
//+------------------------------------------------------------------+
//--- Возвращает описание (1) целочисленного, (2) вещественного и (3) строкового свойства аккаунта
   string            GetPropertyDescription(ENUM_ACCOUNT_PROP_INTEGER property);
   string            GetPropertyDescription(ENUM_ACCOUNT_PROP_DOUBLE property);
   string            GetPropertyDescription(ENUM_ACCOUNT_PROP_STRING property);
//--- Возвращает наименование типа торгового счёта (демо, конкурс, реал)
   string            TradeModeDescription(void)    const;
//--- Возвращает наименование типа торгового сервера (MetaTrader5, MetaTrader4)
   string            ServerTypeDescription(void)    const;
//--- Возвращает описание режима задания минимально допустимого уровня залоговых средств
   string            MarginSOModeDescription(void) const;
//--- Возвращает описание режима расчета маржи
   string            MarginModeDescription(void)   const;
//--- Выводит в журнал описание свойств аккаунта (full_prop=true - все свойства, false - только поддерживаемые - реализуется в наследниках класса)
   void              Print(constbool full_prop=false);
//--- Выводит в журнал краткое описание счёта
   void              PrintShort(void);

В конструкторе класса заполним свойство, хранящее тип торгового сервера:

//+------------------------------------------------------------------+
//| Конструктор                                                      |
//+------------------------------------------------------------------+
CAccount::CAccount(void)
  {
//--- Сохранение целочисленных свойств
   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);
   
//--- Сохранение вещественных свойств
   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);
   
//--- Сохранение строковых свойств
   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);
  }
//+-------------------------------------------------------------------+

Здесь: просто читаем наименование терминала, и если это MetaTrader 5, то вписываем значение 5, иначе — вписываем 4.

В метод создания структуры объекта ObjectToStruct() впишем заполнение нового свойства:

//+------------------------------------------------------------------+
//| Создаёт структуру объекта-аккаунта                               |
//+------------------------------------------------------------------+
bool CAccount::ObjectToStruct(void)
  {
//--- Сохранение целочисленных свойств
   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();
//--- Сохранение вещественных свойств
   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();
//--- Сохранение строковых свойств
   ::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);
   //--- Сохранение структуры в uchar-массив
   ::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;
  }
//+------------------------------------------------------------------+

а в метод создания объекта из структуры StructToObject() впишем получение свойства из структуры:

//+------------------------------------------------------------------+
//| Создаёт объект-аккаунт из структуры                              |
//+------------------------------------------------------------------+
void CAccount::StructToObject(void)
  {
//--- Сохранение целочисленных свойств
   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;
//--- Сохранение вещественных свойств
   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;
//--- Сохранение строковых свойств
   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);
  }
//+------------------------------------------------------------------+

Теперь в методе вывода краткого описания свойств аккаунта допишем вывод типа торгового сервера:

//+------------------------------------------------------------------+
//| Выводит в журнал краткое описание счёта                          |
//+------------------------------------------------------------------+
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);
  }
//+------------------------------------------------------------------+

В метод, возвращающий описание целочисленных свойств аккаунта, допишем описание нового свойства:

//+------------------------------------------------------------------+
//| Возвращает описание целочисленного свойства аккаунта             |
//+------------------------------------------------------------------+
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("Количество знаков после запятой для валюты счета","The number of decimal places in the account currency")+": "+
                                                   (string)this.GetProperty(property)                                                                                                     :
      property==ACCOUNT_PROP_SERVER_TYPE     ?  TextByLanguage("Тип торгового сервера","Type of trading server")+": "+
                                                   (string)this.GetProperty(property)                                                                                                     :
      ""
     );
  }
//+------------------------------------------------------------------+

Напишем реализацию метода, возвращающего наименование типа торгового сервера:

//+------------------------------------------------------------------+
//| Возвращает наименование типа торгового сервера                   |
//+------------------------------------------------------------------+
string CAccount::ServerTypeDescription(void) const
  {
   return(this.ServerType()==5 ? "MetaTrader5" : "MetaTrader4");
  }
//+------------------------------------------------------------------+

Доработка класса CAccount завершена.

Теперь внесём необходимые изменения в класс-коллекцию аккаунтов, так как мы решили, что отслеживание событий будем осуществлять из класса CAccountCollection. Все найденные изменения, произошедшие одновременно, мы будем записывать в int-массив. Для этого воспользуемся готовым классом динамического массива переменных типа int или uint стандартной библиотеки CArrayInt.

Для его использования, подключим файл класса к файлу библиотеки AccountsCollection.mqh:

//+------------------------------------------------------------------+
//|                                           AccountsCollection.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                             https://mql5.com/ru/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright"Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/ru/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Включаемые файлы                                                 |
//+------------------------------------------------------------------+
#include <Arrays\ArrayInt.mqh>
#include "ListObj.mqh"
#include "..\Services\Select.mqh"
#include "..\Objects\Accounts\Account.mqh"
//+------------------------------------------------------------------+
//| Коллекция аккаунтов                                              |
//+------------------------------------------------------------------+
class CAccountsCollection : public CListObj
  {

В качестве небольшого отступления определимся— что же мы хотим получить. Мы хотим знать моменты, когда изменились некоторые свойства счёта. Какое-то свойство может быть включено или выключено — например, разрешение/запрет торговли на счёте в общем, или только разрешение/запрет торговли советниками. О факте изменения данного свойства нам необходимо знать. Так же и уровень наступления MarginCall или StopOut — их изменение может повлиять на успешность выдерживания просадки средств, и т.п. Так же нам необходимо отслеживать прирост и уменьшение средств и баланса для принятия тех или иных решений в советнике.

Если, например, есть некоторое количество открытых позиций, и мы хотим закрывать какие-либо из них при превышении положительной или отрицательной величины средств, то нам нужно сперва задать некий порог, при превышении которого будет сгенерировано событие, и далее уже принимать решение — в зависимости от того прирост это, или падение средств выше заданной величины. Такая же логика применима к текущей прибыли на счёте, размеру баланса, свободной маржи, или к загрузке депозита.

Таким образом, для каких-то свойств нам необходимо иметь свои пороговые уровни прироста и падения и текущее значение свойства, а для других — только значение, которое может либо быть включенным/выключенным, либо измениться/не измениться.

Добавим в приватную секцию класса все необходимые переменные-члены класса для хрениния значений прироста/падения отслеживаемых свойств и их текущего значения, удалим оказавшийся ненужным метод SavePrevValues() и добавим методы инициализации отслеживаемых, и инициализации контролируемых данных аккаунта, метод, проверяющий изменения аккаунта и возвращающий код изменения, метод, устанавливающий тип события и заполняющий список событий, и наконец метод, возвращающий факт наличия флага в событии аккаунта:

//--- Сохраняет текущие значения состояния данных текущего аккаунта как прошлые
   void              SavePrevValues(void)
//+------------------------------------------------------------------+
//| Коллекция аккаунтов                                              |
//+------------------------------------------------------------------+
class CAccountsCollection : public CListObj
  {
private:
   struct MqlDataAccount
     {
      double         hash_sum;               // Хэш-сумма данных аккаунта
      //--- Целочисленные свойства счёта
      long           login;                  // ACCOUNT_LOGIN (Номер счёта)
      long           leverage;               // ACCOUNT_LEVERAGE (Размер предоставленного плеча)
      int            limit_orders;           // ACCOUNT_LIMIT_ORDERS (Максимально допустимое количество действующих отложенных ордеров)
      bool           trade_allowed;          // ACCOUNT_TRADE_ALLOWED (Разрешенность торговли для текущего счета со стороны сервера)
      bool           trade_expert;           // ACCOUNT_TRADE_EXPERT (Разрешенность торговли для эксперта со стороны сервера)
      //--- Вещественные свойства счёта
      double         balance;                // ACCOUNT_BALANCE (Баланс счета в валюте депозита)
      double         credit;                 // ACCOUNT_CREDIT (Размер предоставленного кредита в валюте депозита)
      double         profit;                 // ACCOUNT_PROFIT (Размер текущей прибыли на счете в валюте депозита)
      double         equity;                 // ACCOUNT_EQUITY (Значение собственных средств на счете в валюте депозита)
      double         margin;                 // ACCOUNT_MARGIN (Размер зарезервированных залоговых средств на счете  в валюте депозита)
      double         margin_free;            // ACCOUNT_MARGIN_FREE (Размер свободных средств на счете  в валюте депозита, доступных для открытия позиции)
      double         margin_level;           // ACCOUNT_MARGIN_LEVEL (Уровень залоговых средств на счете в процентах)
      double         margin_so_call;         // ACCOUNT_MARGIN_SO_CALL (Уровень залоговых средств, при котором происходит MarginCall)
      double         margin_so_so;           // ACCOUNT_MARGIN_SO_SO (Уровень залоговых средств, при достижении которого происходит StopOut)
      double         margin_initial;         // ACCOUNT_MARGIN_INITIAL (Размер средств, зарезервированных на счёте, для обеспечения гарантийной суммы по всем отложенным ордерам)
      double         margin_maintenance;     // ACCOUNT_MARGIN_MAINTENANCE (Размер средств, зарезервированных на счёте, для обеспечения минимальной суммы по всем открытым позициям)
      double         assets;                 // ACCOUNT_ASSETS (Текущий размер активов на счёте)
      double         liabilities;            // ACCOUNT_LIABILITIES (Текущий размер обязательств на счёте)
      double         comission_blocked;      // ACCOUNT_COMMISSION_BLOCKED (Текущая сумма заблокированных комиссий по счёту)
     };
   MqlDataAccount    m_struct_curr_account;              // Текущие данные счёта
   MqlDataAccount    m_struct_prev_account;              // Прошлые данные счёта
   
   MqlTick           m_tick;                             // Структура тика
   string            m_symbol;                           // Текущий символ
   long              m_chart_id;                         // Идентификатор графика управляющей программы
   CListObj          m_list_accounts;                    // Список объектов-аккаунтов
   CArrayInt         m_list_changes;                     // Список изменений аккаунта
   string            m_folder_name;                      // Имя папки хранения объектов-аккаунтов
   int               m_index_current;                    // Индекс объекта-аккаунта с данными текущего счёта
//--- Отслеживание изменений аккаунта
   bool              m_is_account_event;                 // Флаг события в данных аккаунта
   int               m_change_code;                      // Код изменения аккаунта
   //--- Плечо
   long              m_changed_leverage_value;           // Величина изменения плеча
   bool              m_is_change_leverage_inc;           // Флаг увеличения плеча
   bool              m_is_change_leverage_dec;           // Флаг уменьшения плеча
   //--- Количество действующих отложенных ордеров
   int               m_changed_limit_orders_value;       // Величина изменения максимально допустимого количества действующих отложенных ордеров
   bool              m_is_change_limit_orders_inc;       // Флаг увеличения максимально допустимого количества действующих отложенных ордеров
   bool              m_is_change_limit_orders_dec;       // Флаг уменьшения максимально допустимого количества действующих отложенных ордеров
   //--- Торговля на счёте
   bool              m_is_change_trade_allowed_on;       // Флаг разрешения торговли для текущего счета со стороны сервера
   bool              m_is_change_trade_allowed_off;      // Флаг запрета торговли для текущего счета со стороны сервера
   //--- Автоторговля на счёте
   bool              m_is_change_trade_expert_on;        // Флаг разрешения торговли для эксперта со стороны сервера
   bool              m_is_change_trade_expert_off;       // Флаг запрета торговли для эксперта со стороны сервера
   //--- Баланс
   double            m_control_balance_inc;              // Контролируемая величина прироста баланса
   double            m_control_balance_dec;              // Контролируемая величина уменьшения баланса
   double            m_changed_balance_value;            // Величина изменения баланса
   bool              m_is_change_balance_inc;            // Флаг изменения баланса больше, чем на величину прироста
   bool              m_is_change_balance_dec;            // Флаг изменения баланса больше, чем на величину уменьшения
   //--- Кредит
   double            m_changed_credit_value;             // Величина изменения кредита
   bool              m_is_change_credit_inc;             // Флаг увеличения кредита
   bool              m_is_change_credit_dec;             // Флаг уменьшения кредита
   //--- Профит
   double            m_control_profit_inc;               // Контролируемая величина прироста прибыли
   double            m_control_profit_dec;               // Контролируемая величина уменьшения прибыли
   double            m_changed_profit_value;             // Величина изменения прибыли
   bool              m_is_change_profit_inc;             // Флаг изменения прибыли больше, чем на величину прироста
   bool              m_is_change_profit_dec;             // Флаг изменения прибыли больше, чем на величину уменьшения
   //--- Средства
   double            m_control_equity_inc;               // Контролируемая величина прироста средств
   double            m_control_equity_dec;               // Контролируемая величина уменьшения средств
   double            m_changed_equity_value;             // Величина изменения средств
   bool              m_is_change_equity_inc;             // Флаг изменения средств больше, чем на величину прироста
   bool              m_is_change_equity_dec;             // Флаг изменения средств больше, чем на величину уменьшения
   //--- Маржа
   double            m_control_margin_inc;               // Контролируемая величина прироста залоговых средств
   double            m_control_margin_dec;               // Контролируемая величина уменьшения залоговых средств
   double            m_changed_margin_value;             // Величина изменения залоговых средств
   bool              m_is_change_margin_inc;             // Флаг изменения залоговых средств больше, чем на величину прироста
   bool              m_is_change_margin_dec;             // Флаг изменения залоговых средств больше, чем на величину уменьшения
   //--- Свободная маржа
   double            m_control_margin_free_inc;          // Контролируемая величина прироста свободных средств
   double            m_control_margin_free_dec;          // Контролируемая величина уменьшения свободных средств
   double            m_changed_margin_free_value;        // Величина изменения свободных средств
   bool              m_is_change_margin_free_inc;        // Флаг изменения свободных средств больше, чем на величину прироста
   bool              m_is_change_margin_free_dec;        // Флаг изменения свободных средств больше, чем на величину уменьшения
   //--- Уровень маржи
   double            m_control_margin_level_inc;         // Контролируемая величина прироста уровня свободных средств
   double            m_control_margin_level_dec;         // Контролируемая величина уменьшения уровня свободных средств
   double            m_changed_margin_level_value;       // Величина изменения уровня свободных средств
   bool              m_is_change_margin_level_inc;       // Флаг изменения уровня свободных средств больше, чем на величину прироста
   bool              m_is_change_margin_level_dec;       // Флаг изменения уровня свободных средств больше, чем на величину уменьшения
   //--- Margin Call
   double            m_changed_margin_so_call_value;     // Величина изменения уровня Margin Call
   bool              m_is_change_margin_so_call_inc;     // Флаг увеличения уровня Margin Call
   bool              m_is_change_margin_so_call_dec;     // Флаг уменьшения уровня Margin Call
   //--- MarginStopOut
   double            m_changed_margin_so_so_value;       // Величина изменения уровня Margin StopOut
   bool              m_is_change_margin_so_so_inc;       // Флаг увеличения уровня Margin StopOut
   bool              m_is_change_margin_so_so_dec;       // Флаг уменьшения уровня Margin StopOut
   //--- Гарантийная сумма по отложенным ордерам
   double            m_control_margin_initial_inc;       // Контролируемая величина прироста размера зарезервированных средств для обеспечения гарантийной суммы по отложенным ордерам
   double            m_control_margin_initial_dec;       // Контролируемая величина уменьшения размера зарезервированных средств для обеспечения гарантийной суммы по отложенным ордерам
   double            m_changed_margin_initial_value;     // Величина изменения размера зарезервированных средств для обеспечения гарантийной суммы по отложенным ордерам
   bool              m_is_change_margin_initial_inc;     // Флаг изменения размера зарезервированных средств для обеспечения гарантийной суммы по отложенным ордерам больше, чем на величину прироста
   bool              m_is_change_margin_initial_dec;     // Флаг изменения размера зарезервированных средств для обеспечения гарантийной суммы по отложенным ордерам больше, чем на величину уменьшения
   //--- Гарантийная сумма по открытым позициям
   double            m_control_margin_maintenance_inc;   // Контролируемая величина прироста зарезервированных средств для обеспечения минимальной суммы по всем открытым позициям
   double            m_control_margin_maintenance_dec;   // Контролируемая величина уменьшения зарезервированных средств для обеспечения минимальной суммы по всем открытым позициям
   double            m_changed_margin_maintenance_value; // Величина изменения размера зарезервированных средств для обеспечения минимальной суммы по всем открытым позициям
   bool              m_is_change_margin_maintenance_inc; // Флаг изменения размера зарезервированных средств для обеспечения минимальной суммы по всем открытым позициям
   bool              m_is_change_margin_maintenance_dec; // Флаг изменения размера зарезервированных средств для обеспечения минимальной суммы по всем открытым позициям
   //--- Активы
   double            m_control_assets_inc;               // Контролируемая величина прироста активов
   double            m_control_assets_dec;               // Контролируемая величина уменьшения активов
   double            m_changed_assets_value;             // Величина изменения активов
   bool              m_is_change_assets_inc;             // Флаг изменения активов больше, чем на величину прироста
   bool              m_is_change_assets_dec;             // Флаг изменения активов больше, чем на величину уменьшения
   //--- Обязательства
   double            m_control_liabilities_inc;          // Контролируемая величина прироста обязательств
   double            m_control_liabilities_dec;          // Контролируемая величина уменьшения обязательств
   double            m_changed_liabilities_value;        // Величина изменения обязательств
   bool              m_is_change_liabilities_inc;        // Флаг изменения обязательств больше, чем на величину прироста
   bool              m_is_change_liabilities_dec;        // Флаг изменения обязательств больше, чем на величину уменьшения
   //--- Заблокированные комиссии
   double            m_control_comission_blocked_inc;    // Контролируемая величина прироста заблокированных комиссий
   double            m_control_comission_blocked_dec;    // Контролируемая величина уменьшения заблокированных комиссий
   double            m_changed_comission_blocked_value;  // Величина изменения заблокированных комиссий
   bool              m_is_change_comission_blocked_inc;  // Флаг изменения заблокированных комиссий больше, чем на величину прироста
   bool              m_is_change_comission_blocked_dec;  // Флаг изменения заблокированных комиссий больше, чем на величину уменьшения
   
//--- Инициализирует переменные (1) отслеживаемых, (2) контролируемых данных аккаунта
   void              InitChangesParams(void); 
   void              InitControlsParams(void);
//--- Проверяет изменения аккаунта, возвращает код изменения
   int               SetChangeCode(void);
//--- Устанавливает тип события и заполняет список событий
   void              SetTypeEvent(void);
//--- возвращает факт наличия флага в событии аккаунта
   bool              IsPresentEventFlag(constint change_code)   const { return (this.m_change_code & change_code)==change_code;               }
//--- Записывает данные текущего счёта в свойства объекта-аккаунта
   void              SetAccountsParams(CAccount* account);

//--- Проверяет наличие объекта-аккаунта в списке-коллекции
   bool              IsPresent(CAccount* account);
//--- Находит и возвращает индекс объекта-аккаунта с данными текущего счёта
   int               Index(void);
public:

Метод SavePrevValues() оказался невостребованным, так как при определении факта изменения свойств аккаунта нам необходимо сразу же записывать в структуру, хранящую данные о прошлом состоянии изменённого свойства, новое значение свойства, и при этом оставить остальные свойства теми, которые были на прошлой проверке — до момента фактического их изменения. А метод SavePrevValues() сохранял сразу все свойства аккаунта как прошлые, тем самым нарушая отслеживание конкретной величины изменения, установленной по умолчанию для каждого из отслеживаемых свойств, и которые можно задать отдельно — каждый своим методом установки размера отслеживаемой величины.

За пределами тела класса напишем метод инициализации отслеживаемых данных:

//+------------------------------------------------------------------+
//| Инициализирует переменные отслеживаемых данных аккаунта          |
//+------------------------------------------------------------------+
void CAccountsCollection::InitChangesParams(void)
  {
//--- Список и код изменений
   this.m_list_changes.Clear();                    // Очищаем список изменений
   this.m_list_changes.Sort();                     // Сортируем список изменений
//--- Плечо
   this.m_changed_leverage_value=0;                // Величина изменения плеча
   this.m_is_change_leverage_inc=false;            // Флаг увеличения плеча
   this.m_is_change_leverage_dec=false;            // Флаг уменьшения плеча
//--- Количество действующих отложенных ордеров
   this.m_changed_limit_orders_value=0;            // Величина изменения максимально допустимого количества действующих отложенных ордеров
   this.m_is_change_limit_orders_inc=false;        // Флаг увеличения максимально допустимого количества действующих отложенных ордеров
   this.m_is_change_limit_orders_dec=false;        // Флаг уменьшения максимально допустимого количества действующих отложенных ордеров
//--- Торговля на счёте
   this.m_is_change_trade_allowed_on=false;        // Флаг разрешения торговли для текущего счета со стороны сервера
   this.m_is_change_trade_allowed_off=false;       // Флаг запрета торговли для текущего счета со стороны сервера
//--- Автоторговля на счёте
   this.m_is_change_trade_expert_on=false;         // Флаг разрешения торговли для эксперта со стороны сервера
   this.m_is_change_trade_expert_off=false;        // Флаг запрета торговли для эксперта со стороны сервера
//--- Баланс
   this.m_changed_balance_value=0;                 // Величина изменения баланса
   this.m_is_change_balance_inc=false;             // Флаг изменения баланса больше, чем на величину прироста
   this.m_is_change_balance_dec=false;             // Флаг изменения баланса больше, чем на величину уменьшения
//--- Кредит
   this.m_changed_credit_value=0;                  // Величина изменения кредита
   this.m_is_change_credit_inc=false;              // Флаг увеличения кредита
   this.m_is_change_credit_dec=false;              // Флаг уменьшения кредита
//--- Прибыль
   this.m_changed_profit_value=0;                  // Величина изменения прибыли
   this.m_is_change_profit_inc=false;              // Флаг изменения прибыли больше, чем на величину прироста
   this.m_is_change_profit_dec=false;              // Флаг изменения прибыли больше, чем на величину уменьшения
//--- Средства
   this.m_changed_equity_value=0;                  // Величина изменения средств
   this.m_is_change_equity_inc=false;              // Флаг изменения средств больше, чем на величину прироста
   this.m_is_change_equity_dec=false;              // Флаг изменения средств больше, чем на величину уменьшения
//--- Маржа
   this.m_changed_margin_value=0;                  // Величина изменения залоговых средств
   this.m_is_change_margin_inc=false;              // Флаг изменения залоговых средств больше, чем на величину прироста
   this.m_is_change_margin_dec=false;              // Флаг изменения залоговых средств больше, чем на величину уменьшения
//--- Свободная маржа
   this.m_changed_margin_free_value=0;             // Величина изменения свободных средств
   this.m_is_change_margin_free_inc=false;         // Флаг изменения свободных средств больше, чем на величину прироста
   this.m_is_change_margin_free_dec=false;         // Флаг изменения свободных средств больше, чем на величину уменьшения
//--- Уровень маржи
   this.m_changed_margin_level_value=0;            // Величина изменения уровня свободных средств
   this.m_is_change_margin_level_inc=false;        // Флаг изменения уровня свободных средств больше, чем на величину прироста
   this.m_is_change_margin_level_dec=false;        // Флаг изменения уровня свободных средств больше, чем на величину уменьшения
//--- Уровень Margin Call
   this.m_changed_margin_so_call_value=0;          // Величина изменения уровня Margin Call
   this.m_is_change_margin_so_call_inc=false;      // Флаг увеличения уровня Margin Call
   this.m_is_change_margin_so_call_dec=false;      // Флаг уменьшения уровня Margin Call
//--- Уровень Margin StopOut
   this.m_changed_margin_so_so_value=0;            // Величина изменения уровня Margin StopOut
   this.m_is_change_margin_so_so_inc=false;        // Флаг увеличения уровня Margin StopOut
   this.m_is_change_margin_so_so_dec=false;        // Флаг уменьшения уровня Margin StopOut
//--- Гарантийная сумма по отложенным ордерам
   this.m_changed_margin_initial_value=0;          // Величина изменения размера зарезервированных средств для обеспечения гарантийной суммы по отложенным ордерам
   this.m_is_change_margin_initial_inc=false;      // Флаг изменения размера зарезервированных средств для обеспечения гарантийной суммы по отложенным ордерам больше, чем на величину прироста
   this.m_is_change_margin_initial_dec=false;      // Флаг изменения размера зарезервированных средств для обеспечения гарантийной суммы по отложенным ордерам больше, чем на величину уменьшения
//--- Гарантийная сумма по открытым позициям
   this.m_changed_margin_maintenance_value=0;      // Величина изменения размера зарезервированных средств для обеспечения минимальной суммы по всем открытым позициям
   this.m_is_change_margin_maintenance_inc=false;  // Флаг изменения размера зарезервированных средств для обеспечения минимальной суммы по всем открытым позициям
   this.m_is_change_margin_maintenance_dec=false;  // Флаг изменения размера зарезервированных средств для обеспечения минимальной суммы по всем открытым позициям
//--- Активы
   this.m_changed_assets_value=0;                  // Величина изменения активов
   this.m_is_change_assets_inc=false;              // Флаг изменения активов больше, чем на величину прироста
   this.m_is_change_assets_dec=false;              // Флаг изменения активов больше, чем на величину уменьшения
//--- Обязательства
   this.m_changed_liabilities_value=0;             // Величина изменения обязательств
   this.m_is_change_liabilities_inc=false;         // Флаг изменения обязательств больше, чем на величину прироста
   this.m_is_change_liabilities_dec=false;         // Флаг изменения обязательств больше, чем на величину уменьшения
//--- Заблокированные комиссии
   this.m_changed_comission_blocked_value=0;       // Величина изменения заблокированных комиссий
   this.m_is_change_comission_blocked_inc=false;   // Флаг изменения заблокированных комиссий больше, чем на величину прироста
   this.m_is_change_comission_blocked_dec=false;   // Флаг изменения заблокированных комиссий больше, чем на величину уменьшения
  }
//+------------------------------------------------------------------+

Метод инициализации контролируемых данных:

//+------------------------------------------------------------------+
//| Инициализирует переменные контролируемых  данных аккаунта        |
//+------------------------------------------------------------------+
void CAccountsCollection::InitControlsParams(void)
  {
//--- Баланс
   this.m_control_balance_inc=50;                  // Контролируемая величина прироста баланса
   this.m_control_balance_dec=50;                  // Контролируемая величина уменьшения баланса
//--- Прибыль
   this.m_control_profit_inc=20;                   // Контролируемая величина прироста прибыли
   this.m_control_profit_dec=20;                   // Контролируемая величина уменьшения прибыли
//--- Средства
   this.m_control_equity_inc=15;                   // Контролируемая величина прироста средств
   this.m_control_equity_dec=15;                   // Контролируемая величина уменьшения средств
//--- Маржа
   this.m_control_margin_inc=1000;                 // Контролируемая величина прироста залоговых средств
   this.m_control_margin_dec=1000;                 // Контролируемая величина уменьшения залоговых средств
//--- Свободная маржа
   this.m_control_margin_free_inc=1000;            // Контролируемая величина прироста свободных средств
   this.m_control_margin_free_dec=1000;            // Контролируемая величина уменьшения свободных средств
//--- Уровень маржи
   this.m_control_margin_level_inc=10000;          // Контролируемая величина прироста уровня свободных средств
   this.m_control_margin_level_dec=500;            // Контролируемая величина уменьшения уровня свободных средств
//--- Гарантийная сумма по отложенным ордерам
   this.m_control_margin_initial_inc=1000;         // Контролируемая величина прироста размера зарезервированных средств для обеспечения гарантийной суммы по отложенным ордерам
   this.m_control_margin_initial_dec=1000;         // Контролируемая величина уменьшения размера зарезервированных средств для обеспечения гарантийной суммы по отложенным ордерам
//--- Гарантийная сумма по открытым позициям
   this.m_control_margin_maintenance_inc=1000;     // Контролируемая величина прироста зарезервированных средств для обеспечения минимальной суммы по всем открытым позициям
   this.m_control_margin_maintenance_dec=1000;     // Контролируемая величина уменьшения зарезервированных средств для обеспечения минимальной суммы по всем открытым позициям
//--- Активы
   this.m_control_assets_inc=1000;                 // Контролируемая величина прироста активов
   this.m_control_assets_dec=1000;                 // Контролируемая величина уменьшения активов
//--- Обязательства
   this.m_control_liabilities_inc=1000;            // Контролируемая величина прироста обязательств
   this.m_control_liabilities_dec=1000;            // Контролируемая величина уменьшения обязательств
//--- Заблокированные комиссии
   this.m_control_comission_blocked_inc=1000;      // Контролируемая величина прироста заблокированных комиссий
   this.m_control_comission_blocked_dec=1000;      // Контролируемая величина уменьшения заблокированных комиссий
  }
//+------------------------------------------------------------------+

В данном методе мы просто инициализируем переменные значениями по умолчанию. При превышении свойствами значений, вписанных в эти переменные будет сгенерировано соответствующее событие аккаунта. Эти свойства можно будет также установить непосредственно вызовом соответствующих методов установки контролируемых свойств аккаунта.

Метод, проверяющий факт изменения свойств аккаунта, и заполняющий код события соответствующими изменению флагами:

//+------------------------------------------------------------------+
//| Проверяет изменения аккаунта, возвращает код изменения           |
//+------------------------------------------------------------------+
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;
  }
//+------------------------------------------------------------------+

В методе сначала обнуляется код события, затем сравниваются значения контролируемых параметров аккаунта в структуре текущих данных и структуре прошлых данных. Если данные не равны, то к коду события добавляется соответствующий флаг.

Метод, устанавливающий тип события, и добавляющий событие в список изменений аккаунта:

//+------------------------------------------------------------------+
//| Устанавливает тип события объекта-аккаунта                       |
//+------------------------------------------------------------------+
void CAccountsCollection::SetTypeEvent(void)
  {
   this.InitChangesParams();
   ENUM_ACCOUNT_EVENT event=ACCOUNT_EVENT_NO_EVENT;
//--- Изменение разрешения торговли на счёте
   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);
     }
//--- Изменение разрешения автоторговли на счёте
   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);
     }
//--- Изменение предоставленного плеча
   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;
     }
//--- Изменение максимально допустимого количества действующих отложенных ордеров
   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;
     }
//--- Изменение размера предоставленного кредита в валюте депозита
   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;
     }
//--- Изменение уровня залоговых средств, при котором происходит MarginCall
   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;
     }
//--- Изменение уровня залоговых средств, при достижении которого происходит StopOut
   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;
     }
//--- Изменение баланса больше заданной величины в +/-
   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;
        }
     }
//--- Изменение профита больше заданной величины в +/-
   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;
        }
     }
//--- Изменение собственных средств больше заданной величины в +/-
   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;
        }
     }
//--- Изменение размера зарезервированных залоговых средств на счете в валюте депозита больше заданной величины в +/-
   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;
        }
     }
//--- Изменение размера средств на счете в валюте депозита, доступных для открытия позиции больше заданной величины в +/-
   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;
        }
     }
//--- Изменение уровня залоговых средств на счете в процентах больше заданной величины в +/-
   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;
        }
     }
//--- Изменение размера средств, зарезервированных на счёте, для обеспечения гарантийной суммы по всем отложенным ордерам больше заданной величины в +/-
   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;
        }
     }
//--- Изменение размера средств, зарезервированных на счёте, для обеспечения минимальной суммы по всем открытым позициям больше заданной величины в +/-
   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;
        }
     }
//--- Изменение текущего размера активов на счёте больше заданной величины в +/-
   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;
        }
     }
//--- Изменение текущего размера обязательств на счёте больше заданной величины в +/-
   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;
        }
     }
//--- Изменение текущей суммы заблокированных комиссий по счёту больше заданной величины в +/-
   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;
        }
     }
  }
//+------------------------------------------------------------------+

В методе есть два типа логики определения событий:

  1. простое отслеживание разрешения/запрета или изменения/неизменения свойства,
  2. отслеживание изменения больше заданной величины как в сторону увеличения значения, так и в сторону его уменьшения.

Так как метод дотаточно объёмен, то рассмотрим его работу на примере двух типов определения событий аккаунта:

Сначала обнуляются все флаги и данные об изменениях и тип события устанавливается в ноль.
Затем для первого типа  логики (на примере разрешения торговли на счёте):

  • проверяем наличие флага изменения разрешения торговли на счёте в коде события
  • если на данный момент торговля не разрешена — значит только что было отключено разрешение
    • устанавливаем флаг запрета торговли на счёте
    • устанавливаем событие "торговля на счёте запрещена"
    • сохраняем в структуру прошлых данных текущее состояние данного свойства счёта для последующей проверки
  • иначе, если на данный момент торговля разрешена
    • устанавливаем флаг разрешения торговли на счёте
    • устанавливаем событие "торговля на счёте разрешена"
    • сохраняем в структуру прошлых данных текущее состояние данного свойства счёта для последующей проверки
  • если такого события ещё нет в списке изменений
  • добавляем событие в список

Для второго типа логики (на примере изменения суммы заблокированных комиссий):

  • проверяем наличие флага изменения суммы заблокированных комиссий
  • рассчитываем величину изменения суммы заблокированных комиссий
  • если величина изменения больше контролируемой величины прироста
    • устанавливаем флаг прироста суммы заблокированных комиссий
    • устанавливаем событие "сумма заблокированных комиссий увеличена больше заданного значения"
    • если такого события ещё нет в списке изменений и событие успешно добавлено в список
      • сохраняем в структуру прошлых данных текущее состояние данного свойства счёта для последующей проверки
  • иначе, если величина изменения больше контролируемой величины уменьшения
    • устанавливаем флаг уменьшения суммы заблокированных комиссий
    • устанавливаем событие "сумма заблокированных комиссий уменьшена больше заданного значения"
    • если такого события ещё нет в списке изменений и событие успешно добавлено в список
      • сохраняем в структуру прошлых данных текущее состояние данного свойства счёта для последующей проверки

В публичную секцию класса впишем методы, возвращающие код события аккаунта, список событий аккаунта, событие аккаунта по его индексу в списке; методы, устанавливающий и возвращающий символ, метод, возвращающий идентификатор графика управляющей программы, и метод, возвращающий описание события аккаунта. А так же добавим методы получения и установки параметров отслеживаемых изменений:

public:
//--- Возвращает полный список-коллекцию аккаунтов "как есть"
   CArrayObj        *GetList(void)                                                                          { return &this.m_list_accounts;                                         }
//--- Возвращает список по выбранному (1) целочисленному, (2) вещественному и (3) строковому свойству, удовлетворяющему сравниваемому критерию
   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);}
//--- Возвращает (1) индекс текущего объекта-аккаунта, (2) флаг произошедшего события в данных аккаунта, (3) тип события
   int               IndexCurrentAccount(void)                                                        const { return this.m_index_current;                                           }
   bool              IsAccountEvent(void)                                                             const { return this.m_is_account_event;                                        }
//--- Возвращает (1) код события объекта-аккаунта, (2) список событий, (3) событие аккаунта по его номеру в списке
   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) Устанавливает, (2) возвращает текущий символ
   void              SetSymbol(conststring symbol)                                                          { this.m_symbol=symbol;                                 }
   string            GetSymbol(void)                                                                  const { return this.m_symbol;                                 }
//--- Устанавливает идентификатор графика управляющей программы
   void              SetChartID(constlong id)                                                               { this.m_chart_id=id;                                   }

//--- Конструктор, деструктор
                     CAccountsCollection();
                    ~CAccountsCollection();
//--- Добавляет объект-аккаунт в список
   bool              AddToList(CAccount* account);
//--- (1) Сохраняет объекты-аккаунты из списка в файлы
//--- (2) Загружает объекты-аккаунты из файлов в список
   bool              SaveObjects(void);
   bool              LoadObjects(void);
//--- Возвращает описание события аккаунта
   string            EventDescription(const ENUM_ACCOUNT_EVENT event);
//--- Обновляет данные текущего аккаунта
   void              Refresh(void);
   
//--- Получение и установка параметров отслеживаемых изменений
   //--- Плечо:
   //--- (1) Величина изменения плеча, (2) Флаг увеличения плеча, (3) Флаг уменьшения плеча
   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;                  }
   //--- Количество действующих отложенных ордеров:
   //--- (1) Величина изменения, (2) Флаг увеличения, (3) Флаг уменьшения
   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;              }
   //--- Торговля на счёте:
   //--- (1) Флаг разрешения торговли для текущего счета, (2) Флаг запрета торговли для текущего счета со стороны сервера
   bool              IsOnTradeAllowed(void)                                                           const { return this.m_is_change_trade_allowed_on;              }
   bool              IsOffTradeAllowed(void)                                                          const { return this.m_is_change_trade_allowed_off;             }
   //--- Автоторговля на счёте:
   //--- (1) Флаг разрешения торговли для эксперта, (2) Флаг запрета торговли для эксперта со стороны сервера
   bool              IsOnTradeExpert(void)                                                            const { return this.m_is_change_trade_expert_on;               }
   bool              IsOffTradeExpert(void)                                                           const { return this.m_is_change_trade_expert_off;              }
   //--- Баланс:
   //--- установка контролируемой величины (1) прироста, (2) уменьшения баланса
   //--- получение (3) величины изменения баланса,
   //--- получение флага изменения баланса больше, чем на величину (4) прироста, (5) уменьшения
   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;                   }
   //--- Кредит:
   //--- получение (1) величины изменения кредита, (2) флага увеличения кредита, (3) флага уменьшения кредита
   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;                    }
   //--- Профит:
   //--- установка контролируемой величины (1) прироста, (2) уменьшения прибыли
   //--- получение (3) величины изменения прибыли,
   //--- получение флага изменения прибыли больше, чем на величину (4) прироста, (5) уменьшения
   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;                    }
   //--- Средства:
   //--- установка контролируемой величины (1) прироста, (2) уменьшения средств
   //--- получение (3) величины изменения средств,
   //--- получение флага изменения средств больше, чем на величину (4) прироста, (5) уменьшения
   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;                    }
   //--- Маржа:
   //--- установка контролируемой величины (1) прироста, (2) уменьшения залоговых средств
   //--- получение (3) величины изменения залоговых средств,
   //--- получение флага изменения залоговых средств больше, чем на величину (4) прироста, (5) уменьшения
   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;                    }
   //--- Свободная маржа:
   //--- установка контролируемой величины (1) прироста, (2) уменьшения свободных средств
   //--- получение (3) величины изменения свободных средств,
   //--- получение флага изменения свободных средств больше, чем на величину (4) прироста, (5) уменьшения
   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;               }
   //--- Уровень маржи:
   //--- установка контролируемой величины (1) прироста, (2) уменьшения уровня свободных средств
   //--- получение (3) величины изменения уровня свободных средств,
   //--- получение флага изменения уровня свободных средств больше, чем на величину (4) прироста, (5) уменьшения
   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:
   //--- получение (1) величины изменения Margin Call, (2) флага увеличения уровня Margin Call, (3) флага уменьшения уровня Margin Call
   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:
   //--- получение (1) величины изменения Margin StopOut, (2) флага увеличения уровня Margin StopOut, (3) флага уменьшения уровня Margin StopOut
   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;              }
   //--- Гарантийная сумма по отложенным ордерам:
   //--- установка контролируемой величины (1) прироста, (2) уменьшения размера зарезервированных средств гарантийной суммы по отложенным ордерам
   //--- получение (3) величины изменения уровня зарезервированных средств,
   //--- получение флага изменения уровня зарезервированных средств больше, чем на величину (4) прироста, (5) уменьшения
   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;            }
   //--- Гарантийная сумма по открытым позициям:
   //--- установка контролируемой величины (1) прироста, (2) уменьшения размера зарезервированных средств гарантийной суммы по открытым позициям
   //--- получение (3) величины изменения уровня зарезервированных средств,
   //--- получение флага изменения уровня зарезервированных средств больше, чем на величину (4) прироста, (5) уменьшения
   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;        }
   //--- Активы:
   //--- установка контролируемой величины (1) прироста, (2) уменьшения активов
   //--- получение (3) величины изменения уровня активов,
   //--- получение флага изменения уровня активов больше, чем на величину (4) прироста, (5) уменьшения
   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;                    }
   //--- Обязательства:
   //--- установка контролируемой величины (1) прироста, (2) уменьшения обязательств
   //--- получение (3) величины изменения уровня обязательств,
   //--- получение флага изменения уровня обязательств больше, чем на величину (4) прироста, (5) уменьшения
   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;               }
   //--- Заблокированные комиссии:
   //--- установка контролируемой величины (1) прироста, (2) уменьшения заблокированных комиссий
   //--- получение (3) величины изменения уровня заблокированных комиссий,
   //--- получение флага изменения уровня заблокированных комиссий больше, чем на величину (4) прироста, (5) уменьшения
   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;         }
  };
//+------------------------------------------------------------------+

За пределами тела класса напишем реализацию метода, возвращающего событие аккаунта по его номеру в списке:

//+------------------------------------------------------------------+
//| Возвращает событие аккаунта по его номеру в списке               |
//+------------------------------------------------------------------+
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);
  }
//+------------------------------------------------------------------+

События в списке изменений свойств аккаунта располагаются в порядке их добавления — самое первое находится по индексу 0, а самое последнее — по индексу (размер_списка-1). Нам же нужно, чтобы пользователь мог получить искомое событие как в таймсерии — в нулевом индексе должно находиться самое последнее событие. Для этого в методе сделан расчёт индекса: index = (размер_списка - номер_искомого_события-1). При таком расчёте, если передать 0, то будет возвращено последнее событие в списке, если 1, то предпоследнее, если передать число, превышающее размер списка, то будет вохвращено последнее событие.

Итак: в метод передаётся индекс искомого события.
Сначала проверяем количество событий в списке, и если их нет, то возвращаем отсутствие события.
Далее проверяем индекс искомого события, и если переданное значение меньше нуля, или выходит за пределы размера массива, то индекс будет указывать на последнее событие в списке, иначе — рассчитываем индекс события в списке в соответствии с правилом: если в метод передан 0, то значит хотим получить последнее событие (как в таймсерии), если 1 — то предпоследнее, и т.д. Либо, если необходимо получить последнее событие, то можно передать в качестве входного параметра индекса значение -1.
Далее получаем событие из списка по рассчитанному индексу и возвращаем его.
Если событие получить не удалось, то возвращается NULL, а это значит, что прежде, чем использовать результат работы метода, необходимо проверить его на валидность.

Напишем реализацию метода, возвращающего описание события аккаунта:

//+------------------------------------------------------------------+
//| Возвращает описание события аккаунта                             |
//+------------------------------------------------------------------+
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 the account is allowed now")                                                                                                                        :
      event==ACCOUNT_EVENT_TRADE_ALLOWED_OFF       ? TextByLanguage("Торговля на счёте запрещена","Trading on the account is prohibited now")                                                                                                                     :
      event==ACCOUNT_EVENT_TRADE_EXPERT_ON         ? TextByLanguage("Автоторговля на счёте разрешена","Autotrading on the account is allowed now")                                                                                                                :
      event==ACCOUNT_EVENT_TRADE_EXPERT_OFF        ? TextByLanguage("Автоторговля на счёте запрещена","Autotrade on the account is 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 summ for pending orders increased by ")+::DoubleToString(this.GetValueChangedMarginInitial(),dg)+curency+" ("+::DoubleToString(account.MarginInitial(),dg)+curency+")" :
      event==ACCOUNT_EVENT_MARGIN_INITIAL_DEC      ?
         TextByLanguage("Гарантийная сумма по отложенным ордерам уменьшена на ","Guarantee summ for pending orders decreased by ")+::DoubleToString(this.GetValueChangedMarginInitial(),dg)+curency+" ("+::DoubleToString(account.MarginInitial(),dg)+curency+")" :
      event==ACCOUNT_EVENT_MARGIN_MAINTENANCE_INC  ?
         TextByLanguage("Гарантийная сумма по позициям увеличена на ","Guarantee summ for positions increased by ")+::DoubleToString(this.GetValueChangedMarginMaintenance(),dg)+curency+" ("+::DoubleToString(account.MarginMaintenance(),dg)+curency+")"        :
      event==ACCOUNT_EVENT_MARGIN_MAINTENANCE_DEC  ?
         TextByLanguage("Гарантийная сумма по позициям уменьшена на ","Guarantee summ 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 comissions increased by ")+::DoubleToString(this.GetValueChangedComissionBlocked(),dg)+" ("+::DoubleToString(account.ComissionBlocked(),dg)+")"                                   :
      event==ACCOUNT_EVENT_COMISSION_BLOCKED_DEC   ?
         TextByLanguage("Размер заблокированных комиссий уменьшен на ","Blocked comissions decreased by ")+::DoubleToString(this.GetValueChangedComissionBlocked(),dg)+" ("+::DoubleToString(account.ComissionBlocked(),dg)+")"                                   :
      ::EnumToString(event)
     );
  }
//+------------------------------------------------------------------+

Здесь: в метод передаётся событие аккаунта, описание которого необходимо получить. Проверяем размер списка объектов-аккаунтов, и если он пуст, то возвращаем описание ошибки. Так как мы можем отслеживать только события текущего аккаунта, то получаем текущий объект-аккаунт из списка аккаунтов по индексу текущего объекта-аккаунта. Если объект получить не удалось, то так же возвращаем описание ошибки.
Далее получаем необходимые свойства аккаунта для корректного вывода описания события, проверяем событие и возвращаем его описание.

В конструкторе класса, в его списке инициализации инициализируем переменные символа и идентификатора графика управляющей программы значениями по умолчанию — текущий символ и текущий график, очищаем структуру тика, которая потребуется нам для определения времени события, и инициализируем изменяемые и контролируемые параметры аккаунта:

//+------------------------------------------------------------------+
//| Конструктор                                                      |
//+------------------------------------------------------------------+
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();
//--- Создание папки хранения файлов аккаунтов
   ::ResetLastError();
   if(!::FolderCreate(this.m_folder_name,FILE_COMMON))
      Print(DFUN,TextByLanguage("Не удалось создать папку хранения файлов. Ошибка ","Could not create file storage folder. Error "),::GetLastError());
//--- Создание и добавление в список объекта-аккаунта текущего счёта
   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."));

//--- Загрузка объектов-аккаунтов из файлов в коллекцию
   this.LoadObjects();
//--- Сохранение индекса текущего аккаунта
   this.m_index_current=this.Index();
  }
//+------------------------------------------------------------------+

И наконец, впишем в метод обновления данных аккаунта отслеживание изменений его свойств:

//+------------------------------------------------------------------+
//| Обновляет данные текущего аккаунта                               |
//+------------------------------------------------------------------+
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);
   
//--- Первый запуск
   if(!this.m_struct_prev_account.login)
     {
      this.m_struct_prev_account=this.m_struct_curr_account;
      return;
     }
//--- Если хэш-сумма аккаунта изменилась
   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;
     }
  }
//+------------------------------------------------------------------+
Первым делом сбросим флаг события аккаунта. Так как мы удалили метод SavePrevValues(), то вместо него впишем строку, которая была прописана в удалённом методе — сохранение структуры текущих данных в структуре прошлых данных при первом запуске. При изменении хеш-суммы, сразу же проверим и установим код события и тип произошедшего события.

В методе установки типа события, в список изменений вносятся сразу все события, которые произошли одновременно в свойствах счёта. Поэтому мы сначала проверим размер списка изменений, и если он не пустой, то установим флаг произошедшего изменения, и далее в цикле по данным списка получаем событие, устанавливаем его строковое описание, состоящее из времени в милисекундах и описания события, затем временно, и только для проверки (далее заменим это, и все остальные системные сообщения библиотеки на вывод в журнал только при разрешении логирования) распечатываем в журнал описание события, и наконец, отправляем событие в управляющую программу при помощи EventChartCustom().

В параметрах функции EventChartCustom() передаём:

  • в event_id передаём событие,
  • в lparam — время события в милисекундах
  • в dparam передаём индекс события в списке одновременно произошедших изменений аккаунта
  • в sparam — строковое описание события

В самом конце метода обязательно сохраняем текущую хеш-сумму как прошлую для последующей проверки.

Доработка класса CAccountsCollection на этом завершена.

Теперь перейдём к классу CEngine — всё у нас управляется и начинается из него, и впишем всё необходимое для работы с событиями аккаунта.

В приватной секции класса впишем переменные для хранения флага события изменения свойств аккаунта и для хранения последнего события, произошедшего на счёте. А в публичной секции впишем методы, возвращающие список событий аккаунта, произошедших одновременно, и последнее событие аккаунта:

//+------------------------------------------------------------------+
//| Класс-основа библиотеки                                          |
//+------------------------------------------------------------------+
class CEngine : public CObject
  {
private:
   CHistoryCollection   m_history;                       // Коллекция исторических ордеров и сделок
   CMarketCollection    m_market;                        // Коллекция рыночных ордеров и сделок
   CEventsCollection    m_events;                        // Коллекция событий
   CAccountsCollection  m_accounts;                      // Коллекция аккаунтов
   CArrayObj            m_list_counters;                 // Список счётчиков таймера
   bool                 m_first_start;                   // Флаг первого запуска
   bool                 m_is_hedge;                      // Флаг хедж-счёта
   bool                 m_is_tester;                     // Флаг работы в тестере
   bool                 m_is_market_trade_event;         // Флаг торгового события на счёте
   bool                 m_is_history_trade_event;        // Флаг торгового события в истории счёта
   bool                 m_is_account_event;              // Флаг события изменения аккаунта
   ENUM_TRADE_EVENT     m_last_trade_event;              // Последнее торговое событие на счёте
   ENUM_ACCOUNT_EVENT   m_last_account_event;            // Последнее событие в свойствах счёта
//--- Возвращает индекс счётчика по id
   int                  CounterIndex(constint id) const;
//--- Возвращает (1) флаг первого запуска, (2) факт наличия флага в торговом событии
   bool                 IsFirstStart(void);
//--- Работа с событиями (1) ордеров, сделок и позиций, (2) аккаунтов
   void                 TradeEventsControl(void);
   void                 AccountEventsControl(void);
//--- Возвращает последний (1) рыночный отложенный ордер, (2) маркет-ордер, (3) последнюю позицию, (4) позицию по тикету
   COrder*              GetLastMarketPending(void);
   COrder*              GetLastMarketOrder(void);
   COrder*              GetLastPosition(void);
   COrder*              GetPosition(constulong ticket);
//--- Возвращает последний (1) удалённый отложенный ордер, (2) исторический маркет-ордер, (3) исторический ордер (маркет или отложенный) по его тикету
   COrder*              GetLastHistoryPending(void);
   COrder*              GetLastHistoryOrder(void);
   COrder*              GetHistoryOrder(constulong ticket);
//--- Возвращает (1) первый и (2) последний исторический маркет-ордер из списка всех ордеров позиции, (3) последнюю сделку
   COrder*              GetFirstOrderPosition(constulong position_id);
   COrder*              GetLastOrderPosition(constulong position_id);
   COrder*              GetLastDeal(void);
public:
   //--- Возвращает список рыночных (1) позиций, (2) отложенных ордеров и (3) маркет-ордеров
   CArrayObj*           GetListMarketPosition(void);
   CArrayObj*           GetListMarketPendings(void);
   CArrayObj*           GetListMarketOrders(void);
   //--- Возвращает список исторических (1) ордеров, (2) удалённых отложенных ордеров, (3) сделок, (4) всех маркет-ордеров позиции по её идентификатору
   CArrayObj*           GetListHistoryOrders(void);
   CArrayObj*           GetListHistoryPendings(void);
   CArrayObj*           GetListDeals(void);
   CArrayObj*           GetListAllOrdersByPosID(constulong position_id);
//--- Возвращает список (1) аккаунтов, (2) событий аккаунтов
   CArrayObj*           GetListAllAccounts(void)                        { returnthis.m_accounts.GetList();          }
   CArrayInt*           GetListAccountEvents(void)                      { returnthis.m_accounts.GetListChanges();   }
//--- Возвращает список событий ордеров, сделок и позиций
   CArrayObj*           GetListAllOrdersEvents(void)                    { returnthis.m_events.GetList();            }
//--- Сбрасывает последнее торговое событие
   void                 ResetLastTradeEvent(void)                       { this.m_events.ResetLastTradeEvent(); }
//--- Возвращает (1) последнее торговое событие, (2) последнее событие в свойствах счёта, (3) флаг счёта-хедж, (4) флаг работы в тестере
   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;                   }
//--- Создаёт счётчик таймера
   void                 CreateCounter(constint id,constulong frequency,constulong pause);
//--- Таймер
   void                 OnTimer(void);
//--- Конструктор/Деструктор
                        CEngine();
                       ~CEngine();
  };
//+------------------------------------------------------------------+

В конструкторе класса, в его списке инициализации пропишем инициализацию последнего события аккаунта:

//+------------------------------------------------------------------+
//| CEngine конструктор                                              |
//+------------------------------------------------------------------+
CEngine::CEngine() : m_first_start(true),m_last_trade_event(TRADE_EVENT_NO_EVENT),m_last_account_event(ACCOUNT_EVENT_NO_EVENT)
  {

Допишем метод AccountEventsControl(), который ранее у нас лишь вызывал метод Refresh() класса-коллекции аккаунтов:

//+------------------------------------------------------------------+
//| Проверка событий аккаунта                                        |
//+------------------------------------------------------------------+
void CEngine::AccountEventsControl(void)
  {
//--- Проверка изменений свойств аккаунта и установка флага событий изменения аккаунта
   this.m_accounts.Refresh();
   this.m_is_account_event=this.m_accounts.IsAccountEvent();
//--- Если есть изменения свойств аккаунта
   if(this.m_is_account_event)
     {
      //--- Получаем последнее событие изменения свойств аккаунта
      this.m_last_account_event=this.m_accounts.GetEvent();
     }
  }
//+------------------------------------------------------------------+

Здесь всё просто — сначала обновляем данные аккаунта, и если есть какое-либо изменение свойств аккаунта, просто записываем в переменную последнее событие. Все остальные данные о событии будем извлекать в управляющей программе, работающей на основе библиотеки.

Тест событий аккаунта

Для тестирования событий аккаунта, в принципе, мы можем использовать советник из прошлой статьи, так как библиотека сама находит все изменения свойств аккаунта, отправляет об этом сообщение в события графика и распечатывает в журнале описание произошедшего события аккаунта.
Но давайте попробуем немного выйти из "песочницы" библиотеки и обработать некоторые события аккаунта в программе, например — увеличение размера собственных средств.

Да, сейчас библиотека пока является "программой в самой себе", и доступ к её возможностям извне сильно ограничен. Но это лишь до той поры, пока мы занимаемся сбором и обработкой необходимых данных — библиотека распечатывает в журнал различные события лишь для тестовых проверок правильности работы создаваемых классов и собираемых данных и отслеживаемых ею событий. Далее мы организуем простой и удобный доступ к любым имеющимся данным библиотеки, и тогда возможности их получения из программы сильно облегчатся.

Для возможности получения данных об изменениях свойств аккаунта уже сейчас, внесём небольшие правки в класс CEngine — нам потребуется получить доступ к объекту текущего аккаунта и к событиям текущего аккаунта.
Для этого допишем в файле Engine.mqh, в публичной секции класса CEngine необходимые методы:

public:
   //--- Возвращает список рыночных (1) позиций, (2) отложенных ордеров и (3) маркет-ордеров
   CArrayObj*           GetListMarketPosition(void);
   CArrayObj*           GetListMarketPendings(void);
   CArrayObj*           GetListMarketOrders(void);
   //--- Возвращает список исторических (1) ордеров, (2) удалённых отложенных ордеров, (3) сделок, (4) всех маркет-ордеров позиции по её идентификатору
   CArrayObj*           GetListHistoryOrders(void);
   CArrayObj*           GetListHistoryPendings(void);
   CArrayObj*           GetListDeals(void);
   CArrayObj*           GetListAllOrdersByPosID(constulong position_id);
//--- Возвращает список (1) аккаунтов, (2) событий аккаунтов, (3) событие изменения аккаунта по его индексу в списке
//--- (4) текущий аккаунт, (5) описание события
   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);
   

Здесь мы добавили метод получения события аккаунта по его индексу в списке изменений, метод получения текущего объекта-аккаунта и метод получения описания события аккаунта.

Метод получения события аккаунта по индексу просто возвращает результат работы метода GetEvent() класса-коллекции аккаунтов (ранее мы его рассматривали).

В конце листинга впишем реализацию метода получения текущего объекта-аккаунта:

//+------------------------------------------------------------------+
//| Возвращает текущий аккаунт                                       |
//+------------------------------------------------------------------+
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);  
  }
//+------------------------------------------------------------------+

Сначала получаем индекс текущего аккаунта из класса-коллекции аккаунтов. Если индекс не получен, то возвращаем NULL. Затем получаем полный список аккаунтов и возвращаем нужный нам текущий аккаунт по его индексу в списке аккаунтов. При ошибке получения списка или аккаунта — возвращаем NULL.

И напишем реализацию метода, возвращающего описание события аккаунта:

//+------------------------------------------------------------------+
//| Возвращает описание события аккаунта                             |
//+------------------------------------------------------------------+
string CEngine::GetAccountEventDescription(ENUM_ACCOUNT_EVENT event)
  {
   return this.m_accounts.EventDescription(event);
  }
//+------------------------------------------------------------------+

Метод возвращает результат работы метода класса-коллекции аккаунтов, возвращающего описание события аккаунта.

Для тестирования событий аккаунта возьмём советник из прошлой статьи \MQL5\Experts\TestDoEasy\Part12\TestDoEasyPart12_2.mq5 и сохраним его под именем TestDoEasyPart13.mq5 в новой папке \MQL5\Experts\TestDoEasy\Part13.

Сразу же удалим входной параметр

inputbool     InpFullProperties    =  false;// Show full accounts properties

и из обработчика OnInit() удалим быструю проверку коллекции аккаунтов:

//--- Быстрая проверка коллекции объектов-аккаунтов   
   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();
        }
     }
//---

К сведению:
так как мы изменили структуру объекта-аккаунта (изменили размеры uchar-массивов для хранения строковых свойств аккаунта и добавили ещё одно целочисленное свойство), то все ранее сохранённые файлы объектов-аккаунтов теперь не будут загружаться правильно. Если они есть в общей папке терминалов в каталоге \Files\DoEasy\Accounts\, то их все необходимо удалить перед запуском этого тестового советника — они будут созданы заново при переключениях со счёта на счёт с уже новым размером структуры объектов.

Обработчик OnInit() теперь выглядит так:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Вызов данной функции выводит в журнал список констант перечисления, 
//--- заданного в файле DELib.mqh в строках 22 и 25, для проверки корректности констант
   //EnumNumbersTest();
//--- Установка глобальных переменных советника
   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;

//--- Проверка и удаление неудалённых графических объектов советника
   if(IsPresentObects(prefix))
      ObjectsDeleteAll(0,prefix);

//--- Создание панели кнопок
   if(!CreateButtons(InpButtShiftX,InpButtShiftY))
      returnINIT_FAILED;
//--- Установка состояния кнопки активизации трейлингов
   ButtonState(butt_data[TOTAL_BUTT-1].name,trailing_on);

//--- Установка параметров торгового класса CTrade
#ifdef __MQL5__
   trade.SetDeviationInPoints(slippage);
   trade.SetExpertMagicNumber(magic_number);
   trade.SetTypeFillingBySymbol(Symbol());
   trade.SetMarginMode();
   trade.LogLevel(LOG_LEVEL_NO);
#endif 
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+

Для обработки всех событий, приходящих от библиотеки в программу, и чтобы не засорять штатный OnChartEvent(), сделаем отдельный обработчик: OnDoEasyEvent(), и уже в нём будем разбирать все полученные события. В чём ещё плюс — код станет более читаем и, что не менее важно — мы сможем обрабатывать события в тестере, что нам сейчас как раз и нужно — мы собрались обрабатывать событие аккаунта "увеличение собственных средств выше заданного значения прироста", а для этого — куда быстрее и проще всё проверить в тестере.

Но по порядку.
Допишем в обработчике OnTick() необходимый функционал для обработки событий аккаунта:

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//--- Инициализация последних событий
   static ENUM_TRADE_EVENT last_trade_event=WRONG_VALUE;
   static ENUM_ACCOUNT_EVENT last_account_event=WRONG_VALUE;
//--- Если работа в тестере
   if(MQLInfoInteger(MQL_TESTER))
     {
      engine.OnTimer();
      PressButtonsControl();
     }
//--- Если последнее торговое событие изменилось
   if(engine.LastTradeEvent()!=last_trade_event)
     {
      last_trade_event=engine.LastTradeEvent();
      Comment("\nLast trade event: ",EnumToString(last_trade_event));
     }
//--- Если последнее событие аккаунта изменилось
   if(engine.LastAccountEvent()!=last_account_event)
     {
      last_account_event=engine.LastAccountEvent();
      //--- Если это тестер
      if(MQLInfoInteger(MQL_TESTER))
        {
         //--- Получим список всех событий аккаунта, произошедших одновременно
         CArrayInt* list=engine.GetListAccountEvents();
         if(list!=NULL)
           {
            //--- В цикле получаем очередное событие
            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;
               //--- Отправляем событие в обработчик событий
               OnDoEasyEvent(CHARTEVENT_CUSTOM+event,lparam,dparam,sparam);
              }
           }
        }
      Comment("\nLast account event: ",EnumToString(last_account_event));
     }
//--- Если установлен флаг трейлинга
   if(trailing_on)
     {
      TrailingPositions();
      TrailingOrders();
     }
  }
//+------------------------------------------------------------------+

Здесь: мы ввели новую переменную, хранящую тип последнего события аккаунта. Проверяем её текущее состояние по отношению к типу последнего события, возвращаемого классом CAccountsCollection. Если состояние изменилось — значит было событие аккаунта. Далее только для тестера — получаем в цикле по списку событий аккаунта, произошедших одновременно, очередное событие и отправляем его в обработчик событий библиотеки. В листинге прописаны в комментариях все действия по получению событий и отправке их в обработчик. Отличием работы для тестера является то, что здесь мы не можем получить доступ к данным о времени события в милисекундах. Поэтому просто отправляем текущее время * 1000.

Теперь допишем обработчик событий 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
   if(id>=CHARTEVENT_CUSTOM)
     {
      OnDoEasyEvent(id,lparam,dparam,sparam);
     } 
  }
//+------------------------------------------------------------------+

Здесь мы дописали вызов обработчика событий библиотеки в случае, если идентификатор события указывает на событие, пришедшее из библиотеки.

И наконец, напишем обработчик событий библиотеки:

//+------------------------------------------------------------------+
//| Обработка событий библиотеки DoEasy                              |
//+------------------------------------------------------------------+
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();
//--- Обработка торговых событий
   if(idx<TRADE_EVENTS_NEXT_CODE)
     {
      event=EnumToString((ENUM_TRADE_EVENT)ushort(idx));
      digits=(int)SymbolInfoInteger(sparam,SYMBOL_DIGITS);
     }
//--- Обработка событий аккаунта
   else if(idx<ACCOUNT_EVENTS_NEXT_CODE)
     {
      event=EnumToString((ENUM_ACCOUNT_EVENT)ushort(idx));
      digits=0;
      
      //--- если это увеличение средств
      if((ENUM_ACCOUNT_EVENT)idx==ACCOUNT_EVENT_EQUITY_INC)        
        {
         Print(DFUN,sparam);
         //--- Закроем позицию с самой большой прибылью при увеличении средств больше,
         //--- чем задано в методе CAccountsCollection::InitControlsParams() для
         //--- переменной m_control_equity_inc, контролирующей прирост средств на 15 единиц (по умолчанию)
         //--- Файл AccountCollection, метод InitControlsParams(), строка 1199
         
         //--- Получаем список всех открытых позиций
         CArrayObj* list_positions=engine.GetListMarketPosition();
         //--- Сортируем список по прибыли с учётом комиссии и свопа
         list_positions.Sort(SORT_BY_ORDER_PROFIT_FULL);
         //--- Получаем индекс позиции с наибольшей прибылью
         int index=CSelect::FindOrderMax(list_positions,ORDER_PROP_PROFIT_FULL);
         if(index>WRONG_VALUE)
           {
            COrder* position=list_positions.At(index);
            if(position!=NULL)
              {
               //--- Получаем тикет позиции с наибольшей прибылью и закрываем позицию по тикету
               #ifdef __MQL5__
                  trade.PositionClose(position.Ticket());
               #else 
                  PositionClose(position.Ticket(),position.Volume());
               #endif 
              }
           }
        }
     }
  }
//+------------------------------------------------------------------+

Так как при отправке события на график управляющей программы, к коду события добавляется значение CHARTEVENT_CUSTOM, равное 1000, то для получения истинного кода, нам необходимо из полученного кода события вычесть значение CHARTEVENT_CUSTOM. Если событие — его числовое значение, находится в пределах от TRADE_EVENTS_NEXT_CODE до ACCOUNT_EVENTS_NEXT_CODE-1, то то значит, что пришло событие изменения свойств аккаунта. Так как по нашей задумке мы хотим закрывать самую прибыльную позицию при увеличении размера средств больше, чем задано в настройках (а по умолчанию у нас задано значение прироста от 15), то проверяем событие "увеличение размера средств выше заданной величины", и далее распечатываем в журнал описание события и закрываем самую прибыльную позицию — всё описано в комментариях к коду.

Если теперь просто запустить советник на графике, то спустя некоторое время мы можем получить в журнале записи о запрете торговли на счёте, а делее — о разрешении:

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

На MetaQuotes-Demo такое отключение/включение разрешения торговли можно наблюдать несколько раз в день, что даёт нам возможность проверить работу бибилиотеки по определению таких событий на демо-счёте.

Теперь запустим советник в тестере и откроем побольше позиций — чтобы быстро поймать событие увеличения средств и написанную нами обработку данного события — закрытие самой прибыльной позиции:


Видно, что при увеличении средств больше заданной величины, автоматически закрывается самая прибыльная позиция. В журнал выводятся сообщения об отслеживаемом нами событии аккаунта.

Что дальше

В следующей части начнём делать работу с символами. У нас запланированы к реализации объекты-символы, коллекция объектов-символов и события символов.

Ниже прикреплены все файлы текущей версии библиотеки и файлы тестового советника. Их можно скачать и протестировать всё самостоятельно.
При возникновении вопросов, замечаний и пожеланий, вы можете озвучить их в комментариях к статье.

К содержанию

Статьи этой серии:

Часть 1. Концепция, организация данных.
Часть 2. Коллекция исторических ордеров и сделок.
Часть 3. Коллекция рыночных ордеров и позиций, организация поиска.
Часть 4. Торговые события. Концепция.
Часть 5. Классы и коллекция торговых событий. Отправка событий в программу.
Часть 6. События на счёте с типом неттинг.
Часть 7. События срабатывания StopLimit-ордеров, подготовка функционала для регистрации событий модификации ордеров и позиций.
Часть 8. События модификации ордеров и позиций.
Часть 9. Совместимость с MQL4 - Подготовка данных.
Часть 10. Совместимость с MQL4 - События открытия позиций и активации отложенных ордеров.
Часть 11. Совместимость с MQL4 - События закрытия позиций.
Часть12. Класс объекта "аккаунт", коллекция объектов-аккаунтов.


Прикрепленные файлы |
MQL5.zip (137.57 KB)
MQL4.zip (134.68 KB)
Создаем кроссплатформенный советник-сеточник (Часть II): Сетка в рейндже в направлении тренда Создаем кроссплатформенный советник-сеточник (Часть II): Сетка в рейндже в направлении тренда

Сегодня мы попробуем разработать сеточный советник для работы в диапазоне в направлении тренда. То есть для инструментов Forex или рынков сырья. Как показали тесты, наш сеточник работал в прибыль с 2018 года. Но вот беда, с 2014 по 2018 год это был стабильный слив депозита

Библиотека для простого и быстрого создания программ для MetaTrader (Часть XII): Класс объекта "аккаунт", коллекция объектов-аккаунтов Библиотека для простого и быстрого создания программ для MetaTrader (Часть XII): Класс объекта "аккаунт", коллекция объектов-аккаунтов

В предыдущей статье мы определили события закрытия позиций для MQL4 в библиотеке и избавились от оказавшихся невостребованными свойств ордеров. В данной статье рассмотрим создание объекта "Аккаунт", создадим коллекцию объектов-аккаунтов и подготовим функционал для отслеживания событий аккаунтов.

Грокаем "память" рынка через дифференцирование и энтропийный анализ Грокаем "память" рынка через дифференцирование и энтропийный анализ

Область применения дробного дифференцирования достаточно широка. Например, алгоритмы машинного обучения, обычно, принимают дифференцированный ряд на вход. Проблема в том, что необходимо вывести новые данные в соответствии с имеющейся историей, чтобы модель машинного обучения смогла распознать их. В данной статье рассматривается оригинальный подход к дифференцированию временного ряда, в дополнении к этому приводится пример самооптимизирующейся ТС на основе полученного дифференцированного ряда.

Пишем кроссплатформенный помощник для выставления StopLoss и TakeProfit в соответствии со своими рисками Пишем кроссплатформенный помощник для выставления StopLoss и TakeProfit в соответствии со своими рисками

В данной статье мы создадим советник, который позволит нам автоматизировать процесс определения лота, с которым нужно войти в сделку в соответствии с нашими рисками. Также данный советник позволит автоматически выставлять тейк-профит с выбранным коэффициентом к стоп-лоссу. То есть чтобы соблюдалось соотношение 3 к 1, 4 к 1 или любое другое, выбранное нами.