Работа с таймсериями в библиотеке DoEasy (Часть 51): Составные мультипериодные мультисимвольные стандартные индикаторы

Artyom Trishkin | 28 августа, 2020

Содержание


Концепция

Сегодня мы завершаем создание объектов мультисимвольных мультипериодных стандартных индикаторов. И вышло как раз так, что напоследок мы оставили очень хороший пример создания индикатора Ichimoku Kinko Hyo — для его отрисовки нам потребуется не только создание всех его значимых буферов, отображаемых в окне данных терминала, но также и добавить два дополнительных буфера для закрашивания пространства между двумя его линиями 'Senkou Span A' и 'Senkou Span B' при помощи двух гистограмм, рисуемых между двумя значениями. При этом каждая из гистограмм должна повторять стиль и цвет линии, к которой она относится.
Создание такого индикатора будет хорошим наглядным примером того, как можно создавать собственные сложносоставные пользовательские индикаторы при помощи этой библиотеки.
В завершение мы создадим последний объект мультисимвольного мультипериодного индикатора из полного набора стандартных индикаторов терминала MetaTrader5 — индикатор Билла Вильямса Gator Oscillator, построенного на основе его же индикатора Alligator, рассмотренного нами в прошлой статье.

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


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

В первую очередь внесём новое текстовое сообщение библиотеки в файле \MQL5\Include\DoEasy\Data.mqh.

Пропишем индекс нового сообщения:

//--- CBuffer
   MSG_LIB_TEXT_BUFFER_TEXT_INDEX_BASE,               // Индекс базового буфера данных
   MSG_LIB_TEXT_BUFFER_TEXT_INDEX_PLOT,               // Порядковый номер рисуемого буфера
   MSG_LIB_TEXT_BUFFER_TEXT_INDEX_COLOR,              // Индекс буфера цвета
   MSG_LIB_TEXT_BUFFER_TEXT_NUM_DATAS,                // Количество буферов данных
   MSG_LIB_TEXT_BUFFER_TEXT_INDEX_NEXT_BASE,          // Индекс массива для назначения следующим индикаторным буфером
   MSG_LIB_TEXT_BUFFER_TEXT_INDEX_NEXT_PLOT,          // Индекс следующего по счёту рисуемого буфера
   MSG_LIB_TEXT_BUFFER_TEXT_ID,                       // Идентификатор буферов индикатора
   MSG_LIB_TEXT_BUFFER_TEXT_IND_LINE_MODE,            // Линия индикатора
   MSG_LIB_TEXT_BUFFER_TEXT_IND_HANDLE,               // Хэндл индикатора, использующего буфер
   MSG_LIB_TEXT_BUFFER_TEXT_IND_TYPE,                 // Тип индикатора, использующего буфер
   MSG_LIB_TEXT_BUFFER_TEXT_IND_LINE_ADDITIONAL_NUM,  // Номер дополнительной линии
   MSG_LIB_TEXT_BUFFER_TEXT_TIMEFRAME,                // Период данных буфера (таймфрейм)

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

   {"Индекс базового буфера данных","Index of Base data buffer"},
   {"Порядковый номер рисуемого буфера","Plot buffer sequence number"},
   {"Индекс буфера цвета","Color buffer index"},
   {"Количество буферов данных","Number of data buffers"},
   {"Индекс массива для назначения следующим индикаторным буфером","Array index for assignment as the next indicator buffer"},
   {"Индекс следующего по счёту рисуемого буфера","Index of the next drawable buffer"},
   {"Идентификатор буферов индикатора","Indicator Buffer Id"},
   {"Линия индикатора","Indicator line"},
   {"Хэндл индикатора, использующего буфер","Indicator handle that uses the buffer"},
   {"Тип индикатора, использующего буфер","Indicator type that uses the buffer"},
   {"Номер дополнительной линии","Additional line number"},
   {"Период данных буфера (таймфрейм)","Buffer data Period (Timeframe)"},

В файле \MQL5\Include\DoEasy\Defines.mqh в перечисление типов линий индикаторов добавим новые линии для индикатора Ichimoku Kinko Hyo и ещё одну линию как вспомогательную — для оформления своих индикаторов:

//+------------------------------------------------------------------+
//| Значения линий индикаторов в перечислени                         |
//+------------------------------------------------------------------+
enum ENUM_INDICATOR_LINE_MODE
  {
   INDICATOR_LINE_MODE_MAIN         =  0,                   // Главная линия
   INDICATOR_LINE_MODE_SIGNAL       =  1,                   // Сигнальная линия
   INDICATOR_LINE_MODE_UPPER        =  0,                   // Верхняя линия
   INDICATOR_LINE_MODE_LOWER        =  1,                   // Нижняя линия
   INDICATOR_LINE_MODE_MIDDLE       =  2,                   // Средняя линия
   INDICATOR_LINE_MODE_JAWS         =  0,                   // Линия Jaws
   INDICATOR_LINE_MODE_TEETH        =  1,                   // Линия Teeth
   INDICATOR_LINE_MODE_LIPS         =  2,                   // Линия Lips
   INDICATOR_LINE_MODE_DI_PLUS      =  1,                   // Линия +DI
   INDICATOR_LINE_MODE_DI_MINUS     =  2,                   // Линия -DI
   INDICATOR_LINE_MODE_TENKAN_SEN   =  0,                   // Линия Tenkan-sen
   INDICATOR_LINE_MODE_KIJUN_SEN    =  1,                   // Линия Kijun-sen
   INDICATOR_LINE_MODE_SENKOU_SPANA =  2,                   // Линия Senkou Span A
   INDICATOR_LINE_MODE_SENKOU_SPANB =  3,                   // Линия Senkou Span B
   INDICATOR_LINE_MODE_CHIKOU_SPAN  =  4,                   // Линия Chikou Span
   INDICATOR_LINE_MODE_ADDITIONAL   =  5,                   // Дополнительная линия
  };
//+------------------------------------------------------------------+

Так как мы в прошлой статье задали однотипным линиям индикаторов в этом перечислении одинаковые значения и объединили обработчики разных объектов стандартных индикаторов в один, то теперь при выводе описания типа линии из объекта-буфера нам выводятся описания линий, соответствующие самым первым встреченным значениям данного перечисления. Например, если мы выводим описании линии Jaws стандартного индикатора Alligator, а значение константы INDICATOR_LINE_MODE_JAWS в этом перечислении равно нулю, то нам выводится описание первой константы из этого перечисления, значение которой тоже равно нулю — константы INDICATOR_LINE_MODE_MAIN.
Это не ошибка, но и неприятное недоразумение. Чтобы этого избежать, нам нужно иметь для каждой константы перечисления своё уникальное значение. Но тогда придётся опять разделять обработчики, что гораздо хуже. Поэтому мы поступим так: добавим ещё одно перечисление, и сделаем вывод описания линии объекта-буфера при помощи проверки того, какую именно линию какого именно индикатора отображает буфер, и выводить будем значение нового перечисления, соответствующее объекту-буферу.

Добавим это перечисление:

//+------------------------------------------------------------------+
//| Перечисление линий индикаторов                                   |
//+------------------------------------------------------------------+
enum ENUM_INDICATOR_LINE
  {
   INDICATOR_LINE_MAIN,                                     // Главная линия
   INDICATOR_LINE_SIGNAL,                                   // Сигнальная линия
   INDICATOR_LINE_UPPER,                                    // Верхняя линия
   INDICATOR_LINE_LOWER,                                    // Нижняя линия
   INDICATOR_LINE_MIDDLE,                                   // Средняя линия
   INDICATOR_LINE_JAWS,                                     // Линия Jaws
   INDICATOR_LINE_TEETH,                                    // Линия Teeth
   INDICATOR_LINE_LIPS,                                     // Линия Lips
   INDICATOR_LINE_DI_PLUS,                                  // Линия +DI
   INDICATOR_LINE_DI_MINUS,                                 // Линия -DI
   INDICATOR_LINE_TENKAN_SEN,                               // Линия Tenkan-sen
   INDICATOR_LINE_KIJUN_SEN,                                // Линия Kijun-sen
   INDICATOR_LINE_SENKOU_SPANA,                             // Линия Senkou Span A
   INDICATOR_LINE_SENKOU_SPANB,                             // Линия Senkou Span B
   INDICATOR_LINE_CHIKOU_SPAN,                              // Линия Chikou Span
   INDICATOR_LINE_ADDITIONAL,                               // Дополнительная линия
  };
//+------------------------------------------------------------------+

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

В этом же файле добавим ещё одно целочисленное свойство объекта-буфера, заодно увеличив количество целочисленных свойств объекта с 24 до 25:

//+------------------------------------------------------------------+
//| Целочисленные свойства буфера                                    |
//+------------------------------------------------------------------+
enum ENUM_BUFFER_PROP_INTEGER
  {
   BUFFER_PROP_INDEX_PLOT = 0,                              // Порядковый номер рисуемого буфера
   BUFFER_PROP_STATUS,                                      // Статус (по стилю рисования) буфера (из перечисления ENUM_BUFFER_STATUS)
   BUFFER_PROP_TYPE,                                        // Тип буфера (из перечисления ENUM_BUFFER_TYPE)
   BUFFER_PROP_TIMEFRAME,                                   // Период данных буфера (таймфрейм)
   BUFFER_PROP_ACTIVE,                                      // Флаг использования буфера
   BUFFER_PROP_DRAW_TYPE,                                   // Тип графического построения (из перечисления ENUM_DRAW_TYPE)
   BUFFER_PROP_ARROW_CODE,                                  // Код стрелки для стиля DRAW_ARROW
   BUFFER_PROP_ARROW_SHIFT,                                 // Смещение стрелок по вертикали для стиля DRAW_ARROW
   BUFFER_PROP_LINE_STYLE,                                  // Стиль линии отрисовки
   BUFFER_PROP_LINE_WIDTH,                                  // Толщина линии отрисовки
   BUFFER_PROP_DRAW_BEGIN,                                  // Количество начальных баров без отрисовки и значений в DataWindow
   BUFFER_PROP_SHOW_DATA,                                   // Признак отображения значений построения в окне DataWindow
   BUFFER_PROP_SHIFT,                                       // Сдвиг графического построения индикатора по оси времени в барах
   BUFFER_PROP_COLOR_INDEXES,                               // Количество цветов
   BUFFER_PROP_COLOR,                                       // Цвет отрисовки
   BUFFER_PROP_INDEX_BASE,                                  // Индекс базового буфера данных
   BUFFER_PROP_INDEX_NEXT_BASE,                             // Индекс массива для назначения следующим индикаторным буфером
   BUFFER_PROP_INDEX_NEXT_PLOT,                             // Индекс следующего по счёту рисуемого буфера
   BUFFER_PROP_ID,                                          // Идентификатор множества буферов одного индикатора
   BUFFER_PROP_IND_LINE_MODE,                               // Линия индикатора
   BUFFER_PROP_IND_HANDLE,                                  // Хэндл индикатора, использующего буфер
   BUFFER_PROP_IND_TYPE,                                    // Тип индикатора, использующего буфер
   BUFFER_PROP_IND_LINE_ADDITIONAL_NUM,                     // Номер дополнительной линии индикатора
   BUFFER_PROP_NUM_DATAS,                                   // Количество буферов данных
   BUFFER_PROP_INDEX_COLOR,                                 // Индекс буфера цвета
  }; 
