Работа с таймсериями в библиотеке DoEasy (Часть 42): Класс объекта абстрактного индикаторного буфера

Artyom Trishkin | 16 апреля, 2020

Содержание


Концепция

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

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

Сегодня напишем класс объекта абстрактного индикаторного буфера. Объект будет содержать все свойства индикаторного буфера из перечислений ENUM_PLOT_PROPERTY_INTEGER, ENUM_PLOT_PROPERTY_DOUBLE, ENUM_PLOT_PROPERTY_STRINGи несколько дополнительных свойств:

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

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


Класс абстрактного буфера CBuffer

Для работы класса нам потребуются текстовые сообщения с описаниями свойств буфера.

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

   MSG_LIB_TEXT_WAIT,                                 // Ожидание
   MSG_LIB_TEXT_END,                                  // Окончание
   MSG_LIB_TEXT_PERIOD_CURRENT,                       // Текущий период графика
   

...

   MSG_LIB_TEXT_TS_TEXT_ATTEMPT,                      // Попытка:
   MSG_LIB_TEXT_TS_TEXT_WAIT_FOR_SYNC,                // Ожидание синхронизации данных ...

//--- CBuffer
   MSG_LIB_TEXT_BUFFER_TEXT_INDEX_BASE,               // Индекс базового буфера данных
   MSG_LIB_TEXT_BUFFER_TEXT_INDEX_PLOT,               // Порядковый номер рисуемого буфера
   MSG_LIB_TEXT_BUFFER_TEXT_INDEX_COLOR,              // Индекс буфера цвета
   MSG_LIB_TEXT_BUFFER_TEXT_NUM_DATAS,                // Количество буферов данных
   MSG_LIB_TEXT_BUFFER_TEXT_INDEX_NEXT,               // Индекс свободного массива для назначения следующим индикаторным буфером
   MSG_LIB_TEXT_BUFFER_TEXT_TIMEFRAME,                // Период данных буфера (таймфрейм)
   MSG_LIB_TEXT_BUFFER_TEXT_STATUS,                   // Статус буфера
   MSG_LIB_TEXT_BUFFER_TEXT_TYPE,                     // Тип буфера
   MSG_LIB_TEXT_BUFFER_TEXT_ACTIVE,                   // Активен
   MSG_LIB_TEXT_BUFFER_TEXT_ARROW_CODE,               // Код стрелки
   MSG_LIB_TEXT_BUFFER_TEXT_ARROW_SHIFT,              // Смещение стрелок по вертикали
   MSG_LIB_TEXT_BUFFER_TEXT_DRAW_BEGIN,               // Количество начальных баров без отрисовки и значений в DataWindow
   MSG_LIB_TEXT_BUFFER_TEXT_DRAW_TYPE,                // Тип графического построения
   MSG_LIB_TEXT_BUFFER_TEXT_SHOW_DATA,                // Отображение значений построения в окне DataWindow
   MSG_LIB_TEXT_BUFFER_TEXT_SHIFT,                    // Сдвиг графического построения индикатора по оси времени в барах
   MSG_LIB_TEXT_BUFFER_TEXT_LINE_STYLE,               // Стиль линии отрисовки
   MSG_LIB_TEXT_BUFFER_TEXT_LINE_WIDTH,               // Толщина линии отрисовки
   MSG_LIB_TEXT_BUFFER_TEXT_COLOR_NUM,                // Количество цветов
   MSG_LIB_TEXT_BUFFER_TEXT_COLOR,                    // Цвет отрисовки
   MSG_LIB_TEXT_BUFFER_TEXT_EMPTY_VALUE,              // Пустое значение для построения, для которого нет отрисовки
   MSG_LIB_TEXT_BUFFER_TEXT_SYMBOL,                   // Символ буфера
   MSG_LIB_TEXT_BUFFER_TEXT_LABEL,                    // Имя индикаторной графической серии, отображаемое в окне DataWindow
   MSG_LIB_TEXT_BUFFER_TEXT_STATUS_NAME,              // Индикаторный буфер с типом графического построения

   MSG_LIB_TEXT_BUFFER_TEXT_STATUS_NONE,              // Нет отрисовки
   MSG_LIB_TEXT_BUFFER_TEXT_STATUS_FILLING,           // Цветовая заливка между двумя уровнями
   MSG_LIB_TEXT_BUFFER_TEXT_STATUS_LINE,              // Линия
   MSG_LIB_TEXT_BUFFER_TEXT_STATUS_HISTOGRAM,         // Гистограмма от нулевой линии
   MSG_LIB_TEXT_BUFFER_TEXT_STATUS_ARROW,             // Отрисовка стрелками
   MSG_LIB_TEXT_BUFFER_TEXT_STATUS_SECTION,           // Отрезки
   MSG_LIB_TEXT_BUFFER_TEXT_STATUS_HISTOGRAM2,        // Гистограмма на двух индикаторных буферах
   MSG_LIB_TEXT_BUFFER_TEXT_STATUS_ZIGZAG,            // Zigzag
   MSG_LIB_TEXT_BUFFER_TEXT_STATUS_BARS,              // Отображение в виде баров
   MSG_LIB_TEXT_BUFFER_TEXT_STATUS_CANDLES,           // Отображение в виде свечей
   
   MSG_LIB_TEXT_BUFFER_TEXT_TYPE_CALCULATE,           // Расчётный буфер
   MSG_LIB_TEXT_BUFFER_TEXT_TYPE_DATA,                // Цветной буфер данных
   
   MSG_LIB_TEXT_BUFFER_TEXT_STYLE_SOLID,              // Сплошная линия
   MSG_LIB_TEXT_BUFFER_TEXT_STYLE_DASH,               // Прерывистая линия
   MSG_LIB_TEXT_BUFFER_TEXT_STYLE_DOT,                // Пунктирная линия
   MSG_LIB_TEXT_BUFFER_TEXT_STYLE_DASHDOT,            // Штрих-пунктирная линия
   MSG_LIB_TEXT_BUFFER_TEXT_STYLE_DASHDOTDOT,         // Штрих - две точки

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

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

   {"Ожидание","Wait"},
   {"Окончание","End"},
   {"Текущий период графика","Current chart period"},
   

...

   {"Попытка: ","Attempt: "},
   {"Ожидание синхронизации данных ...","Waiting for data synchronization ..."},
   
   {"Индекс базового буфера данных","Index of Base data buffer"},
   {"Порядковый номер рисуемого буфера","Plot buffer sequence number"},
   {"Индекс буфера цвета","Color buffer index"},
   {"Количество буферов данных","Number of data buffers"},
   {"Индекс массива для назначения следующим индикаторным буфером","Array index for assignment as the next indicator buffer"},
   {"Период данных буфера (таймфрейм)","Buffer data Period (Timeframe)"},
   {"Статус буфера","Buffer status"},
   {"Тип буфера","Buffer type"},
   {"Активен","Active"},
   {"Код стрелки","Arrow code"},
   {"Смещение стрелок по вертикали","Vertical shift of arrows"},
   {"Количество начальных баров без отрисовки и значений в DataWindow","Number of initial bars without drawing and values in the DataWindow"},
   {"Тип графического построения","Type of graphical construction"},
   {"Отображение значений построения в окне DataWindow","Display construction values in the DataWindow"},
   {"Сдвиг графического построения индикатора по оси времени в барах","Shift of indicator plotting along the time axis in bars"},
   {"Стиль линии отрисовки","Drawing line style "},
   {"Толщина линии отрисовки","The thickness of the drawing line"},
   {"Количество цветов","The number of colors"},
   {"Цвет отрисовки","The index of a buffer containing the drawing color"},
   {"Пустое значение для построения, для которого нет отрисовки","An empty value for plotting, for which there is no drawing"},
   {"Символ буфера","Buffer Symbol"},
   {"Имя индикаторной графической серии, отображаемое в окне DataWindow","The name of the indicator graphical series to display in the DataWindow"},
   {"Индикаторный буфер с типом графического построения","Indicator buffer with graphic plot type"},
   
   {"Нет отрисовки","No drawing"},
   {"Цветовая заливка между двумя уровнями","Color fill between the two levels"},
   {"Линия","Line"},
   {"Гистограмма от нулевой линии","Histogram from the zero line"},
   {"Отрисовка стрелками","Drawing arrows"},
   {"Отрезки","Section"},
   {"Гистограмма на двух индикаторных буферах","Histogram of the two indicator buffers"},
   {"Зигзаг","Zigzag"},
   {"Отображение в виде баров","Display as a sequence of bars"},
   {"Отображение в виде свечей","Display as a sequence of candlesticks"},
   
   {"Расчётный буфер","Calculated buffer"},
   {"Цветной буфер данных","Colored Data buffer"},
   
   {"Сплошная линия","Solid line"},
   {"Прерывистая линия","Broken line"},
   {"Пунктирная линия","Dotted line"},
   {"Штрих-пунктирная линия","Dash-dot line"},
   {"Штрих - две точки","Dash - two points"},
   
  };
//+---------------------------------------------------------------------+

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

Откроем файл \MQL5\Include\DoEasy\Defines.mqh и впишем идентификатор коллекции индикаторных буферов:

//--- Идентификаторы списков коллекций
#define COLLECTION_HISTORY_ID          (0x777A)                   // Идентификатор списка исторической коллекции
#define COLLECTION_MARKET_ID           (0x777B)                   // Идентификатор списка рыночной коллекции
#define COLLECTION_EVENTS_ID           (0x777C)                   // Идентификатор списка коллекции событий
#define COLLECTION_ACCOUNT_ID          (0x777D)                   // Идентификатор списка коллекции аккаунтов
#define COLLECTION_SYMBOLS_ID          (0x777E)                   // Идентификатор списка коллекции символов
#define COLLECTION_SERIES_ID           (0x777F)                   // Идентификатор списка коллекции таймсерий
#define COLLECTION_BUFFERS_ID          (0x7780)                   // Идентификатор списка коллекции индикаторных буферов
//--- Параметры данных для файловых операций

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

//+------------------------------------------------------------------+
//| Данные для работы с индикаторными буферами                       |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Статус абстрактного буфера (по стилю рисования)                  |
//+------------------------------------------------------------------+
enum ENUM_BUFFER_STATUS
  {
   BUFFER_STATUS_NONE,                                         // Нет отрисовки
   BUFFER_STATUS_FILLING,                                      // Цветовая заливка между двумя уровнями (MQL5)
   BUFFER_STATUS_LINE,                                         // Линия
   BUFFER_STATUS_HISTOGRAM,                                    // Гистограмма от нулевой линии
   BUFFER_STATUS_ARROW,                                        // Отрисовка стрелками
   BUFFER_STATUS_SECTION,                                      // Отрезки
   BUFFER_STATUS_HISTOGRAM2,                                   // Гистограмма на двух индикаторных буферах
   BUFFER_STATUS_ZIGZAG,                                       // Стиль Zigzag
   BUFFER_STATUS_BARS,                                         // Отображение в виде баров  (MQL5)
   BUFFER_STATUS_CANDLES,                                      // Отображение в виде свечей (MQL5)
  };
//+------------------------------------------------------------------+
//| Тип буфера                                                       |
//+------------------------------------------------------------------+
enum ENUM_BUFFER_TYPE
  {
   BUFFER_TYPE_CALCULATE,                                      // Расчётный буфер
   BUFFER_TYPE_DATA,                                           // Цветной буфер данных
  };
//+------------------------------------------------------------------+
//| Целочисленные свойства буфера                                    |
//+------------------------------------------------------------------+
enum ENUM_BUFFER_PROP_INTEGER
  {
   BUFFER_PROP_INDEX_PLOT = 0,                                 // Порядковый номер рисуемого буфера
   BUFFER_PROP_STATUS,                                         // Статус (по стилю рисования) буфера (из перечисления ENUM_BUFFER_STATUS)
   BUFFER_PROP_TYPE,                                           // Тип буфера (из перечисления ENUM_BUFFER_TYPE)
   BUFFER_PROP_TIMEFRAME,                                      // Период данных буфера (таймфрейм)
   BUFFER_PROP_ACTIVE,                                         // Флаг использования буфера
   BUFFER_PROP_ARROW_CODE,                                     // Код стрелки для стиля DRAW_ARROW
   BUFFER_PROP_ARROW_SHIFT,                                    // Смещение стрелок по вертикали для стиля DRAW_ARROW
   BUFFER_PROP_DRAW_BEGIN,                                     // Количество начальных баров без отрисовки и значений в DataWindow
   BUFFER_PROP_SHOW_DATA,                                      // Признак отображения значений построения в окне DataWindow
   BUFFER_PROP_DRAW_TYPE,                                      // Тип графического построения (из перечисления ENUM_DRAW_TYPE)
   BUFFER_PROP_SHIFT,                                          // Сдвиг графического построения индикатора по оси времени в барах
   BUFFER_PROP_LINE_STYLE,                                     // Стиль линии отрисовки
   BUFFER_PROP_LINE_WIDTH,                                     // Толщина линии отрисовки
   BUFFER_PROP_COLOR_INDEXES,                                  // Количество цветов
   BUFFER_PROP_COLOR,                                          // Цвет отрисовки
   BUFFER_PROP_NUM_DATAS,                                      // Количество буферов данных
   BUFFER_PROP_INDEX_BASE,                                     // Индекс базового буфера данных
   BUFFER_PROP_INDEX_COLOR,                                    // Индекс буфера цвета
   BUFFER_PROP_INDEX_NEXT,                                     // Индекс свободного массива для назначения следующим индикаторным буфером
  }; 
#define BUFFER_PROP_INTEGER_TOTAL (19)                         // Общее количество целочисленных свойств буфера
#define BUFFER_PROP_INTEGER_SKIP  (6)                          // Количество неиспользуемых в сортировке свойств буфера
//+------------------------------------------------------------------+
//| Вещественные свойства буфера                                     |
//+------------------------------------------------------------------+
enum ENUM_BUFFER_PROP_DOUBLE
  {
   BUFFER_PROP_EMPTY_VALUE = BUFFER_PROP_INTEGER_TOTAL,        // Пустое значение для построения, для которого нет отрисовки
  }; 
#define BUFFER_PROP_DOUBLE_TOTAL  (1)                          // Общее количество вещественных свойств буфера
#define BUFFER_PROP_DOUBLE_SKIP   (0)                          // Количество неиспользуемых в сортировке свойств буфера
//+------------------------------------------------------------------+
//| Строковые свойства буфера                                        |
//+------------------------------------------------------------------+
enum ENUM_BUFFER_PROP_STRING
  {
   BUFFER_PROP_SYMBOL = (BUFFER_PROP_INTEGER_TOTAL+BUFFER_PROP_DOUBLE_TOTAL), // Символ буфера
   BUFFER_PROP_LABEL,                                          // Имя индикаторной графической серии, отображаемое в окне DataWindow
  };
#define BUFFER_PROP_STRING_TOTAL  (2)                          // Общее количество строковых свойств буфера
//+------------------------------------------------------------------+
//| Возможные критерии сортировки буферов                            |
//+------------------------------------------------------------------+
#define FIRST_BUFFER_DBL_PROP          (BUFFER_PROP_INTEGER_TOTAL-BUFFER_PROP_INTEGER_SKIP)
#define FIRST_BUFFER_STR_PROP          (BUFFER_PROP_INTEGER_TOTAL-BUFFER_PROP_INTEGER_SKIP+BUFFER_PROP_DOUBLE_TOTAL-BUFFER_PROP_DOUBLE_SKIP)
enum ENUM_SORT_BUFFER_MODE
  {
//--- Сортировка по целочисленным свойствам
   SORT_BY_BUFFER_INDEX_PLOT = 0,                              // Сортировать по порядковому номеру рисуемого буфера
   SORT_BY_BUFFER_STATUS,                                      // Сортировать по стилю рисования (статусу) буфера (из перечисления ENUM_BUFFER_STATUS)
   SORT_BY_BUFFER_TYPE,                                        // Сортировать по типу буфера (из перечисления ENUM_BUFFER_TYPE)
   SORT_BY_BUFFER_TIMEFRAME,                                   // Сортировать по периоду данных буфера (таймфрейму)
   SORT_BY_BUFFER_ACTIVE,                                      // Сортировать по флагу использования буфера
   SORT_BY_BUFFER_ARROW_CODE,                                  // Сортировать по коду стрелки для стиля DRAW_ARROW
   SORT_BY_BUFFER_ARROW_SHIFT,                                 // Сортировать по смещению стрелок по вертикали для стиля DRAW_ARROW
   SORT_BY_BUFFER_DRAW_BEGIN,                                  // Сортировать по количеству начальных баров без отрисовки и значений в DataWindow
   SORT_BY_BUFFER_SHOW_DATA,                                   // Сортировать по признаку отображения значений построения в окне DataWindow
   SORT_BY_BUFFER_DRAW_TYPE,                                   // Сортировать по типу графического построения (из перечисления ENUM_DRAW_TYPE)
   SORT_BY_BUFFER_SHIFT,                                       // Сортировать по сдвигу графического построения индикатора по оси времени в барах
   SORT_BY_BUFFER_LINE_STYLE,                                  // Сортировать по стилю линии отрисовки
   SORT_BY_BUFFER_LINE_WIDTH,                                  // Сортировать по толщине линии отрисовки
   SORT_BY_BUFFER_COLOR_INDEXES,                               // Сортировать по количеству цветов
   SORT_BY_BUFFER_COLOR,                                       // Сортировать по цвету отрисовки
//--- Сортировка по вещественным свойствам
   SORT_BY_BUFFER_EMPTY_VALUE = FIRST_BUFFER_DBL_PROP,         // Сортировать по пустому значению для построения, для которого нет отрисовки
//--- Сортировка по строковым свойствам
   SORT_BY_BUFFER_SYMBOL = FIRST_BUFFER_STR_PROP,              // Сортировать по символу буфера
   SORT_BY_BUFFER_LABEL,                                       // Сортировать по имени индикаторной графической серии, отображаемого в окне DataWindow
  };
//+------------------------------------------------------------------+

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

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

Создадим в каталоге библиотеки \MQL5\Include\DoEasy\Objects\ новую папку Indicators\, а в ней — новый файл Buffer.mqh класса CBuffer.
Базовым классом для класса объекта абстрактного буфера зададам класс базового объекта всех объектов библиотеки CBaseObj,
файл которого подключим к файлу класса буфера
:

//+------------------------------------------------------------------+
//|                                                       Buffer.mqh |
//|                        Copyright 2020, MetaQuotes Software Corp. |
//|                             https://mql5.com/ru/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, MetaQuotes Software Corp."
#property link      "https://mql5.com/ru/users/artmedia70"
#property version   "1.00"
#property strict    // Нужно для mql4
//+------------------------------------------------------------------+
//| Включаемые файлы                                                 |
//+------------------------------------------------------------------+
#include "..\..\Objects\BaseObj.mqh"
//+------------------------------------------------------------------+
//| Класс абстрактного индикаторного буфера                          |
//+------------------------------------------------------------------+
class CBuffer : public CBaseObj
  {

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

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

//+------------------------------------------------------------------+
//| Класс абстрактного индикаторного буфера                          |
//+------------------------------------------------------------------+
class CBuffer : public CBaseObj
  {
private:
   long              m_long_prop[BUFFER_PROP_INTEGER_TOTAL];                     // Целочисленные свойства
   double            m_double_prop[BUFFER_PROP_DOUBLE_TOTAL];                    // Вещественные свойства
   string            m_string_prop[BUFFER_PROP_STRING_TOTAL];                    // Строковые свойства
//--- Возвращает индекс массива, по которому фактически расположено (1) double-свойство и (2) string-свойство буфера
   int               IndexProp(ENUM_BUFFER_PROP_DOUBLE property)           const { return(int)property-BUFFER_PROP_INTEGER_TOTAL;                           }
   int               IndexProp(ENUM_BUFFER_PROP_STRING property)           const { return(int)property-BUFFER_PROP_INTEGER_TOTAL-BUFFER_PROP_DOUBLE_TOTAL;  }
//--- Устанавливает тип графического построения
   void              SetDrawType(void);
   
public:
//--- Массив (1) рисуемого индикаторного буфера, (2) буфера цвета
   double            DataArray[];
   double            ColorArray[];

//--- Устанавливает (1) целочисленное, (2) вещественное и (3) строковое свойство буфера
   void              SetProperty(ENUM_BUFFER_PROP_INTEGER property,long value)   { this.m_long_prop[property]=value;                                        }
   void              SetProperty(ENUM_BUFFER_PROP_DOUBLE property,double value)  { this.m_double_prop[this.IndexProp(property)]=value;                      }
   void              SetProperty(ENUM_BUFFER_PROP_STRING property,string value)  { this.m_string_prop[this.IndexProp(property)]=value;                      }
//--- Возвращает из массива свойств (1) целочисленное, (2) вещественное и (3) строковое свойство буфера
   long              GetProperty(ENUM_BUFFER_PROP_INTEGER property)        const { return this.m_long_prop[property];                                       }
   double            GetProperty(ENUM_BUFFER_PROP_DOUBLE property)         const { return this.m_double_prop[this.IndexProp(property)];                     }
   string            GetProperty(ENUM_BUFFER_PROP_STRING property)         const { return this.m_string_prop[this.IndexProp(property)];                     }
//--- Возвращает описание (1) целочисленного, (2) вещественного и (3) строкового свойства буфера
   string            GetPropertyDescription(ENUM_BUFFER_PROP_INTEGER property);
   string            GetPropertyDescription(ENUM_BUFFER_PROP_DOUBLE property);
   string            GetPropertyDescription(ENUM_BUFFER_PROP_STRING property);
//--- Возвращает флаг поддержания буфером данного свойства
   virtual bool      SupportProperty(ENUM_BUFFER_PROP_INTEGER property)          { return true; }
   virtual bool      SupportProperty(ENUM_BUFFER_PROP_DOUBLE property)           { return true; }
   virtual bool      SupportProperty(ENUM_BUFFER_PROP_STRING property)           { return true; }

//--- Сравнивает объекты CBuffer между собой по всем возможным свойствам (для сортировки списков по указанному свойству объекта-буфера)
   virtual int       Compare(const CObject *node,const int mode=0) const;
//--- Сравнивает объекты CBuffer между собой по всем свойствам (для поиска равных объектов-буферов)
   bool              IsEqual(CBuffer* compared_obj) const;
                     
//--- Конструктор по умолчанию
                     CBuffer(void){;}
//protected:
//--- Защищённый параметрический конструктор
                     CBuffer(ENUM_BUFFER_STATUS status_buffer,ENUM_BUFFER_TYPE buffer_type,const uint index_plot,const uint index_base_array);
public:  
//--- Выводит в журнал описание свойств буфера (full_prop=true - все свойства, false - только поддерживаемые)
   void              Print(const bool full_prop=false);
//--- Выводит в журнал краткое описание буфера (реализация в наследниках)
   virtual void      PrintShort(void) {;}
   

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

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

//--- Устанавливает тип графического построения
   void              SetDrawType(void);
   
public:
//--- Массив (1) рисуемого индикаторного буфера, (2) буфера цвета
   double            DataArray[];
   double            ColorArray[];

В зависимости от статуса буфера (а к его статусу будут "привязаны" объекты-наследники) мы будем определять тип графического построения буфера.
Если внимательно посмотреть на константы перечисления статуса буфера

//+------------------------------------------------------------------+
//| Статус абстрактного буфера (по стилю рисования)                  |
//+------------------------------------------------------------------+
enum ENUM_BUFFER_STATUS
  {
   BUFFER_STATUS_NONE,                                         // Нет отрисовки
   BUFFER_STATUS_FILLING,                                      // Цветовая заливка между двумя уровнями (MQL5)
   BUFFER_STATUS_LINE,                                         // Линия
   BUFFER_STATUS_HISTOGRAM,                                    // Гистограмма от нулевой линии
   BUFFER_STATUS_ARROW,                                        // Отрисовка стрелками
   BUFFER_STATUS_SECTION,                                      // Отрезки
   BUFFER_STATUS_HISTOGRAM2,                                   // Гистограмма на двух индикаторных буферах
   BUFFER_STATUS_ZIGZAG,                                       // Стиль Zigzag
   BUFFER_STATUS_BARS,                                         // Отображение в виде баров  (MQL5)
   BUFFER_STATUS_CANDLES,                                      // Отображение в виде свечей (MQL5)
  };
//+------------------------------------------------------------------+

и сравнить порядок следования констант с порядком следования одноимённых (почти) констант в перечислении ENUM_DRAW_TYPE с помощью простого цикла по количеству констант в перечислении;

for(int i=0;i<18;i++)
   Print(EnumToString((ENUM_DRAW_TYPE)i)," = ",i);
2020.04.15 12:51:53.725 DRAW_NONE = 0
2020.04.15 12:51:53.725 DRAW_LINE = 1
2020.04.15 12:51:53.725 DRAW_HISTOGRAM = 2
2020.04.15 12:51:53.725 DRAW_ARROW = 3
2020.04.15 12:51:53.725 DRAW_SECTION = 4
2020.04.15 12:51:53.725 DRAW_HISTOGRAM2 = 5
2020.04.15 12:51:53.725 DRAW_ZIGZAG = 6
2020.04.15 12:51:53.725 DRAW_FILLING = 7
2020.04.15 12:51:53.725 DRAW_BARS = 8
2020.04.15 12:51:53.725 DRAW_CANDLES = 9
2020.04.15 12:51:53.725 DRAW_COLOR_LINE = 10
2020.04.15 12:51:53.725 DRAW_COLOR_HISTOGRAM = 11
2020.04.15 12:51:53.725 DRAW_COLOR_ARROW = 12
2020.04.15 12:51:53.725 DRAW_COLOR_SECTION = 13
2020.04.15 12:51:53.725 DRAW_COLOR_HISTOGRAM2 = 14
2020.04.15 12:51:53.725 DRAW_COLOR_ZIGZAG = 15
2020.04.15 12:51:53.725 DRAW_COLOR_BARS = 16
2020.04.15 12:51:53.725 DRAW_COLOR_CANDLES = 17

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

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

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

//+------------------------------------------------------------------+
//| Устанавливает тип графического построения                        |
//+------------------------------------------------------------------+
void CBuffer::SetDrawType(void)
  {
   ENUM_DRAW_TYPE type=(!this.TypeBuffer() || !this.Status() ? DRAW_NONE : this.Status()==BUFFER_STATUS_FILLING ? DRAW_FILLING : ENUM_DRAW_TYPE(this.Status()+8));
   this.SetProperty(BUFFER_PROP_DRAW_TYPE,type);
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_DRAW_TYPE,type);
  }
//+------------------------------------------------------------------+

Если тип буфера BUFFER_TYPE_CALCULATE (значение 0) или статус буфера BUFFER_STATUS_NONE (значение 0), то стиль рисования устанавливается как "Нет отрисовки", если статус буфера BUFFER_STATUS_FILLING (заполнение цветом), то стиль рисования устанавливается соответствующий. Все остальные значения просто увеличиваются на 8 — именно такое смещение будет указывать на константу цветного стиля рисования.

Ниже приведён пример увеличения значения константы перечисления ENUM_BUFFER_STATUS на 8,
и на какое значение в перечислении ENUM_DRAW_TYPE оно в этом случае попадает:

enum ENUM_BUFFER_STATUS
  {
   BUFFER_STATUS_NONE,       //     0           // Нет отрисовки
   BUFFER_STATUS_FILLING,    //     1           // Цветовая заливка между двумя уровнями (MQL5)
   BUFFER_STATUS_LINE,       //     2 +8 = 10   // Линия
   BUFFER_STATUS_HISTOGRAM,  //     3 +8 = 11   // Гистограмма от нулевой линии
   BUFFER_STATUS_ARROW,      //     4 +8 = 12   // Отрисовка стрелками
   BUFFER_STATUS_SECTION,    //     5 +8 = 13   // Отрезки
   BUFFER_STATUS_HISTOGRAM2, //     6 +8 = 14   // Гистограмма на двух индикаторных буферах
   BUFFER_STATUS_ZIGZAG,     //     7 +8 = 15   // Стиль Zigzag
   BUFFER_STATUS_BARS,       //     8 +8 = 16   // Отображение в виде баров  (MQL5)
   BUFFER_STATUS_CANDLES,    //     9 +8 = 17   // Отображение в виде свечей (MQL5)
  };
2020.04.15 12:51:53.725 DRAW_NONE = 0
2020.04.15 12:51:53.725 DRAW_LINE = 1
2020.04.15 12:51:53.725 DRAW_HISTOGRAM = 2
2020.04.15 12:51:53.725 DRAW_ARROW = 3
2020.04.15 12:51:53.725 DRAW_SECTION = 4
2020.04.15 12:51:53.725 DRAW_HISTOGRAM2 = 5
2020.04.15 12:51:53.725 DRAW_ZIGZAG = 6
2020.04.15 12:51:53.725 DRAW_FILLING = 7
2020.04.15 12:51:53.725 DRAW_BARS = 8
2020.04.15 12:51:53.725 DRAW_CANDLES = 9
2020.04.15 12:51:53.725 DRAW_COLOR_LINE = 10
2020.04.15 12:51:53.725 DRAW_COLOR_HISTOGRAM = 11
2020.04.15 12:51:53.725 DRAW_COLOR_ARROW = 12
2020.04.15 12:51:53.725 DRAW_COLOR_SECTION = 13
2020.04.15 12:51:53.725 DRAW_COLOR_HISTOGRAM2 = 14
2020.04.15 12:51:53.725 DRAW_COLOR_ZIGZAG = 15
2020.04.15 12:51:53.725 DRAW_COLOR_BARS = 16
2020.04.15 12:51:53.725 DRAW_COLOR_CANDLES = 17

Таким образом, у нас от значения статуса буфера задаётся и стиль его рисования.

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

public:  
   
//--- Устанавливает (1) код стрелки, (2) смещение стрелок по вертикали, (3) символ, (4) таймфрейм, (5) флаг активности буфера
//--- (6) количество начальных баров без отрисовки, (7) признак отображения значений построения в окне DataWindow,
//--- (8) сдвиг графического построения индикатора по оси времени, (9) стиль линии отрисовки, (10) толщину линии отрисовки,
//--- (11) количество цветов, (12) цвет отрисовки, (13) пустое значение, (14) имя графической серии, отображаемое в окне DataWindow
   virtual void      SetArrowCode(const uchar code)                  { return;                                                            }
   virtual void      SetArrowShift(const int shift)                  { return;                                                            }
   void              SetSymbol(const string symbol)                  { this.SetProperty(BUFFER_PROP_SYMBOL,symbol);                       }
   void              SetTimeframe(const ENUM_TIMEFRAMES timeframe)   { this.SetProperty(BUFFER_PROP_TIMEFRAME,timeframe);                 }
   void              SetActive(const bool flag)                      { this.SetProperty(BUFFER_PROP_ACTIVE,flag);                         }
   void              SetDrawBegin(const int value);
   void              SetShowData(const bool flag);
   void              SetShift(const int shift);
   void              SetStyle(const ENUM_LINE_STYLE style);
   void              SetWidth(const int width);
   void              SetColorNumbers(const int number);
   void              SetColor(const color colour);
   void              SetEmptyValue(const double value);
   void              SetLabel(const string label);

//--- Возвращает (1) порядковый номер рисуемого буфера, (2) индекс связанного массива, (3) индекс буфера цвета,
//--- (4) индекс первого свободного связанного массива, (5) период данных буфера (таймфрейм) (6) статус буфера,
//--- (7) тип буфера, (8) флаг использования буфера, (9) код стрелки, (10) смещение стрелок для стиля DRAW_ARROW,
//--- (11) Количество начальных баров без отрисовки и значений в DataWindow, (12) тип графического построения,
//--- (13) флаг отображения значений построения в окне DataWindow, (14) сдвиг графического построения индикатора по оси времени,
//--- (15) стиль линии отрисовки, (16) толщину линии отрисовки, (17) количество цветов, (18) цвет отрисовки,
//--- (19) установленное пустое значение, (20) символ буфера, (21) имя индикаторной графической серии, отображаемое в окне DataWindow
   int               IndexPlot(void)                           const { return (int)this.GetProperty(BUFFER_PROP_INDEX_PLOT);              }
   int               IndexBase(void)                           const { return (int)this.GetProperty(BUFFER_PROP_INDEX_BASE);              }
   int               IndexColor(void)                          const { return (int)this.GetProperty(BUFFER_PROP_INDEX_COLOR);             }
   int               IndexNextBuffer(void)                     const { return (int)this.GetProperty(BUFFER_PROP_INDEX_NEXT);              }
   ENUM_TIMEFRAMES   Timeframe(void)                           const { return (ENUM_TIMEFRAMES)this.GetProperty(BUFFER_PROP_TIMEFRAME);   }
   ENUM_BUFFER_STATUS Status(void)                             const { return (ENUM_BUFFER_STATUS)this.GetProperty(BUFFER_PROP_STATUS);   }
   ENUM_BUFFER_TYPE  TypeBuffer(void)                          const { return (ENUM_BUFFER_TYPE)this.GetProperty(BUFFER_PROP_TYPE);       }
   bool              IsActive(void)                            const { return (bool)this.GetProperty(BUFFER_PROP_ACTIVE);                 }
   uchar             ArrowCode(void)                           const { return (uchar)this.GetProperty(BUFFER_PROP_ARROW_CODE);            }
   int               ArrowShift(void)                          const { return (int)this.GetProperty(BUFFER_PROP_ARROW_SHIFT);             }
   int               DrawBegin(void)                           const { return (int)this.GetProperty(BUFFER_PROP_DRAW_BEGIN);              }
   ENUM_DRAW_TYPE    DrawType(void)                            const { return (ENUM_DRAW_TYPE)this.GetProperty(BUFFER_PROP_DRAW_TYPE);    }
   bool              IsShowData(void)                          const { return (bool)this.GetProperty(BUFFER_PROP_SHOW_DATA);              }
   int               Shift(void)                               const { return (int)this.GetProperty(BUFFER_PROP_SHIFT);                   }
   ENUM_LINE_STYLE   LineStyle(void)                           const { return (ENUM_LINE_STYLE)this.GetProperty(BUFFER_PROP_LINE_STYLE);  }
   int               LineWidth(void)                           const { return (int)this.GetProperty(BUFFER_PROP_LINE_WIDTH);              }
   int               NumberColors(void)                        const { return (int)this.GetProperty(BUFFER_PROP_COLOR_INDEXES);           }
   color             Color(void)                               const { return (color)this.GetProperty(BUFFER_PROP_COLOR);                 }
   double            EmptyValue(void)                          const { return this.GetProperty(BUFFER_PROP_EMPTY_VALUE);                  }
   string            Symbol(void)                              const { return this.GetProperty(BUFFER_PROP_SYMBOL);                       }
   string            Label(void)                               const { return this.GetProperty(BUFFER_PROP_LABEL);                        }
   
//--- Возвращает описание (1) статуса буфера, (2) типа буфера, (3) флага использования буфера, (4) флага отображения значений построения в окне DataWindow,
//--- (5) стиля линии отрисовки, (6) установленного пустого значения, (7) типа графического построения, (8) используемого таймфрейма
   string            GetStatusDescription(bool draw_type=false)const;
   string            GetTypeBufferDescription(void)            const;
   string            GetActiveDescription(void)                const;
   string            GetShowDataDescription(void)              const;
   string            GetLineStyleDescription(void)             const;
   string            GetEmptyValueDescription(void)            const;
   string            GetDrawTypeDescription(void)              const;
   string            GetTimeframeDescription(void)             const;

//--- Возвращает размер массива буфера данных
   int               GetDataTotal(void)                        const { return ::ArraySize(this.DataArray);                                }
   
  };
