English 中文 Español Deutsch 日本語 Português
Библиотека для простого и быстрого создания программ для MetaTrader (Часть XVI): События коллекции символов

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

MetaTrader 5Примеры | 16 июля 2019, 10:56
3 672 3
Artyom Trishkin
Artyom Trishkin

Содержание

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


Принципы отслеживания событий аккаунта отличаются от принципов отслеживания торговых событий, рассмотренных нами начиная со статьи 4: если торговые события определяются и заносятся в коллекцию торговых событий для полноценного доступа к любому из ранее происходивших событий, то события аккаунта просто работают реалтайм — "здесь и сейчас": событие определяется, отправляется посредством EventChartCustom() на чарт с программой и отправляется в базовый объект библиотеки. Затем из базового объекта библиотеки можно в своей программе получить доступ к списку одновременно произошедших событий и обработать их. Так организованы события у объекта-аккаунта.

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

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

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

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

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

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

Класс базового объекта для всех объектов библиотеки

Итак. Создадим в каталоге библиотеки \MQL5\Include\DoEasy\Objects\ новый класс CBaseObj в файле BaseObj.mqh. Базовым объектом класса должен быть базовый объект стандартной библиотеки CObject. Класс будет небольшим, поэтому приведу его полный листинг, а далее разберём его по членам и методам.

Одно замечание: чтобы не создавать новый файл для класса события объектов, пропишем этот класс перед базовым классом объектов прямо в этом же файле:
//+------------------------------------------------------------------+
//|                                                      BaseObj.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                             https://mql5.com/ru/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/ru/users/artmedia70"
#property version   "1.00"
#property strict    // Нужно для mql4
//+------------------------------------------------------------------+
//| Включаемые файлы                                                 |
//+------------------------------------------------------------------+
#include <Arrays\ArrayObj.mqh>
#include "..\Services\DELib.mqh"
//+------------------------------------------------------------------+
//| Класс событий базового объекта всех объектов библиотеки          |
//+------------------------------------------------------------------+
class CEventBaseObj : public CObject
  {
private:
   long              m_time;
   long              m_chart_id;
   ushort            m_event_id;
   long              m_lparam;
   double            m_dparam;
   string            m_sparam;
public:
   void              Time(const long time)               { this.m_time=time;           }
   long              Time(void)                    const { return this.m_time;         }
   void              ChartID(const long chart_id)        { this.m_chart_id=chart_id;   }
   long              ChartID(void)                 const { return this.m_chart_id;     }
   void              ID(const ushort id)                 { this.m_event_id=id;         }
   ushort            ID(void)                      const { return this.m_event_id;     }
   void              LParam(const long lparam)           { this.m_lparam=lparam;       }
   long              LParam(void)                  const { return this.m_lparam;       }
   void              DParam(const double dparam)         { this.m_dparam=dparam;       }
   double            DParam(void)                  const { return this.m_dparam;       }
   void              SParam(const string sparam)         { this.m_sparam=sparam;       }
   string            SParam(void)                  const { return this.m_sparam;       }
public:
//--- Конструктор
                     CEventBaseObj(const ushort event_id,const long lparam,const double dparam,const string sparam) : m_chart_id(::ChartID()) 
                       { 
                        this.m_event_id=event_id;
                        this.m_lparam=lparam;
                        this.m_dparam=dparam;
                        this.m_sparam=sparam;
                       }
//--- Метод сравнения для поиска одинаковых объектов-событий
   virtual int       Compare(const CObject *node,const int mode=0) const 
                       {   
                        const CEventBaseObj *compared=node;
                        return
                          (
                           this.ID()>compared.ID()          ?  1  :
                           this.ID()<compared.ID()          ? -1  :
                           this.LParam()>compared.LParam()  ?  1  :
                           this.LParam()<compared.LParam()  ? -1  :
                           this.DParam()>compared.DParam()  ?  1  :
                           this.DParam()<compared.DParam()  ? -1  :
                           this.SParam()>compared.SParam()  ?  1  :
                           this.SParam()<compared.SParam()  ? -1  :  0
                          );
                       } 
  };
//+------------------------------------------------------------------+
//| Класс базового объекта для всех объектов библиотеки              |
//+------------------------------------------------------------------+
class CBaseObj : public CObject
  {
private:

protected:
   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;                             // Код события объекта
   string            m_name;                                   // Наименование объекта
   string            m_folder_name;                            // Имя папки хранения объектов-наследников CBaseObj

//--- Возвращает время в милисекундах из MqlTick
   long              TickTime(void)                            const { return #ifdef __MQL5__ this.m_tick.time_msc #else this.m_tick.time*1000 #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) контролируемых данных объекта (реализация в наследниках)
   virtual void      InitChangesParams(void);
   virtual void      InitControlsParams(void);
//--- (1) Проверяет изменения объекта, возвращает код изменения, (2) устанавливает тип события и заполняет список событий (реализация в наследниках)
   virtual int       SetEventCode(void);
   virtual void      SetTypeEvent(void);
public:
//--- Добавляет событие объекта в список
   bool              EventAdd(const ushort event_id,const long lparam,const double dparam,const string sparam);
//--- Возвращает флаг произошедшего события в данных объекта
   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;          }
//--- Возвращает объект-событие по его номеру в списке
   CEventBaseObj    *GetEvent(const int shift=WRONG_VALUE,const bool check_out=true);
//--- Возвращает количество событий объекта
   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;                  }
//--- Обновляет данные объекта (реализация в наследниках)
   virtual void      Refresh(void);
   
//--- Конструктор
                     CBaseObj();
  };
//+------------------------------------------------------------------+
//| Конструктор                                                      |
//+------------------------------------------------------------------+
CBaseObj::CBaseObj() : m_global_error(ERR_SUCCESS),
                       m_hash_sum(0),m_hash_sum_prev(0),
                       m_is_event(false),m_event_code(0),
                       m_chart_id(::ChartID()),
                       m_folder_name(DIRECTORY),
                       m_name("")
  {
   ::ZeroMemory(this.m_tick);
   this.m_digits_currency=(#ifdef __MQL5__ (int)::AccountInfoInteger(ACCOUNT_CURRENCY_DIGITS) #else 2 #endif);
   this.m_list_events.Clear();
   this.m_list_events.Sort();
  }
//+------------------------------------------------------------------+
//| Добавляет событие объекта в список                               |
//+------------------------------------------------------------------+
bool CBaseObj::EventAdd(const ushort event_id,const long lparam,const double dparam,const string sparam)
  {
   CEventBaseObj *event=new CEventBaseObj(event_id,lparam,dparam,sparam);
   if(event==NULL)
      return false;
   this.m_list_events.Sort();
   if(this.m_list_events.Search(event)>WRONG_VALUE)
     {
      delete event;
      return false;
     }
   return this.m_list_events.Add(event);
  }
//+------------------------------------------------------------------+
//| Возвращает событие объекта по его индексу в списке               |
//+------------------------------------------------------------------+
CEventBaseObj *CBaseObj::GetEvent(const int shift=WRONG_VALUE,const bool check_out=true)
  {
   int total=this.m_list_events.Total();
   if(total==0 || (!check_out && shift>total-1))
      return NULL;   
   int index=(shift<=0 ? total-1 : shift>total-1 ? 0 : total-shift-1);
   CEventBaseObj *event=this.m_list_events.At(index);
   return(event!=NULL ? event : NULL);
  }
//+------------------------------------------------------------------+
//| Возвращает количество знаков после запятой в double-значении     |
//+------------------------------------------------------------------+
int CBaseObj::GetDigits(const double value) const
  {
   string val_str=(string)value;
   int len=::StringLen(val_str);
   int n=len-::StringFind(val_str,".",0)-1;
   if(::StringSubstr(val_str,len-1,1)=="0")
      n--;
   return n;
  }
//+------------------------------------------------------------------+

Разберём класс событий базового объекта.

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

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

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

//--- Конструктор
                     CEventBaseObj(const ushort event_id,const long lparam,const double dparam,const string sparam) : m_chart_id(::ChartID()) 
                       { 
                        this.m_event_id=event_id;
                        this.m_lparam=lparam;
                        this.m_dparam=dparam;
                        this.m_sparam=sparam;
                       }

где им и присваиваются переданные значения.
Также в списке инициализации класса переменной-идентификатору графика присваивается значение ID текущего графика.

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

//--- Метод сравнения для поиска одинаковых объектов-событий
   virtual int       Compare(const CObject *node,const int mode=0) const 
                       {   
                        const CEventBaseObj *compared=node;
                        return
                          (
                           this.ID()>compared.ID()          ?  1  :
                           this.ID()<compared.ID()          ? -1  :
                           this.LParam()>compared.LParam()  ?  1  :
                           this.LParam()<compared.LParam()  ? -1  :
                           this.DParam()>compared.DParam()  ?  1  :
                           this.DParam()<compared.DParam()  ? -1  :
                           this.SParam()>compared.SParam()  ?  1  :
                           this.SParam()<compared.SParam()  ? -1  :  0  
                          );
                       } 

осуществляет поэлементное сравнение всех полей двух классов — текущего и переданного по указателю в метод. Если все поля равны между собой, то метод возвращает 0, что необходимо для осуществления поиска точно такого же объекта в динамическом списке указателей стандартной библиотеки — ведь эти объекты хранятся в списке CArrayObj, а для его метода Search(), предназначенном для поиска одинаковых объектов в списке:

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

необходимо наличие у объекта виртуального метода Compare(), который возвращает 0 в случае совпадения всех свойств двух объектов)

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

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

Здесь всё наглядно, и в комментариях не нуждается. Это весь класс события объектов.

Рассмотрим класс базового объекта для всех объектов библиотеки.

В защищённой секции класса объявлены уже знакомые нам по прошлым объектам переменные-члены класса:

protected:
   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;                             // Код события объекта
   string            m_name;                                   // Наименование объекта
   string            m_folder_name;                            // Имя папки хранения объектов-наследников CBaseObj
  • Список событий объекта m_list_events — в него будем размещать объекты класса событий, рассмотренного нами выше. За один раз у объекта может произойти несколько событий, поэтому нам необходимо определить их все и разместить в этом списке. Далее из главного объекта библиотеки CEngine мы сможем извлечь список всех событий и обработать его.
  • Структура тиков m_tick служит для получения цен и времени события.
  • Хэш-сумма m_hash_sum необходима для определения изменений, произошедших в свойствах объекта.
    Изменение свойств объекта определяется сравнением текущей и прошлой ( m_hash_sum_prev) хэш-сумм.
  • Число знаков после запятой для валюты счёта m_digits_currency необходимо для корректного вывода информации о некоторых событиях, связанных с изменением денежных величин.
  • В код глобальной ошибки m_global_error записывается номер ошибки, полученной при ошибочном возврате какой-либо из функций. Этот код передаётся в вызывающую программу для его дальнейшей обработки.
  • Идентификатор графика управляющей программы m_chart_id служит для указания на какой график отсылать произошедшее в объекте событие.
  • Флаг события объекта m_is_event необходим для указания программе о факте произошедшего события в опрашиваемом объекте для своевременной реакции на произошедшее событие.
  • Код события объекта m_event_code хранит флаги всех одновременно произошедших событий, и по наличию этих флагов мы определяем список произошедших одновременно событий объекта.
  • Наименование объекта m_name необходимо для указания программе некоторых текстовых свойств объекта, из которого будут получены события. Например, для аккаунта — это номер счёта+имя клиента+наименование сервера, а для символа — наименование символа.
  • Имя папки для хранения файлов объектов m_folder_name необходимо для сохранения объекта в файл: здесь хранится имя подпапки, в которой хранятся файлы объектов одного типа.
    Базовым каталогом для подпапок является каталог расположения общих файлов всех клиентских терминалов+имя папки библиотеки: "DoEasy\\". Хранение файлов мы обсуждали при создании коллекции аккаунтов в части 12 описания библиотеки.

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

Рассмотрим методы, расположенные в приватной секции класса:

//--- Возвращает время в милисекундах из MqlTick
   long              TickTime(void)                            const { return #ifdef __MQL5__ this.m_tick.time_msc #else this.m_tick.time*1000 #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) контролируемых данных объекта (реализация в наследниках)
   virtual void      InitChangesParams(void);
   virtual void      InitControlsParams(void);
//--- (1) Проверяет изменения объекта, возвращает код изменения, (2) устанавливает тип события и заполняет список событий (реализация в наследниках)
   virtual int       SetEventCode(void);
   virtual void      SetTypeEvent(void);
  • Метод TickTime() возвращает время события в милисекундах. Для MQL4 в связи с отсутствием в структуре милисекунд, возвращается время в секундах * 1000
  • Метод IsPresentEventFlag() возвращает наличие кода определённого события в значении переменной m_event_code.
  • Метод DigitsCurrency() возвращает количество знаков после запятой в значении валюты счёта из переменной m_digits_currency.
  • Метод GetDigits() рассчитывает и возвращает количество знаков после запятой в переданном в него double-значении.
  • Виртуальный метод InitChangesParams() инициализирует параметры всех изменяемых свойств объекта.
  • Виртуальный метод InitControlsParams() инициализирует параметры отслеживаемых свойств объекта.
  • Виртуальный метод SetEventCode() проверяет изменение свойств объекта и возвращает код произошедших изменений.
  • Виртуальный метод SetTypeEvent() устанавливает тип произошедшего события на основании кода события и помещает все события в список событий объекта.

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

В публичной секции класса объявлены методы:

public:
//--- Добавляет событие объекта в список
   bool              EventAdd(const ushort event_id,const long lparam,const double dparam,const string sparam);
//--- Возвращает флаг произошедшего события в данных объекта
   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;          }
//--- Возвращает объект-событие по его номеру в списке
   CEventBaseObj    *GetEvent(const int shift=WRONG_VALUE,const bool check_out=true);
//--- Возвращает количество событий объекта
   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;                  }
//--- Обновляет данные объекта (реализация в наследниках)
   virtual void      Refresh(void);

В конструкторе класса, в его списке инициализации переменным-членам класса присваиваются начальные значения:

//+------------------------------------------------------------------+
//| Конструктор                                                      |
//+------------------------------------------------------------------+
CBaseObj::CBaseObj() : m_global_error(ERR_SUCCESS),      
                       m_hash_sum(0),m_hash_sum_prev(0), 
                       m_is_event(false),m_event_code(0),
                       m_chart_id(::ChartID()),          
                       m_folder_name(DIRECTORY),         
                       m_name("")                        
  {
   ::ZeroMemory(this.m_tick);
   this.m_digits_currency=(#ifdef __MQL5__ (int)::AccountInfoInteger(ACCOUNT_CURRENCY_DIGITS) #else 2 #endif);
   this.m_list_events.Clear();
   this.m_list_events.Sort();
  }
//+------------------------------------------------------------------+

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

Метод добавления события в список:

//+------------------------------------------------------------------+
//| Добавляет событие объекта в список                               |
//+------------------------------------------------------------------+
bool CBaseObj::EventAdd(const ushort event_id,const long lparam,const double dparam,const string sparam)
  {
   CEventBaseObj *event=new CEventBaseObj(event_id,lparam,dparam,sparam);
   if(event==NULL)
      return false;
   this.m_list_events.Sort();
   if(this.m_list_events.Search(event)>WRONG_VALUE)
     {
      delete event;
      return false;
     }
   return this.m_list_events.Add(event);
  }
//+------------------------------------------------------------------+

В метод передаются идентификатор события и long-, double- и string-значения свойств события. Затем создаётся новое событие с этими параметрами. Если точно такое же событие уже есть в списке, то объект-событие удаляется и метод возвращает false, иначе — метод возвращает результат добавления объекта-события в список.

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

//+------------------------------------------------------------------+
//| Возвращает событие объекта по его индексу в списке               |
//+------------------------------------------------------------------+
CEventBaseObj *CBaseObj::GetEvent(const int shift=WRONG_VALUE,const bool check_out=true)
  {
   int total=this.m_list_events.Total();
   if(total==0 || (!check_out && shift>total-1))
      return NULL;   
   int index=(shift<=0 ? total-1 : shift>total-1 ? 0 : total-shift-1);
   CEventBaseObj *event=this.m_list_events.At(index);
   return(event!=NULL ? event : NULL);
  }
//+------------------------------------------------------------------+

Метод нами уже рассматривался ранее. Здесь лишь добавлен флаг необходимости проверки и корректировки индекса при выходе его значения за пределы списка. По умолчанию в метод передаётся индекс -1 и проверка выхода индекса за пределы списка. В этом случае метод возвращает самый последний объект-событие из списка. Для получения объекта по его индексу, необходимо передать в метод требуемый индекс и флаг контроля выхода за пределы списка = false. В этом случае будет возвращён объект (если индекс в пределах списка), либо NULL — если индекс находится вне списка.

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

//+------------------------------------------------------------------+
//| Возвращает количество знаков после запятой в double-значении     |
//+------------------------------------------------------------------+
int CBaseObj::GetDigits(const double value) const
  {
   string val_str=(string)value;
   int len=::StringLen(val_str);
   int n=len-::StringFind(val_str,".",0)-1;
   if(::StringSubstr(val_str,len-1,1)=="0")
      n--;
   return n;
  }
//+------------------------------------------------------------------+

Новый базовый объект готов.

Теперь нам нужно просто заменить в каждом из базовых объектов библиотеки базовый класс CObject на CBaseObj. Такими объектами у нас будут:

объект класса CAccount:

//+------------------------------------------------------------------+
//| Класс аккаунта                                                   |
//+------------------------------------------------------------------+
class CAccount : public CBaseObj
  {

объект класса CSymbol:

//+------------------------------------------------------------------+
//| Класс абстрактного символа                                       |
//+------------------------------------------------------------------+
class CSymbol : public CBaseObj
  {

Классы коллекций также наделим общими свойствами объектов:

коллекция торговых событий:

//+------------------------------------------------------------------+
//| Коллекция торговых событий счёта                                 |
//+------------------------------------------------------------------+
class CEventsCollection : public CBaseObj
  {

коллекция аккаунтов:

//+------------------------------------------------------------------+
//| Коллекция аккаунтов                                              |
//+------------------------------------------------------------------+
class CAccountsCollection : public CBaseObj
  {

коллекция символов:

//+------------------------------------------------------------------+
//| Коллекция символов                                               |
//+------------------------------------------------------------------+
class CSymbolsCollection : public CBaseObj
  {

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


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

События коллекции символов

Как обычно всё у нас начинается с определения констант и перечислений. Откроем файл Defines.mqh и впишем в него необходимые для отслеживания событий символов данные.

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

//--- Параметры символов
#define CLR_DEFAULT                    (0xFF000000)               // Цвет по умолчанию
#define SYMBOLS_COMMON_TOTAL           (1000)                     // Общее количество рабочих символов

Из раздела данных для работы с символами перенесём перечисление режимов работы с символами

//+------------------------------------------------------------------+
//| Данные для работы с символами                                    |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Режимы работы с символами                                        |
//+------------------------------------------------------------------+
enum ENUM_SYMBOLS_MODE
  {
   SYMBOLS_MODE_CURRENT,                                    // Работа только с текущим символом
   SYMBOLS_MODE_DEFINES,                                    // Работа с заданным списком символов
   SYMBOLS_MODE_MARKET_WATCH,                               // Работа с символами из окна "Обзор рынка"
   SYMBOLS_MODE_ALL                                         // Работа с полным списком символов
  };
//+------------------------------------------------------------------+

в файл Datas.mqh:

//+------------------------------------------------------------------+
//|                                                        Datas.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                             https://mql5.com/ru/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/ru/users/artmedia70"
//+------------------------------------------------------------------+
//| Перечисления                                                     |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Режимы работы с символами                                        |
//+------------------------------------------------------------------+
enum ENUM_SYMBOLS_MODE
  {
   SYMBOLS_MODE_CURRENT,                                    // Работа только с текущим символом
   SYMBOLS_MODE_DEFINES,                                    // Работа с заданным списком символов
   SYMBOLS_MODE_MARKET_WATCH,                               // Работа с символами из окна "Обзор рынка"
   SYMBOLS_MODE_ALL                                         // Работа с полным списком символов
  };
//+------------------------------------------------------------------+
//| Наборы данных                                                    |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Форекс-символы-мажоры                                            |
//+------------------------------------------------------------------+
string DataSymbolsFXMajors[]=
  {

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

На место перенесённого из Defines.mqh перечисления впишем перечисление со списком флагов событий символа и перечисление со списком возможных событий символа:

//+------------------------------------------------------------------+
//| Данные для работы с символами                                    |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Список флагов событий символа                                    |
//+------------------------------------------------------------------+
enum ENUM_SYMBOL_EVENT_FLAGS
  {
   SYMBOL_EVENT_FLAG_NO_EVENT                =  0,          // Нет события
   SYMBOL_EVENT_FLAG_TRADE_MODE              =  1,          // Изменение разрешений исполнения ордеров
   SYMBOL_EVENT_FLAG_SESSION_DEALS           =  2,          // Изменение количества сделок в текущей сессии
   SYMBOL_EVENT_FLAG_SESSION_BUY_ORDERS      =  4,          // Изменение общего числа ордеров на покупку в текущий момент
   SYMBOL_EVENT_FLAG_SESSION_SELL_ORDERS     =  8,          // Изменение общего числа ордеров на продажу в текущий момент
   SYMBOL_EVENT_FLAG_VOLUME                  =  16,         // Изменение объёма в последней сделке больше заданной величины в +/-
   SYMBOL_EVENT_FLAG_VOLUME_HIGH_DAY         =  32,         // Изменение максимального объёма за день больше заданной величины в +/-
   SYMBOL_EVENT_FLAG_VOLUME_LOW_DAY          =  64,         // Изменение минимального объёма за день больше заданной величины в +/-
   SYMBOL_EVENT_FLAG_SPREAD                  =  128,        // Изменение размера спреда больше заданной величины в +/-
   SYMBOL_EVENT_FLAG_STOPLEVEL               =  256,        // Изменения уровня Stop-ордеров больше заданной величины в +/-
   SYMBOL_EVENT_FLAG_FREEZELEVEL             =  512,        // Изменение уровня заморозки больше заданной величины в +/-
   SYMBOL_EVENT_FLAG_BID_LAST                =  1024,       // Изменение цены Bid или Last больше заданной величины в +/-
   SYMBOL_EVENT_FLAG_BID_LAST_HIGH           =  2048,       // Изменение максимальной цены Bid или Last за день больше заданной величины в +/-
   SYMBOL_EVENT_FLAG_BID_LAST_LOW            =  4096,       // Изменение минимальной цены Bid или Last за день больше заданной величины в +/-
   SYMBOL_EVENT_FLAG_ASK                     =  8192,       // Изменение цены Ask больше заданной величины в +/-
   SYMBOL_EVENT_FLAG_ASK_HIGH                =  16384,      // Изменение максимальной цены Ask за день больше заданной величины в +/-
   SYMBOL_EVENT_FLAG_ASK_LOW                 =  32768,      // Изменение минимальной цены Ask за день больше заданной величины в +/-
   SYMBOL_EVENT_FLAG_VOLUME_REAL_DAY         =  65536,      // Изменение реального объёма за день больше заданной величины в +/-
   SYMBOL_EVENT_FLAG_VOLUME_HIGH_REAL_DAY    =  131072,     // Изменение максимального реального объёма за день больше заданной величины в +/-
   SYMBOL_EVENT_FLAG_VOLUME_LOW_REAL_DAY     =  262144,     // Изменение минимального реального объёма за день больше заданной величины в +/-
   SYMBOL_EVENT_FLAG_OPTION_STRIKE           =  524288,     // Изменение цены исполнения опциона больше заданной величины в +/-
   SYMBOL_EVENT_FLAG_VOLUME_LIMIT            =  1048576,    // Изменение максимально допустимого совокупного объема позиции и отложенных ордеров в одном направлении
   SYMBOL_EVENT_FLAG_SWAP_LONG               =  2097152,    // Изменение свопа длинных позиций
   SYMBOL_EVENT_FLAG_SWAP_SHORT              =  4194304,    // Изменение свопа коротких позиций
   SYMBOL_EVENT_FLAG_SESSION_VOLUME          =  8388608,    // Изменение суммарного объёма сделок в текущую сессию больше заданной величины в +/-
   SYMBOL_EVENT_FLAG_SESSION_TURNOVER        =  16777216,   // Изменение суммарного оборота в текущую сессию больше заданной величины в +/-
   SYMBOL_EVENT_FLAG_SESSION_INTEREST        =  33554432,   // Изменение суммарного объёма открытых позиций в текущую сессию больше заданной величины в +/-
   SYMBOL_EVENT_FLAG_SESSION_BUY_ORD_VOLUME  =  67108864,   // Изменение общего объёма ордеров на покупку больше заданной величины в +/-
   SYMBOL_EVENT_FLAG_SESSION_SELL_ORD_VOLUME =  134217728// Изменение общего объёма ордеров на продажу больше заданной величины в +/-
   SYMBOL_EVENT_FLAG_SESSION_OPEN            =  268435456// Изменение цены открытия сессии больше заданной величины в +/-
   SYMBOL_EVENT_FLAG_SESSION_CLOSE           =  536870912// Изменение цены закрытия сессии больше заданной величины в +/-
   SYMBOL_EVENT_FLAG_SESSION_AW              =  1073741824  // Изменение средневзвешенной цены сессии больше заданной величины в +/-
  };
//+------------------------------------------------------------------+
//| Список возможных событий символа                                 |
//+------------------------------------------------------------------+
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)   // Код следующего событя после последнего кода события символа
//+------------------------------------------------------------------+

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

Для сортировки символов по их расположению в окне "Обзор рынка" добавим ещё одно целочисленное свойство символа:

//+------------------------------------------------------------------+
//| Целочисленные свойства символа                                   |
//+------------------------------------------------------------------+
enum ENUM_SYMBOL_PROP_INTEGER
  {
   SYMBOL_PROP_STATUS = 0,                                  // Статус символа
   SYMBOL_PROP_INDEX_MW,                                    // Индекс символа в окне "Обзор рынка"
   SYMBOL_PROP_CUSTOM,                                      // Признак того, что символ является пользовательским
   SYMBOL_PROP_CHART_MODE,                                  // Тип цены для построения баров – Bid или Last (из перечисления ENUM_SYMBOL_CHART_MODE)
   SYMBOL_PROP_EXIST,                                       // Признак того, что символ с таким именем существует
   SYMBOL_PROP_SELECT,                                      // Признак того, что символ выбран в Market Watch
   SYMBOL_PROP_VISIBLE,                                     // Признак того, что выбранный символ отображается в Market Watch
   SYMBOL_PROP_SESSION_DEALS,                               // Количество сделок в текущей сессии 
   SYMBOL_PROP_SESSION_BUY_ORDERS,                          // Общее число ордеров на покупку в текущий момент
   SYMBOL_PROP_SESSION_SELL_ORDERS,                         // Общее число ордеров на продажу в текущий момент
   SYMBOL_PROP_VOLUME,                                      // Volume - объем в последней сделке
   SYMBOL_PROP_VOLUMEHIGH,                                  // Максимальный Volume за день
   SYMBOL_PROP_VOLUMELOW,                                   // Минимальный Volume за день
   SYMBOL_PROP_TIME,                                        // Время последней котировки
   SYMBOL_PROP_DIGITS,                                      // Количество знаков после запятой
   SYMBOL_PROP_DIGITS_LOTS,                                 // Количество знаков после запятой для лота
   SYMBOL_PROP_SPREAD,                                      // Размер спреда в пунктах
   SYMBOL_PROP_SPREAD_FLOAT,                                // Признак плавающего спреда
   SYMBOL_PROP_TICKS_BOOKDEPTH,                             // Максимальное количество показываемых заявок в стакане
   SYMBOL_PROP_TRADE_CALC_MODE,                             // Способ вычисления стоимости контракта (из перечисления ENUM_SYMBOL_CALC_MODE)
   SYMBOL_PROP_TRADE_MODE,                                  // Тип исполнения ордеров (из перечисления ENUM_SYMBOL_TRADE_MODE)
   SYMBOL_PROP_START_TIME,                                  // Дата начала торгов по инструменту (обычно используется для фьючерсов)
   SYMBOL_PROP_EXPIRATION_TIME,                             // Дата окончания торгов по инструменту (обычно используется для фьючерсов)
   SYMBOL_PROP_TRADE_STOPS_LEVEL,                           // Минимальный отступ в пунктах от текущей цены закрытия для установки Stop ордеров
   SYMBOL_PROP_TRADE_FREEZE_LEVEL,                          // Дистанция заморозки торговых операций (в пунктах)
   SYMBOL_PROP_TRADE_EXEMODE,                               // Режим заключения сделок (из перечисления ENUM_SYMBOL_TRADE_EXECUTION)
   SYMBOL_PROP_SWAP_MODE,                                   // Модель расчета свопа (из перечисления ENUM_SYMBOL_SWAP_MODE)
   SYMBOL_PROP_SWAP_ROLLOVER3DAYS,                          // День недели для начисления тройного свопа (из перечисления ENUM_DAY_OF_WEEK)
   SYMBOL_PROP_MARGIN_HEDGED_USE_LEG,                       // Режим расчета хеджированной маржи по наибольшей стороне (Buy или Sell)
   SYMBOL_PROP_EXPIRATION_MODE,                             // Флаги разрешенных режимов истечения ордера
   SYMBOL_PROP_FILLING_MODE,                                // Флаги разрешенных режимов заливки ордера
   SYMBOL_PROP_ORDER_MODE,                                  // Флаги разрешенных типов ордера
   SYMBOL_PROP_ORDER_GTC_MODE,                              // Срок действия StopLoss и TakeProfit ордеров, если SYMBOL_EXPIRATION_MODE=SYMBOL_EXPIRATION_GTC (из перечисления ENUM_SYMBOL_ORDER_GTC_MODE)
   SYMBOL_PROP_OPTION_MODE,                                 // Тип опциона (из перечисления ENUM_SYMBOL_OPTION_MODE)
   SYMBOL_PROP_OPTION_RIGHT,                                // Право опциона (Call/Put) (из перечисления ENUM_SYMBOL_OPTION_RIGHT)
   //--- пропускаемое свойство
   SYMBOL_PROP_BACKGROUND_COLOR                             // Цвет фона, которым подсвечивается символ в Market Watch
  }; 
#define SYMBOL_PROP_INTEGER_TOTAL    (36)                   // Общее количество целочисленных свойств
#define SYMBOL_PROP_INTEGER_SKIP     (1)                    // Количество неиспользуемых в сортировке целочисленных свойств символа
//+------------------------------------------------------------------+

И раз мы добавили новое свойство, то и общее количество целочисленных свойств нужно увеличить до 36 вместо ранее 35.

Наконец, в список возможных критериев сортировки символов добавим это новое свойство:

//+------------------------------------------------------------------+
//| Возможные критерии сортировки символов                           |
//+------------------------------------------------------------------+
#define FIRST_SYM_DBL_PROP          (SYMBOL_PROP_INTEGER_TOTAL-SYMBOL_PROP_INTEGER_SKIP)
#define FIRST_SYM_STR_PROP          (SYMBOL_PROP_INTEGER_TOTAL-SYMBOL_PROP_INTEGER_SKIP+SYMBOL_PROP_DOUBLE_TOTAL-SYMBOL_PROP_DOUBLE_SKIP)
enum ENUM_SORT_SYMBOLS_MODE
  {
//--- Сортировка по целочисленным свойствам
   SORT_BY_SYMBOL_STATUS = 0,                               // Сортировать по статусу символа
   SORT_BY_SYMBOL_INDEX_MW,                                 // Сортировать по индексу в окне "Обзор рынка"
   SORT_BY_SYMBOL_CUSTOM,                                   // Сортировать по признаку пользовательского символа
   SORT_BY_SYMBOL_CHART_MODE,                               // Сортировать по типу цены для построения баров – Bid или Last (из перечисления ENUM_SYMBOL_CHART_MODE)
   SORT_BY_SYMBOL_EXIST,                                    // Сортировать по признаку того, что символ с таким именем существует
   SORT_BY_SYMBOL_SELECT,                                   // Сортировать по признаку того, что символ выбран в Market Watch
   SORT_BY_SYMBOL_VISIBLE,                                  // Сортировать по признаку того, что выбранный символ отображается в Market Watch
   SORT_BY_SYMBOL_SESSION_DEALS,                            // Сортировать по количеству сделок в текущей сессии 
   SORT_BY_SYMBOL_SESSION_BUY_ORDERS,                       // Сортировать по общему числу ордеров на покупку в текущий момент
   SORT_BY_SYMBOL_SESSION_SELL_ORDERS,                      // Сортировать по общему числу ордеров на продажу в текущий момент
   SORT_BY_SYMBOL_VOLUME,                                   // Сортировать по Volume - объему в последней сделке
   SORT_BY_SYMBOL_VOLUMEHIGH,                               // Сортировать по максимальному Volume за день
   SORT_BY_SYMBOL_VOLUMELOW,                                // Сортировать по минимальному Volume за день
   SORT_BY_SYMBOL_TIME,                                     // Сортировать по времени последней котировки
   SORT_BY_SYMBOL_DIGITS,                                   // Сортировать по количеству знаков после запятой
   SORT_BY_SYMBOL_DIGITS_LOT,                               // Сортировать по количеству знаков после запятой в лоте
   SORT_BY_SYMBOL_SPREAD,                                   // Сортировать по размеру спреда в пунктах
   SORT_BY_SYMBOL_SPREAD_FLOAT,                             // Сортировать по признаку плавающего спреда
   SORT_BY_SYMBOL_TICKS_BOOKDEPTH,                          // Сортировать по максимальному количеству показываемых заявок в стакане
   SORT_BY_SYMBOL_TRADE_CALC_MODE,                          // Сортировать по способу вычисления стоимости контракта (из перечисления ENUM_SYMBOL_CALC_MODE)
   SORT_BY_SYMBOL_TRADE_MODE,                               // Сортировать по типу исполнения ордеров (из перечисления ENUM_SYMBOL_TRADE_MODE)
   SORT_BY_SYMBOL_START_TIME,                               // Сортировать по дате начала торгов по инструменту (обычно используется для фьючерсов)
   SORT_BY_SYMBOL_EXPIRATION_TIME,                          // Сортировать по дате окончания торгов по инструменту (обычно используется для фьючерсов)
   SORT_BY_SYMBOL_TRADE_STOPS_LEVEL,                        // Сортировать по минимальному отступу в пунктах от текущей цены закрытия для установки Stop ордеров
   SORT_BY_SYMBOL_TRADE_FREEZE_LEVEL,                       // Сортировать по дистанции заморозки торговых операций (в пунктах)
   SORT_BY_SYMBOL_TRADE_EXEMODE,                            // Сортировать по режиму заключения сделок (из перечисления ENUM_SYMBOL_TRADE_EXECUTION)
   SORT_BY_SYMBOL_SWAP_MODE,                                // Сортировать по модели расчета свопа (из перечисления ENUM_SYMBOL_SWAP_MODE)
   SORT_BY_SYMBOL_SWAP_ROLLOVER3DAYS,                       // Сортировать по дню недели для начисления тройного свопа (из перечисления ENUM_DAY_OF_WEEK)
   SORT_BY_SYMBOL_MARGIN_HEDGED_USE_LEG,                    // Сортировать по режиму расчета хеджированной маржи по наибольшей стороне (Buy или Sell)
   SORT_BY_SYMBOL_EXPIRATION_MODE,                          // Сортировать по флагам разрешенных режимов истечения ордера
   SORT_BY_SYMBOL_FILLING_MODE,                             // Сортировать по флагам разрешенных режимов заливки ордера
   SORT_BY_SYMBOL_ORDER_MODE,                               // Сортировать по флагам разрешенных типов ордера
   SORT_BY_SYMBOL_ORDER_GTC_MODE,                           // Сортировать по сроку действия StopLoss и TakeProfit ордеров
   SORT_BY_SYMBOL_OPTION_MODE,                              // Сортировать по типу опциона (из перечисления ENUM_SYMBOL_OPTION_MODE)
   SORT_BY_SYMBOL_OPTION_RIGHT,                             // Сортировать по праву опциона (Call/Put) (из перечисления ENUM_SYMBOL_OPTION_RIGHT)
//--- Сортировка по вещественным свойствам
   SORT_BY_SYMBOL_BID = FIRST_SYM_DBL_PROP,                 // Сортировать по Bid
   SORT_BY_SYMBOL_BIDHIGH,                                  // Сортировать по максимальному Bid за день
   SORT_BY_SYMBOL_BIDLOW,                                   // Сортировать по минимальному Bid за день
   SORT_BY_SYMBOL_ASK,                                      // Сортировать по Ask
   SORT_BY_SYMBOL_ASKHIGH,                                  // Сортировать по максимальному Ask за день
   SORT_BY_SYMBOL_ASKLOW,                                   // Сортировать по минимальному Ask за день
   SORT_BY_SYMBOL_LAST,                                     // Сортировать по цене, по которой совершена последняя сделка
   SORT_BY_SYMBOL_LASTHIGH,                                 // Сортировать по максимальному Last за день
   SORT_BY_SYMBOL_LASTLOW,                                  // Сортировать по минимальному Last за день
   SORT_BY_SYMBOL_VOLUME_REAL,                              // Сортировать по Volume за день
   SORT_BY_SYMBOL_VOLUMEHIGH_REAL,                          // Сортировать по максимальному Volume за день
   SORT_BY_SYMBOL_VOLUMELOW_REAL,                           // Сортировать по минимальному Volume за день
   SORT_BY_SYMBOL_OPTION_STRIKE,                            // Сортировать по цене исполнения опциона
   SORT_BY_SYMBOL_POINT,                                    // Сортировать по значению одного пункта
   SORT_BY_SYMBOL_TRADE_TICK_VALUE,                         // Сортировать по значению SYMBOL_TRADE_TICK_VALUE_PROFIT
   SORT_BY_SYMBOL_TRADE_TICK_VALUE_PROFIT,                  // Сортировать по рассчитанной стоимости тика для прибыльной позиции
   SORT_BY_SYMBOL_TRADE_TICK_VALUE_LOSS,                    // Сортировать по рассчитанной стоимости тика для убыточной позиции
   SORT_BY_SYMBOL_TRADE_TICK_SIZE,                          // Сортировать по минимальному изменению цены
   SORT_BY_SYMBOL_TRADE_CONTRACT_SIZE,                      // Сортировать по размеру торгового контракта
   SORT_BY_SYMBOL_TRADE_ACCRUED_INTEREST,                   // Сортировать по накопленному купонному доходу
   SORT_BY_SYMBOL_TRADE_FACE_VALUE,                         // Сортировать по номинальной стоимости
   SORT_BY_SYMBOL_TRADE_LIQUIDITY_RATE,                     // Сортировать по коэффициенту ликвидности
   SORT_BY_SYMBOL_VOLUME_MIN,                               // Сортировать по минимальному объему для заключения сделки
   SORT_BY_SYMBOL_VOLUME_MAX,                               // Сортировать по максимальному объему для заключения сделки
   SORT_BY_SYMBOL_VOLUME_STEP,                              // Сортировать по минимальному шагу изменения объема для заключения сделки
   SORT_BY_SYMBOL_VOLUME_LIMIT,                             // Сортировать по максимально допустимому совокупному объему открытой позиции и отложенных ордеров в одном направлении
   SORT_BY_SYMBOL_SWAP_LONG,                                // Сортировать по значению свопа в покупку
   SORT_BY_SYMBOL_SWAP_SHORT,                               // Сортировать по значению свопа в продажу
   SORT_BY_SYMBOL_MARGIN_INITIAL,                           // Сортировать по начальной (инициирующей) марже
   SORT_BY_SYMBOL_MARGIN_MAINTENANCE,                       // Сортировать по поддерживающей марже по инструменту
   SORT_BY_SYMBOL_MARGIN_LONG_INITIAL,                      // Сортировать по коэффициенту взимания начальной маржи по длинным позициям
   SORT_BY_SYMBOL_MARGIN_BUY_STOP_INITIAL,                  // Сортировать по коэффициенту взимания начальной маржи по BuyStop ордерам
   SORT_BY_SYMBOL_MARGIN_BUY_LIMIT_INITIAL,                 // Сортировать по коэффициенту взимания начальной маржи по BuyLimit ордерам
   SORT_BY_SYMBOL_MARGIN_BUY_STOPLIMIT_INITIAL,             // Сортировать по коэффициенту взимания начальной маржи по BuyStopLimit ордерам
   SORT_BY_SYMBOL_MARGIN_LONG_MAINTENANCE,                  // Сортировать по коэффициенту взимания поддерживающей маржи по длинным позициям
   SORT_BY_SYMBOL_MARGIN_BUY_STOP_MAINTENANCE,              // Сортировать по коэффициенту взимания поддерживающей маржи по BuyStop ордерам
   SORT_BY_SYMBOL_MARGIN_BUY_LIMIT_MAINTENANCE,             // Сортировать по коэффициенту взимания поддерживающей маржи по BuyLimit ордерам
   SORT_BY_SYMBOL_MARGIN_BUY_STOPLIMIT_MAINTENANCE,         // Сортировать по коэффициенту взимания поддерживающей маржи по BuyStopLimit ордерам
   SORT_BY_SYMBOL_MARGIN_SHORT_INITIAL,                     // Сортировать по коэффициенту взимания начальной маржи по коротким позициям
   SORT_BY_SYMBOL_MARGIN_SELL_STOP_INITIAL,                 // Сортировать по коэффициенту взимания начальной маржи по SellStop ордерам
   SORT_BY_SYMBOL_MARGIN_SELL_LIMIT_INITIAL,                // Сортировать по коэффициенту взимания начальной маржи по SellLimit ордерам
   SORT_BY_SYMBOL_MARGIN_SELL_STOPLIMIT_INITIAL,            // Сортировать по коэффициенту взимания начальной маржи по SellStopLimit ордерам
   SORT_BY_SYMBOL_MARGIN_SHORT_MAINTENANCE,                 // Сортировать по коэффициенту взимания поддерживающей маржи по коротким позициям
   SORT_BY_SYMBOL_MARGIN_SELL_STOP_MAINTENANCE,             // Сортировать по коэффициенту взимания поддерживающей маржи по SellStop ордерам
   SORT_BY_SYMBOL_MARGIN_SELL_LIMIT_MAINTENANCE,            // Сортировать по коэффициенту взимания поддерживающей маржи по SellLimit ордерам
   SORT_BY_SYMBOL_MARGIN_SELL_STOPLIMIT_MAINTENANCE,        // Сортировать по коэффициенту взимания поддерживающей маржи по SellStopLimit ордерам
   SORT_BY_SYMBOL_SESSION_VOLUME,                           // Сортировать по суммарному объёму сделок в текущую сессию
   SORT_BY_SYMBOL_SESSION_TURNOVER,                         // Сортировать по суммарному обороту в текущую сессию
   SORT_BY_SYMBOL_SESSION_INTEREST,                         // Сортировать по суммарному объёму открытых позиций
   SORT_BY_SYMBOL_SESSION_BUY_ORDERS_VOLUME,                // Сортировать по общему объёму ордеров на покупку в текущий момент
   SORT_BY_SYMBOL_SESSION_SELL_ORDERS_VOLUME,               // Сортировать по общему объёму ордеров на продажу в текущий момент
   SORT_BY_SYMBOL_SESSION_OPEN,                             // Сортировать по цене открытия сессии
   SORT_BY_SYMBOL_SESSION_CLOSE,                            // Сортировать по цене закрытия сессии
   SORT_BY_SYMBOL_SESSION_AW,                               // Сортировать по средневзвешенной цене сессии
   SORT_BY_SYMBOL_SESSION_PRICE_SETTLEMENT,                 // Сортировать по цене поставки на текущую сессию
   SORT_BY_SYMBOL_SESSION_PRICE_LIMIT_MIN,                  // Сортировать по минимально допустимому значению цены на сессию 
   SORT_BY_SYMBOL_SESSION_PRICE_LIMIT_MAX,                  // Сортировать по максимально допустимому значению цены на сессию
   SORT_BY_SYMBOL_MARGIN_HEDGED,                            // Сортировать по размеру контракта или маржи для одного лота перекрытых позиций
//--- Сортировка по строковым свойствам
   SORT_BY_SYMBOL_NAME = FIRST_SYM_STR_PROP,                // Сортировать по имени символа
   SORT_BY_SYMBOL_BASIS,                                    // Сортировать по имени базового актива для производного инструмента
   SORT_BY_SYMBOL_CURRENCY_BASE,                            // Сортировать по базовой валюте инструмента
   SORT_BY_SYMBOL_CURRENCY_PROFIT,                          // Сортировать по валюте прибыли
   SORT_BY_SYMBOL_CURRENCY_MARGIN,                          // Сортировать по валюте, в которой вычисляются залоговые средства
   SORT_BY_SYMBOL_BANK,                                     // Сортировать по источнику текущей котировки
   SORT_BY_SYMBOL_DESCRIPTION,                              // Сортировать по строковому описанию символа
   SORT_BY_SYMBOL_FORMULA,                                  // Сортировать по формуле для построения цены пользовательского символа
   SORT_BY_SYMBOL_ISIN,                                     // Сортировать по имени торгового символа в системе международных идентификационных кодов ценных бумаг — ISIN
   SORT_BY_SYMBOL_PAGE,                                     // Сортировать по адресу интернет страницы с информацией по символу
   SORT_BY_SYMBOL_PATH                                      // Сортировать по пути в дереве символов
  };
//+------------------------------------------------------------------+

С файлом Defines.mqh завершили.

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

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

//+------------------------------------------------------------------+
//|                                                       Symbol.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                             https://mql5.com/ru/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/ru/users/artmedia70"
#property version   "1.00"
#property strict    // Нужно для mql4
//+------------------------------------------------------------------+
//| Включаемые файлы                                                 |
//+------------------------------------------------------------------+
#include "..\BaseObj.mqh"
//+------------------------------------------------------------------+
//| Класс абстрактного символа                                       |
//+------------------------------------------------------------------+
class CSymbol : public CBaseObj
  {
private:
   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;    // Прошлые данные символа
//---

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

   //--- Исполнение
   bool              m_is_change_trade_mode;                   // Флаг изменения режима торговли для символа
   //--- Сделки текущей сессии
   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;               // Флаг изменения средневзвешенной цены сессии больше, чем на величину уменьшения
   

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

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

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

//+------------------------------------------------------------------+
//| Инициализирует переменные отслеживаемых данных символа           |
//+------------------------------------------------------------------+
void CSymbol::InitChangesParams(void)
  {
//--- Список и код изменений
   this.m_list_events.Clear();                           // Очищаем список изменений
   this.m_list_events.Sort();                            // Сортируем список изменений
//--- Исполнение
   this.m_is_change_trade_mode=false;                    // Флаг изменения режима торговли для символа
//--- Сделки текущей сессии
   this.m_changed_session_deals_value=0;                 // Величина изменения количества сделок
   this.m_is_change_session_deals_inc=false;             // Флаг изменения количества сделок больше, чем на величину прироста
   this.m_is_change_session_deals_dec=false;             // Флаг изменения количества сделок больше, чем на величину уменьшения
//--- Ордера Buy текущей сессии
   this.m_changed_session_buy_ord_value=0;               // Величина изменения количества Buy-ордеров
   this.m_is_change_session_buy_ord_inc=false;           // Флаг изменения количества Buy-ордеров больше, чем на величину прироста
   this.m_is_change_session_buy_ord_dec=false;           // Флаг изменения количества Buy-ордеров больше, чем на величину уменьшения
//--- Ордера Sell текущей сессии
   this.m_changed_session_sell_ord_value=0;              // Величина изменения количества Sell-ордеров
   this.m_is_change_session_sell_ord_inc=false;          // Флаг изменения количества Sell-ордеров больше, чем на величину прироста
   this.m_is_change_session_sell_ord_dec=false;          // Флаг изменения количества Sell-ордеров больше, чем на величину уменьшения
//--- Объем в последней сделке
   this.m_changed_volume_value=0;                        // Величина изменения объема в последней сделке
   this.m_is_change_volume_inc=false;                    // Флаг изменения объема в последней сделке больше, чем на величину прироста
   this.m_is_change_volume_dec=false;                    // Флаг изменения объема в последней сделке больше, чем на величину уменьшения
//--- Максимальный Volume за день
   this.m_changed_volume_high_day_value=0;               // Величина изменения максимального объема за день
   this.m_is_change_volume_high_day_inc=false;           // Флаг изменения максимального объема за день больше, чем на величину прироста
   this.m_is_change_volume_high_day_dec=false;           // Флаг изменения максимального объема за день больше, чем на величину уменьшения
//--- Минимальный Volume за день
   this.m_changed_volume_low_day_value=0;                // Величина изменения минимального объема за день
   this.m_is_change_volume_low_day_inc=false;            // Флаг изменения минимального объема за день больше, чем на величину прироста
   this.m_is_change_volume_low_day_dec=false;            // Флаг изменения минимального объема за день больше, чем на величину уменьшения
//--- Спред
   this.m_changed_spread_value=0;                        // Величина изменения спреда в пунктах
   this.m_is_change_spread_inc=false;                    // Флаг изменения спреда в пунктах больше, чем на величину прироста
   this.m_is_change_spread_dec=false;                    // Флаг изменения спреда в пунктах больше, чем на величину уменьшения
//--- StopLevel
   this.m_changed_stops_level_value=0;                   // Величина изменения StopLevel в пунктах
   this.m_is_change_stops_level_inc=false;               // Флаг изменения StopLevel в пунктах больше, чем на величину прироста
   this.m_is_change_stops_level_dec=false;               // Флаг изменения StopLevel в пунктах больше, чем на величину уменьшения
//--- Дистанция заморозки
   this.m_changed_freeze_level_value=0;                  // Величина изменения FreezeLevel в пунктах
   this.m_is_change_freeze_level_inc=false;              // Флаг изменения FreezeLevel в пунктах больше, чем на величину прироста
   this.m_is_change_freeze_level_dec=false;              // Флаг изменения FreezeLevel в пунктах больше, чем на величину уменьшения
   
//--- Bid/Last
   this.m_changed_bid_last_value=0;                      // Величина изменения цены Bid или Last
   this.m_is_change_bid_last_inc=false;                  // Флаг изменения цены Bid или Last больше, чем на величину прироста
   this.m_is_change_bid_last_dec=false;                  // Флаг изменения цены Bid или Last больше, чем на величину уменьшения
//--- Максимальный Bid/Last за день
   this.m_changed_bid_last_high_value=0;                 // Величина изменения максимального Bid или Last за день
   this.m_is_change_bid_last_high_inc=false;             // Флаг изменения максимального Bid или Last за день больше, чем на величину прироста
   this.m_is_change_bid_last_high_dec=false;             // Флаг изменения максимального Bid или Last за день больше, чем на величину уменьшения
//--- Минимальный Bid/Last за день
   this.m_changed_bid_last_low_value=0;                  // Величина изменения минимального Bid или Last за день
   this.m_is_change_bid_last_low_inc=false;              // Флаг изменения минимального Bid или Last за день больше, чем на величину прироста
   this.m_is_change_bid_last_low_dec=false;              // Флаг изменения минимального Bid или Last за день больше, чем на величину уменьшения
//--- Ask
   this.m_changed_ask_value=0;                           // Величина изменения цены Ask
   this.m_is_change_ask_inc=false;                       // Флаг изменения цены Ask больше, чем на величину прироста
   this.m_is_change_ask_dec=false;                       // Флаг изменения цены Ask больше, чем на величину уменьшения
//--- Максимальный Ask за день
   this.m_changed_ask_high_value=0;                      // Величина изменения максимального Ask за день
   this.m_is_change_ask_high_inc=false;                  // Флаг изменения максимального Ask за день больше, чем на величину прироста
   this.m_is_change_ask_high_dec=false;                  // Флаг изменения максимального Ask за день больше, чем на величину уменьшения
//--- Минимальный Ask за день
   this.m_changed_ask_low_value=0;                       // Величина изменения минимального Ask за день
   this.m_is_change_ask_low_inc=false;                   // Флаг изменения минимального Ask за день больше, чем на величину прироста
   this.m_is_change_ask_low_dec=false;                   // Флаг изменения минимального Ask за день больше, чем на величину уменьшения
//--- Реальный Volume за день
   this.m_changed_volume_real_value=0;                   // Величина изменения реального Volume за день
   this.m_is_change_volume_real_inc=false;               // Флаг изменения реального Volume за день больше, чем на величину прироста
   this.m_is_change_volume_real_dec=false;               // Флаг изменения реального Volume за день больше, чем на величину уменьшения
//--- Максимальный реальный Volume за день
   this.m_changed_volume_high_real_day_value=0;          // Величина изменения максимального реального Volume за день
   this.m_is_change_volume_high_real_day_inc=false;      // Флаг изменения максимального реального Volume за день больше, чем на величину прироста
   this.m_is_change_volume_high_real_day_dec=false;      // Флаг изменения максимального реального Volume за день больше, чем на величину уменьшения
//--- Минимальный реальный Volume за день
   this.m_changed_volume_low_real_day_value=0;           // Величина изменения минимального реального Volume за день
   this.m_is_change_volume_low_real_day_inc=false;       // Флаг изменения минимального реального Volume за день больше, чем на величину прироста
   this.m_is_change_volume_low_real_day_dec=false;       // Флаг изменения минимального реального Volume за день больше, чем на величину уменьшения
//--- Цена исполнения опциона
   this.m_changed_option_strike_value=0;                 // Величина изменения цены исполнения опциона
   this.m_is_change_option_strike_inc=false;             // Флаг изменения цены исполнения опциона больше, чем на величину прироста
   this.m_is_change_option_strike_dec=false;             // Флаг изменения цены исполнения опциона больше, чем на величину уменьшения
//--- Совокупный объём позиций и ордеров
   this.m_changed_volume_limit_value=0;                  // Величина изменения минимального совокупного объёма
   this.m_is_change_volume_limit_inc=false;              // Флаг увеличения минимального совокупного объёма
   this.m_is_change_volume_limit_dec=false;              // Флаг уменьшения минимального совокупного объёма
//---  Своп на покупку
   this.m_changed_swap_long_value=0;                     // Величина изменения свопа на покупку
   this.m_is_change_swap_long_inc=false;                 // Флаг увеличения свопа на покупку
   this.m_is_change_swap_long_dec=false;                 // Флаг уменьшения свопа на покупку
//---  Своп на продажу
   this.m_changed_swap_short_value=0;                    // Величина изменения свопа на продажу
   this.m_is_change_swap_short_inc=false;                // Флаг увеличения свопа на продажу
   this.m_is_change_swap_short_dec=false;                // Флаг уменьшения свопа на продажу
//--- Cуммарный объём сделок в текущую сессию
   this.m_changed_session_volume_value=0;                // Величина изменения суммарного объёма сделок в текущую сессию
   this.m_is_change_session_volume_inc=false;            // Флаг изменения суммарного объёма сделок в текущую сессию больше, чем на величину прироста
   this.m_is_change_session_volume_dec=false;            // Флаг изменения суммарного объёма сделок в текущую сессию больше, чем на величину уменьшения
//--- Cуммарный оборот в текущую сессию
   this.m_changed_session_turnover_value=0;              // Величина изменения суммарного оборота в текущую сессию
   this.m_is_change_session_turnover_inc=false;          // Флаг изменения суммарного оборота в текущую сессию больше, чем на величину прироста
   this.m_is_change_session_turnover_dec=false;          // Флаг изменения суммарного оборота в текущую сессию больше, чем на величину уменьшения
//--- Cуммарный объём открытых позиций
   this.m_changed_session_interest_value=0;              // Величина изменения суммарного объёма открытых позиций в текущую сессию
   this.m_is_change_session_interest_inc=false;          // Флаг изменения суммарного объёма открытых позиций в текущую сессию больше, чем на величину прироста
   this.m_is_change_session_interest_dec=false;          // Флаг изменения суммарного объёма открытых позиций в текущую сессию больше, чем на величину уменьшения
//--- Общий объём ордеров на покупку в текущий момент
   this.m_changed_session_buy_ord_volume_value=0;        // Величина изменения общего объёма ордеров на покупку в текущий момент
   this.m_is_change_session_buy_ord_volume_inc=false;    // Флаг изменения общего объёма ордеров на покупку в текущий момент больше, чем на величину прироста
   this.m_is_change_session_buy_ord_volume_dec=false;    // Флаг изменения общего объёма ордеров на покупку в текущий момент больше, чем на величину уменьшения
//--- Общий объём ордеров на продажу в текущий момент
   this.m_changed_session_sell_ord_volume_value=0;       // Величина изменения общего объёма ордеров на продажу в текущий момент
   this.m_is_change_session_sell_ord_volume_inc=false;   // Флаг изменения общего объёма ордеров на продажу в текущий момент больше, чем на величину прироста
   this.m_is_change_session_sell_ord_volume_dec=false;   // Флаг изменения общего объёма ордеров на продажу в текущий момент больше, чем на величину уменьшения
//--- Цена открытия сессии
   this.m_changed_session_open_value=0;                  // Величина изменения цены открытия сессии
   this.m_is_change_session_open_inc=false;              // Флаг изменения цены открытия сессии больше, чем на величину прироста
   this.m_is_change_session_open_dec=false;              // Флаг изменения цены открытия сессии больше, чем на величину уменьшения
//--- Цена закрытия сессии
   this.m_changed_session_close_value=0;                 // Величина изменения цены закрытия сессии
   this.m_is_change_session_close_inc=false;             // Флаг изменения цены закрытия сессии больше, чем на величину прироста
   this.m_is_change_session_close_dec=false;             // Флаг изменения цены закрытия сессии больше, чем на величину уменьшения
//--- Средневзвешенная цена сессии
   this.m_changed_session_aw_value=0;                    // Величина изменения средневзвешенной цены сессии
   this.m_is_change_session_aw_inc=false;                // Флаг изменения средневзвешенной цены сессии больше, чем на величину прироста
   this.m_is_change_session_aw_dec=false;                // Флаг изменения средневзвешенной цены сессии больше, чем на величину уменьшения
  }
//+------------------------------------------------------------------+

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

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

//+------------------------------------------------------------------+
//| Инициализирует переменные контролируемых  данных символа         |
//+------------------------------------------------------------------+
void CSymbol::InitControlsParams(void)
  {
//--- Сделки текущей сессии
   this.m_control_session_deals_inc=10;                  // Контролируемая величина прироста количества сделок
   this.m_control_session_deals_dec=10;                  // Контролируемая величина уменьшения количества сделок
//--- Ордера Buy текущей сессии
   this.m_control_session_buy_ord_inc=10;                // Контролируемая величина прироста количества Buy-ордеров
   this.m_control_session_buy_ord_dec=10;                // Контролируемая величина уменьшения количества Buy-ордеров
//--- Ордера Sell текущей сессии
   this.m_control_session_sell_ord_inc=10;               // Контролируемая величина прироста количества Sell-ордеров
   this.m_control_session_sell_ord_dec=10;               // Контролируемая величина уменьшения количества Sell-ордеров
//--- Объем в последней сделке
   this.m_control_volume_inc=10;                         // Контролируемая величина прироста объема в последней сделке
   this.m_control_volume_dec=10;                         // Контролируемая величина уменьшения объема в последней сделке
//--- Максимальный Volume за день
   this.m_control_volume_high_day_inc=50;                // Контролируемая величина прироста максимального объема за день
   this.m_control_volume_high_day_dec=50;                // Контролируемая величина уменьшения максимального объема за день
//--- Минимальный Volume за день
   this.m_control_volume_low_day_inc=50;                 // Контролируемая величина прироста минимального объема за день
   this.m_control_volume_low_day_dec=50;                 // Контролируемая величина уменьшения минимального объема за день
//--- Спред
   this.m_control_spread_inc=2;                          // Контролируемая величина прироста спреда в пунктах
   this.m_control_spread_dec=2;                          // Контролируемая величина уменьшения спреда в пунктах
//--- StopLevel
   this.m_control_stops_level_inc=2;                     // Контролируемая величина прироста StopLevel в пунктах
   this.m_control_stops_level_dec=2;                     // Контролируемая величина уменьшения StopLevel в пунктах
//--- Дистанция заморозки
   this.m_control_freeze_level_inc=2;                    // Контролируемая величина прироста FreezeLevel в пунктах
   this.m_control_freeze_level_dec=2;                    // Контролируемая величина уменьшения FreezeLevel в пунктах
   
//--- Bid/Last
   this.m_control_bid_last_inc=DBL_MAX;                  // Контролируемая величина прироста цены Bid или Last
   this.m_control_bid_last_dec=DBL_MAX;                  // Контролируемая величина уменьшения цены Bid или Last
//--- Максимальный Bid/Last за день
   this.m_control_bid_last_high_inc=DBL_MAX;             // Контролируемая величина прироста максимального Bid или Last за день
   this.m_control_bid_last_high_dec=DBL_MAX;             // Контролируемая величина уменьшения максимального Bid или Last за день
//--- Минимальный Bid/Last за день
   this.m_control_bid_last_low_inc=DBL_MAX;              // Контролируемая величина прироста минимального Bid или Last за день
   this.m_control_bid_last_low_dec=DBL_MAX;              // Контролируемая величина уменьшения минимального Bid или Last за день
//--- Ask
   this.m_control_ask_inc=DBL_MAX;                       // Контролируемая величина прироста цены Ask
   this.m_control_ask_dec=DBL_MAX;                       // Контролируемая величина уменьшения цены Ask
//--- Максимальный Ask за день
   this.m_control_ask_high_inc=DBL_MAX;                  // Контролируемая величина прироста максимального Ask за день
   this.m_control_ask_high_dec=DBL_MAX;                  // Контролируемая величина уменьшения максимального Ask за день
//--- Минимальный Ask за день
   this.m_control_ask_low_inc=DBL_MAX;                   // Контролируемая величина прироста минимального Ask за день
   this.m_control_ask_low_dec=DBL_MAX;                   // Контролируемая величина уменьшения минимального Ask за день
//--- Реальный Volume за день
   this.m_control_volume_real_inc=50;                    // Контролируемая величина прироста реального Volume за день
   this.m_control_volume_real_dec=50;                    // Контролируемая величина уменьшения реального Volume за день
//--- Максимальный реальный Volume за день
   this.m_control_volume_high_real_day_inc=20;           // Контролируемая величина прироста максимального реального Volume за день
   this.m_control_volume_high_real_day_dec=20;           // Контролируемая величина уменьшения максимального реального Volume за день
//--- Минимальный реальный Volume за день
   this.m_control_volume_low_real_day_inc=10;            // Контролируемая величина прироста минимального реального Volume за день
   this.m_control_volume_low_real_day_dec=10;            // Контролируемая величина уменьшения минимального реального Volume за день
//--- Цена исполнения опциона
   this.m_control_option_strike_inc=0;                   // Контролируемая величина прироста цены исполнения опциона
   this.m_control_option_strike_dec=0;                   // Контролируемая величина уменьшения цены исполнения опциона
//--- Cуммарный объём сделок в текущую сессию
   this.m_control_session_volume_inc=10;                 // Контролируемая величина прироста суммарного объёма сделок в текущую сессию
   this.m_control_session_volume_dec=10;                 // Контролируемая величина уменьшения суммарного объёма сделок в текущую сессию
//--- Cуммарный оборот в текущую сессию
   this.m_control_session_turnover_inc=1000;             // Контролируемая величина прироста суммарного оборота в текущую сессию
   this.m_control_session_turnover_dec=500;              // Контролируемая величина уменьшения суммарного оборота в текущую сессию
//--- Cуммарный объём открытых позиций
   this.m_control_session_interest_inc=50;               // Контролируемая величина прироста суммарного объёма открытых позиций в текущую сессию
   this.m_control_session_interest_dec=20;               // Контролируемая величина уменьшения суммарного объёма открытых позиций в текущую сессию
//--- Общий объём ордеров на покупку в текущий момент
   this.m_control_session_buy_ord_volume_inc=50;         // Контролируемая величина прироста общего объёма ордеров на покупку в текущий момент
   this.m_control_session_buy_ord_volume_dec=20;         // Контролируемая величина уменьшения общего объёма ордеров на покупку в текущий момент
//--- Общий объём ордеров на продажу в текущий момент
   this.m_control_session_sell_ord_volume_inc=50;        // Контролируемая величина прироста общего объёма ордеров на продажу в текущий момент
   this.m_control_session_sell_ord_volume_dec=20;        // Контролируемая величина уменьшения общего объёма ордеров на продажу в текущий момент
//--- Цена открытия сессии
   this.m_control_session_open_inc=0;                    // Контролируемая величина прироста цены открытия сессии
   this.m_control_session_open_dec=0;                    // Контролируемая величина уменьшения цены открытия сессии
//--- Цена закрытия сессии
   this.m_control_session_close_inc=0;                   // Контролируемая величина прироста цены закрытия сессии
   this.m_control_session_close_dec=0;                   // Контролируемая величина уменьшения цены закрытия сессии
//--- Средневзвешенная цена сессии
   this.m_control_session_aw_inc=0;                      // Контролируемая величина прироста средневзвешенной цены сессии
   this.m_control_session_aw_dec=0;                      // Контролируемая величина уменьшения средневзвешенной цены сессии
  }
//+------------------------------------------------------------------+

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

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

//+------------------------------------------------------------------+
//| Проверяет изменения свойств символа, возвращает код изменения    |
//+------------------------------------------------------------------+
int CSymbol::SetEventCode(void)
  {
   this.m_event_code=SYMBOL_EVENT_FLAG_NO_EVENT;

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

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

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

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

//+------------------------------------------------------------------+
//| Устанавливает тип события объекта-символа                        |
//+------------------------------------------------------------------+
void CSymbol::SetTypeEvent(void)
  {
   this.InitChangesParams();
   ENUM_SYMBOL_EVENT event_id=SYMBOL_EVENT_NO_EVENT;
//--- Изменение режимов торговли на символе
   if(this.IsPresentEventFlag(SYMBOL_EVENT_FLAG_TRADE_MODE))
     {
      event_id=
        (
         this.TradeMode()==SYMBOL_TRADE_MODE_DISABLED  ? SYMBOL_EVENT_TRADE_DISABLE    :
         this.TradeMode()==SYMBOL_TRADE_MODE_LONGONLY  ? SYMBOL_EVENT_TRADE_LONGONLY   :
         this.TradeMode()==SYMBOL_TRADE_MODE_SHORTONLY ? SYMBOL_EVENT_TRADE_SHORTONLY  :
         this.TradeMode()==SYMBOL_TRADE_MODE_CLOSEONLY ? SYMBOL_EVENT_TRADE_CLOSEONLY  :
         SYMBOL_EVENT_TRADE_FULL
        );
      this.m_is_change_trade_mode=true;
      if(this.EventAdd(event_id,this.TickTime(),this.TradeMode(),this.Name()))
         this.m_struct_prev_symbol.trade_mode=this.m_struct_curr_symbol.trade_mode;
     }
//--- Изменение количества сделок в текущей сессии
   if(this.IsPresentEventFlag(SYMBOL_EVENT_FLAG_SESSION_DEALS))
     {
      this.m_changed_session_deals_value=this.m_struct_curr_symbol.session_deals-this.m_struct_prev_symbol.session_deals;
      if(this.m_changed_session_deals_value>this.m_control_session_deals_inc)
        {
         this.m_is_change_session_deals_inc=true;
         event_id=SYMBOL_EVENT_SESSION_DEALS_INC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_session_deals_value,this.Name()))
            this.m_struct_prev_symbol.session_deals=this.m_struct_curr_symbol.session_deals;
        }
      else if(this.m_changed_session_deals_value<-this.m_control_session_deals_dec)
        {
         this.m_is_change_session_deals_dec=true;
         event_id=SYMBOL_EVENT_SESSION_DEALS_DEC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_session_deals_value,this.Name()))
            this.m_struct_prev_symbol.session_deals=this.m_struct_curr_symbol.session_deals;
        }
     }
//--- Изменение количества общего числа ордеров на покупку в текущий момент
   if(this.IsPresentEventFlag(SYMBOL_EVENT_FLAG_SESSION_BUY_ORDERS))
     {
      this.m_changed_session_buy_ord_value=this.m_struct_curr_symbol.session_buy_orders-this.m_struct_prev_symbol.session_buy_orders;
      if(this.m_changed_session_buy_ord_value>this.m_control_session_buy_ord_inc)
        {
         this.m_is_change_session_buy_ord_inc=true;
         event_id=SYMBOL_EVENT_SESSION_BUY_ORDERS_INC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_session_buy_ord_value,this.Name()))
            this.m_struct_prev_symbol.session_buy_orders=this.m_struct_curr_symbol.session_buy_orders;
        }
      else if(this.m_changed_session_buy_ord_value<-this.m_control_session_buy_ord_dec)
        {
         this.m_is_change_session_buy_ord_dec=true;
         event_id=SYMBOL_EVENT_SESSION_BUY_ORDERS_DEC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_session_buy_ord_value,this.Name()))
            this.m_struct_prev_symbol.session_buy_orders=this.m_struct_curr_symbol.session_buy_orders;
        }
     }
//--- Изменение количества общего числа ордеров на продажу в текущий момент
   if(this.IsPresentEventFlag(SYMBOL_EVENT_FLAG_SESSION_SELL_ORDERS))
     {
      this.m_changed_session_sell_ord_value=this.m_struct_curr_symbol.session_sell_orders-this.m_struct_prev_symbol.session_sell_orders;
      if(this.m_changed_session_sell_ord_value>this.m_control_session_sell_ord_inc)
        {
         this.m_is_change_session_sell_ord_inc=true;
         event_id=SYMBOL_EVENT_SESSION_SELL_ORDERS_INC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_session_sell_ord_value,this.Name()))
            this.m_struct_prev_symbol.session_sell_orders=this.m_struct_curr_symbol.session_sell_orders;
        }
      else if(this.m_changed_session_sell_ord_value<-this.m_control_session_sell_ord_dec)
        {
         this.m_is_change_session_sell_ord_dec=true;
         event_id=SYMBOL_EVENT_SESSION_SELL_ORDERS_DEC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_session_sell_ord_value,this.Name()))
            this.m_struct_prev_symbol.session_sell_orders=this.m_struct_curr_symbol.session_sell_orders;
        }
     }
