English 中文 Español Deutsch 日本語 Português
Графика в библиотеке DoEasy (Часть 82): Рефакторинг объектов библиотеки и коллекция графических объектов

Графика в библиотеке DoEasy (Часть 82): Рефакторинг объектов библиотеки и коллекция графических объектов

MetaTrader 5Примеры | 11 сентября 2021, 12:19
1 682 0
Artyom Trishkin
Artyom Trishkin

Содержание


Концепция

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

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

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

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

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

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


Доработка классов библиотеки

Начнём доработку с внесения новых макроподстановок и перечислений в файле \MQL5\Include\DoEasy\Defines.mqh. Нам необходимо добавить типы всех объектов библиотеки — чтобы эти значения записывать в свойство "тип" объекта сразу при его создании.

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

//--- Параметры таймера коллекции чартов
#define COLLECTION_CHARTS_PAUSE        (500)                      // Пауза таймера коллекции чартов в миллисекундах
#define COLLECTION_CHARTS_COUNTER_STEP (16)                       // Шаг приращения счётчика таймера чартов
#define COLLECTION_CHARTS_COUNTER_ID   (9)                        // Идентификатор счётчика таймера чартов
//--- Параметры таймера коллекции графических объектов
#define COLLECTION_GRAPH_OBJ_PAUSE        (250)                   // Пауза таймера коллекции графических объектов в миллисекундах
#define COLLECTION_GRAPH_OBJ_COUNTER_STEP (16)                    // Шаг приращения счётчика таймера графических объектов
#define COLLECTION_GRAPH_OBJ_COUNTER_ID   (10)                    // Идентификатор счётчика таймера графических объектов

//--- Идентификаторы списков коллекций


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

//--- Идентификаторы списков коллекций
#define COLLECTION_HISTORY_ID          (0x777A)                   // Идентификатор списка исторической коллекции
#define COLLECTION_MARKET_ID           (0x777B)                   // Идентификатор списка рыночной коллекции
#define COLLECTION_EVENTS_ID           (0x777C)                   // Идентификатор списка коллекции событий
#define COLLECTION_ACCOUNT_ID          (0x777D)                   // Идентификатор списка коллекции аккаунтов
#define COLLECTION_SYMBOLS_ID          (0x777E)                   // Идентификатор списка коллекции символов
#define COLLECTION_SERIES_ID           (0x777F)                   // Идентификатор списка коллекции таймсерий
#define COLLECTION_BUFFERS_ID          (0x7780)                   // Идентификатор списка коллекции индикаторных буферов
#define COLLECTION_INDICATORS_ID       (0x7781)                   // Идентификатор списка коллекции индикаторов
#define COLLECTION_INDICATORS_DATA_ID  (0x7782)                   // Идентификатор списка коллекции индикаторных данных
#define COLLECTION_TICKSERIES_ID       (0x7783)                   // Идентификатор списка коллекции тиковых серий
#define COLLECTION_MBOOKSERIES_ID      (0x7784)                   // Идентификатор списка коллекции серий стаканов цен
#define COLLECTION_MQL5_SIGNALS_ID     (0x7785)                   // Идентификатор списка коллекции mql5-сигналов
#define COLLECTION_CHARTS_ID           (0x7786)                   // Идентификатор списка коллекции чартов
#define COLLECTION_CHART_WND_ID        (0x7787)                   // Идентификатор списка окон чартов
#define COLLECTION_GRAPH_OBJ_ID        (0x7788)                   // Идентификатор списка коллекции графических объектов

#define COLLECTION_ID_LIST_END         (COLLECTION_GRAPH_OBJ_ID)  // Конец списка идентификаторов коллекций

//--- Идентификаторы типов отложенных запросов

Именно значение этой метки + 1 у нас будет значением для первой константы перечисления типов объектов библиотеки, которое мы сейчас добавим:

//--- Параметры канваса
#define PAUSE_FOR_CANV_UPDATE          (16)                       // Частота обновления канваса
#define NULL_COLOR                     (0x00FFFFFF)               // Ноль для канваса с альфа-каналом
#define OUTER_AREA_SIZE                (16)                       // Размер одной стороны внешней области вокруг рабочего пространства
//+------------------------------------------------------------------+
//| Перечисления                                                     |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Список типов объектов библиотеки                                 |
//+------------------------------------------------------------------+
enum ENUM_OBJECT_DE_TYPE
  {
//--- Графика
   OBJECT_DE_TYPE_GBASE =  COLLECTION_ID_LIST_END+1,              // Тип объекта "Базовый объект всех графических объектов библиотеки"
   OBJECT_DE_TYPE_GELEMENT,                                       // Тип объекта "Графический элемент"
   OBJECT_DE_TYPE_GFORM,                                          // Тип объекта "Форма"
   OBJECT_DE_TYPE_GSHADOW,                                        // Тип объекта "Тень"
//--- Анимация
   OBJECT_DE_TYPE_GFRAME,                                         // Тип объекта "Один кадр анимации"
   OBJECT_DE_TYPE_GFRAME_TEXT,                                    // Тип объекта "Один кадр текстовой анимации"
   OBJECT_DE_TYPE_GFRAME_QUAD,                                    // Тип объекта "Один кадр прямоугольной анимации"
   OBJECT_DE_TYPE_GFRAME_GEOMETRY,                                // Тип объекта "Один кадр геометрической анимации"
   OBJECT_DE_TYPE_GANIMATIONS,                                    // Тип объекта "Анимации"
//--- Управление графическими объектами
   OBJECT_DE_TYPE_GELEMENT_CONTROL,                               // Тип объекта "Управление графическими элементами"
//--- Стандартные графические объекты
   OBJECT_DE_TYPE_GSTD_VLINE              =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_VLINE,             // Тип объекта "Вертикальная линия
   OBJECT_DE_TYPE_GSTD_HLINE              =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_HLINE,             // Тип объекта "Горизонтальная линия
   OBJECT_DE_TYPE_GSTD_TREND              =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_TREND,             // Тип объекта "Трендовая линия
   OBJECT_DE_TYPE_GSTD_TRENDBYANGLE       =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_TRENDBYANGLE,      // Тип объекта "Трендовая линия по углу
   OBJECT_DE_TYPE_GSTD_CYCLES             =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_CYCLES,            // Тип объекта "Циклические линии
   OBJECT_DE_TYPE_GSTD_ARROWED_LINE       =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ARROWED_LINE,      // Тип объекта "Линия со стрелкой"
   OBJECT_DE_TYPE_GSTD_CHANNEL            =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_CHANNEL,           // Тип объекта "Равноудаленный канал
   OBJECT_DE_TYPE_GSTD_STDDEVCHANNEL      =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_STDDEVCHANNEL,     // Тип объекта "Канал стандартного отклонения
   OBJECT_DE_TYPE_GSTD_REGRESSION         =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_REGRESSION,        // Тип объекта "Канал на линейной регрессии
   OBJECT_DE_TYPE_GSTD_PITCHFORK          =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_PITCHFORK,         // Тип объекта "Вилы Эндрюса
   OBJECT_DE_TYPE_GSTD_GANNLINE           =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_GANNLINE,          // Тип объекта "Линия Ганна
   OBJECT_DE_TYPE_GSTD_GANNFAN            =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_GANNFAN,           // Тип объекта "Веер Ганна
   OBJECT_DE_TYPE_GSTD_GANNGRID           =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_GANNGRID,          // Тип объекта "Сетка Ганна
   OBJECT_DE_TYPE_GSTD_FIBO               =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_FIBO,              // Тип объекта "Уровни Фибоначчи
   OBJECT_DE_TYPE_GSTD_FIBOTIMES          =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_FIBOTIMES,         // Тип объекта "Временные зоны Фибоначчи
   OBJECT_DE_TYPE_GSTD_FIBOFAN            =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_FIBOFAN,           // Тип объекта "Веер Фибоначчи
   OBJECT_DE_TYPE_GSTD_FIBOARC            =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_FIBOARC,           // Тип объекта "Дуги Фибоначчи
   OBJECT_DE_TYPE_GSTD_FIBOCHANNEL        =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_FIBOCHANNEL,       // Тип объекта "Канал Фибоначчи
   OBJECT_DE_TYPE_GSTD_EXPANSION          =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_EXPANSION,         // Тип объекта "Расширение Фибоначчи
   OBJECT_DE_TYPE_GSTD_ELLIOTWAVE5        =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ELLIOTWAVE5,       // Тип объекта "5-волновка Эллиота
   OBJECT_DE_TYPE_GSTD_ELLIOTWAVE3        =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ELLIOTWAVE3,       // Тип объекта "3-волновка Эллиота
   OBJECT_DE_TYPE_GSTD_RECTANGLE          =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_RECTANGLE,         // Тип объекта "Прямоугольник
   OBJECT_DE_TYPE_GSTD_TRIANGLE           =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_TRIANGLE,          // Тип объекта "Треугольник
   OBJECT_DE_TYPE_GSTD_ELLIPSE            =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ELLIPSE,           // Тип объекта "Эллипс
   OBJECT_DE_TYPE_GSTD_ARROW_THUMB_UP     =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ARROW_THUMB_UP,    // Тип объекта "Знак "Хорошо" (большой палец вверх)
   OBJECT_DE_TYPE_GSTD_ARROW_THUMB_DOWN   =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ARROW_THUMB_DOWN,  // Тип объекта "Знак "Плохо" (большой палец вниз)
   OBJECT_DE_TYPE_GSTD_ARROW_UP           =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ARROW_UP,          // Тип объекта "Знак "Стрелка вверх"
   OBJECT_DE_TYPE_GSTD_ARROW_DOWN         =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ARROW_DOWN,        // Тип объекта "Знак "Стрелка вниз"
   OBJECT_DE_TYPE_GSTD_ARROW_STOP         =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ARROW_STOP,        // Тип объекта "Знак "Стоп"
   OBJECT_DE_TYPE_GSTD_ARROW_CHECK        =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ARROW_CHECK,       // Тип объекта "Знак "Птичка" (галка)
   OBJECT_DE_TYPE_GSTD_ARROW_LEFT_PRICE   =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ARROW_LEFT_PRICE,  // Тип объекта "Левая ценовая метка
   OBJECT_DE_TYPE_GSTD_ARROW_RIGHT_PRICE  =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ARROW_RIGHT_PRICE, // Тип объекта "Правая ценовая метка
   OBJECT_DE_TYPE_GSTD_ARROW_BUY          =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ARROW_BUY,         // Тип объекта "Знак "Buy"
   OBJECT_DE_TYPE_GSTD_ARROW_SELL         =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ARROW_SELL,        // Тип объекта "Знак "Sell"
   OBJECT_DE_TYPE_GSTD_ARROW              =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ARROW,             // Тип объекта "Стрелка"
   OBJECT_DE_TYPE_GSTD_TEXT               =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_TEXT,              // Тип объекта "Текст"
   OBJECT_DE_TYPE_GSTD_LABEL              =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_LABEL,             // Тип объекта "Текстовая метка"
   OBJECT_DE_TYPE_GSTD_BUTTON             =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_BUTTON,            // Тип объекта "Кнопка"
   OBJECT_DE_TYPE_GSTD_CHART              =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_CHART,             // Тип объекта "График"
   OBJECT_DE_TYPE_GSTD_BITMAP             =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_BITMAP,            // Тип объекта "Рисунок"
   OBJECT_DE_TYPE_GSTD_BITMAP_LABEL       =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_BITMAP_LABEL,      // Тип объекта "Графическая метка"
   OBJECT_DE_TYPE_GSTD_EDIT               =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_EDIT,              // Тип объекта "Поле ввода"
   OBJECT_DE_TYPE_GSTD_EVENT              =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_EVENT,             // Тип объекта "Событие, соответствующий событию в экономическом календаре"
   OBJECT_DE_TYPE_GSTD_RECTANGLE_LABEL    =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_RECTANGLE_LABEL,   // Тип объекта "Прямоугольная метка для создания и оформления пользовательского графического интерфейса"
   
//--- Объекты
   OBJECT_DE_TYPE_BASE  =  OBJECT_DE_TYPE_GSTD_RECTANGLE_LABEL+1, // Базовый объект всех объектов библиотеки
   OBJECT_DE_TYPE_BASE_EXT,                                       // Расширенный базовый объект всех объектов библиотеки
   
   OBJECT_DE_TYPE_ACCOUNT,                                        // Тип объекта "Аккаунт"
   OBJECT_DE_TYPE_BOOK_ORDER,                                     // Тип объекта "Заявка стакана цен"
   OBJECT_DE_TYPE_BOOK_BUY,                                       // Тип объекта "Заявка на покупку в стакане"
   OBJECT_DE_TYPE_BOOK_BUY_MARKET,                                // Тип объекта "Заявка на покупку по рыночной цене в стакане"
   OBJECT_DE_TYPE_BOOK_SELL,                                      // Тип объекта "Заявка на продажу в стакане"
   OBJECT_DE_TYPE_BOOK_SELL_MARKET,                               // Тип объекта "Заявка на продажу по рыночной цене в стакане"
   OBJECT_DE_TYPE_BOOK_SNAPSHOT,                                  // Тип объекта "Снимок стакана цен"
   OBJECT_DE_TYPE_BOOK_SERIES,                                    // Тип объекта "Серия снимков стакана цен"
   
   OBJECT_DE_TYPE_CHART,                                          // Тип объекта "Чарт"
   OBJECT_DE_TYPE_CHART_WND,                                      // Тип объекта "Окно графика"
   OBJECT_DE_TYPE_CHART_WND_IND,                                  // Тип объекта "Индикатор окна графика"
   
   OBJECT_DE_TYPE_EVENT,                                          // Тип объекта "Событие"
   OBJECT_DE_TYPE_EVENT_BALANCE,                                  // Тип объекта "Событие балансовой операции"
   OBJECT_DE_TYPE_EVENT_MODIFY,                                   // Тип объекта "Событие модификации отложенного ордера или позиции"
   OBJECT_DE_TYPE_EVENT_ORDER_PLASED,                             // Тип объекта "Событие установки отложенного ордера"
   OBJECT_DE_TYPE_EVENT_ORDER_REMOVED,                            // Тип объекта "Событие удаления отложенного ордера"
   OBJECT_DE_TYPE_EVENT_POSITION_CLOSE,                           // Тип объекта "Событие закрытия позиции"
   OBJECT_DE_TYPE_EVENT_POSITION_OPEN,                            // Тип объекта "Событие открытия позиции"
   
   OBJECT_DE_TYPE_IND_BUFFER,                                     // Тип объекта "Индикаторный буфер"
   OBJECT_DE_TYPE_IND_BUFFER_ARROW,                               // Тип объекта "Буфер отрисовки стрелками"
   OBJECT_DE_TYPE_IND_BUFFER_BAR,                                 // Тип объекта "<Буфер баров"
   OBJECT_DE_TYPE_IND_BUFFER_CALCULATE,                           // Тип объекта "Расчётный буфер"
   OBJECT_DE_TYPE_IND_BUFFER_CANDLE,                              // Тип объекта "Буфер свечей"
   OBJECT_DE_TYPE_IND_BUFFER_FILLING,                             // Тип объекта "Буфер заливки"
   OBJECT_DE_TYPE_IND_BUFFER_HISTOGRAMM,                          // Тип объекта "Буфер гистограммы"
   OBJECT_DE_TYPE_IND_BUFFER_HISTOGRAMM2,                         // Тип объекта "Буфер гистограммы2"
   OBJECT_DE_TYPE_IND_BUFFER_LINE,                                // Тип объекта "Буфер линии"
   OBJECT_DE_TYPE_IND_BUFFER_SECTION,                             // Тип объекта "Буфер секции"
   OBJECT_DE_TYPE_IND_BUFFER_ZIGZAG,                              // Тип объекта "Буфер зигзага"
   OBJECT_DE_TYPE_INDICATOR,                                      // Тип объекта "Индикатор"
   OBJECT_DE_TYPE_IND_DATA,                                       // Тип объекта "Данные индикатора"
   OBJECT_DE_TYPE_IND_DATA_LIST,                                  // Тип объекта "Список данных индикатора"
   
   OBJECT_DE_TYPE_IND_AC,                                         // Тип объекта "Индикатор Accelerator Oscillator"
   OBJECT_DE_TYPE_IND_AD,                                         // Тип объекта "Индикатор Accumulation/Distribution"
   OBJECT_DE_TYPE_IND_ADX,                                        // Тип объекта "Индикатор Average Directional Index"
   OBJECT_DE_TYPE_IND_ADXW,                                       // Тип объекта "Индикатор ADX by Welles Wilder"
   OBJECT_DE_TYPE_IND_ALLIGATOR,                                  // Тип объекта "Индикатор Alligator"
   OBJECT_DE_TYPE_IND_AMA,                                        // Тип объекта "Индикатор Adaptive Moving Average"
   OBJECT_DE_TYPE_IND_AO,                                         // Тип объекта "Индикатор Awesome Oscillator"
   OBJECT_DE_TYPE_IND_ATR,                                        // Тип объекта "Индикатор Average True Range"
   OBJECT_DE_TYPE_IND_BANDS,                                      // Тип объекта "Индикатор Bollinger Bands®"
   OBJECT_DE_TYPE_IND_BEARS,                                      // Тип объекта "Индикатор Bears Power"
   OBJECT_DE_TYPE_IND_BULLS,                                      // Тип объекта "Индикатор Bulls Power"
   OBJECT_DE_TYPE_IND_BWMFI,                                      // Тип объекта "Индикатор Market Facilitation Index"
   OBJECT_DE_TYPE_IND_CCI,                                        // Тип объекта "Индикатор Commodity Channel Index"
   OBJECT_DE_TYPE_IND_CHAIKIN,                                    // Тип объекта "Индикатор Chaikin Oscillator"
   OBJECT_DE_TYPE_IND_CUSTOM,                                     // Тип объекта "Пользовательский индикатор"
   OBJECT_DE_TYPE_IND_DEMA,                                       // Тип объекта "Индикатор Double Exponential Moving Average"
   OBJECT_DE_TYPE_IND_DEMARKER,                                   // Тип объекта "Индикатор DeMarker"
   OBJECT_DE_TYPE_IND_ENVELOPES,                                  // Тип объекта "Индикатор Envelopes"
   OBJECT_DE_TYPE_IND_FORCE,                                      // Тип объекта "Индикатор Force Index"
   OBJECT_DE_TYPE_IND_FRACTALS,                                   // Тип объекта "Индикатор Fractals"
   OBJECT_DE_TYPE_IND_FRAMA,                                      // Тип объекта "Индикатор Fractal Adaptive Moving Average"
   OBJECT_DE_TYPE_IND_GATOR,                                      // Тип объекта "Индикатор Gator Oscillator"
   OBJECT_DE_TYPE_IND_ICHIMOKU,                                   // Тип объекта "Индикатор Ichimoku Kinko Hyo"
   OBJECT_DE_TYPE_IND_MA,                                         // Тип объекта "Индикатор Moving Average"
   OBJECT_DE_TYPE_IND_MACD,                                       // Тип объекта "Индикатор Moving Average Convergence/Divergence"
   OBJECT_DE_TYPE_IND_MFI,                                        // Тип объекта "Индикатор Money Flow Index"
   OBJECT_DE_TYPE_IND_MOMENTUM,                                   // Тип объекта "Индикатор Momentum"
   OBJECT_DE_TYPE_IND_OBV,                                        // Тип объекта "Индикатор On Balance Volume"
   OBJECT_DE_TYPE_IND_OSMA,                                       // Тип объекта "Индикатор Moving Average of Oscillator"
   OBJECT_DE_TYPE_IND_RSI,                                        // Тип объекта "Индикатор Relative Strength Index"
   OBJECT_DE_TYPE_IND_RVI,                                        // Тип объекта "Индикатор Relative Vigor Index"
   OBJECT_DE_TYPE_IND_SAR,                                        // Тип объекта "Индикатор Parabolic SAR"
   OBJECT_DE_TYPE_IND_STDEV,                                      // Тип объекта "Индикатор Standard Deviation"
   OBJECT_DE_TYPE_IND_STOCH,                                      // Тип объекта "Индикатор Stochastic Oscillator"
   OBJECT_DE_TYPE_IND_TEMA,                                       // Тип объекта "Индикатор Triple Exponential Moving Average"
   OBJECT_DE_TYPE_IND_TRIX,                                       // Тип объекта "Индикатор Triple Exponential Moving Averages Oscillator"
   OBJECT_DE_TYPE_IND_VIDYA,                                      // Тип объекта "Индикатор Variable Index Dynamic Average"
   OBJECT_DE_TYPE_IND_VOLUMES,                                    // Тип объекта "Индикатор Volumes"
   OBJECT_DE_TYPE_IND_WPR,                                        // Тип объекта "Индикатор Williams' Percent Range"
   
   OBJECT_DE_TYPE_MQL5_SIGNAL,                                    // Тип объекта "mql5-сигнал"
   
   OBJECT_DE_TYPE_ORDER_DEAL_POSITION,                            // Тип объекта "Ордер/Сделка/Позиция"
   OBJECT_DE_TYPE_HISTORY_BALANCE,                                // Тип объекта "Историческая балансовая операция"
   OBJECT_DE_TYPE_HISTORY_DEAL,                                   // Тип объекта "Историческая сделка"
   OBJECT_DE_TYPE_HISTORY_ORDER_MARKET,                           // Тип объекта "Исторический маркет-ордер"
   OBJECT_DE_TYPE_HISTORY_ORDER_PENDING,                          // Тип объекта "Исторический удалённый отложенный ордер"
   OBJECT_DE_TYPE_MARKET_ORDER,                                   // Тип объекта "Рыночный маркет-ордер"
   OBJECT_DE_TYPE_MARKET_PENDING,                                 // Тип объекта "Рыночный отложенный ордер"
   OBJECT_DE_TYPE_MARKET_POSITION,                                // Тип объекта "Рыночная позиция"
   
   OBJECT_DE_TYPE_PENDING_REQUEST,                                // Тип объекта "Отложенный торговый запрос"
   OBJECT_DE_TYPE_PENDING_REQUEST_POSITION_OPEN,                  // Тип объекта "Отложенный запрос на открытие позиции"
   OBJECT_DE_TYPE_PENDING_REQUEST_POSITION_CLOSE,                 // Тип объекта "Отложенный запрос на закрытие позиции"
   OBJECT_DE_TYPE_PENDING_REQUEST_POSITION_SLTP,                  // Тип объекта "Отложенный запрос на модификацию стоп-приказов позиции"
   OBJECT_DE_TYPE_PENDING_REQUEST_ORDER_PLACE,                    // Тип объекта "Отложенный запрос на установку отложенного ордера"
   OBJECT_DE_TYPE_PENDING_REQUEST_ORDER_REMOVE,                   // Тип объекта "Отложенный запрос на удаление отложенного ордера"
   OBJECT_DE_TYPE_PENDING_REQUEST_ORDER_MODIFY,                   // Тип объекта "Отложенный запрос на мидификацию параметров отложенного ордера"
   
   OBJECT_DE_TYPE_SERIES_BAR,                                     // Тип объекта "Бар"
   OBJECT_DE_TYPE_SERIES_PERIOD,                                  // Тип объекта "Таймсерия периода"
   OBJECT_DE_TYPE_SERIES_SYMBOL,                                  // Тип объекта "Таймсерии символа"
   
   OBJECT_DE_TYPE_SYMBOL,                                         // Тип объекта "Символ"
   OBJECT_DE_TYPE_SYMBOL_BONDS,                                   // Тип объекта "Символ облигация"
   OBJECT_DE_TYPE_SYMBOL_CFD,                                     // Тип объекта "CFD символ (Контракт на разницу цен)"
   OBJECT_DE_TYPE_SYMBOL_COLLATERAL,                              // Тип объекта "Символ неторгуемый актив"
   OBJECT_DE_TYPE_SYMBOL_COMMODITY,                               // Тип объекта "Товарный символ"
   OBJECT_DE_TYPE_SYMBOL_COMMON,                                  // Тип объекта "Символ общей группы"
   OBJECT_DE_TYPE_SYMBOL_CRYPTO,                                  // Тип объекта "Криптовалютный символ"
   OBJECT_DE_TYPE_SYMBOL_CUSTOM,                                  // Тип объекта "Пользовательский символ"
   OBJECT_DE_TYPE_SYMBOL_EXCHANGE,                                // Тип объекта "Биржевой символ"
   OBJECT_DE_TYPE_SYMBOL_FUTURES,                                 // Тип объекта "Символ фьючерс"
   OBJECT_DE_TYPE_SYMBOL_FX,                                      // Тип объекта "Форекс символ"
   OBJECT_DE_TYPE_SYMBOL_FX_EXOTIC,                               // Тип объекта "Форекс символ экзотик"
   OBJECT_DE_TYPE_SYMBOL_FX_MAJOR,                                // Тип объекта "Форекс символ мажор"
   OBJECT_DE_TYPE_SYMBOL_FX_MINOR,                                // Тип объекта "Форекс символ минор"
   OBJECT_DE_TYPE_SYMBOL_FX_RUB,                                  // Тип объекта "Форекс символ рубль"
   OBJECT_DE_TYPE_SYMBOL_INDEX,                                   // Тип объекта "Символ индекс"
   OBJECT_DE_TYPE_SYMBOL_INDICATIVE,                              // Тип объекта "Символ индикатив"
   OBJECT_DE_TYPE_SYMBOL_METALL,                                  // Тип объекта "Символ металл"
   OBJECT_DE_TYPE_SYMBOL_OPTION,                                  // Тип объекта "Символ опцион"
   OBJECT_DE_TYPE_SYMBOL_STOCKS,                                  // Тип объекта "Символ ценная бумага"
   
   OBJECT_DE_TYPE_TICK,                                           // Тип объекта "Тик"
   OBJECT_DE_TYPE_NEW_TICK,                                       // Тип объекта "Новый тик"
   OBJECT_DE_TYPE_TICKSERIES,                                     // Тип объекта "Серия тиковых данных"
   
   OBJECT_DE_TYPE_TRADE,                                          // Тип объекта "Торговый объект"
  };