#define BUFFER_PROP_INTEGER_TOTAL (25)                      // Общее количество целочисленных свойств буфера
#define BUFFER_PROP_INTEGER_SKIP  (2)                       // Количество неиспользуемых в сортировке свойств буфера
//+------------------------------------------------------------------+

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

//+------------------------------------------------------------------+
//| Возможные критерии сортировки буферов                            |
//+------------------------------------------------------------------+
#define FIRST_BUFFER_DBL_PROP          (BUFFER_PROP_INTEGER_TOTAL-BUFFER_PROP_INTEGER_SKIP)
#define FIRST_BUFFER_STR_PROP          (BUFFER_PROP_INTEGER_TOTAL-BUFFER_PROP_INTEGER_SKIP+BUFFER_PROP_DOUBLE_TOTAL-BUFFER_PROP_DOUBLE_SKIP)
enum ENUM_SORT_BUFFER_MODE
  {
//--- Сортировка по целочисленным свойствам
   SORT_BY_BUFFER_INDEX_PLOT = 0,                           // Сортировать по порядковому номеру рисуемого буфера
   SORT_BY_BUFFER_STATUS,                                   // Сортировать по стилю рисования (статусу) буфера (из перечисления ENUM_BUFFER_STATUS)
   SORT_BY_BUFFER_TYPE,                                     // Сортировать по типу буфера (из перечисления ENUM_BUFFER_TYPE)
   SORT_BY_BUFFER_TIMEFRAME,                                // Сортировать по периоду данных буфера (таймфрейму)
   SORT_BY_BUFFER_ACTIVE,                                   // Сортировать по флагу использования буфера
   SORT_BY_BUFFER_DRAW_TYPE,                                // Сортировать по типу графического построения (из перечисления ENUM_DRAW_TYPE)
   SORT_BY_BUFFER_ARROW_CODE,                               // Сортировать по коду стрелки для стиля DRAW_ARROW
   SORT_BY_BUFFER_ARROW_SHIFT,                              // Сортировать по смещению стрелок по вертикали для стиля DRAW_ARROW
   SORT_BY_BUFFER_LINE_STYLE,                               // Сортировать по стилю линии отрисовки
   SORT_BY_BUFFER_LINE_WIDTH,                               // Сортировать по толщине линии отрисовки
   SORT_BY_BUFFER_DRAW_BEGIN,                               // Сортировать по количеству начальных баров без отрисовки и значений в DataWindow
   SORT_BY_BUFFER_SHOW_DATA,                                // Сортировать по признаку отображения значений построения в окне DataWindow
   SORT_BY_BUFFER_SHIFT,                                    // Сортировать по сдвигу графического построения индикатора по оси времени в барах
   SORT_BY_BUFFER_COLOR_INDEXES,                            // Сортировать по количеству цветов
   SORT_BY_BUFFER_COLOR,                                    // Сортировать по цвету отрисовки
   SORT_BY_BUFFER_INDEX_BASE,                               // Сортировать по индексу базового буфера данных
   SORT_BY_BUFFER_INDEX_NEXT_BASE,                          // Сортировать по индексу массива для назначения следующим индикаторным буфером
   SORT_BY_BUFFER_INDEX_NEXT_PLOT,                          // Сортировать по индексу следующего по счёту рисуемого буфера
   SORT_BY_BUFFER_ID,                                       // Сортировать по идентификатору множества буферов одного индикатора
   SORT_BY_BUFFER_IND_LINE_MODE,                            // Сортировать по линии индикатора
   SORT_BY_BUFFER_IND_HANDLE,                               // Сортировать по хэндлу индикатора, использующего буфер
   SORT_BY_BUFFER_IND_TYPE,                                 // Сортировать по типу индикатора, использующего буфер
   SORT_BY_BUFFER_IND_LINE_ADDITIONAL_NUM,                  // Сортировать по номеру дополнительной линии индикатора
//--- Сортировка по вещественным свойствам
   SORT_BY_BUFFER_EMPTY_VALUE = FIRST_BUFFER_DBL_PROP,      // Сортировать по пустому значению для построения, для которого нет отрисовки
//--- Сортировка по строковым свойствам
   SORT_BY_BUFFER_SYMBOL = FIRST_BUFFER_STR_PROP,           // Сортировать по символу буфера
   SORT_BY_BUFFER_LABEL,                                    // Сортировать по имени индикаторной графической серии, отображаемого в окне DataWindow
   SORT_BY_BUFFER_IND_NAME,                                 // Сортировать по наименованию индикатора, использующего буфер
   SORT_BY_BUFFER_IND_NAME_SHORT,                           // Сортировать по короткому наименованию индикатора, использующего буфер
  };
//+------------------------------------------------------------------+

Теперь нужно немного доработать класс объекта абстрактного буфера в файле \MQL5\Include\DoEasy\Objects\Indicators\Buffer.mqh.

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

public:  
//--- Выводит в журнал описание свойств буфера (full_prop=true - все свойства, false - только поддерживаемые)
   void              Print(const bool full_prop=false);
//--- Выводит в журнал краткое описание буфера (реализация в наследниках)
   virtual void      PrintShort(void) {;}
   
//--- Устанавливает (1) код стрелки, (2) смещение стрелок по вертикали, (3) символ, (4) таймфрейм, (5) флаг активности буфера
//--- (6) тип рисования, (7) количество начальных баров без отрисовки, (8) признак отображения значений построения в окне DataWindow,
//--- (9) сдвиг графического построения индикатора по оси времени, (10) стиль линии отрисовки, (11) толщину линии отрисовки,
//--- (12) общее количество цветов, (13) один цвет отрисовки, (14) цвет отрисовки в указанный индекс цвета,
//--- (15) цвета отрисовки из массива цветов, (16) пустое значение, (17) имя графической серии, отображаемое в окне DataWindow
   virtual void      SetArrowCode(const uchar code)                  { return;                                                               }
   virtual void      SetArrowShift(const int shift)                  { return;                                                               }
   void              SetSymbol(const string symbol)                  { this.SetProperty(BUFFER_PROP_SYMBOL,symbol);                          }
   void              SetTimeframe(const ENUM_TIMEFRAMES timeframe)   { this.SetProperty(BUFFER_PROP_TIMEFRAME,timeframe);                    }
   void              SetActive(const bool flag)                      { this.SetProperty(BUFFER_PROP_ACTIVE,flag);                            }
   void              SetDrawType(const ENUM_DRAW_TYPE draw_type);
   void              SetDrawBegin(const int value);
   void              SetShowData(const bool flag);
   void              SetShift(const int shift);
   void              SetStyle(const ENUM_LINE_STYLE style);
   void              SetWidth(const int width);
   void              SetColorNumbers(const int number);
   void              SetColor(const color colour);
   void              SetColor(const color colour,const uchar index);
   void              SetColors(const color &array_colors[]);
   void              SetEmptyValue(const double value);
   virtual void      SetLabel(const string label);
   void              SetID(const int id)                             { this.SetProperty(BUFFER_PROP_ID,id);                                  }
   void              SetIndicatorHandle(const int handle)            { this.SetProperty(BUFFER_PROP_IND_HANDLE,handle);                      }
   void              SetIndicatorType(const ENUM_INDICATOR type)     { this.SetProperty(BUFFER_PROP_IND_TYPE,type);                          }
   void              SetIndicatorName(const string name)             { this.SetProperty(BUFFER_PROP_IND_NAME,name);                          }
   void              SetIndicatorShortName(const string name)        { this.SetProperty(BUFFER_PROP_IND_NAME_SHORT,name);                    }
   void              SetLineMode(const ENUM_INDICATOR_LINE_MODE mode){ this.SetProperty(BUFFER_PROP_IND_LINE_MODE,mode);                     }
   void              SetIndicatorLineAdditionalNumber(const int number){this.SetProperty(BUFFER_PROP_IND_LINE_ADDITIONAL_NUM,number);        }
   
//--- Возвращает (1) порядковый номер рисуемого буфера, (2) индекс связанного массива, (3) индекс буфера цвета,
//--- (4) индекс первого свободного связанного массива, (5) индекс следующего рисуемого буфера, (6) период данных буфера (7) статус буфера,
//--- (8) тип буфера, (9) флаг использования буфера, (10) код стрелки, (11) смещение стрелок для стиля DRAW_ARROW,
//--- (12) Количество начальных баров без отрисовки и значений в DataWindow, (13) тип графического построения,
//--- (14) флаг отображения значений построения в окне DataWindow, (15) сдвиг графического построения индикатора по оси времени,
//--- (16) стиль линии отрисовки, (17) толщину линии отрисовки, (18) количество цветов, (19) цвет отрисовки, (20) количество буферов для построения
//--- (21) установленное пустое значение, (22) символ буфера, (23) имя индикаторной графической серии, отображаемое в окне DataWindow
//--- (24) идентификатор буфера, (25) хэндл индикатора, (26) тип стандартного индикатора, (27) имя стандартного индикатора,
//--- (28) количество рассчитанных баров стандартного индикатора, (29) номер дополнительной линии индикатора, (30) тип линии (main, signal, etc)
   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               IndexNextBaseBuffer(void)                 const { return (int)this.GetProperty(BUFFER_PROP_INDEX_NEXT_BASE);            }
   int               IndexNextPlotBuffer(void)                 const { return (int)this.GetProperty(BUFFER_PROP_INDEX_NEXT_PLOT);            }
   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);                           }
   int               ID(void)                                  const { return (int)this.GetProperty(BUFFER_PROP_ID);                         }
   int               IndicatorHandle(void)                     const { return (int)this.GetProperty(BUFFER_PROP_IND_HANDLE);                 }
   ENUM_INDICATOR    IndicatorType(void)                       const { return (ENUM_INDICATOR)this.GetProperty(BUFFER_PROP_IND_TYPE);        }
   string            IndicatorName(void)                       const { return this.GetProperty(BUFFER_PROP_IND_NAME);                        }
   string            IndicatorShortName(void)                  const { return this.GetProperty(BUFFER_PROP_IND_NAME_SHORT);                  }
   int               IndicatorBarsCalculated(void)             const { return ::BarsCalculated((int)this.GetProperty(BUFFER_PROP_IND_HANDLE));}
   int               IndicatorLineAdditionalNumber(void)       const { return (int)this.GetProperty(BUFFER_PROP_IND_LINE_ADDITIONAL_NUM);    }
   int               IndicatorLineMode(void)                   const { return (int)this.GetProperty(BUFFER_PROP_IND_LINE_MODE);              }

Так как мы теперь будем выводить описание линии индикатора из другого перечисления (выше его написали), то метод IndicatorLineMode() теперь будет возвращать целочисленное значение вместо значения перечисления ENUM_INDICATOR_LINE_MODE.

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

//--- Возвращает описание (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;
   string            GetIndicatorLineModeDescription(void)     const;

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