//--- Изменение объема в последней сделке
   if(this.IsPresentEventFlag(SYMBOL_EVENT_FLAG_VOLUME))
     {
      this.m_changed_volume_value=this.m_struct_curr_symbol.volume-this.m_struct_prev_symbol.volume;
      if(this.m_changed_volume_value>this.m_control_volume_inc)
        {
         this.m_is_change_volume_inc=true;
         event_id=SYMBOL_EVENT_VOLUME_INC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_volume_value,this.Name()))
            this.m_struct_prev_symbol.volume=this.m_struct_curr_symbol.volume;
        }
      else if(this.m_changed_volume_value<-this.m_control_volume_dec)
        {
         this.m_is_change_volume_dec=true;
         event_id=SYMBOL_EVENT_VOLUME_DEC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_volume_value,this.Name()))
            this.m_struct_prev_symbol.volume=this.m_struct_curr_symbol.volume;
        }
     }
//--- Изменение максимального Volume за день
   if(this.IsPresentEventFlag(SYMBOL_EVENT_FLAG_VOLUME_HIGH_DAY))
     {
      this.m_changed_volume_high_day_value=this.m_struct_curr_symbol.volume_high_day-this.m_struct_prev_symbol.volume_high_day;
      if(this.m_changed_volume_high_day_value>this.m_control_volume_high_day_inc)
        {
         this.m_is_change_volume_high_day_inc=true;
         event_id=SYMBOL_EVENT_VOLUME_HIGH_DAY_INC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_volume_high_day_value,this.Name()))
            this.m_struct_prev_symbol.volume_high_day=this.m_struct_curr_symbol.volume_high_day;
        }
      else if(this.m_changed_volume_high_day_value<-this.m_control_volume_high_day_dec)
        {
         this.m_is_change_volume_high_day_dec=true;
         event_id=SYMBOL_EVENT_VOLUME_HIGH_DAY_DEC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_volume_high_day_value,this.Name()))
            this.m_struct_prev_symbol.volume_high_day=this.m_struct_curr_symbol.volume_high_day;
        }
     }
//--- Изменение минимального Volume за день
   if(this.IsPresentEventFlag(SYMBOL_EVENT_FLAG_VOLUME_LOW_DAY))
     {
      this.m_changed_volume_low_day_value=this.m_struct_curr_symbol.volume_low_day-this.m_struct_prev_symbol.volume_low_day;
      if(this.m_changed_volume_low_day_value>this.m_control_volume_low_day_inc)
        {
         this.m_is_change_volume_low_day_inc=true;
         event_id=SYMBOL_EVENT_VOLUME_LOW_DAY_INC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_volume_low_day_value,this.Name()))
            this.m_struct_prev_symbol.volume_low_day=this.m_struct_curr_symbol.volume_low_day;
        }
      else if(this.m_changed_volume_low_day_value<-this.m_control_volume_low_day_dec)
        {
         this.m_is_change_volume_low_day_dec=true;
         event_id=SYMBOL_EVENT_VOLUME_LOW_DAY_DEC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_volume_low_day_value,this.Name()))
            this.m_struct_prev_symbol.volume_low_day=this.m_struct_curr_symbol.volume_low_day;
        }
     }
//--- Изменение размера спреда в пунктах
   if(this.IsPresentEventFlag(SYMBOL_EVENT_FLAG_SPREAD))
     {
      this.m_changed_spread_value=this.m_struct_curr_symbol.spread-this.m_struct_prev_symbol.spread;
      if(this.m_changed_spread_value>this.m_control_spread_inc)
        {
         this.m_is_change_spread_inc=true;
         event_id=SYMBOL_EVENT_SPREAD_INC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_spread_value,this.Name()))
            this.m_struct_prev_symbol.spread=this.m_struct_curr_symbol.spread;
        }
      else if(this.m_changed_spread_value<-this.m_control_spread_dec)
        {
         this.m_is_change_spread_dec=true;
         event_id=SYMBOL_EVENT_SPREAD_DEC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_spread_value,this.Name()))
            this.m_struct_prev_symbol.spread=this.m_struct_curr_symbol.spread;
        }
     }
//--- Изменение размера StopLevel в пунктах
   if(this.IsPresentEventFlag(SYMBOL_EVENT_FLAG_STOPLEVEL))
     {
      this.m_changed_stops_level_value=this.m_struct_curr_symbol.stops_level-this.m_struct_prev_symbol.stops_level;
      if(this.m_changed_stops_level_value>this.m_control_stops_level_inc)
        {
         this.m_is_change_stops_level_inc=true;
         event_id=SYMBOL_EVENT_STOPLEVEL_INC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_stops_level_value,this.Name()))
            this.m_struct_prev_symbol.stops_level=this.m_struct_curr_symbol.stops_level;
        }
      else if(this.m_changed_stops_level_value<-this.m_control_stops_level_dec)
        {
         this.m_is_change_stops_level_dec=true;
         event_id=SYMBOL_EVENT_STOPLEVEL_DEC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_stops_level_value,this.Name()))
            this.m_struct_prev_symbol.stops_level=this.m_struct_curr_symbol.stops_level;
        }
     }
//--- Изменение размера FreezeLevel в пунктах
   if(this.IsPresentEventFlag(SYMBOL_EVENT_FLAG_FREEZELEVEL))
     {
      this.m_changed_freeze_level_value=this.m_struct_curr_symbol.freeze_level-this.m_struct_prev_symbol.freeze_level;
      if(this.m_changed_freeze_level_value>this.m_control_freeze_level_inc)
        {
         this.m_is_change_freeze_level_inc=true;
         event_id=SYMBOL_EVENT_FREEZELEVEL_INC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_freeze_level_value,this.Name()))
            this.m_struct_prev_symbol.freeze_level=this.m_struct_curr_symbol.freeze_level;
        }
      else if(this.m_changed_freeze_level_value<-this.m_control_freeze_level_dec)
        {
         this.m_is_change_freeze_level_dec=true;
         event_id=SYMBOL_EVENT_FREEZELEVEL_DEC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_freeze_level_value,this.Name()))
            this.m_struct_prev_symbol.freeze_level=this.m_struct_curr_symbol.freeze_level;
        }
     }
//--- Изменение цены Bid/Last
   if(this.IsPresentEventFlag(SYMBOL_EVENT_FLAG_BID_LAST))
     {
      this.m_changed_bid_last_value=this.m_struct_curr_symbol.bid_last-this.m_struct_prev_symbol.bid_last;
      if(this.m_changed_bid_last_value>this.m_control_bid_last_inc)
        {
         this.m_is_change_bid_last_inc=true;
         event_id=SYMBOL_EVENT_BID_LAST_INC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_bid_last_value,this.Name()))
            this.m_struct_prev_symbol.bid_last=this.m_struct_curr_symbol.bid_last;
        }
      else if(this.m_changed_bid_last_value<-this.m_control_bid_last_dec)
        {
         this.m_is_change_bid_last_dec=true;
         event_id=SYMBOL_EVENT_BID_LAST_DEC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_bid_last_value,this.Name()))
            this.m_struct_prev_symbol.bid_last=this.m_struct_curr_symbol.bid_last;
        }
     }
//--- Изменение максимального Bid/Last за день
   if(this.IsPresentEventFlag(SYMBOL_EVENT_FLAG_BID_LAST_HIGH))
     {
      this.m_changed_bid_last_high_value=this.m_struct_curr_symbol.bid_last_high-this.m_struct_prev_symbol.bid_last_high;
      if(this.m_changed_bid_last_high_value>this.m_control_bid_last_high_inc)
        {
         this.m_is_change_bid_last_high_inc=true;
         event_id=SYMBOL_EVENT_BID_LAST_HIGH_INC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_bid_last_high_value,this.Name()))
            this.m_struct_prev_symbol.bid_last_high=this.m_struct_curr_symbol.bid_last_high;
        }
      else if(this.m_changed_bid_last_high_value<-this.m_control_bid_last_high_dec)
        {
         this.m_is_change_bid_last_high_dec=true;
         event_id=SYMBOL_EVENT_BID_LAST_HIGH_DEC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_bid_last_high_value,this.Name()))
            this.m_struct_prev_symbol.bid_last_high=this.m_struct_curr_symbol.bid_last_high;
        }
     }
//--- Изменение минимального Bid/Last за день
   if(this.IsPresentEventFlag(SYMBOL_EVENT_FLAG_BID_LAST_LOW))
     {
      this.m_changed_bid_last_low_value=this.m_struct_curr_symbol.bid_last_low-this.m_struct_prev_symbol.bid_last_low;
      if(this.m_changed_bid_last_low_value>this.m_control_bid_last_low_inc)
        {
         this.m_is_change_bid_last_low_inc=true;
         event_id=SYMBOL_EVENT_BID_LAST_LOW_INC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_bid_last_low_value,this.Name()))
            this.m_struct_prev_symbol.bid_last_low=this.m_struct_curr_symbol.bid_last_low;
        }
      else if(this.m_changed_bid_last_low_value<-this.m_control_bid_last_low_dec)
        {
         this.m_is_change_bid_last_low_dec=true;
         event_id=SYMBOL_EVENT_BID_LAST_LOW_DEC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_bid_last_low_value,this.Name()))
            this.m_struct_prev_symbol.bid_last_low=this.m_struct_curr_symbol.bid_last_low;
        }
     }
//--- Изменение цены Ask
   if(this.IsPresentEventFlag(SYMBOL_EVENT_FLAG_ASK))
     {
      this.m_changed_ask_value=this.m_struct_curr_symbol.ask-this.m_struct_prev_symbol.ask;
      if(this.m_changed_ask_value>this.m_control_ask_inc)
        {
         this.m_is_change_ask_inc=true;
         event_id=SYMBOL_EVENT_ASK_INC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_ask_value,this.Name()))
            this.m_struct_prev_symbol.ask=this.m_struct_curr_symbol.ask;
        }
      else if(this.m_changed_ask_value<-this.m_control_ask_dec)
        {
         this.m_is_change_ask_dec=true;
         event_id=SYMBOL_EVENT_ASK_DEC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_ask_value,this.Name()))
            this.m_struct_prev_symbol.ask=this.m_struct_curr_symbol.ask;
        }
     }
//--- Изменение максимального Ask за день
   if(this.IsPresentEventFlag(SYMBOL_EVENT_FLAG_ASK_HIGH))
     {
      this.m_changed_ask_high_value=this.m_struct_curr_symbol.ask_high-this.m_struct_prev_symbol.ask_high;
      if(this.m_changed_ask_high_value>this.m_control_ask_high_inc)
        {
         this.m_is_change_ask_high_inc=true;
         event_id=SYMBOL_EVENT_ASK_HIGH_INC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_ask_high_value,this.Name()))
            this.m_struct_prev_symbol.ask_high=this.m_struct_curr_symbol.ask_high;
        }
      else if(this.m_changed_ask_high_value<-this.m_control_ask_high_dec)
        {
         this.m_is_change_ask_high_dec=true;
         event_id=SYMBOL_EVENT_ASK_HIGH_DEC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_ask_high_value,this.Name()))
            this.m_struct_prev_symbol.ask_high=this.m_struct_curr_symbol.ask_high;
        }
     }
//--- Изменение минимального Ask за день
   if(this.IsPresentEventFlag(SYMBOL_EVENT_FLAG_ASK_LOW))
     {
      this.m_changed_ask_low_value=this.m_struct_curr_symbol.ask_low-this.m_struct_prev_symbol.ask_low;
      if(this.m_changed_ask_low_value>this.m_control_ask_low_inc)
        {
         this.m_is_change_ask_low_inc=true;
         event_id=SYMBOL_EVENT_ASK_LOW_INC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_ask_low_value,this.Name()))
            this.m_struct_prev_symbol.ask_low=this.m_struct_curr_symbol.ask_low;
        }
      else if(this.m_changed_ask_low_value<-this.m_control_ask_low_dec)
        {
         this.m_is_change_ask_low_dec=true;
         event_id=SYMBOL_EVENT_ASK_LOW_DEC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_ask_low_value,this.Name()))
            this.m_struct_prev_symbol.ask_low=this.m_struct_curr_symbol.ask_low;
        }
     }
//--- Изменение реального Volume за день
   if(this.IsPresentEventFlag(SYMBOL_EVENT_FLAG_VOLUME_REAL_DAY))
     {
      this.m_changed_volume_real_value=this.m_struct_curr_symbol.volume_real_day-this.m_struct_prev_symbol.volume_real_day;
      if(this.m_changed_volume_real_value>this.m_control_volume_real_inc)
        {
         this.m_is_change_volume_real_inc=true;
         event_id=SYMBOL_EVENT_VOLUME_REAL_DAY_INC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_volume_real_value,this.Name()))
            this.m_struct_prev_symbol.volume_real_day=this.m_struct_curr_symbol.volume_real_day;
        }
      else if(this.m_changed_volume_real_value<-this.m_control_volume_real_dec)
        {
         this.m_is_change_volume_real_dec=true;
         event_id=SYMBOL_EVENT_VOLUME_REAL_DAY_DEC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_volume_real_value,this.Name()))
            this.m_struct_prev_symbol.volume_real_day=this.m_struct_curr_symbol.volume_real_day;
        }
     }
