Библиотека для простого и быстрого создания программ для MetaTrader (Часть XVII): Интерактивность объектов библиотеки

1 августа 2019, 08:58
Artyom Trishkin
31
630

Содержание


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

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

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

Методы контроля событий базового объекта библиотеки

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

  1. Идентификатор события — свойство объекта, которое было изменено. У каждого объекта имеются свои уникальные свойства, и нет возможности в программе, которая ничего не знает в каком из объектов изменено свойство, и какое из свойств этого объекта изменено — целочисленное или вещественное, поэтому по идентификатору события однозначно его определить нет возможности
  2. Причина события — увеличение или уменьшение значения свойства, либо пересечение контролируемого уровня. Данное значение так же не даёт нам возможности точно определить событие. Но по идентификатору события и по его причине мы можем уже определить, что такое-то свойство некоего объекта было либо увеличено, либо уменьшено, либо пересекло заданную контролируемую величину. Поэтому нам необходимо для точной идентификации события указать на идентификатор класса, в объекте которого произошло событие.
    Таким идентификатором однозначно может служить идентификатор списка коллекции — от точно указывает на принадлежность объекта к определённому классу — символ, аккаунт, либо ещё какой-то иной, созданный в будущем объект коллекции. Поэтому в событие необходимо отправить ещё:
  3. Идентификатор коллекции — тогда все три вышеназванных идентификатора позволят однозначно определить событие.
  4. Строковое свойство события — наименование объекта, в котором произошло событие.

Итак, мы видим, что для определения события нам необходимо получить три целочисленных параметра, но ещё и как-то получить время события, которое так же передаётся посредством long-значения. А у нас есть лишь одно long-свойство события. Как быть? Выход простой: мы будем передавать в одном long-параметре сразу три целочисленных события, но с типом ushort. Тип long имеет восемь байтов, а тип ushort — два байта. Значит в long-контейнере мы можем хранить три ushort-числа, записанные в 0,1 байты, 2,3 байты, 4,5 байты long-числа, и у нас останется ещё два байта 6 и 7 для передачи ещё одного ushort-значения если это потребуется в дальнейшем.
Для определения времени события, на котором оно произошло, нам достаточно передать лишь милисекунды времени в 0 и 1 байтах long-параметра.

  • Дату и время события можно будет взять из TimeCurrent() при получении события и прибавить к этому времени количество милисекунд, переданных в нулевом и первом байтах long-значения события.
  • Причину события мы будем записывать во 2-й и 3-й байты long-параметра события, а
  • идентификатор класса — в 4-й и 5-й байты long-параметра события.

Таким образом, при получении события, мы извлечём из long-параметра три ushort-значения и по ним сможем определить время события и получить дополнительные данные для точной идентификации события по идентификатору события, передаваемому в качестве ushort-параметра custom_event_id в EventChartCustom() и составить из идентификатора события и двух дополнительно-полученных значений из lparam точный идентификатор произошедшего события.

Для определения событий в свойствах объекта-наследника в объекте-родителе (в базовом объекте всех объектов библиотеки) будем в таймере проверять текущее состояние каждого из свойств объекта и сравнивать его с прошлым состоянием этого свойства. В первую очередь будем проверять задано ли значение, величину которого нужно сравнивать с величиной, на которую изменилось значение свойства. Если проверяемое значение не задано (установлено для него LONG_MAX), то это свойство игнорируется.

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

Поясню почему удобнее использовать массивы, а не структуру:
Мы заранее не знаем свойство какого типа будем проверять, но можем понять о его типе из индекса свойства (double-свойства объекта всегда расположены после long-свойств), а значит — нам не придётся в структуре дублировать поля — для long- и для double-значений одного и того же проверяемого значения свойства объекта. Мы просто будем записывать в массив нужного типа (соответствующий типу свойства, определённому из индекса свойства) все необходимые данные для контроля состояний свойства объекта с их правильным типом, и не нужно будет выбирать в какое поле структуры вписывать переданное значение - в long или double.
Как только определено изменение любого из свойств объекта, мы его добавляем в список базовых событий объекта (так как поиск ведётся в базовом объекте, то и событие будет базовым, и его не нужно путать с событием класса-наследника, которое далее будет определяться по списку базовых событий и создаваться из базовых событий, указатели на которые хранятся в этом списке).

В методах Refresh() каждого класса-наследника базового объекта проверяются в таймере списки произошедших изменений свойств (списки базовых событий) и при наличии в списках объектов этих событий, каждое событие преобразуется в событие библиотеки и отправляется в управляющую программу.

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

Комплекс всех проведённых сегодня мероприятий по доработке базового объекта библиотеки позволит нам далее не задумываться о создании контроля событий для всех впоследствии создаваемых объектов — мы будем лишь пользоваться подготовленным сегодня функционалом.

Итак, приступим.

Так как мы теперь будем работать с событиями в базовом объекте всех объектов библиотеки, то для идентификации событий нужно создать перечисление причин события.
В файл \MQL5\Include\DoEasy\ Defines.mqh после вариантов выбора по времени
впишем перечисление возможных причин событий базового объекта:

//+------------------------------------------------------------------+
//| Возможные варианты выбора по времени                             |
//+------------------------------------------------------------------+
enum ENUM_SELECT_BY_TIME
  {
   SELECT_BY_TIME_OPEN,                                     // По времени открытия (в милисекундах)
   SELECT_BY_TIME_CLOSE,                                    // По времени закрытия (в милисекундах)
  };
//+------------------------------------------------------------------+
//| Возможные причины событий базового объекта объектов библиотеки   |
//+------------------------------------------------------------------+
enum ENUM_BASE_EVENT_REASON
  {
   BASE_EVENT_REASON_INC,                                   // Увеличение значения свойства объекта
   BASE_EVENT_REASON_DEC,                                   // Уменьшение значения свойства объекта
   BASE_EVENT_REASON_MORE_THEN,                             // Значение свойства объекта стало больше контрольного значения
   BASE_EVENT_REASON_LESS_THEN,                             // Значение свойства объекта стало меньше контрольного значения
   BASE_EVENT_REASON_EQUALS                                 // Значение свойства объекта стало равно контрольному значению
  };
//+------------------------------------------------------------------+

Так как теперь нам не нужны будут флаги событий, то вместо списков флагов событий символа
впишем список возможных событий символов в окне "Обзор рынка":

//+------------------------------------------------------------------+
//| Данные для работы с символами                                    |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Список возможных событий символов в окне "Обзор рынка"           |
//+------------------------------------------------------------------+
enum ENUM_MW_EVENT
  {
   MARKET_WATCH_EVENT_NO_EVENT = ACCOUNT_EVENTS_NEXT_CODE,  // Нет события
   MARKET_WATCH_EVENT_SYMBOL_ADD,                           // Добавление символа в окно "Обзор рынка"
   MARKET_WATCH_EVENT_SYMBOL_DEL,                           // Удаление символа из окна "Обзор рынка"
   MARKET_WATCH_EVENT_SYMBOL_SORT,                          // Сортировка символов в окне "Обзор рынка"
  };
#define SYMBOL_EVENTS_NEXT_CODE  (MARKET_WATCH_EVENT_SYMBOL_SORT+1)  // Код следующего события после последнего кода события символа
//+------------------------------------------------------------------+

А список возможных событий символа просто удалим за ненадобностью:

//+------------------------------------------------------------------+
//| Список возможных событий символа                                 |
//+------------------------------------------------------------------+
enum ENUM_SYMBOL_EVENT
  {
   SYMBOL_EVENT_NO_EVENT = ACCOUNT_EVENTS_NEXT_CODE,        // Нет события
   SYMBOL_EVENT_MW_ADD,                                     // Добавление символа в окно "Обзор рынка"
   SYMBOL_EVENT_MW_DEL,                                     // Удаление символа из окна "Обзор рынка"
   SYMBOL_EVENT_MW_SORT,                                    // Сортировка символов в окне "Обзор рынка"
   SYMBOL_EVENT_TRADE_DISABLE,                              // Запрет исполнения ордеров
   SYMBOL_EVENT_TRADE_LONGONLY,                             // Разрешены только операции покупки
   SYMBOL_EVENT_TRADE_SHORTONLY,                            // Разрешены только продажи
   SYMBOL_EVENT_TRADE_CLOSEONLY,                            // Разрешены только операции закрытия позиций
   SYMBOL_EVENT_TRADE_FULL,                                 // Нет ограничений на торговые операции
   SYMBOL_EVENT_SESSION_DEALS_INC,                          // Увеличение количества сделок в текущей сессии больше заданной величины
   SYMBOL_EVENT_SESSION_DEALS_DEC,                          // Уменьшение количества сделок в текущей сессии больше заданной величины
   SYMBOL_EVENT_SESSION_BUY_ORDERS_INC,                     // Увеличение общего числа ордеров на покупку в текущий момент больше заданной величины
   SYMBOL_EVENT_SESSION_BUY_ORDERS_DEC,                     // Уменьшение общего числа ордеров на покупку в текущий момент больше заданной величины
   SYMBOL_EVENT_SESSION_SELL_ORDERS_INC,                    // Увеличение общего числа ордеров на продажу в текущий момент больше заданной величины
   SYMBOL_EVENT_SESSION_SELL_ORDERS_DEC,                    // Уменьшение общего числа ордеров на продажу в текущий момент больше заданной величины
   SYMBOL_EVENT_VOLUME_INC,                                 // Увеличение объёма в последней сделке больше заданной величины
   SYMBOL_EVENT_VOLUME_DEC,                                 // Уменьшение объёма в последней сделке больше заданной величины
   SYMBOL_EVENT_VOLUME_HIGH_DAY_INC,                        // Увеличение максимального объёма за день больше заданной величины
   SYMBOL_EVENT_VOLUME_HIGH_DAY_DEC,                        // Уменьшение максимального объёма за день больше заданной величины
   SYMBOL_EVENT_VOLUME_LOW_DAY_INC,                         // Увеличение минимального объёма за день больше заданной величины
   SYMBOL_EVENT_VOLUME_LOW_DAY_DEC,                         // Уменьшение минимального объёма за день больше заданной величины
   SYMBOL_EVENT_SPREAD_INC,                                 // Увеличение размера спреда больше заданной величины
   SYMBOL_EVENT_SPREAD_DEC,                                 // Уменьшение размера спреда больше заданной величины
   SYMBOL_EVENT_STOPLEVEL_INC,                              // Увеличение уровня Stop-ордеров больше заданной величины
   SYMBOL_EVENT_STOPLEVEL_DEC,                              // Уменьшение уровня Stop-ордеров больше заданной величины
   SYMBOL_EVENT_FREEZELEVEL_INC,                            // Увеличение уровня заморозки больше заданной величины
   SYMBOL_EVENT_FREEZELEVEL_DEC,                            // Уменьшение уровня заморозки больше заданной величины
   SYMBOL_EVENT_BID_LAST_INC,                               // Увеличение цены Bid или Last больше заданной величины
   SYMBOL_EVENT_BID_LAST_DEC,                               // Уменьшение цены Bid или Last больше заданной величины
   SYMBOL_EVENT_BID_LAST_HIGH_INC,                          // Увеличение максимальной цены Bid или Last за день больше заданной величины
   SYMBOL_EVENT_BID_LAST_HIGH_DEC,                          // Уменьшение максимальной цены Bid или Last за день больше заданной величины по отношению к заданной цене
   SYMBOL_EVENT_BID_LAST_LOW_INC,                           // Увеличение минимальной цены Bid или Last за день больше заданной величины по отношению к заданной цене
   SYMBOL_EVENT_BID_LAST_LOW_DEC,                           // Уменьшение минимальной цены Bid или Last за день больше заданной величины
   SYMBOL_EVENT_ASK_INC,                                    // Увеличение цены Ask больше заданной величины
   SYMBOL_EVENT_ASK_DEC,                                    // Уменьшение цены Ask больше заданной величины
   SYMBOL_EVENT_ASK_HIGH_INC,                               // Увеличение максимальной цены Ask за день больше заданной величины
   SYMBOL_EVENT_ASK_HIGH_DEC,                               // Уменьшение максимальной цены Ask за день больше заданной величины
   SYMBOL_EVENT_ASK_LOW_INC,                                // Увеличение минимальной цены Ask за день больше заданной величины
   SYMBOL_EVENT_ASK_LOW_DEC,                                // Уменьшение минимальной цены Ask за день больше заданной величины
   SYMBOL_EVENT_VOLUME_REAL_DAY_INC,                        // Увеличение реального объёма за день больше заданной величины
   SYMBOL_EVENT_VOLUME_REAL_DAY_DEC,                        // Уменьшение реального объёма за день больше заданной величины
   SYMBOL_EVENT_VOLUME_HIGH_REAL_DAY_INC,                   // Увеличение максимального реального объёма за день больше заданной величины
   SYMBOL_EVENT_VOLUME_HIGH_REAL_DAY_DEC,                   // Уменьшение максимального реального объёма за день больше заданной величины
   SYMBOL_EVENT_VOLUME_LOW_REAL_DAY_INC,                    // Увеличение минимального реального объёма за день больше заданной величины
   SYMBOL_EVENT_VOLUME_LOW_REAL_DAY_DEC,                    // Уменьшение минимального реального объёма за день больше заданной величины
   SYMBOL_EVENT_OPTION_STRIKE_INC,                          // Увеличение цены исполнения опциона больше заданной величины
   SYMBOL_EVENT_OPTION_STRIKE_DEC,                          // Уменьшение цены исполнения опциона больше заданной величины
   SYMBOL_EVENT_VOLUME_LIMIT_INC,                           // Увеличение максимально допустимого совокупного объема позиции и отложенных ордеров в одном направлении
   SYMBOL_EVENT_VOLUME_LIMIT_DEC,                           // Уменьшение максимально допустимого совокупного объема позиции и отложенных ордеров в одном направлении
   SYMBOL_EVENT_SWAP_LONG_INC,                              // Увеличение свопа длинных позиций
   SYMBOL_EVENT_SWAP_LONG_DEC,                              // Уменьшение свопа длинных позиций
   SYMBOL_EVENT_SWAP_SHORT_INC,                             // Увеличение свопа коротких позиций
   SYMBOL_EVENT_SWAP_SHORT_DEC,                             // Уменьшение свопа коротких позиций
   SYMBOL_EVENT_SESSION_VOLUME_INC,                         // Увеличение суммарного объёма сделок в текущую сессию больше заданной величины
   SYMBOL_EVENT_SESSION_VOLUME_DEC,                         // Уменьшение суммарного объёма сделок в текущую сессию больше заданной величины
   SYMBOL_EVENT_SESSION_TURNOVER_INC,                       // Увеличение суммарного оборота в текущую сессию больше заданной величины
   SYMBOL_EVENT_SESSION_TURNOVER_DEC,                       // Уменьшение суммарного оборота в текущую сессию больше заданной величины по отношению к заданному обороту
   SYMBOL_EVENT_SESSION_INTEREST_INC,                       // Увеличение суммарного объёма открытых позиций в текущую сессию больше заданной величины
   SYMBOL_EVENT_SESSION_INTEREST_DEC,                       // Уменьшение суммарного объёма открытых позиций в текущую сессию больше заданной величины по отношению к заданному объёму
   SYMBOL_EVENT_SESSION_BUY_ORD_VOLUME_INC,                 // Увеличение общего объёма ордеров на покупку больше заданной величины
   SYMBOL_EVENT_SESSION_BUY_ORD_VOLUME_DEC,                 // Уменьшение общего объёма ордеров на покупку больше заданной величины
   SYMBOL_EVENT_SESSION_SELL_ORD_VOLUME_INC,                // Увеличение общего объёма ордеров на продажу больше заданной величины
   SYMBOL_EVENT_SESSION_SELL_ORD_VOLUME_DEC,                // Уменьшение общего объёма ордеров на продажу больше заданной величины
   SYMBOL_EVENT_SESSION_OPEN_INC,                           // Увеличение цены открытия сессии больше заданной величины по отношению к заданной цене
   SYMBOL_EVENT_SESSION_OPEN_DEC,                           // Уменьшение цены открытия сессии больше заданной величины по отношению к заданной цене
   SYMBOL_EVENT_SESSION_CLOSE_INC,                          // Увеличение цены закрытия сессии больше заданной величины по отношению к заданной цене
   SYMBOL_EVENT_SESSION_CLOSE_DEC,                          // Уменьшение цены закрытия сессии больше заданной величины по отношению к заданной цене
   SYMBOL_EVENT_SESSION_AW_INC,                             // Увеличение средневзвешенной цены сессии больше заданной величины
   SYMBOL_EVENT_SESSION_AW_DEC,                             // Уменьшение средневзвешенной цены сессии больше заданной величины
  };
#define SYMBOL_EVENTS_NEXT_CODE       (SYMBOL_EVENT_SESSION_AW_DEC+1)   // Код следующего события после последнего кода события символа
//+------------------------------------------------------------------+

Код следующего события, естественно, был изменён на значение, следующее за значением константы MARKET_WATCH_EVENT_SYMBOL_SORT из перечисления ENUM_MW_EVENT.

Теперь займёмся реализацией задуманного функционала.

В файл базового объекта \MQL5\Include\DoEasy\Objects\BaseObj.mqh впишем новый класс базового события:

//+------------------------------------------------------------------+
//| Класс базового события объекта библиотеки                        |
//+------------------------------------------------------------------+
class CBaseEvent : public CObject
  {
private:
   ENUM_BASE_EVENT_REASON  m_reason;  
   int                     m_event_id;
   double                  m_value;   
public:
   ENUM_BASE_EVENT_REASON  Reason(void)   const { return this.m_reason;    }
   int                     ID(void)       const { return this.m_event_id;  }
   double                  Value(void)    const { return this.m_value;     }
//--- Конструктор
                           CBaseEvent(const int event_id,const ENUM_BASE_EVENT_REASON reason,const double value) : m_reason(reason),
                                                                                                                   m_event_id(event_id),
                                                                                                                   m_value(value){}
//--- Метод сравнения для поиска одинаковых объектов-событий
   virtual int             Compare(const CObject *node,const int mode=0) const 
                             {   
                              const CBaseEvent *compared=node;
                              return
                                (
                                 this.Reason()>compared.Reason()  ?  1  :
                                 this.Reason()<compared.Reason()  ? -1  :
                                 this.ID()>compared.ID()          ?  1  :
                                 this.ID()<compared.ID()          ? -1  : 0
                                );
                             } 
  };
//+------------------------------------------------------------------+

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

Так как хранить список контролируемых свойств объектов мы будем в двумерных массивах, то добавим макроподстановку, указывающую на размер второго измерения массивов, и в приватной секции класса объявим две переменные, в которых будем хранить количество целочисленных и вещественных свойств объекта, который будет наследоваться от данного класса (ведь базовый класс ничего не знает о количествах свойств, имеющихся у его наследников, и эти количества необходимо будет явно указывать). И объявим метод для заполнения массивов свойств и поиска изменений в свойствах объектов-наследников.