//+------------------------------------------------------------------+
//| Данные для поиска и сортировки                                   |
//+------------------------------------------------------------------+

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

//--- Управление графическими объектами
   OBJECT_DE_TYPE_GELEMENT_CONTROL,                               // Тип объекта "Управление графическими элементами"
//--- Стандартные графические объекты
   OBJECT_DE_TYPE_GSTD_VLINE              =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_VLINE,             // Тип объекта "Вертикальная линия
   OBJECT_DE_TYPE_GSTD_HLINE              =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_HLINE,             // Тип объекта "Горизонтальная линия
   OBJECT_DE_TYPE_GSTD_TREND              =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_TREND,             // Тип объекта "Трендовая линия
   OBJECT_DE_TYPE_GSTD_TRENDBYANGLE       =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_TRENDBYANGLE,      // Тип объекта "Трендовая линия по углу
   OBJECT_DE_TYPE_GSTD_CYCLES             =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_CYCLES,            // Тип объекта "Циклические линии
   OBJECT_DE_TYPE_GSTD_ARROWED_LINE       =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ARROWED_LINE,      // Тип объекта "Линия со стрелкой"
   OBJECT_DE_TYPE_GSTD_CHANNEL            =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_CHANNEL,           // Тип объекта "Равноудаленный канал
   OBJECT_DE_TYPE_GSTD_STDDEVCHANNEL      =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_STDDEVCHANNEL,     // Тип объекта "Канал стандартного отклонения
   OBJECT_DE_TYPE_GSTD_REGRESSION         =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_REGRESSION,        // Тип объекта "Канал на линейной регрессии
   OBJECT_DE_TYPE_GSTD_PITCHFORK          =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_PITCHFORK,         // Тип объекта "Вилы Эндрюса
   OBJECT_DE_TYPE_GSTD_GANNLINE           =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_GANNLINE,          // Тип объекта "Линия Ганна
   OBJECT_DE_TYPE_GSTD_GANNFAN            =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_GANNFAN,           // Тип объекта "Веер Ганна
   OBJECT_DE_TYPE_GSTD_GANNGRID           =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_GANNGRID,          // Тип объекта "Сетка Ганна
   OBJECT_DE_TYPE_GSTD_FIBO               =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_FIBO,              // Тип объекта "Уровни Фибоначчи
   OBJECT_DE_TYPE_GSTD_FIBOTIMES          =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_FIBOTIMES,         // Тип объекта "Временные зоны Фибоначчи
   OBJECT_DE_TYPE_GSTD_FIBOFAN            =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_FIBOFAN,           // Тип объекта "Веер Фибоначчи
   OBJECT_DE_TYPE_GSTD_FIBOARC            =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_FIBOARC,           // Тип объекта "Дуги Фибоначчи
   OBJECT_DE_TYPE_GSTD_FIBOCHANNEL        =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_FIBOCHANNEL,       // Тип объекта "Канал Фибоначчи
   OBJECT_DE_TYPE_GSTD_EXPANSION          =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_EXPANSION,         // Тип объекта "Расширение Фибоначчи
   OBJECT_DE_TYPE_GSTD_ELLIOTWAVE5        =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ELLIOTWAVE5,       // Тип объекта "5-волновка Эллиота
   OBJECT_DE_TYPE_GSTD_ELLIOTWAVE3        =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ELLIOTWAVE3,       // Тип объекта "3-волновка Эллиота
   OBJECT_DE_TYPE_GSTD_RECTANGLE          =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_RECTANGLE,         // Тип объекта "Прямоугольник
   OBJECT_DE_TYPE_GSTD_TRIANGLE           =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_TRIANGLE,          // Тип объекта "Треугольник
   OBJECT_DE_TYPE_GSTD_ELLIPSE            =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ELLIPSE,           // Тип объекта "Эллипс
   OBJECT_DE_TYPE_GSTD_ARROW_THUMB_UP     =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ARROW_THUMB_UP,    // Тип объекта "Знак "Хорошо" (большой палец вверх)
   OBJECT_DE_TYPE_GSTD_ARROW_THUMB_DOWN   =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ARROW_THUMB_DOWN,  // Тип объекта "Знак "Плохо" (большой палец вниз)
   OBJECT_DE_TYPE_GSTD_ARROW_UP           =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ARROW_UP,          // Тип объекта "Знак "Стрелка вверх"
   OBJECT_DE_TYPE_GSTD_ARROW_DOWN         =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ARROW_DOWN,        // Тип объекта "Знак "Стрелка вниз"
   OBJECT_DE_TYPE_GSTD_ARROW_STOP         =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ARROW_STOP,        // Тип объекта "Знак "Стоп"
   OBJECT_DE_TYPE_GSTD_ARROW_CHECK        =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ARROW_CHECK,       // Тип объекта "Знак "Птичка" (галка)
   OBJECT_DE_TYPE_GSTD_ARROW_LEFT_PRICE   =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ARROW_LEFT_PRICE,  // Тип объекта "Левая ценовая метка
   OBJECT_DE_TYPE_GSTD_ARROW_RIGHT_PRICE  =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ARROW_RIGHT_PRICE, // Тип объекта "Правая ценовая метка
   OBJECT_DE_TYPE_GSTD_ARROW_BUY          =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ARROW_BUY,         // Тип объекта "Знак "Buy"
   OBJECT_DE_TYPE_GSTD_ARROW_SELL         =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ARROW_SELL,        // Тип объекта "Знак "Sell"
   OBJECT_DE_TYPE_GSTD_ARROW              =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_ARROW,             // Тип объекта "Стрелка"
   OBJECT_DE_TYPE_GSTD_TEXT               =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_TEXT,              // Тип объекта "Текст"
   OBJECT_DE_TYPE_GSTD_LABEL              =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_LABEL,             // Тип объекта "Текстовая метка"
   OBJECT_DE_TYPE_GSTD_BUTTON             =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_BUTTON,            // Тип объекта "Кнопка"
   OBJECT_DE_TYPE_GSTD_CHART              =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_CHART,             // Тип объекта "График"
   OBJECT_DE_TYPE_GSTD_BITMAP             =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_BITMAP,            // Тип объекта "Рисунок"
   OBJECT_DE_TYPE_GSTD_BITMAP_LABEL       =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_BITMAP_LABEL,      // Тип объекта "Графическая метка"
   OBJECT_DE_TYPE_GSTD_EDIT               =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_EDIT,              // Тип объекта "Поле ввода"
   OBJECT_DE_TYPE_GSTD_EVENT              =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_EVENT,             // Тип объекта "Событие, соответствующий событию в экономическом календаре"
   OBJECT_DE_TYPE_GSTD_RECTANGLE_LABEL    =  OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_RECTANGLE_LABEL,   // Тип объекта "Прямоугольная метка для создания и оформления пользовательского графического интерфейса"
   