//--- Изменение максимального реального Volume за день
   if(this.IsPresentEventFlag(SYMBOL_EVENT_FLAG_VOLUME_HIGH_REAL_DAY))
     {
      this.m_changed_volume_high_real_day_value=this.m_struct_curr_symbol.volume_high_real_day-this.m_struct_prev_symbol.volume_high_real_day;
      if(this.m_changed_volume_high_real_day_value>this.m_control_volume_high_real_day_inc)
        {
         this.m_is_change_volume_high_real_day_inc=true;
         event_id=SYMBOL_EVENT_VOLUME_HIGH_REAL_DAY_INC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_volume_high_real_day_value,this.Name()))
            this.m_struct_prev_symbol.volume_high_real_day=this.m_struct_curr_symbol.volume_high_real_day;
        }
      else if(this.m_changed_volume_high_real_day_value<-this.m_control_volume_high_real_day_dec)
        {
         this.m_is_change_volume_high_real_day_dec=true;
         event_id=SYMBOL_EVENT_VOLUME_HIGH_REAL_DAY_DEC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_volume_high_real_day_value,this.Name()))
            this.m_struct_prev_symbol.volume_high_real_day=this.m_struct_curr_symbol.volume_high_real_day;
        }
     }
//--- Изменение минимального реального Volume за день
   if(this.IsPresentEventFlag(SYMBOL_EVENT_FLAG_VOLUME_LOW_REAL_DAY))
     {
      this.m_changed_volume_low_real_day_value=this.m_struct_curr_symbol.volume_low_real_day-this.m_struct_prev_symbol.volume_low_real_day;
      if(this.m_changed_volume_low_real_day_value>this.m_control_volume_low_real_day_inc)
        {
         this.m_is_change_volume_low_real_day_inc=true;
         event_id=SYMBOL_EVENT_VOLUME_LOW_REAL_DAY_INC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_volume_low_real_day_value,this.Name()))
            this.m_struct_prev_symbol.volume_low_real_day=this.m_struct_curr_symbol.volume_low_real_day;
        }
      else if(this.m_changed_volume_low_real_day_value<-this.m_control_volume_low_real_day_dec)
        {
         this.m_is_change_volume_low_real_day_dec=true;
         event_id=SYMBOL_EVENT_VOLUME_LOW_REAL_DAY_DEC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_volume_low_real_day_value,this.Name()))
            this.m_struct_prev_symbol.volume_low_real_day=this.m_struct_curr_symbol.volume_low_real_day;
        }
     }
//--- Изменение цены исполнения опциона
   if(this.IsPresentEventFlag(SYMBOL_EVENT_FLAG_OPTION_STRIKE))
     {
      this.m_changed_option_strike_value=this.m_struct_curr_symbol.option_strike-this.m_struct_prev_symbol.option_strike;
      if(this.m_changed_option_strike_value>this.m_control_option_strike_inc)
        {
         this.m_is_change_option_strike_inc=true;
         event_id=SYMBOL_EVENT_OPTION_STRIKE_INC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_option_strike_value,this.Name()))
            this.m_struct_prev_symbol.option_strike=this.m_struct_curr_symbol.option_strike;
        }
      else if(this.m_changed_option_strike_value<-this.m_control_option_strike_dec)
        {
         this.m_is_change_option_strike_dec=true;
         event_id=SYMBOL_EVENT_OPTION_STRIKE_DEC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_option_strike_value,this.Name()))
            this.m_struct_prev_symbol.option_strike=this.m_struct_curr_symbol.option_strike;
        }
     }
//--- Изменение максимально допустимого совокупного объема позиции и отложенных ордеров в одном направлении
   if(this.IsPresentEventFlag(SYMBOL_EVENT_FLAG_VOLUME_LIMIT))
     {
      this.m_changed_volume_limit_value=this.m_struct_curr_symbol.volume_limit-this.m_struct_prev_symbol.volume_limit;
      if(this.m_changed_volume_limit_value>0)
        {
         this.m_is_change_volume_limit_inc=true;
         event_id=SYMBOL_EVENT_VOLUME_LIMIT_INC;
        }
      else
        {
         this.m_is_change_volume_limit_dec=true;
         event_id=SYMBOL_EVENT_VOLUME_LIMIT_DEC;
        }
      if(this.EventAdd(event_id,this.TickTime(),this.m_changed_volume_limit_value,this.Name()))
         this.m_struct_prev_symbol.volume_limit=this.m_struct_curr_symbol.volume_limit;
     }
//--- Изменение свопа длинных позиций
   if(this.IsPresentEventFlag(SYMBOL_EVENT_FLAG_SWAP_LONG))
     {
      this.m_changed_swap_long_value=this.m_struct_curr_symbol.swap_long-this.m_struct_prev_symbol.swap_long;
      if(this.m_changed_swap_long_value>0)
        {
         this.m_is_change_swap_long_inc=true;
         event_id=SYMBOL_EVENT_SWAP_LONG_INC;
        }
      else
        {
         this.m_is_change_swap_long_dec=true;
         event_id=SYMBOL_EVENT_SWAP_LONG_DEC;
        }
      if(this.EventAdd(event_id,this.TickTime(),this.m_changed_swap_long_value,this.Name()))
         this.m_struct_prev_symbol.swap_long=this.m_struct_curr_symbol.swap_long;
     }
//--- Изменение свопа коротких позиций
   if(this.IsPresentEventFlag(SYMBOL_EVENT_FLAG_SWAP_SHORT))
     {
      this.m_changed_swap_short_value=this.m_struct_curr_symbol.swap_short-this.m_struct_prev_symbol.swap_short;
      if(this.m_changed_swap_short_value>0)
        {
         this.m_is_change_swap_short_inc=true;
         event_id=SYMBOL_EVENT_SWAP_SHORT_INC;
        }
      else
        {
         this.m_is_change_swap_short_dec=true;
         event_id=SYMBOL_EVENT_SWAP_SHORT_DEC;
        }
      if(this.EventAdd(event_id,this.TickTime(),this.m_changed_swap_short_value,this.Name()))
         this.m_struct_prev_symbol.swap_short=this.m_struct_curr_symbol.swap_short;
     }
//--- Изменение суммарного объёма сделок в текущую сессию
   if(this.IsPresentEventFlag(SYMBOL_EVENT_FLAG_SESSION_VOLUME))
     {
      this.m_changed_session_volume_value=this.m_struct_curr_symbol.session_volume-this.m_struct_prev_symbol.session_volume;
      if(this.m_changed_session_volume_value>this.m_control_session_volume_inc)
        {
         this.m_is_change_session_volume_inc=true;
         event_id=SYMBOL_EVENT_SESSION_VOLUME_INC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_session_volume_value,this.Name()))
            this.m_struct_prev_symbol.session_volume=this.m_struct_curr_symbol.session_volume;
        }
      else if(this.m_changed_session_volume_value<-this.m_control_session_volume_dec)
        {
         this.m_is_change_session_volume_dec=true;
         event_id=SYMBOL_EVENT_SESSION_VOLUME_DEC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_session_volume_value,this.Name()))
            this.m_struct_prev_symbol.session_volume=this.m_struct_curr_symbol.session_volume;
        }
     }
//--- Изменение суммарного оборота в текущую сессию
   if(this.IsPresentEventFlag(SYMBOL_EVENT_FLAG_SESSION_TURNOVER))
     {
      this.m_changed_session_turnover_value=this.m_struct_curr_symbol.session_turnover-this.m_struct_prev_symbol.session_turnover;
      if(this.m_changed_session_turnover_value>this.m_control_session_turnover_inc)
        {
         this.m_is_change_session_turnover_inc=true;
         event_id=SYMBOL_EVENT_SESSION_TURNOVER_INC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_session_turnover_value,this.Name()))
            this.m_struct_prev_symbol.session_turnover=this.m_struct_curr_symbol.session_turnover;
        }
      else if(this.m_changed_session_turnover_value<-this.m_control_session_turnover_dec)
        {
         this.m_is_change_session_turnover_dec=true;
         event_id=SYMBOL_EVENT_SESSION_TURNOVER_DEC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_session_turnover_value,this.Name()))
            this.m_struct_prev_symbol.session_turnover=this.m_struct_curr_symbol.session_turnover;
        }
     }
//--- Изменение суммарного объёма открытых позиций в текущую сессию
   if(this.IsPresentEventFlag(SYMBOL_EVENT_FLAG_SESSION_INTEREST))
     {
      this.m_changed_session_interest_value=this.m_struct_curr_symbol.session_interest-this.m_struct_prev_symbol.session_interest;
      if(this.m_changed_session_interest_value>this.m_control_session_interest_inc)
        {
         this.m_is_change_session_interest_inc=true;
         event_id=SYMBOL_EVENT_SESSION_INTEREST_INC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_session_interest_value,this.Name()))
            this.m_struct_prev_symbol.session_interest=this.m_struct_curr_symbol.session_interest;
        }
      else if(this.m_changed_session_interest_value<-this.m_control_session_interest_dec)
        {
         this.m_is_change_session_interest_dec=true;
         event_id=SYMBOL_EVENT_SESSION_INTEREST_DEC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_session_interest_value,this.Name()))
            this.m_struct_prev_symbol.session_interest=this.m_struct_curr_symbol.session_interest;
        }
     }
//--- Изменение общего объёма ордеров на покупку в текущий момент
   if(this.IsPresentEventFlag(SYMBOL_EVENT_FLAG_SESSION_BUY_ORD_VOLUME))
     {
      this.m_changed_session_buy_ord_volume_value=this.m_struct_curr_symbol.session_buy_ord_volume-this.m_struct_prev_symbol.session_buy_ord_volume;
      if(this.m_changed_session_buy_ord_volume_value>this.m_control_session_buy_ord_volume_inc)
        {
         this.m_is_change_session_buy_ord_volume_inc=true;
         event_id=SYMBOL_EVENT_SESSION_BUY_ORD_VOLUME_INC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_session_buy_ord_volume_value,this.Name()))
            this.m_struct_prev_symbol.session_buy_ord_volume=this.m_struct_curr_symbol.session_buy_ord_volume;
        }
      else if(this.m_changed_session_buy_ord_volume_value<-this.m_control_session_buy_ord_volume_dec)
        {
         this.m_is_change_session_buy_ord_volume_dec=true;
         event_id=SYMBOL_EVENT_SESSION_BUY_ORD_VOLUME_DEC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_session_buy_ord_volume_value,this.Name()))
            this.m_struct_prev_symbol.session_buy_ord_volume=this.m_struct_curr_symbol.session_buy_ord_volume;
        }
     }
//--- Изменение общего объёма ордеров на продажу в текущий момент
   if(this.IsPresentEventFlag(SYMBOL_EVENT_FLAG_SESSION_SELL_ORD_VOLUME))
     {
      this.m_changed_session_sell_ord_volume_value=this.m_struct_curr_symbol.session_sell_ord_volume-this.m_struct_prev_symbol.session_sell_ord_volume;
      if(this.m_changed_session_sell_ord_volume_value>this.m_control_session_sell_ord_volume_inc)
        {
         this.m_is_change_session_sell_ord_volume_inc=true;
         event_id=SYMBOL_EVENT_SESSION_SELL_ORD_VOLUME_INC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_session_sell_ord_volume_value,this.Name()))
            this.m_struct_prev_symbol.session_sell_ord_volume=this.m_struct_curr_symbol.session_sell_ord_volume;
        }
      else if(this.m_changed_session_sell_ord_volume_value<-this.m_control_session_sell_ord_volume_dec)
        {
         this.m_is_change_session_sell_ord_volume_dec=true;
         event_id=SYMBOL_EVENT_SESSION_SELL_ORD_VOLUME_DEC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_session_sell_ord_volume_value,this.Name()))
            this.m_struct_prev_symbol.session_sell_ord_volume=this.m_struct_curr_symbol.session_sell_ord_volume;
        }
     }
//--- Изменение цены открытия сессии
   if(this.IsPresentEventFlag(SYMBOL_EVENT_FLAG_SESSION_OPEN))
     {
      this.m_changed_session_open_value=this.m_struct_curr_symbol.session_open-this.m_struct_prev_symbol.session_open;
      if(this.m_changed_session_open_value>this.m_control_session_open_inc)
        {
         this.m_is_change_session_open_inc=true;
         event_id=SYMBOL_EVENT_SESSION_OPEN_INC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_session_open_value,this.Name()))
            this.m_struct_prev_symbol.session_open=this.m_struct_curr_symbol.session_open;
        }
      else if(this.m_changed_session_open_value<-this.m_control_session_open_dec)
        {
         this.m_is_change_session_open_dec=true;
         event_id=SYMBOL_EVENT_SESSION_OPEN_DEC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_session_open_value,this.Name()))
            this.m_struct_prev_symbol.session_open=this.m_struct_curr_symbol.session_open;
        }
     }
//--- Изменение цены закрытия сессии
   if(this.IsPresentEventFlag(SYMBOL_EVENT_FLAG_SESSION_CLOSE))
     {
      this.m_changed_session_close_value=this.m_struct_curr_symbol.session_close-this.m_struct_prev_symbol.session_close;
      if(this.m_changed_session_close_value>this.m_control_session_close_inc)
        {
         this.m_is_change_session_close_inc=true;
         event_id=SYMBOL_EVENT_SESSION_CLOSE_INC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_session_close_value,this.Name()))
            this.m_struct_prev_symbol.session_close=this.m_struct_curr_symbol.session_close;
        }
      else if(this.m_changed_session_close_value<-this.m_control_session_close_dec)
        {
         this.m_is_change_session_close_dec=true;
         event_id=SYMBOL_EVENT_SESSION_CLOSE_DEC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_session_close_value,this.Name()))
            this.m_struct_prev_symbol.session_close=this.m_struct_curr_symbol.session_close;
        }
     }
//--- Изменение средневзвешенной цены сессии
   if(this.IsPresentEventFlag(SYMBOL_EVENT_FLAG_SESSION_AW))
     {
      this.m_changed_session_aw_value=this.m_struct_curr_symbol.session_aw-this.m_struct_prev_symbol.session_aw;
      if(this.m_changed_session_aw_value>this.m_control_session_aw_inc)
        {
         this.m_is_change_session_aw_inc=true;
         event_id=SYMBOL_EVENT_SESSION_AW_INC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_session_aw_value,this.Name()))
            this.m_struct_prev_symbol.session_aw=this.m_struct_curr_symbol.session_aw;
        }
      else if(this.m_changed_session_aw_value<-this.m_control_session_aw_dec)
        {
         this.m_is_change_session_aw_dec=true;
         event_id=SYMBOL_EVENT_SESSION_AW_DEC;
         if(this.EventAdd(event_id,this.TickTime(),this.m_changed_session_aw_value,this.Name()))
            this.m_struct_prev_symbol.session_aw=this.m_struct_curr_symbol.session_aw;
        }
     }
  }
//+------------------------------------------------------------------+

Метод достаточно "увесистый", но лишь за счёт блоков идентичных проверок изменения отслеживаемых свойств символа. В части 13 мы уже рассматривали аналогичный метод при отслеживании изменений аккаунта. Здесь логика аналогична, за исключением того, что у нас теперь весь функционал сохранения событий располагается в базовом объекте CBaseObj.
Рассмотрим на примере изменения средневзвешенной цены сессии.
В самом начале метода обнуляем значения отслеживаемых свойств при помощи метода InitChangesParams() и устанавливаем статус события как "Нет события".

  • При помощи метода IsPresentEventFlag(), расположенного так же в базовом объекте, проверяем наличие флага изменения значения SYMBOL_SESSION_AW символа в коде события, записанного в переменной m_event_code.
    Если флаг изменения проверяемого значения есть в коде события, то
    • рассчитываем величину изменения данного свойства и проверяем данное значение на превышение контролируемого значения.
      • Если рассчитанное значение стало больше контролируемого значения, то
        • устанавливаем флаг события увеличения средневзвешенной цены,
        • записываем в идентификатор события "Событие увеличения средневзвешенной цены" и
        • вызываем метод добавления события в список EventAdd() базового класса CBaseObj.
          Если событие успешно добавлено в список, то
          • сохраняем текущее значение свойства как прошлое для дальнейшей проверки.
Для регистрации события уменьшения значения свойства все проверки проводим точно такие же, только проверяем уменьшение значения больше контролируемой величины.
В метод добавления события в список EventAdd() отправляем данные:
  1. идентификатор события (event_id, заполненный при проверке события)
  2. текущее время в милисекундах (метод TickTime() базового класса CBaseObj)
  3. рассчитанное значение, на которое изменилось свойство символа (m_changed_session_aw_value)
  4. имя объекта (в данном случае — имя символа)

В защищённый конструктор класса также внесём небольшое изменение: чтобы у объекта-символа заполнить новое свойство "индекс символа в окне Обзор рынка", нам необходимо передать этот индекс при сканировании символов в обзоре рынка. И передавать мы будем индекс прямо в конструктор класса:

protected:
//--- Защищённый параметрический конструктор
                     CSymbol(ENUM_SYMBOL_STATUS symbol_status,const string name,const int index);

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

//--- Получает и возвращает целочисленные свойства выбранного символа из его параметров
   bool              SymbolExists(const string name)     const;
   long              SymbolExists(void)                  const;
   long              SymbolCustom(void)                  const;
   long              SymbolChartMode(void)               const;
   long              SymbolMarginHedgedUseLEG(void)      const;
   long              SymbolOrderFillingMode(void)        const;
   long              SymbolOrderMode(void)               const;
   long              SymbolExpirationMode(void)          const;
   long              SymbolOrderGTCMode(void)            const;
   long              SymbolOptionMode(void)              const;
   long              SymbolOptionRight(void)             const;
   long              SymbolBackgroundColor(void)         const;
   long              SymbolCalcMode(void)                const;
   long              SymbolSwapMode(void)                const;
   long              SymbolDigitsLot(void);
   int               SymbolDigitsBySwap(void);
//--- Получает и возвращает вещественные свойства выбранного символа из его параметров

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

//+------------------------------------------------------------------+
//| Возвращает количество знаков после запятой                       |
//| в зависимости от метода расчёта свопа                            |
//+------------------------------------------------------------------+
int CSymbol::SymbolDigitsBySwap(void)
  {
   return
     (
      this.SwapMode()==SYMBOL_SWAP_MODE_POINTS           || 
      this.SwapMode()==SYMBOL_SWAP_MODE_REOPEN_CURRENT   || 
      this.SwapMode()==SYMBOL_SWAP_MODE_REOPEN_BID       ?  this.Digits() :
      this.SwapMode()==SYMBOL_SWAP_MODE_CURRENCY_SYMBOL  || 
      this.SwapMode()==SYMBOL_SWAP_MODE_CURRENCY_MARGIN  || 
      this.SwapMode()==SYMBOL_SWAP_MODE_CURRENCY_DEPOSIT ?  this.DigitsCurrency():
      this.SwapMode()==SYMBOL_SWAP_MODE_INTEREST_CURRENT || 
      this.SwapMode()==SYMBOL_SWAP_MODE_INTEREST_OPEN    ?  1  :  0
     );
  }  
//+------------------------------------------------------------------+

Сделаем метод Refresh() виртуальным так как он так же определён в базовом классе всех объектов CBaseObj. А тип метода обновления котировочных данных RefreshRates() поменяем с void на bool — так как теперь в методе Refresh(), в самом его начале будет вызываться метод RefreshRates(), и если не удалось в нём получить данные, то метод вернёт false, и соответственно сразу же будет осуществлён выход и из метода Refresh().
И добавим определение метода, возвращающего описание события символа:

//--- Обновляет все данные символа
   virtual void      Refresh(void);
//--- Обновляет котировочные данные по символу
   bool              RefreshRates(void);
//--- Возвращает описание события символа
   string            EventDescription(const ENUM_SYMBOL_EVENT event);

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

//+------------------------------------------------------------------+
//| Методы упрощённого доступа к свойствам объекта-символа           |
//+------------------------------------------------------------------+
//--- Целочисленные свойства
   long              Status(void)                                 const { return this.GetProperty(SYMBOL_PROP_STATUS);                                      }
   int               IndexInMarketWatch(void)                     const { return (int)this.GetProperty(SYMBOL_PROP_INDEX_MW);                               }
   bool              IsCustom(void)                               const { return (bool)this.GetProperty(SYMBOL_PROP_CUSTOM);                                }
   color             ColorBackground(void)                        const { return (color)this.GetProperty(SYMBOL_PROP_BACKGROUND_COLOR);                     }
   ENUM_SYMBOL_CHART_MODE ChartMode(void)                         const { return (ENUM_SYMBOL_CHART_MODE)this.GetProperty(SYMBOL_PROP_CHART_MODE);          }
   bool              IsExist(void)                                const { return (bool)this.GetProperty(SYMBOL_PROP_EXIST);                                 }
   bool              IsExist(const string name)                   const { return this.SymbolExists(name);                                                   }
   bool              IsSelect(void)                               const { return (bool)this.GetProperty(SYMBOL_PROP_SELECT);                                }
   bool              IsVisible(void)                              const { return (bool)this.GetProperty(SYMBOL_PROP_VISIBLE);                               }
   long              SessionDeals(void)                           const { return this.GetProperty(SYMBOL_PROP_SESSION_DEALS);                               }
   long              SessionBuyOrders(void)                       const { return this.GetProperty(SYMBOL_PROP_SESSION_BUY_ORDERS);                          }
   long              SessionSellOrders(void)                      const { return this.GetProperty(SYMBOL_PROP_SESSION_SELL_ORDERS);                         }
   long              Volume(void)                                 const { return this.GetProperty(SYMBOL_PROP_VOLUME);                                      }
   long              VolumeHigh(void)                             const { return this.GetProperty(SYMBOL_PROP_VOLUMEHIGH);                                  }
   long              VolumeLow(void)                              const { return this.GetProperty(SYMBOL_PROP_VOLUMELOW);                                   }
   datetime          Time(void)                                   const { return (datetime)this.GetProperty(SYMBOL_PROP_TIME);                              }
   int               Digits(void)                                 const { return (int)this.GetProperty(SYMBOL_PROP_DIGITS);                                 }
   int               DigitsLot(void)                              const { return (int)this.GetProperty(SYMBOL_PROP_DIGITS_LOTS);                            }
   int               Spread(void)                                 const { return (int)this.GetProperty(SYMBOL_PROP_SPREAD);                                 }
   bool              IsSpreadFloat(void)                          const { return (bool)this.GetProperty(SYMBOL_PROP_SPREAD_FLOAT);                          }
   int               TicksBookdepth(void)                         const { return (int)this.GetProperty(SYMBOL_PROP_TICKS_BOOKDEPTH);                        }
   ENUM_SYMBOL_CALC_MODE TradeCalcMode(void)                      const { return (ENUM_SYMBOL_CALC_MODE)this.GetProperty(SYMBOL_PROP_TRADE_CALC_MODE);      }
   ENUM_SYMBOL_TRADE_MODE TradeMode(void)                         const { return (ENUM_SYMBOL_TRADE_MODE)this.GetProperty(SYMBOL_PROP_TRADE_MODE);          }
   datetime          StartTime(void)                              const { return (datetime)this.GetProperty(SYMBOL_PROP_START_TIME);                        }
   datetime          ExpirationTime(void)                         const { return (datetime)this.GetProperty(SYMBOL_PROP_EXPIRATION_TIME);                   }
   int               TradeStopLevel(void)                         const { return (int)this.GetProperty(SYMBOL_PROP_TRADE_STOPS_LEVEL);                      }
   int               TradeFreezeLevel(void)                       const { return (int)this.GetProperty(SYMBOL_PROP_TRADE_FREEZE_LEVEL);                     }
   ENUM_SYMBOL_TRADE_EXECUTION TradeExecutionMode(void)           const { return (ENUM_SYMBOL_TRADE_EXECUTION)this.GetProperty(SYMBOL_PROP_TRADE_EXEMODE);  }
   ENUM_SYMBOL_SWAP_MODE SwapMode(void)                           const { return (ENUM_SYMBOL_SWAP_MODE)this.GetProperty(SYMBOL_PROP_SWAP_MODE);            }
   ENUM_DAY_OF_WEEK  SwapRollover3Days(void)                      const { return (ENUM_DAY_OF_WEEK)this.GetProperty(SYMBOL_PROP_SWAP_ROLLOVER3DAYS);        }
   bool              IsMarginHedgedUseLeg(void)                   const { return (bool)this.GetProperty(SYMBOL_PROP_MARGIN_HEDGED_USE_LEG);                 }
   int               ExpirationModeFlags(void)                    const { return (int)this.GetProperty(SYMBOL_PROP_EXPIRATION_MODE);                        }
   int               FillingModeFlags(void)                       const { return (int)this.GetProperty(SYMBOL_PROP_FILLING_MODE);                           }
   int               OrderModeFlags(void)                         const { return (int)this.GetProperty(SYMBOL_PROP_ORDER_MODE);                             }
   ENUM_SYMBOL_ORDER_GTC_MODE OrderModeGTC(void)                  const { return (ENUM_SYMBOL_ORDER_GTC_MODE)this.GetProperty(SYMBOL_PROP_ORDER_GTC_MODE);  }
   ENUM_SYMBOL_OPTION_MODE OptionMode(void)                       const { return (ENUM_SYMBOL_OPTION_MODE)this.GetProperty(SYMBOL_PROP_OPTION_MODE);        }
   ENUM_SYMBOL_OPTION_RIGHT OptionRight(void)                     const { return (ENUM_SYMBOL_OPTION_RIGHT)this.GetProperty(SYMBOL_PROP_OPTION_RIGHT);      }
//--- Вещественные свойства

А в методах, возвращающих вещественные свойства
добавим метод возврата цены Bid или Last, метод возврата максимальной цены Bid или Last за день и метод возврата минимальной цены Bid или Last за день:

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

За пределами тела класса напишем их реализацию:

//+------------------------------------------------------------------+
//| Возвращает цену Bid или Last                                     |
//| в зависимости от метода построения графика                       |
//+------------------------------------------------------------------+
double CSymbol::BidLast(void) const
  {
   return(this.ChartMode()==SYMBOL_CHART_MODE_BID ? this.GetProperty(SYMBOL_PROP_BID) : this.GetProperty(SYMBOL_PROP_LAST));
  }  
//+------------------------------------------------------------------+
//| Возвращает максимальную цену за день Bid или Last                |
//| в зависимости от метода построения графика                       |
//+------------------------------------------------------------------+
double CSymbol::BidLastHigh(void) const
  {
   return(this.ChartMode()==SYMBOL_CHART_MODE_BID ? this.GetProperty(SYMBOL_PROP_BIDHIGH) : this.GetProperty(SYMBOL_PROP_LASTHIGH));
  }  
//+------------------------------------------------------------------+
//| Возвращает минимальную цену за день Bid или Last                 |
//| в зависимости от метода построения графика                       |
//+------------------------------------------------------------------+
double CSymbol::BidLastLow(void) const
  {
   return(this.ChartMode()==SYMBOL_CHART_MODE_BID ? this.GetProperty(SYMBOL_PROP_BIDLOW) : this.GetProperty(SYMBOL_PROP_LASTLOW));
  }
//+------------------------------------------------------------------+

Здесь всё просто: в зависимости от того, по каким ценам строится ценовой график, возвращается соответствующее свойство символа — либо с ценами Bid, либо с ценами Last.

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

