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

Графика в библиотеке DoEasy (Часть 93): Готовим функционал для создания составных графических объектов

MetaTrader 5Примеры | 21 января 2022, 17:40
1 532 0
Artyom Trishkin
Artyom Trishkin

Содержание


Концепция

Клиентский терминал MetaTrader 5 предоставляет широкий набор графических аналитических инструментов для использования при различных графических построениях на графиках. Но часто слышно, что людям не хватает того или иного инструмента. В терминале мы имеем 44 аналитических инструмента. А теперь представим, что можно было бы сделать, если объединять эти графические объекты в связанные наборы, создавая тем самым некие новые инструменты для технического анализа. И язык MQL5 позволяет нам это сделать!

Наша библиотека будет поддерживать создание сложных составных графических объектов, в которых эти объекты смогут иметь любую иерархию связей. У каждого такого объекта будет базовый графический объект-основа, который будет иметь список присоединённых к нему других графических объектов. Базовый объект будет иметь функционал для управления свойствами подчинённых графических объектов, а подчинённые объекты в свою очередь будут иметь набор координат базового объекта X и Y, к которым они будут прикрепляться. При этом координатой может быть не только одно значение, а целый список свойств базового объекта. Например, для расчёта координаты X зависимого объекта мы сможем использовать не просто координату X какой-то точки базового объекта, а несколько, например, две координаты X — точка 0 и 1 трендовой линии. Тогда координатой X подчинённого объекта сможет быть среднее значение двух координат X базового объекта.

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

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


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

В файле \MQL5\Include\DoEasy\Defines.mqh в перечислении типов графических элементов допишем ещё один тип — расширенный стандартный графический объект:

//+------------------------------------------------------------------+
//| Список типов графических элементов                               |
//+------------------------------------------------------------------+
enum ENUM_GRAPH_ELEMENT_TYPE
  {
   GRAPH_ELEMENT_TYPE_STANDARD,                       // Стандартный графический объект
   GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED,              // Расширенный стандартный графический объект
   GRAPH_ELEMENT_TYPE_ELEMENT,                        // Элемент
   GRAPH_ELEMENT_TYPE_SHADOW_OBJ,                     // Объект тени
   GRAPH_ELEMENT_TYPE_FORM,                           // Форма
   GRAPH_ELEMENT_TYPE_WINDOW,                         // Окно
  };
//+------------------------------------------------------------------+

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

//+------------------------------------------------------------------+
//| Целочисленные свойства стандартного графического объекта         |
//+------------------------------------------------------------------+
enum ENUM_GRAPH_OBJ_PROP_INTEGER
  {
   //--- Дополнительные свойства
   GRAPH_OBJ_PROP_ID = 0,                             // Идентификатор объекта
   GRAPH_OBJ_PROP_BASE_ID,                            // Идентификатор базового объекта
   GRAPH_OBJ_PROP_TYPE,                               // Тип графичесеого объекта (ENUM_OBJECT)
   GRAPH_OBJ_PROP_ELEMENT_TYPE,                       // Тип графического элемента (ENUM_GRAPH_ELEMENT_TYPE)
   GRAPH_OBJ_PROP_SPECIES,                            // Вид графического объекта (ENUM_GRAPH_OBJ_SPECIES)
   GRAPH_OBJ_PROP_BELONG,                             // Принадлежность графического объекта
   GRAPH_OBJ_PROP_CHART_ID,                           // Идентификатор графика
   GRAPH_OBJ_PROP_WND_NUM,                            // Номер подокна графика
   GRAPH_OBJ_PROP_NUM,                                // Номер объекта в списке
   GRAPH_OBJ_PROP_CHANGE_HISTORY,                     // Флаг хранения истории изменений
   GRAPH_OBJ_PROP_GROUP,                              // Группа объектов, к которой принадлежит графический объект
   //--- Общие свойства всех графических объектов
   GRAPH_OBJ_PROP_CREATETIME,                         // Время создания объекта
   GRAPH_OBJ_PROP_TIMEFRAMES,                         // Видимость объекта на таймфреймах
   GRAPH_OBJ_PROP_BACK,                               // Объект на заднем плане
   GRAPH_OBJ_PROP_ZORDER,                             // Приоритет графического объекта на получение события нажатия мышки на графике
   GRAPH_OBJ_PROP_HIDDEN,                             // Запрет на показ имени графического объекта в списке объектов терминала
   GRAPH_OBJ_PROP_SELECTED,                           // Выделенность объекта
   GRAPH_OBJ_PROP_SELECTABLE,                         // Доступность объекта
//--- Свойства, принадлежащие различным графическим объектам
   GRAPH_OBJ_PROP_TIME,                               // Координата времени
   GRAPH_OBJ_PROP_COLOR,                              // Цвет
   GRAPH_OBJ_PROP_STYLE,                              // Стиль
   GRAPH_OBJ_PROP_WIDTH,                              // Толщина линии
   GRAPH_OBJ_PROP_FILL,                               // Заливка объекта цветом
   GRAPH_OBJ_PROP_READONLY,                           // Возможность редактирования текста в объекте Edit
   GRAPH_OBJ_PROP_LEVELS,                             // Количество уровней
   GRAPH_OBJ_PROP_LEVELCOLOR,                         // Цвет линии-уровня
   GRAPH_OBJ_PROP_LEVELSTYLE,                         // Стиль линии-уровня
   GRAPH_OBJ_PROP_LEVELWIDTH,                         // Толщина линии-уровня
   GRAPH_OBJ_PROP_ALIGN,                              // Горизонтальное выравнивание текста в объекте "Поле ввода" (OBJ_EDIT)
   GRAPH_OBJ_PROP_FONTSIZE,                           // Размер шрифта
   GRAPH_OBJ_PROP_RAY_LEFT,                           // Луч продолжается влево
   GRAPH_OBJ_PROP_RAY_RIGHT,                          // Луч продолжается вправо
   GRAPH_OBJ_PROP_RAY,                                // Вертикальная линия продолжается на все окна графика
   GRAPH_OBJ_PROP_ELLIPSE,                            // Отображение полного эллипса для объекта "Дуги Фибоначчи"
   GRAPH_OBJ_PROP_ARROWCODE,                          // Код стрелки для объекта "Стрелка"
   GRAPH_OBJ_PROP_ANCHOR,                             // Положение точки привязки графического объекта
   GRAPH_OBJ_PROP_XDISTANCE,                          // Дистанция в пикселях по оси X от угла привязки
   GRAPH_OBJ_PROP_YDISTANCE,                          // Дистанция в пикселях по оси Y от угла привязки
   GRAPH_OBJ_PROP_DIRECTION,                          // Тренд объекта Ганна
   GRAPH_OBJ_PROP_DEGREE,                             // Уровень волновой разметки Эллиотта
   GRAPH_OBJ_PROP_DRAWLINES,                          // Отображение линий для волновой разметки Эллиотта
   GRAPH_OBJ_PROP_STATE,                              // Состояние кнопки (Нажата/Отжата)
   GRAPH_OBJ_PROP_CHART_OBJ_CHART_ID,                 // Идентификатор объекта "График" (OBJ_CHART).
   GRAPH_OBJ_PROP_CHART_OBJ_PERIOD,                   // Период для объекта "График"
   GRAPH_OBJ_PROP_CHART_OBJ_DATE_SCALE,               // Признак отображения шкалы времени для объекта "График"
   GRAPH_OBJ_PROP_CHART_OBJ_PRICE_SCALE,              // Признак отображения ценовой шкалы для объекта "График"
   GRAPH_OBJ_PROP_CHART_OBJ_CHART_SCALE,              // Масштаб для объекта "График"
   GRAPH_OBJ_PROP_XSIZE,                              // Ширина объекта по оси X в пикселях.
   GRAPH_OBJ_PROP_YSIZE,                              // Высота объекта по оси Y в пикселях.
   GRAPH_OBJ_PROP_XOFFSET,                            // X-координата левого верхнего угла прямоугольной области видимости.
   GRAPH_OBJ_PROP_YOFFSET,                            // Y-координата левого верхнего угла прямоугольной области видимости.
   GRAPH_OBJ_PROP_BGCOLOR,                            // Цвет фона для OBJ_EDIT, OBJ_BUTTON, OBJ_RECTANGLE_LABEL
   GRAPH_OBJ_PROP_CORNER,                             // Угол графика для привязки графического объекта
   GRAPH_OBJ_PROP_BORDER_TYPE,                        // Тип рамки для объекта "Прямоугольная рамка"
   GRAPH_OBJ_PROP_BORDER_COLOR,                       // Цвет рамки для объекта OBJ_EDIT и OBJ_BUTTON
  };
#define GRAPH_OBJ_PROP_INTEGER_TOTAL (55)             // Общее количество целочисленных свойств
#define GRAPH_OBJ_PROP_INTEGER_SKIP  (0)              // Количество неиспользуемых в сортировке целочисленных свойств
//+------------------------------------------------------------------+

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

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

//+------------------------------------------------------------------+
//| Строковые свойства стандартного графического объекта             |
//+------------------------------------------------------------------+
enum ENUM_GRAPH_OBJ_PROP_STRING
  {
   GRAPH_OBJ_PROP_NAME = (GRAPH_OBJ_PROP_INTEGER_TOTAL+GRAPH_OBJ_PROP_DOUBLE_TOTAL), // Имя объекта
   GRAPH_OBJ_PROP_BASE_NAME,                          // Имя базового объекта
   GRAPH_OBJ_PROP_TEXT,                               // Описание объекта (текст, содержащийся в объекте)
   GRAPH_OBJ_PROP_TOOLTIP,                            // Текст всплывающей подсказки
   GRAPH_OBJ_PROP_LEVELTEXT,                          // Описание уровня
   GRAPH_OBJ_PROP_FONT,                               // Шрифт
   GRAPH_OBJ_PROP_BMPFILE,                            // Имя BMP-файла для объекта "Графическая метка"
   GRAPH_OBJ_PROP_CHART_OBJ_SYMBOL,                   // Символ для объекта "График" 
  };
#define GRAPH_OBJ_PROP_STRING_TOTAL  (8)              // Общее количество строковых свойств
//+------------------------------------------------------------------+

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

//+------------------------------------------------------------------+
//| Возможные критерии сортировки графических объектов               |
//+------------------------------------------------------------------+
#define FIRST_GRAPH_OBJ_DBL_PROP  (GRAPH_OBJ_PROP_INTEGER_TOTAL-GRAPH_OBJ_PROP_INTEGER_SKIP)
#define FIRST_GRAPH_OBJ_STR_PROP  (GRAPH_OBJ_PROP_INTEGER_TOTAL-GRAPH_OBJ_PROP_INTEGER_SKIP+GRAPH_OBJ_PROP_DOUBLE_TOTAL-GRAPH_OBJ_PROP_DOUBLE_SKIP)
enum ENUM_SORT_GRAPH_OBJ_MODE
  {
//--- Сортировка по целочисленным свойствам
   SORT_BY_GRAPH_OBJ_ID = 0,                             // Сортировать по идентификатору объекта
   SORT_BY_GRAPH_OBJ_BASE_ID,                            // Сортировать по идентификатору базового объекта
   SORT_BY_GRAPH_OBJ_TYPE,                               // Сортировать по типу объекта
   SORT_BY_GRAPH_OBJ_ELEMENT_TYPE,                       // Сортировать по типу графического элемента
   SORT_BY_GRAPH_OBJ_SPECIES,                            // Сортировать по виду графического объекта
   SORT_BY_GRAPH_OBJ_BELONG,                             // Сортировать по принадлежности графического объекта
   SORT_BY_GRAPH_OBJ_CHART_ID,                           // Сортировать по идентификатору графика
   SORT_BY_GRAPH_OBJ_WND_NUM,                            // Сортировать по номеру подокна графика
   SORT_BY_GRAPH_OBJ_NUM,                                // Сортировать по номеру объекта в списке
   SORT_BY_GRAPH_OBJ_CHANGE_HISTORY,                     // Сортировать по флагу хранения истории изменений
   SORT_BY_GRAPH_OBJ_GROUP,                              // Сортировать по группе объектов, к которой принадлежит графический объект
   SORT_BY_GRAPH_OBJ_CREATETIME,                         // Сортировать по времени создания объекта
   SORT_BY_GRAPH_OBJ_TIMEFRAMES,                         // Сортировать по видимости объекта на таймфреймах
   SORT_BY_GRAPH_OBJ_BACK,                               // Сортировать по свойству "Объект на заднем плане"
   SORT_BY_GRAPH_OBJ_ZORDER,                             // Сортировать по приоритету графического объекта на получение события нажатия мышки на графике
   SORT_BY_GRAPH_OBJ_HIDDEN,                             // Сортировать по запрету на показ имени графического объекта в списке объектов терминала
   SORT_BY_GRAPH_OBJ_SELECTED,                           // Сортировать по свойству"Выделенность объекта"
   SORT_BY_GRAPH_OBJ_SELECTABLE,                         // Сортировать по свойству "Доступность объекта"
   SORT_BY_GRAPH_OBJ_TIME,                               // Сортировать по координате времени
   SORT_BY_GRAPH_OBJ_COLOR,                              // Сортировать по цвету
   SORT_BY_GRAPH_OBJ_STYLE,                              // Сортировать по стилю
   SORT_BY_GRAPH_OBJ_WIDTH,                              // Сортировать по толщине линии
   SORT_BY_GRAPH_OBJ_FILL,                               // Сортировать по свойству"Заливка объекта цветом"
   SORT_BY_GRAPH_OBJ_READONLY,                           // Сортировать по возможности редактирования текста в объекте Edit
   SORT_BY_GRAPH_OBJ_LEVELS,                             // Сортировать по количеству уровней
   SORT_BY_GRAPH_OBJ_LEVELCOLOR,                         // Сортировать по цвету линии-уровня
   SORT_BY_GRAPH_OBJ_LEVELSTYLE,                         // Сортировать по стилю линии-уровня
   SORT_BY_GRAPH_OBJ_LEVELWIDTH,                         // Сортировать по толщине линии-уровня
   SORT_BY_GRAPH_OBJ_ALIGN,                              // Сортировать по свойству "Горизонтальное выравнивание текста в объекте "Поле ввода""
   SORT_BY_GRAPH_OBJ_FONTSIZE,                           // Сортировать по размеру шрифта
   SORT_BY_GRAPH_OBJ_RAY_LEFT,                           // Сортировать по свойству "Луч продолжается влево"
   SORT_BY_GRAPH_OBJ_RAY_RIGHT,                          // Сортировать по свойству"Луч продолжается вправо"
   SORT_BY_GRAPH_OBJ_RAY,                                // Сортировать по свойству "Вертикальная линия продолжается на все окна графика"
   SORT_BY_GRAPH_OBJ_ELLIPSE,                            // Сортировать по свойству "Отображение полного эллипса для объекта "Дуги Фибоначчи"
   SORT_BY_GRAPH_OBJ_ARROWCODE,                          // Сортировать по коду стрелки для объекта "Стрелка"
   SORT_BY_GRAPH_OBJ_ANCHOR,                             // Сортировать по положению точки привязки графического объекта
   SORT_BY_GRAPH_OBJ_XDISTANCE,                          // Сортировать по дистанции в пикселях по оси X от угла привязки
   SORT_BY_GRAPH_OBJ_YDISTANCE,                          // Сортировать по дистанции в пикселях по оси Y от угла привязки
   SORT_BY_GRAPH_OBJ_DIRECTION,                          // Сортировать по свойству "Тренд объекта Ганна"
   SORT_BY_GRAPH_OBJ_DEGREE,                             // Сортировать по свойству "Уровень волновой разметки Эллиотта"
   SORT_BY_GRAPH_OBJ_DRAWLINES,                          // Сортировать по свойству "Отображение линий для волновой разметки Эллиотта"
   SORT_BY_GRAPH_OBJ_STATE,                              // Сортировать по состоянию кнопки (Нажата/Отжата)
   SORT_BY_GRAPH_OBJ_OBJ_CHART_ID,                       // Сортировать по идентификатору объекта "График".
   SORT_BY_GRAPH_OBJ_CHART_OBJ_PERIOD,                   // Сортировать по периоду для объекта "График"
   SORT_BY_GRAPH_OBJ_CHART_OBJ_DATE_SCALE,               // Сортировать по признаку отображения шкалы времени для объекта "График"
   SORT_BY_GRAPH_OBJ_CHART_OBJ_PRICE_SCALE,              // Сортировать по признаку отображения ценовой шкалы для объекта "График"
   SORT_BY_GRAPH_OBJ_CHART_OBJ_CHART_SCALE,              // Сортировать по масштабу для объекта "График"
   SORT_BY_GRAPH_OBJ_XSIZE,                              // Сортировать по ширине объекта по оси X в пикселях
   SORT_BY_GRAPH_OBJ_YSIZE,                              // Сортировать по высоте объекта по оси Y в пикселях
   SORT_BY_GRAPH_OBJ_XOFFSET,                            // Сортировать по X-координате левого верхнего угла прямоугольной области видимости
   SORT_BY_GRAPH_OBJ_YOFFSET,                            // Сортировать по Y-координате левого верхнего угла прямоугольной области видимости
   SORT_BY_GRAPH_OBJ_BGCOLOR,                            // Сортировать по цвету фона для OBJ_EDIT, OBJ_BUTTON, OBJ_RECTANGLE_LABEL
   SORT_BY_GRAPH_OBJ_CORNER,                             // Сортировать по углу графика для привязки графического объекта
   SORT_BY_GRAPH_OBJ_BORDER_TYPE,                        // Сортировать по типу рамки для объекта "Прямоугольная рамка"
   SORT_BY_GRAPH_OBJ_BORDER_COLOR,                       // Сортировать по цвету рамки для объекта OBJ_EDIT и OBJ_BUTTON
//--- Сортировка по вещественным свойствам
   SORT_BY_GRAPH_OBJ_PRICE = FIRST_GRAPH_OBJ_DBL_PROP,   // Сортировать по координате цены
   SORT_BY_GRAPH_OBJ_LEVELVALUE,                         // Сортировать по значению уровня
   SORT_BY_GRAPH_OBJ_SCALE,                              // Сортировать по масштабу (свойство объектов Ганна и объекта "Дуги Фибоначчи")
   SORT_BY_GRAPH_OBJ_ANGLE,                              // Сортировать по углу
   SORT_BY_GRAPH_OBJ_DEVIATION,                          // Сортировать по отклонению для канала стандартного отклонения
//--- Сортировка по строковым свойствам
   SORT_BY_GRAPH_OBJ_NAME = FIRST_GRAPH_OBJ_STR_PROP,    // Сортировать по имени объекта
   SORT_BY_GRAPH_OBJ_BASE_NAME,                          // Сортировать по имени базового объекта
   SORT_BY_GRAPH_OBJ_TEXT,                               // Сортировать по описанию объекта
   SORT_BY_GRAPH_OBJ_TOOLTIP,                            // Сортировать по тексту всплывающей подсказки
   SORT_BY_GRAPH_OBJ_LEVELTEXT,                          // Сортировать по описанию уровня
   SORT_BY_GRAPH_OBJ_FONT,                               // Сортировать по шрифту
   SORT_BY_GRAPH_OBJ_BMPFILE,                            // Сортировать по имени BMP-файла для объекта "Графическая метка"
   SORT_BY_GRAPH_OBJ_CHART_OBJ_SYMBOL,                   // Сортировать по символу для объекта "График" 
  };