//--- Объекты
   OBJECT_DE_TYPE_BASE  =  OBJECT_DE_TYPE_GSTD_RECTANGLE_LABEL+1, // Базовый объект всех объектов библиотеки

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

Далее. Нам нужно немного доработать списки свойств графических объектов, а именно — добавить перечисление принадлежности объекта программе или терминалу (как создан — программно или вручную) и вписать это свойство в перечисление целочисленных свойств графического объекта:

//+------------------------------------------------------------------+
//| Список принадлежностей графических объектов                      |
//+------------------------------------------------------------------+
enum ENUM_GRAPH_OBJ_BELONG
  {
   GRAPH_OBJ_BELONG_PROGRAM,                          // Графический объект принадлежит программе
   GRAPH_OBJ_BELONG_TERMINAL,                         // Графический объект не принадлежит программе
  };
//+------------------------------------------------------------------+
//| Список типов графических элементов                               |
//+------------------------------------------------------------------+
enum ENUM_GRAPH_ELEMENT_TYPE
  {
   GRAPH_ELEMENT_TYPE_ELEMENT,                        // Элемент
   GRAPH_ELEMENT_TYPE_SHADOW_OBJ,                     // Объект тени
   GRAPH_ELEMENT_TYPE_FORM,                           // Форма
   GRAPH_ELEMENT_TYPE_WINDOW,                         // Окно
  };
//+------------------------------------------------------------------+
//| Целочисленные свойства графического элемента на канвасе          |
//+------------------------------------------------------------------+
enum ENUM_CANV_ELEMENT_PROP_INTEGER
  {
   CANV_ELEMENT_PROP_ID = 0,                          // Идентификатор элемента
   CANV_ELEMENT_PROP_TYPE,                            // Тип графического элемента
   CANV_ELEMENT_PROP_BELONG,                          // Принадлежность графического элемента
   CANV_ELEMENT_PROP_NUM,                             // Номер элемента в списке
   CANV_ELEMENT_PROP_CHART_ID,                        // Идентификатор графика
   CANV_ELEMENT_PROP_WND_NUM,                         // Номер подокна графика
   CANV_ELEMENT_PROP_COORD_X,                         // X-координата формы на графике
   CANV_ELEMENT_PROP_COORD_Y,                         // Y-координата формы на графике
   CANV_ELEMENT_PROP_WIDTH,                           // Ширина элемента
   CANV_ELEMENT_PROP_HEIGHT,                          // Высота элемента
   CANV_ELEMENT_PROP_RIGHT,                           // Правая граница элемента
   CANV_ELEMENT_PROP_BOTTOM,                          // Нижняя граница элемента
   CANV_ELEMENT_PROP_ACT_SHIFT_LEFT,                  // Отступ активной зоны от левого края элемента
   CANV_ELEMENT_PROP_ACT_SHIFT_TOP,                   // Отступ активной зоны от верхнего края элемента
   CANV_ELEMENT_PROP_ACT_SHIFT_RIGHT,                 // Отступ активной зоны от правого края элемента
   CANV_ELEMENT_PROP_ACT_SHIFT_BOTTOM,                // Отступ активной зоны от нижнего края элемента
   CANV_ELEMENT_PROP_MOVABLE,                         // Флаг перемещаемости элемента
   CANV_ELEMENT_PROP_ACTIVE,                          // Флаг активности элемента
   CANV_ELEMENT_PROP_COORD_ACT_X,                     // X-координата активной зоны элемента
   CANV_ELEMENT_PROP_COORD_ACT_Y,                     // Y-координата активной зоны элемента
   CANV_ELEMENT_PROP_ACT_RIGHT,                       // Правая граница активной зоны элемента
   CANV_ELEMENT_PROP_ACT_BOTTOM,                      // Нижняя граница активной зоны элемента
  };
#define CANV_ELEMENT_PROP_INTEGER_TOTAL (22)          // Общее количество целочисленных свойств
#define CANV_ELEMENT_PROP_INTEGER_SKIP  (0)           // Количество неиспользуемых в сортировке целочисленных свойств
//+------------------------------------------------------------------+

Соответственно, раз мы вписали новое свойство, то должны и увеличить общее количество этих свойств (с 21 до 22), а также нам нужно добавить сортировку по этому свойству в перечисление возможных критериев сортировки графических объектов:

//+------------------------------------------------------------------+
//| Возможные критерии сортировки графических элементов на канвасе   |
//+------------------------------------------------------------------+
#define FIRST_CANV_ELEMENT_DBL_PROP  (CANV_ELEMENT_PROP_INTEGER_TOTAL-CANV_ELEMENT_PROP_INTEGER_SKIP)
#define FIRST_CANV_ELEMENT_STR_PROP  (CANV_ELEMENT_PROP_INTEGER_TOTAL-CANV_ELEMENT_PROP_INTEGER_SKIP+CANV_ELEMENT_PROP_DOUBLE_TOTAL-CANV_ELEMENT_PROP_DOUBLE_SKIP)
enum ENUM_SORT_CANV_ELEMENT_MODE
  {
//--- Сортировка по целочисленным свойствам
   SORT_BY_CANV_ELEMENT_ID = 0,                       // Сортировать по идентификатору элемента
   SORT_BY_CANV_ELEMENT_TYPE,                         // Сортировать по типу графического элемента
   SORT_BY_CANV_ELEMENT_BELONG,                       // Сортировать по принадлежности графического элемента
   SORT_BY_CANV_ELEMENT_NUM,                          // Сортировать по номеру формы в списке
   SORT_BY_CANV_ELEMENT_CHART_ID,                     // Сортировать по идентификатору графика
   SORT_BY_CANV_ELEMENT_WND_NUM,                      // Сортировать по номеру окна графика
   SORT_BY_CANV_ELEMENT_COORD_X,                      // Сортировать по X-координате элемента на графике
   SORT_BY_CANV_ELEMENT_COORD_Y,                      // Сортировать по Y-координате элемента на графике
   SORT_BY_CANV_ELEMENT_WIDTH,                        // Сортировать по ширине элемента
   SORT_BY_CANV_ELEMENT_HEIGHT,                       // Сортировать по высоте элемента
   SORT_BY_CANV_ELEMENT_RIGHT,                        // Сортировать по правой границе элемента
   SORT_BY_CANV_ELEMENT_BOTTOM,                       // Сортировать по нижней границе элемента
   SORT_BY_CANV_ELEMENT_ACT_SHIFT_LEFT,               // Сортировать по отступу активной зоны от левого края элемента
   SORT_BY_CANV_ELEMENT_ACT_SHIFT_TOP,                // Сортировать по отступу активной зоны от верхнего края элемента
   SORT_BY_CANV_ELEMENT_ACT_SHIFT_RIGHT,              // Сортировать по отступу активной зоны от правого края элемента
   SORT_BY_CANV_ELEMENT_ACT_SHIFT_BOTTOM,             // Сортировать по отступу активной зоны от нижнего края элемента
   SORT_BY_CANV_ELEMENT_MOVABLE,                      // Сортировать по флагу перемещаемости элемента
   SORT_BY_CANV_ELEMENT_ACTIVE,                       // Сортировать по флагу активности элемента
   SORT_BY_CANV_ELEMENT_COORD_ACT_X,                  // Сортировать по X-координате активной зоны элемента
   SORT_BY_CANV_ELEMENT_COORD_ACT_Y,                  // Сортировать по Y-координате активной зоны элемента
   SORT_BY_CANV_ELEMENT_ACT_RIGHT,                    // Сортировать по правой границе активной зоны элемента
   SORT_BY_CANV_ELEMENT_ACT_BOTTOM,                   // Сортировать по нижней границе активной зоны элемента
//--- Сортировка по вещественным свойствам

//--- Сортировка по строковым свойствам
   SORT_BY_CANV_ELEMENT_NAME_OBJ = FIRST_CANV_ELEMENT_STR_PROP,// Сортировать по имени объекта-элемента
   SORT_BY_CANV_ELEMENT_NAME_RES,                     // Сортировать по имени графического ресурса
  };
//+------------------------------------------------------------------+


В файле \MQL5\Include\DoEasy\Data.mqh добавим индексы новых сообщений библиотеки для коллекции графических объектов:

//--- CShadowObj
   MSG_SHADOW_OBJ_IMG_SMALL_BLUR_LARGE,               // Ошибка! Размер изображения очень маленький или очень большое размытие

//--- CGraphElementsCollection
   MSG_CHART_OBJ_COLLECTION_ERR_OBJ_ALREADY_EXISTS,   // Ошибка. Уже существует объект управления чартами с идентификатором чарта 
   MSG_CHART_OBJ_COLLECTION_ERR_FAILED_CREATE_CTRL_OBJ,// Не удалось создать объект управления чартами с идентификатором чарта 
   
  };
//+------------------------------------------------------------------+

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

//--- CShadowObj
   {"Ошибка! Размер изображения очень маленький или очень большое размытие","Error! Image size is very small or very large blur"},
      
//--- CGraphElementsCollection
   {"Ошибка. Уже существует объект управления чартами с идентификатором чарта ","Error. A chart control object already exists with chart id "},
   {"Не удалось создать объект управления чартами с идентификатором чарта ","Failed to create chart control object with chart id "},

  };
//+---------------------------------------------------------------------+


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

Многие объекты библиотеки являются наследниками базового объекта всех объектов библиотеки, и в нём уже есть переменная m_type, которая хранит значение типа объекта, и есть виртуальный метод Type(), возвращающий установленный в переменную тип объекта. Соответственно, нам достаточно будет в конструкторах его наследников указать значение переменной m_type, соответствующее типу объекта.

Так как мы ввели понятие принадлежности объекта, то определять эту принадлежность мы будем по наличию в имени графического объекта наименования программы. Для этого в базовом объекте всех объектов библиотеки в файле \MQL5\Include\DoEasy\Objects\BaseObj.mqh создадим в защищённой секции класса новую переменную для хранения имени программы:

//+------------------------------------------------------------------+
//| Класс базового объекта для всех объектов библиотеки              |
//+------------------------------------------------------------------+
class CBaseObj : public CObject
  {
protected:
   CGraphElmControl  m_graph_elm;                              // Экземпляр класса управления графическими элементами
   ENUM_LOG_LEVEL    m_log_level;                              // Уровень логирования
   ENUM_PROGRAM_TYPE m_program;                                // Тип программы
   bool              m_first_start;                            // Флаг первого запуска
   bool              m_use_sound;                              // Флаг проигрывания установленного объекту звука
   bool              m_available;                              // Флаг использования объекта-наследника в программе
   int               m_global_error;                           // Код глобальной ошибки
   long              m_chart_id_main;                          // Идентификатор графика управляющей программы
   long              m_chart_id;                               // Идентификатор графика
   string            m_name_program;                           // Имя программы
   string            m_name;                                   // Наименование объекта
   string            m_folder_name;                            // Имя папки хранения объектов-наследников CBaseObj
   string            m_sound_name;                             // Имя звукового файла объекта
   int               m_type;                                   // Тип объекта (соответствует типу объекта из перечисления ENUM_OBJECT_DE_TYPE)

public:

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

//--- Конструктор
                     CBaseObj() : m_program((ENUM_PROGRAM_TYPE)::MQLInfoInteger(MQL_PROGRAM_TYPE)),
                                  m_name_program(::MQLInfoString(MQL_PROGRAM_NAME)),
                                  m_global_error(ERR_SUCCESS),
                                  m_log_level(LOG_LEVEL_ERROR_MSG),
                                  m_chart_id_main(::ChartID()),
                                  m_chart_id(::ChartID()),
                                  m_folder_name(DIRECTORY),
                                  m_sound_name(""),
                                  m_name(__FUNCTION__),
                                  m_type(OBJECT_DE_TYPE_BASE),
                                  m_use_sound(false),
                                  m_available(true),
                                  m_first_start(true) {}
  };
//+------------------------------------------------------------------+

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

