Правила сопоставления буферов и диаграмм

При регистрации диаграмм с помощью PlotIndexSetInteger(i, PLOT_DRAW_TYPE, type) каждый вызов последовательно ставит в соответствие i-ой диаграмме некоторое количество буферов согласно их требуемому количеству для типа отрисовки type (см. таблицу ENUM_DRAW_TYPE в предыдущем разделе). Тем самым это количество буферов изымается из рассмотрения при привязке буферов к следующим диаграммам (при следующих вызовах PlotIndexSetInteger).

Например, если первым графическим построением (под индексом 0) идет DRAW_CANDLES, для которого необходимо 4 индикаторных буфера, то именно такое количество и будет с ним ассоциировано. Таким образом, буфера с индексами от 0 по 3 включительно получат привязку, и следующим свободным для привязки буфером станет буфер под индексом 4.

Если следом регистрируется простая линейная диаграмма DRAW_LINE (её индекс в последовательности диаграмм равен 1), она займет только 1 буфер — как раз под индексом 4.

Если далее настраивается диаграмма DRAW_ZIGZAG (следующий индекс среди диаграмм равен 2), то поскольку она использует два буфера, то к ней отойдут буфера с индексами 5 и 6.

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

Индекс буфера в SetIndexBuffer

0

1

2

3

4

5

6

Индекс диаграммы в PlotIndexSetInteger

0

1

2

Тип отрисовки

DRAW_CANDLES

DRAW
_LINE

DRAW_ZIGZAG

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

Хотя принято вызывать функции SetIndexBuffer до PlotIndexSetInteger, это не обязательно. Важным является лишь правильное соответствие индексов буферов и индексов диаграмм. В случае использования директив (см. следующий раздел), которые являются альтернативой вызовам PlotIndexSetInteger, директивы исполняются в любом случае раньше обработчика OnInit.

Для демонстрации различия в индексациях буферов и диаграмм рассмотрим простой пример IndHighLowClose.mq5. В нем станем рисовать размах каждой свечи между High и Low в виде гистограммы типа DRAW_HISTOGRAM2, а цену Close подчеркнем простой линией DRAW_LINE. Для доступа к таймсериям цен разных типов нам также потребуется сменить форму OnCalculate с упрощенной на полную.

Поскольку гистограмма требует 2 буфера, то вместе с еще одним буфером под линию Close следует описать три буфера.

#property indicator_chart_window
#property indicator_buffers 3
#property indicator_plots 2
   
double highs[];
double lows[];
double closes[];

Регистрируем их в OnInit в порядке очередности.

int OnInit()
{
   // массивы для буферов под 3 типа цены
   SetIndexBuffer(0highs);
   SetIndexBuffer(1lows);
   SetIndexBuffer(2closes);
   
   // отрисовка гистограммы между High и Low свечи под индексом 0
   PlotIndexSetInteger(0PLOT_DRAW_TYPEDRAW_HISTOGRAM2);
   PlotIndexSetInteger(0PLOT_LINE_WIDTH5);
   PlotIndexSetInteger(0PLOT_LINE_COLORclrBlue);
   
   // отрисовка линии Close под индексом 1
   PlotIndexSetInteger(1PLOT_DRAW_TYPEDRAW_LINE);
   PlotIndexSetInteger(1PLOT_LINE_WIDTH2);
   PlotIndexSetInteger(1PLOT_LINE_COLORclrRed);
   
   return INIT_SUCCEEDED;
}

Попутно ширина гистограммы установлена равной 5-ти пикселям, а ширины линии — 2-м. Стили явным образом не назначаются, и равны по умолчанию STYLE_SOLID.

Теперь собственно функция OnCalculate.

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[])
{
   // на каждом новом баре или множесте баров (включая первый расчет)
   if(prev_calculated != rates_total)
   {
      // заполняем все новые бары
      ArrayCopy(highshighprev_calculatedprev_calculated);
      ArrayCopy(lowslowprev_calculatedprev_calculated);
      ArrayCopy(closescloseprev_calculatedprev_calculated);
   }
   else // тики на текущем баре
   {
      // обновляем последний бар
      highs[rates_total - 1] = high[rates_total - 1];
      lows[rates_total - 1] = low[rates_total - 1];
      closes[rates_total - 1] = close[rates_total - 1];
   }
   // сообщаем количество обработанных баров для следующего вызова
   return rates_total;
}

Результат работы этого индикатора приведен на следующем изображении:

Гистограмма High-Low и линия Close

Гистограмма High-Low и линия Close

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

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

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