//+------------------------------------------------------------------+

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


В файле \MQL5\Include\DoEasy\Data.mqh впишем индексы новых сообщений библиотеки:

   MSG_LIB_SYS_REQUEST_OUTSIDE_LONG_ARRAY,            // Запрос за пределами long-массива
   MSG_LIB_SYS_REQUEST_OUTSIDE_DOUBLE_ARRAY,          // Запрос за пределами double-массива
   MSG_LIB_SYS_REQUEST_OUTSIDE_STRING_ARRAY,          // Запрос за пределами string-массива
   MSG_LIB_SYS_REQUEST_OUTSIDE_ARRAY,                 // Запрос за пределами массива

...

   MSG_GRAPH_ELEMENT_TYPE_STANDARD,                   // Стандартный графический объект
   MSG_GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED,          // Расширенный стандартный графический объект
   MSG_GRAPH_ELEMENT_TYPE_ELEMENT,                    // Элемент
   MSG_GRAPH_ELEMENT_TYPE_SHADOW_OBJ,                 // Объект тени
   MSG_GRAPH_ELEMENT_TYPE_FORM,                       // Форма
   MSG_GRAPH_ELEMENT_TYPE_WINDOW,                     // Окно

...

   MSG_GRAPH_OBJ_PROP_ID,                             // Идентификатор объекта
   MSG_GRAPH_OBJ_PROP_BASE_ID,                        // Идентификатор базового объекта
   MSG_GRAPH_OBJ_PROP_TYPE,                           // Тип графического объекта (ENUM_OBJECT)
   MSG_GRAPH_OBJ_PROP_ELEMENT_TYPE,                   // Тип графического элемента (ENUM_GRAPH_ELEMENT_TYPE)

...

   MSG_GRAPH_OBJ_PROP_NAME,                           // Имя объекта
   MSG_GRAPH_OBJ_PROP_BASE_NAME,                      // Имя базового объекта
   MSG_GRAPH_OBJ_PROP_TEXT,                           // Описание объекта (текст, содержащийся в объекте)
   MSG_GRAPH_OBJ_PROP_TOOLTIP,                        // Текст всплывающей подсказки
   MSG_GRAPH_OBJ_PROP_LEVELTEXT,                      // Описание уровня
   MSG_GRAPH_OBJ_PROP_FONT,                           // Шрифт
   MSG_GRAPH_OBJ_PROP_BMPFILE,                        // Имя BMP-файла для объекта "Графическая метка"
   MSG_GRAPH_OBJ_PROP_SYMBOL,                         // Символ для объекта "График"

...

   MSG_GRAPH_OBJ_EVN_GRAPH_OBJ_CREATE,                // Создан новый графический объект
   MSG_GRAPH_OBJ_EVN_GRAPH_OBJ_CHANGE,                // Изменено свойство графического объекта
   MSG_GRAPH_OBJ_EVN_GRAPH_OBJ_RENAME,                // Графический объект переименован
   MSG_GRAPH_OBJ_EVN_GRAPH_OBJ_DELETE,                // Графический объект удалён
   MSG_GRAPH_OBJ_EVN_GRAPH_OBJ_DEL_CHART,             // Графический объект удалён вместе с графиком
   
//--- CGStdGraphObj (Extended)
   MSG_GRAPH_OBJ_FAILED_CREATE_NEW_EXT_OBJ,           // Не удалось создать объект класса расширенного графического объекта
   MSG_GRAPH_OBJ_FAILED_CREATE_NEW_BASE_EXT_OBJ,      // Не удалось создать базовый объект расширенного графического объекта
   MSG_GRAPH_OBJ_FAILED_ADD_EXT_OBJ_TO_LIST,          // Не удалось добавить расширенный стандартный графический объект в список
   MSG_GRAPH_OBJ_FAILED_ADD_BASE_EXT_OBJ_TO_LIST,     // Не удалось добавить базовый объект в список
   MSG_GRAPH_OBJ_FAILED_CREATE_NEW_DEP_EXT_OBJ,       // Не удалось создать подчинённый объект расширенного графического объекта
   MSG_GRAPH_OBJ_FAILED_ADD_DEP_EXT_OBJ_TO_LIST,      // Не удалось добавить подчинённый объект в список
   MSG_GRAPH_OBJ_FAILED_GET_EXT_OBJ_FROM_LIST,        // Не удалось получить расширенный графический объект из списка
   MSG_GRAPH_OBJ_PROP_NUM_EXT_BASE_OBJ,               // Базовый объект расширенного графического объекта
   MSG_GRAPH_OBJ_NOT_EXT_OBJ,                         // Объект не является расширенным стандартным графическим объектом
   
//--- CLinkedPivotPoint
   MSG_GRAPH_OBJ_EXT_NOT_ANY_PIVOTS_X,                // Для объекта не установлено ни одной опорной точки по оси X
   MSG_GRAPH_OBJ_EXT_NOT_ANY_PIVOTS_Y,                // Для объекта не установлено ни одной опорной точки по оси Y
   MSG_GRAPH_OBJ_EXT_NOT_ATACHED_TO_BASE,             // Объект не привязан к базовому графическому объекту
   MSG_GRAPH_OBJ_EXT_FAILED_CREATE_PP_DATA_OBJ,       // Не удалось создать объект данных опорной точки X и Y
  };
//+------------------------------------------------------------------+

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

   {"Запрос за пределами long-массива","Data requested outside the long-array"},
   {"Запрос за пределами double-массива","Data requested outside the double-array"},
   {"Запрос за пределами string-массива","Data requested outside the string-array"},
   {"Запрос за пределами массива","Data requested outside the array"},

...

   {"Стандартный графический объект","Standard graphic object"},
   {"Расширенный стандартный графический объект","Extended standard graphic object"},
   {"Элемент","Element"},
   {"Объект тени","Shadow object"},
   {"Форма","Form"},
   {"Окно","Window"},

...

   {"Идентификатор объекта","Object ID"},
   {"Идентификатор базового объекта","Base object ID"},
   {"Тип объекта","Object type"},
   {"Тип графического элемента","Graphic element type"},

...

   {"Имя","Name"},
   {"Имя базового объекта","Base object name"},
   {"Описание","Description"},
   {"Текст всплывающей подсказки","The text of a tooltip"},
   {"Описание уровня","Level description"},
   {"Шрифт","Font"},
   {"Имя BMP-файла","BMP-file name"},
   {"Символ графика","Chart Symbol"},

...

   {"Создан новый графический объект","New graphic object created"},
   {"Изменено свойство графического объекта","Changed graphic object property"},
   {"Графический объект переименован","Graphic object renamed"},
   {"Удалён графический объект","Graphic object deleted"},
   {"Графический объект удалён вместе с графиком","The graphic object has been removed along with the chart"},
   
//--- CGStdGraphObj (Extended)
   {"Не удалось создать объект класса расширенного графического объекта","Failed to create the class object of extended standart graphics object"},
   {"Не удалось создать базовый объект расширенного графического объекта","Failed to create the base object of a extended graphics object"},
   {"Не удалось добавить расширенный стандартный графический объект в список","Failed to add extended standard graphic object to the list"},
   {"Не удалось добавить базовый объект в список","Failed to add base object to list"},
   {"Не удалось создать подчинённый объект расширенного графического объекта","Failed to create the dependent object of a extended graphics object"},
   {"Не удалось добавить подчинённый объект в список","Failed to add dependent object to list"},
   {"Не удалось получить расширенный графический объект из списка","Failed to get extended graphic object from list"},
   {"Базовый объект расширенного графического объекта","The base object of the extended graphical object"},
   {"Объект не является расширенным стандартным графическим объектом","The object is not an extended standard graphical object"},
   
//--- CLinkedPivotPoint
   {"Для объекта не установлено ни одной опорной точки по оси X","The object does not have any pivot points set along the x-axis"},
   {"Для объекта не установлено ни одной опорной точки по оси Y","The object does not have any pivot points set along the y-axis"},
   {"Объект не привязан к базовому графическому объекту","The object is not attached to the base graphical object"},
   {"Не удалось создать объект данных опорной точки X и Y.","Failed to create X and Y reference point data object"},
   
  };
//+---------------------------------------------------------------------+


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

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

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

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

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

public:
   //--- Конструктор
                     CGStdArrowBuyObj(const long chart_id,const string name,const bool extended) : 
                        CGStdGraphObj(OBJECT_DE_TYPE_GSTD_ARROW_BUY,(!extended ? GRAPH_ELEMENT_TYPE_STANDARD : GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED),(!extended ? GRAPH_OBJ_BELONG_NO_PROGRAM : GRAPH_OBJ_BELONG_PROGRAM),GRAPH_OBJ_SPECIES_ARROWS,chart_id,1,name)
                          {
                           //--- Укажем свойство объекта
                           CGStdGraphObj::SetProperty(GRAPH_OBJ_PROP_ANCHOR,0,ANCHOR_TOP);
                          }

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

//+------------------------------------------------------------------+
//| Возвращает истину, если объект поддерживает переданное           |
//| целочисленное свойство, возвращает ложь в противном случае       |
//+------------------------------------------------------------------+
bool CGStdArrowBuyObj::SupportProperty(ENUM_GRAPH_OBJ_PROP_INTEGER property)
  {
   switch((int)property)
     {
      //--- Поддерживаемые свойства
      case GRAPH_OBJ_PROP_ID           :
      case GRAPH_OBJ_PROP_BASE_ID      :
      case GRAPH_OBJ_PROP_TYPE         :
      case GRAPH_OBJ_PROP_ELEMENT_TYPE : 
      case GRAPH_OBJ_PROP_GROUP        : 
      case GRAPH_OBJ_PROP_BELONG       :
      case GRAPH_OBJ_PROP_CHART_ID     :
      case GRAPH_OBJ_PROP_WND_NUM      :
      case GRAPH_OBJ_PROP_NUM          :
      case GRAPH_OBJ_PROP_CREATETIME   :
      case GRAPH_OBJ_PROP_CHANGE_HISTORY:
      case GRAPH_OBJ_PROP_TIMEFRAMES   :
      case GRAPH_OBJ_PROP_BACK         :
      case GRAPH_OBJ_PROP_ZORDER       :
      case GRAPH_OBJ_PROP_HIDDEN       :
      case GRAPH_OBJ_PROP_SELECTED     :
      case GRAPH_OBJ_PROP_SELECTABLE   :
      case GRAPH_OBJ_PROP_TIME         :
      case GRAPH_OBJ_PROP_COLOR        :
      case GRAPH_OBJ_PROP_STYLE        :
      case GRAPH_OBJ_PROP_WIDTH        :
      case GRAPH_OBJ_PROP_ANCHOR       : return true;
      //--- Остальные свойства не поддерживаются
      //--- По умолчанию false
      default: break;
     }
   return false;
  }