//+------------------------------------------------------------------+

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

Закрытый параметрический конструктор:

//+------------------------------------------------------------------+
//| Закрытый параметрический конструктор                             |
//+------------------------------------------------------------------+
CBuffer::CBuffer(ENUM_BUFFER_STATUS buffer_status,ENUM_BUFFER_TYPE buffer_type,const uint index_plot,const uint index_base_array)
  {
   this.m_type=COLLECTION_BUFFERS_ID;
//--- Сохранение целочисленных свойств
   this.m_long_prop[BUFFER_PROP_STATUS]                        = buffer_status;
   this.m_long_prop[BUFFER_PROP_TYPE]                          = buffer_type;
   this.m_long_prop[BUFFER_PROP_TIMEFRAME]                     = PERIOD_CURRENT;
   this.m_long_prop[BUFFER_PROP_ACTIVE]                        = false;
   this.m_long_prop[BUFFER_PROP_ARROW_CODE]                    = 0xFB;
   this.m_long_prop[BUFFER_PROP_ARROW_SHIFT]                   = 0;
   this.m_long_prop[BUFFER_PROP_DRAW_BEGIN]                    = 0;
   this.m_long_prop[BUFFER_PROP_SHOW_DATA]                     = (buffer_type>BUFFER_TYPE_CALCULATE ? true : false);
   this.m_long_prop[BUFFER_PROP_SHIFT]                         = 0;
   this.m_long_prop[BUFFER_PROP_LINE_STYLE]                    = STYLE_SOLID;
   this.m_long_prop[BUFFER_PROP_LINE_WIDTH]                    = 1;
   this.m_long_prop[BUFFER_PROP_COLOR_INDEXES]                 = 1;
   this.m_long_prop[BUFFER_PROP_COLOR]                         = clrRed;
   this.m_long_prop[BUFFER_PROP_NUM_DATAS]                     = 1;
   this.m_long_prop[BUFFER_PROP_INDEX_PLOT]                    = index_plot;
   this.m_long_prop[BUFFER_PROP_INDEX_BASE]                    = index_base_array;
   this.m_long_prop[BUFFER_PROP_INDEX_COLOR]                   = this.GetProperty(BUFFER_PROP_INDEX_BASE)+this.GetProperty(BUFFER_PROP_NUM_DATAS);
   this.m_long_prop[BUFFER_PROP_INDEX_NEXT]                    = this.GetProperty(BUFFER_PROP_INDEX_COLOR)+1;
   this.SetDrawType();
//--- Сохранение вещественных свойств
   this.m_double_prop[this.IndexProp(BUFFER_PROP_EMPTY_VALUE)] = EMPTY_VALUE;
//--- Сохранение строковых свойств
   this.m_string_prop[this.IndexProp(BUFFER_PROP_SYMBOL)]      = ::Symbol();
   this.m_string_prop[this.IndexProp(BUFFER_PROP_LABEL)]       = (this.TypeBuffer()>BUFFER_TYPE_CALCULATE ? "Buffer "+(string)this.IndexPlot() : NULL);

//--- Связывание индикаторных буферов с массивами
   ::SetIndexBuffer((int)this.GetProperty(BUFFER_PROP_INDEX_BASE),this.DataArray,INDICATOR_DATA);
   ::SetIndexBuffer((int)this.GetProperty(BUFFER_PROP_INDEX_COLOR),this.ColorArray,INDICATOR_COLOR_INDEX);

//--- Установка целочисленных параметров буфера
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_DRAW_TYPE,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_DRAW_TYPE));
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_ARROW,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_ARROW_CODE));
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_ARROW_SHIFT,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_ARROW_SHIFT));
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_DRAW_BEGIN,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_DRAW_BEGIN));
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_SHOW_DATA,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_SHOW_DATA));
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_SHIFT,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_SHIFT));
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LINE_STYLE,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_LINE_STYLE));
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LINE_WIDTH,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_LINE_WIDTH));
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_COLOR_INDEXES,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_COLOR_INDEXES));
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LINE_COLOR,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_COLOR));
//--- Установка вещественных параметров буфера
   ::PlotIndexSetDouble((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_EMPTY_VALUE,this.GetProperty(BUFFER_PROP_EMPTY_VALUE));
//--- Установка строковых параметров буфера
   ::PlotIndexSetString((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LABEL,this.GetProperty(BUFFER_PROP_LABEL));
  }
//+------------------------------------------------------------------+

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

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

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

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

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

//+------------------------------------------------------------------+
//| Методы класса                                                    |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//|Сравнивает объекты CBuffer между собой по всем возможным свойствам|
//+------------------------------------------------------------------+
int CBuffer::Compare(const CObject *node,const int mode=0) const
  {
   const CBuffer *compared_obj=node;
//--- сравнение целочисленных свойств двух буферов
   if(mode<BUFFER_PROP_INTEGER_TOTAL)
     {
      long value_compared=compared_obj.GetProperty((ENUM_BUFFER_PROP_INTEGER)mode);
      long value_current=this.GetProperty((ENUM_BUFFER_PROP_INTEGER)mode);
      return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0);
     }
//--- сравнение вещественных свойств двух буферов
   else if(mode<BUFFER_PROP_INTEGER_TOTAL+BUFFER_PROP_DOUBLE_TOTAL)
     {
      double value_compared=compared_obj.GetProperty((ENUM_BUFFER_PROP_DOUBLE)mode);
      double value_current=this.GetProperty((ENUM_BUFFER_PROP_DOUBLE)mode);
      return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0);
     }
