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

24 апреля 2020, 08:38
Artyom Trishkin
0
839

Содержание


Концепция

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

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

   MSG_LIB_SYS_FAILED_CREATE_BAR_OBJ,                 // Не удалось создать объект \"Бар\"
   MSG_LIB_SYS_FAILED_SYNC_DATA,                      // Не удалось синхронизировать данные с сервером
   MSG_LIB_SYS_FAILED_DRAWING_ARRAY_RESIZE,           // Не удалось изменить размер массива рисуемых буферов
   MSG_LIB_SYS_FAILED_COLORS_ARRAY_RESIZE,            // Не удалось изменить размер массива цветов

...

   MSG_LIB_TEXT_BUFFER_TEXT_LINE_WIDTH,               // Толщина линии отрисовки
   MSG_LIB_TEXT_BUFFER_TEXT_ARROW_SIZE,               // Размер значка стрелки
   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_INVALID_PROPERTY_BUFF,    // Неправильно указано количество буферов индикатора в #property indicator_buffers

...

   MSG_LIB_TEXT_BUFFER_TEXT_TYPE_CALCULATE,           // Расчётный буфер
   MSG_LIB_TEXT_BUFFER_TEXT_TYPE_DATA,                // Цветной буфер данных
   MSG_LIB_TEXT_BUFFER_TEXT_BUFFER,                   // Буфер
   
   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,         // Штрих - две точки
   
  };

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

   {"Не удалось создать объект \"Бар\"","Failed to create object \"Bar\""},
   {"Не удалось синхронизировать данные с сервером","Failed to sync data with server"},
   {"Не удалось изменить размер массива рисуемых буферов","Failed to resize drawing buffers array"},
   {"Не удалось изменить размер массива цветов","Failed to resize color array"},

...

   {"Толщина линии отрисовки","The thickness of the drawing line"},
   {"Размер значка стрелки","Arrow icon size"},
   {"Количество цветов","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"},
   {"Неправильно указано количество буферов индикатора (#property indicator_buffers)","The number of indicator buffers is incorrect (#property indicator_buffers)"},

...

   {"Расчётный буфер","Calculated buffer"},
   {"Цветной буфер данных","Colored Data buffer"},
   {"Буфер","Buffer"},
   
   {"Сплошная линия","Solid line"},
   {"Прерывистая линия","Broken line"},
   {"Пунктирная линия","Dotted line"},
   {"Штрих-пунктирная линия","Dash-dot line"},
   {"Штрих - две точки","Dash - two points"},
   
  };
//+---------------------------------------------------------------------+

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

//+------------------------------------------------------------------+
//| Макроподстановки                                                 |
//+------------------------------------------------------------------+
//--- "Описание функции с номером строки ошибки"
#define DFUN_ERR_LINE                  (__FUNCTION__+(TerminalInfoString(TERMINAL_LANGUAGE)=="Russian" ? ", Стр. " : ", Line ")+(string)__LINE__+": ")
#define DFUN                           (__FUNCTION__+": ")        // "Описание функции"
#define COUNTRY_LANG                   ("Russian")                // Язык страны
#define END_TIME                       (D'31.12.3000 23:59:59')   // Конечная дата для запросов данных истории счёта
#define TIMER_FREQUENCY                (16)                       // Минимальная частота таймера библиотеки в милисекундах
#define TOTAL_TRY                      (5)                        // Количество торговых попыток по умолчанию
#define IND_COLORS_TOTAL               (64)                       // Максимально возможное количество цветов индикаторного буфера
//--- Стандартные звуки

Просто для удобства — чтобы не вписывать проверку количества задаваемых цветов на превышение числа 64. Особенно с учётом их возможного увеличения когда-нибудь.

Теперь немного доработаем класс объекта абстрактного буфера.
На данный момент буферы индикатора у нас находятся в публичной секции класса в массивах с вещественным типом данных для буфера данных и буфера индекса цвета. Уберём их в защищённую секцию. А так как буферов для построения индикатора может быть 1, 2 и 4, то создадим структуру буферов, в которой будет один массив.
Зачем это нужно?
Так как буфером индикатора может выступать только одномерный массив double-данных, то чтобы создать массив буферов (их может быть больше одного), мы и создадим структуру с одним-единственным массивом. И тогда в массиве таких структур у нас будут находиться все требуемые для построения индикатора массивы данных — буферы индикатора. Доступ к ним будет осуществляться по индексу требуемого массива.
Чтобы не выйти за пределы этого массива, случайно передав неверный индекс, создадим метод, корректирующий переданный в него индекс массива, на тот случай, если индекс будет указывать за пределы массива. В этом случае индекс будет скорректирован, и будет указывать на самый последний элемент массива структур. В случае одного массива — индекс укажет на него.

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

//+------------------------------------------------------------------+
//| Класс абстрактного индикаторного буфера                          |
//+------------------------------------------------------------------+
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);
//--- Возвращает скорректированный индекс массива буфера
   int               GetCorrectIndexBuffer(const uint buffer_index) const;

protected:
//--- Структура для хранения массивов буферов объекта-буфера
   struct SDataBuffer { double Array[]; };
//--- Массив (1) всех индикаторных буферов объекта-буфера, (2) буфера цвета, (3) для хранения цветов
   SDataBuffer       DataBuffer[];
   double            ColorBufferArray[];
   int               ArrayColors[];
   
public:

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

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

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

protected:
//--- Защищённый параметрический конструктор
                     CBuffer(ENUM_BUFFER_STATUS status_buffer,
                             ENUM_BUFFER_TYPE buffer_type,
                             const uint index_plot,
                             const uint index_base_array,
                             const int num_datas,
                             const int width,
                             const string label);
public:  

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

   void              SetColor(const color colour);
   void              SetColor(const color colour,const uchar index);
   void              SetColors(const color &array_colors[]);
   void              SetEmptyValue(const double value);
   virtual void      SetLabel(const string label);

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

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

//--- Возвращает (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               ColorsTotal(void)                        const { return (int)this.GetProperty(BUFFER_PROP_COLOR_INDEXES);           }
   color             Color(void)                               const { return (color)this.GetProperty(BUFFER_PROP_COLOR);                 }
   int               BuffersTotal(void)                        const { return (int)this.GetProperty(BUFFER_PROP_NUM_DATAS);               }
   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) используемого таймфрейма, (9) установленных уветов
   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;
   string            GetColorsDescription(void)                const;

И в самом конце тела класса добавим блок методов для работы с объектом-буфером:

//--- Возвращает размер массива буфера данных
   virtual int       GetDataTotal(const uint buffer_index=0)   const;
//--- Возвращает значение из указанного индекса указанного массива буфера (1) данных, (2) цвета
   virtual double    GetDataBufferValue(const uint buffer_index,const uint series_index) const;
   virtual color     GetColorBufferValue(const uint series_index)     const;
//--- Устанавливает значение в указанный индекс указанному массиву буфера (1) данных, (2) цвета
   virtual void      SetBufferValue(const uint buffer_index,const uint series_index,const double value);
   virtual void      SetBufferColorIndex(const uint series_index,const uchar color_index);
//--- Инициализирует все буферы объекта (1) указанным, (2) установленным объекту пустым значением
   virtual void      InitializeBuffers(const double value,const uchar color_index);
   virtual void      InitializeBuffers(void);
//--- Заполняет пустым значением все буферы данных в указанном индексе таймсерии
   void              ClearData(const int series_index);
   
  };
//+------------------------------------------------------------------+

Реализацию этих методов рассмотрим далее.

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

//+------------------------------------------------------------------+
//| Закрытый параметрический конструктор                             |
//+------------------------------------------------------------------+
CBuffer::CBuffer(ENUM_BUFFER_STATUS buffer_status,
                 ENUM_BUFFER_TYPE buffer_type,
                 const uint index_plot,
                 const uint index_base_array,
                 const int num_datas,
                 const int width,
                 const string label)
  {
   this.m_type=COLLECTION_BUFFERS_ID;
//--- Сохранение целочисленных свойств
   this.m_long_prop[BUFFER_PROP_STATUS]                        = buffer_status;
   this.m_long_prop[BUFFER_PROP_TYPE]                          = buffer_type;
   ENUM_DRAW_TYPE type=
     (
      !this.TypeBuffer() || !this.Status() ? DRAW_NONE      : 
      this.Status()==BUFFER_STATUS_FILLING ? DRAW_FILLING   : 
      ENUM_DRAW_TYPE(this.Status()+8)
     );
   this.m_long_prop[BUFFER_PROP_DRAW_TYPE]                     = type;
   this.m_long_prop[BUFFER_PROP_TIMEFRAME]                     = PERIOD_CURRENT;
   this.m_long_prop[BUFFER_PROP_ACTIVE]                        = true;
   this.m_long_prop[BUFFER_PROP_ARROW_CODE]                    = 0x9F;
   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]                    = width;
   this.m_long_prop[BUFFER_PROP_COLOR_INDEXES]                 = (this.Status()!=BUFFER_STATUS_FILLING ? 1 : 2);
   this.m_long_prop[BUFFER_PROP_COLOR]                         = clrRed;
   this.m_long_prop[BUFFER_PROP_NUM_DATAS]                     = num_datas;
   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)+(this.Status()!=BUFFER_STATUS_FILLING ? 1 : 0);
   