//+------------------------------------------------------------------+
//| Возвращает истину, если объект поддерживает переданное           |
//| вещественное свойство, возвращает ложь в противном случае        |
//+------------------------------------------------------------------+
bool CGStdArrowBuyObj::SupportProperty(ENUM_GRAPH_OBJ_PROP_DOUBLE property)
  {
   switch((int)property)
     {
      //--- Поддерживаемые свойства
      case GRAPH_OBJ_PROP_PRICE        : return true;
      //--- Остальные свойства не поддерживаются
      //--- По умолчанию false
      default: break;
     }
   return false;
  }
//+------------------------------------------------------------------+
//| Возвращает истину, если объект поддерживает переданное           |
//| строковое свойство, возвращает ложь в противном случае           |
//+------------------------------------------------------------------+
bool CGStdArrowBuyObj::SupportProperty(ENUM_GRAPH_OBJ_PROP_STRING property)
  {
   switch((int)property)
     {
      //--- Поддерживаемые свойства
      case GRAPH_OBJ_PROP_NAME            :
      case GRAPH_OBJ_PROP_BASE_NAME       :
      case GRAPH_OBJ_PROP_TEXT            :
      case GRAPH_OBJ_PROP_TOOLTIP         :  return true;
      //--- Остальные свойства не поддерживаются
      //--- По умолчанию false
      default: break;
     }
   return false;
  }
//+------------------------------------------------------------------+

Такие изменения нужно провести во всех файлах, расположенных в папке \MQL5\Include\DoEasy\Objects\Graph\Standard\, что, впрочем, уже сделано, и посмотреть все доработки можно в прикреплённых к статье файлах.

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

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

//+------------------------------------------------------------------+
//| Возвращает описание типа графического элемента                   |
//+------------------------------------------------------------------+
string CGBaseObj::TypeElementDescription(void)
  {
   return
     (
      this.TypeGraphElement()==GRAPH_ELEMENT_TYPE_STANDARD           ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_STANDARD)           :
      this.TypeGraphElement()==GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED  ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED)  :
      this.TypeGraphElement()==GRAPH_ELEMENT_TYPE_ELEMENT            ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_ELEMENT)            :
      this.TypeGraphElement()==GRAPH_ELEMENT_TYPE_SHADOW_OBJ         ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_SHADOW_OBJ)         :
      this.TypeGraphElement()==GRAPH_ELEMENT_TYPE_FORM               ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_FORM)               :
      this.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WINDOW             ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WINDOW)             :
      "Unknown"
     );
  }
//+------------------------------------------------------------------+


Классы для указания координат зависимого объекта

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

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

У нас будут три класса:

  1. Класс данных опорной точки, который будет хранить те свойства, по которым зависимый объект прикрепляется к одной координатной оси базового объекта;
  2. Класс данных двух опорных точек, который будет хранить в себе два объекта первого класса. В одном будут храниться данные свойств, по которым зависимый объект прикрепляется по X-координате к базовому, а во втором — данные, по которым объект прикрепляется по Y-координате;
  3. Класс связанных опорных точек. Этот класс будет хранить в себе список объектов второго типа — за каждую его точку привязки будет отвечать один объект класса данных двух опорных точек. Т.е. каждый объект будет хранить в себе те свойства базового объекта, по которым строится одна точка привязки зависимого объекта — её координаты X и Y, которые в свою очередь могуть быть рассчитаны из нескольких координат базового объекта, список которых для каждой оси хранится в объекте первого типа.

Объект первого типа будет создан на основе обычного двумерного массива.
Второе измерение массива будет иметь размерность 2, в нулевой ячейке будет указано само свойство, например, "Время", или "Цена , а в первой — модификатор свойства. Например, у трендовой линии есть две точки привязки, и какая именно точка используется для указания кординаты — и будет записываться в ячейку 1 второго измерения массива. Для левой точки трендовой линии это значение будет 0, для правой — 1.
В большинстве случаев массив в его первом измерении будет иметь размер 1 — указание всего одного свойства базового объекта, к координатам которого прикрепляется зависимый. Но, если нам нужно будет прикрепить объект к рассчитанным координатам, например, между двух точек трендовой линии, то нам нужно будет увеличить размерность массива до двух — в первом будут прописаны свойства первой опорной точки базового объекта, во втором — второй. В этом случае зависимый объект будет прикреплён к базовому по некой формуле (по умолчанию среднее), которая устанавливается пользователем библиотеки самостоятельно в коде. Но этот функционал сегодня делать не будем. Сегодня мы подготавливаем всё необходимое для реализиции задуманного.

Напишем эти классы прямо в файле абстрактного стандартного графического объекта \MQL5\Include\DoEasy\Objects\Graph\Standard\GStdGraphObj.mqh, так как именно в нём они и будут объявляться и использоваться в случае, если создаваемый объект является расширенным стандартным графическим объектом.

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

//+------------------------------------------------------------------+
//|                                                 GStdGraphObj.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 "..\GBaseObj.mqh"
#include "..\..\..\Services\Properties.mqh"
//+------------------------------------------------------------------+
//| Класс данных опорной точки зависимого объекта                    |
//+------------------------------------------------------------------+
class CPivotPointData
  {
private:
   bool              m_axis_x;
   int               m_property_x[][2];
public:
//--- (1) Устанавливает (2) возвращает флаг, что опорная точка принадлежит координате X
   void              SetAxisX(const bool axis_x)         { this.m_axis_x=axis_x; }
   bool              IsAxisX(void)                 const { return this.m_axis_x; }
//--- Возвращает количество опорных точек базового объекта для расчёта координаты подчинённого
   int               GetBasePivotsNum(void)  const { return ::ArrayRange(this.m_property_x,0);  }
//--- Добавляет новую опорную точку базового объекта для расчёта координаты подчинённого
   bool              AddNewBasePivotPoint(const string source,const int pivot_prop,const int pivot_num)
                       {
                        //--- Получаем размер массива и 
                        int pivot_index=this.GetBasePivotsNum();
                        //--- если размер массива увеличить не удалось - сообщаем об этом и возвращаем false
                        if(::ArrayResize(this.m_property_x,pivot_index+1)!=pivot_index+1)
                          {
                           CMessage::ToLog(source,MSG_LIB_SYS_FAILED_ARRAY_RESIZE);
                           return false;
                          }
                        //--- Возвращаем результат изменения значений вновь добавленного нового измерения массива
                        return this.ChangeBasePivotPoint(source,pivot_index,pivot_prop,pivot_num);
                       }
//--- Изменяет указанную опорную точку базового объекта для расчёта координаты подчинённого
   bool              ChangeBasePivotPoint(const string source,const int pivot_index,const int pivot_prop,const int pivot_num)
                       {
                        //--- Получаем размер массива и если он нулевой - сообщаем об этом и возвращаем false
                        int n=this.GetBasePivotsNum();
                        if(n==0)
                          {
                           CMessage::ToLog(source,(this.IsAxisX() ? MSG_GRAPH_OBJ_EXT_NOT_ANY_PIVOTS_X : MSG_GRAPH_OBJ_EXT_NOT_ANY_PIVOTS_Y));
                           return false;
                          }
                        //--- Если указанный индекс выхоит за пределы массива - сообщаем об этом и возвращаем false
                        if(pivot_index<0 || pivot_index>n-1)
                          {
                           CMessage::ToLog(source,MSG_LIB_SYS_REQUEST_OUTSIDE_ARRAY);
                           return false;
                          }
                        //--- Устанавливаем переданные в метод значения в указанные ячейки массива по индексу
                        this.m_property_x[pivot_index][0]=pivot_prop;
                        this.m_property_x[pivot_index][1]=pivot_num;
                        return true;
                       }

//--- Конструктор/Деструктор
                     CPivotPointData(void){;}
                    ~CPivotPointData(void){;}
  };
//+------------------------------------------------------------------+

Класс содержит двумерный массив и два метода для изменения его размера (добавлении новой опорной точки) и установки значений в указанное первое измерение массива. Также класс имеет методы для установки указания того, данные какой координаты записываются в массив — X или Y. При выводе сообщений из класса, проверяется этот флаг и выводится соответствующая запись об ошибке.

Следом за листингом этого класса напишем класс данных опорных точек X и Y составного объекта:

//+------------------------------------------------------------------+
//| Класс данных опорных точек X и Y составного объекта              |
//+------------------------------------------------------------------+
class CPivotPointXY : public CObject
  {
private:
   CPivotPointData   m_pivot_point_x;            // Опорная точка X-координаты
   CPivotPointData   m_pivot_point_y;            // Опорная точка Y-координаты
public:
//--- Возвращает указатель на объект данных опорной точки (1) X-координаты, (2) Y-координаты
   CPivotPointData  *GetPivotPointDataX(void)      { return &this.m_pivot_point_x;                    }
   CPivotPointData  *GetPivotPointDataY(void)      { return &this.m_pivot_point_y;                    }
//--- Возвращает количество опорных точек базового объекта для расчёта координаты (1) X, (2) Y
   int               GetBasePivotsNumX(void) const { return this.m_pivot_point_x.GetBasePivotsNum();  }
   int               GetBasePivotsNumY(void) const { return this.m_pivot_point_y.GetBasePivotsNum();  }
//--- Добавляет новую опорную точку базового объекта для расчёта координаты X подчинённого
   bool              AddNewBasePivotPointX(const int pivot_prop,const int pivot_num)
                       {
                        return this.m_pivot_point_x.AddNewBasePivotPoint(DFUN,pivot_prop,pivot_num);
                       }
//--- Добавляет новую опорную точку базового объекта для расчёта координаты Y подчинённого
   bool              AddNewBasePivotPointY(const int pivot_prop,const int pivot_num)
                       {
                        return this.m_pivot_point_y.AddNewBasePivotPoint(DFUN,pivot_prop,pivot_num);
                       }
//--- Добавляет новые опорные точки базового объекта для расчёта координат X и Y подчинённого
   bool              AddNewBasePivotPointXY(const int pivot_prop_x,const int pivot_num_x,
                                            const int pivot_prop_y,const int pivot_num_y)
                       {
                        bool res=true;
                        res &=this.m_pivot_point_x.AddNewBasePivotPoint(DFUN,pivot_prop_x,pivot_num_x);
                        res &=this.m_pivot_point_y.AddNewBasePivotPoint(DFUN,pivot_prop_y,pivot_num_y);
                        return res;
                       }
//--- Изменяет указанную опорную точку базового объекта для расчёта координаты X подчинённого
   bool              ChangeBasePivotPointX(const int pivot_index,const int pivot_prop,const int pivot_num)
                       {
                        return this.m_pivot_point_x.ChangeBasePivotPoint(DFUN,pivot_index,pivot_prop,pivot_num);
                       }
//--- Изменяет указанную опорную точку базового объекта для расчёта координаты Y подчинённого
   bool              ChangeBasePivotPointY(const int pivot_index,const int pivot_prop,const int pivot_num)
                       {
                        return this.m_pivot_point_y.ChangeBasePivotPoint(DFUN,pivot_index,pivot_prop,pivot_num);
                       }
//--- Изменяет указанные опорные точки базового объекта для расчёта координат X и Y
   bool              ChangeBasePivotPointXY(const int pivot_index,
                                            const int pivot_prop_x,const int pivot_num_x,
                                            const int pivot_prop_y,const int pivot_num_y)
                       {
                        bool res=true;
                        res &=this.m_pivot_point_x.ChangeBasePivotPoint(DFUN,pivot_index,pivot_prop_x,pivot_num_x);
                        res &=this.m_pivot_point_y.ChangeBasePivotPoint(DFUN,pivot_index,pivot_prop_y,pivot_num_y);
                        return res;
                       }
                       
//--- Конструктор/Деструктор
                     CPivotPointXY(void){ this.m_pivot_point_x.SetAxisX(true); this.m_pivot_point_y.SetAxisX(false); }
                    ~CPivotPointXY(void){;}
  };  
//+------------------------------------------------------------------+

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

Следом за этим классом напишем третий класс — класс связанных данных опорных точек составного объекта:

//+------------------------------------------------------------------+
//| Класс связанных данных опорных точек составного объекта          |
//+------------------------------------------------------------------+
class CLinkedPivotPoint
  {
private:
   CArrayObj         m_list;                       // Список опорных точек X и Y координат привязанного объекта
   int               m_base_obj_index;             // Индекс базового объекта
public:
//--- (1) Устанавливает, (2) возвращает индекс базового объекта
   void              SetBaseObjIndex(const int index)       { this.m_base_obj_index=index;                  }
   int               GetBaseObjIndex(void)            const { return this.m_base_obj_index;                 }
//--- Создаёт новый объект класса данных опорных точек X и Y составного объекта и добавляет его в список объектов
   bool              CreateNewLinkedPivotPoint(const int pivot_prop_x,const int pivot_num_x,const int pivot_prop_y,const int pivot_num_y)
                       {
                        //--- Создаём объект данных опорных точек X и Y
                        CPivotPointXY *obj=new CPivotPointXY();
                        if(obj==NULL)
                           return false;
                        //--- Добавляем каждому объекту одно измерение с данными опроных точек базового объекта по X и Y
                        if(!obj.AddNewBasePivotPointXY(pivot_prop_x,pivot_num_x,pivot_prop_y,pivot_num_y))
                           return false;
                        //--- Если созданный объект не удалось добавить в список - сообщаем об этом, удаляем объект и возвращаем false
                        if(!this.m_list.Add(obj))
                          {
                           CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST);
                           delete obj;
                           return false;
                          }
                        //--- Всё успешно - возвращаем true
                        return true;
                       }
   
//--- Возвращает количество данных опорных точек координат X и Y
   int               GetNumLinkedPivotPoints(void)    const {  return this.m_list.Total();                  }
//--- Возвращает указатель на (1) первый, (2) последний объект данных опорных точек X и Y (3) по индексу
   CPivotPointXY    *GetLinkedPivotPointXYFirst(void)       const { return this.m_list.At(0);                     }
   CPivotPointXY    *GetLinkedPivotPointXYLast(void)        const { return this.m_list.At(this.m_list.Total()-1); }
   CPivotPointXY    *GetLinkedPivotPointXY(const int index) const { return this.m_list.At(index);                 }
//--- Возвращает указатель на объект данных опорной точки X-координаты по индексу
   CPivotPointData  *GetBasePivotPointDataX(const int index) const
                       {
                        CPivotPointXY *obj=this.GetLinkedPivotPointXY(index);
                        if(obj==NULL)
                           return NULL;
                        return obj.GetPivotPointDataX();
                       }