//+------------------------------------------------------------------+
//| Класс базового объекта для всех объектов библиотеки              |
//+------------------------------------------------------------------+
#define  CONTROLS_TOTAL    (10)
class CBaseObj : public CObject
  {
private:
   int               m_long_prop_total;  
   int               m_double_prop_total;
   //--- Заполняет массив свойств объекта
   template<typename T> bool  FillPropertySettings(const int index,T &array[][CONTROLS_TOTAL],T &array_prev[][CONTROLS_TOTAL],int &event_id);
protected:

В защищённой секции класса объявим список для хранения указателей на экземпляры базовых событий объекта, переменную для хранения идентификатора события, флаг первого запуска и переменную для хранения типа объекта-наследника.
Также мы добавили четыре двумерных массива для хранения свойств и контроля их изменения (текущие и прошлые целочисленные и вещественные свойства объекта-наследника) и метод, возвращающий только милисекунды, хранящиеся во времени события (для MQL4 возвращаем 0, а для MQL5 — остаток от деления на 1000 long-значения времени).
Так как базовый класс ничего не знает о количестве свойств объектов-наследников, и размеры нужно устанавливать из классов-наследников (где они известны), то объявим методы для установки и проверки размеров массивов:

protected:
   CArrayObj         m_list_events_base;                       // Список базовых событий объекта
   CArrayObj         m_list_events;                            // Список событий объекта
   MqlTick           m_tick;                                   // Структура тика для получения котировочных данных
   double            m_hash_sum;                               // Хэш-сумма данных объекта
   double            m_hash_sum_prev;                          // Хэш-сумма данных объекта на прошлой проверке
   int               m_digits_currency;                        // Число знаков после запятой валюты счёта
   int               m_global_error;                           // Код глобальной ошибки
   long              m_chart_id;                               // Идентификатор графика управляющей программы
   bool              m_is_event;                               // Флаг события объекта
   int               m_event_code;                             // Код события объекта
   int               m_event_id;                               // Идентификатор события (равен значению свойства объекта)
   string            m_name;                                   // Наименование объекта
   string            m_folder_name;                            // Имя папки хранения объектов-наследников CBaseObj
   bool              m_first_start;                            // Флаг первого запуска
   int               m_type;                                   // Тип объекта (соответствует идентификаторам коллекций)
//--- Данные в ячейках массива
//--- Данные для хранения, контроля и возврата отслеживаемых свойств:
//--- [Индекс свойства][0] Контролируемая величина прироста значения свойства
//--- [Индекс свойства][1] Контролируемая величина уменьшения значения свойства
//--- [Индекс свойства][2] Контрольный уровень значения свойства
//--- [Индекс свойства][3] Значение свойства
//--- [Индекс свойства][4] Величина изменения значения свойства
//--- [Индекс свойства][5] Флаг изменения значения свойства больше, чем на величину прироста
//--- [Индекс свойства][6] Флаг изменения значения свойства больше, чем на величину уменьшения
//--- [Индекс свойства][7] Флаг увеличения значения свойства больше контрольного уровня
//--- [Индекс свойства][8] Флаг уменьшения значения свойства меньше контрольного уровня
//--- [Индекс свойства][9] Флаг равенства значения свойства контрольному уровню
   long              m_long_prop_event[][CONTROLS_TOTAL];         // Массив для хранения величин целочисленных свойств объекта и контролируемых значений изменения свойств
   double            m_double_prop_event[][CONTROLS_TOTAL];       // Массив для хранения величин вещественных свойств объекта и контролируемых значений изменения свойств
   long              m_long_prop_event_prev[][CONTROLS_TOTAL];    // Массив для хранения величин контролируемых целочисленных свойств объекта на прошлой проверке
   double            m_double_prop_event_prev[][CONTROLS_TOTAL];  // Массив для хранения величин контролируемых вещественных свойств объекта на прошлой проверке

//--- Возвращает (1) время в милисекундах, (2) милисекунды из значения времени из MqlTick
   long              TickTime(void)                            const { return #ifdef __MQL5__ this.m_tick.time_msc #else this.m_tick.time*1000 #endif ;  }
   ushort            MSCfromTime(const long time_msc)          const { return #ifdef __MQL5__ ushort(this.TickTime()%1000) #else 0 #endif ;              }
//--- возвращает факт наличия кода события в событии объекта
   bool              IsPresentEventFlag(const int change_code) const { return (this.m_event_code & change_code)==change_code; }
//--- Возвращает число знаков после запятой валюты счёта
   int               DigitsCurrency(void)                      const { return this.m_digits_currency; }
//--- Возвращает количество знаков после запятой в double-значении
   int               GetDigits(const double value)             const;

//--- Устанавливает размер массива (1) целочисленных, (2) вещественных контролируемых свойств объекта
   bool              SetControlDataArraySizeLong(const int size);    
   bool              SetControlDataArraySizeDouble(const int size);
//--- Проверяет размер массива свойств объекта
   bool              CheckControlDataArraySize(bool check_long=true);

//--- Устанавливает (1) значение контролируемой величины, (2) величину изменения значения свойства объекта
   template<typename T> void  SetControlledValue(const int property,const T value);
   template<typename T> void  SetControlledChangedValue(const int property,const T value);

//--- Устанавливает величину контролируемого (1) приращения, (2) уменьшения, (3) контрольный уровень значения свойства объекта
   template<typename T> void  SetControlledValueINC(const int property,const T value);
   template<typename T> void  SetControlledValueDEC(const int property,const T value);
   template<typename T> void  SetControlledValueLEVEL(const int property,const T value);

//--- Устанавливает Флаг изменения значения свойства больше, чем на величину (1) прироста, (2) уменьшения
   template<typename T> void  SetControlledFlagINC(const int property,const T value);
   template<typename T> void  SetControlledFlagDEC(const int property,const T value);
//--- Устанавливает Флаг изменения значения свойства (1) больше, (2) меньше контрольного уровня, (3) равенства уровню
   template<typename T> void  SetControlledFlagMORE(const int property,const T value);
   template<typename T> void  SetControlledFlagLESS(const int property,const T value);
   template<typename T> void  SetControlledFlagEQUAL(const int property,const T value);

//--- Возвращает установленную величину контролируемого приращения (1) целочисленных, (2) вещественных свойств объекта
   long              GetControlledValueLongINC(const int property)         const { return this.m_long_prop_event[property][0];                           }
   double            GetControlledValueDoubleINC(const int property)       const { return this.m_double_prop_event[property-this.m_long_prop_total][0];  }
//--- Возвращает установленную величину контролируемого уменьшения (1) целочисленных, (2) вещественных свойств объекта
   long              GetControlledValueLongDEC(const int property)         const { return this.m_long_prop_event[property][1];                           }
   double            GetControlledValueDoubleDEC(const int property)       const { return this.m_double_prop_event[property-this.m_long_prop_total][1];  }
//--- Возвращает контрольный уровень (1) целочисленных, (2) вещественных свойств объекта
   long              GetControlledValueLongLEVEL(const int property)       const { return this.m_long_prop_event[property][2];                           }
   double            GetControlledValueDoubleLEVEL(const int property)     const { return this.m_double_prop_event[property-this.m_long_prop_total][2];  }
//--- Возвращает значение (1) целочисленного, (2) вещественного свойства объекта
   long              GetControlledValueLong(const int property)            const { return this.m_long_prop_event[property][3];                           }
   double            GetControlledValueDouble(const int property)          const { return this.m_double_prop_event[property-this.m_long_prop_total][3];  }
//--- Возвращает величину изменения контролируемого (1) целочисленного, (2) вещественного свойства объекта
   long              GetControlledChangedValueLong(const int property)     const { return this.m_long_prop_event[property][4];                           }
   double            GetControlledChangedValueDouble(const int property)   const { return this.m_double_prop_event[property-this.m_long_prop_total][4];  }

//--- Возвращает Флаг изменения значения (1) целочисленного, (2) вещественного свойства больше, чем на величину прироста
   long              GetControlledFlagLongINC(const int property)          const { return this.m_long_prop_event[property][5];                           }
   double            GetControlledFlagDoubleINC(const int property)        const { return this.m_double_prop_event[property-this.m_long_prop_total][5];  }
//--- Возвращает Флаг изменения значения (1) целочисленного, (2) вещественного свойства больше, чем на величину уменьшения
   long              GetControlledFlagLongDEC(const int property)          const { return this.m_long_prop_event[property][6];                           }
   double            GetControlledFlagDoubleDEC(const int property)        const { return this.m_double_prop_event[property-this.m_long_prop_total][6];  }
//--- Возвращает Флаг увеличения значения (1) целочисленного, (2) вещественного свойства больше контрольного уровня
   long              GetControlledFlagLongMORE(const int property)         const { return this.m_long_prop_event[property][7];                           }
   double            GetControlledFlagDoubleMORE(const int property)       const { return this.m_double_prop_event[property-this.m_long_prop_total][7];  }
//--- Возвращает Флаг уменьшения значения (1) целочисленного, (2) вещественного свойства меньше контрольного уровня
   long              GetControlledFlagLongLESS(const int property)         const { return this.m_long_prop_event[property][8];                           }
   double            GetControlledFlagDoubleLESS(const int property)       const { return this.m_double_prop_event[property-this.m_long_prop_total][8];  }
//--- Возвращает Флаг равенства значений (1) целочисленного, (2) вещественного свойства и контрольного уровня
   long              GetControlledFlagLongEQUAL(const int property)        const { return this.m_long_prop_event[property][9];                           }
   double            GetControlledFlagDoubleEQUAL(const int property)      const { return this.m_double_prop_event[property-this.m_long_prop_total][9];  }
   
//--- (1) Упаковывает ushort-число в переданное long-число
//--- (2) преобразует ushort-значение в заданный байт long-числа
   long              UshortToLong(const ushort ushort_value,const uchar index,long &long_value);
   long              UshortToByte(const ushort value,const uchar index) const;
public:

Здесь же — в приватной секции класса, объявлены методы для установки и возврата контролируемых свойств и их величин, а также методы для упаковки ushort-числа в указанные байты long-контейнера по индексу. (Индекс 0 => байты 0-1, Индекс 1 => байты 2-3, Индекс 2 => байты 4-5)

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

public:
//--- Сбрасывает переменные (1) отслеживаемых, (2) контролируемых данных объекта (переназначить можно в наследниках)
   void              ResetChangesParams(void); 
   virtual void      ResetControlsParams(void);
//--- Добавляет (1) событие объекта в список, (2) причину события объекта в список
   bool              EventAdd(const ushort event_id,const long lparam,const double dparam,const string sparam);
   bool              EventBaseAdd(const int event_id,const ENUM_BASE_EVENT_REASON reason,const double value);
//--- Возвращает флаг произошедшего события в данных объекта
   bool              IsEvent(void)                             const { return this.m_is_event;                 }
//--- Возвращает (1) список событий, (2) код события объекта, (3) код глобальной ошибки
   CArrayObj        *GetListEvents(void)                             { return &this.m_list_events;             }
   int               GetEventCode(void)                        const { return this.m_event_code;               }
   int               GetError(void)                            const { return this.m_global_error;             }
//--- Возвращает (1) объект-событие, (2) базовое событие по его номеру в списке
   CEventBaseObj    *GetEvent(const int shift=WRONG_VALUE,const bool check_out=true);
   CBaseEvent       *GetEventBase(const int index);
//--- Возвращает количество (1) событий объекта
   int               GetEventsTotal(void)                      const { return this.m_list_events.Total();      }
//--- (1) Устанавливает, (2) возвращает идентификатор графика управляющей программы
   void              SetChartID(const long id)                       { this.m_chart_id=id;                     }
   long              GetChartID(void)                          const { return this.m_chart_id;                 }
//--- (1) Устанавливает имя подпапки, (2) возвращает имя папки для хранения файлов объектов-наследников
   void              SetSubFolderName(const string name)             { this.m_folder_name=DIRECTORY+name;      }
   string            GetFolderName(void)                       const { return this.m_folder_name;              }
//--- Возвращает наименование объекта
   string            GetName(void)                             const { return this.m_name;                     }
//--- Обновляет данные объекта для поиска изменений в данных объекта (Вызов из наследников: CBaseObj::Refresh())
   virtual void      Refresh(void);
//--- Возвращает тип объекта
   virtual int       Type(void)                                const { return this.m_type;                     }
//--- Возвращает описание события объекта
   string            EventDescription(const int property,
                                      const ENUM_BASE_EVENT_REASON reason,
                                      const int source,
                                      const string value,
                                      const string property_descr,
                                      const int digits);
//--- Конструктор
                     CBaseObj();
  };
//+------------------------------------------------------------------+

Теперь рассмотрим вкратце все объявленные выше методы.

Коструктор класса:

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

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

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

//+------------------------------------------------------------------+
//| Обновляет данные объекта для поиска в них изменений              |
//| Вызов из наследников: CBaseObj::Refresh()                        |
//+------------------------------------------------------------------+
void CBaseObj::Refresh(void)
  {
//--- Проверка размеров массивов, и если нулевые - выход
   if(!this.CheckControlDataArraySize() || !this.CheckControlDataArraySize(false))
      return;
//--- Сброс флага события и очистка всех списков
   this.m_is_event=false;
   this.m_list_events.Clear();
   this.m_list_events.Sort();
   this.m_list_events_base.Clear();
   this.m_list_events_base.Sort();
//--- Заполнение массива целочисленных свойств и контроль их изменений
   for(int i=0;i<this.m_long_prop_total;i++)
      if(!this.FillPropertySettings(i,this.m_long_prop_event,this.m_long_prop_event_prev,this.m_event_id))
         continue;
//--- Заполнение массива вещественных свойств и контроль их изменений
   for(int i=0;i<this.m_double_prop_total;i++)
      if(!this.FillPropertySettings(i,this.m_double_prop_event,this.m_double_prop_event_prev,this.m_event_id))
         continue;
//--- Первый запуск
   if(this.m_first_start)
     {
      ::ArrayCopy(this.m_long_prop_event_prev,this.m_long_prop_event);
      ::ArrayCopy(this.m_double_prop_event_prev,this.m_double_prop_event);
      this.m_hash_sum_prev=this.m_hash_sum;
      this.m_first_start=false;
      this.m_is_event=false;
      this.m_list_events_base.Clear();
      this.m_list_events_base.Sort();
      return;
     }
  }
//+------------------------------------------------------------------+

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

Реализация метода, заполняющего массивы свойств объекта-наследника и контролирующего их изменения:

//+------------------------------------------------------------------+
//| Заполняет массив свойств объекта                                 |
//+------------------------------------------------------------------+
template<typename T> bool CBaseObj::FillPropertySettings(const int index,T &array[][CONTROLS_TOTAL],T &array_prev[][CONTROLS_TOTAL],int &event_id)
  {
   //--- Данные в ячейках массива
   //--- [Индекс свойства][0] Контролируемая величина прироста значения свойства
   //--- [Индекс свойства][1] Контролируемая величина уменьшения значения свойства
   //--- [Индекс свойства][2] Контрольный уровень значения свойства
   //--- [Индекс свойства][3] Значение свойства
   //--- [Индекс свойства][4] Величина изменения значения свойства
   //--- [Индекс свойства][5] Флаг изменения значения свойства больше, чем на величину прироста
   //--- [Индекс свойства][6] Флаг изменения значения свойства больше, чем на величину уменьшения
   //--- [Индекс свойства][7] Флаг увеличения значения свойства больше контрольного уровня
   //--- [Индекс свойства][8] Флаг уменьшения значения свойства меньше контрольного уровня
   //--- [Индекс свойства][9] Флаг равенства значения свойства контрольному уровню
   
   //--- Если не установлены контролируемые значения - уходим с возвратом false
   if(this.m_first_start)
      return false;
   //--- Устанавливаем смещение индекса double-свойства и задаём идентификатор события
   event_id=index+(typename(T)=="double" ? this.m_long_prop_total : 0);
   //--- Обнуляем все флаги событий
   for(int j=5;j<CONTROLS_TOTAL;j++)
      array[index][j]=false;
   //--- Величина изменения свойства
   T value=array[index][3]-array_prev[index][3];
   array[index][4]=value;
   //--- Если контролируемая величина прироста значения свойства установлена
   if(array[index][0]<LONG_MAX)
     {
      //--- Если величина изменения свойства больше контролируемой величины увеличения - есть событие,
      //--- добавляем событие в список, ставим флаг и сохраняем новый размер значения свойства
      if(value>0 && value>array[index][0])
        {
         if(this.EventBaseAdd(event_id,BASE_EVENT_REASON_INC,value))
           {
            array[index][5]=true;
            array_prev[index][4]=value;
           }
        }
     }
   //--- Если контролируемая величина уменьшения значения свойства установлена
   if(array[index][1]<LONG_MAX)
     {
      //--- Если величина изменения свойства больше контролируемой величины уменьшения - есть событие,
      //--- добавляем событие в список, ставим флаг и сохраняем новый размер значения свойства
      if(value<0 && fabs(value)>array[index][1])
        {
         if(this.EventBaseAdd(event_id,BASE_EVENT_REASON_DEC,value))
           {
            array[index][6]=true;
            array_prev[index][4]=value;
           }
        }
     }
   //--- Если значение контролируемого уровня установлено
   if(array[index][2]<LONG_MAX)
     {
      value=array[index][3]-array[index][2];
      //--- Если значение свойства стало больше контрольного уровня - есть событие,
      //--- добавляем событие в список и ставим флаг
      if(value>0 && array_prev[index][3]<=array[index][2])
        {
         if(this.EventBaseAdd(event_id,BASE_EVENT_REASON_MORE_THEN,array[index][2]))
            array[index][7]=true;
        }
      //--- Если значение свойства стало меньше контрольного уровня - есть событие,
      //--- добавляем событие в список и ставим флаг
      else if(value<0 && array_prev[index][3]>=array[index][2])
        {
         if(this.EventBaseAdd(event_id,BASE_EVENT_REASON_LESS_THEN,array[index][2]))
            array[index][8]=true;
        }
      //--- Если значение свойства стало равно контрольному уровню - есть событие,
      //--- добавляем событие в список и ставим флаг
      else if(value==0 && array_prev[index][3]!=array[index][2])
        {
         if(this.EventBaseAdd(event_id,BASE_EVENT_REASON_EQUALS,array[index][2]))
            array[index][9]=true;
        }
     }
   //--- Сохраняем текущее значение свойства как прошлое
   array_prev[index][3]=array[index][3];
   return true;
  }
//+------------------------------------------------------------------+

Здесь все действия прописаны в комментариях к коду. Единственное, что хочу пояснить — выбор смещения индекса double-свойства объекта для получения идентификатора события. Так как у нас вещественные свойства всех объектов располагаются следом за целочисленными свойствам, то начало первого вещественного свойства равно количеству целочисленных свойств (если количество long-свойств равно трём, то первое вещественное свойство будет иметь индекс 3 (0,1,2,  3)). А в массивах у нас отсчёт начинается, естественно, с нуля. Поэтому в случае работы с double-свойствами нам необходимо прибавить к индексу массива количество целочисленных свойства объекта.

Методы установки размеров массивов целочисленных и вещественных свойств объектов-наследников:

//+------------------------------------------------------------------+
//| Устанавливает размер массивов целочисленных свойств объекта      |
//+------------------------------------------------------------------+
bool CBaseObj::SetControlDataArraySizeLong(const int size)
  {
   int x=(#ifdef __MQL4__ CONTROLS_TOTAL #else 1 #endif );
   this.m_long_prop_total=::ArrayResize(this.m_long_prop_event,size,100)/x;
   return((::ArrayResize(this.m_long_prop_event_prev,size,100)/x)==size && this.m_long_prop_total==size ? true : false);
  }
//+------------------------------------------------------------------+
//| Устанавливает размер массивов вещественных свойств объекта       |
//+------------------------------------------------------------------+
bool CBaseObj::SetControlDataArraySizeDouble(const int size)
  {
   int x=(#ifdef __MQL4__ CONTROLS_TOTAL #else 1 #endif );
   this.m_double_prop_total=::ArrayResize(this.m_double_prop_event,size,100)/x;
   return((::ArrayResize(this.m_double_prop_event_prev,size,100)/x)==size && this.m_double_prop_total==size ? true : false);
  }
//+------------------------------------------------------------------+

Методы возвращают результат изменения размеров массивов на переданную в метод величину.

Отмечу одну особенность изменения размера многомерного массива в MQL4. Функция ArrayResize() в MQL4 возвращает суммарный размер всех измерений массива. В MQL5 — размер первого измерения, который и меняется. Например, если у нас второе измерение имеет размер, равный двум, то при изменении размера первого измерения массива до 10, функция вернёт 20, что не логично (мы же изменяем размер только первого измерения). А вот в MQL5 функция возвращает правильное значение — для вышеописанного примера она вернёт 10, как и ожидалось.

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

Метод проверки размера массива целочисленных или вещественных свойств объекта-наследника:

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

В метод передаётся флаг, указывающий размер какого массива проверяется.
При true — проверяется массив long-свойств, при false — массив double-свойств.

Если размер проверяемого массива не задан, то создаётся текст сообщения, сообщение выводится в журнал и возвращается false. В случае, когда размер массива уже задан — возвращается true.

Методы сброса контролируемых значений и величин изменения отслеживаемых данных свойств объекта:

//+------------------------------------------------------------------+
//| Сбрасывает переменные контролируемых значений данных объекта     |
//+------------------------------------------------------------------+
void CBaseObj::ResetControlsParams(void)
  {
   if(!this.CheckControlDataArraySize(true) || !this.CheckControlDataArraySize(false))
      return;
//--- Данные в ячейках массива
//--- [Индекс свойства][0] Контролируемая величина прироста значения свойства
//--- [Индекс свойства][1] Контролируемая величина уменьшения значения свойства
//--- [Индекс свойства][2] Контрольный уровень значения свойства
   for(int i=this.m_long_prop_total-1;i>WRONG_VALUE;i--)
      for(int j=0; j<3; j++)
         this.m_long_prop_event[i][j]=LONG_MAX;
   for(int i=this.m_double_prop_total-1;i>WRONG_VALUE;i--)
      for(int j=0; j<3; j++)
         this.m_double_prop_event[i][j]=(double)LONG_MAX;
  }
//+------------------------------------------------------------------+
//| Сбрасывает переменные отслеживаемых данных объекта               |
//+------------------------------------------------------------------+
void CBaseObj::ResetChangesParams(void)
  {
   if(!this.CheckControlDataArraySize(true) || !this.CheckControlDataArraySize(false))
      return;
   this.m_list_events.Clear();
   this.m_list_events.Sort();
   this.m_list_events_base.Clear();
   this.m_list_events_base.Sort();
//--- Данные в ячейках массива
//--- [Индекс свойства][3] Значение свойства
//--- [Индекс свойства][4] Величина изменения значения свойства
//--- [Индекс свойства][5] Флаг изменения значения свойства больше, чем на величину прироста
//--- [Индекс свойства][6] Флаг изменения значения свойства больше, чем на величину уменьшения
//--- [Индекс свойства][7] Флаг увеличения значения свойства больше контрольного уровня
//--- [Индекс свойства][8] Флаг уменьшения значения свойства меньше контрольного уровня
//--- [Индекс свойства][9] Флаг равенства значения свойства контролируемому значению
   for(int i=this.m_long_prop_total-1;i>WRONG_VALUE;i--)
      for(int j=3; j<CONTROLS_TOTAL; j++)
         this.m_long_prop_event[i][j]=(j<5 ? LONG_MAX : 0);
   for(int i=this.m_double_prop_total-1;i>WRONG_VALUE;i--)
      for(int j=3; j<CONTROLS_TOTAL; j++)
         this.m_double_prop_event[i][j]=(j<5 ? (double)LONG_MAX : 0);
  }
//+------------------------------------------------------------------+

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

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

//+------------------------------------------------------------------+
//| Добавляет базовое событие объекта в список                       |
//+------------------------------------------------------------------+
bool CBaseObj::EventBaseAdd(const int event_id,const ENUM_BASE_EVENT_REASON reason,const double value)
  {
   CBaseEvent* event=new CBaseEvent(event_id,reason,value);
   if(event==NULL)
      return false;
   this.m_list_events_base.Sort();
   if(this.m_list_events_base.Search(event)>WRONG_VALUE)
     {
      delete event;
      return false;
     }
   return this.m_list_events_base.Add(event);
  }
//+------------------------------------------------------------------+

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

Далее создаётся новое базовое событие, и если точно такое же событие уже есть в списке базовых событий, то это событие удаляется и возвращается false — событие не добавлено. Иначе — возвращается результат добавления нового события в список базовых событий объекта.

Метод, возвращающий базовое событие по его индексу в списке базовых событий объекта:

//+------------------------------------------------------------------+
//| Возвращает базовое событие по его индексу в списке               |
//+------------------------------------------------------------------+
CBaseEvent *CBaseObj::GetEventBase(const int index)
  {
   int total=this.m_list_events_base.Total();
   if(total==0 || index<0 || index>total-1)
      return NULL;
   CBaseEvent *event=this.m_list_events_base.At(index);
   return(event!=NULL ? event : NULL);
  }
//+------------------------------------------------------------------+

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

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

Реализация методов установки контролируемых свойств объектов-наследников:

//+------------------------------------------------------------------+
//| Методы установки контролируемых параметров                       |
//+------------------------------------------------------------------+
//--- Данные для хранения, контроля и возврата отслеживаемых свойств:
//--- [Индекс свойства][0] Контролируемая величина прироста значения свойства
//--- [Индекс свойства][1] Контролируемая величина уменьшения значения свойства
//--- [Индекс свойства][2] Контрольный уровень значения свойства
//--- [Индекс свойства][3] Значение свойства
//--- [Индекс свойства][4] Величина изменения значения свойства
//--- [Индекс свойства][5] Флаг изменения значения свойства больше, чем на величину прироста
//--- [Индекс свойства][6] Флаг изменения значения свойства больше, чем на величину уменьшения
//--- [Индекс свойства][7] Флаг увеличения значения свойства больше контрольного уровня
//--- [Индекс свойства][8] Флаг уменьшения значения свойства меньше контрольного уровня
//--- [Индекс свойства][9] Флаг равенства значения свойства контрольному уровню
//+------------------------------------------------------------------+
//| Устанавливает величину контролируемого прироста свойств объекта  |
//+------------------------------------------------------------------+
template<typename T> void CBaseObj::SetControlledValueINC(const int property,const T value)
  {
   if(property<this.m_long_prop_total)
      this.m_long_prop_event[property][0]=(long)value;
   else
      this.m_double_prop_event[property-this.m_long_prop_total][0]=(double)value;
  }  
//+------------------------------------------------------------------+
//| Устанавливает величину контролируемого уменьшения свойств объекта|
//+------------------------------------------------------------------+
template<typename T> void CBaseObj::SetControlledValueDEC(const int property,const T value)
  {
   if(property<this.m_long_prop_total)
      this.m_long_prop_event[property][1]=(long)value;
   else
      this.m_double_prop_event[property-this.m_long_prop_total][1]=(double)value;
  }  
//+------------------------------------------------------------------+
//| Устанавливает контрольный уровень свойств объекта                |
//+------------------------------------------------------------------+
template<typename T> void CBaseObj::SetControlledValueLEVEL(const int property,const T value)
  {
   if(property<this.m_long_prop_total)
      this.m_long_prop_event[property][2]=(long)value;
   else
      this.m_double_prop_event[property-this.m_long_prop_total][2]=(double)value;
  }  
//+------------------------------------------------------------------+
//| Устанавливает значение свойства объекта                          |
//+------------------------------------------------------------------+
template<typename T> void CBaseObj::SetControlledValue(const int property,const T value)
  {
   if(property<this.m_long_prop_total)
      this.m_long_prop_event[property][3]=(long)value;
   else
      this.m_double_prop_event[property-this.m_long_prop_total][3]=(double)value;
  }
//+------------------------------------------------------------------+
//| Устанавливает величину изменения значения свойства объекта       |
//+------------------------------------------------------------------+
template<typename T> void CBaseObj::SetControlledChangedValue(const int property,const T value)
  {
   if(property<this.m_long_prop_total)
      this.m_long_prop_event[property][4]=(long)value;
   else
      this.m_double_prop_event[property-this.m_long_prop_total][4]=(double)value;
  }
//+------------------------------------------------------------------+
//| Устанавливает Флаг изменения значения свойства                   |
//| больше, чем на величину прироста                                 |
//+------------------------------------------------------------------+
template<typename T> void CBaseObj::SetControlledFlagINC(const int property,const T value)
  {
   if(property<this.m_long_prop_total)
      this.m_long_prop_event[property][5]=(long)value;
   else
      this.m_double_prop_event[property-this.m_long_prop_total][5]=(double)value;
  }
//+------------------------------------------------------------------+
//| Устанавливает Флаг изменения значения свойства                   |
//| больше, чем на величину уменьшения                               |
//+------------------------------------------------------------------+
template<typename T> void CBaseObj::SetControlledFlagDEC(const int property,const T value)
  {
   if(property<this.m_long_prop_total)
      this.m_long_prop_event[property][6]=(long)value;
   else
      this.m_double_prop_event[property-this.m_long_prop_total][6]=(double)value;
  }
//+------------------------------------------------------------------+
//| Устанавливает Флаг увеличения значения свойства                  |
//| больше контрольного уровня                                       |
//+------------------------------------------------------------------+
template<typename T> void CBaseObj::SetControlledFlagMORE(const int property,const T value)
  {
   if(property<this.m_long_prop_total)
      this.m_long_prop_event[property][7]=(long)value;
   else
      this.m_double_prop_event[property-this.m_long_prop_total][7]=(double)value;
  }
//+------------------------------------------------------------------+
//| Устанавливает Флаг уменьшения значения свойства                  |
//| меньше контрольного уровня                                       |
//+------------------------------------------------------------------+
template<typename T> void CBaseObj::SetControlledFlagLESS(const int property,const T value)
  {
   if(property<this.m_long_prop_total)
      this.m_long_prop_event[property][8]=(long)value;
   else
      this.m_double_prop_event[property-this.m_long_prop_total][8]=(double)value;
  }
//+------------------------------------------------------------------+
//| Устанавливает Флаг равенства значения свойства                   |
//| и контрольного уровня                                            |
//+------------------------------------------------------------------+
template<typename T> void CBaseObj::SetControlledFlagEQUAL(const int property,const T value)
  {
   if(property<this.m_long_prop_total)
      this.m_long_prop_event[property][9]=(long)value;
   else
      this.m_double_prop_event[property-this.m_long_prop_total][9]=(double)value;
  }
//+------------------------------------------------------------------+

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

Метод, преобразующий ushort-значение в long-значение, смещённое на нужное количество байт для последующей упаковки его в long-контейнер:

//+------------------------------------------------------------------+
//| Преобразует ushort-значение в заданный байт long-числа           |
//+------------------------------------------------------------------+
long CBaseObj::UshortToByte(const ushort value,const uchar index) const
  {
   if(index>3)
     {
      ::Print(DFUN,TextByLanguage("Ошибка. Значение \"index\" должно быть в пределах 0 - 3","Error. The \"index\" value must be between 0 - 3"));
      return 0;
     }
   return(long)value<<(16*index);
  }
//+------------------------------------------------------------------+

Представим себе восьмибайтовое long-число, разбитое на ячейки по два байта (каждой такой ячейке присвоен свой индекс):

Байты 6-7 (индекс 3)
 Байты 4-5 (индекс 2) Байты 2-3 (индекс 1) 
Байты 0-1 (индекс 0) 
ushort 4
ushort 3
ushort 2
ushort 1

В него мы можем разместить четыре ushort-числа. Каждое последующее число нам необходимо сметить влево на 16 бит * индекс (1 байт = 8 бит), а затем полученное значение добавить к long-числу. Таким образом мы получим упакованные в long-контейнер несколько ushort-значений.

В метод передаётся ushort-число и индекс, по которому нужно хранить ushort-значение в long-контейнере.
Проверяется индекс, и если он болше 3
, то выводится сообщение о неверном индексе и возвращается 0.
Если индекс правильный, то ushort-число смещается влево на 16 бит * индекс (в одном байте 8 бит, а сместить нужно двухбайтовое ushort-число),
и результат смещения возвращается из метода.

Метод, упаковывающий смещённое на нужное количество байт ushort-значение в long-контейнер:

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

В метод передаётся ushort-число, которое необходимо упаковать в long-контейнер, передаваемый в метод по ссылке и индекс байт, в которые необходимо поместить ushort-значение в long-контейнере.
Так же, как и в вышеописанном методе проверяется индекс, а затем, при успешной проверке индекса,
к long-числу добавляется смещённое на нужное количество байт методом UshortToByte() ushort-число при помощи побитового "ИЛИ",
и результат возвращается в вызывающую программу
.

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

//+------------------------------------------------------------------+
//| Возвращает описание события объекта                              |
//+------------------------------------------------------------------+
string CBaseObj::EventDescription(const int property,
                                  const ENUM_BASE_EVENT_REASON reason,
                                  const int source,
                                  const string value,
                                  const string property_descr,
                                  const int digits)
  {
//--- В зависимости от ID коллекции создаём описание типа объекта
   string type=
     (
      this.Type()==COLLECTION_SYMBOLS_ID ? TextByLanguage("символа: ","symbol property: ")   :
      this.Type()==COLLECTION_ACCOUNT_ID ? TextByLanguage("аккаунта: ","account property: ")   :
      ""
     );
//--- В зависимости от типа свойства создаём описание величины изменения свойства
   string level=
     (
      property<this.m_long_prop_total ? 
      ::DoubleToString(this.GetControlledValueLongLEVEL(property),digits) : 
      ::DoubleToString(this.GetControlledValueDoubleLEVEL(property),digits)
     );
//--- В зависимости от причины события создаём текст описания события
   string res=
     (
      reason==BASE_EVENT_REASON_INC       ?  TextByLanguage("Значение свойства ","Value of the ")+type+property_descr+TextByLanguage(" увеличено на "," increased by ")+value       :
      reason==BASE_EVENT_REASON_DEC       ?  TextByLanguage("Значение свойства ","Value of the ")+type+property_descr+TextByLanguage(" уменьшено на "," decreased by ")+value       :
      reason==BASE_EVENT_REASON_MORE_THEN ?  TextByLanguage("Значение свойства ","Value of the ")+type+property_descr+TextByLanguage(" стало больше "," became more than ")+level   :
      reason==BASE_EVENT_REASON_LESS_THEN ?  TextByLanguage("Значение свойства ","Value of the ")+type+property_descr+TextByLanguage(" стало меньше "," became less than ")+level   :
      reason==BASE_EVENT_REASON_EQUALS    ?  TextByLanguage("Значение свойства ","Value of the ")+type+property_descr+TextByLanguage(" равно "," is equal to ")+level               :
      TextByLanguage("Неизвестное событие ","Unknown ")+type
     );
//--- Возвращаем наименование объекта+созданный текст описания события
   return this.m_name+": "+res;
  }
//+------------------------------------------------------------------+

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

  • свойство объекта, в котором зарегистрировано событие, 
  • причина события — увеличение/уменьшение значения свойства на заданную величину/пересечение заданного уровня значением свойства,
  • источник события — идентификатор коллекции, в объекте которой произошло событие,
  • значение, на которое было изменено свойство объекта
  • текстовое описание свойства объекта-наследника (доступно в наследнике) и
  • количество знаков после запятой в цифровом представлении изменённого свойства (так же доступно в наследнике).

Все шаги создания описания события объекта-наследника прокомментированы в коде, надеюсь они понятны для самостоятельного разбора.

На этом требуемые изменения класса базового объекта завершены (при дальнейшей разработке библиотеки и создании новых коллекций, в последний метод будут добавляться новые идентификаторы коллекций для создания верного описания событий).

Ставим класс символа и коллекцию символов на новые рельсы

Сегодня мы переведём работу класса символа и коллекции символов с работой с новыми событиями базового объекта. Для этого внесём изменения в классы символа и коллекции символов.
Откроем файл \MQL5\Include\DoEasy\Objects\Symbols\Symbol.mqh и начнём вносить изменения.

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

   struct MqlDataSymbol
     {
      //--- Целочисленные свойства символа
      ENUM_SYMBOL_TRADE_MODE trade_mode;     // SYMBOL_TRADE_MODE Режимы исполнения ордеров
      long session_deals;                    // SYMBOL_SESSION_DEALS Количество сделок в текущей сессии 
      long session_buy_orders;               // SYMBOL_SESSION_BUY_ORDERS Общее число ордеров на покупку в текущий момент
      long session_sell_orders;              // SYMBOL_SESSION_SELL_ORDERS Общее число ордеров на продажу в текущий момент
      long volume;                           // SYMBOL_VOLUME Volume - объем в последней сделке
      long volume_high_day;                  // SYMBOL_VOLUMEHIGH Максимальный Volume за день
      long volume_low_day;                   // SYMBOL_VOLUMELOW Минимальный Volume за день
      int spread;                            // SYMBOL_SPREAD Размер спреда в пунктах
      int stops_level;                       // SYMBOL_TRADE_STOPS_LEVEL Минимальный отступ в пунктах от текущей цены закрытия для установки Stop ордеров
      int freeze_level;                      // SYMBOL_TRADE_FREEZE_LEVEL Дистанция заморозки торговых операций (в пунктах)
      
      //--- Вещественные свойства символа
      double bid_last;                       // SYMBOL_BID/SYMBOL_LAST Bid - лучшее предложение на продажу/Цена, по которой совершена последняя сделка
      double bid_last_high;                  // SYMBOL_BIDHIGH/SYMBOL_LASTHIGH Максимальный Bid за день/Максимальный Last за день
      double bid_last_low;                   // SYMBOL_BIDLOW/SYMBOL_LASTLOW Минимальный Bid за день/Минимальный Last за день
      double ask;                            // SYMBOL_ASK Ask - лучшее предложение на покупку
      double ask_high;                       // SYMBOL_ASKHIGH Максимальный Ask за день
      double ask_low;                        // SYMBOL_ASKLOW Минимальный Ask за день
      double volume_real_day;                // SYMBOL_VOLUME_REAL Реальный Volume за день
      double volume_high_real_day;           // SYMBOL_VOLUMEHIGH_REAL Максимальный реальный Volume за день
      double volume_low_real_day;            // SYMBOL_VOLUMELOW_REAL Минимальный реальный Volume за день
      double option_strike;                  // SYMBOL_OPTION_STRIKE Цена исполнения опциона
      double volume_limit;                   // SYMBOL_VOLUME_LIMIT Максимально допустимый совокупный объем позиции и отложенных ордеров в одном направлении
      double swap_long;                      // SYMBOL_SWAP_LONG Значение свопа в покупку
      double swap_short;                     // SYMBOL_SWAP_SHORT Значение свопа в продажу
      double session_volume;                 // SYMBOL_SESSION_VOLUME Cуммарный объём сделок в текущую сессию
      double session_turnover;               // SYMBOL_SESSION_TURNOVER Cуммарный оборот в текущую сессию
      double session_interest;               // SYMBOL_SESSION_INTEREST Cуммарный объём открытых позиций
      double session_buy_ord_volume;         // SYMBOL_SESSION_BUY_ORDERS_VOLUME Общий объём ордеров на покупку в текущий момент
      double session_sell_ord_volume;        // SYMBOL_SESSION_SELL_ORDERS_VOLUME Общий объём ордеров на продажу в текущий момент
      double session_open;                   // SYMBOL_SESSION_OPEN Цена открытия сессии
      double session_close;                  // SYMBOL_SESSION_CLOSE Цена закрытия сессии
      double session_aw;                     // SYMBOL_SESSION_AW Средневзвешенная цена сессии
     };
   MqlDataSymbol    m_struct_curr_symbol;    // Текущие данные символа
   MqlDataSymbol    m_struct_prev_symbol;    // Прошлые данные символа
//---

Удалим все переменные-члены класса для хранения контролируемых и изменённых свойств объекта-символа — теперь все эти данные хранятся в массивах класса базового объекта:

   //--- Сделки текущей сессии
   long              m_control_session_deals_inc;              // Контролируемая величина прироста количества сделок
   long              m_control_session_deals_dec;              // Контролируемая величина уменьшения количества сделок
   long              m_changed_session_deals_value;            // Величина изменения количества сделок
   bool              m_is_change_session_deals_inc;            // Флаг изменения количества сделок больше, чем на величину прироста
   bool              m_is_change_session_deals_dec;            // Флаг изменения количества сделок больше, чем на величину уменьшения
   //--- Ордера Buy текущей сессии
   long              m_control_session_buy_ord_inc;            // Контролируемая величина прироста количества Buy-ордеров
   long              m_control_session_buy_ord_dec;            // Контролируемая величина уменьшения количества Buy-ордеров
   long              m_changed_session_buy_ord_value;          // Величина изменения количества Buy-ордеров
   bool              m_is_change_session_buy_ord_inc;          // Флаг изменения количества Buy-ордеров больше, чем на величину прироста
   bool              m_is_change_session_buy_ord_dec;          // Флаг изменения количества Buy-ордеров больше, чем на величину уменьшения
   //--- Ордера Sell текущей сессии
   long              m_control_session_sell_ord_inc;           // Контролируемая величина прироста количества Sell-ордеров
   long              m_control_session_sell_ord_dec;           // Контролируемая величина уменьшения количества Sell-ордеров
   long              m_changed_session_sell_ord_value;         // Величина изменения количества Sell-ордеров
   bool              m_is_change_session_sell_ord_inc;         // Флаг изменения количества Sell-ордеров больше, чем на величину прироста
   bool              m_is_change_session_sell_ord_dec;         // Флаг изменения количества Sell-ордеров больше, чем на величину уменьшения
   //--- Объем в последней сделке
   long              m_control_volume_inc;                     // Контролируемая величина прироста объема в последней сделке
   long              m_control_volume_dec;                     // Контролируемая величина уменьшения объема в последней сделке
   long              m_changed_volume_value;                   // Величина изменения объема в последней сделке
   bool              m_is_change_volume_inc;                   // Флаг изменения объема в последней сделке больше, чем на величину прироста
   bool              m_is_change_volume_dec;                   // Флаг изменения объема в последней сделке больше, чем на величину уменьшения
   //--- Максимальный Volume за день
   long              m_control_volume_high_day_inc;            // Контролируемая величина прироста максимального объема за день
   long              m_control_volume_high_day_dec;            // Контролируемая величина уменьшения максимального объема за день
   long              m_changed_volume_high_day_value;          // Величина изменения максимального объема за день
   bool              m_is_change_volume_high_day_inc;          // Флаг изменения максимального объема за день больше, чем на величину прироста
   bool              m_is_change_volume_high_day_dec;          // Флаг изменения максимального объема за день больше, чем на величину уменьшения
   //--- Минимальный Volume за день
   long              m_control_volume_low_day_inc;             // Контролируемая величина прироста минимального объема за день
   long              m_control_volume_low_day_dec;             // Контролируемая величина уменьшения минимального объема за день
   long              m_changed_volume_low_day_value;           // Величина изменения минимального объема за день
   bool              m_is_change_volume_low_day_inc;           // Флаг изменения минимального объема за день больше, чем на величину прироста
   bool              m_is_change_volume_low_day_dec;           // Флаг изменения минимального объема за день больше, чем на величину уменьшения
   //--- Спред
   int               m_control_spread_inc;                     // Контролируемая величина прироста спреда в пунктах
   int               m_control_spread_dec;                     // Контролируемая величина уменьшения спреда в пунктах
   int               m_changed_spread_value;                   // Величина изменения спреда в пунктах
   bool              m_is_change_spread_inc;                   // Флаг изменения спреда в пунктах больше, чем на величину прироста
   bool              m_is_change_spread_dec;                   // Флаг изменения спреда в пунктах больше, чем на величину уменьшения
   //--- StopLevel
   int               m_control_stops_level_inc;                // Контролируемая величина прироста StopLevel в пунктах
   int               m_control_stops_level_dec;                // Контролируемая величина уменьшения StopLevel в пунктах
   int               m_changed_stops_level_value;              // Величина изменения StopLevel в пунктах
   bool              m_is_change_stops_level_inc;              // Флаг изменения StopLevel в пунктах больше, чем на величину прироста
   bool              m_is_change_stops_level_dec;              // Флаг изменения StopLevel в пунктах больше, чем на величину уменьшения
   //--- Дистанция заморозки
   int               m_control_freeze_level_inc;               // Контролируемая величина прироста FreezeLevel в пунктах
   int               m_control_freeze_level_dec;               // Контролируемая величина уменьшения FreezeLevel в пунктах
   int               m_changed_freeze_level_value;             // Величина изменения FreezeLevel в пунктах
   bool              m_is_change_freeze_level_inc;             // Флаг изменения FreezeLevel в пунктах больше, чем на величину прироста
   bool              m_is_change_freeze_level_dec;             // Флаг изменения FreezeLevel в пунктах больше, чем на величину уменьшения
   
   //--- Bid/Last
   double            m_control_bid_last_inc;                   // Контролируемая величина прироста цены Bid или Last
   double            m_control_bid_last_dec;                   // Контролируемая величина уменьшения цены Bid или Last
   double            m_changed_bid_last_value;                 // Величина изменения цены Bid или Last
   bool              m_is_change_bid_last_inc;                 // Флаг изменения цены Bid или Last больше, чем на величину прироста
   bool              m_is_change_bid_last_dec;                 // Флаг изменения цены Bid или Last больше, чем на величину уменьшения
   //--- Максимальный Bid/Last за день
   double            m_control_bid_last_high_inc;              // Контролируемая величина прироста максимального Bid или Last за день
   double            m_control_bid_last_high_dec;              // Контролируемая величина уменьшения максимального Bid или Last за день
   double            m_changed_bid_last_high_value;            // Величина изменения максимального Bid или Last за день
   bool              m_is_change_bid_last_high_inc;            // Флаг изменения максимального Bid или Last за день больше, чем на величину прироста
   bool              m_is_change_bid_last_high_dec;            // Флаг изменения максимального Bid или Last за день больше, чем на величину уменьшения
   //--- Минимальный Bid/Last за день
   double            m_control_bid_last_low_inc;               // Контролируемая величина прироста минимального Bid или Last за день
   double            m_control_bid_last_low_dec;               // Контролируемая величина уменьшения минимального Bid или Last за день
   double            m_changed_bid_last_low_value;             // Величина изменения минимального Bid или Last за день
   bool              m_is_change_bid_last_low_inc;             // Флаг изменения минимального Bid или Last за день больше, чем на величину прироста
   bool              m_is_change_bid_last_low_dec;             // Флаг изменения минимального Bid или Last за день больше, чем на величину уменьшения
   //--- Ask
   double            m_control_ask_inc;                        // Контролируемая величина прироста цены Ask
   double            m_control_ask_dec;                        // Контролируемая величина уменьшения цены Ask
   double            m_changed_ask_value;                      // Величина изменения цены Ask
   bool              m_is_change_ask_inc;                      // Флаг изменения цены Ask больше, чем на величину прироста
   bool              m_is_change_ask_dec;                      // Флаг изменения цены Ask больше, чем на величину уменьшения
   //--- Максимальный Ask за день
   double            m_control_ask_high_inc;                   // Контролируемая величина прироста максимального Ask за день
   double            m_control_ask_high_dec;                   // Контролируемая величина уменьшения максимального Ask за день
   double            m_changed_ask_high_value;                 // Величина изменения максимального Ask за день
   bool              m_is_change_ask_high_inc;                 // Флаг изменения максимального Ask за день больше, чем на величину прироста
   bool              m_is_change_ask_high_dec;                 // Флаг изменения максимального Ask за день больше, чем на величину уменьшения
   //--- Минимальный Ask за день
   double            m_control_ask_low_inc;                    // Контролируемая величина прироста минимального Ask за день
   double            m_control_ask_low_dec;                    // Контролируемая величина уменьшения минимального Ask за день
   double            m_changed_ask_low_value;                  // Величина изменения минимального Ask за день
   bool              m_is_change_ask_low_inc;                  // Флаг изменения минимального Ask за день больше, чем на величину прироста
   bool              m_is_change_ask_low_dec;                  // Флаг изменения минимального Ask за день больше, чем на величину уменьшения
   //--- Реальный Volume за день
   double            m_control_volume_real_inc;                // Контролируемая величина прироста реального Volume за день
   double            m_control_volume_real_dec;                // Контролируемая величина уменьшения реального Volume за день
   double            m_changed_volume_real_value;              // Величина изменения реального Volume за день
   bool              m_is_change_volume_real_inc;              // Флаг изменения реального Volume за день больше, чем на величину прироста
   bool              m_is_change_volume_real_dec;              // Флаг изменения реального Volume за день больше, чем на величину уменьшения
   //--- Максимальный реальный Volume за день
   double            m_control_volume_high_real_day_inc;       // Контролируемая величина прироста максимального реального Volume за день
   double            m_control_volume_high_real_day_dec;       // Контролируемая величина уменьшения максимального реального Volume за день
   double            m_changed_volume_high_real_day_value;     // Величина изменения максимального реального Volume за день
   bool              m_is_change_volume_high_real_day_inc;     // Флаг изменения максимального реального Volume за день больше, чем на величину прироста
   bool              m_is_change_volume_high_real_day_dec;     // Флаг изменения максимального реального Volume за день больше, чем на величину уменьшения
   //--- Минимальный реальный Volume за день
   double            m_control_volume_low_real_day_inc;        // Контролируемая величина прироста минимального реального Volume за день
   double            m_control_volume_low_real_day_dec;        // Контролируемая величина уменьшения минимального реального Volume за день
   double            m_changed_volume_low_real_day_value;      // Величина изменения минимального реального Volume за день
   bool              m_is_change_volume_low_real_day_inc;      // Флаг изменения минимального реального Volume за день больше, чем на величину прироста
   bool              m_is_change_volume_low_real_day_dec;      // Флаг изменения минимального реального Volume за день больше, чем на величину уменьшения
   //--- Цена исполнения опциона
   double            m_control_option_strike_inc;              // Контролируемая величина прироста цены исполнения опциона
   double            m_control_option_strike_dec;              // Контролируемая величина уменьшения цены исполнения опциона
   double            m_changed_option_strike_value;            // Величина изменения цены исполнения опциона
   bool              m_is_change_option_strike_inc;            // Флаг изменения цены исполнения опциона больше, чем на величину прироста
   bool              m_is_change_option_strike_dec;            // Флаг изменения цены исполнения опциона больше, чем на величину уменьшения
   //--- Совокупный объём позиций и ордеров
   double            m_changed_volume_limit_value;             // Величина изменения минимального совокупного объёма
   bool              m_is_change_volume_limit_inc;             // Флаг увеличения минимального совокупного объёма
   bool              m_is_change_volume_limit_dec;             // Флаг уменьшения минимального совокупного объёма
   //---  Своп на покупку
   double            m_changed_swap_long_value;                // Величина изменения свопа на покупку
   bool              m_is_change_swap_long_inc;                // Флаг увеличения свопа на покупку
   bool              m_is_change_swap_long_dec;                // Флаг уменьшения свопа на покупку
   //---  Своп на продажу
   double            m_changed_swap_short_value;               // Величина изменения свопа на продажу
   bool              m_is_change_swap_short_inc;               // Флаг увеличения свопа на продажу
   bool              m_is_change_swap_short_dec;               // Флаг уменьшения свопа на продажу
   //--- Cуммарный объём сделок в текущую сессию
   double            m_control_session_volume_inc;             // Контролируемая величина прироста суммарного объёма сделок в текущую сессию
   double            m_control_session_volume_dec;             // Контролируемая величина уменьшения суммарного объёма сделок в текущую сессию
   double            m_changed_session_volume_value;           // Величина изменения суммарного объёма сделок в текущую сессию
   bool              m_is_change_session_volume_inc;           // Флаг изменения суммарного объёма сделок в текущую сессию больше, чем на величину прироста
   bool              m_is_change_session_volume_dec;           // Флаг изменения суммарного объёма сделок в текущую сессию больше, чем на величину уменьшения
   //--- Cуммарный оборот в текущую сессию
   double            m_control_session_turnover_inc;           // Контролируемая величина прироста суммарного оборота в текущую сессию
   double            m_control_session_turnover_dec;           // Контролируемая величина уменьшения суммарного оборота в текущую сессию
   double            m_changed_session_turnover_value;         // Величина изменения суммарного оборота в текущую сессию
   bool              m_is_change_session_turnover_inc;         // Флаг изменения суммарного оборота в текущую сессию больше, чем на величину прироста
   bool              m_is_change_session_turnover_dec;         // Флаг изменения суммарного оборота в текущую сессию больше, чем на величину уменьшения
   //--- Cуммарный объём открытых позиций
   double            m_control_session_interest_inc;           // Контролируемая величина прироста суммарного объёма открытых позиций в текущую сессию
   double            m_control_session_interest_dec;           // Контролируемая величина уменьшения суммарного объёма открытых позиций в текущую сессию
   double            m_changed_session_interest_value;         // Величина изменения суммарного объёма открытых позиций в текущую сессию
   bool              m_is_change_session_interest_inc;         // Флаг изменения суммарного объёма открытых позиций в текущую сессию больше, чем на величину прироста
   bool              m_is_change_session_interest_dec;         // Флаг изменения суммарного объёма открытых позиций в текущую сессию больше, чем на величину уменьшения
   //--- Общий объём ордеров на покупку в текущий момент
   double            m_control_session_buy_ord_volume_inc;     // Контролируемая величина прироста общего объёма ордеров на покупку в текущий момент
   double            m_control_session_buy_ord_volume_dec;     // Контролируемая величина уменьшения общего объёма ордеров на покупку в текущий момент
   double            m_changed_session_buy_ord_volume_value;   // Величина изменения общего объёма ордеров на покупку в текущий момент
   bool              m_is_change_session_buy_ord_volume_inc;   // Флаг изменения общего объёма ордеров на покупку в текущий момент больше, чем на величину прироста
   bool              m_is_change_session_buy_ord_volume_dec;   // Флаг изменения общего объёма ордеров на покупку в текущий момент больше, чем на величину уменьшения
   //--- Общий объём ордеров на продажу в текущий момент
   double            m_control_session_sell_ord_volume_inc;    // Контролируемая величина прироста общего объёма ордеров на продажу в текущий момент
   double            m_control_session_sell_ord_volume_dec;    // Контролируемая величина уменьшения общего объёма ордеров на продажу в текущий момент
   double            m_changed_session_sell_ord_volume_value;  // Величина изменения общего объёма ордеров на продажу в текущий момент
   bool              m_is_change_session_sell_ord_volume_inc;  // Флаг изменения общего объёма ордеров на продажу в текущий момент больше, чем на величину прироста
   bool              m_is_change_session_sell_ord_volume_dec;  // Флаг изменения общего объёма ордеров на продажу в текущий момент больше, чем на величину уменьшения
   //--- Цена открытия сессии
   double            m_control_session_open_inc;               // Контролируемая величина прироста цены открытия сессии
   double            m_control_session_open_dec;               // Контролируемая величина уменьшения цены открытия сессии
   double            m_changed_session_open_value;             // Величина изменения цены открытия сессии
   bool              m_is_change_session_open_inc;             // Флаг изменения цены открытия сессии больше, чем на величину прироста
   bool              m_is_change_session_open_dec;             // Флаг изменения цены открытия сессии больше, чем на величину уменьшения
   //--- Цена закрытия сессии
   double            m_control_session_close_inc;              // Контролируемая величина прироста цены закрытия сессии
   double            m_control_session_close_dec;              // Контролируемая величина уменьшения цены закрытия сессии
   double            m_changed_session_close_value;            // Величина изменения цены закрытия сессии
   bool              m_is_change_session_close_inc;            // Флаг изменения цены закрытия сессии больше, чем на величину прироста
   bool              m_is_change_session_close_dec;            // Флаг изменения цены закрытия сессии больше, чем на величину уменьшения
   //--- Средневзвешенная цена сессии
   double            m_control_session_aw_inc;                 // Контролируемая величина прироста средневзвешенной цены сессии
   double            m_control_session_aw_dec;                 // Контролируемая величина уменьшения средневзвешенной цены сессии
   double            m_changed_session_aw_value;               // Величина изменения средневзвешенной цены сессии
   bool              m_is_change_session_aw_inc;               // Флаг изменения средневзвешенной цены сессии больше, чем на величину прироста
   bool              m_is_change_session_aw_dec;               // Флаг изменения средневзвешенной цены сессии больше, чем на величину уменьшения

Удалим за ненадобностью выделенные методы:

//--- Инициализирует переменные (1) отслеживаемых, (2) контролируемых данных символа
   virtual void      InitChangesParams(void);
   virtual void      InitControlsParams(void);
//--- Проверяет изменения символа, возвращает код изменения
   virtual int       SetEventCode(void);
//--- Устанавливает тип события и заполняет список событий
   virtual void      SetTypeEvent(void);
//--- Возвращает описание события символа
   string            EventDescription(const ENUM_SYMBOL_EVENT event);

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

//--- Инициализирует переменные контролируемых данных символа
   virtual void      InitControlsParams(void);
//--- Проверяет список изменений свойств символа и создаёт событие
   void              CheckEvents(void);

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

public:
//--- Устанавливает величину изменения контролируемого свойства символа
   template<typename T> void  SetControlChangedValue(const int property,const T value);
//--- Устанавливает величину контролируемого (1) приращения, (2) уменьшения свойства символа, (3) контрольного уровня
   template<typename T> void  SetControlPropertyINC(const int property,const T value);
   template<typename T> void  SetControlPropertyDEC(const int property,const T value);
   template<typename T> void  SetControlPropertyLEVEL(const int property,const T value);
//--- Устанавливает Флаг изменения значения свойства символа больше, чем на величину (1) прироста, (2) уменьшения
   template<typename T> void  SetControlFlagINC(const int property,const T value);
   template<typename T> void  SetControlFlagDEC(const int property,const T value);
   
//--- Возвращает установленную величину контролируемого приращения (1) целочисленного, (2) вещественного свойства символа
   long              GetControlParameterINC(const ENUM_SYMBOL_PROP_INTEGER property)   const { return this.GetControlledValueLongINC(property);             }
   double            GetControlParameterINC(const ENUM_SYMBOL_PROP_DOUBLE property)    const { return this.GetControlledValueDoubleINC(property);           }
//--- Возвращает установленную величину контролируемого уменьшения (1) целочисленного, (2) вещественного свойства символа
   long              GetControlParameterDEC(const ENUM_SYMBOL_PROP_INTEGER property)   const { return this.GetControlledValueLongDEC(property);             }
   double            GetControlParameterDEC(const ENUM_SYMBOL_PROP_DOUBLE property)    const { return this.GetControlledValueDoubleDEC(property);           }
//--- Возвращает Флаг изменения значения (1) целочисленного, (2) вещественного свойства символа больше, чем на величину прироста
   long              GetControlFlagINC(const ENUM_SYMBOL_PROP_INTEGER property)        const { return this.GetControlledFlagLongINC(property);              }
   double            GetControlFlagINC(const ENUM_SYMBOL_PROP_DOUBLE property)         const { return this.GetControlledFlagDoubleINC(property);            }
//--- Возвращает Флаг изменения значения (1) целочисленного, (2) вещественного свойства символа больше, чем на величину уменьшения
   bool              GetControlFlagDEC(const ENUM_SYMBOL_PROP_INTEGER property)        const { return (bool)this.GetControlledFlagLongDEC(property);        }
   bool              GetControlFlagDEC(const ENUM_SYMBOL_PROP_DOUBLE property)         const { return (bool)this.GetControlledFlagDoubleDEC(property);      }
//--- Возвращает величину изменения контролируемого (1) целочисленного, (2) вещественного свойства объекта
   long              GetControlChangedValue(const ENUM_SYMBOL_PROP_INTEGER property)   const { return this.GetControlledChangedValueLong(property);         }
   double            GetControlChangedValue(const ENUM_SYMBOL_PROP_DOUBLE property)    const { return this.GetControlledChangedValueDouble(property);       }
   
//+------------------------------------------------------------------+

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

Ранее мы уже писали методы упрощённого доступа к некоторым свойствам объекта-символа. Допишем туда же методы установки величин контрольных уровней значений свойств и объявим методы установки/получения данных для Bid/Last и связанных с ними параметров (ранее автоматически выбиралось что использовать Bid или Last в зависимости от цен, по которым строится график. Теперь же необходимо создать методы для работы с этими данными):

//+------------------------------------------------------------------+
//| Получение и установка параметров отслеживаемых изменений свойств |
//+------------------------------------------------------------------+
   //--- Исполнение
   //--- Флаг изменения режима торговли для символа
   bool              IsChangedTradeMode(void)                              const { return this.m_is_change_trade_mode;                                      } 
   //--- Сделки текущей сессии
   //--- установка контролируемой величины (1) прироста, (2) уменьшения, (3) контрольного уровня количества сделок в текущую сессию
   //--- получение (3) величины изменения количества сделок в текущую сессию,
   //--- получение флага изменения количества сделок в текущую сессию больше, чем на величину (4) прироста, (5) уменьшения
   void              SetControlSessionDealsInc(const long value)                 { this.SetControlPropertyINC(SYMBOL_PROP_SESSION_DEALS,(long)::fabs(value));        }
   void              SetControlSessionDealsDec(const long value)                 { this.SetControlPropertyDEC(SYMBOL_PROP_SESSION_DEALS,(long)::fabs(value));        }
   void              SetControlSessionDealsLevel(const long value)               { this.SetControlPropertyLEVEL(SYMBOL_PROP_SESSION_DEALS,(long)::fabs(value));      }
   long              GetValueChangedSessionDeals(void)                     const { return this.GetControlChangedValue(SYMBOL_PROP_SESSION_DEALS);                    }
   bool              IsIncreasedSessionDeals(void)                         const { return (bool)this.GetControlFlagINC(SYMBOL_PROP_SESSION_DEALS);                   }
   bool              IsDecreasedSessionDeals(void)                         const { return (bool)this.GetControlFlagDEC(SYMBOL_PROP_SESSION_DEALS);                   }
   //--- Ордера Buy текущей сессии
   //--- установка контролируемой величины (1) прироста, (2) уменьшения, (3) контрольного уровня количества Buy-ордеров в текущий момент
   //--- получение (4) величины изменения количества Buy-ордеров в текущий момент,
   //--- получение флага изменения количества Buy-ордеров в текущий момент больше, чем на величину (5) прироста, (6) уменьшения
   void              SetControlSessionBuyOrdInc(const long value)                { this.SetControlPropertyINC(SYMBOL_PROP_SESSION_BUY_ORDERS,(long)::fabs(value));   }
   void              SetControlSessionBuyOrdDec(const long value)                { this.SetControlPropertyDEC(SYMBOL_PROP_SESSION_BUY_ORDERS,(long)::fabs(value));   }
   void              SetControlSessionBuyOrdLevel(const long value)              { this.SetControlPropertyLEVEL(SYMBOL_PROP_SESSION_BUY_ORDERS,(long)::fabs(value)); }
   long              GetValueChangedSessionBuyOrders(void)                 const { return this.GetControlChangedValue(SYMBOL_PROP_SESSION_BUY_ORDERS);               }
   bool              IsIncreasedSessionBuyOrders(void)                     const { return (bool)this.GetControlFlagINC(SYMBOL_PROP_SESSION_BUY_ORDERS);              }
   bool              IsDecreasedSessionBuyOrders(void)                     const { return (bool)this.GetControlFlagDEC(SYMBOL_PROP_SESSION_BUY_ORDERS);              }
   //--- Ордера Sell текущей сессии
   //--- установка контролируемой величины (1) прироста, (2) уменьшения, (3) контрольного уровня количества Sell-ордеров в текущий момент
   //--- получение (4) величины изменения количества Sell-ордеров в текущий момент,
   //--- получение флага изменения количества Sell-ордеров в текущий момент больше, чем на величину (5) прироста, (6) уменьшения
   void              SetControlSessionSellOrdInc(const long value)               { this.SetControlPropertyINC(SYMBOL_PROP_SESSION_SELL_ORDERS,(long)::fabs(value));  }
   void              SetControlSessionSellOrdDec(const long value)               { this.SetControlPropertyDEC(SYMBOL_PROP_SESSION_SELL_ORDERS,(long)::fabs(value));  }
   void              SetControlSessionSellOrdLevel(const long value)             { this.SetControlPropertyLEVEL(SYMBOL_PROP_SESSION_SELL_ORDERS,(long)::fabs(value));}
   long              GetValueChangedSessionSellOrders(void)                const { return this.GetControlChangedValue(SYMBOL_PROP_SESSION_SELL_ORDERS);              }
   bool              IsIncreasedSessionSellOrders(void)                    const { return (bool)this.GetControlFlagINC(SYMBOL_PROP_SESSION_SELL_ORDERS);             }
   bool              IsDecreasedSessionSellOrders(void)                    const { return (bool)this.GetControlFlagDEC(SYMBOL_PROP_SESSION_SELL_ORDERS);             }
   //--- Объем в последней сделке
   //--- установка контролируемой величины (1) прироста, (2) уменьшения, (3) контрольного уровня объема в последней сделке
   //--- получение (4) величины изменения объема в последней сделке,
   //--- получение флага изменения объема в последней сделке больше, чем на величину (5) прироста, (6) уменьшения
   void              SetControlVolumeInc(const long value)                       { this.SetControlPropertyINC(SYMBOL_PROP_VOLUME,(long)::fabs(value));               }
   void              SetControlVolumeDec(const long value)                       { this.SetControlPropertyDEC(SYMBOL_PROP_VOLUME,(long)::fabs(value));               }
   void              SetControlVolumeLevel(const long value)                     { this.SetControlPropertyLEVEL(SYMBOL_PROP_VOLUME,(long)::fabs(value));             }
   long              GetValueChangedVolume(void)                           const { return this.GetControlChangedValue(SYMBOL_PROP_VOLUME);                           }
   bool              IsIncreasedVolume(void)                               const { return (bool)this.GetControlFlagINC(SYMBOL_PROP_VOLUME);                          }
   bool              IsDecreasedVolume(void)                               const { return (bool)this.GetControlFlagDEC(SYMBOL_PROP_VOLUME);                          }
   //--- Максимальный Volume за день
   //--- установка контролируемой величины (1) прироста, (2) уменьшения, (3) контрольного уровня максимального объема за день
   //--- получение (4) величины изменения максимального объема за день,
   //--- получение флага изменения максимального объема за день больше, чем на величину (5) прироста, (6) уменьшения
   void              SetControlVolumeHighInc(const long value)                   { this.SetControlPropertyINC(SYMBOL_PROP_VOLUMEHIGH,(long)::fabs(value));           }
   void              SetControlVolumeHighDec(const long value)                   { this.SetControlPropertyDEC(SYMBOL_PROP_VOLUMEHIGH,(long)::fabs(value));           }
   void              SetControlVolumeHighLevel(const long value)                 { this.SetControlPropertyLEVEL(SYMBOL_PROP_VOLUMEHIGH,(long)::fabs(value));         }
   long              GetValueChangedVolumeHigh(void)                       const { return this.GetControlChangedValue(SYMBOL_PROP_VOLUMEHIGH);                       }
   bool              IsIncreasedVolumeHigh(void)                           const { return (bool)this.GetControlFlagINC(SYMBOL_PROP_VOLUMEHIGH);                      }
   bool              IsDecreasedVolumeHigh(void)                           const { return (bool)this.GetControlFlagDEC(SYMBOL_PROP_VOLUMEHIGH);                      }
   //--- Минимальный Volume за день
   //--- установка контролируемой величины (1) прироста, (2) уменьшения, (3) контрольного уровня минимального объема за день
   //--- получение (4) величины изменения минимального объема за день,
   //--- получение флага изменения минимального объема за день больше, чем на величину (5) прироста, (6) уменьшения
   void              SetControlVolumeLowInc(const long value)                    { this.SetControlPropertyINC(SYMBOL_PROP_VOLUMELOW,(long)::fabs(value));            }
   void              SetControlVolumeLowDec(const long value)                    { this.SetControlPropertyDEC(SYMBOL_PROP_VOLUMELOW,(long)::fabs(value));            }
   void              SetControlVolumeLowLevel(const long value)                  { this.SetControlPropertyLEVEL(SYMBOL_PROP_VOLUMELOW,(long)::fabs(value));          }
   long              GetValueChangedVolumeLow(void)                        const { return this.GetControlChangedValue(SYMBOL_PROP_VOLUMELOW);                        }
   bool              IsIncreasedVolumeLow(void)                            const { return (bool)this.GetControlFlagINC(SYMBOL_PROP_VOLUMELOW);                       }
   bool              IsDecreasedVolumeLow(void)                            const { return (bool)this.GetControlFlagDEC(SYMBOL_PROP_VOLUMELOW);                       }
   //--- Спред
   //--- установка контролируемой величины (1) прироста, (2) уменьшения, (3) контрольного уровня спреда в пунктах
   //--- получение (4) величины изменения спреда в пунктах,
   //--- получение флага изменения спреда в пунктах больше, чем на величину (5) прироста, (6) уменьшения
   void              SetControlSpreadInc(const int value)                        { this.SetControlPropertyINC(SYMBOL_PROP_SPREAD,(long)::fabs(value));               }
   void              SetControlSpreadDec(const int value)                        { this.SetControlPropertyDEC(SYMBOL_PROP_SPREAD,(long)::fabs(value));               }
   void              SetControlSpreadLevel(const int value)                      { this.SetControlPropertyLEVEL(SYMBOL_PROP_SPREAD,(long)::fabs(value));             }
   int               GetValueChangedSpread(void)                           const { return (int)this.GetControlChangedValue(SYMBOL_PROP_SPREAD);                      }
   bool              IsIncreasedSpread(void)                               const { return (bool)this.GetControlFlagINC(SYMBOL_PROP_SPREAD);                          }
   bool              IsDecreasedSpread(void)                               const { return (bool)this.GetControlFlagDEC(SYMBOL_PROP_SPREAD);                          }
   //--- StopLevel
   //--- установка контролируемой величины (1) прироста, (2) уменьшения, (3) контрольного уровня StopLevel в пунктах
   //--- получение (4) величины изменения StopLevel в пунктах,
   //--- получение флага изменения StopLevel в пунктах больше, чем на величину (5) прироста, (6) уменьшения
   void              SetControlStopLevelInc(const int value)                     { this.SetControlPropertyINC(SYMBOL_PROP_TRADE_STOPS_LEVEL,(long)::fabs(value));    }
   void              SetControlStopLevelDec(const int value)                     { this.SetControlPropertyDEC(SYMBOL_PROP_TRADE_STOPS_LEVEL,(long)::fabs(value));    }
   void              SetControlStopLevelLevel(const int value)                   { this.SetControlPropertyLEVEL(SYMBOL_PROP_TRADE_STOPS_LEVEL,(long)::fabs(value));  }
   int               GetValueChangedStopLevel(void)                        const { return (int)this.GetControlChangedValue(SYMBOL_PROP_TRADE_STOPS_LEVEL);           }
   bool              IsIncreasedStopLevel(void)                            const { return (bool)this.GetControlFlagINC(SYMBOL_PROP_TRADE_STOPS_LEVEL);               }
   bool              IsDecreasedStopLevel(void)                            const { return (bool)this.GetControlFlagDEC(SYMBOL_PROP_TRADE_STOPS_LEVEL);               }
   //--- Дистанция заморозки
   //--- установка контролируемой величины (1) прироста, (2) уменьшения, (3) контрольного уровня FreezeLevel в пунктах
   //--- получение (4) величины изменения FreezeLevel в пунктах,
   //--- получение флага изменения FreezeLevel в пунктах больше, чем на величину (5) прироста, (6) уменьшения
   void              SetControlFreezeLevelInc(const int value)                   { this.SetControlPropertyINC(SYMBOL_PROP_TRADE_FREEZE_LEVEL,(long)::fabs(value));   }
   void              SetControlFreezeLevelDec(const int value)                   { this.SetControlPropertyDEC(SYMBOL_PROP_TRADE_FREEZE_LEVEL,(long)::fabs(value));   }
   void              SetControlFreezeLevelLevel(const int value)                 { this.SetControlPropertyLEVEL(SYMBOL_PROP_TRADE_FREEZE_LEVEL,(long)::fabs(value)); }
   int               GetValueChangedFreezeLevel(void)                      const { return (int)this.GetControlChangedValue(SYMBOL_PROP_TRADE_FREEZE_LEVEL);          }
   bool              IsIncreasedFreezeLevel(void)                          const { return (bool)this.GetControlFlagINC(SYMBOL_PROP_TRADE_FREEZE_LEVEL);              }
   bool              IsDecreasedFreezeLevel(void)                          const { return (bool)this.GetControlFlagDEC(SYMBOL_PROP_TRADE_FREEZE_LEVEL);              }
   
   //--- Bid
   //--- установка контролируемой величины (1) прироста, (2) уменьшения, (3) контрольного уровня цены Bid
   //--- получение (4) величины изменения цены Bid или Last,
   //--- получение флага изменения цены Bid или Last больше, чем на величину (5) прироста, (6) уменьшения
   void              SetControlBidInc(const double value)                        { this.SetControlPropertyINC(SYMBOL_PROP_BID,::fabs(value));                        }
   void              SetControlBidDec(const double value)                        { this.SetControlPropertyDEC(SYMBOL_PROP_BID,::fabs(value));                        }
   void              SetControlBidLevel(const double value)                      { this.SetControlPropertyLEVEL(SYMBOL_PROP_BID,::fabs(value));                      }
   double            GetValueChangedBid(void)                              const { return this.GetControlChangedValue(SYMBOL_PROP_BID);                              }
   bool              IsIncreasedBid(void)                                  const { return (bool)this.GetControlFlagINC(SYMBOL_PROP_BID);                             }
   bool              IsDecreasedBid(void)                                  const { return (bool)this.GetControlFlagDEC(SYMBOL_PROP_BID);                             }
   //--- Максимальный Bid за день
   //--- установка контролируемой величины (1) прироста, (2) уменьшения, (3) контрольного уровня максимального Bid
   //--- получение (4) величины изменения максимального Bid или Last,
   //--- получение флага изменения максимального Bid или Last больше, чем на величину (5) прироста, (6) уменьшения
   void              SetControlBidHighInc(const double value)                    { this.SetControlPropertyINC(SYMBOL_PROP_BIDHIGH,::fabs(value));                    }
   void              SetControlBidHighDec(const double value)                    { this.SetControlPropertyDEC(SYMBOL_PROP_BIDHIGH,::fabs(value));                    }
   void              SetControlBidHighLevel(const double value)                  { this.SetControlPropertyLEVEL(SYMBOL_PROP_BIDHIGH,::fabs(value));                  }
   double            GetValueChangedBidHigh(void)                          const { return this.GetControlChangedValue(SYMBOL_PROP_BIDHIGH);                          }
   bool              IsIncreasedBidHigh(void)                              const { return (bool)this.GetControlFlagINC(SYMBOL_PROP_BIDHIGH);                         }
   bool              IsDecreasedBidHigh(void)                              const { return (bool)this.GetControlFlagDEC(SYMBOL_PROP_BIDHIGH);                         }
   //--- Минимальный Bid за день
   //--- установка контролируемой величины (1) прироста, (2) уменьшения, (3) контрольного уровня минимального Bid
   //--- получение (4) величины изменения минимального Bid или Last,
   //--- получение флага изменения минимального Bid или Last больше, чем на величину (5) прироста, (6) уменьшения
   void              SetControlBidLowInc(const double value)                     { this.SetControlPropertyINC(SYMBOL_PROP_BIDLOW,::fabs(value));                     }
   void              SetControlBidLowDec(const double value)                     { this.SetControlPropertyDEC(SYMBOL_PROP_BIDLOW,::fabs(value));                     }
   void              SetControlBidLowLevel(const double value)                   { this.SetControlPropertyLEVEL(SYMBOL_PROP_BIDLOW,::fabs(value));                   }
   double            GetValueChangedBidLow(void)                           const { return this.GetControlChangedValue(SYMBOL_PROP_BIDLOW);                           }
   bool              IsIncreasedBidLow(void)                               const { return (bool)this.GetControlFlagINC(SYMBOL_PROP_BIDLOW);                          }
   bool              IsDecreasedBidLow(void)                               const { return (bool)this.GetControlFlagDEC(SYMBOL_PROP_BIDLOW);                          }
   
   //--- Last
   //--- установка контролируемой величины (1) прироста, (2) уменьшения, (3) контрольного уровня цены Last
   //--- получение (4) величины изменения цены Bid или Last,
   //--- получение флага изменения цены Bid или Last больше, чем на величину (5) прироста, (6) уменьшения
   void              SetControlLastInc(const double value)                       { this.SetControlPropertyINC(SYMBOL_PROP_LAST,::fabs(value));                       }
   void              SetControlLastDec(const double value)                       { this.SetControlPropertyDEC(SYMBOL_PROP_LAST,::fabs(value));                       }
   void              SetControlLastLevel(const double value)                     { this.SetControlPropertyLEVEL(SYMBOL_PROP_LAST,::fabs(value));                     }
   double            GetValueChangedLast(void)                             const { return this.GetControlChangedValue(SYMBOL_PROP_LAST);                             }
   bool              IsIncreasedLast(void)                                 const { return (bool)this.GetControlFlagINC(SYMBOL_PROP_LAST);                            }
   bool              IsDecreasedLast(void)                                 const { return (bool)this.GetControlFlagDEC(SYMBOL_PROP_LAST);                            }
   //--- Максимальный Last за день
   //--- установка контролируемой величины (1) прироста, (2) уменьшения, (3) контрольного уровня максимального Last
   //--- получение (4) величины изменения максимального Bid или Last,
   //--- получение флага изменения максимального Bid или Last больше, чем на величину (5) прироста, (6) уменьшения
   void              SetControlLastHighInc(const double value)                   { this.SetControlPropertyINC(SYMBOL_PROP_LASTHIGH,::fabs(value));                   }
   void              SetControlLastHighDec(const double value)                   { this.SetControlPropertyDEC(SYMBOL_PROP_LASTHIGH,::fabs(value));                   }
   void              SetControlLastHighLevel(const double value)                 { this.SetControlPropertyLEVEL(SYMBOL_PROP_LASTHIGH,::fabs(value));                 }
   double            GetValueChangedLastHigh(void)                         const { return this.GetControlChangedValue(SYMBOL_PROP_LASTHIGH);                         }
   bool              IsIncreasedLastHigh(void)                             const { return (bool)this.GetControlFlagINC(SYMBOL_PROP_LASTHIGH);                        }
   bool              IsDecreasedLastHigh(void)                             const { return (bool)this.GetControlFlagDEC(SYMBOL_PROP_LASTHIGH);                        }
   //--- Минимальный Last за день
   //--- установка контролируемой величины (1) прироста, (2) уменьшения, (3) контрольного уровня минимального Last
   //--- получение (4) величины изменения минимального Bid или Last,
   //--- получение флага изменения минимального Bid или Last больше, чем на величину (5) прироста, (6) уменьшения
   void              SetControlLastLowInc(const double value)                    { this.SetControlPropertyINC(SYMBOL_PROP_LASTLOW,::fabs(value));                    }
   void              SetControlLastLowDec(const double value)                    { this.SetControlPropertyDEC(SYMBOL_PROP_LASTLOW,::fabs(value));                    }
   void              SetControlLastLowLevel(const double value)                  { this.SetControlPropertyLEVEL(SYMBOL_PROP_LASTLOW,::fabs(value));                  }
   double            GetValueChangedLastLow(void)                          const { return this.GetControlChangedValue(SYMBOL_PROP_LASTLOW);                          }
   bool              IsIncreasedLastLow(void)                              const { return (bool)this.GetControlFlagINC(SYMBOL_PROP_LASTLOW);                         }
   bool              IsDecreasedLastLow(void)                              const { return (bool)this.GetControlFlagDEC(SYMBOL_PROP_LASTLOW);                         }
   
   //--- Bid/Last
   //--- установка контролируемой величины (1) прироста, (2) уменьшения, (3) контрольного уровня цены Bid или Last
   //--- получение (4) величины изменения цены Bid или Last,
   //--- получение флага изменения цены Bid или Last больше, чем на величину (5) прироста, (6) уменьшения
   void              SetControlBidLastInc(const double value);
   void              SetControlBidLastDec(const double value);
   void              SetControlBidLastLevel(const double value);
   double            GetValueChangedBidLast(void)                          const;
   bool              IsIncreasedBidLast(void)                              const;
   bool              IsDecreasedBidLast(void)                              const;
   //--- Максимальный Bid/Last за день
   //--- установка контролируемой величины (1) прироста, (2) уменьшения, (3) контрольного уровня максимального Bid или Last
   //--- получение (4) величины изменения максимального Bid или Last,
   //--- получение флага изменения максимального Bid или Last больше, чем на величину (5) прироста, (6) уменьшения
   void              SetControlBidLastHighInc(const double value);
   void              SetControlBidLastHighDec(const double value);
   void              SetControlBidLastHighLevel(const double value);
   double            GetValueChangedBidLastHigh(void)                      const;
   bool              IsIncreasedBidLastHigh(void)                          const;
   bool              IsDecreasedBidLastHigh(void)                          const;
   //--- Минимальный Bid/Last за день
   //--- установка контролируемой величины (1) прироста, (2) уменьшения, (3) контрольного уровня минимального Bid или Last
   //--- получение (4) величины изменения минимального Bid или Last,
   //--- получение флага изменения минимального Bid или Last больше, чем на величину (5) прироста, (6) уменьшения
   void              SetControlBidLastLowInc(const double value);
   void              SetControlBidLastLowDec(const double value);
   void              SetControlBidLastLowLevev(const double value);
   double            GetValueChangedBidLastLow(void)                       const;
   bool              IsIncreasedBidLastLow(void)                           const;
   bool              IsDecreasedBidLastLow(void)                           const;
   
   //--- Ask
   //--- установка контролируемой величины (1) прироста, (2) уменьшения, (3) контрольного уровня цены Ask
   //--- получение (4) величины изменения цены Ask,
   //--- получение флага изменения цены Ask больше, чем на величину (5) прироста, (6) уменьшения
   void              SetControlAskInc(const double value)                        { this.SetControlPropertyINC(SYMBOL_PROP_ASK,::fabs(value));                        }
   void              SetControlAskDec(const double value)                        { this.SetControlPropertyDEC(SYMBOL_PROP_ASK,::fabs(value));                        }
   void              SetControlAskLevel(const double value)                      { this.SetControlPropertyLEVEL(SYMBOL_PROP_ASK,::fabs(value));                      }
   double            GetValueChangedAsk(void)                              const { return this.GetControlChangedValue(SYMBOL_PROP_ASK);                              }
   bool              IsIncreasedAsk(void)                                  const { return (bool)this.GetControlFlagINC(SYMBOL_PROP_ASK);                             }
   bool              IsDecreasedAsk(void)                                  const { return (bool)this.GetControlFlagDEC(SYMBOL_PROP_ASK);                             }
   //--- Максимальный Ask за день
   //--- установка контролируемой величины (1) прироста, (2) уменьшения, (3) контрольного уровня максимального Ask за день
   //--- получение (4) величины изменения максимального Ask за день,
   //--- получение флага изменения максимального Ask за день больше, чем на величину (5) прироста, (6) уменьшения
   void              SetControlAskHighInc(const double value)                    { this.SetControlPropertyINC(SYMBOL_PROP_ASKHIGH,::fabs(value));                    }
   void              SetControlAskHighDec(const double value)                    { this.SetControlPropertyDEC(SYMBOL_PROP_ASKHIGH,::fabs(value));                    }
   void              SetControlAskHighLevel(const double value)                  { this.SetControlPropertyLEVEL(SYMBOL_PROP_ASKHIGH,::fabs(value));                  }
   double            GetValueChangedAskHigh(void)                          const { return this.GetControlChangedValue(SYMBOL_PROP_ASKHIGH);                          }
   bool              IsIncreasedAskHigh(void)                              const { return (bool)this.GetControlFlagINC(SYMBOL_PROP_ASKHIGH);                         }
   bool              IsDecreasedAskHigh(void)                              const { return (bool)this.GetControlFlagDEC(SYMBOL_PROP_ASKHIGH);                         }
   //--- Минимальный Ask за день
   //--- установка контролируемой величины (1) прироста, (2) уменьшения, (3) контрольного уровня минимального Ask за день
   //--- получение (4) величины изменения минимального Ask за день,
   //--- получение флага изменения минимального Ask за день больше, чем на величину (5) прироста, (6) уменьшения
   void              SetControlAskLowInc(const double value)                     { this.SetControlPropertyINC(SYMBOL_PROP_ASKLOW,::fabs(value));                     }
   void              SetControlAskLowDec(const double value)                     { this.SetControlPropertyDEC(SYMBOL_PROP_ASKLOW,::fabs(value));                     }
   void              SetControlAskLowLevel(const double value)                   { this.SetControlPropertyLEVEL(SYMBOL_PROP_ASKLOW,::fabs(value));                   }
   double            GetValueChangedAskLow(void)                           const { return this.GetControlChangedValue(SYMBOL_PROP_ASKLOW);                           }
   bool              IsIncreasedAskLow(void)                               const { return (bool)this.GetControlFlagINC(SYMBOL_PROP_ASKLOW);                          }
   bool              IsDecreasedAskLow(void)                               const { return (bool)this.GetControlFlagDEC(SYMBOL_PROP_ASKLOW);                          }
   //--- Реальный Volume за день
   //--- установка контролируемой величины (1) прироста, (2) уменьшения, (3) контрольного уровня реального Volume за день
   //--- получение (4) величины изменения реального Volume за день,
   //--- получение флага изменения реального Volume за день больше, чем на величину (5) прироста, (6) уменьшения
   void              SetControlVolumeRealInc(const double value)                 { this.SetControlPropertyINC(SYMBOL_PROP_VOLUME_REAL,::fabs(value));                }
   void              SetControlVolumeRealDec(const double value)                 { this.SetControlPropertyDEC(SYMBOL_PROP_VOLUME_REAL,::fabs(value));                }
   void              SetControlVolumeRealLevel(const double value)               { this.SetControlPropertyLEVEL(SYMBOL_PROP_VOLUME_REAL,::fabs(value));              }
   double            GetValueChangedVolumeReal(void)                       const { return this.GetControlChangedValue(SYMBOL_PROP_VOLUME_REAL);                      }
   bool              IsIncreasedVolumeReal(void)                           const { return (bool)this.GetControlFlagINC(SYMBOL_PROP_VOLUME_REAL);                     }
   bool              IsDecreasedVolumeReal(void)                           const { return (bool)this.GetControlFlagDEC(SYMBOL_PROP_VOLUME_REAL);                     }
   //--- Максимальный реальный Volume за день
   //--- установка контролируемой величины (1) прироста, (2) уменьшения, (3) контрольного уровня максимального реального Volume за день
   //--- получение (4) величины изменения максимального реального Volume за день,
   //--- получение флага изменения максимального реального Volume за день больше, чем на величину (5) прироста, (6) уменьшения
   void              SetControlVolumeHighRealInc(const double value)             { this.SetControlPropertyINC(SYMBOL_PROP_VOLUMEHIGH_REAL,::fabs(value));            }
   void              SetControlVolumeHighRealDec(const double value)             { this.SetControlPropertyDEC(SYMBOL_PROP_VOLUMEHIGH_REAL,::fabs(value));            }
   void              SetControlVolumeHighRealLevel(const double value)           { this.SetControlPropertyLEVEL(SYMBOL_PROP_VOLUMEHIGH_REAL,::fabs(value));          }
   double            GetValueChangedVolumeHighReal(void)                   const { return this.GetControlChangedValue(SYMBOL_PROP_VOLUMEHIGH_REAL);                  }
   bool              IsIncreasedVolumeHighReal(void)                       const { return (bool)this.GetControlFlagINC(SYMBOL_PROP_VOLUMEHIGH_REAL);                 }
   bool              IsDecreasedVolumeHighReal(void)                       const { return (bool)this.GetControlFlagDEC(SYMBOL_PROP_VOLUMEHIGH_REAL);                 }
   //--- Минимальный реальный Volume за день
   //--- установка контролируемой величины (1) прироста, (2) уменьшения, (3) контрольного уровня минимального реального Volume за день
   //--- получение (4) величины изменения минимального реального Volume за день,
   //--- получение флага изменения минимального реального Volume за день больше, чем на величину (5) прироста, (6) уменьшения
   void              SetControlVolumeLowRealInc(const double value)              { this.SetControlPropertyINC(SYMBOL_PROP_VOLUMELOW_REAL,::fabs(value));             }
   void              SetControlVolumeLowRealDec(const double value)              { this.SetControlPropertyDEC(SYMBOL_PROP_VOLUMELOW_REAL,::fabs(value));             }
   void              SetControlVolumeLowRealLevel(const double value)            { this.SetControlPropertyLEVEL(SYMBOL_PROP_VOLUMELOW_REAL,::fabs(value));           }
   double            GetValueChangedVolumeLowReal(void)                    const { return this.GetControlChangedValue(SYMBOL_PROP_VOLUMELOW_REAL);                   }
   bool              IsIncreasedVolumeLowReal(void)                        const { return (bool)this.GetControlFlagINC(SYMBOL_PROP_VOLUMELOW_REAL);                  }
   bool              IsDecreasedVolumeLowReal(void)                        const { return (bool)this.GetControlFlagDEC(SYMBOL_PROP_VOLUMELOW_REAL);                  }
   //--- Цена исполнения опциона
   //--- установка контролируемой величины (1) прироста, (2) уменьшения, (3) контрольного уровня цены исполнения опциона
   //--- получение (4) величины изменения цены исполнения опциона,
   //--- получение флага изменения цены исполнения опциона больше, чем на величину (5) прироста, (6) уменьшения
   void              SetControlOptionStrikeInc(const double value)               { this.SetControlPropertyINC(SYMBOL_PROP_OPTION_STRIKE,::fabs(value));              }
   void              SetControlOptionStrikeDec(const double value)               { this.SetControlPropertyDEC(SYMBOL_PROP_OPTION_STRIKE,::fabs(value));              }
   void              SetControlOptionStrikeLevel(const double value)             { this.SetControlPropertyLEVEL(SYMBOL_PROP_OPTION_STRIKE,::fabs(value));            }
   double            GetValueChangedOptionStrike(void)                     const { return this.GetControlChangedValue(SYMBOL_PROP_OPTION_STRIKE);                    } 
   bool              IsIncreasedOptionStrike(void)                         const { return (bool)this.GetControlFlagINC(SYMBOL_PROP_OPTION_STRIKE);                   }
   bool              IsDecreasedOptionStrike(void)                         const { return (bool)this.GetControlFlagDEC(SYMBOL_PROP_OPTION_STRIKE);                   }
   //--- Максимально-допустимый совокупный объём позиций и ордеров в одном направлении
   //--- (1) Установка контрольного уровня
   //--- (2) получение величины изменения максимально-допустимого совокупного объёма позиций и ордеров в одном направлении,
   //--- получение флага (3) увеличения, (4) уменьшения максимально-допустимого совокупного объёма позиций и ордеров в одном направлении
   void              SetControlVolumeLimitLevel(const double value)              { this.SetControlPropertyLEVEL(SYMBOL_PROP_VOLUME_LIMIT,::fabs(value));             }
   double            GetValueChangedVolumeLimit(void)                      const { return this.GetControlChangedValue(SYMBOL_PROP_VOLUME_LIMIT);                     }
   bool              IsIncreasedVolumeLimit(void)                          const { return (bool)this.GetControlFlagINC(SYMBOL_PROP_VOLUME_LIMIT);                    }
   bool              IsDecreasedVolumeLimit(void)                          const { return (bool)this.GetControlFlagDEC(SYMBOL_PROP_VOLUME_LIMIT);                    }
   //---  Своп на покупку
   //--- (1) Установка контрольного уровня
   //--- (2) получение величины изменения свопа на покупку,
   //--- получение флага (3) увеличения, (4) уменьшения свопа на покупку
   void              SetControlSwapLongLevel(const double value)                 { this.SetControlPropertyLEVEL(SYMBOL_PROP_SWAP_LONG,::fabs(value));                }
   double            GetValueChangedSwapLong(void)                         const { return this.GetControlChangedValue(SYMBOL_PROP_SWAP_LONG);                        }
   bool              IsIncreasedSwapLong(void)                             const { return (bool)this.GetControlFlagINC(SYMBOL_PROP_SWAP_LONG);                       }
   bool              IsDecreasedSwapLong(void)                             const { return (bool)this.GetControlFlagDEC(SYMBOL_PROP_SWAP_LONG);                       }
   //---  Своп на продажу
   //--- (1) Установка контрольного уровня
   //--- (2) получение величины изменения свопа на продажу,
   //--- получение флага (3) увеличения, (4) уменьшения свопа на продажу
   void              SetControlSwapShortLevel(const double value)                { this.SetControlPropertyLEVEL(SYMBOL_PROP_SWAP_SHORT,::fabs(value));               }
   double            GetValueChangedSwapShort(void)                        const { return this.GetControlChangedValue(SYMBOL_PROP_SWAP_SHORT);                       }
   bool              IsIncreasedSwapShort(void)                            const { return (bool)this.GetControlFlagINC(SYMBOL_PROP_SWAP_SHORT);                      }
   bool              IsDecreasedSwapShort(void)                            const { return (bool)this.GetControlFlagDEC(SYMBOL_PROP_SWAP_SHORT);                      }
   //--- Cуммарный объём сделок в текущую сессию
   //--- установка контролируемой величины (1) прироста, (2) уменьшения, (3) контрольного уровня суммарного объёма сделок в текущую сессию
   //--- получение (4) величины изменения суммарного объёма сделок в текущую сессию,
   //--- получение флага изменения суммарного объёма сделок в текущую сессию больше, чем на величину (5) прироста, (6) уменьшения
   void              SetControlSessionVolumeInc(const double value)              { this.SetControlPropertyINC(SYMBOL_PROP_SESSION_VOLUME,::fabs(value));             }
   void              SetControlSessionVolumeDec(const double value)              { this.SetControlPropertyDEC(SYMBOL_PROP_SESSION_VOLUME,::fabs(value));             }
   void              SetControlSessionVolumeLevel(const double value)            { this.SetControlPropertyLEVEL(SYMBOL_PROP_SESSION_VOLUME,::fabs(value));           }
   double            GetValueChangedSessionVolume(void)                    const { return this.GetControlChangedValue(SYMBOL_PROP_SESSION_VOLUME);                   }
   bool              IsIncreasedSessionVolume(void)                        const { return (bool)this.GetControlFlagINC(SYMBOL_PROP_SESSION_VOLUME);                  }
   bool              IsDecreasedSessionVolume(void)                        const { return (bool)this.GetControlFlagDEC(SYMBOL_PROP_SESSION_VOLUME);                  }
   //--- Cуммарный оборот в текущую сессию
   //--- установка контролируемой величины (1) прироста, (2) уменьшения, (3) контрольного уровня суммарного оборота в текущую сессию
   //--- получение (4) величины изменения суммарного оборота в текущую сессию,
   //--- получение флага изменения суммарного оборота в текущую сессию больше, чем на величину (5) прироста, (6) уменьшения
   void              SetControlSessionTurnoverInc(const double value)            { this.SetControlPropertyINC(SYMBOL_PROP_SESSION_TURNOVER,::fabs(value));           }
   void              SetControlSessionTurnoverDec(const double value)            { this.SetControlPropertyDEC(SYMBOL_PROP_SESSION_TURNOVER,::fabs(value));           }
   void              SetControlSessionTurnoverLevel(const double value)          { this.SetControlPropertyLEVEL(SYMBOL_PROP_SESSION_TURNOVER,::fabs(value));         }
   double            GetValueChangedSessionTurnover(void)                  const { return this.GetControlChangedValue(SYMBOL_PROP_SESSION_TURNOVER);                 }
   bool              IsIncreasedSessionTurnover(void)                      const { return (bool)this.GetControlFlagINC(SYMBOL_PROP_SESSION_TURNOVER);                }
   bool              IsDecreasedSessionTurnover(void)                      const { return (bool)this.GetControlFlagDEC(SYMBOL_PROP_SESSION_TURNOVER);                }
   //--- Cуммарный объём открытых позиций
   //--- установка контролируемой величины (1) прироста, (2) уменьшения, (3) контрольного уровня суммарного объёма открытых позиций в текущую сессию
   //--- получение (4) величины изменения суммарного объёма открытых позиций в текущую сессию,
   //--- получение флага изменения суммарного объёма открытых позиций в текущую сессию больше, чем на величину (5) прироста, (6) уменьшения
   void              SetControlSessionInterestInc(const double value)            { this.SetControlPropertyINC(SYMBOL_PROP_SESSION_INTEREST,::fabs(value));           }
   void              SetControlSessionInterestDec(const double value)            { this.SetControlPropertyDEC(SYMBOL_PROP_SESSION_INTEREST,::fabs(value));           }
   void              SetControlSessionInterestLevel(const double value)          { this.SetControlPropertyLEVEL(SYMBOL_PROP_SESSION_INTEREST,::fabs(value));         }
   double            GetValueChangedSessionInterest(void)                  const { return this.GetControlChangedValue(SYMBOL_PROP_SESSION_INTEREST);                 }
   bool              IsIncreasedSessionInterest(void)                      const { return (bool)this.GetControlFlagINC(SYMBOL_PROP_SESSION_INTEREST);                }
   bool              IsDecreasedSessionInterest(void)                      const { return (bool)this.GetControlFlagDEC(SYMBOL_PROP_SESSION_INTEREST);                }
   //--- Общий объём ордеров на покупку в текущий момент
   //--- установка контролируемой величины (1) прироста, (2) уменьшения, (3) контрольного уровня общего объёма ордеров на покупку в текущий момент
   //--- получение (4) величины изменения общего объёма ордеров на покупку в текущий момент,
   //--- получение флага изменения общего объёма ордеров на покупку в текущий момент больше, чем на величину (5) прироста, (6) уменьшения
   void              SetControlSessionBuyOrdVolumeInc(const double value)        { this.SetControlPropertyINC(SYMBOL_PROP_SESSION_BUY_ORDERS_VOLUME,::fabs(value));  }
   void              SetControlSessionBuyOrdVolumeDec(const double value)        { this.SetControlPropertyDEC(SYMBOL_PROP_SESSION_BUY_ORDERS_VOLUME,::fabs(value));  }
   void              SetControlSessionBuyOrdVolumeLevel(const double value)      { this.SetControlPropertyLEVEL(SYMBOL_PROP_SESSION_BUY_ORDERS_VOLUME,::fabs(value));}
   double            GetValueChangedSessionBuyOrdVolume(void)              const { return this.GetControlChangedValue(SYMBOL_PROP_SESSION_BUY_ORDERS_VOLUME);        }
   bool              IsIncreasedSessionBuyOrdVolume(void)                  const { return (bool)this.GetControlFlagINC(SYMBOL_PROP_SESSION_BUY_ORDERS_VOLUME);       }
   bool              IsDecreasedSessionBuyOrdVolume(void)                  const { return (bool)this.GetControlFlagDEC(SYMBOL_PROP_SESSION_BUY_ORDERS_VOLUME);       }
   //--- Общий объём ордеров на продажу в текущий момент
   //--- установка контролируемой величины (1) прироста, (2) уменьшения, (3) контрольного уровня общего объёма ордеров на продажу в текущий момент
   //--- получение (4) величины изменения общего объёма ордеров на продажу в текущий момент,
   //--- получение флага изменения общего объёма ордеров на продажу в текущий момент больше, чем на величину (5) прироста, (6) уменьшения
   void              SetControlSessionSellOrdVolumeInc(const double value)       { this.SetControlPropertyINC(SYMBOL_PROP_SESSION_SELL_ORDERS_VOLUME,::fabs(value)); }
   void              SetControlSessionSellOrdVolumeDec(const double value)       { this.SetControlPropertyDEC(SYMBOL_PROP_SESSION_SELL_ORDERS_VOLUME,::fabs(value)); }
   void              SetControlSessionSellOrdVolumeLevel(const double value)     { this.SetControlPropertyLEVEL(SYMBOL_PROP_SESSION_SELL_ORDERS_VOLUME,::fabs(value));}
   double            GetValueChangedSessionSellOrdVolume(void)             const { return this.GetControlChangedValue(SYMBOL_PROP_SESSION_SELL_ORDERS_VOLUME);       }
   bool              IsIncreasedSessionSellOrdVolume(void)                 const { return (bool)this.GetControlFlagINC(SYMBOL_PROP_SESSION_SELL_ORDERS_VOLUME);      }
   bool              IsDecreasedSessionSellOrdVolume(void)                 const { return (bool)this.GetControlFlagDEC(SYMBOL_PROP_SESSION_SELL_ORDERS_VOLUME);      }
   //--- Цена открытия сессии
   //--- установка контролируемой величины (1) прироста, (2) уменьшения, (3) контрольного уровня цены открытия сессии
   //--- получение (4) величины изменения цены открытия сессии,
   //--- получение флага изменения цены открытия сессии больше, чем на величину (5) прироста, (6) уменьшения
   void              SetControlSessionPriceOpenInc(const double value)           { this.SetControlPropertyINC(SYMBOL_PROP_SESSION_OPEN,::fabs(value));               }
   void              SetControlSessionPriceOpenDec(const double value)           { this.SetControlPropertyDEC(SYMBOL_PROP_SESSION_OPEN,::fabs(value));               }
   void              SetControlSessionPriceOpenLevel(const double value)         { this.SetControlPropertyLEVEL(SYMBOL_PROP_SESSION_OPEN,::fabs(value));             }
   double            GetValueChangedSessionPriceOpen(void)                 const { return this.GetControlChangedValue(SYMBOL_PROP_SESSION_OPEN);                     }
   bool              IsIncreasedSessionPriceOpen(void)                     const { return (bool)this.GetControlFlagINC(SYMBOL_PROP_SESSION_OPEN);                    }
   bool              IsDecreasedSessionPriceOpen(void)                     const { return (bool)this.GetControlFlagDEC(SYMBOL_PROP_SESSION_OPEN);                    }
   //--- Цена закрытия сессии
   //--- установка контролируемой величины (1) прироста, (2) уменьшения, (3) контрольного уровня цены закрытия сессии
   //--- получение (4) величины изменения цены закрытия сессии,
   //--- получение флага изменения цены закрытия сессии больше, чем на величину (5) прироста, (6) уменьшения
   void              SetControlSessionPriceCloseInc(const double value)          { this.SetControlPropertyINC(SYMBOL_PROP_SESSION_CLOSE,::fabs(value));              }
   void              SetControlSessionPriceCloseDec(const double value)          { this.SetControlPropertyDEC(SYMBOL_PROP_SESSION_CLOSE,::fabs(value));              }
   void              SetControlSessionPriceCloseLevel(const double value)        { this.SetControlPropertyLEVEL(SYMBOL_PROP_SESSION_CLOSE,::fabs(value));            }
   double            GetValueChangedSessionPriceClose(void)                const { return this.GetControlChangedValue(SYMBOL_PROP_SESSION_CLOSE);                    }
   bool              IsIncreasedSessionPriceClose(void)                    const { return (bool)this.GetControlFlagINC(SYMBOL_PROP_SESSION_CLOSE);                   }
   bool              IsDecreasedSessionPriceClose(void)                    const { return (bool)this.GetControlFlagDEC(SYMBOL_PROP_SESSION_CLOSE);                   }
   //--- Средневзвешенная цена сессии
   //--- установка контролируемой величины (1) прироста, (2) уменьшения, (3) контрольного уровня средневзвешенной цены сессии
   //--- получение (4) величины изменения средневзвешенной цены сессии,
   //--- получение флага изменения средневзвешенной цены сессии больше, чем на величину (5) прироста, (6) уменьшения
   void              SetControlSessionPriceAWInc(const double value)             { this.SetControlPropertyINC(SYMBOL_PROP_SESSION_AW,::fabs(value));                 }
   void              SetControlSessionPriceAWDec(const double value)             { this.SetControlPropertyDEC(SYMBOL_PROP_SESSION_AW,::fabs(value));                 }
   void              SetControlSessionPriceAWLevel(const double value)           { this.SetControlPropertyLEVEL(SYMBOL_PROP_SESSION_AW,::fabs(value));               }
   double            GetValueChangedSessionPriceAW(void)                   const { return this.GetControlChangedValue(SYMBOL_PROP_SESSION_AW);                       }
   bool              IsIncreasedSessionPriceAW(void)                       const { return (bool)this.GetControlFlagINC(SYMBOL_PROP_SESSION_AW);                      }
   bool              IsDecreasedSessionPriceAW(void)                       const { return (bool)this.GetControlFlagDEC(SYMBOL_PROP_SESSION_AW);                      }
//---

Рассмотрим реализацию объявленных методов и изменения в уже существующих.

В конструктор класса внесём небольшие изменения:

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

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

Также был изменён метод Refresh() объекта-символа:

//+------------------------------------------------------------------+
//| Обновляет все данные символа                                     |
//+------------------------------------------------------------------+
void CSymbol::Refresh(void)
  {
//--- Обновление котировочных данных
   if(!this.RefreshRates())
      return;
#ifdef __MQL5__
   ::ResetLastError();
   if(!this.MarginRates())
     {
      this.m_global_error=::GetLastError();
      return;
     }
#endif 
//--- Инициализация событийных данных
   this.m_is_event=false;

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

Здесь: Так как мы избавились от необходимости создания структур для хранения текущего и прошлого состояний свойств символа, то здесь было удалено заполнение структуры данных текущего состояния символа, а вместо этого мы организовали заполнение массивов целочисленных и вещественных свойств в базовом объекте.
Затем, когда массивы заполнены, нам необходимо вызвать метод Refresh() базового объекта CBaseObj, в котором проводится поиск произошедших изменений и создаётся список базовых событий объекта-наследника.
После того, как список базовых событий в родительском классе создан (при наличии критериев для генерации событий), мы проверяем базовые события методом CheckEvents(), и при их наличии — создаём список событий символа.

Реализация метода проверки событий:

//+------------------------------------------------------------------+
//| Проверяет список изменений свойств символа и создаёт событие     |
//+------------------------------------------------------------------+
void CSymbol::CheckEvents(void)
  {
   int total=this.m_list_events_base.Total();
   if(total==0)
      return;
  for(int i=0;i<total;i++)
     {
      CBaseEvent *event=this.GetEventBase(i);
      if(event==NULL)
         continue;
      long lvalue=0;
      this.UshortToLong(this.MSCfromTime(this.TickTime()),0,lvalue);
      this.UshortToLong(event.Reason(),1,lvalue);
      this.UshortToLong(COLLECTION_SYMBOLS_ID,2,lvalue);
      if(this.EventAdd((ushort)event.ID(),lvalue,event.Value(),this.Name()))
         this.m_is_event=true;
     }
  }  
//+------------------------------------------------------------------+

Здесь: Если список базовых событий пуст — выходим.
В цикле по списку базовых событий получаем очередное событие, и если событие получено — то создаём событие символа:

  • получаем только милисекунды из текущего времени в милисекундах и добавляем их в первые два байта long-параметра события
  • получаем причину события (увеличение/уменшение/больше/меньше уровня) и добавляем её во вторые два байта long-параметра события
  • идентификатор коллекции символов добавляем в третьи два байта long-параметра события
  • добавляем событие символа в список событий символа и ставим флаг наличия события на символе

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

//+------------------------------------------------------------------+
//| Инициализирует переменные контролируемых данных символа          |
//+------------------------------------------------------------------+
void CSymbol::InitControlsParams(void)
  {
   this.ResetControlsParams();
  }
//+------------------------------------------------------------------+

Просто вызывает метод сброса переменных контролируемых значений данных объекта, рассмотренный нами выше.

Методы установки контролируемых величин и флагов произошедших изменений и методы получения размера произошедших изменений и флагов:

//+------------------------------------------------------------------+
//| Устанавливает величину контролируемого приращения свойства       |
//+------------------------------------------------------------------+
template<typename T> void CSymbol::SetControlPropertyINC(const int property,const T value)
  {
   if(property<SYMBOL_PROP_INTEGER_TOTAL)
      this.SetControlledValueINC(property,(long)value);
   else
      this.SetControlledValueINC(property,(double)value);
  }  
//+------------------------------------------------------------------+
//| Устанавливает величину контролируемого уменьшения свойства       |
//+------------------------------------------------------------------+
template<typename T> void CSymbol::SetControlPropertyDEC(const int property,const T value)
  {
   if(property<SYMBOL_PROP_INTEGER_TOTAL)
      this.SetControlledValueDEC(property,(long)value);
   else
      this.SetControlledValueDEC(property,(double)value);
  }
//+------------------------------------------------------------------+
//| Устанавливает величину контрольного уровня свойства              |
//+------------------------------------------------------------------+
template<typename T> void CSymbol::SetControlPropertyLEVEL(const int property,const T value)
  {
   if(property<SYMBOL_PROP_INTEGER_TOTAL)
      this.SetControlledValueLEVEL(property,(long)value);
   else
      this.SetControlledValueLEVEL(property,(double)value);
  }
//+------------------------------------------------------------------+
//| Устанавливает Флаг изменения значения свойства символа больше,   |
//| чем на величину прироста                                         |
//+------------------------------------------------------------------+
template<typename T> void CSymbol::SetControlFlagINC(const int property,const T value)
  {
   if(property<SYMBOL_PROP_INTEGER_TOTAL)
      this.SetControlledFlagINC(property,(long)value);
   else
      this.SetControlledFlagINC(property,(double)value);
  }  
//+------------------------------------------------------------------+
//| Устанавливает Флаг изменения значения свойства символа больше,   |
//| чем на величину уменьшения                                       |
//+------------------------------------------------------------------+
template<typename T> void CSymbol::SetControlFlagDEC(const int property,const T value)
  {
   if(property<SYMBOL_PROP_INTEGER_TOTAL)
      this.SetControlledFlagDEC(property,(long)value);
   else
      this.SetControlledFlagDEC(property,(double)value);
  }
//+------------------------------------------------------------------+
//| Устанавливает величину изменения контролируемого свойства символа|
//+------------------------------------------------------------------+
template<typename T> void CSymbol::SetControlChangedValue(const int property,const T value)
  {
   if(property<SYMBOL_PROP_INTEGER_TOTAL)
      this.SetControlledChangedValue(property,(long)value);
   else
      this.SetControlledChangedValue(property,(double)value);
  }
//+------------------------------------------------------------------+
//| Устанавливает контролируемую величину прироста цены Bid или Last |
//+------------------------------------------------------------------+
void CSymbol::SetControlBidLastInc(const double value)
  {
   this.SetControlPropertyINC((this.ChartMode()==SYMBOL_CHART_MODE_BID ? SYMBOL_PROP_BID : SYMBOL_PROP_LAST),::fabs(value));
  }
//+------------------------------------------------------------------+
//|Устанавливает контролируемую величину уменьшения цены Bid или Last|
//+------------------------------------------------------------------+
void CSymbol::SetControlBidLastDec(const double value)
  {
   this.SetControlPropertyDEC((this.ChartMode()==SYMBOL_CHART_MODE_BID ? SYMBOL_PROP_BID : SYMBOL_PROP_LAST),::fabs(value));
  }
//+------------------------------------------------------------------+
//| Устанавливает контрольный уровень цены Bid или Last              |
//+------------------------------------------------------------------+
void CSymbol::SetControlBidLastLevel(const double value)
  {
   this.SetControlPropertyLEVEL((this.ChartMode()==SYMBOL_CHART_MODE_BID ? SYMBOL_PROP_BID : SYMBOL_PROP_LAST),::fabs(value));
  }
//+------------------------------------------------------------------+
//| Возвращает величину изменения цены Bid или Last                  |
//+------------------------------------------------------------------+
double CSymbol::GetValueChangedBidLast(void) const
  {
   return(this.ChartMode()==SYMBOL_CHART_MODE_BID ? this.GetControlChangedValue(SYMBOL_PROP_BID) : this.GetControlChangedValue(SYMBOL_PROP_LAST));
  }
//+------------------------------------------------------------------+
//| Возвращает флаг изменения цены Bid или Last больше,              |
//| чем на величину прироста                                         |
//+------------------------------------------------------------------+
bool CSymbol::IsIncreasedBidLast(void) const
  {
   return(this.ChartMode()==SYMBOL_CHART_MODE_BID ? (bool)this.GetControlFlagINC(SYMBOL_PROP_BID) : (bool)this.GetControlFlagINC(SYMBOL_PROP_LAST));
  }
//+------------------------------------------------------------------+
//| Возвращает флаг изменения цены Bid или Last больше,              |
//| чем на величину уменьшения                                       |
//+------------------------------------------------------------------+
bool CSymbol::IsDecreasedBidLast(void) const
  {
   return(this.ChartMode()==SYMBOL_CHART_MODE_BID ? (bool)this.GetControlFlagDEC(SYMBOL_PROP_BID) : (bool)this.GetControlFlagDEC(SYMBOL_PROP_LAST));
  }
//+------------------------------------------------------------------+
//| Устанавливает контролируемую величину прироста                   |
//| максимальной цены Bid или Last                                   |
//+------------------------------------------------------------------+
void CSymbol::SetControlBidLastHighInc(const double value)
  {
   this.SetControlPropertyINC((this.ChartMode()==SYMBOL_CHART_MODE_BID ? SYMBOL_PROP_BIDHIGH : SYMBOL_PROP_LASTHIGH),::fabs(value));
  }
//+------------------------------------------------------------------+
//| Устанавливает контролируемую величину уменьшения                 |
//| максимальной цены Bid или Last                                   |
//+------------------------------------------------------------------+
void CSymbol::SetControlBidLastHighDec(const double value)
  {
   this.SetControlPropertyDEC((this.ChartMode()==SYMBOL_CHART_MODE_BID ? SYMBOL_PROP_BIDHIGH : SYMBOL_PROP_LASTHIGH),::fabs(value));
  }
//+------------------------------------------------------------------+
//| Устанавливает контрольный уровень максимальной цены Bid или Last |
//+------------------------------------------------------------------+
void CSymbol::SetControlBidLastHighLevel(const double value)
  {
   this.SetControlPropertyLEVEL((this.ChartMode()==SYMBOL_CHART_MODE_BID ? SYMBOL_PROP_BIDHIGH : SYMBOL_PROP_LASTHIGH),::fabs(value));
  }
//+------------------------------------------------------------------+
//| Возвращает величину изменения максимальной цены Bid или Last     |
//+------------------------------------------------------------------+
double CSymbol::GetValueChangedBidLastHigh(void) const
  {
   return(this.ChartMode()==SYMBOL_CHART_MODE_BID ? this.GetControlChangedValue(SYMBOL_PROP_BIDHIGH) : this.GetControlChangedValue(SYMBOL_PROP_LASTHIGH));
  }
//+------------------------------------------------------------------+
//| Возвращает флаг изменения максимальной цены                      |
//| Bid или Last больше, чем на величину прироста                    |
//+------------------------------------------------------------------+
bool CSymbol::IsIncreasedBidLastHigh(void) const
  {
   return(this.ChartMode()==SYMBOL_CHART_MODE_BID ? (bool)this.GetControlFlagINC(SYMBOL_PROP_BIDHIGH) : (bool)this.GetControlFlagINC(SYMBOL_PROP_LASTHIGH));
  }
//+------------------------------------------------------------------+
//| Возвращает флаг изменения максимальной цены                      |
//| Bid или Last больше, чем на величину уменьшения                  |
//+------------------------------------------------------------------+
bool CSymbol::IsDecreasedBidLastHigh(void) const
  {
   return(this.ChartMode()==SYMBOL_CHART_MODE_BID ? (bool)this.GetControlFlagDEC(SYMBOL_PROP_BIDHIGH) : (bool)this.GetControlFlagDEC(SYMBOL_PROP_LASTHIGH));
  }
//+------------------------------------------------------------------+
//| Устанавливает контролируемую величину прироста                   |
//| минимальной цены Bid или Last                                    |
//+------------------------------------------------------------------+
void CSymbol::SetControlBidLastLowInc(const double value)
  {
   this.SetControlPropertyINC((this.ChartMode()==SYMBOL_CHART_MODE_BID ? SYMBOL_PROP_BIDLOW : SYMBOL_PROP_LASTLOW),::fabs(value));
  }
//+------------------------------------------------------------------+
//| Устанавливает контролируемую величину уменьшения                 |
//| минимальной цены Bid или Last                                    |
//+------------------------------------------------------------------+
void CSymbol::SetControlBidLastLowDec(const double value)
  {
   this.SetControlPropertyDEC((this.ChartMode()==SYMBOL_CHART_MODE_BID ? SYMBOL_PROP_BIDLOW : SYMBOL_PROP_LASTLOW),::fabs(value));
  }
//+------------------------------------------------------------------+
//| Устанавливает контрольный уровень минимальной цены Bid или Last  |
//+------------------------------------------------------------------+
void CSymbol::SetControlBidLastLowLevev(const double value)
  {
   this.SetControlPropertyLEVEL((this.ChartMode()==SYMBOL_CHART_MODE_BID ? SYMBOL_PROP_BIDLOW : SYMBOL_PROP_LASTLOW),::fabs(value));
  }
//+------------------------------------------------------------------+
//| Возвращает величину изменения минимальной цены Bid или Last      |
//+------------------------------------------------------------------+
double CSymbol::GetValueChangedBidLastLow(void) const
  {
   return(this.ChartMode()==SYMBOL_CHART_MODE_BID ? this.GetControlChangedValue(SYMBOL_PROP_BIDLOW) : this.GetControlChangedValue(SYMBOL_PROP_LASTLOW));
  }
//+------------------------------------------------------------------+
//| Возвращает флаг изменения минимальной цены                       |
//| Bid или Last больше, чем на величину прироста                    |
//+------------------------------------------------------------------+
bool CSymbol::IsIncreasedBidLastLow(void) const
  {
   return(this.ChartMode()==SYMBOL_CHART_MODE_BID ? (bool)this.GetControlFlagINC(SYMBOL_PROP_BIDLOW) : (bool)this.GetControlFlagINC(SYMBOL_PROP_LASTLOW));
  }
//+------------------------------------------------------------------+
//| Возвращает флаг изменения минимальной цены                       |
//| Bid или Last больше, чем на величину уменьшения                  |
//+------------------------------------------------------------------+
bool CSymbol::IsDecreasedBidLastLow(void) const
  {
   return(this.ChartMode()==SYMBOL_CHART_MODE_BID ? (bool)this.GetControlFlagDEC(SYMBOL_PROP_BIDLOW) : (bool)this.GetControlFlagDEC(SYMBOL_PROP_LASTLOW));
  }
//+------------------------------------------------------------------+

Похожие методы рассматривались нами при доработке класса базового объекта, и здесь вызываются как раз рассмотренные нами эти методы в зависимости от требуемого свойства объекта-символа.

На этом доработка класса объекта-символа завершена.

Теперь осталось немного доработать класс коллекции символов.
Откроем файл \MQL5\Include\DoEasy\Collections\SymbolsCollection.mqh и внесём необходимые изменения.

Так как мы избавились от необходимости создавать для каждого объекта свои перечисления его событий, то
переменной "последнее событие символа" и методу GetLastEvent() вместо бывшего ранее типа ENUM_SYMBOL_EVENT задатим типы int:

int m_last_event; // Последнее событие
int GetLastEvent(void) const { return this.m_last_event; }

Так как теперь все события символа (да и любого иного объекта-наследника) обрабатываются в классе базового объекта, то
метод EventDescription() переименуем в EventMWDescription(), и передавать в метод будем переменную с типом перечисления событий окна "Обзор рынка":

//--- Возвращает описание (1) события окна "Обзор рынка", (2) режима работы с символами
   string            EventMWDescription(const ENUM_MW_EVENT event);
   string            ModeSymbolsListDescription(void);

В связи с тем, что наименование перечислений у нас поменялись, то
метод работы с окном "Обзор рынка" претерпел небольшие изменения ( изменены наименования перечислений и тип переменной-события):

//+------------------------------------------------------------------+
//| Работа с событиями окна обзора рынка                             |
//+------------------------------------------------------------------+
void CSymbolsCollection::MarketWatchEventsControl(const bool send_events=true)
  {
   ::ResetLastError();
//--- Если текущие цены не получены - уходим
   if(!::SymbolInfoTick(::Symbol(),this.m_tick))
     {
      this.m_global_error=::GetLastError();
      return;
     }
   uchar array[];
   int sum=0;
   this.m_hash_sum=0;
//--- Расчёт хэш-суммы всех видимых символов в окне "Обзор рынка"
   this.m_total_symbols=this.SymbolsTotalVisible();
   //--- В цикле по всем символам окна "Обзор рынка"
   int total_symbols=::SymbolsTotal(true);
   for(int i=0;i<total_symbols;i++)
     {
      //--- получаем имя символа по индексу
      string name=::SymbolName(i,true);
      //--- если невидимый - пропускаем
      if(!::SymbolInfoInteger(name,SYMBOL_VISIBLE))
         continue;
      //--- записываем в uchar-массив коды символов (букв) названия символа
      ::StringToCharArray(name,array);
      //--- в цикле по получившемуся массиву складываем значения всех ячеек массива, тем самым создавая код символа
      for(int j=::ArraySize(array)-1;j>WRONG_VALUE;j--)
         sum+=array[j];
      //--- добавляем к хэш-сумме код символа и индекс цикла, указывающий на индекс символа в списке обзора рынка
      m_hash_sum+=i+sum;
     }
//--- Если запрет отправки событий - создаём список-коллекцию и выходим с сохранением текущей хэш-суммы как прошлой
   if(!send_events)
     {
      //--- Очищаем список
      this.m_list_all_symbols.Clear();
      //--- Создаём список-коллекцию
      this.CreateSymbolsList(true);
      //--- Создаём слепок окна обзора рынка
      this.CopySymbolsNames();
      //--- сохраняем текущую хэш-сумму как прошлую
      this.m_hash_sum_prev=this.m_hash_sum;
      //--- сохраняем текущее количество видимых символов как прошлое
      this.m_total_symbol_prev=this.m_total_symbols;
      return;
     }
   
//--- Если хэш-сумма символов в окне "Обзор рынка" изменилась
   if(this.m_hash_sum!=this.m_hash_sum_prev)
     {
      //--- Определяем событие окна "Обзор рынка"
      this.m_delta_symbol=this.m_total_symbols-this.m_total_symbol_prev;
      ushort event_id=
        (ushort(
         this.m_total_symbols>this.m_total_symbol_prev ? MARKET_WATCH_EVENT_SYMBOL_ADD :
         this.m_total_symbols<this.m_total_symbol_prev ? MARKET_WATCH_EVENT_SYMBOL_DEL :
         MARKET_WATCH_EVENT_SYMBOL_SORT)
        );
      //--- Добавление символа в окно "Обзор рынка"
      if(event_id==MARKET_WATCH_EVENT_SYMBOL_ADD)
        {
         string name="";
         //--- В цикле по всем символам окна "Обзор рынка"
         int total=::SymbolsTotal(true), index=WRONG_VALUE;
         for(int i=0;i<total;i++)
           {
            //--- получаем имя символа и проверяем его "видимость", если невидимый - пропускаем
            name=::SymbolName(i,true);
            if(!::SymbolInfoInteger(name,SYMBOL_VISIBLE))
               continue;
            //--- Если символа ещё нет в списке символов коллекции
            if(!this.IsPresentSymbolInList(name))
              {
               //--- очищаем список-коллекцию
               this.m_list_all_symbols.Clear();
               //--- пересоздаём список-коллекцию
               this.CreateSymbolsList(true);
               //--- создаём слепок коллекции символов
               this.CopySymbolsNames();
               //--- получаем индекс нового символа в окне "Обзор рынка"
               index=this.GetSymbolIndexByName(name);
               //--- Если событие "Добавление нового символа" успешно добавлено в список событий 
               if(this.EventAdd(event_id,this.TickTime(),index,name))
                 {
                  //--- отправляем событие на график:
                  //--- long-значение = время события в милисекундах, double-значение = индекс символа, string-значение = имя добавленного символа
                  ::EventChartCustom(this.m_chart_id,(ushort)event_id,this.TickTime(),index,name);
                 }
              }
           }
         //--- Сохраняем новое количество видимых символов в окне обзора рынка
         this.m_total_symbols=this.SymbolsTotalVisible();
        }
      //--- Удаление символа из окна "Обзор рынка"
      else if(event_id==MARKET_WATCH_EVENT_SYMBOL_DEL)
        {
         //--- очищаем список-коллекцию 
         this.m_list_all_symbols.Clear();
         //--- пересоздаём список-коллекцию
         this.CreateSymbolsList(true);
         //--- В цикле по слепку окна обзора рынка
         int total=this.m_list_names.Total();
         for(int i=0; i<total;i++)
           {
            //--- получаем имя символа 
            string name=this.m_list_names.At(i);
            if(name==NULL)
               continue;
            //--- если симвоа с таким именем не существует в списке символов коллекции
            if(!this.IsPresentSymbolInList(name))
              {
               //--- Если событие "Удаление символа" успешно добавлено в список событий
               if(this.EventAdd(event_id,this.TickTime(),WRONG_VALUE,name))
                 {
                  //--- отправляем событие на график:
                  //--- long-значение = время события в милисекундах, double-значение = -1 для отсутствующего символа, string-значение = имя удалённого символа
                  ::EventChartCustom(this.m_chart_id,(ushort)event_id,this.TickTime(),WRONG_VALUE,name);
                 }
              }
           }
         //--- Пересоздаём слепок обзора рынка
         this.CopySymbolsNames();
         //--- Сохраняем новое количество видимых символов в окне обзора рынка
         this.m_total_symbols=this.SymbolsTotalVisible();
        }
      //--- Сортировка символов в окне "Обзор рынка"
      else if(event_id==MARKET_WATCH_EVENT_SYMBOL_SORT)
        {
         //--- очищаем список-коллекцию 
         this.m_list_all_symbols.Clear();
         //--- устанавливаем сортировку списка коллекции как сортировку по индексу
         this.m_list_all_symbols.Sort(SORT_BY_SYMBOL_INDEX_MW);
         //--- пересоздаём список-коллекцию
         this.CreateSymbolsList(true);
         //--- получаем индекс текущего символа в окне "Обзор рынка"
         int index=this.GetSymbolIndexByName(Symbol());
         //--- отправляем событие на график:
         //--- long-значение = время события в милисекундах, double-значение = индекс текущего символа, string-значение = имя текущего символа
         ::EventChartCustom(this.m_chart_id,(ushort)event_id,this.TickTime(),index,::Symbol());
        }
      //--- сохраняем текущее количество видимых символов как прошлое
      this.m_total_symbol_prev=this.m_total_symbols;
      //--- сохраняем текущую хэш-сумму как прошлую
      this.m_hash_sum_prev=this.m_hash_sum;
     }
  }
//+------------------------------------------------------------------+

И точно так же изменён тип переменной-события в методе работы со списком событий коллекции символов:

//+------------------------------------------------------------------+
//| Работа с событиями списка символов коллекции                     |
//+------------------------------------------------------------------+
void CSymbolsCollection::SymbolsEventsControl(void)
  {
   this.m_is_event=false;
   this.m_list_events.Clear();
   this.m_list_events.Sort();
   //--- Полное обновление всех символов коллекции
   int total=this.m_list_all_symbols.Total();
   for(int i=0;i<total;i++)
     {
      CSymbol *symbol=this.m_list_all_symbols.At(i);
      if(symbol==NULL)
         continue;
      symbol.Refresh();
      if(!symbol.IsEvent())
         continue;
      this.m_is_event=true;
      CArrayObj *list=symbol.GetListEvents();
      if(list==NULL)
         continue;
      this.m_event_code=symbol.GetEventCode();
      int n=list.Total();
      for(int j=0; j<n; j++)
        {
         CEventBaseObj *event=list.At(j);
         if(event==NULL)
            continue;
         ushort event_id=event.ID();
         this.m_last_event=event_id;
         if(this.EventAdd((ushort)event.ID(),event.LParam(),event.DParam(),event.SParam()))
           {
            ::EventChartCustom(this.m_chart_id,(ushort)event_id,event.LParam(),event.DParam(),event.SParam());
           }
        }
     }
  }
//+------------------------------------------------------------------+

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

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

Теперь доработаем класс CEngine. Откроем файл\MQL5\Include\DoEasy\Engine.mqh и внесём в него изменения:

Переменная, хранящая последнее событие в свойствах символа и метод, возвращающий значение этой переменной тоже будут иметь тип int:

   int m_last_symbol_event;  // Последнее событие в свойствах символа
   int LastSymbolsEvent(void) const { return this.m_last_symbol_event; }

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

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

И там же напишем три метода, сразу же возвращающих значения милисекунд, причину и источник события из long-параметра события:

//--- Возвращает (1) милисекунды, (2) причину, (3) источник события из его long-значения
   ushort               EventMSC(const long lparam)               const { return this.LongToUshortFromByte(lparam,0);         }
   ushort               EventReason(const long lparam)            const { return this.LongToUshortFromByte(lparam,1);         }
   ushort               EventSource(const long lparam)            const { return this.LongToUshortFromByte(lparam,2);         }

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

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

Реализация метода, извлекающий ushort-число из long-контейнера по индексу байтов его расположения в составе long-контейнера:

//+------------------------------------------------------------------+
//| Извлекает нужное ushort-число из упакованного long-значения      |
//+------------------------------------------------------------------+
ushort CEngine::LongToUshortFromByte(const long source_value,const uchar index) const
  {
   if(index>3)
     {
      ::Print(DFUN,TextByLanguage("Ошибка. Значение \"index\" должно быть в пределах 0 - 3","Error. The \"index\" value must be between 0 - 3"));
      return 0;
     }
   long res=source_value>>(16*index);
   return ushort(res &=0xFFFF);
  }
//+------------------------------------------------------------------+

В метод передаётся long-значение, из которого необходимо извлечь ushort-число и индекс байт, в которых число располагается (таблицу расположения ushort-чисел в long-контейнере рассматривали выше), затем проверяется корректность указания индекса и выводится сообщение об ошибке если индекс не корректен, и возвращается 0.
Далее смещаем биты long-числа на 16 * index бит вправо, накладываем маску для "погашения" оставшихся старших бит и возвращаем извлечённое таким образом ushort-число.

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

Откроем файл \MQL5\Include\DoEasy\ToMQL4.mqh и впишем неизвестный для компилятора MQL4 код ошибки:

//+------------------------------------------------------------------+
//|                                                       ToMQL4.mqh |
//|              Copyright 2017, Artem A. Trishkin, Skype artmedia70 |
//|                         https://www.mql5.com/ru/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2017, Artem A. Trishkin, Skype artmedia70"
#property link      "https://www.mql5.com/ru/users/artmedia70"
#property strict
#ifdef __MQL4__
//+------------------------------------------------------------------+
//| Коды ошибок                                                      |
//+------------------------------------------------------------------+
#define ERR_SUCCESS                       (ERR_NO_ERROR)
#define ERR_MARKET_UNKNOWN_SYMBOL         (ERR_UNKNOWN_SYMBOL)
#define ERR_ZEROSIZE_ARRAY                (ERR_ARRAY_INVALID)
//+------------------------------------------------------------------+

Это все изменения, которые нам необходимо было сделать, чтобы запустить символы в работу с новым событийным функционалом, предоставляемым объектом CBaseObj всем своим наследникам.


Тест событийного функционала базового объекта всех объектов библиотеки

Для тестирования нового событийного функционала базового объекта возьмём советник из прошлой статьи и сохраним его под новым именем TestDoEasyPart17.mq5 в папке \MQL5\Experts\TestDoEasy\Part17.

Будем тестировать изменение спреда текущего символа на 4 пункта (увеличение и уменьшение), а также контролировать размер спреда на 15 пунктов. Для цены Bid будем контролировать увеличение/уменьшение её значения на +/- 10 пунктов, и отследим пересечение ценой уровня 1.13700.

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

//--- Установка типа используемого списка символов в коллекции символов
   engine.SetUsedSymbols(array_used_symbols);
//--- Отображение выбранного режима работы с коллекцией объектов-символов
   Print(engine.ModeSymbolsListDescription(),TextByLanguage(". Количество используемых символов: ",". The number of symbols used: "),engine.GetSymbolsCollectionTotal());

//--- Установка контрольных значений для текущего символа
   CSymbol* symbol=engine.GetSymbolCurrent();
   if(symbol!=NULL)
     {
      //--- Установка контроля увеличения цены текущего символа на 10 пунктов
      symbol.SetControlBidInc(10*Point());
      //--- Установка контроля уменьшения цены текущего символа на 10 пунктов
      symbol.SetControlBidDec(10*Point());
      //--- Установка контроля увеличения спреда текущего символа на 4 пункт
      symbol.SetControlSpreadInc(4);
      //--- Установка контроля уменьшения спреда текущего символа на 4 пункта
      symbol.SetControlSpreadDec(4);
      //--- Установка контроля размера спреда по значению 15 пунктов
      symbol.SetControlSpreadLevel(15);
      //--- Установка контроля пересечения ценой значения 1.13700
      symbol.SetControlBidLevel(1.13700);
     }

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

//--- Создание панели кнопок
   if(!CreateButtons(InpButtShiftX,InpButtShiftY))
      return INIT_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);
  }
//+------------------------------------------------------------------+

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

Из обработчика OnTick() советника удалим переменную, хранящую последнее событие символа — для отслеживания событий символов у нас имеются иные инструменты, нежели простое сравнение текущего и прошлого состояния.

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//--- Инициализация последних событий
   static ENUM_TRADE_EVENT last_trade_event=WRONG_VALUE;
   static ENUM_ACCOUNT_EVENT last_account_event=WRONG_VALUE;
   static ENUM_SYMBOL_EVENT last_symbol_event=WRONG_VALUE;
//--- Если работа в тестере
   if(MQLInfoInteger(MQL_TESTER))
     {

Изменим обработчик событий библиотеки в части обработки событий коллекции символов:

//+------------------------------------------------------------------+
//| Обработка событий библиотеки DoEasy                              |
//+------------------------------------------------------------------+
void OnDoEasyEvent(const int id,
                   const long &lparam,
                   const double &dparam,
                   const string &sparam)
  {
   int idx=id-CHARTEVENT_CUSTOM;
   string event="::"+string(idx);
   
//--- Извлекаем из lparam (1) милисекунды времени события, (2) причину, (3) источник события и (4) устанавливаем точное время события
   ushort msc=engine.EventMSC(lparam);
   ushort reason=engine.EventReason(lparam);
   ushort source=engine.EventSource(lparam);
   long time=TimeCurrent()*1000+msc;
      
//--- Обработка событий окна обзор рынка
   if(idx>MARKET_WATCH_EVENT_NO_EVENT && idx<SYMBOL_EVENTS_NEXT_CODE)
     {
      string name="";
      //--- Событие окна "Обзор рынка"
      string descr=engine.GetMWEventDescription((ENUM_MW_EVENT)idx);
      name=(idx==MARKET_WATCH_EVENT_SYMBOL_SORT ? "" : ": "+sparam);
      Print(TimeMSCtoString(lparam)," ",descr,name);
     }
//--- Обработка событий символов
   if(source==COLLECTION_SYMBOLS_ID)
     {
      CSymbol *symbol=engine.GetSymbolObjByName(sparam);
      if(symbol==NULL)
         return;
      //--- Количество знаков после запятой в значении события - если long-событие, то 0, иначе - Digits() символа
      int digits=(idx<SYMBOL_PROP_INTEGER_TOTAL ? 0 : symbol.Digits());
      //--- Текстовое описание события
      string id_descr=(idx<SYMBOL_PROP_INTEGER_TOTAL ? symbol.GetPropertyDescription((ENUM_SYMBOL_PROP_INTEGER)idx) : symbol.GetPropertyDescription((ENUM_SYMBOL_PROP_DOUBLE)idx));
      //--- Текстовое значение величины изменения свойства
      string value=DoubleToString(dparam,digits);
      //--- Проверка причин события и просто вывод в журнал его описания
      if(reason==BASE_EVENT_REASON_INC)
        {
         Print(symbol.EventDescription(idx,(ENUM_BASE_EVENT_REASON)reason,source,value,id_descr,digits));
        }
      if(reason==BASE_EVENT_REASON_DEC)
        {
         Print(symbol.EventDescription(idx,(ENUM_BASE_EVENT_REASON)reason,source,value,id_descr,digits));
        }
      if(reason==BASE_EVENT_REASON_MORE_THEN)
        {
         Print(symbol.EventDescription(idx,(ENUM_BASE_EVENT_REASON)reason,source,value,id_descr,digits));
        }
      if(reason==BASE_EVENT_REASON_LESS_THEN)
        {
         Print(symbol.EventDescription(idx,(ENUM_BASE_EVENT_REASON)reason,source,value,id_descr,digits));
        }
      if(reason==BASE_EVENT_REASON_EQUALS)
        {
         Print(symbol.EventDescription(idx,(ENUM_BASE_EVENT_REASON)reason,source,value,id_descr,digits));
        }
     }
      
//--- Обработка торговых событий
   if(idx>TRADE_EVENT_NO_EVENT && idx<TRADE_EVENTS_NEXT_CODE)
     {
      event=EnumToString((ENUM_TRADE_EVENT)ushort(idx));
      int digits=(int)SymbolInfoInteger(sparam,SYMBOL_DIGITS);
     }
//--- Обработка событий аккаунта
   else if(idx>ACCOUNT_EVENT_NO_EVENT && idx<ACCOUNT_EVENTS_NEXT_CODE)
     {
      Print(TimeMSCtoString(lparam)," ",sparam,": ",engine.GetAccountEventDescription((ENUM_ACCOUNT_EVENT)idx));
      
      //--- если это увеличение средств
      if((ENUM_ACCOUNT_EVENT)idx==ACCOUNT_EVENT_EQUITY_INC)
        {
         //--- Закроем позицию с самой большой прибылью больше нуля при увеличении средств больше,
         //--- чем задано в методе CAccountsCollection::InitControlsParams() для
         //--- переменной m_control_equity_inc, контролирующей прирост средств на 15 единиц (по умолчанию)
         //--- Файл AccountCollection, метод InitControlsParams(), строка 1199
         
         //--- Получаем список всех открытых позиций
         CArrayObj* list_positions=engine.GetListMarketPosition();
         //--- Выбираем позиции с прибылью болше нуля
         list_positions=CSelect::ByOrderProperty(list_positions,ORDER_PROP_PROFIT_FULL,0,MORE);
         if(list_positions!=NULL)
           {
            //--- Сортируем список по прибыли с учётом комиссии и свопа
            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 
                 }
              }
           }
        }
     }
  }
//+------------------------------------------------------------------+

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

Скомпилируем и запустим советник в тестере:

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

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

Что дальше

В следующей статье сделаем работу объекта-аккаунта и его событий на основе событийного функционала класса базового объекта CBaseObj.

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

К содержанию

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

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


Прикрепленные файлы |
MQL5.zip (230.62 KB)
MQL4.zip (230.62 KB)
Последние комментарии | Перейти к обсуждению на форуме трейдеров (31)
Artyom Trishkin
Artyom Trishkin | 1 авг 2019 в 23:15
Реter Konow:

Классическая ошибка авторов статей, - считать, что если им материал понятен, то он понятен всем.) Но, это не так. 

...

А классическая ошибка некоторых читателей - что они не читают, но сразу задают вопросы :)