//+------------------------------------------------------------------+
//| Получение и установка параметров отслеживаемых изменений         |
//+------------------------------------------------------------------+
   //--- Исполнение
   //--- Флаг изменения режима торговли для символа
   bool              IsChangedTradeMode(void)                              const { return this.m_is_change_trade_mode;                       } 
   //--- Сделки текущей сессии
   //--- установка контролируемой величины (1) прироста, (2) уменьшения количества сделок в текущую сессию
   //--- получение (3) величины изменения количества сделок в текущую сессию,
   //--- получение флага изменения количества сделок в текущую сессию больше, чем на величину (4) прироста, (5) уменьшения
   void              SetControlSessionDealsInc(const long value)                 { this.m_control_session_deals_inc=::fabs(value);           }
   void              SetControlSessionDealsDec(const long value)                 { this.m_control_session_deals_dec=::fabs(value);           }
   long              GetValueChangedSessionDeals(void)                     const { return this.m_changed_session_deals_value;                }
   bool              IsIncreaseSessionDeals(void)                          const { return this.m_is_change_session_deals_inc;                }
   bool              IsDecreaseSessionDeals(void)                          const { return this.m_is_change_session_deals_dec;                }
   //--- Ордера Buy текущей сессии
   //--- установка контролируемой величины (1) прироста, (2) уменьшения количества Buy-ордеров в текущий момент
   //--- получение (3) величины изменения количества Buy-ордеров в текущий момент,
   //--- получение флага изменения количества Buy-ордеров в текущий момент больше, чем на величину (4) прироста, (5) уменьшения
   void              SetControlSessionBuyOrdInc(const long value)                { this.m_control_session_buy_ord_inc=::fabs(value);         }
   void              SetControlSessionBuyOrdDec(const long value)                { this.m_control_session_buy_ord_dec=::fabs(value);         }
   long              GetValueChangedSessionBuyOrders(void)                 const { return this.m_changed_session_buy_ord_value;              }
   bool              IsIncreaseSessionBuyOrders(void)                      const { return this.m_is_change_session_buy_ord_inc;              }
   bool              IsDecreaseSessionBuyOrders(void)                      const { return this.m_is_change_session_buy_ord_dec;              }
   //--- Ордера Sell текущей сессии
   //--- установка контролируемой величины (1) прироста, (2) уменьшения количества Sell-ордеров в текущий момент
   //--- получение (3) величины изменения количества Sell-ордеров в текущий момент,
   //--- получение флага изменения количества Sell-ордеров в текущий момент больше, чем на величину (4) прироста, (5) уменьшения
   void              SetControlSessionSellOrdInc(const long value)               { this.m_control_session_sell_ord_inc=::fabs(value);        }
   void              SetControlSessionSellOrdDec(const long value)               { this.m_control_session_sell_ord_dec=::fabs(value);        }
   long              GetValueChangedSessionSellOrders(void)                const { return this.m_changed_session_sell_ord_value;             }
   bool              IsIncreaseSessionSellOrders(void)                     const { return this.m_is_change_session_sell_ord_inc;             }
   bool              IsDecreaseSessionSellOrders(void)                     const { return this.m_is_change_session_sell_ord_dec;             }
   //--- Объем в последней сделке
   //--- установка контролируемой величины (1) прироста, (2) уменьшения объема в последней сделке
   //--- получение (3) величины изменения объема в последней сделке,
   //--- получение флага изменения объема в последней сделке больше, чем на величину (4) прироста, (5) уменьшения
   void              SetControlVolumeInc(const long value)                       { this.m_control_volume_inc=::fabs(value);                  }
   void              SetControlVolumeDec(const long value)                       { this.m_control_volume_dec=::fabs(value);                  }
   long              GetValueChangedVolume(void)                           const { return this.m_changed_volume_value;                       }
   bool              IsIncreaseVolume(void)                                const { return this.m_is_change_volume_inc;                       }
   bool              IsDecreaseVolume(void)                                const { return this.m_is_change_volume_dec;                       }
   //--- Максимальный Volume за день
   //--- установка контролируемой величины (1) прироста, (2) уменьшения максимального объема за день
   //--- получение (3) величины изменения максимального объема за день,
   //--- получение флага изменения максимального объема за день больше, чем на величину (4) прироста, (5) уменьшения
   void              SetControlVolumeHighInc(const long value)                   { this.m_control_volume_high_day_inc=::fabs(value);         }
   void              SetControlVolumeHighDec(const long value)                   { this.m_control_volume_high_day_dec=::fabs(value);         }
   long              GetValueChangedVolumeHigh(void)                       const { return this.m_changed_volume_high_day_value;              }
   bool              IsIncreaseVolumeHigh(void)                            const { return this.m_is_change_volume_high_day_inc;              }
   bool              IsDecreaseVolumeHigh(void)                            const { return this.m_is_change_volume_high_day_dec;              }
   //--- Минимальный Volume за день
   //--- установка контролируемой величины (1) прироста, (2) уменьшения минимального объема за день
   //--- получение (3) величины изменения минимального объема за день,
   //--- получение флага изменения минимального объема за день больше, чем на величину (4) прироста, (5) уменьшения
   void              SetControlVolumeLowInc(const long value)                    { this.m_control_volume_low_day_inc=::fabs(value);          }
   void              SetControlVolumeLowDec(const long value)                    { this.m_control_volume_low_day_dec=::fabs(value);          }
   long              GetValueChangedVolumeLow(void)                        const { return this.m_changed_volume_low_day_value;               }
   bool              IsIncreaseVolumeLow(void)                             const { return this.m_is_change_volume_low_day_inc;               }
   bool              IsDecreaseVolumeLow(void)                             const { return this.m_is_change_volume_low_day_dec;               }
   //--- Спред
   //--- установка контролируемой величины (1) прироста, (2) уменьшения спреда в пунктах
   //--- получение (3) величины изменения спреда в пунктах,
   //--- получение флага изменения спреда в пунктах больше, чем на величину (4) прироста, (5) уменьшения
   void              SetControlSpreadInc(const int value)                        { this.m_control_spread_inc=::fabs(value);                  }
   void              SetControlSpreadDec(const int value)                        { this.m_control_spread_dec=::fabs(value);                  }
   int               GetValueChangedSpread(void)                           const { return this.m_changed_spread_value;                       }
   bool              IsIncreaseSpread(void)                                const { return this.m_is_change_spread_inc;                       }
   bool              IsDecreaseSpread(void)                                const { return this.m_is_change_spread_dec;                       }
   //--- StopLevel
   //--- установка контролируемой величины (1) прироста, (2) уменьшения StopLevel в пунктах
   //--- получение (3) величины изменения StopLevel в пунктах,
   //--- получение флага изменения StopLevel в пунктах больше, чем на величину (4) прироста, (5) уменьшения
   void              SetControlStopLevelInc(const int value)                     { this.m_control_stops_level_inc=::fabs(value);             }
   void              SetControlStopLevelDec(const int value)                     { this.m_control_stops_level_dec=::fabs(value);             }
   int               GetValueChangedStopLevel(void)                        const { return this.m_changed_stops_level_value;                  }
   bool              IsIncreaseStopLevel(void)                             const { return this.m_is_change_stops_level_inc;                  }
   bool              IsDecreaseStopLevel(void)                             const { return this.m_is_change_stops_level_dec;                  }
   //--- Дистанция заморозки
   //--- установка контролируемой величины (1) прироста, (2) уменьшения FreezeLevel в пунктах
   //--- получение (3) величины изменения FreezeLevel в пунктах,
   //--- получение флага изменения FreezeLevel в пунктах больше, чем на величину (4) прироста, (5) уменьшения
   void              SetControlFreezeLevelInc(const int value)                   { this.m_control_freeze_level_inc=::fabs(value);            }
   void              SetControlFreezeLevelDec(const int value)                   { this.m_control_freeze_level_dec=::fabs(value);            }
   int               GetValueChangedFreezeLevel(void)                      const { return this.m_changed_freeze_level_value;                 }
   bool              IsIncreaseFreezeLevel(void)                           const { return this.m_is_change_freeze_level_inc;                 }
   bool              IsDecreaseFreezeLevel(void)                           const { return this.m_is_change_freeze_level_dec;                 }
   
   //--- Bid/Last
   //--- установка контролируемой величины (1) прироста, (2) уменьшения цены Bid или Last
   //--- получение (3) величины изменения цены Bid или Last,
   //--- получение флага изменения цены Bid или Last больше, чем на величину (4) прироста, (5) уменьшения
   void              SetControlBidLastInc(const double value)                    { this.m_control_bid_last_inc=::fabs(value);                }
   void              SetControlBidLastDec(const double value)                    { this.m_control_bid_last_dec=::fabs(value);                }
   double            GetValueChangedBidLast(void)                          const { return this.m_changed_bid_last_value;                     }
   bool              IsIncreaseBidLast(void)                               const { return this.m_is_change_bid_last_inc;                     }
   bool              IsDecreaseBidLast(void)                               const { return this.m_is_change_bid_last_dec;                     }
   //--- Максимальный Bid/Last за день
   //--- установка контролируемой величины (1) прироста, (2) уменьшения максимального Bid или Last
   //--- получение (3) величины изменения максимального Bid или Last,
   //--- получение флага изменения максимального Bid или Last больше, чем на величину (4) прироста, (5) уменьшения
   void              SetControlBidLastHighInc(const double value)                { this.m_control_bid_last_high_inc=::fabs(value);           }
   void              SetControlBidLastHighDec(const double value)                { this.m_control_bid_last_high_dec=::fabs(value);           }
   double            GetValueChangedBidLastHigh(void)                      const { return this.m_changed_bid_last_high_value;                }
   bool              IsIncreaseBidLastHigh(void)                           const { return this.m_is_change_bid_last_high_inc;                }
   bool              IsDecreaseBidLastHigh(void)                           const { return this.m_is_change_bid_last_high_dec;                }
   //--- Минимальный Bid/Last за день
   //--- установка контролируемой величины (1) прироста, (2) уменьшения минимального Bid или Last
   //--- получение (3) величины изменения минимального Bid или Last,
   //--- получение флага изменения минимального Bid или Last больше, чем на величину (4) прироста, (5) уменьшения
   void              SetControlBidLastLowInc(const double value)                 { this.m_control_bid_last_low_inc=::fabs(value);            }
   void              SetControlBidLastLowDec(const double value)                 { this.m_control_bid_last_low_dec=::fabs(value);            }
   double            GetValueChangedBidLastLow(void)                       const { return this.m_changed_bid_last_low_value;                 }
   bool              IsIncreaseBidLastLow(void)                            const { return this.m_is_change_bid_last_low_inc;                 }
   bool              IsDecreaseBidLastLow(void)                            const { return this.m_is_change_bid_last_low_dec;                 }
   //--- Ask
   //--- установка контролируемой величины (1) прироста, (2) уменьшения цены Ask
   //--- получение (3) величины изменения цены Ask,
   //--- получение флага изменения цены Ask больше, чем на величину (4) прироста, (5) уменьшения
   void              SetControlAskInc(const double value)                        { this.m_control_ask_inc=::fabs(value);                     }
   void              SetControlAskDec(const double value)                        { this.m_control_ask_dec=::fabs(value);                     }
   double            GetValueChangedAsk(void)                              const { return this.m_changed_ask_value;                          }
   bool              IsIncreaseAsk(void)                                   const { return this.m_is_change_ask_inc;                          }
   bool              IsDecreaseAsk(void)                                   const { return this.m_is_change_ask_dec;                          }
   //--- Максимальный Ask за день
   //--- установка контролируемой величины (1) прироста, (2) уменьшения максимального Ask за день
   //--- получение (3) величины изменения максимального Ask за день,
   //--- получение флага изменения максимального Ask за день больше, чем на величину (4) прироста, (5) уменьшения
   void              SetControlAskHighInc(const double value)                    { this.m_control_ask_high_inc=::fabs(value);                }
   void              SetControlAskHighDec(const double value)                    { this.m_control_ask_high_dec=::fabs(value);                }
   double            GetValueChangedAskHigh(void)                          const { return this.m_changed_ask_high_value;                     }
   bool              IsIncreaseAskHigh(void)                               const { return this.m_is_change_ask_high_inc;                     }
   bool              IsDecreaseAskHigh(void)                               const { return this.m_is_change_ask_high_dec;                     }
   //--- Минимальный Ask за день
   //--- установка контролируемой величины (1) прироста, (2) уменьшения минимального Ask за день
   //--- получение (3) величины изменения минимального Ask за день,
   //--- получение флага изменения минимального Ask за день больше, чем на величину (4) прироста, (5) уменьшения
   void              SetControlAskLowInc(const double value)                     { this.m_control_ask_low_inc=::fabs(value);                 }
   void              SetControlAskLowDec(const double value)                     { this.m_control_ask_low_dec=::fabs(value);                 }
   double            GetValueChangedAskLow(void)                           const { return this.m_changed_ask_low_value;                      }
   bool              IsIncreaseAskLow(void)                                const { return this.m_is_change_ask_low_inc;                      }
   bool              IsDecreaseAskLow(void)                                const { return this.m_is_change_ask_low_dec;                      }
   //--- Реальный Volume за день
   //--- установка контролируемой величины (1) прироста, (2) уменьшения реального Volume за день
   //--- получение (3) величины изменения реального Volume за день,
   //--- получение флага изменения реального Volume за день больше, чем на величину (4) прироста, (5) уменьшения
   void              SetControlVolumeRealInc(const double value)                 { this.m_control_volume_real_inc=::fabs(value);             }
   void              SetControlVolumeRealDec(const double value)                 { this.m_control_volume_real_dec=::fabs(value);             }
   double            GetValueChangedVolumeReal(void)                       const { return this.m_changed_volume_real_value;                  }
   bool              IsIncreaseVolumeReal(void)                            const { return this.m_is_change_volume_real_inc;                  }
   bool              IsDecreaseVolumeReal(void)                            const { return this.m_is_change_volume_real_dec;                  }
   //--- Максимальный реальный Volume за день
   //--- установка контролируемой величины (1) прироста, (2) уменьшения максимального реального Volume за день
   //--- получение (3) величины изменения максимального реального Volume за день,
   //--- получение флага изменения максимального реального Volume за день больше, чем на величину (4) прироста, (5) уменьшения
   void              SetControlVolumeHighRealInc(const double value)             { this.m_control_volume_high_real_day_inc=::fabs(value);    }
   void              SetControlVolumeHighRealDec(const double value)             { this.m_control_volume_high_real_day_dec=::fabs(value);    }
   double            GetValueChangedVolumeHighReal(void)                   const { return this.m_changed_volume_high_real_day_value;         }
   bool              IsIncreaseVolumeHighReal(void)                        const { return this.m_is_change_volume_high_real_day_inc;         }
   bool              IsDecreaseVolumeHighReal(void)                        const { return this.m_is_change_volume_high_real_day_dec;         }
   //--- Минимальный реальный Volume за день
   //--- установка контролируемой величины (1) прироста, (2) уменьшения минимального реального Volume за день
   //--- получение (3) величины изменения минимального реального Volume за день,
   //--- получение флага изменения минимального реального Volume за день больше, чем на величину (4) прироста, (5) уменьшения
   void              SetControlVolumeLowRealInc(const double value)              { this.m_control_volume_low_real_day_inc=::fabs(value);     }
   void              SetControlVolumeLowRealDec(const double value)              { this.m_control_volume_low_real_day_dec=::fabs(value);     }
   double            GetValueChangedVolumeLowReal(void)                    const { return this.m_changed_volume_low_real_day_value;          }
   bool              IsIncreaseVolumeLowReal(void)                         const { return this.m_is_change_volume_low_real_day_inc;          }
   bool              IsDecreaseVolumeLowReal(void)                         const { return this.m_is_change_volume_low_real_day_dec;          }
   //--- Цена исполнения опциона
   //--- установка контролируемой величины (1) прироста, (2) уменьшения цены исполнения опциона
   //--- получение (3) величины изменения цены исполнения опциона,
   //--- получение флага изменения цены исполнения опциона больше, чем на величину (4) прироста, (5) уменьшения
   void              SetControlOptionStrikeInc(const double value)               { this.m_control_option_strike_inc=::fabs(value);           }
   void              SetControlOptionStrikeDec(const double value)               { this.m_control_option_strike_dec=::fabs(value);           }
   double            GetValueChangedOptionStrike(void)                     const { return this.m_changed_option_strike_value;                } 
   bool              IsIncreaseOptionStrike(void)                          const { return this.m_is_change_option_strike_inc;                }
   bool              IsDecreaseOptionStrike(void)                          const { return this.m_is_change_option_strike_dec;                }
   //--- Максимально-допустимый совокупный объём позиций и ордеров в одном направлении
   //--- (1) получение величины изменения максимально-допустимого совокупного объёма позиций и ордеров в одном направлении,
   //--- получение флага (2) увеличения, (3) уменьшения максимально-допустимого совокупного объёма позиций и ордеров в одном направлении
   double            GetValueChangedVolumeLimit(void)                      const { return this.m_changed_volume_limit_value;                 }
   bool              IsIncreaseVolumeLimit(void)                           const { return this.m_is_change_volume_limit_inc;                 }
   bool              IsDecreaseVolumeLimit(void)                           const { return this.m_is_change_volume_limit_dec;                 }
   //---  Своп на покупку
   //--- (1) получение величины изменения свопа на покупку,
   //--- получение флага (2) увеличения, (3) уменьшения свопа на покупку
   double            GetValueChangedSwapLong(void)                         const { return this.m_changed_swap_long_value;                    }
   bool              IsIncreaseSwapLong(void)                              const { return this.m_is_change_swap_long_inc;                    }
   bool              IsDecreaseSwapLong(void)                              const { return this.m_is_change_swap_long_dec;                    }
   //---  Своп на продажу
   //--- (1) получение величины изменения свопа на продажу,
   //--- получение флага (2) увеличения, (3) уменьшения свопа на продажу
   double            GetValueChangedSwapShort(void)                        const { return this.m_changed_swap_short_value;                   }
   bool              IsIncreaseSwapShort(void)                             const { return this.m_is_change_swap_short_inc;                   }
   bool              IsDecreaseSwapShort(void)                             const { return this.m_is_change_swap_short_dec;                   }
   //--- Cуммарный объём сделок в текущую сессию
   //--- установка контролируемой величины (1) прироста, (2) уменьшения суммарного объёма сделок в текущую сессию
   //--- получение (3) величины изменения суммарного объёма сделок в текущую сессию,
   //--- получение флага изменения суммарного объёма сделок в текущую сессию больше, чем на величину (4) прироста, (5) уменьшения
   void              SetControlSessionVolumeInc(const double value)              { this.m_control_session_volume_inc=::fabs(value);          }
   void              SetControlSessionVolumeDec(const double value)              { this.m_control_session_volume_dec=::fabs(value);          }
   double            GetValueChangedSessionVolume(void)                    const { return this.m_changed_session_volume_value;               }
   bool              IsIncreaseSessionVolume(void)                         const { return this.m_is_change_session_volume_inc;               }
   bool              IsDecreaseSessionVolume(void)                         const { return this.m_is_change_session_volume_dec;               }
   //--- Cуммарный оборот в текущую сессию
   //--- установка контролируемой величины (1) прироста, (2) уменьшения суммарного оборота в текущую сессию
   //--- получение (3) величины изменения суммарного оборота в текущую сессию,
   //--- получение флага изменения суммарного оборота в текущую сессию больше, чем на величину (4) прироста, (5) уменьшения
   void              SetControlSessionTurnoverInc(const double value)            { this.m_control_session_turnover_inc=::fabs(value);        }
   void              SetControlSessionTurnoverDec(const double value)            { this.m_control_session_turnover_dec=::fabs(value);        }
   double            GetValueChangedSessionTurnover(void)                  const { return this.m_changed_session_turnover_value;             }
   bool              IsIncreaseSessionTurnover(void)                       const { return this.m_is_change_session_turnover_inc;             }
   bool              IsDecreaseSessionTurnover(void)                       const { return this.m_is_change_session_turnover_dec;             }
   //--- Cуммарный объём открытых позиций
   //--- установка контролируемой величины (1) прироста, (2) уменьшения суммарного объёма открытых позиций в текущую сессию
   //--- получение (3) величины изменения суммарного объёма открытых позиций в текущую сессию,
   //--- получение флага изменения суммарного объёма открытых позиций в текущую сессию больше, чем на величину (4) прироста, (5) уменьшения
   void              SetControlSessionInterestInc(const double value)            { this.m_control_session_interest_inc=::fabs(value);        }
   void              SetControlSessionInterestDec(const double value)            { this.m_control_session_interest_dec=::fabs(value);        }
   double            GetValueChangedSessionInterest(void)                  const { return this.m_changed_session_interest_value;             }
   bool              IsIncreaseSessionInterest(void)                       const { return this.m_is_change_session_interest_inc;             }
   bool              IsDecreaseSessionInterest(void)                       const { return this.m_is_change_session_interest_dec;             }
   //--- Общий объём ордеров на покупку в текущий момент
   //--- установка контролируемой величины (1) прироста, (2) уменьшения общего объёма ордеров на покупку в текущий момент
   //--- получение (3) величины изменения общего объёма ордеров на покупку в текущий момент,
   //--- получение флага изменения общего объёма ордеров на покупку в текущий момент больше, чем на величину (4) прироста, (5) уменьшения
   void              SetControlSessionBuyOrdVolumeInc(const double value)        { this.m_control_session_buy_ord_volume_inc=::fabs(value);  }
   void              SetControlSessionBuyOrdVolumeDec(const double value)        { this.m_control_session_buy_ord_volume_dec=::fabs(value);  }
   double            GetValueChangedSessionBuyOrdVolume(void)              const { return this.m_changed_session_buy_ord_volume_value;       }
   bool              IsIncreaseSessionBuyOrdVolume(void)                   const { return this.m_is_change_session_buy_ord_volume_inc;       }
   bool              IsDecreaseSessionBuyOrdVolume(void)                   const { return this.m_is_change_session_buy_ord_volume_dec;       }
   //--- Общий объём ордеров на продажу в текущий момент
   //--- установка контролируемой величины (1) прироста, (2) уменьшения общего объёма ордеров на продажу в текущий момент
   //--- получение (3) величины изменения общего объёма ордеров на продажу в текущий момент,
   //--- получение флага изменения общего объёма ордеров на продажу в текущий момент больше, чем на величину (4) прироста, (5) уменьшения
   void              SetControlSessionSellOrdVolumeInc(const double value)       { this.m_control_session_sell_ord_volume_inc=::fabs(value); }
   void              SetControlSessionSellOrdVolumeDec(const double value)       { this.m_control_session_sell_ord_volume_dec=::fabs(value); }
   double            GetValueChangedSessionSellOrdVolume(void)             const { return this.m_changed_session_sell_ord_volume_value;      }
   bool              IsIncreaseSessionSellOrdVolume(void)                  const { return this.m_is_change_session_sell_ord_volume_inc;      }
   bool              IsDecreaseSessionSellOrdVolume(void)                  const { return this.m_is_change_session_sell_ord_volume_dec;      }
   //--- Цена открытия сессии
   //--- установка контролируемой величины (1) прироста, (2) уменьшения цены открытия сессии
   //--- получение (3) величины изменения цены открытия сессии,
   //--- получение флага изменения цены открытия сессии больше, чем на величину (4) прироста, (5) уменьшения
   void              SetControlSessionPriceOpenInc(const double value)           { this.m_control_session_open_inc=::fabs(value);            }
   void              SetControlSessionPriceOpenDec(const double value)           { this.m_control_session_open_dec=::fabs(value);            }
   double            GetValueChangedSessionPriceOpen(void)                 const { return this.m_changed_session_open_value;                 }
   bool              IsIncreaseSessionPriceOpen(void)                      const { return this.m_is_change_session_open_inc;                 }
   bool              IsDecreaseSessionPriceOpen(void)                      const { return this.m_is_change_session_open_dec;                 }
   //--- Цена закрытия сессии
   //--- установка контролируемой величины (1) прироста, (2) уменьшения цены закрытия сессии
   //--- получение (3) величины изменения цены закрытия сессии,
   //--- получение флага изменения цены закрытия сессии больше, чем на величину (4) прироста, (5) уменьшения
   void              SetControlSessionPriceCloseInc(const double value)          { this.m_control_session_close_inc=::fabs(value);           }
   void              SetControlSessionPriceCloseDec(const double value)          { this.m_control_session_close_dec=::fabs(value);           }
   double            GetValueChangedSessionPriceClose(void)                const { return this.m_changed_session_close_value;                }
   bool              IsIncreaseSessionPriceClose(void)                     const { return this.m_is_change_session_close_inc;                }
   bool              IsDecreaseSessionPriceClose(void)                     const { return this.m_is_change_session_close_dec;                }
   //--- Средневзвешенная цена сессии
   //--- установка контролируемой величины (1) прироста, (2) уменьшения средневзвешенной цены сессии
   //--- получение (3) величины изменения средневзвешенной цены сессии,
   //--- получение флага изменения средневзвешенной цены сессии больше, чем на величину (4) прироста, (5) уменьшения
   void              SetControlSessionPriceAWInc(const double value)             { this.m_control_session_aw_inc=::fabs(value);              }
   void              SetControlSessionPriceAWDec(const double value)             { this.m_control_session_aw_dec=::fabs(value);              }
   double            GetValueChangedSessionPriceAW(void)                   const { return this.m_changed_session_aw_value;                   }
   bool              IsIncreaseSessionPriceAW(void)                        const { return this.m_is_change_session_aw_inc;                   }
   bool              IsDecreaseSessionPriceAW(void)                        const { return this.m_is_change_session_aw_dec;                   }
//---

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

В конструктор класса внесены некоторые изменения:

//+------------------------------------------------------------------+
//| Закрытый параметрический конструктор                             |
//+------------------------------------------------------------------+
CSymbol::CSymbol(ENUM_SYMBOL_STATUS symbol_status,const string name,const int index)
  {
   this.m_name=name;
   if(!this.Exist())
     {
      ::Print(DFUN_ERR_LINE,"\"",this.m_name,"\"",": ",TextByLanguage("Ошибка. Такого символа нет на сервере","Error. There is no such symbol on the server"));
      this.m_global_error=ERR_MARKET_UNKNOWN_SYMBOL;
     }
   bool select=::SymbolInfoInteger(this.m_name,SYMBOL_SELECT);
   ::ResetLastError();
   if(!select)
     {
      if(!this.SetToMarketWatch())
        {
         this.m_global_error=::GetLastError();
         ::Print(DFUN_ERR_LINE,"\"",this.m_name,"\": ",TextByLanguage("Не удалось поместить в обзор рынка. Ошибка: ","Failed to put in the market watch. Error: "),this.m_global_error);
        }
     }
   ::ResetLastError();
   if(!::SymbolInfoTick(this.m_name,this.m_tick))
     {
      this.m_global_error=::GetLastError();
      ::Print(DFUN_ERR_LINE,"\"",this.m_name,"\": ",TextByLanguage("Не удалось получить текущие цены. Ошибка: ","Could not get current prices. Error: "),this.m_global_error);
     }
//--- Инициализация данных
   this.Reset();
   this.InitMarginRates();
   ::ZeroMemory(this.m_struct_prev_symbol);
   this.m_struct_prev_symbol.trade_mode=WRONG_VALUE;
   this.InitChangesParams();
   this.InitControlsParams();
#ifdef __MQL5__
   ::ResetLastError();
   if(!this.MarginRates())
     {
      this.m_global_error=::GetLastError();
      ::Print(DFUN_ERR_LINE,this.Name(),": ",TextByLanguage("Не удалось получить коэффициенты взимания маржи. Ошибка: ","Failed to get margin rates. Error: "),this.m_global_error);
      return;
     }
#endif 
   
//--- Сохранение целочисленных свойств
   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();
  }
//+------------------------------------------------------------------+

Теперь в конструктор передаётся индекс символа в окне "Обзор рынка", наименованию объекта присваивается имя символаобнуляется структура прошлых данных символа, в поле структуры прошлых данных trade_mode записывается значение WRONG_VALUE — по наличию этого значения в поле режима торговли символа, будем определять первый запуск, инициализируются переменные изменяемых и контролируемых свойств символа, и в свойство символа "индекс в окне Обзор рынка" записываем переданный в конструктор индекс.