//+------------------------------------------------------------------+
//| Возвращает описание линии буфера индикатора                      |
//+------------------------------------------------------------------+
string CBuffer::GetIndicatorLineModeDescription(void) const
  {
   uchar shift=0;
   switch(this.IndicatorType())
     {
      case IND_ENVELOPES   :
      case IND_FRACTALS    :
      case IND_GATOR       :
      case IND_BANDS       :  shift=2; break;
      case IND_ALLIGATOR   :  shift=5; break;
      case IND_ADX         :
      case IND_ADXW        :  shift=8; break;
      case IND_ICHIMOKU    :  shift=10;break;
      default              :  shift=0; break;
     }
   return ::StringSubstr(::EnumToString(ENUM_INDICATOR_LINE(this.GetProperty(BUFFER_PROP_IND_LINE_MODE)+shift)),10);
  }
//+------------------------------------------------------------------+

Здесь: объявляем переменную, хранящую значение смещения, на которое необходимо увеличить значения константы перечисления ENUM_INDICATOR_LINE_MODE так, чтобы попасть на начало объявления констант линий соответствующего индикатора в перечислении ENUM_INDICATOR_LINE.
Например, если нам нужно вывести описание линии Teeth индикатора Allegator, то значение смещения равно 5, что указывает на константу INDICATOR_LINE_JAWS перечисления ENUM_INDICATOR_LINE:

//+------------------------------------------------------------------+
//| Перечисление линий индикаторов                                   |
//+------------------------------------------------------------------+
enum ENUM_INDICATOR_LINE
  {
   INDICATOR_LINE_MAIN,                                     // Главная линия
   INDICATOR_LINE_SIGNAL,                                   // Сигнальная линия
   INDICATOR_LINE_UPPER,                                    // Верхняя линия
   INDICATOR_LINE_LOWER,                                    // Нижняя линия
   INDICATOR_LINE_MIDDLE,                                   // Средняя линия
   INDICATOR_LINE_JAWS,                                     // Линия Jaws
   INDICATOR_LINE_TEETH,                                    // Линия Teeth
   INDICATOR_LINE_LIPS,                                     // Линия Lips
   INDICATOR_LINE_DI_PLUS,                                  // Линия +DI
   INDICATOR_LINE_DI_MINUS,                                 // Линия -DI
   INDICATOR_LINE_TENKAN_SEN,                               // Линия Tenkan-sen
   INDICATOR_LINE_KIJUN_SEN,                                // Линия Kijun-sen
   INDICATOR_LINE_SENKOU_SPANA,                             // Линия Senkou Span A
   INDICATOR_LINE_SENKOU_SPANB,                             // Линия Senkou Span B
   INDICATOR_LINE_CHIKOU_SPAN,                              // Линия Chikou Span
   INDICATOR_LINE_ADDITIONAL,                               // Дополнительная линия
  };
//+------------------------------------------------------------------+

А так как у нас буфер возвращает значение линии Teeth из метода GetProperty(BUFFER_PROP_IND_LINE_MODE), и это значение равно единице, то прибавление к единице значения 5, даёт индекс константы, равный 6, что указывает на константу INDICATOR_LINE_TEETH.

В итоге метод возвращает строковое описание полученной константы, урезанное до значения "LINE_TEETH".

В закрытом конструкторе класса установим значение по умолчанию новому свойству объекта-буфера, равным -1, что будет означать не дополнительную линию индикатора:

//+------------------------------------------------------------------+
//| Закрытый параметрический конструктор                             |
//+------------------------------------------------------------------+
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 uchar total_arrays,
                 const int width,
                 const string label)
  {
   this.m_type=COLLECTION_BUFFERS_ID;
   this.m_act_state_trigger=true;
   this.m_total_arrays=total_arrays;
//--- Сохранение целочисленных свойств
   this.m_long_prop[BUFFER_PROP_STATUS]                        = buffer_status;
   this.m_long_prop[BUFFER_PROP_TYPE]                          = buffer_type;
   this.m_long_prop[BUFFER_PROP_ID]                            = WRONG_VALUE;
   this.m_long_prop[BUFFER_PROP_IND_LINE_MODE]                 = INDICATOR_LINE_MODE_MAIN;
   this.m_long_prop[BUFFER_PROP_IND_HANDLE]                    = INVALID_HANDLE;
   this.m_long_prop[BUFFER_PROP_IND_TYPE]                      = WRONG_VALUE;
   this.m_long_prop[BUFFER_PROP_IND_LINE_ADDITIONAL_NUM]       = WRONG_VALUE;
   ENUM_DRAW_TYPE type=
     (

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

//+------------------------------------------------------------------+
//| Возвращает описание целочисленного свойства буфера               |
//+------------------------------------------------------------------+
string CBuffer::GetPropertyDescription(ENUM_BUFFER_PROP_INTEGER property)
  {
   return
     (
      property==BUFFER_PROP_INDEX_PLOT    ?  CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_INDEX_PLOT)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==BUFFER_PROP_STATUS        ?  CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_STATUS)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+this.GetStatusDescription()
         )  :
      property==BUFFER_PROP_TYPE          ?  CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_TYPE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+this.GetTypeBufferDescription()
         )  :
      property==BUFFER_PROP_TIMEFRAME     ?  CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_TIMEFRAME)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+this.GetTimeframeDescription()
         )  :
      property==BUFFER_PROP_ACTIVE        ?  CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_ACTIVE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+this.GetActiveDescription()
         )  :
      property==BUFFER_PROP_DRAW_TYPE     ?  CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_DRAW_TYPE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+this.GetDrawTypeDescription()
         )  :
      property==BUFFER_PROP_ARROW_CODE    ?  CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_ARROW_CODE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==BUFFER_PROP_ARROW_SHIFT   ?  CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_ARROW_SHIFT)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==BUFFER_PROP_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_DRAW_BEGIN    ?  CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_DRAW_BEGIN)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==BUFFER_PROP_SHOW_DATA     ?  CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_SHOW_DATA)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+this.GetShowDataDescription()
         )  :
      property==BUFFER_PROP_SHIFT         ?  CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_SHIFT)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==BUFFER_PROP_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_INDEX_COLOR   ?  CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_INDEX_COLOR)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==BUFFER_PROP_INDEX_BASE    ?  CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_INDEX_BASE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==BUFFER_PROP_INDEX_NEXT_BASE ?  CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_INDEX_NEXT_BASE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==BUFFER_PROP_INDEX_NEXT_PLOT ?  CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_INDEX_NEXT_PLOT)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==BUFFER_PROP_ID ?  CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_ID)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==BUFFER_PROP_IND_LINE_MODE ?  CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_IND_LINE_MODE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+this.GetIndicatorLineModeDescription()
         )  :
      property==BUFFER_PROP_IND_HANDLE ?  CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_IND_HANDLE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==BUFFER_PROP_IND_TYPE ?  CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_IND_TYPE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::StringSubstr(::EnumToString((ENUM_INDICATOR)this.GetProperty(property)),4)
         )  :
      property==BUFFER_PROP_IND_LINE_ADDITIONAL_NUM ?  CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_IND_LINE_ADDITIONAL_NUM)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.GetProperty(property)==WRONG_VALUE ? CMessage::Text(MSG_LIB_PROP_NOT_SET) : (string)this.GetProperty(property))
         )  :
      property==BUFFER_PROP_NUM_DATAS     ?  CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_NUM_DATAS)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==BUFFER_PROP_COLOR         ?  CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_COLOR)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+this.GetColorsDescription()
         )  :
      ""
     );
  }
//+------------------------------------------------------------------+

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

//+------------------------------------------------------------------+
//| Возвращает истину, если буфер поддерживает переданное            |
//| целочисленное свойство, возвращает ложь в противном случае       |
//+------------------------------------------------------------------+
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_BASE &&
       property!=BUFFER_PROP_IND_LINE_MODE && 
       property!=BUFFER_PROP_IND_HANDLE &&
       property!=BUFFER_PROP_IND_TYPE &&
       property!=BUFFER_PROP_IND_LINE_ADDITIONAL_NUM &&
       property!=BUFFER_PROP_ID
      )
     )
      return false;
   return true;
  }
//+------------------------------------------------------------------+

Точно такие же изменения были внесены во все файлы всех объектов-буферов, таких как BufferArrow.mqh, BufferBars.mqh, BufferCandles.mqh, BufferFilling.mqh, BufferHistogram.mqh, BufferHistogram2.mqh, BufferLine.mqh, BufferSection.mqh и BufferZigZag.mqh.

Все методы для создания объектов стандартных индикаторов у нас расположены в классе-коллекции объектов-буферов
в файле \MQL5\Include\DoEasy\Collections\BuffersCollection.mqh. Прежде, чем добавим два метода для создания оъектов стандартных индикаторов Ichimoku Kinko Hyo и Gator Oscillator, немного доработаем метод подготовки данных указанного стандартного индикатора для установки значений на текущий график символа. Так как в индикаторе Ichimoku Kinko Hyo пять рисуемых буферов, а у нас в метод передаётся только три указателя на объекты-буферы стандартных индикаторов и, соответственно, шесть переменных по ссылке (по две на каждый из буферов) для записи в них значений индикаторных линий, то нам необходимо к методу добавить передачу в него четырёх дополнительных указателей на объекты-буферы (по два для рисуемых и расчётных) и передачу ещё четырёх переменных по ссылке.

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

//--- Устанавливает значения для текущего графика в буферы указанного стандартного индикатора по индексу таймсерии в соответствии с символом/периодом объекта-буфера
   bool                    SetDataBufferStdInd(const ENUM_INDICATOR std_ind,const int id,const int series_index,const datetime series_time,const char color_index=WRONG_VALUE);
private:
//--- Подготавливает данные указанного стандартного индикатора для установки значений на текущий график символа
   int                     PreparingSetDataStdInd(CBuffer *buffer_data0,CBuffer *buffer_data1,CBuffer *buffer_data2,CBuffer *buffer_data3,CBuffer *buffer_data4,
                                                  CBuffer *buffer_calc0,CBuffer *buffer_calc1,CBuffer *buffer_calc2,CBuffer *buffer_calc3,CBuffer *buffer_calc4,
                                                  const ENUM_INDICATOR ind_type,
                                                  const int series_index,
                                                  const datetime series_time,
                                                  int &index_period,
                                                  int &num_bars,
                                                  double &value00,
                                                  double &value01,
                                                  double &value10,
                                                  double &value11,
                                                  double &value20,
                                                  double &value21,
                                                  double &value30,
                                                  double &value31,
                                                  double &value40,
                                                  double &value41);
public:

//--- Возвращает буфер (1) по имени графической серии, (2) по таймфрейму,

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