//+------------------------------------------------------------------+
//| Конструктор                                                      |
//+------------------------------------------------------------------+
CBaseObjExt::CBaseObjExt() : m_hash_sum(0),m_hash_sum_prev(0),
                             m_is_event(false),m_event_code(WRONG_VALUE),
                             m_long_prop_total(0),
                             m_double_prop_total(0)
  {
   this.m_type=OBJECT_DE_TYPE_BASE_EXT;
   ::ArrayResize(this.m_long_prop_event,0,100);
   ::ArrayResize(this.m_double_prop_event,0,100);
   ::ArrayResize(this.m_long_prop_event_prev,0,100);
   ::ArrayResize(this.m_double_prop_event_prev,0,100);
   ::ZeroMemory(this.m_tick);
   this.m_digits_currency=(#ifdef __MQL5__ (int)::AccountInfoInteger(ACCOUNT_CURRENCY_DIGITS) #else 2 #endif);
   this.m_list_events.Clear();
   this.m_list_events.Sort();
   this.m_list_events_base.Clear();
   this.m_list_events_base.Sort();
  }
//+------------------------------------------------------------------+

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

Для объекта-аккаунта в файле \MQL5\Include\DoEasy\Objects\Accounts\Account.mqh это будет выглядеть так (целиком конструктор):

//+------------------------------------------------------------------+
//| Конструктор                                                      |
//+------------------------------------------------------------------+
CAccount::CAccount(void)
  {
   this.m_type=OBJECT_DE_TYPE_ACCOUNT;
//--- Инициализация контролируемых данных
   this.SetControlDataArraySizeLong(ACCOUNT_PROP_INTEGER_TOTAL);
   this.SetControlDataArraySizeDouble(ACCOUNT_PROP_DOUBLE_TOTAL);
   this.ResetChangesParams();
   this.ResetControlsParams();
  
//--- Сохранение целочисленных свойств
   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_long_prop[ACCOUNT_PROP_FIFO_CLOSE]                         = (#ifdef __MQL5__::TerminalInfoInteger(TERMINAL_BUILD)<2155 ? false : ::AccountInfoInteger(ACCOUNT_FIFO_CLOSE) #else false #endif );
   
//--- Сохранение вещественных свойств
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_BALANCE)]          = ::AccountInfoDouble(ACCOUNT_BALANCE);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_CREDIT)]           = ::AccountInfoDouble(ACCOUNT_CREDIT);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_PROFIT)]           = ::AccountInfoDouble(ACCOUNT_PROFIT);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_EQUITY)]           = ::AccountInfoDouble(ACCOUNT_EQUITY);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_MARGIN)]           = ::AccountInfoDouble(ACCOUNT_MARGIN);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_MARGIN_FREE)]      = ::AccountInfoDouble(ACCOUNT_MARGIN_FREE);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_MARGIN_LEVEL)]     = ::AccountInfoDouble(ACCOUNT_MARGIN_LEVEL);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_MARGIN_SO_CALL)]   = ::AccountInfoDouble(ACCOUNT_MARGIN_SO_CALL);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_MARGIN_SO_SO)]     = ::AccountInfoDouble(ACCOUNT_MARGIN_SO_SO);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_MARGIN_INITIAL)]   = ::AccountInfoDouble(ACCOUNT_MARGIN_INITIAL);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_MARGIN_MAINTENANCE)]=::AccountInfoDouble(ACCOUNT_MARGIN_MAINTENANCE);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_ASSETS)]           = ::AccountInfoDouble(ACCOUNT_ASSETS);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_LIABILITIES)]      = ::AccountInfoDouble(ACCOUNT_LIABILITIES);
   this.m_double_prop[this.IndexProp(ACCOUNT_PROP_COMMISSION_BLOCKED)]=::AccountInfoDouble(ACCOUNT_COMMISSION_BLOCKED);
   
//--- Сохранение строковых свойств
   this.m_string_prop[this.IndexProp(ACCOUNT_PROP_NAME)]             = ::AccountInfoString(ACCOUNT_NAME);
   this.m_string_prop[this.IndexProp(ACCOUNT_PROP_SERVER)]           = ::AccountInfoString(ACCOUNT_SERVER);
   this.m_string_prop[this.IndexProp(ACCOUNT_PROP_CURRENCY)]         = ::AccountInfoString(ACCOUNT_CURRENCY);
   this.m_string_prop[this.IndexProp(ACCOUNT_PROP_COMPANY)]          = ::AccountInfoString(ACCOUNT_COMPANY);

//--- Имя объекта-аккаунта, тип объекта и тип аккаунта (MetaTrader 5 или 4)
   this.m_name=CMessage::Text(MSG_LIB_PROP_ACCOUNT)+" "+(string)this.Login()+": "+this.Name()+" ("+this.Company()+")";
   this.m_type=COLLECTION_ACCOUNT_ID;
   this.m_type_server=(::TerminalInfoString(TERMINAL_NAME)=="MetaTrader 5" ? 5 : 4);

//--- Заполнение текущих данных аккаунта
   for(int i=0;i<ACCOUNT_PROP_INTEGER_TOTAL;i++)
      this.m_long_prop_event[i][3]=this.m_long_prop[i];
   for(int i=0;i<ACCOUNT_PROP_DOUBLE_TOTAL;i++)
      this.m_double_prop_event[i][3]=this.m_double_prop[i];

//--- Обновление данных в базовом объекте и поиск изменений
   CBaseObjExt::Refresh();
  }
//+-------------------------------------------------------------------+

Как видим всё, что нам нужно — это задать защищённой переменной m_type нужный тип объекта. В данном случае — это тип "аккаунт". Запись в эту переменную нового значение типа объекта, которая объявлена в классе базового объекта (и в нём же ей присвоен тип "базовый объект"), переопределит тип объекта с "базового" на "аккаунт". И теперь виртуальный метод Type(), возвращающий значение переменной m_type и так же реализованный в базовом объекте, будет возвращать значение переменной, переопределённое в конструкторе класса объекта-аккаунта.

Класс абстрактной заявки стакана цен в файле \MQL5\Include\DoEasy\Objects\Book\MarketBookOrd.mqh имеет два конструктора — по умолчанию и параметрический. В обоих конструкторах пропишем тип объекта.

В конструкторе по умолчанию:

//--- Сравнивает объекты CMarketBookOrd между собой по всем свойствам (для поиска равных объектов-запросов)
   bool              IsEqual(CMarketBookOrd* compared_req) const;
   
//--- Конструктор по умолчанию
                     CMarketBookOrd(){ this.m_type=OBJECT_DE_TYPE_BOOK_ORDER; }
protected:
//--- Защищённый параметрический конструктор
                     CMarketBookOrd(const ENUM_MBOOK_ORD_STATUS status,const MqlBookInfo &book_info,const string symbol);
                     
public:
//+------------------------------------------------------------------+ 
//|Методы упрощённого доступа к свойствам объекта-запроса стакана цен|
//+------------------------------------------------------------------+

и в параметрическом:

//+------------------------------------------------------------------+
//| Защищённый параметрический конструктор                           |
//+------------------------------------------------------------------+
CMarketBookOrd::CMarketBookOrd(const ENUM_MBOOK_ORD_STATUS status,const MqlBookInfo &book_info,const string symbol)
  {
   this.m_type=OBJECT_DE_TYPE_BOOK_ORDER;
//--- Сохраняем Digits символа
   this.m_digits=(int)::SymbolInfoInteger(symbol,SYMBOL_DIGITS);
//--- Сохраняем целочисленные свойства объекта
   this.SetProperty(MBOOK_ORD_PROP_STATUS,status);
   this.SetProperty(MBOOK_ORD_PROP_TYPE,book_info.type);
   this.SetProperty(MBOOK_ORD_PROP_VOLUME,book_info.volume);
//--- Сохраняем вещественные свойства объекта
   this.SetProperty(MBOOK_ORD_PROP_PRICE,book_info.price);
   this.SetProperty(MBOOK_ORD_PROP_VOLUME_REAL,book_info.volume_real);
//--- Сохраняем дополнительные свойства объекта
   this.SetProperty(MBOOK_ORD_PROP_SYMBOL,(symbol==NULL || symbol=="" ? ::Symbol() : symbol));
//--- Время заявки отсутствует в параметрах, и оно учитывается в классе снимка стакана цен. Обнуляем время
   this.SetProperty(MBOOK_ORD_PROP_TIME_MSC,0);
  }
//+------------------------------------------------------------------+

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

Заявка на покупку в стакане в файле \MQL5\Include\DoEasy\Objects\Book\MarketBookBuy.mqh:

//+------------------------------------------------------------------+
//| Заявка на покупку в стакане                                      |
//+------------------------------------------------------------------+
class CMarketBookBuy : public CMarketBookOrd
  {
private:

public:
   //--- Конструктор
                     CMarketBookBuy(const string symbol,const MqlBookInfo &book_info) :
                        CMarketBookOrd(MBOOK_ORD_STATUS_BUY,book_info,symbol) { this.m_type=OBJECT_DE_TYPE_BOOK_BUY; }
   //--- Поддерживаемые свойства ордера (1) вещественные, (2) целочисленные
   virtual bool      SupportProperty(ENUM_MBOOK_ORD_PROP_DOUBLE property);
   virtual bool      SupportProperty(ENUM_MBOOK_ORD_PROP_INTEGER property);
//--- Возвращает краткое наименование объекта
   virtual string    Header(const bool symbol=false);
//--- Возвращает описание типа (ENUM_BOOK_TYPE) заявки
   virtual string    TypeDescription(void);
  };
//+------------------------------------------------------------------+

Заявка на покупку по рыночной цене в стакане в файле \MQL5\Include\DoEasy\Objects\Book\MarketBookBuyMarket.mqh:

//+------------------------------------------------------------------+
//| Заявка на покупку по рыночной цене в стакане                     |
//+------------------------------------------------------------------+
class CMarketBookBuyMarket : public CMarketBookOrd
  {
private:

public:
   //--- Конструктор
                     CMarketBookBuyMarket(const string symbol,const MqlBookInfo &book_info) :
                        CMarketBookOrd(MBOOK_ORD_STATUS_BUY,book_info,symbol) { this.m_type=OBJECT_DE_TYPE_BOOK_BUY_MARKET; }
   //--- Поддерживаемые свойства ордера (1) вещественные, (2) целочисленные
   virtual bool      SupportProperty(ENUM_MBOOK_ORD_PROP_DOUBLE property);
   virtual bool      SupportProperty(ENUM_MBOOK_ORD_PROP_INTEGER property);
//--- Возвращает краткое наименование объекта
   virtual string    Header(const bool symbol=false);
//--- Возвращает описание типа (ENUM_BOOK_TYPE) заявки
   virtual string    TypeDescription(void);
  };
//+------------------------------------------------------------------+

Заявка на продажу в стакане в файле \MQL5\Include\DoEasy\Objects\Book\MarketBookSell.mqh:

//+------------------------------------------------------------------+
//| Заявка на продажу в стакане                                      |
//+------------------------------------------------------------------+
class CMarketBookSell : public CMarketBookOrd
  {
private:

public:
   //--- Конструктор
                     CMarketBookSell(const string symbol,const MqlBookInfo &book_info) :
                        CMarketBookOrd(MBOOK_ORD_STATUS_SELL,book_info,symbol) { this.m_type=OBJECT_DE_TYPE_BOOK_SELL; }
   //--- Поддерживаемые свойства ордера (1) вещественные, (2) целочисленные
   virtual bool      SupportProperty(ENUM_MBOOK_ORD_PROP_DOUBLE property);
   virtual bool      SupportProperty(ENUM_MBOOK_ORD_PROP_INTEGER property);
//--- Возвращает краткое наименование объекта
   virtual string    Header(const bool symbol=false);
//--- Возвращает описание типа (ENUM_BOOK_TYPE) заявки
   virtual string    TypeDescription(void);
  };
//+------------------------------------------------------------------+

Заявка на продажу по рыночной цене в стакане в файле \MQL5\Include\DoEasy\Objects\Book\MarketBookSellMarket.mqh:

//+------------------------------------------------------------------+
//| Заявка на продажу по рыночной цене в стакане                     |
//+------------------------------------------------------------------+
class CMarketBookSellMarket : public CMarketBookOrd
  {
private:

public:
   //--- Конструктор
                     CMarketBookSellMarket(const string symbol,const MqlBookInfo &book_info) :
                        CMarketBookOrd(MBOOK_ORD_STATUS_SELL,book_info,symbol) { this.m_type=OBJECT_DE_TYPE_BOOK_SELL_MARKET; }
   //--- Поддерживаемые свойства ордера (1) вещественные, (2) целочисленные
   virtual bool      SupportProperty(ENUM_MBOOK_ORD_PROP_DOUBLE property);
   virtual bool      SupportProperty(ENUM_MBOOK_ORD_PROP_INTEGER property);
//--- Возвращает краткое наименование объекта
   virtual string    Header(const bool symbol=false);
//--- Возвращает описание типа (ENUM_BOOK_TYPE) заявки
   virtual string    TypeDescription(void);
  };
//+------------------------------------------------------------------+

Класс "Снимок стакана цен" в файле \MQL5\Include\DoEasy\Objects\Book\MarketBookSnapshot.mqh имеет два конструктора — по умолчанию и параметрический. Соответственно, здесь нам нужно в обоих конструкторах указать тип объекта.

В конструкторе по умолчанию:

//--- Возвращает наименование снимка стакана цен
   string            Header(void);
//--- Выводит в журнал (1) описание, (2) краткое описание снимка стакана цен
   virtual void      Print(const bool full_prop=false,const bool dash=false);
   virtual void      PrintShort(const bool dash=false,const bool symbol=false);

//--- Конструкторы
                     CMBookSnapshot(){ this.m_type=OBJECT_DE_TYPE_BOOK_SNAPSHOT; }
                     CMBookSnapshot(const string symbol,const long time,MqlBookInfo &book_array[]);
//+------------------------------------------------------------------+ 
//|Методы упрощённого доступа к свойствам объекта-снимка стакана цен |
//+------------------------------------------------------------------+

и в параметрическом:

//+------------------------------------------------------------------+
//| Параметрический конструктор                                      |
//+------------------------------------------------------------------+
CMBookSnapshot::CMBookSnapshot(const string symbol,const long time,MqlBookInfo &book_array[]) : m_time(time)
  {
   this.m_type=OBJECT_DE_TYPE_BOOK_SNAPSHOT;
   //--- Устанавливаем символ
   this.SetSymbol(symbol);
   //--- Очищаем список
   this.m_list.Clear();
   //--- В цикле по массиву структур
   int total=::ArraySize(book_array);
   this.m_volume_buy=this.m_volume_sell=0;
   this.m_volume_buy_real=this.m_volume_sell_real=0;
   for(int i=0;i<total;i++)
     {
      //--- Создаём объекты-заявки текущего снимка стакана цен в зависимости от типа заявки
      CMarketBookOrd *mbook_ord=NULL;
      switch(book_array[i].type)
        {
         case BOOK_TYPE_BUY         : mbook_ord=new CMarketBookBuy(this.m_symbol,book_array[i]);         break;
         case BOOK_TYPE_SELL        : mbook_ord=new CMarketBookSell(this.m_symbol,book_array[i]);        break;
         case BOOK_TYPE_BUY_MARKET  : mbook_ord=new CMarketBookBuyMarket(this.m_symbol,book_array[i]);   break;
         case BOOK_TYPE_SELL_MARKET : mbook_ord=new CMarketBookSellMarket(this.m_symbol,book_array[i]);  break;
         default: break;
        }
      if(mbook_ord==NULL)
         continue;
      //--- Устанавливаем заявке время снимка стакана цен
      mbook_ord.SetTime(this.m_time);

      //--- Ставим списку флаг сортированного списка (по значению цены) и добавляем в него текущий объект-заявку
      //--- Если не удалось добавить объект в список заявок стакана цен - удаляем объект-заявку
      this.m_list.Sort(SORT_BY_MBOOK_ORD_PRICE);
      if(!this.m_list.InsertSort(mbook_ord))
         delete mbook_ord;
      //--- Если объект-заявка успешно добавлен в список заявок снимка стакана - дополняем общие объёмы снимка
      else
        {
         switch(mbook_ord.TypeOrd())
           {
            case BOOK_TYPE_BUY         : 
              this.m_volume_buy+=mbook_ord.Volume(); 
              this.m_volume_buy_real+=mbook_ord.VolumeReal();
              break;
            case BOOK_TYPE_SELL        : 
              this.m_volume_sell+=mbook_ord.Volume(); 
              this.m_volume_sell_real+=mbook_ord.VolumeReal();
              break;
            case BOOK_TYPE_BUY_MARKET  : 
              this.m_volume_buy+=mbook_ord.Volume(); 
              this.m_volume_buy_real+=mbook_ord.VolumeReal();
              break;
            case BOOK_TYPE_SELL_MARKET : 
              this.m_volume_buy+=mbook_ord.Volume(); 
              this.m_volume_buy_real+=mbook_ord.VolumeReal();
              break;
            default: break;
           }
        }
     }
  }
//+------------------------------------------------------------------+

Класс "Серия снимков стакана цен" также имеет два конструктора, и в обоих пропишем тип объекта.

По умолчанию:

//--- Выводит в журнал (1) описание, (2) краткое описание серии снимков стакана цен
   virtual void      Print(const bool full_prop=false,const bool dash=false);
   virtual void      PrintShort(const bool dash=false,const bool symbol=false);

//--- Конструкторы
                     CMBookSeries(){ this.m_type=OBJECT_DE_TYPE_BOOK_SERIES; }
                     CMBookSeries(const string symbol,const uint required=0);

//+------------------------------------------------------------------+ 
//| Методы работы с объектами и доступа к их свойствам               |
//+------------------------------------------------------------------+

и в параметрическом:

//+------------------------------------------------------------------+
//| Параметрический конструктор                                      |
//+------------------------------------------------------------------+
CMBookSeries::CMBookSeries(const string symbol,const uint required=0) : m_symbol(symbol)
  {
   this.m_type=OBJECT_DE_TYPE_BOOK_SERIES;
   this.m_list.Clear();
   this.m_list.Sort(SORT_BY_MBOOK_ORD_TIME_MSC);
   this.SetRequiredUsedDays(required);
  }
//+------------------------------------------------------------------+


Класс абстрактного события в файле \MQL5\Include\DoEasy\Objects\Events\Event.mqh не является наследником ни базового объекта, ни расширенного базового объекта всех объектов библиотеки. Соответственно, в нём нет переменной m_type, нет виртуального метода Type(), возвращающего значение этой переменной (такой метод есть в базовом объекте CObject, от которого унаследован данный класс, но он возвращает 0 и должен быть переопределён в наследниках). Значит — нам необходимо добавить переменную и метод и прописать в конструкторах класса нужный тип для созданной переменной:

//+------------------------------------------------------------------+
//| Класс абстрактного события                                       |
//+------------------------------------------------------------------+
class CEvent : public CObject
  {
private:
   int               m_event_code;                                   // Код события
//--- Возвращает индекс массива, по которому фактически расположено (1) double-свойство и (2) string-свойство события
   int               IndexProp(ENUM_EVENT_PROP_DOUBLE property)const { return(int)property-EVENT_PROP_INTEGER_TOTAL;                         }
   int               IndexProp(ENUM_EVENT_PROP_STRING property)const { return(int)property-EVENT_PROP_INTEGER_TOTAL-EVENT_PROP_DOUBLE_TOTAL; }
protected:
   ENUM_TRADE_EVENT  m_trade_event;                                  // Торговое событие
   bool              m_is_hedge;                                     // Флаг хедж-счёта
   long              m_chart_id_main;                                // Идентификатор графика управляющей программы
   int               m_type;                                         // Тип объекта
   int               m_digits;                                       // Digits() символа
   int               m_digits_acc;                                   // Количество знаков после запятой для валюты счета
   long              m_long_prop[EVENT_PROP_INTEGER_TOTAL];          // Целочисленные свойства события
   double            m_double_prop[EVENT_PROP_DOUBLE_TOTAL];         // Вещественные свойства события
   string            m_string_prop[EVENT_PROP_STRING_TOTAL];         // Строковые свойства события
//--- возвращает факт наличия флага в торговом событии
   bool              IsPresentEventFlag(const int event_code)  const { return (this.m_event_code & event_code)==event_code;                  }
//--- Возвращает (1) заданный магический номер, идентификатор (2) первой группы, (3) второй группы, (4) отложенного запроса из значения магика
   ushort            GetMagicID(void)                          const { return ushort(this.Magic() & 0xFFFF);                                 }
   uchar             GetGroupID1(void)                         const { return uchar(this.Magic()>>16) & 0x0F;                                }
   uchar             GetGroupID2(void)                         const { return uchar((this.Magic()>>16) & 0xF0)>>4;                           }
   uchar             GetPendReqID(void)                        const { return uchar(this.Magic()>>24) & 0xFF;                                }
//--- Защищённый параметрический конструктор
                     CEvent(const ENUM_EVENT_STATUS event_status,const int event_code,const ulong ticket);
public:
//--- Конструктор по умолчанию
                     CEvent(void){ this.m_type=OBJECT_DE_TYPE_EVENT; }
 
//--- Устанавливает (1) целочисленное, (2) вещественное и (3) строковое свойство события
   void              SetProperty(ENUM_EVENT_PROP_INTEGER property,long value) { this.m_long_prop[property]=value;                            }
   void              SetProperty(ENUM_EVENT_PROP_DOUBLE property,double value){ this.m_double_prop[this.IndexProp(property)]=value;          }
   void              SetProperty(ENUM_EVENT_PROP_STRING property,string value){ this.m_string_prop[this.IndexProp(property)]=value;          }
//--- Возвращает из массива свойств (1) целочисленное, (2) вещественное и (3) строковое свойство события
   long              GetProperty(ENUM_EVENT_PROP_INTEGER property)      const { return this.m_long_prop[property];                           }
   double            GetProperty(ENUM_EVENT_PROP_DOUBLE property)       const { return this.m_double_prop[this.IndexProp(property)];         }
   string            GetProperty(ENUM_EVENT_PROP_STRING property)       const { return this.m_string_prop[this.IndexProp(property)];         }

//--- Возвращает флаг поддержания событием данного свойства
   virtual bool      SupportProperty(ENUM_EVENT_PROP_INTEGER property)        { return true;       }
   virtual bool      SupportProperty(ENUM_EVENT_PROP_DOUBLE property)         { return true;       }
   virtual bool      SupportProperty(ENUM_EVENT_PROP_STRING property)         { return true;       }
//--- Возвращает тип объекта
   virtual int       Type(void)                                         const { return this.m_type;}

//--- Расшифровывает код события и устанавливает торговое событие, (2) возвращает торговое событие
   void              SetTypeEvent(void);
   ENUM_TRADE_EVENT  TradeEvent(void)                                   const { return this.m_trade_event;                                   }
//--- Отправляет событие на график (реализация в потомках класса)
   virtual void      SendEvent(void) {;}

//--- Сравнивает объекты CEvent между собой по заданному свойству (для сортировки списков по указанному свойству объекта-события)
   virtual int       Compare(const CObject *node,const int mode=0) const;
//--- Сравнивает объекты CEvent между собой по всем свойствам (для поиска равных объектов-событий)
   bool              IsEqual(CEvent* compared_event);
//+------------------------------------------------------------------+
//| Методы упрощённого доступа к свойствам объекта-события           |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Конструктор                                                      |
//+------------------------------------------------------------------+
CEvent::CEvent(const ENUM_EVENT_STATUS event_status,const int event_code,const ulong ticket) : m_event_code(event_code),m_digits(0)
  {
   this.m_type=OBJECT_DE_TYPE_EVENT;
   this.m_long_prop[EVENT_PROP_STATUS_EVENT]       =  event_status;
   this.m_long_prop[EVENT_PROP_TICKET_ORDER_EVENT] =  (long)ticket;
   this.m_is_hedge=#ifdef __MQL4__ true #else bool(::AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING) #endif;
   this.m_digits_acc=#ifdef __MQL4__ 2 #else (int)::AccountInfoInteger(ACCOUNT_CURRENCY_DIGITS) #endif;
   this.m_chart_id_main=::ChartID();
  }
//+------------------------------------------------------------------+

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

Класс события балансовой операции в файле \MQL5\Include\DoEasy\Objects\Events\EventBalanceOperation.mqh:

//+------------------------------------------------------------------+
//| Событие балансовой операции                                      |
//+------------------------------------------------------------------+
class CEventBalanceOperation : public CEvent
  {
public:
//--- Конструктор
                     CEventBalanceOperation(const int event_code,const ulong ticket=0) : CEvent(EVENT_STATUS_BALANCE,event_code,ticket)
                       { this.m_type=OBJECT_DE_TYPE_EVENT_BALANCE; }
//--- Поддерживаемые свойства ордера (1) вещественные, (2) целочисленные
   virtual bool      SupportProperty(ENUM_EVENT_PROP_INTEGER property);
   virtual bool      SupportProperty(ENUM_EVENT_PROP_DOUBLE property);
   virtual bool      SupportProperty(ENUM_EVENT_PROP_STRING property);
//--- (1) Выводит в журнал краткое сообщение о событии, (2) Отправляет событие на график
   virtual void      PrintShort(void);
   virtual void      SendEvent(void);
  };
//+------------------------------------------------------------------+

Класс события модификации отложенного ордера или позиции в файле \MQL5\Include\DoEasy\Objects\Events\EventModify.mqh:

//+------------------------------------------------------------------+
//| Событие модификации отложенного ордера или позиции               |
//+------------------------------------------------------------------+
class CEventModify : public CEvent
  {
private:
   double            m_price;                               // Цена, отправляемая в событие
//--- Создаёт и возвращает краткое сообщение события
   string            EventsMessage(void);
public:
//--- Конструктор
                     CEventModify(const int event_code,const ulong ticket=0) : CEvent(EVENT_STATUS_MODIFY,event_code,ticket),m_price(0)
                       { this.m_type=OBJECT_DE_TYPE_EVENT_MODIFY; }
//--- Поддерживаемые свойства ордера (1) вещественные, (2) целочисленные
   virtual bool      SupportProperty(ENUM_EVENT_PROP_INTEGER property);
   virtual bool      SupportProperty(ENUM_EVENT_PROP_DOUBLE property);
//--- (1) Выводит в журнал краткое сообщение о событии, (2) Отправляет событие на график
   virtual void      PrintShort(void);
   virtual void      SendEvent(void);
  };
//+------------------------------------------------------------------+

Класс события установки отложенного ордера в файле \MQL5\Include\DoEasy\Objects\Events\EventOrderPlaced.mqh:

//+------------------------------------------------------------------+
//| Событие установки отложенного ордера                             |
//+------------------------------------------------------------------+
class CEventOrderPlased : public CEvent
  {
public:
//--- Конструктор
                     CEventOrderPlased(const int event_code,const ulong ticket=0) : CEvent(EVENT_STATUS_MARKET_PENDING,event_code,ticket)
                       { this.m_type=OBJECT_DE_TYPE_EVENT_ORDER_PLASED; }
//--- Поддерживаемые свойства ордера (1) вещественные, (2) целочисленные
   virtual bool      SupportProperty(ENUM_EVENT_PROP_INTEGER property);
   virtual bool      SupportProperty(ENUM_EVENT_PROP_DOUBLE property);
//--- (1) Выводит в журнал краткое сообщение о событии, (2) Отправляет событие на график
   virtual void      PrintShort(void);
   virtual void      SendEvent(void);
  };
//+------------------------------------------------------------------+

Класс события удаления отложенного ордера в файле \MQL5\Include\DoEasy\Objects\Events\EventOrderRemoved.mqh:

//+------------------------------------------------------------------+
//| Событие удаления отложенного ордера                              |
//+------------------------------------------------------------------+
class CEventOrderRemoved : public CEvent
  {
public:
//--- Конструктор
                     CEventOrderRemoved(const int event_code,const ulong ticket=0) : CEvent(EVENT_STATUS_HISTORY_PENDING,event_code,ticket)
                       { this.m_type=OBJECT_DE_TYPE_EVENT_ORDER_REMOVED; }
//--- Поддерживаемые свойства ордера (1) вещественные, (2) целочисленные
   virtual bool      SupportProperty(ENUM_EVENT_PROP_INTEGER property);
   virtual bool      SupportProperty(ENUM_EVENT_PROP_DOUBLE property);
//--- (1) Выводит в журнал краткое сообщение о событии, (2) Отправляет событие на график
   virtual void      PrintShort(void);
   virtual void      SendEvent(void);
  };
//+------------------------------------------------------------------+

Класс события закрытия позиции в файле \MQL5\Include\DoEasy\Objects\Events\EventPositionClose.mqh:

//+------------------------------------------------------------------+
//| Событие закрытия позиции                                         |
//+------------------------------------------------------------------+
class CEventPositionClose : public CEvent
  {
private:
//--- Создаёт и возвращает краткое сообщение события
   string            EventsMessage(void);  
public:
//--- Конструктор
                     CEventPositionClose(const int event_code,const ulong ticket=0) : CEvent(EVENT_STATUS_HISTORY_POSITION,event_code,ticket)
                       { this.m_type=OBJECT_DE_TYPE_EVENT_POSITION_CLOSE; }
//--- Поддерживаемые свойства ордера (1) вещественные, (2) целочисленные
   virtual bool      SupportProperty(ENUM_EVENT_PROP_INTEGER property);
   virtual bool      SupportProperty(ENUM_EVENT_PROP_DOUBLE property);
//--- (1) Выводит в журнал краткое сообщение о событии, (2) Отправляет событие на график
   virtual void      PrintShort(void);
   virtual void      SendEvent(void);
  };
//+------------------------------------------------------------------+

Класс события открытия позиции в файле \MQL5\Include\DoEasy\Objects\Events\EventPositionOpen.mqh:

//+------------------------------------------------------------------+
//| Событие открытия позиции                                         |
//+------------------------------------------------------------------+
class CEventPositionOpen : public CEvent
  {
private:
//--- Создаёт и возвращает краткое сообщение события
   string            EventsMessage(void);  
public:
//--- Конструктор
                     CEventPositionOpen(const int event_code,const ulong ticket=0) : CEvent(EVENT_STATUS_MARKET_POSITION,event_code,ticket)
                       { this.m_type=OBJECT_DE_TYPE_EVENT_POSITION_OPEN; }
//--- Поддерживаемые свойства ордера (1) вещественные, (2) целочисленные
   virtual bool      SupportProperty(ENUM_EVENT_PROP_INTEGER property);
   virtual bool      SupportProperty(ENUM_EVENT_PROP_DOUBLE property);
//--- (1) Выводит в журнал краткое сообщение о событии, (2) Отправляет событие на график
   virtual void      PrintShort(void);
   virtual void      SendEvent(void);
  };
//+------------------------------------------------------------------+


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

  • Если объект является наследником базового или расширенного базового объекта всех объектов библиотеки, то нам необходимо в его конструкторах (по умолчанию и в параметрическом) указать тип объекта из перечисления ENUM_OBJECT_DE_TYPE, созданного нами выше в файле Defines.mqh.
  • Если объект не является наследником базового или расширенного базового объекта всех объектов библиотеки, то нам нужно добавить в защищённой секции класса переменную m_type, в которой будет храниться тип объекта, в публичной секции добавить виртуальный метод Type(), который будет возвращать значение переменной m_type, и в конструкторах класса (по умолчанию и в параметрическом) указать тип объекта из перечисления ENUM_OBJECT_DE_TYPE.

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

Вот перечень доработанных классов в каталоге библиотеки \MQL5\Include\DoEasy\Objects:

  • Папка Chart: файлы ChartObj.mqh и ChartWnd.mqh;
  • Папка Indicators: файлы Buffer.mqh, BufferArrow.mqh, BufferBars.mqh, BufferCalculate.mqh, BufferCandles.mqh,  BufferFilling.mqh, BufferHistogram.mqh, BufferHistogram2.mqh, BufferLine.mqh, BufferSection.mqh, BufferZigZag.mqh, DataInd.mqh, IndicatorDE.mqh, SeriesDataInd.mqh;
    Папка Standart: файлы IndAC.mqh, IndAD.mqh, IndADX.mqh, IndADXW.mqh, IndAlligator.mqh, IndAMA.mqh, IndAO.mqh, IndATR.mqh, IndBands.mqh, IndBears.mqh, IndBulls.mqh, IndBWMFI.mqh, IndCCI.mqh, IndChaikin.mqh, IndCustom.mqh, IndDEMA.mqh, IndDeMarker.mqh, IndEnvelopes.mqh, IndForce.mqh, IndFractals.mqh, IndFRAMA.mqh, IndGator.mqh, IndIchimoku.mqh, IndMA.mqh, IndMACD.mqh, IndMFI.mqh, IndMomentum.mqh, IndOBV.mqh, IndOsMA.mqh, IndRSI.mqh, IndRVI.mqh, IndSAR.mqh, IndStDev.mqh, IndStoch.mqh, IndTEMA.mqh, IndTRIX.mqh, IndVIDYA.mqh, IndVolumes.mqh, IndWPR.mqh;
  • Папка MQLSignalBase: файл MQLSignal.mqh;
  • Папка Orders: файлы HistoryBalance.mqh, HistoryDeal.mqh, HistoryOrder.mqh, HistoryPending.mqh, MarketOrder.mqh, MarketPending.mqh, MarketPosition.mqh, Order.mqh;
  • Папка PendRequest: файлы PendReqClose.mqh, PendReqModify.mqh, PendReqOpen.mqh, PendReqPlace.mqh, PendReqRemove.mqh, PendReqSLTP.mqh, PendRequest.mqh;
  • Папка Series: файлы Bar.mqh, SeriesDE.mqh, TimeSeriesDE.mqh;
  • Папка Symbols: файлы Symbol.mqh, SymbolBonds.mqh, SymbolCFD.mqh, SymbolCollateral.mqh, SymbolCommodity.mqh, SymbolCommon.mqh, SymbolCrypto.mqh, SymbolCustom.mqh, SymbolExchange.mqh, SymbolFutures.mqh, SymbolFX.mqh, SymbolFXExotic.mqh, SymbolFXMajor.mqh, SymbolFXMinor.mqh, SymbolFXRub.mqh, SymbolIndex.mqh, SymbolIndicative.mqh, SymbolMetall.mqh, SymbolOption.mqh, SymbolStocks.mqh;
  • Папка Ticks: файлы DataTick.mqh, NewTickObj.mqh, TickSeries.mqh;
  • Папка Trade: файл TradeObj.mqh;
  • Папка Graph: файлы Form.mqh, GCnvElement.mqh, GraphElmControl.mqh, ShadowObj.mqh;
    Папка Animations: файлы Animations.mqh, Frame.mqh, FrameGeometry.mqh, FrameQuad.mqh, FrameText.mqh;

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

Мы упустили из перечня изменённых файлов один класс — это класс базового объекта всех графических объектов библиотеки в файле \MQL5\Include\DoEasy\Objects\Graph\GBaseObj.mqh. В него, помимо указания типа объекта, было добавлено свойство "Принадлежность программе/терминалу" — это нам необходимо для понимания, какой графический объект был создан программой, работающей под управлением библиотеки, а какой добавлен на график вручную в терминале:

//+------------------------------------------------------------------+
//|                                                     GBaseObj.mqh |
//|                                  Copyright 2021, MetaQuotes Ltd. |
//|                             https://mql5.com/ru/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2021, MetaQuotes Ltd."
#property link      "https://mql5.com/ru/users/artmedia70"
#property version   "1.00"
#property strict    // Нужно для mql4
//+------------------------------------------------------------------+
//| Включаемые файлы                                                 |
//+------------------------------------------------------------------+
#include "..\..\Services\DELib.mqh"
#include <Graphics\Graphic.mqh>
//+------------------------------------------------------------------+
//| Класс базового объекта графических объектов библиотеки           |
//+------------------------------------------------------------------+
class CGBaseObj : public CObject
  {
private:
   
protected:
   string            m_name_prefix;                      // Префикс имени объекта
   string            m_name;                             // Имя объекта
   long              m_chart_id;                         // Идентификатор графика
   int               m_subwindow;                        // Номер подокна
   int               m_shift_y;                          // Смещение координаты Y для подокна
   int               m_type;                             // Тип объекта
   bool              m_visible;                          // Видимость объекта
   ENUM_GRAPH_OBJ_BELONG m_belong;                       // Принадлежность программе/терминалу
   
//--- Создаёт (1) структуру объекта, (2) объект из структуры
   virtual bool      ObjectToStruct(void)                      { return true; }
   virtual void      StructToObject(void){;}

public:
//--- Возврат значений переменных класса
   string            Name(void)                          const { return this.m_name;      }
   long              ChartID(void)                       const { return this.m_chart_id;  }
   int               SubWindow(void)                     const { return this.m_subwindow; }
   ENUM_GRAPH_OBJ_BELONG Belong(void)                    const { return this.m_belong;    }
   
//--- (1) Устанавливает, (2) возвращает видимость объекта
   void              SetVisible(const bool flag)   
                       { 
                        long value=(flag ? OBJ_ALL_PERIODS : 0);
                        if(::ObjectSetInteger(this.m_chart_id,this.m_name,OBJPROP_TIMEFRAMES,value))
                           this.m_visible=flag;
                       }
   bool              IsVisible(void)                     const { return this.m_visible;   }

//--- Виртуальный метод, возвращающий тип объекта
   virtual int       Type(void)                          const { return this.m_type;      }
//--- Устанавливает принадлежность
   void              SetBelong(const ENUM_GRAPH_OBJ_BELONG belong){ this.m_belong=belong; }

//--- Конструктор/деструктор
                     CGBaseObj();
                    ~CGBaseObj();
  };
//+------------------------------------------------------------------+
//| Конструктор                                                      |
//+------------------------------------------------------------------+
CGBaseObj::CGBaseObj() : m_shift_y(0),m_visible(false), m_name_prefix(::MQLInfoString(MQL_PROGRAM_NAME)+"_"),m_belong(GRAPH_OBJ_BELONG_PROGRAM)
  {
   this.m_type=OBJECT_DE_TYPE_GBASE;
  }
//+------------------------------------------------------------------+
//| Деструктор                                                       |
//+------------------------------------------------------------------+
CGBaseObj::~CGBaseObj()
  {
  }
//+------------------------------------------------------------------+


Доработки, аналогичные внесённым в файлы классов объектов библиотеки, были внесены и в файлы классов коллекций объектов библиотеки в каталоге \MQL5\Include\DoEasy\Collections\.

Вот перечень файлов доработанных классов коллекций объектов:

AccountsCollection.mqh, BookSeriesCollection.mqh, BuffersCollection.mqh, ChartObjCollection.mqh, EventsCollection.mqh, HistoryCollection.mqh, IndicatorsCollection.mqh, MarketCollection.mqh, MQLSignalsCollection.mqh, ResourceCollection.mqh, SymbolsCollection.mqh, TickSeriesCollection.mqh, TimeSeriesCollection.mqh.

Эти файлы можно так же посмотреть и изучить внесённые в них изменения в прикреплённых к статье файлах.

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


Класс-коллекция графических объектов

В прошлой статье мы сделали заготовку класса-коллекции графических объектов библиотеки (файл \MQL5\Include\DoEasy\Collections\GraphElementsCollection.mqh). Сегодня продолжим её развитие.

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

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

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

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

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

Пропишем такой класс прямо в файле класса-коллекции графических объектов \MQL5\Include\DoEasy\Collections\GraphElementsCollection.mqh:

//+------------------------------------------------------------------+
//|                                      GraphElementsCollection.mqh |
//|                                  Copyright 2021, MetaQuotes Ltd. |
//|                             https://mql5.com/ru/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2021, MetaQuotes Ltd."
#property link      "https://mql5.com/ru/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Включаемые файлы                                                 |
//+------------------------------------------------------------------+
#include "ListObj.mqh"
#include "..\Services\Select.mqh"
#include "..\Objects\Graph\Form.mqh"
//+------------------------------------------------------------------+
//| Класс управления объектами чарта                                 |
//+------------------------------------------------------------------+
class CChartObjectsControl : public CObject
  {
private:
   ENUM_TIMEFRAMES   m_chart_timeframe;         // Период графика
   long              m_chart_id;                // Идентификатор графика
   string            m_chart_symbol;            // Символ графика
   bool              m_is_graph_obj_event;      // Флаг события в списке графических объектов
   int               m_total_objects;           // Количество графических объектов
   int               m_last_objects;            // Количество графических объектов на прошлой проверке
   int               m_index_object;            // Индекс последнего добавленного графического объекта в коллекцию из списка объектов терминала
   int               m_delta_graph_obj;         // Разница в количестве графических объектов по сравнению с прошлой проверкой
public:
//--- Возврат значений переменных
   ENUM_TIMEFRAMES   Timeframe(void)                           const { return this.m_chart_timeframe;    }
   long              ChartID(void)                             const { return this.m_chart_id;           }
   string            Symbol(void)                              const { return this.m_chart_symbol;       }
   bool              IsEvent(void)                             const { return this.m_is_graph_obj_event; }
   int               TotalObjects(void)                        const { return this.m_total_objects;      }
   int               Delta(void)                               const { return this.m_delta_graph_obj;    }
//--- Проверяет объекты на чарте
   void              Refresh(void);
//--- Конструкторы
                     CChartObjectsControl(void)
                       { 
                        this.m_chart_id=::ChartID();
                        this.m_chart_timeframe=(ENUM_TIMEFRAMES)::ChartPeriod(this.m_chart_id);
                        this.m_chart_symbol=::ChartSymbol(this.m_chart_id);
                        this.m_is_graph_obj_event=false;
                        this.m_total_objects=0;
                        this.m_last_objects=0;
                        this.m_index_object=0;
                        this.m_delta_graph_obj=0;
                       }
                     CChartObjectsControl(const long chart_id)
                       {
                        this.m_chart_id=chart_id;
                        this.m_chart_timeframe=(ENUM_TIMEFRAMES)::ChartPeriod(this.m_chart_id);
                        this.m_chart_symbol=::ChartSymbol(this.m_chart_id);
                        this.m_is_graph_obj_event=false;
                        this.m_total_objects=0;
                        this.m_last_objects=0;
                        this.m_index_object=0;
                        this.m_delta_graph_obj=0;
                       }
                     
//--- Сравнивает объекты CChartObjectsControl между собой по идентификатору графика (для сортировки списка по свойству объекта)
   virtual int       Compare(const CObject *node,const int mode=0) const
                       {
                        const CChartObjectsControl *obj_compared=node;
                        return(this.ChartID()>obj_compared.ChartID() ? 1 : this.ChartID()<obj_compared.ChartID() ? -1 : 0);
                       }
  };
//+------------------------------------------------------------------+
//| CChartObjectsControl Проверяет объекты на чарте                  |
//+------------------------------------------------------------------+
void CChartObjectsControl::Refresh(void)
  {
//--- Графические объекты на чарте
   this.m_total_objects=::ObjectsTotal(this.ChartID());
   int i=this.m_index_object;
   int delta=this.m_total_objects-this.m_last_objects;
   
//--- Если количество объектов изменилось
   if(delta!=0)
     {
      //--- Создадим строку и выведем её в журнал с указание идентификатора графика, его символа и периода
      string txt=", "+(delta>0 ? "Added: " : "Deleted: ")+(string)fabs(delta)+" obj";
      Print(DFUN,"ChartID=",this.ChartID(),", ",this.Symbol(),", ",TimeframeDescription(this.Timeframe()),txt);
     }

//--- сохранение индекса последнего добавленного ордера и разницы по сравнению с прошлой проверкой
   this.m_delta_graph_obj=i-this.m_index_object;
   this.m_index_object=i;
   this.m_last_objects=this.m_total_objects;
   this.m_is_graph_obj_event=(bool)this.m_delta_graph_obj;
  }
//+------------------------------------------------------------------+

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

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

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

Добавим в ранее созданный класс CGraphElementsCollection новые переменные и методы.

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

//+------------------------------------------------------------------+
//| Коллекция графических объектов                                   |
//+------------------------------------------------------------------+
class CGraphElementsCollection : public CBaseObj
  {
private:
   CArrayObj         m_list_charts_control;     // Список объектов управления чартами
   CListObj          m_list_all_graph_obj;      // Список всех графических объектов
   bool              m_is_graph_obj_event;      // Флаг события в списке графических объектов
   int               m_total_objects;           // Количество графических объектов
   int               m_delta_graph_obj;         // Разница в количестве графических объектов по сравнению с прошлой проверкой
   
//--- Возвращает флаг наличия объекта-графического элемента в списке графических объектов
   bool              IsPresentGraphElmInList(const int id,const ENUM_GRAPH_ELEMENT_TYPE type_obj);

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

//--- Возвращает флаг наличия объекта-графического элемента в списке графических объектов
   bool              IsPresentGraphElmInList(const int id,const ENUM_GRAPH_ELEMENT_TYPE type_obj);
//--- Возвращает указатель на объект управления объектами указанного чарта
   CChartObjectsControl *GetChartObjectCtrlObj(const long chart_id);
//--- Создаёт новый объект управления графическими объектами указанного чарта и добавляет его в список
   CChartObjectsControl *CreateChartObjectCtrlObj(const long chart_id);
//--- Обновляет список графических объектов по идентификатору чарта
   void              RefreshByChartID(const long chart_id);

public:

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

public:
//--- Возвращает себя
   CGraphElementsCollection *GetObject(void)                                                             { return &this;                        }
   //--- Возвращает полный список-коллекцию "как есть"
   CArrayObj        *GetList(void)                                                                       { return &this.m_list_all_graph_obj;   }
   //--- Возвращает список по выбранному (1) целочисленному, (2) вещественному и (3) строковому свойству, удовлетворяющему сравниваемому критерию
   CArrayObj        *GetList(ENUM_CANV_ELEMENT_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode=EQUAL)  { return CSelect::ByGraphCanvElementProperty(this.GetList(),property,value,mode);  }
   CArrayObj        *GetList(ENUM_CANV_ELEMENT_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByGraphCanvElementProperty(this.GetList(),property,value,mode);  }
   CArrayObj        *GetList(ENUM_CANV_ELEMENT_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByGraphCanvElementProperty(this.GetList(),property,value,mode);  }
   //--- Возвращает количество новых графических объектов, (3) флаг произошедшего изменения в списке графических объектов
   int               NewObjects(void)   const                                                            { return this.m_delta_graph_obj;       }
   bool              IsEvent(void) const                                                                 { return this.m_is_graph_obj_event;    }
   //--- Конструктор
                     CGraphElementsCollection();
//--- Выводит в журнал описание свойств объекта (full_prop=true - все свойства, false - только поддерживаемые - реализуется в наследниках класса)
   virtual void      Print(const bool full_prop=false,const bool dash=false);
//--- Выводит в журнал краткое описание объекта
   virtual void      PrintShort(const bool dash=false,const bool symbol=false);

//--- Создаёт список объектов управления чартами, возвращает количество чартов
   int               CreateChartControlList(void);
//--- Обновляет список (1) всех графических объектов, (2) на указанном чарте, заполняет данные о количестве новых и устанавливает флаг события
   void              Refresh(void);
   void              Refresh(const long chart_id);
  };
//+------------------------------------------------------------------+

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

//+------------------------------------------------------------------+
//| Конструктор                                                      |
//+------------------------------------------------------------------+
CGraphElementsCollection::CGraphElementsCollection()
  {
   this.m_type=COLLECTION_GRAPH_OBJ_ID;
   ::ChartSetInteger(::ChartID(),CHART_EVENT_MOUSE_MOVE,true);
   ::ChartSetInteger(::ChartID(),CHART_EVENT_MOUSE_WHEEL,true);
   this.m_list_all_graph_obj.Type(COLLECTION_GRAPH_OBJ_ID);
   this.m_list_all_graph_obj.Sort(SORT_BY_CANV_ELEMENT_ID);
   this.m_list_all_graph_obj.Clear();
   this.m_list_charts_control.Sort();
   this.m_list_charts_control.Clear();
   this.m_total_objects=0;
   this.m_is_graph_obj_event=false;
  }
//+------------------------------------------------------------------+

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

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

//+------------------------------------------------------------------+
//| Создаёт новый объект управления графическими объектами           |
//| указанного чарта и добавляет его в список                        |
//+------------------------------------------------------------------+
CChartObjectsControl *CGraphElementsCollection::CreateChartObjectCtrlObj(const long chart_id)
  {
//--- Создаём новый объект управления объектами графика по идентификатору
   CChartObjectsControl *obj=new CChartObjectsControl(chart_id);
//--- Еслиобъект не создан - сообщаем об ошибке и возвращаем NULL
   if(obj==NULL)
     {
      ::Print(DFUN,CMessage::Text(MSG_CHART_OBJ_COLLECTION_ERR_FAILED_CREATE_CTRL_OBJ),(string)chart_id);
      return NULL;
     }
//--- Если объект не удалось добавить в список - сообщаем об ошибке, удаляем объект и возвращаем NULL
   if(!this.m_list_charts_control.Add(obj))
     {
      CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST);
      delete obj;
      return NULL;
     }
//--- Возвращаем указатель на созданный и добавленный в список объект
   return obj;
  }