Так как у нас появилась переменная m_name в базовом классе CBaseObj, в которую мы будем записывать наименование объекта, а в данном случае — имя символа, то из класса CSymbol необходимо удалить переменную m_symbol_name, и все её вхождения заменить на m_name (присвоение имени символа у нас выполняется в конструкторе класса).
Выделим любое из вхождений текста "this.m_symbol_name" в листинге класса CSymbol и нажмём сочетание клавишь Ctrl+H. Откроется окно поиска и замены, где в поле искомого значения уже будет введено выделенное нами в тексте вхождение. В поле замены введём this.m_name и заменим в листинге все найденные вхождения this.m_symbol_name на this.m_name.
Также нам нужно удалить остальные переменные-члены класса, которые теперь находятся в базовом объекте CBaseObj, и их нахождение в CSymbol вызывает при компиляции предупреждение о дублировании переменной. Впрочем, в приложенных к статье файлах уже все такие переменные удалены — можно просто загрузить файлы и ознакомиться с их содержимым.

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

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

Удалим из листинга реализации методов класса CSymbol вторую форму метода Exist() и метод, возвращающий количество знаков после запятой в значении числа:

//+------------------------------------------------------------------+
bool CSymbol::Exist(const string name) const
  {
   int total=::SymbolsTotal(false);
   for(int i=0;i<total;i++)
      if(::SymbolName(i,false)==name)
         return true;
   return false;
  }
//+------------------------------------------------------------------+
//| Возвращает количество знаков после запятой в double-значении     |
//+------------------------------------------------------------------+
int CSymbol::GetDigits(const double value) const
  {
   string val_str=(string)value;
   int len=::StringLen(val_str);
   int n=len-::StringFind(val_str,".",0)-1;
   if(::StringSubstr(val_str,len-1,1)=="0")
      n--;
   return n;
  }
//+------------------------------------------------------------------+

Эти методы здесь просто лишние — Exist() с входным параметром в данном классе не нужен и был перенесён в файл сервисных функций DELib.mqh, где ему как раз самое место, а GetDigits() теперь располагается в базовом классе CBaseObj.

Метод Refresh() класса CSymbol запускается у нас из таймера и обновляет все данные символа. В этом же методе будем осуществлять поиск изменений свойств символа. Но у нас есть ещё один метод — RefreshRates(), который так же запускается из таймера, но с большей частотой обновления, и в этом методе обновляются лишь котировочные данные символа. Если поиск изменений свойств символа сделать в обоих методах, то это приведёт к задваиванию сообщений о событиях.
Решением в данной ситуации будет таким: метод RefreshRates() будет обновлять котировочные данные и возвращать флаг успешности их получения. И вызываться этот метод будет так же, как и раньше — из своего таймера, но в дополнение ещё и из метода Refresh(). Таким образом у нас будут как и ранее вызываться оба метода — каждый в своём таймере, а поиск изменений свойств символа будет только в методе Refresh().

Внесём необходимые изменения в методы RefreshRates() и Refresh():

//+------------------------------------------------------------------+
//| Обновляет котировочные данные по символу                         |
//+------------------------------------------------------------------+
bool CSymbol::RefreshRates(void)
  {
//--- Получение котировочных данных
   ::ResetLastError();
   if(!::SymbolInfoTick(this.m_name,this.m_tick))
     {
      this.m_global_error=::GetLastError();
      return false;
     }
//--- Обновление целочисленных свойств
   this.m_long_prop[SYMBOL_PROP_VOLUME]                                             = (long)this.m_tick.volume;
   this.m_long_prop[SYMBOL_PROP_TIME]                                               = this.TickTime();
//--- Обновление вещественных свойств
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_ASK)]                              = this.m_tick.ask;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_ASKHIGH)]                          = ::SymbolInfoDouble(this.m_name,SYMBOL_ASKHIGH);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_ASKLOW)]                           = ::SymbolInfoDouble(this.m_name,SYMBOL_ASKLOW);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_BID)]                              = this.m_tick.bid;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_BIDHIGH)]                          = this.SymbolBidHigh();
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_BIDLOW)]                           = this.SymbolBidLow();
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_LAST)]                             = this.m_tick.last;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_LASTHIGH)]                         = ::SymbolInfoDouble(this.m_name,SYMBOL_LASTHIGH);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_LASTLOW)]                          = ::SymbolInfoDouble(this.m_name,SYMBOL_LASTLOW);
   return true;
  }  
//+------------------------------------------------------------------+

Здесь сначала получаем котировочные данные по символу, и если данные получить не удалось, то записываем код ошибки и выходим из метода с возвратом false. Если данные получить удалось, то заполняем нужные свойства символа и возвращаем true.

//+------------------------------------------------------------------+
//| Обновляет все данные символа                                     |
//+------------------------------------------------------------------+
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;
   ::ZeroMemory(this.m_struct_curr_symbol);
   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;
   
//--- Заполнение текущей структуры данных символа
   this.m_struct_curr_symbol.ask                                                    = this.Ask();
   this.m_struct_curr_symbol.ask_high                                               = this.AskHigh();
   this.m_struct_curr_symbol.ask_low                                                = this.AskLow();
   this.m_struct_curr_symbol.bid_last                                               = (this.ChartMode()==SYMBOL_CHART_MODE_BID ? this.Bid() : this.Last());
   this.m_struct_curr_symbol.bid_last_high                                          = (this.ChartMode()==SYMBOL_CHART_MODE_BID ? this.BidHigh() : this.LastHigh());
   this.m_struct_curr_symbol.bid_last_low                                           = (this.ChartMode()==SYMBOL_CHART_MODE_BID ? this.BidLow() : this.LastLow());
   this.m_struct_curr_symbol.volume                                                 = this.Volume();
   this.m_struct_curr_symbol.session_deals                                          = this.SessionDeals();
   this.m_struct_curr_symbol.session_buy_orders                                     = this.SessionBuyOrders();
   this.m_struct_curr_symbol.session_sell_orders                                    = this.SessionSellOrders();
   this.m_struct_curr_symbol.volume_high_day                                        = this.VolumeHigh();
   this.m_struct_curr_symbol.volume_low_day                                         = this.VolumeLow();
   this.m_struct_curr_symbol.spread                                                 = this.Spread();
   this.m_struct_curr_symbol.stops_level                                            = this.TradeStopLevel();
   this.m_struct_curr_symbol.freeze_level                                           = this.TradeFreezeLevel();
   this.m_struct_curr_symbol.volume_limit                                           = this.VolumeLimit();
   this.m_struct_curr_symbol.swap_long                                              = this.SwapLong();
   this.m_struct_curr_symbol.swap_short                                             = this.SwapShort();
   this.m_struct_curr_symbol.session_volume                                         = this.SessionVolume();
   this.m_struct_curr_symbol.session_turnover                                       = this.SessionTurnover();
   this.m_struct_curr_symbol.session_interest                                       = this.SessionInterest();
   this.m_struct_curr_symbol.session_buy_ord_volume                                 = this.SessionBuyOrdersVolume();
   this.m_struct_curr_symbol.session_sell_ord_volume                                = this.SessionSellOrdersVolume();
   this.m_struct_curr_symbol.session_open                                           = this.SessionOpen();
   this.m_struct_curr_symbol.session_close                                          = this.SessionClose();
   this.m_struct_curr_symbol.session_aw                                             = this.SessionAW();
   this.m_struct_curr_symbol.volume_real_day                                        = this.VolumeReal();
   this.m_struct_curr_symbol.volume_high_real_day                                   = this.VolumeHighReal();
   this.m_struct_curr_symbol.volume_low_real_day                                    = this.VolumeLowReal();
   this.m_struct_curr_symbol.option_strike                                          = this.OptionStrike();
   this.m_struct_curr_symbol.trade_mode                                             = this.TradeMode();

//--- Расчёт хэш-суммы
   this.m_hash_sum+=(double)this.m_struct_curr_symbol.volume;
   this.m_hash_sum+=(double)this.m_struct_curr_symbol.session_deals;
   this.m_hash_sum+=(double)this.m_struct_curr_symbol.session_buy_orders;
   this.m_hash_sum+=(double)this.m_struct_curr_symbol.session_sell_orders;
   this.m_hash_sum+=(double)this.m_struct_curr_symbol.volume_high_day;
   this.m_hash_sum+=(double)this.m_struct_curr_symbol.volume_low_day;
   this.m_hash_sum+=(double)this.m_struct_curr_symbol.spread;
   this.m_hash_sum+=(double)this.m_struct_curr_symbol.stops_level;
   this.m_hash_sum+=(double)this.m_struct_curr_symbol.freeze_level;
   this.m_hash_sum+=this.m_struct_curr_symbol.ask;
   this.m_hash_sum+=this.m_struct_curr_symbol.ask_high;
   this.m_hash_sum+=this.m_struct_curr_symbol.ask_low;
   this.m_hash_sum+=this.m_struct_curr_symbol.bid_last;
   this.m_hash_sum+=this.m_struct_curr_symbol.bid_last_high;
   this.m_hash_sum+=this.m_struct_curr_symbol.bid_last_low;
   this.m_hash_sum+=this.m_struct_curr_symbol.volume_limit;
   this.m_hash_sum+=this.m_struct_curr_symbol.swap_long;
   this.m_hash_sum+=this.m_struct_curr_symbol.swap_short;
   this.m_hash_sum+=this.m_struct_curr_symbol.session_volume;
   this.m_hash_sum+=this.m_struct_curr_symbol.session_turnover;
   this.m_hash_sum+=this.m_struct_curr_symbol.session_interest;
   this.m_hash_sum+=this.m_struct_curr_symbol.session_buy_ord_volume;
   this.m_hash_sum+=this.m_struct_curr_symbol.session_sell_ord_volume;
   this.m_hash_sum+=this.m_struct_curr_symbol.session_open;
   this.m_hash_sum+=this.m_struct_curr_symbol.session_close;
   this.m_hash_sum+=this.m_struct_curr_symbol.session_aw;
   this.m_hash_sum+=this.m_struct_curr_symbol.volume_real_day;
   this.m_hash_sum+=this.m_struct_curr_symbol.volume_high_real_day;
   this.m_hash_sum+=this.m_struct_curr_symbol.volume_low_real_day;
   this.m_hash_sum+=this.m_struct_curr_symbol.option_strike;
   this.m_hash_sum+=this.m_struct_curr_symbol.trade_mode;

//--- Первый запуск
   if(this.m_struct_prev_symbol.trade_mode==WRONG_VALUE)
     {
      this.m_struct_prev_symbol=this.m_struct_curr_symbol;
      this.m_hash_sum_prev=this.m_hash_sum;
      return;
     }
//--- Если хэш-сумма символа изменилась
   if(this.m_hash_sum!=this.m_hash_sum_prev)
     {
      this.m_event_code=this.SetEventCode();
      this.SetTypeEvent();
      CEventBaseObj *event=this.GetEvent(WRONG_VALUE,false);
      if(event!=NULL)
        {
         ENUM_SYMBOL_EVENT event_id=(ENUM_SYMBOL_EVENT)event.ID();
         if(event_id!=SYMBOL_EVENT_NO_EVENT)
           {
            this.m_is_event=true;
           }
        }
      this.m_hash_sum_prev=this.m_hash_sum;
     }
  }
//+------------------------------------------------------------------+

Здесь: сначала вызываем метод RefreshRates(), и если не удалось получить котировочные данные, то и остальные данные получать не имеет смысла — выходим из метода.
Далее сбрасываем флаг события, обнуляем структуру текущего состояния свойств символа и обнуляем текущую хэш-сумму. Затем заполняем все свойства символа текущими данными и записываем эти данные в структуру текущего состояния свойств символа. После заполнения структуры рассчитываем текущую хэш-сумму.
Если это превый запуск (свойство символа SYMBOL_TRADE_MODE установлено в значение WRONG_VALUE), то сохраняем в структуру прошлого состояния значения структуры текущего состояния, в прошлую хэш-сумму записываем текущую хэш-сумму и выходим из метода.
Если же это не первый запуск, то нам нужно проверить на неравенство значение прошлой и текущей хэш-сумм.
Если хэш-сумма изменилась
— значит произошло какое-то изменение свойств символа — вызываем метод установки кода события, метод получения события из кода события и записи события в список произошедших событий, получаем последнее событие из только что добавленных событий из списка, проверяем тип события, и если это не "События нет", то взводим флаг события. Наконец сохраняем текущую хэш-сумму как прошлую для дальнейших проверок.

На этом доработка класса CSymbol для поиска событий и работы с новым базовым объектом завершена.

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

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

Итак. Откроем файл класса коллекции символов SymbolsCollection.mqh и внесём необходимые изменения:

//+------------------------------------------------------------------+
//|                                            SymbolsCollection.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                             https://mql5.com/ru/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/ru/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Включаемые файлы                                                 |
//+------------------------------------------------------------------+
#include <Arrays\ArrayString.mqh>
#include "ListObj.mqh"
#include "..\Services\Select.mqh"
#include "..\Objects\Symbols\Symbol.mqh"
#include "..\Objects\Symbols\SymbolFX.mqh"
#include "..\Objects\Symbols\SymbolFXMajor.mqh"
#include "..\Objects\Symbols\SymbolFXMinor.mqh"
#include "..\Objects\Symbols\SymbolFXExotic.mqh"
#include "..\Objects\Symbols\SymbolFXRub.mqh"
#include "..\Objects\Symbols\SymbolMetall.mqh"
#include "..\Objects\Symbols\SymbolIndex.mqh"
#include "..\Objects\Symbols\SymbolIndicative.mqh"
#include "..\Objects\Symbols\SymbolCrypto.mqh"
#include "..\Objects\Symbols\SymbolCommodity.mqh"
#include "..\Objects\Symbols\SymbolExchange.mqh"
#include "..\Objects\Symbols\SymbolFutures.mqh"
#include "..\Objects\Symbols\SymbolCFD.mqh"
#include "..\Objects\Symbols\SymbolStocks.mqh"
#include "..\Objects\Symbols\SymbolBonds.mqh"
#include "..\Objects\Symbols\SymbolOption.mqh"
#include "..\Objects\Symbols\SymbolCollateral.mqh"
#include "..\Objects\Symbols\SymbolCustom.mqh"
#include "..\Objects\Symbols\SymbolCommon.mqh"
//+------------------------------------------------------------------+
//| Коллекция символов                                               |
//+------------------------------------------------------------------+
class CSymbolsCollection : public CBaseObj
  {
private:
   CListObj          m_list_all_symbols;                          // Список всех объектов-символов
   CArrayString      m_list_names;                                // Контрольный список имён символов
   ENUM_SYMBOLS_MODE m_mode_list;                                 // Режим работы со списками символов
   ENUM_SYMBOL_EVENT m_last_event;                                // Последнее событие
   string            m_array_symbols[];                           // Массив используемых символов, передаваемых из программы
   int               m_delta_symbol;                              // Разница в количестве символов по сравнению с прошлой проверкой
   int               m_total_symbols;                             // Количество символов в окне "Обзор рынка"
   int               m_total_symbol_prev;                         // Количество символов в окне "Обзор рынка" на прошлой проверке
//--- Возвращает флаг наличия объекта-символа по его имени (1) в списке всех символов, (2) в окне "Обзор рынка", (3) в контрольном списке
   bool              IsPresentSymbolInList(const string symbol_name);
   bool              IsPresentSymbolInMW(const string symbol_name);
   bool              IsPresentSymbolInControlList(const string symbol_name);
//--- Создаёт объект-символ и помещает его в список
   bool              CreateNewSymbol(const ENUM_SYMBOL_STATUS symbol_status,const string name,const int index);
//--- Возвращает (1) тип используемого списка символов (Обзор рынка/Сервер),
//--- (2) количество видимых символов, (3) индекс символа в окне "Обзор рынка"
   ENUM_SYMBOLS_MODE TypeSymbolsList(const string &symbol_used_array[]);
   int               SymbolsTotalVisible(void)                    const;
   int               SymbolIndexInMW(const string name)           const;
   
//--- Определяет по имени и возвращает принадлежность символа к группе
   ENUM_SYMBOL_STATUS SymbolStatus(const string symbol_name)      const;
//--- Возвращает принадлежность символа к категории по пользовательским критериям
   ENUM_SYMBOL_STATUS StatusByCustomPredefined(const string symbol_name)  const;
//--- Возвращает принадлежность символа к категориям по расчёту залоговых средств
   ENUM_SYMBOL_STATUS StatusByCalcMode(const string symbol_name)  const;
//--- Возвращает принадлежность символа к предопределённым (1) мажорам, (2) минорам, (3) экзотикам, (4) рублёвым,
//--- (5) индикативам, (6) металлам, (7) товарам, (8) индексам, (9) криптовалюте, (10) опционам
   bool              IsPredefinedFXMajor(const string name)       const;
   bool              IsPredefinedFXMinor(const string name)       const;
   bool              IsPredefinedFXExotic(const string name)      const;
   bool              IsPredefinedFXRUB(const string name)         const;
   bool              IsPredefinedIndicative(const string name)    const;
   bool              IsPredefinedMetall(const string name)        const;
   bool              IsPredefinedCommodity(const string name)     const;
   bool              IsPredefinedIndex(const string name)         const;
   bool              IsPredefinedCrypto(const string name)        const;
   bool              IsPredefinedOption(const string name)        const;

public:
//--- Возвращает полный список-коллекцию "как есть"
   CArrayObj        *GetList(void)                                                                          { return &this.m_list_all_symbols;                                      }
//--- Возвращает список по выбранному (1) целочисленному, (2) вещественному и (3) строковому свойству, удовлетворяющему сравниваемому критерию
   CArrayObj        *GetList(ENUM_SYMBOL_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode=EQUAL)    { return CSelect::BySymbolProperty(this.GetList(),property,value,mode); }
   CArrayObj        *GetList(ENUM_SYMBOL_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode=EQUAL)   { return CSelect::BySymbolProperty(this.GetList(),property,value,mode); }
   CArrayObj        *GetList(ENUM_SYMBOL_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode=EQUAL)   { return CSelect::BySymbolProperty(this.GetList(),property,value,mode); }
//--- Возвращает из списка по имени (1) объект-символ, (2) индекс объекта-символа
   CSymbol          *GetSymbolByName(const string name);
   int               GetSymbolIndexByName(const string name);
//--- Возвращает количество новых символов в окне "Обзор рынка"
   int               NewSymbols(void)    const                                                              { return this.m_delta_symbol;                                           }
//--- Возвращает (1) режим работы со списками символов, (2) флаг события и (3) событие одного из символов коллекции
   ENUM_SYMBOLS_MODE ModeSymbolsList(void)                        const                                     { return this.m_mode_list;                                              }
   bool              IsEvent(void)                                const                                     { return this.m_is_event;                                               }
   int               GetLastEventsCode(void)                      const                                     { return this.m_event_code;                                             }
   ENUM_SYMBOL_EVENT GetLastEvent(void)                           const                                     { return this.m_last_event;                                             }
//--- Возвращает количество символов в коллекции
   int               GetSymbolsCollectionTotal(void)              const                                     { return this.m_list_all_symbols.Total();                               }

//--- Конструктор
                     CSymbolsCollection();
   
//--- (1) Устанавливает список используемых символов, (2) создание списка символов (Обзор рынка или полный список)
   bool              SetUsedSymbols(const string &symbol_used_array[]);
   bool              CreateSymbolsList(const bool flag);
//--- Сохраняет имена используемых символов из "Обзора рынка"
   void              CopySymbolsNames(void);
//--- Обновляет (1) все, (2) котировочные данные символов коллекции
   virtual void      Refresh(void);
   void              RefreshRates(void);
//--- Работа с событиями (1) списка символов коллекции, (2) окна обзора рынка
   void              SymbolsEventsControl(void);
   void              MarketWatchEventsControl(const bool send_events=true);
//--- Возвращает описание (1) события окна "Обзор рынка", (2) режима работы с символами
   string            EventDescription(const ENUM_SYMBOL_EVENT event);
   string            ModeSymbolsListDescription(void);
  };
//+------------------------------------------------------------------+

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

В переменную m_last_event будем записывать последнее событие, произошедшее с любым из символов в коллекции. Таким образом в переменной будет храниться самое последнее событие, произошедшее на одном из используемых символов.
Массив m_array_symbols служит для передачи в класс коллекции символов из управляющей программы массива используемых символов в программе.
В переменных-членах класса m_total_symbols и m_total_symbols_prev будем хранить текущее и прошлое количество символов в окне "Обзор рынка". Сравнение значений этих переменных позволит узнать события добавления или удаления символов в обзор рынка.

Приватные методы класса IsPresentSymbolInMW() и IsPresentSymbolInControlList() возвращают флаги наличия символа по его имени в окне "Обзор рынка" и списке-слепке окна обзора рынка соответственно. А методы SymbolsTotalVisible() и SymbolIndexInMW() возвращают количество видимых символов в окне "Обзор рынка" и индекс символа в списке этого окна соответственно.

В публичную секцию класса впишем методы:
IsEvent() — возвращает флаг наличия любого события в коллекции символов или в окне "Обзор рынка",
GetLastEventsCode() — возвращает код последнего события в коллекции символов или в окне "Обзор рынка",
GetLastEvent() — возвращает последнее событие в коллекции символов или в окне "Обзор рынка",
GetSymbolsCollectionTotal() — возвращает общее количество символов в коллекции,
CreateSymbolsList() — создаёт список коллекции при работе с окном "Обзор рынка" или с полным списком символов на сервере (не более 1000),
CopySymbolsNames() — создаёт слепок символов окна "Обзор рынка" из списка всех символов коллекции,
Refresh() — теперь метод объявлен как виртуальный, так как он объявлен виртуальным в классе базового объекта (а коллекция символов теперь создана на основе класса базового объекта CBaseObj),
SymbolsEventsControl() — метод работы со списком коллекции символов для определения событий коллекции символов,
MarketWatchEventsControl() — метод работы со списком окна "Обзор рынка" для определения событий в окне обзора рынка,
EventDescription() — возвращает описание события коллекции символов,
ModeSymbolsListDescription() — возвращает описание режима работы с символами.

Рассмотрим реализацию методов.
В конструкторе класса, в его списке инициализации, инициализируем переменные для работы с окном "Обзор рынка", а в теле класса поменяем сортировку списка символов коллекции с сортировки по имени на сортировку по индексу в окне "Обзор рынка" и очистим список слепка окна обзора рынка.

//+------------------------------------------------------------------+
//| Конструктор                                                      |
//+------------------------------------------------------------------+
CSymbolsCollection::CSymbolsCollection(void) : m_total_symbol_prev(0),
                                               m_delta_symbol(0),
                                               m_mode_list(SYMBOLS_MODE_CURRENT)
  {
   this.m_list_all_symbols.Sort(SORT_BY_SYMBOL_INDEX_MW);
   this.m_list_all_symbols.Clear();
   this.m_list_all_symbols.Type(COLLECTION_SYMBOLS_ID);
   this.m_list_names.Clear();
  }
//+------------------------------------------------------------------+

Так как в окне "Обзор рынка" символ может находиться, но не отображаться (свойство символа SYMBOL_VISIBLE — некоторые символы (как правило, это кросс-курсы, которые необходимы для расчёта маржевых требований и прибыли в валюте депозита) выбираются автоматически, при этом могут не отображаться в Market Watch), то чтобы узнать количество только видимых символов, нам необходимо в цикле по символам окна посчитать только те символы, у которых данное свойство установлено.
Метод SymbolsTotalVisible() возвращает количество видимых символов в окне "Обзор рынка":

//+------------------------------------------------------------------+
//| Возвращает количество видимых символов в окне "Обзор рынка"      |
//+------------------------------------------------------------------+
int CSymbolsCollection::SymbolsTotalVisible(void) const
  {
   int total=::SymbolsTotal(true),n=0;
   for(int i=0;i<total;i++)
     {
      if(!::SymbolInfoInteger(::SymbolName(i,true),SYMBOL_VISIBLE))
         continue;
      n++;
     }
   return n;
  }
//+------------------------------------------------------------------+

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

//+------------------------------------------------------------------+
//| Возвращает индекс символа в окне "Обзор рынка"                   |
//+------------------------------------------------------------------+
int CSymbolsCollection::SymbolIndexInMW(const string name) const
  {
   int total=::SymbolsTotal(true);
   for(int i=0;i<total;i++)
     {
      if(!::SymbolInfoInteger(::SymbolName(i,true),SYMBOL_VISIBLE))
         continue;
      if(SymbolName(i,true)==name)
         return i;
     }
   return WRONG_VALUE;
  }
//+------------------------------------------------------------------+

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

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

//+------------------------------------------------------------------+
//| Возвращает флаг наличия видимого объекта-символа                 |
//| по его имени в окне "Обзор рынка"                                |
//+------------------------------------------------------------------+
bool CSymbolsCollection::IsPresentSymbolInMW(const string symbol_name)
  {
   int total=SymbolsTotal(true);
   for(int i=0;i<total;i++)
     {
      string name=::SymbolName(i,true);
      if(!::SymbolInfoInteger(name,SYMBOL_VISIBLE))
         continue;
      if(name==symbol_name)
         return true;
     }
   return false;
  }
//+------------------------------------------------------------------+

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

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

//+------------------------------------------------------------------+
//| Возвращает флаг наличия объекта-символа в контрольном списке     |
//+------------------------------------------------------------------+
bool CSymbolsCollection::IsPresentSymbolInControlList(const string symbol_name)
  {
   int total=this.m_list_names.Total();
   for(int i=0;i<total;i++)
     {
      string name=this.m_list_names.At(i);
      if(name==NULL)
         continue;
      if(name==symbol_name)
         return true;
     }
   return false;
  }
//+------------------------------------------------------------------+

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

Метод создания списка при работе с окном "Обзор рынка" или с полным списком символов на сервере:

//+------------------------------------------------------------------+
//| Создание списка символов (Обзор рынка или полный список)         |
//+------------------------------------------------------------------+
bool CSymbolsCollection::CreateSymbolsList(const bool flag)
  {
   bool res=true;
   int total=::SymbolsTotal(flag);
   for(int i=0;i<total && i<SYMBOLS_COMMON_TOTAL;i++)
     {
      string name=::SymbolName(i,flag);
      if(flag && !::SymbolInfoInteger(name,SYMBOL_VISIBLE))
         continue;
      ENUM_SYMBOL_STATUS status=this.SymbolStatus(name);
      bool add=this.CreateNewSymbol(status,name,i);
      res &=add;
      if(!add) 
         continue;
     }
   return res;
  }
//+------------------------------------------------------------------+

В метод передаётся флаг, устанавливающий режим поиска: true — работа с выбранными символами в окне обзора рынка, false — работа с полным списком символов, имеющихся на сервере. Получаем общее количество символов в зависимости от флага — либо в окне обзора рынка, либо на сервере, и в цикле по списку, но не более величины, заданной константой SYMBOLS_COMMON_TOTAL в файле Defines.mqh (1000 символов) получаем имя очередного символа из списка, проверяем его свойство видимости при условии работы с окном обзора рынка, и если символ не видим, то пропускаем его.
Затем получаем статус объекта символа по его имени и добавляем символ в список всех символов коллекции при помощи метода CreateNewSymbol(), рассмотренного нами в прошлой статье. (Отметим, что метод немного изменён в связи с добавлением нового свойства символа — его индекса в списке символов окна "Обзор рынка" — теперь в объект-символ передаётся ещё и индекс).
В переменную, возвращающую результат работы метода вносятся результаты добавления каждого символа в список всех символов коллекции
, и итоговое значение этой переменной возвращается из метода по завершении всего цикла обработки символов.

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