Если начать читать с первой статьи, то таких вопросов точно не будет, ведь вся "философия" - там, и она разъяснена.

Aleksei Mikhanoshin
Aleksei Mikhanoshin | 2 авг 2019 в 00:02

Артем, спасибо!

Не надо, не останавливайся, пиши - получается классно.

С интересом прочитал вашу дискуссию с Петром. Очень корректно и по делу.

Написал письмо в компанию "AMD",-  Я хоть и не занимаюсь проектированием и выпуском процессоров, но, ребята, ерунду вы спороли в 8-нм техпроцессе...

Жду ответа

Andrey F. Zelinsky
Andrey F. Zelinsky | 2 авг 2019 в 06:14
Реter Konow:

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

Философия здесь вот в чём: индукция (от частного к общему) или дедукция (от общего к частному).

Артём использует индуктивный метод подачи материала.

Шеф: Нус, Глеб Георгиевич, имеется пуля. Ваши суждения...

Жеглов: Ну, что скажешь, "разведка"?

Шарапов: Ну что, пуля как пуля, обыкновенная, пистолетная...

Жеглов: Да, хорошо бы еще гильзу найти.

Шеф: Лучше уж посмотреть само оружие.

Жеглов: Верно. ну значится так: пуля выпущенная из импортного оружия калибра 6.35 системы "Баярд" или, скажем, "Омега".