//--- Возвращает указатель на объект данных опорной точки Y-координаты по индексу
   CPivotPointData  *GetBasePivotPointDataY(const int index) const
                       {
                        CPivotPointXY *obj=this.GetLinkedPivotPointXY(index);
                        if(obj==NULL)
                           return NULL;
                        return obj.GetPivotPointDataY();
                       }
//--- Возвращает количество опорных точек базового объекта для расчёта координаты X по индексу
   int               GetBasePivotsNumX(const int index) const
                       {
                        CPivotPointData *obj=this.GetBasePivotPointDataX(index);
                        if(obj==NULL)
                           return NULL;
                        return obj.GetBasePivotsNum();
                       }
//--- Возвращает количество опорных точек базового объекта для расчёта координаты Y по индексу
   int               GetBasePivotsNumY(const int index) const
                       {
                        CPivotPointData *obj=this.GetBasePivotPointDataY(index);
                        if(obj==NULL)
                           return NULL;
                        return obj.GetBasePivotsNum();
                       }

//--- Конструктор/Деструктор
                     CLinkedPivotPoint(void){;}
                    ~CLinkedPivotPoint(void){;}
  };  
//+------------------------------------------------------------------+

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

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

//+------------------------------------------------------------------+
//| Класс абстрактного стандартного графического объекта             |
//+------------------------------------------------------------------+
class CGStdGraphObj : public CGBaseObj
  {
private:
   CArrayObj         m_list;                                            // Список зависимых графических объектов
   CProperties      *Prop;                                              // Указатель на объект свойств
   CLinkedPivotPoint m_linked_pivots;                                   // Связанные опорные точки
   int               m_pivots;                                          // Количество опорных точек объекта
//--- Считывает и устанавливает (1) время, (2) цену указанной опорной точки объекта
   void              SetTimePivot(const int index);
   void              SetPricePivot(const int index);
//--- Считывает и устанавливает (1) цвет, (2) стиль, (3) толщину, (4) значение, (5) текст указанного уровня объекта
   void              SetLevelColor(const int index);
   void              SetLevelStyle(const int index);
   void              SetLevelWidth(const int index);
   void              SetLevelValue(const int index);
   void              SetLevelText(const int index);
//--- Считывает и устанавливает имя BMP-файла для объекта "Графическая метка". Индекс: 0-состояние ON, 1-состояние OFF
   void              SetBMPFile(const int index);

public:

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

//--- Устанавливает прошлое имя объекта
   bool              SetNamePrev(const string name)
                       {
                        if(!this.Prop.SetSizeRange(GRAPH_OBJ_PROP_NAME,this.Prop.CurrSize(GRAPH_OBJ_PROP_NAME)+1))
                           return false;
                        this.SetProperty(GRAPH_OBJ_PROP_NAME,this.Prop.CurrSize(GRAPH_OBJ_PROP_NAME)-1,name);
                        return true;
                       }
                       
//--- Возвращает (1) список зависимых объектов, (2) зависимый графический объект по индексу
   CArrayObj        *GetListDependentObj(void)        { return &this.m_list;           }
   CGStdGraphObj    *GetDependentObj(const int index) { return this.m_list.At(index);  }
//--- Возвращает имя зависимого графического объекта по индексу
   string            NameDependent(const int index);
//--- Добавляет зависимый графический объект в список
   bool              AddDependentObj(CGStdGraphObj *obj);

//--- Возвращает объект данных опорных точек
   CLinkedPivotPoint*GetLinkedPivotPoint(void)        { return &this.m_linked_pivots;  }
   
//--- Добавляет новую опорную точку для расчёта координат X и Y в текущий объект
   bool              AddNewLinkedPivotPointXY(const int pivot_prop_x,const int pivot_num_x,const int pivot_prop_y,const int pivot_num_y)
                       {
                        //--- Если текущий объект не привязан к базовому - выводим об этом сообщение и возвращаем false
                        if(this.BaseObjectID()==0)
                          {
                           CMessage::ToLog(DFUN,MSG_GRAPH_OBJ_EXT_NOT_ATACHED_TO_BASE);
                           return false;
                          }
                        //--- Возвращаем результат добавления новой связанной опорной точки из класса CLinkedPivotPoint в текущий объект
                        return this.m_linked_pivots.CreateNewLinkedPivotPoint(pivot_prop_x,pivot_num_x,pivot_prop_y,pivot_num_y);
                       }
//--- Добавляет новую опорную точку для расчёта координат X и Y в указанный объект
   bool              AddNewLinkedPivotPointXY(CGStdGraphObj *obj,const int pivot_prop_x,const int pivot_num_x,const int pivot_prop_y,const int pivot_num_y)
                       {
                        //--- Если текущий объект не является расширенным - выводим об этом сообщение и возвращаем false
                        if(this.TypeGraphElement()!=GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED)
                          {
                           CMessage::ToLog(DFUN,MSG_GRAPH_OBJ_NOT_EXT_OBJ);
                           return false;
                          }
                        //--- Если передан нулевой указатель на объект - возвращаем false
                        if(obj==NULL)
                           return false;
                        //--- Возвращаем результат добавления новой связанной опорной точки из класса CLinkedPivotPoint в указанный объект
                        return obj.AddNewLinkedPivotPointXY(pivot_prop_x,pivot_num_x,pivot_prop_y,pivot_num_y);
                       }
                       
//--- Добавляет новую точку привязки базового объекта для расчёта координаты X
   bool              AddNewBaseLinkedPivotX(const int index,const int pivot_prop,const int pivot_num)
                       {
                        //--- Если текущий объект не привязан к базовому - выводим об этом сообщение и возвращаем false
                        if(this.BaseObjectID()==0)
                          {
                           CMessage::ToLog(DFUN,MSG_GRAPH_OBJ_EXT_NOT_ATACHED_TO_BASE);
                           return false;
                          }
                        //--- Получаем указатель на данные нужной опорной точки
                        CPivotPointData *data=m_linked_pivots.GetBasePivotPointDataX(index); 
                        if(data==NULL)
                           return false;
                        //--- Возвращаем результат добавления новой точки привязки в текущий объект
                        return data.AddNewBasePivotPoint(DFUN,pivot_prop,pivot_num);
                       }
//--- Добавляет новую точку привязки базового объекта для расчёта координаты Y
   bool              AddNewBaseLinkedPivotY(const int index,const int pivot_prop,const int pivot_num)
                       {
                        //--- Если текущий объект не привязан к базовому - выводим об этом сообщение и возвращаем false
                        if(this.BaseObjectID()==0)
                          {
                           CMessage::ToLog(DFUN,MSG_GRAPH_OBJ_EXT_NOT_ATACHED_TO_BASE);
                           return false;
                          }
                        //--- Получаем указатель на данные нужной опорной точки
                        CPivotPointData *data=m_linked_pivots.GetBasePivotPointDataY(index); 
                        if(data==NULL)
                           return false;
                        //--- Возвращаем результат добавления новой точки привязки в текущий объект
                        return data.AddNewBasePivotPoint(DFUN,pivot_prop,pivot_num);
                       }

//--- Добавляет новую точку привязки базового объекта для расчёта координаты X в указанный подчинённый графический объект
   bool              AddNewBaseLinkedPivotX(CGStdGraphObj *obj,const int index,const int pivot_prop,const int pivot_num)
                       {
                        //--- Если текущий объект не является расширенным - выводим об этом сообщение и возвращаем false
                        if(this.TypeGraphElement()!=GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED)
                          {
                           CMessage::ToLog(DFUN,MSG_GRAPH_OBJ_NOT_EXT_OBJ);
                           return false;
                          }
                        //--- Если передан нулевой указатель на объект - возвращаем false
                        if(obj==NULL)
                           return false;
                        //--- Возвращаем результат добавления новой точки привязки в указанный объект
                        return obj.AddNewBaseLinkedPivotX(index,pivot_prop,pivot_num);
                       }
//--- Добавляет новую точку привязки базового объекта для расчёта координаты Y в указанный подчинённый графический объект
   bool              AddNewBaseLinkedPivotY(CGStdGraphObj *obj,const int index,const int pivot_prop,const int pivot_num)
                       {
                        //--- Если текущий объект не является расширенным - выводим об этом сообщение и возвращаем false
                        if(this.TypeGraphElement()!=GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED)
                          {
                           CMessage::ToLog(DFUN,MSG_GRAPH_OBJ_NOT_EXT_OBJ);
                           return false;
                          }
                        //--- Если передан нулевой указатель на объект - возвращаем false
                        if(obj==NULL)
                           return false;
                        //--- Возвращаем результат добавления новой точки привязки в указанный объект
                        return obj.AddNewBaseLinkedPivotY(index,pivot_prop,pivot_num);
                       }

//--- Возвращает количество опорных точек базового объекта для расчёта координаты (1) X, (2) Y в текущем объекте
   int               GetBasePivotsNumX(const int index)           { return this.m_linked_pivots.GetBasePivotsNumX(index);  }
   int               GetBasePivotsNumY(const int index)           { return this.m_linked_pivots.GetBasePivotsNumY(index);  }
//--- Возвращает количество опорных точек базового объекта для расчёта координаты (1) X, (2) Y в указанном объекте
   int               GetBasePivotsNumX(CGStdGraphObj *obj,const int index) const { return(obj!=NULL ? obj.GetBasePivotsNumX(index): 0); }
   int               GetBasePivotsNumY(CGStdGraphObj *obj,const int index) const { return(obj!=NULL ? obj.GetBasePivotsNumY(index): 0); }
//--- Возвращает количество связанных опорных точек базового объекта для расчёта координат в (1) текущем, (2) объекте
   int               GetLinkedPivotsNum(void)               const { return this.m_linked_pivots.GetNumLinkedPivotPoints(); }
   int               GetLinkedPivotsNum(CGStdGraphObj *obj) const { return(obj!=NULL ? obj.GetLinkedPivotsNum() : 0);      }
   
//--- Конструктор по умолчанию
                     CGStdGraphObj(){ this.m_type=OBJECT_DE_TYPE_GSTD_OBJ; this.m_species=WRONG_VALUE; }
//--- Деструктор
                    ~CGStdGraphObj()
                       {
                        if(this.Prop!=NULL)
                           delete this.Prop;
                       }
protected:

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

В защищённый параметрический конструктор теперь будем передавать тип создаваемого графического элемента:

//--- Конструктор по умолчанию
                     CGStdGraphObj(){ this.m_type=OBJECT_DE_TYPE_GSTD_OBJ; this.m_species=WRONG_VALUE; }
//--- Деструктор
                    ~CGStdGraphObj()
                       {
                        if(this.Prop!=NULL)
                           delete this.Prop;
                       }
protected:
//--- Защищённый параметрический конструктор
                     CGStdGraphObj(const ENUM_OBJECT_DE_TYPE obj_type,
                                   const ENUM_GRAPH_ELEMENT_TYPE elm_type,
                                   const ENUM_GRAPH_OBJ_BELONG belong,
                                   const ENUM_GRAPH_OBJ_SPECIES species,
                                   const long chart_id, const int pivots,
                                   const string name);
                     
public:

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

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

public:
//+-------------------------------------------------------------------+ 
//|Методы упрощённого доступа и установки свойств графического объекта|
//+-------------------------------------------------------------------+
//--- Номер объекта в списке
   int               Number(void)                  const { return (int)this.GetProperty(GRAPH_OBJ_PROP_NUM,0);                            }
   void              SetNumber(const int number)         { this.SetProperty(GRAPH_OBJ_PROP_NUM,0,number);                                 }
//--- Флаг хранения истории изменений
   bool              AllowChangeHistory(void)      const { return (bool)this.GetProperty(GRAPH_OBJ_PROP_CHANGE_HISTORY,0);                }
   void              SetAllowChangeMemory(const bool flag){ this.SetProperty(GRAPH_OBJ_PROP_CHANGE_HISTORY,0,flag);                       }
//--- Идентификатор объекта
   long              ObjectID(void)                const { return this.GetProperty(GRAPH_OBJ_PROP_ID,0);                                  }
   void              SetObjectID(const long obj_id)
                       {
                        CGBaseObj::SetObjectID(obj_id);
                        this.SetProperty(GRAPH_OBJ_PROP_ID,0,obj_id);
                        this.SetPropertyPrev(GRAPH_OBJ_PROP_ID,0,obj_id);
                       }
//--- Идентификатор базового объекта
   long              BaseObjectID(void)            const { return this.GetProperty(GRAPH_OBJ_PROP_BASE_ID,0);                                  }
   void              SetBaseObjectID(const long obj_id)
                       {
                        this.SetProperty(GRAPH_OBJ_PROP_BASE_ID,0,obj_id);
                        this.SetPropertyPrev(GRAPH_OBJ_PROP_BASE_ID,0,obj_id);
                       }
//--- Тип графического объекта
   ENUM_OBJECT       GraphObjectType(void)         const { return (ENUM_OBJECT)this.GetProperty(GRAPH_OBJ_PROP_TYPE,0);                   }
   void              SetGraphObjectType(const ENUM_OBJECT obj_type)
                       {
                        CGBaseObj::SetTypeGraphObject(obj_type);
                        this.SetProperty(GRAPH_OBJ_PROP_TYPE,0,obj_type);
                       }

...

//--- Имя объекта
   string            Name(void)                    const { return this.GetProperty(GRAPH_OBJ_PROP_NAME,0);                                }
   bool              SetName(const string name)
                       {
                        if(CGBaseObj::Name()==name)
                           return true;
                        if(CGBaseObj::Name()=="")
                          {
                           CGBaseObj::SetName(name);
                           this.SetProperty(GRAPH_OBJ_PROP_NAME,0,name);
                           return true;
                          }
                        else
                          {
                           if(!::ObjectSetString(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_NAME,name))
                              return false;
                           CGBaseObj::SetName(name);
                           this.SetProperty(GRAPH_OBJ_PROP_NAME,0,name);
                           return true;
                          }
                       }
//--- Имя базового объекта
   string            BaseName(void)                const { return this.GetProperty(GRAPH_OBJ_PROP_BASE_NAME,0);                           }
   bool              SetBaseName(const string name)
                       {
                        this.SetProperty(GRAPH_OBJ_PROP_BASE_NAME,0,name);
                        return true;
                       }
//--- Описание объекта (текст, содержащийся в объекте)

Методы позволяют установить в свойства объекта идентификатор и имя базового объекта и вернуть эти данные.

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

//--- Возвращает флаги видимости объекта на таймфреймах
   bool              IsVisibleOnTimeframeM1(void)  const { return IsVisibleOnTimeframe(PERIOD_M1, (int)this.GetProperty(GRAPH_OBJ_PROP_TIMEFRAMES,0)); }
   bool              IsVisibleOnTimeframeM2(void)  const { return IsVisibleOnTimeframe(PERIOD_M2, (int)this.GetProperty(GRAPH_OBJ_PROP_TIMEFRAMES,0)); }
   bool              IsVisibleOnTimeframeM3(void)  const { return IsVisibleOnTimeframe(PERIOD_M3, (int)this.GetProperty(GRAPH_OBJ_PROP_TIMEFRAMES,0)); }
   bool              IsVisibleOnTimeframeM4(void)  const { return IsVisibleOnTimeframe(PERIOD_M4, (int)this.GetProperty(GRAPH_OBJ_PROP_TIMEFRAMES,0)); }
   bool              IsVisibleOnTimeframeM5(void)  const { return IsVisibleOnTimeframe(PERIOD_M5, (int)this.GetProperty(GRAPH_OBJ_PROP_TIMEFRAMES,0)); }
   bool              IsVisibleOnTimeframeM6(void)  const { return IsVisibleOnTimeframe(PERIOD_M6, (int)this.GetProperty(GRAPH_OBJ_PROP_TIMEFRAMES,0)); }
   bool              IsVisibleOnTimeframeM10(void) const { return IsVisibleOnTimeframe(PERIOD_M10,(int)this.GetProperty(GRAPH_OBJ_PROP_TIMEFRAMES,0)); }
   bool              IsVisibleOnTimeframeM12(void) const { return IsVisibleOnTimeframe(PERIOD_M12,(int)this.GetProperty(GRAPH_OBJ_PROP_TIMEFRAMES,0)); }
   bool              IsVisibleOnTimeframeM15(void) const { return IsVisibleOnTimeframe(PERIOD_M15,(int)this.GetProperty(GRAPH_OBJ_PROP_TIMEFRAMES,0)); }
   bool              IsVisibleOnTimeframeM20(void) const { return IsVisibleOnTimeframe(PERIOD_M20,(int)this.GetProperty(GRAPH_OBJ_PROP_TIMEFRAMES,0)); }
   bool              IsVisibleOnTimeframeM30(void) const { return IsVisibleOnTimeframe(PERIOD_M30,(int)this.GetProperty(GRAPH_OBJ_PROP_TIMEFRAMES,0)); }
   bool              IsVisibleOnTimeframeH1(void)  const { return IsVisibleOnTimeframe(PERIOD_H1, (int)this.GetProperty(GRAPH_OBJ_PROP_TIMEFRAMES,0)); }
   bool              IsVisibleOnTimeframeH2(void)  const { return IsVisibleOnTimeframe(PERIOD_H2, (int)this.GetProperty(GRAPH_OBJ_PROP_TIMEFRAMES,0)); }
   bool              IsVisibleOnTimeframeH3(void)  const { return IsVisibleOnTimeframe(PERIOD_H3, (int)this.GetProperty(GRAPH_OBJ_PROP_TIMEFRAMES,0)); }
   bool              IsVisibleOnTimeframeH4(void)  const { return IsVisibleOnTimeframe(PERIOD_H4, (int)this.GetProperty(GRAPH_OBJ_PROP_TIMEFRAMES,0)); }
   bool              IsVisibleOnTimeframeH6(void)  const { return IsVisibleOnTimeframe(PERIOD_H6, (int)this.GetProperty(GRAPH_OBJ_PROP_TIMEFRAMES,0)); }
   bool              IsVisibleOnTimeframeH8(void)  const { return IsVisibleOnTimeframe(PERIOD_H8, (int)this.GetProperty(GRAPH_OBJ_PROP_TIMEFRAMES,0)); }
   bool              IsVisibleOnTimeframeH12(void) const { return IsVisibleOnTimeframe(PERIOD_H12,(int)this.GetProperty(GRAPH_OBJ_PROP_TIMEFRAMES,0)); }
   bool              IsVisibleOnTimeframeD1(void)  const { return IsVisibleOnTimeframe(PERIOD_D1, (int)this.GetProperty(GRAPH_OBJ_PROP_TIMEFRAMES,0)); }
   bool              IsVisibleOnTimeframeW1(void)  const { return IsVisibleOnTimeframe(PERIOD_W1, (int)this.GetProperty(GRAPH_OBJ_PROP_TIMEFRAMES,0)); }
   bool              IsVisibleOnTimeframeMN1(void) const { return IsVisibleOnTimeframe(PERIOD_MN1,(int)this.GetProperty(GRAPH_OBJ_PROP_TIMEFRAMES,0)); }

//--- Возвращает описание (1) видимости объекта на таймфреймах, (2) номера в списке составного графического объекта
   string            VisibleOnTimeframeDescription(void);
   string            NumberDescription(void);

//--- Перезаписывает все свойства графического объекта


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

//+------------------------------------------------------------------+
//| Защищённый параметрический конструктор                           |
//+------------------------------------------------------------------+
CGStdGraphObj::CGStdGraphObj(const ENUM_OBJECT_DE_TYPE obj_type,
                             const ENUM_GRAPH_ELEMENT_TYPE elm_type,
                             const ENUM_GRAPH_OBJ_BELONG belong,
                             const ENUM_GRAPH_OBJ_SPECIES species,
                             const long chart_id,const int pivots,
                             const string name)
  {
   //--- Создаём объект свойств со значениями по умолчанию
   this.Prop=new CProperties(GRAPH_OBJ_PROP_INTEGER_TOTAL,GRAPH_OBJ_PROP_DOUBLE_TOTAL,GRAPH_OBJ_PROP_STRING_TOTAL);
   
//--- Устанавливаем количество опорных точек и уровней объекта
   this.m_pivots=pivots;
   int levels=(int)::ObjectGetInteger(chart_id,name,OBJPROP_LEVELS);

//--- Устанавливаем размерности массивов свойств в соответствии с количеством опорных точек и уровней
   this.Prop.SetSizeRange(GRAPH_OBJ_PROP_TIME,this.m_pivots);
   this.Prop.SetSizeRange(GRAPH_OBJ_PROP_PRICE,this.m_pivots);
   this.Prop.SetSizeRange(GRAPH_OBJ_PROP_LEVELCOLOR,levels);
   this.Prop.SetSizeRange(GRAPH_OBJ_PROP_LEVELSTYLE,levels);
   this.Prop.SetSizeRange(GRAPH_OBJ_PROP_LEVELWIDTH,levels);
   this.Prop.SetSizeRange(GRAPH_OBJ_PROP_LEVELVALUE,levels);
   this.Prop.SetSizeRange(GRAPH_OBJ_PROP_LEVELTEXT,levels);
   this.Prop.SetSizeRange(GRAPH_OBJ_PROP_BMPFILE,2);
   
//--- Устанавливаем объекту (1) его тип, тип графического (2) объекта, (3) елемента, (4) принадлежность и (5) номер подокна, (6) Digits символа графика
   this.m_type=obj_type;
   this.SetName(name);
   CGBaseObj::SetChartID(chart_id);
   CGBaseObj::SetTypeGraphObject(CGBaseObj::GraphObjectType(obj_type));
   CGBaseObj::SetTypeElement(elm_type);
   CGBaseObj::SetBelong(belong);
   CGBaseObj::SetSpecies(species);
   CGBaseObj::SetSubwindow(chart_id,name);
   CGBaseObj::SetDigits((int)::SymbolInfoInteger(::ChartSymbol(chart_id),SYMBOL_DIGITS));
   
//--- Сохранение целочисленных свойств, присущих всем графическим объектам, но не имеющиеся у графического объекта
   this.SetProperty(GRAPH_OBJ_PROP_CHART_ID,0,CGBaseObj::ChartID());                // Идентификатор графика
   this.SetProperty(GRAPH_OBJ_PROP_WND_NUM,0,CGBaseObj::SubWindow());               // Номер подокна графика
   this.SetProperty(GRAPH_OBJ_PROP_TYPE,0,CGBaseObj::TypeGraphObject());            // Тип графического объекта (ENUM_OBJECT)
   this.SetProperty(GRAPH_OBJ_PROP_ELEMENT_TYPE,0,CGBaseObj::TypeGraphElement());   // Тип графического элемента (ENUM_GRAPH_ELEMENT_TYPE)
   this.SetProperty(GRAPH_OBJ_PROP_BELONG,0,CGBaseObj::Belong());                   // Принадлежность графического объекта
   this.SetProperty(GRAPH_OBJ_PROP_SPECIES,0,CGBaseObj::Species());                 // Вид графического объекта
   this.SetProperty(GRAPH_OBJ_PROP_GROUP,0,0);                                      // Группа графических объектов
   this.SetProperty(GRAPH_OBJ_PROP_ID,0,0);                                         // Идентификатор объекта
   this.SetProperty(GRAPH_OBJ_PROP_BASE_ID,0,0);                                    // Идентификатор базового объекта
   this.SetProperty(GRAPH_OBJ_PROP_NUM,0,0);                                        // Номер объекта в списке
   this.SetProperty(GRAPH_OBJ_PROP_CHANGE_HISTORY,0,false);                         // Флаг хранения истории изменений
   this.SetProperty(GRAPH_OBJ_PROP_BASE_NAME,0,this.Name());                        // Имя базового объекта
   
//--- Сохранение свойств, присущих всем графическим объектам, имеющимся у графического объекта
   this.PropertiesRefresh();
   
//--- Сохранение базовых свойств в родительском объекте
   this.m_create_time=(datetime)this.GetProperty(GRAPH_OBJ_PROP_CREATETIME,0);
   this.m_back=(bool)this.GetProperty(GRAPH_OBJ_PROP_BACK,0);
   this.m_selected=(bool)this.GetProperty(GRAPH_OBJ_PROP_SELECTED,0);
   this.m_selectable=(bool)this.GetProperty(GRAPH_OBJ_PROP_SELECTABLE,0);
   this.m_hidden=(bool)this.GetProperty(GRAPH_OBJ_PROP_HIDDEN,0);

//--- Сохранение текущих свойств в прошлые
   this.PropertiesCopyToPrevData();
  }
//+-------------------------------------------------------------------+

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

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

//+------------------------------------------------------------------+
//| Возвращает описание целочисленного свойства объекта              |
//+------------------------------------------------------------------+
string CGStdGraphObj::GetPropertyDescription(ENUM_GRAPH_OBJ_PROP_INTEGER property)
  {
   return
     (
      property==GRAPH_OBJ_PROP_ID         ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_ID)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property,0)
         )  :
      property==GRAPH_OBJ_PROP_BASE_ID    ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_BASE_ID)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property,0)
         )  :
      property==GRAPH_OBJ_PROP_TYPE       ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_TYPE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+this.TypeDescription()
         )  :
      property==GRAPH_OBJ_PROP_ELEMENT_TYPE  ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_ELEMENT_TYPE)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+CGBaseObj::TypeElementDescription()
         )  :
      property==GRAPH_OBJ_PROP_SPECIES    ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_SPECIES)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+CGBaseObj::SpeciesDescription()
         )  :
      property==GRAPH_OBJ_PROP_GROUP    ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_GROUP)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(CGBaseObj::Group()>0 ? (string)this.GetProperty(property,0) : CMessage::Text(MSG_LIB_PROP_EMPTY))
         )  :
      property==GRAPH_OBJ_PROP_BELONG     ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_BELONG)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+CGBaseObj::BelongDescription()
         )  :
      property==GRAPH_OBJ_PROP_CHART_ID   ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_CHART_ID)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property,0)
         )  :
      property==GRAPH_OBJ_PROP_WND_NUM    ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_WND_NUM)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property,0)
         )  :
      property==GRAPH_OBJ_PROP_CHANGE_HISTORY ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_CHANGE_MEMORY)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.GetProperty(property,0) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==GRAPH_OBJ_PROP_CREATETIME   ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_CREATETIME)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::TimeToString(this.GetProperty(property,0),TIME_DATE|TIME_MINUTES|TIME_SECONDS)
         )  :
      property==GRAPH_OBJ_PROP_TIMEFRAMES ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_TIMEFRAMES)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+this.VisibleOnTimeframeDescription()
         )  :
      property==GRAPH_OBJ_PROP_BACK       ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_BACK)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.IsBack() ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==GRAPH_OBJ_PROP_ZORDER     ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_ZORDER)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property,0)
         )  :
      property==GRAPH_OBJ_PROP_HIDDEN     ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_HIDDEN)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.IsHidden() ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==GRAPH_OBJ_PROP_SELECTED   ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_SELECTED)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.IsSelected() ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==GRAPH_OBJ_PROP_SELECTABLE ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_SELECTABLE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.IsSelectable() ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==GRAPH_OBJ_PROP_NUM        ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_NUM)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+this.NumberDescription()
         )  :
      property==GRAPH_OBJ_PROP_TIME       ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_TIME)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+"\n"+this.TimesDescription()
         )  :
      property==GRAPH_OBJ_PROP_COLOR      ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_COLOR)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::ColorToString((color)this.GetProperty(property,0),true)
         )  :
      property==GRAPH_OBJ_PROP_STYLE      ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_STYLE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+LineStyleDescription((ENUM_LINE_STYLE)this.GetProperty(property,0))
         )  :
      property==GRAPH_OBJ_PROP_WIDTH     ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_WIDTH)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property,0)
         )  :
      property==GRAPH_OBJ_PROP_FILL       ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_FILL)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.GetProperty(property,0) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==GRAPH_OBJ_PROP_READONLY   ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_READONLY)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.GetProperty(property,0) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==GRAPH_OBJ_PROP_LEVELS     ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_LEVELS)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property,0)
         )  :
      property==GRAPH_OBJ_PROP_LEVELCOLOR ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_LEVELCOLOR)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ":\n"+this.LevelsColorDescription()
         )  :
      property==GRAPH_OBJ_PROP_LEVELSTYLE ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_LEVELSTYLE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ":\n"+this.LevelsStyleDescription()
         )  :
      property==GRAPH_OBJ_PROP_LEVELWIDTH ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_LEVELWIDTH)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ":\n"+this.LevelsWidthDescription()
         )  :
      property==GRAPH_OBJ_PROP_ALIGN      ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_ALIGN)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+AlignModeDescription((ENUM_ALIGN_MODE)this.GetProperty(property,0))
         )  :
      property==GRAPH_OBJ_PROP_FONTSIZE   ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_FONTSIZE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property,0)
         )  :
      property==GRAPH_OBJ_PROP_RAY_LEFT   ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_RAY_LEFT)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.GetProperty(property,0) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==GRAPH_OBJ_PROP_RAY_RIGHT  ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_RAY_RIGHT)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.GetProperty(property,0) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==GRAPH_OBJ_PROP_RAY        ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_RAY)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.GetProperty(property,0) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==GRAPH_OBJ_PROP_ELLIPSE    ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_ELLIPSE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.GetProperty(property,0) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==GRAPH_OBJ_PROP_ARROWCODE  ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_ARROWCODE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property,0)
         )  :
      property==GRAPH_OBJ_PROP_ANCHOR     ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_ANCHOR)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+this.AnchorDescription()
         )  :
      property==GRAPH_OBJ_PROP_XDISTANCE  ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_XDISTANCE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property,0)
         )  :
      property==GRAPH_OBJ_PROP_YDISTANCE  ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_YDISTANCE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property,0)
         )  :
      property==GRAPH_OBJ_PROP_DIRECTION  ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_DIRECTION)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+GannDirectDescription((ENUM_GANN_DIRECTION)this.GetProperty(property,0))
         )  :
      property==GRAPH_OBJ_PROP_DEGREE     ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_DEGREE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+ElliotWaveDegreeDescription((ENUM_ELLIOT_WAVE_DEGREE)this.GetProperty(property,0))
         )  :
      property==GRAPH_OBJ_PROP_DRAWLINES  ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_DRAWLINES)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.GetProperty(property,0) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==GRAPH_OBJ_PROP_STATE      ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_STATE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.GetProperty(property,0) ? CMessage::Text(MSG_LIB_TEXT_BUTTON_STATE_PRESSED) : CMessage::Text(MSG_LIB_TEXT_BUTTON_STATE_DEPRESSED))
         )  :
      property==GRAPH_OBJ_PROP_CHART_OBJ_CHART_ID  ?  CMessage::Text(MSG_CHART_OBJ_ID)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property,0)
         )  :
      property==GRAPH_OBJ_PROP_CHART_OBJ_PERIOD ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_CHART_OBJ_PERIOD)+
         (!this.SupportProperty(property)       ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+TimeframeDescription((ENUM_TIMEFRAMES)this.GetProperty(property,0))
         )  :
      property==GRAPH_OBJ_PROP_CHART_OBJ_DATE_SCALE   ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_CHART_OBJ_DATE_SCALE)+
         (!this.SupportProperty(property)             ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.GetProperty(property,0) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==GRAPH_OBJ_PROP_CHART_OBJ_PRICE_SCALE  ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_CHART_OBJ_PRICE_SCALE)+
         (!this.SupportProperty(property)             ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.GetProperty(property,0) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==GRAPH_OBJ_PROP_CHART_OBJ_CHART_SCALE  ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_CHART_OBJ_CHART_SCALE)+
         (!this.SupportProperty(property)             ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property,0)
         )  :
      property==GRAPH_OBJ_PROP_XSIZE      ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_XSIZE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property,0)
         )  :
      property==GRAPH_OBJ_PROP_YSIZE      ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_YSIZE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property,0)
         )  :
      property==GRAPH_OBJ_PROP_XOFFSET    ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_XOFFSET)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property,0)
         )  :
      property==GRAPH_OBJ_PROP_YOFFSET    ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_YOFFSET)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property,0)
         )  :
      property==GRAPH_OBJ_PROP_BGCOLOR    ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_BGCOLOR)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::ColorToString((color)this.GetProperty(property,0),true)
         )  :
      property==GRAPH_OBJ_PROP_CORNER     ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_CORNER)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+BaseCornerDescription((ENUM_BASE_CORNER)this.GetProperty(property,0))
         )  :
      property==GRAPH_OBJ_PROP_BORDER_TYPE   ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_BORDER_TYPE)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+BorderTypeDescription((ENUM_BORDER_TYPE)this.GetProperty(property,0))
         )  :
      property==GRAPH_OBJ_PROP_BORDER_COLOR  ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_BORDER_COLOR)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::ColorToString((color)this.GetProperty(property,0),true)
         )  :
      ""
     );
  }