//--- Сохранение вещественных свойств
   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 ? label : NULL);

//--- Если не удалось изменить размер массива рисуемых буферов на новый - выводим об этом сообщение с указанием на строку
   if(::ArrayResize(this.DataBuffer,(int)this.GetProperty(BUFFER_PROP_NUM_DATAS))==WRONG_VALUE)
      ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_SYS_FAILED_DRAWING_ARRAY_RESIZE),". ",CMessage::Text(MSG_LIB_SYS_ERROR),": ",(string)::GetLastError());
      
//--- Если не удалось изменить размер массива цветов на новый - выводим об этом сообщение с указанием на строку
   if(::ArrayResize(this.ArrayColors,(int)this.ColorsTotal())==WRONG_VALUE)
      ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_SYS_FAILED_COLORS_ARRAY_RESIZE),". ",CMessage::Text(MSG_LIB_SYS_ERROR),": ",(string)::GetLastError());

//--- Для DRAW_FILLING заполняем массив цветов двумя цветами по умолчанию
   if(this.Status()==BUFFER_STATUS_FILLING)
     {
      this.SetColor(clrBlue,0);
      this.SetColor(clrRed,1);
     }

//--- Связывание индикаторных буферов с массивами
//--- В цикле по количеству рисуемых буферов
   int total=::ArraySize(DataBuffer);
   for(int i=0;i<total;i++)
     {
      //--- рассчитываем индекс очередного массива и
      //--- связываем индикаторный буфер по рассчитанному индексу с динамическим массивом,
      //--- находящимся по индексу цикла i в массиве DataBuffer
      int index=(int)this.GetProperty(BUFFER_PROP_INDEX_BASE)+i;
      ::SetIndexBuffer(index,this.DataBuffer[i].Array,INDICATOR_DATA);
      //--- Установка всем массивам буферов флага индексации как в таймсерии
      ::ArraySetAsSeries(this.DataBuffer[i].Array,true);
     }
//--- Связывание буфера цвета с массивом
   if(this.Status()!=BUFFER_STATUS_FILLING)
     {
      ::SetIndexBuffer((int)this.GetProperty(BUFFER_PROP_INDEX_COLOR),this.ColorBufferArray,INDICATOR_COLOR_INDEX);
      ::ArraySetAsSeries(this.ColorBufferArray,true);
     }

//--- Установка целочисленных параметров буфера
   ::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));
   this.SetColor((color)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));
  }
//+------------------------------------------------------------------+

Установка типа буфера:

   ENUM_DRAW_TYPE type=
     (
      !this.TypeBuffer() || !this.Status() ? DRAW_NONE      : 
      this.Status()==BUFFER_STATUS_FILLING ? DRAW_FILLING   : 
      ENUM_DRAW_TYPE(this.Status()+8)
     );
   this.m_long_prop[BUFFER_PROP_DRAW_TYPE]                     = type;

Для определения типа рисования у нас есть метод SetDrawType(), описанный подробно в прошлой статье:

У нас все буферы будут цветными. Поэтому для установки стиля рисования мы проверим переданный в метод 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 — именно такое смещение будет указывать на константу цветного стиля рисования.

Здесь в конструкторе класса мы используем такой же способ расчёта типа буфера.
Пока метод SetDrawType() остаётся в составе класса, но скорее всего мы его вообще уберём, так как он нужен только при создании объекта, а далее уже тип рисования менять бессмысленно.
Останется временно на случай, если найдётся вскоре какой-либо способ его иного применения.


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

   this.m_long_prop[BUFFER_PROP_LINE_WIDTH]                    = width;
   this.m_long_prop[BUFFER_PROP_COLOR_INDEXES]                 = (this.Status()!=BUFFER_STATUS_FILLING ? 1 : 2);

Количество буферов данных для отрисовки тоже теперь переаётся в параметрах конструктора — их и вписываем:

   this.m_long_prop[BUFFER_PROP_NUM_DATAS]                     = num_datas;
   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)+(this.Status()!=BUFFER_STATUS_FILLING ? 1 : 0);

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

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

//--- Установка целочисленных параметров буфера
   ::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));
   this.SetColor((color)this.GetProperty(BUFFER_PROP_COLOR));

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

//+------------------------------------------------------------------+
//| Возвращает описание целочисленного свойства буфера               |
//+------------------------------------------------------------------+
string CBuffer::GetPropertyDescription(ENUM_BUFFER_PROP_INTEGER property)
  {
   return
     (
   // ... Удалено лишнее
   // ...
   // ...

      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    ?  
         (this.Status()==BUFFER_STATUS_ARROW ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_ARROW_SIZE) :
          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) :
          ": "+this.GetColorsDescription()
         )  :
      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)
         )  :
   
   // ...
   // ...
   // ... Удалено лишнее

      ""
     );
  }
//+------------------------------------------------------------------+

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

Метод возврата описания установленного пустого значения теперь учитывает отрицательное значение EMPTY_VALUE, установленное как "Пустое значение" буфера:

//+------------------------------------------------------------------+
//| Возвращает описание установленного пустого значения              |
//+------------------------------------------------------------------+
string CBuffer::GetEmptyValueDescription(void) const
  {
   double value=fabs(this.EmptyValue());
   return(value<EMPTY_VALUE ? ::DoubleToString(this.EmptyValue(),(this.EmptyValue()==0 ? 1 : 8)) : (this.EmptyValue()>0 ? "EMPTY_VALUE" : "-EMPTY_VALUE"));
  }
//+------------------------------------------------------------------+

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

//+------------------------------------------------------------------+
//| Возвращает описание установленных уветов                         |
//+------------------------------------------------------------------+
string CBuffer::GetColorsDescription(void) const
  {
   int total=this.ColorsTotal();
   if(total==1)
      return ::ColorToString(this.Color(),true);
   string res="";
   for(int i=0;i<total;i++)
     {
      res+=::ColorToString(this.ArrayColors[i],true)+(i<total-1 ? "," : "");
     }
   return res;
  }
//+------------------------------------------------------------------+

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

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

//+------------------------------------------------------------------+
//| Устанавливает количество цветов                                  |
//+------------------------------------------------------------------+
void CBuffer::SetColorNumbers(const int number)
  {
   if(number>IND_COLORS_TOTAL)
      return;
   int n=(this.Status()!=BUFFER_STATUS_FILLING ? number : 2);
   this.SetProperty(BUFFER_PROP_COLOR_INDEXES,n);
   ::ArrayResize(this.ArrayColors,n);
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_COLOR_INDEXES,n);
  }
//+------------------------------------------------------------------+

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

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

//+------------------------------------------------------------------+
//| Устанавливает буферу один заданный цвет отрисовки                |
//+------------------------------------------------------------------+
void CBuffer::SetColor(const color colour)
  {
   if(this.Status()==BUFFER_STATUS_FILLING)
      return;
   this.SetColorNumbers(1);
   this.SetProperty(BUFFER_PROP_COLOR,colour);
   this.ArrayColors[0]=colour;
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LINE_COLOR,0,this.ArrayColors[0]);
  }
//+------------------------------------------------------------------+

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

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

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

//+------------------------------------------------------------------+
//| Устанавливает цвет отрисовки в указанный индекс цвета            |
//+------------------------------------------------------------------+
void CBuffer::SetColor(const color colour,const uchar index)
  {
   if(index>IND_COLORS_TOTAL-1)
      return;
   if(index>this.ColorsTotal()-1)
      this.SetColorNumbers(index+1);
   this.ArrayColors[index]=colour;
   if(index==0)
      this.SetProperty(BUFFER_PROP_COLOR,(color)this.ArrayColors[0]);
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LINE_COLOR,index,this.ArrayColors[index]);
  }
//+------------------------------------------------------------------+

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

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