//+------------------------------------------------------------------+
//| Подготавливает данные указанного стандартного индикатора         |
//| для установки значений на текущий график символа                 |
//+------------------------------------------------------------------+
int CBuffersCollection::PreparingSetDataStdInd(CBuffer *buffer_data0,CBuffer *buffer_data1,CBuffer *buffer_data2,CBuffer *buffer_data3,CBuffer *buffer_data4,
                                               CBuffer *buffer_calc0,CBuffer *buffer_calc1,CBuffer *buffer_calc2,CBuffer *buffer_calc3,CBuffer *buffer_calc4,
                                               const ENUM_INDICATOR ind_type,
                                               const int series_index,
                                               const datetime series_time,
                                               int &index_period,
                                               int &num_bars,
                                               double &value00,
                                               double &value01,
                                               double &value10,
                                               double &value11,
                                               double &value20,
                                               double &value21,
                                               double &value30,
                                               double &value31,
                                               double &value40,
                                               double &value41)
  {
     //--- Находим индекс бара на периоде, соответствующий времени начала текущего бара
     index_period=::iBarShift(buffer_calc0.Symbol(),buffer_calc0.Timeframe(),series_time,true);
     if(index_period==WRONG_VALUE || index_period>buffer_calc0.GetDataTotal()-1)
        return WRONG_VALUE;
     
     //--- Получаем значение по этому индексу из буфера индикатора
     if(buffer_calc0!=NULL)
        value00=buffer_calc0.GetDataBufferValue(0,index_period);
     if(buffer_calc1!=NULL)
        value10=buffer_calc1.GetDataBufferValue(0,index_period);
     if(buffer_calc2!=NULL)
        value20=buffer_calc2.GetDataBufferValue(0,index_period);
     if(buffer_calc3!=NULL)
        value30=buffer_calc3.GetDataBufferValue(0,index_period);
     if(buffer_calc4!=NULL)
        value40=buffer_calc4.GetDataBufferValue(0,index_period);
     
     int series_index_start=series_index;
     //--- Для текущего графика никаких расчётов количества обрабатываемых баров делать не нужно - бар всего один
     if(buffer_calc0.Symbol()==::Symbol() && buffer_calc0.Timeframe()==::Period())
       {
        series_index_start=series_index;
        num_bars=1;
       }
     else
       {
        //--- Получаем время бара, в который попадает бар с индексом index_period на периоде и символе расчётного буфера
        datetime time_period=::iTime(buffer_calc0.Symbol(),buffer_calc0.Timeframe(),index_period);
        if(time_period==0) return false;
        //--- Получаем соответствующий времени бар текущего графика
        series_index_start=::iBarShift(::Symbol(),::Period(),time_period,true);
        if(series_index_start==WRONG_VALUE) return WRONG_VALUE;
        //--- Рассчитываем количество баров на текущем графике, которые нужно заполнить данными расчётного буфера
        num_bars=::PeriodSeconds(buffer_calc0.Timeframe())/::PeriodSeconds(PERIOD_CURRENT);
        if(num_bars==0) num_bars=1;
       }
     //--- Записываем значения для расчёта цвета
     if(buffer_calc0!=NULL)
        value01=(series_index_start+num_bars>buffer_data0.GetDataTotal()-1 ? value00 : buffer_data0.GetDataBufferValue(0,series_index_start+num_bars));
     if(buffer_calc1!=NULL)
        value11=(series_index_start+num_bars>buffer_data1.GetDataTotal()-1 ? value10 : buffer_data1.GetDataBufferValue(0,series_index_start+num_bars));
     if(buffer_calc2!=NULL)
        value21=(series_index_start+num_bars>buffer_data2.GetDataTotal()-1 ? value20 : buffer_data2.GetDataBufferValue(0,series_index_start+num_bars));
     if(buffer_calc3!=NULL)
        value31=(series_index_start+num_bars>buffer_data3.GetDataTotal()-1 ? value30 : buffer_data3.GetDataBufferValue(0,series_index_start+num_bars));
     if(buffer_calc4!=NULL)
        value41=(series_index_start+num_bars>buffer_data4.GetDataTotal()-1 ? value40 : buffer_data4.GetDataBufferValue(0,series_index_start+num_bars));
   
   return series_index_start;
  }
//+------------------------------------------------------------------+

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

Так как в стандартном индикаторе Ichimoku Kinko Hyo между двумя его линиями рисуется штриховка, и при этом для каждой из линий устанавливается свой тип штриховки и цвета, соответствующий типу и цвету линии:


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

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

public:

//--- Возвращает буфер (1) по имени графической серии, (2) по таймфрейму,
//--- (3) по индексу Plot, (4) по индексу объекта в списке-коллекции, (5) последний созданный,
//--- (6) буфер стандартного индикатора по типу индикатора, его идентификатору и линии
   CBuffer                *GetBufferByLabel(const string plot_label);
   CBuffer                *GetBufferByTimeframe(const ENUM_TIMEFRAMES timeframe);
   CBuffer                *GetBufferByPlot(const int plot_index);
   CBuffer                *GetBufferByListIndex(const int index_list);
   CBuffer                *GetLastCreateBuffer(void);
   CBuffer                *GetBufferStdInd(const ENUM_INDICATOR indicator_type,const int id,const ENUM_INDICATOR_LINE_MODE line_mode,const char additional_id=WRONG_VALUE);
//--- Возвращает список буферов (1) по идентификатору, (2) по типу стандартного индикатора, (3) по типу и идентификатору

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

//+------------------------------------------------------------------+
//| Возвращает буфер стандартного индикатора                         |
//| по типу индикатора, его идентификатору и линии                   |
//+------------------------------------------------------------------+
CBuffer *CBuffersCollection::GetBufferStdInd(const ENUM_INDICATOR indicator_type,const int id,const ENUM_INDICATOR_LINE_MODE line_mode,const char additional_id=WRONG_VALUE)
  {
   CArrayObj *list=this.GetListBufferByTypeID(indicator_type,id);
   list=CSelect::ByBufferProperty(list,BUFFER_PROP_IND_LINE_MODE,line_mode,EQUAL);
   list=CSelect::ByBufferProperty(list,BUFFER_PROP_IND_LINE_ADDITIONAL_NUM,additional_id,EQUAL);
   if(list==NULL)
      return NULL;
   return list.At(0);
  }
//+------------------------------------------------------------------+

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

Дополним метод, подготавливающий данные расчётного буфера указанного стандартного индикатора для обработки индикаторов Ichimoku Kinko Hyo и Gator Oscillator.
В принципе, нам нужно лишь дописать соответствующую типу индикаторов обработку, которая идентична для всех индикаторов, и уже написана в методе. Но есть небольшое отличие для стандартного индикатора Gator Oscillator его второй буфер имеет индекс 2, а не 1 как у остальных двухбуферных индикаторов по причине того, что буфер с индексом 1 принадлежит буферу цвета буфера данных с индексом 0. Буферы цвета стандартного индикатора мы использовать не будем, так как у нас уже создана обработка и установка цвета для линий индикаторов, плюс мы можем сами задавать нужный цвет для каждого бара, поэтому по умолчанию цвет столбцов индикатора Gator Oscillator будет рассчитываться автоматически библиотекой, и такой расчёт уже сделан, а при желании пользователь может сам передавать нужный цвет для каждого бара в методы рисования данных стандартных индикаторов для того, чтобы создать собственную цветовую окраску индикатора.
Для индикатора Ichimoku Kinko Hyo подготовка данных идентична подготовке данных других стандартных индикаторов — лишь на два буфера больше
.

//+------------------------------------------------------------------+
//| Подготавливает данные расчётного буфера                          |
//| указанного стандартного индикатора                               |
//+------------------------------------------------------------------+
int CBuffersCollection::PreparingDataBufferStdInd(const ENUM_INDICATOR std_ind,const int id,const int total_copy)
  {
   CArrayObj *list_ind=this.GetListBufferByTypeID(std_ind,id);
   CArrayObj *list;
   list_ind=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_TYPE,BUFFER_TYPE_CALCULATE,EQUAL);
   if(list_ind==NULL || list_ind.Total()==0)
     {
      ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_NO_BUFFER_OBJ));
      return 0;
     }
   CBufferCalculate *buffer=NULL;
   int copied=WRONG_VALUE;
   int idx0=0,idx1=1,idx2=2;
   switch((int)std_ind)
     {
   //--- Однобуферные стандартные индикаторы
      case IND_AC          :
      case IND_AD          :
      case IND_AMA         :
      case IND_AO          :
      case IND_ATR         :
      case IND_BEARS       :
      case IND_BULLS       :
      case IND_BWMFI       :
      case IND_CCI         :
      case IND_CHAIKIN     :
      case IND_DEMA        :
      case IND_DEMARKER    :
      case IND_FORCE       :
      case IND_FRAMA       :
      case IND_MA          :
      case IND_MFI         :
      case IND_MOMENTUM    :
      case IND_OBV         :
      case IND_OSMA        :
      case IND_RSI         :
      case IND_SAR         :
      case IND_STDDEV      :
      case IND_TEMA        :
      case IND_TRIX        :
      case IND_VIDYA       :
      case IND_VOLUMES     :
      case IND_WPR         :
      
        buffer=list_ind.At(0);
        if(buffer==NULL) return 0;
        copied=buffer.FillAsSeries(buffer.IndicatorHandle(),0,buffer.Shift(),total_copy);
        return copied;
      
   //--- Многобуферные стандартные индикаторы
      case IND_ENVELOPES   :
      case IND_FRACTALS    :
      case IND_MACD        :
      case IND_RVI         :
      case IND_STOCHASTIC  :
      case IND_GATOR       :
        if(std_ind==IND_GATOR)
          {
           idx0=0;
           idx1=2;
          }
        else
          {
           idx0=0;
           idx1=1;
          }
        list=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,0,EQUAL);
        buffer=list.At(0);
        if(buffer==NULL) return 0;
        copied=buffer.FillAsSeries(buffer.IndicatorHandle(),idx0,buffer.Shift(),total_copy);
        if(copied<total_copy) return 0;
        
        list=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,1,EQUAL);
        buffer=list.At(0);
        if(buffer==NULL) return 0;
        copied=buffer.FillAsSeries(buffer.IndicatorHandle(),idx1,buffer.Shift(),total_copy);
        if(copied<total_copy) return 0;
        return copied;
      
      case IND_ALLIGATOR   :
      case IND_ADX         :
      case IND_ADXW        :
      case IND_BANDS       :
        if(std_ind==IND_BANDS)
          {
           idx0=1;
           idx1=2;
           idx2=0;
          }
        else
          {
           idx0=0;
           idx1=1;
           idx2=2;
          }
        list=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,0,EQUAL);
        buffer=list.At(0);
        if(buffer==NULL) return 0;
        copied=buffer.FillAsSeries(buffer.IndicatorHandle(),idx0,buffer.Shift(),total_copy);
        if(copied<total_copy) return 0;
        
        list=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,1,EQUAL);
        buffer=list.At(0);
        if(buffer==NULL) return 0;
        copied=buffer.FillAsSeries(buffer.IndicatorHandle(),idx1,buffer.Shift(),total_copy);
        if(copied<total_copy) return 0;
        
        list=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,2,EQUAL);
        buffer=list.At(0);
        if(buffer==NULL) return 0;
        copied=buffer.FillAsSeries(buffer.IndicatorHandle(),idx2,buffer.Shift(),total_copy);
        if(copied<total_copy) return 0;
        return copied;
      
      case IND_ICHIMOKU    :
        list=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_TENKAN_SEN,EQUAL);
        buffer=list.At(0);
        if(buffer==NULL) return 0;
        copied=buffer.FillAsSeries(buffer.IndicatorHandle(),TENKANSEN_LINE,buffer.Shift(),total_copy);
        if(copied<total_copy) return 0;
        
        list=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_KIJUN_SEN,EQUAL);
        buffer=list.At(0);
        if(buffer==NULL) return 0;
        copied=buffer.FillAsSeries(buffer.IndicatorHandle(),KIJUNSEN_LINE,buffer.Shift(),total_copy);
        if(copied<total_copy) return 0;
        
        list=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_SENKOU_SPANA,EQUAL);
        buffer=list.At(0);
        if(buffer==NULL) return 0;
        copied=buffer.FillAsSeries(buffer.IndicatorHandle(),SENKOUSPANA_LINE,buffer.Shift(),total_copy);
        if(copied<total_copy) return 0;
        
        list=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_SENKOU_SPANB,EQUAL);
        buffer=list.At(0);
        if(buffer==NULL) return 0;
        copied=buffer.FillAsSeries(buffer.IndicatorHandle(),SENKOUSPANB_LINE,buffer.Shift(),total_copy);
        if(copied<total_copy) return 0;
        
        list=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_CHIKOU_SPAN,EQUAL);
        buffer=list.At(0);
        if(buffer==NULL) return 0;
        copied=buffer.FillAsSeries(buffer.IndicatorHandle(),CHIKOUSPAN_LINE,buffer.Shift(),total_copy);
        if(copied<total_copy) return 0;
        return copied;
      
      default:
        break;
     }
   return 0;
  }