Шеф: А сие из чего следует?

Жеглов: Из пули, Сергей Ипатич, из пули. Шесть левых верликальных нарезов, вот они - почерк вполне "самостоятельный".

Шеф: А что вы скажете на это? Судя по маркировке - гильза наша, отечественная.

Жеглов: Да. А где нашли?

Шеф: Там где и следует. Слева от тела. Нормально сработал отражатель.

Жеглов: Да, гильза наша. Хм. Ну что же, запишем в загадки. И всё равно надо искать оружие. Надя, вы не знаете, в доме было оружие?

Надежда: Не знаю.

[Вайнеры. Эра милосердия]

Artyom Trishkin
Artyom Trishkin | 9 авг 2019 в 01:32
Aleksei Mikhanoshin:

Артем, спасибо!

Не надо, не останавливайся, пиши - получается классно.

С интересом прочитал вашу дискуссию с Петром. Очень корректно и по делу.

Написал письмо в компанию "AMD",-  Я хоть и не занимаюсь проектированием и выпуском процессоров, но, ребята, ерунду вы спороли в 8-нм техпроцессе...

Жду ответа

Привет, Алексей.

Зачем останавливаться в начале пути? Уже все остановки на раздумье были - теперь только вперёд по кочкам ;)

Ну и как, ответили тебе из AMD ?