//+------------------------------------------------------------------+
//| Устанавливает цвета отрисовки из массива цветов                  |
//+------------------------------------------------------------------+
void CBuffer::SetColors(const color &array_colors[])
  {
//--- Если переданный массив пустой - уходим
   if(::ArraySize(array_colors)==0)
      return;
//--- Копируем переданный массив в массив цветов объекта-буфера
   ::ArrayCopy(this.ArrayColors,array_colors,0,0,IND_COLORS_TOTAL);
//--- Если массив цветов был пуст, и по какой-то причине не был скопирован - уходим
   int total=::ArraySize(this.ArrayColors);
   if(total==0)
      return;
//--- Если стиль рисования - не DRAW_FILLING
   if(this.Status()!=BUFFER_STATUS_FILLING)
     {
      //--- если новое количество цветов больше установленного на данный момент - 
      //--- устанавливаем новое значение количества цветов
      if(total>this.ColorsTotal())
         this.SetColorNumbers(total);
     }
   //--- Если стиль рисования - DRAW_FILLING - устанавливаем количество цветов равным 2
   else
      total=2;
//--- Устанавливаем в свойство цвета объекта-буфера самый первый цвет из массива цветов (для одного цвета)
   this.SetProperty(BUFFER_PROP_COLOR,(color)this.ArrayColors[0]);
//--- Устанавливаем индикаторному буферу новое количество цветов
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_COLOR_INDEXES,total);
//--- В цикле по новому количеству цветов устанавливаем индикаторному буферу все цвета по их индексам
   for(int i=0;i<total;i++)
      ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LINE_COLOR,i,this.ArrayColors[i]);
  }
//+------------------------------------------------------------------+

Все строки метода подробно расписаны в комментариях. Думаю, вопросов не возникнет.

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

//+------------------------------------------------------------------+
//| Возвращает скорректированный индекс массива буфера               |
//+------------------------------------------------------------------+
int CBuffer::GetCorrectIndexBuffer(const uint buffer_index) const
  {
   return int(buffer_index<this.GetProperty(BUFFER_PROP_NUM_DATAS) ? buffer_index : this.GetProperty(BUFFER_PROP_NUM_DATAS)-1);
  }
//+------------------------------------------------------------------+

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

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

//+------------------------------------------------------------------+
//| Возвращает размер массива указанного буфера данных               |
//+------------------------------------------------------------------+
int CBuffer::GetDataTotal(const uint buffer_index=0) const
  {
    int index=this.GetCorrectIndexBuffer(buffer_index);
    return(index>WRONG_VALUE ? ::ArraySize(this.DataBuffer[index].Array) : 0);
  }
//+------------------------------------------------------------------+

Метод скорее служит для отладки и для внутреннего использования.
Возвращает размер массива, назначенного индикаторным буфером по его индексу в массиве DataBuffer[].
В OnInit() всегда возвращает ноль. В OnCalculate() возвращает размер rates_total.

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

//+------------------------------------------------------------------+
//| Возвращает значение из указанного индекса таймсерии              |
//| указанного массива буфера данных                                 |
//+------------------------------------------------------------------+
double CBuffer::GetDataBufferValue(const uint buffer_index,const uint series_index) const
  {
   int correct_buff_index=this.GetCorrectIndexBuffer(buffer_index);
   int data_total=this.GetDataTotal(correct_buff_index);
   if(data_total==0)
      return this.EmptyValue();
   int data_index=((int)series_index<data_total ? (int)series_index : data_total-1);
   return this.DataBuffer[correct_buff_index].Array[data_index];
  }
//+------------------------------------------------------------------+

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

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

//+------------------------------------------------------------------+
//| Возвращает значение из указанного индекса таймсерии              |
//| указанного массива буфера цвета                                  |
//+------------------------------------------------------------------+
color CBuffer::GetColorBufferValue(const uint series_index) const
  {
   int data_total=this.GetDataTotal(0);
   if(data_total==0)
      return clrNONE;
   int data_index=((int)series_index<data_total ? (int)series_index : data_total-1);
   int color_index=(this.ColorsTotal()==1 ? 0 : (int)this.ColorBufferArray[data_index]);
   return (color)this.ArrayColors[color_index];
  }
//+------------------------------------------------------------------+

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

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

//+------------------------------------------------------------------+
//| Устанавливает значение в указанный индекс таймсерии              |
//| указанному массиву буфера данных                                 |
//+------------------------------------------------------------------+
void CBuffer::SetBufferValue(const uint buffer_index,const uint series_index,const double value)
  {
   if(this.GetDataTotal(buffer_index)==0)
      return;
   int correct_buff_index=this.GetCorrectIndexBuffer(buffer_index);
   int data_total=this.GetDataTotal(buffer_index);
   int data_index=((int)series_index<data_total ? (int)series_index : data_total-1);
   this.DataBuffer[correct_buff_index].Array[data_index]=value;
  }
//+------------------------------------------------------------------+

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

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

//+------------------------------------------------------------------+
//| Устанавливает индекс цвета в указанный индекс таймсерии          |
//| массива буфера цвета                                             |
//+------------------------------------------------------------------+
void CBuffer::SetBufferColorIndex(const uint series_index,const uchar color_index)
  {
   if(this.GetDataTotal(0)==0 || color_index>this.ColorsTotal()-1 || this.Status()==BUFFER_STATUS_FILLING)
      return;
   int data_total=this.GetDataTotal(0);
   int data_index=((int)series_index<data_total ? (int)series_index : data_total-1);
   if(::ArraySize(this.ColorBufferArray)==0)
      ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR),": ",CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_INVALID_PROPERTY_BUFF));
   this.ColorBufferArray[data_index]=color_index;
  }
//+------------------------------------------------------------------+

Метод идентичен методу получения индекса цвета из указанного индекса таймсерии. За исключением того, что вместо возврата значения, метод записывает в буфер цвета переданное значение индекса цвета по указанному индексу таймсерии.
Плюс — если нет соответствующих данный в массиве данных (он пустой), или переданный индекс цвета больше размера массива цветов, или стиль рисования объекта-буфера DRAW_FILLING (где нет буфера цвета), то уходим из метода.
И если размер буфера цвета нулевой, то вероятно неверно заданы значения в #property indicator_buffersвыводится об этом сообщение, а после этого будет ошибка выхода за пределы массива, которая специально не обрабатывается — чтобы можно было понять по записям в журнале о том, что нужно правильно выставить в свойствах индикатора количество буферов.

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

//+------------------------------------------------------------------+
//| Инициализирует все буферы объекта указанным значением            |
//+------------------------------------------------------------------+
void CBuffer::InitializeBuffers(const double value,const uchar color_index)
  {
   for(int i=0;i<this.GetProperty(BUFFER_PROP_NUM_DATAS);i++)
      ::ArrayInitialize(this.DataBuffer[i].Array,value);
   if(this.Status()!=BUFFER_STATUS_FILLING)
      ::ArrayInitialize(this.ColorBufferArray,(color_index>this.ColorsTotal()-1 ? 0 : color_index));
  }
//+------------------------------------------------------------------+

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

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

//+------------------------------------------------------------------+
//| Инициализирует все буферы объекта                                |
//| установленным объекту пустым значением                           |
//+------------------------------------------------------------------+
void CBuffer::InitializeBuffers(void)
  {
   for(int i=0;i<this.GetProperty(BUFFER_PROP_NUM_DATAS);i++)
      ::ArrayInitialize(this.DataBuffer[i].Array,this.EmptyValue());
   if(this.Status()!=BUFFER_STATUS_FILLING)
      ::ArrayInitialize(this.ColorBufferArray,0);
  }
//+------------------------------------------------------------------+

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

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

//+------------------------------------------------------------------+
//| Заполняет пустым значением все буферы данных                     |
//| в указанном индексе таймсерии                                    |
//+------------------------------------------------------------------+
void CBuffer::ClearData(const int series_index)
  {
   for(int i=0;i<this.GetProperty(BUFFER_PROP_NUM_DATAS);i++)
      this.SetBufferValue(i,series_index,this.EmptyValue());
   this.SetBufferColorIndex(series_index,0);
  }
//+------------------------------------------------------------------+

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

Это все изменения в классе объекта абстрактного буфера, необходимые для создания и работы с объектами-наследниками данного класса.

Объекты-индикаторные буферы — наследники объекта абстрактного буфера

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

Объекты-буферы тоже не будут исключением. Каждый такой объект будет представлять собой индикаторный буфер с конкретным для него стилем рисования. От стиля же рисования он и будет иметь своё название.

Первый объект-буфер будет иметь стиль рисования "Стрелки", и название будет иметь соответствующее.

Создадим в папке терминала \MQL5\Include\DoEasy\Objects\Indicators\ новый файл BufferArrow.mqh класса CBufferArrow, базовым классом для которого будет объект абстрактного буфера CBuffer:

//+------------------------------------------------------------------+
//|                                                  BufferArrow.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"
//+------------------------------------------------------------------+
//| Включаемые файлы                                                 |
//+------------------------------------------------------------------+
#include "Buffer.mqh"
//+------------------------------------------------------------------+
//| Буфер со стилем рисования "Отрисовка стрелками"                  |
//+------------------------------------------------------------------+
class CBufferArrow : public CBuffer
  {
private:

public:
//--- Конструктор
                     CBufferArrow(const uint index_plot,const uint index_base_array) :
                        CBuffer(BUFFER_STATUS_ARROW,BUFFER_TYPE_DATA,index_plot,index_base_array,1,1,"Arrows") {}
//--- Поддерживаемые целочисленные свойства буфера
   virtual bool      SupportProperty(ENUM_BUFFER_PROP_INTEGER property);
//--- Поддерживаемые вещественные свойства буфера
   virtual bool      SupportProperty(ENUM_BUFFER_PROP_DOUBLE property);
//--- Поддерживаемые строковые свойства буфера
   virtual bool      SupportProperty(ENUM_BUFFER_PROP_STRING property);
//--- Выводит в журнал краткое описание буфера
   virtual void      PrintShort(void);
  };
//+------------------------------------------------------------------+

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

CBufferArrow(const uint index_plot,const uint index_base_array) :
   CBuffer(BUFFER_STATUS_ARROW,BUFFER_TYPE_DATA,index_plot,index_base_array,1,1,"Arrows") {}

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

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

//+------------------------------------------------------------------+
//| Возвращает истину, если буфер поддерживает переданное            |
//| целочисленное свойство, возвращает ложь в противном случае       |
//+------------------------------------------------------------------+
bool CBufferArrow::SupportProperty(ENUM_BUFFER_PROP_INTEGER property)
  {
   if(property==BUFFER_PROP_LINE_STYLE || (this.TypeBuffer()==BUFFER_TYPE_CALCULATE && property!=BUFFER_PROP_TYPE && property!=BUFFER_PROP_INDEX_NEXT))
      return false;
   return true;
  }
//+------------------------------------------------------------------+

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

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

//+------------------------------------------------------------------+
//| Возвращает истину, если буфер поддерживает переданное            |
//| вещественное свойство, возвращает ложь в противном случае        |
//+------------------------------------------------------------------+
bool CBufferArrow::SupportProperty(ENUM_BUFFER_PROP_DOUBLE property)
  {
   if(this.TypeBuffer()==BUFFER_TYPE_CALCULATE)
      return false;
   return true;
  }
//+------------------------------------------------------------------+

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

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

//+------------------------------------------------------------------+
//| Возвращает истину, если буфер поддерживает переданное            |
//| строковое свойство, возвращает ложь в противном случае           |
//+------------------------------------------------------------------+
bool CBufferArrow::SupportProperty(ENUM_BUFFER_PROP_STRING property)
  {
   if(this.TypeBuffer()==BUFFER_TYPE_CALCULATE)
      return false;
   return true;
  }
//+------------------------------------------------------------------+

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

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

//+------------------------------------------------------------------+
//| Выводит в журнал краткое описание буфера                         |
//+------------------------------------------------------------------+
void CBufferArrow::PrintShort(void)
  {
   ::Print
     (
      CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_BUFFER),"(",(string)this.IndexPlot(),"): ",
      this.GetStatusDescription(true)," ",this.Symbol()," ",TimeframeDescription(this.Timeframe())
     );
  }
//+------------------------------------------------------------------+

В методе создаётся строка из заголовка "Буфер" с указанием индекса рисуемого буфера (порядкового номера буфера в окне DataWindow) с описанием статуса буфера (стиль рисования), символа и таймфрейма.
В виде, например:

Буфер(0): Отрисовка стрелками EURUSD H1

И это весь класс объекта-буфера с типом рисования "Отрисовка стрелками".


Создадим новый класс-наследник объекта абстрактного буфера CBufferLine — со стилем отрисовки "Линия"
в файле \MQL5\Include\DoEasy\Objects\Indicators\BufferLine.mqh:

//+------------------------------------------------------------------+
//|                                                   BufferLine.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"
//+------------------------------------------------------------------+
//| Включаемые файлы                                                 |
//+------------------------------------------------------------------+
#include "Buffer.mqh"
//+------------------------------------------------------------------+
//| Буфер со стилем рисования "Линия"                                |
//+------------------------------------------------------------------+
class CBufferLine : public CBuffer
  {
private:

public:
//--- Конструктор
                     CBufferLine(const uint index_plot,const uint index_base_array) :
                        CBuffer(BUFFER_STATUS_LINE,BUFFER_TYPE_DATA,index_plot,index_base_array,1,1,"Line") {}
//--- Поддерживаемые целочисленные свойства буфера
   virtual bool      SupportProperty(ENUM_BUFFER_PROP_INTEGER property);
//--- Поддерживаемые вещественные свойства буфера
   virtual bool      SupportProperty(ENUM_BUFFER_PROP_DOUBLE property);
//--- Поддерживаемые строковые свойства буфера
   virtual bool      SupportProperty(ENUM_BUFFER_PROP_STRING property);
//--- Выводит в журнал краткое описание буфера
   virtual void      PrintShort(void);
  };
//+------------------------------------------------------------------+
//| Возвращает истину, если буфер поддерживает переданное            |
//| целочисленное свойство, возвращает ложь в противном случае       |
//+------------------------------------------------------------------+
bool CBufferLine::SupportProperty(ENUM_BUFFER_PROP_INTEGER property)
  {
   if((property==BUFFER_PROP_ARROW_CODE || property==BUFFER_PROP_ARROW_SHIFT) || 
      (this.TypeBuffer()==BUFFER_TYPE_CALCULATE && property!=BUFFER_PROP_TYPE && property!=BUFFER_PROP_INDEX_NEXT)
     ) return false;
   return true; 
  }
//+------------------------------------------------------------------+
//| Возвращает истину, если буфер поддерживает переданное            |
//| вещественное свойство, возвращает ложь в противном случае        |
//+------------------------------------------------------------------+
bool CBufferLine::SupportProperty(ENUM_BUFFER_PROP_DOUBLE property)
  {
   if(this.TypeBuffer()==BUFFER_TYPE_CALCULATE)
      return false;
   return true;
  }
//+------------------------------------------------------------------+
//| Возвращает истину, если буфер поддерживает переданное            |
//| строковое свойство, возвращает ложь в противном случае           |
//+------------------------------------------------------------------+
bool CBufferLine::SupportProperty(ENUM_BUFFER_PROP_STRING property)
  {
   if(this.TypeBuffer()==BUFFER_TYPE_CALCULATE)
      return false;
   return true;
  }
//+------------------------------------------------------------------+
//| Выводит в журнал краткое описание буфера                         |
//+------------------------------------------------------------------+
void CBufferLine::PrintShort(void)
  {
   ::Print
     (
      CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_BUFFER),"(",(string)this.IndexPlot(),"): ",
      this.GetStatusDescription(true)," ",this.Symbol()," ",TimeframeDescription(this.Timeframe())
     );
  }
//+------------------------------------------------------------------+

В сравнении с классом буфера стрелок, здесь в конструкторе класса в списке инициализации в конструктор базового объекта-буфера передаём статус "Линия" и имя графической серии "Line". Остальные параметры такие же, как и у объекта-буфера стрелок:

CBufferLine(const uint index_plot,const uint index_base_array) :
   CBuffer(BUFFER_STATUS_LINE,BUFFER_TYPE_DATA,index_plot,index_base_array,1,1,"Line") {}

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

//+------------------------------------------------------------------+
//| Возвращает истину, если буфер поддерживает переданное            |
//| целочисленное свойство, возвращает ложь в противном случае       |
//+------------------------------------------------------------------+
bool CBufferLine::SupportProperty(ENUM_BUFFER_PROP_INTEGER property)
  {
   if((property==BUFFER_PROP_ARROW_CODE || property==BUFFER_PROP_ARROW_SHIFT) || 
      (this.TypeBuffer()==BUFFER_TYPE_CALCULATE && property!=BUFFER_PROP_TYPE && property!=BUFFER_PROP_INDEX_NEXT)
     ) return false;
   return true; 
  }
//+------------------------------------------------------------------+

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


Создадим новый класс-наследник объекта абстрактного буфера CBufferSection — со стилем отрисовки "Отрезки"
в файле \MQL5\Include\DoEasy\Objects\Indicators\BufferSection.mqh.
Рассматривать дальше будем только конструкторы новых классов и методы поддержания целочисленных свойств, так как всё остальное идентично уже рассмотренным двум классам.

В конструкторе класса в списке инициализации в конструктор базового объекта-буфера передаём статус "Отрезки" и имя графической серии "Section".

