English Deutsch
preview
Цветные буферы в мультисимвольных мультипериодных индикаторах

Цветные буферы в мультисимвольных мультипериодных индикаторах

MetaTrader 5Примеры | 7 декабря 2023, 17:07
995 0
Artyom Trishkin
Artyom Trishkin

Содержание


Введение

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

Одноцветный индикаторный буфер является обычным double-массивом, который заполняется данными при просчёте индикатора. Данные из этого массива мы можем получить и отобразить на графике при помощи функции CopyBuffer() при условии, что приёмным массивом будет служить double-массив, назначенный как рисуемый индикаторный буфер (SetIndexBuffer()). При копировании данных из буфера расчётной части индикатора в буфер рисуемой части индикатора, данные отображаются на графике одним цветом, установленным для массива-буфера рисуемой части индикатора. С цветными же буферами дело обстоит немного иначе. У цветного буфера помимо массива данных есть ещё один массив — массив индексов цвета.

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

#property indicator_color1  clrGreen,clrRed

либо функцией PlotIndexSetInteger(), например

PlotIndexSetInteger(0,PLOT_COLOR_INDEXES,2);
PlotIndexSetInteger(0,PLOT_LINE_COLOR,0,clrGreen);
PlotIndexSetInteger(0,PLOT_LINE_COLOR,1,clrRed);

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

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

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


Основные принципы

Исходя из вышеизложенного, понимаем, что

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

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


В итоге получим такой шаблон индикатора:

//+------------------------------------------------------------------+
//|                                                         Test.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_chart_window
#property indicator_buffers 17                     // Всего 17 массивов для расчёта индикатора
#property indicator_plots   6                      // Из 17-ти массивов 6 графических серий - рисуемых буферов
//--- plot Label1
#property indicator_label1  "Label1"
#property indicator_type1   DRAW_LINE              // Рисуемый буфер с индексом 0, простая линия, требует один массив для построения
#property indicator_color1  clrRed                 // Цвет линии: Red
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1
//--- plot Label2
#property indicator_label2  "Label2"
#property indicator_type2   DRAW_FILLING           // Рисуемый буфер с индексом 1, цветная область, рисуемая между двумя индикаторными линиями, требует два массива для построения
#property indicator_color2  clrRed,clrDeepSkyBlue  // Цвет области: либо Red, либо DeepSkyBlue, в зависимости от того, какая линия из двух выше
#property indicator_style2  STYLE_SOLID
#property indicator_width2  1
//--- plot Label3
#property indicator_label3  "Label3"
#property indicator_type3   DRAW_CANDLES           // Рисуемый буфер с индексом 2, отображение в виде одноцветных свечей, требует четыре массива данных OHLC для построения
#property indicator_color3  clrDarkSalmon          // Цвет: DarkSalmon
#property indicator_style3  STYLE_SOLID
#property indicator_width3  1
//--- plot Label4
#property indicator_label4  "Label4"
#property indicator_type4   DRAW_COLOR_LINE        // Рисуемый буфер с индексом 3, цветная линия, требует два массива для построения: массив данных + массив индексов цвета
#property indicator_color4  clrRed,clrRoyalBlue    // Два цвета: Red и RoyalBlue. Может быть до 64-х цветов
#property indicator_style4  STYLE_SOLID
#property indicator_width4  1
//--- plot Label5
#property indicator_label5  "Label5"
#property indicator_type5   DRAW_COLOR_HISTOGRAM2  // Рисуемый буфер с индексом 4, цветная гистограмма между двумя линиями, требует три массива для построения - два массива данных + массив индексов цвета
#property indicator_color5  clrRed,clrForestGreen,clrBurlyWood // Три цвета: Red, ForestGreen и BurlyWood. Может быть до 64-х цветов
#property indicator_style5  STYLE_SOLID
#property indicator_width5  1
//--- plot Label6
#property indicator_label6  "Label6"               // Рисуемый буфер с индексом 5, отображение в виде цветных свечей, требует пять массивов для построения: 4 массива данных OHLC + массив индексов цвета
#property indicator_type6   DRAW_COLOR_CANDLES     // Три цвета: Red, Blue и Gray. Может быть до 64-х цветов
#property indicator_color6  clrRed,clrBlue,clrGray
#property indicator_style6  STYLE_SOLID
#property indicator_width6  1
//--- input variables
input uchar    InpHidePlotIndex  =  0;             // Hide Plot Index
//--- indicator buffers
double         Label1Buffer[];                     // Рисуемый буфер с индексом 0
double         Label2Buffer1[];                    // Рисуемый буфер с индексом 1, массив 1
double         Label2Buffer2[];                    // Рисуемый буфер с индексом 1, массив 2
double         Label3Buffer1[];                    // Рисуемый буфер с индексом 2, массив 1
double         Label3Buffer2[];                    // Рисуемый буфер с индексом 2, массив 2
double         Label3Buffer3[];                    // Рисуемый буфер с индексом 2, массив 3
double         Label3Buffer4[];                    // Рисуемый буфер с индексом 2, массив 4
double         Label4Buffer[];                     // Рисуемый буфер с индексом 3
double         Label4Colors[];                     // Массив индексов цветов рисуемого буфера с индексом 3
double         Label5Buffer1[];                    // Рисуемый буфер с индексом 4, массив 1
double         Label5Buffer2[];                    // Рисуемый буфер с индексом 4, массив 2
double         Label5Colors[];                     // Массив индексов цветов рисуемого буфера с индексом 4
double         Label6Buffer1[];                    // Рисуемый буфер с индексом 5, массив 1
double         Label6Buffer2[];                    // Рисуемый буфер с индексом 5, массив 2
double         Label6Buffer3[];                    // Рисуемый буфер с индексом 5, массив 3
double         Label6Buffer4[];                    // Рисуемый буфер с индексом 5, массив 4
double         Label6Colors[];                     // Массив индексов цветов рисуемого буфера с индексом 5

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
//--- Рисуемый буфер 0. Один массив для построения
   SetIndexBuffer(0,Label1Buffer,INDICATOR_DATA);        // Буфер данных
   
//--- Рисуемый буфер 1. Два массива для построения
   SetIndexBuffer(1,Label2Buffer1,INDICATOR_DATA);       // Буфер данных линии 1
   SetIndexBuffer(2,Label2Buffer2,INDICATOR_DATA);       // Буфер данных линии 2
   
//--- Рисуемый буфер 2. Четыре массива для построения
   SetIndexBuffer(3,Label3Buffer1,INDICATOR_DATA);       // Буфер данных линии 1 Open
   SetIndexBuffer(4,Label3Buffer2,INDICATOR_DATA);       // Буфер данных линии 2 High
   SetIndexBuffer(5,Label3Buffer3,INDICATOR_DATA);       // Буфер данных линии 3 Low
   SetIndexBuffer(6,Label3Buffer4,INDICATOR_DATA);       // Буфер данных линии 4 Close
   
//--- Рисуемый буфер 3. Два массива для построения
   SetIndexBuffer(7,Label4Buffer,INDICATOR_DATA);        // Буфер данных
   SetIndexBuffer(8,Label4Colors,INDICATOR_COLOR_INDEX); // Буфер индексов цвета
   
//--- Рисуемый буфер 4. Три массива для построения
   SetIndexBuffer(9,Label5Buffer1,INDICATOR_DATA);       // Буфер данных линии 1
   SetIndexBuffer(10,Label5Buffer2,INDICATOR_DATA);      // Буфер данных линии 2
   SetIndexBuffer(11,Label5Colors,INDICATOR_COLOR_INDEX);// Буфер индексов цвета
   
//--- Рисуемый буфер 5. Пять массивов для построения
   SetIndexBuffer(12,Label6Buffer1,INDICATOR_DATA);      // Буфер данных линии 1 Open
   SetIndexBuffer(13,Label6Buffer2,INDICATOR_DATA);      // Буфер данных линии 2 High
   SetIndexBuffer(14,Label6Buffer3,INDICATOR_DATA);      // Буфер данных линии 3 Low
   SetIndexBuffer(15,Label6Buffer4,INDICATOR_DATA);      // Буфер данных линии 4 Close
   SetIndexBuffer(16,Label6Colors,INDICATOR_COLOR_INDEX);// Буфер индексов цвета
  }
//+------------------------------------------------------------------+
//| 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[])
  {

//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+

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

Например,

  • Label5Buffer1 — это первый массив пятого рисуемого буфера (его индекс на самом деле равен 4, так как отсчёт начинается с нуля) для построения цветной гистограммы, рисуемой между двумя линиями,
  • Label5Buffer2 — это второй массив пятого рисуемого буфера для построения цветной гистограммы, рисуемой между двумя линиями,
  • Label5Colors — это массив индексов цвета пятого рисуемого буфера для построения цветной гистограммы, рисуемой между двумя линиями.

Для рассмотренного выше примера индекс графической серии (рисуемого буфера) равен 4, хотя для него назначены массивы с индексами 9, 10 и 11. Т.е., чтобы назначить этой графической серии какое-либо свойство, нужно устанавливать его не по индексам массивов, назначенных для построения графической серии, а по индексу рисуемого буфера, а их у нас в этом примере шесть — от 0 до 5.

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

//+------------------------------------------------------------------+
//|                                                         Test.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_chart_window
#property indicator_buffers 17                     // Всего 17 массивов для расчёта индикатора
#property indicator_plots   6                      // Из 17-ти массивов 6 графических серий - рисуемых буферов
//--- plot Label1
#property indicator_label1  "Label1"
#property indicator_type1   DRAW_LINE              // Рисуемый буфер с индексом 0, простая линия, требует один массив для построения
#property indicator_color1  clrRed                 // Цвет линии: Red
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1
//--- plot Label2
#property indicator_label2  "Label2"
#property indicator_type2   DRAW_FILLING           // Рисуемый буфер с индексом 1, цветная область, рисуемая между двумя индикаторными линиями, требует два массива для построения
#property indicator_color2  clrRed,clrDeepSkyBlue  // Цвет области: либо Red, либо DeepSkyBlue, в зависимости от того, какая линия из двух выше
#property indicator_style2  STYLE_SOLID
#property indicator_width2  1
//--- plot Label3
#property indicator_label3  "Label3"
#property indicator_type3   DRAW_CANDLES           // Рисуемый буфер с индексом 2, отображение в виде одноцветных свечей, требует четыре массива данных OHLC для построения
#property indicator_color3  clrDarkSalmon          // Цвет: DarkSalmon
#property indicator_style3  STYLE_SOLID
#property indicator_width3  1
//--- plot Label4
#property indicator_label4  "Label4"
#property indicator_type4   DRAW_COLOR_LINE        // Рисуемый буфер с индексом 3, цветная линия, требует два массива для построения: массив данных + массив индексов цвета
#property indicator_color4  clrRed,clrRoyalBlue    // Два цвета: Red и RoyalBlue. Может быть до 64-х цветов
#property indicator_style4  STYLE_SOLID
#property indicator_width4  1
//--- plot Label5
#property indicator_label5  "Label5"
#property indicator_type5   DRAW_COLOR_HISTOGRAM2  // Рисуемый буфер с индексом 4, цветная гистограмма между двумя линиями, требует три массива для построения - два массива данных + массив индексов цвета
#property indicator_color5  clrRed,clrForestGreen,clrBurlyWood // Три цвета: Red, ForestGreen и BurlyWood. Может быть до 64-х цветов
#property indicator_style5  STYLE_SOLID
#property indicator_width5  1
//--- plot Label6
#property indicator_label6  "Label6"               // Рисуемый буфер с индексом 5, отображение в виде цветных свечей, требует пять массивов для построения: 4 массива данных OHLC + массив индексов цвета
#property indicator_type6   DRAW_COLOR_CANDLES     // Три цвета: Red, Blue и Gray. Может быть до 64-х цветов
#property indicator_color6  clrRed,clrBlue,clrGray
#property indicator_style6  STYLE_SOLID
#property indicator_width6  1
//--- input variables
input uchar    InpHidePlotIndex  =  0;             // Hide Plot Index
//--- indicator buffers
double         Label1Buffer[];                     // Рисуемый буфер с индексом 0
double         Label2Buffer1[];                    // Рисуемый буфер с индексом 1, массив 1
double         Label2Buffer2[];                    // Рисуемый буфер с индексом 1, массив 2
double         Label3Buffer1[];                    // Рисуемый буфер с индексом 2, массив 1
double         Label3Buffer2[];                    // Рисуемый буфер с индексом 2, массив 2
double         Label3Buffer3[];                    // Рисуемый буфер с индексом 2, массив 3
double         Label3Buffer4[];                    // Рисуемый буфер с индексом 2, массив 4
double         Label4Buffer[];                     // Рисуемый буфер с индексом 3
double         Label4Colors[];                     // Массив индексов цветов рисуемого буфера с индексом 3
double         Label5Buffer1[];                    // Рисуемый буфер с индексом 4, массив 1
double         Label5Buffer2[];                    // Рисуемый буфер с индексом 4, массив 2
double         Label5Colors[];                     // Массив индексов цветов рисуемого буфера с индексом 4
double         Label6Buffer1[];                    // Рисуемый буфер с индексом 5, массив 1
double         Label6Buffer2[];                    // Рисуемый буфер с индексом 5, массив 2
double         Label6Buffer3[];                    // Рисуемый буфер с индексом 5, массив 3
double         Label6Buffer4[];                    // Рисуемый буфер с индексом 5, массив 4
double         Label6Colors[];                     // Массив индексов цветов рисуемого буфера с индексом 5
//--- global variables
int   hide_index=(InpHidePlotIndex>5 ? 5 : InpHidePlotIndex);
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
//--- Рисуемый буфер 0. Один массив для построения
   SetIndexBuffer(0,Label1Buffer,INDICATOR_DATA);        // Буфер данных
   
//--- Рисуемый буфер 1. Два массива для построения
   SetIndexBuffer(1,Label2Buffer1,INDICATOR_DATA);       // Буфер данных линии 1
   SetIndexBuffer(2,Label2Buffer2,INDICATOR_DATA);       // Буфер данных линии 2
   
//--- Рисуемый буфер 2. Четыре массива для построения
   SetIndexBuffer(3,Label3Buffer1,INDICATOR_DATA);       // Буфер данных линии 1 Open
   SetIndexBuffer(4,Label3Buffer2,INDICATOR_DATA);       // Буфер данных линии 2 High
   SetIndexBuffer(5,Label3Buffer3,INDICATOR_DATA);       // Буфер данных линии 3 Low
   SetIndexBuffer(6,Label3Buffer4,INDICATOR_DATA);       // Буфер данных линии 4 Close
   
//--- Рисуемый буфер 3. Два массива для построения
   SetIndexBuffer(7,Label4Buffer,INDICATOR_DATA);        // Буфер данных
   SetIndexBuffer(8,Label4Colors,INDICATOR_COLOR_INDEX); // Буфер индексов цвета
   
//--- Рисуемый буфер 4. Три массива для построения
   SetIndexBuffer(9,Label5Buffer1,INDICATOR_DATA);       // Буфер данных линии 1
   SetIndexBuffer(10,Label5Buffer2,INDICATOR_DATA);      // Буфер данных линии 2
   SetIndexBuffer(11,Label5Colors,INDICATOR_COLOR_INDEX);// Буфер индексов цвета
   
//--- Рисуемый буфер 5. Пять массивов для построения
   SetIndexBuffer(12,Label6Buffer1,INDICATOR_DATA);      // Буфер данных линии 1 Open
   SetIndexBuffer(13,Label6Buffer2,INDICATOR_DATA);      // Буфер данных линии 2 High
   SetIndexBuffer(14,Label6Buffer3,INDICATOR_DATA);      // Буфер данных линии 3 Low
   SetIndexBuffer(15,Label6Buffer4,INDICATOR_DATA);      // Буфер данных линии 4 Close
   SetIndexBuffer(16,Label6Colors,INDICATOR_COLOR_INDEX);// Буфер индексов цвета

//--- Скроем в окне данных указанный рисуемый буфер
   PlotIndexSetInteger(0,PLOT_SHOW_DATA,true);
   PlotIndexSetInteger(1,PLOT_SHOW_DATA,true);
   PlotIndexSetInteger(2,PLOT_SHOW_DATA,true);
   PlotIndexSetInteger(3,PLOT_SHOW_DATA,true);
   PlotIndexSetInteger(4,PLOT_SHOW_DATA,true);
   PlotIndexSetInteger(5,PLOT_SHOW_DATA,true);
   PlotIndexSetInteger(hide_index,PLOT_SHOW_DATA,false);
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| 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[])
  {

//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+


Если задать в настройках буфер 0, которому соответствует графическая серия с именем Label1, то в окне данных этот буфер отображён не будет:


Видно, что список данных начинается с буфера с именем Label2.

Если ввести 5 в настройках индикатора, то будет скрыт из окна данных рисуемый буфер с именем Label6:


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

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

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


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

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

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

Все изменения будем вносить в файл библиотеки \MQL5\Include\IndMSTF\IndMSTF.mqh.

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

//--- struct
struct SBuffer                // Структура индикаторного буфера
  {
private:
   double            init_value;                            // Инициализирующее значение
   uchar             init_color_idx;                        // Инициализирующее значение индекса цвета
   int               shift;                                 // Сдвиг буфера по горизонтали
   uint              source;                                // Номер буфера исходного индикатора
   bool              colored;                               // Флаг цветного буфера
   ENUM_DRAW_TYPE    draw_type;                             // Стиль рисования буфера
//--- Изменяет размер всех массивов
   bool              ResizeArrays(const int new_size)
                       {
                        bool res=true;
                        switch(draw_type)
                          {
                           //--- Один буфер
                           case DRAW_LINE             :
                           case DRAW_HISTOGRAM        :
                           case DRAW_ARROW            :
                           case DRAW_SECTION          :
                              return ArrayResize(array0,new_size)==new_size;
                           //--- Два буфера
                           case DRAW_HISTOGRAM2       :
                           case DRAW_ZIGZAG           :
                           case DRAW_FILLING          :
                              res  =(ArrayResize(array0,new_size)==new_size);
                              res &=(ArrayResize(array1,new_size)==new_size);
                              return res;
                           //--- Четыре буфера
                           case DRAW_BARS             :
                           case DRAW_CANDLES          :
                              res  =(ArrayResize(array0,new_size)==new_size);
                              res &=(ArrayResize(array1,new_size)==new_size);
                              res &=(ArrayResize(array2,new_size)==new_size);
                              res &=(ArrayResize(array3,new_size)==new_size);
                              return res;
                           //--- Один буфер + буфер цвета
                           case DRAW_COLOR_LINE       :
                           case DRAW_COLOR_HISTOGRAM  :
                           case DRAW_COLOR_ARROW      :
                           case DRAW_COLOR_SECTION    :
                              res  =(ArrayResize(array0,new_size)==new_size);
                              res &=(ArrayResize(color_indexes,new_size)==new_size);
                              return res;
                           //--- Два буфера + буфер цвета
                           case DRAW_COLOR_HISTOGRAM2 :
                           case DRAW_COLOR_ZIGZAG     :
                              res  =(ArrayResize(array0,new_size)==new_size);
                              res &=(ArrayResize(array1,new_size)==new_size);
                              res &=(ArrayResize(color_indexes,new_size)==new_size);
                              return res;
                           //--- Четыре буфера + буфер цвета
                           case DRAW_COLOR_BARS       :
                           case DRAW_COLOR_CANDLES    :
                              res  =(ArrayResize(array0,new_size)==new_size);
                              res &=(ArrayResize(array1,new_size)==new_size);
                              res &=(ArrayResize(array2,new_size)==new_size);
                              res &=(ArrayResize(array3,new_size)==new_size);
                              res &=(ArrayResize(color_indexes,new_size)==new_size);
                              return res;
                           //---DRAW_NONE
                           default:
                             break;
                          }
                        return false;
                       }
//--- Инициализирует все массивы
   int               InitArrays(void)
                       {
                        bool res=0;
                        switch(draw_type)
                          {
                           //--- Один буфер
                           case DRAW_LINE             :
                           case DRAW_HISTOGRAM        :
                           case DRAW_ARROW            :
                           case DRAW_SECTION          :
                              return ArrayInitialize(array0,init_value);
                           //--- Два буфера
                           case DRAW_HISTOGRAM2       :
                           case DRAW_ZIGZAG           :
                           case DRAW_FILLING          :
                              res+=ArrayInitialize(array0,init_value);
                              res+=ArrayInitialize(array1,init_value);
                              return res/2;
                           //--- Четыре буфера
                           case DRAW_BARS             :
                           case DRAW_CANDLES          :
                              res+=ArrayInitialize(array0,init_value);
                              res+=ArrayInitialize(array1,init_value);
                              res+=ArrayInitialize(array2,init_value);
                              res+=ArrayInitialize(array3,init_value);
                              return res/4;
                           //--- Один буфер + буфер цвета
                           case DRAW_COLOR_LINE       :
                           case DRAW_COLOR_HISTOGRAM  :
                           case DRAW_COLOR_ARROW      :
                           case DRAW_COLOR_SECTION    :
                              res+=ArrayInitialize(array0,init_value);
                              res+=ArrayInitialize(color_indexes,init_color_idx);
                              return res/2;
                           //--- Два буфера + буфер цвета
                           case DRAW_COLOR_HISTOGRAM2 :
                           case DRAW_COLOR_ZIGZAG     :
                              res+=ArrayInitialize(array0,init_value);
                              res+=ArrayInitialize(array1,init_value);
                              res+=ArrayInitialize(color_indexes,init_color_idx);
                              return res/3;
                           //--- Четыре буфера + буфер цвета
                           case DRAW_COLOR_BARS       :
                           case DRAW_COLOR_CANDLES    :
                              res+=ArrayInitialize(array0,init_value);
                              res+=ArrayInitialize(array1,init_value);
                              res+=ArrayInitialize(array2,init_value);
                              res+=ArrayInitialize(array3,init_value);
                              res+=ArrayInitialize(color_indexes,init_color_idx);
                              return res/5;
                           //---DRAW_NONE
                           default:
                             break;
                          }
                        return false;
                       }
public:
   double            array0[];                              // Массив-буфер0 индикатора
   double            array1[];                              // Массив-буфер1 индикатора (2-й массив для расчёта)
   double            array2[];                              // Массив-буфер2 индикатора (3-й массив для расчёта)
   double            array3[];                              // Массив-буфер3 индикатора (4-й массив для расчёта)
   double            color_indexes[];                       // Массив-буфер индексов цветов индикатора
   color             clrs[];                                // Массив цветов, назначенных буферу
   string            descript;                              // Описание буфера
//--- Возвращает флаг цветного буфера
   bool              IsColoredBuffer(void)                        { return colored;                }
//--- (1) Устанавливает, (2) возвращает стиль рисования буфера, (3) номер соответствующего буфера исходного индикатора
   void              SetBufferDrawType(const ENUM_DRAW_TYPE type,const uint buff_source)
                       {
                        draw_type=type;
                        source=buff_source;
                        switch(draw_type)
                          {
                           case DRAW_COLOR_LINE       :
                           case DRAW_COLOR_SECTION    :
                           case DRAW_COLOR_HISTOGRAM  :
                           case DRAW_COLOR_HISTOGRAM2 :
                           case DRAW_COLOR_ARROW      :
                           case DRAW_COLOR_ZIGZAG     :
                           case DRAW_COLOR_BARS       :
                           case DRAW_COLOR_CANDLES    :  colored=true;  break;
                           default                    :  colored=false; break;
                          }
                       }
   ENUM_DRAW_TYPE    DrawType(void)                               { return draw_type;              }
   uint              BufferFrom(void)                             { return source;                 }
//--- (1) Устанавливает, (2) возвращает инициализирующее значение
   void              SetInitValue(const double value)             { init_value=value;              }
   double            InitValue(void)                              { return init_value;             }
//--- (1) Устанавливает, (2) возвращает инициализирующее значение индекса цвета
   void              SetInitColorIdx(const uchar idx)             { init_color_idx=idx;            }
   uchar             InitColorIdx(void)                           { return init_color_idx;         }
//--- (1) Устанавливает, (2) возвращает сдвиг буфера
   void              SetShift(const int value)                    { shift=value;                   }
   int               Shift(void)                                  { return shift;                  }
//--- (1) Возвращает размер массива буфера, (2) изменяет размер массива буфера,
//--- (3) инициализирует массив установленным "пустым" значением
   uint              BufferSize(void)                             { return array0.Size();          }
   bool              BuffResize(const int new_size)               { return ResizeArrays(new_size); }
   int               InitBuffer(void)                             { return InitArrays();           }
//--- (1) Возвращает размер массива буфера индексов цвета,
   uint              BufferColorIdxSize(void)                     { return color_indexes.Size();   }
   //--- (1) Устанавливает, (2) возвращает значение цвета по индексу
   void              SetColorToIdx(const uchar idx,const color clr)
                       {
                        if(idx>(int)clrs.Size()-1)
                          {
                           ResetLastError();
                           if(ArrayResize(clrs,idx+1)!=idx+1)
                             {
                              PrintFormat("%s: ArrayResize 'clrs' failed. Error %lu",__FUNCTION__,GetLastError());
                              return;
                             }
                          }
                        clrs[idx]=clr;
                       }
   color             ColorByIdx(const uchar idx){ return(idx<clrs.Size() ? clrs[idx] : clrNONE);   }
  };

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

//+------------------------------------------------------------------+
//| Базовый класс мультисимвольного мультипериодного индикатора      |
//+------------------------------------------------------------------+
class CIndMSTF : public CObject
  {
private:
   ENUM_PROGRAM_TYPE m_program;           // Тип программы
   ENUM_INDICATOR    m_type;              // Тип индикатора
   ENUM_TIMEFRAMES   m_timeframe;         // Период графика
   string            m_symbol;            // Символ графика
   int               m_handle;            // Хэндл индикатора
   int               m_id;                // Идентификатор
   bool              m_success;           // Флаг успешного расчёта
   ENUM_ERR_TYPE     m_type_err;          // Тип ошибки при расчёте
   string            m_description;       // Пользовательское описание индикатора
   string            m_name;              // Наименование индикатора
   string            m_parameters;        // Описание параметров индикатора

protected:
   ENUM_IND_CATEGORY m_category;          // Категория индикатора
   MqlParam          m_param[];           // Массив параметров индикатора
   string            m_title;             // Заголовок (наименование индикатора + описание параметров)
   SBuffer           m_buffers[];         // Буферы индикатора
   int               m_digits;            // Digits значений индикатора
   int               m_limit;             // Количество баров, необходимое для просчёта индикатора на текущем тике
   int               m_rates_total;       // Количество доступных баров для просчёта индикатора
   int               m_prev_calculated;   // Количество просчитанных баров на прошлом вызове индикатора
   
//--- (1) Устанавливает наименование индикатора, (2) описание параметров
   void              SetName(const string name)                      { this.m_name=name;           }
   void              SetParameters(const string str)                 { this.m_parameters=str;      }
   
//--- Изменяет размер (1) указанного, (2) всех буферов индикатора
   bool              BufferResize(const uint buffer_num,const int new_buff_size);
   bool              BuffersResize(const int new_buff_size);
//--- Инициализирует (1) укакзанный, (2) все буферы индикатора
   bool              BufferInitialize(const uint buffer_num,const int new_buff_size);
   bool              BuffersInitialize(const int new_buff_size);
   
//--- Возвращает флаг равенства структуры одного параметра двух объектов
   bool              IsEqualParameters(const MqlParam &this_param,const MqlParam &compared_param) const
                       {
                        if(this_param.type==compared_param.type                     && 
                           this_param.integer_value==compared_param.integer_value   && 
                           this_param.string_value==compared_param.string_value     && 
                           ::NormalizeDouble(this_param.double_value-compared_param.double_value,8)==0
                          ) return true;
                        return false;
                       }
//--- Возвращает результат сравнения одного параметра двух объектов
   int               CompareParams(const MqlParam &this_param,const MqlParam &compared_param)
                       {
                        if(this.IsEqualParameters(this_param,compared_param))
                           return 0;
                        else if(this_param.type>compared_param.type                 || 
                           this_param.integer_value>compared_param.integer_value    || 
                           this_param.string_value>compared_param.string_value      || 
                           this_param.double_value>compared_param.double_value
                          ) return 1;
                        else if(this_param.type<compared_param.type                 || 
                           this_param.integer_value<compared_param.integer_value    || 
                           this_param.string_value<compared_param.string_value      || 
                           this_param.double_value<compared_param.double_value
                          ) return -1;
                        else
                           return -1;
                       }
//--- Копирует данные указанного массива указанного буфера
   bool              CopyArray(const uint buff_num,const uint array_num,const int to_copy,double &array[]);
//--- Копирует данные всех массивов указанного буфера
   bool              CopyArrays(const uint buff_num,const int to_copy);
   
public:
//--- Создаёт расчётную часть индикатора, возвращает хэндл
   int               CreateIndicator(void);
//--- (1) Рассчитывает индикатор, заполняет переданный (2) рисуемый массив-буфер, (3) массив-буфер индексов цвета (с учётом символа-периода графика) данными из буфера расчётной части индикатора данного класса
   bool              Calculate(void);
   bool              DataToBuffer(const string symbol_to,const ENUM_TIMEFRAMES timeframe_to,const uint buffer_num,const uint array_num,const int limit,double &buffer[]);
   bool              DataToColorBuffer(const string symbol_to,const ENUM_TIMEFRAMES timeframe_to,const uint buffer_num,const uint array_num,const int limit,double &plot_buffer[],double &color_buffer[]);

//--- (1) Устанавливает (2) возвращает инициализирующее значение для указанного буфера
   void              SetBufferInitValue(const uint buffer_num,const double value);
   double            BufferInitValue(const uint buffer_num) const;
//--- (1) Устанавливает (2) возвращает инициализирующее значение индекса цвета для указанного буфера
   void              SetBufferInitColorIndex(const uint buffer_num,const uchar index);
   uchar             BufferInitColorIndex(const uint buffer_num) const;
//--- (1) Устанавливает, (2) возвращает значение цвета по индексу для указанного буфера
   void              SetBufferColorToIndex(const uint buffer_num,const uchar color_idx,const color clr);
   color             BufferColorByIndex(const uint buffer_num,const uchar color_idx);
   //--- Возвращает флаг цветного буфера
   bool              IsColoredBuffer(const uint buffer_num) const;

//--- (1) Устанавливает (2) возвращает значение сдвига для указанного буфера
   void              SetBufferShift(const uint buffer_num,const int value);
   double            BufferShift(const uint buffer_num) const;

   //--- (1) Устанавливает, (2) возвращает стиль рисования указанного буфера, (3) номер соответствующего буфера исходного индикатора
   void              SetBufferDrawType(const uint buffer_num,const ENUM_DRAW_TYPE type,const uint buff_source);
   ENUM_DRAW_TYPE    BufferDrawType(const uint buffer_num);
   uint              BufferFrom(const uint buffer_num);

//--- Возвращает данные указанного буфера и массива (1) как есть, (2) относительно указанного символа/таймфрейма,
//--- данные указанного буфера цвета (3) как есть, (4) относительно указанного символа/таймфрейма,
//--- (5) количество данных в указанном буфере, (7) количество цветов, установленных буферу, (7) состояние линии индикатора как есть в буфере расчётной части,
//--- (8) состояние линии индикатора с учётом символа/периода графика, описание состояния линии (9) как есть в буфере (10) с учётом символа/периода графика
   double            GetData(const uint buffer_num,const uint array_num,const int index)  const;
   double            GetDataTo(const string symbol_to,const ENUM_TIMEFRAMES timeframe_to,const uint buffer_num,const uint array_num,const int index) const;
   double            GetColorData(const uint buffer_num,const int index)      const;
   double            GetColorDataTo(const string symbol_to,const ENUM_TIMEFRAMES timeframe_to,const uint buffer_num,const int index) const;
   uint              DataTotal(const uint buffer_num,const uint array_num)    const;
   uint              ColorsTotal(const uint buffer_num)                       const;
   ENUM_LINE_STATE   BufferLineState(const uint buffer_num,const uint array_num,const int index)   const;
   ENUM_LINE_STATE   BufferLineState(const string symbol_from,const ENUM_TIMEFRAMES timeframes_from,const uint buffer_num,const uint array_num,const int index) const;
   ENUM_LINE_STATE   BufferLineStateRelative(const int buffer_num,const uint array_num,const int index,const double level0,const double level1=EMPTY_VALUE);
   ENUM_LINE_STATE   BufferLineStateRelative(const string symbol_from,const ENUM_TIMEFRAMES timeframes_from,const int buffer_num,const uint array_num,const int index,const double level0,const double level1=EMPTY_VALUE);

//--- Возвращает (1) флаг успешности, (2) тип ошибки расчёта
   bool              IsSuccess(void)                           const { return this.m_success;               }
   ENUM_ERR_TYPE     TypeError(void)                           const { return this.m_type_err;              }
   
//--- Устанавливает (1) идентификатор, (2) Digits, (3) пользовательское описание, (4) описание указанного буфера
   void              SetID(const int id)                             { this.m_id=id;                        }
   void              SetDigits(const uint digits)                    { this.m_digits=(int)digits;           }
   void              SetDescription(const string descr)              { this.m_description=descr;            }
   void              SetBufferDescription(const uint buffer_num,const string descr);

//--- Устанавливает индексацию массивов буферов расчётной части не как в таймсерии
   void              SetAsSeriesOff(void);
//--- Возвращает флаг серийности указанного буфера, (2) синхронизированности исторических данных по символу/периоду
   bool              IsSeries(const uint buffer_num,const uint array_num) const;
   bool              IsSynchronized(void) const
                       {
                        return (bool)::SeriesInfoInteger(this.m_symbol,this.m_timeframe,SERIES_SYNCHRONIZED);
                       }
   
//--- Возвращает (1) таймфрейм, (2) символ, (3) наименование, (4) список параметров, (5) хэндл, (6) Digits
//--- количество (7) буферов, (8) баров, (9) идентификатор, (10) описание, (11) заголовок, (12) категорию,
//--- (13) количество параметрпов, (14) тип программы, описание (15) категории, (16) буфера индикатора
   ENUM_TIMEFRAMES   Timeframe(void)                           const { return this.m_timeframe;             }
   string            Symbol(void)                              const { return this.m_symbol;                }
   string            Name(void)                                const { return this.m_name;                  }
   string            Parameters(void)                          const { return this.m_parameters;            }
   int               Handle(void)                              const { return this.m_handle;                }
   int               Digits(void)                              const { return this.m_digits;                }
   uint              BuffersTotal(void)                        const { return this.m_buffers.Size();        }
   uint              RatesTotal(void)                          const { return this.m_rates_total;           }
   int               ID(void)                                  const { return this.m_id;                    }
   string            Description(void)                         const { return this.m_description;           }
   string            Title(void)                               const { return this.m_title;                 }
   ENUM_IND_CATEGORY Category(void)                            const { return this.m_category;              }
   uint              ParamsTotal(void)                         const { return this.m_param.Size();          }
   ENUM_PROGRAM_TYPE Program(void)                             const { return this.m_program;               }
   string            CategoryDescription(void);
   string            BufferDescription(const uint buffer_num);

//--- Возвращает (1) структуру параметров по индексу из массива, (2) флаг программы-индикатора, (3) описание таймфрейма
   MqlParam          GetMqlParam(const int index)              const { return this.m_param[index];          }
   bool              IsIndicator()                 const { return(this.Program()==PROGRAM_INDICATOR);       }
   string            TimeframeDescription(void)    const
                       {
                        return ::StringSubstr(::EnumToString(this.m_timeframe),7);
                       }
//--- Возвращает количество рассчитанных данных
   int               Calculated(void)                          const { return ::BarsCalculated(this.m_handle); }
   
//--- Виртуальный метод, возвращающий тип объекта (индикатора)
   virtual int       Type(void)                                const { return this.m_type;                  }
//--- Виртуальный метод сравнения двух объектов
   virtual int       Compare(const CObject *node,const int mode=0) const
                       {
                        const CIndMSTF *compared=node;
                        switch(mode)
                          {
                           case COMPARE_MODE_ID          : return(this.ID()>compared.ID()                   ? 1 : this.ID()<compared.ID()                   ? -1 : 0);
                           case COMPARE_MODE_HANDLE      : return(this.Handle()>compared.Handle()           ? 1 : this.Handle()<compared.Handle()           ? -1 : 0);
                           case COMPARE_MODE_CATEGORY    : return(this.Category()>compared.Category()       ? 1 : this.Category()<compared.Category()       ? -1 : 0);
                           case COMPARE_MODE_SYMBOL      : return(this.Symbol()>compared.Symbol()           ? 1 : this.Symbol()<compared.Symbol()           ? -1 : 0);
                           case COMPARE_MODE_TIMEFRAME   : return(this.Timeframe()>compared.Timeframe()     ? 1 : this.Timeframe()<compared.Timeframe()     ? -1 : 0);
                           case COMPARE_MODE_DESCRIPTION : return(this.Description()>compared.Description() ? 1 : this.Description()<compared.Description() ? -1 : 0);
                           //---Равенство всех параметров объектов
                           default                       : return(this.IsEqualIndicators(compared) ? 0 : -1);
                          }
                       }
//--- Возвращает флаг равенства параметров двух объектов-индикаторов
   bool              IsEqualIndicators(const CIndMSTF *compared) const
                       {
                        if(this.Type()!=compared.Type() || this.ParamsTotal()!=compared.ParamsTotal())
                           return false;
                        bool res=true;
                        int total=(int)this.ParamsTotal();
                        for(int i=0;i<total;i++)
                           res &=this.IsEqualParameters(this.m_param[i],compared.GetMqlParam(i));
                        res &=(this.Timeframe()==compared.Timeframe());
                        res &=(this.Symbol()==compared.Symbol());
                        return res;
                       }
//--- Таймер
   void OnTimer(void);
   
//--- Конструктор/деструктор
                     CIndMSTF(){}
                     CIndMSTF(const ENUM_INDICATOR type,const uint buffers,const string symbol,const ENUM_TIMEFRAMES timeframe);
                    ~CIndMSTF();
  };

В деструкторе класса освобождаем память всех массивов буферов индикатора:

//+------------------------------------------------------------------+
//| Деструктор                                                       |
//+------------------------------------------------------------------+
CIndMSTF::~CIndMSTF()
  {
//--- Удаляем таймер
   ::EventKillTimer();
//--- Освобождаем хэндл индикатора
   ::ResetLastError();
   if(this.m_handle!=INVALID_HANDLE && !::IndicatorRelease(this.m_handle))
      ::PrintFormat("%s: %s, handle %ld IndicatorRelease failed. Error %ld",__FUNCTION__,this.Title(),m_handle,::GetLastError());
//--- Освобождаем память массивов буферов
   for(int i=0;i<(int)this.BuffersTotal();i++)
     {
      ::ArrayFree(this.m_buffers[i].array0);
      ::ArrayFree(this.m_buffers[i].array1);
      ::ArrayFree(this.m_buffers[i].array2);
      ::ArrayFree(this.m_buffers[i].array3);
      ::ArrayFree(this.m_buffers[i].color_indexes);
     }
  }

Реализация новых объявленных методов:

//+------------------------------------------------------------------+
//| Устанавливает инициализирующее значение индекса цвета            |
//| для указанного буфера                                            |
//+------------------------------------------------------------------+
void CIndMSTF::SetBufferInitColorIndex(const uint buffer_num,const uchar index)
  {
//--- Проверяем корректность переданного в метод номера буфера и, если номер не верный, сообщаем об этом в журнал и уходим
   if(buffer_num>this.BuffersTotal()-1)
     {
      string buff_limit=(this.BuffersTotal()==1 ? "0" : "0 - "+string(this.BuffersTotal()-1));
      ::PrintFormat("%s: Invalid buffer number passed (%lu). Value must be %s",__FUNCTION__,buffer_num,buff_limit);
      return;
     }
//--- Устанавливаем новое инициализирующее значение индекса цвета для указанного буфера
   this.m_buffers[buffer_num].SetInitColorIdx(index);
  }
//+------------------------------------------------------------------+
//| Возвращает инициализирующее значение индекса цвета               |
//| для указанного буфера                                            |
//+------------------------------------------------------------------+
uchar CIndMSTF::BufferInitColorIndex(const uint buffer_num) const
  {
//--- Проверяем корректность переданного в метод номера буфера и, если номер не верный, сообщаем об этом в журнал
   if(buffer_num>this.BuffersTotal()-1)
     {
      string buff_limit=(this.BuffersTotal()==1 ? "0" : "0 - "+string(this.BuffersTotal()-1));
      ::PrintFormat("%s: Invalid buffer number passed (%lu). Value must be %s",__FUNCTION__,buffer_num,buff_limit);
      //--- Если у индикатора есть буферы, то возвращаем инициализирующее значение индекса цвета самого первого, иначе - 0
      return uchar(this.BuffersTotal()>0 ? this.m_buffers[0].InitColorIdx() : 0);
     }
//--- Возвращаем инициализирующее значение индекса цвета запрошенного буфера
   return this.m_buffers[buffer_num].InitColorIdx();
  }
//+------------------------------------------------------------------+
//| Устанавливает значение цвета по индексу для указанного буфера    |
//+------------------------------------------------------------------+
void CIndMSTF::SetBufferColorToIndex(const uint buffer_num,const uchar color_idx,const color clr)
  {
//--- Проверяем корректность переданного в метод номера буфера и, если номер не верный, сообщаем об этом в журнал и уходим
   if(buffer_num>this.BuffersTotal()-1)
     {
      string buff_limit=(this.BuffersTotal()==1 ? "0" : "0 - "+string(this.BuffersTotal()-1));
      ::PrintFormat("%s: Invalid buffer number passed (%lu). Value must be %s",__FUNCTION__,buffer_num,buff_limit);
      return;
     }
//--- Устанавливаем новое значение цвета по индексу для указанного буфера
   this.m_buffers[buffer_num].SetColorToIdx(color_idx,clr);
  }
//+------------------------------------------------------------------+
//| Возвращает значение цвета по индексу для указанного буфера       |
//+------------------------------------------------------------------+
color CIndMSTF::BufferColorByIndex(const uint buffer_num,const uchar color_idx)
  {
//--- Проверяем корректность переданного в метод номера буфера и, если номер не верный, сообщаем об этом в журнал
   if(buffer_num>this.BuffersTotal()-1)
     {
      string buff_limit=(this.BuffersTotal()==1 ? "0" : "0 - "+string(this.BuffersTotal()-1));
      ::PrintFormat("%s: Invalid buffer number passed (%lu). Value must be %s",__FUNCTION__,buffer_num,buff_limit);
      //--- Если у индикатора есть буферы, то возвращаем инициализирующее значение индекса цвета самого первого, иначе - 0
      return clrNONE;
     }
//--- Возвращаем значение цвета по индексу для запрошенного буфера
   return this.m_buffers[buffer_num].ColorByIdx(color_idx);
  }
//+------------------------------------------------------------------+
//| Возвращает флаг цветного буфера                                  |
//+------------------------------------------------------------------+
bool CIndMSTF::IsColoredBuffer(const uint buffer_num) const
  {
//--- Проверяем корректность переданного в метод номера буфера и, если номер не верный, сообщаем об этом в журнал
   if(buffer_num>this.BuffersTotal()-1)
     {
      string buff_limit=(this.BuffersTotal()==1 ? "0" : "0 - "+string(this.BuffersTotal()-1));
      ::PrintFormat("%s: Invalid buffer number passed (%lu). Value must be %s",__FUNCTION__,buffer_num,buff_limit);
      //--- Возвращаем false
      return false;
     }
//--- Возвращаем флаг цветности для указанного буфера
   return this.m_buffers[buffer_num].IsColoredBuffer();
  }
//+------------------------------------------------------------------+
//| Устанавливает стиль рисования указанного буфера                  |
//+------------------------------------------------------------------+
void CIndMSTF::SetBufferDrawType(const uint buffer_num,const ENUM_DRAW_TYPE type,const uint buff_source)
  {
//--- Проверяем корректность переданного в метод номера буфера и, если номер не верный, сообщаем об этом в журнал и уходим
   if(buffer_num>this.BuffersTotal()-1)
     {
      string buff_limit=(this.BuffersTotal()==1 ? "0" : "0 - "+string(this.BuffersTotal()-1));
      ::PrintFormat("%s: Invalid buffer number passed (%lu). Value must be %s",__FUNCTION__,buffer_num,buff_limit);
      return;
     }
//--- Устанавливаем стиль рисования для буфера
   this.m_buffers[buffer_num].SetBufferDrawType(type,buff_source);
  }
//+------------------------------------------------------------------+
//| Возвращает стиль рисования указанного буфера                     |
//+------------------------------------------------------------------+
ENUM_DRAW_TYPE CIndMSTF::BufferDrawType(const uint buffer_num)
  {
//--- Проверяем корректность переданного в метод номера буфера и, если номер не верный, сообщаем об этом в журнал
   if(buffer_num>this.BuffersTotal()-1)
     {
      string buff_limit=(this.BuffersTotal()==1 ? "0" : "0 - "+string(this.BuffersTotal()-1));
      ::PrintFormat("%s: Invalid buffer number passed (%lu). Value must be %s",__FUNCTION__,buffer_num,buff_limit);
      //--- Если у индикатора есть буферы, то возвращаем значение сдвига самого первого, иначе - 0
      return(this.BuffersTotal()>0 ? this.m_buffers[0].DrawType() : DRAW_NONE);
     }
//--- Возвращаем стиль рисования запрошенного буфера
   return this.m_buffers[buffer_num].DrawType();
  }
//+------------------------------------------------------------------+
//| Возвращает номер соответствующего буфера исходного индикатора    |
//+------------------------------------------------------------------+
uint CIndMSTF::BufferFrom(const uint buffer_num)
  {
//--- Проверяем корректность переданного в метод номера буфера и, если номер не верный, сообщаем об этом в журнал
   if(buffer_num>this.BuffersTotal()-1)
     {
      string buff_limit=(this.BuffersTotal()==1 ? "0" : "0 - "+string(this.BuffersTotal()-1));
      ::PrintFormat("%s: Invalid buffer number passed (%lu). Value must be %s",__FUNCTION__,buffer_num,buff_limit);
      //--- Если у индикатора есть буферы, то возвращаем значение сдвига самого первого, иначе - 0
      return(this.BuffersTotal()>0 ? this.m_buffers[0].Shift() : 0);
     }
//--- Возвращаем номер соответствующего буфера исходного индикатора для запрошенного буфера
   return this.m_buffers[buffer_num].BufferFrom();
  }
//+------------------------------------------------------------------+
//| Копирует данные указанного массива указанного буфера             |
//+------------------------------------------------------------------+
bool CIndMSTF::CopyArray(const uint buff_num,const uint array_num,const int to_copy,double &array[])
  {
   ::ResetLastError();
//--- Копируем либо два последних бара в массив array, либо все имеющиеся исторические данные из массива расчётной части индикатора в массив-буфер объекта индикатора
   int copied=0;
   if(to_copy==2)
     {
      switch(array_num)
        {
         case 0   :  case 1 : case 2 :
         case 3   :  copied=::CopyBuffer(this.m_handle,this.m_buffers[buff_num].BufferFrom(),  -this.m_buffers[buff_num].Shift(),to_copy,array);   break;
         case 4   :  copied=::CopyBuffer(this.m_handle,this.m_buffers[buff_num].BufferFrom()+1,-this.m_buffers[buff_num].Shift(),to_copy,array);   break;
         default  :  break;
        }
     }
   else
     {
      switch(array_num)
        {
         case 0   :  copied=::CopyBuffer(this.m_handle,this.m_buffers[buff_num].BufferFrom(),  -this.m_buffers[buff_num].Shift(),to_copy,this.m_buffers[buff_num].array0);          break;
         case 1   :  copied=::CopyBuffer(this.m_handle,this.m_buffers[buff_num].BufferFrom(),  -this.m_buffers[buff_num].Shift(),to_copy,this.m_buffers[buff_num].array1);          break;
         case 2   :  copied=::CopyBuffer(this.m_handle,this.m_buffers[buff_num].BufferFrom(),  -this.m_buffers[buff_num].Shift(),to_copy,this.m_buffers[buff_num].array2);          break;
         case 3   :  copied=::CopyBuffer(this.m_handle,this.m_buffers[buff_num].BufferFrom(),  -this.m_buffers[buff_num].Shift(),to_copy,this.m_buffers[buff_num].array3);          break;
         case 4   :  copied=::CopyBuffer(this.m_handle,this.m_buffers[buff_num].BufferFrom()+1,-this.m_buffers[buff_num].Shift(),to_copy,this.m_buffers[buff_num].color_indexes);   break;
         default  :  break;
        }
     }
//--- Если копирование успешно
   if(copied>0)
      return true;
//--- Если скопированы не все данные
//--- Если CopyBuffer вернула -1, то это означает начало загрузки исторических данных
//--- выведем сообщение об этом в журнал
   if(copied==WRONG_VALUE)
      ::PrintFormat("%s::%s: Start downloading data by %s/%s. Waiting for the next tick...",__FUNCTION__,this.Title(),this.m_symbol,this.TimeframeDescription());
//--- В любом ином случае - ещё не все данные удалось скопировать
//--- выведем сообщение об этом в журнал
   else
      ::PrintFormat("%s::%s: Not all data was copied. Data available: %lu, total copied: %ld",__FUNCTION__,this.Title(),this.m_rates_total,copied);
   return false;
  }
//+------------------------------------------------------------------+
//| Копирует данные всех массивов указанного буфера                  |
//+------------------------------------------------------------------+
bool CIndMSTF::CopyArrays(const uint buff_num,const int to_copy)
  {
   bool res=true;
   double array[2];
   if(to_copy==2)
     {
      switch(this.BufferDrawType(buff_num))
        {
         //--- Один буфер
         case DRAW_LINE             :
         case DRAW_HISTOGRAM        :
         case DRAW_ARROW            :
         case DRAW_SECTION          :
            res=this.CopyArray(buff_num,0,to_copy,array);
            if(res)
              {
               this.m_buffers[buff_num].array0[this.DataTotal(buff_num,0)-1]=array[1];
               this.m_buffers[buff_num].array0[this.DataTotal(buff_num,0)-2]=array[0];
              }
            return res;
         //--- Два буфера
         case DRAW_HISTOGRAM2       :
         case DRAW_ZIGZAG           :
         case DRAW_FILLING          :
            res=this.CopyArray(buff_num,0,to_copy,array);
            if(res)
              {
               this.m_buffers[buff_num].array0[this.DataTotal(buff_num,0)-1]=array[1];
               this.m_buffers[buff_num].array0[this.DataTotal(buff_num,0)-2]=array[0];
              }
            res &=this.CopyArray(buff_num,1,to_copy,array);
            if(res)
              {
               this.m_buffers[buff_num].array1[this.DataTotal(buff_num,1)-1]=array[1];
               this.m_buffers[buff_num].array1[this.DataTotal(buff_num,1)-2]=array[0];
              }
            return res;
         //--- Четыре буфера
         case DRAW_BARS             :
         case DRAW_CANDLES          :
            res=this.CopyArray(buff_num,0,to_copy,array);
            if(res)
              {
               this.m_buffers[buff_num].array0[this.DataTotal(buff_num,0)-1]=array[1];
               this.m_buffers[buff_num].array0[this.DataTotal(buff_num,0)-2]=array[0];
              }
            res &=this.CopyArray(buff_num,1,to_copy,array);
            if(res)
              {
               this.m_buffers[buff_num].array1[this.DataTotal(buff_num,1)-1]=array[1];
               this.m_buffers[buff_num].array1[this.DataTotal(buff_num,1)-2]=array[0];
              }
            res &=this.CopyArray(buff_num,2,to_copy,array);
            if(res)
              {
               this.m_buffers[buff_num].array2[this.DataTotal(buff_num,2)-1]=array[1];
               this.m_buffers[buff_num].array2[this.DataTotal(buff_num,2)-2]=array[0];
              }
            res &=this.CopyArray(buff_num,3,to_copy,array);
            if(res)
              {
               this.m_buffers[buff_num].array3[this.DataTotal(buff_num,3)-1]=array[1];
               this.m_buffers[buff_num].array3[this.DataTotal(buff_num,3)-2]=array[0];
              }
            return res;
         //--- Один буфер + буфер цвета
         case DRAW_COLOR_LINE       :
         case DRAW_COLOR_HISTOGRAM  :
         case DRAW_COLOR_ARROW      :
         case DRAW_COLOR_SECTION    :
            res=this.CopyArray(buff_num,0,to_copy,array);
            if(res)
              {
               this.m_buffers[buff_num].array0[this.DataTotal(buff_num,0)-1]=array[1];
               this.m_buffers[buff_num].array0[this.DataTotal(buff_num,0)-2]=array[0];
              }
            res &=this.CopyArray(buff_num,4,to_copy,array);
            if(res)
              {
               this.m_buffers[buff_num].color_indexes[this.DataTotal(buff_num,4)-1]=array[1];
               this.m_buffers[buff_num].color_indexes[this.DataTotal(buff_num,4)-2]=array[0];
              }
            return res;
         //--- Два буфера + буфер цвета
         case DRAW_COLOR_HISTOGRAM2 :
         case DRAW_COLOR_ZIGZAG     :
            res=this.CopyArray(buff_num,0,to_copy,array);
            if(res)
              {
               this.m_buffers[buff_num].array0[this.DataTotal(buff_num,0)-1]=array[1];
               this.m_buffers[buff_num].array0[this.DataTotal(buff_num,0)-2]=array[0];
              }
            res &=this.CopyArray(buff_num,1,to_copy,array);
            if(res)
              {
               this.m_buffers[buff_num].array1[this.DataTotal(buff_num,1)-1]=array[1];
               this.m_buffers[buff_num].array1[this.DataTotal(buff_num,1)-2]=array[0];
              }
            res &=this.CopyArray(buff_num,4,to_copy,array);
            if(res)
              {
               this.m_buffers[buff_num].color_indexes[this.DataTotal(buff_num,4)-1]=array[1];
               this.m_buffers[buff_num].color_indexes[this.DataTotal(buff_num,4)-2]=array[0];
              }
            return res;
         //--- Четыре буфера + буфер цвета
         case DRAW_COLOR_BARS       :
         case DRAW_COLOR_CANDLES    :
            res=this.CopyArray(buff_num,0,to_copy,array);
            if(res)
              {
               this.m_buffers[buff_num].array0[this.DataTotal(buff_num,0)-1]=array[1];
               this.m_buffers[buff_num].array0[this.DataTotal(buff_num,0)-2]=array[0];
              }
            res &=this.CopyArray(buff_num,1,to_copy,array);
            if(res)
              {
               this.m_buffers[buff_num].array1[this.DataTotal(buff_num,1)-1]=array[1];
               this.m_buffers[buff_num].array1[this.DataTotal(buff_num,1)-2]=array[0];
              }
            res &=this.CopyArray(buff_num,2,to_copy,array);
            if(res)
              {
               this.m_buffers[buff_num].array2[this.DataTotal(buff_num,2)-1]=array[1];
               this.m_buffers[buff_num].array2[this.DataTotal(buff_num,2)-2]=array[0];
              }
            res &=this.CopyArray(buff_num,3,to_copy,array);
            if(res)
              {
               this.m_buffers[buff_num].array3[this.DataTotal(buff_num,3)-1]=array[1];
               this.m_buffers[buff_num].array3[this.DataTotal(buff_num,3)-2]=array[0];
              }
            res &=this.CopyArray(buff_num,4,to_copy,array);
            if(res)
              {
               this.m_buffers[buff_num].color_indexes[this.DataTotal(buff_num,4)-1]=array[1];
               this.m_buffers[buff_num].color_indexes[this.DataTotal(buff_num,4)-2]=array[0];
              }
            return res;
         //---DRAW_NONE
         default:
           break;
        }
     }
   else
     {
      switch(this.BufferDrawType(buff_num))
        {
         //--- Один буфер
         case DRAW_LINE             :
         case DRAW_HISTOGRAM        :
         case DRAW_ARROW            :
         case DRAW_SECTION          :
            return this.CopyArray(buff_num,0,to_copy,array);
         //--- Два буфера
         case DRAW_HISTOGRAM2       :
         case DRAW_ZIGZAG           :
         case DRAW_FILLING          :
            res  =this.CopyArray(buff_num,0,to_copy,array);
            res &=this.CopyArray(buff_num,1,to_copy,array);
            return res;
         //--- Четыре буфера
         case DRAW_BARS             :
         case DRAW_CANDLES          :
            res  =this.CopyArray(buff_num,0,to_copy,array);
            res &=this.CopyArray(buff_num,1,to_copy,array);
            res &=this.CopyArray(buff_num,2,to_copy,array);
            res &=this.CopyArray(buff_num,3,to_copy,array);
            return res;
         //--- Один буфер + буфер цвета
         case DRAW_COLOR_LINE       :
         case DRAW_COLOR_HISTOGRAM  :
         case DRAW_COLOR_ARROW      :
         case DRAW_COLOR_SECTION    :
            res  =this.CopyArray(buff_num,0,to_copy,array);
            res &=this.CopyArray(buff_num,4,to_copy,array);
            return res;
         //--- Два буфера + буфер цвета
         case DRAW_COLOR_HISTOGRAM2 :
         case DRAW_COLOR_ZIGZAG     :
            res  =this.CopyArray(buff_num,0,to_copy,array);
            res &=this.CopyArray(buff_num,1,to_copy,array);
            res &=this.CopyArray(buff_num,4,to_copy,array);
            return res;
         //--- Четыре буфера + буфер цвета
         case DRAW_COLOR_BARS       :
         case DRAW_COLOR_CANDLES    :
            res  =this.CopyArray(buff_num,0,to_copy,array);
            res &=this.CopyArray(buff_num,1,to_copy,array);
            res &=this.CopyArray(buff_num,2,to_copy,array);
            res &=this.CopyArray(buff_num,3,to_copy,array);
            res &=this.CopyArray(buff_num,4,to_copy,array);
            return res;
         //---DRAW_NONE
         default:
           break;
        }
     }
   return false;
  }

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

//+------------------------------------------------------------------+
//| Заполняет буферы объекта данными из буфера расчётной части       |
//+------------------------------------------------------------------+
bool CIndMSTF::Calculate(void)
  {
//--- Устанавливаем флагу успешности значение true, а типу ошибки - её отсутствие
   this.m_success=true;
   this.m_type_err=ERR_TYPE_NO_ERROR;
//--- Если данные ещё не синхронизированы с торговым сервером,
   if(!this.IsSynchronized())
     {
      //--- Выводим в журнал сообщение о несинхронизированности,
      ::PrintFormat("%s::%s: Waiting for data to sync...",__FUNCTION__,this.Title());
      //--- устанавливаем тип ошибки, к флагу ошибки добавляем false и возвращаем false
      this.m_type_err=ERR_TYPE_NO_SYNC;
      this.m_success=false;
      return false;
     }
//--- Если метод Calculated вернул -1, это означает начало закачки данных
   if(this.Calculated()==WRONG_VALUE)
     {
      //--- Выводим в журнал сообщение о начале закачки данных,
      ::PrintFormat("%s::%s: Start downloading data by %s/%s. Waiting for the next tick...",__FUNCTION__,this.Title(),this.m_symbol,this.TimeframeDescription());
      //--- устанавливаем тип ошибки, к флагу ошибки добавляем false и возвращаем false
      this.m_type_err=ERR_TYPE_NO_DATA;
      this.m_success=false;
      return false;
     }
//--- Если метод Calculated вернул 0, это означает, что индикатор ещё не рассчитан
   if(this.Calculated()==0)
     {
      //--- Выводим в журнал сообщение об ожидании расчёта индикатора,
      ::PrintFormat("%s::%s: Waiting for a new tick and when the indicator will be calculated...",__FUNCTION__,this.Title());
      //--- устанавливаем тип ошибки, к флагу ошибки добавляем false и возвращаем false
      this.m_type_err=ERR_TYPE_NO_CALC;
      this.m_success=false;
      return false;
     }
//--- Получаем количество баров данных по символу/периоду индикатора
   int bars=::Bars(this.m_symbol,this.m_timeframe);
//--- Если функция Bars вернула нулевое значение, что часто бывает на выходных, рассчитаем доступное количество баров
   if(bars==0)
     {
      //--- Получим дату самого первого доступного бара в истории по символу/периоду
      datetime firstdate=(datetime)::SeriesInfoInteger(this.m_symbol,this.m_timeframe,SERIES_FIRSTDATE);
      //--- Получим дату последнего (текущего) бара в истории по символу/периоду
      datetime lastdate=(datetime)::SeriesInfoInteger(this.m_symbol,this.m_timeframe,SERIES_LASTBAR_DATE);
      //--- Рассчитаем количество баров между первой и последней датами истории
      int sec=::PeriodSeconds(this.m_timeframe);
      ulong date_bars=(((ulong)lastdate-(ulong)firstdate)/(sec>0 ? sec : 1))+1;
      //--- В переменную bars запишем меньшее значение из рассчитанного количества баров и максимального количества баров, доступных в терминале
      bars=(int)fmin(date_bars,::TerminalInfoInteger(TERMINAL_MAXBARS));
     }
//--- Запишем в m_rates_total полученное количество доступных баров
   if(this.m_rates_total!=bars)
      this.m_rates_total=bars;
//--- Если количество доступных баров получено, и их два и меньше,
   if(this.m_rates_total>=0 && this.m_rates_total<3)
     {
      //--- Выведем в журнал сообщение о слишком маленьком количестве доступных баров
      ::PrintFormat("%s::%s: Not enough data for calculation: %ld bars. Waiting for the next tick...",__FUNCTION__,this.Title(),this.m_rates_total);
      //--- установим тип ошибки, к флагу ошибки добавим false и вернём false
      this.m_type_err=ERR_TYPE_NO_DATA;
      this.m_success=false;
      return false;
     }

//--- Рассчитаем количество необходимых баров для расчёта индикатора
//--- Либо вся доступная история, либо 1 при открытии нового бара, либо 0 на текущем тике
   this.m_limit=this.m_rates_total-this.m_prev_calculated;
   this.m_prev_calculated=this.Calculated();

//--- Объявляем массив размером 2 для получения в него данных из буфера расчётной части индикатора
//--- Получать будем всегда по два бара - прошлый и текущий
   double array[2];
//--- Получаем количество буферов индикатора
   int total=(int)this.BuffersTotal();
//--- Если рассчитанное значение m_limit больше 1 - значит это либо первый запуск, либо изменения в исторических данных
//--- В этом случае необходим полный перерасчёт индикатора
   if(this.m_limit>1)
     {
      //--- В цикле по количеству буферов индикатора
      for(int i=0;i<total;i++)
        {
         //--- изменяем размер массива буфера индикатора и инициализируем его установленным для этого буфера "пустым" значением
         this.BufferInitialize(i,this.m_rates_total);
         //--- Определяем количество копируемых данных
         int to_copy=(this.m_prev_calculated>this.m_rates_total ? this.m_rates_total : this.m_prev_calculated);
         
         //--- Если не все массивы успешно скопированы - запишем в m_success значение false
         if(!this.CopyArrays(i,to_copy))
            this.m_success &=false;
        }
      //--- Если после цикла есть ошибки - возвращаем false
      if(!this.m_success)
        {
         this.m_type_err=ERR_TYPE_NO_DATA;
         return false;
        }
      //--- Всё успешно - вернём true
      this.m_type_err=ERR_TYPE_NO_ERROR;
      this.m_success=true;
      return true;
     }
//--- Если рассчитанное значение m_limit меньше, либо равно 1 - значит это либо открытие нового бара (m_limit==1), либо текущий тик (m_limit==0)
//--- В этом случае необходимо рассчитать два бара - первый и текущий
   if(this.m_limit<=1)
     {
      //--- В цикле по количеству буферов индикатора
      for(int i=0;i<total;i++)
        {
         //--- Если это открытие нового бара и не удалось изменить размер буфера индикатора,
         if(this.m_limit==1 && !this.BufferResize(i,this.m_rates_total))
           {
            //--- добавим к переменной m_success значение false и вернём false
            //--- При этом сообщение об ошибке будет выведено в журнал из метода BufferResize
            this.m_success=false;
            return false;
           }
         //--- Если не удалось скопировать два бара из буфера расчётной части индикатора,
         ::ResetLastError();
         if(!this.CopyArrays(i,2))
           {
            //--- сообщим об этом в журнал, добавим к переменной m_success значение false и вернём false
            ::PrintFormat("%s::%s: CopyBuffer(%lu) failed. Error %lu",__FUNCTION__,this.Title(),i,::GetLastError());
            this.m_success &=false;
           }
        }
      //--- Если после цикла есть ошибки - возвращаем false
      if(!this.m_success)
        {
         this.m_type_err=ERR_TYPE_NO_DATA;
         return false;
        }    
      //--- Успешно
      this.m_type_err=ERR_TYPE_NO_ERROR;
      this.m_success=true;
      return true;
     }
//--- Неопределённый вариант limit - возвращаем false
   return false;
  }

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

//+------------------------------------------------------------------+
//| Заполняет переданные в метод рисуемый массив                     |
//|  и массив индексов цвета данными из буфера класса                |
//+------------------------------------------------------------------+
bool CIndMSTF::DataToColorBuffer(const string symbol_to,const ENUM_TIMEFRAMES timeframe_to,const uint buffer_num,const uint array_num,const int limit,double &plot_buffer[],double &color_buffer[])
  {
//--- Устанавливаем флаг успешности
   this.m_success=true;
//--- Получаем направление индексации переданного в метод массива буфера и,
//--- если индексация не как у таймсерии, - устанавливаем индексацию как у таймсерии
   bool as_series_plot=::ArrayGetAsSeries(plot_buffer);
   if(!as_series_plot)
      ::ArraySetAsSeries(plot_buffer,true);
   bool as_series_color=::ArrayGetAsSeries(color_buffer);
   if(!as_series_color)
      ::ArraySetAsSeries(color_buffer,true);
//--- Устанавливаем наименование символа и значение таймфрейма, переданные в метод
   string symbol=(symbol_to=="" || symbol_to==NULL ? ::Symbol() : symbol_to);
   ENUM_TIMEFRAMES timeframe=(timeframe_to==PERIOD_CURRENT ? ::Period() : timeframe_to);
   datetime array[2];
//--- Если это первый запуск, или изменения в истории - инициальзируем массив буфера, переданный в метод
   if(limit>1 && this.m_limit>1)
     {
      ::PrintFormat("%s::%s First start, or historical data has been changed. Initialize Buffer(%lu)",__FUNCTION__,this.Title(),buffer_num);
      ::ArrayInitialize(plot_buffer,this.BufferInitValue(buffer_num));
      ::ArrayInitialize(color_buffer,this.BufferInitColorIndex(buffer_num));
     }
//--- Устанавливаем значение счётчика цикла (не более максимального количества баров в терминале на графике)
   int count=(limit<=1 ? 2 : ::fmin(::TerminalInfoInteger(TERMINAL_MAXBARS),limit));
//--- В цикле от нулевого бара до значения счётчика цикла
   for(int i=0;i<count;i++)
     {
      //--- Если таймфрейм графика совпадает с таймфреймом объекта класса - заполняем буфер напрямую из массива объекта класса
      if(timeframe==::Period() && this.m_timeframe==::Period())
        {
         plot_buffer[i]=this.GetData(buffer_num,array_num,i);
         color_buffer[i]=this.GetColorData(buffer_num,i);
        }
      //--- Иначе, если таймфрейм графика не равен таймфрейму объекта класса
      else
        {
         //--- Узнаём какому времени данного класса принадлежит бар текущего таймфрейма графика, соответствующий индексу цикла
         ::ResetLastError();
         if(::CopyTime(symbol,timeframe,i,2,array)!=2)
           {
            //--- Если данных нет в терминале - идём дальше
            if(::GetLastError()==4401)
               continue;
            //--- Ошибка получения существующих данных - возвращаем false
            this.m_success &=false;
            return false;
           }
         //--- По времени бара текущего таймфрейма графика находим соответствующий индекс бара периода графика объекта класса
         ::ResetLastError();
         int bar=::iBarShift(this.m_symbol,this.m_timeframe,array[0]);
         if(bar==WRONG_VALUE)
           {
            this.m_success &=false;
            continue;
           }
         //--- Если это исторические данные (не первый и не нулевой бар) -
         //--- записываем в буфер индикатора по индексу цикла значение, полученное из буфера расчётной части
         if(i>1)
           {
            plot_buffer[i]=this.GetData(buffer_num,array_num,bar);
            color_buffer[i]=this.GetColorData(buffer_num,bar);
           }
         //--- Если это текущий (нулевой) или предыдущий (первый) бар
         else
           {
            //--- Получаем время баров 0 и 1 по символу/таймфрейму объекта класса
            if(::CopyTime(this.m_symbol,this.m_timeframe,0,2,array)!=2)
              {
               this.m_success &=false;
               return false;
              }
            //--- Получаем по времени индексы текущего и предыдущего баров на графике, символ/период которого передан в метод
            int bar0=::iBarShift(symbol,timeframe,array[1]);
            int bar1=::iBarShift(symbol,timeframe,array[0]);
            if(bar0==WRONG_VALUE || bar1==WRONG_VALUE)
              {
               this.m_success &=false;
               return false;
              }
            //--- Если таймфрейм графика младше таймфрейма объекта класса,
            if(timeframe<this.m_timeframe)
              {
               //--- в цикле от бара с меньшим временем до текущего бара графика заполняем буфер данными из двух последних ячеек массива буфера индикатора
               for(int j=bar1;j>=0;j--)
                 {
                  plot_buffer[j]=this.GetData(buffer_num,array_num,(j>bar0 ? 1 : 0));
                  color_buffer[j]=this.GetColorData(buffer_num,(j>bar0 ? 1 : 0));
                 }
              }
            //--- Если таймфрейм графика старше таймфрейма объекта класса,
            else
              {
               //--- Получаем время текущего и предыдущего баров по символу/таймфрейму текущего графика
               if(::CopyTime(symbol,timeframe,0,2,array)!=2)
                 {
                  this.m_success &=false;
                  return false;
                 }
               //--- Получаем по времени индексы баров в буфере расчётной части индикатора, соответствующие времени текущего и предыдущего баров на графике
               int bar0=::iBarShift(this.m_symbol,this.m_timeframe,array[1]);
               int bar1=::iBarShift(this.m_symbol,this.m_timeframe,array[0]);
               //--- Записываем в буфер индикатора по индексам 1 и 0 значения из соответствующих индексов буфера расчётной части
               plot_buffer[1]=this.GetData(buffer_num,array_num,bar1);
               plot_buffer[0]=this.GetData(buffer_num,array_num,bar0);
               color_buffer[1]=this.GetColorData(buffer_num,bar1);
               color_buffer[0]=this.GetColorData(buffer_num,bar0);
              } 
           }
        }
     }
//--- Устанавливаем изначальную индексацию переданного в метод массива буфера
   ::ArraySetAsSeries(plot_buffer,as_series_plot);
   ::ArraySetAsSeries(color_buffer,as_series_color);
//--- Успешно
   return true;
  }

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

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

//+------------------------------------------------------------------+
//| Заполняет переданный рисуемый массив данными из буфера класса    |
//+------------------------------------------------------------------+
bool CIndMSTF::DataToBuffer(const string symbol_to,const ENUM_TIMEFRAMES timeframe_to,const uint buffer_num,const uint array_num,const int limit,double &buffer[])
  {
//--- Устанавливаем флаг успешности
   this.m_success=true;
//--- Получаем направление индексации переданного в метод массива буфера и,
//--- если индексация не как у таймсерии, - устанавливаем индексацию как у таймсерии
   bool as_series=::ArrayGetAsSeries(buffer);
   if(!as_series)
      ::ArraySetAsSeries(buffer,true);
//--- Устанавливаем наименование символа и значение таймфрейма, переданные в метод
   string symbol=(symbol_to=="" || symbol_to==NULL ? ::Symbol() : symbol_to);
   ENUM_TIMEFRAMES timeframe=(timeframe_to==PERIOD_CURRENT ? ::Period() : timeframe_to);
   datetime array[2];
//--- Если это первый запуск, или изменения в истории - инициальзируем массив буфера, переданный в метод
   if(limit>1 && this.m_limit>1)
     {
      ::PrintFormat("%s::%s First start, or historical data has been changed. Initialize Buffer(%lu)",__FUNCTION__,this.Title(),buffer_num);
      ::ArrayInitialize(buffer,this.BufferInitValue(buffer_num));
     }
//--- Устанавливаем значение счётчика цикла (не более максимального количества баров в терминале на графике)
   int count=(limit<=1 ? 2 : ::fmin(::TerminalInfoInteger(TERMINAL_MAXBARS),limit));
//--- В цикле от нулевого бара до значения счётчика цикла
   for(int i=0;i<count;i++)
     {
      //--- Если таймфрейм графика совпадает с таймфреймом объекта класса - заполняем буфер напрямую из массива объекта класса
      if(timeframe==::Period() && this.m_timeframe==::Period())
         buffer[i]=this.GetData(buffer_num,array_num,i);
      //--- Иначе, если таймфрейм графика не равен таймфрейму объекта класса
      else
        {
         //--- Узнаём какому времени данного класса принадлежит бар текущего таймфрейма графика, соответствующий индексу цикла
         ::ResetLastError();
         if(::CopyTime(symbol,timeframe,i,2,array)!=2)
           {
            //--- Если данных нет в терминале - идём дальше
            if(::GetLastError()==4401)
               continue;
            //--- Ошибка получения существующих данных - возвращаем false
            this.m_success &=false;
            return false;
           }
         //--- По времени бара текущего таймфрейма графика находим соответствующий индекс бара периода графика объекта класса
         ::ResetLastError();
         int bar=::iBarShift(this.m_symbol,this.m_timeframe,array[0]);
         if(bar==WRONG_VALUE)
           {
            this.m_success &=false;
            continue;
           }
         //--- Если это исторические данные (не первый и не нулевой бар) -
         //--- записываем в буфер индикатора по индексу цикла значение, полученное из буфера расчётной части
         if(i>1)
            buffer[i]=this.GetData(buffer_num,array_num,bar);
         //--- Если это текущий (нулевой) или предыдущий (первый) бар
         else
           {
            //--- Получаем время баров 0 и 1 по символу/таймфрейму объекта класса
            if(::CopyTime(this.m_symbol,this.m_timeframe,0,2,array)!=2)
              {
               this.m_success &=false;
               return false;
              }
            //--- Получаем по времени индексы текущего и предыдущего баров на графике, символ/период которого передан в метод
            int bar0=::iBarShift(symbol,timeframe,array[1]);
            int bar1=::iBarShift(symbol,timeframe,array[0]);
            if(bar0==WRONG_VALUE || bar1==WRONG_VALUE)
              {
               this.m_success &=false;
               return false;
              }
            //--- Если таймфрейм графика младше таймфрейма объекта класса,
            if(timeframe<this.m_timeframe)
              {
               //--- в цикле от бара с меньшим временем до текущего бара графика заполняем буфер данными из двух последних ячеек массива буфера индикатора
               for(int j=bar1;j>=0;j--)
                  buffer[j]=this.GetData(buffer_num,array_num,(j>bar0 ? 1 : 0));
              }
            //--- Если таймфрейм графика старше таймфрейма объекта класса,
            else
              {
               //--- Получаем время текущего и предыдущего баров по символу/таймфрейму текущего графика
               if(::CopyTime(symbol,timeframe,0,2,array)!=2)
                 {
                  this.m_success &=false;
                  return false;
                 }
               //--- Получаем по времени индексы баров в буфере расчётной части индикатора, соответствующие времени текущего и предыдущего баров на графике
               int bar0=::iBarShift(this.m_symbol,this.m_timeframe,array[1]);
               int bar1=::iBarShift(this.m_symbol,this.m_timeframe,array[0]);
               //--- Записываем в буфер индикатора по индексам 1 и 0 значения из соответствующих индексов буфера расчётной части
               buffer[1]=this.GetData(buffer_num,array_num,bar1);
               buffer[0]=this.GetData(buffer_num,array_num,bar0);
              } 
           }
        }
     }
//--- Устанавливаем изначальную индексацию переданного в метод массива буфера
   ::ArraySetAsSeries(buffer,as_series);
//--- Успешно
   return true;
  }
//+------------------------------------------------------------------+
//| Возвращает данные указанного буфера как есть                     |
//+------------------------------------------------------------------+
double CIndMSTF::GetData(const uint buffer_num,const uint array_num,const int index) const
  {
//--- Проверяем корректность переданного в метод номера буфера и, если номер не верный, сообщаем об этом в журнал
   if(buffer_num>this.BuffersTotal()-1)
     {
      string buff_limit=(this.BuffersTotal()==1 ? "0" : "0 - "+string(this.BuffersTotal()-1));
      ::PrintFormat("%s: Invalid buffer number passed (%lu). Value must be %s",__FUNCTION__,buffer_num,buff_limit);
      //--- Если у индикатора есть буферы, то возвращаем "пустое" значение самого первого, иначе - EMPTY_VALUE
      return(this.BuffersTotal()>0 ? this.BufferInitValue(0) : EMPTY_VALUE);
     }
//--- Если указан не правильный индекс - возвращаем "пустое" значение указанного буфера
   if(index<0 || index>(int)this.DataTotal(buffer_num,array_num)-1)
      return this.BufferInitValue(buffer_num);
//--- Рассчитываем реальный индекс в массиве-буфере и возвращаем значение по этому индексу
   int n=int(this.DataTotal(buffer_num,array_num)-1-index);
   switch(array_num)
     {
      case 0 : return this.m_buffers[buffer_num].array0[n];
      case 1 : return this.m_buffers[buffer_num].array1[n];
      case 2 : return this.m_buffers[buffer_num].array2[n];
      case 3 : return this.m_buffers[buffer_num].array3[n];
      case 4 : return this.m_buffers[buffer_num].color_indexes[n];
      default: break;
     }
   return EMPTY_VALUE;
  }
//+-------------------------------------------------------------------+
//| Возвращает данные указанного буфера на указанный символ/таймфрейм |
//+-------------------------------------------------------------------+
double CIndMSTF::GetDataTo(const string symbol_to,const ENUM_TIMEFRAMES timeframe_to,const uint buffer_num,const uint array_num,const int index) const
  {
//--- Если указан текущий символ/период графика
   if(timeframe_to==::Period() && this.m_timeframe==::Period() && symbol_to==::Symbol() && this.m_symbol==::Symbol())
      return this.GetData(buffer_num,array_num,index);
//--- Проверяем корректность переданного в метод номера буфера и, если номер не верный, сообщаем об этом в журнал
   if(buffer_num>this.BuffersTotal()-1)
     {
      string buff_limit=(this.BuffersTotal()==1 ? "0" : "0 - "+string(this.BuffersTotal()-1));
      ::PrintFormat("%s: Invalid buffer number passed (%lu). Value must be %s",__FUNCTION__,buffer_num,buff_limit);
      //--- Если у индикатора есть буферы, то возвращаем "пустое" значение самого первого, иначе - EMPTY_VALUE
      return(this.BuffersTotal()>0 ? this.BufferInitValue(0) : EMPTY_VALUE);
     }
//--- Узнаём какому времени данного класса принадлежит бар текущего таймфрейма графика, соответствующий индексу цикла
   datetime array[];
   if(::CopyTime(symbol_to,timeframe_to,index,1,array)!=1)
      return this.BufferInitValue(buffer_num);
//--- По времени бара текущего таймфрейма графика находим соответствующий индекс бара периода графика данного класса
   int bar=iBarShift(this.m_symbol,this.m_timeframe,array[0]);
//--- Если бар не найден - возвращаем "пустое" значение, установленное для буфера
   if(bar==WRONG_VALUE)
      return this.BufferInitValue(buffer_num);
//--- Возвращаем значение из буфера объекта-индикатора по найденному индексу
   return this.GetData(buffer_num,array_num,bar);
  }
//+------------------------------------------------------------------+
//| Возвращает состояние линии индикатора как есть                   |
//+------------------------------------------------------------------+
ENUM_LINE_STATE CIndMSTF::BufferLineState(const uint buffer_num,const uint array_num,const int index) const
  {
//--- Получаем значения линии индикатора со смещением (0,1,2) относительно переданного индекса
   const double value0=this.GetData(buffer_num,array_num,index);
   const double value1=this.GetData(buffer_num,array_num,index+1);
   const double value2=this.GetData(buffer_num,array_num,index+2);
//--- Если хоть одно из значений получить не удалось - возвращаем неопределённое значение 
   if(value0==EMPTY_VALUE || value1==EMPTY_VALUE || value2==EMPTY_VALUE)
      return LINE_STATE_NONE;
//--- Разворот линии вверх (value2>value1 && value0>value1)
   if(::NormalizeDouble(value2-value1,this.m_digits)>0 && ::NormalizeDouble(value0-value1,this.m_digits)>0)
      return LINE_STATE_TURN_UP;
//--- Направление линии вверх (value2<=value1 && value0>value1)
   else if(::NormalizeDouble(value2-value1,this.m_digits)<=0 && ::NormalizeDouble(value0-value1,this.m_digits)>0)
      return LINE_STATE_UP;
//--- Остановка направления линии вверх (value2<=value1 && value0==value1)
   else if(::NormalizeDouble(value2-value1,this.m_digits)<=0 && ::NormalizeDouble(value0-value1,this.m_digits)==0)
      return LINE_STATE_STOP_UP;
//--- Разворот линии вниз (value2<value1 && value0<value1)
   if(::NormalizeDouble(value2-value1,this.m_digits)<0 && ::NormalizeDouble(value0-value1,this.m_digits)<0)
      return LINE_STATE_TURN_DOWN;
//--- Направление линии вниз (value2>=value1 && value0<value1)
   else if(::NormalizeDouble(value2-value1,this.m_digits)>=0 && ::NormalizeDouble(value0-value1,this.m_digits)<0)
      return LINE_STATE_DOWN;
//--- Остановка направления линии вниз (value2>=value1 && value0==value1)
   else if(::NormalizeDouble(value2-value1,this.m_digits)>=0 && ::NormalizeDouble(value0-value1,this.m_digits)==0)
      return LINE_STATE_STOP_DOWN;
//--- Неопределённое состояние
   return LINE_STATE_NONE;
  }
//+------------------------------------------------------------------+
//| Возвращает состояние линии индикатора с учётом символа/периода   |
//+------------------------------------------------------------------+
ENUM_LINE_STATE CIndMSTF::BufferLineState(const string symbol_from,const ENUM_TIMEFRAMES timeframes_from,const uint buffer_num,const uint array_num,const int index) const
  {
//--- Определяем переданные в метод символ/период графика
   string symbol=(symbol_from=="" || symbol_from==NULL ? ::Symbol() : symbol_from);
   ENUM_TIMEFRAMES timeframe=(timeframes_from==PERIOD_CURRENT ? ::Period() : timeframes_from);
//--- Если получаем данные от символа/периода, равные текущему графику - возвращаем состояние из буфера "как есть"
   if(symbol==::Symbol() && symbol==this.m_symbol && timeframe==::Period() && timeframe==this.m_timeframe)
      return this.BufferLineState(buffer_num,array_num,index);
//--- Объявляем переменные для поиска нужных баров на текущем графике
   datetime array[1];
   int      bar0=WRONG_VALUE;
   int      bar1=WRONG_VALUE;
   int      bar2=WRONG_VALUE;

//--- Получаем время первого бара на графике
   ::ResetLastError();
   if(::CopyTime(symbol,timeframe,index,1,array)!=1)
     {
      ::PrintFormat("%s: CopyTime for %s/%s, bar %ld failed. Error %lu",__FUNCTION__,symbol,::StringSubstr(::EnumToString(timeframe),7),index,::GetLastError());
      return LINE_STATE_NONE;
     }
//--- Получаем индекс первого бара в буфере объекта-индикатора по времени открытия бара на графике
   bar0=::iBarShift(this.m_symbol,this.m_timeframe,array[0]);
   if(bar0==WRONG_VALUE)
     {
      ::PrintFormat("%s: iBarShift for %s/%s, time %s failed. Error %lu",__FUNCTION__,this.m_symbol,this.TimeframeDescription(),string(array[0]),::GetLastError());
      return LINE_STATE_NONE;
     }
//--- Получаем время второго бара на графике
   ::ResetLastError();
   if(::CopyTime(symbol,timeframe,index+1,1,array)!=1)
     {
      ::PrintFormat("%s: CopyTime for %s/%s, bar %ld failed. Error %lu",__FUNCTION__,symbol,::StringSubstr(::EnumToString(timeframe),7),index+1,::GetLastError());
      return LINE_STATE_NONE;
     }
//--- Получаем индекс второго бара в буфере объекта-индикатора по времени открытия бара на графике
   bar1=::iBarShift(this.m_symbol,this.m_timeframe,array[0]);
   if(bar1==WRONG_VALUE)
     {
      ::PrintFormat("%s: iBarShift for %s/%s, time %s failed. Error %lu",__FUNCTION__,this.m_symbol,this.TimeframeDescription(),string(array[0]),::GetLastError());
      return LINE_STATE_NONE;
     }
//--- Получаем время третьего бара на графике
   ::ResetLastError();
   if(::CopyTime(symbol,timeframe,index+2,1,array)!=1)
     {
      ::PrintFormat("%s: CopyTime for %s/%s, bar %ld failed. Error %lu",__FUNCTION__,symbol,::StringSubstr(::EnumToString(timeframe),7),index+2,::GetLastError());
      return LINE_STATE_NONE;
     }
//--- Получаем индекс третьего бара в буфере объекта-индикатора по времени открытия бара на графике
   bar2=::iBarShift(this.m_symbol,this.m_timeframe,array[0]);
   if(bar2==WRONG_VALUE)
     {
      ::PrintFormat("%s: iBarShift for %s/%s, time %s failed. Error %lu",__FUNCTION__,this.m_symbol,this.TimeframeDescription(),string(array[0]),::GetLastError());
      return LINE_STATE_NONE;
     }
     
//--- Получаем значения линии индикатора со смещением (0,1,2) относительно переданного индекса
   const double value0=this.GetData(buffer_num,array_num,bar0);
   const double value1=this.GetData(buffer_num,array_num,bar1);
   const double value2=this.GetData(buffer_num,array_num,bar2);
//--- Если хоть одно из значений получить не удалось - возвращаем неопределённое значение 
   if(value0==EMPTY_VALUE || value1==EMPTY_VALUE || value2==EMPTY_VALUE)
      return LINE_STATE_NONE;
//--- Разворот линии вверх (value2>value1 && value0>value1)
   if(::NormalizeDouble(value2-value1,this.m_digits)>0 && ::NormalizeDouble(value0-value1,this.m_digits)>0)
      return LINE_STATE_TURN_UP;
//--- Направление линии вверх (value2<=value1 && value0>value1)
   else if(::NormalizeDouble(value2-value1,this.m_digits)<=0 && ::NormalizeDouble(value0-value1,this.m_digits)>0)
      return LINE_STATE_UP;
//--- Остановка направления линии вверх (value2<=value1 && value0==value1)
   else if(::NormalizeDouble(value2-value1,this.m_digits)<=0 && ::NormalizeDouble(value0-value1,this.m_digits)==0)
      return LINE_STATE_STOP_UP;
//--- Разворот линии вниз (value2<value1 && value0<value1)
   if(::NormalizeDouble(value2-value1,this.m_digits)<0 && ::NormalizeDouble(value0-value1,this.m_digits)<0)
      return LINE_STATE_TURN_DOWN;
//--- Направление линии вниз (value2>=value1 && value0<value1)
   else if(::NormalizeDouble(value2-value1,this.m_digits)>=0 && ::NormalizeDouble(value0-value1,this.m_digits)<0)
      return LINE_STATE_DOWN;
//--- Остановка направления линии вниз (value2>=value1 && value0==value1)
   else if(::NormalizeDouble(value2-value1,this.m_digits)>=0 && ::NormalizeDouble(value0-value1,this.m_digits)==0)
      return LINE_STATE_STOP_DOWN;
//--- Неопределённое состояние
   return LINE_STATE_NONE;
  }
//+------------------------------------------------------------------+
//| Возвращает состояние линии относительно указанного уровня        |
//+------------------------------------------------------------------+
ENUM_LINE_STATE CIndMSTF::BufferLineStateRelative(const int buffer_num,const uint array_num,const int index,const double level0,const double level1=EMPTY_VALUE)
  {
//--- Получаем значения линии индикатора со смещением (0,1) относительно переданного индекса
   const double value0=this.GetData(buffer_num,array_num,index);
   const double value1=this.GetData(buffer_num,array_num,index+1);
//--- Если хоть одно из значений получить не удалось - возвращаем неопределённое значение 
   if(value0==EMPTY_VALUE || value1==EMPTY_VALUE)
      return LINE_STATE_NONE;
//--- Определяем второй сравниваемый уровень
   double level=(level1==EMPTY_VALUE ? level0 : level1);
//--- Линия находится под уровнем (value1<level && value0<level0)
   if(::NormalizeDouble(value1-level,this.m_digits)<0 && ::NormalizeDouble(value0-level0,this.m_digits)<0)
      return LINE_STATE_BELOW;
//--- Линия находится над уровнем (value1>level && value0>level0)
   if(::NormalizeDouble(value1-level,this.m_digits)>0 && ::NormalizeDouble(value0-level0,this.m_digits)>0)
      return LINE_STATE_ABOVE;
//--- Линия пересекла уровень снизу-вверх (value1<=level && value0>level0)
   if(::NormalizeDouble(value1-level,this.m_digits)<=0 && ::NormalizeDouble(value0-level0,this.m_digits)>0)
      return LINE_STATE_CROSS_UP;
//--- Линия пересекла уровень сверху-вниз (value1>=level && value0<level0)
   if(::NormalizeDouble(value1-level,this.m_digits)>=0 && ::NormalizeDouble(value0-level0,this.m_digits)<0)
      return LINE_STATE_CROSS_DOWN;
//--- Линия коснулась уровня снизу (value1<level0 && value0==level0)
   if(::NormalizeDouble(value1-level,this.m_digits)<0 && ::NormalizeDouble(value0-level0,this.m_digits)==0)
      return LINE_STATE_TOUCH_BELOW;
//--- Линия коснулась уровня сверху (value1>level0 && value0==level0)
   if(::NormalizeDouble(value1-level,this.m_digits)>0 && ::NormalizeDouble(value0-level0,this.m_digits)==0)
      return LINE_STATE_TOUCH_BELOW;
//--- Линия равна значению уровня (value1==level0 && value0==level0)
   if(::NormalizeDouble(value1-level,this.m_digits)==0 && ::NormalizeDouble(value0-level0,this.m_digits)==0)
      return LINE_STATE_EQUALS;
//--- Неопределённое состояние
   return LINE_STATE_NONE;
  }
//+------------------------------------------------------------------+
//| Возвращает состояние линии относительно указанного уровня        |
//| на указанном символе/периоде графика                             |
//+------------------------------------------------------------------+
ENUM_LINE_STATE CIndMSTF::BufferLineStateRelative(const string symbol_from,const ENUM_TIMEFRAMES timeframes_from,const int buffer_num,const uint array_num,const int index,const double level0,const double level1=EMPTY_VALUE)
  {
//--- Определяем переданные в метод символ/период графика
   string symbol=(symbol_from=="" || symbol_from==NULL ? ::Symbol() : symbol_from);
   ENUM_TIMEFRAMES timeframe=(timeframes_from==PERIOD_CURRENT ? ::Period() : timeframes_from);
//--- Получаем значения линии индикатора со смещением (0,1) относительно переданного индекса
   const double value0=this.GetDataTo(symbol,timeframe,buffer_num,array_num,index);
   const double value1=this.GetDataTo(symbol,timeframe,buffer_num,array_num,index+1);
//--- Если хоть одно из значений получить не удалось - возвращаем неопределённое значение 
   if(value0==EMPTY_VALUE || value1==EMPTY_VALUE)
      return LINE_STATE_NONE;
//--- Определяем второй сравниваемый уровень
   double level=(level1==EMPTY_VALUE ? level0 : level1);
//--- Линия находится под уровнем (value1<level && value0<level0)
   if(::NormalizeDouble(value1-level,this.m_digits)<0 && ::NormalizeDouble(value0-level0,this.m_digits)<0)
      return LINE_STATE_BELOW;
//--- Линия находится над уровнем (value1>level && value0>level0)
   if(::NormalizeDouble(value1-level,this.m_digits)>0 && ::NormalizeDouble(value0-level0,this.m_digits)>0)
      return LINE_STATE_ABOVE;
//--- Линия пересекла уровень снизу-вверх (value1<=level && value0>level0)
   if(::NormalizeDouble(value1-level,this.m_digits)<=0 && ::NormalizeDouble(value0-level0,this.m_digits)>0)
      return LINE_STATE_CROSS_UP;
//--- Линия пересекла уровень сверху-вниз (value1>=level && value0<level0)
   if(::NormalizeDouble(value1-level,this.m_digits)>=0 && ::NormalizeDouble(value0-level0,this.m_digits)<0)
      return LINE_STATE_CROSS_DOWN;
//--- Линия коснулась уровня снизу (value1<level0 && value0==level0)
   if(::NormalizeDouble(value1-level,this.m_digits)<0 && ::NormalizeDouble(value0-level0,this.m_digits)==0)
      return LINE_STATE_TOUCH_BELOW;
//--- Линия коснулась уровня сверху (value1>level0 && value0==level0)
   if(::NormalizeDouble(value1-level,this.m_digits)>0 && ::NormalizeDouble(value0-level0,this.m_digits)==0)
      return LINE_STATE_TOUCH_BELOW;
//--- Линия равна значению уровня (value1==level0 && value0==level0)
   if(::NormalizeDouble(value1-level,this.m_digits)==0 && ::NormalizeDouble(value0-level0,this.m_digits)==0)
      return LINE_STATE_EQUALS;
//--- Неопределённое состояние
   return LINE_STATE_NONE;
  }

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

//+------------------------------------------------------------------+
//| Возвращает данные указанного буфера индексов цвета как есть      |
//+------------------------------------------------------------------+
double CIndMSTF::GetColorData(const uint buffer_num,const int index) const
  {
//--- Проверяем корректность переданного в метод номера буфера и, если номер не верный, сообщаем об этом в журнал и возвращаем 0
   if(buffer_num>this.BuffersTotal()-1)
     {
      string buff_limit=(this.BuffersTotal()==1 ? "0" : "0 - "+string(this.BuffersTotal()-1));
      ::PrintFormat("%s: Invalid buffer number passed (%lu). Value must be %s",__FUNCTION__,buffer_num,buff_limit);
      return 0;
     }
//--- Если это не цветной буфер - сообщаем об этом в журнал и возвращаем 0
   if(!this.IsColoredBuffer(buffer_num))
     {
      ::PrintFormat("%s: Buffer %lu is not a color buffer",__FUNCTION__,buffer_num);
      return 0;
     }
//--- Если указан не правильный индекс - возвращаем "пустое" значение указанного буфера цвета
   if(index<0 || index>(int)this.DataTotal(buffer_num,4)-1)
      return this.BufferInitColorIndex(buffer_num);
//--- Рассчитываем реальный индекс в массиве-буфере цвета и возвращаем значение по этому индексу
   int n=int(this.DataTotal(buffer_num,4)-1-index);
   return this.m_buffers[buffer_num].color_indexes[n];
  }
//+------------------------------------------------------------------+
//| Возвращает данные указанного буфера индексов цвета               |
//| на указанный символ/таймфрейм                                    |
//+------------------------------------------------------------------+
double CIndMSTF::GetColorDataTo(const string symbol_to,const ENUM_TIMEFRAMES timeframe_to,const uint buffer_num,const int index) const
  {
//--- Если указан текущий символ/период графика
   if(timeframe_to==::Period() && this.m_timeframe==::Period() && symbol_to==::Symbol() && this.m_symbol==::Symbol())
      return this.GetColorData(buffer_num,index);
//--- Проверяем корректность переданного в метод номера буфера и, если номер не верный, сообщаем об этом в журнал и возвращаем 0
   if(buffer_num>this.BuffersTotal()-1)
     {
      string buff_limit=(this.BuffersTotal()==1 ? "0" : "0 - "+string(this.BuffersTotal()-1));
      ::PrintFormat("%s: Invalid buffer number passed (%lu). Value must be %s",__FUNCTION__,buffer_num,buff_limit);
      return 0;
     }
//--- Если это не цветной буфер - сообщаем об этом в журнал и возвращаем 0
   if(!this.IsColoredBuffer(buffer_num))
     {
      ::PrintFormat("%s: Buffer %lu is not a color buffer",__FUNCTION__,buffer_num);
      return 0;
     }
//--- Узнаём какому времени данного класса принадлежит бар текущего таймфрейма графика, соответствующий индексу цикла
   datetime array[];
   if(::CopyTime(symbol_to,timeframe_to,index,1,array)!=1)
      return this.BufferInitColorIndex(buffer_num);
//--- По времени бара текущего таймфрейма графика находим соответствующий индекс бара периода графика данного класса
   int bar=iBarShift(this.m_symbol,this.m_timeframe,array[0]);
//--- Если бар не найден - возвращаем "пустое" значение, установленное для буфера цвета
   if(bar==WRONG_VALUE)
      return this.BufferInitColorIndex(buffer_num);
//--- Возвращаем значение из буфера цвета объекта-индикатора по найденному индексу
   return this.GetColorData(buffer_num,bar);
//--- Рассчитываем реальный индекс в массиве-буфере цвета и возвращаем значение по этому индексу
   int n=int(this.DataTotal(buffer_num,4)-1-bar);
   return this.m_buffers[buffer_num].color_indexes[n];
  }

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

//+------------------------------------------------------------------+
//| Отключает индексацию массивов буферов как у таймсерии            |
//+------------------------------------------------------------------+
void CIndMSTF::SetAsSeriesOff(void)
  {
//--- В цикле по всем буферам индикатора отключаем флаг серийности массивов
   for(int i=0;i<(int)this.BuffersTotal();i++)
     {
      ::ArraySetAsSeries(this.m_buffers[i].array0,false);
      ::ArraySetAsSeries(this.m_buffers[i].array1,false);
      ::ArraySetAsSeries(this.m_buffers[i].array2,false);
      ::ArraySetAsSeries(this.m_buffers[i].array3,false);
      ::ArraySetAsSeries(this.m_buffers[i].color_indexes,false);
     }
  }
//+------------------------------------------------------------------+
//| Возвращает флаг серийности указанного буфера                     |
//+------------------------------------------------------------------+
bool CIndMSTF::IsSeries(const uint buffer_num,const uint array_num) const
  {
//--- Проверяем корректность переданного в метод номера буфера и, если номер не верный, сообщаем об этом в журнал и возвращаем false
   if(buffer_num>this.BuffersTotal()-1)
     {
      string buff_limit=(this.BuffersTotal()==1 ? "0" : "0 - "+string(this.BuffersTotal()-1));
      ::PrintFormat("%s: Invalid buffer number passed (%lu). Value must be %s",__FUNCTION__,buffer_num,buff_limit);
      return false;
     }
//--- Возвращаем флаг серийности массива указанного буфера
   switch(array_num)
     {
      case 0 : return (bool)::ArrayGetAsSeries(this.m_buffers[buffer_num].array0);
      case 1 : return (bool)::ArrayGetAsSeries(this.m_buffers[buffer_num].array1);
      case 2 : return (bool)::ArrayGetAsSeries(this.m_buffers[buffer_num].array2);
      case 3 : return (bool)::ArrayGetAsSeries(this.m_buffers[buffer_num].array3);
      case 4 : return (bool)::ArrayGetAsSeries(this.m_buffers[buffer_num].color_indexes);
      default: break;
     }
   return false;
  }
//+------------------------------------------------------------------+
//| Возвращает количество данных указанного буфера                   |
//+------------------------------------------------------------------+
uint CIndMSTF::DataTotal(const uint buffer_num,const uint array_num) const
  {
//--- Проверяем корректность переданного в метод номера буфера и, если номер не верный, сообщаем об этом в журнал и возвращаем ноль
   if(buffer_num>this.BuffersTotal()-1)
     {
      string buff_limit=(this.BuffersTotal()==1 ? "0" : "0 - "+string(this.BuffersTotal()-1));
      ::PrintFormat("%s: Invalid buffer number passed (%lu). Value must be %s",__FUNCTION__,buffer_num,buff_limit);
      return 0;
     }
//--- Возвращаем размер массива указанного буфера
   switch(array_num)
     {
      case 0 : return this.m_buffers[buffer_num].array0.Size();
      case 1 : return this.m_buffers[buffer_num].array1.Size();
      case 2 : return this.m_buffers[buffer_num].array2.Size();
      case 3 : return this.m_buffers[buffer_num].array3.Size();
      case 4 : return this.m_buffers[buffer_num].color_indexes.Size();
      default: break;
     }
   return 0;
  }

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

//+------------------------------------------------------------------+
//| Возвращает количество цветов, установленных буферу               |
//+------------------------------------------------------------------+
uint CIndMSTF::ColorsTotal(const uint buffer_num) const
  {
//--- Проверяем корректность переданного в метод номера буфера и, если номер не верный, сообщаем об этом в журнал и возвращаем ноль
   if(buffer_num>this.BuffersTotal()-1)
     {
      string buff_limit=(this.BuffersTotal()==1 ? "0" : "0 - "+string(this.BuffersTotal()-1));
      ::PrintFormat("%s: Invalid buffer number passed (%lu). Value must be %s",__FUNCTION__,buffer_num,buff_limit);
      return 0;
     }
//--- Возвращаем размер массива цветов указанного буфера
   return this.m_buffers[buffer_num].clrs.Size();
  }


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

//+------------------------------------------------------------------+
//| Класс индикатора Accelerator Oscillator                          |
//+------------------------------------------------------------------+
class CIndAC : public CIndMSTF
  {
public:
//--- Конструктор
   CIndAC(const string symbol,const ENUM_TIMEFRAMES timeframe) : CIndMSTF(IND_AC,1,symbol,timeframe)
     {
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=(current ? "" : StringFormat("(%s)",symbol_period));
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок и категорию
      this.SetParameters(param);
      this.SetName("AC");
      this.SetDescription("Accelerator Oscillator");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_WILLIAMS;
      this.m_digits=::Digits()+2;
      //--- Записываем описание буфера линии
      this.SetBufferDescription(0,this.m_title);
      //--- Установим стиль рисования для буфера 0 и номер буфера данных расчётной части
      this.SetBufferDrawType(0,DRAW_COLOR_HISTOGRAM,0);
      //--- Зададим для цветного буфера 0 два цвета по умолчанию
      this.SetBufferColorToIndex(0,0,clrGreen);
      this.SetBufferColorToIndex(0,1,clrRed);
      //--- Установим инициализирующий индекс цвета по умолчанию
      this.SetBufferInitColorIndex(0,0);
     }
  };

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

Листинг классов остальных стандартных индикаторов (некоторые классы индикаторов ещё не доработаны):

//+------------------------------------------------------------------+
//| Класс индикатора Accumulation/Distribution                       |
//+------------------------------------------------------------------+
class CIndAD : public CIndMSTF
  {
public:
//--- Конструктор
   CIndAD(const string symbol,const ENUM_TIMEFRAMES timeframe,
          const ENUM_APPLIED_VOLUME applied_volume // используемый объем
         ) : CIndMSTF(IND_AD,1,symbol,timeframe)
     {
      //--- Устанавливаем размер массива параметров и заполняем его
      ::ResetLastError();
      if(::ArrayResize(this.m_param,1)==1)
        {
         ::ZeroMemory(this.m_param);
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=applied_volume;
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=(current ? "" : StringFormat("(%s)",symbol_period));
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок, категорию и Digits индикатора
      this.SetParameters(param);
      this.SetName("A/D");
      this.SetDescription("Accumulation/Distribution");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_VOLUME;
      this.m_digits=0;
      //--- Записываем описание буфера линии
      this.SetBufferDescription(0,this.m_title);
      //--- Установим стиль рисования для буфера 0 и номер буфера данных расчётной части
      this.SetBufferDrawType(0,DRAW_LINE,0);
      //--- Зададим для буфера 0 цвет по умолчанию
      this.SetBufferColorToIndex(0,0,clrLightSeaGreen);
     }
  };
//+------------------------------------------------------------------+
//| Класс индикатора Average Directional Movement Index              |
//+------------------------------------------------------------------+
class CIndADX : public CIndMSTF
  {
public:
//--- Конструктор
   CIndADX(const string symbol,const ENUM_TIMEFRAMES timeframe,
           const int adx_period                    // период усреднения
          ) : CIndMSTF(IND_ADX,3,symbol,timeframe)
     {
      // Номера буферов: 0 - MAIN_LINE, 1 - PLUSDI_LINE, 2 - MINUSDI_LINE
      //--- Устанавливаем размер массива параметров и заполняем его
      ::ResetLastError();
      if(::ArrayResize(this.m_param,1)==1)
        {
         ::ZeroMemory(this.m_param);
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(adx_period<1 ? 14 : adx_period);
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=StringFormat("(%s%s%lu)",symbol_period,(current ? "" : ":"),adx_period);
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок, категорию и Digits индикатора
      this.SetParameters(param);
      this.SetName("ADX");
      this.SetDescription("Average Directional Movement Index");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_TREND;
      this.m_digits=2;
      //--- записываем описания буферов линий MAIN_LINE, PLUSDI_LINE и MINUSDI_LINE
      this.SetBufferDescription(MAIN_LINE,this.m_title);
      this.SetBufferDescription(PLUSDI_LINE,"+DI");
      this.SetBufferDescription(MINUSDI_LINE,"-DI");
      
      //--- Установим стиль рисования для буферов 0, 1, 2 и номера буферов данных расчётной части
      this.SetBufferDrawType(0,DRAW_LINE,MAIN_LINE);
      this.SetBufferDrawType(1,DRAW_LINE,PLUSDI_LINE);
      this.SetBufferDrawType(2,DRAW_LINE,MINUSDI_LINE);
      
      //--- Зададим для буферов 0, 1 и 2 цвета по умолчанию
      this.SetBufferColorToIndex(MAIN_LINE,0,clrLightSeaGreen);
      this.SetBufferColorToIndex(PLUSDI_LINE,0,clrYellowGreen);
      this.SetBufferColorToIndex(MINUSDI_LINE,0,clrWheat);
     }
  };
//+------------------------------------------------------------------+
//| Класс индикатора Average Directional Movement Index Wilder       |
//+------------------------------------------------------------------+
class CIndADXW : public CIndMSTF
  {
public:
//--- Конструктор
   CIndADXW(const string symbol,const ENUM_TIMEFRAMES timeframe,
            const int adx_period                      // период усреднения
           ) : CIndMSTF(IND_ADXW,3,symbol,timeframe)
     {
      // Номера буферов: 0 - MAIN_LINE, 1 - PLUSDI_LINE, 2 - MINUSDI_LINE
      //--- Устанавливаем размер массива параметров и заполняем его
      ::ResetLastError();
      if(::ArrayResize(this.m_param,1)==1)
        {
         ::ZeroMemory(this.m_param);
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(adx_period<1 ? 14 : adx_period);
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=StringFormat("(%s%s%lu)",symbol_period,(current ? "" : ":"),adx_period);
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок, категорию и Digits индикатора
      this.SetParameters(param);
      this.SetName("ADX Wilder");
      this.SetDescription("Average Directional Movement Index Wilder");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_TREND;
      this.m_digits=2;
      //--- записываем описания буферов линий MAIN_LINE, PLUSDI_LINE и MINUSDI_LINE
      this.SetBufferDescription(MAIN_LINE,this.m_title);
      this.SetBufferDescription(PLUSDI_LINE,"+DI");
      this.SetBufferDescription(MINUSDI_LINE,"-DI");
      
      //--- Установим стиль рисования для буферов 0, 1, 2 и номера буферов данных расчётной части
      this.SetBufferDrawType(0,DRAW_LINE,MAIN_LINE);
      this.SetBufferDrawType(1,DRAW_LINE,PLUSDI_LINE);
      this.SetBufferDrawType(2,DRAW_LINE,MINUSDI_LINE);
      
      //--- Зададим для буферов 0, 1 и 2 цвета по умолчанию
      this.SetBufferColorToIndex(MAIN_LINE,0,clrLightSeaGreen);
      this.SetBufferColorToIndex(PLUSDI_LINE,0,clrYellowGreen);
      this.SetBufferColorToIndex(MINUSDI_LINE,0,clrWheat);
     }
  };
//+------------------------------------------------------------------+
//| Класс индикатора Alligator                                       |
//+------------------------------------------------------------------+
class CIndAlligator : public CIndMSTF
  {
public:
//--- Конструктор
   CIndAlligator(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  // тип цены или handle
                ) : CIndMSTF(IND_ALLIGATOR,3,symbol,timeframe)
     {
      // Номера буферов: 0 - GATORJAW_LINE, 1 - GATORTEETH_LINE, 2 - GATORLIPS_LINE
      //--- Устанавливаем размер массива параметров и заполняем его
      ::ResetLastError();
      if(::ArrayResize(this.m_param,8)==8)
        {
         ::ZeroMemory(this.m_param);
         //--- период для расчета челюстей
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(jaw_period<1 ? 13 : jaw_period);
         //--- смещение челюстей по горизонтали
         this.m_param[1].type=TYPE_INT;
         this.m_param[1].integer_value=jaw_shift;
         //--- период для расчета зубов
         this.m_param[2].type=TYPE_UINT;
         this.m_param[2].integer_value=(teeth_period<1 ? 8 : teeth_period);
         //--- смещение зубов по горизонтали
         this.m_param[3].type=TYPE_INT;
         this.m_param[3].integer_value=teeth_shift;
         //--- период для расчета губ
         this.m_param[4].type=TYPE_UINT;
         this.m_param[4].integer_value=(lips_period<1 ? 5 : lips_period);
         //--- смещение губ по горизонтали
         this.m_param[5].type=TYPE_INT;
         this.m_param[5].integer_value=lips_shift;
         //--- тип сглаживания
         this.m_param[6].type=TYPE_UINT;
         this.m_param[6].integer_value=ma_method;
         //--- тип цены или handle
         this.m_param[7].type=TYPE_UINT;
         this.m_param[7].integer_value=applied_price;
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=StringFormat("(%s%s%lu,%lu,%lu)",symbol_period,(current ? "" : ":"),jaw_period,teeth_period,lips_period);
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок и категорию
      this.SetParameters(param);
      this.SetName("Alligator");
      this.SetDescription("Alligator");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_WILLIAMS;
      //--- Записываем описания буферов линий GATORJAW_LINE, GATORTEETH_LINE и GATORLIPS_LINE
      this.SetBufferDescription(GATORJAW_LINE,::StringFormat("Jaws(%s%lu)", (current ? "" : symbol_period+":"),jaw_period));
      this.SetBufferDescription(GATORTEETH_LINE,::StringFormat("Teeth(%s%lu)",(current ? "" : symbol_period+":"),teeth_period));
      this.SetBufferDescription(GATORLIPS_LINE,::StringFormat("Lips(%s%lu)", (current ? "" : symbol_period+":"),lips_period));
      //--- Записываем смещения в буферы GATORJAW_LINE, GATORTEETH_LINE и GATORLIPS_LINE
      this.SetBufferShift(GATORJAW_LINE,jaw_shift);
      this.SetBufferShift(GATORTEETH_LINE,teeth_shift);
      this.SetBufferShift(GATORLIPS_LINE,lips_shift);
      
      //--- Установим стиль рисования для буферов 0, 1, 2 и номера буферов данных расчётной части
      this.SetBufferDrawType(0,DRAW_LINE,GATORJAW_LINE);
      this.SetBufferDrawType(1,DRAW_LINE,GATORTEETH_LINE);
      this.SetBufferDrawType(2,DRAW_LINE,GATORLIPS_LINE);
      
      //--- Зададим для буферов 0, 1 и 2 цвета по умолчанию
      this.SetBufferColorToIndex(GATORJAW_LINE,0,clrBlue);
      this.SetBufferColorToIndex(GATORTEETH_LINE,0,clrRed);
      this.SetBufferColorToIndex(GATORLIPS_LINE,0,clrLime);
     }
  };
//+------------------------------------------------------------------+
//| Класс индикатора Adaptive Moving Average                         |
//+------------------------------------------------------------------+
class CIndAMA : public CIndMSTF
  {
public:
//--- Конструктор
   CIndAMA(const string symbol,const ENUM_TIMEFRAMES timeframe,
           const int ama_period,                   // период AMA
           const int fast_ma_period,               // период быстрой скользящей
           const int slow_ma_period,               // период медленной скользящей
           const int ama_shift,                    // смещение индикатора по горизонтали
           const ENUM_APPLIED_PRICE applied_price  // тип цены или handle
          ) : CIndMSTF(IND_AMA,1,symbol,timeframe)
     {
      //--- Устанавливаем размер массива параметров и заполняем его
      ::ResetLastError();
      if(::ArrayResize(this.m_param,5)==5)
        {
         ::ZeroMemory(this.m_param);
         //--- период AMA
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(ama_period<1 ? 9 : ama_period);
         //--- период быстрой скользящей
         this.m_param[1].type=TYPE_UINT;
         this.m_param[1].integer_value=(fast_ma_period<1 ? 2 : fast_ma_period);
         //--- период медленной скользящей
         this.m_param[2].type=TYPE_UINT;
         this.m_param[2].integer_value=(slow_ma_period<1 ? 30 : slow_ma_period);
         //--- смещение индикатора по горизонтали
         this.m_param[3].type=TYPE_INT;
         this.m_param[3].integer_value=ama_shift;
         //--- тип цены или handle
         this.m_param[4].type=TYPE_UINT;
         this.m_param[4].integer_value=applied_price;
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=StringFormat("(%s%s%lu,%lu,%lu)",symbol_period,(current ? "" : ":"),ama_period,fast_ma_period,slow_ma_period);
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок, категорию и Digits индикатора
      this.SetParameters(param);
      this.SetName("AMA");
      this.SetDescription("Adaptive Moving Average");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_TREND;
      this.m_digits=::Digits()+1;
      //--- Записываем описание буфера линии
      this.SetBufferDescription(0,this.m_title);
      //--- Записываем смещение в буфер 0
      this.SetBufferShift(0,ama_shift);
      //--- Установим стиль рисования для буфера 0 и номер буфера данных расчётной части
      this.SetBufferDrawType(0,DRAW_LINE,0);
      //--- Зададим для буфера 0 цвет по умолчанию
      this.SetBufferColorToIndex(0,0,clrRed);
     }
  };
//+------------------------------------------------------------------+
//| Класс индикатора Awesome Oscillator                              |
//+------------------------------------------------------------------+
class CIndAO : public CIndMSTF
  {
public:
//--- Конструктор
   CIndAO(const string symbol,const ENUM_TIMEFRAMES timeframe) : CIndMSTF(IND_AO,1,symbol,timeframe)
     {
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=(current ? "" : StringFormat("(%s)",symbol_period));
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок, категорию и Digits индикатора
      this.SetParameters(param);
      this.SetName("AO");
      this.SetDescription("Awesome Oscillator");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_WILLIAMS;
      this.m_digits=::Digits()+1;
      //--- Записываем описание буфера линии
      this.SetBufferDescription(0,this.m_title);
      
      //--- Установим стиль рисования для буфера 0 и номер буфера данных расчётной части
      this.SetBufferDrawType(0,DRAW_COLOR_HISTOGRAM,0);
      
      //--- Зададим для цветного буфера 0 два цвета по умолчанию
      this.SetBufferColorToIndex(0,0,clrGreen);
      this.SetBufferColorToIndex(0,1,clrRed);
      //--- Установим инициализирующий индекс цвета по умолчанию
      this.SetBufferInitColorIndex(0,0);
     }
  };
//+------------------------------------------------------------------+
//| Класс индикатора Average True Range                              |
//+------------------------------------------------------------------+
class CIndATR : public CIndMSTF
  {
public:
//--- Конструктор
   CIndATR(const string symbol,const ENUM_TIMEFRAMES timeframe,
           const int ma_period                     // период усреднения
          ) : CIndMSTF(IND_ATR,1,symbol,timeframe)
     {
      //--- Устанавливаем размер массива параметров и заполняем его
      ::ResetLastError();
      if(::ArrayResize(this.m_param,1)==1)
        {
         ::ZeroMemory(this.m_param);
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(ma_period<1 ? 14 : ma_period);
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=StringFormat("(%s%s%lu)",symbol_period,(current ? "" : ":"),ma_period);
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок и категорию
      this.SetParameters(param);
      this.SetName("ATR");
      this.SetDescription("Average True Range");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_OSCILLATOR;
      //--- Записываем описание буфера линии
      this.SetBufferDescription(0,this.m_title);
      
      //--- Установим стиль рисования для буфера 0 и номер буфера данных расчётной части
      this.SetBufferDrawType(0,DRAW_LINE,0);
      //--- Зададим для буфера 0 цвет по умолчанию
      this.SetBufferColorToIndex(0,0,clrLightSeaGreen);
     }
  };
//+------------------------------------------------------------------+
//| Класс индикатора Bears Power                                     |
//+------------------------------------------------------------------+
class CIndBears : public CIndMSTF
  {
public:
//--- Конструктор
   CIndBears(const string symbol,const ENUM_TIMEFRAMES timeframe,
             const int ma_period                      // период усреднения
            ) : CIndMSTF(IND_BEARS,1,symbol,timeframe)
     {
      //--- Устанавливаем размер массива параметров и заполняем его
      ::ResetLastError();
      if(::ArrayResize(this.m_param,1)==1)
        {
         ::ZeroMemory(this.m_param);
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(ma_period<1 ? 13 : ma_period);
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=StringFormat("(%s%s%lu)",symbol_period,(current ? "" : ":"),ma_period);
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок, категорию и Digits индикатора
      this.SetParameters(param);
      this.SetName("Bears");
      this.SetDescription("Bears Power");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_OSCILLATOR;
      this.m_digits=::Digits()+1;
      //--- Записываем описание буфера линии
      this.SetBufferDescription(0,this.m_title);
      
      //--- Установим стиль рисования для буфера 0 и номер буфера данных расчётной части
      this.SetBufferDrawType(0,DRAW_HISTOGRAM,0);
      //--- Зададим для буфера 0 цвет по умолчанию
      this.SetBufferColorToIndex(0,0,clrSilver);
     }
  };
//+------------------------------------------------------------------+
//| Класс индикатора Bulls Power                                     |
//+------------------------------------------------------------------+
class CIndBulls : public CIndMSTF
  {
public:
//--- Конструктор
   CIndBulls(const string symbol,const ENUM_TIMEFRAMES timeframe,
             const int ma_period                      // период усреднения
            ) : CIndMSTF(IND_BULLS,1,symbol,timeframe)
     {
      //--- Устанавливаем размер массива параметров и заполняем его
      ::ResetLastError();
      if(::ArrayResize(this.m_param,1)==1)
        {
         ::ZeroMemory(this.m_param);
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(ma_period<1 ? 13 : ma_period);
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=StringFormat("(%s%s%lu)",symbol_period,(current ? "" : ":"),ma_period);
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок, категорию и Digits индикатора
      this.SetParameters(param);
      this.SetName("Bulls");
      this.SetDescription("Bulls Power");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_OSCILLATOR;
      this.m_digits=::Digits()+1;
      //--- Записываем описание буфера линии
      this.SetBufferDescription(0,this.m_title);
      
      //--- Установим стиль рисования для буфера 0 и номер буфера данных расчётной части
      this.SetBufferDrawType(0,DRAW_HISTOGRAM,0);
      //--- Зададим для буфера 0 цвет по умолчанию
      this.SetBufferColorToIndex(0,0,clrSilver);
     }
  };
//+------------------------------------------------------------------+
//| Класс индикатора Bollinger Bands®                                |
//+------------------------------------------------------------------+
class CIndBands : public CIndMSTF
  {
public:
//--- Конструктор
   CIndBands(const string symbol,const ENUM_TIMEFRAMES timeframe,
             const int bands_period,                  // период для расчета средней линии
             const int bands_shift,                   // смещение индикатора по горизонтали
             const double deviation,                  // кол-во стандартных отклонений
             const ENUM_APPLIED_PRICE applied_price   // тип цены или handle
            ) : CIndMSTF(IND_BANDS,3,symbol,timeframe)
     {
      // Номера буферов: 0 - BASE_LINE, 1 - UPPER_BAND, 2 - LOWER_BAND
      //--- Устанавливаем размер массива параметров и заполняем его
      ::ResetLastError();
      if(::ArrayResize(this.m_param,4)==4)
        {
         ::ZeroMemory(this.m_param);
         //--- период для расчета средней линии
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(bands_period<1 ? 20 : bands_period);
         //--- смещение индикатора по горизонтали
         this.m_param[1].type=TYPE_INT;
         this.m_param[1].integer_value=bands_shift;
         //--- кол-во стандартных отклонений
         this.m_param[2].type=TYPE_DOUBLE;
         this.m_param[2].double_value=deviation;
         //--- тип цены или handle
         this.m_param[3].type=TYPE_UINT;
         this.m_param[3].integer_value=applied_price;
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=StringFormat("(%s%s%lu)",symbol_period,(current ? "" : ":"),bands_period);
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок, категорию и Digits индикатора
      this.SetParameters(param);
      this.SetName("Bands");
      this.SetDescription("Bollinger Bands");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_TREND;
      this.m_digits=::Digits()+1;
      //---Описание буферов линий BASE_LINE, UPPER_BAND и LOWER_BAND
      this.SetBufferDescription(BASE_LINE,this.m_title+" Middle");
      this.SetBufferDescription(UPPER_BAND,this.m_title+" Upper");
      this.SetBufferDescription(LOWER_BAND,this.m_title+" Lower");
      //--- Записываем смещения в буферы BASE_LINE, UPPER_BAND и LOWER_BAND
      this.SetBufferShift(BASE_LINE,bands_shift);
      this.SetBufferShift(UPPER_BAND,bands_shift);
      this.SetBufferShift(LOWER_BAND,bands_shift);
      
      //--- Установим стиль рисования для буферов 0, 1, 2 и номера буферов данных расчётной части
      this.SetBufferDrawType(0,DRAW_LINE,BASE_LINE);
      this.SetBufferDrawType(1,DRAW_LINE,UPPER_BAND);
      this.SetBufferDrawType(2,DRAW_LINE,LOWER_BAND);
      
      //--- Зададим для буферов 0, 1 и 2 цвета по умолчанию
      this.SetBufferColorToIndex(BASE_LINE,0,clrMediumSeaGreen);
      this.SetBufferColorToIndex(UPPER_BAND,0,clrMediumSeaGreen);
      this.SetBufferColorToIndex(LOWER_BAND,0,clrMediumSeaGreen);
     }
  };
//+------------------------------------------------------------------+
//| Класс индикатора Commodity Channel Index                         |
//+------------------------------------------------------------------+
class CIndCCI : public CIndMSTF
  {
public:
//--- Конструктор
   CIndCCI(const string symbol,const ENUM_TIMEFRAMES timeframe,
           const int ma_period,                    // период усреднения
           const ENUM_APPLIED_PRICE applied_price  // тип цены или handle
          ) : CIndMSTF(IND_CCI,1,symbol,timeframe)
     {
      //--- Устанавливаем размер массива параметров и заполняем его
      ::ResetLastError();
      if(::ArrayResize(this.m_param,2)==2)
        {
         ::ZeroMemory(this.m_param);
         //--- период усреднения
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(ma_period<1 ? 14 : ma_period<2 ? 2 : ma_period);
         //--- тип цены или handle
         this.m_param[1].type=TYPE_UINT;
         this.m_param[1].integer_value=applied_price;
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=StringFormat("(%s%s%lu)",symbol_period,(current ? "" : ":"),ma_period);
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок, категорию и Digits индикатора
      this.SetParameters(param);
      this.SetName("CCI");
      this.SetDescription("Commodity Channel Index");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_OSCILLATOR;
      this.m_digits=2;
      //--- Записываем описание буфера линии
      this.SetBufferDescription(0,this.m_title);
      
      //--- Установим стиль рисования для буфера 0 и номер буфера данных расчётной части
      this.SetBufferDrawType(0,DRAW_LINE,0);
      //--- Зададим для буфера 0 цвет по умолчанию
      this.SetBufferColorToIndex(0,0,clrLightSeaGreen);
     }
  };
//+------------------------------------------------------------------+
//| Класс индикатора Chaikin Oscillator                              |
//+------------------------------------------------------------------+
class CIndCHO : public CIndMSTF
  {
public:
//--- Конструктор
   CIndCHO(const string symbol,const ENUM_TIMEFRAMES timeframe,
           const int fast_ma_period,                  // быстрый период
           const int slow_ma_period,                  // медленный период
           const ENUM_MA_METHOD ma_method,            // тип сглаживания
           const ENUM_APPLIED_VOLUME applied_volume   // используемый объем
          ) : CIndMSTF(IND_CHAIKIN,1,symbol,timeframe)
     {
      //--- Устанавливаем размер массива параметров и заполняем его
      ::ResetLastError();
      if(::ArrayResize(this.m_param,4)==4)
        {
         ::ZeroMemory(this.m_param);
         //--- быстрый период
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(fast_ma_period<1 ? 3 : fast_ma_period);
         //--- медленный период
         this.m_param[1].type=TYPE_UINT;
         this.m_param[1].integer_value=(slow_ma_period<1 ? 10 : slow_ma_period);
         //--- тип сглаживания
         this.m_param[2].type=TYPE_UINT;
         this.m_param[2].integer_value=ma_method;
         //--- используемый объем
         this.m_param[3].type=TYPE_UINT;
         this.m_param[3].integer_value=applied_volume;
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=StringFormat("(%s%s%lu,%lu)",symbol_period,(current ? "" : ":"),slow_ma_period,fast_ma_period);
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок, категорию и Digits индикатора
      this.SetParameters(param);
      this.SetName("CHO");
      this.SetDescription("Chaikin Oscillator");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_OSCILLATOR;
      this.m_digits=0;
      //--- Записываем описание буфера линии
      this.SetBufferDescription(0,this.m_title);
      
      //--- Установим стиль рисования для буфера 0 и номер буфера данных расчётной части
      this.SetBufferDrawType(0,DRAW_LINE,0);
      //--- Зададим для буфера 0 цвет по умолчанию
      this.SetBufferColorToIndex(0,0,clrLightSeaGreen);
     }
  };
//+------------------------------------------------------------------+
//| Класс индикатора Double Exponential Moving Average               |
//+------------------------------------------------------------------+
class CIndDEMA : public CIndMSTF
  {
public:
//--- Конструктор
   CIndDEMA(const string symbol,const ENUM_TIMEFRAMES timeframe,
            const int ma_period,                      // период усреднения
            const int ma_shift,                       // смещение индикатора по горизонтали
            const ENUM_APPLIED_PRICE applied_price    // тип цены или handle
          ) : CIndMSTF(IND_DEMA,1,symbol,timeframe)
     {
      //--- Устанавливаем размер массива параметров и заполняем его
      ::ResetLastError();
      if(::ArrayResize(this.m_param,3)==3)
        {
         ::ZeroMemory(this.m_param);
         //--- период усреднения
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(ma_period<1 ? 14 : ma_period);
         //--- смещение индикатора по горизонтали
         this.m_param[1].type=TYPE_INT;
         this.m_param[1].integer_value=ma_shift;
         //--- тип цены или handle
         this.m_param[2].type=TYPE_UINT;
         this.m_param[2].integer_value=applied_price;
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=StringFormat("(%s%s%lu)",symbol_period,(current ? "" : ":"),ma_period);
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок, категорию и Digits индикатора
      this.SetParameters(param);
      this.SetName("DEMA");
      this.SetDescription("Double Exponential Moving Average");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_TREND;
      this.m_digits=::Digits()+1;
      //--- Записываем описание буфера линии
      this.SetBufferDescription(0,this.m_title);
      //--- Записываем смещение в буфер 0
      this.SetBufferShift(0,ma_shift);
      
      //--- Установим стиль рисования для буфера 0 и номер буфера данных расчётной части
      this.SetBufferDrawType(0,DRAW_LINE,0);
      //--- Зададим для буфера 0 цвет по умолчанию
      this.SetBufferColorToIndex(0,0,clrRed);
     }
  };
//+------------------------------------------------------------------+
//| Класс индикатора DeMarker                                        |
//+------------------------------------------------------------------+
class CIndDeM : public CIndMSTF
  {
public:
//--- Конструктор
   CIndDeM(const string symbol,const ENUM_TIMEFRAMES timeframe,
           const int ma_period                     // период усреднения
          ) : CIndMSTF(IND_DEMARKER,1,symbol,timeframe)
     {
      //--- Устанавливаем размер массива параметров и заполняем его
      ::ResetLastError();
      if(::ArrayResize(this.m_param,1)==1)
        {
         ::ZeroMemory(this.m_param);
         //--- период усреднения
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(ma_period<1 ? 14 : ma_period);
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=StringFormat("(%s%s%lu)",symbol_period,(current ? "" : ":"),ma_period);
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок, категорию и Digits индикатора
      this.SetParameters(param);
      this.SetName("DeM");
      this.SetDescription("DeMarker");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_OSCILLATOR;
      this.m_digits=3;
      //--- Записываем описание буфера линии
      this.SetBufferDescription(0,this.m_title);
      
      //--- Установим стиль рисования для буфера 0 и номер буфера данных расчётной части
      this.SetBufferDrawType(0,DRAW_LINE,0);
      //--- Зададим для буфера 0 цвет по умолчанию
      this.SetBufferColorToIndex(0,0,clrLightSeaGreen);
     }
  };
//+------------------------------------------------------------------+
//| Класс индикатора Envelopes                                       |
//+------------------------------------------------------------------+
class CIndEnvelopes : public CIndMSTF
  {
public:
//--- Конструктор
   CIndEnvelopes(const string symbol,const ENUM_TIMEFRAMES timeframe,
                 const int ma_period,                    // период для расчета средней линии
                 const int ma_shift,                     // смещение индикатора по горизонтали
                 const ENUM_MA_METHOD ma_method,         // тип сглаживания
                 const ENUM_APPLIED_PRICE applied_price, // тип цены или handle
                 const double deviation                  // отклонение границ от средней линии
          ) : CIndMSTF(IND_ENVELOPES,2,symbol,timeframe)
     {
      // Номера буферов: 0 - UPPER_LINE, 1 - LOWER_LINE
      //--- Устанавливаем размер массива параметров и заполняем его
      ::ResetLastError();
      if(::ArrayResize(this.m_param,5)==5)
        {
         ::ZeroMemory(this.m_param);
         //--- период для расчета средней линии
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(ma_period<1 ? 14 : ma_period);
         //--- смещение индикатора по горизонтали
         this.m_param[1].type=TYPE_INT;
         this.m_param[1].integer_value=ma_shift;
         //--- тип сглаживания
         this.m_param[2].type=TYPE_UINT;
         this.m_param[2].integer_value=ma_method;
         //--- тип цены или handle
         this.m_param[3].type=TYPE_UINT;
         this.m_param[3].integer_value=applied_price;
         //--- отклонение границ от средней линии
         this.m_param[4].type=TYPE_DOUBLE;
         this.m_param[4].double_value=deviation;
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=StringFormat("(%s%s%lu)",symbol_period,(current ? "" : ":"),ma_period);
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок, категорию и Digits индикатора
      this.SetParameters(param);
      this.SetName("Envelopes");
      this.SetDescription("Envelopes");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_TREND;
      this.m_digits=::Digits()+1;
      //--- Описание буферов линий UPPER_LINE и LOWER_LINE
      this.SetBufferDescription(UPPER_LINE,this.m_title+" Upper");
      this.SetBufferDescription(LOWER_LINE,this.m_title+" Lower");
      //--- Записываем смещение в буферы
      this.SetBufferShift(0,ma_shift);
      this.SetBufferShift(1,ma_shift);
      
      //--- Установим стиль рисования для буферов 0, 1 и номера буферов данных расчётной части
      this.SetBufferDrawType(0,DRAW_LINE,UPPER_LINE);
      this.SetBufferDrawType(1,DRAW_LINE,LOWER_LINE);
      
      //--- Зададим для буферов 0 и 1 цвета по умолчанию
      this.SetBufferColorToIndex(UPPER_LINE,0,clrBlue);
      this.SetBufferColorToIndex(LOWER_LINE,0,clrRed);
     }
  };
//+------------------------------------------------------------------+
//| Класс индикатора Force Index                                     |
//+------------------------------------------------------------------+
class CIndForce : public CIndMSTF
  {
public:
//--- Конструктор
   CIndForce(const string symbol,const ENUM_TIMEFRAMES timeframe,
             const int                 ma_period,     // период усреднения
             const ENUM_MA_METHOD      ma_method,     // тип сглаживания
             const ENUM_APPLIED_VOLUME applied_volume // тип объема для расчета
            ) : CIndMSTF(IND_FORCE,1,symbol,timeframe)
     {
      //--- Устанавливаем размер массива параметров и заполняем его
      ::ResetLastError();
      if(::ArrayResize(this.m_param,3)==3)
        {
         ::ZeroMemory(this.m_param);
         //--- период усреднения
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(ma_period<1 ? 13 : ma_period);
         //--- тип сглаживания
         this.m_param[1].type=TYPE_UINT;
         this.m_param[1].integer_value=ma_method;
         //--- тип объема для расчета
         this.m_param[2].type=TYPE_UINT;
         this.m_param[2].integer_value=applied_volume;
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=StringFormat("(%s%s%lu)",symbol_period,(current ? "" : ":"),ma_period);
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок, категорию и Digits индикатора
      this.SetParameters(param);
      this.SetName("Force");
      this.SetDescription("Force Index");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_OSCILLATOR;
      this.m_digits=::Digits()+1;
      //--- Записываем описание буфера линии
      this.SetBufferDescription(0,this.m_title);
      
      //--- Установим стиль рисования для буфера 0 и номер буфера данных расчётной части
      this.SetBufferDrawType(0,DRAW_LINE,0);
      //--- Зададим для буфера 0 цвет по умолчанию
      this.SetBufferColorToIndex(0,0,clrLightSeaGreen);
     }
  };
//+------------------------------------------------------------------+
//| Класс индикатора Fractals                                        |
//+------------------------------------------------------------------+
class CIndFractals : public CIndMSTF
  {
public:
//--- Конструктор
   CIndFractals(const string symbol,const ENUM_TIMEFRAMES timeframe) : CIndMSTF(IND_FRACTALS,2,symbol,timeframe)
     {
      // Номера буферов: 0 - UPPER_LINE, 1 - LOWER_LINE
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=(current ? "" : StringFormat("(%s)",symbol_period));
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок и категорию
      this.SetParameters(param);
      this.SetName("Fractals");
      this.SetDescription("Fractals");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_WILLIAMS;
      //--- Описание буферов линий UPPER_LINE и LOWER_LINE
      this.SetBufferDescription(UPPER_LINE,this.m_title+" Up");
      this.SetBufferDescription(LOWER_LINE,this.m_title+" Down");
      
      //--- Установим стиль рисования для буферов 0, 1 и номера буферов данных расчётной части
      this.SetBufferDrawType(0,DRAW_ARROW,UPPER_LINE);
      this.SetBufferDrawType(1,DRAW_ARROW,LOWER_LINE);
      
      //--- Зададим для буферов 0 и 1 цвета по умолчанию
      this.SetBufferColorToIndex(UPPER_LINE,0,clrGray);
      this.SetBufferColorToIndex(LOWER_LINE,0,clrGray);
     }
  };
//+------------------------------------------------------------------+
//| Класс индикатора Fractal Adaptive Moving Average                 |
//+------------------------------------------------------------------+
class CIndFrAMA : public CIndMSTF
  {
public:
//--- Конструктор
   CIndFrAMA(const string symbol,const ENUM_TIMEFRAMES timeframe,
             const int ma_period,                     // период усреднения
             const int ma_shift,                      // смещение индикатора по горизонтали
             const ENUM_APPLIED_PRICE applied_price   // тип цены или handle
            ) : CIndMSTF(IND_FRAMA,1,symbol,timeframe)
     {
      //--- Устанавливаем размер массива параметров и заполняем его
      ::ResetLastError();
      if(::ArrayResize(this.m_param,3)==3)
        {
         ::ZeroMemory(this.m_param);
         //--- период усреднения
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(ma_period<1 ? 14 : ma_period);
         //--- смещение индикатора по горизонтали
         this.m_param[1].type=TYPE_INT;
         this.m_param[1].integer_value=ma_shift;
         //--- тип цены или handle
         this.m_param[2].type=TYPE_UINT;
         this.m_param[2].integer_value=applied_price;
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=StringFormat("(%s%s%lu)",symbol_period,(current ? "" : ":"),ma_period);
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок, категорию и Digits индикатора
      this.SetParameters(param);
      this.SetName("FRAMA");
      this.SetDescription("Fractal Adaptive Moving Average");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_TREND;
      this.m_digits=::Digits()+1;
      //--- Записываем описание буфера линии
      this.SetBufferDescription(0,this.m_title);
      //--- Записываем смещение в буфер 0
      this.SetBufferShift(0,ma_shift);
      
      //--- Установим стиль рисования для буфера 0 и номер буфера данных расчётной части
      this.SetBufferDrawType(0,DRAW_LINE,0);
      //--- Зададим для буфера 0 цвет по умолчанию
      this.SetBufferColorToIndex(0,0,clrBlue);
     }
  };
//+------------------------------------------------------------------+
//| Класс индикатора Gator Oscillator                                |
//+------------------------------------------------------------------+
class CIndGator : public CIndMSTF
  {
public:
//--- Конструктор
   CIndGator(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   // тип цены или handle
            ) : CIndMSTF(IND_GATOR,2,symbol,timeframe)
     {
      // Номера буферов: 0 - UPPER_HISTOGRAM, 1- цветовой буфер верхней гистограммы, 2 - LOWER_HISTOGRAM, 3- цветовой буфер нижней гистограммы
      //--- Устанавливаем размер массива параметров и заполняем его
      ::ResetLastError();
      if(::ArrayResize(this.m_param,8)==8)
        {
         ::ZeroMemory(this.m_param);
         //--- период для расчета челюстей
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(jaw_period<1 ? 13 : jaw_period);
         //--- смещение челюстей по горизонтали
         this.m_param[1].type=TYPE_INT;
         this.m_param[1].integer_value=jaw_shift;
         //--- период для расчета зубов
         this.m_param[2].type=TYPE_UINT;
         this.m_param[2].integer_value=(teeth_period<1 ? 8 : teeth_period);
         //--- смещение зубов по горизонтали
         this.m_param[3].type=TYPE_INT;
         this.m_param[3].integer_value=teeth_shift;
         //--- период для расчета губ
         this.m_param[4].type=TYPE_UINT;
         this.m_param[4].integer_value=(lips_period<1 ? 5 : lips_period);
         //--- смещение губ по горизонтали
         this.m_param[5].type=TYPE_INT;
         this.m_param[5].integer_value=lips_shift;
         //--- тип сглаживания
         this.m_param[6].type=TYPE_UINT;
         this.m_param[6].integer_value=ma_method;
         //--- тип цены или handle
         this.m_param[7].type=TYPE_UINT;
         this.m_param[7].integer_value=applied_price;
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=StringFormat("(%s%s%lu,%lu,%lu)",symbol_period,(current ? "" : ":"),jaw_period,teeth_period,lips_period);
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок, категорию и Digits индикатора
      this.SetParameters(param);
      this.SetName("Gator");
      this.SetDescription("Gator Oscillator");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_WILLIAMS;
      this.m_digits=::Digits()+1;
      
      //--- Описание буферов линий UPPER_HISTOGRAM, буфер цвета верхней гистограммы, LOWER_HISTOGRAM и буфер цвета нижней гистограммы
      this.SetBufferDescription(0,this.m_title+" Up");
      this.SetBufferDescription(1,this.m_title+" Down");
      //--- Записываем смещения в буферы UPPER_HISTOGRAM, 1, LOWER_HISTOGRAM и 2
      this.SetBufferShift(0,teeth_shift);
      this.SetBufferShift(1,lips_shift);
      
      //--- Установим стиль рисования для буферов 0, 1 и номера буферов данных расчётной части
      this.SetBufferDrawType(0,DRAW_COLOR_HISTOGRAM,UPPER_HISTOGRAM);
      this.SetBufferDrawType(1,DRAW_COLOR_HISTOGRAM,LOWER_HISTOGRAM);

      //--- Зададим для цветных буферов цвета по умолчанию
      this.SetBufferColorToIndex(0,0,clrGreen);
      this.SetBufferColorToIndex(0,1,clrRed);
      this.SetBufferColorToIndex(1,0,clrGreen);
      this.SetBufferColorToIndex(1,1,clrRed);
      //--- Установим инициализирующий индекс цвета по умолчанию
      this.SetBufferInitColorIndex(0,0);
      this.SetBufferInitColorIndex(1,0);
     }
  };
//+------------------------------------------------------------------+
//| Класс индикатора Ichimoku Kinko Hyo                              |
//+------------------------------------------------------------------+
class CIndIchimoku : public CIndMSTF
  {
public:
//--- Конструктор
   CIndIchimoku(const string symbol,const ENUM_TIMEFRAMES timeframe,
                const int tenkan_sen,                    // период Tenkan-sen
                const int kijun_sen,                     // период Kijun-sen
                const int senkou_span_b                  // период Senkou Span B
               ) : CIndMSTF(IND_ICHIMOKU,5,symbol,timeframe)
     {
      // Номера буферов: 0 - TENKANSEN_LINE, 1 - KIJUNSEN_LINE, 2 - SENKOUSPANA_LINE, 3 - SENKOUSPANB_LINE, 4 - CHIKOUSPAN_LINE
      //--- Устанавливаем размер массива параметров и заполняем его
      ::ResetLastError();
      if(::ArrayResize(this.m_param,3)==3)
        {
         ::ZeroMemory(this.m_param);
         //--- период Tenkan-sen
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(tenkan_sen<1 ? 9 : tenkan_sen);
         //--- период Kijun-sen
         this.m_param[1].type=TYPE_UINT;
         this.m_param[1].integer_value=(kijun_sen<1 ? 26 : kijun_sen);
         //--- период Senkou Span B
         this.m_param[2].type=TYPE_UINT;
         this.m_param[2].integer_value=(senkou_span_b<1 ? 52 : senkou_span_b);
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=StringFormat("(%s%s%lu,%lu,%lu)",symbol_period,(current ? "" : ":"),tenkan_sen,kijun_sen,senkou_span_b);
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок, категорию и Digits индикатора
      this.SetParameters(param);
      this.SetName("Ichimoku");
      this.SetDescription("Ichimoku Kinko Hyo");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_TREND;
      this.m_digits=::Digits()+1;
      //--- Описание буферов линий TENKANSEN_LINE, KIJUNSEN_LINE, SENKOUSPANA_LINE, SENKOUSPANB_LINE и CHIKOUSPAN_LINE
      this.SetBufferDescription(TENKANSEN_LINE,::StringFormat("Tenkan-sen(%lu)",tenkan_sen));
      this.SetBufferDescription(KIJUNSEN_LINE,::StringFormat("Kijun-sen(%lu)",kijun_sen));
      this.SetBufferDescription(SENKOUSPANA_LINE,"Senkou Span A");
      this.SetBufferDescription(SENKOUSPANB_LINE,::StringFormat("Senkou Span B(%lu)",senkou_span_b));
      this.SetBufferDescription(CHIKOUSPAN_LINE,"Chikou Span");
      //--- Записываем смещения в буферы SENKOUSPANA_LINE, SENKOUSPANB_LINE и CHIKOUSPAN_LINE
      //this.SetBufferShift(SENKOUSPANA_LINE,kijun_sen);
      //this.SetBufferShift(SENKOUSPANB_LINE,kijun_sen);
      //this.SetBufferShift(CHIKOUSPAN_LINE,kijun_sen-senkou_span_b);
     }
  };
//+------------------------------------------------------------------+
//| Класс индикатора Market Facilitation Index                       |
//+------------------------------------------------------------------+
class CIndBWMFI : public CIndMSTF
  {
public:
//--- Конструктор
   CIndBWMFI(const string symbol,const ENUM_TIMEFRAMES timeframe,
             const ENUM_APPLIED_VOLUME applied_volume // тип объема для расчета
            ) : CIndMSTF(IND_BWMFI,1,symbol,timeframe)
     {
      //--- Устанавливаем размер массива параметров и заполняем его
      ::ResetLastError();
      if(::ArrayResize(this.m_param,1)==1)
        {
         ::ZeroMemory(this.m_param);
         //--- тип объема для расчета
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=applied_volume;
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=(current ? "" : StringFormat("(%s)",symbol_period));
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок и категорию
      this.SetParameters(param);
      this.SetName("BW MFI");
      this.SetDescription("Market Facilitation Index");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_WILLIAMS;
      //--- Записываем описание буфера линии
      this.SetBufferDescription(0,this.m_title);
      
      //--- Установим стиль рисования для буфера 0 и номер буфера данных расчётной части
      this.SetBufferDrawType(0,DRAW_COLOR_HISTOGRAM,0);
      
      //--- Зададим для цветного буфера 0 четыре цвета по умолчанию
      this.SetBufferColorToIndex(0,0,clrLime);
      this.SetBufferColorToIndex(0,1,clrSaddleBrown);
      this.SetBufferColorToIndex(0,2,clrBlue);
      this.SetBufferColorToIndex(0,3,clrPink);
      //--- Установим инициализирующий индекс цвета по умолчанию
      this.SetBufferInitColorIndex(0,0);
     }
  };
//+------------------------------------------------------------------+
//| Класс индикатора Momentum                                        |
//+------------------------------------------------------------------+
class CIndMomentum : public CIndMSTF
  {
public:
//--- Конструктор
   CIndMomentum(const string symbol,const ENUM_TIMEFRAMES timeframe,
                const int                 mom_period,    // период усреднения
                const ENUM_APPLIED_PRICE  applied_price  // тип цены или handle
               ) : CIndMSTF(IND_MOMENTUM,1,symbol,timeframe)
     {
      //--- Устанавливаем размер массива параметров и заполняем его
      ::ResetLastError();
      if(::ArrayResize(this.m_param,2)==2)
        {
         ::ZeroMemory(this.m_param);
         //--- период усреднения
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(mom_period<1 ? 14 : mom_period);
         //--- тип цены или handle
         this.m_param[1].type=TYPE_UINT;
         this.m_param[1].integer_value=applied_price;
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=StringFormat("(%s%s%lu)",symbol_period,(current ? "" : ":"),mom_period);
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок, категорию и Digits индикатора
      this.SetParameters(param);
      this.SetName("Momentum");
      this.SetDescription("Momentum");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_OSCILLATOR;
      this.m_digits=2;
      //--- Записываем описание буфера линии
      this.SetBufferDescription(0,this.m_title);
      
      //--- Установим стиль рисования для буфера 0 и номер буфера данных расчётной части
      this.SetBufferDrawType(0,DRAW_LINE,0);
      //--- Зададим для буфера 0 цвет по умолчанию
      this.SetBufferColorToIndex(0,0,clrDodgerBlue);
     }
  };
//+------------------------------------------------------------------+
//| Класс индикатора Money Flow Index                                |
//+------------------------------------------------------------------+
class CIndMFI : public CIndMSTF
  {
public:
//--- Конструктор
   CIndMFI(const string symbol,const ENUM_TIMEFRAMES timeframe,
           const int                 ma_period,       // период усреднения
           const ENUM_APPLIED_VOLUME applied_volume   // тип объема для расчета
          ) : CIndMSTF(IND_MFI,1,symbol,timeframe)
     {
      //--- Устанавливаем размер массива параметров и заполняем его
      ::ResetLastError();
      if(::ArrayResize(this.m_param,2)==2)
        {
         ::ZeroMemory(this.m_param);
         //--- период усреднения
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(ma_period<1 ? 14 : ma_period);
         //--- тип объема для расчета
         this.m_param[1].type=TYPE_UINT;
         this.m_param[1].integer_value=applied_volume;
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=StringFormat("(%s%s%lu)",symbol_period,(current ? "" : ":"),ma_period);
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок и категорию
      this.SetParameters(param);
      this.SetName("MFI");
      this.SetDescription("Money Flow Index");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_VOLUME;
      //--- Записываем описание буфера линии
      this.SetBufferDescription(0,this.m_title);
      
      //--- Установим стиль рисования для буфера 0 и номер буфера данных расчётной части
      this.SetBufferDrawType(0,DRAW_LINE,0);
      //--- Зададим для буфера 0 цвет по умолчанию
      this.SetBufferColorToIndex(0,0,clrDodgerBlue);
     }
  };
//+------------------------------------------------------------------+
//| Класс индикатора Moving Average                                  |
//+------------------------------------------------------------------+
class CIndMA : public CIndMSTF
  {
public:
//--- Конструктор
   CIndMA(const string symbol,const ENUM_TIMEFRAMES timeframe,
          const int                 ma_period,     // период усреднения
          const int                 ma_shift,      // смещение индикатора по горизонтали
          const ENUM_MA_METHOD      ma_method,     // тип сглаживания
          const ENUM_APPLIED_PRICE  applied_price  // тип цены или handle
         ) : CIndMSTF(IND_MA,1,symbol,timeframe)
     {
      //--- Устанавливаем размер массива параметров и заполняем его
      ::ResetLastError();
      if(::ArrayResize(this.m_param,4)==4)
        {
         ::ZeroMemory(this.m_param);
         //--- период усреднения
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(ma_period<1 ? 10 : ma_period);
         //--- смещение индикатора по горизонтали
         this.m_param[1].type=TYPE_INT;
         this.m_param[1].integer_value=ma_shift;
         //--- тип сглаживания
         this.m_param[2].type=TYPE_UINT;
         this.m_param[2].integer_value=ma_method;
         //--- тип цены или handle
         this.m_param[3].type=TYPE_UINT;
         this.m_param[3].integer_value=applied_price;
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=StringFormat("(%s%s%lu)",symbol_period,(current ? "" : ":"),ma_period);
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок, категорию и Digits индикатора
      this.SetParameters(param);
      this.SetName("MA");
      this.SetDescription("Moving Average");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_TREND;
      this.m_digits=::Digits()+1;
      //--- Записываем описание буфера линии
      this.SetBufferDescription(0,this.m_title);
      //--- Записываем смещение в буфер 0
      this.SetBufferShift(0,ma_shift);
      
      //--- Установим стиль рисования для буфера 0 и номер буфера данных расчётной части
      this.SetBufferDrawType(0,DRAW_LINE,0);
      //--- Зададим для буфера 0 цвет по умолчанию
      this.SetBufferColorToIndex(0,0,clrRed);
     }
  };
//+------------------------------------------------------------------+
//| Класс индикатора Moving Average of Oscillator                    |
//+------------------------------------------------------------------+
class CIndOsMA : public CIndMSTF
  {
public:
//--- Конструктор
   CIndOsMA(const string symbol,const ENUM_TIMEFRAMES timeframe,
            const int                fast_ema_period, // период быстрой средней
            const int                slow_ema_period, // период медленной средней
            const int                signal_period,   // период усреднения разности
            const ENUM_APPLIED_PRICE applied_price    // тип цены или handle
           ) : CIndMSTF(IND_OSMA,1,symbol,timeframe)
     {
      //--- Устанавливаем размер массива параметров и заполняем его
      ::ResetLastError();
      if(::ArrayResize(this.m_param,4)==4)
        {
         ::ZeroMemory(this.m_param);
         //--- период быстрой средней
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(fast_ema_period<1 ? 12 : fast_ema_period);
         //--- период медленной средней
         this.m_param[1].type=TYPE_UINT;
         this.m_param[1].integer_value=(slow_ema_period<1 ? 26 : slow_ema_period);
         //--- период усреднения разности
         this.m_param[2].type=TYPE_UINT;
         this.m_param[2].integer_value=(signal_period<1 ? 9 : signal_period<2 ? 2 : signal_period);
         //--- тип цены или handle
         this.m_param[3].type=TYPE_UINT;
         this.m_param[3].integer_value=applied_price;
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=StringFormat("(%s%s%lu,%lu,%lu)",symbol_period,(current ? "" : ":"),fast_ema_period,slow_ema_period,signal_period);
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок, категорию и Digits индикатора
      this.SetParameters(param);
      this.SetName("OsMA");
      this.SetDescription("Moving Average of Oscillator");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_OSCILLATOR;
      this.m_digits=::Digits()+2;
      //--- Записываем описание буфера линии
      this.SetBufferDescription(0,this.m_title);
      
      //--- Установим стиль рисования для буфера 0 и номер буфера данных расчётной части
      this.SetBufferDrawType(0,DRAW_HISTOGRAM,0);
      //--- Зададим для буфера 0 цвет по умолчанию
      this.SetBufferColorToIndex(0,0,clrSilver);
     }
  };
//+------------------------------------------------------------------+
//| Класс индикатора Moving Averages Convergence/Divergence          |
//+------------------------------------------------------------------+
class CIndMACD : public CIndMSTF
  {
public:
//--- Конструктор
   CIndMACD(const string symbol,const ENUM_TIMEFRAMES timeframe,
            const int                fast_ema_period, // период быстрой средней
            const int                slow_ema_period, // период медленной средней
            const int                signal_period,   // период усреднения разности
            const ENUM_APPLIED_PRICE applied_price    // тип цены или handle
           ) : CIndMSTF(IND_MACD,2,symbol,timeframe)
     {
      // Номера буферов: 0 - MAIN_LINE, 1 - SIGNAL_LINE
      //--- Устанавливаем размер массива параметров и заполняем его
      ::ResetLastError();
      if(::ArrayResize(this.m_param,4)==4)
        {
         ::ZeroMemory(this.m_param);
         //--- период быстрой средней
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(fast_ema_period<1 ? 12 : fast_ema_period);
         //--- период медленной средней
         this.m_param[1].type=TYPE_UINT;
         this.m_param[1].integer_value=(slow_ema_period<1 ? 26 : slow_ema_period);
         //--- период усреднения разности
         this.m_param[2].type=TYPE_UINT;
         this.m_param[2].integer_value=(signal_period<1 ? 9 : signal_period);
         //--- тип цены или handle
         this.m_param[3].type=TYPE_UINT;
         this.m_param[3].integer_value=applied_price;
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=StringFormat("(%s%s%lu,%lu,%lu)",symbol_period,(current ? "" : ":"),fast_ema_period,slow_ema_period,signal_period);
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок, категорию и Digits индикатора
      this.SetParameters(param);
      this.SetName("MACD");
      this.SetDescription("Moving Averages Convergence/Divergence");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_OSCILLATOR;
      this.m_digits=::Digits()+1;
      //--- Описание буферов линии MAIN_LINE и SIGNAL_LINE
      this.SetBufferDescription(MAIN_LINE,this.m_title);
      this.SetBufferDescription(SIGNAL_LINE,"Signal");
      
      //--- Установим стиль рисования для буферов 0, 1 и номера буферов данных расчётной части
      this.SetBufferDrawType(0,DRAW_HISTOGRAM,MAIN_LINE);
      this.SetBufferDrawType(1,DRAW_LINE,SIGNAL_LINE);
      
      //--- Зададим для буферов 0 и 1 цвета по умолчанию
      this.SetBufferColorToIndex(MAIN_LINE,0,clrSilver);
      this.SetBufferColorToIndex(SIGNAL_LINE,0,clrRed);
     }
  };
//+------------------------------------------------------------------+
//| Класс индикатора On Balance Volume                               |
//+------------------------------------------------------------------+
class CIndOBV : public CIndMSTF
  {
public:
//--- Конструктор
   CIndOBV(const string symbol,const ENUM_TIMEFRAMES timeframe,
           const ENUM_APPLIED_VOLUME applied_volume   // тип объема для расчета
          ) : CIndMSTF(IND_OBV,1,symbol,timeframe)
     {
      //--- Устанавливаем размер массива параметров и заполняем его
      ::ResetLastError();
      if(::ArrayResize(this.m_param,1)==1)
        {
         ::ZeroMemory(this.m_param);
         //--- тип объема для расчета
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=applied_volume;
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=(current ? "" : StringFormat("(%s)",symbol_period));
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок, категорию и Digits индикатора
      this.SetParameters(param);
      this.SetName("OBV");
      this.SetDescription("On Balance Volume");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_VOLUME;
      this.m_digits=0;
      //--- Записываем описание буфера линии
      this.SetBufferDescription(0,this.m_title);
      
      //--- Установим стиль рисования для буфера 0 и номер буфера данных расчётной части
      this.SetBufferDrawType(0,DRAW_LINE,0);
      //--- Зададим для буфера 0 цвет по умолчанию
      this.SetBufferColorToIndex(0,0,clrLightSeaGreen);
     }
  };
//+------------------------------------------------------------------+
//| Класс индикатора Parabolic Stop and Reverse system               |
//+------------------------------------------------------------------+
class CIndSAR : public CIndMSTF
  {
public:
//--- Конструктор
   CIndSAR(const string symbol,const ENUM_TIMEFRAMES timeframe,
           const double step,                      // шаг изменения цены - коэффициент ускорения
           const double maximum                    // максимальный шаг
          ) : CIndMSTF(IND_SAR,1,symbol,timeframe)
     {
      //--- Устанавливаем размер массива параметров и заполняем его
      ::ResetLastError();
      if(::ArrayResize(this.m_param,2)==2)
        {
         ::ZeroMemory(this.m_param);
         //--- шаг изменения цены - коэффициент ускорения
         this.m_param[0].type=TYPE_DOUBLE;
         this.m_param[0].double_value=step;
         //--- максимальный шаг
         this.m_param[1].type=TYPE_DOUBLE;
         this.m_param[1].double_value=maximum;
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=StringFormat("(%s%s%.2f,%.2f)",symbol_period,(current ? "" : ":"),step,maximum);
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок и категорию
      this.SetParameters(param);
      this.SetName("SAR");
      this.SetDescription("Parabolic SAR");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_TREND;
      //--- Записываем описание буфера линии
      this.SetBufferDescription(0,this.m_title);
      
      //--- Установим стиль рисования для буфера 0 и номер буфера данных расчётной части
      this.SetBufferDrawType(0,DRAW_ARROW,0);
      //--- Зададим для буфера 0 цвет по умолчанию
      this.SetBufferColorToIndex(0,0,clrLime);
     }
  };
//+------------------------------------------------------------------+
//| Класс индикатора Relative Strength Index                         |
//+------------------------------------------------------------------+
class CIndRSI : public CIndMSTF
  {
public:
//--- Конструктор
   CIndRSI(const string symbol,const ENUM_TIMEFRAMES timeframe,
           const int                ma_period,     // период усреднения
           const ENUM_APPLIED_PRICE applied_price  // тип цены или handle
          ) : CIndMSTF(IND_RSI,1,symbol,timeframe)
     {
      //--- Устанавливаем размер массива параметров и заполняем его
      ::ResetLastError();
      if(::ArrayResize(this.m_param,2)==2)
        {
         ::ZeroMemory(this.m_param);
         //--- период усреднения
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(ma_period<1 ? 14 : ma_period<2 ? 2 : ma_period);
         //--- тип цены или handle
         this.m_param[1].type=TYPE_UINT;
         this.m_param[1].integer_value=applied_price;
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=StringFormat("(%s%s%lu)",symbol_period,(current ? "" : ":"),ma_period);
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок, категорию и Digits индикатора
      this.SetParameters(param);
      this.SetName("RSI");
      this.SetDescription("Relative Strength Index");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_OSCILLATOR;
      this.m_digits=2;
      //--- Записываем описание буфера линии
      this.SetBufferDescription(0,this.m_title);
      
      //--- Установим стиль рисования для буфера 0 и номер буфера данных расчётной части
      this.SetBufferDrawType(0,DRAW_LINE,0);
      //--- Зададим для буфера 0 цвет по умолчанию
      this.SetBufferColorToIndex(0,0,clrDodgerBlue);
     }
  };
//+------------------------------------------------------------------+
//| Класс индикатора Relative Vigor Index                            |
//+------------------------------------------------------------------+
class CIndRVI : public CIndMSTF
  {
public:
//--- Конструктор
   CIndRVI(const string symbol,const ENUM_TIMEFRAMES timeframe,
           const int ma_period                     // период усреднения
          ) : CIndMSTF(IND_RVI,2,symbol,timeframe)
     {
      // Номера буферов: 0 - MAIN_LINE, 1 - SIGNAL_LINE
      //--- Устанавливаем размер массива параметров и заполняем его
      ::ResetLastError();
      if(::ArrayResize(this.m_param,1)==1)
        {
         ::ZeroMemory(this.m_param);
         //--- период усреднения
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(ma_period<1 ? 10 : ma_period);
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=StringFormat("(%s%s%lu)",symbol_period,(current ? "" : ":"),ma_period);
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок, категорию и Digits индикатора
      this.SetParameters(param);
      this.SetName("RVI");
      this.SetDescription("Relative Vigor Index");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_OSCILLATOR;
      this.m_digits=3;
      //--- Описание буферов линии MAIN_LINE и SIGNAL_LINE
      this.SetBufferDescription(MAIN_LINE,this.m_title);
      this.SetBufferDescription(SIGNAL_LINE,"Signal");
      
      //--- Установим стиль рисования для буферов 0, 1 и номера буферов данных расчётной части
      this.SetBufferDrawType(0,DRAW_LINE,MAIN_LINE);
      this.SetBufferDrawType(1,DRAW_LINE,SIGNAL_LINE);
      
      //--- Зададим для буферов 0 и 1 цвета по умолчанию
      this.SetBufferColorToIndex(MAIN_LINE,0,clrGreen);
      this.SetBufferColorToIndex(SIGNAL_LINE,0,clrRed);
     }
  };
//+------------------------------------------------------------------+
//| Класс индикатора Standard Deviation                              |
//+------------------------------------------------------------------+
class CIndStdDev : public CIndMSTF
  {
public:
//--- Конструктор
   CIndStdDev(const string symbol,const ENUM_TIMEFRAMES timeframe,
              const int                ma_period,        // период усреднения
              const int                ma_shift,         // смещение индикатора по горизонтали
              const ENUM_MA_METHOD     ma_method,        // тип сглаживания
              const ENUM_APPLIED_PRICE applied_price     // тип цены или handle
             ) : CIndMSTF(IND_STDDEV,1,symbol,timeframe)
     {
      //--- Устанавливаем размер массива параметров и заполняем его
      ::ResetLastError();
      if(::ArrayResize(this.m_param,4)==4)
        {
         ::ZeroMemory(this.m_param);
         //--- период усреднения
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(ma_period<1 ? 20 : ma_period<2 ? 2 : ma_period);
         //--- смещение индикатора по горизонтали
         this.m_param[1].type=TYPE_INT;
         this.m_param[1].integer_value=ma_shift;
         //--- тип сглаживания
         this.m_param[2].type=TYPE_UINT;
         this.m_param[2].integer_value=ma_method;
         //--- тип цены или handle
         this.m_param[3].type=TYPE_UINT;
         this.m_param[3].integer_value=applied_price;
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=StringFormat("(%s%s%lu)",symbol_period,(current ? "" : ":"),ma_period);
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок, категорию и Digits индикатора
      this.SetParameters(param);
      this.SetName("StdDev");
      this.SetDescription("Standard Deviation");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_TREND;
      this.m_digits=::Digits()+1;
      //--- Записываем описание буфера линии
      this.SetBufferDescription(0,this.m_title);
      //--- Записываем смещение в буфер 0
      this.SetBufferShift(0,ma_shift);
      
      //--- Установим стиль рисования для буфера 0 и номер буфера данных расчётной части
      this.SetBufferDrawType(0,DRAW_LINE,0);
      //--- Зададим для буфера 0 цвет по умолчанию
      this.SetBufferColorToIndex(0,0,clrMediumSeaGreen);
     }
  };
//+------------------------------------------------------------------+
//| Класс индикатора Stochastic Oscillator                           |
//+------------------------------------------------------------------+
class CIndStoch : public CIndMSTF
  {
public:
//--- Конструктор
   CIndStoch(const string symbol,const ENUM_TIMEFRAMES timeframe,
             const int              Kperiod,          // K-период (количество баров для расчетов)
             const int              Dperiod,          // D-период (период первичного сглаживания)
             const int              slowing,          // окончательное сглаживание
             const ENUM_MA_METHOD   ma_method,        // тип сглаживания
             const ENUM_STO_PRICE   price_field       // способ расчета стохастика
            ) : CIndMSTF(IND_STOCHASTIC,2,symbol,timeframe)
     {
      // Номера буферов: 0 - MAIN_LINE, 1 - SIGNAL_LINE
      //--- Устанавливаем размер массива параметров и заполняем его
      ::ResetLastError();
      if(::ArrayResize(this.m_param,5)==5)
        {
         ::ZeroMemory(this.m_param);
         //--- K-период (количество баров для расчетов)
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(Kperiod<1 ? 5 : Kperiod);
         //--- D-период (период первичного сглаживания)
         this.m_param[1].type=TYPE_UINT;
         this.m_param[1].integer_value=(Dperiod<1 ? 3 : Dperiod);
         //--- окончательное сглаживание
         this.m_param[2].type=TYPE_UINT;
         this.m_param[2].integer_value=(slowing<1 ? 3 : slowing);
         //--- тип сглаживания
         this.m_param[3].type=TYPE_UINT;
         this.m_param[3].integer_value=ma_method;
         //--- способ расчета стохастика
         this.m_param[4].type=TYPE_UINT;
         this.m_param[4].integer_value=price_field;
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=StringFormat("(%s%s%lu,%lu,%lu)",symbol_period,(current ? "" : ":"),Kperiod,Dperiod,slowing);
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок, категорию и Digits индикатора
      this.SetParameters(param);
      this.SetName("Stoch");
      this.SetDescription("Stochastic Oscillator");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_OSCILLATOR;
      this.m_digits=2;
      //--- Описание буферов линии MAIN_LINE и SIGNAL_LINE
      this.SetBufferDescription(MAIN_LINE,this.m_title);
      this.SetBufferDescription(SIGNAL_LINE,"Signal");
      
      //--- Установим стиль рисования для буферов 0, 1 и номера буферов данных расчётной части
      this.SetBufferDrawType(0,DRAW_LINE,MAIN_LINE);
      this.SetBufferDrawType(1,DRAW_LINE,SIGNAL_LINE);
      
      //--- Зададим для буферов 0 и 1 цвета по умолчанию
      this.SetBufferColorToIndex(MAIN_LINE,0,clrLightSeaGreen);
      this.SetBufferColorToIndex(SIGNAL_LINE,0,clrRed);
     }
  };
//+------------------------------------------------------------------+
//| Класс индикатора Triple Exponential Moving Average               |
//+------------------------------------------------------------------+
class CIndTEMA : public CIndMSTF
  {
public:
//--- Конструктор
   CIndTEMA(const string symbol,const ENUM_TIMEFRAMES timeframe,
            const int                  ma_period,     // период усреднения
            const int                  ma_shift,      // смещение индикатора по горизонтали
            const ENUM_APPLIED_PRICE   applied_price  // тип цены или handle
           ) : CIndMSTF(IND_TEMA,1,symbol,timeframe)
     {
      //--- Устанавливаем размер массива параметров и заполняем его
      ::ResetLastError();
      if(::ArrayResize(this.m_param,3)==3)
        {
         ::ZeroMemory(this.m_param);
         //--- период усреднения
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(ma_period<1 ? 14 : ma_period);
         //--- смещение индикатора по горизонтали
         this.m_param[1].type=TYPE_INT;
         this.m_param[1].integer_value=ma_shift;
         //--- тип цены или handle
         this.m_param[2].type=TYPE_UINT;
         this.m_param[2].integer_value=applied_price;
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=StringFormat("(%s%s%lu)",symbol_period,(current ? "" : ":"),ma_period);
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок, категорию и Digits индикатора
      this.SetParameters(param);
      this.SetName("TEMA");
      this.SetDescription("Triple Exponential Moving Average");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_TREND;
      this.m_digits=::Digits()+1;
      //--- Записываем описание буфера линии
      this.SetBufferDescription(0,this.m_title);
      //--- Записываем смещение в буфер 0
      this.SetBufferShift(0,ma_shift);
      
      //--- Установим стиль рисования для буфера 0 и номер буфера данных расчётной части
      this.SetBufferDrawType(0,DRAW_LINE,0);
      //--- Зададим для буфера 0 цвет по умолчанию
      this.SetBufferColorToIndex(0,0,clrRed);
     }
  };
//+------------------------------------------------------------------+
//| Класс индикатора Triple Exponential Moving Averages Oscillator   |
//+------------------------------------------------------------------+
class CIndTriX : public CIndMSTF
  {
public:
//--- Конструктор
   CIndTriX(const string symbol,const ENUM_TIMEFRAMES timeframe,
            const int                ma_period,       // период усреднения
            const ENUM_APPLIED_PRICE applied_price    // тип цены или handle
           ) : CIndMSTF(IND_TRIX,1,symbol,timeframe)
     {
      //--- Устанавливаем размер массива параметров и заполняем его
      ::ResetLastError();
      if(::ArrayResize(this.m_param,2)==2)
        {
         ::ZeroMemory(this.m_param);
         //--- период усреднения
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(ma_period<1 ? 14 : ma_period<2 ? 2 : ma_period);
         //--- тип цены или handle
         this.m_param[1].type=TYPE_UINT;
         this.m_param[1].integer_value=applied_price;
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=StringFormat("(%s%s%lu)",symbol_period,(current ? "" : ":"),ma_period);
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок и категорию
      this.SetParameters(param);
      this.SetName("TRIX");
      this.SetDescription("Triple Exponential Average");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_OSCILLATOR;
      //--- Записываем описание буфера линии
      this.SetBufferDescription(0,this.m_title);
      
      //--- Установим стиль рисования для буфера 0 и номер буфера данных расчётной части
      this.SetBufferDrawType(0,DRAW_LINE,0);
      //--- Зададим для буфера 0 цвет по умолчанию
      this.SetBufferColorToIndex(0,0,clrRed);
     }
  };
//+------------------------------------------------------------------+
//| Класс индикатора Larry Williams' Percent Range                   |
//+------------------------------------------------------------------+
class CIndWPR : public CIndMSTF
  {
public:
//--- Конструктор
   CIndWPR(const string symbol,const ENUM_TIMEFRAMES timeframe,
           const int calc_period                   // период усреднения
          ) : CIndMSTF(IND_WPR,1,symbol,timeframe)
     {
      //--- Устанавливаем размер массива параметров и заполняем его
      ::ResetLastError();
      if(::ArrayResize(this.m_param,1)==1)
        {
         ::ZeroMemory(this.m_param);
         //--- период усреднения
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(calc_period<1 ? 14 : calc_period);
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=StringFormat("(%s%s%lu)",symbol_period,(current ? "" : ":"),calc_period);
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок, категорию и Digits индикатора
      this.SetParameters(param);
      this.SetName("%R");
      this.SetDescription("Williams' Percent Range");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_OSCILLATOR;
      this.m_digits=2;
      //--- Записываем описание буфера линии
      this.SetBufferDescription(0,this.m_title);
      
      //--- Установим стиль рисования для буфера 0 и номер буфера данных расчётной части
      this.SetBufferDrawType(0,DRAW_LINE,0);
      //--- Зададим для буфера 0 цвет по умолчанию
      this.SetBufferColorToIndex(0,0,clrAqua);
     }
  };
//+------------------------------------------------------------------+
//| Класс индикатора Variable Index Dynamic Average                  |
//+------------------------------------------------------------------+
class CIndVIDyA : public CIndMSTF
  {
public:
//--- Конструктор
   CIndVIDyA(const string symbol,const ENUM_TIMEFRAMES timeframe,
             const int                 cmo_period,    // период Chande Momentum
             const int                 ema_period,    // период фактора сглаживания
             const int                 ma_shift,      // смещение индикатора по горизонтали
             const ENUM_APPLIED_PRICE  applied_price  // тип цены или handle
            ) : CIndMSTF(IND_VIDYA,1,symbol,timeframe)
     {
      //--- Устанавливаем размер массива параметров и заполняем его
      ::ResetLastError();
      if(::ArrayResize(this.m_param,4)==4)
        {
         ::ZeroMemory(this.m_param);
         //--- период Chande Momentum
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=(cmo_period<1 ? 9 : cmo_period);
         //--- период фактора сглаживания
         this.m_param[1].type=TYPE_UINT;
         this.m_param[1].integer_value=(ema_period<1 ? 12 : ema_period);
         //--- смещение индикатора по горизонтали
         this.m_param[2].type=TYPE_INT;
         this.m_param[2].integer_value=ma_shift;
         //--- тип цены или handle
         this.m_param[3].type=TYPE_UINT;
         this.m_param[3].integer_value=applied_price;
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=StringFormat("(%s%s%lu,%lu)",symbol_period,(current ? "" : ":"),cmo_period,ema_period);
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок, категорию и Digits индикатора
      this.SetParameters(param);
      this.SetName("VIDYA");
      this.SetDescription("Variable Index Dynamic Average");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_TREND;
      this.m_digits=::Digits()+1;
      //--- Записываем описание буфера линии
      this.SetBufferDescription(0,this.m_title);
      //--- Записываем смещение в буфер 0
      this.SetBufferShift(0,ma_shift);
      
      //--- Установим стиль рисования для буфера 0 и номер буфера данных расчётной части
      this.SetBufferDrawType(0,DRAW_LINE,0);
      //--- Зададим для буфера 0 цвет по умолчанию
      this.SetBufferColorToIndex(0,0,clrRed);
     }
  };
//+------------------------------------------------------------------+
//| Класс индикатора Volumes                                         |
//+------------------------------------------------------------------+
class CIndVolumes : public CIndMSTF
  {
public:
//--- Конструктор
   CIndVolumes(const string symbol,const ENUM_TIMEFRAMES timeframe,
               const ENUM_APPLIED_VOLUME applied_volume  // тип объема
              ) : CIndMSTF(IND_VOLUMES,1,symbol,timeframe)
     {
      //--- Устанавливаем размер массива параметров и заполняем его
      ::ResetLastError();
      if(::ArrayResize(this.m_param,1)==1)
        {
         ::ZeroMemory(this.m_param);
         //--- тип объема
         this.m_param[0].type=TYPE_UINT;
         this.m_param[0].integer_value=applied_volume;
        }
      else
         ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError());
      //--- Создаём описание параметров.
      //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
      bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
      string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
      string param=(current ? "" : StringFormat("(%s)",symbol_period));
      //--- Записываем описание параметров, наименование индикатора, его описание, заголовок, категорию и Digits индикатора
      this.SetParameters(param);
      this.SetName("Volumes");
      this.SetDescription("Volumes");
      this.m_title=this.Name()+this.Parameters();
      this.m_category=IND_CATEGORY_VOLUME;
      this.m_digits=0;
      //--- Записываем описание буфера линии
      this.SetBufferDescription(0,this.m_title);
      
      //--- Установим стиль рисования для буфера 0 и номер буфера данных расчётной части
      this.SetBufferDrawType(0,DRAW_COLOR_HISTOGRAM,0);
      
      //--- Зададим для цветного буфера 0 два цвета по умолчанию
      this.SetBufferColorToIndex(0,0,clrGreen);
      this.SetBufferColorToIndex(0,1,clrRed);
      //--- Установим инициализирующий индекс цвета по умолчанию
      this.SetBufferInitColorIndex(0,0);
     }
  };
//+------------------------------------------------------------------+
//| Класс пользовательского индикатора                               |
//+------------------------------------------------------------------+
class CIndCustom : public CIndMSTF
  {
public:
//--- Конструктор
   CIndCustom(const string symbol,const ENUM_TIMEFRAMES timeframe,
              const string path,                      // путь к индикатору (например, "Examples\\MACD.ex5")
              const string name,                      // имя пользовательского индикатора
              const uint   buffers,                   // количество буферов индикатора
              const MqlParam &param[]                 // массив параметров пользовательского индикатора
             ) : CIndMSTF(IND_CUSTOM,buffers,symbol,timeframe)
     {
      //--- Если передан пустой массив параметров - сообщаем об этом в журнал
      int total=(int)param.Size();
      if(total==0)
         ::PrintFormat("%s Error. Passed an empty array",__FUNCTION__);
      //--- Если массив не пустой и его размер увеличен на 1 (в первый параметр типа string должно быть записано имя индикатора)
      ResetLastError();
      if(total>0 && ::ArrayResize(this.m_param,total+1)==total+1)
        {
         //--- Обнуляем данные в массиве и вписываем имя (путь к файлу и имя .ex5 файла)
         ::ZeroMemory(this.m_param);
         //--- имя пользовательского индикатора
         this.m_param[0].type=TYPE_STRING;
         this.m_param[0].string_value=path;
         //--- заполняем массив параметров индикатора
         for(int i=0;i<total;i++)
           {
            this.m_param[i+1].type=param[i].type;
            this.m_param[i+1].double_value=param[i].double_value;
            this.m_param[i+1].integer_value=param[i].integer_value;
            this.m_param[i+1].string_value=param[i].string_value;
           }
         //--- Создаём описание параметров.
         //--- Если символ или период графика не текущие, то к параметрам добавляются их описания
         bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period());
         string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription()));
         string param=(current ? "" : StringFormat("(%s)",symbol_period));
         //--- Записываем описание параметров, наименование индикатора, его описание, заголовок и категорию
         this.SetParameters(param);
         this.SetName(name);
         this.SetDescription(name);
         this.m_title=this.Name()+this.Parameters();
         this.m_category=IND_CATEGORY_CUSTOM;
         //--- Записываем описание первого буфера линии
         this.SetBufferDescription(0,this.m_title);
        }
     }
  };

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

В классе-коллекции индикаторов тоже необходимо внести доработки в методы и добавить новые.

//+------------------------------------------------------------------+
//| Класс-коллекция индикаторов                                      |
//+------------------------------------------------------------------+
#include <Arrays\ArrayObj.mqh>
class CMSTFIndicators
  {
private:
   CArrayObj         m_list;
//--- Создаёт индикатор для переданного объекта
   bool              CreateIndicator(CIndMSTF *ind_obj);
//--- Добавляет указанный индикатор в коллекцию
   int               AddNewIndicator(CIndMSTF *ind_obj,const string source);

public:
//--- Возвращает (1) объект индикатора по хэндлу, (2) количество индикаторов в коллекции
   CIndMSTF         *GetIndicatorObj(const int ind_handle,const string source) const;
   uint              IndicatorsTotal(void)                  const { return this.m_list.Total(); }

//--- Заполняет данными буферы (1) индикатора по хэндлу, (2) всех индикаторов в коллекции
   bool              Calculate(const int ind_handle);
   bool              Calculate(void);
//--- Устанавливает (1) указанное, (2) по умолчанию описание, (3) цвета линии буфера индикатора
   void              SetPlotLabel(const uint plot_index,const string descript);
   void              SetPlotLabelFromBuffer(const uint plot_index,const int ind_handle,const uint buffer_num);
   void              SetPlotColorsFromBuffer(const uint plot_index,const int ind_handle,const uint buffer_num);
//--- Устанавливает смещение указанному рисуемому буферу
   void              SetPlotShift(const uint plot_index,const int shift);
//--- (1) Устанавливает (2) возвращает инициализирующее значение указанного буфера указанного по хэндлу индикатора
   void              SetBufferInitValue(const int ind_handle,const uint buffer_num,const double value);
   double            BufferInitValue(const int ind_handle,const uint buffer_num) const;

//--- (1) Устанавливает (2) возвращает инициализирующее значение индекса цвета для указанного буфера
   void              SetBufferInitColorIndex(const int ind_handle,const uint buffer_num,const uchar index);
   uchar             BufferInitColorIndex(const int ind_handle,const uint buffer_num) const;
//--- (1) Устанавливает, (2) возвращает значение цвета по индексу для указанного буфера
   void              SetBufferColorToIndex(const int ind_handle,const uint buffer_num,const uchar color_idx,const color clr);
   color             BufferColorByIndex(const int ind_handle,const uint buffer_num,const uchar color_idx) const;
   //--- Возвращает флаг цветного буфера
   bool              IsColoredBuffer(const int ind_handle,const uint buffer_num) const;

//--- Возвращает данные индикатора по хэндлу из указанного буфера по индексу (1) как есть, (2) на указанный символ/таймфрейм
   double            GetData(const int ind_handle,const uint buffer_num,const uint array_num,const uint index);
   double            GetDataTo(const string symbol_to,const ENUM_TIMEFRAMES timeframe_to,const int ind_handle,const uint buffer_num,const uint array_num,const uint index);
//--- Возвращает данные индексов цвета индикатора по хэндлу из указанного буфера по индексу (1) как есть, (2) на указанный символ/таймфрейм
   double            GetColorData(const int ind_handle,const uint buffer_num,const uint index);
   double            GetColorDataTo(const string symbol_to,const ENUM_TIMEFRAMES timeframe_to,const int ind_handle,const uint buffer_num,const uint index);

//--- (1) Копирует данные указанного буфера расчётной части указанного по хэндлу индикатора в буфер индикатора с учётом символа/периода графика,
//--- (2) Копирует данные указанного цветного буфера расчётной части указанного по хэндлу индикатора в цветной буфер индикатора с учётом символа/периода графика,
//--- (2) возвращает количество данных в укакзанном буфере указанного по хэндлу индикатора
   bool              DataToBuffer(const string symbol_to,const ENUM_TIMEFRAMES timeframe_to,const int ind_handle,const uint buffer_num,const uint array_num,const int limit,double &buffer[]);
   bool              DataToColorBuffer(const string symbol_to,const ENUM_TIMEFRAMES timeframe_to,const int ind_handle,const uint buffer_num,const uint array_num,const int limit,double &plot_buffer[],double &color_buffer[]);
   uint              DataTotal(const int ind_handle,const uint buffer_num) const;

   //--- Возвращает (1) описание буфера, (2) состояние данных линии указанного буфера указанного по хэндлу индикатора на указанном баре
   //--- (3) состояние линии индикатора с учётом символа/периода графика, (4) состояние отношения линии индикатора с указанным уровнем,
   //--- (5)  состояние отношения линии индикатора с указанным уровнем с учётом символа/периода графика, (6) описание категории индикатора
   string            BufferDescription(const int ind_handle,const uint buffer_num);
   ENUM_LINE_STATE   BufferLineState(const int ind_handle,const uint buffer_num,const uint array_num,const int index);
   ENUM_LINE_STATE   BufferLineState(const string symbol,const ENUM_TIMEFRAMES timeframe,const int ind_handle,const uint buffer_num,const uint array_num,const int index);
   ENUM_LINE_STATE   BufferLineStateRelative(const int ind_handle,const int buffer_num,const uint array_num,const int index,const double level0,const double level1=EMPTY_VALUE);
   ENUM_LINE_STATE   BufferLineStateRelative(const string symbol,const ENUM_TIMEFRAMES timeframe,const int ind_handle,const int buffer_num,const uint array_num,const int index,const double level0,const double level1=EMPTY_VALUE);
   string            CategoryDescription(const int ind_handle);
   
//--- Устанавливает (1) идентификатор, (2) Digits, (3) пользовательское описание, (4) описание буфера
   void              SetID(const int ind_handle,const int id);
   void              SetDigits(const int ind_handle,const int digits);
   void              SetDescription(const int ind_handle,const string descr);
   void              SetBufferDescription(const int ind_handle,const uint buffer_num,const string descr);

//--- Возвращает флаг серийности указанного буфера, (2) синхронизированности исторических данных по символу/периоду
   bool              IsSeries(const int ind_handle,const uint buffer_num,const uint array_num) const;
   bool              IsSynchronized(const int ind_handle) const;
   
//--- ...
//--- ...
//--- ...

//--- Таймер
   void OnTimer(void)
     {
      //--- В цикле по всем индикаторам коллекции
      int total=this.m_list.Total();
      for(int i=0;i<total;i++)
        {
         //--- получаем указатель на очередной объект-индикатор
         //--- и вызываем его таймер
         CIndMSTF *obj=this.m_list.At(i);
         if(obj!=NULL)
            obj.OnTimer();
        }
     }
//--- Конструктор/Деструктор
                     CMSTFIndicators(void){ this.m_list.Clear(); }
                    ~CMSTFIndicators(void){;}
  };

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

//+------------------------------------------------------------------+
//| Возвращает данные индикатора по хэндлу                           |
//| из указанного буфера по индексу как есть                         |
//+------------------------------------------------------------------+
double CMSTFIndicators::GetData(const int ind_handle,const uint buffer_num,const uint array_num,const uint index)
  {
//--- Получаем указатель на объект-индикатор по хэндлу, переданному в метод
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
   if(obj==NULL)
     {
      ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__);
      return EMPTY_VALUE;
     }
//--- Возвращаем данные из указанного буфера индикатора по индексу, переданному в метод
   return obj.GetData(buffer_num,array_num,index);
  }

Реализация вновь объявленных методов:

//+------------------------------------------------------------------+
//| Возвращает данные индексов цвета индикатора по хэндлу            |
//| из указанного буфера по индексу как есть                         |
//+------------------------------------------------------------------+
double CMSTFIndicators::GetColorData(const int ind_handle,const uint buffer_num,const uint index)
  {
//--- Получаем указатель на объект-индикатор по хэндлу, переданному в метод
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
   if(obj==NULL)
     {
      ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__);
      return EMPTY_VALUE;
     }
//--- Возвращаем данные индекса цвета из указанного буфера индикатора по индексу, переданному в метод
   return obj.GetColorData(buffer_num,index);
  }
//+------------------------------------------------------------------+
//| Возвращает данные индексов цвета индикатора по хэндлу            |
//| из указанного буфера по индексу на указанный символ/таймфрейм    |
//+------------------------------------------------------------------+
double CMSTFIndicators::GetColorDataTo(const string symbol_to,const ENUM_TIMEFRAMES timeframe_to,const int ind_handle,const uint buffer_num,const uint index)
  {
//--- Получаем указатель на объект-индикатор по хэндлу, переданному в метод
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
   if(obj==NULL)
     {
      ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__);
      return EMPTY_VALUE;
     }
//--- Возвращаем данные индекса цвета из указанного буфера индикатора по индексу, переданному в метод
   return obj.GetColorDataTo(symbol_to,timeframe_to,buffer_num,index);
  }
//+------------------------------------------------------------------+
//| Заполняет переданный цветной буфер индикатора данными            |
//+------------------------------------------------------------------+
bool CMSTFIndicators::DataToColorBuffer(const string symbol_to,const ENUM_TIMEFRAMES timeframe_to,const int ind_handle,const uint buffer_num,const uint array_num,const int limit,double &plot_buffer[],double &color_buffer[])
  {
//--- Получаем указатель на объект-индикатор по хэндлу, переданному в метод
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
   if(obj==NULL)
     {
      ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__);
      return false;
     }
//--- Заполняем переданный в метод цветной массив-буфер из указанного цветного буфера индикатора
   return obj.DataToColorBuffer(symbol_to,timeframe_to,buffer_num,array_num,limit,plot_buffer,color_buffer);
  }
//+------------------------------------------------------------------+
//| Устанавливает цвета линии буфера индикатора                      |
//+------------------------------------------------------------------+
void CMSTFIndicators::SetPlotColorsFromBuffer(const uint plot_index,const int ind_handle,const uint buffer_num)
  {
//--- Получаем указатель на объект-индикатор по хэндлу, переданному в метод
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
   if(obj==NULL)
     {
      ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__);
      return;
     }
//--- Устанавливаем в указанный рисуемый буфер цвета указанного цветного буфера индикатора
   uint colors=obj.ColorsTotal(buffer_num);
   if(colors==0)
      return;
   ::PlotIndexSetInteger(plot_index,PLOT_COLOR_INDEXES,colors);
   for(int i=0;i<(int)colors;i++)
      ::PlotIndexSetInteger(plot_index,PLOT_LINE_COLOR,i,obj.BufferColorByIndex(buffer_num,(uchar)i));
  }
//+------------------------------------------------------------------+
//| Устанавливает инициализирующее значение индекса цвета            |
//| для указанного буфера указанного по хэндлу индикатора            |
//+------------------------------------------------------------------+
void CMSTFIndicators::SetBufferInitColorIndex(const int ind_handle,const uint buffer_num,const uchar index)
  {
//--- Получаем указатель на объект-индикатор по хэндлу, переданному в метод
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
   if(obj==NULL)
     {
      ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__);
      return;
     }
//--- Устанавливаем для указанного буфера инициализирующее значение индекса цвета
   obj.SetBufferInitColorIndex(buffer_num,index);
  }
//+------------------------------------------------------------------+
//| Возвращает инициализирующее значение индекса цвета               |
//| для указанного буфера указанного по хэндлу индикатора            |
//+------------------------------------------------------------------+
uchar CMSTFIndicators::BufferInitColorIndex(const int ind_handle,const uint buffer_num) const
  {
//--- Получаем указатель на объект-индикатор по хэндлу, переданному в метод
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
   if(obj==NULL)
     {
      ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__);
      return WRONG_VALUE;
     }
//--- Возвращаем инициализирующее значение индекса цвета, установленное для указанного буфера
   return obj.BufferInitColorIndex(buffer_num);
  }
//+------------------------------------------------------------------+
//| Устанавливает значение цвета по индексу для указанного буфера    |
//| указанного по хэндлу индикатора                                  |
//+------------------------------------------------------------------+
void CMSTFIndicators::SetBufferColorToIndex(const int ind_handle,const uint buffer_num,const uchar color_idx,const color clr)
  {
//--- Получаем указатель на объект-индикатор по хэндлу, переданному в метод
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
   if(obj==NULL)
     {
      ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__);
      return;
     }
//--- Устанавливаем для указанного буфера значение цвета по индексу
   obj.SetBufferColorToIndex(buffer_num,color_idx,clr);
  }
//+------------------------------------------------------------------+
//| Возвращает значение цвета по индексу для указанного буфера       |
//| указанного по хэндлу индикатора                                  |
//+------------------------------------------------------------------+
color CMSTFIndicators::BufferColorByIndex(const int ind_handle,const uint buffer_num,const uchar color_idx) const
  {
//--- Получаем указатель на объект-индикатор по хэндлу, переданному в метод
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
   if(obj==NULL)
     {
      ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__);
      return WRONG_VALUE;
     }
//--- Возвращаем значение цвета по индексу, установленное для указанного буфера
   return obj.BufferColorByIndex(buffer_num,color_idx);
  }
//+------------------------------------------------------------------+
//| Возвращает флаг цветности указанного буфера                      |
//| указанного по хэндлу индикатора                                  |
//+------------------------------------------------------------------+
bool CMSTFIndicators::IsColoredBuffer(const int ind_handle,const uint buffer_num) const
  {
//--- Получаем указатель на объект-индикатор по хэндлу, переданному в метод
   CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__);
   if(obj==NULL)
     {
      ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__);
      return false;
     }
//--- Возвращаем значение флаг цветности, установленный для указанного буфера
   return obj.IsColoredBuffer(buffer_num);
  }

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

//+------------------------------------------------------------------+
//| Добавляет в коллекцию индикатор Accelerator Oscillator           |
//+------------------------------------------------------------------+
int CMSTFIndicators::AddNewAC(const string symbol,const ENUM_TIMEFRAMES timeframe)
  {
//--- Создаём новый объект индикатора. При ошибке сообщаем об этом в журнал и возвращаем INVALID_HANDLE
   CIndAC *ind_obj=new CIndAC(symbol,timeframe);
   if(ind_obj==NULL)
     {
      ::PrintFormat("%s: Error. Failed to create AC indicator object",__FUNCTION__);
      return INVALID_HANDLE;
     }
//--- Получаем результат добавления созданного объекта-индикатора в список коллекцию (хэндл индикатора)
   int handle=this.AddNewIndicator(ind_obj,__FUNCTION__);
//--- Если индикатор успешно создан и добавлен в коллекцию, установим параметры его отображения
   if(handle!=INVALID_HANDLE && ::CheckPointer(ind_obj)!=POINTER_INVALID)
     {
      ::IndicatorSetInteger(INDICATOR_DIGITS,ind_obj.Digits());
      ind_obj.SetBufferInitValue(0,EMPTY_VALUE);
      ind_obj.SetBufferInitColorIndex(0,0);
     }
//--- Возвращаем хэндл созданного индикатора, либо INVALID_HANDLE
   return handle;
  }

Где необходимо, также устанавливается количество уровней в индикаторе и их значения:

//+------------------------------------------------------------------+
//| Добавляет в коллекцию индикатор Commodity Channel Index          |
//+------------------------------------------------------------------+
int CMSTFIndicators::AddNewCCI(const string symbol,const ENUM_TIMEFRAMES timeframe,
                               const int ma_period=14,
                               const ENUM_APPLIED_PRICE applied_price=PRICE_TYPICAL)
  {
//--- Создаём новый объект индикатора. При ошибке сообщаем об этом в журнал и возвращаем INVALID_HANDLE
   CIndCCI *ind_obj=new CIndCCI(symbol,timeframe,ma_period,applied_price);
   if(ind_obj==NULL)
     {
      ::PrintFormat("%s: Error. Failed to create CCI indicator object",__FUNCTION__);
      return INVALID_HANDLE;
     }
//--- Получаем результат добавления созданного объекта-индикатора в список коллекцию (хэндл индикатора)
   int handle=this.AddNewIndicator(ind_obj,__FUNCTION__);
//--- Если индикатор успешно создан и добавлен в коллекцию, установим параметры его отображения
   if(handle!=INVALID_HANDLE && ::CheckPointer(ind_obj)!=POINTER_INVALID)
     {
      ::IndicatorSetInteger(INDICATOR_LEVELS,2);
      ::IndicatorSetDouble(INDICATOR_LEVELVALUE,0,-100.0);
      ::IndicatorSetDouble(INDICATOR_LEVELVALUE,1, 100.0);
      ::IndicatorSetInteger(INDICATOR_DIGITS,ind_obj.Digits());
      ind_obj.SetBufferInitValue(0,EMPTY_VALUE);
     }
//--- Возвращаем хэндл созданного индикатора, либо INVALID_HANDLE
   return handle;
  }

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

Все доработки в библиотеку мультисимвольных мультипериодных индикаторов внесены. Полностью все исправления можно посмотреть в прикреплённом к статье файле IndMSTF.mqh.


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

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

Файл класса панели прикреплён к статье и должен располагаться по пути \MQL5\Include\Dashboard\Dashboard.mqh.

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

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

Индикатор Accumulation/Distribution:

//+------------------------------------------------------------------+
//|                                      TestMSTFAccDistribution.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_separate_window
#property indicator_buffers 1
#property indicator_plots   1
//--- enums

//--- plot MSTF A/D
#property indicator_label1  "MSTF A/D"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrDodgerBlue
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1

//--- includes
#include <IndMSTF\IndMSTF.mqh>
#include <Dashboard\Dashboard.mqh>
//--- input parameters
input string               InpSymbol      =  NULL;             /* Symbol                     */ // Символ
input ENUM_TIMEFRAMES      InpTimeframe   =  PERIOD_CURRENT;   /* Timeframe                  */ // Таймфрейм
input ENUM_APPLIED_VOLUME  InpVolume      =  VOLUME_TICK;      /* Applied Volume             */ // Используемый объём для расчёта
input uchar                InpLineWidth1  =  2;                /* Senior period Line Width   */ // Толщина линии для старшего периода
input uchar                InpLineWidth2  =  1;                /* Junior period Line Width   */ // Толщина линии для младшего периода
input bool                 InpAsSeries    =  true;             /* As Series flag             */ // Флаг серийности массивов буферов индикатора

//--- indicator buffers
double         BufferAD[];
//--- global variables
int handle_ad;
CMSTFIndicators indicators;      // Экземпляр объекта коллекции индикаторов
//--- переменные для панели
CDashboard *panel=NULL;          // Указатель на объект панели
int         mouse_bar_index;     // Индекс бара, с которого берутся данные
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Устанавливаем таймер с периодичностью в 1 секунду
   EventSetTimer(1);
//--- Назначаем рисуемым буферам 0 и 1 массивы BufferMA1 и BufferMA2 соответственно
   SetIndexBuffer(0,BufferAD,INDICATOR_DATA);
//--- Устанавливаем ширину линий
   int w1=0,w2=0;
   if(InpTimeframe>Period())
     {
      w1=InpLineWidth2;
      w2=InpLineWidth1;
     }
   else
     {
      w1=InpLineWidth1;
      w2=InpLineWidth2;
     }
   PlotIndexSetInteger(0,PLOT_LINE_WIDTH,w1);
   PlotIndexSetInteger(1,PLOT_LINE_WIDTH,w2);
//--- sets indicator shift

//--- Устанавливаем флаги серийности массивам буферов индикатора (для теста, чтобы видно было отсутствие разницы)
   ArraySetAsSeries(BufferAD,InpAsSeries);
   
//--- Создаём два индикатора одного типа
//--- Первый рассчитывается на текущих символе/периоде графика, а второй - на тех, что заданы в настройках
   handle_ad=indicators.AddNewAD(InpSymbol,InpTimeframe,InpVolume);

//--- Если не удалось создать хэндлы индикаторов - возвращаем ошибку инициализации
   if(handle_ad==INVALID_HANDLE)
      return INIT_FAILED;
//--- Устанавливаем описания линий индикатора из описаний буферов расчётной части созданных индикаторов
   indicators.SetPlotLabelFromBuffer(0,handle_ad,0);
      
//--- Панель
//--- Создаём панель
   int width=220;
   panel=new CDashboard(1,20,20,width,264,0);
   if(panel==NULL)
     {
      Print("Error. Failed to create panel object");
      return INIT_FAILED;
     }
//--- Устанавливаем параметры шрифта
   panel.SetFontParams("Calibri",9);
//--- Отображаем панель с текстом в заголовке "Символ, Описание таймфрейма"
   panel.View(Symbol()+", "+StringSubstr(EnumToString(Period()),7));

//--- Создаём таблицу с идентификатором 0 для отображения в ней данных бара
   panel.CreateNewTable(0);
//--- Рисуем таблицу с идентификатором 0 на фоне панели
   panel.DrawGrid(0,2,20,6,2,18,width/2-2);

//--- Создаём таблицу с идентификатором 1 для отображения в ней данных индикатора 1
   panel.CreateNewTable(1);
//--- Получаем координату Y2 таблицы с идентификатором 0 и
//--- устанавливаем координату Y1 для таблицы с идентификатором 1
   int y1=panel.TableY2(0)+22;
//--- Рисуем таблицу с идентификатором 1 на фоне панели
   panel.DrawGrid(1,2,y1,2,2,18,width/2-2);

//--- Создаём таблицу с идентификатором 2 для отображения в ней данных индикатора 2
   panel.CreateNewTable(2);
//--- Получаем координату Y2 таблицы с идентификатором 1 и
//--- устанавливаем координату Y1 для таблицы с идентификатором 2
   int y2=panel.TableY2(1)+3;
//--- Рисуем таблицу с идентификатором 2 на фоне панели
   panel.DrawGrid(2,2,y2,3,2,18,width/2-2);
   
//--- Инициализируем переменную с индексом бара указателя мышки
   mouse_bar_index=0;
//--- Выводим на панель данные текущего бара
   DrawData(mouse_bar_index,TimeCurrent());

//--- Успешная инициализация
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- Уничтожаем таймер
   EventKillTimer();
//--- Если объект панели существует - удаляем его
   if(panel!=NULL)
      delete panel;
//--- Стираем все комментарии
   Comment("");
  }
//+------------------------------------------------------------------+
//| 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[])
  {
//--- Количество баров для расчёта
   int limit=rates_total-prev_calculated;
//--- Если limit > 1, значит это первый расчёт, либо изменение в истории
   if(limit>1)
     {
      //--- указываем для просчёта всю доступную историю
      limit=rates_total-1;
      /*
      // Если в индикаторе есть какие-либо буферы, в которых отображены другие расчёты (не мульти- индикаторы),
      // то здесь их нужно инициализировать "пустым" значением, установленным для этих буферов
      */
     }
//--- Рассчитываем все созданные мультисимвольные мультипериодные индикаторы
   if(!indicators.Calculate())
      return 0;

//--- Выводим на панель данные бара под курсором (либо текущий бар, если курсор за пределами графика)
   DrawData(mouse_bar_index,time[mouse_bar_index]);

//--- Из буферов рассчитанных индикаторов выводим данные в индикаторные буферы
   if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_ad,0,0,limit,BufferAD))
      return 0;

//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| Timer function                                                   | 
//+------------------------------------------------------------------+
void OnTimer()
  {
//--- Вызываем таймер коллекции индикаторов
   indicators.OnTimer();
  }
//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- Работа с панелью
//--- Вызываем обработчик событий панели
   panel.OnChartEvent(id,lparam,dparam,sparam);

//--- Если курсор перемещается или щелчок по графику
   if(id==CHARTEVENT_MOUSE_MOVE || id==CHARTEVENT_CLICK)
     {
      //--- Объявляем переменные для записи в них координат времени и цены
      datetime time=0;
      double price=0;
      int wnd=0;
      //--- Если координаты курсора преобразованы в дату и время
      if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price))
        {
         //--- записываем индекс бара, где расположен курсор в глобальную переменную
         mouse_bar_index=iBarShift(Symbol(),PERIOD_CURRENT,time);
         //--- Выводим данные бара под курсором на панель
         DrawData(mouse_bar_index,time);
        }
     }

//--- Если получили пользовательское событие - выводим об этом сообщение в журнал
   if(id>CHARTEVENT_CUSTOM)
     {
      //--- Здесь может быть обработка щелчка по кнопке закрытия на панели
      PrintFormat("%s: Event id=%ld, object id (lparam): %lu, event message (sparam): %s",__FUNCTION__,id,lparam,sparam);
     }
  }
//+------------------------------------------------------------------+
//| Выводит данные с указанного индекса таймсерии на панель          |
//+------------------------------------------------------------------+
void DrawData(const int index,const datetime time)
  {
//--- Объявляем переменные для получения в них данных
   MqlRates rates[1];

//--- Если данные бара по указанному индексу получить не удалось - уходим
   if(CopyRates(Symbol(),PERIOD_CURRENT,index,1,rates)!=1)
      return;

//--- Устанавливаем параметры шрифта для заголовков данных бара и индикатора
   int  size=0;
   uint flags=0;
   uint angle=0;
   string name=panel.FontParams(size,flags,angle);
   panel.SetFontParams(name,9,FW_BOLD);
   panel.DrawText("Bar data ["+(string)index+"]",3,panel.TableY1(0)-16,clrMaroon,panel.Width()-6);
   panel.DrawText("Indicators data ["+(string)index+"]",3,panel.TableY1(1)-16,clrGreen,panel.Width()-6);
//--- Устанавливаем параметры шрифта для данных бара и индикатора
   panel.SetFontParams(name,9);

//--- Выводим на панель данные указанного бара в таблицу 0
   panel.DrawText("Date",  panel.CellX(0,0,0)+2, panel.CellY(0,0,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_DATE),     panel.CellX(0,0,1)+2, panel.CellY(0,0,1)+2,clrNONE,90);
   panel.DrawText("Time",  panel.CellX(0,1,0)+2, panel.CellY(0,1,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_MINUTES),  panel.CellX(0,1,1)+2, panel.CellY(0,1,1)+2,clrNONE,90);
   panel.DrawText("Open",  panel.CellX(0,2,0)+2, panel.CellY(0,2,0)+2); panel.DrawText(DoubleToString(rates[0].open,Digits()),      panel.CellX(0,2,1)+2, panel.CellY(0,2,1)+2,clrNONE,90);
   panel.DrawText("High",  panel.CellX(0,3,0)+2, panel.CellY(0,3,0)+2); panel.DrawText(DoubleToString(rates[0].high,Digits()),      panel.CellX(0,3,1)+2, panel.CellY(0,3,1)+2,clrNONE,90);
   panel.DrawText("Low",   panel.CellX(0,4,0)+2, panel.CellY(0,4,0)+2); panel.DrawText(DoubleToString(rates[0].low,Digits()),       panel.CellX(0,4,1)+2, panel.CellY(0,4,1)+2,clrNONE,90);
   panel.DrawText("Close", panel.CellX(0,5,0)+2, panel.CellY(0,5,0)+2); panel.DrawText(DoubleToString(rates[0].close,Digits()),     panel.CellX(0,5,1)+2, panel.CellY(0,5,1)+2,clrNONE,90);

//--- Выводим в таблицу 1 данные индикатора 1 с указанного бара в таблицу 1
   panel.DrawText(indicators.Title(handle_ad), panel.CellX(1,0,0)+2, panel.CellY(1,0,0)+2);
   double value1=indicators.GetData(handle_ad,0,0,index);
   string value_str1=(value1!=EMPTY_VALUE ? DoubleToString(value1,indicators.Digits(handle_ad)) : " ");
   panel.DrawText(value_str1,panel.CellX(1,0,1)+2,panel.CellY(1,0,1)+2,clrNONE,110);
   
//--- Выводим описание состояния линии индикатора 1
   panel.DrawText("Line state", panel.CellX(1,1,0)+2, panel.CellY(1,1,0)+2);
   ENUM_LINE_STATE state1=indicators.BufferLineState(Symbol(),PERIOD_CURRENT,handle_ad,0,0,index);
   panel.DrawText(BufferLineStateDescription(state1),panel.CellX(1,1,1)+2,panel.CellY(1,1,1)+2,clrNONE,110);
   
//--- Выводим в таблицу 2 данные индикатора 2 с указанного бара в таблицу 2
   panel.DrawText(indicators.Title(handle_ad), panel.CellX(2,0,0)+2, panel.CellY(2,0,0)+2);
   double value2=indicators.GetDataTo(Symbol(),PERIOD_CURRENT,handle_ad,0,0,index);
   string value_str2=(value2!=EMPTY_VALUE ? DoubleToString(value2,indicators.Digits(handle_ad)) : " ");
   panel.DrawText(value_str2,panel.CellX(2,0,1)+2,panel.CellY(2,0,1)+2,clrNONE,110);
   
//--- Выводим описание состояния линии индикатора 2
   panel.DrawText("Line state", panel.CellX(2,1,0)+2, panel.CellY(2,1,0)+2);
   ENUM_LINE_STATE state2=indicators.BufferLineState(Symbol(),PERIOD_CURRENT,handle_ad,0,0,index);
   panel.DrawText(BufferLineStateDescription(state2),panel.CellX(2,1,1)+2,panel.CellY(2,1,1)+2,clrNONE,110);
   
//--- Перерисовываем график для немедленного отображения всех изменений на панели
   ChartRedraw(ChartID());
  }
//+------------------------------------------------------------------+

После компиляции и запуска индикатора на графике М1 и выбранном периоде графика для расчёта М5, видим:



Индикатор Accelerator Oscillator:

//+------------------------------------------------------------------+
//|                                TestMSTFAcceleratorOscillator.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_separate_window
#property indicator_buffers 4
#property indicator_plots   2
//--- enums

//--- plot AC1
#property indicator_label1  "AC1"
#property indicator_type1   DRAW_COLOR_HISTOGRAM
#property indicator_color1  clrGreen,clrRed
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1

//--- plot AC2
#property indicator_label2  "AC2"
#property indicator_type2   DRAW_COLOR_HISTOGRAM
#property indicator_color2  clrGreen,clrRed
#property indicator_style2  STYLE_SOLID
#property indicator_width2  1

//--- includes
#include <IndMSTF\IndMSTF.mqh>
#include <Dashboard\Dashboard.mqh>
//--- input parameters
input string               InpSymbol      =  NULL;             /* Symbol                     */ // Символ
input ENUM_TIMEFRAMES      InpTimeframe   =  PERIOD_CURRENT;   /* Timeframe                  */ // Таймфрейм
input uchar                InpLineWidth1  =  2;                /* Senior period Line Width   */ // Толщина линии для старшего периода
input uchar                InpLineWidth2  =  1;                /* Junior period Line Width   */ // Толщина линии для младшего периода
input bool                 InpAsSeries    =  true;             /* As Series flag             */ // Флаг серийности массивов буферов индикатора

//--- indicator buffers
double         BufferAC1[];
double         BufferClrAC1[];
double         BufferAC2[];
double         BufferClrAC2[];
//--- global variables
int handle_ac1;
int handle_ac2;
CMSTFIndicators indicators;      // Экземпляр объекта коллекции индикаторов
//--- переменные для панели
CDashboard *panel=NULL;          // Указатель на объект панели
int         mouse_bar_index;     // Индекс бара, с которого берутся данные
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Устанавливаем таймер с периодичностью в 1 секунду
   EventSetTimer(1);
//--- Назначаем рисуемым буферам 0 и 2 массивы BufferAC1 и BufferAC2 соответственно,
//--- а буферам 1 и 3 массивы цвета BufferClrAC1 и BufferClrAC2
   SetIndexBuffer(0,BufferAC1,INDICATOR_DATA);
   SetIndexBuffer(1,BufferClrAC1,INDICATOR_COLOR_INDEX);
   SetIndexBuffer(2,BufferAC2,INDICATOR_DATA);
   SetIndexBuffer(3,BufferClrAC2,INDICATOR_COLOR_INDEX);
//--- Устанавливаем ширину линий
   int w1=0,w2=0;
   if(InpTimeframe>Period())
     {
      w1=InpLineWidth2;
      w2=InpLineWidth1;
     }
   else
     {
      w1=InpLineWidth1;
      w2=InpLineWidth2;
     }
   PlotIndexSetInteger(0,PLOT_LINE_WIDTH,w1);
   PlotIndexSetInteger(1,PLOT_LINE_WIDTH,w2);
//--- sets indicator shift

//--- Устанавливаем флаги серийности массивам буферов индикатора (для теста, чтобы видно было отсутствие разницы)
   ArraySetAsSeries(BufferAC1,InpAsSeries);
   ArraySetAsSeries(BufferClrAC1,InpAsSeries);
   ArraySetAsSeries(BufferAC2,InpAsSeries);
   ArraySetAsSeries(BufferClrAC2,InpAsSeries);
   
//--- Создаём два индикатора одного типа
//--- Первый рассчитывается на текущих символе/периоде графика, а второй - на тех, что заданы в настройках
   handle_ac1=indicators.AddNewAC(NULL,PERIOD_CURRENT);
   handle_ac2=indicators.AddNewAC(InpSymbol,InpTimeframe);

//--- Если не удалось создать хэндлы индикаторов - возвращаем ошибку инициализации
   if(handle_ac1==INVALID_HANDLE || handle_ac2==INVALID_HANDLE)
      return INIT_FAILED;
//--- Устанавливаем описания линий индикатора из описаний буферов расчётной части созданных индикаторов
   indicators.SetPlotLabelFromBuffer(0,handle_ac1,0);
   indicators.SetPlotLabelFromBuffer(1,handle_ac2,0);
      
//--- Панель
//--- Создаём панель
   int width=247;
   panel=new CDashboard(1,20,20,width,264,0);
   if(panel==NULL)
     {
      Print("Error. Failed to create panel object");
      return INIT_FAILED;
     }
//--- Устанавливаем параметры шрифта
   panel.SetFontParams("Calibri",9);
//--- Отображаем панель с текстом в заголовке "Символ, Описание таймфрейма"
   panel.View(Symbol()+", "+StringSubstr(EnumToString(Period()),7));

//--- Создаём таблицу с идентификатором 0 для отображения в ней данных бара
   panel.CreateNewTable(0);
//--- Рисуем таблицу с идентификатором 0 на фоне панели
   panel.DrawGrid(0,2,20,6,2,18,width/2-2);

//--- Создаём таблицу с идентификатором 1 для отображения в ней данных индикатора 1
   panel.CreateNewTable(1);
//--- Получаем координату Y2 таблицы с идентификатором 0 и
//--- устанавливаем координату Y1 для таблицы с идентификатором 1
   int y1=panel.TableY2(0)+22;
//--- Рисуем таблицу с идентификатором 1 на фоне панели
   panel.DrawGrid(1,2,y1,2,2,18,width/2-2);

//--- Создаём таблицу с идентификатором 2 для отображения в ней данных индикатора 2
   panel.CreateNewTable(2);
//--- Получаем координату Y2 таблицы с идентификатором 1 и
//--- устанавливаем координату Y1 для таблицы с идентификатором 2
   int y2=panel.TableY2(1)+3;
//--- Рисуем таблицу с идентификатором 2 на фоне панели
   panel.DrawGrid(2,2,y2,3,2,18,width/2-2);
   
//--- Инициализируем переменную с индексом бара указателя мышки
   mouse_bar_index=0;
//--- Выводим на панель данные текущего бара
   DrawData(mouse_bar_index,TimeCurrent());

//--- Успешная инициализация
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- Уничтожаем таймер
   EventKillTimer();
//--- Если объект панели существует - удаляем его
   if(panel!=NULL)
      delete panel;
//--- Стираем все комментарии
   Comment("");
  }
//+------------------------------------------------------------------+
//| 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[])
  {
//--- Количество баров для расчёта
   int limit=rates_total-prev_calculated;
//--- Если limit > 1, значит это первый расчёт, либо изменение в истории
   if(limit>1)
     {
      //--- указываем для просчёта всю доступную историю
      limit=rates_total-1;
      /*
      // Если в индикаторе есть какие-либо буферы, в которых отображены другие расчёты (не мульти- индикаторы),
      // то здесь их нужно инициализировать "пустым" значением, установленным для этих буферов
      */
     }
//--- Рассчитываем все созданные мультисимвольные мультипериодные индикаторы
   if(!indicators.Calculate())
      return 0;

//--- Выводим на панель данные бара под курсором (либо текущий бар, если курсор за пределами графика)
   DrawData(mouse_bar_index,time[mouse_bar_index]);

//--- Из буферов рассчитанных индикаторов выводим данные в индикаторные буферы
   if(!indicators.DataToColorBuffer(NULL,PERIOD_CURRENT,handle_ac1,0,0,limit,BufferAC1,BufferClrAC1))
      return 0;
   if(!indicators.DataToColorBuffer(NULL,PERIOD_CURRENT,handle_ac2,0,0,limit,BufferAC2,BufferClrAC2))
      return 0;

//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| Timer function                                                   | 
//+------------------------------------------------------------------+
void OnTimer()
  {
//--- Вызываем таймер коллекции индикаторов
   indicators.OnTimer();
  }
//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- Работа с панелью
//--- Вызываем обработчик событий панели
   panel.OnChartEvent(id,lparam,dparam,sparam);

//--- Если курсор перемещается или щелчок по графику
   if(id==CHARTEVENT_MOUSE_MOVE || id==CHARTEVENT_CLICK)
     {
      //--- Объявляем переменные для записи в них координат времени и цены
      datetime time=0;
      double price=0;
      int wnd=0;
      //--- Если координаты курсора преобразованы в дату и время
      if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price))
        {
         //--- записываем индекс бара, где расположен курсор в глобальную переменную
         mouse_bar_index=iBarShift(Symbol(),PERIOD_CURRENT,time);
         //--- Выводим данные бара под курсором на панель
         DrawData(mouse_bar_index,time);
        }
     }

//--- Если получили пользовательское событие - выводим об этом сообщение в журнал
   if(id>CHARTEVENT_CUSTOM)
     {
      //--- Здесь может быть обработка щелчка по кнопке закрытия на панели
      PrintFormat("%s: Event id=%ld, object id (lparam): %lu, event message (sparam): %s",__FUNCTION__,id,lparam,sparam);
     }
  }
//+------------------------------------------------------------------+
//| Выводит данные с указанного индекса таймсерии на панель          |
//+------------------------------------------------------------------+
void DrawData(const int index,const datetime time)
  {
//--- Объявляем переменные для получения в них данных
   MqlRates rates[1];

//--- Если данные бара по указанному индексу получить не удалось - уходим
   if(CopyRates(Symbol(),PERIOD_CURRENT,index,1,rates)!=1)
      return;

//--- Устанавливаем параметры шрифта для заголовков данных бара и индикатора
   int  size=0;
   uint flags=0;
   uint angle=0;
   string name=panel.FontParams(size,flags,angle);
   panel.SetFontParams(name,9,FW_BOLD);
   panel.DrawText("Bar data ["+(string)index+"]",3,panel.TableY1(0)-16,clrMaroon,panel.Width()-6);
   panel.DrawText("Indicators data ["+(string)index+"]",3,panel.TableY1(1)-16,clrGreen,panel.Width()-6);
//--- Устанавливаем параметры шрифта для данных бара и индикатора
   panel.SetFontParams(name,9);

//--- Выводим на панель данные указанного бара в таблицу 0
   panel.DrawText("Date",  panel.CellX(0,0,0)+2, panel.CellY(0,0,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_DATE),     panel.CellX(0,0,1)+2, panel.CellY(0,0,1)+2,clrNONE,90);
   panel.DrawText("Time",  panel.CellX(0,1,0)+2, panel.CellY(0,1,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_MINUTES),  panel.CellX(0,1,1)+2, panel.CellY(0,1,1)+2,clrNONE,90);
   panel.DrawText("Open",  panel.CellX(0,2,0)+2, panel.CellY(0,2,0)+2); panel.DrawText(DoubleToString(rates[0].open,Digits()),      panel.CellX(0,2,1)+2, panel.CellY(0,2,1)+2,clrNONE,90);
   panel.DrawText("High",  panel.CellX(0,3,0)+2, panel.CellY(0,3,0)+2); panel.DrawText(DoubleToString(rates[0].high,Digits()),      panel.CellX(0,3,1)+2, panel.CellY(0,3,1)+2,clrNONE,90);
   panel.DrawText("Low",   panel.CellX(0,4,0)+2, panel.CellY(0,4,0)+2); panel.DrawText(DoubleToString(rates[0].low,Digits()),       panel.CellX(0,4,1)+2, panel.CellY(0,4,1)+2,clrNONE,90);
   panel.DrawText("Close", panel.CellX(0,5,0)+2, panel.CellY(0,5,0)+2); panel.DrawText(DoubleToString(rates[0].close,Digits()),     panel.CellX(0,5,1)+2, panel.CellY(0,5,1)+2,clrNONE,90);

//--- Выводим в таблицу 1 данные индикатора 1 с указанного бара в таблицу 1
   panel.DrawText(indicators.Title(handle_ac1), panel.CellX(1,0,0)+2, panel.CellY(1,0,0)+2);
   double value1=indicators.GetData(handle_ac1,0,0,index);
   string value_str1=(value1!=EMPTY_VALUE ? DoubleToString(value1,indicators.Digits(handle_ac1)) : " ");
   panel.DrawText(value_str1,panel.CellX(1,0,1)+2,panel.CellY(1,0,1)+2,clrNONE,110);
   
//--- Выводим описание состояния линии индикатора 1
   panel.DrawText("Line state", panel.CellX(1,1,0)+2, panel.CellY(1,1,0)+2);
   ENUM_LINE_STATE state1=indicators.BufferLineState(Symbol(),PERIOD_CURRENT,handle_ac1,0,0,index);
   panel.DrawText(BufferLineStateDescription(state1),panel.CellX(1,1,1)+2,panel.CellY(1,1,1)+2,clrNONE,110);
   
//--- Выводим в таблицу 2 данные индикатора 2 с указанного бара в таблицу 2
   panel.DrawText(indicators.Title(handle_ac2), panel.CellX(2,0,0)+2, panel.CellY(2,0,0)+2);
   double value2=indicators.GetDataTo(Symbol(),PERIOD_CURRENT,handle_ac2,0,0,index);
   string value_str2=(value2!=EMPTY_VALUE ? DoubleToString(value2,indicators.Digits(handle_ac2)) : " ");
   panel.DrawText(value_str2,panel.CellX(2,0,1)+2,panel.CellY(2,0,1)+2,clrNONE,110);
   
//--- Выводим описание состояния линии индикатора 2
   panel.DrawText("Line state", panel.CellX(2,1,0)+2, panel.CellY(2,1,0)+2);
   ENUM_LINE_STATE state2=indicators.BufferLineState(Symbol(),PERIOD_CURRENT,handle_ac2,0,0,index);
   panel.DrawText(BufferLineStateDescription(state2),panel.CellX(2,1,1)+2,panel.CellY(2,1,1)+2,clrNONE,110);
   
//--- Выводим описание соотношения линии индикатора 1 относительно линии индикатора 2
   double value21=indicators.GetDataTo(Symbol(),PERIOD_CURRENT,handle_ac2,0,0,index+1);
   ENUM_LINE_STATE stateR=indicators.BufferLineStateRelative(Symbol(),PERIOD_CURRENT,handle_ac1,0,0,index,value2,value21);
   string ma1=indicators.Name(handle_ac1);
   string ma2=indicators.Name(handle_ac2);
   string state_relative=
     (
      stateR==LINE_STATE_ABOVE      ? StringFormat("%s1 > %s2",ma1,ma2)   :
      stateR==LINE_STATE_BELOW      ? StringFormat("%s1 < %s2",ma1,ma2)           :
      stateR==LINE_STATE_CROSS_DOWN ? "Top-down crossing"   :
      stateR==LINE_STATE_CROSS_UP   ? "Bottom-up crossing"  :
      BufferLineStateDescription(stateR)
     );
   panel.DrawText(StringFormat("%s1 vs %s2",ma1,ma2), panel.CellX(2,2,0)+2, panel.CellY(2,2,0)+2);
   panel.DrawText(state_relative, panel.CellX(2,2,1)+2, panel.CellY(2,2,1)+2,clrNONE,110);
   
//--- Перерисовываем график для немедленного отображения всех изменений на панели
   ChartRedraw(ChartID());
  }
//+------------------------------------------------------------------+

После компиляции и запуска индикатора на графике М1 и выбранном периоде графика для расчёта М5, видим:



Индикатор Alligator:

//+------------------------------------------------------------------+
//|                                        TestMSTFAlligator.mq5.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_chart_window
#property indicator_buffers 6
#property indicator_plots   6
//--- enums

//--- plot Jaws1
#property indicator_label1  "Jaws1"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrDodgerBlue
#property indicator_width1  1

//--- plot Teeth1
#property indicator_label2  "Teeth1"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrOrangeRed
#property indicator_width2  1

//--- plot Lips1
#property indicator_label3  "Lips1"
#property indicator_type3   DRAW_LINE
#property indicator_color3  clrLime
#property indicator_width3  1

//--- plot Jaws2
#property indicator_label4  "Jaws2"
#property indicator_type4  DRAW_LINE
#property indicator_color4  clrBlue
#property indicator_width4  1

//--- plot Teeth2
#property indicator_label5  "Teeth"
#property indicator_type5   DRAW_LINE
#property indicator_color5  clrRed
#property indicator_width5  1

//--- plot Lips2
#property indicator_label6  "Lips"
#property indicator_type6   DRAW_LINE
#property indicator_color6  clrLimeGreen
#property indicator_width6  1

//--- includes
#include <IndMSTF\IndMSTF.mqh>
#include <Dashboard\Dashboard.mqh>
//--- input parameters
input string               InpSymbol      =  NULL;             /* Symbol                     */ // Символ скользящей средней
input ENUM_TIMEFRAMES      InpTimeframe   =  PERIOD_CURRENT;   /* Timeframe                  */ // Таймфрейм скользящей средней
input int                  InpJawsPeriod  =  13;               /* Jaws Period                */ // Период расчёта челюстей
input int                  InpJawsShift   =  8;                /* Jaws Shift                 */ // Сдвиг челюстей
input int                  InpTeethPeriod =  8;                /* Teeth Period               */ // Период расчёта зубов
input int                  InpTeethShift  =  5;                /* Teeth Shift                */ // Сдвиг зубов
input int                  InpLipsPeriod  =  5;                /* Lips Period                */ // Период расчёта губ
input int                  InpLipsShift   =  3;                /* Lips Shift                 */ // Сдвиг губ
input ENUM_MA_METHOD       InpMethod      =  MODE_SMMA;        /* Method                     */ // Метод расчёта
input ENUM_APPLIED_PRICE   InpPrice       =  PRICE_MEDIAN;     /* Applied Price              */ // Используемая цена для расчёта
input uchar                InpLineWidth1  =  2;                /* Senior period Line Width   */ // Толщина линии для старшего периода
input uchar                InpLineWidth2  =  1;                /* Junior period Line Width   */ // Толщина линии для младшего периода
input bool                 InpAsSeries    =  true;             /* As Series flag             */ // Флаг серийности массивов буферов индикатора

//--- indicator buffers
double         BufferJaws1[];
double         BufferTeeth1[];
double         BufferLips1[];
double         BufferJaws2[];
double         BufferTeeth2[];
double         BufferLips2[];
//--- global variables
int handle_alligator1;
int handle_alligator2;
CMSTFIndicators indicators;      // Экземпляр объекта коллекции индикаторов
//--- переменные для панели
CDashboard *panel=NULL;          // Указатель на объект панели
int         mouse_bar_index;     // Индекс бара, с которого берутся данные
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Устанавливаем таймер с периодичностью в 1 секунду
   EventSetTimer(1);
//--- Назначаем рисуемым буферам 0, 1 и 2 массивы BufferBandsUp1, BufferBandsDn1 и BufferBandsMd1 соответственно
   SetIndexBuffer(0,BufferJaws1,INDICATOR_DATA);
   SetIndexBuffer(1,BufferTeeth1,INDICATOR_DATA);
   SetIndexBuffer(2,BufferLips1,INDICATOR_DATA);
//--- Назначаем рисуемым буферам 3, 4 и 5 массивы BufferBandsUp2, BufferBandsDn2 и BufferBandsMd2 соответственно
   SetIndexBuffer(3,BufferJaws2,INDICATOR_DATA);
   SetIndexBuffer(4,BufferTeeth2,INDICATOR_DATA);
   SetIndexBuffer(5,BufferLips2,INDICATOR_DATA);
//--- Устанавливаем ширину линий
   int w1=0,w2=0;
   if(InpTimeframe>Period())
     {
      w1=InpLineWidth2;
      w2=InpLineWidth1;
     }
   else
     {
      w1=InpLineWidth1;
      w2=InpLineWidth2;
     }
   PlotIndexSetInteger(0,PLOT_LINE_WIDTH,w1);
   PlotIndexSetInteger(1,PLOT_LINE_WIDTH,w1);
   PlotIndexSetInteger(2,PLOT_LINE_WIDTH,w1);
   PlotIndexSetInteger(3,PLOT_LINE_WIDTH,w2);
   PlotIndexSetInteger(4,PLOT_LINE_WIDTH,w2);
   PlotIndexSetInteger(5,PLOT_LINE_WIDTH,w2);
//--- Устанавливаем флаги серийности массивам буферов индикатора (для теста, чтобы видно было отсутствие разницы)
   ArraySetAsSeries(BufferJaws1,InpAsSeries);
   ArraySetAsSeries(BufferTeeth1,InpAsSeries);
   ArraySetAsSeries(BufferLips1,InpAsSeries);
   ArraySetAsSeries(BufferJaws2,InpAsSeries);
   ArraySetAsSeries(BufferTeeth2,InpAsSeries);
   ArraySetAsSeries(BufferLips2,InpAsSeries);
//--- Создаём два индикатора одного типа
//--- Первый рассчитывается на текущих символе/периоде графика, а второй - на тех, что заданы в настройках
   handle_alligator1=indicators.AddNewAlligator(NULL,PERIOD_CURRENT,InpJawsPeriod,InpJawsShift,InpTeethPeriod,InpTeethShift,InpLipsPeriod,InpLipsShift,InpMethod,InpPrice);
   handle_alligator2=indicators.AddNewAlligator(InpSymbol,InpTimeframe,InpJawsPeriod,InpJawsShift,InpTeethPeriod,InpTeethShift,InpLipsPeriod,InpLipsShift,InpMethod,InpPrice);

//--- Если не удалось создать хэндлы индикаторов - возвращаем ошибку инициализации
   if(handle_alligator1==INVALID_HANDLE || handle_alligator2==INVALID_HANDLE)
      return INIT_FAILED;
//--- Устанавливаем описания линий индикатора из описаний буферов расчётной части созданных индикаторов
   indicators.SetPlotLabelFromBuffer(0,handle_alligator1,0);
   indicators.SetPlotLabelFromBuffer(1,handle_alligator1,1);
   indicators.SetPlotLabelFromBuffer(2,handle_alligator1,2);
   indicators.SetPlotLabelFromBuffer(3,handle_alligator2,0);
   indicators.SetPlotLabelFromBuffer(4,handle_alligator2,1);
   indicators.SetPlotLabelFromBuffer(5,handle_alligator2,2);
//--- Устанавливаем смещения линиям индикатора
   indicators.SetPlotShift(0,InpJawsShift);
   indicators.SetPlotShift(1,InpTeethShift);
   indicators.SetPlotShift(2,InpLipsShift);
   indicators.SetPlotShift(3,InpJawsShift);
   indicators.SetPlotShift(4,InpTeethShift);
   indicators.SetPlotShift(5,InpLipsShift);

//--- Панель
//--- Создаём панель
   int width=321;
   panel=new CDashboard(1,20,20,width,264);
   if(panel==NULL)
     {
      Print("Error. Failed to create panel object");
      return INIT_FAILED;
     }
//--- Устанавливаем параметры шрифта
   panel.SetFontParams("Calibri",9);
//--- Отображаем панель с текстом в заголовке "Символ, Описание таймфрейма"
   panel.View(Symbol()+", "+StringSubstr(EnumToString(Period()),7));

//--- Создаём таблицу с идентификатором 0 для отображения в ней данных бара
   panel.CreateNewTable(0);
//--- Рисуем таблицу с идентификатором 0 на фоне панели
   panel.DrawGrid(0,2,20,6,2,18,width/2-2);

//--- Создаём таблицу с идентификатором 1 для отображения в ней данных индикатора 1
   panel.CreateNewTable(1);
//--- Получаем координату Y2 таблицы с идентификатором 0 и
//--- устанавливаем координату Y1 для таблицы с идентификатором 1
   int y1=panel.TableY2(0)+22;
//--- Рисуем таблицу с идентификатором 1 на фоне панели
   panel.DrawGrid(1,2,y1,2,2,18,width/2-2);

//--- Создаём таблицу с идентификатором 2 для отображения в ней данных индикатора 2
   panel.CreateNewTable(2);
//--- Получаем координату Y2 таблицы с идентификатором 1 и
//--- устанавливаем координату Y1 для таблицы с идентификатором 2
   int y2=panel.TableY2(1)+3;
//--- Рисуем таблицу с идентификатором 2 на фоне панели
   panel.DrawGrid(2,2,y2,3,2,18,width/2-2);
   
//--- Инициализируем переменную с индексом бара указателя мышки
   mouse_bar_index=0;
//--- Выводим на панель данные текущего бара
   DrawData(mouse_bar_index,TimeCurrent());

//--- Успешная инициализация
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- Уничтожаем таймер
   EventKillTimer();
//--- Если объект панели существует - удаляем его
   if(panel!=NULL)
      delete panel;
//--- Стираем все комментарии
   Comment("");
  }
//+------------------------------------------------------------------+
//| 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[])
  {
//--- Количество баров для расчёта
   int limit=rates_total-prev_calculated;
//--- Если limit > 1, значит это первый расчёт, либо изменение в истории
   if(limit>1)
     {
      //--- указываем для просчёта всю доступную историю
      limit=rates_total-1;
      /*
      // Если в индикаторе есть какие-либо буферы, в которых отображены другие расчёты (не мульти- индикаторы),
      // то здесь их нужно инициализировать "пустым" значением, установленным для этих буферов
      */
     }
//--- Рассчитываем все созданные мультисимвольные мультипериодные индикаторы
   if(!indicators.Calculate())
      return 0;

//--- Выводим на панель данные бара под курсором (либо текущий бар, если курсор за пределами графика)
   DrawData(mouse_bar_index,time[mouse_bar_index]);

//--- Из буферов рассчитанных индикаторов выводим данные в индикаторные буферы
   if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_alligator1,0,0,limit,BufferJaws1))
      return 0;
   if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_alligator1,1,0,limit,BufferTeeth1))
      return 0;
   if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_alligator1,2,0,limit,BufferLips1))
      return 0;
   
   if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_alligator2,0,0,limit,BufferJaws2))
      return 0;
   if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_alligator2,1,0,limit,BufferTeeth2))
      return 0;
   if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_alligator2,2,0,limit,BufferLips2))
      return 0;

//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| Timer function                                                   | 
//+------------------------------------------------------------------+
void OnTimer()
  {
//--- Вызываем таймер коллекции индикаторов
   indicators.OnTimer();
  }
//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- Работа с панелью
//--- Вызываем обработчик событий панели
   panel.OnChartEvent(id,lparam,dparam,sparam);

//--- Если курсор перемещается или щелчок по графику
   if(id==CHARTEVENT_MOUSE_MOVE || id==CHARTEVENT_CLICK)
     {
      //--- Объявляем переменные для записи в них координат времени и цены
      datetime time=0;
      double price=0;
      int wnd=0;
      //--- Если координаты курсора преобразованы в дату и время
      if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price))
        {
         //--- записываем индекс бара, где расположен курсор в глобальную переменную
         mouse_bar_index=iBarShift(Symbol(),PERIOD_CURRENT,time);
         //--- Выводим данные бара под курсором на панель
         DrawData(mouse_bar_index,time);
        }
     }

//--- Если получили пользовательское событие - выводим об этом сообщение в журнал
   if(id>CHARTEVENT_CUSTOM)
     {
      //--- Здесь может быть обработка щелчка по кнопке закрытия на панели
      PrintFormat("%s: Event id=%ld, object id (lparam): %lu, event message (sparam): %s",__FUNCTION__,id,lparam,sparam);
     }
  }
//+------------------------------------------------------------------+
//| Выводит данные с указанного индекса таймсерии на панель          |
//+------------------------------------------------------------------+
void DrawData(const int index,const datetime time)
  {
//--- Объявляем переменные для получения в них данных
   MqlRates rates[1];

//--- Если данные бара по указанному индексу получить не удалось - уходим
   if(CopyRates(Symbol(),PERIOD_CURRENT,index,1,rates)!=1)
      return;

//--- Устанавливаем параметры шрифта для заголовков данных бара и индикатора
   int  size=0;
   uint flags=0;
   uint angle=0;
   string name=panel.FontParams(size,flags,angle);
   panel.SetFontParams(name,9,FW_BOLD);
   panel.DrawText("Bar data ["+(string)index+"]",3,panel.TableY1(0)-16,clrMaroon,panel.Width()-6);
   panel.DrawText("Indicators data ["+(string)index+"]",3,panel.TableY1(1)-16,clrGreen,panel.Width()-6);
//--- Устанавливаем параметры шрифта для данных бара и индикатора
   panel.SetFontParams(name,9);

//--- Выводим на панель данные указанного бара в таблицу 0
   panel.DrawText("Date",  panel.CellX(0,0,0)+2, panel.CellY(0,0,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_DATE),     panel.CellX(0,0,1)+2, panel.CellY(0,0,1)+2,clrNONE,90);
   panel.DrawText("Time",  panel.CellX(0,1,0)+2, panel.CellY(0,1,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_MINUTES),  panel.CellX(0,1,1)+2, panel.CellY(0,1,1)+2,clrNONE,90);
   panel.DrawText("Open",  panel.CellX(0,2,0)+2, panel.CellY(0,2,0)+2); panel.DrawText(DoubleToString(rates[0].open,Digits()),      panel.CellX(0,2,1)+2, panel.CellY(0,2,1)+2,clrNONE,90);
   panel.DrawText("High",  panel.CellX(0,3,0)+2, panel.CellY(0,3,0)+2); panel.DrawText(DoubleToString(rates[0].high,Digits()),      panel.CellX(0,3,1)+2, panel.CellY(0,3,1)+2,clrNONE,90);
   panel.DrawText("Low",   panel.CellX(0,4,0)+2, panel.CellY(0,4,0)+2); panel.DrawText(DoubleToString(rates[0].low,Digits()),       panel.CellX(0,4,1)+2, panel.CellY(0,4,1)+2,clrNONE,90);
   panel.DrawText("Close", panel.CellX(0,5,0)+2, panel.CellY(0,5,0)+2); panel.DrawText(DoubleToString(rates[0].close,Digits()),     panel.CellX(0,5,1)+2, panel.CellY(0,5,1)+2,clrNONE,90);

//--- Выводим в таблицу 1 данные индикатора 1 с указанного бара в таблицу 1
   panel.DrawText(indicators.Title(handle_alligator1), panel.CellX(1,0,0)+2, panel.CellY(1,0,0)+2);
   double value1=indicators.GetData(handle_alligator1,0,0,index+InpJawsShift);
   string value_str1=(value1!=EMPTY_VALUE ? DoubleToString(value1,indicators.Digits(handle_alligator1)) : " ");
   panel.DrawText(value_str1,panel.CellX(1,0,1)+2,panel.CellY(1,0,1)+2,clrNONE,150);
   
//--- Выводим описание состояния линии индикатора 1
   panel.DrawText("Line state", panel.CellX(1,1,0)+2, panel.CellY(1,1,0)+2);
   ENUM_LINE_STATE state1=indicators.BufferLineState(Symbol(),PERIOD_CURRENT,handle_alligator1,0,0,index);
   panel.DrawText(BufferLineStateDescription(state1),panel.CellX(1,1,1)+2,panel.CellY(1,1,1)+2,clrNONE,150);
   
//--- Выводим в таблицу 2 данные индикатора 2 с указанного бара в таблицу 2
   panel.DrawText(indicators.Title(handle_alligator2), panel.CellX(2,0,0)+2, panel.CellY(2,0,0)+2);
   double value2=indicators.GetDataTo(Symbol(),PERIOD_CURRENT,handle_alligator2,0,0,index);
   string value_str2=(value2!=EMPTY_VALUE ? DoubleToString(value2,indicators.Digits(handle_alligator2)) : " ");
   panel.DrawText(value_str2,panel.CellX(2,0,1)+2,panel.CellY(2,0,1)+2,clrNONE,150);
   
//--- Выводим описание состояния линии индикатора 2
   panel.DrawText("Line state", panel.CellX(2,1,0)+2, panel.CellY(2,1,0)+2);
   ENUM_LINE_STATE state2=indicators.BufferLineState(Symbol(),PERIOD_CURRENT,handle_alligator2,0,0,index);
   panel.DrawText(BufferLineStateDescription(state2),panel.CellX(2,1,1)+2,panel.CellY(2,1,1)+2,clrNONE,150);
   
//--- Выводим описание соотношения линии индикатора 1 относительно линии индикатора 2
   double value21=indicators.GetDataTo(Symbol(),PERIOD_CURRENT,handle_alligator2,0,0,index+1);
   ENUM_LINE_STATE stateR=indicators.BufferLineStateRelative(Symbol(),PERIOD_CURRENT,handle_alligator1,0,0,index,value2,value21);
   string ma1=indicators.Name(handle_alligator1);
   string ma2=indicators.Name(handle_alligator2);
   string state_relative=
     (
      stateR==LINE_STATE_ABOVE      ? StringFormat("%s1 > %s2",ma1,ma2)   :
      stateR==LINE_STATE_BELOW      ? StringFormat("%s1 < %s2",ma1,ma2)           :
      stateR==LINE_STATE_CROSS_DOWN ? "Top-down crossing"   :
      stateR==LINE_STATE_CROSS_UP   ? "Bottom-up crossing"  :
      BufferLineStateDescription(stateR)
     );
   panel.DrawText(StringFormat("%s1 vs %s2",ma1,ma2), panel.CellX(2,2,0)+2, panel.CellY(2,2,0)+2);
   panel.DrawText(state_relative, panel.CellX(2,2,1)+2, panel.CellY(2,2,1)+2,clrNONE,150);
   
//--- Перерисовываем график для немедленного отображения всех изменений на панели
   ChartRedraw(ChartID());
  }
//+------------------------------------------------------------------+

После компиляции и запуска индикатора на графике М1 и выбранном периоде графика для расчёта М5, видим:



Индикатор Average Directional Movement Index:

//+------------------------------------------------------------------+
//|                      TestMSTFAverageDirectionalMovementIndex.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_separate_window
#property indicator_buffers 6
#property indicator_plots   6
//--- enums

//--- plot ADX1
#property indicator_label1  "ADX1"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrDodgerBlue
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1

//--- plot +DI1
#property indicator_label2  "+DI1"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrGreen
#property indicator_style2  STYLE_SOLID
#property indicator_width2  1

//--- plot -DI1
#property indicator_label3  "-DI1"
#property indicator_type3   DRAW_LINE
#property indicator_color3  clrRed
#property indicator_style3  STYLE_SOLID
#property indicator_width3  1

//--- plot ADX2
#property indicator_label4  "ADX2"
#property indicator_type4   DRAW_LINE
#property indicator_color4  clrBlue
#property indicator_style4  STYLE_SOLID
#property indicator_width4  1

//--- plot +DI2
#property indicator_label5  "+DI2"
#property indicator_type5   DRAW_LINE
#property indicator_color5  clrGreen
#property indicator_style5  STYLE_SOLID
#property indicator_width5  1

//--- plot -DI2
#property indicator_label6  "-DI2"
#property indicator_type6   DRAW_LINE
#property indicator_color6  clrRed
#property indicator_style6  STYLE_SOLID
#property indicator_width6  1

//--- includes
#include <IndMSTF\IndMSTF.mqh>
#include <Dashboard\Dashboard.mqh>
//--- input parameters
input string               InpSymbol      =  NULL;             /* Symbol                     */ // Символ
input ENUM_TIMEFRAMES      InpTimeframe   =  PERIOD_CURRENT;   /* Timeframe                  */ // Таймфрейм
input int                  InpPeriod      =  14;               /* Period                     */ // Период расчёта
input uchar                InpLineWidth1  =  2;                /* Senior period Line Width   */ // Толщина линии для старшего периода
input uchar                InpLineWidth2  =  1;                /* Junior period Line Width   */ // Толщина линии для младшего периода
input bool                 InpAsSeries    =  true;             /* As Series flag             */ // Флаг серийности массивов буферов индикатора

//--- indicator buffers
double         BufferADX1[];
double         BufferDIPlus1[];
double         BufferDIMinus1[];
double         BufferADX2[];
double         BufferDIPlus2[];
double         BufferDIMinus2[];
//--- global variables
int handle_adx1;
int handle_adx2;
CMSTFIndicators indicators;      // Экземпляр объекта коллекции индикаторов
//--- переменные для панели
CDashboard *panel=NULL;          // Указатель на объект панели
int         mouse_bar_index;     // Индекс бара, с которого берутся данные
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Устанавливаем таймер с периодичностью в 1 секунду
   EventSetTimer(1);
//--- Назначаем рисуемым буферам 0 и 1 массивы BufferMA1 и BufferMA2 соответственно
   SetIndexBuffer(0,BufferADX1,INDICATOR_DATA);
   SetIndexBuffer(1,BufferDIPlus1,INDICATOR_DATA);
   SetIndexBuffer(2,BufferDIMinus1,INDICATOR_DATA);
   SetIndexBuffer(3,BufferADX2,INDICATOR_DATA);
   SetIndexBuffer(4,BufferDIPlus2,INDICATOR_DATA);
   SetIndexBuffer(5,BufferDIMinus2,INDICATOR_DATA);
//--- Устанавливаем ширину линий
   int w1=0,w2=0;
   if(InpTimeframe>Period())
     {
      w1=InpLineWidth2;
      w2=InpLineWidth1;
     }
   else
     {
      w1=InpLineWidth1;
      w2=InpLineWidth2;
     }
   PlotIndexSetInteger(0,PLOT_LINE_WIDTH,w1);
   PlotIndexSetInteger(1,PLOT_LINE_WIDTH,w1);
   PlotIndexSetInteger(2,PLOT_LINE_WIDTH,w1);
   PlotIndexSetInteger(3,PLOT_LINE_WIDTH,w2);
   PlotIndexSetInteger(4,PLOT_LINE_WIDTH,w2);
   PlotIndexSetInteger(5,PLOT_LINE_WIDTH,w2);
//--- sets indicator shift

//--- Устанавливаем флаги серийности массивам буферов индикатора (для теста, чтобы видно было отсутствие разницы)
   ArraySetAsSeries(BufferADX1,InpAsSeries);
   ArraySetAsSeries(BufferDIPlus1,InpAsSeries);
   ArraySetAsSeries(BufferDIMinus1,InpAsSeries);
   ArraySetAsSeries(BufferADX2,InpAsSeries);
   ArraySetAsSeries(BufferDIPlus2,InpAsSeries);
   ArraySetAsSeries(BufferDIMinus2,InpAsSeries);
   
//--- Создаём два индикатора одного типа
//--- Первый рассчитывается на текущих символе/периоде графика, а второй - на тех, что заданы в настройках
   handle_adx1=indicators.AddNewADX(NULL,PERIOD_CURRENT,InpPeriod);
   handle_adx2=indicators.AddNewADX(InpSymbol,InpTimeframe,InpPeriod);

//--- Если не удалось создать хэндлы индикаторов - возвращаем ошибку инициализации
   if(handle_adx1==INVALID_HANDLE || handle_adx2==INVALID_HANDLE)
      return INIT_FAILED;
//--- Устанавливаем описания линий индикатора из описаний буферов расчётной части созданных индикаторов
   indicators.SetPlotLabelFromBuffer(0,handle_adx1,0);
   indicators.SetPlotLabelFromBuffer(1,handle_adx1,1);
   indicators.SetPlotLabelFromBuffer(2,handle_adx1,2);
   indicators.SetPlotLabelFromBuffer(3,handle_adx2,0);
   indicators.SetPlotLabelFromBuffer(4,handle_adx2,1);
   indicators.SetPlotLabelFromBuffer(5,handle_adx2,2);
      
//--- Панель
//--- Создаём панель
   int width=301;
   panel=new CDashboard(1,20,20,width,264,0);
   if(panel==NULL)
     {
      Print("Error. Failed to create panel object");
      return INIT_FAILED;
     }
//--- Устанавливаем параметры шрифта
   panel.SetFontParams("Calibri",9);
//--- Отображаем панель с текстом в заголовке "Символ, Описание таймфрейма"
   panel.View(Symbol()+", "+StringSubstr(EnumToString(Period()),7));

//--- Создаём таблицу с идентификатором 0 для отображения в ней данных бара
   panel.CreateNewTable(0);
//--- Рисуем таблицу с идентификатором 0 на фоне панели
   panel.DrawGrid(0,2,20,6,2,18,width/2-2);

//--- Создаём таблицу с идентификатором 1 для отображения в ней данных индикатора 1
   panel.CreateNewTable(1);
//--- Получаем координату Y2 таблицы с идентификатором 0 и
//--- устанавливаем координату Y1 для таблицы с идентификатором 1
   int y1=panel.TableY2(0)+22;
//--- Рисуем таблицу с идентификатором 1 на фоне панели
   panel.DrawGrid(1,2,y1,2,2,18,width/2-2);

//--- Создаём таблицу с идентификатором 2 для отображения в ней данных индикатора 2
   panel.CreateNewTable(2);
//--- Получаем координату Y2 таблицы с идентификатором 1 и
//--- устанавливаем координату Y1 для таблицы с идентификатором 2
   int y2=panel.TableY2(1)+3;
//--- Рисуем таблицу с идентификатором 2 на фоне панели
   panel.DrawGrid(2,2,y2,3,2,18,width/2-2);
   
//--- Инициализируем переменную с индексом бара указателя мышки
   mouse_bar_index=0;
//--- Выводим на панель данные текущего бара
   DrawData(mouse_bar_index,TimeCurrent());

//--- Успешная инициализация
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- Уничтожаем таймер
   EventKillTimer();
//--- Если объект панели существует - удаляем его
   if(panel!=NULL)
      delete panel;
//--- Стираем все комментарии
   Comment("");
  }
//+------------------------------------------------------------------+
//| 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[])
  {
//--- Количество баров для расчёта
   int limit=rates_total-prev_calculated;
//--- Если limit > 1, значит это первый расчёт, либо изменение в истории
   if(limit>1)
     {
      //--- указываем для просчёта всю доступную историю
      limit=rates_total-1;
      /*
      // Если в индикаторе есть какие-либо буферы, в которых отображены другие расчёты (не мульти- индикаторы),
      // то здесь их нужно инициализировать "пустым" значением, установленным для этих буферов
      */
     }
//--- Рассчитываем все созданные мультисимвольные мультипериодные индикаторы
   if(!indicators.Calculate())
      return 0;

//--- Выводим на панель данные бара под курсором (либо текущий бар, если курсор за пределами графика)
   DrawData(mouse_bar_index,time[mouse_bar_index]);

//--- Из буферов рассчитанных индикаторов выводим данные в индикаторные буферы
   if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_adx1,0,0,limit,BufferADX1))
      return 0;
   if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_adx1,1,0,limit,BufferDIPlus1))
      return 0;
   if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_adx1,2,0,limit,BufferDIMinus1))
      return 0;
      
   if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_adx2,0,0,limit,BufferADX2))
      return 0;
   if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_adx2,1,0,limit,BufferDIPlus2))
      return 0;
   if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_adx2,2,0,limit,BufferDIMinus2))
      return 0;
      
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| Timer function                                                   | 
//+------------------------------------------------------------------+
void OnTimer()
  {
//--- Вызываем таймер коллекции индикаторов
   indicators.OnTimer();
  }
//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- Работа с панелью
//--- Вызываем обработчик событий панели
   panel.OnChartEvent(id,lparam,dparam,sparam);

//--- Если курсор перемещается или щелчок по графику
   if(id==CHARTEVENT_MOUSE_MOVE || id==CHARTEVENT_CLICK)
     {
      //--- Объявляем переменные для записи в них координат времени и цены
      datetime time=0;
      double price=0;
      int wnd=0;
      //--- Если координаты курсора преобразованы в дату и время
      if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price))
        {
         //--- записываем индекс бара, где расположен курсор в глобальную переменную
         mouse_bar_index=iBarShift(Symbol(),PERIOD_CURRENT,time);
         //--- Выводим данные бара под курсором на панель
         DrawData(mouse_bar_index,time);
        }
     }

//--- Если получили пользовательское событие - выводим об этом сообщение в журнал
   if(id>CHARTEVENT_CUSTOM)
     {
      //--- Здесь может быть обработка щелчка по кнопке закрытия на панели
      PrintFormat("%s: Event id=%ld, object id (lparam): %lu, event message (sparam): %s",__FUNCTION__,id,lparam,sparam);
     }
  }
//+------------------------------------------------------------------+
//| Выводит данные с указанного индекса таймсерии на панель          |
//+------------------------------------------------------------------+
void DrawData(const int index,const datetime time)
  {
//--- Объявляем переменные для получения в них данных
   MqlRates rates[1];

//--- Если данные бара по указанному индексу получить не удалось - уходим
   if(CopyRates(Symbol(),PERIOD_CURRENT,index,1,rates)!=1)
      return;

//--- Устанавливаем параметры шрифта для заголовков данных бара и индикатора
   int  size=0;
   uint flags=0;
   uint angle=0;
   string name=panel.FontParams(size,flags,angle);
   panel.SetFontParams(name,9,FW_BOLD);
   panel.DrawText("Bar data ["+(string)index+"]",3,panel.TableY1(0)-16,clrMaroon,panel.Width()-6);
   panel.DrawText("Indicators data ["+(string)index+"]",3,panel.TableY1(1)-16,clrGreen,panel.Width()-6);
//--- Устанавливаем параметры шрифта для данных бара и индикатора
   panel.SetFontParams(name,9);

//--- Выводим на панель данные указанного бара в таблицу 0
   panel.DrawText("Date",  panel.CellX(0,0,0)+2, panel.CellY(0,0,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_DATE),     panel.CellX(0,0,1)+2, panel.CellY(0,0,1)+2,clrNONE,90);
   panel.DrawText("Time",  panel.CellX(0,1,0)+2, panel.CellY(0,1,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_MINUTES),  panel.CellX(0,1,1)+2, panel.CellY(0,1,1)+2,clrNONE,90);
   panel.DrawText("Open",  panel.CellX(0,2,0)+2, panel.CellY(0,2,0)+2); panel.DrawText(DoubleToString(rates[0].open,Digits()),      panel.CellX(0,2,1)+2, panel.CellY(0,2,1)+2,clrNONE,90);
   panel.DrawText("High",  panel.CellX(0,3,0)+2, panel.CellY(0,3,0)+2); panel.DrawText(DoubleToString(rates[0].high,Digits()),      panel.CellX(0,3,1)+2, panel.CellY(0,3,1)+2,clrNONE,90);
   panel.DrawText("Low",   panel.CellX(0,4,0)+2, panel.CellY(0,4,0)+2); panel.DrawText(DoubleToString(rates[0].low,Digits()),       panel.CellX(0,4,1)+2, panel.CellY(0,4,1)+2,clrNONE,90);
   panel.DrawText("Close", panel.CellX(0,5,0)+2, panel.CellY(0,5,0)+2); panel.DrawText(DoubleToString(rates[0].close,Digits()),     panel.CellX(0,5,1)+2, panel.CellY(0,5,1)+2,clrNONE,90);

//--- Выводим в таблицу 1 данные индикатора 1 с указанного бара в таблицу 1
   panel.DrawText(indicators.Title(handle_adx1), panel.CellX(1,0,0)+2, panel.CellY(1,0,0)+2);
   double value1=indicators.GetData(handle_adx1,0,0,index);
   string value_str1=(value1!=EMPTY_VALUE ? DoubleToString(value1,indicators.Digits(handle_adx1)) : " ");
   panel.DrawText(value_str1,panel.CellX(1,0,1)+2,panel.CellY(1,0,1)+2,clrNONE,190);
   
//--- Выводим описание состояния линии индикатора 1
   panel.DrawText("Line state", panel.CellX(1,1,0)+2, panel.CellY(1,1,0)+2);
   ENUM_LINE_STATE state1=indicators.BufferLineState(Symbol(),PERIOD_CURRENT,handle_adx1,0,0,index);
   panel.DrawText(BufferLineStateDescription(state1),panel.CellX(1,1,1)+2,panel.CellY(1,1,1)+2,clrNONE,190);
   
//--- Выводим в таблицу 2 данные индикатора 2 с указанного бара в таблицу 2
   panel.DrawText(indicators.Title(handle_adx2), panel.CellX(2,0,0)+2, panel.CellY(2,0,0)+2);
   double value2=indicators.GetDataTo(Symbol(),PERIOD_CURRENT,handle_adx2,0,0,index);
   string value_str2=(value2!=EMPTY_VALUE ? DoubleToString(value2,indicators.Digits(handle_adx2)) : " ");
   panel.DrawText(value_str2,panel.CellX(2,0,1)+2,panel.CellY(2,0,1)+2,clrNONE,190);
   
//--- Выводим описание состояния линии индикатора 2
   panel.DrawText("Line state", panel.CellX(2,1,0)+2, panel.CellY(2,1,0)+2);
   ENUM_LINE_STATE state2=indicators.BufferLineState(Symbol(),PERIOD_CURRENT,handle_adx2,0,0,index);
   panel.DrawText(BufferLineStateDescription(state2),panel.CellX(2,1,1)+2,panel.CellY(2,1,1)+2,clrNONE,190);
   
//--- Выводим описание соотношения линии индикатора 1 относительно линии индикатора 2
   double value21=indicators.GetDataTo(Symbol(),PERIOD_CURRENT,handle_adx2,0,0,index+1);
   ENUM_LINE_STATE stateR=indicators.BufferLineStateRelative(Symbol(),PERIOD_CURRENT,handle_adx1,0,0,index,value2,value21);
   string ma1=indicators.Name(handle_adx1);
   string ma2=indicators.Name(handle_adx2);
   string state_relative=
     (
      stateR==LINE_STATE_ABOVE      ? StringFormat("%s1 > %s2",ma1,ma2)   :
      stateR==LINE_STATE_BELOW      ? StringFormat("%s1 < %s2",ma1,ma2)           :
      stateR==LINE_STATE_CROSS_DOWN ? "Top-down crossing"   :
      stateR==LINE_STATE_CROSS_UP   ? "Bottom-up crossing"  :
      BufferLineStateDescription(stateR)
     );
   panel.DrawText(StringFormat("%s1 vs %s2",ma1,ma2), panel.CellX(2,2,0)+2, panel.CellY(2,2,0)+2);
   panel.DrawText(state_relative, panel.CellX(2,2,1)+2, panel.CellY(2,2,1)+2,clrNONE,190);
   
//--- Перерисовываем график для немедленного отображения всех изменений на панели
   ChartRedraw(ChartID());
  }
//+------------------------------------------------------------------+

После компиляции и запуска индикатора на графике М1 и выбранном периоде графика для расчёта М5, видим:



Индикатор Average Directional Movement Index Wilder:

//+------------------------------------------------------------------+
//|                TestMSTFAverageDirectionalMovementIndexWilder.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_separate_window
#property indicator_buffers 6
#property indicator_plots   6
//--- enums

//--- plot ADXW1
#property indicator_label1  "ADXW1"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrDodgerBlue
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1

//--- plot +DI1
#property indicator_label2  "+DI1"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrGreen
#property indicator_style2  STYLE_SOLID
#property indicator_width2  1

//--- plot -DI1
#property indicator_label3  "-DI1"
#property indicator_type3   DRAW_LINE
#property indicator_color3  clrRed
#property indicator_style3  STYLE_SOLID
#property indicator_width3  1

//--- plot ADXW2
#property indicator_label4  "ADXW2"
#property indicator_type4   DRAW_LINE
#property indicator_color4  clrBlue
#property indicator_style4  STYLE_SOLID
#property indicator_width4  1

//--- plot +DI2
#property indicator_label5  "+DI2"
#property indicator_type5   DRAW_LINE
#property indicator_color5  clrGreen
#property indicator_style5  STYLE_SOLID
#property indicator_width5  1

//--- plot -DI2
#property indicator_label6  "-DI2"
#property indicator_type6   DRAW_LINE
#property indicator_color6  clrRed
#property indicator_style6  STYLE_SOLID
#property indicator_width6  1

//--- includes
#include <IndMSTF\IndMSTF.mqh>
#include <Dashboard\Dashboard.mqh>
//--- input parameters
input string               InpSymbol      =  NULL;             /* Symbol                     */ // Символ
input ENUM_TIMEFRAMES      InpTimeframe   =  PERIOD_CURRENT;   /* Timeframe                  */ // Таймфрейм
input int                  InpPeriod      =  14;               /* Period                     */ // Период расчёта
input uchar                InpLineWidth1  =  2;                /* Senior period Line Width   */ // Толщина линии для старшего периода
input uchar                InpLineWidth2  =  1;                /* Junior period Line Width   */ // Толщина линии для младшего периода
input bool                 InpAsSeries    =  true;             /* As Series flag             */ // Флаг серийности массивов буферов индикатора

//--- indicator buffers
double         BufferADXW1[];
double         BufferDIPlus1[];
double         BufferDIMinus1[];
double         BufferADXW2[];
double         BufferDIPlus2[];
double         BufferDIMinus2[];
//--- global variables
int handle_adxw1;
int handle_adxw2;
CMSTFIndicators indicators;      // Экземпляр объекта коллекции индикаторов
//--- переменные для панели
CDashboard *panel=NULL;          // Указатель на объект панели
int         mouse_bar_index;     // Индекс бара, с которого берутся данные
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Устанавливаем таймер с периодичностью в 1 секунду
   EventSetTimer(1);
//--- Назначаем рисуемым буферам 0 и 1 массивы BufferMA1 и BufferMA2 соответственно
   SetIndexBuffer(0,BufferADXW1,INDICATOR_DATA);
   SetIndexBuffer(1,BufferDIPlus1,INDICATOR_DATA);
   SetIndexBuffer(2,BufferDIMinus1,INDICATOR_DATA);
   SetIndexBuffer(3,BufferADXW2,INDICATOR_DATA);
   SetIndexBuffer(4,BufferDIPlus2,INDICATOR_DATA);
   SetIndexBuffer(5,BufferDIMinus2,INDICATOR_DATA);
//--- Устанавливаем ширину линий
   int w1=0,w2=0;
   if(InpTimeframe>Period())
     {
      w1=InpLineWidth2;
      w2=InpLineWidth1;
     }
   else
     {
      w1=InpLineWidth1;
      w2=InpLineWidth2;
     }
   PlotIndexSetInteger(0,PLOT_LINE_WIDTH,w1);
   PlotIndexSetInteger(1,PLOT_LINE_WIDTH,w1);
   PlotIndexSetInteger(2,PLOT_LINE_WIDTH,w1);
   PlotIndexSetInteger(3,PLOT_LINE_WIDTH,w2);
   PlotIndexSetInteger(4,PLOT_LINE_WIDTH,w2);
   PlotIndexSetInteger(5,PLOT_LINE_WIDTH,w2);
//--- sets indicator shift

//--- Устанавливаем флаги серийности массивам буферов индикатора (для теста, чтобы видно было отсутствие разницы)
   ArraySetAsSeries(BufferADXW1,InpAsSeries);
   ArraySetAsSeries(BufferDIPlus1,InpAsSeries);
   ArraySetAsSeries(BufferDIMinus1,InpAsSeries);
   ArraySetAsSeries(BufferADXW2,InpAsSeries);
   ArraySetAsSeries(BufferDIPlus2,InpAsSeries);
   ArraySetAsSeries(BufferDIMinus2,InpAsSeries);
   
//--- Создаём два индикатора одного типа
//--- Первый рассчитывается на текущих символе/периоде графика, а второй - на тех, что заданы в настройках
   handle_adxw1=indicators.AddNewADX(NULL,PERIOD_CURRENT,InpPeriod);
   handle_adxw2=indicators.AddNewADX(InpSymbol,InpTimeframe,InpPeriod);

//--- Если не удалось создать хэндлы индикаторов - возвращаем ошибку инициализации
   if(handle_adxw1==INVALID_HANDLE || handle_adxw2==INVALID_HANDLE)
      return INIT_FAILED;
//--- Устанавливаем описания линий индикатора из описаний буферов расчётной части созданных индикаторов
   indicators.SetPlotLabelFromBuffer(0,handle_adxw1,0);
   indicators.SetPlotLabelFromBuffer(1,handle_adxw1,1);
   indicators.SetPlotLabelFromBuffer(2,handle_adxw1,2);
   indicators.SetPlotLabelFromBuffer(3,handle_adxw2,0);
   indicators.SetPlotLabelFromBuffer(4,handle_adxw2,1);
   indicators.SetPlotLabelFromBuffer(5,handle_adxw2,2);
      
//--- Панель
//--- Создаём панель
   int width=301;
   panel=new CDashboard(1,20,20,width,264,0);
   if(panel==NULL)
     {
      Print("Error. Failed to create panel object");
      return INIT_FAILED;
     }
//--- Устанавливаем параметры шрифта
   panel.SetFontParams("Calibri",9);
//--- Отображаем панель с текстом в заголовке "Символ, Описание таймфрейма"
   panel.View(Symbol()+", "+StringSubstr(EnumToString(Period()),7));

//--- Создаём таблицу с идентификатором 0 для отображения в ней данных бара
   panel.CreateNewTable(0);
//--- Рисуем таблицу с идентификатором 0 на фоне панели
   panel.DrawGrid(0,2,20,6,2,18,width/2-2);

//--- Создаём таблицу с идентификатором 1 для отображения в ней данных индикатора 1
   panel.CreateNewTable(1);
//--- Получаем координату Y2 таблицы с идентификатором 0 и
//--- устанавливаем координату Y1 для таблицы с идентификатором 1
   int y1=panel.TableY2(0)+22;
//--- Рисуем таблицу с идентификатором 1 на фоне панели
   panel.DrawGrid(1,2,y1,2,2,18,width/2-2);

//--- Создаём таблицу с идентификатором 2 для отображения в ней данных индикатора 2
   panel.CreateNewTable(2);
//--- Получаем координату Y2 таблицы с идентификатором 1 и
//--- устанавливаем координату Y1 для таблицы с идентификатором 2
   int y2=panel.TableY2(1)+3;
//--- Рисуем таблицу с идентификатором 2 на фоне панели
   panel.DrawGrid(2,2,y2,3,2,18,width/2-2);
   
//--- Инициализируем переменную с индексом бара указателя мышки
   mouse_bar_index=0;
//--- Выводим на панель данные текущего бара
   DrawData(mouse_bar_index,TimeCurrent());

//--- Успешная инициализация
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- Уничтожаем таймер
   EventKillTimer();
//--- Если объект панели существует - удаляем его
   if(panel!=NULL)
      delete panel;
//--- Стираем все комментарии
   Comment("");
  }
//+------------------------------------------------------------------+
//| 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[])
  {
//--- Количество баров для расчёта
   int limit=rates_total-prev_calculated;
//--- Если limit > 1, значит это первый расчёт, либо изменение в истории
   if(limit>1)
     {
      //--- указываем для просчёта всю доступную историю
      limit=rates_total-1;
      /*
      // Если в индикаторе есть какие-либо буферы, в которых отображены другие расчёты (не мульти- индикаторы),
      // то здесь их нужно инициализировать "пустым" значением, установленным для этих буферов
      */
     }
//--- Рассчитываем все созданные мультисимвольные мультипериодные индикаторы
   if(!indicators.Calculate())
      return 0;

//--- Выводим на панель данные бара под курсором (либо текущий бар, если курсор за пределами графика)
   DrawData(mouse_bar_index,time[mouse_bar_index]);

//--- Из буферов рассчитанных индикаторов выводим данные в индикаторные буферы
   if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_adxw1,0,0,limit,BufferADXW1))
      return 0;
   if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_adxw1,1,0,limit,BufferDIPlus1))
      return 0;
   if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_adxw1,2,0,limit,BufferDIMinus1))
      return 0;
      
   if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_adxw2,0,0,limit,BufferADXW2))
      return 0;
   if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_adxw2,1,0,limit,BufferDIPlus2))
      return 0;
   if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_adxw2,2,0,limit,BufferDIMinus2))
      return 0;
      
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| Timer function                                                   | 
//+------------------------------------------------------------------+
void OnTimer()
  {
//--- Вызываем таймер коллекции индикаторов
   indicators.OnTimer();
  }
//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- Работа с панелью
//--- Вызываем обработчик событий панели
   panel.OnChartEvent(id,lparam,dparam,sparam);

//--- Если курсор перемещается или щелчок по графику
   if(id==CHARTEVENT_MOUSE_MOVE || id==CHARTEVENT_CLICK)
     {
      //--- Объявляем переменные для записи в них координат времени и цены
      datetime time=0;
      double price=0;
      int wnd=0;
      //--- Если координаты курсора преобразованы в дату и время
      if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price))
        {
         //--- записываем индекс бара, где расположен курсор в глобальную переменную
         mouse_bar_index=iBarShift(Symbol(),PERIOD_CURRENT,time);
         //--- Выводим данные бара под курсором на панель
         DrawData(mouse_bar_index,time);
        }
     }

//--- Если получили пользовательское событие - выводим об этом сообщение в журнал
   if(id>CHARTEVENT_CUSTOM)
     {
      //--- Здесь может быть обработка щелчка по кнопке закрытия на панели
      PrintFormat("%s: Event id=%ld, object id (lparam): %lu, event message (sparam): %s",__FUNCTION__,id,lparam,sparam);
     }
  }
//+------------------------------------------------------------------+
//| Выводит данные с указанного индекса таймсерии на панель          |
//+------------------------------------------------------------------+
void DrawData(const int index,const datetime time)
  {
//--- Объявляем переменные для получения в них данных
   MqlRates rates[1];

//--- Если данные бара по указанному индексу получить не удалось - уходим
   if(CopyRates(Symbol(),PERIOD_CURRENT,index,1,rates)!=1)
      return;

//--- Устанавливаем параметры шрифта для заголовков данных бара и индикатора
   int  size=0;
   uint flags=0;
   uint angle=0;
   string name=panel.FontParams(size,flags,angle);
   panel.SetFontParams(name,9,FW_BOLD);
   panel.DrawText("Bar data ["+(string)index+"]",3,panel.TableY1(0)-16,clrMaroon,panel.Width()-6);
   panel.DrawText("Indicators data ["+(string)index+"]",3,panel.TableY1(1)-16,clrGreen,panel.Width()-6);
//--- Устанавливаем параметры шрифта для данных бара и индикатора
   panel.SetFontParams(name,9);

//--- Выводим на панель данные указанного бара в таблицу 0
   panel.DrawText("Date",  panel.CellX(0,0,0)+2, panel.CellY(0,0,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_DATE),     panel.CellX(0,0,1)+2, panel.CellY(0,0,1)+2,clrNONE,90);
   panel.DrawText("Time",  panel.CellX(0,1,0)+2, panel.CellY(0,1,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_MINUTES),  panel.CellX(0,1,1)+2, panel.CellY(0,1,1)+2,clrNONE,90);
   panel.DrawText("Open",  panel.CellX(0,2,0)+2, panel.CellY(0,2,0)+2); panel.DrawText(DoubleToString(rates[0].open,Digits()),      panel.CellX(0,2,1)+2, panel.CellY(0,2,1)+2,clrNONE,90);
   panel.DrawText("High",  panel.CellX(0,3,0)+2, panel.CellY(0,3,0)+2); panel.DrawText(DoubleToString(rates[0].high,Digits()),      panel.CellX(0,3,1)+2, panel.CellY(0,3,1)+2,clrNONE,90);
   panel.DrawText("Low",   panel.CellX(0,4,0)+2, panel.CellY(0,4,0)+2); panel.DrawText(DoubleToString(rates[0].low,Digits()),       panel.CellX(0,4,1)+2, panel.CellY(0,4,1)+2,clrNONE,90);
   panel.DrawText("Close", panel.CellX(0,5,0)+2, panel.CellY(0,5,0)+2); panel.DrawText(DoubleToString(rates[0].close,Digits()),     panel.CellX(0,5,1)+2, panel.CellY(0,5,1)+2,clrNONE,90);

//--- Выводим в таблицу 1 данные индикатора 1 с указанного бара в таблицу 1
   panel.DrawText(indicators.Title(handle_adxw1), panel.CellX(1,0,0)+2, panel.CellY(1,0,0)+2);
   double value1=indicators.GetData(handle_adxw1,0,0,index);
   string value_str1=(value1!=EMPTY_VALUE ? DoubleToString(value1,indicators.Digits(handle_adxw1)) : " ");
   panel.DrawText(value_str1,panel.CellX(1,0,1)+2,panel.CellY(1,0,1)+2,clrNONE,190);
   
//--- Выводим описание состояния линии индикатора 1
   panel.DrawText("Line state", panel.CellX(1,1,0)+2, panel.CellY(1,1,0)+2);
   ENUM_LINE_STATE state1=indicators.BufferLineState(Symbol(),PERIOD_CURRENT,handle_adxw1,0,0,index);
   panel.DrawText(BufferLineStateDescription(state1),panel.CellX(1,1,1)+2,panel.CellY(1,1,1)+2,clrNONE,190);
   
//--- Выводим в таблицу 2 данные индикатора 2 с указанного бара в таблицу 2
   panel.DrawText(indicators.Title(handle_adxw2), panel.CellX(2,0,0)+2, panel.CellY(2,0,0)+2);
   double value2=indicators.GetDataTo(Symbol(),PERIOD_CURRENT,handle_adxw2,0,0,index);
   string value_str2=(value2!=EMPTY_VALUE ? DoubleToString(value2,indicators.Digits(handle_adxw2)) : " ");
   panel.DrawText(value_str2,panel.CellX(2,0,1)+2,panel.CellY(2,0,1)+2,clrNONE,190);
   
//--- Выводим описание состояния линии индикатора 2
   panel.DrawText("Line state", panel.CellX(2,1,0)+2, panel.CellY(2,1,0)+2);
   ENUM_LINE_STATE state2=indicators.BufferLineState(Symbol(),PERIOD_CURRENT,handle_adxw2,0,0,index);
   panel.DrawText(BufferLineStateDescription(state2),panel.CellX(2,1,1)+2,panel.CellY(2,1,1)+2,clrNONE,190);
   
//--- Выводим описание соотношения линии индикатора 1 относительно линии индикатора 2
   double value21=indicators.GetDataTo(Symbol(),PERIOD_CURRENT,handle_adxw2,0,0,index+1);
   ENUM_LINE_STATE stateR=indicators.BufferLineStateRelative(Symbol(),PERIOD_CURRENT,handle_adxw1,0,0,index,value2,value21);
   string ma1=indicators.Name(handle_adxw1);
   string ma2=indicators.Name(handle_adxw2);
   string state_relative=
     (
      stateR==LINE_STATE_ABOVE      ? StringFormat("%s1 > %s2",ma1,ma2)   :
      stateR==LINE_STATE_BELOW      ? StringFormat("%s1 < %s2",ma1,ma2)           :
      stateR==LINE_STATE_CROSS_DOWN ? "Top-down crossing"   :
      stateR==LINE_STATE_CROSS_UP   ? "Bottom-up crossing"  :
      BufferLineStateDescription(stateR)
     );
   panel.DrawText(StringFormat("%s1 vs %s2",ma1,ma2), panel.CellX(2,2,0)+2, panel.CellY(2,2,0)+2);
   panel.DrawText(state_relative, panel.CellX(2,2,1)+2, panel.CellY(2,2,1)+2,clrNONE,190);
   
//--- Перерисовываем график для немедленного отображения всех изменений на панели
   ChartRedraw(ChartID());
  }
//+------------------------------------------------------------------+

После компиляции и запуска индикатора на графике М1 и выбранном периоде графика для расчёта М5, видим:



Индикатор Average True Range:

//+------------------------------------------------------------------+
//|                                     TestMSTFAverageTrueRange.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_separate_window
#property indicator_buffers 2
#property indicator_plots   2
//--- enums

//--- plot ATR1
#property indicator_label1  "ATR1"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrDodgerBlue
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1

//--- plot ATR2
#property indicator_label2  "ATR2"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrRed
#property indicator_style2  STYLE_SOLID
#property indicator_width2  1

//--- includes
#include <IndMSTF\IndMSTF.mqh>
#include <Dashboard\Dashboard.mqh>
//--- input parameters
input string               InpSymbol      =  NULL;             /* Symbol                     */ // Символ
input ENUM_TIMEFRAMES      InpTimeframe   =  PERIOD_CURRENT;   /* Timeframe                  */ // Таймфрейм
input int                  InpPeriod      =  14;               /* Period                     */ // Период расчёта
input uchar                InpLineWidth1  =  2;                /* Senior period Line Width   */ // Толщина линии для старшего периода
input uchar                InpLineWidth2  =  1;                /* Junior period Line Width   */ // Толщина линии для младшего периода
input bool                 InpAsSeries    =  true;             /* As Series flag             */ // Флаг серийности массивов буферов индикатора

//--- indicator buffers
double         BufferATR1[];
double         BufferATR2[];
//--- global variables
int handle_atr1;
int handle_atr2;
CMSTFIndicators indicators;      // Экземпляр объекта коллекции индикаторов
//--- переменные для панели
CDashboard *panel=NULL;          // Указатель на объект панели
int         mouse_bar_index;     // Индекс бара, с которого берутся данные
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Устанавливаем таймер с периодичностью в 1 секунду
   EventSetTimer(1);
//--- Назначаем рисуемым буферам 0 и 1 массивы BufferMA1 и BufferMA2 соответственно
   SetIndexBuffer(0,BufferATR1,INDICATOR_DATA);
   SetIndexBuffer(1,BufferATR2,INDICATOR_DATA);
//--- Устанавливаем ширину линий
   int w1=0,w2=0;
   if(InpTimeframe>Period())
     {
      w1=InpLineWidth2;
      w2=InpLineWidth1;
     }
   else
     {
      w1=InpLineWidth1;
      w2=InpLineWidth2;
     }
   PlotIndexSetInteger(0,PLOT_LINE_WIDTH,w1);
   PlotIndexSetInteger(1,PLOT_LINE_WIDTH,w2);
//--- sets indicator shift

//--- Устанавливаем флаги серийности массивам буферов индикатора (для теста, чтобы видно было отсутствие разницы)
   ArraySetAsSeries(BufferATR1,InpAsSeries);
   ArraySetAsSeries(BufferATR2,InpAsSeries);
   
//--- Создаём два индикатора одного типа
//--- Первый рассчитывается на текущих символе/периоде графика, а второй - на тех, что заданы в настройках
   handle_atr1=indicators.AddNewATR(NULL,PERIOD_CURRENT,InpPeriod);
   handle_atr2=indicators.AddNewATR(InpSymbol,InpTimeframe,InpPeriod);

//--- Если не удалось создать хэндлы индикаторов - возвращаем ошибку инициализации
   if(handle_atr1==INVALID_HANDLE || handle_atr2==INVALID_HANDLE)
      return INIT_FAILED;
//--- Устанавливаем описания линий индикатора из описаний буферов расчётной части созданных индикаторов
   indicators.SetPlotLabelFromBuffer(0,handle_atr1,0);
   indicators.SetPlotLabelFromBuffer(1,handle_atr2,0);
      
//--- Панель
//--- Создаём панель
   int width=237;
   panel=new CDashboard(1,20,20,width,264,0);
   if(panel==NULL)
     {
      Print("Error. Failed to create panel object");
      return INIT_FAILED;
     }
//--- Устанавливаем параметры шрифта
   panel.SetFontParams("Calibri",9);
//--- Отображаем панель с текстом в заголовке "Символ, Описание таймфрейма"
   panel.View(Symbol()+", "+StringSubstr(EnumToString(Period()),7));

//--- Создаём таблицу с идентификатором 0 для отображения в ней данных бара
   panel.CreateNewTable(0);
//--- Рисуем таблицу с идентификатором 0 на фоне панели
   panel.DrawGrid(0,2,20,6,2,18,width/2-2);

//--- Создаём таблицу с идентификатором 1 для отображения в ней данных индикатора 1
   panel.CreateNewTable(1);
//--- Получаем координату Y2 таблицы с идентификатором 0 и
//--- устанавливаем координату Y1 для таблицы с идентификатором 1
   int y1=panel.TableY2(0)+22;
//--- Рисуем таблицу с идентификатором 1 на фоне панели
   panel.DrawGrid(1,2,y1,2,2,18,width/2-2);

//--- Создаём таблицу с идентификатором 2 для отображения в ней данных индикатора 2
   panel.CreateNewTable(2);
//--- Получаем координату Y2 таблицы с идентификатором 1 и
//--- устанавливаем координату Y1 для таблицы с идентификатором 2
   int y2=panel.TableY2(1)+3;
//--- Рисуем таблицу с идентификатором 2 на фоне панели
   panel.DrawGrid(2,2,y2,3,2,18,width/2-2);
   
//--- Инициализируем переменную с индексом бара указателя мышки
   mouse_bar_index=0;
//--- Выводим на панель данные текущего бара
   DrawData(mouse_bar_index,TimeCurrent());

//--- Успешная инициализация
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- Уничтожаем таймер
   EventKillTimer();
//--- Если объект панели существует - удаляем его
   if(panel!=NULL)
      delete panel;
//--- Стираем все комментарии
   Comment("");
  }
//+------------------------------------------------------------------+
//| 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[])
  {
//--- Количество баров для расчёта
   int limit=rates_total-prev_calculated;
//--- Если limit > 1, значит это первый расчёт, либо изменение в истории
   if(limit>1)
     {
      //--- указываем для просчёта всю доступную историю
      limit=rates_total-1;
      /*
      // Если в индикаторе есть какие-либо буферы, в которых отображены другие расчёты (не мульти- индикаторы),
      // то здесь их нужно инициализировать "пустым" значением, установленным для этих буферов
      */
     }
//--- Рассчитываем все созданные мультисимвольные мультипериодные индикаторы
   if(!indicators.Calculate())
      return 0;

//--- Выводим на панель данные бара под курсором (либо текущий бар, если курсор за пределами графика)
   DrawData(mouse_bar_index,time[mouse_bar_index]);

//--- Из буферов рассчитанных индикаторов выводим данные в индикаторные буферы
   if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_atr1,0,0,limit,BufferATR1))
      return 0;
   if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_atr2,0,0,limit,BufferATR2))
      return 0;

//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| Timer function                                                   | 
//+------------------------------------------------------------------+
void OnTimer()
  {
//--- Вызываем таймер коллекции индикаторов
   indicators.OnTimer();
  }
//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- Работа с панелью
//--- Вызываем обработчик событий панели
   panel.OnChartEvent(id,lparam,dparam,sparam);

//--- Если курсор перемещается или щелчок по графику
   if(id==CHARTEVENT_MOUSE_MOVE || id==CHARTEVENT_CLICK)
     {
      //--- Объявляем переменные для записи в них координат времени и цены
      datetime time=0;
      double price=0;
      int wnd=0;
      //--- Если координаты курсора преобразованы в дату и время
      if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price))
        {
         //--- записываем индекс бара, где расположен курсор в глобальную переменную
         mouse_bar_index=iBarShift(Symbol(),PERIOD_CURRENT,time);
         //--- Выводим данные бара под курсором на панель
         DrawData(mouse_bar_index,time);
        }
     }

//--- Если получили пользовательское событие - выводим об этом сообщение в журнал
   if(id>CHARTEVENT_CUSTOM)
     {
      //--- Здесь может быть обработка щелчка по кнопке закрытия на панели
      PrintFormat("%s: Event id=%ld, object id (lparam): %lu, event message (sparam): %s",__FUNCTION__,id,lparam,sparam);
     }
  }
//+------------------------------------------------------------------+
//| Выводит данные с указанного индекса таймсерии на панель          |
//+------------------------------------------------------------------+
void DrawData(const int index,const datetime time)
  {
//--- Объявляем переменные для получения в них данных
   MqlRates rates[1];

//--- Если данные бара по указанному индексу получить не удалось - уходим
   if(CopyRates(Symbol(),PERIOD_CURRENT,index,1,rates)!=1)
      return;

//--- Устанавливаем параметры шрифта для заголовков данных бара и индикатора
   int  size=0;
   uint flags=0;
   uint angle=0;
   string name=panel.FontParams(size,flags,angle);
   panel.SetFontParams(name,9,FW_BOLD);
   panel.DrawText("Bar data ["+(string)index+"]",3,panel.TableY1(0)-16,clrMaroon,panel.Width()-6);
   panel.DrawText("Indicators data ["+(string)index+"]",3,panel.TableY1(1)-16,clrGreen,panel.Width()-6);
//--- Устанавливаем параметры шрифта для данных бара и индикатора
   panel.SetFontParams(name,9);

//--- Выводим на панель данные указанного бара в таблицу 0
   panel.DrawText("Date",  panel.CellX(0,0,0)+2, panel.CellY(0,0,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_DATE),     panel.CellX(0,0,1)+2, panel.CellY(0,0,1)+2,clrNONE,90);
   panel.DrawText("Time",  panel.CellX(0,1,0)+2, panel.CellY(0,1,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_MINUTES),  panel.CellX(0,1,1)+2, panel.CellY(0,1,1)+2,clrNONE,90);
   panel.DrawText("Open",  panel.CellX(0,2,0)+2, panel.CellY(0,2,0)+2); panel.DrawText(DoubleToString(rates[0].open,Digits()),      panel.CellX(0,2,1)+2, panel.CellY(0,2,1)+2,clrNONE,90);
   panel.DrawText("High",  panel.CellX(0,3,0)+2, panel.CellY(0,3,0)+2); panel.DrawText(DoubleToString(rates[0].high,Digits()),      panel.CellX(0,3,1)+2, panel.CellY(0,3,1)+2,clrNONE,90);
   panel.DrawText("Low",   panel.CellX(0,4,0)+2, panel.CellY(0,4,0)+2); panel.DrawText(DoubleToString(rates[0].low,Digits()),       panel.CellX(0,4,1)+2, panel.CellY(0,4,1)+2,clrNONE,90);
   panel.DrawText("Close", panel.CellX(0,5,0)+2, panel.CellY(0,5,0)+2); panel.DrawText(DoubleToString(rates[0].close,Digits()),     panel.CellX(0,5,1)+2, panel.CellY(0,5,1)+2,clrNONE,90);

//--- Выводим в таблицу 1 данные индикатора 1 с указанного бара в таблицу 1
   panel.DrawText(indicators.Title(handle_atr1), panel.CellX(1,0,0)+2, panel.CellY(1,0,0)+2);
   double value1=indicators.GetData(handle_atr1,0,0,index);
   string value_str1=(value1!=EMPTY_VALUE ? DoubleToString(value1,indicators.Digits(handle_atr1)) : " ");
   panel.DrawText(value_str1,panel.CellX(1,0,1)+2,panel.CellY(1,0,1)+2,clrNONE,110);
   
//--- Выводим описание состояния линии индикатора 1
   panel.DrawText("Line state", panel.CellX(1,1,0)+2, panel.CellY(1,1,0)+2);
   ENUM_LINE_STATE state1=indicators.BufferLineState(Symbol(),PERIOD_CURRENT,handle_atr1,0,0,index);
   panel.DrawText(BufferLineStateDescription(state1),panel.CellX(1,1,1)+2,panel.CellY(1,1,1)+2,clrNONE,110);
   
//--- Выводим в таблицу 2 данные индикатора 2 с указанного бара в таблицу 2
   panel.DrawText(indicators.Title(handle_atr2), panel.CellX(2,0,0)+2, panel.CellY(2,0,0)+2);
   double value2=indicators.GetDataTo(Symbol(),PERIOD_CURRENT,handle_atr2,0,0,index);
   string value_str2=(value2!=EMPTY_VALUE ? DoubleToString(value2,indicators.Digits(handle_atr2)) : " ");
   panel.DrawText(value_str2,panel.CellX(2,0,1)+2,panel.CellY(2,0,1)+2,clrNONE,110);
   
//--- Выводим описание состояния линии индикатора 2
   panel.DrawText("Line state", panel.CellX(2,1,0)+2, panel.CellY(2,1,0)+2);
   ENUM_LINE_STATE state2=indicators.BufferLineState(Symbol(),PERIOD_CURRENT,handle_atr2,0,0,index);
   panel.DrawText(BufferLineStateDescription(state2),panel.CellX(2,1,1)+2,panel.CellY(2,1,1)+2,clrNONE,110);
   
//--- Выводим описание соотношения линии индикатора 1 относительно линии индикатора 2
   double value21=indicators.GetDataTo(Symbol(),PERIOD_CURRENT,handle_atr2,0,0,index+1);
   ENUM_LINE_STATE stateR=indicators.BufferLineStateRelative(Symbol(),PERIOD_CURRENT,handle_atr1,0,0,index,value2,value21);
   string ma1=indicators.Name(handle_atr1);
   string ma2=indicators.Name(handle_atr2);
   string state_relative=
     (
      stateR==LINE_STATE_ABOVE      ? StringFormat("%s1 > %s2",ma1,ma2)   :
      stateR==LINE_STATE_BELOW      ? StringFormat("%s1 < %s2",ma1,ma2)           :
      stateR==LINE_STATE_CROSS_DOWN ? "Top-down crossing"   :
      stateR==LINE_STATE_CROSS_UP   ? "Bottom-up crossing"  :
      BufferLineStateDescription(stateR)
     );
   panel.DrawText(StringFormat("%s1 vs %s2",ma1,ma2), panel.CellX(2,2,0)+2, panel.CellY(2,2,0)+2);
   panel.DrawText(state_relative, panel.CellX(2,2,1)+2, panel.CellY(2,2,1)+2,clrNONE,110);
   
//--- Перерисовываем график для немедленного отображения всех изменений на панели
   ChartRedraw(ChartID());
  }
//+------------------------------------------------------------------+

После компиляции и запуска индикатора на графике М1 и выбранном периоде графика для расчёта М5, видим:


Иногда, для более быстрой прорисовки индикатора можно переключить таймфрейм графика.


Индикатор Awesome Oscillator:

//+------------------------------------------------------------------+
//|                                    TestMSTFAwesomeOscillator.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_separate_window
#property indicator_buffers 4
#property indicator_plots   2
//--- enums

//--- plot AC1
#property indicator_label1  "AO1"
#property indicator_type1   DRAW_COLOR_HISTOGRAM
#property indicator_color1  clrGreen,clrRed
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1

//--- plot AC2
#property indicator_label2  "AO2"
#property indicator_type2   DRAW_COLOR_HISTOGRAM
#property indicator_color2  clrGreen,clrRed
#property indicator_style2  STYLE_SOLID
#property indicator_width2  1

//--- includes
#include <IndMSTF\IndMSTF.mqh>
#include <Dashboard\Dashboard.mqh>
//--- input parameters
input string               InpSymbol      =  NULL;             /* Symbol                     */ // Символ
input ENUM_TIMEFRAMES      InpTimeframe   =  PERIOD_CURRENT;   /* Timeframe                  */ // Таймфрейм
input uchar                InpLineWidth1  =  2;                /* Senior period Line Width   */ // Толщина линии для старшего периода
input uchar                InpLineWidth2  =  1;                /* Junior period Line Width   */ // Толщина линии для младшего периода
input bool                 InpAsSeries    =  true;             /* As Series flag             */ // Флаг серийности массивов буферов индикатора

//--- indicator buffers
double         BufferAO1[];
double         BufferClrAO1[];
double         BufferAO2[];
double         BufferClrAO2[];
//--- global variables
int handle_ao1;
int handle_ao2;
CMSTFIndicators indicators;      // Экземпляр объекта коллекции индикаторов
//--- переменные для панели
CDashboard *panel=NULL;          // Указатель на объект панели
int         mouse_bar_index;     // Индекс бара, с которого берутся данные
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Устанавливаем таймер с периодичностью в 1 секунду
   EventSetTimer(1);
//--- Назначаем рисуемым буферам 0 и 2 массивы BufferAO1 и BufferAO2 соответственно,
//--- а буферам 1 и 3 массивы цвета BufferClrAO1 и BufferClrAO2
   SetIndexBuffer(0,BufferAO1,INDICATOR_DATA);
   SetIndexBuffer(1,BufferClrAO1,INDICATOR_COLOR_INDEX);
   SetIndexBuffer(2,BufferAO2,INDICATOR_DATA);
   SetIndexBuffer(3,BufferClrAO2,INDICATOR_COLOR_INDEX);
//--- Устанавливаем ширину линий
   int w1=0,w2=0;
   if(InpTimeframe>Period())
     {
      w1=InpLineWidth2;
      w2=InpLineWidth1;
     }
   else
     {
      w1=InpLineWidth1;
      w2=InpLineWidth2;
     }
   PlotIndexSetInteger(0,PLOT_LINE_WIDTH,w1);
   PlotIndexSetInteger(1,PLOT_LINE_WIDTH,w2);
//--- sets indicator shift

//--- Устанавливаем флаги серийности массивам буферов индикатора (для теста, чтобы видно было отсутствие разницы)
   ArraySetAsSeries(BufferAO1,InpAsSeries);
   ArraySetAsSeries(BufferClrAO1,InpAsSeries);
   ArraySetAsSeries(BufferAO2,InpAsSeries);
   ArraySetAsSeries(BufferClrAO2,InpAsSeries);
   
//--- Создаём два индикатора одного типа
//--- Первый рассчитывается на текущих символе/периоде графика, а второй - на тех, что заданы в настройках
   handle_ao1=indicators.AddNewAO(NULL,PERIOD_CURRENT);
   handle_ao2=indicators.AddNewAO(InpSymbol,InpTimeframe);

//--- Если не удалось создать хэндлы индикаторов - возвращаем ошибку инициализации
   if(handle_ao1==INVALID_HANDLE || handle_ao2==INVALID_HANDLE)
      return INIT_FAILED;
//--- Устанавливаем описания линий индикатора из описаний буферов расчётной части созданных индикаторов
   indicators.SetPlotLabelFromBuffer(0,handle_ao1,0);
   indicators.SetPlotLabelFromBuffer(1,handle_ao2,0);
      
//--- Панель
//--- Создаём панель
   int width=247;
   panel=new CDashboard(1,20,20,width,264,0);
   if(panel==NULL)
     {
      Print("Error. Failed to create panel object");
      return INIT_FAILED;
     }
//--- Устанавливаем параметры шрифта
   panel.SetFontParams("Calibri",9);
//--- Отображаем панель с текстом в заголовке "Символ, Описание таймфрейма"
   panel.View(Symbol()+", "+StringSubstr(EnumToString(Period()),7));

//--- Создаём таблицу с идентификатором 0 для отображения в ней данных бара
   panel.CreateNewTable(0);
//--- Рисуем таблицу с идентификатором 0 на фоне панели
   panel.DrawGrid(0,2,20,6,2,18,width/2-2);

//--- Создаём таблицу с идентификатором 1 для отображения в ней данных индикатора 1
   panel.CreateNewTable(1);
//--- Получаем координату Y2 таблицы с идентификатором 0 и
//--- устанавливаем координату Y1 для таблицы с идентификатором 1
   int y1=panel.TableY2(0)+22;
//--- Рисуем таблицу с идентификатором 1 на фоне панели
   panel.DrawGrid(1,2,y1,2,2,18,width/2-2);

//--- Создаём таблицу с идентификатором 2 для отображения в ней данных индикатора 2
   panel.CreateNewTable(2);
//--- Получаем координату Y2 таблицы с идентификатором 1 и
//--- устанавливаем координату Y1 для таблицы с идентификатором 2
   int y2=panel.TableY2(1)+3;
//--- Рисуем таблицу с идентификатором 2 на фоне панели
   panel.DrawGrid(2,2,y2,3,2,18,width/2-2);
   
//--- Инициализируем переменную с индексом бара указателя мышки
   mouse_bar_index=0;
//--- Выводим на панель данные текущего бара
   DrawData(mouse_bar_index,TimeCurrent());

//--- Успешная инициализация
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- Уничтожаем таймер
   EventKillTimer();
//--- Если объект панели существует - удаляем его
   if(panel!=NULL)
      delete panel;
//--- Стираем все комментарии
   Comment("");
  }
//+------------------------------------------------------------------+
//| 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[])
  {
//--- Количество баров для расчёта
   int limit=rates_total-prev_calculated;
//--- Если limit > 1, значит это первый расчёт, либо изменение в истории
   if(limit>1)
     {
      //--- указываем для просчёта всю доступную историю
      limit=rates_total-1;
      /*
      // Если в индикаторе есть какие-либо буферы, в которых отображены другие расчёты (не мульти- индикаторы),
      // то здесь их нужно инициализировать "пустым" значением, установленным для этих буферов
      */
     }
//--- Рассчитываем все созданные мультисимвольные мультипериодные индикаторы
   if(!indicators.Calculate())
      return 0;

//--- Выводим на панель данные бара под курсором (либо текущий бар, если курсор за пределами графика)
   DrawData(mouse_bar_index,time[mouse_bar_index]);

//--- Из буферов рассчитанных индикаторов выводим данные в индикаторные буферы
   if(!indicators.DataToColorBuffer(NULL,PERIOD_CURRENT,handle_ao1,0,0,limit,BufferAO1,BufferClrAO1))
      return 0;
   if(!indicators.DataToColorBuffer(NULL,PERIOD_CURRENT,handle_ao2,0,0,limit,BufferAO2,BufferClrAO2))
      return 0;

//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| Timer function                                                   | 
//+------------------------------------------------------------------+
void OnTimer()
  {
//--- Вызываем таймер коллекции индикаторов
   indicators.OnTimer();
  }
//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- Работа с панелью
//--- Вызываем обработчик событий панели
   panel.OnChartEvent(id,lparam,dparam,sparam);

//--- Если курсор перемещается или щелчок по графику
   if(id==CHARTEVENT_MOUSE_MOVE || id==CHARTEVENT_CLICK)
     {
      //--- Объявляем переменные для записи в них координат времени и цены
      datetime time=0;
      double price=0;
      int wnd=0;
      //--- Если координаты курсора преобразованы в дату и время
      if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price))
        {
         //--- записываем индекс бара, где расположен курсор в глобальную переменную
         mouse_bar_index=iBarShift(Symbol(),PERIOD_CURRENT,time);
         //--- Выводим данные бара под курсором на панель
         DrawData(mouse_bar_index,time);
        }
     }

//--- Если получили пользовательское событие - выводим об этом сообщение в журнал
   if(id>CHARTEVENT_CUSTOM)
     {
      //--- Здесь может быть обработка щелчка по кнопке закрытия на панели
      PrintFormat("%s: Event id=%ld, object id (lparam): %lu, event message (sparam): %s",__FUNCTION__,id,lparam,sparam);
     }
  }
//+------------------------------------------------------------------+
//| Выводит данные с указанного индекса таймсерии на панель          |
//+------------------------------------------------------------------+
void DrawData(const int index,const datetime time)
  {
//--- Объявляем переменные для получения в них данных
   MqlRates rates[1];

//--- Если данные бара по указанному индексу получить не удалось - уходим
   if(CopyRates(Symbol(),PERIOD_CURRENT,index,1,rates)!=1)
      return;

//--- Устанавливаем параметры шрифта для заголовков данных бара и индикатора
   int  size=0;
   uint flags=0;
   uint angle=0;
   string name=panel.FontParams(size,flags,angle);
   panel.SetFontParams(name,9,FW_BOLD);
   panel.DrawText("Bar data ["+(string)index+"]",3,panel.TableY1(0)-16,clrMaroon,panel.Width()-6);
   panel.DrawText("Indicators data ["+(string)index+"]",3,panel.TableY1(1)-16,clrGreen,panel.Width()-6);
//--- Устанавливаем параметры шрифта для данных бара и индикатора
   panel.SetFontParams(name,9);

//--- Выводим на панель данные указанного бара в таблицу 0
   panel.DrawText("Date",  panel.CellX(0,0,0)+2, panel.CellY(0,0,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_DATE),     panel.CellX(0,0,1)+2, panel.CellY(0,0,1)+2,clrNONE,90);
   panel.DrawText("Time",  panel.CellX(0,1,0)+2, panel.CellY(0,1,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_MINUTES),  panel.CellX(0,1,1)+2, panel.CellY(0,1,1)+2,clrNONE,90);
   panel.DrawText("Open",  panel.CellX(0,2,0)+2, panel.CellY(0,2,0)+2); panel.DrawText(DoubleToString(rates[0].open,Digits()),      panel.CellX(0,2,1)+2, panel.CellY(0,2,1)+2,clrNONE,90);
   panel.DrawText("High",  panel.CellX(0,3,0)+2, panel.CellY(0,3,0)+2); panel.DrawText(DoubleToString(rates[0].high,Digits()),      panel.CellX(0,3,1)+2, panel.CellY(0,3,1)+2,clrNONE,90);
   panel.DrawText("Low",   panel.CellX(0,4,0)+2, panel.CellY(0,4,0)+2); panel.DrawText(DoubleToString(rates[0].low,Digits()),       panel.CellX(0,4,1)+2, panel.CellY(0,4,1)+2,clrNONE,90);
   panel.DrawText("Close", panel.CellX(0,5,0)+2, panel.CellY(0,5,0)+2); panel.DrawText(DoubleToString(rates[0].close,Digits()),     panel.CellX(0,5,1)+2, panel.CellY(0,5,1)+2,clrNONE,90);

//--- Выводим в таблицу 1 данные индикатора 1 с указанного бара в таблицу 1
   panel.DrawText(indicators.Title(handle_ao1), panel.CellX(1,0,0)+2, panel.CellY(1,0,0)+2);
   double value1=indicators.GetData(handle_ao1,0,0,index);
   string value_str1=(value1!=EMPTY_VALUE ? DoubleToString(value1,indicators.Digits(handle_ao1)) : " ");
   panel.DrawText(value_str1,panel.CellX(1,0,1)+2,panel.CellY(1,0,1)+2,clrNONE,110);
   
//--- Выводим описание состояния линии индикатора 1
   panel.DrawText("Line state", panel.CellX(1,1,0)+2, panel.CellY(1,1,0)+2);
   ENUM_LINE_STATE state1=indicators.BufferLineState(Symbol(),PERIOD_CURRENT,handle_ao1,0,0,index);
   panel.DrawText(BufferLineStateDescription(state1),panel.CellX(1,1,1)+2,panel.CellY(1,1,1)+2,clrNONE,110);
   
//--- Выводим в таблицу 2 данные индикатора 2 с указанного бара в таблицу 2
   panel.DrawText(indicators.Title(handle_ao2), panel.CellX(2,0,0)+2, panel.CellY(2,0,0)+2);
   double value2=indicators.GetDataTo(Symbol(),PERIOD_CURRENT,handle_ao2,0,0,index);
   string value_str2=(value2!=EMPTY_VALUE ? DoubleToString(value2,indicators.Digits(handle_ao2)) : " ");
   panel.DrawText(value_str2,panel.CellX(2,0,1)+2,panel.CellY(2,0,1)+2,clrNONE,110);
   
//--- Выводим описание состояния линии индикатора 2
   panel.DrawText("Line state", panel.CellX(2,1,0)+2, panel.CellY(2,1,0)+2);
   ENUM_LINE_STATE state2=indicators.BufferLineState(Symbol(),PERIOD_CURRENT,handle_ao2,0,0,index);
   panel.DrawText(BufferLineStateDescription(state2),panel.CellX(2,1,1)+2,panel.CellY(2,1,1)+2,clrNONE,110);
   
//--- Выводим описание соотношения линии индикатора 1 относительно линии индикатора 2
   double value21=indicators.GetDataTo(Symbol(),PERIOD_CURRENT,handle_ao2,0,0,index+1);
   ENUM_LINE_STATE stateR=indicators.BufferLineStateRelative(Symbol(),PERIOD_CURRENT,handle_ao1,0,0,index,value2,value21);
   string ma1=indicators.Name(handle_ao1);
   string ma2=indicators.Name(handle_ao2);
   string state_relative=
     (
      stateR==LINE_STATE_ABOVE      ? StringFormat("%s1 > %s2",ma1,ma2)   :
      stateR==LINE_STATE_BELOW      ? StringFormat("%s1 < %s2",ma1,ma2)           :
      stateR==LINE_STATE_CROSS_DOWN ? "Top-down crossing"   :
      stateR==LINE_STATE_CROSS_UP   ? "Bottom-up crossing"  :
      BufferLineStateDescription(stateR)
     );
   panel.DrawText(StringFormat("%s1 vs %s2",ma1,ma2), panel.CellX(2,2,0)+2, panel.CellY(2,2,0)+2);
   panel.DrawText(state_relative, panel.CellX(2,2,1)+2, panel.CellY(2,2,1)+2,clrNONE,110);
   
//--- Перерисовываем график для немедленного отображения всех изменений на панели
   ChartRedraw(ChartID());
  }
//+------------------------------------------------------------------+

После компиляции и запуска индикатора на графике М1 и выбранном периоде графика для расчёта М5, видим:



Индикатор Bears Power:

//+------------------------------------------------------------------+
//|                                           TestMSTFBearsPower.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_separate_window
#property indicator_buffers 2
#property indicator_plots   2
//--- enums

//--- plot Bears1
#property indicator_label1  "Bears1"
#property indicator_type1   DRAW_HISTOGRAM
#property indicator_color1  clrGray
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1

//--- plot Bears2
#property indicator_label2  "Bears2"
#property indicator_type2   DRAW_HISTOGRAM
#property indicator_color2  clrDodgerBlue
#property indicator_style2  STYLE_SOLID
#property indicator_width2  1

//--- includes
#include <IndMSTF\IndMSTF.mqh>
#include <Dashboard\Dashboard.mqh>
//--- input parameters
input string               InpSymbol      =  NULL;             /* Symbol                     */ // Символ
input ENUM_TIMEFRAMES      InpTimeframe   =  PERIOD_CURRENT;   /* Timeframe                  */ // Таймфрейм
input int                  InpPeriod      =  13;               /* Period                     */ // Период расчёта
input uchar                InpLineWidth1  =  2;                /* Senior period Line Width   */ // Толщина линии для старшего периода
input uchar                InpLineWidth2  =  1;                /* Junior period Line Width   */ // Толщина линии для младшего периода
input bool                 InpAsSeries    =  true;             /* As Series flag             */ // Флаг серийности массивов буферов индикатора

//--- indicator buffers
double         BufferBears1[];
double         BufferBears2[];
//--- global variables
int handle_bears1;
int handle_bears2;
CMSTFIndicators indicators;      // Экземпляр объекта коллекции индикаторов
//--- переменные для панели
CDashboard *panel=NULL;          // Указатель на объект панели
int         mouse_bar_index;     // Индекс бара, с которого берутся данные
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Устанавливаем таймер с периодичностью в 1 секунду
   EventSetTimer(1);
//--- Назначаем рисуемым буферам 0 и 1 массивы BufferMA1 и BufferMA2 соответственно
   SetIndexBuffer(0,BufferBears1,INDICATOR_DATA);
   SetIndexBuffer(1,BufferBears2,INDICATOR_DATA);
//--- Устанавливаем ширину линий
   int w1=0,w2=0;
   if(InpTimeframe>Period())
     {
      w1=InpLineWidth2;
      w2=InpLineWidth1;
     }
   else
     {
      w1=InpLineWidth1;
      w2=InpLineWidth2;
     }
   PlotIndexSetInteger(0,PLOT_LINE_WIDTH,w1);
   PlotIndexSetInteger(1,PLOT_LINE_WIDTH,w2);
//--- sets indicator shift

//--- Устанавливаем флаги серийности массивам буферов индикатора (для теста, чтобы видно было отсутствие разницы)
   ArraySetAsSeries(BufferBears1,InpAsSeries);
   ArraySetAsSeries(BufferBears2,InpAsSeries);
   
//--- Создаём два индикатора одного типа
//--- Первый рассчитывается на текущих символе/периоде графика, а второй - на тех, что заданы в настройках
   handle_bears1=indicators.AddNewBearsPower(NULL,PERIOD_CURRENT,InpPeriod);
   handle_bears2=indicators.AddNewBearsPower(InpSymbol,InpTimeframe,InpPeriod);

//--- Если не удалось создать хэндлы индикаторов - возвращаем ошибку инициализации
   if(handle_bears1==INVALID_HANDLE || handle_bears2==INVALID_HANDLE)
      return INIT_FAILED;
//--- Устанавливаем описания линий индикатора из описаний буферов расчётной части созданных индикаторов
   indicators.SetPlotLabelFromBuffer(0,handle_bears1,0);
   indicators.SetPlotLabelFromBuffer(1,handle_bears2,0);
      
//--- Панель
//--- Создаём панель
   int width=247;
   panel=new CDashboard(1,20,20,width,264,0);
   if(panel==NULL)
     {
      Print("Error. Failed to create panel object");
      return INIT_FAILED;
     }
//--- Устанавливаем параметры шрифта
   panel.SetFontParams("Calibri",9);
//--- Отображаем панель с текстом в заголовке "Символ, Описание таймфрейма"
   panel.View(Symbol()+", "+StringSubstr(EnumToString(Period()),7));

//--- Создаём таблицу с идентификатором 0 для отображения в ней данных бара
   panel.CreateNewTable(0);
//--- Рисуем таблицу с идентификатором 0 на фоне панели
   panel.DrawGrid(0,2,20,6,2,18,width/2-2);

//--- Создаём таблицу с идентификатором 1 для отображения в ней данных индикатора 1
   panel.CreateNewTable(1);
//--- Получаем координату Y2 таблицы с идентификатором 0 и
//--- устанавливаем координату Y1 для таблицы с идентификатором 1
   int y1=panel.TableY2(0)+22;
//--- Рисуем таблицу с идентификатором 1 на фоне панели
   panel.DrawGrid(1,2,y1,2,2,18,width/2-2);

//--- Создаём таблицу с идентификатором 2 для отображения в ней данных индикатора 2
   panel.CreateNewTable(2);
//--- Получаем координату Y2 таблицы с идентификатором 1 и
//--- устанавливаем координату Y1 для таблицы с идентификатором 2
   int y2=panel.TableY2(1)+3;
//--- Рисуем таблицу с идентификатором 2 на фоне панели
   panel.DrawGrid(2,2,y2,3,2,18,width/2-2);
   
//--- Инициализируем переменную с индексом бара указателя мышки
   mouse_bar_index=0;
//--- Выводим на панель данные текущего бара
   DrawData(mouse_bar_index,TimeCurrent());

//--- Успешная инициализация
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- Уничтожаем таймер
   EventKillTimer();
//--- Если объект панели существует - удаляем его
   if(panel!=NULL)
      delete panel;
//--- Стираем все комментарии
   Comment("");
  }
//+------------------------------------------------------------------+
//| 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[])
  {
//--- Количество баров для расчёта
   int limit=rates_total-prev_calculated;
//--- Если limit > 1, значит это первый расчёт, либо изменение в истории
   if(limit>1)
     {
      //--- указываем для просчёта всю доступную историю
      limit=rates_total-1;
      /*
      // Если в индикаторе есть какие-либо буферы, в которых отображены другие расчёты (не мульти- индикаторы),
      // то здесь их нужно инициализировать "пустым" значением, установленным для этих буферов
      */
     }
//--- Рассчитываем все созданные мультисимвольные мультипериодные индикаторы
   if(!indicators.Calculate())
      return 0;

//--- Выводим на панель данные бара под курсором (либо текущий бар, если курсор за пределами графика)
   DrawData(mouse_bar_index,time[mouse_bar_index]);

//--- Из буферов рассчитанных индикаторов выводим данные в индикаторные буферы
   if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_bears1,0,0,limit,BufferBears1))
      return 0;
   if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_bears2,0,0,limit,BufferBears2))
      return 0;

//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| Timer function                                                   | 
//+------------------------------------------------------------------+
void OnTimer()
  {
//--- Вызываем таймер коллекции индикаторов
   indicators.OnTimer();
  }
//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- Работа с панелью
//--- Вызываем обработчик событий панели
   panel.OnChartEvent(id,lparam,dparam,sparam);

//--- Если курсор перемещается или щелчок по графику
   if(id==CHARTEVENT_MOUSE_MOVE || id==CHARTEVENT_CLICK)
     {
      //--- Объявляем переменные для записи в них координат времени и цены
      datetime time=0;
      double price=0;
      int wnd=0;
      //--- Если координаты курсора преобразованы в дату и время
      if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price))
        {
         //--- записываем индекс бара, где расположен курсор в глобальную переменную
         mouse_bar_index=iBarShift(Symbol(),PERIOD_CURRENT,time);
         //--- Выводим данные бара под курсором на панель
         DrawData(mouse_bar_index,time);
        }
     }

//--- Если получили пользовательское событие - выводим об этом сообщение в журнал
   if(id>CHARTEVENT_CUSTOM)
     {
      //--- Здесь может быть обработка щелчка по кнопке закрытия на панели
      PrintFormat("%s: Event id=%ld, object id (lparam): %lu, event message (sparam): %s",__FUNCTION__,id,lparam,sparam);
     }
  }
//+------------------------------------------------------------------+
//| Выводит данные с указанного индекса таймсерии на панель          |
//+------------------------------------------------------------------+
void DrawData(const int index,const datetime time)
  {
//--- Объявляем переменные для получения в них данных
   MqlRates rates[1];

//--- Если данные бара по указанному индексу получить не удалось - уходим
   if(CopyRates(Symbol(),PERIOD_CURRENT,index,1,rates)!=1)
      return;

//--- Устанавливаем параметры шрифта для заголовков данных бара и индикатора
   int  size=0;
   uint flags=0;
   uint angle=0;
   string name=panel.FontParams(size,flags,angle);
   panel.SetFontParams(name,9,FW_BOLD);
   panel.DrawText("Bar data ["+(string)index+"]",3,panel.TableY1(0)-16,clrMaroon,panel.Width()-6);
   panel.DrawText("Indicators data ["+(string)index+"]",3,panel.TableY1(1)-16,clrGreen,panel.Width()-6);
//--- Устанавливаем параметры шрифта для данных бара и индикатора
   panel.SetFontParams(name,9);

//--- Выводим на панель данные указанного бара в таблицу 0
   panel.DrawText("Date",  panel.CellX(0,0,0)+2, panel.CellY(0,0,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_DATE),     panel.CellX(0,0,1)+2, panel.CellY(0,0,1)+2,clrNONE,90);
   panel.DrawText("Time",  panel.CellX(0,1,0)+2, panel.CellY(0,1,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_MINUTES),  panel.CellX(0,1,1)+2, panel.CellY(0,1,1)+2,clrNONE,90);
   panel.DrawText("Open",  panel.CellX(0,2,0)+2, panel.CellY(0,2,0)+2); panel.DrawText(DoubleToString(rates[0].open,Digits()),      panel.CellX(0,2,1)+2, panel.CellY(0,2,1)+2,clrNONE,90);
   panel.DrawText("High",  panel.CellX(0,3,0)+2, panel.CellY(0,3,0)+2); panel.DrawText(DoubleToString(rates[0].high,Digits()),      panel.CellX(0,3,1)+2, panel.CellY(0,3,1)+2,clrNONE,90);
   panel.DrawText("Low",   panel.CellX(0,4,0)+2, panel.CellY(0,4,0)+2); panel.DrawText(DoubleToString(rates[0].low,Digits()),       panel.CellX(0,4,1)+2, panel.CellY(0,4,1)+2,clrNONE,90);
   panel.DrawText("Close", panel.CellX(0,5,0)+2, panel.CellY(0,5,0)+2); panel.DrawText(DoubleToString(rates[0].close,Digits()),     panel.CellX(0,5,1)+2, panel.CellY(0,5,1)+2,clrNONE,90);

//--- Выводим в таблицу 1 данные индикатора 1 с указанного бара в таблицу 1
   panel.DrawText(indicators.Title(handle_bears1), panel.CellX(1,0,0)+2, panel.CellY(1,0,0)+2);
   double value1=indicators.GetData(handle_bears1,0,0,index);
   string value_str1=(value1!=EMPTY_VALUE ? DoubleToString(value1,indicators.Digits(handle_bears1)) : " ");
   panel.DrawText(value_str1,panel.CellX(1,0,1)+2,panel.CellY(1,0,1)+2,clrNONE,110);
   
//--- Выводим описание состояния линии индикатора 1
   panel.DrawText("Line state", panel.CellX(1,1,0)+2, panel.CellY(1,1,0)+2);
   ENUM_LINE_STATE state1=indicators.BufferLineState(Symbol(),PERIOD_CURRENT,handle_bears1,0,0,index);
   panel.DrawText(BufferLineStateDescription(state1),panel.CellX(1,1,1)+2,panel.CellY(1,1,1)+2,clrNONE,110);
   
//--- Выводим в таблицу 2 данные индикатора 2 с указанного бара в таблицу 2
   panel.DrawText(indicators.Title(handle_bears2), panel.CellX(2,0,0)+2, panel.CellY(2,0,0)+2);
   double value2=indicators.GetDataTo(Symbol(),PERIOD_CURRENT,handle_bears2,0,0,index);
   string value_str2=(value2!=EMPTY_VALUE ? DoubleToString(value2,indicators.Digits(handle_bears2)) : " ");
   panel.DrawText(value_str2,panel.CellX(2,0,1)+2,panel.CellY(2,0,1)+2,clrNONE,110);
   
//--- Выводим описание состояния линии индикатора 2
   panel.DrawText("Line state", panel.CellX(2,1,0)+2, panel.CellY(2,1,0)+2);
   ENUM_LINE_STATE state2=indicators.BufferLineState(Symbol(),PERIOD_CURRENT,handle_bears2,0,0,index);
   panel.DrawText(BufferLineStateDescription(state2),panel.CellX(2,1,1)+2,panel.CellY(2,1,1)+2,clrNONE,110);
   
//--- Выводим описание соотношения линии индикатора 1 относительно линии индикатора 2
   double value21=indicators.GetDataTo(Symbol(),PERIOD_CURRENT,handle_bears2,0,0,index+1);
   ENUM_LINE_STATE stateR=indicators.BufferLineStateRelative(Symbol(),PERIOD_CURRENT,handle_bears1,0,0,index,value2,value21);
   string ma1=indicators.Name(handle_bears1);
   string ma2=indicators.Name(handle_bears2);
   string state_relative=
     (
      stateR==LINE_STATE_ABOVE      ? StringFormat("%s1 > %s2",ma1,ma2)   :
      stateR==LINE_STATE_BELOW      ? StringFormat("%s1 < %s2",ma1,ma2)           :
      stateR==LINE_STATE_CROSS_DOWN ? "Top-down crossing"   :
      stateR==LINE_STATE_CROSS_UP   ? "Bottom-up crossing"  :
      BufferLineStateDescription(stateR)
     );
   panel.DrawText(StringFormat("%s1 vs %s2",ma1,ma2), panel.CellX(2,2,0)+2, panel.CellY(2,2,0)+2);
   panel.DrawText(state_relative, panel.CellX(2,2,1)+2, panel.CellY(2,2,1)+2,clrNONE,110);
   
//--- Перерисовываем график для немедленного отображения всех изменений на панели
   ChartRedraw(ChartID());
  }
//+------------------------------------------------------------------+

После компиляции и запуска индикатора на графике М1 и выбранном периоде графика для расчёта М5, видим:



Индикатор Bollinger Bands:

//+------------------------------------------------------------------+
//|                                   TestMSTFBollingerBands.mq5.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_chart_window
#property indicator_buffers 6
#property indicator_plots   6
//--- enums

//--- plot BandsUpper1
#property indicator_label1  "BandsUpper1"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrGreen
#property indicator_width1  1

//--- plot BandsLower1
#property indicator_label2  "BandsLower1"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrGreen
#property indicator_width2  1

//--- plot BandsMiddle1
#property indicator_label3  "BandsMiddle1"
#property indicator_type3   DRAW_LINE
#property indicator_color3  clrGreen
#property indicator_width3  1

//--- plot BandsUpper2
#property indicator_label4  "BandsUpper2"
#property indicator_type4  DRAW_LINE
#property indicator_color4  clrDodgerBlue
#property indicator_width4  1

//--- plot BandsLower2
#property indicator_label5  "BandsLower2"
#property indicator_type5   DRAW_LINE
#property indicator_color5  clrDodgerBlue
#property indicator_width5  1

//--- plot BandsMiddle2
#property indicator_label6  "BandsMiddle2"
#property indicator_type6   DRAW_LINE
#property indicator_color6  clrDodgerBlue
#property indicator_width6  1

//--- includes
#include <IndMSTF\IndMSTF.mqh>
#include <Dashboard\Dashboard.mqh>
//--- input parameters
input string               InpSymbol      =  NULL;             /* Symbol                     */ // Символ скользящей средней
input ENUM_TIMEFRAMES      InpTimeframe   =  PERIOD_CURRENT;   /* Timeframe                  */ // Таймфрейм скользящей средней
input int                  InpPeriod      =  20;               /* Period                     */ // Период расчёта
input int                  InpShift       =  0;                /* Shift                      */ // Сдвиг скользящей средней
input double               InpDeviation   =  2.0;              /* Deviation                  */ // Отклонение
input ENUM_APPLIED_PRICE   InpPrice       =  PRICE_CLOSE;      /* Applied Price              */ // Используемая цена для расчёта
input uchar                InpLineWidth1  =  2;                /* Senior period Line Width   */ // Толщина линии для старшего периода
input uchar                InpLineWidth2  =  1;                /* Junior period Line Width   */ // Толщина линии для младшего периода
input bool                 InpAsSeries    =  true;             /* As Series flag             */ // Флаг серийности массивов буферов индикатора

//--- indicator buffers
double         BufferBandsUp1[];
double         BufferBandsDn1[];
double         BufferBandsMd1[];
double         BufferBandsUp2[];
double         BufferBandsDn2[];
double         BufferBandsMd2[];
//--- global variables
int handle_bands1;
int handle_bands2;
CMSTFIndicators indicators;      // Экземпляр объекта коллекции индикаторов
//--- переменные для панели
CDashboard *panel=NULL;          // Указатель на объект панели
int         mouse_bar_index;     // Индекс бара, с которого берутся данные
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Устанавливаем таймер с периодичностью в 1 секунду
   EventSetTimer(1);
//--- Назначаем рисуемым буферам 0, 1 и 2 массивы BufferBandsUp1, BufferBandsDn1 и BufferBandsMd1 соответственно
   SetIndexBuffer(0,BufferBandsUp1,INDICATOR_DATA);
   SetIndexBuffer(1,BufferBandsDn1,INDICATOR_DATA);
   SetIndexBuffer(2,BufferBandsMd1,INDICATOR_DATA);
//--- Назначаем рисуемым буферам 3, 4 и 5 массивы BufferBandsUp2, BufferBandsDn2 и BufferBandsMd2 соответственно
   SetIndexBuffer(3,BufferBandsUp2,INDICATOR_DATA);
   SetIndexBuffer(4,BufferBandsDn2,INDICATOR_DATA);
   SetIndexBuffer(5,BufferBandsMd2,INDICATOR_DATA);
//--- Устанавливаем ширину линий
   int w1=0,w2=0;
   if(InpTimeframe>Period())
     {
      w1=InpLineWidth2;
      w2=InpLineWidth1;
     }
   else
     {
      w1=InpLineWidth1;
      w2=InpLineWidth2;
     }
   PlotIndexSetInteger(0,PLOT_LINE_WIDTH,w1);
   PlotIndexSetInteger(1,PLOT_LINE_WIDTH,w1);
   PlotIndexSetInteger(2,PLOT_LINE_WIDTH,w1);
   PlotIndexSetInteger(3,PLOT_LINE_WIDTH,w2);
   PlotIndexSetInteger(4,PLOT_LINE_WIDTH,w2);
   PlotIndexSetInteger(5,PLOT_LINE_WIDTH,w2);
//--- Устанавливаем флаги серийности массивам буферов индикатора (для теста, чтобы видно было отсутствие разницы)
   ArraySetAsSeries(BufferBandsUp1,InpAsSeries);
   ArraySetAsSeries(BufferBandsDn1,InpAsSeries);
   ArraySetAsSeries(BufferBandsMd1,InpAsSeries);
   ArraySetAsSeries(BufferBandsUp2,InpAsSeries);
   ArraySetAsSeries(BufferBandsDn2,InpAsSeries);
   ArraySetAsSeries(BufferBandsMd2,InpAsSeries);
   
//--- Создаём два индикатора одного типа
//--- Первый рассчитывается на текущих символе/периоде графика, а второй - на тех, что заданы в настройках
   handle_bands1=indicators.AddNewBands(NULL,PERIOD_CURRENT,InpPeriod,InpShift,InpDeviation,InpPrice);
   handle_bands2=indicators.AddNewBands(InpSymbol,InpTimeframe,InpPeriod,InpShift,InpDeviation,InpPrice);

//--- Если не удалось создать хэндлы индикаторов - возвращаем ошибку инициализации
   if(handle_bands1==INVALID_HANDLE || handle_bands2==INVALID_HANDLE)
      return INIT_FAILED;
//--- Устанавливаем описания линий индикатора из описаний буферов расчётной части созданных индикаторов
   indicators.SetPlotLabelFromBuffer(0,handle_bands1,0);
   indicators.SetPlotLabelFromBuffer(1,handle_bands1,1);
   indicators.SetPlotLabelFromBuffer(2,handle_bands1,2);
   indicators.SetPlotLabelFromBuffer(3,handle_bands2,0);
   indicators.SetPlotLabelFromBuffer(4,handle_bands2,1);
   indicators.SetPlotLabelFromBuffer(5,handle_bands2,2);
//--- Устанавливаем смещения линиям индикатора
   indicators.SetPlotShift(0,InpShift);
   indicators.SetPlotShift(1,InpShift);
   indicators.SetPlotShift(2,InpShift);
   indicators.SetPlotShift(3,InpShift);
   indicators.SetPlotShift(4,InpShift);
   indicators.SetPlotShift(5,InpShift);

//--- Панель
//--- Создаём панель
   int width=301;
   panel=new CDashboard(1,20,20,width,264);
   if(panel==NULL)
     {
      Print("Error. Failed to create panel object");
      return INIT_FAILED;
     }
//--- Устанавливаем параметры шрифта
   panel.SetFontParams("Calibri",9);
//--- Отображаем панель с текстом в заголовке "Символ, Описание таймфрейма"
   panel.View(Symbol()+", "+StringSubstr(EnumToString(Period()),7));

//--- Создаём таблицу с идентификатором 0 для отображения в ней данных бара
   panel.CreateNewTable(0);
//--- Рисуем таблицу с идентификатором 0 на фоне панели
   panel.DrawGrid(0,2,20,6,2,18,width/2-2);

//--- Создаём таблицу с идентификатором 1 для отображения в ней данных индикатора 1
   panel.CreateNewTable(1);
//--- Получаем координату Y2 таблицы с идентификатором 0 и
//--- устанавливаем координату Y1 для таблицы с идентификатором 1
   int y1=panel.TableY2(0)+22;
//--- Рисуем таблицу с идентификатором 1 на фоне панели
   panel.DrawGrid(1,2,y1,2,2,18,width/2-2);

//--- Создаём таблицу с идентификатором 2 для отображения в ней данных индикатора 2
   panel.CreateNewTable(2);
//--- Получаем координату Y2 таблицы с идентификатором 1 и
//--- устанавливаем координату Y1 для таблицы с идентификатором 2
   int y2=panel.TableY2(1)+3;
//--- Рисуем таблицу с идентификатором 2 на фоне панели
   panel.DrawGrid(2,2,y2,3,2,18,width/2-2);
   
//--- Инициализируем переменную с индексом бара указателя мышки
   mouse_bar_index=0;
//--- Выводим на панель данные текущего бара
   DrawData(mouse_bar_index,TimeCurrent());

//--- Успешная инициализация
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- Уничтожаем таймер
   EventKillTimer();
//--- Если объект панели существует - удаляем его
   if(panel!=NULL)
      delete panel;
//--- Стираем все комментарии
   Comment("");
  }
//+------------------------------------------------------------------+
//| 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[])
  {
//--- Количество баров для расчёта
   int limit=rates_total-prev_calculated;
//--- Если limit > 1, значит это первый расчёт, либо изменение в истории
   if(limit>1)
     {
      //--- указываем для просчёта всю доступную историю
      limit=rates_total-1;
      /*
      // Если в индикаторе есть какие-либо буферы, в которых отображены другие расчёты (не мульти- индикаторы),
      // то здесь их нужно инициализировать "пустым" значением, установленным для этих буферов
      */
     }
//--- Рассчитываем все созданные мультисимвольные мультипериодные индикаторы
   if(!indicators.Calculate())
      return 0;

//--- Выводим на панель данные бара под курсором (либо текущий бар, если курсор за пределами графика)
   DrawData(mouse_bar_index,time[mouse_bar_index]);

//--- Из буферов рассчитанных индикаторов выводим данные в индикаторные буферы
   if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_bands1,0,0,limit,BufferBandsUp1))
      return 0;
   if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_bands1,1,0,limit,BufferBandsDn1))
      return 0;
   if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_bands1,2,0,limit,BufferBandsMd1))
      return 0;
   
   if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_bands2,0,0,limit,BufferBandsUp2))
      return 0;
   if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_bands2,1,0,limit,BufferBandsDn2))
      return 0;
   if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_bands2,2,0,limit,BufferBandsMd2))
      return 0;

//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| Timer function                                                   | 
//+------------------------------------------------------------------+
void OnTimer()
  {
//--- Вызываем таймер коллекции индикаторов
   indicators.OnTimer();
  }
//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- Работа с панелью
//--- Вызываем обработчик событий панели
   panel.OnChartEvent(id,lparam,dparam,sparam);

//--- Если курсор перемещается или щелчок по графику
   if(id==CHARTEVENT_MOUSE_MOVE || id==CHARTEVENT_CLICK)
     {
      //--- Объявляем переменные для записи в них координат времени и цены
      datetime time=0;
      double price=0;
      int wnd=0;
      //--- Если координаты курсора преобразованы в дату и время
      if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price))
        {
         //--- записываем индекс бара, где расположен курсор в глобальную переменную
         mouse_bar_index=iBarShift(Symbol(),PERIOD_CURRENT,time);
         //--- Выводим данные бара под курсором на панель
         DrawData(mouse_bar_index,time);
        }
     }

//--- Если получили пользовательское событие - выводим об этом сообщение в журнал
   if(id>CHARTEVENT_CUSTOM)
     {
      //--- Здесь может быть обработка щелчка по кнопке закрытия на панели
      PrintFormat("%s: Event id=%ld, object id (lparam): %lu, event message (sparam): %s",__FUNCTION__,id,lparam,sparam);
     }
  }
//+------------------------------------------------------------------+
//| Выводит данные с указанного индекса таймсерии на панель          |
//+------------------------------------------------------------------+
void DrawData(const int index,const datetime time)
  {
//--- Объявляем переменные для получения в них данных
   MqlRates rates[1];

//--- Если данные бара по указанному индексу получить не удалось - уходим
   if(CopyRates(Symbol(),PERIOD_CURRENT,index,1,rates)!=1)
      return;

//--- Устанавливаем параметры шрифта для заголовков данных бара и индикатора
   int  size=0;
   uint flags=0;
   uint angle=0;
   string name=panel.FontParams(size,flags,angle);
   panel.SetFontParams(name,9,FW_BOLD);
   panel.DrawText("Bar data ["+(string)index+"]",3,panel.TableY1(0)-16,clrMaroon,panel.Width()-6);
   panel.DrawText("Indicators data ["+(string)index+"]",3,panel.TableY1(1)-16,clrGreen,panel.Width()-6);
//--- Устанавливаем параметры шрифта для данных бара и индикатора
   panel.SetFontParams(name,9);

//--- Выводим на панель данные указанного бара в таблицу 0
   panel.DrawText("Date",  panel.CellX(0,0,0)+2, panel.CellY(0,0,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_DATE),     panel.CellX(0,0,1)+2, panel.CellY(0,0,1)+2,clrNONE,90);
   panel.DrawText("Time",  panel.CellX(0,1,0)+2, panel.CellY(0,1,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_MINUTES),  panel.CellX(0,1,1)+2, panel.CellY(0,1,1)+2,clrNONE,90);
   panel.DrawText("Open",  panel.CellX(0,2,0)+2, panel.CellY(0,2,0)+2); panel.DrawText(DoubleToString(rates[0].open,Digits()),      panel.CellX(0,2,1)+2, panel.CellY(0,2,1)+2,clrNONE,90);
   panel.DrawText("High",  panel.CellX(0,3,0)+2, panel.CellY(0,3,0)+2); panel.DrawText(DoubleToString(rates[0].high,Digits()),      panel.CellX(0,3,1)+2, panel.CellY(0,3,1)+2,clrNONE,90);
   panel.DrawText("Low",   panel.CellX(0,4,0)+2, panel.CellY(0,4,0)+2); panel.DrawText(DoubleToString(rates[0].low,Digits()),       panel.CellX(0,4,1)+2, panel.CellY(0,4,1)+2,clrNONE,90);
   panel.DrawText("Close", panel.CellX(0,5,0)+2, panel.CellY(0,5,0)+2); panel.DrawText(DoubleToString(rates[0].close,Digits()),     panel.CellX(0,5,1)+2, panel.CellY(0,5,1)+2,clrNONE,90);

//--- Выводим в таблицу 1 данные индикатора 1 с указанного бара в таблицу 1
   panel.DrawText(indicators.Title(handle_bands1), panel.CellX(1,0,0)+2, panel.CellY(1,0,0)+2);
   double value1=indicators.GetData(handle_bands1,0,0,index);
   string value_str1=(value1!=EMPTY_VALUE ? DoubleToString(value1,indicators.Digits(handle_bands1)) : " ");
   panel.DrawText(value_str1,panel.CellX(1,0,1)+2,panel.CellY(1,0,1)+2,clrNONE,150);
   
//--- Выводим описание состояния линии индикатора 1
   panel.DrawText("Line state", panel.CellX(1,1,0)+2, panel.CellY(1,1,0)+2);
   ENUM_LINE_STATE state1=indicators.BufferLineState(Symbol(),PERIOD_CURRENT,handle_bands1,0,0,index);
   panel.DrawText(BufferLineStateDescription(state1),panel.CellX(1,1,1)+2,panel.CellY(1,1,1)+2,clrNONE,150);
   
//--- Выводим в таблицу 2 данные индикатора 2 с указанного бара в таблицу 2
   panel.DrawText(indicators.Title(handle_bands2), panel.CellX(2,0,0)+2, panel.CellY(2,0,0)+2);
   double value2=indicators.GetDataTo(Symbol(),PERIOD_CURRENT,handle_bands2,0,0,index);
   string value_str2=(value2!=EMPTY_VALUE ? DoubleToString(value2,indicators.Digits(handle_bands2)) : " ");
   panel.DrawText(value_str2,panel.CellX(2,0,1)+2,panel.CellY(2,0,1)+2,clrNONE,150);
   
//--- Выводим описание состояния линии индикатора 2
   panel.DrawText("Line state", panel.CellX(2,1,0)+2, panel.CellY(2,1,0)+2);
   ENUM_LINE_STATE state2=indicators.BufferLineState(Symbol(),PERIOD_CURRENT,handle_bands2,0,0,index);
   panel.DrawText(BufferLineStateDescription(state2),panel.CellX(2,1,1)+2,panel.CellY(2,1,1)+2,clrNONE,150);
   
//--- Выводим описание соотношения линии индикатора 1 относительно линии индикатора 2
   double value21=indicators.GetDataTo(Symbol(),PERIOD_CURRENT,handle_bands2,0,0,index+1);
   ENUM_LINE_STATE stateR=indicators.BufferLineStateRelative(Symbol(),PERIOD_CURRENT,handle_bands1,0,0,index,value2,value21);
   string ma1=indicators.Name(handle_bands1);
   string ma2=indicators.Name(handle_bands2);
   string state_relative=
     (
      stateR==LINE_STATE_ABOVE      ? StringFormat("%s1 > %s2",ma1,ma2)   :
      stateR==LINE_STATE_BELOW      ? StringFormat("%s1 < %s2",ma1,ma2)           :
      stateR==LINE_STATE_CROSS_DOWN ? "Top-down crossing"   :
      stateR==LINE_STATE_CROSS_UP   ? "Bottom-up crossing"  :
      BufferLineStateDescription(stateR)
     );
   panel.DrawText(StringFormat("%s1 vs %s2",ma1,ma2), panel.CellX(2,2,0)+2, panel.CellY(2,2,0)+2);
   panel.DrawText(state_relative, panel.CellX(2,2,1)+2, panel.CellY(2,2,1)+2,clrNONE,150);
   
//--- Перерисовываем график для немедленного отображения всех изменений на панели
   ChartRedraw(ChartID());
  }
//+------------------------------------------------------------------+

После компиляции и запуска индикатора на графике М1 и выбранном периоде графика для расчёта М5, видим:



Индикатор Bulls Power:

//+------------------------------------------------------------------+
//|                                           TestMSTFBullsPower.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_separate_window
#property indicator_buffers 2
#property indicator_plots   2
//--- enums

//--- plot Bulls1
#property indicator_label1  "Bulls1"
#property indicator_type1   DRAW_HISTOGRAM
#property indicator_color1  clrGray
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1

//--- plot Bulls2
#property indicator_label2  "Bulls2"
#property indicator_type2   DRAW_HISTOGRAM
#property indicator_color2  clrDodgerBlue
#property indicator_style2  STYLE_SOLID
#property indicator_width2  1

//--- includes
#include <IndMSTF\IndMSTF.mqh>
#include <Dashboard\Dashboard.mqh>
//--- input parameters
input string               InpSymbol      =  NULL;             /* Symbol                     */ // Символ
input ENUM_TIMEFRAMES      InpTimeframe   =  PERIOD_CURRENT;   /* Timeframe                  */ // Таймфрейм
input int                  InpPeriod      =  13;               /* Period                     */ // Период расчёта
input uchar                InpLineWidth1  =  2;                /* Senior period Line Width   */ // Толщина линии для старшего периода
input uchar                InpLineWidth2  =  1;                /* Junior period Line Width   */ // Толщина линии для младшего периода
input bool                 InpAsSeries    =  true;             /* As Series flag             */ // Флаг серийности массивов буферов индикатора

//--- indicator buffers
double         BufferBulls1[];
double         BufferBulls2[];
//--- global variables
int handle_bulls1;
int handle_bulls2;
CMSTFIndicators indicators;      // Экземпляр объекта коллекции индикаторов
//--- переменные для панели
CDashboard *panel=NULL;          // Указатель на объект панели
int         mouse_bar_index;     // Индекс бара, с которого берутся данные
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Устанавливаем таймер с периодичностью в 1 секунду
   EventSetTimer(1);
//--- Назначаем рисуемым буферам 0 и 1 массивы BufferMA1 и BufferMA2 соответственно
   SetIndexBuffer(0,BufferBulls1,INDICATOR_DATA);
   SetIndexBuffer(1,BufferBulls2,INDICATOR_DATA);
//--- Устанавливаем ширину линий
   int w1=0,w2=0;
   if(InpTimeframe>Period())
     {
      w1=InpLineWidth2;
      w2=InpLineWidth1;
     }
   else
     {
      w1=InpLineWidth1;
      w2=InpLineWidth2;
     }
   PlotIndexSetInteger(0,PLOT_LINE_WIDTH,w1);
   PlotIndexSetInteger(1,PLOT_LINE_WIDTH,w2);
//--- sets indicator shift

//--- Устанавливаем флаги серийности массивам буферов индикатора (для теста, чтобы видно было отсутствие разницы)
   ArraySetAsSeries(BufferBulls1,InpAsSeries);
   ArraySetAsSeries(BufferBulls2,InpAsSeries);
   
//--- Создаём два индикатора одного типа
//--- Первый рассчитывается на текущих символе/периоде графика, а второй - на тех, что заданы в настройках
   handle_bulls1=indicators.AddNewBullsPower(NULL,PERIOD_CURRENT,InpPeriod);
   handle_bulls2=indicators.AddNewBullsPower(InpSymbol,InpTimeframe,InpPeriod);

//--- Если не удалось создать хэндлы индикаторов - возвращаем ошибку инициализации
   if(handle_bulls1==INVALID_HANDLE || handle_bulls2==INVALID_HANDLE)
      return INIT_FAILED;
//--- Устанавливаем описания линий индикатора из описаний буферов расчётной части созданных индикаторов
   indicators.SetPlotLabelFromBuffer(0,handle_bulls1,0);
   indicators.SetPlotLabelFromBuffer(1,handle_bulls2,0);
      
//--- Панель
//--- Создаём панель
   int width=247;
   panel=new CDashboard(1,20,20,width,264,0);
   if(panel==NULL)
     {
      Print("Error. Failed to create panel object");
      return INIT_FAILED;
     }
//--- Устанавливаем параметры шрифта
   panel.SetFontParams("Calibri",9);
//--- Отображаем панель с текстом в заголовке "Символ, Описание таймфрейма"
   panel.View(Symbol()+", "+StringSubstr(EnumToString(Period()),7));

//--- Создаём таблицу с идентификатором 0 для отображения в ней данных бара
   panel.CreateNewTable(0);
//--- Рисуем таблицу с идентификатором 0 на фоне панели
   panel.DrawGrid(0,2,20,6,2,18,width/2-2);

//--- Создаём таблицу с идентификатором 1 для отображения в ней данных индикатора 1
   panel.CreateNewTable(1);
//--- Получаем координату Y2 таблицы с идентификатором 0 и
//--- устанавливаем координату Y1 для таблицы с идентификатором 1
   int y1=panel.TableY2(0)+22;
//--- Рисуем таблицу с идентификатором 1 на фоне панели
   panel.DrawGrid(1,2,y1,2,2,18,width/2-2);

//--- Создаём таблицу с идентификатором 2 для отображения в ней данных индикатора 2
   panel.CreateNewTable(2);
//--- Получаем координату Y2 таблицы с идентификатором 1 и
//--- устанавливаем координату Y1 для таблицы с идентификатором 2
   int y2=panel.TableY2(1)+3;
//--- Рисуем таблицу с идентификатором 2 на фоне панели
   panel.DrawGrid(2,2,y2,3,2,18,width/2-2);
   
//--- Инициализируем переменную с индексом бара указателя мышки
   mouse_bar_index=0;
//--- Выводим на панель данные текущего бара
   DrawData(mouse_bar_index,TimeCurrent());

//--- Успешная инициализация
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- Уничтожаем таймер
   EventKillTimer();
//--- Если объект панели существует - удаляем его
   if(panel!=NULL)
      delete panel;
//--- Стираем все комментарии
   Comment("");
  }
//+------------------------------------------------------------------+
//| 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[])
  {
//--- Количество баров для расчёта
   int limit=rates_total-prev_calculated;
//--- Если limit > 1, значит это первый расчёт, либо изменение в истории
   if(limit>1)
     {
      //--- указываем для просчёта всю доступную историю
      limit=rates_total-1;
      /*
      // Если в индикаторе есть какие-либо буферы, в которых отображены другие расчёты (не мульти- индикаторы),
      // то здесь их нужно инициализировать "пустым" значением, установленным для этих буферов
      */
     }
//--- Рассчитываем все созданные мультисимвольные мультипериодные индикаторы
   if(!indicators.Calculate())
      return 0;

//--- Выводим на панель данные бара под курсором (либо текущий бар, если курсор за пределами графика)
   DrawData(mouse_bar_index,time[mouse_bar_index]);

//--- Из буферов рассчитанных индикаторов выводим данные в индикаторные буферы
   if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_bulls1,0,0,limit,BufferBulls1))
      return 0;
   if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_bulls2,0,0,limit,BufferBulls2))
      return 0;

//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| Timer function                                                   | 
//+------------------------------------------------------------------+
void OnTimer()
  {
//--- Вызываем таймер коллекции индикаторов
   indicators.OnTimer();
  }
//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- Работа с панелью
//--- Вызываем обработчик событий панели
   panel.OnChartEvent(id,lparam,dparam,sparam);

//--- Если курсор перемещается или щелчок по графику
   if(id==CHARTEVENT_MOUSE_MOVE || id==CHARTEVENT_CLICK)
     {
      //--- Объявляем переменные для записи в них координат времени и цены
      datetime time=0;
      double price=0;
      int wnd=0;
      //--- Если координаты курсора преобразованы в дату и время
      if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price))
        {
         //--- записываем индекс бара, где расположен курсор в глобальную переменную
         mouse_bar_index=iBarShift(Symbol(),PERIOD_CURRENT,time);
         //--- Выводим данные бара под курсором на панель
         DrawData(mouse_bar_index,time);
        }
     }

//--- Если получили пользовательское событие - выводим об этом сообщение в журнал
   if(id>CHARTEVENT_CUSTOM)
     {
      //--- Здесь может быть обработка щелчка по кнопке закрытия на панели
      PrintFormat("%s: Event id=%ld, object id (lparam): %lu, event message (sparam): %s",__FUNCTION__,id,lparam,sparam);
     }
  }
//+------------------------------------------------------------------+
//| Выводит данные с указанного индекса таймсерии на панель          |
//+------------------------------------------------------------------+
void DrawData(const int index,const datetime time)
  {
//--- Объявляем переменные для получения в них данных
   MqlRates rates[1];

//--- Если данные бара по указанному индексу получить не удалось - уходим
   if(CopyRates(Symbol(),PERIOD_CURRENT,index,1,rates)!=1)
      return;

//--- Устанавливаем параметры шрифта для заголовков данных бара и индикатора
   int  size=0;
   uint flags=0;
   uint angle=0;
   string name=panel.FontParams(size,flags,angle);
   panel.SetFontParams(name,9,FW_BOLD);
   panel.DrawText("Bar data ["+(string)index+"]",3,panel.TableY1(0)-16,clrMaroon,panel.Width()-6);
   panel.DrawText("Indicators data ["+(string)index+"]",3,panel.TableY1(1)-16,clrGreen,panel.Width()-6);
//--- Устанавливаем параметры шрифта для данных бара и индикатора
   panel.SetFontParams(name,9);

//--- Выводим на панель данные указанного бара в таблицу 0
   panel.DrawText("Date",  panel.CellX(0,0,0)+2, panel.CellY(0,0,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_DATE),     panel.CellX(0,0,1)+2, panel.CellY(0,0,1)+2,clrNONE,90);
   panel.DrawText("Time",  panel.CellX(0,1,0)+2, panel.CellY(0,1,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_MINUTES),  panel.CellX(0,1,1)+2, panel.CellY(0,1,1)+2,clrNONE,90);
   panel.DrawText("Open",  panel.CellX(0,2,0)+2, panel.CellY(0,2,0)+2); panel.DrawText(DoubleToString(rates[0].open,Digits()),      panel.CellX(0,2,1)+2, panel.CellY(0,2,1)+2,clrNONE,90);
   panel.DrawText("High",  panel.CellX(0,3,0)+2, panel.CellY(0,3,0)+2); panel.DrawText(DoubleToString(rates[0].high,Digits()),      panel.CellX(0,3,1)+2, panel.CellY(0,3,1)+2,clrNONE,90);
   panel.DrawText("Low",   panel.CellX(0,4,0)+2, panel.CellY(0,4,0)+2); panel.DrawText(DoubleToString(rates[0].low,Digits()),       panel.CellX(0,4,1)+2, panel.CellY(0,4,1)+2,clrNONE,90);
   panel.DrawText("Close", panel.CellX(0,5,0)+2, panel.CellY(0,5,0)+2); panel.DrawText(DoubleToString(rates[0].close,Digits()),     panel.CellX(0,5,1)+2, panel.CellY(0,5,1)+2,clrNONE,90);

//--- Выводим в таблицу 1 данные индикатора 1 с указанного бара в таблицу 1
   panel.DrawText(indicators.Title(handle_bulls1), panel.CellX(1,0,0)+2, panel.CellY(1,0,0)+2);
   double value1=indicators.GetData(handle_bulls1,0,0,index);
   string value_str1=(value1!=EMPTY_VALUE ? DoubleToString(value1,indicators.Digits(handle_bulls1)) : " ");
   panel.DrawText(value_str1,panel.CellX(1,0,1)+2,panel.CellY(1,0,1)+2,clrNONE,110);
   
//--- Выводим описание состояния линии индикатора 1
   panel.DrawText("Line state", panel.CellX(1,1,0)+2, panel.CellY(1,1,0)+2);
   ENUM_LINE_STATE state1=indicators.BufferLineState(Symbol(),PERIOD_CURRENT,handle_bulls1,0,0,index);
   panel.DrawText(BufferLineStateDescription(state1),panel.CellX(1,1,1)+2,panel.CellY(1,1,1)+2,clrNONE,110);
   
//--- Выводим в таблицу 2 данные индикатора 2 с указанного бара в таблицу 2
   panel.DrawText(indicators.Title(handle_bulls2), panel.CellX(2,0,0)+2, panel.CellY(2,0,0)+2);
   double value2=indicators.GetDataTo(Symbol(),PERIOD_CURRENT,handle_bulls2,0,0,index);
   string value_str2=(value2!=EMPTY_VALUE ? DoubleToString(value2,indicators.Digits(handle_bulls2)) : " ");
   panel.DrawText(value_str2,panel.CellX(2,0,1)+2,panel.CellY(2,0,1)+2,clrNONE,110);
   
//--- Выводим описание состояния линии индикатора 2
   panel.DrawText("Line state", panel.CellX(2,1,0)+2, panel.CellY(2,1,0)+2);
   ENUM_LINE_STATE state2=indicators.BufferLineState(Symbol(),PERIOD_CURRENT,handle_bulls2,0,0,index);
   panel.DrawText(BufferLineStateDescription(state2),panel.CellX(2,1,1)+2,panel.CellY(2,1,1)+2,clrNONE,110);
   
//--- Выводим описание соотношения линии индикатора 1 относительно линии индикатора 2
   double value21=indicators.GetDataTo(Symbol(),PERIOD_CURRENT,handle_bulls2,0,0,index+1);
   ENUM_LINE_STATE stateR=indicators.BufferLineStateRelative(Symbol(),PERIOD_CURRENT,handle_bulls1,0,0,index,value2,value21);
   string ma1=indicators.Name(handle_bulls1);
   string ma2=indicators.Name(handle_bulls2);
   string state_relative=
     (
      stateR==LINE_STATE_ABOVE      ? StringFormat("%s1 > %s2",ma1,ma2)   :
      stateR==LINE_STATE_BELOW      ? StringFormat("%s1 < %s2",ma1,ma2)           :
      stateR==LINE_STATE_CROSS_DOWN ? "Top-down crossing"   :
      stateR==LINE_STATE_CROSS_UP   ? "Bottom-up crossing"  :
      BufferLineStateDescription(stateR)
     );
   panel.DrawText(StringFormat("%s1 vs %s2",ma1,ma2), panel.CellX(2,2,0)+2, panel.CellY(2,2,0)+2);
   panel.DrawText(state_relative, panel.CellX(2,2,1)+2, panel.CellY(2,2,1)+2,clrNONE,110);
   
//--- Перерисовываем график для немедленного отображения всех изменений на панели
   ChartRedraw(ChartID());
  }
//+------------------------------------------------------------------+

После компиляции и запуска индикатора на графике М1 и выбранном периоде графика для расчёта М5, видим:


Здесь тоже пришлось попереключать периоды графика для прорисовки.


Индикатор Chaikin Oscillator:

//+------------------------------------------------------------------+
//|                                    TestMSTFChaikinOscillator.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_separate_window
#property indicator_buffers 2
#property indicator_plots   2
//--- enums

//--- plot CHO1
#property indicator_label1  "CHO1"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrSeaGreen
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1

//--- plot CHO2
#property indicator_label2  "CHO2"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrDodgerBlue
#property indicator_style2  STYLE_SOLID
#property indicator_width2  1

//--- includes
#include <IndMSTF\IndMSTF.mqh>
#include <Dashboard\Dashboard.mqh>
//--- input parameters
input string               InpSymbol      =  NULL;             /* Symbol                     */ // Символ
input ENUM_TIMEFRAMES      InpTimeframe   =  PERIOD_CURRENT;   /* Timeframe                  */ // Таймфрейм
input int                  InpFastMAPeriod=  3;                /* Fast MA Period             */ // Период быстрого MA
input int                  InpSlowMAPeriod=  10;               /* Slow MA Period             */ // Период медленного MA
input ENUM_MA_METHOD       InpMethod      =  MODE_EMA;         /* Method                     */ // Метод расчёта
input ENUM_APPLIED_VOLUME  InpVolume      =  VOLUME_TICK;      /* Applied Volume             */ // Объёмы
input uchar                InpLineWidth1  =  2;                /* Senior period Line Width   */ // Толщина линии для старшего периода
input uchar                InpLineWidth2  =  1;                /* Junior period Line Width   */ // Толщина линии для младшего периода
input bool                 InpAsSeries    =  true;             /* As Series flag             */ // Флаг серийности массивов буферов индикатора

//--- indicator buffers
double         BufferCHO1[];
double         BufferCHO2[];
//--- global variables
int handle_cho1;
int handle_cho2;
CMSTFIndicators indicators;      // Экземпляр объекта коллекции индикаторов
//--- переменные для панели
CDashboard *panel=NULL;          // Указатель на объект панели
int         mouse_bar_index;     // Индекс бара, с которого берутся данные
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Устанавливаем таймер с периодичностью в 1 секунду
   EventSetTimer(1);
//--- Назначаем рисуемым буферам 0 и 1 массивы BufferMA1 и BufferMA2 соответственно
   SetIndexBuffer(0,BufferCHO1,INDICATOR_DATA);
   SetIndexBuffer(1,BufferCHO2,INDICATOR_DATA);
//--- Устанавливаем ширину линий
   int w1=0,w2=0;
   if(InpTimeframe>Period())
     {
      w1=InpLineWidth2;
      w2=InpLineWidth1;
     }
   else
     {
      w1=InpLineWidth1;
      w2=InpLineWidth2;
     }
   PlotIndexSetInteger(0,PLOT_LINE_WIDTH,w1);
   PlotIndexSetInteger(1,PLOT_LINE_WIDTH,w2);
//--- sets indicator shift

//--- Устанавливаем флаги серийности массивам буферов индикатора (для теста, чтобы видно было отсутствие разницы)
   ArraySetAsSeries(BufferCHO1,InpAsSeries);
   ArraySetAsSeries(BufferCHO2,InpAsSeries);
   
//--- Создаём два индикатора одного типа
//--- Первый рассчитывается на текущих символе/периоде графика, а второй - на тех, что заданы в настройках
   handle_cho1=indicators.AddNewChaikin(NULL,PERIOD_CURRENT,InpFastMAPeriod,InpSlowMAPeriod,InpMethod,InpVolume);
   handle_cho2=indicators.AddNewChaikin(InpSymbol,InpTimeframe,InpFastMAPeriod,InpSlowMAPeriod,InpMethod,InpVolume);

//--- Если не удалось создать хэндлы индикаторов - возвращаем ошибку инициализации
   if(handle_cho1==INVALID_HANDLE || handle_cho2==INVALID_HANDLE)
      return INIT_FAILED;
//--- Устанавливаем описания линий индикатора из описаний буферов расчётной части созданных индикаторов
   indicators.SetPlotLabelFromBuffer(0,handle_cho1,0);
   indicators.SetPlotLabelFromBuffer(1,handle_cho2,0);
      
//--- Панель
//--- Создаём панель
   int width=247;
   panel=new CDashboard(1,20,20,width,264,0);
   if(panel==NULL)
     {
      Print("Error. Failed to create panel object");
      return INIT_FAILED;
     }
//--- Устанавливаем параметры шрифта
   panel.SetFontParams("Calibri",9);
//--- Отображаем панель с текстом в заголовке "Символ, Описание таймфрейма"
   panel.View(Symbol()+", "+StringSubstr(EnumToString(Period()),7));

//--- Создаём таблицу с идентификатором 0 для отображения в ней данных бара
   panel.CreateNewTable(0);
//--- Рисуем таблицу с идентификатором 0 на фоне панели
   panel.DrawGrid(0,2,20,6,2,18,width/2-2);

//--- Создаём таблицу с идентификатором 1 для отображения в ней данных индикатора 1
   panel.CreateNewTable(1);
//--- Получаем координату Y2 таблицы с идентификатором 0 и
//--- устанавливаем координату Y1 для таблицы с идентификатором 1
   int y1=panel.TableY2(0)+22;
//--- Рисуем таблицу с идентификатором 1 на фоне панели
   panel.DrawGrid(1,2,y1,2,2,18,width/2-2);

//--- Создаём таблицу с идентификатором 2 для отображения в ней данных индикатора 2
   panel.CreateNewTable(2);
//--- Получаем координату Y2 таблицы с идентификатором 1 и
//--- устанавливаем координату Y1 для таблицы с идентификатором 2
   int y2=panel.TableY2(1)+3;
//--- Рисуем таблицу с идентификатором 2 на фоне панели
   panel.DrawGrid(2,2,y2,3,2,18,width/2-2);
   
//--- Инициализируем переменную с индексом бара указателя мышки
   mouse_bar_index=0;
//--- Выводим на панель данные текущего бара
   DrawData(mouse_bar_index,TimeCurrent());

//--- Успешная инициализация
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- Уничтожаем таймер
   EventKillTimer();
//--- Если объект панели существует - удаляем его
   if(panel!=NULL)
      delete panel;
//--- Стираем все комментарии
   Comment("");
  }
//+------------------------------------------------------------------+
//| 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[])
  {
//--- Количество баров для расчёта
   int limit=rates_total-prev_calculated;
//--- Если limit > 1, значит это первый расчёт, либо изменение в истории
   if(limit>1)
     {
      //--- указываем для просчёта всю доступную историю
      limit=rates_total-1;
      /*
      // Если в индикаторе есть какие-либо буферы, в которых отображены другие расчёты (не мульти- индикаторы),
      // то здесь их нужно инициализировать "пустым" значением, установленным для этих буферов
      */
     }
//--- Рассчитываем все созданные мультисимвольные мультипериодные индикаторы
   if(!indicators.Calculate())
      return 0;

//--- Выводим на панель данные бара под курсором (либо текущий бар, если курсор за пределами графика)
   DrawData(mouse_bar_index,time[mouse_bar_index]);

//--- Из буферов рассчитанных индикаторов выводим данные в индикаторные буферы
   if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_cho1,0,0,limit,BufferCHO1))
      return 0;
   if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_cho2,0,0,limit,BufferCHO2))
      return 0;

//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| Timer function                                                   | 
//+------------------------------------------------------------------+
void OnTimer()
  {
//--- Вызываем таймер коллекции индикаторов
   indicators.OnTimer();
  }
//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- Работа с панелью
//--- Вызываем обработчик событий панели
   panel.OnChartEvent(id,lparam,dparam,sparam);

//--- Если курсор перемещается или щелчок по графику
   if(id==CHARTEVENT_MOUSE_MOVE || id==CHARTEVENT_CLICK)
     {
      //--- Объявляем переменные для записи в них координат времени и цены
      datetime time=0;
      double price=0;
      int wnd=0;
      //--- Если координаты курсора преобразованы в дату и время
      if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price))
        {
         //--- записываем индекс бара, где расположен курсор в глобальную переменную
         mouse_bar_index=iBarShift(Symbol(),PERIOD_CURRENT,time);
         //--- Выводим данные бара под курсором на панель
         DrawData(mouse_bar_index,time);
        }
     }

//--- Если получили пользовательское событие - выводим об этом сообщение в журнал
   if(id>CHARTEVENT_CUSTOM)
     {
      //--- Здесь может быть обработка щелчка по кнопке закрытия на панели
      PrintFormat("%s: Event id=%ld, object id (lparam): %lu, event message (sparam): %s",__FUNCTION__,id,lparam,sparam);
     }
  }
//+------------------------------------------------------------------+
//| Выводит данные с указанного индекса таймсерии на панель          |
//+------------------------------------------------------------------+
void DrawData(const int index,const datetime time)
  {
//--- Объявляем переменные для получения в них данных
   MqlRates rates[1];

//--- Если данные бара по указанному индексу получить не удалось - уходим
   if(CopyRates(Symbol(),PERIOD_CURRENT,index,1,rates)!=1)
      return;

//--- Устанавливаем параметры шрифта для заголовков данных бара и индикатора
   int  size=0;
   uint flags=0;
   uint angle=0;
   string name=panel.FontParams(size,flags,angle);
   panel.SetFontParams(name,9,FW_BOLD);
   panel.DrawText("Bar data ["+(string)index+"]",3,panel.TableY1(0)-16,clrMaroon,panel.Width()-6);
   panel.DrawText("Indicators data ["+(string)index+"]",3,panel.TableY1(1)-16,clrGreen,panel.Width()-6);
//--- Устанавливаем параметры шрифта для данных бара и индикатора
   panel.SetFontParams(name,9);

//--- Выводим на панель данные указанного бара в таблицу 0
   panel.DrawText("Date",  panel.CellX(0,0,0)+2, panel.CellY(0,0,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_DATE),     panel.CellX(0,0,1)+2, panel.CellY(0,0,1)+2,clrNONE,90);
   panel.DrawText("Time",  panel.CellX(0,1,0)+2, panel.CellY(0,1,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_MINUTES),  panel.CellX(0,1,1)+2, panel.CellY(0,1,1)+2,clrNONE,90);
   panel.DrawText("Open",  panel.CellX(0,2,0)+2, panel.CellY(0,2,0)+2); panel.DrawText(DoubleToString(rates[0].open,Digits()),      panel.CellX(0,2,1)+2, panel.CellY(0,2,1)+2,clrNONE,90);
   panel.DrawText("High",  panel.CellX(0,3,0)+2, panel.CellY(0,3,0)+2); panel.DrawText(DoubleToString(rates[0].high,Digits()),      panel.CellX(0,3,1)+2, panel.CellY(0,3,1)+2,clrNONE,90);
   panel.DrawText("Low",   panel.CellX(0,4,0)+2, panel.CellY(0,4,0)+2); panel.DrawText(DoubleToString(rates[0].low,Digits()),       panel.CellX(0,4,1)+2, panel.CellY(0,4,1)+2,clrNONE,90);
   panel.DrawText("Close", panel.CellX(0,5,0)+2, panel.CellY(0,5,0)+2); panel.DrawText(DoubleToString(rates[0].close,Digits()),     panel.CellX(0,5,1)+2, panel.CellY(0,5,1)+2,clrNONE,90);

//--- Выводим в таблицу 1 данные индикатора 1 с указанного бара в таблицу 1
   panel.DrawText(indicators.Title(handle_cho1), panel.CellX(1,0,0)+2, panel.CellY(1,0,0)+2);
   double value1=indicators.GetData(handle_cho1,0,0,index);
   string value_str1=(value1!=EMPTY_VALUE ? DoubleToString(value1,indicators.Digits(handle_cho1)) : " ");
   panel.DrawText(value_str1,panel.CellX(1,0,1)+2,panel.CellY(1,0,1)+2,clrNONE,110);
   
//--- Выводим описание состояния линии индикатора 1
   panel.DrawText("Line state", panel.CellX(1,1,0)+2, panel.CellY(1,1,0)+2);
   ENUM_LINE_STATE state1=indicators.BufferLineState(Symbol(),PERIOD_CURRENT,handle_cho1,0,0,index);
   panel.DrawText(BufferLineStateDescription(state1),panel.CellX(1,1,1)+2,panel.CellY(1,1,1)+2,clrNONE,110);
   
//--- Выводим в таблицу 2 данные индикатора 2 с указанного бара в таблицу 2
   panel.DrawText(indicators.Title(handle_cho2), panel.CellX(2,0,0)+2, panel.CellY(2,0,0)+2);
   double value2=indicators.GetDataTo(Symbol(),PERIOD_CURRENT,handle_cho2,0,0,index);
   string value_str2=(value2!=EMPTY_VALUE ? DoubleToString(value2,indicators.Digits(handle_cho2)) : " ");
   panel.DrawText(value_str2,panel.CellX(2,0,1)+2,panel.CellY(2,0,1)+2,clrNONE,110);
   
//--- Выводим описание состояния линии индикатора 2
   panel.DrawText("Line state", panel.CellX(2,1,0)+2, panel.CellY(2,1,0)+2);
   ENUM_LINE_STATE state2=indicators.BufferLineState(Symbol(),PERIOD_CURRENT,handle_cho2,0,0,index);
   panel.DrawText(BufferLineStateDescription(state2),panel.CellX(2,1,1)+2,panel.CellY(2,1,1)+2,clrNONE,110);
   
//--- Выводим описание соотношения линии индикатора 1 относительно линии индикатора 2
   double value21=indicators.GetDataTo(Symbol(),PERIOD_CURRENT,handle_cho2,0,0,index+1);
   ENUM_LINE_STATE stateR=indicators.BufferLineStateRelative(Symbol(),PERIOD_CURRENT,handle_cho1,0,0,index,value2,value21);
   string ma1=indicators.Name(handle_cho1);
   string ma2=indicators.Name(handle_cho2);
   string state_relative=
     (
      stateR==LINE_STATE_ABOVE      ? StringFormat("%s1 > %s2",ma1,ma2)   :
      stateR==LINE_STATE_BELOW      ? StringFormat("%s1 < %s2",ma1,ma2)           :
      stateR==LINE_STATE_CROSS_DOWN ? "Top-down crossing"   :
      stateR==LINE_STATE_CROSS_UP   ? "Bottom-up crossing"  :
      BufferLineStateDescription(stateR)
     );
   panel.DrawText(StringFormat("%s1 vs %s2",ma1,ma2), panel.CellX(2,2,0)+2, panel.CellY(2,2,0)+2);
   panel.DrawText(state_relative, panel.CellX(2,2,1)+2, panel.CellY(2,2,1)+2,clrNONE,110);
   
//--- Перерисовываем график для немедленного отображения всех изменений на панели
   ChartRedraw(ChartID());
  }
//+------------------------------------------------------------------+

После компиляции и запуска индикатора на графике М1 и выбранном периоде графика для расчёта М5, видим:



Индикатор Commodity Channel Index:

//+------------------------------------------------------------------+
//|                                TestMSTFCommodityChannelIndex.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_separate_window
#property indicator_buffers 2
#property indicator_plots   2
//--- enums

//--- plot CCI1
#property indicator_label1  "CCI1"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrSeaGreen
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1

//--- plot CCI2
#property indicator_label2  "CCI2"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrDodgerBlue
#property indicator_style2  STYLE_SOLID
#property indicator_width2  1

//--- includes
#include <IndMSTF\IndMSTF.mqh>
#include <Dashboard\Dashboard.mqh>
//--- input parameters
input string               InpSymbol      =  NULL;             /* Symbol                     */ // Символ
input ENUM_TIMEFRAMES      InpTimeframe   =  PERIOD_CURRENT;   /* Timeframe                  */ // Таймфрейм
input int                  InpPeriod      =  14;               /* Period                     */ // Период расчёта
input ENUM_APPLIED_PRICE   InpPrice       =  PRICE_TYPICAL;    /* Applied Price              */ // Цена расчёта
input uchar                InpLineWidth1  =  2;                /* Senior period Line Width   */ // Толщина линии для старшего периода
input uchar                InpLineWidth2  =  1;                /* Junior period Line Width   */ // Толщина линии для младшего периода
input bool                 InpAsSeries    =  true;             /* As Series flag             */ // Флаг серийности массивов буферов индикатора

//--- indicator buffers
double         BufferCCI1[];
double         BufferCCI2[];
//--- global variables
int handle_cci1;
int handle_cci2;
CMSTFIndicators indicators;      // Экземпляр объекта коллекции индикаторов
//--- переменные для панели
CDashboard *panel=NULL;          // Указатель на объект панели
int         mouse_bar_index;     // Индекс бара, с которого берутся данные
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Устанавливаем таймер с периодичностью в 1 секунду
   EventSetTimer(1);
//--- Назначаем рисуемым буферам 0 и 1 массивы BufferMA1 и BufferMA2 соответственно
   SetIndexBuffer(0,BufferCCI1,INDICATOR_DATA);
   SetIndexBuffer(1,BufferCCI2,INDICATOR_DATA);
//--- Устанавливаем ширину линий
   int w1=0,w2=0;
   if(InpTimeframe>Period())
     {
      w1=InpLineWidth2;
      w2=InpLineWidth1;
     }
   else
     {
      w1=InpLineWidth1;
      w2=InpLineWidth2;
     }
   PlotIndexSetInteger(0,PLOT_LINE_WIDTH,w1);
   PlotIndexSetInteger(1,PLOT_LINE_WIDTH,w2);
//--- sets indicator shift

//--- Устанавливаем флаги серийности массивам буферов индикатора (для теста, чтобы видно было отсутствие разницы)
   ArraySetAsSeries(BufferCCI1,InpAsSeries);
   ArraySetAsSeries(BufferCCI2,InpAsSeries);
   
//--- Создаём два индикатора одного типа
//--- Первый рассчитывается на текущих символе/периоде графика, а второй - на тех, что заданы в настройках
   handle_cci1=indicators.AddNewCCI(NULL,PERIOD_CURRENT,InpPeriod,InpPrice);
   handle_cci2=indicators.AddNewCCI(InpSymbol,InpTimeframe,InpPeriod,InpPrice);

//--- Если не удалось создать хэндлы индикаторов - возвращаем ошибку инициализации
   if(handle_cci1==INVALID_HANDLE || handle_cci2==INVALID_HANDLE)
      return INIT_FAILED;
//--- Устанавливаем описания линий индикатора из описаний буферов расчётной части созданных индикаторов
   indicators.SetPlotLabelFromBuffer(0,handle_cci1,0);
   indicators.SetPlotLabelFromBuffer(1,handle_cci2,0);
      
//--- Панель
//--- Создаём панель
   int width=247;
   panel=new CDashboard(1,20,20,width,264,0);
   if(panel==NULL)
     {
      Print("Error. Failed to create panel object");
      return INIT_FAILED;
     }
//--- Устанавливаем параметры шрифта
   panel.SetFontParams("Calibri",9);
//--- Отображаем панель с текстом в заголовке "Символ, Описание таймфрейма"
   panel.View(Symbol()+", "+StringSubstr(EnumToString(Period()),7));

//--- Создаём таблицу с идентификатором 0 для отображения в ней данных бара
   panel.CreateNewTable(0);
//--- Рисуем таблицу с идентификатором 0 на фоне панели
   panel.DrawGrid(0,2,20,6,2,18,width/2-2);

//--- Создаём таблицу с идентификатором 1 для отображения в ней данных индикатора 1
   panel.CreateNewTable(1);
//--- Получаем координату Y2 таблицы с идентификатором 0 и
//--- устанавливаем координату Y1 для таблицы с идентификатором 1
   int y1=panel.TableY2(0)+22;
//--- Рисуем таблицу с идентификатором 1 на фоне панели
   panel.DrawGrid(1,2,y1,2,2,18,width/2-2);

//--- Создаём таблицу с идентификатором 2 для отображения в ней данных индикатора 2
   panel.CreateNewTable(2);
//--- Получаем координату Y2 таблицы с идентификатором 1 и
//--- устанавливаем координату Y1 для таблицы с идентификатором 2
   int y2=panel.TableY2(1)+3;
//--- Рисуем таблицу с идентификатором 2 на фоне панели
   panel.DrawGrid(2,2,y2,3,2,18,width/2-2);
   
//--- Инициализируем переменную с индексом бара указателя мышки
   mouse_bar_index=0;
//--- Выводим на панель данные текущего бара
   DrawData(mouse_bar_index,TimeCurrent());

//--- Успешная инициализация
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- Уничтожаем таймер
   EventKillTimer();
//--- Если объект панели существует - удаляем его
   if(panel!=NULL)
      delete panel;
//--- Стираем все комментарии
   Comment("");
  }
//+------------------------------------------------------------------+
//| 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[])
  {
//--- Количество баров для расчёта
   int limit=rates_total-prev_calculated;
//--- Если limit > 1, значит это первый расчёт, либо изменение в истории
   if(limit>1)
     {
      //--- указываем для просчёта всю доступную историю
      limit=rates_total-1;
      /*
      // Если в индикаторе есть какие-либо буферы, в которых отображены другие расчёты (не мульти- индикаторы),
      // то здесь их нужно инициализировать "пустым" значением, установленным для этих буферов
      */
     }
//--- Рассчитываем все созданные мультисимвольные мультипериодные индикаторы
   if(!indicators.Calculate())
      return 0;

//--- Выводим на панель данные бара под курсором (либо текущий бар, если курсор за пределами графика)
   DrawData(mouse_bar_index,time[mouse_bar_index]);

//--- Из буферов рассчитанных индикаторов выводим данные в индикаторные буферы
   if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_cci1,0,0,limit,BufferCCI1))
      return 0;
   if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_cci2,0,0,limit,BufferCCI2))
      return 0;

//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| Timer function                                                   | 
//+------------------------------------------------------------------+
void OnTimer()
  {
//--- Вызываем таймер коллекции индикаторов
   indicators.OnTimer();
  }
//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- Работа с панелью
//--- Вызываем обработчик событий панели
   panel.OnChartEvent(id,lparam,dparam,sparam);

//--- Если курсор перемещается или щелчок по графику
   if(id==CHARTEVENT_MOUSE_MOVE || id==CHARTEVENT_CLICK)
     {
      //--- Объявляем переменные для записи в них координат времени и цены
      datetime time=0;
      double price=0;
      int wnd=0;
      //--- Если координаты курсора преобразованы в дату и время
      if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price))
        {
         //--- записываем индекс бара, где расположен курсор в глобальную переменную
         mouse_bar_index=iBarShift(Symbol(),PERIOD_CURRENT,time);
         //--- Выводим данные бара под курсором на панель
         DrawData(mouse_bar_index,time);
        }
     }

//--- Если получили пользовательское событие - выводим об этом сообщение в журнал
   if(id>CHARTEVENT_CUSTOM)
     {
      //--- Здесь может быть обработка щелчка по кнопке закрытия на панели
      PrintFormat("%s: Event id=%ld, object id (lparam): %lu, event message (sparam): %s",__FUNCTION__,id,lparam,sparam);
     }
  }
//+------------------------------------------------------------------+
//| Выводит данные с указанного индекса таймсерии на панель          |
//+------------------------------------------------------------------+
void DrawData(const int index,const datetime time)
  {
//--- Объявляем переменные для получения в них данных
   MqlRates rates[1];

//--- Если данные бара по указанному индексу получить не удалось - уходим
   if(CopyRates(Symbol(),PERIOD_CURRENT,index,1,rates)!=1)
      return;

//--- Устанавливаем параметры шрифта для заголовков данных бара и индикатора
   int  size=0;
   uint flags=0;
   uint angle=0;
   string name=panel.FontParams(size,flags,angle);
   panel.SetFontParams(name,9,FW_BOLD);
   panel.DrawText("Bar data ["+(string)index+"]",3,panel.TableY1(0)-16,clrMaroon,panel.Width()-6);
   panel.DrawText("Indicators data ["+(string)index+"]",3,panel.TableY1(1)-16,clrGreen,panel.Width()-6);
//--- Устанавливаем параметры шрифта для данных бара и индикатора
   panel.SetFontParams(name,9);

//--- Выводим на панель данные указанного бара в таблицу 0
   panel.DrawText("Date",  panel.CellX(0,0,0)+2, panel.CellY(0,0,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_DATE),     panel.CellX(0,0,1)+2, panel.CellY(0,0,1)+2,clrNONE,90);
   panel.DrawText("Time",  panel.CellX(0,1,0)+2, panel.CellY(0,1,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_MINUTES),  panel.CellX(0,1,1)+2, panel.CellY(0,1,1)+2,clrNONE,90);
   panel.DrawText("Open",  panel.CellX(0,2,0)+2, panel.CellY(0,2,0)+2); panel.DrawText(DoubleToString(rates[0].open,Digits()),      panel.CellX(0,2,1)+2, panel.CellY(0,2,1)+2,clrNONE,90);
   panel.DrawText("High",  panel.CellX(0,3,0)+2, panel.CellY(0,3,0)+2); panel.DrawText(DoubleToString(rates[0].high,Digits()),      panel.CellX(0,3,1)+2, panel.CellY(0,3,1)+2,clrNONE,90);
   panel.DrawText("Low",   panel.CellX(0,4,0)+2, panel.CellY(0,4,0)+2); panel.DrawText(DoubleToString(rates[0].low,Digits()),       panel.CellX(0,4,1)+2, panel.CellY(0,4,1)+2,clrNONE,90);
   panel.DrawText("Close", panel.CellX(0,5,0)+2, panel.CellY(0,5,0)+2); panel.DrawText(DoubleToString(rates[0].close,Digits()),     panel.CellX(0,5,1)+2, panel.CellY(0,5,1)+2,clrNONE,90);

//--- Выводим в таблицу 1 данные индикатора 1 с указанного бара в таблицу 1
   panel.DrawText(indicators.Title(handle_cci1), panel.CellX(1,0,0)+2, panel.CellY(1,0,0)+2);
   double value1=indicators.GetData(handle_cci1,0,0,index);
   string value_str1=(value1!=EMPTY_VALUE ? DoubleToString(value1,indicators.Digits(handle_cci1)) : " ");
   panel.DrawText(value_str1,panel.CellX(1,0,1)+2,panel.CellY(1,0,1)+2,clrNONE,110);
   
//--- Выводим описание состояния линии индикатора 1
   panel.DrawText("Line state", panel.CellX(1,1,0)+2, panel.CellY(1,1,0)+2);
   ENUM_LINE_STATE state1=indicators.BufferLineState(Symbol(),PERIOD_CURRENT,handle_cci1,0,0,index);
   panel.DrawText(BufferLineStateDescription(state1),panel.CellX(1,1,1)+2,panel.CellY(1,1,1)+2,clrNONE,110);
   
//--- Выводим в таблицу 2 данные индикатора 2 с указанного бара в таблицу 2
   panel.DrawText(indicators.Title(handle_cci2), panel.CellX(2,0,0)+2, panel.CellY(2,0,0)+2);
   double value2=indicators.GetDataTo(Symbol(),PERIOD_CURRENT,handle_cci2,0,0,index);
   string value_str2=(value2!=EMPTY_VALUE ? DoubleToString(value2,indicators.Digits(handle_cci2)) : " ");
   panel.DrawText(value_str2,panel.CellX(2,0,1)+2,panel.CellY(2,0,1)+2,clrNONE,110);
   
//--- Выводим описание состояния линии индикатора 2
   panel.DrawText("Line state", panel.CellX(2,1,0)+2, panel.CellY(2,1,0)+2);
   ENUM_LINE_STATE state2=indicators.BufferLineState(Symbol(),PERIOD_CURRENT,handle_cci2,0,0,index);
   panel.DrawText(BufferLineStateDescription(state2),panel.CellX(2,1,1)+2,panel.CellY(2,1,1)+2,clrNONE,110);
   
//--- Выводим описание соотношения линии индикатора 1 относительно линии индикатора 2
   double value21=indicators.GetDataTo(Symbol(),PERIOD_CURRENT,handle_cci2,0,0,index+1);
   ENUM_LINE_STATE stateR=indicators.BufferLineStateRelative(Symbol(),PERIOD_CURRENT,handle_cci1,0,0,index,value2,value21);
   string ma1=indicators.Name(handle_cci1);
   string ma2=indicators.Name(handle_cci2);
   string state_relative=
     (
      stateR==LINE_STATE_ABOVE      ? StringFormat("%s1 > %s2",ma1,ma2)   :
      stateR==LINE_STATE_BELOW      ? StringFormat("%s1 < %s2",ma1,ma2)           :
      stateR==LINE_STATE_CROSS_DOWN ? "Top-down crossing"   :
      stateR==LINE_STATE_CROSS_UP   ? "Bottom-up crossing"  :
      BufferLineStateDescription(stateR)
     );
   panel.DrawText(StringFormat("%s1 vs %s2",ma1,ma2), panel.CellX(2,2,0)+2, panel.CellY(2,2,0)+2);
   panel.DrawText(state_relative, panel.CellX(2,2,1)+2, panel.CellY(2,2,1)+2,clrNONE,110);
   
//--- Перерисовываем график для немедленного отображения всех изменений на панели
   ChartRedraw(ChartID());
  }
//+------------------------------------------------------------------+

После компиляции и запуска индикатора на графике М1 и выбранном периоде графика для расчёта М5, видим:



Индикатор DeMarker:

//+------------------------------------------------------------------+
//|                                             TestMSTFDeMarker.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_separate_window
#property indicator_buffers 2
#property indicator_plots   2
//--- enums

//--- plot DeM1
#property indicator_label1  "DeM1"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrSeaGreen
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1

//--- plot DeM2
#property indicator_label2  "DeM2"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrDodgerBlue
#property indicator_style2  STYLE_SOLID
#property indicator_width2  1

//--- includes
#include <IndMSTF\IndMSTF.mqh>
#include <Dashboard\Dashboard.mqh>
//--- input parameters
input string               InpSymbol      =  NULL;             /* Symbol                     */ // Символ
input ENUM_TIMEFRAMES      InpTimeframe   =  PERIOD_CURRENT;   /* Timeframe                  */ // Таймфрейм
input int                  InpPeriod      =  14;               /* Period                     */ // Период расчёта
input uchar                InpLineWidth1  =  2;                /* Senior period Line Width   */ // Толщина линии для старшего периода
input uchar                InpLineWidth2  =  1;                /* Junior period Line Width   */ // Толщина линии для младшего периода
input bool                 InpAsSeries    =  true;             /* As Series flag             */ // Флаг серийности массивов буферов индикатора

//--- indicator buffers
double         BufferDeM1[];
double         BufferDeM2[];
//--- global variables
int handle_dem1;
int handle_dem2;
CMSTFIndicators indicators;      // Экземпляр объекта коллекции индикаторов
//--- переменные для панели
CDashboard *panel=NULL;          // Указатель на объект панели
int         mouse_bar_index;     // Индекс бара, с которого берутся данные
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Устанавливаем таймер с периодичностью в 1 секунду
   EventSetTimer(1);
//--- Назначаем рисуемым буферам 0 и 1 массивы BufferMA1 и BufferMA2 соответственно
   SetIndexBuffer(0,BufferDeM1,INDICATOR_DATA);
   SetIndexBuffer(1,BufferDeM2,INDICATOR_DATA);
//--- Устанавливаем ширину линий
   int w1=0,w2=0;
   if(InpTimeframe>Period())
     {
      w1=InpLineWidth2;
      w2=InpLineWidth1;
     }
   else
     {
      w1=InpLineWidth1;
      w2=InpLineWidth2;
     }
   PlotIndexSetInteger(0,PLOT_LINE_WIDTH,w1);
   PlotIndexSetInteger(1,PLOT_LINE_WIDTH,w2);
//--- sets indicator shift

//--- Устанавливаем флаги серийности массивам буферов индикатора (для теста, чтобы видно было отсутствие разницы)
   ArraySetAsSeries(BufferDeM1,InpAsSeries);
   ArraySetAsSeries(BufferDeM2,InpAsSeries);
   
//--- Создаём два индикатора одного типа
//--- Первый рассчитывается на текущих символе/периоде графика, а второй - на тех, что заданы в настройках
   handle_dem1=indicators.AddNewDeMarker(NULL,PERIOD_CURRENT,InpPeriod);
   handle_dem2=indicators.AddNewDeMarker(InpSymbol,InpTimeframe,InpPeriod);

//--- Если не удалось создать хэндлы индикаторов - возвращаем ошибку инициализации
   if(handle_dem1==INVALID_HANDLE || handle_dem2==INVALID_HANDLE)
      return INIT_FAILED;
//--- Устанавливаем описания линий индикатора из описаний буферов расчётной части созданных индикаторов
   indicators.SetPlotLabelFromBuffer(0,handle_dem1,0);
   indicators.SetPlotLabelFromBuffer(1,handle_dem2,0);
      
//--- Панель
//--- Создаём панель
   int width=247;
   panel=new CDashboard(1,20,20,width,264,0);
   if(panel==NULL)
     {
      Print("Error. Failed to create panel object");
      return INIT_FAILED;
     }
//--- Устанавливаем параметры шрифта
   panel.SetFontParams("Calibri",9);
//--- Отображаем панель с текстом в заголовке "Символ, Описание таймфрейма"
   panel.View(Symbol()+", "+StringSubstr(EnumToString(Period()),7));

//--- Создаём таблицу с идентификатором 0 для отображения в ней данных бара
   panel.CreateNewTable(0);
//--- Рисуем таблицу с идентификатором 0 на фоне панели
   panel.DrawGrid(0,2,20,6,2,18,width/2-2);

//--- Создаём таблицу с идентификатором 1 для отображения в ней данных индикатора 1
   panel.CreateNewTable(1);
//--- Получаем координату Y2 таблицы с идентификатором 0 и
//--- устанавливаем координату Y1 для таблицы с идентификатором 1
   int y1=panel.TableY2(0)+22;
//--- Рисуем таблицу с идентификатором 1 на фоне панели
   panel.DrawGrid(1,2,y1,2,2,18,width/2-2);

//--- Создаём таблицу с идентификатором 2 для отображения в ней данных индикатора 2
   panel.CreateNewTable(2);
//--- Получаем координату Y2 таблицы с идентификатором 1 и
//--- устанавливаем координату Y1 для таблицы с идентификатором 2
   int y2=panel.TableY2(1)+3;
//--- Рисуем таблицу с идентификатором 2 на фоне панели
   panel.DrawGrid(2,2,y2,3,2,18,width/2-2);
   
//--- Инициализируем переменную с индексом бара указателя мышки
   mouse_bar_index=0;
//--- Выводим на панель данные текущего бара
   DrawData(mouse_bar_index,TimeCurrent());

//--- Успешная инициализация
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- Уничтожаем таймер
   EventKillTimer();
//--- Если объект панели существует - удаляем его
   if(panel!=NULL)
      delete panel;
//--- Стираем все комментарии
   Comment("");
  }
//+------------------------------------------------------------------+
//| 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[])
  {
//--- Количество баров для расчёта
   int limit=rates_total-prev_calculated;
//--- Если limit > 1, значит это первый расчёт, либо изменение в истории
   if(limit>1)
     {
      //--- указываем для просчёта всю доступную историю
      limit=rates_total-1;
      /*
      // Если в индикаторе есть какие-либо буферы, в которых отображены другие расчёты (не мульти- индикаторы),
      // то здесь их нужно инициализировать "пустым" значением, установленным для этих буферов
      */
     }
//--- Рассчитываем все созданные мультисимвольные мультипериодные индикаторы
   if(!indicators.Calculate())
      return 0;

//--- Выводим на панель данные бара под курсором (либо текущий бар, если курсор за пределами графика)
   DrawData(mouse_bar_index,time[mouse_bar_index]);

//--- Из буферов рассчитанных индикаторов выводим данные в индикаторные буферы
   if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_dem1,0,0,limit,BufferDeM1))
      return 0;
   if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_dem2,0,0,limit,BufferDeM2))
      return 0;

//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| Timer function                                                   | 
//+------------------------------------------------------------------+
void OnTimer()
  {
//--- Вызываем таймер коллекции индикаторов
   indicators.OnTimer();
  }
//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- Работа с панелью
//--- Вызываем обработчик событий панели
   panel.OnChartEvent(id,lparam,dparam,sparam);

//--- Если курсор перемещается или щелчок по графику
   if(id==CHARTEVENT_MOUSE_MOVE || id==CHARTEVENT_CLICK)
     {
      //--- Объявляем переменные для записи в них координат времени и цены
      datetime time=0;
      double price=0;
      int wnd=0;
      //--- Если координаты курсора преобразованы в дату и время
      if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price))
        {
         //--- записываем индекс бара, где расположен курсор в глобальную переменную
         mouse_bar_index=iBarShift(Symbol(),PERIOD_CURRENT,time);
         //--- Выводим данные бара под курсором на панель
         DrawData(mouse_bar_index,time);
        }
     }

//--- Если получили пользовательское событие - выводим об этом сообщение в журнал
   if(id>CHARTEVENT_CUSTOM)
     {
      //--- Здесь может быть обработка щелчка по кнопке закрытия на панели
      PrintFormat("%s: Event id=%ld, object id (lparam): %lu, event message (sparam): %s",__FUNCTION__,id,lparam,sparam);
     }
  }
//+------------------------------------------------------------------+
//| Выводит данные с указанного индекса таймсерии на панель          |
//+------------------------------------------------------------------+
void DrawData(const int index,const datetime time)
  {
//--- Объявляем переменные для получения в них данных
   MqlRates rates[1];

//--- Если данные бара по указанному индексу получить не удалось - уходим
   if(CopyRates(Symbol(),PERIOD_CURRENT,index,1,rates)!=1)
      return;

//--- Устанавливаем параметры шрифта для заголовков данных бара и индикатора
   int  size=0;
   uint flags=0;
   uint angle=0;
   string name=panel.FontParams(size,flags,angle);
   panel.SetFontParams(name,9,FW_BOLD);
   panel.DrawText("Bar data ["+(string)index+"]",3,panel.TableY1(0)-16,clrMaroon,panel.Width()-6);
   panel.DrawText("Indicators data ["+(string)index+"]",3,panel.TableY1(1)-16,clrGreen,panel.Width()-6);
//--- Устанавливаем параметры шрифта для данных бара и индикатора
   panel.SetFontParams(name,9);

//--- Выводим на панель данные указанного бара в таблицу 0
   panel.DrawText("Date",  panel.CellX(0,0,0)+2, panel.CellY(0,0,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_DATE),     panel.CellX(0,0,1)+2, panel.CellY(0,0,1)+2,clrNONE,90);
   panel.DrawText("Time",  panel.CellX(0,1,0)+2, panel.CellY(0,1,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_MINUTES),  panel.CellX(0,1,1)+2, panel.CellY(0,1,1)+2,clrNONE,90);
   panel.DrawText("Open",  panel.CellX(0,2,0)+2, panel.CellY(0,2,0)+2); panel.DrawText(DoubleToString(rates[0].open,Digits()),      panel.CellX(0,2,1)+2, panel.CellY(0,2,1)+2,clrNONE,90);
   panel.DrawText("High",  panel.CellX(0,3,0)+2, panel.CellY(0,3,0)+2); panel.DrawText(DoubleToString(rates[0].high,Digits()),      panel.CellX(0,3,1)+2, panel.CellY(0,3,1)+2,clrNONE,90);
   panel.DrawText("Low",   panel.CellX(0,4,0)+2, panel.CellY(0,4,0)+2); panel.DrawText(DoubleToString(rates[0].low,Digits()),       panel.CellX(0,4,1)+2, panel.CellY(0,4,1)+2,clrNONE,90);
   panel.DrawText("Close", panel.CellX(0,5,0)+2, panel.CellY(0,5,0)+2); panel.DrawText(DoubleToString(rates[0].close,Digits()),     panel.CellX(0,5,1)+2, panel.CellY(0,5,1)+2,clrNONE,90);

//--- Выводим в таблицу 1 данные индикатора 1 с указанного бара в таблицу 1
   panel.DrawText(indicators.Title(handle_dem1), panel.CellX(1,0,0)+2, panel.CellY(1,0,0)+2);
   double value1=indicators.GetData(handle_dem1,0,0,index);
   string value_str1=(value1!=EMPTY_VALUE ? DoubleToString(value1,indicators.Digits(handle_dem1)) : " ");
   panel.DrawText(value_str1,panel.CellX(1,0,1)+2,panel.CellY(1,0,1)+2,clrNONE,110);
   
//--- Выводим описание состояния линии индикатора 1
   panel.DrawText("Line state", panel.CellX(1,1,0)+2, panel.CellY(1,1,0)+2);
   ENUM_LINE_STATE state1=indicators.BufferLineState(Symbol(),PERIOD_CURRENT,handle_dem1,0,0,index);
   panel.DrawText(BufferLineStateDescription(state1),panel.CellX(1,1,1)+2,panel.CellY(1,1,1)+2,clrNONE,110);
   
//--- Выводим в таблицу 2 данные индикатора 2 с указанного бара в таблицу 2
   panel.DrawText(indicators.Title(handle_dem2), panel.CellX(2,0,0)+2, panel.CellY(2,0,0)+2);
   double value2=indicators.GetDataTo(Symbol(),PERIOD_CURRENT,handle_dem2,0,0,index);
   string value_str2=(value2!=EMPTY_VALUE ? DoubleToString(value2,indicators.Digits(handle_dem2)) : " ");
   panel.DrawText(value_str2,panel.CellX(2,0,1)+2,panel.CellY(2,0,1)+2,clrNONE,110);
   
//--- Выводим описание состояния линии индикатора 2
   panel.DrawText("Line state", panel.CellX(2,1,0)+2, panel.CellY(2,1,0)+2);
   ENUM_LINE_STATE state2=indicators.BufferLineState(Symbol(),PERIOD_CURRENT,handle_dem2,0,0,index);
   panel.DrawText(BufferLineStateDescription(state2),panel.CellX(2,1,1)+2,panel.CellY(2,1,1)+2,clrNONE,110);
   
//--- Выводим описание соотношения линии индикатора 1 относительно линии индикатора 2
   double value21=indicators.GetDataTo(Symbol(),PERIOD_CURRENT,handle_dem2,0,0,index+1);
   ENUM_LINE_STATE stateR=indicators.BufferLineStateRelative(Symbol(),PERIOD_CURRENT,handle_dem1,0,0,index,value2,value21);
   string ma1=indicators.Name(handle_dem1);
   string ma2=indicators.Name(handle_dem2);
   string state_relative=
     (
      stateR==LINE_STATE_ABOVE      ? StringFormat("%s1 > %s2",ma1,ma2)   :
      stateR==LINE_STATE_BELOW      ? StringFormat("%s1 < %s2",ma1,ma2)           :
      stateR==LINE_STATE_CROSS_DOWN ? "Top-down crossing"   :
      stateR==LINE_STATE_CROSS_UP   ? "Bottom-up crossing"  :
      BufferLineStateDescription(stateR)
     );
   panel.DrawText(StringFormat("%s1 vs %s2",ma1,ma2), panel.CellX(2,2,0)+2, panel.CellY(2,2,0)+2);
   panel.DrawText(state_relative, panel.CellX(2,2,1)+2, panel.CellY(2,2,1)+2,clrNONE,110);
   
//--- Перерисовываем график для немедленного отображения всех изменений на панели
   ChartRedraw(ChartID());
  }
//+------------------------------------------------------------------+

После компиляции и запуска индикатора на графике М1 и выбранном периоде графика для расчёта М5, видим:



Индикатор Envelopes:

//+------------------------------------------------------------------+
//|                                        TestMSTFEnvelopes.mq5.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_chart_window
#property indicator_buffers 4
#property indicator_plots   4
//--- enums

//--- plot EnvelopesUp1
#property indicator_label1  "EnvelopesUp1"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrDodgerBlue
#property indicator_width1  1

//--- plot EnvelopesDown1
#property indicator_label2  "EnvelopesDown1"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrRed
#property indicator_width2  1

//--- plot EnvelopesUp2
#property indicator_label3  "EnvelopesUp2"
#property indicator_type3   DRAW_LINE
#property indicator_color3  clrDodgerBlue
#property indicator_width3  1

//--- plot EnvelopesDown2
#property indicator_label4  "EnvelopesDown2"
#property indicator_type4   DRAW_LINE
#property indicator_color4  clrRed
#property indicator_width4  1

//--- includes
#include <IndMSTF\IndMSTF.mqh>
#include <Dashboard\Dashboard.mqh>
//--- input parameters
input string               InpSymbol      =  NULL;             /* Symbol                     */ // Символ скользящей средней
input ENUM_TIMEFRAMES      InpTimeframe   =  PERIOD_CURRENT;   /* Timeframe                  */ // Таймфрейм скользящей средней
input int                  InpPeriod      =  14;               /* Period                     */ // Период расчёта
input ENUM_APPLIED_PRICE   InpPrice       =  PRICE_CLOSE;      /* Applied Price              */ // Используемая цена для расчёта
input ENUM_MA_METHOD       InpMethod      =  MODE_SMA;         /* Method                     */ // Метод расчёта Moving Average
input int                  InpShift       =  0;                /* Shift                      */ // Сдвиг скользящей средней
input double               InpDeviation   =  0.1;              /* Deviation                  */ // Отклонение
input uchar                InpLineWidth1  =  2;                /* Senior period Line Width   */ // Толщина линии для старшего периода
input uchar                InpLineWidth2  =  1;                /* Junior period Line Width   */ // Толщина линии для младшего периода
input bool                 InpAsSeries    =  true;             /* As Series flag             */ // Флаг серийности массивов буферов индикатора

//--- indicator buffers
double         BufferEnvelopesUp1[];
double         BufferEnvelopesDn1[];
double         BufferEnvelopesUp2[];
double         BufferEnvelopesDn2[];
//--- global variables
int handle_envelopes1;
int handle_envelopes2;
CMSTFIndicators indicators;      // Экземпляр объекта коллекции индикаторов
//--- переменные для панели
CDashboard *panel=NULL;          // Указатель на объект панели
int         mouse_bar_index;     // Индекс бара, с которого берутся данные
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Устанавливаем таймер с периодичностью в 1 секунду
   EventSetTimer(1);
//--- Назначаем рисуемым буферам 0 и 1 массивы BufferEnvelopesUp1 и BufferEnvelopesDn1 соответственно
   SetIndexBuffer(0,BufferEnvelopesUp1,INDICATOR_DATA);
   SetIndexBuffer(1,BufferEnvelopesDn1,INDICATOR_DATA);
//--- Назначаем рисуемым буферам 2 и 3 массивы BufferEnvelopesUp2 и BufferEnvelopesDn2 соответственно
   SetIndexBuffer(2,BufferEnvelopesUp2,INDICATOR_DATA);
   SetIndexBuffer(3,BufferEnvelopesDn2,INDICATOR_DATA);
//--- Устанавливаем ширину линий
   int w1=0,w2=0;
   if(InpTimeframe>Period())
     {
      w1=InpLineWidth2;
      w2=InpLineWidth1;
     }
   else
     {
      w1=InpLineWidth1;
      w2=InpLineWidth2;
     }
   PlotIndexSetInteger(0,PLOT_LINE_WIDTH,w1);
   PlotIndexSetInteger(1,PLOT_LINE_WIDTH,w1);
   PlotIndexSetInteger(2,PLOT_LINE_WIDTH,w2);
   PlotIndexSetInteger(3,PLOT_LINE_WIDTH,w2);
//--- Устанавливаем флаги серийности массивам буферов индикатора (для теста, чтобы видно было отсутствие разницы)
   ArraySetAsSeries(BufferEnvelopesUp1,InpAsSeries);
   ArraySetAsSeries(BufferEnvelopesDn1,InpAsSeries);
   ArraySetAsSeries(BufferEnvelopesUp2,InpAsSeries);
   ArraySetAsSeries(BufferEnvelopesDn2,InpAsSeries);
   
//--- Создаём два индикатора одного типа
//--- Первый рассчитывается на текущих символе/периоде графика, а второй - на тех, что заданы в настройках
   handle_envelopes1=indicators.AddNewEnvelopes(NULL,PERIOD_CURRENT,InpPeriod,InpShift,InpMethod,InpPrice,InpDeviation);
   handle_envelopes2=indicators.AddNewEnvelopes(InpSymbol,InpTimeframe,InpPeriod,InpShift,InpMethod,InpPrice,InpDeviation);

//--- Если не удалось создать хэндлы индикаторов - возвращаем ошибку инициализации
   if(handle_envelopes1==INVALID_HANDLE || handle_envelopes2==INVALID_HANDLE)
      return INIT_FAILED;
//--- Устанавливаем описания линий индикатора из описаний буферов расчётной части созданных индикаторов
   indicators.SetPlotLabelFromBuffer(0,handle_envelopes1,0);
   indicators.SetPlotLabelFromBuffer(1,handle_envelopes1,1);
   indicators.SetPlotLabelFromBuffer(2,handle_envelopes2,0);
   indicators.SetPlotLabelFromBuffer(3,handle_envelopes2,1);
//--- Устанавливаем смещения линиям индикатора
   indicators.SetPlotShift(0,InpShift);
   indicators.SetPlotShift(1,InpShift);
   indicators.SetPlotShift(2,InpShift);
   indicators.SetPlotShift(3,InpShift);

//--- Панель
//--- Создаём панель
   int width=301;
   panel=new CDashboard(1,20,20,width,264);
   if(panel==NULL)
     {
      Print("Error. Failed to create panel object");
      return INIT_FAILED;
     }
//--- Устанавливаем параметры шрифта
   panel.SetFontParams("Calibri",9);
//--- Отображаем панель с текстом в заголовке "Символ, Описание таймфрейма"
   panel.View(Symbol()+", "+StringSubstr(EnumToString(Period()),7));

//--- Создаём таблицу с идентификатором 0 для отображения в ней данных бара
   panel.CreateNewTable(0);
//--- Рисуем таблицу с идентификатором 0 на фоне панели
   panel.DrawGrid(0,2,20,6,2,18,width/2-2);

//--- Создаём таблицу с идентификатором 1 для отображения в ней данных индикатора 1
   panel.CreateNewTable(1);
//--- Получаем координату Y2 таблицы с идентификатором 0 и
//--- устанавливаем координату Y1 для таблицы с идентификатором 1
   int y1=panel.TableY2(0)+22;
//--- Рисуем таблицу с идентификатором 1 на фоне панели
   panel.DrawGrid(1,2,y1,2,2,18,width/2-2);

//--- Создаём таблицу с идентификатором 2 для отображения в ней данных индикатора 2
   panel.CreateNewTable(2);
//--- Получаем координату Y2 таблицы с идентификатором 1 и
//--- устанавливаем координату Y1 для таблицы с идентификатором 2
   int y2=panel.TableY2(1)+3;
//--- Рисуем таблицу с идентификатором 2 на фоне панели
   panel.DrawGrid(2,2,y2,3,2,18,width/2-2);
   
//--- Инициализируем переменную с индексом бара указателя мышки
   mouse_bar_index=0;
//--- Выводим на панель данные текущего бара
   DrawData(mouse_bar_index,TimeCurrent());

//--- Успешная инициализация
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- Уничтожаем таймер
   EventKillTimer();
//--- Если объект панели существует - удаляем его
   if(panel!=NULL)
      delete panel;
//--- Стираем все комментарии
   Comment("");
  }
//+------------------------------------------------------------------+
//| 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[])
  {
//--- Количество баров для расчёта
   int limit=rates_total-prev_calculated;
//--- Если limit > 1, значит это первый расчёт, либо изменение в истории
   if(limit>1)
     {
      //--- указываем для просчёта всю доступную историю
      limit=rates_total-1;
      /*
      // Если в индикаторе есть какие-либо буферы, в которых отображены другие расчёты (не мульти- индикаторы),
      // то здесь их нужно инициализировать "пустым" значением, установленным для этих буферов
      */
     }
//--- Рассчитываем все созданные мультисимвольные мультипериодные индикаторы
   if(!indicators.Calculate())
      return 0;

//--- Выводим на панель данные бара под курсором (либо текущий бар, если курсор за пределами графика)
   DrawData(mouse_bar_index,time[mouse_bar_index]);

//--- Из буферов рассчитанных индикаторов выводим данные в индикаторные буферы
   if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_envelopes1,0,0,limit,BufferEnvelopesUp1))
      return 0;
   if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_envelopes1,1,0,limit,BufferEnvelopesDn1))
      return 0;
   if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_envelopes2,0,0,limit,BufferEnvelopesUp2))
      return 0;
   if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_envelopes2,1,0,limit,BufferEnvelopesDn2))
      return 0;

//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| Timer function                                                   | 
//+------------------------------------------------------------------+
void OnTimer()
  {
//--- Вызываем таймер коллекции индикаторов
   indicators.OnTimer();
  }
//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- Работа с панелью
//--- Вызываем обработчик событий панели
   panel.OnChartEvent(id,lparam,dparam,sparam);

//--- Если курсор перемещается или щелчок по графику
   if(id==CHARTEVENT_MOUSE_MOVE || id==CHARTEVENT_CLICK)
     {
      //--- Объявляем переменные для записи в них координат времени и цены
      datetime time=0;
      double price=0;
      int wnd=0;
      //--- Если координаты курсора преобразованы в дату и время
      if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price))
        {
         //--- записываем индекс бара, где расположен курсор в глобальную переменную
         mouse_bar_index=iBarShift(Symbol(),PERIOD_CURRENT,time);
         //--- Выводим данные бара под курсором на панель
         DrawData(mouse_bar_index,time);
        }
     }

//--- Если получили пользовательское событие - выводим об этом сообщение в журнал
   if(id>CHARTEVENT_CUSTOM)
     {
      //--- Здесь может быть обработка щелчка по кнопке закрытия на панели
      PrintFormat("%s: Event id=%ld, object id (lparam): %lu, event message (sparam): %s",__FUNCTION__,id,lparam,sparam);
     }
  }
//+------------------------------------------------------------------+
//| Выводит данные с указанного индекса таймсерии на панель          |
//+------------------------------------------------------------------+
void DrawData(const int index,const datetime time)
  {
//--- Объявляем переменные для получения в них данных
   MqlRates rates[1];

//--- Если данные бара по указанному индексу получить не удалось - уходим
   if(CopyRates(Symbol(),PERIOD_CURRENT,index,1,rates)!=1)
      return;

//--- Устанавливаем параметры шрифта для заголовков данных бара и индикатора
   int  size=0;
   uint flags=0;
   uint angle=0;
   string name=panel.FontParams(size,flags,angle);
   panel.SetFontParams(name,9,FW_BOLD);
   panel.DrawText("Bar data ["+(string)index+"]",3,panel.TableY1(0)-16,clrMaroon,panel.Width()-6);
   panel.DrawText("Indicators data ["+(string)index+"]",3,panel.TableY1(1)-16,clrGreen,panel.Width()-6);
//--- Устанавливаем параметры шрифта для данных бара и индикатора
   panel.SetFontParams(name,9);

//--- Выводим на панель данные указанного бара в таблицу 0
   panel.DrawText("Date",  panel.CellX(0,0,0)+2, panel.CellY(0,0,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_DATE),     panel.CellX(0,0,1)+2, panel.CellY(0,0,1)+2,clrNONE,90);
   panel.DrawText("Time",  panel.CellX(0,1,0)+2, panel.CellY(0,1,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_MINUTES),  panel.CellX(0,1,1)+2, panel.CellY(0,1,1)+2,clrNONE,90);
   panel.DrawText("Open",  panel.CellX(0,2,0)+2, panel.CellY(0,2,0)+2); panel.DrawText(DoubleToString(rates[0].open,Digits()),      panel.CellX(0,2,1)+2, panel.CellY(0,2,1)+2,clrNONE,90);
   panel.DrawText("High",  panel.CellX(0,3,0)+2, panel.CellY(0,3,0)+2); panel.DrawText(DoubleToString(rates[0].high,Digits()),      panel.CellX(0,3,1)+2, panel.CellY(0,3,1)+2,clrNONE,90);
   panel.DrawText("Low",   panel.CellX(0,4,0)+2, panel.CellY(0,4,0)+2); panel.DrawText(DoubleToString(rates[0].low,Digits()),       panel.CellX(0,4,1)+2, panel.CellY(0,4,1)+2,clrNONE,90);
   panel.DrawText("Close", panel.CellX(0,5,0)+2, panel.CellY(0,5,0)+2); panel.DrawText(DoubleToString(rates[0].close,Digits()),     panel.CellX(0,5,1)+2, panel.CellY(0,5,1)+2,clrNONE,90);

//--- Выводим в таблицу 1 данные индикатора 1 с указанного бара в таблицу 1
   panel.DrawText(indicators.Title(handle_envelopes1), panel.CellX(1,0,0)+2, panel.CellY(1,0,0)+2);
   double value1=indicators.GetData(handle_envelopes1,0,0,index);
   string value_str1=(value1!=EMPTY_VALUE ? DoubleToString(value1,indicators.Digits(handle_envelopes1)) : " ");
   panel.DrawText(value_str1,panel.CellX(1,0,1)+2,panel.CellY(1,0,1)+2,clrNONE,150);
   
//--- Выводим описание состояния линии индикатора 1
   panel.DrawText("Line state", panel.CellX(1,1,0)+2, panel.CellY(1,1,0)+2);
   ENUM_LINE_STATE state1=indicators.BufferLineState(Symbol(),PERIOD_CURRENT,handle_envelopes1,0,0,index);
   panel.DrawText(BufferLineStateDescription(state1),panel.CellX(1,1,1)+2,panel.CellY(1,1,1)+2,clrNONE,150);
   
//--- Выводим в таблицу 2 данные индикатора 2 с указанного бара в таблицу 2
   panel.DrawText(indicators.Title(handle_envelopes2), panel.CellX(2,0,0)+2, panel.CellY(2,0,0)+2);
   double value2=indicators.GetDataTo(Symbol(),PERIOD_CURRENT,handle_envelopes2,0,0,index);
   string value_str2=(value2!=EMPTY_VALUE ? DoubleToString(value2,indicators.Digits(handle_envelopes2)) : " ");
   panel.DrawText(value_str2,panel.CellX(2,0,1)+2,panel.CellY(2,0,1)+2,clrNONE,150);
   
//--- Выводим описание состояния линии индикатора 2
   panel.DrawText("Line state", panel.CellX(2,1,0)+2, panel.CellY(2,1,0)+2);
   ENUM_LINE_STATE state2=indicators.BufferLineState(Symbol(),PERIOD_CURRENT,handle_envelopes2,0,0,index);
   panel.DrawText(BufferLineStateDescription(state2),panel.CellX(2,1,1)+2,panel.CellY(2,1,1)+2,clrNONE,150);
   
//--- Выводим описание соотношения линии индикатора 1 относительно линии индикатора 2
   double value21=indicators.GetDataTo(Symbol(),PERIOD_CURRENT,handle_envelopes2,0,0,index+1);
   ENUM_LINE_STATE stateR=indicators.BufferLineStateRelative(Symbol(),PERIOD_CURRENT,handle_envelopes1,0,0,index,value2,value21);
   string ma1=indicators.Name(handle_envelopes1);
   string ma2=indicators.Name(handle_envelopes2);
   string state_relative=
     (
      stateR==LINE_STATE_ABOVE      ? StringFormat("%s1 > %s2",ma1,ma2)   :
      stateR==LINE_STATE_BELOW      ? StringFormat("%s1 < %s2",ma1,ma2)           :
      stateR==LINE_STATE_CROSS_DOWN ? "Top-down crossing"   :
      stateR==LINE_STATE_CROSS_UP   ? "Bottom-up crossing"  :
      BufferLineStateDescription(stateR)
     );
   panel.DrawText(StringFormat("%s1 vs %s2",ma1,ma2), panel.CellX(2,2,0)+2, panel.CellY(2,2,0)+2);
   panel.DrawText(state_relative, panel.CellX(2,2,1)+2, panel.CellY(2,2,1)+2,clrNONE,150);
   
//--- Перерисовываем график для немедленного отображения всех изменений на панели
   ChartRedraw(ChartID());
  }
//+------------------------------------------------------------------+

После компиляции и запуска индикатора на графике М1 и выбранном периоде графика для расчёта М5, видим:



Индикатор Force Index:

//+------------------------------------------------------------------+
//|                                           TestMSTFForceIndex.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_separate_window
#property indicator_buffers 2
#property indicator_plots   2
//--- enums

//--- plot Force1
#property indicator_label1  "Force1"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrSeaGreen
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1

//--- plot Force2
#property indicator_label2  "Force2"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrDodgerBlue
#property indicator_style2  STYLE_SOLID
#property indicator_width2  1

//--- includes
#include <IndMSTF\IndMSTF.mqh>
#include <Dashboard\Dashboard.mqh>
//--- input parameters
input string               InpSymbol      =  NULL;             /* Symbol                     */ // Символ
input ENUM_TIMEFRAMES      InpTimeframe   =  PERIOD_CURRENT;   /* Timeframe                  */ // Таймфрейм
input int                  InpPeriod      =  13;               /* Period                     */ // Период расчёта
input ENUM_MA_METHOD       InpMethod      =  MODE_SMA;         /* Method                     */ // Метод расчёта
input ENUM_APPLIED_VOLUME  InpVolume      =  VOLUME_TICK;      /* Applied Volume             */ // Объёмы
input uchar                InpLineWidth1  =  2;                /* Senior period Line Width   */ // Толщина линии для старшего периода
input uchar                InpLineWidth2  =  1;                /* Junior period Line Width   */ // Толщина линии для младшего периода
input bool                 InpAsSeries    =  true;             /* As Series flag             */ // Флаг серийности массивов буферов индикатора

//--- indicator buffers
double         BufferForce1[];
double         BufferForce2[];
//--- global variables
int handle_force1;
int handle_force2;
CMSTFIndicators indicators;      // Экземпляр объекта коллекции индикаторов
//--- переменные для панели
CDashboard *panel=NULL;          // Указатель на объект панели
int         mouse_bar_index;     // Индекс бара, с которого берутся данные
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Устанавливаем таймер с периодичностью в 1 секунду
   EventSetTimer(1);
//--- Назначаем рисуемым буферам 0 и 1 массивы BufferMA1 и BufferMA2 соответственно
   SetIndexBuffer(0,BufferForce1,INDICATOR_DATA);
   SetIndexBuffer(1,BufferForce2,INDICATOR_DATA);
//--- Устанавливаем ширину линий
   int w1=0,w2=0;
   if(InpTimeframe>Period())
     {
      w1=InpLineWidth2;
      w2=InpLineWidth1;
     }
   else
     {
      w1=InpLineWidth1;
      w2=InpLineWidth2;
     }
   PlotIndexSetInteger(0,PLOT_LINE_WIDTH,w1);
   PlotIndexSetInteger(1,PLOT_LINE_WIDTH,w2);
//--- sets indicator shift

//--- Устанавливаем флаги серийности массивам буферов индикатора (для теста, чтобы видно было отсутствие разницы)
   ArraySetAsSeries(BufferForce1,InpAsSeries);
   ArraySetAsSeries(BufferForce2,InpAsSeries);
   
//--- Создаём два индикатора одного типа
//--- Первый рассчитывается на текущих символе/периоде графика, а второй - на тех, что заданы в настройках
   handle_force1=indicators.AddNewForce(NULL,PERIOD_CURRENT,InpPeriod,InpMethod,InpVolume);
   handle_force2=indicators.AddNewForce(InpSymbol,InpTimeframe,InpPeriod,InpMethod,InpVolume);

//--- Если не удалось создать хэндлы индикаторов - возвращаем ошибку инициализации
   if(handle_force1==INVALID_HANDLE || handle_force2==INVALID_HANDLE)
      return INIT_FAILED;
//--- Устанавливаем описания линий индикатора из описаний буферов расчётной части созданных индикаторов
   indicators.SetPlotLabelFromBuffer(0,handle_force1,0);
   indicators.SetPlotLabelFromBuffer(1,handle_force2,0);
      
//--- Панель
//--- Создаём панель
   int width=247;
   panel=new CDashboard(1,20,20,width,264,0);
   if(panel==NULL)
     {
      Print("Error. Failed to create panel object");
      return INIT_FAILED;
     }
//--- Устанавливаем параметры шрифта
   panel.SetFontParams("Calibri",9);
//--- Отображаем панель с текстом в заголовке "Символ, Описание таймфрейма"
   panel.View(Symbol()+", "+StringSubstr(EnumToString(Period()),7));

//--- Создаём таблицу с идентификатором 0 для отображения в ней данных бара
   panel.CreateNewTable(0);
//--- Рисуем таблицу с идентификатором 0 на фоне панели
   panel.DrawGrid(0,2,20,6,2,18,width/2-2);

//--- Создаём таблицу с идентификатором 1 для отображения в ней данных индикатора 1
   panel.CreateNewTable(1);
//--- Получаем координату Y2 таблицы с идентификатором 0 и
//--- устанавливаем координату Y1 для таблицы с идентификатором 1
   int y1=panel.TableY2(0)+22;
//--- Рисуем таблицу с идентификатором 1 на фоне панели
   panel.DrawGrid(1,2,y1,2,2,18,width/2-2);

//--- Создаём таблицу с идентификатором 2 для отображения в ней данных индикатора 2
   panel.CreateNewTable(2);
//--- Получаем координату Y2 таблицы с идентификатором 1 и
//--- устанавливаем координату Y1 для таблицы с идентификатором 2
   int y2=panel.TableY2(1)+3;
//--- Рисуем таблицу с идентификатором 2 на фоне панели
   panel.DrawGrid(2,2,y2,3,2,18,width/2-2);
   
//--- Инициализируем переменную с индексом бара указателя мышки
   mouse_bar_index=0;
//--- Выводим на панель данные текущего бара
   DrawData(mouse_bar_index,TimeCurrent());

//--- Успешная инициализация
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- Уничтожаем таймер
   EventKillTimer();
//--- Если объект панели существует - удаляем его
   if(panel!=NULL)
      delete panel;
//--- Стираем все комментарии
   Comment("");
  }
//+------------------------------------------------------------------+
//| 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[])
  {
//--- Количество баров для расчёта
   int limit=rates_total-prev_calculated;
//--- Если limit > 1, значит это первый расчёт, либо изменение в истории
   if(limit>1)
     {
      //--- указываем для просчёта всю доступную историю
      limit=rates_total-1;
      /*
      // Если в индикаторе есть какие-либо буферы, в которых отображены другие расчёты (не мульти- индикаторы),
      // то здесь их нужно инициализировать "пустым" значением, установленным для этих буферов
      */
     }
//--- Рассчитываем все созданные мультисимвольные мультипериодные индикаторы
   if(!indicators.Calculate())
      return 0;

//--- Выводим на панель данные бара под курсором (либо текущий бар, если курсор за пределами графика)
   DrawData(mouse_bar_index,time[mouse_bar_index]);

//--- Из буферов рассчитанных индикаторов выводим данные в индикаторные буферы
   if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_force1,0,0,limit,BufferForce1))
      return 0;
   if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_force2,0,0,limit,BufferForce2))
      return 0;

//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| Timer function                                                   | 
//+------------------------------------------------------------------+
void OnTimer()
  {
//--- Вызываем таймер коллекции индикаторов
   indicators.OnTimer();
  }
//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- Работа с панелью
//--- Вызываем обработчик событий панели
   panel.OnChartEvent(id,lparam,dparam,sparam);

//--- Если курсор перемещается или щелчок по графику
   if(id==CHARTEVENT_MOUSE_MOVE || id==CHARTEVENT_CLICK)
     {
      //--- Объявляем переменные для записи в них координат времени и цены
      datetime time=0;
      double price=0;
      int wnd=0;
      //--- Если координаты курсора преобразованы в дату и время
      if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price))
        {
         //--- записываем индекс бара, где расположен курсор в глобальную переменную
         mouse_bar_index=iBarShift(Symbol(),PERIOD_CURRENT,time);
         //--- Выводим данные бара под курсором на панель
         DrawData(mouse_bar_index,time);
        }
     }

//--- Если получили пользовательское событие - выводим об этом сообщение в журнал
   if(id>CHARTEVENT_CUSTOM)
     {
      //--- Здесь может быть обработка щелчка по кнопке закрытия на панели
      PrintFormat("%s: Event id=%ld, object id (lparam): %lu, event message (sparam): %s",__FUNCTION__,id,lparam,sparam);
     }
  }
//+------------------------------------------------------------------+
//| Выводит данные с указанного индекса таймсерии на панель          |
//+------------------------------------------------------------------+
void DrawData(const int index,const datetime time)
  {
//--- Объявляем переменные для получения в них данных
   MqlRates rates[1];

//--- Если данные бара по указанному индексу получить не удалось - уходим
   if(CopyRates(Symbol(),PERIOD_CURRENT,index,1,rates)!=1)
      return;

//--- Устанавливаем параметры шрифта для заголовков данных бара и индикатора
   int  size=0;
   uint flags=0;
   uint angle=0;
   string name=panel.FontParams(size,flags,angle);
   panel.SetFontParams(name,9,FW_BOLD);
   panel.DrawText("Bar data ["+(string)index+"]",3,panel.TableY1(0)-16,clrMaroon,panel.Width()-6);
   panel.DrawText("Indicators data ["+(string)index+"]",3,panel.TableY1(1)-16,clrGreen,panel.Width()-6);
//--- Устанавливаем параметры шрифта для данных бара и индикатора
   panel.SetFontParams(name,9);

//--- Выводим на панель данные указанного бара в таблицу 0
   panel.DrawText("Date",  panel.CellX(0,0,0)+2, panel.CellY(0,0,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_DATE),     panel.CellX(0,0,1)+2, panel.CellY(0,0,1)+2,clrNONE,90);
   panel.DrawText("Time",  panel.CellX(0,1,0)+2, panel.CellY(0,1,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_MINUTES),  panel.CellX(0,1,1)+2, panel.CellY(0,1,1)+2,clrNONE,90);
   panel.DrawText("Open",  panel.CellX(0,2,0)+2, panel.CellY(0,2,0)+2); panel.DrawText(DoubleToString(rates[0].open,Digits()),      panel.CellX(0,2,1)+2, panel.CellY(0,2,1)+2,clrNONE,90);
   panel.DrawText("High",  panel.CellX(0,3,0)+2, panel.CellY(0,3,0)+2); panel.DrawText(DoubleToString(rates[0].high,Digits()),      panel.CellX(0,3,1)+2, panel.CellY(0,3,1)+2,clrNONE,90);
   panel.DrawText("Low",   panel.CellX(0,4,0)+2, panel.CellY(0,4,0)+2); panel.DrawText(DoubleToString(rates[0].low,Digits()),       panel.CellX(0,4,1)+2, panel.CellY(0,4,1)+2,clrNONE,90);
   panel.DrawText("Close", panel.CellX(0,5,0)+2, panel.CellY(0,5,0)+2); panel.DrawText(DoubleToString(rates[0].close,Digits()),     panel.CellX(0,5,1)+2, panel.CellY(0,5,1)+2,clrNONE,90);

//--- Выводим в таблицу 1 данные индикатора 1 с указанного бара в таблицу 1
   panel.DrawText(indicators.Title(handle_force1), panel.CellX(1,0,0)+2, panel.CellY(1,0,0)+2);
   double value1=indicators.GetData(handle_force1,0,0,index);
   string value_str1=(value1!=EMPTY_VALUE ? DoubleToString(value1,indicators.Digits(handle_force1)) : " ");
   panel.DrawText(value_str1,panel.CellX(1,0,1)+2,panel.CellY(1,0,1)+2,clrNONE,110);
   
//--- Выводим описание состояния линии индикатора 1
   panel.DrawText("Line state", panel.CellX(1,1,0)+2, panel.CellY(1,1,0)+2);
   ENUM_LINE_STATE state1=indicators.BufferLineState(Symbol(),PERIOD_CURRENT,handle_force1,0,0,index);
   panel.DrawText(BufferLineStateDescription(state1),panel.CellX(1,1,1)+2,panel.CellY(1,1,1)+2,clrNONE,110);
   
//--- Выводим в таблицу 2 данные индикатора 2 с указанного бара в таблицу 2
   panel.DrawText(indicators.Title(handle_force2), panel.CellX(2,0,0)+2, panel.CellY(2,0,0)+2);
   double value2=indicators.GetDataTo(Symbol(),PERIOD_CURRENT,handle_force2,0,0,index);
   string value_str2=(value2!=EMPTY_VALUE ? DoubleToString(value2,indicators.Digits(handle_force2)) : " ");
   panel.DrawText(value_str2,panel.CellX(2,0,1)+2,panel.CellY(2,0,1)+2,clrNONE,110);
   
//--- Выводим описание состояния линии индикатора 2
   panel.DrawText("Line state", panel.CellX(2,1,0)+2, panel.CellY(2,1,0)+2);
   ENUM_LINE_STATE state2=indicators.BufferLineState(Symbol(),PERIOD_CURRENT,handle_force2,0,0,index);
   panel.DrawText(BufferLineStateDescription(state2),panel.CellX(2,1,1)+2,panel.CellY(2,1,1)+2,clrNONE,110);
   
//--- Выводим описание соотношения линии индикатора 1 относительно линии индикатора 2
   double value21=indicators.GetDataTo(Symbol(),PERIOD_CURRENT,handle_force2,0,0,index+1);
   ENUM_LINE_STATE stateR=indicators.BufferLineStateRelative(Symbol(),PERIOD_CURRENT,handle_force1,0,0,index,value2,value21);
   string ma1=indicators.Name(handle_force1);
   string ma2=indicators.Name(handle_force2);
   string state_relative=
     (
      stateR==LINE_STATE_ABOVE      ? StringFormat("%s1 > %s2",ma1,ma2)   :
      stateR==LINE_STATE_BELOW      ? StringFormat("%s1 < %s2",ma1,ma2)           :
      stateR==LINE_STATE_CROSS_DOWN ? "Top-down crossing"   :
      stateR==LINE_STATE_CROSS_UP   ? "Bottom-up crossing"  :
      BufferLineStateDescription(stateR)
     );
   panel.DrawText(StringFormat("%s1 vs %s2",ma1,ma2), panel.CellX(2,2,0)+2, panel.CellY(2,2,0)+2);
   panel.DrawText(state_relative, panel.CellX(2,2,1)+2, panel.CellY(2,2,1)+2,clrNONE,110);
   
//--- Перерисовываем график для немедленного отображения всех изменений на панели
   ChartRedraw(ChartID());
  }
//+------------------------------------------------------------------+

После компиляции и запуска индикатора на графике М1 и выбранном периоде графика для расчёта М5, видим:



Индикатор MACD:

//+------------------------------------------------------------------+
//|                                                 TestMSTFMACD.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_separate_window
#property indicator_buffers 4
#property indicator_plots   4
//--- enums

//--- plot MACD1
#property indicator_label1  "MACD1"
#property indicator_type1   DRAW_HISTOGRAM
#property indicator_color1  clrGreen
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1

//--- plot Signal1
#property indicator_label2  "Signal1"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrRed
#property indicator_style2  STYLE_DOT
#property indicator_width2  1

//--- plot MACD2
#property indicator_label3  "MACD2"
#property indicator_type3   DRAW_HISTOGRAM
#property indicator_color3  clrPaleGreen
#property indicator_style3  STYLE_SOLID
#property indicator_width3  1

//--- plot Signal2
#property indicator_label4  "Signal2"
#property indicator_type4   DRAW_LINE
#property indicator_color4  clrOrange
#property indicator_style4  STYLE_DOT
#property indicator_width4  1

//--- includes
#include <IndMSTF\IndMSTF.mqh>
#include <Dashboard\Dashboard.mqh>
//--- input parameters
input string               InpSymbol      =  NULL;             /* Symbol                     */ // Символ
input ENUM_TIMEFRAMES      InpTimeframe   =  PERIOD_CURRENT;   /* Timeframe                  */ // Таймфрейм
input int                  InpFastPeriod  =  12;               /* Fast EMA Period            */ // Период быстрого EMA
input int                  InpSlowPeriod  =  26;               /* Slow EMA Period            */ // Период медленного EMA
input int                  InpSignal      =  9;                /* MACD SMA                   */ // Период сигнальной линии
input ENUM_APPLIED_PRICE   InpPrice       =  PRICE_CLOSE;      /* Applied Price              */ // Цена расчёта
input uchar                InpLineWidth1  =  2;                /* Senior period Line Width   */ // Толщина линии для старшего периода
input uchar                InpLineWidth2  =  1;                /* Junior period Line Width   */ // Толщина линии для младшего периода
input bool                 InpAsSeries    =  true;             /* As Series flag             */ // Флаг серийности массивов буферов индикатора

//--- indicator buffers
double         BufferMACD1[];
double         BufferSig1[];
double         BufferMACD2[];
double         BufferSig2[];
//--- global variables
int macd1;
int macd2;
CMSTFIndicators indicators;      // Экземпляр объекта коллекции индикаторов
//--- переменные для панели
CDashboard *panel=NULL;          // Указатель на объект панели
int         mouse_bar_index;     // Индекс бара, с которого берутся данные
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Устанавливаем таймер с периодичностью в 1 секунду
   EventSetTimer(1);
//--- Назначаем рисуемым буферам 0 и 1 массивы BufferMA1 и BufferMA2 соответственно
   SetIndexBuffer(0,BufferMACD1,INDICATOR_DATA);
   SetIndexBuffer(1,BufferSig1,INDICATOR_DATA);
   SetIndexBuffer(2,BufferMACD2,INDICATOR_DATA);
   SetIndexBuffer(3,BufferSig2,INDICATOR_DATA);
//--- Устанавливаем ширину линий
   int w1=0,w2=0;
   if(InpTimeframe>Period())
     {
      w1=InpLineWidth2;
      w2=InpLineWidth1;
     }
   else
     {
      w1=InpLineWidth1;
      w2=InpLineWidth2;
     }
   PlotIndexSetInteger(0,PLOT_LINE_WIDTH,w1);
   PlotIndexSetInteger(1,PLOT_LINE_WIDTH,w1);
   PlotIndexSetInteger(2,PLOT_LINE_WIDTH,w2);
   PlotIndexSetInteger(3,PLOT_LINE_WIDTH,w2);
//--- sets indicator shift

//--- Устанавливаем флаги серийности массивам буферов индикатора (для теста, чтобы видно было отсутствие разницы)
   ArraySetAsSeries(BufferMACD1,InpAsSeries);
   ArraySetAsSeries(BufferSig1,InpAsSeries);
   ArraySetAsSeries(BufferMACD2,InpAsSeries);
   ArraySetAsSeries(BufferSig2,InpAsSeries);
   
//--- Создаём два индикатора одного типа
//--- Первый рассчитывается на текущих символе/периоде графика, а второй - на тех, что заданы в настройках
   macd1=indicators.AddNewMACD(NULL,PERIOD_CURRENT,InpFastPeriod,InpSlowPeriod,InpSignal,InpPrice);
   macd2=indicators.AddNewMACD(InpSymbol,InpTimeframe,InpFastPeriod,InpSlowPeriod,InpSignal,InpPrice);

//--- Если не удалось создать хэндлы индикаторов - возвращаем ошибку инициализации
   if(macd1==INVALID_HANDLE || macd2==INVALID_HANDLE)
      return INIT_FAILED;
//--- Устанавливаем описания линий индикатора из описаний буферов расчётной части созданных индикаторов
   indicators.SetPlotLabelFromBuffer(0,macd1,0);
   indicators.SetPlotLabelFromBuffer(1,macd1,1);
   indicators.SetPlotLabelFromBuffer(2,macd2,0);
   indicators.SetPlotLabelFromBuffer(3,macd2,1);
      
//--- Панель
//--- Создаём панель
   int width=301;
   panel=new CDashboard(1,20,20,width,264,0);
   if(panel==NULL)
     {
      Print("Error. Failed to create panel object");
      return INIT_FAILED;
     }
//--- Устанавливаем параметры шрифта
   panel.SetFontParams("Calibri",9);
//--- Отображаем панель с текстом в заголовке "Символ, Описание таймфрейма"
   panel.View(Symbol()+", "+StringSubstr(EnumToString(Period()),7));

//--- Создаём таблицу с идентификатором 0 для отображения в ней данных бара
   panel.CreateNewTable(0);
//--- Рисуем таблицу с идентификатором 0 на фоне панели
   panel.DrawGrid(0,2,20,6,2,18,width/2-2);

//--- Создаём таблицу с идентификатором 1 для отображения в ней данных индикатора 1
   panel.CreateNewTable(1);
//--- Получаем координату Y2 таблицы с идентификатором 0 и
//--- устанавливаем координату Y1 для таблицы с идентификатором 1
   int y1=panel.TableY2(0)+22;
//--- Рисуем таблицу с идентификатором 1 на фоне панели
   panel.DrawGrid(1,2,y1,2,2,18,width/2-2);

//--- Создаём таблицу с идентификатором 2 для отображения в ней данных индикатора 2
   panel.CreateNewTable(2);
//--- Получаем координату Y2 таблицы с идентификатором 1 и
//--- устанавливаем координату Y1 для таблицы с идентификатором 2
   int y2=panel.TableY2(1)+3;
//--- Рисуем таблицу с идентификатором 2 на фоне панели
   panel.DrawGrid(2,2,y2,3,2,18,width/2-2);
   
//--- Инициализируем переменную с индексом бара указателя мышки
   mouse_bar_index=0;
//--- Выводим на панель данные текущего бара
   DrawData(mouse_bar_index,TimeCurrent());

//--- Успешная инициализация
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- Уничтожаем таймер
   EventKillTimer();
//--- Если объект панели существует - удаляем его
   if(panel!=NULL)
      delete panel;
//--- Стираем все комментарии
   Comment("");
  }
//+------------------------------------------------------------------+
//| 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[])
  {
//--- Количество баров для расчёта
   int limit=rates_total-prev_calculated;
//--- Если limit > 1, значит это первый расчёт, либо изменение в истории
   if(limit>1)
     {
      //--- указываем для просчёта всю доступную историю
      limit=rates_total-1;
      /*
      // Если в индикаторе есть какие-либо буферы, в которых отображены другие расчёты (не мульти- индикаторы),
      // то здесь их нужно инициализировать "пустым" значением, установленным для этих буферов
      */
     }
//--- Рассчитываем все созданные мультисимвольные мультипериодные индикаторы
   if(!indicators.Calculate())
      return 0;

//--- Выводим на панель данные бара под курсором (либо текущий бар, если курсор за пределами графика)
   DrawData(mouse_bar_index,time[mouse_bar_index]);

//--- Из буферов рассчитанных индикаторов выводим данные в индикаторные буферы
   if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,macd1,0,0,limit,BufferMACD1))
      return 0;
   if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,macd1,1,0,limit,BufferSig1))
      return 0;
      
   if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,macd2,0,0,limit,BufferMACD2))
      return 0;
   if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,macd2,1,0,limit,BufferSig2))
      return 0;
      
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| Timer function                                                   | 
//+------------------------------------------------------------------+
void OnTimer()
  {
//--- Вызываем таймер коллекции индикаторов
   indicators.OnTimer();
  }
//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- Работа с панелью
//--- Вызываем обработчик событий панели
   panel.OnChartEvent(id,lparam,dparam,sparam);

//--- Если курсор перемещается или щелчок по графику
   if(id==CHARTEVENT_MOUSE_MOVE || id==CHARTEVENT_CLICK)
     {
      //--- Объявляем переменные для записи в них координат времени и цены
      datetime time=0;
      double price=0;
      int wnd=0;
      //--- Если координаты курсора преобразованы в дату и время
      if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price))
        {
         //--- записываем индекс бара, где расположен курсор в глобальную переменную
         mouse_bar_index=iBarShift(Symbol(),PERIOD_CURRENT,time);
         //--- Выводим данные бара под курсором на панель
         DrawData(mouse_bar_index,time);
        }
     }

//--- Если получили пользовательское событие - выводим об этом сообщение в журнал
   if(id>CHARTEVENT_CUSTOM)
     {
      //--- Здесь может быть обработка щелчка по кнопке закрытия на панели
      PrintFormat("%s: Event id=%ld, object id (lparam): %lu, event message (sparam): %s",__FUNCTION__,id,lparam,sparam);
     }
  }
//+------------------------------------------------------------------+
//| Выводит данные с указанного индекса таймсерии на панель          |
//+------------------------------------------------------------------+
void DrawData(const int index,const datetime time)
  {
//--- Объявляем переменные для получения в них данных
   MqlRates rates[1];

//--- Если данные бара по указанному индексу получить не удалось - уходим
   if(CopyRates(Symbol(),PERIOD_CURRENT,index,1,rates)!=1)
      return;

//--- Устанавливаем параметры шрифта для заголовков данных бара и индикатора
   int  size=0;
   uint flags=0;
   uint angle=0;
   string name=panel.FontParams(size,flags,angle);
   panel.SetFontParams(name,9,FW_BOLD);
   panel.DrawText("Bar data ["+(string)index+"]",3,panel.TableY1(0)-16,clrMaroon,panel.Width()-6);
   panel.DrawText("Indicators data ["+(string)index+"]",3,panel.TableY1(1)-16,clrGreen,panel.Width()-6);
//--- Устанавливаем параметры шрифта для данных бара и индикатора
   panel.SetFontParams(name,9);

//--- Выводим на панель данные указанного бара в таблицу 0
   panel.DrawText("Date",  panel.CellX(0,0,0)+2, panel.CellY(0,0,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_DATE),     panel.CellX(0,0,1)+2, panel.CellY(0,0,1)+2,clrNONE,90);
   panel.DrawText("Time",  panel.CellX(0,1,0)+2, panel.CellY(0,1,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_MINUTES),  panel.CellX(0,1,1)+2, panel.CellY(0,1,1)+2,clrNONE,90);
   panel.DrawText("Open",  panel.CellX(0,2,0)+2, panel.CellY(0,2,0)+2); panel.DrawText(DoubleToString(rates[0].open,Digits()),      panel.CellX(0,2,1)+2, panel.CellY(0,2,1)+2,clrNONE,90);
   panel.DrawText("High",  panel.CellX(0,3,0)+2, panel.CellY(0,3,0)+2); panel.DrawText(DoubleToString(rates[0].high,Digits()),      panel.CellX(0,3,1)+2, panel.CellY(0,3,1)+2,clrNONE,90);
   panel.DrawText("Low",   panel.CellX(0,4,0)+2, panel.CellY(0,4,0)+2); panel.DrawText(DoubleToString(rates[0].low,Digits()),       panel.CellX(0,4,1)+2, panel.CellY(0,4,1)+2,clrNONE,90);
   panel.DrawText("Close", panel.CellX(0,5,0)+2, panel.CellY(0,5,0)+2); panel.DrawText(DoubleToString(rates[0].close,Digits()),     panel.CellX(0,5,1)+2, panel.CellY(0,5,1)+2,clrNONE,90);

//--- Выводим в таблицу 1 данные индикатора 1 с указанного бара в таблицу 1
   panel.DrawText(indicators.Title(macd1), panel.CellX(1,0,0)+2, panel.CellY(1,0,0)+2);
   double value1=indicators.GetData(macd1,0,0,index);
   string value_str1=(value1!=EMPTY_VALUE ? DoubleToString(value1,indicators.Digits(macd1)) : " ");
   panel.DrawText(value_str1,panel.CellX(1,0,1)+2,panel.CellY(1,0,1)+2,clrNONE,190);
   
//--- Выводим описание состояния линии индикатора 1
   panel.DrawText("Line state", panel.CellX(1,1,0)+2, panel.CellY(1,1,0)+2);
   ENUM_LINE_STATE state1=indicators.BufferLineState(Symbol(),PERIOD_CURRENT,macd1,0,0,index);
   panel.DrawText(BufferLineStateDescription(state1),panel.CellX(1,1,1)+2,panel.CellY(1,1,1)+2,clrNONE,190);
   
//--- Выводим в таблицу 2 данные индикатора 2 с указанного бара в таблицу 2
   panel.DrawText(indicators.Title(macd2), panel.CellX(2,0,0)+2, panel.CellY(2,0,0)+2);
   double value2=indicators.GetDataTo(Symbol(),PERIOD_CURRENT,macd2,0,0,index);
   string value_str2=(value2!=EMPTY_VALUE ? DoubleToString(value2,indicators.Digits(macd2)) : " ");
   panel.DrawText(value_str2,panel.CellX(2,0,1)+2,panel.CellY(2,0,1)+2,clrNONE,190);
   
//--- Выводим описание состояния линии индикатора 2
   panel.DrawText("Line state", panel.CellX(2,1,0)+2, panel.CellY(2,1,0)+2);
   ENUM_LINE_STATE state2=indicators.BufferLineState(Symbol(),PERIOD_CURRENT,macd2,0,0,index);
   panel.DrawText(BufferLineStateDescription(state2),panel.CellX(2,1,1)+2,panel.CellY(2,1,1)+2,clrNONE,190);
   
//--- Выводим описание соотношения линии индикатора 1 относительно линии индикатора 2
   double value21=indicators.GetDataTo(Symbol(),PERIOD_CURRENT,macd2,0,0,index+1);
   ENUM_LINE_STATE stateR=indicators.BufferLineStateRelative(Symbol(),PERIOD_CURRENT,macd1,0,0,index,value2,value21);
   string ma1=indicators.Name(macd1);
   string ma2=indicators.Name(macd2);
   string state_relative=
     (
      stateR==LINE_STATE_ABOVE      ? StringFormat("%s1 > %s2",ma1,ma2)   :
      stateR==LINE_STATE_BELOW      ? StringFormat("%s1 < %s2",ma1,ma2)           :
      stateR==LINE_STATE_CROSS_DOWN ? "Top-down crossing"   :
      stateR==LINE_STATE_CROSS_UP   ? "Bottom-up crossing"  :
      BufferLineStateDescription(stateR)
     );
   panel.DrawText(StringFormat("%s1 vs %s2",ma1,ma2), panel.CellX(2,2,0)+2, panel.CellY(2,2,0)+2);
   panel.DrawText(state_relative, panel.CellX(2,2,1)+2, panel.CellY(2,2,1)+2,clrNONE,190);
   
//--- Перерисовываем график для немедленного отображения всех изменений на панели
   ChartRedraw(ChartID());
  }
//+------------------------------------------------------------------+

После компиляции и запуска индикатора на графике М1 и выбранном периоде графика для расчёта М5, видим:



Индикатор MA of Oscillator:

//+------------------------------------------------------------------+
//|                                       TestMSTFMAofOscillator.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_separate_window
#property indicator_buffers 2
#property indicator_plots   2
//--- enums

//--- plot OsMA1
#property indicator_label1  "OsMA1"
#property indicator_type1   DRAW_HISTOGRAM
#property indicator_color1  clrSeaGreen
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1

//--- plot OsMA2
#property indicator_label2  "OsMA2"
#property indicator_type2   DRAW_HISTOGRAM
#property indicator_color2  clrDodgerBlue
#property indicator_style2  STYLE_SOLID
#property indicator_width2  1

//--- includes
#include <IndMSTF\IndMSTF.mqh>
#include <Dashboard\Dashboard.mqh>
//--- input parameters
input string               InpSymbol      =  NULL;             /* Symbol                     */ // Символ
input ENUM_TIMEFRAMES      InpTimeframe   =  PERIOD_CURRENT;   /* Timeframe                  */ // Таймфрейм
input int                  InpFastPeriod  =  12;               /* Fast MA Period             */ // Период быстрого EMA
input int                  InpSlowPeriod  =  26;               /* Fast MA Period             */ // Период медленного EMA
input int                  InpSignalPeriod=  9;                /* Signal Period              */ // Период сигнального MA
input ENUM_APPLIED_PRICE   InpPrice       =  PRICE_CLOSE;      /* Applied Price              */ // Цена расчёта
input uchar                InpLineWidth1  =  2;                /* Senior period Line Width   */ // Толщина линии для старшего периода
input uchar                InpLineWidth2  =  1;                /* Junior period Line Width   */ // Толщина линии для младшего периода
input bool                 InpAsSeries    =  true;             /* As Series flag             */ // Флаг серийности массивов буферов индикатора

//--- indicator buffers
double         BufferOsMA1[];
double         BufferOsMA2[];
//--- global variables
int handle_osma1;
int handle_osma2;
CMSTFIndicators indicators;      // Экземпляр объекта коллекции индикаторов
//--- переменные для панели
CDashboard *panel=NULL;          // Указатель на объект панели
int         mouse_bar_index;     // Индекс бара, с которого берутся данные
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Устанавливаем таймер с периодичностью в 1 секунду
   EventSetTimer(1);
//--- Назначаем рисуемым буферам 0 и 1 массивы BufferMA1 и BufferMA2 соответственно
   SetIndexBuffer(0,BufferOsMA1,INDICATOR_DATA);
   SetIndexBuffer(1,BufferOsMA2,INDICATOR_DATA);
//--- Устанавливаем ширину линий
   int w1=0,w2=0;
   if(InpTimeframe>Period())
     {
      w1=InpLineWidth2;
      w2=InpLineWidth1;
     }
   else
     {
      w1=InpLineWidth1;
      w2=InpLineWidth2;
     }
   PlotIndexSetInteger(0,PLOT_LINE_WIDTH,w1);
   PlotIndexSetInteger(1,PLOT_LINE_WIDTH,w2);
//--- sets indicator shift

//--- Устанавливаем флаги серийности массивам буферов индикатора (для теста, чтобы видно было отсутствие разницы)
   ArraySetAsSeries(BufferOsMA1,InpAsSeries);
   ArraySetAsSeries(BufferOsMA2,InpAsSeries);
   
//--- Создаём два индикатора одного типа
//--- Первый рассчитывается на текущих символе/периоде графика, а второй - на тех, что заданы в настройках
   handle_osma1=indicators.AddNewOsMA(NULL,PERIOD_CURRENT,InpFastPeriod,InpSlowPeriod,InpSignalPeriod,InpPrice);
   handle_osma2=indicators.AddNewOsMA(InpSymbol,InpTimeframe,InpFastPeriod,InpSlowPeriod,InpSignalPeriod,InpPrice);

//--- Если не удалось создать хэндлы индикаторов - возвращаем ошибку инициализации
   if(handle_osma1==INVALID_HANDLE || handle_osma2==INVALID_HANDLE)
      return INIT_FAILED;
//--- Устанавливаем описания линий индикатора из описаний буферов расчётной части созданных индикаторов
   indicators.SetPlotLabelFromBuffer(0,handle_osma1,0);
   indicators.SetPlotLabelFromBuffer(1,handle_osma2,0);
      
//--- Панель
//--- Создаём панель
   int width=301;
   panel=new CDashboard(1,20,20,width,264,0);
   if(panel==NULL)
     {
      Print("Error. Failed to create panel object");
      return INIT_FAILED;
     }
//--- Устанавливаем параметры шрифта
   panel.SetFontParams("Calibri",9);
//--- Отображаем панель с текстом в заголовке "Символ, Описание таймфрейма"
   panel.View(Symbol()+", "+StringSubstr(EnumToString(Period()),7));

//--- Создаём таблицу с идентификатором 0 для отображения в ней данных бара
   panel.CreateNewTable(0);
//--- Рисуем таблицу с идентификатором 0 на фоне панели
   panel.DrawGrid(0,2,20,6,2,18,width/2-2);

//--- Создаём таблицу с идентификатором 1 для отображения в ней данных индикатора 1
   panel.CreateNewTable(1);
//--- Получаем координату Y2 таблицы с идентификатором 0 и
//--- устанавливаем координату Y1 для таблицы с идентификатором 1
   int y1=panel.TableY2(0)+22;
//--- Рисуем таблицу с идентификатором 1 на фоне панели
   panel.DrawGrid(1,2,y1,2,2,18,width/2-2);

//--- Создаём таблицу с идентификатором 2 для отображения в ней данных индикатора 2
   panel.CreateNewTable(2);
//--- Получаем координату Y2 таблицы с идентификатором 1 и
//--- устанавливаем координату Y1 для таблицы с идентификатором 2
   int y2=panel.TableY2(1)+3;
//--- Рисуем таблицу с идентификатором 2 на фоне панели
   panel.DrawGrid(2,2,y2,3,2,18,width/2-2);
   
//--- Инициализируем переменную с индексом бара указателя мышки
   mouse_bar_index=0;
//--- Выводим на панель данные текущего бара
   DrawData(mouse_bar_index,TimeCurrent());

//--- Успешная инициализация
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- Уничтожаем таймер
   EventKillTimer();
//--- Если объект панели существует - удаляем его
   if(panel!=NULL)
      delete panel;
//--- Стираем все комментарии
   Comment("");
  }
//+------------------------------------------------------------------+
//| 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[])
  {
//--- Количество баров для расчёта
   int limit=rates_total-prev_calculated;
//--- Если limit > 1, значит это первый расчёт, либо изменение в истории
   if(limit>1)
     {
      //--- указываем для просчёта всю доступную историю
      limit=rates_total-1;
      /*
      // Если в индикаторе есть какие-либо буферы, в которых отображены другие расчёты (не мульти- индикаторы),
      // то здесь их нужно инициализировать "пустым" значением, установленным для этих буферов
      */
     }
//--- Рассчитываем все созданные мультисимвольные мультипериодные индикаторы
   if(!indicators.Calculate())
      return 0;

//--- Выводим на панель данные бара под курсором (либо текущий бар, если курсор за пределами графика)
   DrawData(mouse_bar_index,time[mouse_bar_index]);

//--- Из буферов рассчитанных индикаторов выводим данные в индикаторные буферы
   if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_osma1,0,0,limit,BufferOsMA1))
      return 0;
   if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_osma2,0,0,limit,BufferOsMA2))
      return 0;

//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| Timer function                                                   | 
//+------------------------------------------------------------------+
void OnTimer()
  {
//--- Вызываем таймер коллекции индикаторов
   indicators.OnTimer();
  }
//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- Работа с панелью
//--- Вызываем обработчик событий панели
   panel.OnChartEvent(id,lparam,dparam,sparam);

//--- Если курсор перемещается или щелчок по графику
   if(id==CHARTEVENT_MOUSE_MOVE || id==CHARTEVENT_CLICK)
     {
      //--- Объявляем переменные для записи в них координат времени и цены
      datetime time=0;
      double price=0;
      int wnd=0;
      //--- Если координаты курсора преобразованы в дату и время
      if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price))
        {
         //--- записываем индекс бара, где расположен курсор в глобальную переменную
         mouse_bar_index=iBarShift(Symbol(),PERIOD_CURRENT,time);
         //--- Выводим данные бара под курсором на панель
         DrawData(mouse_bar_index,time);
        }
     }

//--- Если получили пользовательское событие - выводим об этом сообщение в журнал
   if(id>CHARTEVENT_CUSTOM)
     {
      //--- Здесь может быть обработка щелчка по кнопке закрытия на панели
      PrintFormat("%s: Event id=%ld, object id (lparam): %lu, event message (sparam): %s",__FUNCTION__,id,lparam,sparam);
     }
  }
//+------------------------------------------------------------------+
//| Выводит данные с указанного индекса таймсерии на панель          |
//+------------------------------------------------------------------+
void DrawData(const int index,const datetime time)
  {
//--- Объявляем переменные для получения в них данных
   MqlRates rates[1];

//--- Если данные бара по указанному индексу получить не удалось - уходим
   if(CopyRates(Symbol(),PERIOD_CURRENT,index,1,rates)!=1)
      return;

//--- Устанавливаем параметры шрифта для заголовков данных бара и индикатора
   int  size=0;
   uint flags=0;
   uint angle=0;
   string name=panel.FontParams(size,flags,angle);
   panel.SetFontParams(name,9,FW_BOLD);
   panel.DrawText("Bar data ["+(string)index+"]",3,panel.TableY1(0)-16,clrMaroon,panel.Width()-6);
   panel.DrawText("Indicators data ["+(string)index+"]",3,panel.TableY1(1)-16,clrGreen,panel.Width()-6);
//--- Устанавливаем параметры шрифта для данных бара и индикатора
   panel.SetFontParams(name,9);

//--- Выводим на панель данные указанного бара в таблицу 0
   panel.DrawText("Date",  panel.CellX(0,0,0)+2, panel.CellY(0,0,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_DATE),     panel.CellX(0,0,1)+2, panel.CellY(0,0,1)+2,clrNONE,90);
   panel.DrawText("Time",  panel.CellX(0,1,0)+2, panel.CellY(0,1,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_MINUTES),  panel.CellX(0,1,1)+2, panel.CellY(0,1,1)+2,clrNONE,90);
   panel.DrawText("Open",  panel.CellX(0,2,0)+2, panel.CellY(0,2,0)+2); panel.DrawText(DoubleToString(rates[0].open,Digits()),      panel.CellX(0,2,1)+2, panel.CellY(0,2,1)+2,clrNONE,90);
   panel.DrawText("High",  panel.CellX(0,3,0)+2, panel.CellY(0,3,0)+2); panel.DrawText(DoubleToString(rates[0].high,Digits()),      panel.CellX(0,3,1)+2, panel.CellY(0,3,1)+2,clrNONE,90);
   panel.DrawText("Low",   panel.CellX(0,4,0)+2, panel.CellY(0,4,0)+2); panel.DrawText(DoubleToString(rates[0].low,Digits()),       panel.CellX(0,4,1)+2, panel.CellY(0,4,1)+2,clrNONE,90);
   panel.DrawText("Close", panel.CellX(0,5,0)+2, panel.CellY(0,5,0)+2); panel.DrawText(DoubleToString(rates[0].close,Digits()),     panel.CellX(0,5,1)+2, panel.CellY(0,5,1)+2,clrNONE,90);

//--- Выводим в таблицу 1 данные индикатора 1 с указанного бара в таблицу 1
   panel.DrawText(indicators.Title(handle_osma1), panel.CellX(1,0,0)+2, panel.CellY(1,0,0)+2);
   double value1=indicators.GetData(handle_osma1,0,0,index);
   string value_str1=(value1!=EMPTY_VALUE ? DoubleToString(value1,indicators.Digits(handle_osma1)) : " ");
   panel.DrawText(value_str1,panel.CellX(1,0,1)+2,panel.CellY(1,0,1)+2,clrNONE,190);
   
//--- Выводим описание состояния линии индикатора 1
   panel.DrawText("Line state", panel.CellX(1,1,0)+2, panel.CellY(1,1,0)+2);
   ENUM_LINE_STATE state1=indicators.BufferLineState(Symbol(),PERIOD_CURRENT,handle_osma1,0,0,index);
   panel.DrawText(BufferLineStateDescription(state1),panel.CellX(1,1,1)+2,panel.CellY(1,1,1)+2,clrNONE,190);
   
//--- Выводим в таблицу 2 данные индикатора 2 с указанного бара в таблицу 2
   panel.DrawText(indicators.Title(handle_osma2), panel.CellX(2,0,0)+2, panel.CellY(2,0,0)+2);
   double value2=indicators.GetDataTo(Symbol(),PERIOD_CURRENT,handle_osma2,0,0,index);
   string value_str2=(value2!=EMPTY_VALUE ? DoubleToString(value2,indicators.Digits(handle_osma2)) : " ");
   panel.DrawText(value_str2,panel.CellX(2,0,1)+2,panel.CellY(2,0,1)+2,clrNONE,190);
   
//--- Выводим описание состояния линии индикатора 2
   panel.DrawText("Line state", panel.CellX(2,1,0)+2, panel.CellY(2,1,0)+2);
   ENUM_LINE_STATE state2=indicators.BufferLineState(Symbol(),PERIOD_CURRENT,handle_osma2,0,0,index);
   panel.DrawText(BufferLineStateDescription(state2),panel.CellX(2,1,1)+2,panel.CellY(2,1,1)+2,clrNONE,190);
   
//--- Выводим описание соотношения линии индикатора 1 относительно линии индикатора 2
   double value21=indicators.GetDataTo(Symbol(),PERIOD_CURRENT,handle_osma2,0,0,index+1);
   ENUM_LINE_STATE stateR=indicators.BufferLineStateRelative(Symbol(),PERIOD_CURRENT,handle_osma1,0,0,index,value2,value21);
   string ma1=indicators.Name(handle_osma1);
   string ma2=indicators.Name(handle_osma2);
   string state_relative=
     (
      stateR==LINE_STATE_ABOVE      ? StringFormat("%s1 > %s2",ma1,ma2)   :
      stateR==LINE_STATE_BELOW      ? StringFormat("%s1 < %s2",ma1,ma2)           :
      stateR==LINE_STATE_CROSS_DOWN ? "Top-down crossing"   :
      stateR==LINE_STATE_CROSS_UP   ? "Bottom-up crossing"  :
      BufferLineStateDescription(stateR)
     );
   panel.DrawText(StringFormat("%s1 vs %s2",ma1,ma2), panel.CellX(2,2,0)+2, panel.CellY(2,2,0)+2);
   panel.DrawText(state_relative, panel.CellX(2,2,1)+2, panel.CellY(2,2,1)+2,clrNONE,190);
   
//--- Перерисовываем график для немедленного отображения всех изменений на панели
   ChartRedraw(ChartID());
  }
//+------------------------------------------------------------------+

После компиляции и запуска индикатора на графике М1 и выбранном периоде графика для расчёта М5, видим:



Индикатор Market Facilitation Index:

//+------------------------------------------------------------------+
//|                              TestMSTFMarketFacilitationIndex.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_separate_window
#property indicator_buffers 4
#property indicator_plots   2
//--- enums

//--- plot AC1
#property indicator_label1  "BWMFI1"
#property indicator_type1   DRAW_COLOR_HISTOGRAM
//#property indicator_color1  clrLime,clrSaddleBrown,clrBlue,clrPink // аналог в стр.98
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1

//--- plot AC2
#property indicator_label2  "BWMFI2"
#property indicator_type2   DRAW_COLOR_HISTOGRAM
//#property indicator_color2  clrLime,clrSaddleBrown,clrBlue,clrPink // аналог в стр.99
#property indicator_style2  STYLE_SOLID
#property indicator_width2  1

//--- includes
#include <IndMSTF\IndMSTF.mqh>
#include <Dashboard\Dashboard.mqh>
//--- input parameters
input string               InpSymbol      =  NULL;             /* Symbol                     */ // Символ
input ENUM_TIMEFRAMES      InpTimeframe   =  PERIOD_CURRENT;   /* Timeframe                  */ // Таймфрейм
input ENUM_APPLIED_VOLUME  InpVolume      =  VOLUME_TICK;      /* Applied Volume             */ // Используемый объём
input uchar                InpLineWidth1  =  2;                /* Senior period Line Width   */ // Толщина линии для старшего периода
input uchar                InpLineWidth2  =  1;                /* Junior period Line Width   */ // Толщина линии для младшего периода
input bool                 InpAsSeries    =  true;             /* As Series flag             */ // Флаг серийности массивов буферов индикатора

//--- indicator buffers
double         BufferBWMFI1[];
double         BufferClrBWMFI1[];
double         BufferBWMFI2[];
double         BufferClrBWMFI2[];
//--- global variables
int handle_bwmfi1;
int handle_bwmfi2;
CMSTFIndicators indicators;      // Экземпляр объекта коллекции индикаторов
//--- переменные для панели
CDashboard *panel=NULL;          // Указатель на объект панели
int         mouse_bar_index;     // Индекс бара, с которого берутся данные
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Устанавливаем таймер с периодичностью в 1 секунду
   EventSetTimer(1);
//--- Назначаем рисуемым буферам 0 и 2 массивы BufferBWMFI1 и BufferBWMFI2 соответственно,
//--- а буферам 1 и 3 массивы цвета BufferClrBWMFI1 и BufferClrBWMFI2
   SetIndexBuffer(0,BufferBWMFI1,INDICATOR_DATA);
   SetIndexBuffer(1,BufferClrBWMFI1,INDICATOR_COLOR_INDEX);
   SetIndexBuffer(2,BufferBWMFI2,INDICATOR_DATA);
   SetIndexBuffer(3,BufferClrBWMFI2,INDICATOR_COLOR_INDEX);
//--- Устанавливаем ширину линий
   int w1=0,w2=0;
   if(InpTimeframe>Period())
     {
      w1=InpLineWidth2;
      w2=InpLineWidth1;
     }
   else
     {
      w1=InpLineWidth1;
      w2=InpLineWidth2;
     }
   PlotIndexSetInteger(0,PLOT_LINE_WIDTH,w1);
   PlotIndexSetInteger(1,PLOT_LINE_WIDTH,w2);
//--- sets indicator shift

//--- Устанавливаем флаги серийности массивам буферов индикатора (для теста, чтобы видно было отсутствие разницы)
   ArraySetAsSeries(BufferBWMFI1,InpAsSeries);
   ArraySetAsSeries(BufferClrBWMFI1,InpAsSeries);
   ArraySetAsSeries(BufferBWMFI2,InpAsSeries);
   ArraySetAsSeries(BufferClrBWMFI2,InpAsSeries);
   
//--- Создаём два индикатора одного типа
//--- Первый рассчитывается на текущих символе/периоде графика, а второй - на тех, что заданы в настройках
   handle_bwmfi1=indicators.AddNewBWMFI(NULL,PERIOD_CURRENT,InpVolume);
   handle_bwmfi2=indicators.AddNewBWMFI(InpSymbol,InpTimeframe,InpVolume);

//--- Если не удалось создать хэндлы индикаторов - возвращаем ошибку инициализации
   if(handle_bwmfi1==INVALID_HANDLE || handle_bwmfi2==INVALID_HANDLE)
      return INIT_FAILED;
//--- Устанавливаем описания линий индикаторов из описаний буферов расчётной части созданных индикаторов
   indicators.SetPlotLabelFromBuffer(0,handle_bwmfi1,0);
   indicators.SetPlotLabelFromBuffer(1,handle_bwmfi2,0);
//--- Устанавливаем цвета линиям индикаторов из набора цветов буферов расчётной части созданных индикаторов
   indicators.SetPlotColorsFromBuffer(0,handle_bwmfi1,0);
   indicators.SetPlotColorsFromBuffer(1,handle_bwmfi2,0);
      
//--- Панель
//--- Создаём панель
   int width=247;
   panel=new CDashboard(1,20,20,width,264,0);
   if(panel==NULL)
     {
      Print("Error. Failed to create panel object");
      return INIT_FAILED;
     }
//--- Устанавливаем параметры шрифта
   panel.SetFontParams("Calibri",9);
//--- Отображаем панель с текстом в заголовке "Символ, Описание таймфрейма"
   panel.View(Symbol()+", "+StringSubstr(EnumToString(Period()),7));

//--- Создаём таблицу с идентификатором 0 для отображения в ней данных бара
   panel.CreateNewTable(0);
//--- Рисуем таблицу с идентификатором 0 на фоне панели
   panel.DrawGrid(0,2,20,6,2,18,width/2-2);

//--- Создаём таблицу с идентификатором 1 для отображения в ней данных индикатора 1
   panel.CreateNewTable(1);
//--- Получаем координату Y2 таблицы с идентификатором 0 и
//--- устанавливаем координату Y1 для таблицы с идентификатором 1
   int y1=panel.TableY2(0)+22;
//--- Рисуем таблицу с идентификатором 1 на фоне панели
   panel.DrawGrid(1,2,y1,2,2,18,width/2-2);

//--- Создаём таблицу с идентификатором 2 для отображения в ней данных индикатора 2
   panel.CreateNewTable(2);
//--- Получаем координату Y2 таблицы с идентификатором 1 и
//--- устанавливаем координату Y1 для таблицы с идентификатором 2
   int y2=panel.TableY2(1)+3;
//--- Рисуем таблицу с идентификатором 2 на фоне панели
   panel.DrawGrid(2,2,y2,3,2,18,width/2-2);
   
//--- Инициализируем переменную с индексом бара указателя мышки
   mouse_bar_index=0;
//--- Выводим на панель данные текущего бара
   DrawData(mouse_bar_index,TimeCurrent());

//--- Успешная инициализация
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- Уничтожаем таймер
   EventKillTimer();
//--- Если объект панели существует - удаляем его
   if(panel!=NULL)
      delete panel;
//--- Стираем все комментарии
   Comment("");
  }
//+------------------------------------------------------------------+
//| 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[])
  {
//--- Количество баров для расчёта
   int limit=rates_total-prev_calculated;
//--- Если limit > 1, значит это первый расчёт, либо изменение в истории
   if(limit>1)
     {
      //--- указываем для просчёта всю доступную историю
      limit=rates_total-1;
      /*
      // Если в индикаторе есть какие-либо буферы, в которых отображены другие расчёты (не мульти- индикаторы),
      // то здесь их нужно инициализировать "пустым" значением, установленным для этих буферов
      */
     }
//--- Рассчитываем все созданные мультисимвольные мультипериодные индикаторы
   if(!indicators.Calculate())
      return 0;

//--- Выводим на панель данные бара под курсором (либо текущий бар, если курсор за пределами графика)
   DrawData(mouse_bar_index,time[mouse_bar_index]);

//--- Из буферов рассчитанных индикаторов выводим данные в индикаторные буферы
   if(!indicators.DataToColorBuffer(NULL,PERIOD_CURRENT,handle_bwmfi1,0,0,limit,BufferBWMFI1,BufferClrBWMFI1))
      return 0;
   if(!indicators.DataToColorBuffer(NULL,PERIOD_CURRENT,handle_bwmfi2,0,0,limit,BufferBWMFI2,BufferClrBWMFI2))
      return 0;

//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| Timer function                                                   | 
//+------------------------------------------------------------------+
void OnTimer()
  {
//--- Вызываем таймер коллекции индикаторов
   indicators.OnTimer();
  }
//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- Работа с панелью
//--- Вызываем обработчик событий панели
   panel.OnChartEvent(id,lparam,dparam,sparam);

//--- Если курсор перемещается или щелчок по графику
   if(id==CHARTEVENT_MOUSE_MOVE || id==CHARTEVENT_CLICK)
     {
      //--- Объявляем переменные для записи в них координат времени и цены
      datetime time=0;
      double price=0;
      int wnd=0;
      //--- Если координаты курсора преобразованы в дату и время
      if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price))
        {
         //--- записываем индекс бара, где расположен курсор в глобальную переменную
         mouse_bar_index=iBarShift(Symbol(),PERIOD_CURRENT,time);
         //--- Выводим данные бара под курсором на панель
         DrawData(mouse_bar_index,time);
        }
     }

//--- Если получили пользовательское событие - выводим об этом сообщение в журнал
   if(id>CHARTEVENT_CUSTOM)
     {
      //--- Здесь может быть обработка щелчка по кнопке закрытия на панели
      PrintFormat("%s: Event id=%ld, object id (lparam): %lu, event message (sparam): %s",__FUNCTION__,id,lparam,sparam);
     }
  }
//+------------------------------------------------------------------+
//| Выводит данные с указанного индекса таймсерии на панель          |
//+------------------------------------------------------------------+
void DrawData(const int index,const datetime time)
  {
//--- Объявляем переменные для получения в них данных
   MqlRates rates[1];

//--- Если данные бара по указанному индексу получить не удалось - уходим
   if(CopyRates(Symbol(),PERIOD_CURRENT,index,1,rates)!=1)
      return;

//--- Устанавливаем параметры шрифта для заголовков данных бара и индикатора
   int  size=0;
   uint flags=0;
   uint angle=0;
   string name=panel.FontParams(size,flags,angle);
   panel.SetFontParams(name,9,FW_BOLD);
   panel.DrawText("Bar data ["+(string)index+"]",3,panel.TableY1(0)-16,clrMaroon,panel.Width()-6);
   panel.DrawText("Indicators data ["+(string)index+"]",3,panel.TableY1(1)-16,clrGreen,panel.Width()-6);
//--- Устанавливаем параметры шрифта для данных бара и индикатора
   panel.SetFontParams(name,9);

//--- Выводим на панель данные указанного бара в таблицу 0
   panel.DrawText("Date",  panel.CellX(0,0,0)+2, panel.CellY(0,0,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_DATE),     panel.CellX(0,0,1)+2, panel.CellY(0,0,1)+2,clrNONE,90);
   panel.DrawText("Time",  panel.CellX(0,1,0)+2, panel.CellY(0,1,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_MINUTES),  panel.CellX(0,1,1)+2, panel.CellY(0,1,1)+2,clrNONE,90);
   panel.DrawText("Open",  panel.CellX(0,2,0)+2, panel.CellY(0,2,0)+2); panel.DrawText(DoubleToString(rates[0].open,Digits()),      panel.CellX(0,2,1)+2, panel.CellY(0,2,1)+2,clrNONE,90);
   panel.DrawText("High",  panel.CellX(0,3,0)+2, panel.CellY(0,3,0)+2); panel.DrawText(DoubleToString(rates[0].high,Digits()),      panel.CellX(0,3,1)+2, panel.CellY(0,3,1)+2,clrNONE,90);
   panel.DrawText("Low",   panel.CellX(0,4,0)+2, panel.CellY(0,4,0)+2); panel.DrawText(DoubleToString(rates[0].low,Digits()),       panel.CellX(0,4,1)+2, panel.CellY(0,4,1)+2,clrNONE,90);
   panel.DrawText("Close", panel.CellX(0,5,0)+2, panel.CellY(0,5,0)+2); panel.DrawText(DoubleToString(rates[0].close,Digits()),     panel.CellX(0,5,1)+2, panel.CellY(0,5,1)+2,clrNONE,90);

//--- Выводим в таблицу 1 данные индикатора 1 с указанного бара в таблицу 1
   panel.DrawText(indicators.Title(handle_bwmfi1), panel.CellX(1,0,0)+2, panel.CellY(1,0,0)+2);
   double value1=indicators.GetData(handle_bwmfi1,0,0,index);
   string value_str1=(value1!=EMPTY_VALUE ? DoubleToString(value1,indicators.Digits(handle_bwmfi1)) : " ");
   panel.DrawText(value_str1,panel.CellX(1,0,1)+2,panel.CellY(1,0,1)+2,clrNONE,110);
   
//--- Выводим описание состояния линии индикатора 1
   panel.DrawText("Line state", panel.CellX(1,1,0)+2, panel.CellY(1,1,0)+2);
   ENUM_LINE_STATE state1=indicators.BufferLineState(Symbol(),PERIOD_CURRENT,handle_bwmfi1,0,0,index);
   panel.DrawText(BufferLineStateDescription(state1),panel.CellX(1,1,1)+2,panel.CellY(1,1,1)+2,clrNONE,110);
   
//--- Выводим в таблицу 2 данные индикатора 2 с указанного бара в таблицу 2
   panel.DrawText(indicators.Title(handle_bwmfi2), panel.CellX(2,0,0)+2, panel.CellY(2,0,0)+2);
   double value2=indicators.GetDataTo(Symbol(),PERIOD_CURRENT,handle_bwmfi2,0,0,index);
   string value_str2=(value2!=EMPTY_VALUE ? DoubleToString(value2,indicators.Digits(handle_bwmfi2)) : " ");
   panel.DrawText(value_str2,panel.CellX(2,0,1)+2,panel.CellY(2,0,1)+2,clrNONE,110);
   
//--- Выводим описание состояния линии индикатора 2
   panel.DrawText("Line state", panel.CellX(2,1,0)+2, panel.CellY(2,1,0)+2);
   ENUM_LINE_STATE state2=indicators.BufferLineState(Symbol(),PERIOD_CURRENT,handle_bwmfi2,0,0,index);
   panel.DrawText(BufferLineStateDescription(state2),panel.CellX(2,1,1)+2,panel.CellY(2,1,1)+2,clrNONE,110);
   
//--- Выводим описание соотношения линии индикатора 1 относительно линии индикатора 2
   double value21=indicators.GetDataTo(Symbol(),PERIOD_CURRENT,handle_bwmfi2,0,0,index+1);
   ENUM_LINE_STATE stateR=indicators.BufferLineStateRelative(Symbol(),PERIOD_CURRENT,handle_bwmfi1,0,0,index,value2,value21);
   string ma1=indicators.Name(handle_bwmfi1);
   string ma2=indicators.Name(handle_bwmfi2);
   string state_relative=
     (
      stateR==LINE_STATE_ABOVE      ? StringFormat("%s1 > %s2",ma1,ma2)   :
      stateR==LINE_STATE_BELOW      ? StringFormat("%s1 < %s2",ma1,ma2)           :
      stateR==LINE_STATE_CROSS_DOWN ? "Top-down crossing"   :
      stateR==LINE_STATE_CROSS_UP   ? "Bottom-up crossing"  :
      BufferLineStateDescription(stateR)
     );
   panel.DrawText(StringFormat("%s1 vs %s2",ma1,ma2), panel.CellX(2,2,0)+2, panel.CellY(2,2,0)+2);
   panel.DrawText(state_relative, panel.CellX(2,2,1)+2, panel.CellY(2,2,1)+2,clrNONE,110);
   
//--- Перерисовываем график для немедленного отображения всех изменений на панели
   ChartRedraw(ChartID());
  }
//+------------------------------------------------------------------+

После компиляции и запуска индикатора на графике М1 и выбранном периоде графика для расчёта М5, видим:



Индикатор Momentum:

//+------------------------------------------------------------------+
//|                                             TestMSTFMomentum.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_separate_window
#property indicator_buffers 2
#property indicator_plots   2
//--- enums

//--- plot Momentum1
#property indicator_label1  "Momentum1"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrSeaGreen
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1

//--- plot Momentum2
#property indicator_label2  "Momentum2"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrDodgerBlue
#property indicator_style2  STYLE_SOLID
#property indicator_width2  1

//--- includes
#include <IndMSTF\IndMSTF.mqh>
#include <Dashboard\Dashboard.mqh>
//--- input parameters
input string               InpSymbol      =  NULL;             /* Symbol                     */ // Символ
input ENUM_TIMEFRAMES      InpTimeframe   =  PERIOD_CURRENT;   /* Timeframe                  */ // Таймфрейм
input int                  InpPeriod      =  14;               /* Period                     */ // Период расчёта
input ENUM_APPLIED_PRICE   InpPrice       =  PRICE_CLOSE;      /* Applied Price              */ // Цена расчёта
input uchar                InpLineWidth1  =  2;                /* Senior period Line Width   */ // Толщина линии для старшего периода
input uchar                InpLineWidth2  =  1;                /* Junior period Line Width   */ // Толщина линии для младшего периода
input bool                 InpAsSeries    =  true;             /* As Series flag             */ // Флаг серийности массивов буферов индикатора

//--- indicator buffers
double         BufferMomentum1[];
double         BufferMomentum2[];
//--- global variables
int handle_mom1;
int handle_mom2;
CMSTFIndicators indicators;      // Экземпляр объекта коллекции индикаторов
//--- переменные для панели
CDashboard *panel=NULL;          // Указатель на объект панели
int         mouse_bar_index;     // Индекс бара, с которого берутся данные
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Устанавливаем таймер с периодичностью в 1 секунду
   EventSetTimer(1);
//--- Назначаем рисуемым буферам 0 и 1 массивы BufferMA1 и BufferMA2 соответственно
   SetIndexBuffer(0,BufferMomentum1,INDICATOR_DATA);
   SetIndexBuffer(1,BufferMomentum2,INDICATOR_DATA);
//--- Устанавливаем ширину линий
   int w1=0,w2=0;
   if(InpTimeframe>Period())
     {
      w1=InpLineWidth2;
      w2=InpLineWidth1;
     }
   else
     {
      w1=InpLineWidth1;
      w2=InpLineWidth2;
     }
   PlotIndexSetInteger(0,PLOT_LINE_WIDTH,w1);
   PlotIndexSetInteger(1,PLOT_LINE_WIDTH,w2);
//--- sets indicator shift

//--- Устанавливаем флаги серийности массивам буферов индикатора (для теста, чтобы видно было отсутствие разницы)
   ArraySetAsSeries(BufferMomentum1,InpAsSeries);
   ArraySetAsSeries(BufferMomentum2,InpAsSeries);
   
//--- Создаём два индикатора одного типа
//--- Первый рассчитывается на текущих символе/периоде графика, а второй - на тех, что заданы в настройках
   handle_mom1=indicators.AddNewMomentum(NULL,PERIOD_CURRENT,InpPeriod,InpPrice);
   handle_mom2=indicators.AddNewMomentum(InpSymbol,InpTimeframe,InpPeriod,InpPrice);

//--- Если не удалось создать хэндлы индикаторов - возвращаем ошибку инициализации
   if(handle_mom1==INVALID_HANDLE || handle_mom2==INVALID_HANDLE)
      return INIT_FAILED;
//--- Устанавливаем описания линий индикатора из описаний буферов расчётной части созданных индикаторов
   indicators.SetPlotLabelFromBuffer(0,handle_mom1,0);
   indicators.SetPlotLabelFromBuffer(1,handle_mom2,0);
      
//--- Панель
//--- Создаём панель
   int width=317;
   panel=new CDashboard(1,20,20,width,264,0);
   if(panel==NULL)
     {
      Print("Error. Failed to create panel object");
      return INIT_FAILED;
     }
//--- Устанавливаем параметры шрифта
   panel.SetFontParams("Calibri",9);
//--- Отображаем панель с текстом в заголовке "Символ, Описание таймфрейма"
   panel.View(Symbol()+", "+StringSubstr(EnumToString(Period()),7));

//--- Создаём таблицу с идентификатором 0 для отображения в ней данных бара
   panel.CreateNewTable(0);
//--- Рисуем таблицу с идентификатором 0 на фоне панели
   panel.DrawGrid(0,2,20,6,2,18,width/2-2);

//--- Создаём таблицу с идентификатором 1 для отображения в ней данных индикатора 1
   panel.CreateNewTable(1);
//--- Получаем координату Y2 таблицы с идентификатором 0 и
//--- устанавливаем координату Y1 для таблицы с идентификатором 1
   int y1=panel.TableY2(0)+22;
//--- Рисуем таблицу с идентификатором 1 на фоне панели
   panel.DrawGrid(1,2,y1,2,2,18,width/2-2);

//--- Создаём таблицу с идентификатором 2 для отображения в ней данных индикатора 2
   panel.CreateNewTable(2);
//--- Получаем координату Y2 таблицы с идентификатором 1 и
//--- устанавливаем координату Y1 для таблицы с идентификатором 2
   int y2=panel.TableY2(1)+3;
//--- Рисуем таблицу с идентификатором 2 на фоне панели
   panel.DrawGrid(2,2,y2,3,2,18,width/2-2);
   
//--- Инициализируем переменную с индексом бара указателя мышки
   mouse_bar_index=0;
//--- Выводим на панель данные текущего бара
   DrawData(mouse_bar_index,TimeCurrent());

//--- Успешная инициализация
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- Уничтожаем таймер
   EventKillTimer();
//--- Если объект панели существует - удаляем его
   if(panel!=NULL)
      delete panel;
//--- Стираем все комментарии
   Comment("");
  }
//+------------------------------------------------------------------+
//| 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[])
  {
//--- Количество баров для расчёта
   int limit=rates_total-prev_calculated;
//--- Если limit > 1, значит это первый расчёт, либо изменение в истории
   if(limit>1)
     {
      //--- указываем для просчёта всю доступную историю
      limit=rates_total-1;
      /*
      // Если в индикаторе есть какие-либо буферы, в которых отображены другие расчёты (не мульти- индикаторы),
      // то здесь их нужно инициализировать "пустым" значением, установленным для этих буферов
      */
     }
//--- Рассчитываем все созданные мультисимвольные мультипериодные индикаторы
   if(!indicators.Calculate())
      return 0;

//--- Выводим на панель данные бара под курсором (либо текущий бар, если курсор за пределами графика)
   DrawData(mouse_bar_index,time[mouse_bar_index]);

//--- Из буферов рассчитанных индикаторов выводим данные в индикаторные буферы
   if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_mom1,0,0,limit,BufferMomentum1))
      return 0;
   if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_mom2,0,0,limit,BufferMomentum2))
      return 0;

//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| Timer function                                                   | 
//+------------------------------------------------------------------+
void OnTimer()
  {
//--- Вызываем таймер коллекции индикаторов
   indicators.OnTimer();
  }
//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- Работа с панелью
//--- Вызываем обработчик событий панели
   panel.OnChartEvent(id,lparam,dparam,sparam);

//--- Если курсор перемещается или щелчок по графику
   if(id==CHARTEVENT_MOUSE_MOVE || id==CHARTEVENT_CLICK)
     {
      //--- Объявляем переменные для записи в них координат времени и цены
      datetime time=0;
      double price=0;
      int wnd=0;
      //--- Если координаты курсора преобразованы в дату и время
      if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price))
        {
         //--- записываем индекс бара, где расположен курсор в глобальную переменную
         mouse_bar_index=iBarShift(Symbol(),PERIOD_CURRENT,time);
         //--- Выводим данные бара под курсором на панель
         DrawData(mouse_bar_index,time);
        }
     }

//--- Если получили пользовательское событие - выводим об этом сообщение в журнал
   if(id>CHARTEVENT_CUSTOM)
     {
      //--- Здесь может быть обработка щелчка по кнопке закрытия на панели
      PrintFormat("%s: Event id=%ld, object id (lparam): %lu, event message (sparam): %s",__FUNCTION__,id,lparam,sparam);
     }
  }
//+------------------------------------------------------------------+
//| Выводит данные с указанного индекса таймсерии на панель          |
//+------------------------------------------------------------------+
void DrawData(const int index,const datetime time)
  {
//--- Объявляем переменные для получения в них данных
   MqlRates rates[1];

//--- Если данные бара по указанному индексу получить не удалось - уходим
   if(CopyRates(Symbol(),PERIOD_CURRENT,index,1,rates)!=1)
      return;

//--- Устанавливаем параметры шрифта для заголовков данных бара и индикатора
   int  size=0;
   uint flags=0;
   uint angle=0;
   string name=panel.FontParams(size,flags,angle);
   panel.SetFontParams(name,9,FW_BOLD);
   panel.DrawText("Bar data ["+(string)index+"]",3,panel.TableY1(0)-16,clrMaroon,panel.Width()-6);
   panel.DrawText("Indicators data ["+(string)index+"]",3,panel.TableY1(1)-16,clrGreen,panel.Width()-6);
//--- Устанавливаем параметры шрифта для данных бара и индикатора
   panel.SetFontParams(name,9);

//--- Выводим на панель данные указанного бара в таблицу 0
   panel.DrawText("Date",  panel.CellX(0,0,0)+2, panel.CellY(0,0,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_DATE),     panel.CellX(0,0,1)+2, panel.CellY(0,0,1)+2,clrNONE,90);
   panel.DrawText("Time",  panel.CellX(0,1,0)+2, panel.CellY(0,1,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_MINUTES),  panel.CellX(0,1,1)+2, panel.CellY(0,1,1)+2,clrNONE,90);
   panel.DrawText("Open",  panel.CellX(0,2,0)+2, panel.CellY(0,2,0)+2); panel.DrawText(DoubleToString(rates[0].open,Digits()),      panel.CellX(0,2,1)+2, panel.CellY(0,2,1)+2,clrNONE,90);
   panel.DrawText("High",  panel.CellX(0,3,0)+2, panel.CellY(0,3,0)+2); panel.DrawText(DoubleToString(rates[0].high,Digits()),      panel.CellX(0,3,1)+2, panel.CellY(0,3,1)+2,clrNONE,90);
   panel.DrawText("Low",   panel.CellX(0,4,0)+2, panel.CellY(0,4,0)+2); panel.DrawText(DoubleToString(rates[0].low,Digits()),       panel.CellX(0,4,1)+2, panel.CellY(0,4,1)+2,clrNONE,90);
   panel.DrawText("Close", panel.CellX(0,5,0)+2, panel.CellY(0,5,0)+2); panel.DrawText(DoubleToString(rates[0].close,Digits()),     panel.CellX(0,5,1)+2, panel.CellY(0,5,1)+2,clrNONE,90);

//--- Выводим в таблицу 1 данные индикатора 1 с указанного бара в таблицу 1
   panel.DrawText(indicators.Title(handle_mom1), panel.CellX(1,0,0)+2, panel.CellY(1,0,0)+2);
   double value1=indicators.GetData(handle_mom1,0,0,index);
   string value_str1=(value1!=EMPTY_VALUE ? DoubleToString(value1,indicators.Digits(handle_mom1)) : " ");
   panel.DrawText(value_str1,panel.CellX(1,0,1)+2,panel.CellY(1,0,1)+2,clrNONE,190);
   
//--- Выводим описание состояния линии индикатора 1
   panel.DrawText("Line state", panel.CellX(1,1,0)+2, panel.CellY(1,1,0)+2);
   ENUM_LINE_STATE state1=indicators.BufferLineState(Symbol(),PERIOD_CURRENT,handle_mom1,0,0,index);
   panel.DrawText(BufferLineStateDescription(state1),panel.CellX(1,1,1)+2,panel.CellY(1,1,1)+2,clrNONE,190);
   
//--- Выводим в таблицу 2 данные индикатора 2 с указанного бара в таблицу 2
   panel.DrawText(indicators.Title(handle_mom2), panel.CellX(2,0,0)+2, panel.CellY(2,0,0)+2);
   double value2=indicators.GetDataTo(Symbol(),PERIOD_CURRENT,handle_mom2,0,0,index);
   string value_str2=(value2!=EMPTY_VALUE ? DoubleToString(value2,indicators.Digits(handle_mom2)) : " ");
   panel.DrawText(value_str2,panel.CellX(2,0,1)+2,panel.CellY(2,0,1)+2,clrNONE,190);
   
//--- Выводим описание состояния линии индикатора 2
   panel.DrawText("Line state", panel.CellX(2,1,0)+2, panel.CellY(2,1,0)+2);
   ENUM_LINE_STATE state2=indicators.BufferLineState(Symbol(),PERIOD_CURRENT,handle_mom2,0,0,index);
   panel.DrawText(BufferLineStateDescription(state2),panel.CellX(2,1,1)+2,panel.CellY(2,1,1)+2,clrNONE,190);
   
//--- Выводим описание соотношения линии индикатора 1 относительно линии индикатора 2
   double value21=indicators.GetDataTo(Symbol(),PERIOD_CURRENT,handle_mom2,0,0,index+1);
   ENUM_LINE_STATE stateR=indicators.BufferLineStateRelative(Symbol(),PERIOD_CURRENT,handle_mom1,0,0,index,value2,value21);
   string ma1=indicators.Name(handle_mom1);
   string ma2=indicators.Name(handle_mom2);
   string state_relative=
     (
      stateR==LINE_STATE_ABOVE      ? StringFormat("%s1 > %s2",ma1,ma2)   :
      stateR==LINE_STATE_BELOW      ? StringFormat("%s1 < %s2",ma1,ma2)           :
      stateR==LINE_STATE_CROSS_DOWN ? "Top-down crossing"   :
      stateR==LINE_STATE_CROSS_UP   ? "Bottom-up crossing"  :
      BufferLineStateDescription(stateR)
     );
   panel.DrawText(StringFormat("%s1 vs %s2",ma1,ma2), panel.CellX(2,2,0)+2, panel.CellY(2,2,0)+2);
   panel.DrawText(state_relative, panel.CellX(2,2,1)+2, panel.CellY(2,2,1)+2,clrNONE,190);
   
//--- Перерисовываем график для немедленного отображения всех изменений на панели
   ChartRedraw(ChartID());
  }
//+------------------------------------------------------------------+

После компиляции и запуска индикатора на графике М1 и выбранном периоде графика для расчёта М5, видим:



Индикатор Money Flow Index:

//+------------------------------------------------------------------+
//|                                       TestMSTFMoneyFlowIndex.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_separate_window
#property indicator_buffers 2
#property indicator_plots   2
//--- enums

//--- plot MFI1
#property indicator_label1  "MFI1"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrDodgerBlue
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1

//--- plot MFI2
#property indicator_label2  "MFI2"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrRed
#property indicator_style2  STYLE_SOLID
#property indicator_width2  1

//--- includes
#include <IndMSTF\IndMSTF.mqh>
#include <Dashboard\Dashboard.mqh>
//--- input parameters
input string               InpSymbol      =  NULL;             /* Symbol                     */ // Символ
input ENUM_TIMEFRAMES      InpTimeframe   =  PERIOD_CURRENT;   /* Timeframe                  */ // Таймфрейм
input int                  InpPeriod      =  14;               /* Period                     */ // Период расчёта
input ENUM_APPLIED_VOLUME  InpVolume      =  VOLUME_TICK;      /* Applied Volume             */ // Используемый объем для расчёта
input uchar                InpLineWidth1  =  2;                /* Senior period Line Width   */ // Толщина линии для старшего периода
input uchar                InpLineWidth2  =  1;                /* Junior period Line Width   */ // Толщина линии для младшего периода
input bool                 InpAsSeries    =  true;             /* As Series flag             */ // Флаг серийности массивов буферов индикатора

//--- indicator buffers
double         BufferMFI1[];
double         BufferMFI2[];
//--- global variables
int handle_mfi1;
int handle_mfi2;
CMSTFIndicators indicators;      // Экземпляр объекта коллекции индикаторов
//--- переменные для панели
CDashboard *panel=NULL;          // Указатель на объект панели
int         mouse_bar_index;     // Индекс бара, с которого берутся данные
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Устанавливаем таймер с периодичностью в 1 секунду
   EventSetTimer(1);
//--- Назначаем рисуемым буферам 0 и 1 массивы BufferMA1 и BufferMA2 соответственно
   SetIndexBuffer(0,BufferMFI1,INDICATOR_DATA);
   SetIndexBuffer(1,BufferMFI2,INDICATOR_DATA);
//--- Устанавливаем ширину линий
   int w1=0,w2=0;
   if(InpTimeframe>Period())
     {
      w1=InpLineWidth2;
      w2=InpLineWidth1;
     }
   else
     {
      w1=InpLineWidth1;
      w2=InpLineWidth2;
     }
   PlotIndexSetInteger(0,PLOT_LINE_WIDTH,w1);
   PlotIndexSetInteger(1,PLOT_LINE_WIDTH,w2);
//--- sets indicator shift

//--- Устанавливаем флаги серийности массивам буферов индикатора (для теста, чтобы видно было отсутствие разницы)
   ArraySetAsSeries(BufferMFI1,InpAsSeries);
   ArraySetAsSeries(BufferMFI2,InpAsSeries);
   
//--- Создаём два индикатора одного типа
//--- Первый рассчитывается на текущих символе/периоде графика, а второй - на тех, что заданы в настройках
   handle_mfi1=indicators.AddNewMFI(NULL,PERIOD_CURRENT,InpPeriod,InpVolume);
   handle_mfi2=indicators.AddNewMFI(InpSymbol,InpTimeframe,InpPeriod,InpVolume);

//--- Если не удалось создать хэндлы индикаторов - возвращаем ошибку инициализации
   if(handle_mfi1==INVALID_HANDLE || handle_mfi2==INVALID_HANDLE)
      return INIT_FAILED;
//--- Устанавливаем описания линий индикатора из описаний буферов расчётной части созданных индикаторов
   indicators.SetPlotLabelFromBuffer(0,handle_mfi1,0);
   indicators.SetPlotLabelFromBuffer(1,handle_mfi2,0);
      
//--- Панель
//--- Создаём панель
   int width=237;
   panel=new CDashboard(1,20,20,width,264,0);
   if(panel==NULL)
     {
      Print("Error. Failed to create panel object");
      return INIT_FAILED;
     }
//--- Устанавливаем параметры шрифта
   panel.SetFontParams("Calibri",9);
//--- Отображаем панель с текстом в заголовке "Символ, Описание таймфрейма"
   panel.View(Symbol()+", "+StringSubstr(EnumToString(Period()),7));

//--- Создаём таблицу с идентификатором 0 для отображения в ней данных бара
   panel.CreateNewTable(0);
//--- Рисуем таблицу с идентификатором 0 на фоне панели
   panel.DrawGrid(0,2,20,6,2,18,width/2-2);

//--- Создаём таблицу с идентификатором 1 для отображения в ней данных индикатора 1
   panel.CreateNewTable(1);
//--- Получаем координату Y2 таблицы с идентификатором 0 и
//--- устанавливаем координату Y1 для таблицы с идентификатором 1
   int y1=panel.TableY2(0)+22;
//--- Рисуем таблицу с идентификатором 1 на фоне панели
   panel.DrawGrid(1,2,y1,2,2,18,width/2-2);

//--- Создаём таблицу с идентификатором 2 для отображения в ней данных индикатора 2
   panel.CreateNewTable(2);
//--- Получаем координату Y2 таблицы с идентификатором 1 и
//--- устанавливаем координату Y1 для таблицы с идентификатором 2
   int y2=panel.TableY2(1)+3;
//--- Рисуем таблицу с идентификатором 2 на фоне панели
   panel.DrawGrid(2,2,y2,3,2,18,width/2-2);
   
//--- Инициализируем переменную с индексом бара указателя мышки
   mouse_bar_index=0;
//--- Выводим на панель данные текущего бара
   DrawData(mouse_bar_index,TimeCurrent());

//--- Успешная инициализация
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- Уничтожаем таймер
   EventKillTimer();
//--- Если объект панели существует - удаляем его
   if(panel!=NULL)
      delete panel;
//--- Стираем все комментарии
   Comment("");
  }
//+------------------------------------------------------------------+
//| 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[])
  {
//--- Количество баров для расчёта
   int limit=rates_total-prev_calculated;
//--- Если limit > 1, значит это первый расчёт, либо изменение в истории
   if(limit>1)
     {
      //--- указываем для просчёта всю доступную историю
      limit=rates_total-1;
      /*
      // Если в индикаторе есть какие-либо буферы, в которых отображены другие расчёты (не мульти- индикаторы),
      // то здесь их нужно инициализировать "пустым" значением, установленным для этих буферов
      */
     }
//--- Рассчитываем все созданные мультисимвольные мультипериодные индикаторы
   if(!indicators.Calculate())
      return 0;

//--- Выводим на панель данные бара под курсором (либо текущий бар, если курсор за пределами графика)
   DrawData(mouse_bar_index,time[mouse_bar_index]);

//--- Из буферов рассчитанных индикаторов выводим данные в индикаторные буферы
   if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_mfi1,0,0,limit,BufferMFI1))
      return 0;
   if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_mfi2,0,0,limit,BufferMFI2))
      return 0;

//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| Timer function                                                   | 
//+------------------------------------------------------------------+
void OnTimer()
  {
//--- Вызываем таймер коллекции индикаторов
   indicators.OnTimer();
  }
//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- Работа с панелью
//--- Вызываем обработчик событий панели
   panel.OnChartEvent(id,lparam,dparam,sparam);

//--- Если курсор перемещается или щелчок по графику
   if(id==CHARTEVENT_MOUSE_MOVE || id==CHARTEVENT_CLICK)
     {
      //--- Объявляем переменные для записи в них координат времени и цены
      datetime time=0;
      double price=0;
      int wnd=0;
      //--- Если координаты курсора преобразованы в дату и время
      if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price))
        {
         //--- записываем индекс бара, где расположен курсор в глобальную переменную
         mouse_bar_index=iBarShift(Symbol(),PERIOD_CURRENT,time);
         //--- Выводим данные бара под курсором на панель
         DrawData(mouse_bar_index,time);
        }
     }

//--- Если получили пользовательское событие - выводим об этом сообщение в журнал
   if(id>CHARTEVENT_CUSTOM)
     {
      //--- Здесь может быть обработка щелчка по кнопке закрытия на панели
      PrintFormat("%s: Event id=%ld, object id (lparam): %lu, event message (sparam): %s",__FUNCTION__,id,lparam,sparam);
     }
  }
//+------------------------------------------------------------------+
//| Выводит данные с указанного индекса таймсерии на панель          |
//+------------------------------------------------------------------+
void DrawData(const int index,const datetime time)
  {
//--- Объявляем переменные для получения в них данных
   MqlRates rates[1];

//--- Если данные бара по указанному индексу получить не удалось - уходим
   if(CopyRates(Symbol(),PERIOD_CURRENT,index,1,rates)!=1)
      return;

//--- Устанавливаем параметры шрифта для заголовков данных бара и индикатора
   int  size=0;
   uint flags=0;
   uint angle=0;
   string name=panel.FontParams(size,flags,angle);
   panel.SetFontParams(name,9,FW_BOLD);
   panel.DrawText("Bar data ["+(string)index+"]",3,panel.TableY1(0)-16,clrMaroon,panel.Width()-6);
   panel.DrawText("Indicators data ["+(string)index+"]",3,panel.TableY1(1)-16,clrGreen,panel.Width()-6);
//--- Устанавливаем параметры шрифта для данных бара и индикатора
   panel.SetFontParams(name,9);

//--- Выводим на панель данные указанного бара в таблицу 0
   panel.DrawText("Date",  panel.CellX(0,0,0)+2, panel.CellY(0,0,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_DATE),     panel.CellX(0,0,1)+2, panel.CellY(0,0,1)+2,clrNONE,90);
   panel.DrawText("Time",  panel.CellX(0,1,0)+2, panel.CellY(0,1,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_MINUTES),  panel.CellX(0,1,1)+2, panel.CellY(0,1,1)+2,clrNONE,90);
   panel.DrawText("Open",  panel.CellX(0,2,0)+2, panel.CellY(0,2,0)+2); panel.DrawText(DoubleToString(rates[0].open,Digits()),      panel.CellX(0,2,1)+2, panel.CellY(0,2,1)+2,clrNONE,90);
   panel.DrawText("High",  panel.CellX(0,3,0)+2, panel.CellY(0,3,0)+2); panel.DrawText(DoubleToString(rates[0].high,Digits()),      panel.CellX(0,3,1)+2, panel.CellY(0,3,1)+2,clrNONE,90);
   panel.DrawText("Low",   panel.CellX(0,4,0)+2, panel.CellY(0,4,0)+2); panel.DrawText(DoubleToString(rates[0].low,Digits()),       panel.CellX(0,4,1)+2, panel.CellY(0,4,1)+2,clrNONE,90);
   panel.DrawText("Close", panel.CellX(0,5,0)+2, panel.CellY(0,5,0)+2); panel.DrawText(DoubleToString(rates[0].close,Digits()),     panel.CellX(0,5,1)+2, panel.CellY(0,5,1)+2,clrNONE,90);

//--- Выводим в таблицу 1 данные индикатора 1 с указанного бара в таблицу 1
   panel.DrawText(indicators.Title(handle_mfi1), panel.CellX(1,0,0)+2, panel.CellY(1,0,0)+2);
   double value1=indicators.GetData(handle_mfi1,0,0,index);
   string value_str1=(value1!=EMPTY_VALUE ? DoubleToString(value1,indicators.Digits(handle_mfi1)) : " ");
   panel.DrawText(value_str1,panel.CellX(1,0,1)+2,panel.CellY(1,0,1)+2,clrNONE,110);
   
//--- Выводим описание состояния линии индикатора 1
   panel.DrawText("Line state", panel.CellX(1,1,0)+2, panel.CellY(1,1,0)+2);
   ENUM_LINE_STATE state1=indicators.BufferLineState(Symbol(),PERIOD_CURRENT,handle_mfi1,0,0,index);
   panel.DrawText(BufferLineStateDescription(state1),panel.CellX(1,1,1)+2,panel.CellY(1,1,1)+2,clrNONE,110);
   
//--- Выводим в таблицу 2 данные индикатора 2 с указанного бара в таблицу 2
   panel.DrawText(indicators.Title(handle_mfi2), panel.CellX(2,0,0)+2, panel.CellY(2,0,0)+2);
   double value2=indicators.GetDataTo(Symbol(),PERIOD_CURRENT,handle_mfi2,0,0,index);
   string value_str2=(value2!=EMPTY_VALUE ? DoubleToString(value2,indicators.Digits(handle_mfi2)) : " ");
   panel.DrawText(value_str2,panel.CellX(2,0,1)+2,panel.CellY(2,0,1)+2,clrNONE,110);
   
//--- Выводим описание состояния линии индикатора 2
   panel.DrawText("Line state", panel.CellX(2,1,0)+2, panel.CellY(2,1,0)+2);
   ENUM_LINE_STATE state2=indicators.BufferLineState(Symbol(),PERIOD_CURRENT,handle_mfi2,0,0,index);
   panel.DrawText(BufferLineStateDescription(state2),panel.CellX(2,1,1)+2,panel.CellY(2,1,1)+2,clrNONE,110);
   
//--- Выводим описание соотношения линии индикатора 1 относительно линии индикатора 2
   double value21=indicators.GetDataTo(Symbol(),PERIOD_CURRENT,handle_mfi2,0,0,index+1);
   ENUM_LINE_STATE stateR=indicators.BufferLineStateRelative(Symbol(),PERIOD_CURRENT,handle_mfi1,0,0,index,value2,value21);
   string ma1=indicators.Name(handle_mfi1);
   string ma2=indicators.Name(handle_mfi2);
   string state_relative=
     (
      stateR==LINE_STATE_ABOVE      ? StringFormat("%s1 > %s2",ma1,ma2)   :
      stateR==LINE_STATE_BELOW      ? StringFormat("%s1 < %s2",ma1,ma2)           :
      stateR==LINE_STATE_CROSS_DOWN ? "Top-down crossing"   :
      stateR==LINE_STATE_CROSS_UP   ? "Bottom-up crossing"  :
      BufferLineStateDescription(stateR)
     );
   panel.DrawText(StringFormat("%s1 vs %s2",ma1,ma2), panel.CellX(2,2,0)+2, panel.CellY(2,2,0)+2);
   panel.DrawText(state_relative, panel.CellX(2,2,1)+2, panel.CellY(2,2,1)+2,clrNONE,110);
   
//--- Перерисовываем график для немедленного отображения всех изменений на панели
   ChartRedraw(ChartID());
  }
//+------------------------------------------------------------------+

После компиляции и запуска индикатора на графике М1 и выбранном периоде графика для расчёта М5, видим:



Индикаторы Moving Averages:

//+------------------------------------------------------------------+
//|                                       TestMSTFMovingAverages.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_chart_window
#property indicator_buffers 2
#property indicator_plots   2
//--- enums
enum ENUM_USED_MA
  {
   USED_MA_AMA    =  IND_AMA,    // Adaptive Moving Average
   USED_MA_DEMA   =  IND_DEMA,   // Double Exponential Moving Average
   USED_MA_FRAMA  =  IND_FRAMA,  // Fractal Adaptive Moving Average
   USED_MA_MA     =  IND_MA,     // Moving Average
   USED_MA_TEMA   =  IND_TEMA,   // Triple Exponential Moving Average
   USED_MA_VIDYA  =  IND_VIDYA,  // Variable Index Dynamic Average
  };
//--- plot MA1
#property indicator_label1  "MA1"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrDodgerBlue
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1

//--- plot MA2
#property indicator_label2  "MA2"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrRed
#property indicator_style2  STYLE_SOLID
#property indicator_width2  1

//--- includes
#include <IndMSTF\IndMSTF.mqh>
#include <Dashboard\Dashboard.mqh>
//--- input parameters
input ENUM_USED_MA         InpIndicator   =  USED_MA_MA;       /* Used MA        */ // Используемый тип скользящей средней
input string               InpSymbol      =  NULL;             /* Symbol         */ // Символ скользящей средней
input ENUM_TIMEFRAMES      InpTimeframe   =  PERIOD_CURRENT;   /* Timeframe      */ // Таймфрейм скользящей средней
input ENUM_APPLIED_PRICE   InpPrice       =  PRICE_CLOSE;      /* Applied Price  */ // Используемая цена для расчёта
input ENUM_MA_METHOD       InpMethod      =  MODE_SMA;         /* MA Method      */ // Метод расчёта Moving Average
input int                  InpShift       =  0;                /* MA Shift       */ // Сдвиг скользящей средней
input bool                 InpAsSeries    =  true;             /* As Series flag */ // Флаг серийности массивов буферов индикатора

//--- indicator buffers
double         BufferMA1[];
double         BufferMA2[];
//--- global variables
int handle_ma1;
int handle_ma2;
CMSTFIndicators indicators;      // Экземпляр объекта коллекции индикаторов
//--- переменные для панели
CDashboard *panel=NULL;          // Указатель на объект панели
int         mouse_bar_index;     // Индекс бара, с которого берутся данные
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Устанавливаем таймер с периодичностью в 1 секунду
   EventSetTimer(1);
//--- Назначаем рисуемым буферам 0 и 1 массивы BufferMA1 и BufferMA2 соответственно
   SetIndexBuffer(0,BufferMA1,INDICATOR_DATA);
   SetIndexBuffer(1,BufferMA2,INDICATOR_DATA);
//--- sets indicator shift
   //PlotIndexSetInteger(0,PLOT_SHIFT,InpShift);   // аналог в стр. 116
   //PlotIndexSetInteger(1,PLOT_SHIFT,InpShift);   // аналог в стр. 117
//--- Устанавливаем флаги серийности массивам буферов индикатора (для теста, чтобы видно было отсутствие разницы)
   ArraySetAsSeries(BufferMA1,InpAsSeries);
   ArraySetAsSeries(BufferMA2,InpAsSeries);
   
//--- Для разных индикаторов ширина панели (width) будет индивидуальной (из-за количества параметров в описании)
   int width=0;
//--- В зависимости от выбранного в настройках индикатора создаём два индикатора одного типа
//--- Первый рассчитывается на текущих символе/периоде графика, а второй - на тех, что заданы в настройках
   switch(InpIndicator)
     {
      case USED_MA_AMA     :
         handle_ma1=indicators.AddNewAMA(NULL,PERIOD_CURRENT,9,2,30,InpShift);
         handle_ma2=indicators.AddNewAMA(InpSymbol,InpTimeframe,9,2,30,InpShift);
         width=269;
        break;
      case USED_MA_DEMA    :
         handle_ma1=indicators.AddNewDEMA(NULL,PERIOD_CURRENT,14,InpShift,InpPrice);
         handle_ma2=indicators.AddNewDEMA(InpSymbol,InpTimeframe,14,InpShift,InpPrice);
         width=255;
        break;
      case USED_MA_FRAMA   :
         handle_ma1=indicators.AddNewFrAMA(NULL,PERIOD_CURRENT,14,InpShift,InpPrice);
         handle_ma2=indicators.AddNewFrAMA(InpSymbol,InpTimeframe,14,InpShift,InpPrice);
         width=259;
        break;
      case USED_MA_TEMA    :
         handle_ma1=indicators.AddNewTEMA(NULL,PERIOD_CURRENT,14,InpShift,InpPrice);
         handle_ma2=indicators.AddNewTEMA(InpSymbol,InpTimeframe,14,InpShift,InpPrice);
         width=253;
        break;
      case USED_MA_VIDYA   :
         handle_ma1=indicators.AddNewVIDyA(NULL,PERIOD_CURRENT,9,12,InpShift,InpPrice);
         handle_ma2=indicators.AddNewVIDyA(InpSymbol,InpTimeframe,9,12,InpShift,InpPrice);
         width=267;
        break;
      default:
         handle_ma1=indicators.AddNewMA(NULL,PERIOD_CURRENT,10,InpShift,InpMethod,InpPrice);
         handle_ma2=indicators.AddNewMA(InpSymbol,InpTimeframe,10,InpShift,InpMethod,InpPrice);
         width=231;
        break;
     }
//--- Если не удалось создать хэндлы индикаторов - возвращаем ошибку инициализации
   if(handle_ma1==INVALID_HANDLE || handle_ma2==INVALID_HANDLE)
      return INIT_FAILED;
//--- Устанавливаем описания линий индикатора из описаний буферов расчётной части созданных индикаторов
   indicators.SetPlotLabelFromBuffer(0,handle_ma1,0);
   indicators.SetPlotLabelFromBuffer(1,handle_ma2,0);
//--- Устанавливаем смещения линиям индикатора
   indicators.SetPlotShift(0,InpShift);
   indicators.SetPlotShift(1,InpShift);
      
//--- Панель
//--- Создаём панель
   panel=new CDashboard(1,20,20,width,264);
   if(panel==NULL)
     {
      Print("Error. Failed to create panel object");
      return INIT_FAILED;
     }
//--- Устанавливаем параметры шрифта
   panel.SetFontParams("Calibri",9);
//--- Отображаем панель с текстом в заголовке "Символ, Описание таймфрейма"
   panel.View(Symbol()+", "+StringSubstr(EnumToString(Period()),7));

//--- Создаём таблицу с идентификатором 0 для отображения в ней данных бара
   panel.CreateNewTable(0);
//--- Рисуем таблицу с идентификатором 0 на фоне панели
   panel.DrawGrid(0,2,20,6,2,18,width/2-2);

//--- Создаём таблицу с идентификатором 1 для отображения в ней данных индикатора 1
   panel.CreateNewTable(1);
//--- Получаем координату Y2 таблицы с идентификатором 0 и
//--- устанавливаем координату Y1 для таблицы с идентификатором 1
   int y1=panel.TableY2(0)+22;
//--- Рисуем таблицу с идентификатором 1 на фоне панели
   panel.DrawGrid(1,2,y1,2,2,18,width/2-2);

//--- Создаём таблицу с идентификатором 2 для отображения в ней данных индикатора 2
   panel.CreateNewTable(2);
//--- Получаем координату Y2 таблицы с идентификатором 1 и
//--- устанавливаем координату Y1 для таблицы с идентификатором 2
   int y2=panel.TableY2(1)+3;
//--- Рисуем таблицу с идентификатором 2 на фоне панели
   panel.DrawGrid(2,2,y2,3,2,18,width/2-2);
   
//--- Инициализируем переменную с индексом бара указателя мышки
   mouse_bar_index=0;
//--- Выводим на панель данные текущего бара
   DrawData(mouse_bar_index,TimeCurrent());

//--- Успешная инициализация
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- Уничтожаем таймер
   EventKillTimer();
//--- Если объект панели существует - удаляем его
   if(panel!=NULL)
      delete panel;
//--- Стираем все комментарии
   Comment("");
  }
//+------------------------------------------------------------------+
//| 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[])
  {
//--- Количество баров для расчёта
   int limit=rates_total-prev_calculated;
//--- Если limit > 1, значит это первый расчёт, либо изменение в истории
   if(limit>1)
     {
      //--- указываем для просчёта всю доступную историю
      limit=rates_total-1;
      /*
      // Если в индикаторе есть какие-либо буферы, в которых отображены другие расчёты (не мульти- индикаторы),
      // то здесь их нужно инициализировать "пустым" значением, установленным для этих буферов
      */
     }
//--- Рассчитываем все созданные мультисимвольные мультипериодные индикаторы
   if(!indicators.Calculate())
      return 0;

//--- Выводим на панель данные бара под курсором (либо текущий бар, если курсор за пределами графика)
   if(mouse_bar_index>WRONG_VALUE && mouse_bar_index<rates_total)
      DrawData(mouse_bar_index,time[mouse_bar_index]);

//--- Из буферов рассчитанных индикаторов выводим данные в индикаторные буферы
   if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_ma1,0,0,limit,BufferMA1))
      return 0;
   if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_ma2,0,0,limit,BufferMA2))
      return 0;

//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| Timer function                                                   | 
//+------------------------------------------------------------------+
void OnTimer()
  {
//--- Вызываем таймер коллекции индикаторов
   indicators.OnTimer();
  }
//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- Работа с панелью
//--- Вызываем обработчик событий панели
   panel.OnChartEvent(id,lparam,dparam,sparam);

//--- Если курсор перемещается или щелчок по графику
   if(id==CHARTEVENT_MOUSE_MOVE || id==CHARTEVENT_CLICK)
     {
      //--- Объявляем переменные для записи в них координат времени и цены
      datetime time=0;
      double price=0;
      int wnd=0;
      //--- Если координаты курсора преобразованы в дату и время
      if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price))
        {
         //--- записываем индекс бара, где расположен курсор в глобальную переменную
         mouse_bar_index=iBarShift(Symbol(),PERIOD_CURRENT,time);
         //--- Выводим данные бара под курсором на панель
         DrawData(mouse_bar_index,time);
        }
     }

//--- Если получили пользовательское событие - выводим об этом сообщение в журнал
   if(id>CHARTEVENT_CUSTOM)
     {
      //--- Здесь может быть обработка щелчка по кнопке закрытия на панели
      PrintFormat("%s: Event id=%ld, object id (lparam): %lu, event message (sparam): %s",__FUNCTION__,id,lparam,sparam);
     }
  }
//+------------------------------------------------------------------+
//| Выводит данные с указанного индекса таймсерии на панель          |
//+------------------------------------------------------------------+
void DrawData(const int index,const datetime time)
  {
//--- Объявляем переменные для получения в них данных
   MqlRates rates[1];

//--- Если данные бара по указанному индексу получить не удалось - уходим
   if(CopyRates(Symbol(),PERIOD_CURRENT,index,1,rates)!=1)
      return;

//--- Устанавливаем параметры шрифта для заголовков данных бара и индикатора
   int  size=0;
   uint flags=0;
   uint angle=0;
   string name=panel.FontParams(size,flags,angle);
   panel.SetFontParams(name,9,FW_BOLD);
   panel.DrawText("Bar data ["+(string)index+"]",3,panel.TableY1(0)-16,clrMaroon,panel.Width()-6);
   panel.DrawText("Indicators data ["+(string)index+"]",3,panel.TableY1(1)-16,clrGreen,panel.Width()-6);
//--- Устанавливаем параметры шрифта для данных бара и индикатора
   panel.SetFontParams(name,9);

//--- Выводим на панель данные указанного бара в таблицу 0
   panel.DrawText("Date",  panel.CellX(0,0,0)+2, panel.CellY(0,0,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_DATE),     panel.CellX(0,0,1)+2, panel.CellY(0,0,1)+2,clrNONE,90);
   panel.DrawText("Time",  panel.CellX(0,1,0)+2, panel.CellY(0,1,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_MINUTES),  panel.CellX(0,1,1)+2, panel.CellY(0,1,1)+2,clrNONE,90);
   panel.DrawText("Open",  panel.CellX(0,2,0)+2, panel.CellY(0,2,0)+2); panel.DrawText(DoubleToString(rates[0].open,Digits()),      panel.CellX(0,2,1)+2, panel.CellY(0,2,1)+2,clrNONE,90);
   panel.DrawText("High",  panel.CellX(0,3,0)+2, panel.CellY(0,3,0)+2); panel.DrawText(DoubleToString(rates[0].high,Digits()),      panel.CellX(0,3,1)+2, panel.CellY(0,3,1)+2,clrNONE,90);
   panel.DrawText("Low",   panel.CellX(0,4,0)+2, panel.CellY(0,4,0)+2); panel.DrawText(DoubleToString(rates[0].low,Digits()),       panel.CellX(0,4,1)+2, panel.CellY(0,4,1)+2,clrNONE,90);
   panel.DrawText("Close", panel.CellX(0,5,0)+2, panel.CellY(0,5,0)+2); panel.DrawText(DoubleToString(rates[0].close,Digits()),     panel.CellX(0,5,1)+2, panel.CellY(0,5,1)+2,clrNONE,90);

//--- Выводим в таблицу 1 данные индикатора 1 с указанного бара в таблицу 1
   panel.DrawText(indicators.Title(handle_ma1), panel.CellX(1,0,0)+2, panel.CellY(1,0,0)+2);
   double value1=indicators.GetData(handle_ma1,0,0,index);
   string value_str1=(value1!=EMPTY_VALUE ? DoubleToString(value1,indicators.Digits(handle_ma1)) : " ");
   panel.DrawText(value_str1,panel.CellX(1,0,1)+2,panel.CellY(1,0,1)+2,clrNONE,110);
   
//--- Выводим описание состояния линии индикатора 1
   panel.DrawText("Line state", panel.CellX(1,1,0)+2, panel.CellY(1,1,0)+2);
   ENUM_LINE_STATE state1=indicators.BufferLineState(Symbol(),PERIOD_CURRENT,handle_ma1,0,0,index);
   panel.DrawText(BufferLineStateDescription(state1),panel.CellX(1,1,1)+2,panel.CellY(1,1,1)+2,clrNONE,110);
   
//--- Выводим в таблицу 2 данные индикатора 2 с указанного бара в таблицу 2
   panel.DrawText(indicators.Title(handle_ma2), panel.CellX(2,0,0)+2, panel.CellY(2,0,0)+2);
   double value2=indicators.GetDataTo(Symbol(),PERIOD_CURRENT,handle_ma2,0,0,index);
   string value_str2=(value2!=EMPTY_VALUE ? DoubleToString(value2,indicators.Digits(handle_ma2)) : " ");
   panel.DrawText(value_str2,panel.CellX(2,0,1)+2,panel.CellY(2,0,1)+2,clrNONE,110);
   
//--- Выводим описание состояния линии индикатора 2
   panel.DrawText("Line state", panel.CellX(2,1,0)+2, panel.CellY(2,1,0)+2);
   ENUM_LINE_STATE state2=indicators.BufferLineState(Symbol(),PERIOD_CURRENT,handle_ma2,0,0,index);
   panel.DrawText(BufferLineStateDescription(state2),panel.CellX(2,1,1)+2,panel.CellY(2,1,1)+2,clrNONE,110);
   
//--- Выводим описание соотношения линии индикатора 1 относительно линии индикатора 2
   double value21=indicators.GetDataTo(Symbol(),PERIOD_CURRENT,handle_ma2,0,0,index+1);
   ENUM_LINE_STATE stateR=indicators.BufferLineStateRelative(Symbol(),PERIOD_CURRENT,handle_ma1,0,0,index,value2,value21);
   string ma1=indicators.Name(handle_ma1);
   string ma2=indicators.Name(handle_ma2);
   string state_relative=
     (
      stateR==LINE_STATE_ABOVE      ? StringFormat("%s1 > %s2",ma1,ma2)   :
      stateR==LINE_STATE_BELOW      ? StringFormat("%s1 < %s2",ma1,ma2)           :
      stateR==LINE_STATE_CROSS_DOWN ? "Top-down crossing"   :
      stateR==LINE_STATE_CROSS_UP   ? "Bottom-up crossing"  :
      BufferLineStateDescription(stateR)
     );
   panel.DrawText(StringFormat("%s1 vs %s2",ma1,ma2), panel.CellX(2,2,0)+2, panel.CellY(2,2,0)+2);
   panel.DrawText(state_relative, panel.CellX(2,2,1)+2, panel.CellY(2,2,1)+2,clrNONE,110);
   
//--- Перерисовываем график для немедленного отображения всех изменений на панели
   ChartRedraw(ChartID());
  }
//+------------------------------------------------------------------+

После компиляции и запуска индикатора на графике М1 и выбранном периоде графика для расчёта М5, видим:



Индикатор On Balance Volume:

//+------------------------------------------------------------------+
//|                                      TestMSTFOnBalanceVolume.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_separate_window
#property indicator_buffers 1
#property indicator_plots   1
//--- enums

//--- plot OBV1
#property indicator_label1  "OBV1"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrDodgerBlue
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1

//--- includes
#include <IndMSTF\IndMSTF.mqh>
#include <Dashboard\Dashboard.mqh>
//--- input parameters
input string               InpSymbol      =  NULL;             /* Symbol                     */ // Символ
input ENUM_TIMEFRAMES      InpTimeframe   =  PERIOD_CURRENT;   /* Timeframe                  */ // Таймфрейм
input ENUM_APPLIED_VOLUME  InpVolume      =  VOLUME_TICK;      /* Applied Volume             */ // Используемый объем для расчёта
input uchar                InpLineWidth1  =  2;                /* Senior period Line Width   */ // Толщина линии для старшего периода
input uchar                InpLineWidth2  =  1;                /* Junior period Line Width   */ // Толщина линии для младшего периода
input bool                 InpAsSeries    =  true;             /* As Series flag             */ // Флаг серийности массивов буферов индикатора

//--- indicator buffers
double         BufferOBV[];
//--- global variables
int handle_obv;
CMSTFIndicators indicators;      // Экземпляр объекта коллекции индикаторов
//--- переменные для панели
CDashboard *panel=NULL;          // Указатель на объект панели
int         mouse_bar_index;     // Индекс бара, с которого берутся данные
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Устанавливаем таймер с периодичностью в 1 секунду
   EventSetTimer(1);
//--- Назначаем рисуемым буферам 0 и 1 массивы BufferMA1 и BufferMA2 соответственно
   SetIndexBuffer(0,BufferOBV,INDICATOR_DATA);
//--- Устанавливаем ширину линий
   int w1=0,w2=0;
   if(InpTimeframe>Period())
     {
      w1=InpLineWidth2;
      w2=InpLineWidth1;
     }
   else
     {
      w1=InpLineWidth1;
      w2=InpLineWidth2;
     }
   PlotIndexSetInteger(0,PLOT_LINE_WIDTH,w1);
   PlotIndexSetInteger(1,PLOT_LINE_WIDTH,w2);
//--- sets indicator shift

//--- Устанавливаем флаги серийности массивам буферов индикатора (для теста, чтобы видно было отсутствие разницы)
   ArraySetAsSeries(BufferOBV,InpAsSeries);
   
//--- Создаём два индикатора одного типа
//--- Первый рассчитывается на текущих символе/периоде графика, а второй - на тех, что заданы в настройках
   handle_obv=indicators.AddNewOBV(InpSymbol,InpTimeframe,InpVolume);

//--- Если не удалось создать хэндлы индикаторов - возвращаем ошибку инициализации
   if(handle_obv==INVALID_HANDLE)
      return INIT_FAILED;
//--- Устанавливаем описания линий индикатора из описаний буферов расчётной части созданных индикаторов
   indicators.SetPlotLabelFromBuffer(0,handle_obv,0);
      
//--- Панель
//--- Создаём панель
   int width=237;
   panel=new CDashboard(1,20,20,width,264,0);
   if(panel==NULL)
     {
      Print("Error. Failed to create panel object");
      return INIT_FAILED;
     }
//--- Устанавливаем параметры шрифта
   panel.SetFontParams("Calibri",9);
//--- Отображаем панель с текстом в заголовке "Символ, Описание таймфрейма"
   panel.View(Symbol()+", "+StringSubstr(EnumToString(Period()),7));

//--- Создаём таблицу с идентификатором 0 для отображения в ней данных бара
   panel.CreateNewTable(0);
//--- Рисуем таблицу с идентификатором 0 на фоне панели
   panel.DrawGrid(0,2,20,6,2,18,width/2-2);

//--- Создаём таблицу с идентификатором 1 для отображения в ней данных индикатора 1
   panel.CreateNewTable(1);
//--- Получаем координату Y2 таблицы с идентификатором 0 и
//--- устанавливаем координату Y1 для таблицы с идентификатором 1
   int y1=panel.TableY2(0)+22;
//--- Рисуем таблицу с идентификатором 1 на фоне панели
   panel.DrawGrid(1,2,y1,2,2,18,width/2-2);

//--- Создаём таблицу с идентификатором 2 для отображения в ней данных индикатора 2
   panel.CreateNewTable(2);
//--- Получаем координату Y2 таблицы с идентификатором 1 и
//--- устанавливаем координату Y1 для таблицы с идентификатором 2
   int y2=panel.TableY2(1)+3;
//--- Рисуем таблицу с идентификатором 2 на фоне панели
   panel.DrawGrid(2,2,y2,3,2,18,width/2-2);
   
//--- Инициализируем переменную с индексом бара указателя мышки
   mouse_bar_index=0;
//--- Выводим на панель данные текущего бара
   DrawData(mouse_bar_index,TimeCurrent());

//--- Успешная инициализация
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- Уничтожаем таймер
   EventKillTimer();
//--- Если объект панели существует - удаляем его
   if(panel!=NULL)
      delete panel;
//--- Стираем все комментарии
   Comment("");
  }
//+------------------------------------------------------------------+
//| 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[])
  {
//--- Количество баров для расчёта
   int limit=rates_total-prev_calculated;
//--- Если limit > 1, значит это первый расчёт, либо изменение в истории
   if(limit>1)
     {
      //--- указываем для просчёта всю доступную историю
      limit=rates_total-1;
      /*
      // Если в индикаторе есть какие-либо буферы, в которых отображены другие расчёты (не мульти- индикаторы),
      // то здесь их нужно инициализировать "пустым" значением, установленным для этих буферов
      */
     }
//--- Рассчитываем все созданные мультисимвольные мультипериодные индикаторы
   if(!indicators.Calculate())
      return 0;

//--- Выводим на панель данные бара под курсором (либо текущий бар, если курсор за пределами графика)
   DrawData(mouse_bar_index,time[mouse_bar_index]);

//--- Из буферов рассчитанных индикаторов выводим данные в индикаторные буферы
   if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_obv,0,0,limit,BufferOBV))
      return 0;

//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| Timer function                                                   | 
//+------------------------------------------------------------------+
void OnTimer()
  {
//--- Вызываем таймер коллекции индикаторов
   indicators.OnTimer();
  }
//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- Работа с панелью
//--- Вызываем обработчик событий панели
   panel.OnChartEvent(id,lparam,dparam,sparam);

//--- Если курсор перемещается или щелчок по графику
   if(id==CHARTEVENT_MOUSE_MOVE || id==CHARTEVENT_CLICK)
     {
      //--- Объявляем переменные для записи в них координат времени и цены
      datetime time=0;
      double price=0;
      int wnd=0;
      //--- Если координаты курсора преобразованы в дату и время
      if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price))
        {
         //--- записываем индекс бара, где расположен курсор в глобальную переменную
         mouse_bar_index=iBarShift(Symbol(),PERIOD_CURRENT,time);
         //--- Выводим данные бара под курсором на панель
         DrawData(mouse_bar_index,time);
        }
     }

//--- Если получили пользовательское событие - выводим об этом сообщение в журнал
   if(id>CHARTEVENT_CUSTOM)
     {
      //--- Здесь может быть обработка щелчка по кнопке закрытия на панели
      PrintFormat("%s: Event id=%ld, object id (lparam): %lu, event message (sparam): %s",__FUNCTION__,id,lparam,sparam);
     }
  }
//+------------------------------------------------------------------+
//| Выводит данные с указанного индекса таймсерии на панель          |
//+------------------------------------------------------------------+
void DrawData(const int index,const datetime time)
  {
//--- Объявляем переменные для получения в них данных
   MqlRates rates[1];

//--- Если данные бара по указанному индексу получить не удалось - уходим
   if(CopyRates(Symbol(),PERIOD_CURRENT,index,1,rates)!=1)
      return;

//--- Устанавливаем параметры шрифта для заголовков данных бара и индикатора
   int  size=0;
   uint flags=0;
   uint angle=0;
   string name=panel.FontParams(size,flags,angle);
   panel.SetFontParams(name,9,FW_BOLD);
   panel.DrawText("Bar data ["+(string)index+"]",3,panel.TableY1(0)-16,clrMaroon,panel.Width()-6);
   panel.DrawText("Indicators data ["+(string)index+"]",3,panel.TableY1(1)-16,clrGreen,panel.Width()-6);
//--- Устанавливаем параметры шрифта для данных бара и индикатора
   panel.SetFontParams(name,9);

//--- Выводим на панель данные указанного бара в таблицу 0
   panel.DrawText("Date",  panel.CellX(0,0,0)+2, panel.CellY(0,0,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_DATE),     panel.CellX(0,0,1)+2, panel.CellY(0,0,1)+2,clrNONE,90);
   panel.DrawText("Time",  panel.CellX(0,1,0)+2, panel.CellY(0,1,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_MINUTES),  panel.CellX(0,1,1)+2, panel.CellY(0,1,1)+2,clrNONE,90);
   panel.DrawText("Open",  panel.CellX(0,2,0)+2, panel.CellY(0,2,0)+2); panel.DrawText(DoubleToString(rates[0].open,Digits()),      panel.CellX(0,2,1)+2, panel.CellY(0,2,1)+2,clrNONE,90);
   panel.DrawText("High",  panel.CellX(0,3,0)+2, panel.CellY(0,3,0)+2); panel.DrawText(DoubleToString(rates[0].high,Digits()),      panel.CellX(0,3,1)+2, panel.CellY(0,3,1)+2,clrNONE,90);
   panel.DrawText("Low",   panel.CellX(0,4,0)+2, panel.CellY(0,4,0)+2); panel.DrawText(DoubleToString(rates[0].low,Digits()),       panel.CellX(0,4,1)+2, panel.CellY(0,4,1)+2,clrNONE,90);
   panel.DrawText("Close", panel.CellX(0,5,0)+2, panel.CellY(0,5,0)+2); panel.DrawText(DoubleToString(rates[0].close,Digits()),     panel.CellX(0,5,1)+2, panel.CellY(0,5,1)+2,clrNONE,90);

//--- Выводим в таблицу 1 данные индикатора 1 с указанного бара в таблицу 1
   panel.DrawText(indicators.Title(handle_obv), panel.CellX(1,0,0)+2, panel.CellY(1,0,0)+2);
   double value1=indicators.GetData(handle_obv,0,0,index);
   string value_str1=(value1!=EMPTY_VALUE ? DoubleToString(value1,indicators.Digits(handle_obv)) : " ");
   panel.DrawText(value_str1,panel.CellX(1,0,1)+2,panel.CellY(1,0,1)+2,clrNONE,110);
   
//--- Выводим описание состояния линии индикатора 1
   panel.DrawText("Line state", panel.CellX(1,1,0)+2, panel.CellY(1,1,0)+2);
   ENUM_LINE_STATE state1=indicators.BufferLineState(Symbol(),PERIOD_CURRENT,handle_obv,0,0,index);
   panel.DrawText(BufferLineStateDescription(state1),panel.CellX(1,1,1)+2,panel.CellY(1,1,1)+2,clrNONE,110);
   
//--- Перерисовываем график для немедленного отображения всех изменений на панели
   ChartRedraw(ChartID());
  }
//+------------------------------------------------------------------+

После компиляции и запуска индикатора на графике М1 и выбранном периоде графика для расчёта М5, видим:


Индикатор Parabolic SAR:

//+------------------------------------------------------------------+
//|                                         TestMSTFParabolicSAR.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_chart_window
#property indicator_buffers 2
#property indicator_plots   2
//--- enums

//--- plot PSAR1
#property indicator_label1  "PSAR1"
#property indicator_type1   DRAW_ARROW
#property indicator_color1  clrDodgerBlue
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1

//--- plot MA2
#property indicator_label2  "PSAR2"
#property indicator_type2   DRAW_ARROW
#property indicator_color2  clrRed
#property indicator_style2  STYLE_SOLID
#property indicator_width2  1

//--- includes
#include <IndMSTF\IndMSTF.mqh>
#include <Dashboard\Dashboard.mqh>
//--- input parameters
input string               InpSymbol      =  NULL;             /* Symbol                        */ // Символ скользящей средней
input ENUM_TIMEFRAMES      InpTimeframe   =  PERIOD_CURRENT;   /* Timeframe                     */ // Таймфрейм скользящей средней
input uchar                InpArrowCode1  =  158;              /* SAR Senior period Arrow Code  */ // Код стрелки для Parabolic SAR 1
input uchar                InpArrowCode2  =  159;              /* SAR Junior period Arrow Code  */ // Код стрелки для Parabolic SAR 2
input bool                 InpAsSeries    =  true;             /* As Series flag                */ // Флаг серийности массивов буферов индикатора

//--- indicator buffers
double         BufferPSAR1[];
double         BufferPSAR2[];
//--- global variables
int handle_psar1;
int handle_psar2;
CMSTFIndicators indicators;      // Экземпляр объекта коллекции индикаторов
//--- переменные для панели
CDashboard *panel=NULL;          // Указатель на объект панели
int         mouse_bar_index;     // Индекс бара, с которого берутся данные
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Устанавливаем таймер с периодичностью в 1 секунду
   EventSetTimer(1);
//--- Назначаем рисуемым буферам 0 и 1 массивы BufferMA1 и BufferMA2 соответственно
   SetIndexBuffer(0,BufferPSAR1,INDICATOR_DATA);
   SetIndexBuffer(1,BufferPSAR2,INDICATOR_DATA);
//--- Зададём код символа из шрифта Wingdings для отрисовки в PLOT_ARROW
//--- Старший период рисуется первым кодом стрелки, младший период - вторым
   uchar code1=InpArrowCode1;
   uchar code2=InpArrowCode2;
   if(InpTimeframe>Period())
     {
      code1=InpArrowCode1;
      code2=InpArrowCode2;
     }
   else
     {
      code1=InpArrowCode2;
      code2=InpArrowCode1;
     }
   PlotIndexSetInteger(0,PLOT_ARROW,code1);
   PlotIndexSetInteger(1,PLOT_ARROW,code2);
//--- sets indicator shift

//--- Устанавливаем флаги серийности массивам буферов индикатора (для теста, чтобы видно было отсутствие разницы)
   ArraySetAsSeries(BufferPSAR1,InpAsSeries);
   ArraySetAsSeries(BufferPSAR2,InpAsSeries);
   
//--- Создаём два индикатора одного типа
//--- Первый рассчитывается на текущих символе/периоде графика, а второй - на тех, что заданы в настройках
   handle_psar1=indicators.AddNewSAR(NULL,PERIOD_CURRENT);
   handle_psar2=indicators.AddNewSAR(InpSymbol,InpTimeframe);

//--- Если не удалось создать хэндлы индикаторов - возвращаем ошибку инициализации
   if(handle_psar1==INVALID_HANDLE || handle_psar2==INVALID_HANDLE)
      return INIT_FAILED;
//--- Устанавливаем описания линий индикатора из описаний буферов расчётной части созданных индикаторов
   indicators.SetPlotLabelFromBuffer(0,handle_psar1,0);
   indicators.SetPlotLabelFromBuffer(1,handle_psar2,0);
      
//--- Панель
//--- Создаём панель
   int width=290;
   panel=new CDashboard(1,20,20,width,264);
   if(panel==NULL)
     {
      Print("Error. Failed to create panel object");
      return INIT_FAILED;
     }
//--- Устанавливаем параметры шрифта
   panel.SetFontParams("Calibri",9);
//--- Отображаем панель с текстом в заголовке "Символ, Описание таймфрейма"
   panel.View(Symbol()+", "+StringSubstr(EnumToString(Period()),7));

//--- Создаём таблицу с идентификатором 0 для отображения в ней данных бара
   panel.CreateNewTable(0);
//--- Рисуем таблицу с идентификатором 0 на фоне панели
   panel.DrawGrid(0,2,20,6,2,18,width/2-2);

//--- Создаём таблицу с идентификатором 1 для отображения в ней данных индикатора 1
   panel.CreateNewTable(1);
//--- Получаем координату Y2 таблицы с идентификатором 0 и
//--- устанавливаем координату Y1 для таблицы с идентификатором 1
   int y1=panel.TableY2(0)+22;
//--- Рисуем таблицу с идентификатором 1 на фоне панели
   panel.DrawGrid(1,2,y1,2,2,18,width/2-2);

//--- Создаём таблицу с идентификатором 2 для отображения в ней данных индикатора 2
   panel.CreateNewTable(2);
//--- Получаем координату Y2 таблицы с идентификатором 1 и
//--- устанавливаем координату Y1 для таблицы с идентификатором 2
   int y2=panel.TableY2(1)+3;
//--- Рисуем таблицу с идентификатором 2 на фоне панели
   panel.DrawGrid(2,2,y2,3,2,18,width/2-2);
   
//--- Инициализируем переменную с индексом бара указателя мышки
   mouse_bar_index=0;
//--- Выводим на панель данные текущего бара
   DrawData(mouse_bar_index,TimeCurrent());

//--- Успешная инициализация
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- Уничтожаем таймер
   EventKillTimer();
//--- Если объект панели существует - удаляем его
   if(panel!=NULL)
      delete panel;
//--- Стираем все комментарии
   Comment("");
  }
//+------------------------------------------------------------------+
//| 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[])
  {
//--- Количество баров для расчёта
   int limit=rates_total-prev_calculated;
//--- Если limit > 1, значит это первый расчёт, либо изменение в истории
   if(limit>1)
     {
      //--- указываем для просчёта всю доступную историю
      limit=rates_total-1;
      /*
      // Если в индикаторе есть какие-либо буферы, в которых отображены другие расчёты (не мульти- индикаторы),
      // то здесь их нужно инициализировать "пустым" значением, установленным для этих буферов
      */
     }
//--- Рассчитываем все созданные мультисимвольные мультипериодные индикаторы
   if(!indicators.Calculate())
      return 0;

//--- Выводим на панель данные бара под курсором (либо текущий бар, если курсор за пределами графика)
   DrawData(mouse_bar_index,time[mouse_bar_index]);

//--- Из буферов рассчитанных индикаторов выводим данные в индикаторные буферы
   if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_psar1,0,0,limit,BufferPSAR1))
      return 0;
   if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_psar2,0,0,limit,BufferPSAR2))
      return 0;

//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| Timer function                                                   | 
//+------------------------------------------------------------------+
void OnTimer()
  {
//--- Вызываем таймер коллекции индикаторов
   indicators.OnTimer();
  }
//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- Работа с панелью
//--- Вызываем обработчик событий панели
   panel.OnChartEvent(id,lparam,dparam,sparam);

//--- Если курсор перемещается или щелчок по графику
   if(id==CHARTEVENT_MOUSE_MOVE || id==CHARTEVENT_CLICK)
     {
      //--- Объявляем переменные для записи в них координат времени и цены
      datetime time=0;
      double price=0;
      int wnd=0;
      //--- Если координаты курсора преобразованы в дату и время
      if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price))
        {
         //--- записываем индекс бара, где расположен курсор в глобальную переменную
         mouse_bar_index=iBarShift(Symbol(),PERIOD_CURRENT,time);
         //--- Выводим данные бара под курсором на панель
         DrawData(mouse_bar_index,time);
        }
     }

//--- Если получили пользовательское событие - выводим об этом сообщение в журнал
   if(id>CHARTEVENT_CUSTOM)
     {
      //--- Здесь может быть обработка щелчка по кнопке закрытия на панели
      PrintFormat("%s: Event id=%ld, object id (lparam): %lu, event message (sparam): %s",__FUNCTION__,id,lparam,sparam);
     }
  }
//+------------------------------------------------------------------+
//| Выводит данные с указанного индекса таймсерии на панель          |
//+------------------------------------------------------------------+
void DrawData(const int index,const datetime time)
  {
//--- Объявляем переменные для получения в них данных
   MqlRates rates[1];

//--- Если данные бара по указанному индексу получить не удалось - уходим
   if(CopyRates(Symbol(),PERIOD_CURRENT,index,1,rates)!=1)
      return;

//--- Устанавливаем параметры шрифта для заголовков данных бара и индикатора
   int  size=0;
   uint flags=0;
   uint angle=0;
   string name=panel.FontParams(size,flags,angle);
   panel.SetFontParams(name,9,FW_BOLD);
   panel.DrawText("Bar data ["+(string)index+"]",3,panel.TableY1(0)-16,clrMaroon,panel.Width()-6);
   panel.DrawText("Indicators data ["+(string)index+"]",3,panel.TableY1(1)-16,clrGreen,panel.Width()-6);
//--- Устанавливаем параметры шрифта для данных бара и индикатора
   panel.SetFontParams(name,9);

//--- Выводим на панель данные указанного бара в таблицу 0
   panel.DrawText("Date",  panel.CellX(0,0,0)+2, panel.CellY(0,0,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_DATE),     panel.CellX(0,0,1)+2, panel.CellY(0,0,1)+2,clrNONE,90);
   panel.DrawText("Time",  panel.CellX(0,1,0)+2, panel.CellY(0,1,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_MINUTES),  panel.CellX(0,1,1)+2, panel.CellY(0,1,1)+2,clrNONE,90);
   panel.DrawText("Open",  panel.CellX(0,2,0)+2, panel.CellY(0,2,0)+2); panel.DrawText(DoubleToString(rates[0].open,Digits()),      panel.CellX(0,2,1)+2, panel.CellY(0,2,1)+2,clrNONE,90);
   panel.DrawText("High",  panel.CellX(0,3,0)+2, panel.CellY(0,3,0)+2); panel.DrawText(DoubleToString(rates[0].high,Digits()),      panel.CellX(0,3,1)+2, panel.CellY(0,3,1)+2,clrNONE,90);
   panel.DrawText("Low",   panel.CellX(0,4,0)+2, panel.CellY(0,4,0)+2); panel.DrawText(DoubleToString(rates[0].low,Digits()),       panel.CellX(0,4,1)+2, panel.CellY(0,4,1)+2,clrNONE,90);
   panel.DrawText("Close", panel.CellX(0,5,0)+2, panel.CellY(0,5,0)+2); panel.DrawText(DoubleToString(rates[0].close,Digits()),     panel.CellX(0,5,1)+2, panel.CellY(0,5,1)+2,clrNONE,90);

//--- Выводим в таблицу 1 данные индикатора 1 с указанного бара в таблицу 1
   panel.DrawText(indicators.Title(handle_psar1), panel.CellX(1,0,0)+2, panel.CellY(1,0,0)+2);
   double value1=indicators.GetData(handle_psar1,0,0,index);
   string value_str1=(value1!=EMPTY_VALUE ? DoubleToString(value1,indicators.Digits(handle_psar1)) : " ");
   panel.DrawText(value_str1,panel.CellX(1,0,1)+2,panel.CellY(1,0,1)+2,clrNONE,110);
   
//--- Выводим описание состояния линии индикатора 1
   panel.DrawText("Line state", panel.CellX(1,1,0)+2, panel.CellY(1,1,0)+2);
   ENUM_LINE_STATE state1=indicators.BufferLineState(Symbol(),PERIOD_CURRENT,handle_psar1,0,0,index);
   panel.DrawText(BufferLineStateDescription(state1),panel.CellX(1,1,1)+2,panel.CellY(1,1,1)+2,clrNONE,110);
   
//--- Выводим в таблицу 2 данные индикатора 2 с указанного бара в таблицу 2
   panel.DrawText(indicators.Title(handle_psar2), panel.CellX(2,0,0)+2, panel.CellY(2,0,0)+2);
   double value2=indicators.GetDataTo(Symbol(),PERIOD_CURRENT,handle_psar2,0,0,index);
   string value_str2=(value2!=EMPTY_VALUE ? DoubleToString(value2,indicators.Digits(handle_psar2)) : " ");
   panel.DrawText(value_str2,panel.CellX(2,0,1)+2,panel.CellY(2,0,1)+2,clrNONE,110);
   
//--- Выводим описание состояния линии индикатора 2
   panel.DrawText("Line state", panel.CellX(2,1,0)+2, panel.CellY(2,1,0)+2);
   ENUM_LINE_STATE state2=indicators.BufferLineState(Symbol(),PERIOD_CURRENT,handle_psar2,0,0,index);
   panel.DrawText(BufferLineStateDescription(state2),panel.CellX(2,1,1)+2,panel.CellY(2,1,1)+2,clrNONE,110);
   
//--- Выводим описание соотношения линии индикатора 1 относительно линии индикатора 2
   double value21=indicators.GetDataTo(Symbol(),PERIOD_CURRENT,handle_psar2,0,0,index+1);
   ENUM_LINE_STATE stateR=indicators.BufferLineStateRelative(Symbol(),PERIOD_CURRENT,handle_psar1,0,0,index,value2,value21);
   string ma1=indicators.Name(handle_psar1);
   string ma2=indicators.Name(handle_psar2);
   string state_relative=
     (
      stateR==LINE_STATE_ABOVE      ? StringFormat("%s1 > %s2",ma1,ma2)   :
      stateR==LINE_STATE_BELOW      ? StringFormat("%s1 < %s2",ma1,ma2)           :
      stateR==LINE_STATE_CROSS_DOWN ? "Top-down crossing"   :
      stateR==LINE_STATE_CROSS_UP   ? "Bottom-up crossing"  :
      BufferLineStateDescription(stateR)
     );
   panel.DrawText(StringFormat("%s1 vs %s2",ma1,ma2), panel.CellX(2,2,0)+2, panel.CellY(2,2,0)+2);
   panel.DrawText(state_relative, panel.CellX(2,2,1)+2, panel.CellY(2,2,1)+2,clrNONE,110);
   
//--- Перерисовываем график для немедленного отображения всех изменений на панели
   ChartRedraw(ChartID());
  }
//+------------------------------------------------------------------+

После компиляции и запуска индикатора на графике М1 и выбранном периоде графика для расчёта М5, видим:



Индикатор Relative Strength Index:

//+------------------------------------------------------------------+
//|                                TestMSTFRelativeStrengthIndex.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_separate_window
#property indicator_buffers 2
#property indicator_plots   2
//--- enums

//--- plot RSI1
#property indicator_label1  "RSI1"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrSeaGreen
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1

//--- plot RSI2
#property indicator_label2  "RSI2"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrDodgerBlue
#property indicator_style2  STYLE_SOLID
#property indicator_width2  1

//--- includes
#include <IndMSTF\IndMSTF.mqh>
#include <Dashboard\Dashboard.mqh>
//--- input parameters
input string               InpSymbol      =  NULL;             /* Symbol                     */ // Символ
input ENUM_TIMEFRAMES      InpTimeframe   =  PERIOD_CURRENT;   /* Timeframe                  */ // Таймфрейм
input int                  InpPeriod      =  14;               /* Period                     */ // Период расчёта
input ENUM_APPLIED_PRICE   InpPrice       =  PRICE_CLOSE;      /* Applied Price              */ // Цена расчёта
input uchar                InpLineWidth1  =  2;                /* Senior period Line Width   */ // Толщина линии для старшего периода
input uchar                InpLineWidth2  =  1;                /* Junior period Line Width   */ // Толщина линии для младшего периода
input bool                 InpAsSeries    =  true;             /* As Series flag             */ // Флаг серийности массивов буферов индикатора

//--- indicator buffers
double         BufferRSI1[];
double         BufferRSI2[];
//--- global variables
int handle_rsi1;
int handle_rsi2;
CMSTFIndicators indicators;      // Экземпляр объекта коллекции индикаторов
//--- переменные для панели
CDashboard *panel=NULL;          // Указатель на объект панели
int         mouse_bar_index;     // Индекс бара, с которого берутся данные
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Устанавливаем таймер с периодичностью в 1 секунду
   EventSetTimer(1);
//--- Назначаем рисуемым буферам 0 и 1 массивы BufferMA1 и BufferMA2 соответственно
   SetIndexBuffer(0,BufferRSI1,INDICATOR_DATA);
   SetIndexBuffer(1,BufferRSI2,INDICATOR_DATA);
//--- Устанавливаем ширину линий
   int w1=0,w2=0;
   if(InpTimeframe>Period())
     {
      w1=InpLineWidth2;
      w2=InpLineWidth1;
     }
   else
     {
      w1=InpLineWidth1;
      w2=InpLineWidth2;
     }
   PlotIndexSetInteger(0,PLOT_LINE_WIDTH,w1);
   PlotIndexSetInteger(1,PLOT_LINE_WIDTH,w2);
//--- sets indicator shift

//--- Устанавливаем флаги серийности массивам буферов индикатора (для теста, чтобы видно было отсутствие разницы)
   ArraySetAsSeries(BufferRSI1,InpAsSeries);
   ArraySetAsSeries(BufferRSI2,InpAsSeries);
   
//--- Создаём два индикатора одного типа
//--- Первый рассчитывается на текущих символе/периоде графика, а второй - на тех, что заданы в настройках
   handle_rsi1=indicators.AddNewRSI(NULL,PERIOD_CURRENT,InpPeriod,InpPrice);
   handle_rsi2=indicators.AddNewRSI(InpSymbol,InpTimeframe,InpPeriod,InpPrice);

//--- Если не удалось создать хэндлы индикаторов - возвращаем ошибку инициализации
   if(handle_rsi1==INVALID_HANDLE || handle_rsi2==INVALID_HANDLE)
      return INIT_FAILED;
//--- Устанавливаем описания линий индикатора из описаний буферов расчётной части созданных индикаторов
   indicators.SetPlotLabelFromBuffer(0,handle_rsi1,0);
   indicators.SetPlotLabelFromBuffer(1,handle_rsi2,0);
      
//--- Панель
//--- Создаём панель
   int width=231;
   panel=new CDashboard(1,20,20,width,264,0);
   if(panel==NULL)
     {
      Print("Error. Failed to create panel object");
      return INIT_FAILED;
     }
//--- Устанавливаем параметры шрифта
   panel.SetFontParams("Calibri",9);
//--- Отображаем панель с текстом в заголовке "Символ, Описание таймфрейма"
   panel.View(Symbol()+", "+StringSubstr(EnumToString(Period()),7));

//--- Создаём таблицу с идентификатором 0 для отображения в ней данных бара
   panel.CreateNewTable(0);
//--- Рисуем таблицу с идентификатором 0 на фоне панели
   panel.DrawGrid(0,2,20,6,2,18,width/2-2);

//--- Создаём таблицу с идентификатором 1 для отображения в ней данных индикатора 1
   panel.CreateNewTable(1);
//--- Получаем координату Y2 таблицы с идентификатором 0 и
//--- устанавливаем координату Y1 для таблицы с идентификатором 1
   int y1=panel.TableY2(0)+22;
//--- Рисуем таблицу с идентификатором 1 на фоне панели
   panel.DrawGrid(1,2,y1,2,2,18,width/2-2);

//--- Создаём таблицу с идентификатором 2 для отображения в ней данных индикатора 2
   panel.CreateNewTable(2);
//--- Получаем координату Y2 таблицы с идентификатором 1 и
//--- устанавливаем координату Y1 для таблицы с идентификатором 2
   int y2=panel.TableY2(1)+3;
//--- Рисуем таблицу с идентификатором 2 на фоне панели
   panel.DrawGrid(2,2,y2,3,2,18,width/2-2);
   
//--- Инициализируем переменную с индексом бара указателя мышки
   mouse_bar_index=0;
//--- Выводим на панель данные текущего бара
   DrawData(mouse_bar_index,TimeCurrent());

//--- Успешная инициализация
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- Уничтожаем таймер
   EventKillTimer();
//--- Если объект панели существует - удаляем его
   if(panel!=NULL)
      delete panel;
//--- Стираем все комментарии
   Comment("");
  }
//+------------------------------------------------------------------+
//| 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[])
  {
//--- Количество баров для расчёта
   int limit=rates_total-prev_calculated;
//--- Если limit > 1, значит это первый расчёт, либо изменение в истории
   if(limit>1)
     {
      //--- указываем для просчёта всю доступную историю
      limit=rates_total-1;
      /*
      // Если в индикаторе есть какие-либо буферы, в которых отображены другие расчёты (не мульти- индикаторы),
      // то здесь их нужно инициализировать "пустым" значением, установленным для этих буферов
      */
     }
//--- Рассчитываем все созданные мультисимвольные мультипериодные индикаторы
   if(!indicators.Calculate())
      return 0;

//--- Выводим на панель данные бара под курсором (либо текущий бар, если курсор за пределами графика)
   DrawData(mouse_bar_index,time[mouse_bar_index]);

//--- Из буферов рассчитанных индикаторов выводим данные в индикаторные буферы
   if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_rsi1,0,0,limit,BufferRSI1))
      return 0;
   if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_rsi2,0,0,limit,BufferRSI2))
      return 0;

//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| Timer function                                                   | 
//+------------------------------------------------------------------+
void OnTimer()
  {
//--- Вызываем таймер коллекции индикаторов
   indicators.OnTimer();
  }
//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- Работа с панелью
//--- Вызываем обработчик событий панели
   panel.OnChartEvent(id,lparam,dparam,sparam);

//--- Если курсор перемещается или щелчок по графику
   if(id==CHARTEVENT_MOUSE_MOVE || id==CHARTEVENT_CLICK)
     {
      //--- Объявляем переменные для записи в них координат времени и цены
      datetime time=0;
      double price=0;
      int wnd=0;
      //--- Если координаты курсора преобразованы в дату и время
      if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price))
        {
         //--- записываем индекс бара, где расположен курсор в глобальную переменную
         mouse_bar_index=iBarShift(Symbol(),PERIOD_CURRENT,time);
         //--- Выводим данные бара под курсором на панель
         DrawData(mouse_bar_index,time);
        }
     }

//--- Если получили пользовательское событие - выводим об этом сообщение в журнал
   if(id>CHARTEVENT_CUSTOM)
     {
      //--- Здесь может быть обработка щелчка по кнопке закрытия на панели
      PrintFormat("%s: Event id=%ld, object id (lparam): %lu, event message (sparam): %s",__FUNCTION__,id,lparam,sparam);
     }
  }
//+------------------------------------------------------------------+
//| Выводит данные с указанного индекса таймсерии на панель          |
//+------------------------------------------------------------------+
void DrawData(const int index,const datetime time)
  {
//--- Объявляем переменные для получения в них данных
   MqlRates rates[1];

//--- Если данные бара по указанному индексу получить не удалось - уходим
   if(CopyRates(Symbol(),PERIOD_CURRENT,index,1,rates)!=1)
      return;

//--- Устанавливаем параметры шрифта для заголовков данных бара и индикатора
   int  size=0;
   uint flags=0;
   uint angle=0;
   string name=panel.FontParams(size,flags,angle);
   panel.SetFontParams(name,9,FW_BOLD);
   panel.DrawText("Bar data ["+(string)index+"]",3,panel.TableY1(0)-16,clrMaroon,panel.Width()-6);
   panel.DrawText("Indicators data ["+(string)index+"]",3,panel.TableY1(1)-16,clrGreen,panel.Width()-6);
//--- Устанавливаем параметры шрифта для данных бара и индикатора
   panel.SetFontParams(name,9);

//--- Выводим на панель данные указанного бара в таблицу 0
   panel.DrawText("Date",  panel.CellX(0,0,0)+2, panel.CellY(0,0,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_DATE),     panel.CellX(0,0,1)+2, panel.CellY(0,0,1)+2,clrNONE,90);
   panel.DrawText("Time",  panel.CellX(0,1,0)+2, panel.CellY(0,1,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_MINUTES),  panel.CellX(0,1,1)+2, panel.CellY(0,1,1)+2,clrNONE,90);
   panel.DrawText("Open",  panel.CellX(0,2,0)+2, panel.CellY(0,2,0)+2); panel.DrawText(DoubleToString(rates[0].open,Digits()),      panel.CellX(0,2,1)+2, panel.CellY(0,2,1)+2,clrNONE,90);
   panel.DrawText("High",  panel.CellX(0,3,0)+2, panel.CellY(0,3,0)+2); panel.DrawText(DoubleToString(rates[0].high,Digits()),      panel.CellX(0,3,1)+2, panel.CellY(0,3,1)+2,clrNONE,90);
   panel.DrawText("Low",   panel.CellX(0,4,0)+2, panel.CellY(0,4,0)+2); panel.DrawText(DoubleToString(rates[0].low,Digits()),       panel.CellX(0,4,1)+2, panel.CellY(0,4,1)+2,clrNONE,90);
   panel.DrawText("Close", panel.CellX(0,5,0)+2, panel.CellY(0,5,0)+2); panel.DrawText(DoubleToString(rates[0].close,Digits()),     panel.CellX(0,5,1)+2, panel.CellY(0,5,1)+2,clrNONE,90);

//--- Выводим в таблицу 1 данные индикатора 1 с указанного бара в таблицу 1
   panel.DrawText(indicators.Title(handle_rsi1), panel.CellX(1,0,0)+2, panel.CellY(1,0,0)+2);
   double value1=indicators.GetData(handle_rsi1,0,0,index);
   string value_str1=(value1!=EMPTY_VALUE ? DoubleToString(value1,indicators.Digits(handle_rsi1)) : " ");
   panel.DrawText(value_str1,panel.CellX(1,0,1)+2,panel.CellY(1,0,1)+2,clrNONE,190);
   
//--- Выводим описание состояния линии индикатора 1
   panel.DrawText("Line state", panel.CellX(1,1,0)+2, panel.CellY(1,1,0)+2);
   ENUM_LINE_STATE state1=indicators.BufferLineState(Symbol(),PERIOD_CURRENT,handle_rsi1,0,0,index);
   panel.DrawText(BufferLineStateDescription(state1),panel.CellX(1,1,1)+2,panel.CellY(1,1,1)+2,clrNONE,190);
   
//--- Выводим в таблицу 2 данные индикатора 2 с указанного бара в таблицу 2
   panel.DrawText(indicators.Title(handle_rsi2), panel.CellX(2,0,0)+2, panel.CellY(2,0,0)+2);
   double value2=indicators.GetDataTo(Symbol(),PERIOD_CURRENT,handle_rsi2,0,0,index);
   string value_str2=(value2!=EMPTY_VALUE ? DoubleToString(value2,indicators.Digits(handle_rsi2)) : " ");
   panel.DrawText(value_str2,panel.CellX(2,0,1)+2,panel.CellY(2,0,1)+2,clrNONE,190);
   
//--- Выводим описание состояния линии индикатора 2
   panel.DrawText("Line state", panel.CellX(2,1,0)+2, panel.CellY(2,1,0)+2);
   ENUM_LINE_STATE state2=indicators.BufferLineState(Symbol(),PERIOD_CURRENT,handle_rsi2,0,0,index);
   panel.DrawText(BufferLineStateDescription(state2),panel.CellX(2,1,1)+2,panel.CellY(2,1,1)+2,clrNONE,190);
   
//--- Выводим описание соотношения линии индикатора 1 относительно линии индикатора 2
   double value21=indicators.GetDataTo(Symbol(),PERIOD_CURRENT,handle_rsi2,0,0,index+1);
   ENUM_LINE_STATE stateR=indicators.BufferLineStateRelative(Symbol(),PERIOD_CURRENT,handle_rsi1,0,0,index,value2,value21);
   string ma1=indicators.Name(handle_rsi1);
   string ma2=indicators.Name(handle_rsi2);
   string state_relative=
     (
      stateR==LINE_STATE_ABOVE      ? StringFormat("%s1 > %s2",ma1,ma2)   :
      stateR==LINE_STATE_BELOW      ? StringFormat("%s1 < %s2",ma1,ma2)           :
      stateR==LINE_STATE_CROSS_DOWN ? "Top-down crossing"   :
      stateR==LINE_STATE_CROSS_UP   ? "Bottom-up crossing"  :
      BufferLineStateDescription(stateR)
     );
   panel.DrawText(StringFormat("%s1 vs %s2",ma1,ma2), panel.CellX(2,2,0)+2, panel.CellY(2,2,0)+2);
   panel.DrawText(state_relative, panel.CellX(2,2,1)+2, panel.CellY(2,2,1)+2,clrNONE,190);
   
//--- Перерисовываем график для немедленного отображения всех изменений на панели
   ChartRedraw(ChartID());
  }
//+------------------------------------------------------------------+

После компиляции и запуска индикатора на графике М1 и выбранном периоде графика для расчёта М5, видим:



Индикатор Relative Vigor Index:

//+------------------------------------------------------------------+
//|                                   TestMSTFRelativeVigorIndex.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_separate_window
#property indicator_buffers 4
#property indicator_plots   4
//--- enums

//--- plot RVI1
#property indicator_label1  "RVI1"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrSeaGreen
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1

//--- plot Signal1
#property indicator_label2  "Signal1"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrOrangeRed
#property indicator_style2  STYLE_SOLID
#property indicator_width2  1

//--- plot RVI2
#property indicator_label3  "RVI2"
#property indicator_type3   DRAW_LINE
#property indicator_color3  clrGreen
#property indicator_style3  STYLE_SOLID
#property indicator_width3  1

//--- plot Signal2
#property indicator_label4  "Signal2"
#property indicator_type4   DRAW_LINE
#property indicator_color4  clrRed
#property indicator_style4  STYLE_SOLID
#property indicator_width4  1

//--- includes
#include <IndMSTF\IndMSTF.mqh>
#include <Dashboard\Dashboard.mqh>
//--- input parameters
input string               InpSymbol      =  NULL;             /* Symbol                     */ // Символ
input ENUM_TIMEFRAMES      InpTimeframe   =  PERIOD_CURRENT;   /* Timeframe                  */ // Таймфрейм
input int                  InpPeriod      =  10;               /* Period                     */ // Период расчёта
input uchar                InpLineWidth1  =  2;                /* Senior period Line Width   */ // Толщина линии для старшего периода
input uchar                InpLineWidth2  =  1;                /* Junior period Line Width   */ // Толщина линии для младшего периода
input bool                 InpAsSeries    =  true;             /* As Series flag             */ // Флаг серийности массивов буферов индикатора

//--- indicator buffers
double         BufferRVI1[];
double         BufferSig1[];
double         BufferRVI2[];
double         BufferSig2[];
//--- global variables
int handle_rvi1;
int handle_rvi2;
CMSTFIndicators indicators;      // Экземпляр объекта коллекции индикаторов
//--- переменные для панели
CDashboard *panel=NULL;          // Указатель на объект панели
int         mouse_bar_index;     // Индекс бара, с которого берутся данные
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Устанавливаем таймер с периодичностью в 1 секунду
   EventSetTimer(1);
//--- Назначаем рисуемым буферам 0 и 1 массивы BufferMA1 и BufferMA2 соответственно
   SetIndexBuffer(0,BufferRVI1,INDICATOR_DATA);
   SetIndexBuffer(1,BufferSig1,INDICATOR_DATA);
   SetIndexBuffer(2,BufferRVI2,INDICATOR_DATA);
   SetIndexBuffer(3,BufferSig2,INDICATOR_DATA);
//--- Устанавливаем ширину линий
   int w1=0,w2=0;
   if(InpTimeframe>Period())
     {
      w1=InpLineWidth2;
      w2=InpLineWidth1;
     }
   else
     {
      w1=InpLineWidth1;
      w2=InpLineWidth2;
     }
   PlotIndexSetInteger(0,PLOT_LINE_WIDTH,w1);
   PlotIndexSetInteger(1,PLOT_LINE_WIDTH,w1);
   PlotIndexSetInteger(2,PLOT_LINE_WIDTH,w2);
   PlotIndexSetInteger(3,PLOT_LINE_WIDTH,w2);
//--- sets indicator shift

//--- Устанавливаем флаги серийности массивам буферов индикатора (для теста, чтобы видно было отсутствие разницы)
   ArraySetAsSeries(BufferRVI1,InpAsSeries);
   ArraySetAsSeries(BufferSig1,InpAsSeries);
   ArraySetAsSeries(BufferRVI2,InpAsSeries);
   ArraySetAsSeries(BufferSig2,InpAsSeries);
   
//--- Создаём два индикатора одного типа
//--- Первый рассчитывается на текущих символе/периоде графика, а второй - на тех, что заданы в настройках
   handle_rvi1=indicators.AddNewRVI(NULL,PERIOD_CURRENT,InpPeriod);
   handle_rvi2=indicators.AddNewRVI(InpSymbol,InpTimeframe,InpPeriod);

//--- Если не удалось создать хэндлы индикаторов - возвращаем ошибку инициализации
   if(handle_rvi1==INVALID_HANDLE || handle_rvi2==INVALID_HANDLE)
      return INIT_FAILED;
//--- Устанавливаем описания линий индикатора из описаний буферов расчётной части созданных индикаторов
   indicators.SetPlotLabelFromBuffer(0,handle_rvi1,0);
   indicators.SetPlotLabelFromBuffer(1,handle_rvi1,1);
   indicators.SetPlotLabelFromBuffer(2,handle_rvi2,0);
   indicators.SetPlotLabelFromBuffer(3,handle_rvi2,1);
      
//--- Панель
//--- Создаём панель
   int width=231;
   panel=new CDashboard(1,20,20,width,264,0);
   if(panel==NULL)
     {
      Print("Error. Failed to create panel object");
      return INIT_FAILED;
     }
//--- Устанавливаем параметры шрифта
   panel.SetFontParams("Calibri",9);
//--- Отображаем панель с текстом в заголовке "Символ, Описание таймфрейма"
   panel.View(Symbol()+", "+StringSubstr(EnumToString(Period()),7));

//--- Создаём таблицу с идентификатором 0 для отображения в ней данных бара
   panel.CreateNewTable(0);
//--- Рисуем таблицу с идентификатором 0 на фоне панели
   panel.DrawGrid(0,2,20,6,2,18,width/2-2);

//--- Создаём таблицу с идентификатором 1 для отображения в ней данных индикатора 1
   panel.CreateNewTable(1);
//--- Получаем координату Y2 таблицы с идентификатором 0 и
//--- устанавливаем координату Y1 для таблицы с идентификатором 1
   int y1=panel.TableY2(0)+22;
//--- Рисуем таблицу с идентификатором 1 на фоне панели
   panel.DrawGrid(1,2,y1,2,2,18,width/2-2);

//--- Создаём таблицу с идентификатором 2 для отображения в ней данных индикатора 2
   panel.CreateNewTable(2);
//--- Получаем координату Y2 таблицы с идентификатором 1 и
//--- устанавливаем координату Y1 для таблицы с идентификатором 2
   int y2=panel.TableY2(1)+3;
//--- Рисуем таблицу с идентификатором 2 на фоне панели
   panel.DrawGrid(2,2,y2,3,2,18,width/2-2);
   
//--- Инициализируем переменную с индексом бара указателя мышки
   mouse_bar_index=0;
//--- Выводим на панель данные текущего бара
   DrawData(mouse_bar_index,TimeCurrent());

//--- Успешная инициализация
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- Уничтожаем таймер
   EventKillTimer();
//--- Если объект панели существует - удаляем его
   if(panel!=NULL)
      delete panel;
//--- Стираем все комментарии
   Comment("");
  }
//+------------------------------------------------------------------+
//| 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[])
  {
//--- Количество баров для расчёта
   int limit=rates_total-prev_calculated;
//--- Если limit > 1, значит это первый расчёт, либо изменение в истории
   if(limit>1)
     {
      //--- указываем для просчёта всю доступную историю
      limit=rates_total-1;
      /*
      // Если в индикаторе есть какие-либо буферы, в которых отображены другие расчёты (не мульти- индикаторы),
      // то здесь их нужно инициализировать "пустым" значением, установленным для этих буферов
      */
     }
//--- Рассчитываем все созданные мультисимвольные мультипериодные индикаторы
   if(!indicators.Calculate())
      return 0;

//--- Выводим на панель данные бара под курсором (либо текущий бар, если курсор за пределами графика)
   DrawData(mouse_bar_index,time[mouse_bar_index]);

//--- Из буферов рассчитанных индикаторов выводим данные в индикаторные буферы
   if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_rvi1,0,0,limit,BufferRVI1))
      return 0;
   if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_rvi1,1,0,limit,BufferSig1))
      return 0;
      
   if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_rvi2,0,0,limit,BufferRVI2))
      return 0;
   if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_rvi2,1,0,limit,BufferSig2))
      return 0;
      
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| Timer function                                                   | 
//+------------------------------------------------------------------+
void OnTimer()
  {
//--- Вызываем таймер коллекции индикаторов
   indicators.OnTimer();
  }
//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- Работа с панелью
//--- Вызываем обработчик событий панели
   panel.OnChartEvent(id,lparam,dparam,sparam);

//--- Если курсор перемещается или щелчок по графику
   if(id==CHARTEVENT_MOUSE_MOVE || id==CHARTEVENT_CLICK)
     {
      //--- Объявляем переменные для записи в них координат времени и цены
      datetime time=0;
      double price=0;
      int wnd=0;
      //--- Если координаты курсора преобразованы в дату и время
      if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price))
        {
         //--- записываем индекс бара, где расположен курсор в глобальную переменную
         mouse_bar_index=iBarShift(Symbol(),PERIOD_CURRENT,time);
         //--- Выводим данные бара под курсором на панель
         DrawData(mouse_bar_index,time);
        }
     }

//--- Если получили пользовательское событие - выводим об этом сообщение в журнал
   if(id>CHARTEVENT_CUSTOM)
     {
      //--- Здесь может быть обработка щелчка по кнопке закрытия на панели
      PrintFormat("%s: Event id=%ld, object id (lparam): %lu, event message (sparam): %s",__FUNCTION__,id,lparam,sparam);
     }
  }
//+------------------------------------------------------------------+
//| Выводит данные с указанного индекса таймсерии на панель          |
//+------------------------------------------------------------------+
void DrawData(const int index,const datetime time)
  {
//--- Объявляем переменные для получения в них данных
   MqlRates rates[1];

//--- Если данные бара по указанному индексу получить не удалось - уходим
   if(CopyRates(Symbol(),PERIOD_CURRENT,index,1,rates)!=1)
      return;

//--- Устанавливаем параметры шрифта для заголовков данных бара и индикатора
   int  size=0;
   uint flags=0;
   uint angle=0;
   string name=panel.FontParams(size,flags,angle);
   panel.SetFontParams(name,9,FW_BOLD);
   panel.DrawText("Bar data ["+(string)index+"]",3,panel.TableY1(0)-16,clrMaroon,panel.Width()-6);
   panel.DrawText("Indicators data ["+(string)index+"]",3,panel.TableY1(1)-16,clrGreen,panel.Width()-6);
//--- Устанавливаем параметры шрифта для данных бара и индикатора
   panel.SetFontParams(name,9);

//--- Выводим на панель данные указанного бара в таблицу 0
   panel.DrawText("Date",  panel.CellX(0,0,0)+2, panel.CellY(0,0,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_DATE),     panel.CellX(0,0,1)+2, panel.CellY(0,0,1)+2,clrNONE,90);
   panel.DrawText("Time",  panel.CellX(0,1,0)+2, panel.CellY(0,1,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_MINUTES),  panel.CellX(0,1,1)+2, panel.CellY(0,1,1)+2,clrNONE,90);
   panel.DrawText("Open",  panel.CellX(0,2,0)+2, panel.CellY(0,2,0)+2); panel.DrawText(DoubleToString(rates[0].open,Digits()),      panel.CellX(0,2,1)+2, panel.CellY(0,2,1)+2,clrNONE,90);
   panel.DrawText("High",  panel.CellX(0,3,0)+2, panel.CellY(0,3,0)+2); panel.DrawText(DoubleToString(rates[0].high,Digits()),      panel.CellX(0,3,1)+2, panel.CellY(0,3,1)+2,clrNONE,90);
   panel.DrawText("Low",   panel.CellX(0,4,0)+2, panel.CellY(0,4,0)+2); panel.DrawText(DoubleToString(rates[0].low,Digits()),       panel.CellX(0,4,1)+2, panel.CellY(0,4,1)+2,clrNONE,90);
   panel.DrawText("Close", panel.CellX(0,5,0)+2, panel.CellY(0,5,0)+2); panel.DrawText(DoubleToString(rates[0].close,Digits()),     panel.CellX(0,5,1)+2, panel.CellY(0,5,1)+2,clrNONE,90);

//--- Выводим в таблицу 1 данные индикатора 1 с указанного бара в таблицу 1
   panel.DrawText(indicators.Title(handle_rvi1), panel.CellX(1,0,0)+2, panel.CellY(1,0,0)+2);
   double value1=indicators.GetData(handle_rvi1,0,0,index);
   string value_str1=(value1!=EMPTY_VALUE ? DoubleToString(value1,indicators.Digits(handle_rvi1)) : " ");
   panel.DrawText(value_str1,panel.CellX(1,0,1)+2,panel.CellY(1,0,1)+2,clrNONE,190);
   
//--- Выводим описание состояния линии индикатора 1
   panel.DrawText("Line state", panel.CellX(1,1,0)+2, panel.CellY(1,1,0)+2);
   ENUM_LINE_STATE state1=indicators.BufferLineState(Symbol(),PERIOD_CURRENT,handle_rvi1,0,0,index);
   panel.DrawText(BufferLineStateDescription(state1),panel.CellX(1,1,1)+2,panel.CellY(1,1,1)+2,clrNONE,190);
   
//--- Выводим в таблицу 2 данные индикатора 2 с указанного бара в таблицу 2
   panel.DrawText(indicators.Title(handle_rvi2), panel.CellX(2,0,0)+2, panel.CellY(2,0,0)+2);
   double value2=indicators.GetDataTo(Symbol(),PERIOD_CURRENT,handle_rvi2,0,0,index);
   string value_str2=(value2!=EMPTY_VALUE ? DoubleToString(value2,indicators.Digits(handle_rvi2)) : " ");
   panel.DrawText(value_str2,panel.CellX(2,0,1)+2,panel.CellY(2,0,1)+2,clrNONE,190);
   
//--- Выводим описание состояния линии индикатора 2
   panel.DrawText("Line state", panel.CellX(2,1,0)+2, panel.CellY(2,1,0)+2);
   ENUM_LINE_STATE state2=indicators.BufferLineState(Symbol(),PERIOD_CURRENT,handle_rvi2,0,0,index);
   panel.DrawText(BufferLineStateDescription(state2),panel.CellX(2,1,1)+2,panel.CellY(2,1,1)+2,clrNONE,190);
   
//--- Выводим описание соотношения линии индикатора 1 относительно линии индикатора 2
   double value21=indicators.GetDataTo(Symbol(),PERIOD_CURRENT,handle_rvi2,0,0,index+1);
   ENUM_LINE_STATE stateR=indicators.BufferLineStateRelative(Symbol(),PERIOD_CURRENT,handle_rvi1,0,0,index,value2,value21);
   string ma1=indicators.Name(handle_rvi1);
   string ma2=indicators.Name(handle_rvi2);
   string state_relative=
     (
      stateR==LINE_STATE_ABOVE      ? StringFormat("%s1 > %s2",ma1,ma2)   :
      stateR==LINE_STATE_BELOW      ? StringFormat("%s1 < %s2",ma1,ma2)           :
      stateR==LINE_STATE_CROSS_DOWN ? "Top-down crossing"   :
      stateR==LINE_STATE_CROSS_UP   ? "Bottom-up crossing"  :
      BufferLineStateDescription(stateR)
     );
   panel.DrawText(StringFormat("%s1 vs %s2",ma1,ma2), panel.CellX(2,2,0)+2, panel.CellY(2,2,0)+2);
   panel.DrawText(state_relative, panel.CellX(2,2,1)+2, panel.CellY(2,2,1)+2,clrNONE,190);
   
//--- Перерисовываем график для немедленного отображения всех изменений на панели
   ChartRedraw(ChartID());
  }
//+------------------------------------------------------------------+

После компиляции и запуска индикатора на графике М1 и выбранном периоде графика для расчёта М5, видим:



Индикатор Standard Deviation:

//+------------------------------------------------------------------+
//|                                         TestMSTFStdDeviation.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_separate_window
#property indicator_buffers 2
#property indicator_plots   2
//--- enums

//--- plot StdDev1
#property indicator_label1  "StdDev1"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrDodgerBlue
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1

//--- plot StdDev2
#property indicator_label2  "StdDev2"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrRed
#property indicator_style2  STYLE_SOLID
#property indicator_width2  1

//--- includes
#include <IndMSTF\IndMSTF.mqh>
#include <Dashboard\Dashboard.mqh>
//--- input parameters
input string               InpSymbol      =  NULL;             /* Symbol                     */ // Символ
input ENUM_TIMEFRAMES      InpTimeframe   =  PERIOD_CURRENT;   /* Timeframe                  */ // Таймфрейм
input int                  InpPeriod      =  20;               /* Period                     */ // Период расчёта
input int                  InpShift       =  0;                /* Shift                      */ // Сдвиг по горизонтали
input ENUM_MA_METHOD       InpMethod      =  MODE_SMA;         /* Method                     */ // Отклонение
input ENUM_APPLIED_PRICE   InpPrice       =  PRICE_CLOSE;      /* Applied Price              */ // Используемая цена для расчёта
input uchar                InpLineWidth1  =  2;                /* Senior period Line Width   */ // Толщина линии для старшего периода
input uchar                InpLineWidth2  =  1;                /* Junior period Line Width   */ // Толщина линии для младшего периода
input bool                 InpAsSeries    =  true;             /* As Series flag             */ // Флаг серийности массивов буферов индикатора

//--- indicator buffers
double         BufferStdDev1[];
double         BufferStdDev2[];
//--- global variables
int handle_stddev1;
int handle_stddev2;
CMSTFIndicators indicators;      // Экземпляр объекта коллекции индикаторов
//--- переменные для панели
CDashboard *panel=NULL;          // Указатель на объект панели
int         mouse_bar_index;     // Индекс бара, с которого берутся данные
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Устанавливаем таймер с периодичностью в 1 секунду
   EventSetTimer(1);
//--- Назначаем рисуемым буферам 0 и 1 массивы BufferMA1 и BufferMA2 соответственно
   SetIndexBuffer(0,BufferStdDev1,INDICATOR_DATA);
   SetIndexBuffer(1,BufferStdDev2,INDICATOR_DATA);
//--- Устанавливаем ширину линий
   int w1=0,w2=0;
   if(InpTimeframe>Period())
     {
      w1=InpLineWidth2;
      w2=InpLineWidth1;
     }
   else
     {
      w1=InpLineWidth1;
      w2=InpLineWidth2;
     }
   PlotIndexSetInteger(0,PLOT_LINE_WIDTH,w1);
   PlotIndexSetInteger(1,PLOT_LINE_WIDTH,w2);
//--- sets indicator shift

//--- Устанавливаем флаги серийности массивам буферов индикатора (для теста, чтобы видно было отсутствие разницы)
   ArraySetAsSeries(BufferStdDev1,InpAsSeries);
   ArraySetAsSeries(BufferStdDev2,InpAsSeries);
   
//--- Создаём два индикатора одного типа
//--- Первый рассчитывается на текущих символе/периоде графика, а второй - на тех, что заданы в настройках
   handle_stddev1=indicators.AddNewStdDev(NULL,PERIOD_CURRENT,InpPeriod,InpShift,InpMethod,InpPrice);
   handle_stddev2=indicators.AddNewStdDev(InpSymbol,InpTimeframe,InpPeriod,InpShift,InpMethod,InpPrice);

//--- Если не удалось создать хэндлы индикаторов - возвращаем ошибку инициализации
   if(handle_stddev1==INVALID_HANDLE || handle_stddev2==INVALID_HANDLE)
      return INIT_FAILED;
//--- Устанавливаем описания линий индикатора из описаний буферов расчётной части созданных индикаторов
   indicators.SetPlotLabelFromBuffer(0,handle_stddev1,0);
   indicators.SetPlotLabelFromBuffer(1,handle_stddev2,0);
      
//--- Панель
//--- Создаём панель
   int width=290;
   panel=new CDashboard(1,20,20,width,264,0);
   if(panel==NULL)
     {
      Print("Error. Failed to create panel object");
      return INIT_FAILED;
     }
//--- Устанавливаем параметры шрифта
   panel.SetFontParams("Calibri",9);
//--- Отображаем панель с текстом в заголовке "Символ, Описание таймфрейма"
   panel.View(Symbol()+", "+StringSubstr(EnumToString(Period()),7));

//--- Создаём таблицу с идентификатором 0 для отображения в ней данных бара
   panel.CreateNewTable(0);
//--- Рисуем таблицу с идентификатором 0 на фоне панели
   panel.DrawGrid(0,2,20,6,2,18,width/2-2);

//--- Создаём таблицу с идентификатором 1 для отображения в ней данных индикатора 1
   panel.CreateNewTable(1);
//--- Получаем координату Y2 таблицы с идентификатором 0 и
//--- устанавливаем координату Y1 для таблицы с идентификатором 1
   int y1=panel.TableY2(0)+22;
//--- Рисуем таблицу с идентификатором 1 на фоне панели
   panel.DrawGrid(1,2,y1,2,2,18,width/2-2);

//--- Создаём таблицу с идентификатором 2 для отображения в ней данных индикатора 2
   panel.CreateNewTable(2);
//--- Получаем координату Y2 таблицы с идентификатором 1 и
//--- устанавливаем координату Y1 для таблицы с идентификатором 2
   int y2=panel.TableY2(1)+3;
//--- Рисуем таблицу с идентификатором 2 на фоне панели
   panel.DrawGrid(2,2,y2,3,2,18,width/2-2);
   
//--- Инициализируем переменную с индексом бара указателя мышки
   mouse_bar_index=0;
//--- Выводим на панель данные текущего бара
   DrawData(mouse_bar_index,TimeCurrent());

//--- Успешная инициализация
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- Уничтожаем таймер
   EventKillTimer();
//--- Если объект панели существует - удаляем его
   if(panel!=NULL)
      delete panel;
//--- Стираем все комментарии
   Comment("");
  }
//+------------------------------------------------------------------+
//| 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[])
  {
//--- Количество баров для расчёта
   int limit=rates_total-prev_calculated;
//--- Если limit > 1, значит это первый расчёт, либо изменение в истории
   if(limit>1)
     {
      //--- указываем для просчёта всю доступную историю
      limit=rates_total-1;
      /*
      // Если в индикаторе есть какие-либо буферы, в которых отображены другие расчёты (не мульти- индикаторы),
      // то здесь их нужно инициализировать "пустым" значением, установленным для этих буферов
      */
     }
//--- Рассчитываем все созданные мультисимвольные мультипериодные индикаторы
   if(!indicators.Calculate())
      return 0;

//--- Выводим на панель данные бара под курсором (либо текущий бар, если курсор за пределами графика)
   DrawData(mouse_bar_index,time[mouse_bar_index]);

//--- Из буферов рассчитанных индикаторов выводим данные в индикаторные буферы
   if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_stddev1,0,0,limit,BufferStdDev1))
      return 0;
   if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_stddev2,0,0,limit,BufferStdDev2))
      return 0;

//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| Timer function                                                   | 
//+------------------------------------------------------------------+
void OnTimer()
  {
//--- Вызываем таймер коллекции индикаторов
   indicators.OnTimer();
  }
//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- Работа с панелью
//--- Вызываем обработчик событий панели
   panel.OnChartEvent(id,lparam,dparam,sparam);

//--- Если курсор перемещается или щелчок по графику
   if(id==CHARTEVENT_MOUSE_MOVE || id==CHARTEVENT_CLICK)
     {
      //--- Объявляем переменные для записи в них координат времени и цены
      datetime time=0;
      double price=0;
      int wnd=0;
      //--- Если координаты курсора преобразованы в дату и время
      if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price))
        {
         //--- записываем индекс бара, где расположен курсор в глобальную переменную
         mouse_bar_index=iBarShift(Symbol(),PERIOD_CURRENT,time);
         //--- Выводим данные бара под курсором на панель
         DrawData(mouse_bar_index,time);
        }
     }

//--- Если получили пользовательское событие - выводим об этом сообщение в журнал
   if(id>CHARTEVENT_CUSTOM)
     {
      //--- Здесь может быть обработка щелчка по кнопке закрытия на панели
      PrintFormat("%s: Event id=%ld, object id (lparam): %lu, event message (sparam): %s",__FUNCTION__,id,lparam,sparam);
     }
  }
//+------------------------------------------------------------------+
//| Выводит данные с указанного индекса таймсерии на панель          |
//+------------------------------------------------------------------+
void DrawData(const int index,const datetime time)
  {
//--- Объявляем переменные для получения в них данных
   MqlRates rates[1];

//--- Если данные бара по указанному индексу получить не удалось - уходим
   if(CopyRates(Symbol(),PERIOD_CURRENT,index,1,rates)!=1)
      return;

//--- Устанавливаем параметры шрифта для заголовков данных бара и индикатора
   int  size=0;
   uint flags=0;
   uint angle=0;
   string name=panel.FontParams(size,flags,angle);
   panel.SetFontParams(name,9,FW_BOLD);
   panel.DrawText("Bar data ["+(string)index+"]",3,panel.TableY1(0)-16,clrMaroon,panel.Width()-6);
   panel.DrawText("Indicators data ["+(string)index+"]",3,panel.TableY1(1)-16,clrGreen,panel.Width()-6);
//--- Устанавливаем параметры шрифта для данных бара и индикатора
   panel.SetFontParams(name,9);

//--- Выводим на панель данные указанного бара в таблицу 0
   panel.DrawText("Date",  panel.CellX(0,0,0)+2, panel.CellY(0,0,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_DATE),     panel.CellX(0,0,1)+2, panel.CellY(0,0,1)+2,clrNONE,90);
   panel.DrawText("Time",  panel.CellX(0,1,0)+2, panel.CellY(0,1,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_MINUTES),  panel.CellX(0,1,1)+2, panel.CellY(0,1,1)+2,clrNONE,90);
   panel.DrawText("Open",  panel.CellX(0,2,0)+2, panel.CellY(0,2,0)+2); panel.DrawText(DoubleToString(rates[0].open,Digits()),      panel.CellX(0,2,1)+2, panel.CellY(0,2,1)+2,clrNONE,90);
   panel.DrawText("High",  panel.CellX(0,3,0)+2, panel.CellY(0,3,0)+2); panel.DrawText(DoubleToString(rates[0].high,Digits()),      panel.CellX(0,3,1)+2, panel.CellY(0,3,1)+2,clrNONE,90);
   panel.DrawText("Low",   panel.CellX(0,4,0)+2, panel.CellY(0,4,0)+2); panel.DrawText(DoubleToString(rates[0].low,Digits()),       panel.CellX(0,4,1)+2, panel.CellY(0,4,1)+2,clrNONE,90);
   panel.DrawText("Close", panel.CellX(0,5,0)+2, panel.CellY(0,5,0)+2); panel.DrawText(DoubleToString(rates[0].close,Digits()),     panel.CellX(0,5,1)+2, panel.CellY(0,5,1)+2,clrNONE,90);

//--- Выводим в таблицу 1 данные индикатора 1 с указанного бара в таблицу 1
   panel.DrawText(indicators.Title(handle_stddev1), panel.CellX(1,0,0)+2, panel.CellY(1,0,0)+2);
   double value1=indicators.GetData(handle_stddev1,0,0,index);
   string value_str1=(value1!=EMPTY_VALUE ? DoubleToString(value1,indicators.Digits(handle_stddev1)) : " ");
   panel.DrawText(value_str1,panel.CellX(1,0,1)+2,panel.CellY(1,0,1)+2,clrNONE,110);
   
//--- Выводим описание состояния линии индикатора 1
   panel.DrawText("Line state", panel.CellX(1,1,0)+2, panel.CellY(1,1,0)+2);
   ENUM_LINE_STATE state1=indicators.BufferLineState(Symbol(),PERIOD_CURRENT,handle_stddev1,0,0,index);
   panel.DrawText(BufferLineStateDescription(state1),panel.CellX(1,1,1)+2,panel.CellY(1,1,1)+2,clrNONE,110);
   
//--- Выводим в таблицу 2 данные индикатора 2 с указанного бара в таблицу 2
   panel.DrawText(indicators.Title(handle_stddev2), panel.CellX(2,0,0)+2, panel.CellY(2,0,0)+2);
   double value2=indicators.GetDataTo(Symbol(),PERIOD_CURRENT,handle_stddev2,0,0,index);
   string value_str2=(value2!=EMPTY_VALUE ? DoubleToString(value2,indicators.Digits(handle_stddev2)) : " ");
   panel.DrawText(value_str2,panel.CellX(2,0,1)+2,panel.CellY(2,0,1)+2,clrNONE,110);
   
//--- Выводим описание состояния линии индикатора 2
   panel.DrawText("Line state", panel.CellX(2,1,0)+2, panel.CellY(2,1,0)+2);
   ENUM_LINE_STATE state2=indicators.BufferLineState(Symbol(),PERIOD_CURRENT,handle_stddev2,0,0,index);
   panel.DrawText(BufferLineStateDescription(state2),panel.CellX(2,1,1)+2,panel.CellY(2,1,1)+2,clrNONE,110);
   
//--- Выводим описание соотношения линии индикатора 1 относительно линии индикатора 2
   double value21=indicators.GetDataTo(Symbol(),PERIOD_CURRENT,handle_stddev2,0,0,index+1);
   ENUM_LINE_STATE stateR=indicators.BufferLineStateRelative(Symbol(),PERIOD_CURRENT,handle_stddev1,0,0,index,value2,value21);
   string ma1=indicators.Name(handle_stddev1);
   string ma2=indicators.Name(handle_stddev2);
   string state_relative=
     (
      stateR==LINE_STATE_ABOVE      ? StringFormat("%s1 > %s2",ma1,ma2)   :
      stateR==LINE_STATE_BELOW      ? StringFormat("%s1 < %s2",ma1,ma2)           :
      stateR==LINE_STATE_CROSS_DOWN ? "Top-down crossing"   :
      stateR==LINE_STATE_CROSS_UP   ? "Bottom-up crossing"  :
      BufferLineStateDescription(stateR)
     );
   panel.DrawText(StringFormat("%s1 vs %s2",ma1,ma2), panel.CellX(2,2,0)+2, panel.CellY(2,2,0)+2);
   panel.DrawText(state_relative, panel.CellX(2,2,1)+2, panel.CellY(2,2,1)+2,clrNONE,110);
   
//--- Перерисовываем график для немедленного отображения всех изменений на панели
   ChartRedraw(ChartID());
  }
//+------------------------------------------------------------------+

После компиляции и запуска индикатора на графике М1 и выбранном периоде графика для расчёта М5, видим:



Индикатор Stochastic Odcillator:

//+------------------------------------------------------------------+
//|                                 TestMSTFStochasticOdcillator.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_separate_window
#property indicator_buffers 4
#property indicator_plots   4
//--- enums

//--- plot RVI1
#property indicator_label1  "RVI1"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrSeaGreen
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1

//--- plot Signal1
#property indicator_label2  "Signal1"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrOrangeRed
#property indicator_style2  STYLE_SOLID
#property indicator_width2  1

//--- plot RVI2
#property indicator_label3  "RVI2"
#property indicator_type3   DRAW_LINE
#property indicator_color3  clrGreen
#property indicator_style3  STYLE_SOLID
#property indicator_width3  1

//--- plot Signal2
#property indicator_label4  "Signal2"
#property indicator_type4   DRAW_LINE
#property indicator_color4  clrRed
#property indicator_style4  STYLE_SOLID
#property indicator_width4  1

//--- includes
#include <IndMSTF\IndMSTF.mqh>
#include <Dashboard\Dashboard.mqh>
//--- input parameters
input string               InpSymbol      =  NULL;             /* Symbol                     */ // Символ
input ENUM_TIMEFRAMES      InpTimeframe   =  PERIOD_CURRENT;   /* Timeframe                  */ // Таймфрейм
input int                  InpKPeriod     =  5;                /* %K Period                  */ // Период %K
input int                  InpDPeriod     =  3;                /* %D Period                  */ // Период %D
input int                  InpSlowing     =  3;                /* Slowing                    */ // Замедление
input ENUM_STO_PRICE       InpPrice       =  STO_LOWHIGH;      /* Applied Price              */ // Цена расчёта
input ENUM_MA_METHOD       InpMethod      =  MODE_SMA;         /* Method                     */ // Метод расчёта
input uchar                InpLineWidth1  =  2;                /* Senior period Line Width   */ // Толщина линии для старшего периода
input uchar                InpLineWidth2  =  1;                /* Junior period Line Width   */ // Толщина линии для младшего периода
input bool                 InpAsSeries    =  true;             /* As Series flag             */ // Флаг серийности массивов буферов индикатора

//--- indicator buffers
double         BufferStoch1[];
double         BufferSig1[];
double         BufferStoch2[];
double         BufferSig2[];
//--- global variables
int handle_stoch1;
int handle_stoch2;
CMSTFIndicators indicators;      // Экземпляр объекта коллекции индикаторов
//--- переменные для панели
CDashboard *panel=NULL;          // Указатель на объект панели
int         mouse_bar_index;     // Индекс бара, с которого берутся данные
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Устанавливаем таймер с периодичностью в 1 секунду
   EventSetTimer(1);
//--- Назначаем рисуемым буферам 0 и 1 массивы BufferMA1 и BufferMA2 соответственно
   SetIndexBuffer(0,BufferStoch1,INDICATOR_DATA);
   SetIndexBuffer(1,BufferSig1,INDICATOR_DATA);
   SetIndexBuffer(2,BufferStoch2,INDICATOR_DATA);
   SetIndexBuffer(3,BufferSig2,INDICATOR_DATA);
//--- Устанавливаем ширину линий
   int w1=0,w2=0;
   if(InpTimeframe>Period())
     {
      w1=InpLineWidth2;
      w2=InpLineWidth1;
     }
   else
     {
      w1=InpLineWidth1;
      w2=InpLineWidth2;
     }
   PlotIndexSetInteger(0,PLOT_LINE_WIDTH,w1);
   PlotIndexSetInteger(1,PLOT_LINE_WIDTH,w1);
   PlotIndexSetInteger(2,PLOT_LINE_WIDTH,w2);
   PlotIndexSetInteger(3,PLOT_LINE_WIDTH,w2);
//--- sets indicator shift

//--- Устанавливаем флаги серийности массивам буферов индикатора (для теста, чтобы видно было отсутствие разницы)
   ArraySetAsSeries(BufferStoch1,InpAsSeries);
   ArraySetAsSeries(BufferSig1,InpAsSeries);
   ArraySetAsSeries(BufferStoch2,InpAsSeries);
   ArraySetAsSeries(BufferSig2,InpAsSeries);
   
//--- Создаём два индикатора одного типа
//--- Первый рассчитывается на текущих символе/периоде графика, а второй - на тех, что заданы в настройках
   handle_stoch1=indicators.AddNewStochastic(NULL,PERIOD_CURRENT,InpKPeriod,InpDPeriod,InpSlowing,InpMethod,InpPrice);
   handle_stoch2=indicators.AddNewStochastic(InpSymbol,InpTimeframe,InpKPeriod,InpDPeriod,InpSlowing,InpMethod,InpPrice);

//--- Если не удалось создать хэндлы индикаторов - возвращаем ошибку инициализации
   if(handle_stoch1==INVALID_HANDLE || handle_stoch2==INVALID_HANDLE)
      return INIT_FAILED;
//--- Устанавливаем описания линий индикатора из описаний буферов расчётной части созданных индикаторов
   indicators.SetPlotLabelFromBuffer(0,handle_stoch1,0);
   indicators.SetPlotLabelFromBuffer(1,handle_stoch1,1);
   indicators.SetPlotLabelFromBuffer(2,handle_stoch2,0);
   indicators.SetPlotLabelFromBuffer(3,handle_stoch2,1);
      
//--- Панель
//--- Создаём панель
   int width=271;
   panel=new CDashboard(1,20,20,width,264,0);
   if(panel==NULL)
     {
      Print("Error. Failed to create panel object");
      return INIT_FAILED;
     }
//--- Устанавливаем параметры шрифта
   panel.SetFontParams("Calibri",9);
//--- Отображаем панель с текстом в заголовке "Символ, Описание таймфрейма"
   panel.View(Symbol()+", "+StringSubstr(EnumToString(Period()),7));

//--- Создаём таблицу с идентификатором 0 для отображения в ней данных бара
   panel.CreateNewTable(0);
//--- Рисуем таблицу с идентификатором 0 на фоне панели
   panel.DrawGrid(0,2,20,6,2,18,width/2-2);

//--- Создаём таблицу с идентификатором 1 для отображения в ней данных индикатора 1
   panel.CreateNewTable(1);
//--- Получаем координату Y2 таблицы с идентификатором 0 и
//--- устанавливаем координату Y1 для таблицы с идентификатором 1
   int y1=panel.TableY2(0)+22;
//--- Рисуем таблицу с идентификатором 1 на фоне панели
   panel.DrawGrid(1,2,y1,2,2,18,width/2-2);

//--- Создаём таблицу с идентификатором 2 для отображения в ней данных индикатора 2
   panel.CreateNewTable(2);
//--- Получаем координату Y2 таблицы с идентификатором 1 и
//--- устанавливаем координату Y1 для таблицы с идентификатором 2
   int y2=panel.TableY2(1)+3;
//--- Рисуем таблицу с идентификатором 2 на фоне панели
   panel.DrawGrid(2,2,y2,3,2,18,width/2-2);
   
//--- Инициализируем переменную с индексом бара указателя мышки
   mouse_bar_index=0;
//--- Выводим на панель данные текущего бара
   DrawData(mouse_bar_index,TimeCurrent());

//--- Успешная инициализация
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- Уничтожаем таймер
   EventKillTimer();
//--- Если объект панели существует - удаляем его
   if(panel!=NULL)
      delete panel;
//--- Стираем все комментарии
   Comment("");
  }
//+------------------------------------------------------------------+
//| 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[])
  {
//--- Количество баров для расчёта
   int limit=rates_total-prev_calculated;
//--- Если limit > 1, значит это первый расчёт, либо изменение в истории
   if(limit>1)
     {
      //--- указываем для просчёта всю доступную историю
      limit=rates_total-1;
      /*
      // Если в индикаторе есть какие-либо буферы, в которых отображены другие расчёты (не мульти- индикаторы),
      // то здесь их нужно инициализировать "пустым" значением, установленным для этих буферов
      */
     }
//--- Рассчитываем все созданные мультисимвольные мультипериодные индикаторы
   if(!indicators.Calculate())
      return 0;

//--- Выводим на панель данные бара под курсором (либо текущий бар, если курсор за пределами графика)
   DrawData(mouse_bar_index,time[mouse_bar_index]);

//--- Из буферов рассчитанных индикаторов выводим данные в индикаторные буферы
   if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_stoch1,0,0,limit,BufferStoch1))
      return 0;
   if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_stoch1,1,0,limit,BufferSig1))
      return 0;
      
   if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_stoch2,0,0,limit,BufferStoch2))
      return 0;
   if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_stoch2,1,0,limit,BufferSig2))
      return 0;
      
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| Timer function                                                   | 
//+------------------------------------------------------------------+
void OnTimer()
  {
//--- Вызываем таймер коллекции индикаторов
   indicators.OnTimer();
  }
//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- Работа с панелью
//--- Вызываем обработчик событий панели
   panel.OnChartEvent(id,lparam,dparam,sparam);

//--- Если курсор перемещается или щелчок по графику
   if(id==CHARTEVENT_MOUSE_MOVE || id==CHARTEVENT_CLICK)
     {
      //--- Объявляем переменные для записи в них координат времени и цены
      datetime time=0;
      double price=0;
      int wnd=0;
      //--- Если координаты курсора преобразованы в дату и время
      if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price))
        {
         //--- записываем индекс бара, где расположен курсор в глобальную переменную
         mouse_bar_index=iBarShift(Symbol(),PERIOD_CURRENT,time);
         //--- Выводим данные бара под курсором на панель
         DrawData(mouse_bar_index,time);
        }
     }

//--- Если получили пользовательское событие - выводим об этом сообщение в журнал
   if(id>CHARTEVENT_CUSTOM)
     {
      //--- Здесь может быть обработка щелчка по кнопке закрытия на панели
      PrintFormat("%s: Event id=%ld, object id (lparam): %lu, event message (sparam): %s",__FUNCTION__,id,lparam,sparam);
     }
  }
//+------------------------------------------------------------------+
//| Выводит данные с указанного индекса таймсерии на панель          |
//+------------------------------------------------------------------+
void DrawData(const int index,const datetime time)
  {
//--- Объявляем переменные для получения в них данных
   MqlRates rates[1];

//--- Если данные бара по указанному индексу получить не удалось - уходим
   if(CopyRates(Symbol(),PERIOD_CURRENT,index,1,rates)!=1)
      return;

//--- Устанавливаем параметры шрифта для заголовков данных бара и индикатора
   int  size=0;
   uint flags=0;
   uint angle=0;
   string name=panel.FontParams(size,flags,angle);
   panel.SetFontParams(name,9,FW_BOLD);
   panel.DrawText("Bar data ["+(string)index+"]",3,panel.TableY1(0)-16,clrMaroon,panel.Width()-6);
   panel.DrawText("Indicators data ["+(string)index+"]",3,panel.TableY1(1)-16,clrGreen,panel.Width()-6);
//--- Устанавливаем параметры шрифта для данных бара и индикатора
   panel.SetFontParams(name,9);

//--- Выводим на панель данные указанного бара в таблицу 0
   panel.DrawText("Date",  panel.CellX(0,0,0)+2, panel.CellY(0,0,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_DATE),     panel.CellX(0,0,1)+2, panel.CellY(0,0,1)+2,clrNONE,90);
   panel.DrawText("Time",  panel.CellX(0,1,0)+2, panel.CellY(0,1,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_MINUTES),  panel.CellX(0,1,1)+2, panel.CellY(0,1,1)+2,clrNONE,90);
   panel.DrawText("Open",  panel.CellX(0,2,0)+2, panel.CellY(0,2,0)+2); panel.DrawText(DoubleToString(rates[0].open,Digits()),      panel.CellX(0,2,1)+2, panel.CellY(0,2,1)+2,clrNONE,90);
   panel.DrawText("High",  panel.CellX(0,3,0)+2, panel.CellY(0,3,0)+2); panel.DrawText(DoubleToString(rates[0].high,Digits()),      panel.CellX(0,3,1)+2, panel.CellY(0,3,1)+2,clrNONE,90);
   panel.DrawText("Low",   panel.CellX(0,4,0)+2, panel.CellY(0,4,0)+2); panel.DrawText(DoubleToString(rates[0].low,Digits()),       panel.CellX(0,4,1)+2, panel.CellY(0,4,1)+2,clrNONE,90);
   panel.DrawText("Close", panel.CellX(0,5,0)+2, panel.CellY(0,5,0)+2); panel.DrawText(DoubleToString(rates[0].close,Digits()),     panel.CellX(0,5,1)+2, panel.CellY(0,5,1)+2,clrNONE,90);

//--- Выводим в таблицу 1 данные индикатора 1 с указанного бара в таблицу 1
   panel.DrawText(indicators.Title(handle_stoch1), panel.CellX(1,0,0)+2, panel.CellY(1,0,0)+2);
   double value1=indicators.GetData(handle_stoch1,0,0,index);
   string value_str1=(value1!=EMPTY_VALUE ? DoubleToString(value1,indicators.Digits(handle_stoch1)) : " ");
   panel.DrawText(value_str1,panel.CellX(1,0,1)+2,panel.CellY(1,0,1)+2,clrNONE,190);
   
//--- Выводим описание состояния линии индикатора 1
   panel.DrawText("Line state", panel.CellX(1,1,0)+2, panel.CellY(1,1,0)+2);
   ENUM_LINE_STATE state1=indicators.BufferLineState(Symbol(),PERIOD_CURRENT,handle_stoch1,0,0,index);
   panel.DrawText(BufferLineStateDescription(state1),panel.CellX(1,1,1)+2,panel.CellY(1,1,1)+2,clrNONE,190);
   
//--- Выводим в таблицу 2 данные индикатора 2 с указанного бара в таблицу 2
   panel.DrawText(indicators.Title(handle_stoch2), panel.CellX(2,0,0)+2, panel.CellY(2,0,0)+2);
   double value2=indicators.GetDataTo(Symbol(),PERIOD_CURRENT,handle_stoch2,0,0,index);
   string value_str2=(value2!=EMPTY_VALUE ? DoubleToString(value2,indicators.Digits(handle_stoch2)) : " ");
   panel.DrawText(value_str2,panel.CellX(2,0,1)+2,panel.CellY(2,0,1)+2,clrNONE,190);
   
//--- Выводим описание состояния линии индикатора 2
   panel.DrawText("Line state", panel.CellX(2,1,0)+2, panel.CellY(2,1,0)+2);
   ENUM_LINE_STATE state2=indicators.BufferLineState(Symbol(),PERIOD_CURRENT,handle_stoch2,0,0,index);
   panel.DrawText(BufferLineStateDescription(state2),panel.CellX(2,1,1)+2,panel.CellY(2,1,1)+2,clrNONE,190);
   
//--- Выводим описание соотношения линии индикатора 1 относительно линии индикатора 2
   double value21=indicators.GetDataTo(Symbol(),PERIOD_CURRENT,handle_stoch2,0,0,index+1);
   ENUM_LINE_STATE stateR=indicators.BufferLineStateRelative(Symbol(),PERIOD_CURRENT,handle_stoch1,0,0,index,value2,value21);
   string ma1=indicators.Name(handle_stoch1);
   string ma2=indicators.Name(handle_stoch2);
   string state_relative=
     (
      stateR==LINE_STATE_ABOVE      ? StringFormat("%s1 > %s2",ma1,ma2)   :
      stateR==LINE_STATE_BELOW      ? StringFormat("%s1 < %s2",ma1,ma2)           :
      stateR==LINE_STATE_CROSS_DOWN ? "Top-down crossing"   :
      stateR==LINE_STATE_CROSS_UP   ? "Bottom-up crossing"  :
      BufferLineStateDescription(stateR)
     );
   panel.DrawText(StringFormat("%s1 vs %s2",ma1,ma2), panel.CellX(2,2,0)+2, panel.CellY(2,2,0)+2);
   panel.DrawText(state_relative, panel.CellX(2,2,1)+2, panel.CellY(2,2,1)+2,clrNONE,190);
   
//--- Перерисовываем график для немедленного отображения всех изменений на панели
   ChartRedraw(ChartID());
  }
//+------------------------------------------------------------------+

После компиляции и запуска индикатора на графике М1 и выбранном периоде графика для расчёта М5, видим:



Индикатор Triple Exponential Average:

//+------------------------------------------------------------------+
//|                             TestMSTFTripleExponentialAverage.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_separate_window
#property indicator_buffers 2
#property indicator_plots   2
//--- enums

//--- plot TRIX1
#property indicator_label1  "TRIX1"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrRed
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1

//--- plot TRIX2
#property indicator_label2  "TRIX2"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrDodgerBlue
#property indicator_style2  STYLE_SOLID
#property indicator_width2  1

//--- includes
#include <IndMSTF\IndMSTF.mqh>
#include <Dashboard\Dashboard.mqh>
//--- input parameters
input string               InpSymbol      =  NULL;             /* Symbol                     */ // Символ
input ENUM_TIMEFRAMES      InpTimeframe   =  PERIOD_CURRENT;   /* Timeframe                  */ // Таймфрейм
input int                  InpPeriod      =  13;               /* Period                     */ // Период расчёта
input ENUM_APPLIED_PRICE   InpPrice       =  PRICE_CLOSE;      /* Applied Price              */ // Цена расчёта
input uchar                InpLineWidth1  =  2;                /* Senior period Line Width   */ // Толщина линии для старшего периода
input uchar                InpLineWidth2  =  1;                /* Junior period Line Width   */ // Толщина линии для младшего периода
input bool                 InpAsSeries    =  true;             /* As Series flag             */ // Флаг серийности массивов буферов индикатора

//--- indicator buffers
double         BufferTRIX1[];
double         BufferTRIX2[];
//--- global variables
int handle_trix1;
int handle_trix2;
CMSTFIndicators indicators;      // Экземпляр объекта коллекции индикаторов
//--- переменные для панели
CDashboard *panel=NULL;          // Указатель на объект панели
int         mouse_bar_index;     // Индекс бара, с которого берутся данные
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Устанавливаем таймер с периодичностью в 1 секунду
   EventSetTimer(1);
//--- Назначаем рисуемым буферам 0 и 1 массивы BufferMA1 и BufferMA2 соответственно
   SetIndexBuffer(0,BufferTRIX1,INDICATOR_DATA);
   SetIndexBuffer(1,BufferTRIX2,INDICATOR_DATA);
//--- Устанавливаем ширину линий
   int w1=0,w2=0;
   if(InpTimeframe>Period())
     {
      w1=InpLineWidth2;
      w2=InpLineWidth1;
     }
   else
     {
      w1=InpLineWidth1;
      w2=InpLineWidth2;
     }
   PlotIndexSetInteger(0,PLOT_LINE_WIDTH,w1);
   PlotIndexSetInteger(1,PLOT_LINE_WIDTH,w2);
//--- sets indicator shift

//--- Устанавливаем флаги серийности массивам буферов индикатора (для теста, чтобы видно было отсутствие разницы)
   ArraySetAsSeries(BufferTRIX1,InpAsSeries);
   ArraySetAsSeries(BufferTRIX2,InpAsSeries);
   
//--- Создаём два индикатора одного типа
//--- Первый рассчитывается на текущих символе/периоде графика, а второй - на тех, что заданы в настройках
   handle_trix1=indicators.AddNewTriX(NULL,PERIOD_CURRENT,InpPeriod,InpPrice);
   handle_trix2=indicators.AddNewTriX(InpSymbol,InpTimeframe,InpPeriod,InpPrice);

//--- Если не удалось создать хэндлы индикаторов - возвращаем ошибку инициализации
   if(handle_trix1==INVALID_HANDLE || handle_trix2==INVALID_HANDLE)
      return INIT_FAILED;
//--- Устанавливаем описания линий индикатора из описаний буферов расчётной части созданных индикаторов
   indicators.SetPlotLabelFromBuffer(0,handle_trix1,0);
   indicators.SetPlotLabelFromBuffer(1,handle_trix2,0);
      
//--- Панель
//--- Создаём панель
   int width=247;
   panel=new CDashboard(1,20,20,width,264,0);
   if(panel==NULL)
     {
      Print("Error. Failed to create panel object");
      return INIT_FAILED;
     }
//--- Устанавливаем параметры шрифта
   panel.SetFontParams("Calibri",9);
//--- Отображаем панель с текстом в заголовке "Символ, Описание таймфрейма"
   panel.View(Symbol()+", "+StringSubstr(EnumToString(Period()),7));

//--- Создаём таблицу с идентификатором 0 для отображения в ней данных бара
   panel.CreateNewTable(0);
//--- Рисуем таблицу с идентификатором 0 на фоне панели
   panel.DrawGrid(0,2,20,6,2,18,width/2-2);

//--- Создаём таблицу с идентификатором 1 для отображения в ней данных индикатора 1
   panel.CreateNewTable(1);
//--- Получаем координату Y2 таблицы с идентификатором 0 и
//--- устанавливаем координату Y1 для таблицы с идентификатором 1
   int y1=panel.TableY2(0)+22;
//--- Рисуем таблицу с идентификатором 1 на фоне панели
   panel.DrawGrid(1,2,y1,2,2,18,width/2-2);

//--- Создаём таблицу с идентификатором 2 для отображения в ней данных индикатора 2
   panel.CreateNewTable(2);
//--- Получаем координату Y2 таблицы с идентификатором 1 и
//--- устанавливаем координату Y1 для таблицы с идентификатором 2
   int y2=panel.TableY2(1)+3;
//--- Рисуем таблицу с идентификатором 2 на фоне панели
   panel.DrawGrid(2,2,y2,3,2,18,width/2-2);
   
//--- Инициализируем переменную с индексом бара указателя мышки
   mouse_bar_index=0;
//--- Выводим на панель данные текущего бара
   DrawData(mouse_bar_index,TimeCurrent());

//--- Успешная инициализация
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- Уничтожаем таймер
   EventKillTimer();
//--- Если объект панели существует - удаляем его
   if(panel!=NULL)
      delete panel;
//--- Стираем все комментарии
   Comment("");
  }
//+------------------------------------------------------------------+
//| 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[])
  {
//--- Количество баров для расчёта
   int limit=rates_total-prev_calculated;
//--- Если limit > 1, значит это первый расчёт, либо изменение в истории
   if(limit>1)
     {
      //--- указываем для просчёта всю доступную историю
      limit=rates_total-1;
      /*
      // Если в индикаторе есть какие-либо буферы, в которых отображены другие расчёты (не мульти- индикаторы),
      // то здесь их нужно инициализировать "пустым" значением, установленным для этих буферов
      */
     }
//--- Рассчитываем все созданные мультисимвольные мультипериодные индикаторы
   if(!indicators.Calculate())
      return 0;

//--- Выводим на панель данные бара под курсором (либо текущий бар, если курсор за пределами графика)
   DrawData(mouse_bar_index,time[mouse_bar_index]);

//--- Из буферов рассчитанных индикаторов выводим данные в индикаторные буферы
   if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_trix1,0,0,limit,BufferTRIX1))
      return 0;
   if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_trix2,0,0,limit,BufferTRIX2))
      return 0;

//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| Timer function                                                   | 
//+------------------------------------------------------------------+
void OnTimer()
  {
//--- Вызываем таймер коллекции индикаторов
   indicators.OnTimer();
  }
//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- Работа с панелью
//--- Вызываем обработчик событий панели
   panel.OnChartEvent(id,lparam,dparam,sparam);

//--- Если курсор перемещается или щелчок по графику
   if(id==CHARTEVENT_MOUSE_MOVE || id==CHARTEVENT_CLICK)
     {
      //--- Объявляем переменные для записи в них координат времени и цены
      datetime time=0;
      double price=0;
      int wnd=0;
      //--- Если координаты курсора преобразованы в дату и время
      if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price))
        {
         //--- записываем индекс бара, где расположен курсор в глобальную переменную
         mouse_bar_index=iBarShift(Symbol(),PERIOD_CURRENT,time);
         //--- Выводим данные бара под курсором на панель
         DrawData(mouse_bar_index,time);
        }
     }

//--- Если получили пользовательское событие - выводим об этом сообщение в журнал
   if(id>CHARTEVENT_CUSTOM)
     {
      //--- Здесь может быть обработка щелчка по кнопке закрытия на панели
      PrintFormat("%s: Event id=%ld, object id (lparam): %lu, event message (sparam): %s",__FUNCTION__,id,lparam,sparam);
     }
  }
//+------------------------------------------------------------------+
//| Выводит данные с указанного индекса таймсерии на панель          |
//+------------------------------------------------------------------+
void DrawData(const int index,const datetime time)
  {
//--- Объявляем переменные для получения в них данных
   MqlRates rates[1];

//--- Если данные бара по указанному индексу получить не удалось - уходим
   if(CopyRates(Symbol(),PERIOD_CURRENT,index,1,rates)!=1)
      return;

//--- Устанавливаем параметры шрифта для заголовков данных бара и индикатора
   int  size=0;
   uint flags=0;
   uint angle=0;
   string name=panel.FontParams(size,flags,angle);
   panel.SetFontParams(name,9,FW_BOLD);
   panel.DrawText("Bar data ["+(string)index+"]",3,panel.TableY1(0)-16,clrMaroon,panel.Width()-6);
   panel.DrawText("Indicators data ["+(string)index+"]",3,panel.TableY1(1)-16,clrGreen,panel.Width()-6);
//--- Устанавливаем параметры шрифта для данных бара и индикатора
   panel.SetFontParams(name,9);

//--- Выводим на панель данные указанного бара в таблицу 0
   panel.DrawText("Date",  panel.CellX(0,0,0)+2, panel.CellY(0,0,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_DATE),     panel.CellX(0,0,1)+2, panel.CellY(0,0,1)+2,clrNONE,90);
   panel.DrawText("Time",  panel.CellX(0,1,0)+2, panel.CellY(0,1,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_MINUTES),  panel.CellX(0,1,1)+2, panel.CellY(0,1,1)+2,clrNONE,90);
   panel.DrawText("Open",  panel.CellX(0,2,0)+2, panel.CellY(0,2,0)+2); panel.DrawText(DoubleToString(rates[0].open,Digits()),      panel.CellX(0,2,1)+2, panel.CellY(0,2,1)+2,clrNONE,90);
   panel.DrawText("High",  panel.CellX(0,3,0)+2, panel.CellY(0,3,0)+2); panel.DrawText(DoubleToString(rates[0].high,Digits()),      panel.CellX(0,3,1)+2, panel.CellY(0,3,1)+2,clrNONE,90);
   panel.DrawText("Low",   panel.CellX(0,4,0)+2, panel.CellY(0,4,0)+2); panel.DrawText(DoubleToString(rates[0].low,Digits()),       panel.CellX(0,4,1)+2, panel.CellY(0,4,1)+2,clrNONE,90);
   panel.DrawText("Close", panel.CellX(0,5,0)+2, panel.CellY(0,5,0)+2); panel.DrawText(DoubleToString(rates[0].close,Digits()),     panel.CellX(0,5,1)+2, panel.CellY(0,5,1)+2,clrNONE,90);

//--- Выводим в таблицу 1 данные индикатора 1 с указанного бара в таблицу 1
   panel.DrawText(indicators.Title(handle_trix1), panel.CellX(1,0,0)+2, panel.CellY(1,0,0)+2);
   double value1=indicators.GetData(handle_trix1,0,0,index);
   string value_str1=(value1!=EMPTY_VALUE ? DoubleToString(value1,indicators.Digits(handle_trix1)) : " ");
   panel.DrawText(value_str1,panel.CellX(1,0,1)+2,panel.CellY(1,0,1)+2,clrNONE,110);
   
//--- Выводим описание состояния линии индикатора 1
   panel.DrawText("Line state", panel.CellX(1,1,0)+2, panel.CellY(1,1,0)+2);
   ENUM_LINE_STATE state1=indicators.BufferLineState(Symbol(),PERIOD_CURRENT,handle_trix1,0,0,index);
   panel.DrawText(BufferLineStateDescription(state1),panel.CellX(1,1,1)+2,panel.CellY(1,1,1)+2,clrNONE,110);
   
//--- Выводим в таблицу 2 данные индикатора 2 с указанного бара в таблицу 2
   panel.DrawText(indicators.Title(handle_trix2), panel.CellX(2,0,0)+2, panel.CellY(2,0,0)+2);
   double value2=indicators.GetDataTo(Symbol(),PERIOD_CURRENT,handle_trix2,0,0,index);
   string value_str2=(value2!=EMPTY_VALUE ? DoubleToString(value2,indicators.Digits(handle_trix2)) : " ");
   panel.DrawText(value_str2,panel.CellX(2,0,1)+2,panel.CellY(2,0,1)+2,clrNONE,110);
   
//--- Выводим описание состояния линии индикатора 2
   panel.DrawText("Line state", panel.CellX(2,1,0)+2, panel.CellY(2,1,0)+2);
   ENUM_LINE_STATE state2=indicators.BufferLineState(Symbol(),PERIOD_CURRENT,handle_trix2,0,0,index);
   panel.DrawText(BufferLineStateDescription(state2),panel.CellX(2,1,1)+2,panel.CellY(2,1,1)+2,clrNONE,110);
   
//--- Выводим описание соотношения линии индикатора 1 относительно линии индикатора 2
   double value21=indicators.GetDataTo(Symbol(),PERIOD_CURRENT,handle_trix2,0,0,index+1);
   ENUM_LINE_STATE stateR=indicators.BufferLineStateRelative(Symbol(),PERIOD_CURRENT,handle_trix1,0,0,index,value2,value21);
   string ma1=indicators.Name(handle_trix1);
   string ma2=indicators.Name(handle_trix2);
   string state_relative=
     (
      stateR==LINE_STATE_ABOVE      ? StringFormat("%s1 > %s2",ma1,ma2)   :
      stateR==LINE_STATE_BELOW      ? StringFormat("%s1 < %s2",ma1,ma2)           :
      stateR==LINE_STATE_CROSS_DOWN ? "Top-down crossing"   :
      stateR==LINE_STATE_CROSS_UP   ? "Bottom-up crossing"  :
      BufferLineStateDescription(stateR)
     );
   panel.DrawText(StringFormat("%s1 vs %s2",ma1,ma2), panel.CellX(2,2,0)+2, panel.CellY(2,2,0)+2);
   panel.DrawText(state_relative, panel.CellX(2,2,1)+2, panel.CellY(2,2,1)+2,clrNONE,110);
   
//--- Перерисовываем график для немедленного отображения всех изменений на панели
   ChartRedraw(ChartID());
  }
//+------------------------------------------------------------------+

После компиляции и запуска индикатора на графике М1 и выбранном периоде графика для расчёта М5, видим:



Индикатор Volumes:

//+------------------------------------------------------------------+
//|                                              TestMSTFVolumes.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_separate_window
#property indicator_buffers 4
#property indicator_plots   2
//--- enums

//--- plot AC1
#property indicator_label1  "Volumes1"
#property indicator_type1   DRAW_COLOR_HISTOGRAM
//#property indicator_color1  clrGreen,clrRed // аналог в стр.98
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1

//--- plot AC2
#property indicator_label2  "Volumes2"
#property indicator_type2   DRAW_COLOR_HISTOGRAM
//#property indicator_color2  clrGreen,clrRed // аналог в стр.99
#property indicator_style2  STYLE_SOLID
#property indicator_width2  1

//--- includes
#include <IndMSTF\IndMSTF.mqh>
#include <Dashboard\Dashboard.mqh>
//--- input parameters
input string               InpSymbol      =  NULL;             /* Symbol                     */ // Символ
input ENUM_TIMEFRAMES      InpTimeframe   =  PERIOD_CURRENT;   /* Timeframe                  */ // Таймфрейм
input ENUM_APPLIED_VOLUME  InpVolume      =  VOLUME_TICK;      /* Applied Volume             */ // Используемый объём
input uchar                InpLineWidth1  =  2;                /* Senior period Line Width   */ // Толщина линии для старшего периода
input uchar                InpLineWidth2  =  1;                /* Junior period Line Width   */ // Толщина линии для младшего периода
input bool                 InpAsSeries    =  true;             /* As Series flag             */ // Флаг серийности массивов буферов индикатора

//--- indicator buffers
double         BufferVolumes1[];
double         BufferClrVolumes1[];
double         BufferVolumes2[];
double         BufferClrVolumes2[];
//--- global variables
int handle_volumes1;
int handle_volumes2;
CMSTFIndicators indicators;      // Экземпляр объекта коллекции индикаторов
//--- переменные для панели
CDashboard *panel=NULL;          // Указатель на объект панели
int         mouse_bar_index;     // Индекс бара, с которого берутся данные
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Устанавливаем таймер с периодичностью в 1 секунду
   EventSetTimer(1);
//--- Назначаем рисуемым буферам 0 и 2 массивы BufferVolumes1 и BufferVolumes2 соответственно,
//--- а буферам 1 и 3 массивы цвета BufferClrVolumes1 и BufferClrVolumes2
   SetIndexBuffer(0,BufferVolumes1,INDICATOR_DATA);
   SetIndexBuffer(1,BufferClrVolumes1,INDICATOR_COLOR_INDEX);
   SetIndexBuffer(2,BufferVolumes2,INDICATOR_DATA);
   SetIndexBuffer(3,BufferClrVolumes2,INDICATOR_COLOR_INDEX);
//--- Устанавливаем ширину линий
   int w1=0,w2=0;
   if(InpTimeframe>Period())
     {
      w1=InpLineWidth2;
      w2=InpLineWidth1;
     }
   else
     {
      w1=InpLineWidth1;
      w2=InpLineWidth2;
     }
   PlotIndexSetInteger(0,PLOT_LINE_WIDTH,w1);
   PlotIndexSetInteger(1,PLOT_LINE_WIDTH,w2);
//--- sets indicator shift

//--- Устанавливаем флаги серийности массивам буферов индикатора (для теста, чтобы видно было отсутствие разницы)
   ArraySetAsSeries(BufferVolumes1,InpAsSeries);
   ArraySetAsSeries(BufferClrVolumes1,InpAsSeries);
   ArraySetAsSeries(BufferVolumes2,InpAsSeries);
   ArraySetAsSeries(BufferClrVolumes2,InpAsSeries);
   
//--- Создаём два индикатора одного типа
//--- Первый рассчитывается на текущих символе/периоде графика, а второй - на тех, что заданы в настройках
   handle_volumes1=indicators.AddNewVolumes(NULL,PERIOD_CURRENT,InpVolume);
   handle_volumes2=indicators.AddNewVolumes(InpSymbol,InpTimeframe,InpVolume);

//--- Если не удалось создать хэндлы индикаторов - возвращаем ошибку инициализации
   if(handle_volumes1==INVALID_HANDLE || handle_volumes2==INVALID_HANDLE)
      return INIT_FAILED;
//--- Устанавливаем описания линий индикаторов из описаний буферов расчётной части созданных индикаторов
   indicators.SetPlotLabelFromBuffer(0,handle_volumes1,0);
   indicators.SetPlotLabelFromBuffer(1,handle_volumes2,0);
//--- Устанавливаем цвета линиям индикаторов из набора цветов буферов расчётной части созданных индикаторов
   indicators.SetPlotColorsFromBuffer(0,handle_volumes1,0);
   indicators.SetPlotColorsFromBuffer(1,handle_volumes2,0);
      
//--- Панель
//--- Создаём панель
   int width=267;
   panel=new CDashboard(1,20,20,width,264,0);
   if(panel==NULL)
     {
      Print("Error. Failed to create panel object");
      return INIT_FAILED;
     }
//--- Устанавливаем параметры шрифта
   panel.SetFontParams("Calibri",9);
//--- Отображаем панель с текстом в заголовке "Символ, Описание таймфрейма"
   panel.View(Symbol()+", "+StringSubstr(EnumToString(Period()),7));

//--- Создаём таблицу с идентификатором 0 для отображения в ней данных бара
   panel.CreateNewTable(0);
//--- Рисуем таблицу с идентификатором 0 на фоне панели
   panel.DrawGrid(0,2,20,6,2,18,width/2-2);

//--- Создаём таблицу с идентификатором 1 для отображения в ней данных индикатора 1
   panel.CreateNewTable(1);
//--- Получаем координату Y2 таблицы с идентификатором 0 и
//--- устанавливаем координату Y1 для таблицы с идентификатором 1
   int y1=panel.TableY2(0)+22;
//--- Рисуем таблицу с идентификатором 1 на фоне панели
   panel.DrawGrid(1,2,y1,2,2,18,width/2-2);

//--- Создаём таблицу с идентификатором 2 для отображения в ней данных индикатора 2
   panel.CreateNewTable(2);
//--- Получаем координату Y2 таблицы с идентификатором 1 и
//--- устанавливаем координату Y1 для таблицы с идентификатором 2
   int y2=panel.TableY2(1)+3;
//--- Рисуем таблицу с идентификатором 2 на фоне панели
   panel.DrawGrid(2,2,y2,3,2,18,width/2-2);
   
//--- Инициализируем переменную с индексом бара указателя мышки
   mouse_bar_index=0;
//--- Выводим на панель данные текущего бара
   DrawData(mouse_bar_index,TimeCurrent());

//--- Успешная инициализация
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- Уничтожаем таймер
   EventKillTimer();
//--- Если объект панели существует - удаляем его
   if(panel!=NULL)
      delete panel;
//--- Стираем все комментарии
   Comment("");
  }
//+------------------------------------------------------------------+
//| 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[])
  {
//--- Количество баров для расчёта
   int limit=rates_total-prev_calculated;
//--- Если limit > 1, значит это первый расчёт, либо изменение в истории
   if(limit>1)
     {
      //--- указываем для просчёта всю доступную историю
      limit=rates_total-1;
      /*
      // Если в индикаторе есть какие-либо буферы, в которых отображены другие расчёты (не мульти- индикаторы),
      // то здесь их нужно инициализировать "пустым" значением, установленным для этих буферов
      */
     }
//--- Рассчитываем все созданные мультисимвольные мультипериодные индикаторы
   if(!indicators.Calculate())
      return 0;

//--- Выводим на панель данные бара под курсором (либо текущий бар, если курсор за пределами графика)
   DrawData(mouse_bar_index,time[mouse_bar_index]);

//--- Из буферов рассчитанных индикаторов выводим данные в индикаторные буферы
   if(!indicators.DataToColorBuffer(NULL,PERIOD_CURRENT,handle_volumes1,0,0,limit,BufferVolumes1,BufferClrVolumes1))
      return 0;
   if(!indicators.DataToColorBuffer(NULL,PERIOD_CURRENT,handle_volumes2,0,0,limit,BufferVolumes2,BufferClrVolumes2))
      return 0;

//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| Timer function                                                   | 
//+------------------------------------------------------------------+
void OnTimer()
  {
//--- Вызываем таймер коллекции индикаторов
   indicators.OnTimer();
  }
//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- Работа с панелью
//--- Вызываем обработчик событий панели
   panel.OnChartEvent(id,lparam,dparam,sparam);

//--- Если курсор перемещается или щелчок по графику
   if(id==CHARTEVENT_MOUSE_MOVE || id==CHARTEVENT_CLICK)
     {
      //--- Объявляем переменные для записи в них координат времени и цены
      datetime time=0;
      double price=0;
      int wnd=0;
      //--- Если координаты курсора преобразованы в дату и время
      if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price))
        {
         //--- записываем индекс бара, где расположен курсор в глобальную переменную
         mouse_bar_index=iBarShift(Symbol(),PERIOD_CURRENT,time);
         //--- Выводим данные бара под курсором на панель
         DrawData(mouse_bar_index,time);
        }
     }

//--- Если получили пользовательское событие - выводим об этом сообщение в журнал
   if(id>CHARTEVENT_CUSTOM)
     {
      //--- Здесь может быть обработка щелчка по кнопке закрытия на панели
      PrintFormat("%s: Event id=%ld, object id (lparam): %lu, event message (sparam): %s",__FUNCTION__,id,lparam,sparam);
     }
  }
//+------------------------------------------------------------------+
//| Выводит данные с указанного индекса таймсерии на панель          |
//+------------------------------------------------------------------+
void DrawData(const int index,const datetime time)
  {
//--- Объявляем переменные для получения в них данных
   MqlRates rates[1];

//--- Если данные бара по указанному индексу получить не удалось - уходим
   if(CopyRates(Symbol(),PERIOD_CURRENT,index,1,rates)!=1)
      return;

//--- Устанавливаем параметры шрифта для заголовков данных бара и индикатора
   int  size=0;
   uint flags=0;
   uint angle=0;
   string name=panel.FontParams(size,flags,angle);
   panel.SetFontParams(name,9,FW_BOLD);
   panel.DrawText("Bar data ["+(string)index+"]",3,panel.TableY1(0)-16,clrMaroon,panel.Width()-6);
   panel.DrawText("Indicators data ["+(string)index+"]",3,panel.TableY1(1)-16,clrGreen,panel.Width()-6);
//--- Устанавливаем параметры шрифта для данных бара и индикатора
   panel.SetFontParams(name,9);

//--- Выводим на панель данные указанного бара в таблицу 0
   panel.DrawText("Date",  panel.CellX(0,0,0)+2, panel.CellY(0,0,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_DATE),     panel.CellX(0,0,1)+2, panel.CellY(0,0,1)+2,clrNONE,90);
   panel.DrawText("Time",  panel.CellX(0,1,0)+2, panel.CellY(0,1,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_MINUTES),  panel.CellX(0,1,1)+2, panel.CellY(0,1,1)+2,clrNONE,90);
   panel.DrawText("Open",  panel.CellX(0,2,0)+2, panel.CellY(0,2,0)+2); panel.DrawText(DoubleToString(rates[0].open,Digits()),      panel.CellX(0,2,1)+2, panel.CellY(0,2,1)+2,clrNONE,90);
   panel.DrawText("High",  panel.CellX(0,3,0)+2, panel.CellY(0,3,0)+2); panel.DrawText(DoubleToString(rates[0].high,Digits()),      panel.CellX(0,3,1)+2, panel.CellY(0,3,1)+2,clrNONE,90);
   panel.DrawText("Low",   panel.CellX(0,4,0)+2, panel.CellY(0,4,0)+2); panel.DrawText(DoubleToString(rates[0].low,Digits()),       panel.CellX(0,4,1)+2, panel.CellY(0,4,1)+2,clrNONE,90);
   panel.DrawText("Close", panel.CellX(0,5,0)+2, panel.CellY(0,5,0)+2); panel.DrawText(DoubleToString(rates[0].close,Digits()),     panel.CellX(0,5,1)+2, panel.CellY(0,5,1)+2,clrNONE,90);

//--- Выводим в таблицу 1 данные индикатора 1 с указанного бара в таблицу 1
   panel.DrawText(indicators.Title(handle_volumes1), panel.CellX(1,0,0)+2, panel.CellY(1,0,0)+2);
   double value1=indicators.GetData(handle_volumes1,0,0,index);
   string value_str1=(value1!=EMPTY_VALUE ? DoubleToString(value1,indicators.Digits(handle_volumes1)) : " ");
   panel.DrawText(value_str1,panel.CellX(1,0,1)+2,panel.CellY(1,0,1)+2,clrNONE,120);
   
//--- Выводим описание состояния линии индикатора 1
   panel.DrawText("Line state", panel.CellX(1,1,0)+2, panel.CellY(1,1,0)+2);
   ENUM_LINE_STATE state1=indicators.BufferLineState(Symbol(),PERIOD_CURRENT,handle_volumes1,0,0,index);
   panel.DrawText(BufferLineStateDescription(state1),panel.CellX(1,1,1)+2,panel.CellY(1,1,1)+2,clrNONE,120);
   
//--- Выводим в таблицу 2 данные индикатора 2 с указанного бара в таблицу 2
   panel.DrawText(indicators.Title(handle_volumes2), panel.CellX(2,0,0)+2, panel.CellY(2,0,0)+2);
   double value2=indicators.GetDataTo(Symbol(),PERIOD_CURRENT,handle_volumes2,0,0,index);
   string value_str2=(value2!=EMPTY_VALUE ? DoubleToString(value2,indicators.Digits(handle_volumes2)) : " ");
   panel.DrawText(value_str2,panel.CellX(2,0,1)+2,panel.CellY(2,0,1)+2,clrNONE,120);
   
//--- Выводим описание состояния линии индикатора 2
   panel.DrawText("Line state", panel.CellX(2,1,0)+2, panel.CellY(2,1,0)+2);
   ENUM_LINE_STATE state2=indicators.BufferLineState(Symbol(),PERIOD_CURRENT,handle_volumes2,0,0,index);
   panel.DrawText(BufferLineStateDescription(state2),panel.CellX(2,1,1)+2,panel.CellY(2,1,1)+2,clrNONE,120);
   
//--- Выводим описание соотношения линии индикатора 1 относительно линии индикатора 2
   double value21=indicators.GetDataTo(Symbol(),PERIOD_CURRENT,handle_volumes2,0,0,index+1);
   ENUM_LINE_STATE stateR=indicators.BufferLineStateRelative(Symbol(),PERIOD_CURRENT,handle_volumes1,0,0,index,value2,value21);
   string ma1=indicators.Name(handle_volumes1);
   string ma2=indicators.Name(handle_volumes2);
   string state_relative=
     (
      stateR==LINE_STATE_ABOVE      ? StringFormat("%s1 > %s2",ma1,ma2)   :
      stateR==LINE_STATE_BELOW      ? StringFormat("%s1 < %s2",ma1,ma2)           :
      stateR==LINE_STATE_CROSS_DOWN ? "Top-down crossing"   :
      stateR==LINE_STATE_CROSS_UP   ? "Bottom-up crossing"  :
      BufferLineStateDescription(stateR)
     );
   panel.DrawText(StringFormat("%s1 vs %s2",ma1,ma2), panel.CellX(2,2,0)+2, panel.CellY(2,2,0)+2);
   panel.DrawText(state_relative, panel.CellX(2,2,1)+2, panel.CellY(2,2,1)+2,clrNONE,120);
   
//--- Перерисовываем график для немедленного отображения всех изменений на панели
   ChartRedraw(ChartID());
  }
//+------------------------------------------------------------------+

После компиляции и запуска индикатора на графике М1 и выбранном периоде графика для расчёта М5, видим:



Индикатор Williams Percent Range:

//+------------------------------------------------------------------+
//|                                 TestMSTFWilliamsPercentRange.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_separate_window
#property indicator_buffers 2
#property indicator_plots   2
//--- enums

//--- plot WPR1
#property indicator_label1  "WPR1"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrRed
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1

//--- plot WPR2
#property indicator_label2  "WPR2"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrDodgerBlue
#property indicator_style2  STYLE_SOLID
#property indicator_width2  1

//--- includes
#include <IndMSTF\IndMSTF.mqh>
#include <Dashboard\Dashboard.mqh>
//--- input parameters
input string               InpSymbol      =  NULL;             /* Symbol                     */ // Символ
input ENUM_TIMEFRAMES      InpTimeframe   =  PERIOD_CURRENT;   /* Timeframe                  */ // Таймфрейм
input int                  InpPeriod      =  14;               /* Period                     */ // Период расчёта
input uchar                InpLineWidth1  =  2;                /* Senior period Line Width   */ // Толщина линии для старшего периода
input uchar                InpLineWidth2  =  1;                /* Junior period Line Width   */ // Толщина линии для младшего периода
input bool                 InpAsSeries    =  true;             /* As Series flag             */ // Флаг серийности массивов буферов индикатора

//--- indicator buffers
double         BufferWPR1[];
double         BufferWPR2[];
//--- global variables
int handle_wpr1;
int handle_wpr2;
CMSTFIndicators indicators;      // Экземпляр объекта коллекции индикаторов
//--- переменные для панели
CDashboard *panel=NULL;          // Указатель на объект панели
int         mouse_bar_index;     // Индекс бара, с которого берутся данные
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Устанавливаем таймер с периодичностью в 1 секунду
   EventSetTimer(1);
//--- Назначаем рисуемым буферам 0 и 1 массивы BufferMA1 и BufferMA2 соответственно
   SetIndexBuffer(0,BufferWPR1,INDICATOR_DATA);
   SetIndexBuffer(1,BufferWPR2,INDICATOR_DATA);
//--- Устанавливаем ширину линий
   int w1=0,w2=0;
   if(InpTimeframe>Period())
     {
      w1=InpLineWidth2;
      w2=InpLineWidth1;
     }
   else
     {
      w1=InpLineWidth1;
      w2=InpLineWidth2;
     }
   PlotIndexSetInteger(0,PLOT_LINE_WIDTH,w1);
   PlotIndexSetInteger(1,PLOT_LINE_WIDTH,w2);
//--- sets indicator shift

//--- Устанавливаем флаги серийности массивам буферов индикатора (для теста, чтобы видно было отсутствие разницы)
   ArraySetAsSeries(BufferWPR1,InpAsSeries);
   ArraySetAsSeries(BufferWPR2,InpAsSeries);
   
//--- Создаём два индикатора одного типа
//--- Первый рассчитывается на текущих символе/периоде графика, а второй - на тех, что заданы в настройках
   handle_wpr1=indicators.AddNewWPR(NULL,PERIOD_CURRENT,InpPeriod);
   handle_wpr2=indicators.AddNewWPR(InpSymbol,InpTimeframe,InpPeriod);

//--- Если не удалось создать хэндлы индикаторов - возвращаем ошибку инициализации
   if(handle_wpr1==INVALID_HANDLE || handle_wpr2==INVALID_HANDLE)
      return INIT_FAILED;
//--- Устанавливаем описания линий индикатора из описаний буферов расчётной части созданных индикаторов
   indicators.SetPlotLabelFromBuffer(0,handle_wpr1,0);
   indicators.SetPlotLabelFromBuffer(1,handle_wpr2,0);
      
//--- Панель
//--- Создаём панель
   int width=231;
   panel=new CDashboard(1,20,20,width,264,0);
   if(panel==NULL)
     {
      Print("Error. Failed to create panel object");
      return INIT_FAILED;
     }
//--- Устанавливаем параметры шрифта
   panel.SetFontParams("Calibri",9);
//--- Отображаем панель с текстом в заголовке "Символ, Описание таймфрейма"
   panel.View(Symbol()+", "+StringSubstr(EnumToString(Period()),7));

//--- Создаём таблицу с идентификатором 0 для отображения в ней данных бара
   panel.CreateNewTable(0);
//--- Рисуем таблицу с идентификатором 0 на фоне панели
   panel.DrawGrid(0,2,20,6,2,18,width/2-2);

//--- Создаём таблицу с идентификатором 1 для отображения в ней данных индикатора 1
   panel.CreateNewTable(1);
//--- Получаем координату Y2 таблицы с идентификатором 0 и
//--- устанавливаем координату Y1 для таблицы с идентификатором 1
   int y1=panel.TableY2(0)+22;
//--- Рисуем таблицу с идентификатором 1 на фоне панели
   panel.DrawGrid(1,2,y1,2,2,18,width/2-2);

//--- Создаём таблицу с идентификатором 2 для отображения в ней данных индикатора 2
   panel.CreateNewTable(2);
//--- Получаем координату Y2 таблицы с идентификатором 1 и
//--- устанавливаем координату Y1 для таблицы с идентификатором 2
   int y2=panel.TableY2(1)+3;
//--- Рисуем таблицу с идентификатором 2 на фоне панели
   panel.DrawGrid(2,2,y2,3,2,18,width/2-2);
   
//--- Инициализируем переменную с индексом бара указателя мышки
   mouse_bar_index=0;
//--- Выводим на панель данные текущего бара
   DrawData(mouse_bar_index,TimeCurrent());

//--- Успешная инициализация
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- Уничтожаем таймер
   EventKillTimer();
//--- Если объект панели существует - удаляем его
   if(panel!=NULL)
      delete panel;
//--- Стираем все комментарии
   Comment("");
  }
//+------------------------------------------------------------------+
//| 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[])
  {
//--- Количество баров для расчёта
   int limit=rates_total-prev_calculated;
//--- Если limit > 1, значит это первый расчёт, либо изменение в истории
   if(limit>1)
     {
      //--- указываем для просчёта всю доступную историю
      limit=rates_total-1;
      /*
      // Если в индикаторе есть какие-либо буферы, в которых отображены другие расчёты (не мульти- индикаторы),
      // то здесь их нужно инициализировать "пустым" значением, установленным для этих буферов
      */
     }
//--- Рассчитываем все созданные мультисимвольные мультипериодные индикаторы
   if(!indicators.Calculate())
      return 0;

//--- Выводим на панель данные бара под курсором (либо текущий бар, если курсор за пределами графика)
   DrawData(mouse_bar_index,time[mouse_bar_index]);

//--- Из буферов рассчитанных индикаторов выводим данные в индикаторные буферы
   if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_wpr1,0,0,limit,BufferWPR1))
      return 0;
   if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_wpr2,0,0,limit,BufferWPR2))
      return 0;

//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| Timer function                                                   | 
//+------------------------------------------------------------------+
void OnTimer()
  {
//--- Вызываем таймер коллекции индикаторов
   indicators.OnTimer();
  }
//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- Работа с панелью
//--- Вызываем обработчик событий панели
   panel.OnChartEvent(id,lparam,dparam,sparam);

//--- Если курсор перемещается или щелчок по графику
   if(id==CHARTEVENT_MOUSE_MOVE || id==CHARTEVENT_CLICK)
     {
      //--- Объявляем переменные для записи в них координат времени и цены
      datetime time=0;
      double price=0;
      int wnd=0;
      //--- Если координаты курсора преобразованы в дату и время
      if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price))
        {
         //--- записываем индекс бара, где расположен курсор в глобальную переменную
         mouse_bar_index=iBarShift(Symbol(),PERIOD_CURRENT,time);
         //--- Выводим данные бара под курсором на панель
         DrawData(mouse_bar_index,time);
        }
     }

//--- Если получили пользовательское событие - выводим об этом сообщение в журнал
   if(id>CHARTEVENT_CUSTOM)
     {
      //--- Здесь может быть обработка щелчка по кнопке закрытия на панели
      PrintFormat("%s: Event id=%ld, object id (lparam): %lu, event message (sparam): %s",__FUNCTION__,id,lparam,sparam);
     }
  }
//+------------------------------------------------------------------+
//| Выводит данные с указанного индекса таймсерии на панель          |
//+------------------------------------------------------------------+
void DrawData(const int index,const datetime time)
  {
//--- Объявляем переменные для получения в них данных
   MqlRates rates[1];

//--- Если данные бара по указанному индексу получить не удалось - уходим
   if(CopyRates(Symbol(),PERIOD_CURRENT,index,1,rates)!=1)
      return;

//--- Устанавливаем параметры шрифта для заголовков данных бара и индикатора
   int  size=0;
   uint flags=0;
   uint angle=0;
   string name=panel.FontParams(size,flags,angle);
   panel.SetFontParams(name,9,FW_BOLD);
   panel.DrawText("Bar data ["+(string)index+"]",3,panel.TableY1(0)-16,clrMaroon,panel.Width()-6);
   panel.DrawText("Indicators data ["+(string)index+"]",3,panel.TableY1(1)-16,clrGreen,panel.Width()-6);
//--- Устанавливаем параметры шрифта для данных бара и индикатора
   panel.SetFontParams(name,9);

//--- Выводим на панель данные указанного бара в таблицу 0
   panel.DrawText("Date",  panel.CellX(0,0,0)+2, panel.CellY(0,0,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_DATE),     panel.CellX(0,0,1)+2, panel.CellY(0,0,1)+2,clrNONE,90);
   panel.DrawText("Time",  panel.CellX(0,1,0)+2, panel.CellY(0,1,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_MINUTES),  panel.CellX(0,1,1)+2, panel.CellY(0,1,1)+2,clrNONE,90);
   panel.DrawText("Open",  panel.CellX(0,2,0)+2, panel.CellY(0,2,0)+2); panel.DrawText(DoubleToString(rates[0].open,Digits()),      panel.CellX(0,2,1)+2, panel.CellY(0,2,1)+2,clrNONE,90);
   panel.DrawText("High",  panel.CellX(0,3,0)+2, panel.CellY(0,3,0)+2); panel.DrawText(DoubleToString(rates[0].high,Digits()),      panel.CellX(0,3,1)+2, panel.CellY(0,3,1)+2,clrNONE,90);
   panel.DrawText("Low",   panel.CellX(0,4,0)+2, panel.CellY(0,4,0)+2); panel.DrawText(DoubleToString(rates[0].low,Digits()),       panel.CellX(0,4,1)+2, panel.CellY(0,4,1)+2,clrNONE,90);
   panel.DrawText("Close", panel.CellX(0,5,0)+2, panel.CellY(0,5,0)+2); panel.DrawText(DoubleToString(rates[0].close,Digits()),     panel.CellX(0,5,1)+2, panel.CellY(0,5,1)+2,clrNONE,90);

//--- Выводим в таблицу 1 данные индикатора 1 с указанного бара в таблицу 1
   panel.DrawText(indicators.Title(handle_wpr1), panel.CellX(1,0,0)+2, panel.CellY(1,0,0)+2);
   double value1=indicators.GetData(handle_wpr1,0,0,index);
   string value_str1=(value1!=EMPTY_VALUE ? DoubleToString(value1,indicators.Digits(handle_wpr1)) : " ");
   panel.DrawText(value_str1,panel.CellX(1,0,1)+2,panel.CellY(1,0,1)+2,clrNONE,110);
   
//--- Выводим описание состояния линии индикатора 1
   panel.DrawText("Line state", panel.CellX(1,1,0)+2, panel.CellY(1,1,0)+2);
   ENUM_LINE_STATE state1=indicators.BufferLineState(Symbol(),PERIOD_CURRENT,handle_wpr1,0,0,index);
   panel.DrawText(BufferLineStateDescription(state1),panel.CellX(1,1,1)+2,panel.CellY(1,1,1)+2,clrNONE,110);
   
//--- Выводим в таблицу 2 данные индикатора 2 с указанного бара в таблицу 2
   panel.DrawText(indicators.Title(handle_wpr2), panel.CellX(2,0,0)+2, panel.CellY(2,0,0)+2);
   double value2=indicators.GetDataTo(Symbol(),PERIOD_CURRENT,handle_wpr2,0,0,index);
   string value_str2=(value2!=EMPTY_VALUE ? DoubleToString(value2,indicators.Digits(handle_wpr2)) : " ");
   panel.DrawText(value_str2,panel.CellX(2,0,1)+2,panel.CellY(2,0,1)+2,clrNONE,110);
   
//--- Выводим описание состояния линии индикатора 2
   panel.DrawText("Line state", panel.CellX(2,1,0)+2, panel.CellY(2,1,0)+2);
   ENUM_LINE_STATE state2=indicators.BufferLineState(Symbol(),PERIOD_CURRENT,handle_wpr2,0,0,index);
   panel.DrawText(BufferLineStateDescription(state2),panel.CellX(2,1,1)+2,panel.CellY(2,1,1)+2,clrNONE,110);
   
//--- Выводим описание соотношения линии индикатора 1 относительно линии индикатора 2
   double value21=indicators.GetDataTo(Symbol(),PERIOD_CURRENT,handle_wpr2,0,0,index+1);
   ENUM_LINE_STATE stateR=indicators.BufferLineStateRelative(Symbol(),PERIOD_CURRENT,handle_wpr1,0,0,index,value2,value21);
   string ma1=indicators.Name(handle_wpr1);
   string ma2=indicators.Name(handle_wpr2);
   string state_relative=
     (
      stateR==LINE_STATE_ABOVE      ? StringFormat("%s1 > %s2",ma1,ma2)   :
      stateR==LINE_STATE_BELOW      ? StringFormat("%s1 < %s2",ma1,ma2)           :
      stateR==LINE_STATE_CROSS_DOWN ? "Top-down crossing"   :
      stateR==LINE_STATE_CROSS_UP   ? "Bottom-up crossing"  :
      BufferLineStateDescription(stateR)
     );
   panel.DrawText(StringFormat("%s1 vs %s2",ma1,ma2), panel.CellX(2,2,0)+2, panel.CellY(2,2,0)+2);
   panel.DrawText(state_relative, panel.CellX(2,2,1)+2, panel.CellY(2,2,1)+2,clrNONE,110);
   
//--- Перерисовываем график для немедленного отображения всех изменений на панели
   ChartRedraw(ChartID());
  }
//+------------------------------------------------------------------+

После компиляции и запуска индикатора на графике М1 и выбранном периоде графика для расчёта М5, видим:



Заключение

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


Количественный анализ на MQL5: реализуем перспективный алгоритм Количественный анализ на MQL5: реализуем перспективный алгоритм
Разбираем вопрос, что такое количественный анализ, как его применяют крупные игроки, создадим один из алгоритмов количественного анализа на языке MQL5.
Популяционные алгоритмы оптимизации: Алгоритм имитации отжига (Simulated Annealing, SA). Часть I Популяционные алгоритмы оптимизации: Алгоритм имитации отжига (Simulated Annealing, SA). Часть I
Алгоритм имитации отжига (Simulated Annealing) является метаэвристикой, вдохновленной процессом отжига металлов. В нашей статье проведем тщательный анализ алгоритма и покажем, как многие распространенные представления и мифы, вокруг этого наиболее популярного и широко известного метода оптимизации, могут быть ошибочными и неполными. Анонс второй части статьи: "Встречайте собственный авторский алгоритм имитации изотропного отжига (Simulated Isotropic Annealing, SIA)!"
Нейросети — это просто (Часть 67): Использование прошлого опыта для решения новых задач Нейросети — это просто (Часть 67): Использование прошлого опыта для решения новых задач
В данной статье мы продолжим разговор о методах сбора данных в обучающую выборку. Очевидно, что в процессе обучения необходимо постоянное взаимодействие с окружающей средой. Но ситуации бывают разные.
Тестируем информативность разных типов скользящих средних Тестируем информативность разных типов скользящих средних
Мы все знаем важность скользящей средней для многих трейдеров. Существуют разные типы скользящих средних, которые могут быть полезны в торговле. Мы рассмотрим их и проведем простое сравнение, чтобы увидеть, какой из них может показать лучшие результаты.