//+------------------------------------------------------------------+


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

//+------------------------------------------------------------------+
//| Возвращает описание строкового свойства объекта                  |
//+------------------------------------------------------------------+
string CGStdGraphObj::GetPropertyDescription(ENUM_GRAPH_OBJ_PROP_STRING property)
  {
   return
     (
      property==GRAPH_OBJ_PROP_NAME       ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_NAME)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+"\""+this.GetProperty(property,0)+"\""
         )  :
      property==GRAPH_OBJ_PROP_BASE_NAME  ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_BASE_NAME)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+"\""+this.GetProperty(property,0)+"\""
         )  :
      property==GRAPH_OBJ_PROP_TEXT       ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_TEXT)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.GetProperty(property,0)=="" ? CMessage::Text(MSG_LIB_PROP_EMPTY) : "\""+this.GetProperty(property,0)+"\"")
         )  :
      property==GRAPH_OBJ_PROP_TOOLTIP    ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_TOOLTIP)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.GetProperty(property,0)=="" ? CMessage::Text(MSG_LIB_PROP_AUTO) : 
                this.GetProperty(property,0)=="\n" ? CMessage::Text(MSG_LIB_PROP_EMPTY) :
                "\""+this.GetProperty(property,0)+"\"")
         )  :
      property==GRAPH_OBJ_PROP_LEVELTEXT  ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_LEVELTEXT)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ":\n"+this.LevelsTextDescription()
         )  :
      property==GRAPH_OBJ_PROP_FONT       ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_FONT)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+"\""+this.GetProperty(property,0)+"\""
         )  :
      property==GRAPH_OBJ_PROP_BMPFILE    ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_BMPFILE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ":\n"+this.BMPFilesDescription()
         )  :
      property==GRAPH_OBJ_PROP_CHART_OBJ_SYMBOL ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_SYMBOL)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+this.GetProperty(property,0)
         )  :
      ""
     );
  }