//+------------------------------------------------------------------+

В метод очистки данных буфера указанного стандартного индикатора по индексу таймсерии
добавим обработку буферов индикатора Ichimoku Kinko Hyo
:

//+------------------------------------------------------------------+
//| Очищает данные буфера указанного стандартного индикатора         |
//| по индексу таймсерии                                             |
//+------------------------------------------------------------------+
void CBuffersCollection::ClearDataBufferStdInd(const ENUM_INDICATOR std_ind,const int id,const int series_index)
  {
//--- Получаем список объектов-буферов по типу и идентификатору
   CArrayObj *list_ind=this.GetListBufferByTypeID(std_ind,id);
   CArrayObj *list=NULL;
   if(list_ind==NULL || list_ind.Total()==0)
      return;
   list_ind=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_TYPE,BUFFER_TYPE_DATA,EQUAL);
   if(list_ind.Total()==0)
      return;
   CBuffer *buffer=NULL;
   switch((int)std_ind)
     {
   //--- Однобуферные стандартные индикаторы
      case IND_AC          :
      case IND_AD          :
      case IND_AMA         :
      case IND_AO          :
      case IND_ATR         :
      case IND_BEARS       :
      case IND_BULLS       :
      case IND_BWMFI       :
      case IND_CCI         :
      case IND_CHAIKIN     :
      case IND_DEMA        :
      case IND_DEMARKER    :
      case IND_FORCE       :
      case IND_FRAMA       :
      case IND_MA          :
      case IND_MFI         :
      case IND_MOMENTUM    :
      case IND_OBV         :
      case IND_OSMA        :
      case IND_RSI         :
      case IND_SAR         :
      case IND_STDDEV      :
      case IND_TEMA        :
      case IND_TRIX        :
      case IND_VIDYA       :
      case IND_VOLUMES     :
      case IND_WPR         :
        buffer=list_ind.At(0);
        if(buffer==NULL) return;
        buffer.SetBufferValue(0,series_index,buffer.EmptyValue());
        break;
      
   //--- Многобуферные стандартные индикаторы
      case IND_ENVELOPES   :
      case IND_FRACTALS    :
      case IND_MACD        :
      case IND_RVI         :
      case IND_STOCHASTIC  :
      case IND_GATOR       :
        list=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,0,EQUAL);
        buffer=list.At(0);
        if(buffer==NULL) return;
        buffer.SetBufferValue(0,series_index,buffer.EmptyValue());
        
        list=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,1,EQUAL);
        buffer=list.At(0);
        if(buffer==NULL) return;
        buffer.SetBufferValue(0,series_index,buffer.EmptyValue());
        break;

      case IND_ALLIGATOR   :
      case IND_ADX         :
      case IND_ADXW        :
      case IND_BANDS       :
        list=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,0,EQUAL);
        buffer=list.At(0);
        if(buffer==NULL) return;
        buffer.SetBufferValue(0,series_index,buffer.EmptyValue());
        
        list=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,1,EQUAL);
        buffer=list.At(0);
        if(buffer==NULL) return;
        buffer.SetBufferValue(0,series_index,buffer.EmptyValue());
        
        list=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,2,EQUAL);
        buffer=list.At(0);
        if(buffer==NULL) return;
        buffer.SetBufferValue(0,series_index,buffer.EmptyValue());
        break;
      
      case IND_ICHIMOKU :
        list=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_TENKAN_SEN,EQUAL);
        buffer=list.At(0);
        if(buffer==NULL) return;
        buffer.SetBufferValue(0,series_index,buffer.EmptyValue());
        
        list=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_KIJUN_SEN,EQUAL);
        buffer=list.At(0);
        if(buffer==NULL) return;
        buffer.SetBufferValue(0,series_index,buffer.EmptyValue());
        
        list=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_SENKOU_SPANA,EQUAL);
        buffer=list.At(0);
        if(buffer==NULL) return;
        buffer.SetBufferValue(0,series_index,buffer.EmptyValue());
        
        list=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_SENKOU_SPANB,EQUAL);
        buffer=list.At(0);
        if(buffer==NULL) return;
        buffer.SetBufferValue(0,series_index,buffer.EmptyValue());
        
        list=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_CHIKOU_SPAN,EQUAL);
        buffer=list.At(0);
        if(buffer==NULL) return;
        buffer.SetBufferValue(0,series_index,buffer.EmptyValue());
        
        list=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_ADDITIONAL,EQUAL);
        list=CSelect::ByBufferProperty(list,BUFFER_PROP_IND_LINE_ADDITIONAL_NUM,0,EQUAL);
        buffer=list.At(0);
        if(buffer==NULL) return;
        buffer.SetBufferValue(0,series_index,buffer.EmptyValue());
        
        list=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_ADDITIONAL,EQUAL);
        list=CSelect::ByBufferProperty(list,BUFFER_PROP_IND_LINE_ADDITIONAL_NUM,1,EQUAL);
        buffer=list.At(0);
        if(buffer==NULL) return;
        buffer.SetBufferValue(0,series_index,buffer.EmptyValue());
        break;
      
      default:
        break;
     }
  }
//+------------------------------------------------------------------+

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

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

Теперь в методе будет больше объектов-буферов, так как у индикатора Ichimoku Kinko Hyo их пять, плюс два вспомогательных буфера-гистограммы для оформления внешнего вида индикатора. Соответственно, и количество переменных для хранения значений всех линий объектов-буферов у нас увеличилось:

//+------------------------------------------------------------------+
//| Устанавливает значения для текущего графика в буферы указанного  |
//| стандартного индикатора по индексу таймсерии в соответствии      |
//| с символом/периодом объекта-буфера                               |
//+------------------------------------------------------------------+
bool CBuffersCollection::SetDataBufferStdInd(const ENUM_INDICATOR ind_type,const int id,const int series_index,const datetime series_time,const char color_index=WRONG_VALUE)
  {
//--- Получаем список объектов-буферов по типу и идентификатору
   CArrayObj *list=this.GetListBufferByTypeID(ind_type,id);
   if(list==NULL || list.Total()==0)
     {
      ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_NO_BUFFER_OBJ));
      return false;
     }
//--- Получаем список рисуемых буферов с идентификатором
   CArrayObj *list_data=CSelect::ByBufferProperty(list,BUFFER_PROP_TYPE,BUFFER_TYPE_DATA,EQUAL);
   list_data=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_TYPE,ind_type,EQUAL);
//--- Получаем список расчётных буферов с идентификатором
   CArrayObj *list_calc=CSelect::ByBufferProperty(list,BUFFER_PROP_TYPE,BUFFER_TYPE_CALCULATE,EQUAL);
   list_calc=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_TYPE,ind_type,EQUAL);
//--- Если любой из списков пустой - уходим
   if(list_data.Total()==0 || list_calc.Total()==0)
      return false;
  
//--- Объявляем необходимые объекты и переменные
   CBuffer *buffer_data0=NULL,*buffer_data1=NULL,*buffer_data2=NULL,*buffer_data3=NULL,*buffer_data4=NULL,*buffer_tmp0=NULL,*buffer_tmp1=NULL;
   CBuffer *buffer_calc0=NULL,*buffer_calc1=NULL,*buffer_calc2=NULL,*buffer_calc3=NULL,*buffer_calc4=NULL;
   double value00=EMPTY_VALUE, value01=EMPTY_VALUE;
   double value10=EMPTY_VALUE, value11=EMPTY_VALUE;
   double value20=EMPTY_VALUE, value21=EMPTY_VALUE;
   double value30=EMPTY_VALUE, value31=EMPTY_VALUE;
   double value40=EMPTY_VALUE, value41=EMPTY_VALUE;
   double value_tmp0=EMPTY_VALUE,value_tmp1=EMPTY_VALUE;
   long vol0=0,vol1=0;
   int series_index_start=series_index,index_period=0, index=0,num_bars=1;
   uchar clr=0;
//--- В зависимости от типа стандартного индикатора

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

        series_index_start=PreparingSetDataStdInd(buffer_data0,buffer_data1,buffer_data2,buffer_data3,buffer_data4,
                                                  buffer_calc0,buffer_calc1,buffer_calc2,buffer_calc3,buffer_calc4,
                                                  ind_type,series_index,series_time,index_period,num_bars,
                                                  value00,value01,value10,value11,value20,value21,value30,value31,value40,value41);