//--- сравнение строковых свойств двух буферов
   else if(mode<BUFFER_PROP_INTEGER_TOTAL+BUFFER_PROP_DOUBLE_TOTAL+BUFFER_PROP_STRING_TOTAL)
     {
      string value_compared=compared_obj.GetProperty((ENUM_BUFFER_PROP_STRING)mode);
      string value_current=this.GetProperty((ENUM_BUFFER_PROP_STRING)mode);
      return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0);
     }
   return 0;
  }
//+------------------------------------------------------------------+
//| Сравнивает объекты CBuffer между собой по всем свойствам         |
//+------------------------------------------------------------------+
bool CBuffer::IsEqual(CBuffer *compared_obj) const
  {
   int beg=0, end=BUFFER_PROP_INTEGER_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_BUFFER_PROP_INTEGER prop=(ENUM_BUFFER_PROP_INTEGER)i;
      if(this.GetProperty(prop)!=compared_obj.GetProperty(prop)) return false; 
     }
   beg=end; end+=BUFFER_PROP_DOUBLE_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_BUFFER_PROP_DOUBLE prop=(ENUM_BUFFER_PROP_DOUBLE)i;
      if(this.GetProperty(prop)!=compared_obj.GetProperty(prop)) return false; 
     }
   beg=end; end+=BUFFER_PROP_STRING_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_BUFFER_PROP_STRING prop=(ENUM_BUFFER_PROP_STRING)i;
      if(this.GetProperty(prop)!=compared_obj.GetProperty(prop)) return false; 
     }
   return true;
  }
//+------------------------------------------------------------------+

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

//+------------------------------------------------------------------+
//| Выводит в журнал свойства буфера                                 |
//+------------------------------------------------------------------+
void CBuffer::Print(const bool full_prop=false)
  {
   ::Print("============= ",
           CMessage::Text(MSG_LIB_PARAMS_LIST_BEG),": ",
           this.GetTypeBufferDescription(),"[",(string)this.IndexPlot(),"] \"",this.GetStatusDescription(true),"\"",
           " =================="
          );
   int beg=0, end=BUFFER_PROP_INTEGER_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_BUFFER_PROP_INTEGER prop=(ENUM_BUFFER_PROP_INTEGER)i;
      if(!full_prop && !this.SupportProperty(prop)) continue;
      ::Print(this.GetPropertyDescription(prop));
     }
   ::Print("------");
   beg=end; end+=BUFFER_PROP_DOUBLE_TOTAL;
   for(int i=beg; i<end; i++)
     {

      ENUM_BUFFER_PROP_DOUBLE prop=(ENUM_BUFFER_PROP_DOUBLE)i;
      if(!full_prop && !this.SupportProperty(prop)) continue;
      ::Print(this.GetPropertyDescription(prop));
     }
   ::Print("------");
   beg=end; end+=BUFFER_PROP_STRING_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_BUFFER_PROP_STRING prop=(ENUM_BUFFER_PROP_STRING)i;
      if(!full_prop && !this.SupportProperty(prop)) continue;
      ::Print(this.GetPropertyDescription(prop));
     }
   ::Print("================== ",
           CMessage::Text(MSG_LIB_PARAMS_LIST_END),": ",
           this.GetTypeBufferDescription(),"[",(string)this.IndexPlot(),"] \"",this.GetStatusDescription(true),"\"",
           " ==================\n"
          );
  }
//+------------------------------------------------------------------+

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

//+------------------------------------------------------------------+
//| Возвращает описание целочисленного свойства буфера               |
//+------------------------------------------------------------------+
string CBuffer::GetPropertyDescription(ENUM_BUFFER_PROP_INTEGER property)
  {
   return
     (
      property==BUFFER_PROP_INDEX_PLOT    ?  CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_INDEX_PLOT)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==BUFFER_PROP_NUM_DATAS     ?  CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_NUM_DATAS)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==BUFFER_PROP_INDEX_NEXT    ?  CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_INDEX_NEXT)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==BUFFER_PROP_TIMEFRAME     ?  CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_TIMEFRAME)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+this.GetTimeframeDescription()
         )  :
      property==BUFFER_PROP_STATUS        ?  CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_STATUS)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+this.GetStatusDescription()
         )  :
      property==BUFFER_PROP_TYPE          ?  CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_TYPE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+this.GetTypeBufferDescription()
         )  :
      property==BUFFER_PROP_ACTIVE        ?  CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_ACTIVE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+this.GetActiveDescription()
         )  :
      property==BUFFER_PROP_ARROW_CODE    ?  CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_ARROW_CODE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==BUFFER_PROP_ARROW_SHIFT   ?  CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_ARROW_SHIFT)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==BUFFER_PROP_DRAW_BEGIN    ?  CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_DRAW_BEGIN)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==BUFFER_PROP_DRAW_TYPE     ?  CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_DRAW_TYPE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+this.GetDrawTypeDescription()
         )  :
      property==BUFFER_PROP_SHOW_DATA     ?  CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_SHOW_DATA)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+this.GetShowDataDescription()
         )  :
      property==BUFFER_PROP_SHIFT         ?  CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_SHIFT)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==BUFFER_PROP_LINE_STYLE    ?  CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_LINE_STYLE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+this.GetLineStyleDescription()
         )  :
      property==BUFFER_PROP_LINE_WIDTH    ?  CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_LINE_WIDTH)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==BUFFER_PROP_COLOR_INDEXES ?  CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_COLOR_NUM)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==BUFFER_PROP_COLOR         ?  CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_COLOR)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+ColorToString(this.Color(),true)
         )  :
      property==BUFFER_PROP_INDEX_BASE    ?  CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_INDEX_BASE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==BUFFER_PROP_NUM_DATAS ?  CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_NUM_DATAS)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==BUFFER_PROP_INDEX_COLOR   ?  CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_INDEX_COLOR)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==BUFFER_PROP_INDEX_NEXT         ?  CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_INDEX_NEXT)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+ColorToString(this.Color(),true)
         )  :
      ""
     );
  }