//+------------------------------------------------------------------+
//| Создаёт объект-символ и помещает его в список                    |
//+------------------------------------------------------------------+
bool CSymbolsCollection::CreateNewSymbol(const ENUM_SYMBOL_STATUS symbol_status,const string name,const int index)
  {
   if(this.IsPresentSymbolInList(name))
     {
      return true;
     }
   if(#ifdef __MQL5__ !::SymbolInfoInteger(name,SYMBOL_EXIST) #else !Exist(name) #endif )
     {
      string t1=TextByLanguage("Ошибка входных данных: нет символа ","Input error: no ");
      string t2=TextByLanguage(" на сервере"," symbol on the server");
      ::Print(DFUN,t1,name,t2);
      this.m_global_error=ERR_MARKET_UNKNOWN_SYMBOL;
      return false;
     }
   CSymbol *symbol=NULL;
   switch(symbol_status)
     {
      case SYMBOL_STATUS_FX         :  symbol=new CSymbolFX(name,index);         break;   // Форекс символ
      case SYMBOL_STATUS_FX_MAJOR   :  symbol=new CSymbolFXMajor(name,index);    break;   // Форекс символ-мажор
      case SYMBOL_STATUS_FX_MINOR   :  symbol=new CSymbolFXMinor(name,index);    break;   // Форекс символ-минор
      case SYMBOL_STATUS_FX_EXOTIC  :  symbol=new CSymbolFXExotic(name,index);   break;   // Форекс символ-экзотик
      case SYMBOL_STATUS_FX_RUB     :  symbol=new CSymbolFXRub(name,index);      break;   // Форекс символ/рубль
      case SYMBOL_STATUS_METAL      :  symbol=new CSymbolMetall(name,index);     break;   // Металл
      case SYMBOL_STATUS_INDEX      :  symbol=new CSymbolIndex(name,index);      break;   // Индекс
      case SYMBOL_STATUS_INDICATIVE :  symbol=new CSymbolIndicative(name,index); break;   // Индикатив
      case SYMBOL_STATUS_CRYPTO     :  symbol=new CSymbolCrypto(name,index);     break;   // Криптовалютный символ
      case SYMBOL_STATUS_COMMODITY  :  symbol=new CSymbolCommodity(name,index);  break;   // Товарный символ
      case SYMBOL_STATUS_EXCHANGE   :  symbol=new CSymbolExchange(name,index);   break;   // Биржевой символ
      case SYMBOL_STATUS_FUTURES    :  symbol=new CSymbolFutures(name,index);    break;   // Фьючерс
      case SYMBOL_STATUS_CFD        :  symbol=new CSymbolCFD(name,index);        break;   // Контракт на разницу
      case SYMBOL_STATUS_STOCKS     :  symbol=new CSymbolStocks(name,index);     break;   // Ценная бумага
      case SYMBOL_STATUS_BONDS      :  symbol=new CSymbolBonds(name,index);      break;   // Облигация
      case SYMBOL_STATUS_OPTION     :  symbol=new CSymbolOption(name,index);     break;   // Опцион
      case SYMBOL_STATUS_COLLATERAL :  symbol=new CSymbolCollateral(name,index); break;   // Неторгуемый актив
      case SYMBOL_STATUS_CUSTOM     :  symbol=new CSymbolCustom(name,index);     break;   // Пользовательский символ
      default                       :  symbol=new CSymbolCommon(name,index);     break;   // Остальное
     }
   if(symbol==NULL)
     {
      ::Print(DFUN,TextByLanguage("Не удалось создать объект-символ ","Failed to create symbol object "),name);
      return false;
     }
   if(!this.m_list_all_symbols.Add(symbol))
     {
      string t1=TextByLanguage("Не удалось добавить символ ","Failed to add ");
      string t2=TextByLanguage(" в список"," symbol to the list");
      ::Print(DFUN,t1,name,t2);
      delete symbol;
      return false;
     }
   return true;
  }
//+------------------------------------------------------------------+

Как видно из листинга, здесь мы в метод дополнительно передаём индекс, и в каждый из конструкторов классов объектов-символов отправляем вместе с именем символа и этот индекс. Соответственно, требуется доработка каждого из классов-наследников класса абстрактного символа CSymbol.
Рассмотрим эту доработку на примере класса CSymbolFX:

//+------------------------------------------------------------------+
//|                                                     SymbolFX.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                             https://mql5.com/ru/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/ru/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Включаемые файлы                                                 |
//+------------------------------------------------------------------+
#include "Symbol.mqh"
//+------------------------------------------------------------------+
//| Форекс символ                                                    |
//+------------------------------------------------------------------+
class CSymbolFX : public CSymbol
  {
public:
//--- Конструктор
                     CSymbolFX(const string name,const int index) : CSymbol(SYMBOL_STATUS_FX,name,index) {}
//--- Поддерживаемые целочисленные свойства символа
   virtual bool      SupportProperty(ENUM_SYMBOL_PROP_INTEGER property);
//--- Поддерживаемые вещественные свойства символа
   virtual bool      SupportProperty(ENUM_SYMBOL_PROP_DOUBLE property);
//--- Поддерживаемые строковые свойства символа
   virtual bool      SupportProperty(ENUM_SYMBOL_PROP_STRING property);
//--- Выводит в журнал краткое описание символа
   virtual void      PrintShort(void);
  };
//+------------------------------------------------------------------+

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

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

//+------------------------------------------------------------------+
//| Устанавливает список используемых символов                       |
//+------------------------------------------------------------------+
bool CSymbolsCollection::SetUsedSymbols(const string &symbol_used_array[])
  {
   ::ArrayCopy(this.m_array_symbols,symbol_used_array);
   this.m_mode_list=this.TypeSymbolsList(this.m_array_symbols);
   this.m_list_all_symbols.Clear();
   this.m_list_all_symbols.Sort(SORT_BY_SYMBOL_INDEX_MW);
   //--- Используется только текущий символ
   if(this.m_mode_list==SYMBOLS_MODE_CURRENT)
     {
      string name=::Symbol();
      ENUM_SYMBOL_STATUS status=this.SymbolStatus(name);
      return this.CreateNewSymbol(status,name,this.SymbolIndexInMW(name));
     }
   else
     {
      bool res=true;
      //--- Используется предопределённый список символов
      if(this.m_mode_list==SYMBOLS_MODE_DEFINES)
        {
         int total=::ArraySize(this.m_array_symbols);
         for(int i=0;i<total;i++)
           {
            string name=this.m_array_symbols[i];
            ENUM_SYMBOL_STATUS status=this.SymbolStatus(name);
            bool add=this.CreateNewSymbol(status,name,this.SymbolIndexInMW(name));
            res &=add;
            if(!add) 
               continue;
           }
         return res;
        }
      //--- Используется полный список символов сервера
      else if(this.m_mode_list==SYMBOLS_MODE_ALL)
        {
         return this.CreateSymbolsList(false);
        }
      //--- Используется список символов из окна "Обзор рынка"
      else if(this.m_mode_list==SYMBOLS_MODE_MARKET_WATCH)
        {
         this.MarketWatchEventsControl(false);
         return true;
        }
     }
   return false;
  }
//+------------------------------------------------------------------+

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

Метод работы с событиями всех символов коллекции:

//+------------------------------------------------------------------+
//| Работа с событиями списка символов коллекции                     |
//+------------------------------------------------------------------+
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;
         ENUM_SYMBOL_EVENT event_id=(ENUM_SYMBOL_EVENT)event.ID();
         if(event_id==SYMBOL_EVENT_NO_EVENT)
            continue;
         this.m_last_event=event_id;
         if(this.EventAdd((ushort)event.ID(),event.LParam(),event.DParam(),event.SParam()))
           {
            ::EventChartCustom(this.m_chart_id,(ushort)event_id,event.LParam(),event.DParam(),event.SParam());
           }
        }
     }
  }
//+------------------------------------------------------------------+

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

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

Для распознавания событий в окне обзор рынка нам необходимо рассчитать хэш-сумму всех символов, находящихся в окне обзора рынка. Её изменение нам будет сигнализировать о произошедшем событии. Первое, что приходит в голову — это простой подсчёт количества символов в окне, но... Добавление или удаление символа увеличит или уменьшит размер списка, но сортировка символов мышью не изменит количества символов. Значит, количество символов в окне "Обзор рынка" не подходит для расчёта хэш-суммы.
И мы поступим так: каждый символ, хранящийся в списке — его наименование, можно представить как число (код символа), представляющее собой сумму uchar-значений кодов символов (букв), из которых состоит наименование символа, с добавлением у этому значению индекса символа в списке окна обзора рынка. И хэш-суммой будет являться сумма всех кодов символов.

  • Добавление символа в список изменит хэш-сумму (к хэш-сумме прибавится новый код добавленного символа).
  • Удаление символа из списка изменит хэш-сумму (из хэш-суммы будет вычтен код удалённого символа).
  • Сортировка списка символов изменит хэш-сумму (изменятся коды сортированных символов, так как у них изменятся индексы)

Реализация метода работы с событиями окна "Обзор рынка":

//+------------------------------------------------------------------+
//| Работа с событиями окна обзора рынка                             |
//+------------------------------------------------------------------+
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;
      ENUM_SYMBOL_EVENT event_id=
        (
         this.m_total_symbols>this.m_total_symbol_prev ? SYMBOL_EVENT_MW_ADD :
         this.m_total_symbols<this.m_total_symbol_prev ? SYMBOL_EVENT_MW_DEL :
         SYMBOL_EVENT_MW_SORT
        );
      //--- Добавление символа в окно "Обзор рынка"
      if(event_id==SYMBOL_EVENT_MW_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==SYMBOL_EVENT_MW_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==SYMBOL_EVENT_MW_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::CopySymbolsNames(void)
  {
   this.m_list_names.Clear();
   int total=this.m_list_all_symbols.Total();
   for(int i=0;i<total;i++)
     {
      CSymbol *symbol=this.m_list_all_symbols.At(i);
      if(symbol==NULL)
         continue;
      this.m_list_names.Add(symbol.Name());
     }
  }
//+------------------------------------------------------------------+

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

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

//+------------------------------------------------------------------+
//| Возвращает объект-символ из списка по имени                      |
//+------------------------------------------------------------------+
CSymbol *CSymbolsCollection::GetSymbolByName(const string name)
  {
   CArrayObj *list=this.GetList(SYMBOL_PROP_NAME,name,EQUAL);
   if(list==NULL || list.Total()==0)
      return NULL;
   CSymbol *symbol=list.At(0);
   return(symbol!=NULL ? symbol : NULL);
  }
//+------------------------------------------------------------------+

В метод передаётся имя символа. Затем при помощи метода получения списка объектов GetList() по свойству "Имя символа" создаём новый список, в котором должен находиться единственный объект-символ при условии, что его имя совпадает с именем, переданным в метод.
Получаем из этого списка объект-символ и возвращаем его при удачном исходе поиска, либо NULL при отсутствии символа с таким именем в списке-коллекции символов.

Метод, возвращающий индекс символа в списке-коллекции символов:

//+------------------------------------------------------------------+
//| Возвращает из списка по имени индекс объекта-символа             |
//+------------------------------------------------------------------+
int CSymbolsCollection::GetSymbolIndexByName(const string name)
  {
   int total=this.m_list_all_symbols.Total();
   for(int i=0;i<total;i++)
     {
      CSymbol *symbol=this.m_list_all_symbols.At(i);
      if(symbol==NULL)
         continue;
      if(symbol.Name()==name)
         return i;
     }
   return WRONG_VALUE;
  }
//+------------------------------------------------------------------+

Здесь: в метод передаётся имя искомого символа. Затем в цикле по всем символам, находящимся в списке символов коллекции, получаем очередной объект-символ из списка. Если его имя совпадает с искомым — возвращаем индекс цикла. Иначе — возвращаем -1.

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

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

В метод передаётся событие, и исходя из того, какое событие передано, возвращается его текстовое описание.

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

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

Здесь: проверяется значение переменной m_mode_list и возвращается текстовое описание режима работы в соответствии со значением данной переменной.

Создание класса событий символов на этом завершено.

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

Доработка класса событий аккаунта

Откроем файл класса аккаунта Account.mqh и внесём необходимые изменения.
Заменим подключение файла Object.mqh на подключение BaseObj.mqh:

//+------------------------------------------------------------------+
//|                                                      Account.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                             https://mql5.com/ru/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/ru/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Включаемые файлы                                                 |
//+------------------------------------------------------------------+
#include "..\BaseObj.mqh"
#include "..\..\Services\DELib.mqh"
//+------------------------------------------------------------------+
//| Класс аккаунта                                                   |
//+------------------------------------------------------------------+
class CAccount : public CBaseObj
  {
private:

И теперь вместо класса CObject базовым классом сделаем CBaseObj.

Так как теперь у нас появилась возможность давать наименование объекту, унаследованному от CBaseObj, то воспользуемся данной возможностью и зададим для объекта-аккаунта его наименование.
В самом конце конструктора класса CAccount впишем строку, задающую имя объекту-аккаунту:

//+------------------------------------------------------------------+
//| Конструктор                                                      |
//+------------------------------------------------------------------+
CAccount::CAccount(void)
  {
//--- Сохранение целочисленных свойств
   this.m_long_prop[ACCOUNT_PROP_LOGIN]                              = ::AccountInfoInteger(ACCOUNT_LOGIN);
   this.m_long_prop[ACCOUNT_PROP_TRADE_MODE]                         = ::AccountInfoInteger(ACCOUNT_TRADE_MODE);
   this.m_long_prop[ACCOUNT_PROP_LEVERAGE]                           = ::AccountInfoInteger(ACCOUNT_LEVERAGE);
   this.m_long_prop[ACCOUNT_PROP_LIMIT_ORDERS]                       = ::AccountInfoInteger(ACCOUNT_LIMIT_ORDERS);
   this.m_long_prop[ACCOUNT_PROP_MARGIN_SO_MODE]                     = ::AccountInfoInteger(ACCOUNT_MARGIN_SO_MODE);
   this.m_long_prop[ACCOUNT_PROP_TRADE_ALLOWED]                      = ::AccountInfoInteger(ACCOUNT_TRADE_ALLOWED);
   this.m_long_prop[ACCOUNT_PROP_TRADE_EXPERT]                       = ::AccountInfoInteger(ACCOUNT_TRADE_EXPERT);
   this.m_long_prop[ACCOUNT_PROP_MARGIN_MODE]                        = #ifdef __MQL5__::AccountInfoInteger(ACCOUNT_MARGIN_MODE) #else ACCOUNT_MARGIN_MODE_RETAIL_HEDGING #endif ;
   this.m_long_prop[ACCOUNT_PROP_CURRENCY_DIGITS]                    = #ifdef __MQL5__::AccountInfoInteger(ACCOUNT_CURRENCY_DIGITS) #else 2 #endif ;
   this.m_long_prop[ACCOUNT_PROP_SERVER_TYPE]                        = (::TerminalInfoString(TERMINAL_NAME)=="MetaTrader 5" ? 5 : 4);
   
//--- Сохранение вещественных свойств
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_BALANCE)]          = ::AccountInfoDouble(ACCOUNT_BALANCE);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_CREDIT)]           = ::AccountInfoDouble(ACCOUNT_CREDIT);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_PROFIT)]           = ::AccountInfoDouble(ACCOUNT_PROFIT);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_EQUITY)]           = ::AccountInfoDouble(ACCOUNT_EQUITY);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_MARGIN)]           = ::AccountInfoDouble(ACCOUNT_MARGIN);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_MARGIN_FREE)]      = ::AccountInfoDouble(ACCOUNT_MARGIN_FREE);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_MARGIN_LEVEL)]     = ::AccountInfoDouble(ACCOUNT_MARGIN_LEVEL);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_MARGIN_SO_CALL)]   = ::AccountInfoDouble(ACCOUNT_MARGIN_SO_CALL);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_MARGIN_SO_SO)]     = ::AccountInfoDouble(ACCOUNT_MARGIN_SO_SO);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_MARGIN_INITIAL)]   = ::AccountInfoDouble(ACCOUNT_MARGIN_INITIAL);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_MARGIN_MAINTENANCE)]=::AccountInfoDouble(ACCOUNT_MARGIN_MAINTENANCE);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_ASSETS)]           = ::AccountInfoDouble(ACCOUNT_ASSETS);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_LIABILITIES)]      = ::AccountInfoDouble(ACCOUNT_LIABILITIES);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_COMMISSION_BLOCKED)]=::AccountInfoDouble(ACCOUNT_COMMISSION_BLOCKED);
   
//--- Сохранение строковых свойств
   this.m_string_prop[this.IndexProp(ACCOUNT_PROP_NAME)]             = ::AccountInfoString(ACCOUNT_NAME);
   this.m_string_prop[this.IndexProp(ACCOUNT_PROP_SERVER)]           = ::AccountInfoString(ACCOUNT_SERVER);
   this.m_string_prop[this.IndexProp(ACCOUNT_PROP_CURRENCY)]         = ::AccountInfoString(ACCOUNT_CURRENCY);
   this.m_string_prop[this.IndexProp(ACCOUNT_PROP_COMPANY)]          = ::AccountInfoString(ACCOUNT_COMPANY);

//--- Имя объекта-аккаунта
   this.m_name=TextByLanguage("Счёт ","Account ")+(string)this.Login()+": "+this.Name()+" ("+this.Company()+")";
  }
//+-------------------------------------------------------------------+

Как видим, имя объекта будет систоять из текста "Счёт", номера счёта, имени клиента и наименования компании, обслуживающей счёт.
Например, при подключении к одному из счетов на MetaQuotes-Demo под моим именем, имя объекта-аккаунта будет таким: "Account 8550475: Artyom Trishkin (MetaQuotes Software Corp.)"

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

//+------------------------------------------------------------------+
//| Выводит в журнал краткое описание счёта                          |
//+------------------------------------------------------------------+
void CAccount::PrintShort(void)
  {
   string mode=(this.MarginMode()==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING ? ", Hedge" : this.MarginMode()==ACCOUNT_MARGIN_MODE_EXCHANGE ? ", Exhange" : "");
   string names=this.m_name+" ";
   string values=::DoubleToString(this.Balance(),(int)this.CurrencyDigits())+" "+this.Currency()+", 1:"+(string)+this.Leverage()+mode+", "+this.TradeModeDescription()+" "+this.ServerTypeDescription();
   ::Print(names,values);
  }
//+------------------------------------------------------------------+

Это все доработки класса CAccount.

Теперь доработаем класс коллекции аккаунтов. Откроем файл AccountsCollection.mqh и впишем необходимые изменения.
Сделаем базовым объектом класса коллекции аккаунтов класс CBaseObj:

//+------------------------------------------------------------------+
//| Коллекция аккаунтов                                              |
//+------------------------------------------------------------------+
class CAccountsCollection : public CBaseObj
  {
private:

Так как теперь класс унаследован от базового объекта, в котором уже прописан функционал для отслеживания событий объекта, то из класса коллекции аккаунтов удалим оказавшиеся продублированными переменные и методы.
Из структуры данных аккаунта удалим поле хэш-суммы:

   struct MqlDataAccount
     {
      double         hash_sum;               // Хэш-сумма данных аккаунта
      //--- Целочисленные свойства счёта
      long           login;                  // ACCOUNT_LOGIN (Номер счёта)
      long           leverage;               // ACCOUNT_LEVERAGE (Размер предоставленного плеча)
      int            limit_orders;           // ACCOUNT_LIMIT_ORDERS (Максимально допустимое количество действующих отложенных ордеров)
      bool           trade_allowed;          // ACCOUNT_TRADE_ALLOWED (Разрешенность торговли для текущего счета со стороны сервера)
      bool           trade_expert;           // ACCOUNT_TRADE_EXPERT (Разрешенность торговли для эксперта со стороны сервера)
      //--- Вещественные свойства счёта
      double         balance;                // ACCOUNT_BALANCE (Баланс счета в валюте депозита)
      double         credit;                 // ACCOUNT_CREDIT (Размер предоставленного кредита в валюте депозита)
      double         profit;                 // ACCOUNT_PROFIT (Размер текущей прибыли на счете в валюте депозита)
      double         equity;                 // ACCOUNT_EQUITY (Значение собственных средств на счете в валюте депозита)
      double         margin;                 // ACCOUNT_MARGIN (Размер зарезервированных залоговых средств на счете  в валюте депозита)
      double         margin_free;            // ACCOUNT_MARGIN_FREE (Размер свободных средств на счете  в валюте депозита, доступных для открытия позиции)
      double         margin_level;           // ACCOUNT_MARGIN_LEVEL (Уровень залоговых средств на счете в процентах)
      double         margin_so_call;         // ACCOUNT_MARGIN_SO_CALL (Уровень залоговых средств, при котором происходит MarginCall)
      double         margin_so_so;           // ACCOUNT_MARGIN_SO_SO (Уровень залоговых средств, при достижении которого происходит StopOut)
      double         margin_initial;         // ACCOUNT_MARGIN_INITIAL (Размер средств, зарезервированных на счёте, для обеспечения гарантийной суммы по всем отложенным ордерам)
      double         margin_maintenance;     // ACCOUNT_MARGIN_MAINTENANCE (Размер средств, зарезервированных на счёте, для обеспечения минимальной суммы по всем открытым позициям)
      double         assets;                 // ACCOUNT_ASSETS (Текущий размер активов на счёте)
      double         liabilities;            // ACCOUNT_LIABILITIES (Текущий размер обязательств на счёте)
      double         comission_blocked;      // ACCOUNT_COMMISSION_BLOCKED (Текущая сумма заблокированных комиссий по счёту)
     };

Удалим приватные переменные-члены класса:

   MqlTick           m_tick;                             // Структура тика
   string            m_symbol;                           // Текущий символ
   long              m_chart_id;                         // Идентификатор графика управляющей программы
   CListObj          m_list_accounts;                    // Список объектов-аккаунтов
   CArrayInt         m_list_changes;                     // Список изменений аккаунта
   string            m_folder_name;                      // Имя папки хранения объектов-аккаунтов
   int               m_index_current;                    // Индекс объекта-аккаунта с данными текущего счёта
//--- Отслеживание изменений аккаунта
   bool              m_is_account_event;                 // Флаг события в данных аккаунта
   int               m_change_code;                      // Код изменения аккаунта

Метод  SetChangeCode() переименуем в SetEventCode() — чтобы названия однотипных методов были одинаковыми в разных классах.
Метод SetTypeEvent() сделаем виртуальным, так как он уже объявлен в классе CBaseObj, и должен реализовываться в наследниках.
Метод IsPresentEventFlag() удалим из класса — он уже реализован в CBaseObj.

В публичной секции класса так же немного "проредим" дублирующиеся методы.
Удалим методы GetEventCode(), GetListChanges() и SetChartID(), а метод ENUM_ACCOUNT_EVENT GetEvent(const int shift=WRONG_VALUE) сделаем таким:

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

И сразу же рассмотрим его реализацию за пределами тела класса:

//+------------------------------------------------------------------+
//| Возвращает событие аккаунта по его номеру в списке               |
//+------------------------------------------------------------------+
ENUM_ACCOUNT_EVENT CAccountsCollection::GetEventID(const int shift=WRONG_VALUE,const bool check_out=true)
  {
   CEventBaseObj *event=this.GetEvent(shift,check_out);
   if(event==NULL)
      return ACCOUNT_EVENT_NO_EVENT;
   return (ENUM_ACCOUNT_EVENT)event.ID();
  }
//+------------------------------------------------------------------+

В метод передаются индекс искомого события (-1 для выбора последнего) и флаг контроля выхода индекса за пределы размеров списка событий.
Получаем объект события при помощи метода базового объекта CBaseObj GetEvent(), рассмотренного нами в начале статьи. Если события нет, то возвращаем "Отсутствие события", иначе — возвращаем идентификатор события.

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

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

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

Метод обновления данных аккаунта Refresh() сделаем виртуальным, так как он объявлен в классе CBaseObj и реализуется в его наследниках.

В реализацию метода внесены некоторые изменения:

//+------------------------------------------------------------------+
//| Обновляет данные текущего аккаунта                               |
//+------------------------------------------------------------------+
void CAccountsCollection::Refresh(void)
  {
   ::ResetLastError();
   if(!::SymbolInfoTick(::Symbol(),this.m_tick))
     {
      this.m_global_error=::GetLastError();
      return;
     }
   if(this.m_index_current==WRONG_VALUE)
      return;
   CAccount* account=this.m_list_accounts.At(this.m_index_current);
   if(account==NULL)
      return;
//--- Подготовка событийных данных
   this.m_is_event=false;
   ::ZeroMemory(this.m_struct_curr_account);
   this.m_hash_sum=0;
   this.SetAccountsParams(account);

//--- Первый запуск
   if(!this.m_struct_prev_account.login)
     {
      this.m_struct_prev_account=this.m_struct_curr_account;
      this.m_hash_sum_prev=this.m_hash_sum;
      return;
     }
//--- Если хэш-сумма аккаунта изменилась
   if(this.m_hash_sum!=this.m_hash_sum_prev)
     {
      this.m_list_events.Clear();
      this.m_event_code=this.SetEventCode();
      this.SetTypeEvent();
      int total=this.m_list_events.Total();
      if(total>0)
        {
         this.m_is_event=true;
         for(int i=0;i<total;i++)
           {
            CEventBaseObj *event=this.GetEvent(i,false);
            if(event==NULL)
               continue;
            ENUM_ACCOUNT_EVENT event_id=(ENUM_ACCOUNT_EVENT)event.ID();
            if(event_id==ACCOUNT_EVENT_NO_EVENT)
               continue;
            long lparam=event.LParam();
            double dparam=event.DParam();
            string sparam=event.SParam();
            ::EventChartCustom(this.m_chart_id,(ushort)event_id,lparam,dparam,sparam);
           }
        }
      this.m_hash_sum_prev=this.m_hash_sum;
     }
  }
//+------------------------------------------------------------------+

Рассмотрим только внесённые изменения.
Сначала получаем котировочные данные по символу (для определения милисекундного времени), и если получить их не удалось, то выходим из метода.

Сбрасываем флаг события аккаунта и значение текущей хэш-суммы. При первом запуске сохраним текущую хэш-сумму как прошлую.

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

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

В методе сохранения свойств аккаунта в объекта-аккаунте и структуре данных аккаунта заменим обращение к полю структуры хэш-суммы на переменную класса CBaseObj хэш-суммы, и сохраним имя объекта:

//+------------------------------------------------------------------+
//| Записывает данные текущего счёта в свойства объекта-аккаунта     |
//+------------------------------------------------------------------+
void CAccountsCollection::SetAccountsParams(CAccount *account)
  {
   if(account==NULL)
      return;
//--- Наименование
   this.m_name=account.GetName();
//--- Номер счёта
   this.m_struct_curr_account.login=account.Login();
//--- Размер предоставленного плеча
   account.SetProperty(ACCOUNT_PROP_LEVERAGE,::AccountInfoInteger(ACCOUNT_LEVERAGE));
   this.m_struct_curr_account.leverage=account.Leverage();
   this.m_hash_sum+=(double)this.m_struct_curr_account.leverage;
//--- Максимально допустимое количество действующих отложенных ордеров
   account.SetProperty(ACCOUNT_PROP_LIMIT_ORDERS,::AccountInfoInteger(ACCOUNT_LIMIT_ORDERS));
   this.m_struct_curr_account.limit_orders=(int)account.LimitOrders();
   this.m_hash_sum+=(double)this.m_struct_curr_account.limit_orders;
//--- Разрешенность торговли для текущего счета со стороны сервера
   account.SetProperty(ACCOUNT_PROP_TRADE_ALLOWED,::AccountInfoInteger(ACCOUNT_TRADE_ALLOWED));
   this.m_struct_curr_account.trade_allowed=account.TradeAllowed();
   this.m_hash_sum+=(double)this.m_struct_curr_account.trade_allowed;
//--- Разрешенность торговли для эксперта со стороны сервера
   account.SetProperty(ACCOUNT_PROP_TRADE_EXPERT,::AccountInfoInteger(ACCOUNT_TRADE_EXPERT));
   this.m_struct_curr_account.trade_expert=account.TradeExpert();
   this.m_hash_sum+=(double)this.m_struct_curr_account.trade_expert;
//--- Баланс счета в валюте депозита
   account.SetProperty(ACCOUNT_PROP_BALANCE,::AccountInfoDouble(ACCOUNT_BALANCE));
   this.m_struct_curr_account.balance=account.Balance();
   this.m_hash_sum+=(double)this.m_struct_curr_account.balance;
//--- Размер предоставленного кредита в валюте депозита
   account.SetProperty(ACCOUNT_PROP_CREDIT,::AccountInfoDouble(ACCOUNT_CREDIT));
   this.m_struct_curr_account.credit=account.Credit();
   this.m_hash_sum+=(double)this.m_struct_curr_account.credit;
//--- Размер текущей прибыли на счете в валюте депозита
   account.SetProperty(ACCOUNT_PROP_PROFIT,::AccountInfoDouble(ACCOUNT_PROFIT));
   this.m_struct_curr_account.profit=account.Profit();
   this.m_hash_sum+=(double)this.m_struct_curr_account.profit;
//--- Значение собственных средств на счете в валюте депозита
   account.SetProperty(ACCOUNT_PROP_EQUITY,::AccountInfoDouble(ACCOUNT_EQUITY));
   this.m_struct_curr_account.equity=account.Equity();
   this.m_hash_sum+=(double)this.m_struct_curr_account.equity;
//--- Размер зарезервированных залоговых средств на счете  в валюте депозита
   account.SetProperty(ACCOUNT_PROP_MARGIN,::AccountInfoDouble(ACCOUNT_MARGIN));
   this.m_struct_curr_account.margin=account.Margin();
   this.m_hash_sum+=(double)this.m_struct_curr_account.margin;
//--- Размер свободных средств на счете в валюте депозита, доступных для открытия позиции
   account.SetProperty(ACCOUNT_PROP_MARGIN_FREE,::AccountInfoDouble(ACCOUNT_MARGIN_FREE));
   this.m_struct_curr_account.margin_free=account.MarginFree();
   this.m_hash_sum+=(double)this.m_struct_curr_account.margin_free;
//--- Уровень залоговых средств на счете в процентах
   account.SetProperty(ACCOUNT_PROP_MARGIN_LEVEL,::AccountInfoDouble(ACCOUNT_MARGIN_LEVEL));
   this.m_struct_curr_account.margin_level=account.MarginLevel();
   this.m_hash_sum+=(double)this.m_struct_curr_account.margin_level;
//--- Уровень залоговых средств, при котором происходит MarginCall
   account.SetProperty(ACCOUNT_PROP_MARGIN_SO_CALL,::AccountInfoDouble(ACCOUNT_MARGIN_SO_CALL));
   this.m_struct_curr_account.margin_so_call=account.MarginSOCall();
   this.m_hash_sum+=(double)this.m_struct_curr_account.margin_so_call;
//--- Уровень залоговых средств, при достижении которого происходит StopOut
   account.SetProperty(ACCOUNT_PROP_MARGIN_SO_SO,::AccountInfoDouble(ACCOUNT_MARGIN_SO_SO));
   this.m_struct_curr_account.margin_so_so=account.MarginSOSO();
   this.m_hash_sum+=(double)this.m_struct_curr_account.margin_so_so;
//--- Размер средств, зарезервированных на счёте, для обеспечения гарантийной суммы по всем отложенным ордерам
   account.SetProperty(ACCOUNT_PROP_MARGIN_INITIAL,::AccountInfoDouble(ACCOUNT_MARGIN_INITIAL));
   this.m_struct_curr_account.margin_initial=account.MarginInitial();
   this.m_hash_sum+=(double)this.m_struct_curr_account.margin_initial;
//--- Размер средств, зарезервированных на счёте, для обеспечения минимальной суммы по всем открытым позициям
   account.SetProperty(ACCOUNT_PROP_MARGIN_MAINTENANCE,::AccountInfoDouble(ACCOUNT_MARGIN_MAINTENANCE));
   this.m_struct_curr_account.margin_maintenance=account.MarginMaintenance();
   this.m_hash_sum+=(double)this.m_struct_curr_account.margin_maintenance;
//--- Текущий размер активов на счёте
   account.SetProperty(ACCOUNT_PROP_ASSETS,::AccountInfoDouble(ACCOUNT_ASSETS));
   this.m_struct_curr_account.assets=account.Assets();
   this.m_hash_sum+=(double)this.m_struct_curr_account.assets;
//--- Текущий размер обязательств на счёте
   account.SetProperty(ACCOUNT_PROP_LIABILITIES,::AccountInfoDouble(ACCOUNT_LIABILITIES));
   this.m_struct_curr_account.liabilities=account.Liabilities();
   this.m_hash_sum+=(double)this.m_struct_curr_account.liabilities;
//--- Текущая сумма заблокированных комиссий по счёту
   account.SetProperty(ACCOUNT_PROP_COMMISSION_BLOCKED,::AccountInfoDouble(ACCOUNT_COMMISSION_BLOCKED));
   this.m_struct_curr_account.comission_blocked=account.ComissionBlocked();
   this.m_hash_sum+=(double)this.m_struct_curr_account.comission_blocked;
  }
//+------------------------------------------------------------------+

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

//+------------------------------------------------------------------+
//| Устанавливает тип события объекта-аккаунта                       |
//+------------------------------------------------------------------+
void CAccountsCollection::SetTypeEvent(void)
  {
   this.InitChangesParams();
   ENUM_ACCOUNT_EVENT event_id=ACCOUNT_EVENT_NO_EVENT;
//--- Изменение разрешения торговли на счёте
   if(this.IsPresentEventFlag(ACCOUNT_EVENT_FLAG_TRADE_ALLOWED))
     {
      if(!this.m_struct_curr_account.trade_allowed)
        {
         this.m_is_change_trade_allowed_off=true;
         event_id=ACCOUNT_EVENT_TRADE_ALLOWED_OFF;
         if(this.EventAdd(event_id,this.TickTime(),this.m_is_change_trade_allowed_off,this.m_name))
            this.m_struct_prev_account.trade_allowed=this.m_struct_curr_account.trade_allowed;
        }
      else
        {
         this.m_is_change_trade_allowed_on=true;
         event_id=ACCOUNT_EVENT_TRADE_ALLOWED_ON;
         if(this.EventAdd(event_id,this.TickTime(),this.m_is_change_trade_allowed_on,this.m_name))
            this.m_struct_prev_account.trade_allowed=this.m_struct_curr_account.trade_allowed;
        }
     }
//--- Изменение разрешения автоторговли на счёте

На этом изменения и доработка класса коллекции аккаунтов завершена, и можно включать обновлённые классы событий символов и аккаунта в работу.

Как мы помним — всё управление начинается с класса CEngine, и все данные в него же и отправляются. Не составляют исключения и классы событий символа и аккаунта.

Включение класса событий символов и обновлённого класса аккаунта в работу

Откроем файл Engine.mqh и внесём необходимые правки и изменения.

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

//+------------------------------------------------------------------+
//| Класс-основа библиотеки                                          |
//+------------------------------------------------------------------+
class CEngine : public CObject
  {
private:
   CHistoryCollection   m_history;                       // Коллекция исторических ордеров и сделок
   CMarketCollection    m_market;                        // Коллекция рыночных ордеров и сделок
   CEventsCollection    m_events;                        // Коллекция событий
   CAccountsCollection  m_accounts;                      // Коллекция аккаунтов
   CSymbolsCollection   m_symbols;                       // Коллекция символов
   CArrayObj            m_list_counters;                 // Список счётчиков таймера
   int                  m_global_error;                  // Код глобальной ошибки
   bool                 m_first_start;                   // Флаг первого запуска
   bool                 m_is_hedge;                      // Флаг хедж-счёта
   bool                 m_is_tester;                     // Флаг работы в тестере
   bool                 m_is_market_trade_event;         // Флаг торгового события на счёте
   bool                 m_is_history_trade_event;        // Флаг торгового события в истории счёта
   bool                 m_is_account_event;              // Флаг события изменения аккаунта
   bool                 m_is_symbol_event;               // Флаг события изменения свойств символа
   ENUM_TRADE_EVENT     m_last_trade_event;              // Последнее торговое событие на счёте
   ENUM_ACCOUNT_EVENT   m_last_account_event;            // Последнее событие в свойствах счёта
   ENUM_SYMBOL_EVENT    m_last_symbol_event;             // Последнее событие в свойствах символа
//--- Возвращает индекс счётчика по id

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

//--- Возвращает список исторических (1) ордеров, (2) удалённых отложенных ордеров, (3) сделок,
//--- (4) всех маркет-ордеров позиции по её идентификатору, (5) описание последнего торгового события
   CArrayObj           *GetListHistoryOrders(void);
   CArrayObj           *GetListHistoryPendings(void);
   CArrayObj           *GetListDeals(void);
   CArrayObj           *GetListAllOrdersByPosID(const ulong position_id);
   string               GetLastTradeEventDescription(void);

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

//--- Возвращает список (1) используемых символов, (2) событий символов, (3) последнее событие изменения символа
//--- (4) текущий символ, (5) описание события символа, (6) описание события окна "Обзор рынка"
   CArrayObj           *GetListAllUsedSymbols(void)                     { return this.m_symbols.GetList();                    }
   CArrayObj           *GetListSymbolsEvents(void)                      { return this.m_symbols.GetListEvents();              }
   ENUM_SYMBOL_EVENT    GetLastSymbolsEvent()                           { return this.m_symbols.GetLastEvent();               }
   CSymbol             *GetSymbolCurrent(void);
   string               GetSymbolEventDescription(ENUM_SYMBOL_EVENT event);
   string               GetMWEventDescription(ENUM_SYMBOL_EVENT event)  { return this.m_symbols.EventDescription(event);      }
   string               ModeSymbolsListDescription(void)                { return this.m_symbols.ModeSymbolsListDescription(); }
   
//--- Возвращает список событий ордеров, сделок и позиций
   CArrayObj           *GetListAllOrdersEvents(void)                    { return this.m_events.GetList();                     }
//--- Сбрасывает последнее торговое событие
   void                 ResetLastTradeEvent(void)                       { this.m_events.ResetLastTradeEvent(); }
//--- Возвращает (1) последнее торговое событие, (2) последнее событие в свойствах счёта, (3) флаг счёта-хедж, (4) флаг работы в тестере
   ENUM_TRADE_EVENT     LastTradeEvent(void)                      const { return this.m_last_trade_event;                     }
   ENUM_ACCOUNT_EVENT   LastAccountEvent(void)                    const { return this.m_last_account_event;                   }
   ENUM_SYMBOL_EVENT    LastSymbolsEvent(void)                    const { return this.m_last_symbol_event;                    }
//--- Возвращает флаг (1) счёта-хедж, (2) работы в тестере, (3) события аккаунта, (4) события символа
   bool                 IsHedge(void)                             const { return this.m_is_hedge;                             }
   bool                 IsTester(void)                            const { return this.m_is_tester;                            }
   bool                 IsAccountsEvent(void)                     const { return this.m_accounts.IsEvent();                   }
   bool                 IsSymbolsEvent(void)                      const { return this.m_symbols.IsEvent();                    }
//--- Возвращает (1) объект-символ по имени, код последнего события (2)  аккаунта, (3) символа
   CSymbol             *GetSymbolObjByName(const string name)           { return this.m_symbols.GetSymbolByName(name);        }
   int                  GetAccountEventsCode(void)                const { return this.m_accounts.GetEventCode();              }
   int                  GetSymbolsEventsCode(void)                const { return this.m_symbols.GetLastEventsCode();          }
//--- Возвращает количество (1) символов, (2) событий в коллекции символов
   int                  GetSymbolsCollectionTotal(void)           const { return this.m_symbols.GetSymbolsCollectionTotal();  }
   int                  GetSymbolsCollectionEventsTotal(void)     const { return this.m_symbols.GetEventsTotal();             }

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

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

В обработчике таймера класса внесём поправки в блоки обработки таймера1 и таймера2 коллекции символов:

//+------------------------------------------------------------------+
//| CEngine таймер                                                   |
//+------------------------------------------------------------------+
void CEngine::OnTimer(void)
  {
//--- Таймер коллекций исторических ордеров и сделок и рыночных ордеров и позиций
   int index=this.CounterIndex(COLLECTION_ORD_COUNTER_ID);
   if(index>WRONG_VALUE)
     {
      CTimerCounter* counter=this.m_list_counters.At(index);
      if(counter!=NULL)
        {
         //--- Если это не тестер
         if(!this.IsTester())
           {
            //--- Если пауза завершилась - работаем с событиями коллекций ордеров, сделок и позиций
            if(counter.IsTimeDone())
               this.TradeEventsControl();
           }
         //--- Если тестер - работаем с событиями коллекций по тику
         else
            this.TradeEventsControl();
        }
     }
//--- Таймер коллекции аккаунтов
   index=this.CounterIndex(COLLECTION_ACC_COUNTER_ID);
   if(index>WRONG_VALUE)
     {
      CTimerCounter* counter=this.m_list_counters.At(index);
      if(counter!=NULL)
        {
         //--- Если это не тестер
         if(!this.IsTester())
           {
            //--- Если пауза завершилась - работаем с событиями коллекции аккаунтов
            if(counter.IsTimeDone())
               this.AccountEventsControl();
           }
         //--- Если тестер - работаем с событиями коллекций по тику
         else
            this.AccountEventsControl();
        }
     }
     
//--- Таймер1 коллекции символов (обновление котировочных данных символов в коллекции)
   index=this.CounterIndex(COLLECTION_SYM_COUNTER_ID1);
   if(index>WRONG_VALUE)
     {
      CTimerCounter* counter=this.m_list_counters.At(index);
      if(counter!=NULL)
        {
         //--- Если это не тестер
         if(!this.IsTester())
           {
            //--- Если пауза завершилась - обновляем котировочные данные всех символов в коллекции
            if(counter.IsTimeDone())
               this.m_symbols.RefreshRates();
           }
         //--- Если тестер - обновляем котировочные данные всех символов в коллекции по тику
         else
            this.m_symbols.RefreshRates();
        }
     }
//--- Таймер2 коллекции символов (обновление всех данных всех символов в коллекции и отслеживание событий символов и списка символов в окне обзора рынка)
   index=this.CounterIndex(COLLECTION_SYM_COUNTER_ID2);
   if(index>WRONG_VALUE)
     {
      CTimerCounter* counter=this.m_list_counters.At(index);
      if(counter!=NULL)
        {
         //--- Если это не тестер
         if(!this.IsTester())
           {
            //--- Если пауза завершилась
            if(counter.IsTimeDone())
              {
               //--- обновляем данные и работаем с событиями всех символов в коллекции
               this.SymbolEventsControl();
               //--- Если работаем со списком из обзора рынка - проверяем события окна обзора рынка
               if(this.m_symbols.ModeSymbolsList()==SYMBOLS_MODE_MARKET_WATCH)
                  this.MarketWatchEventsControl();
              }
           }
         //--- Если тестер - работаем с событиями всех символов в коллекции по тику
         else
            this.SymbolEventsControl();
        }
     }
  }
//+------------------------------------------------------------------+

Здесь: при завершении счётчика таймера1 нам необходимо просто обновить котировочные данные всех символов коллекции, поэтому вызываем метод RefreshRates() коллекции символов.
При завершении счётчика таймера2 нам необходимо сделать полное обновление всех символов коллекции символов и отследить произошедшие события как символов коллекции, так и списка символов в окне "Обзор рынка", поэтому вызываем методы класса CEngine SymbolEventsControl() и только при работе не в тестере MarketWatchEventsControl().

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

//+------------------------------------------------------------------+
//| Работа с событиями коллекции символов                            |
//+------------------------------------------------------------------+
void CEngine::SymbolEventsControl(void)
  {
   this.m_symbols.SymbolsEventsControl();
   this.m_is_symbol_event=this.m_symbols.IsEvent();
//--- Если есть изменения свойств символа
   if(this.m_is_symbol_event)
     {
      //--- Получаем последнее событие изменения свойств символа
      this.m_last_symbol_event=this.m_symbols.GetLastEvent();
     }
  }
//+------------------------------------------------------------------+

Здесь: вызываем метод коллекции символов SymbolsEventsControl(), рассмотренный в данной статье выше при обсуждении класса событий коллекции символов. После работы данного метода, в классе коллекции символов будет взведён флаг события, при условии что на любом из символов коллекции было зарегистрировано событие, и состояние этого флага записываем при помощи метода IsEvent() класса базового объекта CBaseObj в переменную-флаг события колекции символов m_is_symbol_event, значение которой можно отследить в вызывающей программе. Если было зарегистрировано событие в коллекции символовзапишем последнее событие в переменную m_last_symbol_event, значение которой так же можно отслеживать в вызывающей программе.

Реализация метода для работы с событиями окна "Обзор рынка":

//+------------------------------------------------------------------+
//| Работа с событиями списка символов в окне обзора рынка           |
//+------------------------------------------------------------------+
void CEngine::MarketWatchEventsControl(void)
  {
   if(this.IsTester())
      return;
   this.m_symbols.MarketWatchEventsControl();
  }
//+------------------------------------------------------------------+

Здесь: если это тестер — выходим, иначе — вызываем метод MarketWatchEventsControl() класса коллекции символов для обработки событий окна обзора рынка, рассмотренный нами выше при обсуждении отслеживания событий класса коллекции символов.

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

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

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

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

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

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

Доработка класса CEngine завершена, и у нас всё готово для тестирования событий символов и обновлённого класса аккаунта и событий аккаунта.

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

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

Для тестирования возьмём тестовый советник из прошлой статьи, сохраним его под именем \MQL5\Experts\TestDoEasy\ Part16\TestDoEasyPart16.mq5 и внесём в него необходимые изменения.

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

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

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

Сделаем так: если в настройках советника будет выбран режим работы с полным списком доступных символов на сервере, то программа выдаст стандартное окно функции MessageBox() с предупреждением

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

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

//--- Проверка на выбор работы с полным списком
   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_mode присваиваем значение режима работы с символами, выбранного пользователем в настройках советника.
Если выбран режим работы с полными списком
, то создаём текст сообщения окна предупреждения и выводим это окно на экран. Затем проверяем какую кнопку нажал пользователь. Если это кнопка "Нет", то переменной used_symbols_mode присваиваем значение режима работы с текущим символом.
В остальных случаях (кнопка "Да" или Esc) — оставляем режим работы с полным списком доступных символов.

Далее создаём массив используемых символов (отправляем в функцию создания массива переменную used_symbols_mode):

//--- Заполнение массива используемых символов
   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());

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

//--- Быстрая проверка коллекции объектов-символов
   CArrayObj *list=engine.GetListAllUsedSymbols();
   CSymbol *symbol=NULL;
   if(list!=NULL)
     {
      int total=list.Total();
      for(int i=0;i<total;i++)
        {
         symbol=list.At(i);
         if(symbol==NULL)
            continue;
         symbol.Refresh();
         symbol.RefreshRates();
         symbol.PrintShort();
         if(InpModeUsedSymbols<SYMBOLS_MODE_MARKET_WATCH)
            symbol.Print();
        }
     }

В обработчик 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))
     {
      engine.OnTimer();
      PressButtonsControl();
     }
//--- Если последнее торговое событие изменилось
   if(engine.LastTradeEvent()!=last_trade_event)
     {
      last_trade_event=engine.LastTradeEvent();
      Comment("\nLast trade event: ",engine.GetLastTradeEventDescription());
      engine.ResetLastTradeEvent();
     }
//--- Если есть событие аккаунта
   if(engine.IsAccountsEvent())
     {
      //--- последнее событие аккаунта
      last_account_event=engine.LastAccountEvent();
      //--- Если это тестер
      if(MQLInfoInteger(MQL_TESTER))
        {
         //--- Получим список всех событий аккаунта, произошедших одновременно
         CArrayObj* list=engine.GetListAccountEvents();
         if(list!=NULL)
           {
            //--- В цикле получаем очередное событие
            int total=list.Total();
            for(int i=0;i<total;i++)
              {
               //--- берём событие из списка
               CEventBaseObj *event=list.At(i);
               if(event==NULL)
                  continue;
               //--- Отправляем событие в обработчик событий
               long lparam=event.LParam();
               double dparam=event.DParam();
               string sparam=event.SParam();
               OnDoEasyEvent(CHARTEVENT_CUSTOM+event.ID(),lparam,dparam,sparam);
              }
           }
        }
     }
//--- Если есть событие коллекции символов
   if(engine.IsSymbolsEvent())
     {
      //--- последнее событие в коллекции символов
      last_symbol_event=engine.LastSymbolsEvent();
      //--- Если это тестер
      if(MQLInfoInteger(MQL_TESTER))
        {
         //--- Получим список всех событий символов, произошедших одновременно
         CArrayObj* list=engine.GetListSymbolsEvents();
         if(list!=NULL)
           {
            //--- В цикле получаем очередное событие
            int total=list.Total();
            for(int i=0;i<total;i++)
              {
               //--- берём событие из списка
               CEventBaseObj *event=list.At(i);
               if(event==NULL)
                  continue;
               //--- Отправляем событие в обработчик событий
               long lparam=event.LParam();
               double dparam=event.DParam();
               string sparam=event.SParam();
               OnDoEasyEvent(CHARTEVENT_CUSTOM+event.ID(),lparam,dparam,sparam);
              }
           }
        }
     }
//--- Если установлен флаг трейлинга
   if(trailing_on)
     {
      TrailingPositions();
      TrailingOrders();
     }
  }
//+------------------------------------------------------------------+

Здесь всё просто, и все необходимые действия по обработке событий аккаунта и коллекции символов прокомментированы в листинге.

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

В обработчике советника OnDoEasyEvent() добавим код обработки событий коллекции символов:

//+------------------------------------------------------------------+
//| Обработка событий библиотеки DoEasy                              |
//+------------------------------------------------------------------+
void OnDoEasyEvent(const int id,
                   const long &lparam,
                   const double &dparam,
                   const string &sparam)
  {
   int idx=id-CHARTEVENT_CUSTOM;
   string event="::"+string(idx);
   int digits=Digits();
//--- Обработка торговых событий
   if(idx>TRADE_EVENT_NO_EVENT && idx<TRADE_EVENTS_NEXT_CODE)
     {
      event=EnumToString((ENUM_TRADE_EVENT)ushort(idx));
      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 
                 }
              }
           }
        }
     }
     
//--- Обработка событий символов
   else if(idx>SYMBOL_EVENT_NO_EVENT && idx<SYMBOL_EVENTS_NEXT_CODE)
     {
      string name="";
      //--- Событие окна "Обзор рынка"
      if(idx<SYMBOL_EVENT_TRADE_DISABLE)
        {
         string descr=engine.GetMWEventDescription((ENUM_SYMBOL_EVENT)idx);
         name=(idx==SYMBOL_EVENT_MW_SORT ? "" : ": "+sparam);
         Print(TimeMSCtoString(lparam)," ",descr,name);
        }
      //--- Событие символа
      else
        {
         CSymbol *symbol=engine.GetSymbolObjByName(sparam);
         if(symbol!=NULL)
           {
            string descr=": "+symbol.EventDescription((ENUM_SYMBOL_EVENT)ushort(idx));
            Print(TimeMSCtoString(lparam)," ",sparam,descr);
           }
        }
     }
  }
//+------------------------------------------------------------------+

Здесь всего два варианта: обработка событий окна "Обзор рынка" и обработка событий символов коллекции.

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

На этом изменения тестового советника закончены.
Полный листинг советника можно увидеть в прилагаемых к статье файлах.

Если запустить советник на демо-счёте, то спустя некоторое время можно увидеть в журнале записи об изменениях свойств символов. Например, при запуске советнике накануне открытия торговой сессии в понедельник, в журнал начинают достаточно активно выводиться записи об изменениях спреда различных символов.
Как пример, для всего четырёх символов в окне "Обзор рынка" уже целый час как в журнале мельтешат сообщения об изменениях спредов символов:

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

... много строк пропущено ...

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

Давайте теперь запустим советника в тестере с двумя символами и посмотрим какие записи он нам покажет.

В настройках тестера для входного параметра советника Mode of used symbols list выберем из разворачивающегося списка "Работа с заданным списком символов", а в параметре List of used symbols (comma - separator) введём через запятую два символа: EURUSD,GBPUSD и запустим визуальный тест советника:


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

Что дальше

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

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

К содержанию

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

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


Прикрепленные файлы |
MQL5.zip (237.22 KB)
MQL4.zip (237.22 KB)
Последние комментарии | Перейти к обсуждению на форуме трейдеров (3)
Dmitiry Ananiev
Dmitiry Ananiev | 17 июл. 2019 в 09:11
Может кто-то и научится чему то из этих кодов. А вот применение этой библы какое ?
Может лучше прилагать какой то тестовый робот что б наглядно было понятно зачем все это нагромождение ? 
Artyom Trishkin
Artyom Trishkin | 17 июл. 2019 в 09:21
Dmitiry Ananiev:
Может кто-то и научится чему то из этих кодов. А вот применение этой библы какое ?
Может лучше прилагать какой то тестовый робот что б наглядно было понятно зачем все это нагромождение ? 

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

Понимаете, для того чтобы "приложить тестовый робот", нужно точно знать что показать в нём из множества возможностей. Вы не спросили "как получить", вы спросили "зачем"...

MQL_User
MQL_User | 12 июл. 2022 в 20:50
Попробовал на реальном счёте. При работе с текущим символом (BR-8.22) работает нормально. Но что странно, при работе с заданным списком, брал названия символов из окна "Обзор рынка" и фьючерс BR-8.22 не распознаёт (Количество используемых символов 0), а вот @BR распознаёт.
Исследования технических фигур Меррилла Исследования технических фигур Меррилла
В этой мы статье рассмотрим модель технических фигур Меррилла и попробуем выяснить, насколько актуальны эти технические паттерны сегодня. Для этого мы создадим инструмент для их тестирования и применим данную модель к различным типам данных, такие как цена закрытия, ее максимумы и минимумы, индикаторы осцилляторного типа.
Создаем кроссплатформенный советник-сеточник (Часть III): сетка на коррекциях с мартингейлом Создаем кроссплатформенный советник-сеточник (Часть III): сетка на коррекциях с мартингейлом
В этой статье мы попробуем создать лучший из возможных советников, работающих по принципу сеточника. Как обычно, это будет кроссплатформенный советник, способный работать как в MetaTrader 4, так и в MetaTrader 5. Первый советник был хорош всем, кроме того, что не мог принести прибыль на длительном промежутке времени. Второй советник мог работать на интервалах более нескольких лет. Но принести более 50% прибыли в год при максимальной просадке менее 50% он был не способен.
Выцарапываем профит до последнего пипса Выцарапываем профит до последнего пипса
В статье сделана попытка совместить теорию с практикой на поприще алготрейдинга. Большинство разговоров на тему создания Торговых Систем связано с использованием исторических ценовых баров и различных индикаторов на них. Это то самое истоптанное поле, которое мы трогать не будем. Бары — это совсем искусственная сущность, поэтому возьмем что-то ближе к прото-информации — ценовые тики.
Управление оптимизацией (Часть 2): Создание ключевых объектов и логики приложения Управление оптимизацией (Часть 2): Создание ключевых объектов и логики приложения
Данная статья является продолжением предыдущей публикации на тему создания графического интерфейса для управления оптимизациями. В ней будет рассмотрена логика работы создаваемого дополнения. Создадим обертку для терминала MetaTrader 5 для его запуска как управляемый процесс через C#. А также будет рассмотрена работа с конфигурационными файлами и файлами настроек. Логика программы же будет поделена на две части: в первой описаны методы, вызываемые после нажатия на ту или иную клавишу, а вторая часть — запуск и управление оптимизациями.