//+------------------------------------------------------------------+


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

//+------------------------------------------------------------------+
//| Возвращает описание номера графического объекта                  |
//| в списке объектов составного графического объекта                |
//+------------------------------------------------------------------+
string CGStdGraphObj::NumberDescription(void)
  {
   if(CGBaseObj::TypeGraphElement()==GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED)
     {
      if(this.Number()==0)
         return CMessage::Text(MSG_GRAPH_OBJ_PROP_NUM_EXT_BASE_OBJ);
     }
   return (string)this.Number();
  }
//+------------------------------------------------------------------+

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


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

//+------------------------------------------------------------------+
//| Возвращает имя подчинённого графического объекта по индексу      |
//+------------------------------------------------------------------+
string CGStdGraphObj::NameDependent(const int index)
  {
   CGStdGraphObj *obj=this.GetDependentObj(index);
   return(obj!=NULL ? obj.Name() : "");
  }
//+------------------------------------------------------------------+

Получаем указатель на привязанный графический объект в списке по указанному индексу и возвращаем его имя. Если объект получить не удалось — возвращаем пустую строку.

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

//+------------------------------------------------------------------+
//| Добавляет подчинённый стандартный графический объект в список    |
//+------------------------------------------------------------------+
bool CGStdGraphObj::AddDependentObj(CGStdGraphObj *obj)
  {
   //--- Если текущий объект не является расширенным - сообщаем об этом и возвращаем false
   if(this.TypeGraphElement()!=GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED)
     {
      CMessage::ToLog(MSG_GRAPH_OBJ_NOT_EXT_OBJ);
      return false;
     }
   //--- Если не удалось добавить указатель на переданный объект в список - сообщаем об этом и возвращаем false
   if(!this.m_list.Add(obj))
     {
      CMessage::ToLog(DFUN,MSG_GRAPH_OBJ_FAILED_ADD_DEP_EXT_OBJ_TO_LIST);
      return false;
     }
   //--- Объект добавлен в список - устанавливаем для него номер в списке,
   //--- имя и идентификатор текущего объекта как базового
   obj.SetNumber(this.m_list.Total()-1);
   obj.SetBaseName(this.Name());
   obj.SetBaseObjectID(this.ObjectID());
   return true;
  }
//+------------------------------------------------------------------+

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

Сделано это по причине, что любой графический объект может быть как базовым, так и подчинённым, причём даже одновременно. По этой причине в нём есть методы как для работы со своими подчинёнными объектами, как базового, так и для изменения его свойств извне — как подчинённого.


Доработаем класс-коллекцию графических элементов в файле \MQL5\Include\DoEasy\Collections\GraphElementsCollection.mqh.

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

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;    }
//--- Создаёт новый объект стандартного (или расширенного) графического объекта
   CGStdGraphObj    *CreateNewGraphObj(const ENUM_OBJECT obj_type,const string name,const bool extended);
//--- Возвращает список вновь добавленных объектов

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

//+------------------------------------------------------------------+
//| CChartObjectsControl: Проверяет объекты на чарте                 |
//+------------------------------------------------------------------+
void CChartObjectsControl::Refresh(void)
  {
//--- Очищаем список вновь добавленных объектов
   this.m_list_new_graph_obj.Clear();
//--- Рассчитаем количество новых объектов на чарте
   this.m_total_objects=::ObjectsTotal(this.ChartID());
   this.m_delta_graph_obj=this.m_total_objects-this.m_last_objects;
   //--- Если добавлен объект на график

   if(this.m_delta_graph_obj>0)
     {
      //--- Создаём список добавленных графических объектов
      for(int i=0;i<this.m_delta_graph_obj;i++)
        {
         //--- Получаем имя последнего добавленного объекта (если добавлен один новый объект),
         //--- или имя из списка объектов в терминале по индексу (если объектов добавлено несколько)
         string name=(this.m_delta_graph_obj==1 ? this.LastAddedGraphObjName() : ::ObjectName(this.m_chart_id,i));
         //--- Обрабатываем только объекты, созданные не программно
         if(name==NULL || ::StringFind(name,this.m_name_program)>WRONG_VALUE)
            continue;
         //--- Создаём объект класса графического объекта, соответствующий типу добавленного графического объекта
         ENUM_OBJECT type=(ENUM_OBJECT)::ObjectGetInteger(this.ChartID(),name,OBJPROP_TYPE);
         ENUM_OBJECT_DE_TYPE obj_type=ENUM_OBJECT_DE_TYPE(type+OBJECT_DE_TYPE_GSTD_OBJ+1);
         CGStdGraphObj *obj=this.CreateNewGraphObj(type,name,false);
         //--- Если объект создать не удалось - сообщаем об этом и идём на новую итерацию
         if(obj==NULL)
           {
            CMessage::ToLog(DFUN,MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ);
            continue;
           }
         //--- Устанавливаем объекту его принадлежность и добавляем созданный объект в список новых объектов
         obj.SetBelong(GRAPH_OBJ_BELONG_NO_PROGRAM); 
         //--- Если объект добавить в список не удалось - сообщаем об этом, удаляем объект и идём на следующую итерацию
         if(!this.m_list_new_graph_obj.Add(obj))
           {
            CMessage::ToLog(DFUN_ERR_LINE,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST);
            delete obj;
            continue;
           }
        }
      //--- Из созданного списка отправляем события на график управляющей программы
      for(int i=0;i<this.m_list_new_graph_obj.Total();i++)
        {
         CGStdGraphObj *obj=this.m_list_new_graph_obj.At(i);
         if(obj==NULL)
            continue;
         //--- Отправляем событие на график управляющей программы
         ::EventChartCustom(this.m_chart_id_main,GRAPH_OBJ_EVENT_CREATE,this.ChartID(),obj.TimeCreate(),obj.Name());
        }
     }
//--- сохранение индекса последнего добавленного графического объекта и разницы по сравнению с прошлой проверкой
   this.m_last_objects=this.m_total_objects;
   this.m_is_graph_obj_event=(bool)this.m_delta_graph_obj;
  }
//+------------------------------------------------------------------+

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

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

//+------------------------------------------------------------------+
//| CChartObjectsControl:                                            |
//| Создаёт новый объект стандартного графического объекта           |
//+------------------------------------------------------------------+
CGStdGraphObj *CChartObjectsControl::CreateNewGraphObj(const ENUM_OBJECT obj_type,const string name,const bool extended)
  {

   switch((int)obj_type)
     {
      //--- Линии
      case OBJ_VLINE             : return new CGStdVLineObj(this.ChartID(),name,extended);
      case OBJ_HLINE             : return new CGStdHLineObj(this.ChartID(),name,extended);
      case OBJ_TREND             : return new CGStdTrendObj(this.ChartID(),name,extended);
      case OBJ_TRENDBYANGLE      : return new CGStdTrendByAngleObj(this.ChartID(),name,extended);
      case OBJ_CYCLES            : return new CGStdCyclesObj(this.ChartID(),name,extended);
      case OBJ_ARROWED_LINE      : return new CGStdArrowedLineObj(this.ChartID(),name,extended);
      //--- Каналы
      case OBJ_CHANNEL           : return new CGStdChannelObj(this.ChartID(),name,extended);
      case OBJ_STDDEVCHANNEL     : return new CGStdStdDevChannelObj(this.ChartID(),name,extended);
      case OBJ_REGRESSION        : return new CGStdRegressionObj(this.ChartID(),name,extended);
      case OBJ_PITCHFORK         : return new CGStdPitchforkObj(this.ChartID(),name,extended);
      //--- Ганн
      case OBJ_GANNLINE          : return new CGStdGannLineObj(this.ChartID(),name,extended);
      case OBJ_GANNFAN           : return new CGStdGannFanObj(this.ChartID(),name,extended);
      case OBJ_GANNGRID          : return new CGStdGannGridObj(this.ChartID(),name,extended);
      //--- Фибоначчи
      case OBJ_FIBO              : return new CGStdFiboObj(this.ChartID(),name,extended);
      case OBJ_FIBOTIMES         : return new CGStdFiboTimesObj(this.ChartID(),name,extended);
      case OBJ_FIBOFAN           : return new CGStdFiboFanObj(this.ChartID(),name,extended);
      case OBJ_FIBOARC           : return new CGStdFiboArcObj(this.ChartID(),name,extended);
      case OBJ_FIBOCHANNEL       : return new CGStdFiboChannelObj(this.ChartID(),name,extended);
      case OBJ_EXPANSION         : return new CGStdExpansionObj(this.ChartID(),name,extended);
      //--- Эллиотт
      case OBJ_ELLIOTWAVE5       : return new CGStdElliotWave5Obj(this.ChartID(),name,extended);
      case OBJ_ELLIOTWAVE3       : return new CGStdElliotWave3Obj(this.ChartID(),name,extended);
      //--- Фигуры
      case OBJ_RECTANGLE         : return new CGStdRectangleObj(this.ChartID(),name,extended);
      case OBJ_TRIANGLE          : return new CGStdTriangleObj(this.ChartID(),name,extended);
      case OBJ_ELLIPSE           : return new CGStdEllipseObj(this.ChartID(),name,extended);
      //--- Стрелки
      case OBJ_ARROW_THUMB_UP    : return new CGStdArrowThumbUpObj(this.ChartID(),name,extended);
      case OBJ_ARROW_THUMB_DOWN  : return new CGStdArrowThumbDownObj(this.ChartID(),name,extended);
      case OBJ_ARROW_UP          : return new CGStdArrowUpObj(this.ChartID(),name,extended);
      case OBJ_ARROW_DOWN        : return new CGStdArrowDownObj(this.ChartID(),name,extended);
      case OBJ_ARROW_STOP        : return new CGStdArrowStopObj(this.ChartID(),name,extended);
      case OBJ_ARROW_CHECK       : return new CGStdArrowCheckObj(this.ChartID(),name,extended);
      case OBJ_ARROW_LEFT_PRICE  : return new CGStdArrowLeftPriceObj(this.ChartID(),name,extended);
      case OBJ_ARROW_RIGHT_PRICE : return new CGStdArrowRightPriceObj(this.ChartID(),name,extended);
      case OBJ_ARROW_BUY         : return new CGStdArrowBuyObj(this.ChartID(),name,extended);
      case OBJ_ARROW_SELL        : return new CGStdArrowSellObj(this.ChartID(),name,extended);
      case OBJ_ARROW             : return new CGStdArrowObj(this.ChartID(),name,extended);
      //--- Графические объекты
      case OBJ_TEXT              : return new CGStdTextObj(this.ChartID(),name,extended);
      case OBJ_LABEL             : return new CGStdLabelObj(this.ChartID(),name,extended);
      case OBJ_BUTTON            : return new CGStdButtonObj(this.ChartID(),name,extended);
      case OBJ_CHART             : return new CGStdChartObj(this.ChartID(),name,extended);
      case OBJ_BITMAP            : return new CGStdBitmapObj(this.ChartID(),name,extended);
      case OBJ_BITMAP_LABEL      : return new CGStdBitmapLabelObj(this.ChartID(),name,extended);
      case OBJ_EDIT              : return new CGStdEditObj(this.ChartID(),name,extended);
      case OBJ_EVENT             : return new CGStdEventObj(this.ChartID(),name,extended);
      case OBJ_RECTANGLE_LABEL   : return new CGStdRectangleLabelObj(this.ChartID(),name,extended);
      default                    : return NULL;
     }
  }