CBufferSection(const uint index_plot,const uint index_base_array) :
   CBuffer(BUFFER_STATUS_SECTION,BUFFER_TYPE_DATA,index_plot,index_base_array,1,1,"Section") {}

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

//+------------------------------------------------------------------+
//| Возвращает истину, если буфер поддерживает переданное            |
//| целочисленное свойство, возвращает ложь в противном случае       |
//+------------------------------------------------------------------+ 
bool CBufferSection::SupportProperty(ENUM_BUFFER_PROP_INTEGER property)
  {
   if((property==BUFFER_PROP_ARROW_CODE || property==BUFFER_PROP_ARROW_SHIFT) || 
      (this.TypeBuffer()==BUFFER_TYPE_CALCULATE && property!=BUFFER_PROP_TYPE && property!=BUFFER_PROP_INDEX_NEXT)
     ) return false;
   return true; 
  }
//+------------------------------------------------------------------+

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


Создадим новый класс-наследник объекта абстрактного буфера CBufferHistogram — со стилем отрисовки "Гистограмма от нулевой линии"
в файле \MQL5\Include\DoEasy\Objects\Indicators\BufferHistogram.mqh.

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

CBufferHistogram(const uint index_plot,const uint index_base_array) :
   CBuffer(BUFFER_STATUS_HISTOGRAM,BUFFER_TYPE_DATA,index_plot,index_base_array,1,2,"Histogram") {}

Остальные методы идентичны классу объекта-буфера "Отрезки".


Создадим новый класс-наследник объекта абстрактного буфера CBufferHistogram2 — со стилем отрисовки "Гистограмма на двух индикаторных буферах"
в файле \MQL5\Include\DoEasy\Objects\Indicators\BufferHistogram2.mqh.

В конструкторе класса в списке инициализации в конструктор базового объекта-буфера передаём статус "Гистограмма на двух индикаторных буферах" и
имя графической серии "Histogram2 0;Histogram2 1"
, два буфера для построения и толщина линии гистограммы по умолчанию будет равна 8.

CBufferHistogram2(const uint index_plot,const uint index_base_array) :
   CBuffer(BUFFER_STATUS_HISTOGRAM2,BUFFER_TYPE_DATA,index_plot,index_base_array,2,8,"Histogram2 0;Histogram2 1") {}

Почему толщина линии гистограммы 8.
В индикаторах-гистограммах, строящихся на двух буферах, в MQL5 это максимальный размер ширины столбца гистограммы, и эта ширина зависит от горизонтального масштаба графика:


При максимальном масштабе графика (5) ширина столбца гистограммы имеет свой максимальный размер. Если размер нужен меньше, то после создания буфера можно задать ему новое значение ширины при помощи метода SetWidth().

Почему такое имя графической серии "Histogram2 0;Histogram2 1".
В индикаторах, использующих для своего построения несколько буферов, названия для каждого буфера указывается в наименовании графической серии. Если нужно каждому буферу задать своё уникальное наименование, то все они указываются через точку с запятой (;) при указании имени графической серии.
Здесь у нас для первого буфера задано наименование "Histogram2 0" (ноль — указание на индекс первого буфера), а для второго буфера задано наименование "Histogram2 1" (один — указание на индекс второго буфера).
В окне DataWindow это будет отображено так:


Если нужны иные наименования, то после создания буфера можно задать ему новые значения наименований буферов при помощи метода SetLabel().

Остальные методы идентичны классу объекта-буфера "Отрезки".


Создадим новый класс-наследник объекта абстрактного буфера CBufferZigZag — со стилем отрисовки "Зигзаг"
в файле \MQL5\Include\DoEasy\Objects\Indicators\BufferZigZag.mqh.

В конструкторе класса в списке инициализации в конструктор базового объекта-буфера передаём статус "Зигзаг" и
имя графической серии "ZigZag 0;ZigZag 1", два буфера для построения и толщина линии загзага по умолчанию будет равна 1.

CBufferZigZag(const uint index_plot,const uint index_base_array) :
   CBuffer(BUFFER_STATUS_ZIGZAG,BUFFER_TYPE_DATA,index_plot,index_base_array,2,1,"ZigZag 0;ZigZag 1") {}

Остальные методы идентичны классу объекта-буфера "Отрезки".


Создадим новый класс-наследник объекта абстрактного буфера CBufferFilling — со стилем отрисовки "Цветовая заливка между двумя уровнями"
в файле \MQL5\Include\DoEasy\Objects\Indicators\BufferFilling.mqh.

В конструкторе класса в списке инициализации в конструктор базового объекта-буфера передаём статус "Цветовая заливка между двумя уровнями" и
имя графической серии "Filling 0;Filling 1", два буфера для построения и толщина линии по умолчанию будет равна 1 (линий нет в этом стиле рисования, и это свойство не имеет значения).

CBufferFilling(const uint index_plot,const uint index_base_array) :
   CBuffer(BUFFER_STATUS_FILLING,BUFFER_TYPE_DATA,index_plot,index_base_array,2,1,"Filling 0;Filling 1") {}

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

//+------------------------------------------------------------------+
//| Возвращает истину, если буфер поддерживает переданное            |
//| целочисленное свойство, возвращает ложь в противном случае       |
//+------------------------------------------------------------------+ 
bool CBufferFilling::SupportProperty(ENUM_BUFFER_PROP_INTEGER property)
  {
   if((property==BUFFER_PROP_ARROW_CODE || property==BUFFER_PROP_ARROW_SHIFT) || property==BUFFER_PROP_LINE_STYLE || 
       property==BUFFER_PROP_LINE_WIDTH || property==BUFFER_PROP_INDEX_COLOR ||
      (this.TypeBuffer()==BUFFER_TYPE_CALCULATE && property!=BUFFER_PROP_TYPE && property!=BUFFER_PROP_INDEX_NEXT)
     ) return false;
   return true; 
  }
//+------------------------------------------------------------------+

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

Остальные методы идентичны классу объекта-буфера "Отрезки".


Создадим новый класс-наследник объекта абстрактного буфера CBufferBars — со стилем отрисовки "Отображение в виде баров"
в файле \MQL5\Include\DoEasy\Objects\Indicators\BufferBars.mqh.

В конструкторе класса в списке инициализации в конструктор базового объекта-буфера передаём статус "Отображение в виде баров" и
имя графической серии "Bar Open;Bar High;Bar Low;Bar Close", четыре буфера для построения и толщина баров по умолчанию будет равна 2.

CBufferBars(const uint index_plot,const uint index_base_array) :
   CBuffer(BUFFER_STATUS_BARS,BUFFER_TYPE_DATA,index_plot,index_base_array,4,2,"Bar Open;Bar High;Bar Low;Bar Close") {}

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

//+------------------------------------------------------------------+
//| Возвращает истину, если буфер поддерживает переданное            |
//| целочисленное свойство, возвращает ложь в противном случае       |
//+------------------------------------------------------------------+ 
bool CBufferBars::SupportProperty(ENUM_BUFFER_PROP_INTEGER property)
  {
   if((property==BUFFER_PROP_ARROW_CODE || property==BUFFER_PROP_ARROW_SHIFT) || property==BUFFER_PROP_LINE_STYLE ||
      (this.TypeBuffer()==BUFFER_TYPE_CALCULATE && property!=BUFFER_PROP_TYPE && property!=BUFFER_PROP_INDEX_NEXT)
     ) return false;
   return true; 
  }
//+------------------------------------------------------------------+

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

Остальные методы идентичны классу объекта-буфера "Отрезки".


Создадим новый класс-наследник объекта абстрактного буфера CBufferCandles — со стилем отрисовки "Отображение в виде свечей"
в файле \MQL5\Include\DoEasy\Objects\Indicators\BufferCandles.mqh.

В конструкторе класса в списке инициализации в конструктор базового объекта-буфера передаём статус "Отображение в виде свечей" и
имя графической серии "Candle Open;Candle High;Candle Low;Candle Close", четыре буфера для построения и толщина свечи по умолчанию будет равна 1 (параметр не влияет на реальную ширину отрисовки — ширина свечей всегда равна ширине свечей графика в зависимости от его горизонтального масштаба).

CBufferCandles(const uint index_plot,const uint index_base_array) : 
   CBuffer(BUFFER_STATUS_CANDLES,BUFFER_TYPE_DATA,index_plot,index_base_array,4,1,"Candle Open;Candle High;Candle Low;Candle Close") {}

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