В конце метода добавим обработчики стандартных индикаторов Ichimoku Kinko Hyo и Gator Oscillator:

      case IND_ICHIMOKU :
        list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_TENKAN_SEN,EQUAL);
        buffer_data0=list.At(0);
        list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_KIJUN_SEN,EQUAL);
        buffer_data1=list.At(0);
        list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_SENKOU_SPANA,EQUAL);
        buffer_data2=list.At(0);
        list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_SENKOU_SPANB,EQUAL);
        buffer_data3=list.At(0);
        list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_CHIKOU_SPAN,EQUAL);
        buffer_data4=list.At(0);
        
        //--- Получаем список объектов-буферов, у которых есть идентификатор вспомогательной линии, а из него - объект-буфер с номером линии 0
        list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_ADDITIONAL,EQUAL);
        list=CSelect::ByBufferProperty(list,BUFFER_PROP_IND_LINE_ADDITIONAL_NUM,0,EQUAL);
        buffer_tmp0=list.At(0);
        //--- Получаем список объектов-буферов, у которых есть идентификатор вспомогательной линии, а из него - объект-буфер с номером линии 1
        list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_ADDITIONAL,EQUAL);
        list=CSelect::ByBufferProperty(list,BUFFER_PROP_IND_LINE_ADDITIONAL_NUM,1,EQUAL);
        buffer_tmp1=list.At(0);
        
        list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_TENKAN_SEN,EQUAL);
        buffer_calc0=list.At(0);
        list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_KIJUN_SEN,EQUAL);
        buffer_calc1=list.At(0);
        list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_SENKOU_SPANA,EQUAL);
        buffer_calc2=list.At(0);
        list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_SENKOU_SPANB,EQUAL);
        buffer_calc3=list.At(0);
        list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_CHIKOU_SPAN,EQUAL);
        buffer_calc4=list.At(0);
        
        if(buffer_calc0==NULL || buffer_data0==NULL || buffer_calc0.GetDataTotal(0)==0)
           return false;
        if(buffer_calc1==NULL || buffer_data1==NULL || buffer_calc1.GetDataTotal(0)==0)
           return false;
        if(buffer_calc2==NULL || buffer_data2==NULL || buffer_calc2.GetDataTotal(0)==0)
           return false;
        if(buffer_calc3==NULL || buffer_data3==NULL || buffer_calc3.GetDataTotal(0)==0)
           return false;
        if(buffer_calc4==NULL || buffer_data4==NULL || buffer_calc4.GetDataTotal(0)==0)
           return false;
        
        series_index_start=PreparingSetDataStdInd(buffer_data0,buffer_data1,buffer_data2,buffer_data3,buffer_data4,
                                                  buffer_calc0,buffer_calc1,buffer_calc2,buffer_calc3,buffer_calc4,
                                                  ind_type,series_index,series_time,index_period,num_bars,
                                                  value00,value01,value10,value11,value20,value21,value30,value31,value40,value41);
        if(series_index_start==WRONG_VALUE)
           return false;
        //--- В цикле по количеству баров в num_bars заполняем рисуемый буфер значением из расчётного буфера, взятых по индексу index_period
        //--- и устанавливаем цвет рисуемого буфера в зависимости от соотношения значений value00 и value01
        for(int i=0;i<num_bars;i++)
          {
           index=series_index_start-i;
           buffer_data0.SetBufferValue(0,index,value00);
           buffer_data1.SetBufferValue(0,index,value10);
           buffer_data2.SetBufferValue(0,index,value20);
           buffer_data3.SetBufferValue(0,index,value30);
           buffer_data4.SetBufferValue(0,index,value40);
           buffer_data0.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value00>value01 ? 0 : value00<value01 ? 1 : 2) : color_index);
           buffer_data1.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value10>value11 ? 0 : value10<value11 ? 1 : 2) : color_index);
           buffer_data2.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value20>value21 ? 0 : value20<value21 ? 1 : 2) : color_index);
           buffer_data3.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value30>value31 ? 0 : value30<value31 ? 1 : 2) : color_index);
           buffer_data4.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value40>value41 ? 0 : value40<value41 ? 1 : 2) : color_index);
           
           //--- Установим значения для вспомогательных линий индикатора в зависимости от взаимного расположения линий Senkou Span A и Senkou Span B
           value_tmp0=buffer_data2.GetDataBufferValue(0,index);
           value_tmp1=buffer_data3.GetDataBufferValue(0,index);
           if(value_tmp0<value_tmp1)
             {
              buffer_tmp0.SetBufferValue(0,index,buffer_tmp0.EmptyValue());
              buffer_tmp0.SetBufferValue(1,index,buffer_tmp0.EmptyValue());
              
              buffer_tmp1.SetBufferValue(0,index,value_tmp0);
              buffer_tmp1.SetBufferValue(1,index,value_tmp1);
             }
           else
             {
              buffer_tmp0.SetBufferValue(0,index,value_tmp0);
              buffer_tmp0.SetBufferValue(1,index,value_tmp1);
              
              buffer_tmp1.SetBufferValue(0,index,buffer_tmp1.EmptyValue());
              buffer_tmp1.SetBufferValue(1,index,buffer_tmp1.EmptyValue());
             }
           
          }
        return true;
      
      case IND_GATOR    :
        list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,0,EQUAL);
        buffer_data0=list.At(0);
        list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,1,EQUAL);
        buffer_data1=list.At(0);
           
        list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,0,EQUAL);
        buffer_calc0=list.At(0);
        list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,1,EQUAL);
        buffer_calc1=list.At(0);
           
        if(buffer_calc0==NULL || buffer_data0==NULL || buffer_calc0.GetDataTotal(0)==0)
           return false;
        if(buffer_calc1==NULL || buffer_data1==NULL || buffer_calc1.GetDataTotal(0)==0)
           return false;
        
        series_index_start=PreparingSetDataStdInd(buffer_data0,buffer_data1,buffer_data2,buffer_data3,buffer_data4,
                                                  buffer_calc0,buffer_calc1,buffer_calc2,buffer_calc3,buffer_calc4,
                                                  ind_type,series_index,series_time,index_period,num_bars,
                                                  value00,value01,value10,value11,value20,value21,value30,value31,value40,value41);
        if(series_index_start==WRONG_VALUE)
           return false;
        //--- В цикле по количеству баров в num_bars заполняем рисуемый буфер значением из расчётного буфера, взятых по индексу index_period
        //--- и устанавливаем цвет рисуемого буфера в зависимости от соотношения значений value00 и value01
        for(int i=0;i<num_bars;i++)
          {
           index=series_index_start-i;
           buffer_data0.SetBufferValue(0,index,value00);
           buffer_data1.SetBufferValue(1,index,value10);
           buffer_data0.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value00>value01 ? 0 : value00<value01 ? 1 : 2) : color_index);
           buffer_data1.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value10<value11 ? 0 : value10>value11 ? 1 : 2) : color_index);
          }
        return true;
      
      default:
        break;

В коде прокомментированы действия, отличающиеся от точно таких же блоков кода для обработки других стандартных индикаторов, рассмотренных нами в предыдущих статьях — это коснулось только получения и обработки данных для внешнего оформления индикатора Ichimoku Kinko Hyo. Для индикатора Gator Oscillator вся логика осталась такой же, как и для других стандартных индикаторов.

Теперь напишем методы для создания объектов стандартных индикаторов Ichimoku Kinko Hyo и Gator Oscillator.

Метод для создания Gator Oscillator:

//+------------------------------------------------------------------+
//| Создаёт мультисимвольный мультипериодный Gator                   |
//+------------------------------------------------------------------+
int CBuffersCollection::CreateGator(const string symbol,const ENUM_TIMEFRAMES timeframe,
                                    const int jaw_period,
                                    const int jaw_shift,
                                    const int teeth_period,
                                    const int teeth_shift,
                                    const int lips_period,
                                    const int lips_shift,
                                    const ENUM_MA_METHOD ma_method,
                                    const ENUM_APPLIED_PRICE applied_price,
                                    const int id=WRONG_VALUE)
  {
//--- Создаём хэндл индикатора и устанавливаем идентификатор по умолчанию
   int num_bars=::PeriodSeconds(timeframe)/::PeriodSeconds(PERIOD_CURRENT);
   int shift=::fmin(jaw_shift,teeth_shift);
   int handle=::iGator(symbol,timeframe,jaw_period,jaw_shift,teeth_period,teeth_shift,lips_period,lips_shift,ma_method,applied_price);
   int identifier=(id==WRONG_VALUE ? IND_GATOR : id);
   color array_colors[3]={clrGreen,clrRed,clrGreen};
   CBuffer *buff=NULL;
   if(handle!=INVALID_HANDLE)
     {
      //--- Создаём буфер-гистограмму от нулевой линии
      this.CreateHistogram();
      //--- Получаем последний созданный объект-буфер (рисуемый) и устанавливаем ему все необходимые параметры линии Up
      buff=this.GetLastCreateBuffer();
      if(buff==NULL)
         return INVALID_HANDLE;
      buff.SetSymbol(symbol);
      buff.SetTimeframe(timeframe);
      buff.SetShift(shift*num_bars);
      buff.SetID(identifier);
      buff.SetIndicatorHandle(handle);
      buff.SetIndicatorType(IND_GATOR);
      buff.SetShowData(true);
      buff.SetLineMode(INDICATOR_LINE_MODE_UPPER);
      buff.SetIndicatorName("Gator Oscillator");
      buff.SetIndicatorShortName("Gator("+symbol+","+TimeframeDescription(timeframe)+": "+(string)jaw_period+","+(string)teeth_period+","+(string)lips_period+")");
      buff.SetLabel("Gator("+symbol+","+TimeframeDescription(timeframe)+": "+(string)jaw_period+","+(string)teeth_period+","+(string)lips_period+","+") Up");
      buff.SetColors(array_colors);
      
      //--- Создаём буфер-гистограмму от нулевой линии
      this.CreateHistogram();
      //--- Получаем последний созданный объект-буфер (рисуемый) и устанавливаем ему все необходимые параметры линии Down
      buff=this.GetLastCreateBuffer();
      if(buff==NULL)
         return INVALID_HANDLE;
      buff.SetSymbol(symbol);
      buff.SetTimeframe(timeframe);
      buff.SetShift(shift*num_bars);
      buff.SetID(identifier);
      buff.SetIndicatorHandle(handle);
      buff.SetIndicatorType(IND_GATOR);
      buff.SetShowData(true);
      buff.SetLineMode(INDICATOR_LINE_MODE_LOWER);
      buff.SetIndicatorName("Gator Oscillator");
      buff.SetIndicatorShortName("Gator("+symbol+","+TimeframeDescription(timeframe)+": "+(string)jaw_period+","+(string)teeth_period+","+(string)lips_period+")");
      buff.SetLabel("Gator("+symbol+","+TimeframeDescription(timeframe)+": "+(string)jaw_period+","+(string)teeth_period+","+(string)lips_period+","+") Down");
      buff.SetColors(array_colors);
      
      //--- Создаём расчётный буфер линии Up, в котором будут храниться данные стандартного индикатора
      this.CreateCalculate();
      //--- Получаем последний созданный объект-буфер (расчётный) и устанавливаем ему все необходимые параметры линии Up
      buff=this.GetLastCreateBuffer();
      if(buff==NULL)
         return INVALID_HANDLE;
      buff.SetSymbol(symbol);
      buff.SetTimeframe(timeframe);
      buff.SetShift(shift);
      buff.SetID(identifier);
      buff.SetIndicatorHandle(handle);
      buff.SetIndicatorType(IND_GATOR);
      buff.SetEmptyValue(EMPTY_VALUE);
      buff.SetLineMode(INDICATOR_LINE_MODE_UPPER);
      buff.SetIndicatorName("Gator Oscillator");
      buff.SetLabel("Gator("+symbol+","+TimeframeDescription(timeframe)+": "+(string)jaw_period+","+(string)teeth_period+","+(string)lips_period+","+") Up");
      
      //--- Создаём расчётный буфер линии Teeth, в котором будут храниться данные стандартного индикатора
      this.CreateCalculate();
      //--- Получаем последний созданный объект-буфер (расчётный) и устанавливаем ему все необходимые параметры линии Teeth
      buff=this.GetLastCreateBuffer();
      if(buff==NULL)
         return INVALID_HANDLE;
      buff.SetSymbol(symbol);
      buff.SetTimeframe(timeframe);
      buff.SetShift(shift);
      buff.SetID(identifier);
      buff.SetIndicatorHandle(handle);
      buff.SetIndicatorType(IND_GATOR);
      buff.SetEmptyValue(EMPTY_VALUE);
      buff.SetLineMode(INDICATOR_LINE_MODE_LOWER);
      buff.SetIndicatorName("Gator Oscillator");
      buff.SetLabel("Gator("+symbol+","+TimeframeDescription(timeframe)+": "+(string)jaw_period+","+(string)teeth_period+","+(string)lips_period+","+") Down");
     }
   return handle;
  }
//+------------------------------------------------------------------+