Artyom Trishkin
Artyom Trishkin | 9 авг 2019 в 01:34
Andrey F. Zelinsky:

Философия здесь вот в чём: индукция (от частного к общему) или дедукция (от общего к частному).

Артём использует индуктивный метод подачи материала.

Шеф: Нус, Глеб Георгиевич, имеется пуля. Ваши суждения...

Жеглов: Ну, что скажешь, "разведка"?

Шарапов: Ну что, пуля как пуля, обыкновенная, пистолетная...

Жеглов: Да, хорошо бы еще гильзу найти.

Шеф: Лучше уж посмотреть само оружие.

Жеглов: Верно. ну значится так: пуля выпущенная из импортного оружия калибра 6.35 системы "Баярд" или, скажем, "Омега".

Шеф: А сие из чего следует?

Жеглов: Из пули, Сергей Ипатич, из пули. Шесть левых верликальных нарезов, вот они - почерк вполне "самостоятельный".

Шеф: А что вы скажете на это? Судя по маркировке - гильза наша, отечественная.

Жеглов: Да. А где нашли?

Шеф: Там где и следует. Слева от тела. Нормально сработал отражатель.

Жеглов: Да, гильза наша. Хм. Ну что же, запишем в загадки. И всё равно надо искать оружие. Надя, вы не знаете, в доме было оружие?