//+------------------------------------------------------------------+
//| Возвращает описание вещественного свойства буфера                |
//+------------------------------------------------------------------+
string CBuffer::GetPropertyDescription(ENUM_BUFFER_PROP_DOUBLE property)
  {
   return
     (
      property==BUFFER_PROP_EMPTY_VALUE    ?  CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_EMPTY_VALUE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+this.GetEmptyValueDescription()
         )  :
      ""
     );
  }
//+------------------------------------------------------------------+
//| Возвращает описание строкового свойства буфера                   |
//+------------------------------------------------------------------+
string CBuffer::GetPropertyDescription(ENUM_BUFFER_PROP_STRING property)
  {
   return
     (
      property==BUFFER_PROP_SYMBOL    ?  CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_SYMBOL)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+this.Symbol()
         )  :
      property==BUFFER_PROP_LABEL    ?  CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_LABEL)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.Label()==NULL || this.Label()=="" ? CMessage::Text(MSG_LIB_PROP_NOT_SET) : this.Label())
         )  :
      ""
     );
  }
//+------------------------------------------------------------------+

Рассмотрим реализацию остальных добавленных методов в публичную секцию класса.

Метод, возвращающий описание статуса буфера:

//+------------------------------------------------------------------+
//| Возвращает описание статуса буфера                               |
//+------------------------------------------------------------------+
string CBuffer::GetStatusDescription(bool draw_type=false) const
  {
   string type=
     (
      this.Status()==BUFFER_STATUS_NONE         ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_STATUS_NONE)         :
      this.Status()==BUFFER_STATUS_ARROW        ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_STATUS_ARROW)        :
      this.Status()==BUFFER_STATUS_BARS         ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_STATUS_BARS)         :
      this.Status()==BUFFER_STATUS_CANDLES      ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_STATUS_CANDLES)      :
      this.Status()==BUFFER_STATUS_FILLING      ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_STATUS_FILLING)      :
      this.Status()==BUFFER_STATUS_HISTOGRAM    ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_STATUS_HISTOGRAM)    :
      this.Status()==BUFFER_STATUS_HISTOGRAM2   ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_STATUS_HISTOGRAM2)   :
      this.Status()==BUFFER_STATUS_LINE         ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_STATUS_LINE)         :
      this.Status()==BUFFER_STATUS_SECTION      ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_STATUS_SECTION)      :
      this.Status()==BUFFER_STATUS_ZIGZAG       ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_STATUS_ZIGZAG)       :
      "Unknown"
     );
   return(!draw_type ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_STATUS_NAME)+" \""+type+"\"" : type);
  }
//+------------------------------------------------------------------+

Так как статус буфера заодно определяет и стиль рисования, то в метод передаётся флаг, указывающий на то, как вернуть описание:
— если запрошен статус буфера (draw_type установлен в false), то будет возвращён статус буфера в виде

Статус буфера: Индикаторный буфер с типом графического построения "Линия"

— если же запрошен тип рисования (draw_type установлен в true), то будет возвращён тип рисования в виде

Тип графического построения: Линия

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

//+------------------------------------------------------------------+
//| Возвращает описание типа буфера                                  |
//+------------------------------------------------------------------+
string CBuffer::GetTypeBufferDescription(void) const
  {
   return
     (
      this.TypeBuffer()==BUFFER_TYPE_DATA       ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_TYPE_DATA)        :
      this.TypeBuffer()==BUFFER_TYPE_CALCULATE  ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_TYPE_CALCULATE)   :
      "Unknown"
     );
  }
//+------------------------------------------------------------------+
//| Возвращает описание флага использования буфера                   |
//+------------------------------------------------------------------+
string CBuffer::GetActiveDescription(void) const
  {
   return(this.IsActive() ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO));
  }
//+---------------------------------------------------------------------+
//|Возвращает описание отображения значений построения в окне DataWindow|
//+---------------------------------------------------------------------+
string CBuffer::GetShowDataDescription(void) const
  {
   return(this.IsShowData() ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO));
  }
//+------------------------------------------------------------------+
//| Возвращает описание стиля линии отрисовки                        |
//+------------------------------------------------------------------+
string CBuffer::GetLineStyleDescription(void) const
  {
   return
     (
      this.LineStyle()==STYLE_SOLID       ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_STYLE_SOLID)      :
      this.LineStyle()==STYLE_DASH        ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_STYLE_DASH)       :
      this.LineStyle()==STYLE_DOT         ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_STYLE_DOT)        :
      this.LineStyle()==STYLE_DASHDOT     ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_STYLE_DASHDOT)    :
      this.LineStyle()==STYLE_DASHDOTDOT  ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_STYLE_DASHDOTDOT) :
      "Unknown"
     );
  }
//+------------------------------------------------------------------+
//| Возвращает описание установленного пустого значения              |
//+------------------------------------------------------------------+
string CBuffer::GetEmptyValueDescription(void) const
  {
   return(this.EmptyValue()<EMPTY_VALUE ? ::DoubleToString(this.EmptyValue(),(this.EmptyValue()==0 ? 1 : 8)) : "EMPTY_VALUE");
  }
//+------------------------------------------------------------------+
//| Возвращает описание типа графического построения                 |
//+------------------------------------------------------------------+
string CBuffer::GetDrawTypeDescription(void) const
  {
   return this.GetStatusDescription(true);
  } 
//+------------------------------------------------------------------+
//| Возвращает описание используемого таймфрейма                     |
//+------------------------------------------------------------------+
string CBuffer::GetTimeframeDescription(void) const
  {
   string timeframe=TimeframeDescription(this.Timeframe());
   return(this.Timeframe()==PERIOD_CURRENT ? CMessage::Text(MSG_LIB_TEXT_PERIOD_CURRENT)+" ("+timeframe+")" : timeframe);
  } 
//+------------------------------------------------------------------+

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

//+------------------------------------------------------------------+
//| Устанавливает количество начальных баров                         |
//| без отрисовки и значений в DataWindow                            |
//+------------------------------------------------------------------+
void CBuffer::SetDrawBegin(const int value)
  {
   this.SetProperty(BUFFER_PROP_DRAW_BEGIN,value);
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_DRAW_BEGIN,value);
  }
//+------------------------------------------------------------------+
//| Устанавливает признак отображения                                |
//| значений построения в окне DataWindow                            |
//+------------------------------------------------------------------+
void CBuffer::SetShowData(const bool flag)
  {
   this.SetProperty(BUFFER_PROP_SHOW_DATA,flag);
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_SHOW_DATA,flag);
  }
//+------------------------------------------------------------------+
//| Устанавливает сдвиг графического построения индикатора           |
//+------------------------------------------------------------------+
void CBuffer::SetShift(const int shift)
  {
   this.SetProperty(BUFFER_PROP_SHIFT,shift);
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_SHIFT,shift);
  }
//+------------------------------------------------------------------+
//| Устанавливает стиль линии отрисовки                              |
//+------------------------------------------------------------------+
void CBuffer::SetStyle(const ENUM_LINE_STYLE style)
  {
   this.SetProperty(BUFFER_PROP_LINE_STYLE,style);
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LINE_STYLE,style);
  }
//+------------------------------------------------------------------+
//| Устанавливает толщину линии отрисовки                            |
//+------------------------------------------------------------------+
void CBuffer::SetWidth(const int width)
  {
   this.SetProperty(BUFFER_PROP_LINE_WIDTH,width);
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LINE_WIDTH,width);
  }
//+------------------------------------------------------------------+
//| Устанавливает количество цветов                                  |
//+------------------------------------------------------------------+
void CBuffer::SetColorNumbers(const int number)
  {
   this.SetProperty(BUFFER_PROP_COLOR_INDEXES,number);
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_COLOR_INDEXES,number);
  }
//+------------------------------------------------------------------+
//| Устанавливает цвет отрисовки                                     |
//+------------------------------------------------------------------+
void CBuffer::SetColor(const color colour)
  {
   this.SetProperty(BUFFER_PROP_COLOR,colour);
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LINE_COLOR,colour);
  }