При создании хэндла индикатора мы в него передаём данные о смещении линий индикатора Alligator, на данных коорого рассчитывается Gator, такими, какие они и есть во входных параметрах, независимо от того, что индикатор может отрисовываться на неродном таймфрейме — все эти данные нужны для внутреннего расчёта индикатора. Визуальное же смещение линий стандартного индикатора Gator рассчитывается как минимальная величина из значений смещения линий Jaw и Teeth индикатора Alligator, на котором рассчитывается Gator. И это визуальное смещение нам необходимо домножить на количество баров, которые нужно отобразить на текущем таймфрейме, что мы и делаем, устанавливая размер смещения для рисуемых объектов-буферов индикатора. Расчётным объектам-буферам устанавливаем смещение без домножения на количество баров.

Метод для создания Ichimoku Kinko Hyo:

//+------------------------------------------------------------------+
//| Создаёт мультисимвольный мультипериодный Ichimoku                |
//+------------------------------------------------------------------+
int CBuffersCollection::CreateIchimoku(const string symbol,const ENUM_TIMEFRAMES timeframe,
                                       const int tenkan_sen,
                                       const int kijun_sen,
                                       const int senkou_span_b,
                                       const int id=WRONG_VALUE)
  {
//--- Создаём хэндл индикатора и устанавливаем идентификатор по умолчанию
   int num_bars=::PeriodSeconds(timeframe)/::PeriodSeconds(PERIOD_CURRENT);
   int handle=::iIchimoku(symbol,timeframe,tenkan_sen,kijun_sen,senkou_span_b);
   int identifier=(id==WRONG_VALUE ? IND_ICHIMOKU : id);
   color array_colors[1]={clrRed};
   CBuffer *buff=NULL;
   if(handle!=INVALID_HANDLE)
     {
      //--- Создаём буфер-линию
      this.CreateLine();
      //--- Получаем последний созданный объект-буфер (рисуемый) и устанавливаем ему все необходимые параметры Tenkan-Sen
      buff=this.GetLastCreateBuffer();
      if(buff==NULL)
         return INVALID_HANDLE;
      buff.SetSymbol(symbol);
      buff.SetTimeframe(timeframe);
      buff.SetID(identifier);
      buff.SetIndicatorHandle(handle);
      buff.SetIndicatorType(IND_ICHIMOKU);
      buff.SetLineMode(INDICATOR_LINE_MODE_TENKAN_SEN);
      buff.SetShowData(true);
      buff.SetLabel("Tenkan-Sen("+symbol+","+TimeframeDescription(timeframe)+": "+(string)tenkan_sen+")");
      buff.SetIndicatorName("Ichimoku Kinko Hyo");
      buff.SetIndicatorShortName("Ichimoku("+symbol+","+TimeframeDescription(timeframe)+")");
      buff.SetColors(array_colors);
      
      //--- Создаём буфер-линию
      this.CreateLine();
      //--- Получаем последний созданный объект-буфер (рисуемый) и устанавливаем ему все необходимые параметры Kijun-Sen
      buff=this.GetLastCreateBuffer();
      if(buff==NULL)
         return INVALID_HANDLE;
      buff.SetSymbol(symbol);
      buff.SetTimeframe(timeframe);
      buff.SetID(identifier);
      buff.SetIndicatorHandle(handle);
      buff.SetIndicatorType(IND_ICHIMOKU);
      buff.SetLineMode(INDICATOR_LINE_MODE_KIJUN_SEN);
      buff.SetShowData(true);
      buff.SetLabel("Kijun-Sen("+symbol+","+TimeframeDescription(timeframe)+": "+(string)kijun_sen+")");
      buff.SetIndicatorName("Ichimoku Kinko Hyo");
      buff.SetIndicatorShortName("Ichimoku("+symbol+","+TimeframeDescription(timeframe)+")");
      array_colors[0]=clrBlue;
      buff.SetColors(array_colors);
      
      //--- Создаём буфер-линию
      this.CreateLine();
      //--- Получаем последний созданный объект-буфер (рисуемый) и устанавливаем ему все необходимые параметры Senkou Span A
      buff=this.GetLastCreateBuffer();
      if(buff==NULL)
         return INVALID_HANDLE;
      buff.SetSymbol(symbol);
      buff.SetTimeframe(timeframe);
      buff.SetShift(kijun_sen*num_bars);
      buff.SetID(identifier);
      buff.SetIndicatorHandle(handle);
      buff.SetIndicatorType(IND_ICHIMOKU);
      buff.SetLineMode(INDICATOR_LINE_MODE_SENKOU_SPANA);
      buff.SetShowData(true);
      buff.SetLabel("Senkou Span A("+symbol+","+TimeframeDescription(timeframe)+")");
      buff.SetIndicatorName("Ichimoku Kinko Hyo");
      buff.SetIndicatorShortName("Ichimoku("+symbol+","+TimeframeDescription(timeframe)+")");
      array_colors[0]=clrSandyBrown;
      buff.SetColors(array_colors);
      buff.SetStyle(STYLE_DOT);
      
      //--- Создаём буфер-линию
      this.CreateLine();
      //--- Получаем последний созданный объект-буфер (рисуемый) и устанавливаем ему все необходимые параметры Senkou Span B
      buff=this.GetLastCreateBuffer();
      if(buff==NULL)
         return INVALID_HANDLE;
      buff.SetSymbol(symbol);
      buff.SetTimeframe(timeframe);
      buff.SetShift(kijun_sen*num_bars);
      buff.SetID(identifier);
      buff.SetIndicatorHandle(handle);
      buff.SetIndicatorType(IND_ICHIMOKU);
      buff.SetLineMode(INDICATOR_LINE_MODE_SENKOU_SPANB);
      buff.SetShowData(true);
      buff.SetLabel("Senkou Span B("+symbol+","+TimeframeDescription(timeframe)+": "+(string)senkou_span_b+")");
      buff.SetIndicatorName("Ichimoku Kinko Hyo");
      buff.SetIndicatorShortName("Ichimoku("+symbol+","+TimeframeDescription(timeframe)+")");
      array_colors[0]=clrThistle;
      buff.SetColors(array_colors);
      buff.SetStyle(STYLE_DOT);
      
      //--- Создаём буфер-линию
      this.CreateLine();
      //--- Получаем последний созданный объект-буфер (рисуемый) и устанавливаем ему все необходимые параметры Chikou Span
      buff=this.GetLastCreateBuffer();
      if(buff==NULL)
         return INVALID_HANDLE;
      buff.SetSymbol(symbol);
      buff.SetTimeframe(timeframe);
      buff.SetID(identifier);
      buff.SetIndicatorHandle(handle);
      buff.SetIndicatorType(IND_ICHIMOKU);
      buff.SetLineMode(INDICATOR_LINE_MODE_CHIKOU_SPAN);
      buff.SetShowData(true);
      buff.SetLabel("Chikou Span("+symbol+","+TimeframeDescription(timeframe)+")");
      buff.SetIndicatorName("Ichimoku Kinko Hyo");
      buff.SetIndicatorShortName("Ichimoku("+symbol+","+TimeframeDescription(timeframe)+")");
      array_colors[0]=clrLime;
      buff.SetColors(array_colors);
      
      //--- Создаём буфер-гистограмму на двух линиях для отображения гистограммы линии Senkou Span A
      this.CreateHistogram2();
      //--- Получаем последний созданный объект-буфер (рисуемый) и устанавливаем ему все необходимые параметры
      buff=this.GetLastCreateBuffer();
      if(buff==NULL)
         return INVALID_HANDLE;
      buff.SetSymbol(symbol);
      buff.SetTimeframe(timeframe);
      buff.SetShift(kijun_sen*num_bars);
      buff.SetID(identifier);
      buff.SetIndicatorHandle(handle);
      buff.SetIndicatorType(IND_ICHIMOKU);
      buff.SetLineMode(INDICATOR_LINE_MODE_ADDITIONAL);
      buff.SetShowData(false);
      buff.SetLabel("Senkou Span A("+symbol+","+TimeframeDescription(timeframe)+")");
      buff.SetIndicatorName("Ichimoku Kinko Hyo");
      buff.SetIndicatorShortName("Ichimoku("+symbol+","+TimeframeDescription(timeframe)+")");
      buff.SetIndicatorLineAdditionalNumber(0);
      //--- Получаем данные буфера Senkou Span A и устанавливаем гистограмме значения цвета, толщины и стиля линии
      CBuffer *tmp=GetBufferStdInd(IND_ICHIMOKU,identifier,INDICATOR_LINE_MODE_SENKOU_SPANA);
      array_colors[0]=(tmp!=NULL ? tmp.Color() : clrSandyBrown);
      buff.SetColors(array_colors);
      buff.SetWidth(tmp!=NULL ? tmp.LineWidth() : 1);
      buff.SetStyle(tmp!=NULL ? tmp.LineStyle() : STYLE_DOT);
      
      //--- Создаём буфер-гистограмму на двух линиях для отображения гистограммы линии Senkou Span B
      this.CreateHistogram2();
      //--- Получаем последний созданный объект-буфер (рисуемый) и устанавливаем ему все необходимые параметры
      buff=this.GetLastCreateBuffer();
      if(buff==NULL)
         return INVALID_HANDLE;
      buff.SetSymbol(symbol);
      buff.SetTimeframe(timeframe);
      buff.SetShift(kijun_sen*num_bars);
      buff.SetID(identifier);
      buff.SetIndicatorHandle(handle);
      buff.SetIndicatorType(IND_ICHIMOKU);
      buff.SetLineMode(INDICATOR_LINE_MODE_ADDITIONAL);
      buff.SetShowData(false);
      buff.SetLabel("Senkou Span B("+symbol+","+TimeframeDescription(timeframe)+": "+(string)senkou_span_b+")");
      buff.SetIndicatorName("Ichimoku Kinko Hyo");
      buff.SetIndicatorShortName("Ichimoku("+symbol+","+TimeframeDescription(timeframe)+")");
      buff.SetIndicatorLineAdditionalNumber(1);
      //--- Получаем данные буфера Senkou Span B и устанавливаем гистограмме значения цвета, толщины и стиля линии
      tmp=GetBufferStdInd(IND_ICHIMOKU,identifier,INDICATOR_LINE_MODE_SENKOU_SPANB);
      array_colors[0]=(tmp!=NULL ? tmp.Color() : clrThistle);
      buff.SetColors(array_colors);
      buff.SetWidth(tmp!=NULL ? tmp.LineWidth() : 1);
      buff.SetStyle(tmp!=NULL ? tmp.LineStyle() : STYLE_DOT);
      
      //--- Создаём расчётный буфер, в котором будут храниться данные линии Tenkan-Sen
      this.CreateCalculate();
      //--- Получаем последний созданный объект-буфер (расчётный) и устанавливаем ему все необходимые параметры
      buff=this.GetLastCreateBuffer();
      if(buff==NULL)
         return INVALID_HANDLE;
      buff.SetSymbol(symbol);
      buff.SetTimeframe(timeframe);
      buff.SetID(identifier);
      buff.SetIndicatorHandle(handle);
      buff.SetIndicatorType(IND_ICHIMOKU);
      buff.SetLineMode(INDICATOR_LINE_MODE_TENKAN_SEN);
      buff.SetEmptyValue(EMPTY_VALUE);
      buff.SetLabel("Tenkan-Sen("+symbol+","+TimeframeDescription(timeframe)+": "+(string)tenkan_sen+")");
      buff.SetIndicatorName("Ichimoku Kinko Hyo");
      buff.SetIndicatorShortName("Ichimoku("+symbol+","+TimeframeDescription(timeframe)+")");
      
      //--- Создаём расчётный буфер, в котором будут храниться данные линии Kijun-Sen
      this.CreateCalculate();
      //--- Получаем последний созданный объект-буфер (расчётный) и устанавливаем ему все необходимые параметры
      buff=this.GetLastCreateBuffer();
      if(buff==NULL)
         return INVALID_HANDLE;
      buff.SetSymbol(symbol);
      buff.SetTimeframe(timeframe);
      buff.SetID(identifier);
      buff.SetIndicatorHandle(handle);
      buff.SetIndicatorType(IND_ICHIMOKU);
      buff.SetLineMode(INDICATOR_LINE_MODE_KIJUN_SEN);
      buff.SetEmptyValue(EMPTY_VALUE);
      buff.SetLabel("Kijun-Sen("+symbol+","+TimeframeDescription(timeframe)+": "+(string)kijun_sen+")");
      buff.SetIndicatorName("Ichimoku Kinko Hyo");
      buff.SetIndicatorShortName("Ichimoku("+symbol+","+TimeframeDescription(timeframe)+")");
      
      //--- Создаём расчётный буфер, в котором будут храниться данные линии Senkou Span A
      this.CreateCalculate();
      //--- Получаем последний созданный объект-буфер (расчётный) и устанавливаем ему все необходимые параметры
      buff=this.GetLastCreateBuffer();
      if(buff==NULL)
         return INVALID_HANDLE;
      buff.SetSymbol(symbol);
      buff.SetTimeframe(timeframe);
      buff.SetShift(kijun_sen);
      buff.SetID(identifier);
      buff.SetIndicatorHandle(handle);
      buff.SetIndicatorType(IND_ICHIMOKU);
      buff.SetLineMode(INDICATOR_LINE_MODE_SENKOU_SPANA);
      buff.SetEmptyValue(EMPTY_VALUE);
      buff.SetLabel("Senkou Span A("+symbol+","+TimeframeDescription(timeframe)+")");
      buff.SetIndicatorName("Ichimoku Kinko Hyo");
      buff.SetIndicatorShortName("Ichimoku("+symbol+","+TimeframeDescription(timeframe)+")");
      
      //--- Создаём расчётный буфер, в котором будут храниться данные линии Senkou Span B
      this.CreateCalculate();
      //--- Получаем последний созданный объект-буфер (расчётный) и устанавливаем ему все необходимые параметры
      buff=this.GetLastCreateBuffer();
      if(buff==NULL)
         return INVALID_HANDLE;
      buff.SetSymbol(symbol);
      buff.SetTimeframe(timeframe);
      buff.SetShift(kijun_sen);
      buff.SetID(identifier);
      buff.SetIndicatorHandle(handle);
      buff.SetIndicatorType(IND_ICHIMOKU);
      buff.SetLineMode(INDICATOR_LINE_MODE_SENKOU_SPANB);
      buff.SetEmptyValue(EMPTY_VALUE);
      buff.SetLabel("Senkou Span B("+symbol+","+TimeframeDescription(timeframe)+": "+(string)senkou_span_b+")");
      buff.SetIndicatorName("Ichimoku Kinko Hyo");
      buff.SetIndicatorShortName("Ichimoku("+symbol+","+TimeframeDescription(timeframe)+")");
      
      //--- Создаём расчётный буфер, в котором будут храниться данные линии Chikou Span
      this.CreateCalculate();
      //--- Получаем последний созданный объект-буфер (расчётный) и устанавливаем ему все необходимые параметры
      buff=this.GetLastCreateBuffer();
      if(buff==NULL)
         return INVALID_HANDLE;
      buff.SetSymbol(symbol);
      buff.SetTimeframe(timeframe);
      buff.SetID(identifier);
      buff.SetIndicatorHandle(handle);
      buff.SetIndicatorType(IND_ICHIMOKU);
      buff.SetLineMode(INDICATOR_LINE_MODE_CHIKOU_SPAN);
      buff.SetEmptyValue(EMPTY_VALUE);
      buff.SetLabel("Chikou Span("+symbol+","+TimeframeDescription(timeframe)+")");
      buff.SetIndicatorName("Ichimoku Kinko Hyo");
      buff.SetIndicatorShortName("Ichimoku("+symbol+","+TimeframeDescription(timeframe)+")");
     }
   return handle;
  }