Надежда: Не знаю.

[Вайнеры. Эра милосердия]

О как... Не знал, что так вот...

А Вайнеров ещё в детстве прочёл.

Выцарапываем профит до последнего пипса Выцарапываем профит до последнего пипса

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

Исследования технических фигур Меррилла Исследования технических фигур Меррилла

В этой мы статье рассмотрим модель технических фигур Меррилла и попробуем выяснить, насколько актуальны эти технические паттерны сегодня. Для этого мы создадим инструмент для их тестирования и применим данную модель к различным типам данных, такие как цена закрытия, ее максимумы и минимумы, индикаторы осцилляторного типа.

Новый подход к интерпретации классической и обратной дивергенции. Часть 2 Новый подход к интерпретации классической и обратной дивергенции. Часть 2

В этой статье мы критическом ключе рассмотрим классическую дивергенцию и проанализируем эффективность различных индикаторов. А также предложим варианты фильтрации для повышения точности анализа и продолжим рассматривать нестандартные решения. Как результат - создадим нетипичный инструмент для решения поставленной задачи.

Библиотека для простого и быстрого создания программ для MetaTrader (Часть XVIII): Интерактивность объекта-аккаунт и любых других объектов библиотеки Библиотека для простого и быстрого создания программ для MetaTrader (Часть XVIII): Интерактивность объекта-аккаунт и любых других объектов библиотеки

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