//+------------------------------------------------------------------+

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

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

//+------------------------------------------------------------------+
//| Коллекция графических объектов                                   |
//+------------------------------------------------------------------+
#resource "\\"+PATH_TO_EVENT_CTRL_IND;          // Индикатор контроля событий графических объектов, упакованный в ресурсы программы
class CGraphElementsCollection : public CBaseObj
  {
private:
   CArrayObj         m_list_charts_control;     // Список объектов управления чартами
   CListObj          m_list_all_canv_elm_obj;   // Список всех графических элементов на канвасе
   CListObj          m_list_all_graph_obj;      // Список всех графических объектов
   CArrayObj         m_list_deleted_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              IsPresentGraphObjInList(const long chart_id,const string name);
//--- Возвращает флаг наличия графического объекта на графике по имени
   bool              IsPresentGraphObjOnChart(const long chart_id,const string name);
//--- Возвращает указатель на объект управления объектами указанного чарта
   CChartObjectsControl *GetChartObjectCtrlObj(const long chart_id);
//--- Создаёт новый объект управления графическими объектами указанного чарта и добавляет его в список
   CChartObjectsControl *CreateChartObjectCtrlObj(const long chart_id);
//--- Обновляет список графических объектов по идентификатору чарта
   CChartObjectsControl *RefreshByChartID(const long chart_id);
//--- Проверяет наличие окна графика
   bool              IsPresentChartWindow(const long chart_id);
//--- Обрабатывает удаление окна графика
   void              RefreshForExtraObjects(void);
//--- Возвращает первый свободный идентификатор графического (1) объекта, (2) элемента на канвасе
   long              GetFreeGraphObjID(bool program_object);
   long              GetFreeCanvElmID(void);
//--- Добавляет графический объект в коллекцию
   bool              AddGraphObjToCollection(const string source,CChartObjectsControl *obj_control);
//--- Находит объект, имеющийся в коллекции, но отсутствующий на графике
   CGStdGraphObj    *FindMissingObj(const long chart_id);
   CGStdGraphObj    *FindMissingObj(const long chart_id,int &index);
//--- Находит графический объект, имеющийся на графике, но отсутствующий в коллекции
   string            FindExtraObj(const long chart_id);
//--- Удаляет (1) указанный, (2) по идентификатору графика объект класса графического объекта из списка-коллекции графических объектов
   bool              DeleteGraphObjFromList(CGStdGraphObj *obj);
   void              DeleteGraphObjectsFromList(const long chart_id);
//--- Перемещает (1) указанный, (2) по индексу объект класса графического объекта в список удалённых графических объектов
   bool              MoveGraphObjToDeletedObjList(CGStdGraphObj *obj);
   bool              MoveGraphObjToDeletedObjList(const int index);
//--- Перемещает в список удалённых графических объектов все объекты по идентификатору графика
   void              MoveGraphObjectsToDeletedObjList(const long chart_id);
//--- Удаляет объект управления графиками из списка
   bool              DeleteGraphObjCtrlObjFromList(CChartObjectsControl *obj);
//--- Создаёт новый стандартный графический объект, возвращает имя объекта
   bool              CreateNewStdGraphObject(const long chart_id,
                                             const string name,
                                             const ENUM_OBJECT type,
                                             const int subwindow,
                                             const datetime time1,
                                             const double price1,
                                             const datetime time2=0,
                                             const double price2=0,
                                             const datetime time3=0,
                                             const double price3=0,
                                             const datetime time4=0,
                                             const double price4=0,
                                             const datetime time5=0,
                                             const double price5=0);
public:


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

//--- Возвращает (1) существующий, (2) удалённый графический объект по имени и идентификатору графика
   CGStdGraphObj    *GetStdGraphObject(const string name,const long chart_id);
   CGStdGraphObj    *GetStdDelGraphObject(const string name,const long chart_id);
//--- Возвращает список (1) объектов управления графиками, (2) удалённых графических объектов
   CArrayObj        *GetListChartsControl(void)                                                          { return &this.m_list_charts_control;  }
   CArrayObj        *GetListDeletedObj(void)                                                             { return &this.m_list_deleted_obj;     }
//--- Возвращает список (1) стандартных, (2) расширенных графических объектов
   CArrayObj        *GetListStdGraphObject(void)                              { return CSelect::ByGraphicStdObjectProperty(this.GetListGraphObj(),GRAPH_OBJ_PROP_ELEMENT_TYPE,0,GRAPH_ELEMENT_TYPE_STANDARD,EQUAL);           }
   CArrayObj        *GetListStdGraphObjectExt(void)                           { return CSelect::ByGraphicStdObjectProperty(this.GetListGraphObj(),GRAPH_OBJ_PROP_ELEMENT_TYPE,0,GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED,EQUAL);  }
//--- Возвращает (1) последний удалённый графический объект, (2) размер массива свойств графического объекта

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

В приватном методе, создающем новый графический объект, и возвращающем указатель на объект управления чартами, теперь будем вызывать метод создания графического объекта не из контекста этого класса, а из файла DELib.mqh (просто уберём "this." из строки вызова метода):

private:
//--- Создаёт новый графический объект, возвращает указатель на объект управления чартами
   CChartObjectsControl *CreateNewStdGraphObjectAndGetCtrlObj(const long chart_id,
                                                              const string name,
                                                              int subwindow,
                                                              const ENUM_OBJECT type_object,
                                                              const datetime time1,
                                                              const double price1,
                                                              const datetime time2=0,
                                                              const double price2=0,
                                                              const datetime time3=0,
                                                              const double price3=0,
                                                              const datetime time4=0,
                                                              const double price4=0,
                                                              const datetime time5=0,
                                                              const double price5=0)
                       {
                        //--- Если объект с идентификатором графика и именем уже есть в коллекции - сообщаем об этом и возвращаем NULL
                        if(this.IsPresentGraphObjInList(chart_id,name))
                          {
                           ::Print(DFUN,CMessage::Text(MSG_GRAPH_ELM_COLLECTION_ERR_GR_OBJ_ALREADY_EXISTS)," ChartID ",(string)chart_id,", ",name);
                           return NULL;
                          }
                        //--- Если не удалось создать новый стандартный графический объект - сообщаем об этом и возвращаем NULL
                        if(!CreateNewStdGraphObject(chart_id,name,type_object,subwindow,time1,price1,time2,price2,time3,price3,time4,price4,time5,price5))
                          {
                           ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_STD_GRAPH_OBJ),StdGraphObjectTypeDescription(type_object));
                           CMessage::ToLog(::GetLastError(),true);
                           return NULL;
                          }
                        //--- Если не удалось получить объект управления графиком - сообщаем об этом
                        CChartObjectsControl *ctrl=this.GetChartObjectCtrlObj(chart_id);
                        if(ctrl==NULL)
                           ::Print(DFUN,CMessage::Text(MSG_GRAPH_ELM_COLLECTION_ERR_FAILED_GET_CTRL_OBJ),(string)chart_id);
                        //--- Возвращаем указатель на объект управления графиком, либо NULL при ошибке его получения
                        return ctrl;
                       }


Во всех публичных методах для создания графических объектов впишем однотипные изменения — флаг расширенного объекта во входных параметрах метода и передачу этого флага в метод создания нового объекта класса стандартного графического объекта:

public:
//--- Создаёт графический объект "Вертикальная линия"
   bool              CreateLineVertical(const long chart_id,const string name,const int subwindow,const bool extended,const datetime time)
                       {
                        //--- Задаём имя и тип создаваемого объекта
                        string nm=this.m_name_program+"_"+name;
                        ENUM_OBJECT type_object=OBJ_VLINE;
                        //--- Создаём новый графический объект и получаем указатель на объект управления графиком
                        CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time,0);
                        if(ctrl==NULL)
                           return false;
                        //--- Создаём новый объект класса, соответствующий созданному графическому объекту
                        CGStdVLineObj *obj=ctrl.CreateNewGraphObj(type_object,nm,extended);
                        if(obj==NULL)
                          {
                           ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object));
                           return false;
                          }
                        //--- Устанавливаем объекту необходимые минимальные параметры
                        obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM);
                        obj.SetFlagSelectable(true,false);
                        obj.SetFlagSelected(true,false);
                        obj.SetObjectID(this.GetFreeGraphObjID(true));
                        obj.PropertiesCopyToPrevData();
                        //--- Возвращаем результат добавления объекта в список
                        return this.AddCreatedObjToList(DFUN,chart_id,nm,obj);
                       }

//--- Создаёт графический объект "Горизонтальная линия"
   bool              CreateLineHorizontal(const long chart_id,const string name,const int subwindow,const bool extended,const double price)
                       {
                        string nm=this.m_name_program+"_"+name;
                        ENUM_OBJECT type_object=OBJ_HLINE;
                        CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,0,price);
                        if(ctrl==NULL)
                           return false;
                        CGStdHLineObj *obj=ctrl.CreateNewGraphObj(type_object,nm,extended);

                        if(obj==NULL)
                          {
                           ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object));
                           return false;
                          }
                        //--- Устанавливаем объекту необходимые минимальные параметры
                        obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM);
                        obj.SetFlagSelectable(true,false);
                        obj.SetFlagSelected(true,false);
                        obj.SetObjectID(this.GetFreeGraphObjID(true));
                        obj.PropertiesCopyToPrevData();
                        //--- Возвращаем результат добавления объекта в список
                        return this.AddCreatedObjToList(DFUN,chart_id,nm,obj);
                       }
 
//--- Создаёт графический объект "Трендовая линия"

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

А листинг реализации метода, создающего новый стандартный графический объект, перенесём из этого класса в файл сервисных функций библиотеки \MQL5\Include\DoEasy\Services\DELib.mqh в самый его конец:

//+------------------------------------------------------------------+
//| Создаёт новый стандартный графический объект                     |
//+------------------------------------------------------------------+
bool CreateNewStdGraphObject(const long chart_id,
                             const string name,
                             const ENUM_OBJECT type,
                             const int subwindow,
                             const datetime time1,
                             const double price1,
                             const datetime time2=0,
                             const double price2=0,
                             const datetime time3=0,
                             const double price3=0,
                             const datetime time4=0,
                             const double price4=0,
                             const datetime time5=0,
                             const double price5=0)
  {
   ::ResetLastError();
   switch(type)
     {
      //--- Линии
      case OBJ_VLINE             : return ::ObjectCreate(chart_id,name,OBJ_VLINE,subwindow,time1,0);
      case OBJ_HLINE             : return ::ObjectCreate(chart_id,name,OBJ_HLINE,subwindow,0,price1);
      case OBJ_TREND             : return ::ObjectCreate(chart_id,name,OBJ_TREND,subwindow,time1,price1,time2,price2);
      case OBJ_TRENDBYANGLE      : return ::ObjectCreate(chart_id,name,OBJ_TRENDBYANGLE,subwindow,time1,price1,time2,price2);
      case OBJ_CYCLES            : return ::ObjectCreate(chart_id,name,OBJ_CYCLES,subwindow,time1,price1,time2,price2);
      case OBJ_ARROWED_LINE      : return ::ObjectCreate(chart_id,name,OBJ_ARROWED_LINE,subwindow,time1,price1,time2,price2);
      //--- Каналы
      case OBJ_CHANNEL           : return ::ObjectCreate(chart_id,name,OBJ_CHANNEL,subwindow,time1,price1,time2,price2,time3,price3);
      case OBJ_STDDEVCHANNEL     : return ::ObjectCreate(chart_id,name,OBJ_STDDEVCHANNEL,subwindow,time1,price1,time2,price2);
      case OBJ_REGRESSION        : return ::ObjectCreate(chart_id,name,OBJ_REGRESSION,subwindow,time1,price1,time2,price2);
      case OBJ_PITCHFORK         : return ::ObjectCreate(chart_id,name,OBJ_PITCHFORK,subwindow,time1,price1,time2,price2,time3,price3);
      //--- Ганн
      case OBJ_GANNLINE          : return ::ObjectCreate(chart_id,name,OBJ_GANNLINE,subwindow,time1,price1,time2,price2);
      case OBJ_GANNFAN           : return ::ObjectCreate(chart_id,name,OBJ_GANNFAN,subwindow,time1,price1,time2,price2);
      case OBJ_GANNGRID          : return ::ObjectCreate(chart_id,name,OBJ_GANNGRID,subwindow,time1,price1,time2,price2);
      //--- Фибоначчи
      case OBJ_FIBO              : return ::ObjectCreate(chart_id,name,OBJ_FIBO,subwindow,time1,price1,time2,price2);
      case OBJ_FIBOTIMES         : return ::ObjectCreate(chart_id,name,OBJ_FIBOTIMES,subwindow,time1,price1,time2,price2);
      case OBJ_FIBOFAN           : return ::ObjectCreate(chart_id,name,OBJ_FIBOFAN,subwindow,time1,price1,time2,price2);
      case OBJ_FIBOARC           : return ::ObjectCreate(chart_id,name,OBJ_FIBOARC,subwindow,time1,price1,time2,price2);
      case OBJ_FIBOCHANNEL       : return ::ObjectCreate(chart_id,name,OBJ_FIBOCHANNEL,subwindow,time1,price1,time2,price2,time3,price3);
      case OBJ_EXPANSION         : return ::ObjectCreate(chart_id,name,OBJ_EXPANSION,subwindow,time1,price1,time2,price2,time3,price3);
      //--- Эллиотт
      case OBJ_ELLIOTWAVE5       : return ::ObjectCreate(chart_id,name,OBJ_ELLIOTWAVE5,subwindow,time1,price1,time2,price2,time3,price3,time4,price4,time5,price5);
      case OBJ_ELLIOTWAVE3       : return ::ObjectCreate(chart_id,name,OBJ_ELLIOTWAVE3,subwindow,time1,price1,time2,price2,time3,price3);
      //--- Фигуры
      case OBJ_RECTANGLE         : return ::ObjectCreate(chart_id,name,OBJ_RECTANGLE,subwindow,time1,price1,time2,price2);
      case OBJ_TRIANGLE          : return ::ObjectCreate(chart_id,name,OBJ_TRIANGLE,subwindow,time1,price1,time2,price2,time3,price3);
      case OBJ_ELLIPSE           : return ::ObjectCreate(chart_id,name,OBJ_ELLIPSE,subwindow,time1,price1,time2,price2,time3,price3);
      //--- Стрелки
      case OBJ_ARROW_THUMB_UP    : return ::ObjectCreate(chart_id,name,OBJ_ARROW_THUMB_UP,subwindow,time1,price1);
      case OBJ_ARROW_THUMB_DOWN  : return ::ObjectCreate(chart_id,name,OBJ_ARROW_THUMB_DOWN,subwindow,time1,price1);
      case OBJ_ARROW_UP          : return ::ObjectCreate(chart_id,name,OBJ_ARROW_UP,subwindow,time1,price1);
      case OBJ_ARROW_DOWN        : return ::ObjectCreate(chart_id,name,OBJ_ARROW_DOWN,subwindow,time1,price1);
      case OBJ_ARROW_STOP        : return ::ObjectCreate(chart_id,name,OBJ_ARROW_STOP,subwindow,time1,price1);
      case OBJ_ARROW_CHECK       : return ::ObjectCreate(chart_id,name,OBJ_ARROW_CHECK,subwindow,time1,price1);
      case OBJ_ARROW_LEFT_PRICE  : return ::ObjectCreate(chart_id,name,OBJ_ARROW_LEFT_PRICE,subwindow,time1,price1);
      case OBJ_ARROW_RIGHT_PRICE : return ::ObjectCreate(chart_id,name,OBJ_ARROW_RIGHT_PRICE,subwindow,time1,price1);
      case OBJ_ARROW_BUY         : return ::ObjectCreate(chart_id,name,OBJ_ARROW_BUY,subwindow,time1,price1);
      case OBJ_ARROW_SELL        : return ::ObjectCreate(chart_id,name,OBJ_ARROW_SELL,subwindow,time1,price1);
      case OBJ_ARROW             : return ::ObjectCreate(chart_id,name,OBJ_ARROW,subwindow,time1,price1);
      //--- Графические объекты
      case OBJ_TEXT              : return ::ObjectCreate(chart_id,name,OBJ_TEXT,subwindow,time1,price1);
      case OBJ_LABEL             : return ::ObjectCreate(chart_id,name,OBJ_LABEL,subwindow,0,0);
      case OBJ_BUTTON            : return ::ObjectCreate(chart_id,name,OBJ_BUTTON,subwindow,0,0);
      case OBJ_CHART             : return ::ObjectCreate(chart_id,name,OBJ_CHART,subwindow,0,0);
      case OBJ_BITMAP            : return ::ObjectCreate(chart_id,name,OBJ_BITMAP,subwindow,time1,price1);
      case OBJ_BITMAP_LABEL      : return ::ObjectCreate(chart_id,name,OBJ_BITMAP_LABEL,subwindow,0,0);
      case OBJ_EDIT              : return ::ObjectCreate(chart_id,name,OBJ_EDIT,subwindow,0,0);
      case OBJ_EVENT             : return ::ObjectCreate(chart_id,name,OBJ_EVENT,subwindow,time1,0);
      case OBJ_RECTANGLE_LABEL   : return ::ObjectCreate(chart_id,name,OBJ_RECTANGLE_LABEL,subwindow,0,0);
      //---
      default: return false;
     }
  }