//+------------------------------------------------------------------+

Здесь точно так же, как и с созданием объекта стандартного индикатора Gator, при создании хэндла передаём в него входные параметры без изменения, а уже при создании объектов-буферов для отображения линий Senkou Span A и Senkou Span B и дополнительных буферов для оформления пространства между двумя этими линиями в виде гистограмм, мы домножаем смещение до нужного количества баров, которое необходимо отобразить на текущем графике. При создании объектов расчётных буферов, устанавливаем смещение без домножения на количество баров текщего графика.
Смещением для линий Senkou Span A и Senkou Span B служит период расчёта линии Kijun-Sen.
При создании дополнительных буферов-гистограмм, мы сначала устанавливаем для них все стандартные параметры индикатора, а затем получаем соответствующий гистограмме объект-буфер линии и задаём параметры рисования для гистограммы точно такими же, как у линии индикатора, получая значения из взятого буфера линии.

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

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

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

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

Разница между ними будет лишь в параметре #property indicator_chart_window, либо #property indicator_separate_window, так как один из них будет рисоваться на основном графике, а второй — в подокне. Ну и создавать будем в одном случае индикатор Ichimoku Kinko Hyo, а во втором — Gator Oscillator.

Удалим из входных параметров строки задания типа индикаторов и смещения его линий:

sinput   ENUM_INDICATOR       InpIndType        =  IND_AC;        // Type standard indicator
sinput   int                  InpShift          =  0;             // Indicator line shift

В обработчике OnInit() в файле TestDoEasyPart51_1.mq5 создадим объект индикатора Ichimoku Kinko Hyo:

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Запишем в переменную InpUsedTFs наименование выбранного в настройках рабочего таймфрейма
   InpUsedTFs=TimeframeDescription(InpPeriod);
//--- Инициализация библиотеки DoEasy
   OnInitDoEasy();
   
//--- Установка глобальных переменных индикатора
   prefix=engine.Name()+"_";
   //--- Рассчитываем количество баров текущего периода, умещающихся в максимальном используемом периоде
   //--- Используем полученное значение если оно больше 2, иначе используем 2
   int num_bars=NumberBarsInTimeframe(InpPeriod);
   min_bars=(num_bars>2 ? num_bars : 2);

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

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

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

//--- indicator buffers mapping
//--- Создаём все необходимые объекты-буферы для построения выбранного стандартного индикатора
   if(!engine.BufferCreateIchimoku(InpUsedSymbols,InpPeriod,9,26,52,1))
     {
      Print(TextByLanguage("Ошибка. Индикатор не создан","Error. Indicator not created"));
      return INIT_FAILED;
     }
//--- Проверяем количество буферов, указанных в блоке properties
   if(engine.BuffersPropertyPlotsTotal()!=indicator_plots)
      Alert(TextByLanguage("Внимание! Значение \"indicator_plots\" должно быть ","Attention! Value of \"indicator_plots\" should be "),engine.BuffersPropertyPlotsTotal());
   if(engine.BuffersPropertyBuffersTotal()!=indicator_buffers)
      Alert(TextByLanguage("Внимание! Значение \"indicator_buffers\" должно быть ","Attention! Value of \"indicator_buffers\" should be "),engine.BuffersPropertyBuffersTotal());
      
//--- Создаём массив цветов и зададём всем буферам в коллекции значения цвета не по умолчанию
//--- (закомментировано так как в методах создания стандартных индикаторов по умолчанию уже заданы цвета)
//--- (всегда можно задать нужные цвета либо для всех индикаторов как здесь, либо для каждого индивидуально)
   //color array_colors[]={clrGreen,clrRed,clrGray};
   //engine.BuffersSetColors(array_colors);

//--- Выведем краткие описания созданных индикаторных буферов
   engine.BuffersPrintShort();

//--- Установим короткое имя индикатора, разрядность данных и уровни
   string label=engine.BufferGetIndicatorShortNameByTypeID(IND_ICHIMOKU,1);
   IndicatorSetString(INDICATOR_SHORTNAME,label);
   SetIndicatorLevels(InpUsedSymbols,IND_ICHIMOKU);

//--- Успешно
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+

А в обработчике OnCalculate() будем обрабатывать только один созданный индикатор:

//+------------------------------------------------------------------+
//| 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() и установка массивам флага "как таймсерия"
   CopyDataAsSeries(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.OnCalculate(rates_data)==0)
      return 0;
   
//--- Если работа в тестере
   if(MQLInfoInteger(MQL_TESTER)) 
     {
      engine.OnTimer(rates_data);   // Работа в таймере библиотеки
      engine.EventsHandling();      // Работа с событиями библиотеки
     }
//+------------------------------------------------------------------+
//| OnCalculate Блок кода для работы с индикатором:                  |
//+------------------------------------------------------------------+
//--- Проверка и расчёт количества просчитываемых баров
//--- Если limit = 0, значит новых баров нет - рассчитываем текущий
//--- Если limit = 1, значит появился новый бар - рассчитываем первый и текущий
//--- Если limit > 1, значит первый запуск, или есть изменения в истории - полный перерасчёт всех данных
   int limit=rates_total-prev_calculated;
   
//--- Перерасчёт всей истории
   if(limit>1)
     {
      limit=rates_total-1;
      engine.BuffersInitPlots();
      engine.BuffersInitCalculates();
     }
//--- Подготовка данных 
//--- Заполняем данными расчётные буферы всех созданных стандартных индикаторов
   int bars_total=engine.SeriesGetBarsTotal(InpUsedSymbols,InpPeriod);
   int total_copy=(limit<min_bars ? min_bars : fmin(limit,bars_total));
   if(!engine.BufferPreparingDataAllBuffersStdInd())
      return 0;

//--- Расчёт индикатора
//--- Основной цикл расчёта индикатора
   for(int i=limit; i>WRONG_VALUE && !IsStopped(); i--)
     {
      engine.GetBuffersCollection().SetDataBufferStdInd(IND_ICHIMOKU,1,i,time[i]);
     }
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+

В файле TestDoEasyPart51_2.mq5 сделаем точно такие же изменения, но вместо создания и обработки индикатора Ichimoku Kinko Hyo создадим и будем обрабатывать индикатор Gator Oscillator. И зададим указание препроцессору создать индикатор, работающий в подокне:

//+------------------------------------------------------------------+
//|                                           TestDoEasyPart51_2.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>
//--- properties
#property indicator_separate_window
#property indicator_buffers 6
#property indicator_plots   2

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


Как видим, данные обоих индикаторов совпадают с данными стандартных индикаторов.

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

Что дальше

В следующей статье начнём работу над совместимостью классов объектов стандартных индикаторов с MQL4.

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

К содержанию

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

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