//+------------------------------------------------------------------+

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

//+------------------------------------------------------------------+
//| Возвращает указатель на объект                                   |
//| управления объектами указанного чарта                            |
//+------------------------------------------------------------------+
CChartObjectsControl *CGraphElementsCollection::GetChartObjectCtrlObj(const long chart_id)
  {
//--- В цикле во общему количеству объектов в списке
   for(int i=0;i<this.m_list_charts_control.Total();i++)
     {
      //--- Получаем указатель на очередной объект
      CChartObjectsControl *obj=this.m_list_charts_control.At(i);
      //--- Если указатель получить не удалось - переходим к следующему
      if(obj==NULL)
         continue;
      //--- Если идентификатор графика объекта равен искомому - возвращаем указатель на объект в списке
      if(obj.ChartID()==chart_id)
         return obj;
     }
//--- Не нашли объект - возвращаем NULL
   return NULL;
  }
//+------------------------------------------------------------------+

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

//+------------------------------------------------------------------+
//| Создаёт список объектов управления чартами                       |
//+------------------------------------------------------------------+
int CGraphElementsCollection::CreateChartControlList(void)
  {
   //--- Очистим список объектов управления чартами и установим ему флаг сортированного списка
   this.m_list_charts_control.Clear();
   this.m_list_charts_control.Sort();
   //--- Объявим переменные для поиска графиков
   long chart_id=0;
   int i=0;
//--- В цикле по всем открытым графикам терминала (не более 100)
   while(i<CHARTS_MAX)
     {
      //--- Получим идентификатор графика
      chart_id=::ChartNext(chart_id);
      if(chart_id<0)
         break;
      //--- Создаём объект управления объектами чарта на основании идентификатора текущего графика в цикле и добавим его в список
      CChartObjectsControl *chart_control=new CChartObjectsControl(chart_id);
      if(chart_control==NULL)
         continue;
      //--- Если такой объект уже есть в списке - сообщаем об этом, удаляем объект и идём к следующему графику
      if(this.m_list_charts_control.Search(chart_control)>WRONG_VALUE)
        {
         ::Print(DFUN,CMessage::Text(MSG_CHART_OBJ_COLLECTION_ERR_OBJ_ALREADY_EXISTS),(string)chart_id);
         delete chart_control;
         continue;
        }
      //--- Если не удалось добавить объект в список - сообщаем об этом, удаляем объект и идём к следующему графику
      if(!this.m_list_charts_control.Add(chart_control))
        {
         CMessage::ToLog(DFUN_ERR_LINE,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST);
         delete chart_control;
         continue;
        }
      //--- Увеличиваем индекс цикла
      i++;
     }
   //--- Успешно заполнили список - возвращаем количество его элементов
   return this.m_list_charts_control.Total();
  }
//+------------------------------------------------------------------+

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

//+------------------------------------------------------------------+
//| Обновляет список графических объектов по идентификатору чарта    |
//+------------------------------------------------------------------+
void CGraphElementsCollection::RefreshByChartID(const long chart_id)
  {
//--- Получаем указатель на объект управления графическими объектами
   CChartObjectsControl *obj=GetChartObjectCtrlObj(chart_id);
//--- Если такого объекта нет в списке - создаём новый и добавляем его в список
   if(obj==NULL)
      obj=this.CreateChartObjectCtrlObj(chart_id);
//--- Если указатель на объект валидный - обновляем список графических объектов на указанном графике
   if(obj!=NULL)
      obj.Refresh();
  }
//+------------------------------------------------------------------+

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

//+------------------------------------------------------------------+
//| Обновляет список графических объектов на указанном чарте         |
//+------------------------------------------------------------------+
void CGraphElementsCollection::Refresh(const long chart_id)
  {
//--- Получаем указатель на объект управления графическими объектами
   CChartObjectsControl *obj=GetChartObjectCtrlObj(chart_id);
//--- Если цказатель получить не удалось - уходим из метода
   if(obj==NULL)
      return;
//--- Обновляем список графических объектов на указанном графике
   obj.Refresh();
  }
//+------------------------------------------------------------------+

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

//+------------------------------------------------------------------+
//| Обновляет список всех графических объектов                       |
//+------------------------------------------------------------------+
void CGraphElementsCollection::Refresh(void)
  {
//--- Объявим переменные для поиска графиков
   long chart_id=0;
   int i=0;
//--- В цикле по всем открытым графикам терминала (не более 100)
   while(i<CHARTS_MAX)
     {
      //--- Получим идентификатор графика
      chart_id=::ChartNext(chart_id);
      if(chart_id<0)
         break;
      //--- Обновляем список графических объектов по идентификатору чарта
      this.RefreshByChartID(chart_id);
      //--- Увеличиваем индекс цикла
      i++;
     }
  }
//+------------------------------------------------------------------+

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

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

В файле \MQL5\Include\DoEasy\Engine.mqh в приватной секции класса объявим экземпляр класса-коллекции графических объектов, переменную-флаг события в списке графических объектов и объявим метод контроля за событиями графических объектов:

//+------------------------------------------------------------------+
//| Класс-основа библиотеки                                          |
//+------------------------------------------------------------------+
class CEngine
  {
private:
   CHistoryCollection   m_history;                       // Коллекция исторических ордеров и сделок
   CMarketCollection    m_market;                        // Коллекция рыночных ордеров и сделок
   CEventsCollection    m_events;                        // Коллекция событий
   CAccountsCollection  m_accounts;                      // Коллекция аккаунтов
   CSymbolsCollection   m_symbols;                       // Коллекция символов
   CTimeSeriesCollection m_time_series;                  // Коллекция таймсерий
   CBuffersCollection   m_buffers;                       // Коллекция индикаторных буферов
   CIndicatorsCollection m_indicators;                   // Коллекция индикаторов
   CTickSeriesCollection m_tick_series;                  // Коллекция тиковых серий
   CMBookSeriesCollection m_book_series;                 // Коллекция серий стаканов цен
   CMQLSignalsCollection m_signals_mql5;                 // Коллекция сигналов сервиса сигналов mql5.com
   CChartObjCollection  m_charts;                        // Коллекция чартов
   CGraphElementsCollection m_graph_objects;             // Коллекция графических объектов
   CResourceCollection  m_resource;                      // Список ресурсов
   CTradingControl      m_trading;                       // Объект управления торговлей
   CPause               m_pause;                         // Объект "Пауза"
   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;               // Флаг события изменения свойств символа
   bool                 m_is_graph_obj_event;            // Флаг события в списке графических объектов
   ENUM_TRADE_EVENT     m_last_trade_event;              // Последнее торговое событие на счёте
   int                  m_last_account_event;            // Последнее событие в свойствах счёта
   int                  m_last_symbol_event;             // Последнее событие в свойствах символа
   ENUM_PROGRAM_TYPE    m_program;                       // Тип программы
   string               m_name;                          // Имя программы
//--- Возвращает индекс счётчика по id
   int                  CounterIndex(const int id) const;
//--- Возвращает флаг первого запуска
   bool                 IsFirstStart(void);
//--- Работа с событиями (1) ордеров, сделок и позиций, (2) аккаунтов, (3) графических объектов
   void                 TradeEventsControl(void);
   void                 AccountEventsControl(void);
   void                 GraphObjEventsControl(void);
//--- (1) Работа с коллекцией символов и (2) событиями списка символов в окне обзора рынка
   void                 SymbolEventsControl(void);
   void                 MarketWatchEventsControl(void);
//--- Возвращает последний (1) рыночный отложенный ордер, (2) маркет-ордер, (3) последнюю позицию, (4) позицию по тикету
   COrder              *GetLastMarketPending(void);
   COrder              *GetLastMarketOrder(void);
   COrder              *GetLastPosition(void);
   COrder              *GetPosition(const ulong ticket);
//--- Возвращает последний (1) удалённый отложенный ордер, (2) исторический маркет-ордер, (3) исторический ордер (маркет или отложенный) по его тикету
   COrder              *GetLastHistoryPending(void);
   COrder              *GetLastHistoryOrder(void);
   COrder              *GetHistoryOrder(const ulong ticket);
//--- Возвращает (1) первый и (2) последний исторический маркет-ордер из списка всех ордеров позиции, (3) последнюю сделку
   COrder              *GetFirstOrderPosition(const ulong position_id);
   COrder              *GetLastOrderPosition(const ulong position_id);
   COrder              *GetLastDeal(void);
//--- Извлекает нужное ushort-число из упакованного long-значения
   ushort               LongToUshortFromByte(const long source_value,const uchar index) const;
   
public:

В конструкторе класса создадим счётчик таймера коллекции графических объектов:

//+------------------------------------------------------------------+
//| CEngine конструктор                                              |
//+------------------------------------------------------------------+
CEngine::CEngine() : m_first_start(true),
                     m_last_trade_event(TRADE_EVENT_NO_EVENT),
                     m_last_account_event(WRONG_VALUE),
                     m_last_symbol_event(WRONG_VALUE),
                     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_program=(ENUM_PROGRAM_TYPE)::MQLInfoInteger(MQL_PROGRAM_TYPE);
   this.m_name=::MQLInfoString(MQL_PROGRAM_NAME);
   
   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);
   this.CreateCounter(COLLECTION_REQ_COUNTER_ID,COLLECTION_REQ_COUNTER_STEP,COLLECTION_REQ_PAUSE);
   this.CreateCounter(COLLECTION_TS_COUNTER_ID,COLLECTION_TS_COUNTER_STEP,COLLECTION_TS_PAUSE);
   this.CreateCounter(COLLECTION_IND_TS_COUNTER_ID,COLLECTION_IND_TS_COUNTER_STEP,COLLECTION_IND_TS_PAUSE);
   this.CreateCounter(COLLECTION_TICKS_COUNTER_ID,COLLECTION_TICKS_COUNTER_STEP,COLLECTION_TICKS_PAUSE);
   this.CreateCounter(COLLECTION_CHARTS_COUNTER_ID,COLLECTION_CHARTS_COUNTER_STEP,COLLECTION_CHARTS_PAUSE);
   this.CreateCounter(COLLECTION_GRAPH_OBJ_COUNTER_ID,COLLECTION_GRAPH_OBJ_COUNTER_STEP,COLLECTION_GRAPH_OBJ_PAUSE);
   
   ::ResetLastError();
   #ifdef __MQL5__
      if(!::EventSetMillisecondTimer(TIMER_FREQUENCY))
        {
         ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_TIMER),(string)::GetLastError());
         this.m_global_error=::GetLastError();
        }
   //---__MQL4__
   #else 
      if(!this.IsTester() && !::EventSetMillisecondTimer(TIMER_FREQUENCY))
        {
         ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_TIMER),(string)::GetLastError());
         this.m_global_error=::GetLastError();
        }
   #endif 
   //---
  }