//+------------------------------------------------------------------+


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

Метод, возвращающий имя программы, переименуем из безликого Name() в более точный ProgramName():

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

//--- Возвращает имя программы
   string               ProgramName(void)                         const { return this.m_name;                                 }

//--- Устанавливает новое (1) время начала отсчёта паузы, (2) паузу в миллисекундах

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

//--- Возвращает класс объекта стандартного графического объекта по имени и идентификатору графика
   CGStdGraphObj       *GraphGetStdGraphObject(const string name,const long chart_id)
                          {
                           CGStdGraphObj *obj=this.m_graph_objects.GetStdGraphObject(name,chart_id);
                           if(obj==NULL)
                              obj=this.m_graph_objects.GetStdGraphObject(this.ProgramName()+"_"+name,chart_id);
                           return obj;
                          }

Если объект был создан вручную, то его имя никак не изменяется библиотекой. Но если мы создаём графический объект программно, то методы его создания добавляют к его имени префикс с наименованием программы. Поэтому, если мы создали объект из своей программы с именем, например, "object_name", и пытаемся его найти, то у нас ничего не выйдет, так как, к имени был добавлен префикс с именем программы, и теперь имя объекта выглядит как "имя_программы_object_name". Исходя из этого, в методе сначала производится поиск точного имени, переданного в метод, а далее, если объект с таким именем не найден, то пытаемся найти объект с добавленным к его имени префиксом с именем программы.

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

//--- Возвращает класс объекта расширенного стандартного графического объекта по имени и идентификатору графика
   CGStdGraphObj       *GraphGetStdGraphObjectExt(const string name,const long chart_id)
                          {
                           CArrayObj *list=this.m_graph_objects.GetListStdGraphObjectExt();
                           string nm=(::StringFind(name,this.ProgramName()+"_")==WRONG_VALUE ? this.ProgramName()+"_"+name : name);
                           list=CSelect::ByGraphicStdObjectProperty(list,GRAPH_OBJ_PROP_NAME,0,nm,EQUAL);
                           return(list!=NULL ? list.At(0) : NULL);
                          }

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

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

//--- Создаёт графический объект "Вертикальная линия"
   bool              CreateLineVertical(const long chart_id,const string name,const int subwindow,const bool extended,const datetime time)
                       { return this.m_graph_objects.CreateLineVertical(chart_id,name,subwindow,extended,time); }
   bool              CreateLineVertical(const string name,const int subwindow,const bool extended,const datetime time)
                       { return this.m_graph_objects.CreateLineVertical(::ChartID(),name,subwindow,extended,time); }

//--- Создаёт графический объект "Горизонтальная линия"
   bool              CreateLineHorizontal(const long chart_id,const string name,const int subwindow,const bool extended,const double price)
                       { return this.m_graph_objects.CreateLineHorizontal(chart_id,name,subwindow,extended,price); }
   bool              CreateLineHorizontal(const string name,const int subwindow,const bool extended,const double price)
                       { return this.m_graph_objects.CreateLineHorizontal(::ChartID(),name,subwindow,extended,price); }

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

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


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

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

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

В обработчике советника OnChartEvent() в блоке обработки щелчка по графику впишем такой код:

   if(id==CHARTEVENT_CLICK)
     {
      if(!IsCtrlKeyPressed())
         return;
      //--- Получаем координаты щелчка по графику
      datetime time=0;
      double price=0;
      int sw=0;
      if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,sw,time,price))
        {
         //--- Получаем координаты правой точки для трендовой линии
         datetime time2=iTime(Symbol(),PERIOD_CURRENT,1);
         double price2=iOpen(Symbol(),PERIOD_CURRENT,1);
         
         //--- Создаём объект "Трендовая линия"
         string name_base="TrendLineExt";
         engine.CreateLineTrend(name_base,0,true,time,price,time2,price2);
         //--- Получаем объект из списка графических объектов по имени и идентификатору графика и распечатываем его свойства в журнал
         CGStdGraphObj *obj=engine.GraphGetStdGraphObjectExt(name_base,ChartID());
         obj.Print();
         
         //--- Создаём объект "Левая ценовая метка"
         string name_dep="PriceLeft";
         engine.CreatePriceLabelLeft(name_dep,0,false,time,price);
         //--- Получаем объект из списка графических объектов по имени и идентификатору графика и
         CGStdGraphObj *dep=engine.GraphGetStdGraphObject(name_dep,ChartID());
         //--- добавляем его в список привязанных графических объектов к объекту "Трендовая линия"
         obj.AddDependentObj(dep);
         //--- Устанавливаем его точку привязки по оси X и Y к левой точке трендовой линии
         obj.AddNewLinkedPivotPointXY(dep,GRAPH_OBJ_PROP_TIME,0,GRAPH_OBJ_PROP_PRICE,0);
         //--- Распечатаем в журнал количество опорных точек привязки по обем осям (для каждой оси одна точка)
         Print(DFUN,"PriceLeft: Num linked coord X: ",dep.GetBasePivotsNumX(0),", Num linked coord Y: ",dep.GetBasePivotsNumY(0));
         
         //--- Создаём объект "Правая ценовая метка"
         name_dep="PriceRight";
         engine.CreatePriceLabelRight(name_dep,0,false,time2,price2);
         //--- Получаем объект из списка графических объектов по имени и идентификатору графика и
         dep=engine.GraphGetStdGraphObject(name_dep,ChartID());
         //--- добавляем его в список привязанных графических объектов к объекту "Трендовая линия"
         obj.AddDependentObj(dep);
         //--- Устанавливаем его точку привязки по оси X и Y к правой точке трендовой линии
         obj.AddNewLinkedPivotPointXY(dep,GRAPH_OBJ_PROP_TIME,1,GRAPH_OBJ_PROP_PRICE,1);
         //--- Распечатаем в журнал количество опорных точек привязки по обем осям (для каждой оси одна точка)
         Print(DFUN,"PriceRight: Num linked coord X: ",dep.GetBasePivotsNumX(0),", Num linked coord Y: ",dep.GetBasePivotsNumY(0));
        }
     }

Здесь полностью вся логика прокомментирована в листинге и, надеюсь, вопросов не вызывает.

Скомпилируем советник, запустим его на графике и щёлкнем мышкой где-нибудь подальше от правого края графика.

Будет построена трендовая линия, а по её краям — объекты-ценовые метки:


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

2022.01.20 16:37:29.340 ============= The beginning of the parameter list (Trend Line) =============
2022.01.20 16:37:29.340 Object ID: 1
2022.01.20 16:37:29.340 Base object ID: 0
2022.01.20 16:37:29.340 Object type: Trend Line
2022.01.20 16:37:29.340 Graphic element type: Extended standard graphic object
2022.01.20 16:37:29.340 Object belongs to: The graphic object belongs to the program
2022.01.20 16:37:29.340 Object chart ID: 131733844391938630
2022.01.20 16:37:29.340 Chart subwindow number: 0
2022.01.20 16:37:29.340 Object number in the list: The base object of the extended graphical object
2022.01.20 16:37:29.340 Change history: No
2022.01.20 16:37:29.340 Object group: Not set
2022.01.20 16:37:29.340 Time of creation: 2022.01.20 16:37:29
2022.01.20 16:37:29.340 Visibility of an object at timeframes: Drawn on all timeframes
2022.01.20 16:37:29.340 Object in the background: No
2022.01.20 16:37:29.340 Priority of a graphical object for receiving events of clicking on a chart: 0
2022.01.20 16:37:29.340 Prohibit showing of the name of a graphical object in the terminal objects list: Yes
2022.01.20 16:37:29.340 Object is selected: Yes
2022.01.20 16:37:29.340 Object availability: Yes
2022.01.20 16:37:29.340 Time coordinate: 
2022.01.20 16:37:29.340  - Pivot point 0: 2022.01.13 09:00
2022.01.20 16:37:29.340  - Pivot point 1: 2022.01.20 10:00
2022.01.20 16:37:29.340 Color: clrRed
2022.01.20 16:37:29.340 Style: Solid line
2022.01.20 16:37:29.340 Line thickness: 1
2022.01.20 16:37:29.340 Ray goes to the left: No
2022.01.20 16:37:29.340 Ray goes to the right: No
2022.01.20 16:37:29.340 ------
2022.01.20 16:37:29.340 Price coordinate: 
2022.01.20 16:37:29.340  - Pivot point 0: 1.14728
2022.01.20 16:37:29.340  - Pivot point 1: 1.13598
2022.01.20 16:37:29.340 ------
2022.01.20 16:37:29.340 Name: "TestDoEasyPart93_TrendLineExt"
2022.01.20 16:37:29.340 Base object name: "TestDoEasyPart93_TrendLineExt"
2022.01.20 16:37:29.340 Description: Not set
2022.01.20 16:37:29.340 The text of a tooltip: Formed by the terminal
2022.01.20 16:37:29.340 ============= End of the parameter list (Trend Line) =============
2022.01.20 16:37:29.340 
2022.01.20 16:37:29.352 OnChartEvent: PriceLeft: Num linked coord X: 1, Num linked coord Y: 1
2022.01.20 16:37:29.364 OnChartEvent: PriceRight: Num linked coord X: 1, Num linked coord Y: 1

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

Что дальше

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

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

К содержанию

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

Графика в библиотеке DoEasy (Часть 89): Программное создание стандартных графических объектов. Базовый функционал
Графика в библиотеке DoEasy (Часть 90): События стандартных графических объектов. Базовый функционал
Графика в библиотеке DoEasy (Часть 91): События стандартных графических объектов в программе. История изменения имени объекта
Графика в библиотеке DoEasy (Часть 92): Класс памяти стандартных графических объектов. История изменения свойств объекта

Прикрепленные файлы |
MQL5.zip (4259.12 KB)
Стать хорошим программистом (Часть 7): как стать успешным исполнителем во Фрилансе Стать хорошим программистом (Часть 7): как стать успешным исполнителем во Фрилансе
Хотите стать успешным исполнителем во Фрилансе MQL5.Community? Тогда рекомендую почитать советы из этой статьи.
Пишем глубокую нейронную сеть с нуля на языке MQL Пишем глубокую нейронную сеть с нуля на языке MQL
Статья познакомит вас с глубокой нейронной сетью, написанной на MQL, и с различными функциями активации этой сети, такими как функция гиперболического тангенса для скрытых слоев и Softmax для выходного слоя. Мы будем изучать нейросеть постепенно, двигаясь от первого шага до последнего, и вместе создадим глубокую нейронную сеть.
Фиксированный стоп-лосс на основе ценового действия и RSI ("умный" стоп-лосс) Фиксированный стоп-лосс на основе ценового действия и RSI ("умный" стоп-лосс)
Стоп-лосс – основной инструмент управления капиталом в трейдинге. Эффективное использование стоп-лосса, тейк-профита и размера лота может сделать торговлю более последовательной и в целом более прибыльной. Тем не менее, в использовании стоп-лосса есть и свои трудности. Основная из них – охота на стоп-лоссы. В этой статье рассматривается, как свести к минимуму эффект от охоты на стоп-лоссы, а также проводится сравнение с классическим применением стоп-лосса для определения его прибыльности.
Стать хорошим программистом (Часть 6): 9 привычек для эффективной разработки Стать хорошим программистом (Часть 6): 9 привычек для эффективной разработки
Качество разработки — это не только про написание кода. На своем опыте я выявил определенные привычки, которые помогают повысить эффективность разработки. О некоторых из них мы поговорим в этой статье. Статья обязательна к прочтению для всех, кто хочет улучшить навыки написания сложных алгоритмов.