//+------------------------------------------------------------------+
//| Устанавливает "пустое" значение для построения,                  |
//| для которого нет отрисовки                                       |
//+------------------------------------------------------------------+
void CBuffer::SetEmptyValue(const double value)
  {
   this.SetProperty(BUFFER_PROP_EMPTY_VALUE,value);
   ::PlotIndexSetDouble((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_EMPTY_VALUE,value);
  }
//+------------------------------------------------------------------+
//| Устанавливает цвет отрисовки                                     |
//+------------------------------------------------------------------+
void CBuffer::SetLabel(const string label)
  {
   this.SetProperty(BUFFER_PROP_LABEL,label);
   ::PlotIndexSetString((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LABEL,label);
  }
//+------------------------------------------------------------------+

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

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

Проверка создания объектов-буферов в индикаторе

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

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

Кроме того, функции копирования данных из OnCalculate() в структуру цен библиотеки мы перенесём их кода индикатора в файл сервисных функций библиотеки. Откроем файл \MQL5\Include\DoEasy\Services\DELib.mqh и впишем в него две функции:

//+------------------------------------------------------------------+
//| Копирование данных из OnCalculate() первой формы в структуру     |
//+------------------------------------------------------------------+
void CopyData(const int rates_total,
              const int prev_calculated,
              const int begin,
              const double &price[])
  {
//--- Получим флаг индексации массива как в таймсерии, и если нет, то
//--- установим направление индексации массиву как в таймсерии
   bool as_series_price=ArrayGetAsSeries(price);
   if(!as_series_price)
      ArraySetAsSeries(price,true);
//--- Скопируем нулевой бар массива в структуру данных OnCalculate() SDataCalculate
   rates_data.rates_total=rates_total;
   rates_data.prev_calculated=prev_calculated;
   rates_data.begin=begin;
   rates_data.price=price[0];
//--- Вернём массивум его изначальное направление индексации
   if(!as_series_price)
      ArraySetAsSeries(price,false);
  }
//+------------------------------------------------------------------+
//| Копирование данных из OnCalculate() второй формы в структуру     |
//+------------------------------------------------------------------+
void CopyData(const int rates_total,
              const int prev_calculated,
              const datetime &time[],
              const double &open[],
              const double &high[],
              const double &low[],
              const double &close[],
              const long &tick_volume[],
              const long &volume[],
              const int &spread[])
  {
//--- Получим флаги индексации массивов как в таймсерии, и если нет, то
//--- установим направление индексации массивам как в таймсерии
   bool as_series_time=ArrayGetAsSeries(time);
   if(!as_series_time)
      ArraySetAsSeries(time,true);
   bool as_series_open=ArrayGetAsSeries(open);
   if(!as_series_open)
      ArraySetAsSeries(open,true);
   bool as_series_high=ArrayGetAsSeries(high);
   if(!as_series_high)
      ArraySetAsSeries(high,true);
   bool as_series_low=ArrayGetAsSeries(low);
   if(!as_series_low)
      ArraySetAsSeries(low,true);
   bool as_series_close=ArrayGetAsSeries(close);
   if(!as_series_close)
      ArraySetAsSeries(close,true);
   bool as_series_tick_volume=ArrayGetAsSeries(tick_volume);
   if(!as_series_tick_volume)
      ArraySetAsSeries(tick_volume,true);
   bool as_series_volume=ArrayGetAsSeries(volume);
   if(!as_series_volume)
      ArraySetAsSeries(volume,true);
   bool as_series_spread=ArrayGetAsSeries(spread);
   if(!as_series_spread)
      ArraySetAsSeries(spread,true);
//--- Скопируем нулевой бар массивов в структуру данных OnCalculate() SDataCalculate
   rates_data.rates_total=rates_total;
   rates_data.prev_calculated=prev_calculated;
   rates_data.rates.time=time[0];
   rates_data.rates.open=open[0];
   rates_data.rates.high=high[0];
   rates_data.rates.low=low[0];
   rates_data.rates.close=close[0];
   rates_data.rates.tick_volume=tick_volume[0];
   rates_data.rates.real_volume=(#ifdef __MQL5__ volume[0] #else 0 #endif);
   rates_data.rates.spread=(#ifdef __MQL5__ spread[0] #else 0 #endif);
//--- Вернём массивам их изначальное направление индексации
   if(!as_series_time)
      ArraySetAsSeries(time,false);
   if(!as_series_open)
      ArraySetAsSeries(open,false);
   if(!as_series_high)
      ArraySetAsSeries(high,false);
   if(!as_series_low)
      ArraySetAsSeries(low,false);
   if(!as_series_close)
      ArraySetAsSeries(close,false);
   if(!as_series_tick_volume)
      ArraySetAsSeries(tick_volume,false);
   if(!as_series_volume)
      ArraySetAsSeries(volume,false);
   if(!as_series_spread)
      ArraySetAsSeries(spread,false);
  }
//+------------------------------------------------------------------+

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

"Заголовок" индикатора будет таким:

//+------------------------------------------------------------------+
//|                                             TestDoEasyPart42.mq5 |
//|                        Copyright 2020, MetaQuotes Software Corp. |
//|                             https://mql5.com/ru/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, MetaQuotes Software Corp."
#property link      "https://mql5.com/ru/users/artmedia70"
#property version   "1.00"
//--- includes
#include <DoEasy\Engine.mqh>
#include <DoEasy\Objects\Indicators\Buffer.mqh>
//--- properties
#property indicator_chart_window
#property indicator_buffers 4
#property indicator_plots   2

//--- classes

//--- enums

//--- defines

//--- structures

//--- input variables
/*sinput*/ ENUM_SYMBOLS_MODE  InpModeUsedSymbols=  SYMBOLS_MODE_DEFINES;            // Mode of used symbols list
sinput   string               InpUsedSymbols    =  "EURUSD,AUDUSD,EURAUD,EURGBP,EURCAD,EURJPY,EURUSD,GBPUSD,NZDUSD,USDCAD,USDJPY";  // List of used symbols (comma - separator)
sinput   ENUM_TIMEFRAMES_MODE InpModeUsedTFs    =  TIMEFRAMES_MODE_LIST;            // Mode of used timeframes list
sinput   string               InpUsedTFs        =  "M1,M5,M15,M30,H1,H4,D1,W1,MN1"; // List of used timeframes (comma - separator)
sinput   bool                 InpUseSounds      =  true; // Use sounds
//--- indicator buffers
CArrayObj      list_buffers;                    // Временный список для хранения двух объектов-буферов
//--- global variables
CEngine        engine;                          // Главный объект библиотеки CEngine
string         prefix;                          // Префикс имён графических объектов
int            min_bars;                        // Минимальное количество баров для расчёта индикатора
int            used_symbols_mode;               // Режим работы с символами
string         array_used_symbols[];            // Массив для передачи в библиотеку используемых символов
string         array_used_periods[];            // Массив для передачи в библиотеку используемых таймфреймов
//+------------------------------------------------------------------+

Здесь мы указываем компилятору создать индикатор в отдельном окне и установить для него четыре буфера (два рисуемых и два буфера цвета).
Остальные параметры буферов будут устанавливаться во время и после создания объектов-индикаторных буферов.
В качестве индикаторного буфера впишем динамический массив указателей CArrayObj — в него будем складывать создаваемые буферы.

При работе с объектами-буферами нам не нужно объявлять double-массивы для назначения их в качестве индикаторных буферов — всё находится внутри создаваемых объектов-буферов, и назначается во время их создания в обработчике OnInit() индикатора:

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Инициализация библиотеки DoEasy
   OnInitDoEasy();

//--- Установка глобальных переменных индикатора
   prefix=engine.Name()+"_";
   //--- Получаем индекс максимального используемого таймфрейма в массиве,
   //--- рассчитываем количество баров текущего периода, умещающихся в максимальном используемом периоде
   //--- Используем полученное значение если оно больше 2, иначе используем 2
   int index=ArrayMaximum(ArrayUsedTimeframes);
   int num_bars=NumberBarsInTimeframe(ArrayUsedTimeframes[index]);
   min_bars=(index>WRONG_VALUE ? (num_bars>2 ? num_bars : 2) : 2);

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

//--- Создание панели кнопок


//--- Проверка воспроизведения стандартного звука по макроподстановкам
   engine.PlaySoundByDescription(SND_OK);
//--- Ждём 600 милисекунд
   engine.Pause(600);
   engine.PlaySoundByDescription(SND_NEWS);

//--- indicator buffers mapping
  
//--- Создаём два объекта-буфера
   CBuffer *buffer0=new CBuffer(BUFFER_STATUS_ARROW,BUFFER_TYPE_DATA, 0, 0);
   CBuffer *buffer1=new CBuffer(BUFFER_STATUS_LINE,BUFFER_TYPE_DATA, 1, buffer0.IndexNextBuffer());
//--- Второму буферу задаём значения не по умолчанию для "пустого" значения и цвета
   buffer1.SetEmptyValue(0);
   buffer1.SetColor(clrBlue);
//--- Добавляем оба буфера в список индикаторных буферов
   list_buffers.Add(buffer0);
   list_buffers.Add(buffer1);
//--- Распечатаем данные созданных буферов
   buffer0.Print();
   buffer1.Print();
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+

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

Далее, мы второму созданному буферу назначили в качестве "пустого" значения 0 (ноль) и цвет отрисовки линии "Синий" (чтобы проверить как устанавливаются значения в свойства объекта-буфера).
Оба буфера добавляем в ранее объявленный список list_buffers и наконец распечатываем в журнал все свойства обоих только что созданных буферов.

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

Обработчик OnCalculate(), взятый из прошлого тестового индикатора, мы тоже "подчистим", оставив лишь самое необходимое для теста:
а нужно нам лишь проверить успешность создания двух объектов-буферов и правильного их назначения в качестве индикаторных буферов.

Как мы это можем сделать?
Создание буферов мы проверяем в OnInit() — когда распечатываем все свойства каждого созданного буфера в журнал. А успешное назначение этих созданных объектов в качестве индикаторных буферов мы можем проверить только в OnCalculate(). Для этого достаточно сравнить размер массивов, использующихся в объектах как индикаторные буферы, с количеством баров на символе.

Как только мы назначаем какой-то массив в качестве индикаторного буфера, то исполняемая подсистема терминала берёт эти массивы под свою опеку и сама распределяет для них память и управляет размером этих массивов. Поэтому нам достаточно получить из списка list_buffers  каждый из объектов и сравнить размер его массива, назначенного в качестве буфера, с величиной rates_total в OnCalculate(). Равенство этих значений укажет нам на то, что подсистема терминала взяла под свой контроль массивы из объектов-буферов.

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

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//+------------------------------------------------------------------+
//| OnCalculate Блок кода для работы с библиотекой:                  |
//+------------------------------------------------------------------+
   
//--- Передача в структуру цен текущих данных массивов из OnCalculate()
   CopyData(rates_total,prev_calculated,time,open,high,low,close,tick_volume,volume,spread);

//--- Проверка на минимальное количество баров для расчёта
   if(rates_total<min_bars || Point()==0) return 0;
   
//--- Обработка события Calculate в библиотеке
//--- Если метод OnCalculate() библиотеки вернул ноль - значит не все таймсерии готовы - уходим до следкющего тика
   if(engine.0)
      return 0;
   
//--- Если работа в тестере
   if(MQLInfoInteger(MQL_TESTER)) 
     {
      engine.OnTimer(rates_data);   // Работа в таймере библиотеки
      EventsHandling();             // Работа с событиями библиотеки
     }
//+------------------------------------------------------------------+
//| OnCalculate Блок кода для работы с индикатором:                  |
//+------------------------------------------------------------------+
//--- Установка массивов OnCalculate как таймсерий
   ArraySetAsSeries(open,true);
   ArraySetAsSeries(high,true);
   ArraySetAsSeries(low,true);
   ArraySetAsSeries(close,true);
   ArraySetAsSeries(time,true);
   ArraySetAsSeries(tick_volume,true);
   ArraySetAsSeries(volume,true);
   ArraySetAsSeries(spread,true);

//--- Проверка и расчёт количества просчитываемых баров
//--- Если limit = 0, значит новых баров нет - рассчитываем текущий
//--- Если limit = 1, значит появился новый бар - рассчитываем первый и текущий
//--- Если limit > 1, значит первый запуск, или есть изменения в истории - полный перерасчёт всех данных
   int limit=rates_total-prev_calculated;
   
//--- Перерасчёт всей истории
   if(limit>1)
     {
      //--- В цикле по количеству буферов в списке
      for(int i=0;i<list_buffers.Total();i++)
        {
         //--- получаем очередной буфер и выводим в журнал тип его графического построения
         //--- и размер назначенного буферу double-массива (если всё верно, то размер будет равен rates_total)
         CBuffer *buff=list_buffers.At(i);
         Print(buff.Label()," type = ",EnumToString(buff.DrawType()),", data total = ",buff.GetDataTotal(),", rates_total=",rates_total);
        }
      
      limit=rates_total-1;
     }
//--- Подготовка данных

//--- Расчёт индикатора
   for(int i=limit; i>WRONG_VALUE && !IsStopped(); i--)
     {
      CalculateSeries(i,time[i]);
     }
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+

Полный код тестового индикатора можно посмотреть в прикреплённых в конце статьи файлах.

Скомпилируем индикатор и запустим его на графике, предварительно установив такие параметры:


В журнал будут выведены записи:

Счёт 8550475: Artyom Trishkin (MetaQuotes Software Corp.) 10425.23 USD, 1:100, Hedge, Демонстрационный счёт MetaTrader 5
--- Инициализация библиотеки "DoEasy" ---
Работа только с текущим символом. Количество используемых символов: 1
"EURUSD"
Работа с заданным списком таймфреймов:
"M5"  "M15" "M30" "H1" 
Таймсерия символа EURUSD: 
- Таймсерия "EURUSD" M5: Запрошено: 1000, Фактически: 1000, Создано: 1000, На сервере: 3684
- Таймсерия "EURUSD" M15: Запрошено: 1000, Фактически: 1000, Создано: 1000, На сервере: 3042
- Таймсерия "EURUSD" M30: Запрошено: 1000, Фактически: 0, Создано: 0, На сервере: 0
- Таймсерия "EURUSD" H1: Запрошено: 1000, Фактически: 1000, Создано: 1000, На сервере: 6240
Время инициализации библиотеки: 00:00:00.156
 
============= Начало списка параметров: Цветной буфер данных[0] "Отрисовка стрелками" ==================
Порядковый номер рисуемого буфера: 0
Статус буфера: Индикаторный буфер с типом графического построения "Отрисовка стрелками"
Тип буфера: Цветной буфер данных
Период данных буфера (таймфрейм): Текущий период графика (M30)
Активен: Нет
Код стрелки: 251
Смещение стрелок по вертикали: 0
Количество начальных баров без отрисовки и значений в DataWindow: 0
Отображение значений построения в окне DataWindow: Да
Тип графического построения: Отрисовка стрелками
Сдвиг графического построения индикатора по оси времени в барах: 0
Стиль линии отрисовки: Сплошная линия
Толщина линии отрисовки: 1
Количество цветов: 1
Цвет отрисовки: clrRed
Количество буферов данных: 1
Индекс базового буфера данных: 0
Индекс буфера цвета: 1
Индекс массива для назначения следующим индикаторным буфером: 2
------
Пустое значение для построения, для которого нет отрисовки: EMPTY_VALUE
------
Символ буфера: EURUSD
Имя индикаторной графической серии, отображаемое в окне DataWindow: Buffer 0
================== Конец списка параметров: Цветной буфер данных[0] "Отрисовка стрелками" ==================
 
============= Начало списка параметров: Цветной буфер данных[1] "Линия" ==================
Порядковый номер рисуемого буфера: 1
Статус буфера: Индикаторный буфер с типом графического построения "Линия"
Тип буфера: Цветной буфер данных
Период данных буфера (таймфрейм): Текущий период графика (M30)
Активен: Нет
Код стрелки: 251
Смещение стрелок по вертикали: 0
Количество начальных баров без отрисовки и значений в DataWindow: 0
Отображение значений построения в окне DataWindow: Да
Тип графического построения: Линия
Сдвиг графического построения индикатора по оси времени в барах: 0
Стиль линии отрисовки: Сплошная линия
Толщина линии отрисовки: 1
Количество цветов: 1
Цвет отрисовки: clrBlue
Количество буферов данных: 1
Индекс базового буфера данных: 2
Индекс буфера цвета: 3
Индекс массива для назначения следующим индикаторным буфером: 4
------
Пустое значение для построения, для которого нет отрисовки: 0.0
------
Символ буфера: EURUSD
Имя индикаторной графической серии, отображаемое в окне DataWindow: Buffer 1
================== Конец списка параметров: Цветной буфер данных[1] "Линия" ==================
 
Таймсерия "EURUSD" M30 создана успешно:
- Таймсерия "EURUSD" M30: Запрошено: 1000, Фактически: 1000, Создано: 1000, На сервере: 5111

Buffer 0 type = DRAW_COLOR_ARROW, data total = 5111, rates_total=5111
Buffer 1 type = DRAW_COLOR_LINE, data total = 5111, rates_total=5111

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

Чтобы ещё раз в этом убедиться, достаточно открыть свойства индикатора (Ctrl+I) и посмотреть вкладку "Цвета":


Обоим буферам индикатора заданы названия и цвет. А мы ни названия, ни цвет нигде не указывали, кроме как создаваемые в конструкторе класса объекта-буфера по умолчанию, а для второго буфера — переназначили цвет на синий после его создания в OnInit().

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


Что дальше

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

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

К содержанию

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

Работа с таймсериями в библиотеке DoEasy (Часть 35): Объект "Бар" и список-таймсерия символа
Работа с таймсериями в библиотеке DoEasy (Часть 36): Объект таймсерий всех используемых периодов символа
Работа с таймсериями в библиотеке DoEasy (Часть 37): Коллекция таймсерий - база данных таймсерий по символам и периодам
Работа с таймсериями в библиотеке DoEasy (Часть 38): Коллекция таймсерий - реалтайм обновление и доступ к данным из программы
Работа с таймсериями в библиотеке DoEasy (Часть 39): Индикаторы на основе библиотеки - подготовка данных и события таймсерий
Работа с таймсериями в библиотеке DoEasy (Часть 40): Индикаторы на основе библиотеки - реалтайм обновление данных
Работа с таймсериями в библиотеке DoEasy (Часть 41): Пример мультисимвольного мультипериодного индикатора