//+------------------------------------------------------------------+
//| Возвращает истину, если буфер поддерживает переданное            |
//| целочисленное свойство, возвращает ложь в противном случае       |
//+------------------------------------------------------------------+ 
bool CBufferCandles::SupportProperty(ENUM_BUFFER_PROP_INTEGER property)
  {
   if((property==BUFFER_PROP_ARROW_CODE || property==BUFFER_PROP_ARROW_SHIFT) ||  
       property==BUFFER_PROP_LINE_STYLE || property==BUFFER_PROP_LINE_WIDTH   ||
      (this.TypeBuffer()==BUFFER_TYPE_CALCULATE && property!=BUFFER_PROP_TYPE && property!=BUFFER_PROP_INDEX_NEXT)
     ) return false;
   return true; 
  }
//+------------------------------------------------------------------+

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

Остальные методы идентичны классу объекта-буфера "Отрезки".

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

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

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

Для возможности выбора нужных графических построений добавим в файл перечислений входных параметров \MQL5\Include\DoEasy\InpDatas.mqh новые перечисления для английского и русского языков компиляции:

//+------------------------------------------------------------------+
//|                                                     InpDatas.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"
//+------------------------------------------------------------------+
//| Макроподстановки                                                 |
//+------------------------------------------------------------------+
#define COMPILE_EN // Закомментировать строку для компиляции на русском языке 
//+------------------------------------------------------------------+
//| Перечисления входных параметров                                  |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Английский язык входных параметров                               |
//+------------------------------------------------------------------+
#ifdef COMPILE_EN
//+------------------------------------------------------------------+
//| Режимы работы с символами                                        |
//+------------------------------------------------------------------+
enum ENUM_SYMBOLS_MODE
  {
   SYMBOLS_MODE_CURRENT,                              // Work only with the current Symbol
   SYMBOLS_MODE_DEFINES,                              // Work with a given list of Symbols
   SYMBOLS_MODE_MARKET_WATCH,                         // Working with Symbols from the "Market Watch" window
   SYMBOLS_MODE_ALL                                   // Work with a complete list of Symbols
  };
//+------------------------------------------------------------------+
//| Режимы работы с таймфреймами                                     |
//+------------------------------------------------------------------+
enum ENUM_TIMEFRAMES_MODE
  {
   TIMEFRAMES_MODE_CURRENT,                           // Work only with the current timeframe
   TIMEFRAMES_MODE_LIST,                              // Work with a given list of timeframes
   TIMEFRAMES_MODE_ALL                                // Work with a complete list of timeframes
  };
//+------------------------------------------------------------------+
//| Выбор "Да"/"Нет"                                                 |
//+------------------------------------------------------------------+
enum ENUM_INPUT_YES_NO
  {
   INPUT_NO  = 0,                                     // No
   INPUT_YES = 1                                      // Yes
  };
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Русский язык входных параметров                                  |
//+------------------------------------------------------------------+
#else  
//+------------------------------------------------------------------+
//| Режимы работы с символами                                        |
//+------------------------------------------------------------------+
enum ENUM_SYMBOLS_MODE
  {
   SYMBOLS_MODE_CURRENT,                              // Работа только с текущим символом
   SYMBOLS_MODE_DEFINES,                              // Работа с заданным списком символов
   SYMBOLS_MODE_MARKET_WATCH,                         // Работа с символами из окна "Обзор рынка"
   SYMBOLS_MODE_ALL                                   // Работа с полным списком символов
  };
//+------------------------------------------------------------------+
//| Режимы работы с таймфреймами                                     |
//+------------------------------------------------------------------+
enum ENUM_TIMEFRAMES_MODE
  {
   TIMEFRAMES_MODE_CURRENT,                           // Работа только с текущим таймфреймом
   TIMEFRAMES_MODE_LIST,                              // Работа с заданным списком таймфреймов
   TIMEFRAMES_MODE_ALL                                // Работа с полным списком таймфреймов
  };
//+------------------------------------------------------------------+
//| Выбор "Да"/"Нет"                                                 |
//+------------------------------------------------------------------+
enum ENUM_INPUT_YES_NO
  {
   INPUT_NO  = 0,                                     // Нет
   INPUT_YES = 1                                      // Да
  };
//+------------------------------------------------------------------+
#endif 
//+------------------------------------------------------------------+

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


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

Подключим к файлу индикатора файлы всех созданных буферов, установим количество используемых буферов равным 27, а рисуемых — равным 9:

//+------------------------------------------------------------------+
//|                                             TestDoEasyPart43.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\BufferArrow.mqh>        // 1 буфер для построения + 1 буфер цвета
#include <DoEasy\Objects\Indicators\BufferLine.mqh>         // 1 буфер для построения + 1 буфер цвета
#include <DoEasy\Objects\Indicators\BufferSection.mqh>      // 1 буфер для построения + 1 буфер цвета
#include <DoEasy\Objects\Indicators\BufferHistogram.mqh>    // 1 буфер для построения + 1 буфер цвета
#include <DoEasy\Objects\Indicators\BufferHistogram2.mqh>   // 2 буфера для построения + 1 буфер цвета
#include <DoEasy\Objects\Indicators\BufferZigZag.mqh>       // 2 буфера для построения + 1 буфер цвета
#include <DoEasy\Objects\Indicators\BufferFilling.mqh>      // 2 буфера для построения + 1 буфер цвета
#include <DoEasy\Objects\Indicators\BufferBars.mqh>         // 4 буфера для построения + 1 буфер цвета
#include <DoEasy\Objects\Indicators\BufferCandles.mqh>      // 4 буфера для построения + 1 буфер цвета
//--- Итого 18 буферов для построения + 9 буферов цвета:       27 индикаторных буферов. Из них 9 рисуемых
//--- properties
#property indicator_chart_window
#property indicator_buffers 27
#property indicator_plots   9 

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

//--- classes

//--- enums

//--- defines

//--- structures

//--- input variables
/*sinput*/ ENUM_SYMBOLS_MODE  InpModeUsedSymbols=  SYMBOLS_MODE_CURRENT;            // 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_CURRENT;            // Mode of used timeframes list
/*sinput*/   string               InpUsedTFs        =  "M1,M5,M15,M30,H1,H4,D1,W1,MN1"; // List of used timeframes (comma - separator)
sinput   ENUM_INPUT_YES_NO    InpDrawArrow      =  INPUT_YES;  // Draw Arrow
sinput   ENUM_INPUT_YES_NO    InpDrawLine       =  INPUT_NO;   // Draw Line
sinput   ENUM_INPUT_YES_NO    InpDrawSection    =  INPUT_NO;   // Draw Section
sinput   ENUM_INPUT_YES_NO    InpDrawHistogram  =  INPUT_NO;   // Draw Histogram
sinput   ENUM_INPUT_YES_NO    InpDrawHistogram2 =  INPUT_NO;   // Draw Histogram2
sinput   ENUM_INPUT_YES_NO    InpDrawZigZag     =  INPUT_NO;   // Draw ZigZag
sinput   ENUM_INPUT_YES_NO    InpDrawFilling    =  INPUT_NO;   // Draw Filling
sinput   ENUM_INPUT_YES_NO    InpDrawBars       =  INPUT_NO;   // Draw Bars
sinput   ENUM_INPUT_YES_NO    InpDrawCandles    =  INPUT_YES;  // Draw Candles
 
sinput   bool                 InpUseSounds      =  true; // Use sounds

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

//--- 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[];            // Массив для передачи в библиотеку используемых таймфреймов
//+------------------------------------------------------------------+

В обработчике 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 CBufferArrow(0,0);
   CBuffer *buffer1=new CBufferLine(1,buffer0.IndexNextBuffer());
   CBuffer *buffer2=new CBufferSection(2,buffer1.IndexNextBuffer());
   CBuffer *buffer3=new CBufferHistogram(3,buffer2.IndexNextBuffer());
   CBuffer *buffer4=new CBufferHistogram2(4,buffer3.IndexNextBuffer());
   CBuffer *buffer5=new CBufferZigZag(5,buffer4.IndexNextBuffer());
   CBuffer *buffer6=new CBufferFilling(6,buffer5.IndexNextBuffer());
   CBuffer *buffer7=new CBufferBars(7,buffer6.IndexNextBuffer());
   CBuffer *buffer8=new CBufferCandles(8,buffer7.IndexNextBuffer());
//--- Добавляем буферы в список индикаторных буферов
   list_buffers.Add(buffer0);
   list_buffers.Add(buffer1);
   list_buffers.Add(buffer2);
   list_buffers.Add(buffer3);
   list_buffers.Add(buffer4);
   list_buffers.Add(buffer5);
   list_buffers.Add(buffer6);
   list_buffers.Add(buffer7);
   list_buffers.Add(buffer8);
//--- Создаём массив цветов
   color array_colors[]={clrDodgerBlue,clrRed,clrGray};
//--- Задаём буферам значения света не по умолчанию
   for(int i=0;i<list_buffers.Total();i++)
     {
      CBuffer *buff=list_buffers.At(i);
      buff.SetColors(array_colors);
      //--- Распечатаем данные очередного буфера
      buff.Print();
     }