//+------------------------------------------------------------------+

В таймере класса впишем обработку таймера коллекции графических объектов:

//+------------------------------------------------------------------+
//| CEngine таймер                                                   |
//+------------------------------------------------------------------+
void CEngine::OnTimer(SDataCalculate &data_calculate)
  {
//--- Если это не тестер - работаем с событиями коллекций по таймеру
   if(!this.IsTester())
     {
   //--- Таймер коллекций исторических ордеров и сделок и рыночных ордеров и позиций
      int index=this.CounterIndex(COLLECTION_ORD_COUNTER_ID);
      CTimerCounter* cnt1=this.m_list_counters.At(index);
      if(cnt1!=NULL)
        {
         //--- Если пауза завершилась - работаем с событиями коллекций ордеров, сделок и позиций
         if(cnt1.IsTimeDone())
            this.TradeEventsControl();
        }
   //--- Таймер коллекции аккаунтов
      index=this.CounterIndex(COLLECTION_ACC_COUNTER_ID);
      CTimerCounter* cnt2=this.m_list_counters.At(index);
      if(cnt2!=NULL)
        {
         //--- Если пауза завершилась - работаем с событиями коллекции аккаунтов
         if(cnt2.IsTimeDone())
            this.AccountEventsControl();
        }
   //--- Таймер1 коллекции символов (обновление котировочных данных символов в коллекции)
      index=this.CounterIndex(COLLECTION_SYM_COUNTER_ID1);
      CTimerCounter* cnt3=this.m_list_counters.At(index);
      if(cnt3!=NULL)
        {
         //--- Если пауза завершилась - обновляем котировочные данные всех символов в коллекции
         if(cnt3.IsTimeDone())
            this.m_symbols.RefreshRates();
        }
   //--- Таймер2 коллекции символов (обновление всех данных всех символов в коллекции и отслеживание событий символов и списка символов в окне обзора рынка)
      index=this.CounterIndex(COLLECTION_SYM_COUNTER_ID2);
      CTimerCounter* cnt4=this.m_list_counters.At(index);
      if(cnt4!=NULL)
        {
         //--- Если пауза завершилась
         if(cnt4.IsTimeDone())
           {
            //--- обновляем данные и работаем с событиями всех символов в коллекции
            this.SymbolEventsControl();
            //--- Если работаем со списком из обзора рынка - проверяем события окна обзора рынка
            if(this.m_symbols.ModeSymbolsList()==SYMBOLS_MODE_MARKET_WATCH)
               this.MarketWatchEventsControl();
           }
        }
   //--- Таймер торгового класса
      index=this.CounterIndex(COLLECTION_REQ_COUNTER_ID);
      CTimerCounter* cnt5=this.m_list_counters.At(index);
      if(cnt5!=NULL)
        {
         //--- Если пауза завершилась - работаем со списком отложенных запросов
         if(cnt5.IsTimeDone())
            this.m_trading.OnTimer();
        }
   //--- Таймер коллекции таймсерий
      index=this.CounterIndex(COLLECTION_TS_COUNTER_ID);
      CTimerCounter* cnt6=this.m_list_counters.At(index);
      if(cnt6!=NULL)
        {
         //--- Если пауза завершилась - работаем со списком таймсерий (обновляем все кроме текущей)
         if(cnt6.IsTimeDone())
            this.SeriesRefreshAllExceptCurrent(data_calculate);
        }
        
   //--- Таймер коллекции таймсерий данных индикаторных буферов
      index=this.CounterIndex(COLLECTION_IND_TS_COUNTER_ID);
      CTimerCounter* cnt7=this.m_list_counters.At(index);
      if(cnt7!=NULL)
        {
         //--- Если пауза завершилась - работаем со списком таймсерий индикаторных данных (обновляем все кроме текущей)
         if(cnt7.IsTimeDone()) 
            this.IndicatorSeriesRefreshAll();
        }
   //--- Таймер коллекции тиковых серий
      index=this.CounterIndex(COLLECTION_TICKS_COUNTER_ID);
      CTimerCounter* cnt8=this.m_list_counters.At(index);
      if(cnt8!=NULL)
        {
         //--- Если пауза завершилась - работаем со списком тиковых серий (обновляем все кроме текущей)
         if(cnt8.IsTimeDone())
            this.TickSeriesRefreshAllExceptCurrent();
        }
   //--- Таймер коллекции чартов
      index=this.CounterIndex(COLLECTION_CHARTS_COUNTER_ID);
      CTimerCounter* cnt9=this.m_list_counters.At(index);
      if(cnt9!=NULL)
        {
         //--- Если пауза завершилась - работаем со списком чартов
         if(cnt9.IsTimeDone())
            this.ChartsRefreshAll();
        }
        
   //--- Таймер коллекции графических объектов
      index=this.CounterIndex(COLLECTION_GRAPH_OBJ_COUNTER_ID);
      CTimerCounter* cnt10=this.m_list_counters.At(index);
      if(cnt10!=NULL)
        {
         //--- Если пауза завершилась - работаем со списком графических объектов
         if(cnt10.IsTimeDone())
            this.GraphObjEventsControl();
        }
        
     }
//--- Если тестер - работаем с событиями коллекций по тику
   else
     {
      //--- работаем с событиями коллекций ордеров, сделок и позиций по тику
      this.TradeEventsControl();
      //--- работаем с событиями коллекции аккаунтов по тику
      this.AccountEventsControl();
      //--- обновляем котировочные данные всех символов в коллекции по тику
      this.m_symbols.RefreshRates();
      //--- работаем с событиями всех символов в коллекции по тику
      this.SymbolEventsControl();
      //--- работаем со списком отложенных запросов по тику
      this.m_trading.OnTimer();
      //--- работаем со списком таймсерий по тику
      this.SeriesRefresh(data_calculate);
      //--- работаем со списком таймсерий индикаторных буферов по тику
      this.IndicatorSeriesRefreshAll();
      //--- работаем со списком тиковых серий по тику
      this.TickSeriesRefreshAll();
      //--- работаем со списком графических объектов по тику
      this.GraphObjEventsControl();
     }
  }
//+------------------------------------------------------------------+

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

//+------------------------------------------------------------------+
//| Проверка событий графических объектов                            |
//+------------------------------------------------------------------+
void CEngine::GraphObjEventsControl(void)
  {
//--- Проверка изменений в списке графических объектов и установка флага их событий
   this.m_graph_objects.Refresh();
   this.m_is_graph_obj_event=this.m_graph_objects.IsEvent();
//--- Если есть изменения в списке графических объектов
   if(this.m_is_graph_obj_event)
     {
      Print(DFUN,"Graph obj is event. NewObjects: ",m_graph_objects.NewObjects());
      //--- Получаем последнее событие изменения свойств графических объектов
      // ...
     }
  }
//+------------------------------------------------------------------+

Здесь просто вызываем метод обновления всех открытых графиков терминала на предмет изменения количества графических объектов на них.

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


Тестирование

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

Здесь нам потребуется внести совсем немного изменений.

Впишем обработчик OnTimer(), вызывающий таймер библиотеки в случае, если работа не в тестере:

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {

  }
//+------------------------------------------------------------------+
//| Timer function                                                   |
//+------------------------------------------------------------------+
void OnTimer()
  {
//--- Запускаем таймер библиотеки только не в тестере
   if(!MQLInfoInteger(MQL_TESTER))
      engine.OnTimer(rates_data);
  }
//+------------------------------------------------------------------+

В обработчике OnChartEvent() добавим запрет на вызов контекстного меню правой кнопки мышки в случае, если нажата клавиша Ctrl, и разрешение в случае, если клавиша отпущена — при её удержании мы создаём графические элементы из объекта-бара с описанием типа этого бара (это делали в прошлой статье), и немного увеличим ширину создаваемого объекта — чтобы в него полностью помещалось длинное описание бара ("Свеча с нулевым телом"):

//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- Если работа в тестере - выход
   if(MQLInfoInteger(MQL_TESTER))
      return;
//--- Если перемещаем мышь
   if(id==CHARTEVENT_MOUSE_MOVE)
     {
      CForm *form=NULL;
      datetime time=0;
      double price=0;
      int wnd=0;
      
      //--- Если клавиша Ctrl не нажата
      if(!IsCtrlKeyPressed())
        {
         //--- очищаем список созданных объектов-форм и разрешаем прокручивать график мышкой и показывать контекстное меню
         list_forms.Clear();
         ChartSetInteger(ChartID(),CHART_MOUSE_SCROLL,true);
         ChartSetInteger(ChartID(),CHART_CONTEXT_MENU,true);
         return;
        }
      
      //--- Если координаты  X и Y графика успешно преобразованы в значения время и цена
      if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price))
        {
         //--- получим индекс бара, над которым находится курсор
         int index=iBarShift(Symbol(),PERIOD_CURRENT,time);
         if(index==WRONG_VALUE)
            return;
         
         //--- Получим объект-бар по индексу
         CBar *bar=engine.SeriesGetBar(Symbol(),Period(),index);
         if(bar==NULL)
            return;
         
         //--- Преобразуем координаты графика из представления время/цена объекта-бара в координаты по оси X и Y
         int x=(int)lparam,y=(int)dparam;
         if(!ChartTimePriceToXY(ChartID(),0,bar.Time(),(bar.Open()+bar.Close())/2.0,x,y))
            return;
         
         //--- Запретим перемещать график мышкой и показывать контекстное меню
         ChartSetInteger(ChartID(),CHART_MOUSE_SCROLL,false);
         ChartSetInteger(ChartID(),CHART_CONTEXT_MENU,false);
         
         //--- Создадим имя объекта-формы и скроем все объекты кроме одного с таким именем
         string name="FormBar_"+(string)index;
         HideFormAllExceptOne(name);
         
         //--- Если ещё нет объекта-формы с таким именем
         if(!IsPresentForm(name))
           {
            //--- создадим новый объект-форму
            form=bar.CreateForm(index,name,x,y,114,16);   
            if(form==NULL)
               return;
            
            //--- Установим форме флаги активности и неперемещаемости
            form.SetActive(true);
            form.SetMovable(false);
            //--- Установим непрозрачность 200
            form.SetOpacity(200);
            //--- Цвет фона формы зададим как первый цвет из массива цветов
            form.SetColorBackground(array_clr[0]);
            //--- Цвет очерчивающей рамки формы
            form.SetColorFrame(C'47,70,59');
            //--- Установим флаг рисования тени
            form.SetShadow(true);
            //--- Рассчитаем цвет тени как цвет фона графика, преобразованный в монохромный
            color clrS=form.ChangeColorSaturation(form.ColorBackground(),-100);
            //--- Если в настройках задано использовать цвет фона графика, то затемним монохромный цвет на 20 единиц
            //--- Иначе - будем использовать для рисования тени заданный в настройках цвет
            color clr=(InpUseColorBG ? form.ChangeColorLightness(clrS,-20) : InpColorForm3);
            //--- Нарисуем тень формы со смещением от формы вправо-вниз на три пикселя по всем осям
            //--- Непрозрачность тени при этом установим равной 200, а радиус размытия равный 4
            form.DrawShadow(2,2,clr,200,3);
            //--- Зальём фон формы вертикальным градиентом
            form.Erase(array_clr,form.Opacity());
            //--- Нарисуем очерчивающий прямоугольник по краям формы
            form.DrawRectangle(0,0,form.Width()-1,form.Height()-1,form.ColorFrame(),form.Opacity());
            //--- Если объект-форму не удалось добавить в список - удаляем форму и уходим из обработчика
            if(!list_forms.Add(form))
              {
               delete form;
               return;
              }
            //--- Зафиксируем внешний вид формы
            form.Done();
           }
         //--- Если объект-форма существует
         if(form!=NULL)
           {
            //--- нарисуем на нём текст с описанием типа бара, соответствующего положению курсора мышки и покажем форму
            form.TextOnBG(0,bar.BodyTypeDescription(),form.Width()/2,form.Height()/2-1,FRAME_ANCHOR_CENTER,C'7,28,21');
            form.Show();
           }
         //--- Перерисуем чарт
         ChartRedraw();
        }
     }
  }
//+------------------------------------------------------------------+

И это все доработки, которые нам нужно было сделать в советнике.

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



Что дальше

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

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

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

К содержанию

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

Графика в библиотеке DoEasy (Часть 73): Объект-форма графического элемента
Графика в библиотеке DoEasy (Часть 74): Базовый графический элемент на основе класса CCanvas
Графика в библиотеке DoEasy (Часть 75): Методы работы с примитивами и текстом в базовом графическом элементе
Графика в библиотеке DoEasy (Часть 76): Объект Форма и предопределённые цветовые темы
Графика в библиотеке DoEasy (Часть 77): Класс объекта Тень
Графика в библиотеке DoEasy (Часть 78): Принципы анимации в библиотеке. Нарезка изображений
Графика в библиотеке DoEasy (Часть 79): Класс объекта "Кадр анимации" и его объекты-наследники
Графика в библиотеке DoEasy (Часть 80): Класс объекта "Кадр геометрической анимации"
Графика в библиотеке DoEasy (Часть 81): Интегрируем графику в объекты библиотеки

Прикрепленные файлы |
MQL5.zip (4097.98 KB)
Почти конструктор для создания советника Почти конструктор для создания советника
Предлагаю свой набор торговых функций в виде готового советника. Представленный способ позволяет получать множество торговых стратегий простым добавлением индикаторов и изменением входных параметров.
Стать хорошим программистом (Часть 1): избавляемся от пяти привычек, чтобы лучше программировать на MQL5 Стать хорошим программистом (Часть 1): избавляемся от пяти привычек, чтобы лучше программировать на MQL5
У начинающих и даже у продвинутых программистов есть различные вредные привычки, которые мешают им стать лучше. В этой статье мы обсудим их и посмотрим, что с ними можно сделать. Статья предназначена для всех, кто хочет стать успешным MQL5-программистом.
Графика в библиотеке DoEasy (Часть 83): Класс абстрактного стандартного графического объекта Графика в библиотеке DoEasy (Часть 83): Класс абстрактного стандартного графического объекта
В статье создадим класс абстрактного графического объекта. Этот объект будет основой для создания классов стандартных графических объектов. Свойств у графических объектов много, и сегодня, прежде чем создать класс абстрактного графического объекта, нам необходимо будет сделать объёмную подготовительную работу — прописать эти свойства в перечислениях библиотеки.
Графика в библиотеке DoEasy (Часть 81): Интегрируем графику в объекты библиотеки Графика в библиотеке DoEasy (Часть 81): Интегрируем графику в объекты библиотеки
Начинаем интегрирование уже созданных графических объектов в остальные — ранее созданные объекты библиотеки, что в итоге наделит каждый объект библиотеки своим графическим объектом, позволяющим интерактивно взаимодействовать пользователю с программой.