//--- Задаём размер значка для буфера стрелок и толщину линии для ZigZag
   buffer0.SetWidth(2);
   buffer5.SetWidth(2);
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+

В обработчике OnCalculate()
При инициализации или изменении исторических данных инициализируем все созданные буферы и выведем их короткие наименования
.
При открытии нового бара или на текущем тике — запишем в буферы значения цен:
для одного буфера
— цену Close,
для двух буферов
— в первый Open, во второй — Close,
для четырёх
буферов в каждый буфер запишем цены OHLC соответственно:

//+------------------------------------------------------------------+
//| 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);
         buff.PrintShort();
         buff.InitializeBuffers();
        }
      limit=rates_total-1;
     }
//--- Подготовка данных

//--- Расчёт индикатора
   for(int i=limit; i>WRONG_VALUE && !IsStopped(); i--)
     {
      //--- В цикле по количеству буферов в списке
      for(int j=0;j<list_buffers.Total();j++)
        {
         //--- получаем очередной буфер и
         CBuffer *buff=list_buffers.At(j);
         //--- очищаем его текущие данные
         buff.ClearData(0);
         //--- Если отрисовка не используется - идём к следующему
         if(!IsUse(buff.Status()))
            continue;
         //--- В зависимости от количества буферов, заполняем их ценовыми данными
         //--- один буфер
         if(buff.BuffersTotal()==1)
            buff.SetBufferValue(0,i,close[i]);
         //--- два буфера - в первый записываем цену open бара, во второй - close
         else if(buff.BuffersTotal()==2)
           {
            buff.SetBufferValue(0,i,open[i]);
            buff.SetBufferValue(1,i,close[i]);
           }
         //--- четыре буфера - в каждый записываем цены OHLC баров
         else if(buff.BuffersTotal()==4)
           {
            buff.SetBufferValue(0,i,open[i]);
            buff.SetBufferValue(1,i,high[i]);
            buff.SetBufferValue(2,i,low[i]);
            buff.SetBufferValue(3,i,close[i]);
           }
         //--- В зависимости от направления свечи устанавливаем цвет буфера
         if(open[i]<close[i])
            buff.SetBufferColorIndex(i,0);
         else if(open[i]>close[i])
            buff.SetBufferColorIndex(i,1);
         else
            buff.SetBufferColorIndex(i,2);
        }
     }
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+

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

//+------------------------------------------------------------------+
//| Возвращает флаг разрешения рисования соответствующего стиля      |
//+------------------------------------------------------------------+
bool IsUse(const ENUM_BUFFER_STATUS status)
  {
   switch(status)
     {
      case BUFFER_STATUS_FILLING    : return (bool)InpDrawFilling;
      case BUFFER_STATUS_LINE       : return (bool)InpDrawLine;
      case BUFFER_STATUS_HISTOGRAM  : return (bool)InpDrawHistogram;
      case BUFFER_STATUS_ARROW      : return (bool)InpDrawArrow;
      case BUFFER_STATUS_SECTION    : return (bool)InpDrawSection;
      case BUFFER_STATUS_HISTOGRAM2 : return (bool)InpDrawHistogram2;
      case BUFFER_STATUS_ZIGZAG     : return (bool)InpDrawZigZag;
      case BUFFER_STATUS_BARS       : return (bool)InpDrawBars;
      case BUFFER_STATUS_CANDLES    : return (bool)InpDrawCandles;
      default: return false;
     }
  }
//+------------------------------------------------------------------+

В функцию передаётся статус буфера и возвращается значение входного параметра, соответствующее переданному в функцию статусу.

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

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

Счёт 8550475: Artyom Trishkin (MetaQuotes Software Corp.) 10425.23 USD, 1:100, Hedge, Демонстрационный счёт MetaTrader 5
--- Инициализация библиотеки "DoEasy" ---
Работа только с текущим символом: "EURUSD"
Работа только с текущим таймфреймом: H1
Таймсерия символа EURUSD: 
- Таймсерия "EURUSD" H1: Запрошено: 1000, Фактически: 0, Создано: 0, На сервере: 0
Время инициализации библиотеки: 00:00:00.156

============= Начало списка параметров: Цветной буфер данных[0] "Отрисовка стрелками" ==================
Порядковый номер рисуемого буфера: 0
Статус буфера: Индикаторный буфер с типом графического построения "Отрисовка стрелками"
Тип буфера: Цветной буфер данных
Период данных буфера (таймфрейм): Текущий период графика (H1)
Активен: Да
Тип графического построения: Отрисовка стрелками
Код стрелки: 159
Смещение стрелок по вертикали: 0
Размер значка стрелки: 1
Количество начальных баров без отрисовки и значений в DataWindow: 0
Отображение значений построения в окне DataWindow: Да
Сдвиг графического построения индикатора по оси времени в барах: 0
Количество цветов: 3
Цвет отрисовки: clrDodgerBlue,clrRed,clrGray
Количество буферов данных: 1
Индекс базового буфера данных: 0
Индекс буфера цвета: 1
Индекс массива для назначения следующим индикаторным буфером: 2
------
Пустое значение для построения, для которого нет отрисовки: EMPTY_VALUE
------
Символ буфера: EURUSD
Имя индикаторной графической серии, отображаемое в окне DataWindow: Arrows
================== Конец списка параметров: Цветной буфер данных[0] "Отрисовка стрелками" ==================

============= Начало списка параметров: Цветной буфер данных[1] "Линия" ==================
Порядковый номер рисуемого буфера: 1
Статус буфера: Индикаторный буфер с типом графического построения "Линия"
Тип буфера: Цветной буфер данных
Период данных буфера (таймфрейм): Текущий период графика (H1)
Активен: Да
Тип графического построения: Линия
Стиль линии отрисовки: Сплошная линия
Толщина линии отрисовки: 1
Количество начальных баров без отрисовки и значений в DataWindow: 0
Отображение значений построения в окне DataWindow: Да
Сдвиг графического построения индикатора по оси времени в барах: 0
Количество цветов: 3
Цвет отрисовки: clrDodgerBlue,clrRed,clrGray
Количество буферов данных: 1
Индекс базового буфера данных: 2
Индекс буфера цвета: 3
Индекс массива для назначения следующим индикаторным буфером: 4
------
Пустое значение для построения, для которого нет отрисовки: EMPTY_VALUE
------
Символ буфера: EURUSD
Имя индикаторной графической серии, отображаемое в окне DataWindow: Line
================== Конец списка параметров: Цветной буфер данных[1] "Линия" ==================

============= Начало списка параметров: Цветной буфер данных[2] "Отрезки" ==================
Порядковый номер рисуемого буфера: 2
Статус буфера: Индикаторный буфер с типом графического построения "Отрезки"
Тип буфера: Цветной буфер данных
Период данных буфера (таймфрейм): Текущий период графика (H1)
Активен: Да
Тип графического построения: Отрезки
Стиль линии отрисовки: Сплошная линия
Толщина линии отрисовки: 1
Количество начальных баров без отрисовки и значений в DataWindow: 0
Отображение значений построения в окне DataWindow: Да
Сдвиг графического построения индикатора по оси времени в барах: 0
Количество цветов: 3
Цвет отрисовки: clrDodgerBlue,clrRed,clrGray
Количество буферов данных: 1
Индекс базового буфера данных: 4
Индекс буфера цвета: 5
Индекс массива для назначения следующим индикаторным буфером: 6
------
Пустое значение для построения, для которого нет отрисовки: EMPTY_VALUE
------
Символ буфера: EURUSD
Имя индикаторной графической серии, отображаемое в окне DataWindow: Section
================== Конец списка параметров: Цветной буфер данных[2] "Отрезки" ==================

============= Начало списка параметров: Цветной буфер данных[3] "Гистограмма от нулевой линии" ==================
Порядковый номер рисуемого буфера: 3
Статус буфера: Индикаторный буфер с типом графического построения "Гистограмма от нулевой линии"
Тип буфера: Цветной буфер данных
Период данных буфера (таймфрейм): Текущий период графика (H1)
Активен: Да
Тип графического построения: Гистограмма от нулевой линии
Стиль линии отрисовки: Сплошная линия
Толщина линии отрисовки: 2
Количество начальных баров без отрисовки и значений в DataWindow: 0
Отображение значений построения в окне DataWindow: Да
Сдвиг графического построения индикатора по оси времени в барах: 0
Количество цветов: 3
Цвет отрисовки: clrDodgerBlue,clrRed,clrGray
Количество буферов данных: 1
Индекс базового буфера данных: 6
Индекс буфера цвета: 7
Индекс массива для назначения следующим индикаторным буфером: 8
------
Пустое значение для построения, для которого нет отрисовки: EMPTY_VALUE
------
Символ буфера: EURUSD
Имя индикаторной графической серии, отображаемое в окне DataWindow: Histogram
================== Конец списка параметров: Цветной буфер данных[3] "Гистограмма от нулевой линии" ==================

============= Начало списка параметров: Цветной буфер данных[4] "Гистограмма на двух индикаторных буферах" ==================
Порядковый номер рисуемого буфера: 4
Статус буфера: Индикаторный буфер с типом графического построения "Гистограмма на двух индикаторных буферах"
Тип буфера: Цветной буфер данных
Период данных буфера (таймфрейм): Текущий период графика (H1)
Активен: Да
Тип графического построения: Гистограмма на двух индикаторных буферах
Стиль линии отрисовки: Сплошная линия
Толщина линии отрисовки: 8
Количество начальных баров без отрисовки и значений в DataWindow: 0
Отображение значений построения в окне DataWindow: Да
Сдвиг графического построения индикатора по оси времени в барах: 0
Количество цветов: 3
Цвет отрисовки: clrDodgerBlue,clrRed,clrGray
Количество буферов данных: 2
Индекс базового буфера данных: 8
Индекс буфера цвета: 10
Индекс массива для назначения следующим индикаторным буфером: 11
------
Пустое значение для построения, для которого нет отрисовки: EMPTY_VALUE
------
Символ буфера: EURUSD
Имя индикаторной графической серии, отображаемое в окне DataWindow: Histogram2 0;Histogram2 1
================== Конец списка параметров: Цветной буфер данных[4] "Гистограмма на двух индикаторных буферах" ==================

============= Начало списка параметров: Цветной буфер данных[5] "Зигзаг" ==================
Порядковый номер рисуемого буфера: 5
Статус буфера: Индикаторный буфер с типом графического построения "Зигзаг"
Тип буфера: Цветной буфер данных
Период данных буфера (таймфрейм): Текущий период графика (H1)
Активен: Да
Тип графического построения: Зигзаг
Стиль линии отрисовки: Сплошная линия
Толщина линии отрисовки: 1
Количество начальных баров без отрисовки и значений в DataWindow: 0
Отображение значений построения в окне DataWindow: Да
Сдвиг графического построения индикатора по оси времени в барах: 0
Количество цветов: 3
Цвет отрисовки: clrDodgerBlue,clrRed,clrGray
Количество буферов данных: 2
Индекс базового буфера данных: 11
Индекс буфера цвета: 13
Индекс массива для назначения следующим индикаторным буфером: 14
------
Пустое значение для построения, для которого нет отрисовки: EMPTY_VALUE
------
Символ буфера: EURUSD
Имя индикаторной графической серии, отображаемое в окне DataWindow: ZigZag 0;ZigZag 1
================== Конец списка параметров: Цветной буфер данных[5] "Зигзаг" ==================

============= Начало списка параметров: Цветной буфер данных[6] "Цветовая заливка между двумя уровнями" ==================
Порядковый номер рисуемого буфера: 6
Статус буфера: Индикаторный буфер с типом графического построения "Цветовая заливка между двумя уровнями"
Тип буфера: Цветной буфер данных
Период данных буфера (таймфрейм): Текущий период графика (H1)
Активен: Да
Тип графического построения: Цветовая заливка между двумя уровнями
Количество начальных баров без отрисовки и значений в DataWindow: 0
Отображение значений построения в окне DataWindow: Да
Сдвиг графического построения индикатора по оси времени в барах: 0
Количество цветов: 2
Цвет отрисовки: clrDodgerBlue,clrRed
Количество буферов данных: 2
Индекс базового буфера данных: 14
Индекс массива для назначения следующим индикаторным буфером: 16
------
Пустое значение для построения, для которого нет отрисовки: EMPTY_VALUE
------
Символ буфера: EURUSD
Имя индикаторной графической серии, отображаемое в окне DataWindow: Filling 0;Filling 1
================== Конец списка параметров: Цветной буфер данных[6] "Цветовая заливка между двумя уровнями" ==================

============= Начало списка параметров: Цветной буфер данных[7] "Отображение в виде баров" ==================
Порядковый номер рисуемого буфера: 7
Статус буфера: Индикаторный буфер с типом графического построения "Отображение в виде баров"
Тип буфера: Цветной буфер данных
Период данных буфера (таймфрейм): Текущий период графика (H1)
Активен: Да
Тип графического построения: Отображение в виде баров
Толщина линии отрисовки: 2
Количество начальных баров без отрисовки и значений в DataWindow: 0
Отображение значений построения в окне DataWindow: Да
Сдвиг графического построения индикатора по оси времени в барах: 0
Количество цветов: 3
Цвет отрисовки: clrDodgerBlue,clrRed,clrGray
Количество буферов данных: 4
Индекс базового буфера данных: 16
Индекс буфера цвета: 20
Индекс массива для назначения следующим индикаторным буфером: 21
------
Пустое значение для построения, для которого нет отрисовки: EMPTY_VALUE
------
Символ буфера: EURUSD
Имя индикаторной графической серии, отображаемое в окне DataWindow: Bar Open;Bar High;Bar Low;Bar Close
================== Конец списка параметров: Цветной буфер данных[7] "Отображение в виде баров" ==================

============= Начало списка параметров: Цветной буфер данных[8] "Отображение в виде свечей" ==================
Порядковый номер рисуемого буфера: 8
Статус буфера: Индикаторный буфер с типом графического построения "Отображение в виде свечей"
Тип буфера: Цветной буфер данных
Период данных буфера (таймфрейм): Текущий период графика (H1)
Активен: Да
Тип графического построения: Отображение в виде свечей
Количество начальных баров без отрисовки и значений в DataWindow: 0
Отображение значений построения в окне DataWindow: Да
Сдвиг графического построения индикатора по оси времени в барах: 0
Количество цветов: 3
Цвет отрисовки: clrDodgerBlue,clrRed,clrGray
Количество буферов данных: 4
Индекс базового буфера данных: 21
Индекс буфера цвета: 25
Индекс массива для назначения следующим индикаторным буфером: 26
------
Пустое значение для построения, для которого нет отрисовки: EMPTY_VALUE
------
Символ буфера: EURUSD
Имя индикаторной графической серии, отображаемое в окне DataWindow: Candle Open;Candle High;Candle Low;Candle Close
================== Конец списка параметров: Цветной буфер данных[8] "Отображение в виде свечей" ==================

После первого запуска — когда будет произведена инициализация библиотеки и всех буферов индикатора, в журнал будут выведены записи:

Таймсерия "EURUSD" H1 создана успешно:
- Таймсерия "EURUSD" H1: Запрошено: 1000, Фактически: 1000, Создано: 1000, На сервере: 6230
Буфер(0): Отрисовка стрелками EURUSD H1
Буфер(1): Линия EURUSD H1
Буфер(2): Отрезки EURUSD H1
Буфер(3): Гистограмма от нулевой линии EURUSD H1
Буфер(4): Гистограмма на двух индикаторных буферах EURUSD H1
Буфер(5): Зигзаг EURUSD H1
Буфер(6): Цветовая заливка между двумя уровнями EURUSD H1
Буфер(7): Отображение в виде баров EURUSD H1
Буфер(8): Отображение в виде свечей EURUSD H1

Попереключаем в настройках разрешения на вывод линий индикатора:


Что дальше

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

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

К содержанию

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

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



Прикрепленные файлы |
MQL5.zip (3750.23 KB)
Как создать 3D-графику на DirectX в MetaTrader 5 Как создать 3D-графику на DirectX в MetaTrader 5

Компьютерная 3D-графика хорошо подходит для анализа больших объемов данных, так как позволяет визуализировать скрытые закономерности. Такие задачи можно решать и напрямую в MQL5 — функции для работы с DireсtX позволяют при желании написать свою трехмерную игру для MetaTrader 5. Начните изучение с рисования простых объемных фигур.

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

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

Мультивалютный мониторинг торговых сигналов (Часть 5): Составные сигналы Мультивалютный мониторинг торговых сигналов (Часть 5): Составные сигналы

В пятой части разработки приложения мониторинга торговых сигналов введем в систему понятие составного сигнала и реализуем необходимый для этого функционал. Ранее в приложении использовались простые сигналы, такие как RSI, WPR, CCI, а также введенная возможность использования собственного индикатора.

Работа с таймсериями в библиотеке DoEasy (Часть 44): Класс-коллекция объектов индикаторных буферов Работа с таймсериями в библиотеке DoEasy (Часть 44): Класс-коллекция объектов индикаторных буферов

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