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

Artyom Trishkin | 14 сентября, 2020

Содержание


Концепция

Начиная со статьи 39 мы шаг за шагом создали функционал для построения собственных мультисимвольных мультипериодных индикаторов. Было естественным на созданной основе дать возможность работы в мультирежимах и для стандартных индикаторов. И начиная со статьи 47 мы создали такой функционал (есть недоработки, которые постепенно локализуем и подправим). Но всё, что мы делали, мы делали для платформы MetaTrader 5.
Чтобы программы, написанные под устаревшую платформу MetaTrader 4, основанные на данной библиотеке, могли нормально работать при переходе на MetaTrader 5, мы немного доработаем классы библиотеки касаемо индикаторов.

В отличии от MQL5, в MQL4 мы не можем для одного буфера иметь несколько цветов его отрисовки — в MetaTrader 4 все индикаторные буферы монохромны. Это ограничение накладывает свой отпечаток и на концепцию построения мультибуферов для MetaTrader 4. Здесь мы не сможем указать для конкретного бара его цвет отрисовки, как это легко можно сделать в MetaTrader 5. Тут нам придётся использовать по одному монохромному индикаторному буферу для каждого цвета. Но при этом нам не нужно создавать дополнительные расчётные буферы для хранения данных индикатора с указанного символа/периода графика — все функции обращения к индикатору в MQL4 возвращают значение по указанному бару индикатора с указанного символа/периода, тогда как в MQL5 нам необходимо создать хэндл индикатора, и уже с этого хэндла запрашивать данные копированием нужного количества в приёмный массив, коим для индикаторов и является его расчётный буфер. И уже потом мы получаем данные указанного индикатора из этого массива по индексу нужного бара. При этом мы получаем ускорение доступа к данным индикатора в MQL5.

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

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

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

В MQL5 мы будем иметь три массива данных (три буфера):

  1. Рисуемый буфер (данные отображаются в Окне данных)
  2. Буфер цвета (не отображается в Окне данных, но указывает каким цветом рисовать линию буфера 1 на каждом баре)
  3. Расчётный буфер для хранения данных Moving Average с указанного символа/периода (не отображается в Окне данных)

В MQL4 мы будем иметь три массива данных (три буфера):

  1. Рисуемый буфер для цвета 1 (данные отображаются в Окне данных)
  2. Рисуемый буфер для цвета 2 (данные отображаются в Окне данных)
  3. Рисуемый буфер для цвета 3 (данные отображаются в Окне данных)

При уменьшении количества цветов количество буферов для MQL4 будет уменьшаться, при увеличении — увеличиваться. В MQL5 количество буферов для данного примера всегда будет равным 3. При этом в MQL5 есть возможность динамически менять количество цветов до 64. В MQL4 не всё так просто с окрашиванием линий индикаторов. Просто потому, что для 64-х цветов нам необходимо создать уже 64 буфера. И это только для одной линии. Если же индикатор имеет 4 линии, то уже потребуется 256 массивов-индикаторных буферов. Для восьми линий — 512 буферов, что является пределом. Ну и для MQL5 мы просто указываем для каждого бара индекс соответствующего цвета, и линия на этом баре окрашивается в указанный цвет. Для MQL4 нам придётся показывать соответствующий цвету буфер, а остальные скрывать. И все буферы для каждого цвета в MQL4 будут видны в Окне данных терминала. В MQL5 для данного примера в Окне данных терминала будет виден один буфер, что правильно — одна линия индикатора = одно значение в Окне данных терминала.

Мы не будем сразу, одним махом, исправлять и переделывать всё уже сделанное. А постепенно, шаг за шагом за несколько статей, от простого к сложному, доработаем классы библиотеки до совместимости некоторых аспектов работы с индикаторами в библиотеке с MQL4. Сегодня на примере индикатора Accumulation/Distribution проверим создание однобуферного монохромного мультисимвольного мультипериодного стандартного индикатора в MQL4 при помощи библиотеки.

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

Как обычно, сначала добавим необходимые текстовые сообщения. Ранее мы прямо в конечной программе-индикаторе проверяли соответствие созданных буферов индикатора библиотекой с записями в коде индикатора о необходимом количестве индикаторных буферов:

#property indicator_buffers 3
#property indicator_plots   1

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

//--- Проверяем количество буферов, указанных в блоке properties
   if(engine.BuffersPropertyPlotsTotal()!=indicator_plots)
      Alert(TextByLanguage("Внимание! Значение \"indicator_plots\" должно быть ","Attention! Value of \"indicator_plots\" should be "),engine.BuffersPropertyPlotsTotal());
   if(engine.BuffersPropertyBuffersTotal()!=indicator_buffers)
      Alert(TextByLanguage("Внимание! Значение \"indicator_buffers\" должно быть ","Attention! Value of \"indicator_buffers\" should be "),engine.BuffersPropertyBuffersTotal());

Вот эту проверку — немного доработанную для совместимости с MQL4 — мы и перенесём в библиотеку. А тексты, выводимые в ходе проверки мы расположим там, где им и место в библиотеке — в файле \MQL5\Include\DoEasy\Data.mqh. Впишем в него индексы новых сообщений:

//--- CEngine
   MSG_ENG_NO_TRADE_EVENTS,                           // С момента последнего запуска ЕА торговых событий не было
   MSG_ENG_FAILED_GET_LAST_TRADE_EVENT_DESCR,         // Не удалось получить описание последнего торгового события
   MSG_ENG_FAILED_GET_MARKET_POS_LIST,                // Не удалось получить список открытых позиций
   MSG_ENG_FAILED_GET_PENDING_ORD_LIST,               // Не удалось получить список установленных ордеров
   MSG_ENG_NO_OPEN_POSITIONS,                         // Нет открытых позиций
   MSG_ENG_NO_PLACED_ORDERS,                          // Нет установленных ордеров
   MSG_ENG_ERR_VALUE_PLOTS,                           // Внимание! Значение \"indicator_plots\" должно быть 
   MSG_ENG_ERR_VALUE_ORDERS,                          // Внимание! Значение \"indicator_buffers\" должно быть 

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

//--- CEngine
   {"С момента последнего запуска ЕА торговых событий не было","There have been no trade events since the last launch of EA"},
   {"Не удалось получить описание последнего торгового события","Failed to get the description of the last trading event"},
   {"Не удалось получить список открытых позиций","Failed to get open positions list"},
   {"Не удалось получить список установленных ордеров","Failed to get pending orders list"},
   {"Нет открытых позиций","No open positions"},
   {"Нет установленных ордеров","No placed orders"},
   {"Внимание! Значение \"indicator_plots\" должно быть ","Attention! Value of \"indicator_plots\" should be "},
   {"Внимание! Значение \"indicator_buffers\" должно быть ","Attention! Value of \"indicator_buffers\" should be "},

Файл, содержащий данные для входных параметров программ, у нас называется InpDatas.mqh... Поменяем его название на правильное с точки зрения английского языка (ошибся я при наименовании файла). Теперь этот файл будет называться так: \MQL5\Include\DoEasy\InpData.mqh.
Просто переименуем его в папке, где он расположен.

Файл этот у нас подключается к библиотеке в файле Data.mqh (который мы сейчас и редактируем), исправим строку подключения:

//+------------------------------------------------------------------+
//|                                                         Data.mqh |
//|                        Copyright 2020, MetaQuotes Software Corp. |
//|                             https://mql5.com/ru/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, MetaQuotes Software Corp."
#property link      "https://mql5.com/ru/users/artmedia70"
//+------------------------------------------------------------------+
//| Включаемые файлы                                                 |
//+------------------------------------------------------------------+
#include "InpData.mqh"
//+------------------------------------------------------------------+


Приступим к реализации кроссплатформенности.

Если сейчас попытаться скомпилировать библиотеку в редакторе MetaEditor от MetaTrader 4 (F7 на файле Engine.mqh), то получим массу ошибок:


Что ж, это и не удивительно. Просто начнём по порядку. В первую очередь увидим, что MQL4 не знает некоторых констант и перечислений.
Пропишем новые константы и перечисления в файле \MQL5\Include\DoEasy\ToMQL4.mqh:

//+------------------------------------------------------------------+
//|                                                       ToMQL4.mqh |
//|              Copyright 2017, Artem A. Trishkin, Skype artmedia70 |
//|                         https://www.mql5.com/ru/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2017, Artem A. Trishkin, Skype artmedia70"
#property link      "https://www.mql5.com/ru/users/artmedia70"
#property strict
#ifdef __MQL4__
//+------------------------------------------------------------------+
//| Коды ошибок                                                      |
//+------------------------------------------------------------------+
#define ERR_SUCCESS                       (ERR_NO_ERROR)
#define ERR_MARKET_UNKNOWN_SYMBOL         (ERR_UNKNOWN_SYMBOL)
#define ERR_ZEROSIZE_ARRAY                (ERR_ARRAY_INVALID)
#define ERR_MAIL_SEND_FAILED              (ERR_SEND_MAIL_ERROR)
#define ERR_NOTIFICATION_SEND_FAILED      (ERR_NOTIFICATION_ERROR)
#define ERR_FTP_SEND_FAILED               (ERR_FTP_ERROR)
//+------------------------------------------------------------------+
//| Типы ордеров, политика исполнения, срок действия, причины        |
//+------------------------------------------------------------------+
#define ORDER_TYPE_CLOSE_BY               (8)
#define ORDER_TYPE_BUY_STOP_LIMIT         (9)
#define ORDER_TYPE_SELL_STOP_LIMIT        (10)
#define ORDER_REASON_EXPERT               (3)
#define ORDER_REASON_SL                   (4)
#define ORDER_REASON_TP                   (5)
#define ORDER_REASON_BALANCE              (6)
#define ORDER_REASON_CREDIT               (7)
//+------------------------------------------------------------------+
//| Флаги разрешенных режимов истечения ордера                       |
//+------------------------------------------------------------------+
#define SYMBOL_EXPIRATION_GTC             (1)
#define SYMBOL_EXPIRATION_DAY             (2)
#define SYMBOL_EXPIRATION_SPECIFIED       (4)
#define SYMBOL_EXPIRATION_SPECIFIED_DAY   (8)
//+------------------------------------------------------------------+
//| Флаги разрешенных режимов заливки ордера                         |
//+------------------------------------------------------------------+
#define SYMBOL_FILLING_FOK                (1)
#define SYMBOL_FILLING_IOC                (2)
//+------------------------------------------------------------------+
//| Флаги разрешенных типов ордера                                   |
//+------------------------------------------------------------------+
#define SYMBOL_ORDER_MARKET               (1)
#define SYMBOL_ORDER_LIMIT                (2)
#define SYMBOL_ORDER_STOP                 (4)
#define SYMBOL_ORDER_STOP_LIMIT           (8)
#define SYMBOL_ORDER_SL                   (16)
#define SYMBOL_ORDER_TP                   (32)
#define SYMBOL_ORDER_CLOSEBY              (64)
//+------------------------------------------------------------------+
//| Идентификаторы линий индикаторов                                 |
//+------------------------------------------------------------------+
#define TENKANSEN_LINE                    (0)
#define KIJUNSEN_LINE                     (1)
#define SENKOUSPANA_LINE                  (2)
#define SENKOUSPANB_LINE                  (3)
#define CHIKOUSPAN_LINE                   (4)
//+------------------------------------------------------------------+
//| Типы сделок MQL5                                                 |
//+------------------------------------------------------------------+
//--- код удалён для освобождения места в статье
//+------------------------------------------------------------------+
//| Способ изменения позиции                                         |
//+------------------------------------------------------------------+
//--- код удалён для освобождения места в статье
//+------------------------------------------------------------------+
//| Направление открытой позиции                                     |
//+------------------------------------------------------------------+
//--- код удалён для освобождения места в статье
//+------------------------------------------------------------------+
//| Состояние ордера                                                 |
//+------------------------------------------------------------------+
//--- код удалён для освобождения места в статье
//+------------------------------------------------------------------+
//| Режим расчета маржи                                              |
//+------------------------------------------------------------------+
//--- код удалён для освобождения места в статье
//+------------------------------------------------------------------+
//| Цены, на основе которых строится график по символу               |
//+------------------------------------------------------------------+
//--- код удалён для освобождения места в статье
//+------------------------------------------------------------------+
//| Срок действия отложенных ордеров и                               |
//| установленных уровней StopLoss/TakeProfit                        |
//+------------------------------------------------------------------+
//--- код удалён для освобождения места в статье
//+------------------------------------------------------------------+
//| Типы опционов                                                    |
//+------------------------------------------------------------------+
//--- код удалён для освобождения места в статье
//+------------------------------------------------------------------+
//| Право, предоставляемое опционом                                  |
//+------------------------------------------------------------------+
//--- код удалён для освобождения места в статье
//+------------------------------------------------------------------+
//| Способ вычисления величины залоговых средств по инструменту      |
//+------------------------------------------------------------------+
//--- код удалён для освобождения места в статье
//+------------------------------------------------------------------+
//| Способы начисления свопов при переносе позиции                   |
//+------------------------------------------------------------------+
//--- код удалён для освобождения места в статье
//+------------------------------------------------------------------+
//| Типы торговых операций                                           |
//+------------------------------------------------------------------+
//--- код удалён для освобождения места в статье
//+------------------------------------------------------------------+
//| Политики исполнения ордеров                                      |
//+------------------------------------------------------------------+
//--- код удалён для освобождения места в статье
//+------------------------------------------------------------------+
//| Срок действия ордера                                             |
//+------------------------------------------------------------------+
//--- код удалён для освобождения места в статье
//+------------------------------------------------------------------+
//| Целочисленные свойства выбранной позиции                         |
//+------------------------------------------------------------------+
//--- код удалён для освобождения места в статье
//+------------------------------------------------------------------+
//| Вещественные свойства выбранной позиции                          |
//+------------------------------------------------------------------+
//--- код удалён для освобождения места в статье
//+------------------------------------------------------------------+
//| Строковые свойства выбранной позиции                             |
//+------------------------------------------------------------------+
//--- код удалён для освобождения места в статье
//+------------------------------------------------------------------+
//| Типы технических индикаторов                                     |
//+------------------------------------------------------------------+
enum ENUM_INDICATOR
  {
   IND_AC         = 5,
   IND_AD         = 6,
   IND_ALLIGATOR  = 7,
   IND_ADX        = 8,
   IND_ADXW       = 9,
   IND_ATR        = 10,
   IND_AO         = 11,
   IND_BEARS      = 12,
   IND_BANDS      = 13,
   IND_BULLS      = 14,
   IND_CCI        = 15,
   IND_DEMARKER   = 16,
   IND_ENVELOPES  = 17,
   IND_FORCE      = 18,
   IND_FRACTALS   = 19,
   IND_GATOR      = 20,
   IND_ICHIMOKU   = 21,
   IND_BWMFI      = 22,
   IND_MACD       = 23,
   IND_MOMENTUM   = 24,
   IND_MFI        = 25,
   IND_MA         = 26,
   IND_OSMA       = 27,
   IND_OBV        = 28,
   IND_SAR        = 29,
   IND_RSI        = 30,
   IND_RVI        = 31,
   IND_STDDEV     = 32,
   IND_STOCHASTIC = 33,
   IND_VOLUMES    = 34,
   IND_WPR        = 35,
   IND_DEMA       = 36,
   IND_TEMA       = 37,
   IND_TRIX       = 38,
   IND_FRAMA      = 39,
   IND_AMA        = 40,
   IND_CHAIKIN    = 41,
   IND_VIDYA      = 42,
   IND_CUSTOM     = 43,
  };
//+------------------------------------------------------------------+
//| Стили рисования                                                  |
//+------------------------------------------------------------------+
enum ENUM_DRAW_TYPE
  {
   DRAW_COLOR_LINE = DRAW_LINE,              // MQL5 = 1, MQL4 = 0
   DRAW_COLOR_HISTOGRAM = DRAW_HISTOGRAM,    // MQL5 = 2, MQL4 = 2
   DRAW_COLOR_ARROW = DRAW_ARROW,            // MQL5 = 3, MQL4 = 3
   DRAW_COLOR_SECTION = DRAW_SECTION,        // MQL5 = 4, MQL4 = 1
   DRAW_COLOR_HISTOGRAM2 = DRAW_NONE,        // MQL5 = 0, MQL4 = 12
   DRAW_COLOR_ZIGZAG = DRAW_ZIGZAG,          // MQL5 = 6, MQL4 = 4
   DRAW_COLOR_BARS = DRAW_NONE,              // MQL5 = 0, MQL4 = 12
   DRAW_COLOR_CANDLES = DRAW_NONE,           // MQL5 = 0, MQL4 = 12
// DRAW_FILLING                                           MQL4 = 5
   DRAW_COLOR_NONE = DRAW_NONE,              // MQL5 = 0, MQL4 = 12
  };
//+------------------------------------------------------------------+
//| Тип объёма                                                       |
//+------------------------------------------------------------------+
enum ENUM_APPLIED_VOLUME
  {
   VOLUME_TICK,
   VOLUME_REAL
  };
//+------------------------------------------------------------------+
//| Структура торгового запроса                                      |
//+------------------------------------------------------------------+
//--- код далее удалён для освобождения места в статье
#endif 

Далее, при последующей компиляции попадаем на ошибку отсутствия mql5-функций в MQL4. В частности BarsCalculated(). Эта функция возвращает количество рассчитанных индикатором баров по его хэндлу. Для языка MQL4 это всё неизвестные понятия. Ближайшей же по смыслу функцией к BarsCalculated() будет mql4-функция Bars(), которая возвращает количество доступных баров указанной таймсерии.
Так как в MQL4 считается, что индикатор при обращении к нему уже рассчитан, то мы можем подменить количество рассчитанных данных индикатора (MQL5 BarsCalculated() ) на количество доступных баров таймсерии (MQL4 Bars() ). В любом случае, методы библиотеки при получении данных индикатора возвращают полученные данные и проверяют их на корректность, поэтому будем считать, что указание доступных баров таймсерии вполне может подменить нам неизвестное в MQL4 количество рассчитанных данных индикатора.

Метод IndicatorBarsCalculated(), использующий функцию BarsCalculated(), расположен в файле \MQL5\Include\DoEasy\Objects\Indicators\Buffer.mqh. И там же нам придётся сразу же внести большое количество доработок в другие методы работы с индикаторами.

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

   ENUM_INDICATOR    IndicatorType(void)                       const { return (ENUM_INDICATOR)this.GetProperty(BUFFER_PROP_IND_TYPE);        }
   string            IndicatorName(void)                       const { return this.GetProperty(BUFFER_PROP_IND_NAME);                        }
   string            IndicatorShortName(void)                  const { return this.GetProperty(BUFFER_PROP_IND_NAME_SHORT);                  }
   int               IndicatorBarsCalculated(void)             const { return ::BarsCalculated((int)this.GetProperty(BUFFER_PROP_IND_HANDLE));}
   int               IndicatorLineAdditionalNumber(void)       const { return (int)this.GetProperty(BUFFER_PROP_IND_LINE_ADDITIONAL_NUM);    }
   int               IndicatorLineMode(void)                   const { return (int)this.GetProperty(BUFFER_PROP_IND_LINE_MODE);              }

Теперь же оставим лишь объявление метода

   ENUM_INDICATOR    IndicatorType(void)                       const { return (ENUM_INDICATOR)this.GetProperty(BUFFER_PROP_IND_TYPE);        }
   string            IndicatorName(void)                       const { return this.GetProperty(BUFFER_PROP_IND_NAME);                        }
   string            IndicatorShortName(void)                  const { return this.GetProperty(BUFFER_PROP_IND_NAME_SHORT);                  }
   int               IndicatorLineAdditionalNumber(void)       const { return (int)this.GetProperty(BUFFER_PROP_IND_LINE_ADDITIONAL_NUM);    }
   int               IndicatorLineMode(void)                   const { return (int)this.GetProperty(BUFFER_PROP_IND_LINE_MODE);              }
   int               IndicatorBarsCalculated(void);

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

//+------------------------------------------------------------------+
//| Возвращает количество рассчитанных баров стандартного индикатора |
//+------------------------------------------------------------------+
int CBuffer::IndicatorBarsCalculated(void)
  {
   return(#ifdef __MQL5__ ::BarsCalculated((int)this.GetProperty(BUFFER_PROP_IND_HANDLE)) #else ::Bars(this.Symbol(),this.Timeframe()) #endif);
  }
//+------------------------------------------------------------------+

Здесь мы для MQL5 возвращаем количество рассчитанных данных индикатора, а для MQL4 — количество доступных данных таймсерии.

Закрытый параметрический конструктор класса разделим на две части.
Первая часть — та, что уже есть, останется только для MQL5, а для MQL4 сделаем копию mql5-кода и удалим из него ненужное:

//+------------------------------------------------------------------+
//| Закрытый параметрический конструктор                             |
//+------------------------------------------------------------------+
CBuffer::CBuffer(ENUM_BUFFER_STATUS buffer_status,
                 ENUM_BUFFER_TYPE buffer_type,
                 const uint index_plot,
                 const uint index_base_array,
                 const int num_datas,
                 const uchar total_arrays,
                 const int width,
                 const string label)
  {
#ifdef __MQL5__
   this.m_type=COLLECTION_BUFFERS_ID;
   this.m_act_state_trigger=true;
   this.m_total_arrays=total_arrays;
//--- Сохранение целочисленных свойств
   this.m_long_prop[BUFFER_PROP_STATUS]                        = buffer_status;
   this.m_long_prop[BUFFER_PROP_TYPE]                          = buffer_type;
   this.m_long_prop[BUFFER_PROP_ID]                            = WRONG_VALUE;
   this.m_long_prop[BUFFER_PROP_IND_LINE_MODE]                 = INDICATOR_LINE_MODE_MAIN;
   this.m_long_prop[BUFFER_PROP_IND_HANDLE]                    = INVALID_HANDLE;
   this.m_long_prop[BUFFER_PROP_IND_TYPE]                      = WRONG_VALUE;
   this.m_long_prop[BUFFER_PROP_IND_LINE_ADDITIONAL_NUM]       = WRONG_VALUE;
   ENUM_DRAW_TYPE type=
     (
      !this.TypeBuffer() || !this.Status() ? DRAW_NONE      : 
      this.Status()==BUFFER_STATUS_FILLING ? DRAW_FILLING   : 
      ENUM_DRAW_TYPE(this.Status()+8)
     );
   this.m_long_prop[BUFFER_PROP_DRAW_TYPE]                     = type;
   this.m_long_prop[BUFFER_PROP_TIMEFRAME]                     = PERIOD_CURRENT;
   this.m_long_prop[BUFFER_PROP_ACTIVE]                        = true;
   this.m_long_prop[BUFFER_PROP_ARROW_CODE]                    = 0x9F;
   this.m_long_prop[BUFFER_PROP_ARROW_SHIFT]                   = 0;
   this.m_long_prop[BUFFER_PROP_DRAW_BEGIN]                    = 0;
   this.m_long_prop[BUFFER_PROP_SHOW_DATA]                     = (buffer_type>BUFFER_TYPE_CALCULATE ? true : false);
   this.m_long_prop[BUFFER_PROP_SHIFT]                         = 0;
   this.m_long_prop[BUFFER_PROP_LINE_STYLE]                    = STYLE_SOLID;
   this.m_long_prop[BUFFER_PROP_LINE_WIDTH]                    = width;
   this.m_long_prop[BUFFER_PROP_COLOR_INDEXES]                 = (this.Status()>BUFFER_STATUS_NONE ? (this.Status()!=BUFFER_STATUS_FILLING ? 1 : 2) : 0);
   this.m_long_prop[BUFFER_PROP_COLOR]                         = clrRed;
   this.m_long_prop[BUFFER_PROP_NUM_DATAS]                     = num_datas;
   this.m_long_prop[BUFFER_PROP_INDEX_PLOT]                    = index_plot;
   this.m_long_prop[BUFFER_PROP_INDEX_BASE]                    = index_base_array;
   this.m_long_prop[BUFFER_PROP_INDEX_COLOR]                   = this.GetProperty(BUFFER_PROP_INDEX_BASE)+
                                                                   (this.TypeBuffer()!=BUFFER_TYPE_CALCULATE ? this.GetProperty(BUFFER_PROP_NUM_DATAS) : 0);
   this.m_long_prop[BUFFER_PROP_INDEX_NEXT_BASE]               = index_base_array+this.m_total_arrays;
   this.m_long_prop[BUFFER_PROP_INDEX_NEXT_PLOT]               = (this.TypeBuffer()>BUFFER_TYPE_CALCULATE ? index_plot+1 : index_plot);
   
//--- Сохранение вещественных свойств
   this.m_double_prop[this.IndexProp(BUFFER_PROP_EMPTY_VALUE)] = (this.TypeBuffer()>BUFFER_TYPE_CALCULATE ? EMPTY_VALUE : 0);
//--- Сохранение строковых свойств
   this.m_string_prop[this.IndexProp(BUFFER_PROP_SYMBOL)]      = ::Symbol();
   this.m_string_prop[this.IndexProp(BUFFER_PROP_LABEL)]       = (this.TypeBuffer()>BUFFER_TYPE_CALCULATE ? label : NULL);
   this.m_string_prop[this.IndexProp(BUFFER_PROP_IND_NAME)]    = NULL;
   this.m_string_prop[this.IndexProp(BUFFER_PROP_IND_NAME_SHORT)]=NULL;

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

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

//--- Связывание индикаторных буферов с массивами
//--- В цикле по количеству индикаторных буферов
   int total=::ArraySize(DataBuffer);
   for(int i=0;i<total;i++)
     {
      //--- рассчитываем индекс очередного массива и
      //--- связываем индикаторный буфер по рассчитанному индексу с динамическим массивом,
      //--- находящимся по индексу цикла i в массиве DataBuffer
      int index=(int)this.GetProperty(BUFFER_PROP_INDEX_BASE)+i;
      ::SetIndexBuffer(index,this.DataBuffer[i].Array,(this.TypeBuffer()==BUFFER_TYPE_DATA ? INDICATOR_DATA : INDICATOR_CALCULATIONS));
      //--- Установка всем массивам буферов флага индексации как в таймсерии
      ::ArraySetAsSeries(this.DataBuffer[i].Array,true);
     }
//--- Связывание буфера цвета с массивом (только для не расчётного буфера и не для буфера заливки)
   if(this.Status()!=BUFFER_STATUS_FILLING && this.TypeBuffer()!=BUFFER_TYPE_CALCULATE)
     {
      ::SetIndexBuffer((int)this.GetProperty(BUFFER_PROP_INDEX_COLOR),this.ColorBufferArray,INDICATOR_COLOR_INDEX);
      ::ArraySetAsSeries(this.ColorBufferArray,true);
     }
//--- Если буфер расчётный - закончили
   if(this.TypeBuffer()==BUFFER_TYPE_CALCULATE)
      return;
//--- Установка целочисленных параметров графической серии
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_DRAW_TYPE,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_DRAW_TYPE));
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_ARROW,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_ARROW_CODE));
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_ARROW_SHIFT,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_ARROW_SHIFT));
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_DRAW_BEGIN,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_DRAW_BEGIN));
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_SHOW_DATA,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_SHOW_DATA));
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_SHIFT,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_SHIFT));
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LINE_STYLE,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_LINE_STYLE));
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LINE_WIDTH,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_LINE_WIDTH));
   this.SetColor((color)this.GetProperty(BUFFER_PROP_COLOR));
//--- Установка вещественных параметров графической серии
   ::PlotIndexSetDouble((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_EMPTY_VALUE,this.GetProperty(BUFFER_PROP_EMPTY_VALUE));
//--- Установка строковых параметров графической серии
   ::PlotIndexSetString((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LABEL,this.GetProperty(BUFFER_PROP_LABEL));

//--- MQL4
#else 
   this.m_type=COLLECTION_BUFFERS_ID;
   this.m_act_state_trigger=true;
   this.m_total_arrays=1;
//--- Сохранение целочисленных свойств
   this.m_long_prop[BUFFER_PROP_STATUS]                        = buffer_status;
   this.m_long_prop[BUFFER_PROP_TYPE]                          = buffer_type;
   this.m_long_prop[BUFFER_PROP_ID]                            = WRONG_VALUE;
   this.m_long_prop[BUFFER_PROP_IND_LINE_MODE]                 = INDICATOR_LINE_MODE_MAIN;
   this.m_long_prop[BUFFER_PROP_IND_HANDLE]                    = INVALID_HANDLE;
   this.m_long_prop[BUFFER_PROP_IND_TYPE]                      = WRONG_VALUE;
   this.m_long_prop[BUFFER_PROP_IND_LINE_ADDITIONAL_NUM]       = WRONG_VALUE;
   
   ENUM_DRAW_TYPE type=DRAW_COLOR_NONE;
   switch((int)this.Status())
     {
      case BUFFER_STATUS_LINE       :  type=DRAW_COLOR_LINE;      break;
      case BUFFER_STATUS_HISTOGRAM  :  type=DRAW_COLOR_HISTOGRAM; break;
      case BUFFER_STATUS_ARROW      :  type=DRAW_COLOR_ARROW;     break;
      case BUFFER_STATUS_SECTION    :  type=DRAW_COLOR_SECTION;   break;
      case BUFFER_STATUS_ZIGZAG     :  type=DRAW_COLOR_ZIGZAG;    break;
      case BUFFER_STATUS_NONE       :  type=DRAW_COLOR_NONE;      break;
      case BUFFER_STATUS_FILLING    :  type=DRAW_COLOR_NONE;      break;
      case BUFFER_STATUS_HISTOGRAM2 :  type=DRAW_COLOR_NONE;      break;
      case BUFFER_STATUS_BARS       :  type=DRAW_COLOR_NONE;      break;
      case BUFFER_STATUS_CANDLES    :  type=DRAW_COLOR_NONE;      break;
      default                       :  type=DRAW_COLOR_NONE;      break;
     }
   this.m_long_prop[BUFFER_PROP_DRAW_TYPE]                     = type;
   this.m_long_prop[BUFFER_PROP_TIMEFRAME]                     = PERIOD_CURRENT;
   this.m_long_prop[BUFFER_PROP_ACTIVE]                        = true;
   this.m_long_prop[BUFFER_PROP_ARROW_CODE]                    = 0x9F;
   this.m_long_prop[BUFFER_PROP_ARROW_SHIFT]                   = 0;
   this.m_long_prop[BUFFER_PROP_DRAW_BEGIN]                    = 0;
   this.m_long_prop[BUFFER_PROP_SHOW_DATA]                     = (buffer_type>BUFFER_TYPE_CALCULATE ? true : false);
   this.m_long_prop[BUFFER_PROP_SHIFT]                         = 0;
   this.m_long_prop[BUFFER_PROP_LINE_STYLE]                    = STYLE_SOLID;
   this.m_long_prop[BUFFER_PROP_LINE_WIDTH]                    = width;
   this.m_long_prop[BUFFER_PROP_COLOR_INDEXES]                 = (this.Status()>BUFFER_STATUS_NONE ? (this.Status()!=BUFFER_STATUS_FILLING ? 1 : 2) : 0);
   this.m_long_prop[BUFFER_PROP_COLOR]                         = clrRed;
   this.m_long_prop[BUFFER_PROP_NUM_DATAS]                     = num_datas;
   this.m_long_prop[BUFFER_PROP_INDEX_PLOT]                    = index_plot;
   this.m_long_prop[BUFFER_PROP_INDEX_BASE]                    = index_base_array;
   this.m_long_prop[BUFFER_PROP_INDEX_COLOR]                   = this.GetProperty(BUFFER_PROP_INDEX_BASE);
   this.m_long_prop[BUFFER_PROP_INDEX_NEXT_BASE]               = index_base_array+this.m_total_arrays;
   this.m_long_prop[BUFFER_PROP_INDEX_NEXT_PLOT]               = (this.TypeBuffer()>BUFFER_TYPE_CALCULATE ? index_plot+1 : index_plot);
   
//--- Сохранение вещественных свойств
   this.m_double_prop[this.IndexProp(BUFFER_PROP_EMPTY_VALUE)] = (this.TypeBuffer()>BUFFER_TYPE_CALCULATE ? EMPTY_VALUE : 0);
//--- Сохранение строковых свойств
   this.m_string_prop[this.IndexProp(BUFFER_PROP_SYMBOL)]      = ::Symbol();
   this.m_string_prop[this.IndexProp(BUFFER_PROP_LABEL)]       = (this.TypeBuffer()>BUFFER_TYPE_CALCULATE ? label : NULL);
   this.m_string_prop[this.IndexProp(BUFFER_PROP_IND_NAME)]    = NULL;
   this.m_string_prop[this.IndexProp(BUFFER_PROP_IND_NAME_SHORT)]=NULL;

//--- Если не удалось изменить размер массива индикаторных буферов на новый - выводим об этом сообщение с указанием на строку
   if(::ArrayResize(this.DataBuffer,(int)this.GetProperty(BUFFER_PROP_NUM_DATAS))==WRONG_VALUE)
      ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_SYS_FAILED_DRAWING_ARRAY_RESIZE),". ",CMessage::Text(MSG_LIB_SYS_ERROR),": ",(string)::GetLastError());
      
//--- Связывание индикаторных буферов с массивами
//--- В цикле по количеству индикаторных буферов
   int total=::ArraySize(DataBuffer);
   for(int i=0;i<total;i++)
     {
      //--- рассчитываем индекс очередного массива и
      //--- связываем индикаторный буфер по рассчитанному индексу с динамическим массивом,
      //--- находящимся по индексу цикла i в массиве DataBuffer
      int index=(int)this.GetProperty(BUFFER_PROP_INDEX_BASE)+i;
      ::SetIndexBuffer(index,this.DataBuffer[i].Array,(this.TypeBuffer()==BUFFER_TYPE_DATA ? INDICATOR_DATA : INDICATOR_CALCULATIONS));
      //--- Установка всем массивам буферов флага индексации как в таймсерии
      ::ArraySetAsSeries(this.DataBuffer[i].Array,true);
     }

//--- Если буфер расчётный - закончили
   if(this.TypeBuffer()==BUFFER_TYPE_CALCULATE)
      return;
//--- Установка целочисленных параметров графической серии
   this.SetDrawType(type);
   ::SetIndexStyle((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),
                   (ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_DRAW_TYPE),
                   (ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_LINE_STYLE),
                   (ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_LINE_WIDTH),
                   (ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_COLOR));
   ::SetIndexArrow((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_ARROW_CODE));
   ::SetIndexShift((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_ARROW_SHIFT));
   ::SetIndexDrawBegin((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_DRAW_BEGIN));
   ::SetIndexShift((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_SHIFT));
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_SHOW_DATA,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_SHOW_DATA));
   
//--- Установка вещественных параметров графической серии
   ::SetIndexEmptyValue((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),this.GetProperty(BUFFER_PROP_EMPTY_VALUE));
   
//--- Установка строковых параметров графической серии
   ::SetIndexLabel((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),this.GetProperty(BUFFER_PROP_LABEL));
   
#endif 
  }
//+------------------------------------------------------------------+

Основное отличие тут в расчёте типа рисования. Для MQL5 мы его рассчитываем от типа буфера (его статуса), здесь же проще было просто присвоить нужные значения. Для установки индикаторному буферу необходимых значений используем соответствующие mql4-функции, так как хоть mql5-функции PlotIndexSetInteger(), PlotIndexSetDouble() и PlotIndexSetString() и не вызывают ошибок компиляции, но они с таким же успехом и никак не устанавливают в MQL4 необходимых значений индикаторному буферу.

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

//+------------------------------------------------------------------+
//| Устанавливает тип графического построения по типу и статусу      |
//+------------------------------------------------------------------+
void CBuffer::SetDrawType(void)
  {
   ENUM_DRAW_TYPE type=(!this.TypeBuffer() || !this.Status() ? (ENUM_DRAW_TYPE)DRAW_NONE : this.Status()==BUFFER_STATUS_FILLING ? (ENUM_DRAW_TYPE)DRAW_FILLING : ENUM_DRAW_TYPE(this.Status()+8));
   this.SetProperty(BUFFER_PROP_DRAW_TYPE,type);
   #ifdef __MQL5__
      ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_DRAW_TYPE,type);
   #else 
      ::SetIndexStyle((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),type,EMPTY,EMPTY,EMPTY);
   #endif 
  }
//+------------------------------------------------------------------+
//| Устанавливает переданный тип графического построения             |
//+------------------------------------------------------------------+
void CBuffer::SetDrawType(const ENUM_DRAW_TYPE draw_type)
  {
   if(this.TypeBuffer()==BUFFER_TYPE_CALCULATE)
      return;
   this.SetProperty(BUFFER_PROP_DRAW_TYPE,draw_type);
   #ifdef __MQL5__
      ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_DRAW_TYPE,draw_type);
   #else 
      ::SetIndexStyle((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),draw_type,EMPTY,EMPTY,EMPTY);
   #endif 
  }
//+------------------------------------------------------------------+
//| Устанавливает количество начальных баров                         |
//| без отрисовки и значений в DataWindow                            |
//+------------------------------------------------------------------+
void CBuffer::SetDrawBegin(const int value)
  {
   if(this.TypeBuffer()==BUFFER_TYPE_CALCULATE)
      return;
   this.SetProperty(BUFFER_PROP_DRAW_BEGIN,value);
   #ifdef __MQL5__
      ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_DRAW_BEGIN,value);
   #else 
      ::SetIndexDrawBegin((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),value);
   #endif 
  }
//+------------------------------------------------------------------+
//| Устанавливает признак отображения                                |
//| значений построения в окне DataWindow                            |
//+------------------------------------------------------------------+
void CBuffer::SetShowData(const bool flag)
  {
   if(this.TypeBuffer()==BUFFER_TYPE_CALCULATE)
      return;
   this.SetProperty(BUFFER_PROP_SHOW_DATA,flag);
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_SHOW_DATA,flag);
  }
//+------------------------------------------------------------------+
//| Устанавливает сдвиг графического построения индикатора           |
//+------------------------------------------------------------------+
void CBuffer::SetShift(const int shift)
  {
   this.SetProperty(BUFFER_PROP_SHIFT,shift);
   if(this.TypeBuffer()==BUFFER_TYPE_CALCULATE)
      return;
   #ifdef __MQL5__
      ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_SHIFT,shift);
   #else 
      ::SetIndexShift((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),shift);
   #endif 
  }
//+------------------------------------------------------------------+
//| Устанавливает стиль линии отрисовки                              |
//+------------------------------------------------------------------+
void CBuffer::SetStyle(const ENUM_LINE_STYLE style)
  {
   if(this.TypeBuffer()==BUFFER_TYPE_CALCULATE)
      return;
   this.SetProperty(BUFFER_PROP_LINE_STYLE,style);
   #ifdef __MQL5__
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LINE_STYLE,style);
   #else 
      ::SetIndexStyle((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),this.DrawType(),style,EMPTY,EMPTY);
   #endif 
  }
//+------------------------------------------------------------------+
//| Устанавливает толщину линии отрисовки                            |
//+------------------------------------------------------------------+
void CBuffer::SetWidth(const int width)
  {
   if(this.TypeBuffer()==BUFFER_TYPE_CALCULATE)
      return;
   this.SetProperty(BUFFER_PROP_LINE_WIDTH,width);
   #ifdef __MQL5__
      ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LINE_WIDTH,width);
   #else 
      ::SetIndexStyle((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),this.DrawType(),EMPTY,width,EMPTY);
   #endif 
  }
//+------------------------------------------------------------------+
//| Устанавливает количество цветов                                  |
//+------------------------------------------------------------------+
void CBuffer::SetColorNumbers(const int number)
  {
   if(number>IND_COLORS_TOTAL || this.TypeBuffer()==BUFFER_TYPE_CALCULATE)
      return;
   int n=(this.Status()!=BUFFER_STATUS_FILLING ? number : 2);
   this.SetProperty(BUFFER_PROP_COLOR_INDEXES,n);
   ::ArrayResize(this.ArrayColors,n);
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_COLOR_INDEXES,n);
  }
//+------------------------------------------------------------------+
//| Устанавливает буферу один заданный цвет отрисовки                |
//+------------------------------------------------------------------+
void CBuffer::SetColor(const color colour)
  {
   if(this.Status()==BUFFER_STATUS_FILLING || this.TypeBuffer()==BUFFER_TYPE_CALCULATE)
      return;
   this.SetColorNumbers(1);
   this.SetProperty(BUFFER_PROP_COLOR,colour);
   this.ArrayColors[0]=colour;
   #ifdef __MQL5__
      ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LINE_COLOR,0,this.ArrayColors[0]);
   #else 
      ::SetIndexStyle((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),this.DrawType(),EMPTY,EMPTY,this.ArrayColors[0]);
   #endif 
  }
//+------------------------------------------------------------------+
//| Устанавливает цвет отрисовки в указанный индекс цвета            |
//+------------------------------------------------------------------+
void CBuffer::SetColor(const color colour,const uchar index)
  {
#ifdef __MQL5__
   if(index>IND_COLORS_TOTAL-1 || this.TypeBuffer()==BUFFER_TYPE_CALCULATE)
      return;
   if(index>this.ColorsTotal()-1)
      this.SetColorNumbers(index+1);
   this.ArrayColors[index]=colour;
   if(index==0)
      this.SetProperty(BUFFER_PROP_COLOR,(color)this.ArrayColors[0]);
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LINE_COLOR,index,this.ArrayColors[index]);
#else 
#endif 
  }
//+------------------------------------------------------------------+
//| Устанавливает цвета отрисовки из массива цветов                  |
//+------------------------------------------------------------------+
void CBuffer::SetColors(const color &array_colors[])
  {
#ifdef __MQL5__
//--- Если переданный массив пустой - уходим
   if(::ArraySize(array_colors)==0 || this.TypeBuffer()==BUFFER_TYPE_CALCULATE)
      return;
//--- Копируем переданный массив в массив цветов объекта-буфера
   ::ArrayCopy(this.ArrayColors,array_colors,0,0,IND_COLORS_TOTAL);
//--- Если массив цветов был пуст, и по какой-то причине не был скопирован - уходим
   int total=::ArraySize(this.ArrayColors);
   if(total==0)
      return;
//--- Если стиль рисования - не DRAW_FILLING
   if(this.Status()!=BUFFER_STATUS_FILLING)
     {
      //--- если новое количество цветов больше установленного на данный момент - 
      //--- устанавливаем новое значение количества цветов
      if(total>this.ColorsTotal())
         this.SetColorNumbers(total);
     }
   //--- Если стиль рисования - DRAW_FILLING - устанавливаем количество цветов равным 2
   else
      total=2;
//--- Устанавливаем в свойство цвета объекта-буфера самый первый цвет из массива цветов (для одного цвета)
   this.SetProperty(BUFFER_PROP_COLOR,(color)this.ArrayColors[0]);
//--- Устанавливаем индикаторному буферу новое количество цветов
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_COLOR_INDEXES,total);
//--- В цикле по новому количеству цветов устанавливаем индикаторному буферу все цвета по их индексам
   for(int i=0;i<total;i++)
      ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LINE_COLOR,i,this.ArrayColors[i]);
#else 
#endif 
  }
//+------------------------------------------------------------------+
//| Устанавливает "пустое" значение для построения,                  |
//| для которого нет отрисовки                                       |
//+------------------------------------------------------------------+
void CBuffer::SetEmptyValue(const double value)
  {
   this.SetProperty(BUFFER_PROP_EMPTY_VALUE,value);
   if(this.TypeBuffer()!=BUFFER_TYPE_CALCULATE)
      #ifdef __MQL5__
         ::PlotIndexSetDouble((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_EMPTY_VALUE,value);
      #else 
         ::SetIndexEmptyValue((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),value);
      #endif 
  }
//+------------------------------------------------------------------+
//| Устанавливает имя индикаторной графической серии                 |
//+------------------------------------------------------------------+
void CBuffer::SetLabel(const string label)
  {
   this.SetProperty(BUFFER_PROP_LABEL,label);
   if(this.TypeBuffer()!=BUFFER_TYPE_CALCULATE)
      #ifdef __MQL5__
         ::PlotIndexSetString((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LABEL,label);
      #else 
         ::SetIndexLabel((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),label);
      #endif 
  }
//+------------------------------------------------------------------
//+------------------------------------------------------------------+
//| Устанавливает индекс цвета в указанный индекс таймсерии          |
//| массива буфера цвета                                             |
//+------------------------------------------------------------------+
void CBuffer::SetBufferColorIndex(const uint series_index,const uchar color_index)
  {
   #ifdef __MQL4__
      return;
   #endif 
   if(this.GetDataTotal(0)==0 || color_index>this.ColorsTotal()-1 || this.Status()==BUFFER_STATUS_FILLING || this.Status()==BUFFER_STATUS_NONE)
      return;
   int data_total=this.GetDataTotal(0);
   int data_index=((int)series_index<data_total ? (int)series_index : data_total-1);
   if(::ArraySize(this.ColorBufferArray)==0)
      ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR),": ",CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_INVALID_PROPERTY_BUFF));
   if(data_index<0)
      return;
   this.ColorBufferArray[data_index]=color_index;
  }
//+------------------------------------------------------------------+

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

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

//+------------------------------------------------------------------+
//| Копирует данные указанного индикатора в массив объекта-буфера    |
//+------------------------------------------------------------------+
int CBufferCalculate::FillAsSeries(const int indicator_handle,const int buffer_num,const int start_pos,const int count)
  {
   return(#ifdef __MQL5__ ::CopyBuffer(indicator_handle,buffer_num,-start_pos,count,this.DataBuffer[0].Array) #else count #endif );
  }
//+------------------------------------------------------------------+
int CBufferCalculate::FillAsSeries(const int indicator_handle,const int buffer_num,const datetime start_time,const int count)
  {
   return(#ifdef __MQL5__ ::CopyBuffer(indicator_handle,buffer_num,start_time,count,this.DataBuffer[0].Array) #else count #endif );
  }
//+------------------------------------------------------------------+
int CBufferCalculate::FillAsSeries(const int indicator_handle,const int buffer_num,const datetime start_time,const datetime stop_time)
  {
   return
     (
      #ifdef __MQL5__ ::CopyBuffer(indicator_handle,buffer_num,start_time,stop_time,this.DataBuffer[0].Array) 
      #else int(::fabs(start_time-stop_time)/::PeriodSeconds(this.Timeframe())+1) 
      #endif 
     );
  }
//+------------------------------------------------------------------+

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

Все объекты-буферы для стандартных индикаторов мы создаём в классе-коллекции индикаторных буферов
в файле \MQL5\Include\DoEasy\Collections\BuffersCollection.mqh.
Данный класс у нас тоже подвергся доработке для совместимости с MQL4.

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

//+------------------------------------------------------------------+
//| Создаёт мультисимвольный мультипериодный AC                      |
//+------------------------------------------------------------------+
int CBuffersCollection::CreateAC(const string symbol,const ENUM_TIMEFRAMES timeframe,const int id=WRONG_VALUE)
  {
//--- Создаём хэндл индикатора и устанавливаем идентификатор по умолчанию
   int handle= #ifdef __MQL5__ ::iAC(symbol,timeframe) #else 0 #endif ;
   int identifier=(id==WRONG_VALUE ? IND_AC : id);
   color array_colors[3]={clrGreen,clrRed,clrGreen};
   CBuffer *buff=NULL;
   if(handle!=INVALID_HANDLE)
     {
      //--- Создаём буфер-гистограмму от нулевой линии
      this.CreateHistogram();

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

Строка с эмуляцией создания хэндла уже добавлена во все методы создания объектов стандартных индикаторов.
Здесь их рассматривать не будем, а рассмотрим на примере метода создания индикатора AD изменения, необходимые для создания однобуферного одноцветного стандартного индикатора для MQL4:

//+------------------------------------------------------------------+
//| Создаёт мультисимвольный мультипериодный AD                      |
//+------------------------------------------------------------------+
int CBuffersCollection::CreateAD(const string symbol,const ENUM_TIMEFRAMES timeframe,const ENUM_APPLIED_VOLUME applied_volume,const int id=WRONG_VALUE)
  {
//--- Создаём хэндл индикатора и устанавливаем идентификатор по умолчанию
   int handle= #ifdef __MQL5__ ::iAD(symbol,timeframe,applied_volume) #else 0 #endif ;
   int identifier=(id==WRONG_VALUE ? IND_AD : id);
   color array_colors[1]={clrLightSeaGreen};
   CBuffer *buff=NULL;
   if(handle!=INVALID_HANDLE)
     {
      //--- Создаём буфер-линию
      this.CreateLine();
      //--- Получаем последний созданный объект-буфер (рисуемый) и устанавливаем ему все необходимые параметры
      buff=this.GetLastCreateBuffer();
      if(buff==NULL)
         return INVALID_HANDLE;
      buff.SetSymbol(symbol);
      buff.SetTimeframe(timeframe);
      buff.SetID(identifier);
      buff.SetIndicatorHandle(handle);
      buff.SetIndicatorType(IND_AD);
      buff.SetLineMode(INDICATOR_LINE_MODE_MAIN);
      buff.SetShowData(true);
      buff.SetLabel("A/D("+symbol+","+TimeframeDescription(timeframe)+")");
      buff.SetIndicatorName("Accumulation/Distribution");
      buff.SetIndicatorShortName("A/D("+symbol+","+TimeframeDescription(timeframe)+")");
      #ifdef __MQL5__ buff.SetColors(array_colors); #else buff.SetColor(array_colors[0]); #endif 

      //--- MQL5
      #ifdef __MQL5__
         //--- Создаём расчётный буфер, в котором будут храниться данные стандартного индикатора
         this.CreateCalculate();
         //--- Получаем последний созданный объект-буфер (расчётный) и устанавливаем ему все необходимые параметры
         buff=this.GetLastCreateBuffer();
         if(buff==NULL)
            return INVALID_HANDLE;
         buff.SetSymbol(symbol);
         buff.SetTimeframe(timeframe);
         buff.SetID(identifier);
         buff.SetIndicatorHandle(handle);
         buff.SetIndicatorType(IND_AD);
         buff.SetLineMode(INDICATOR_LINE_MODE_MAIN);
         buff.SetEmptyValue(EMPTY_VALUE);
         buff.SetLabel("A/D("+symbol+","+TimeframeDescription(timeframe)+")");
         buff.SetIndicatorName("Accumulation/Distribution");
         buff.SetIndicatorShortName("A/D("+symbol+","+TimeframeDescription(timeframe)+")");
      #endif 
     }
   return handle;
  }
//+------------------------------------------------------------------+

Здесь для MQL5 устанавливаем буферу набор его цветов методом их передачи в объект посредством массива цветов, а для MQL4 устанавливаем всего один цвет — самый первый в массиве цветов. Расчётный же буфер нам нужен только для MQL5 — в нём будут храниться данные созданного индикатора AD на указанном символе и периоде графика. Для MQL4 такой буфер не нужен, так как все данные будем получать непосредственно из функции обращения к индикатору iAD().

Метод, подготавливающий данные указанного стандартного индикатора для установки значений на текущий график символа, для MQL5 считывает данные из расчётного буфера. Для MQL4 нам достаточно просто получить запрашиваемые данные из функции обращения к данным стандартного индикатора:

//+------------------------------------------------------------------+
//| Подготавливает данные указанного стандартного индикатора         |
//| для установки значений на текущий график символа                 |
//+------------------------------------------------------------------+
int CBuffersCollection::PreparingSetDataStdInd(CBuffer *buffer_data0,CBuffer *buffer_data1,CBuffer *buffer_data2,CBuffer *buffer_data3,CBuffer *buffer_data4,
                                               CBuffer *buffer_calc0,CBuffer *buffer_calc1,CBuffer *buffer_calc2,CBuffer *buffer_calc3,CBuffer *buffer_calc4,
                                               const ENUM_INDICATOR ind_type,
                                               const int series_index,
                                               const datetime series_time,
                                               int &index_period,
                                               int &num_bars,
                                               double &value00,
                                               double &value01,
                                               double &value10,
                                               double &value11,
                                               double &value20,
                                               double &value21,
                                               double &value30,
                                               double &value31,
                                               double &value40,
                                               double &value41)
  {
     //--- Находим индекс бара на периоде, соответствующий времени начала текущего бара
     index_period=::iBarShift(buffer_data0.Symbol(),buffer_data0.Timeframe(),series_time,true);
     if(index_period==WRONG_VALUE || #ifdef __MQL5__ index_period>buffer_calc0.GetDataTotal()-1 #else index_period>buffer_data0.GetDataTotal()-1 #endif )
        return WRONG_VALUE;
     
     //--- Для MQL5
     #ifdef __MQL5__
        //--- Получаем значение по этому индексу из буфера индикатора
        if(buffer_calc0!=NULL)
           value00=buffer_calc0.GetDataBufferValue(0,index_period);
        if(buffer_calc1!=NULL)
           value10=buffer_calc1.GetDataBufferValue(0,index_period);
        if(buffer_calc2!=NULL)
           value20=buffer_calc2.GetDataBufferValue(0,index_period);
        if(buffer_calc3!=NULL)
           value30=buffer_calc3.GetDataBufferValue(0,index_period);
        if(buffer_calc4!=NULL)
           value40=buffer_calc4.GetDataBufferValue(0,index_period);
     
     //--- для MQL4
     #else 
        switch((int)ind_type)
          {
           //--- Однобуферные стандартные индикаторы
           case IND_AC           :  value00=::iAC(buffer_data0.Symbol(),buffer_data0.Timeframe(),index_period);      break;
           case IND_AD           :  value00=::iAD(buffer_data0.Symbol(),buffer_data0.Timeframe(),index_period);      break;
           case IND_AMA          :  break;
           case IND_AO           :  value00=::iAO(buffer_data0.Symbol(),buffer_data0.Timeframe(),index_period);      break;
           case IND_ATR          :  break;
           case IND_BEARS        :  break;
           case IND_BULLS        :  break;
           case IND_BWMFI        :  value00=::iBWMFI(buffer_data0.Symbol(),buffer_data0.Timeframe(),index_period);   break;
           case IND_CCI          :  break;
           case IND_CHAIKIN      :  break;
           case IND_DEMA         :  break;
           case IND_DEMARKER     :  break;
           case IND_FORCE        :  break;
           case IND_FRAMA        :  break;
           case IND_MA           :  break;
           case IND_MFI          :  break;
           case IND_MOMENTUM     :  break;
           case IND_OBV          :  break;
           case IND_OSMA         :  break;
           case IND_RSI          :  break;
           case IND_SAR          :  break;
           case IND_STDDEV       :  break;
           case IND_TEMA         :  break;
           case IND_TRIX         :  break;
           case IND_VIDYA        :  break;
           case IND_VOLUMES      :  value00=(double)::iVolume(buffer_data0.Symbol(),buffer_data0.Timeframe(),index_period);  break;
           case IND_WPR          :  break;
            
           //--- Многобуферные стандартные индикаторы
           case IND_ENVELOPES    :  break;
           case IND_FRACTALS     :  break;
           
           case IND_ADX          :  break;
           case IND_ADXW         :  break;
           case IND_BANDS        :  break;
           case IND_MACD         :  break;
           case IND_RVI          :  break;
           case IND_STOCHASTIC   :  break;
           case IND_ALLIGATOR    :  break;
           
           case IND_ICHIMOKU     :  break;
           case IND_GATOR        :  break;
           
           default:
             break;
          }
     #endif 
     
     int series_index_start=series_index;
     //--- Для текущего графика никаких расчётов количества обрабатываемых баров делать не нужно - бар всего один
     if(buffer_data0.Symbol()==::Symbol() && buffer_data0.Timeframe()==::Period())
       {
        series_index_start=series_index;
        num_bars=1;
       }
     else
       {
        //--- Получаем время бара, в который попадает бар с индексом index_period на периоде и символе расчётного буфера
        datetime time_period=::iTime(buffer_data0.Symbol(),buffer_data0.Timeframe(),index_period);
        if(time_period==0) return false;
        //--- Получаем соответствующий времени бар текущего графика
        series_index_start=::iBarShift(::Symbol(),::Period(),time_period,true);
        if(series_index_start==WRONG_VALUE) return WRONG_VALUE;
        //--- Рассчитываем количество баров на текущем графике, которые нужно заполнить данными расчётного буфера
        num_bars=::PeriodSeconds(buffer_data0.Timeframe())/::PeriodSeconds(PERIOD_CURRENT);
        if(num_bars==0) num_bars=1;
       }
     //--- Записываем значения для расчёта цвета
     if(buffer_data0!=NULL)
        value01=(series_index_start+num_bars>buffer_data0.GetDataTotal()-1 ? value00 : buffer_data0.GetDataBufferValue(0,series_index_start+num_bars));
     if(buffer_data1!=NULL)
        value11=(series_index_start+num_bars>buffer_data1.GetDataTotal()-1 ? value10 : buffer_data1.GetDataBufferValue(0,series_index_start+num_bars));
     if(buffer_data2!=NULL)
        value21=(series_index_start+num_bars>buffer_data2.GetDataTotal()-1 ? value20 : buffer_data2.GetDataBufferValue(0,series_index_start+num_bars));
     if(buffer_data3!=NULL)
        value31=(series_index_start+num_bars>buffer_data3.GetDataTotal()-1 ? value30 : buffer_data3.GetDataBufferValue(0,series_index_start+num_bars));
     if(buffer_data4!=NULL)
        value41=(series_index_start+num_bars>buffer_data4.GetDataTotal()-1 ? value40 : buffer_data4.GetDataBufferValue(0,series_index_start+num_bars));
   
   return series_index_start;
  }
//+------------------------------------------------------------------+

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

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

//+------------------------------------------------------------------+
//| Устанавливает значения для текущего графика в буферы указанного  |
//| стандартного индикатора по индексу таймсерии в соответствии      |
//| с символом/периодом объекта-буфера                               |
//+------------------------------------------------------------------+
bool CBuffersCollection::SetDataBufferStdInd(const ENUM_INDICATOR ind_type,const int id,const int series_index,const datetime series_time,const char color_index=WRONG_VALUE)
  {
//--- Получаем список объектов-буферов по типу и идентификатору
   CArrayObj *list=this.GetListBufferByTypeID(ind_type,id);
   if(list==NULL || list.Total()==0)
     {
      ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_NO_BUFFER_OBJ));
      return false;
     }
     
//--- Получаем список рисуемых буферов с идентификатором
   CArrayObj *list_data=CSelect::ByBufferProperty(list,BUFFER_PROP_TYPE,BUFFER_TYPE_DATA,EQUAL);
   list_data=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_TYPE,ind_type,EQUAL);
//--- Получаем список расчётных буферов с идентификатором
   CArrayObj *list_calc=CSelect::ByBufferProperty(list,BUFFER_PROP_TYPE,BUFFER_TYPE_CALCULATE,EQUAL);
   list_calc=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_TYPE,ind_type,EQUAL);
 
//--- Если любой из списков пустой - уходим
   if(list_data.Total()==0 #ifdef __MQL5__ || list_calc.Total()==0 #endif )
      return false;
  
//--- Объявляем необходимые объекты и переменные
   CBuffer *buffer_data0=NULL,*buffer_data1=NULL,*buffer_data2=NULL,*buffer_data3=NULL,*buffer_data4=NULL,*buffer_tmp0=NULL,*buffer_tmp1=NULL;
   CBuffer *buffer_calc0=NULL,*buffer_calc1=NULL,*buffer_calc2=NULL,*buffer_calc3=NULL,*buffer_calc4=NULL;

   double value00=EMPTY_VALUE, value01=EMPTY_VALUE;
   double value10=EMPTY_VALUE, value11=EMPTY_VALUE;
   double value20=EMPTY_VALUE, value21=EMPTY_VALUE;
   double value30=EMPTY_VALUE, value31=EMPTY_VALUE;
   double value40=EMPTY_VALUE, value41=EMPTY_VALUE;
   double value_tmp0=EMPTY_VALUE,value_tmp1=EMPTY_VALUE;
   long vol0=0,vol1=0;
   int series_index_start=series_index,index_period=0, index=0,num_bars=1;
   uchar clr=0;
//--- В зависимости от типа стандартного индикатора

   switch((int)ind_type)
     {
   //--- Однобуферные стандартные индикаторы
      case IND_AC       :
      case IND_AD       :
      case IND_AMA      :
      case IND_AO       :
      case IND_ATR      :
      case IND_BEARS    :
      case IND_BULLS    :
      case IND_BWMFI    :
      case IND_CCI      :
      case IND_CHAIKIN  :
      case IND_DEMA     :
      case IND_DEMARKER :
      case IND_FORCE    :
      case IND_FRAMA    :
      case IND_MA       :
      case IND_MFI      :
      case IND_MOMENTUM :
      case IND_OBV      :
      case IND_OSMA     :
      case IND_RSI      :
      case IND_SAR      :
      case IND_STDDEV   :
      case IND_TEMA     :
      case IND_TRIX     :
      case IND_VIDYA    :
      case IND_VOLUMES  :
      case IND_WPR      :
        list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,0,EQUAL);
        buffer_data0=list.At(0);
      #ifdef __MQL5__
        list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,0,EQUAL);
        buffer_calc0=list.At(0);
      #endif 
        
        if(buffer_data0==NULL #ifdef __MQL5__ || buffer_calc0==NULL || buffer_calc0.GetDataTotal(0)==0 #endif )
           return false;

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

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

Так как проверку соответствия количества созданных библиотекой объектов-буферов со значениями в #property программы мы решили перенести в библиотеку, то добавим такой метод в класс главного объекта библиотеки CEngine в файле \MQL5\Include\DoEasy\Engine.mqh.

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

//--- Выводит краткое описание всех индикаторных буферов коллекции буферов
   void                 BuffersPrintShort(void);

//--- Для индикаторов указывает требуемое количество буферов
   void                 CheckIndicatorsBuffers(const int buffers,const int plots #ifdef __MQL4__ =1 #endif );
 
//--- Возвращает индекс бара на графике указанного таймфрейма по индексу бара текущего графика

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

//+------------------------------------------------------------------+
//| Для индикаторов указывает требуемое количество буферов           |
//+------------------------------------------------------------------+
void CEngine::CheckIndicatorsBuffers(const int buffers,const int plots #ifdef __MQL4__ =1 #endif )
  {
   #ifdef __MQL5__
      if(this.BuffersPropertyPlotsTotal()!=plots)
         ::Alert(CMessage::Text(MSG_ENG_ERR_VALUE_PLOTS),this.BuffersPropertyPlotsTotal());
      if(this.BuffersPropertyBuffersTotal()!=buffers)
         ::Alert(CMessage::Text(MSG_ENG_ERR_VALUE_ORDERS),this.BuffersPropertyBuffersTotal());
   #else 
      if(buffers!=this.BuffersPropertyPlotsTotal())
         ::Alert(CMessage::Text(MSG_ENG_ERR_VALUE_ORDERS),this.BuffersPropertyPlotsTotal());
      ::IndicatorBuffers(this.BuffersPropertyBuffersTotal());
   #endif 
  }
//+------------------------------------------------------------------+

Для MQL5 мы просто выводим на экран предупреждающие сообщения о несоответствии созданного количества индикаторных буферов (рисуемых и расчётных) с указанным значением в #property программы-индикатора.
Для MQL4 при несоответствии значения, указанного в #property indicator_buffers, выводим на экран об этом сообщение и устанавливаем общее количество всех индикаторных буферов (рисуемых и расчётных) в соответствии с общим количеством всех созданных библиотекой буферов
.

Теперь осталось установить разрядность выводимых данных для индикаторов в MQL4. Для этого доработаем функцию для установки разрядности и уровней стандартным индикаторам в файле сервисных функций библиотеки \MQL5\Include\DoEasy\Services\DELib.mqh:

//+------------------------------------------------------------------+
//| Устанавливает разрядность и уровни стандартному индикатору       |
//+------------------------------------------------------------------+
void SetIndicatorLevels(const string symbol,const ENUM_INDICATOR ind_type)
  {
   int digits=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS);
   switch(ind_type)
     {
      case IND_AD          :
      case IND_CHAIKIN     :
      case IND_OBV         :
      case IND_VOLUMES     : digits=0;    break;
      
      case IND_AO          :
      case IND_BEARS       :
      case IND_BULLS       :
      case IND_FORCE       :
      case IND_STDDEV      :
      case IND_AMA         :
      case IND_DEMA        :
      case IND_FRAMA       :
      case IND_MA          :
      case IND_TEMA        :
      case IND_VIDYA       :
      case IND_BANDS       :
      case IND_ENVELOPES   :
      case IND_MACD        : digits+=1;   break;
      
      case IND_AC          :
      case IND_OSMA        : digits+=2;   break;
      
      case IND_MOMENTUM    : digits=2;    break;
      
      case IND_CCI         :
        IndicatorSetInteger(INDICATOR_LEVELS,2);
        IndicatorSetDouble(INDICATOR_LEVELVALUE,0,100);
        IndicatorSetDouble(INDICATOR_LEVELVALUE,1,-100);
        digits=2;
        break;
      case IND_DEMARKER    :
        IndicatorSetInteger(INDICATOR_LEVELS,2);
        IndicatorSetDouble(INDICATOR_LEVELVALUE,0,0.7);
        IndicatorSetDouble(INDICATOR_LEVELVALUE,1,0.3);
        digits=3;
        break;
      case IND_MFI         :
        IndicatorSetInteger(INDICATOR_LEVELS,2);
        IndicatorSetDouble(INDICATOR_LEVELVALUE,0,80);
        IndicatorSetDouble(INDICATOR_LEVELVALUE,1,20);
        break;
      case IND_RSI         :
        IndicatorSetInteger(INDICATOR_LEVELS,3);
        IndicatorSetDouble(INDICATOR_LEVELVALUE,0,70);
        IndicatorSetDouble(INDICATOR_LEVELVALUE,1,50);
        IndicatorSetDouble(INDICATOR_LEVELVALUE,2,30);
        digits=2;
        break;
      case IND_STOCHASTIC  :
        IndicatorSetInteger(INDICATOR_LEVELS,2);
        IndicatorSetDouble(INDICATOR_LEVELVALUE,0,80);
        IndicatorSetDouble(INDICATOR_LEVELVALUE,1,20);
        digits=2;
        break;
      case IND_WPR         :
        IndicatorSetInteger(INDICATOR_LEVELS,2);
        IndicatorSetDouble(INDICATOR_LEVELVALUE,0,-80);
        IndicatorSetDouble(INDICATOR_LEVELVALUE,1,-20);
        digits=2;
        break;
     
      case IND_ATR         :              break;
      case IND_SAR         :              break;
      case IND_TRIX        :              break;
      
      default:
        IndicatorSetInteger(INDICATOR_LEVELS,0);
        break;
     }
   #ifdef __MQL5__
      IndicatorSetInteger(INDICATOR_DIGITS,digits);
   #else 
      IndicatorDigits(digits);
   #endif 
  }
//+------------------------------------------------------------------+

Здесь для MQL4 используем для установки разрядности отображаемых данных индикатора стандартную mql4-функцию IndicatorDigits().

На этом доработка классов библиотеки для создания однобуферных мультисимвольных мультипериодных стандартных индикаторов завершена.


Тест

Для тестирования возьмём второй индикатор (TestDoEasyPart51_2.mq5) из прошлой статьи и
сохраним его в папке индикаторов терминала MetaTrader 4 \MQL4\Indicators\TestDoEasy\Part52\ под именем TestDoEasyPart52.mq4.

Прошлый тестовый индикатор создавал мультисимвольный мультипериодный стандартный индикатор Gator Oscillator. Нам же нужно создать индикатор Accumulation/Distribution.

В заголовке файла установим требуемое количество для MQL4 индикаторных буферов:

//+------------------------------------------------------------------+
//|                                             TestDoEasyPart52.mq4 |
//|                        Copyright 2020, MetaQuotes Software Corp. |
//|                             https://mql5.com/ru/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, MetaQuotes Software Corp."
#property link      "https://mql5.com/ru/users/artmedia70"
#property version   "1.00"
//--- includes
#include <DoEasy\Engine.mqh>
//--- properties
#property indicator_separate_window
#property indicator_buffers 1
#property indicator_plots   1

//--- classes

//--- enums

//--- defines

//--- structures

//--- input variables
sinput   string               InpUsedSymbols    =  "EURUSD";      // Used symbol (one only)
sinput   ENUM_TIMEFRAMES      InpPeriod         =  PERIOD_H4;     // Used chart period
//---
sinput   bool                 InpUseSounds      =  true;          // Use sounds
//--- indicator buffers

//--- global variables
ENUM_SYMBOLS_MODE    InpModeUsedSymbols=  SYMBOLS_MODE_DEFINES;   // Mode of used symbols list
ENUM_TIMEFRAMES_MODE InpModeUsedTFs    =  TIMEFRAMES_MODE_LIST;   // Mode of used timeframes list
string               InpUsedTFs;                                  // Список используемых таймфреймов
CEngine              engine;                                      // Главный объект библиотеки CEngine
string               prefix;                                      // Префикс имён графических объектов
int                  min_bars;                                    // Минимальное количество баров для расчёта индикатора
int                  used_symbols_mode;                           // Режим работы с символами
string               array_used_symbols[];                        // Массив для передачи в библиотеку используемых символов
string               array_used_periods[];                        // Массив для передачи в библиотеку используемых таймфреймов
//+------------------------------------------------------------------+

В обработчике OnInit() создадим объект стандартного индикатора Accumulation/Distribution, и укажем где требуется тип индикатора AD:

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

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

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

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

//--- indicator buffers mapping
//--- Создаём все необходимые объекты-буферы для построения выбранного стандартного индикатора
   if(!engine.BufferCreateAD(InpUsedSymbols,InpPeriod,VOLUME_TICK,1))
     {
      Print(TextByLanguage("Ошибка. Индикатор не создан","Error. Indicator not created"));
      return INIT_FAILED;
     }
//--- Проверяем количество буферов, указанных в блоке properties
   engine.CheckIndicatorsBuffers(indicator_buffers,indicator_plots);

//--- Создаём массив цветов и зададём всем буферам в коллекции значения цвета не по умолчанию
//--- (закомментировано так как в методах создания стандартных индикаторов по умолчанию уже заданы цвета)
//--- (всегда можно задать нужные цвета либо для всех индикаторов как здесь, либо для каждого индивидуально)
   //color array_colors[]={clrGreen,clrRed,clrGray};
   //engine.BuffersSetColors(array_colors);

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

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

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

Ранее проверка соответствия количества указанных и созданных буферов индикатора проводилась в обработчике OnInit():

//--- Проверяем количество буферов, указанных в блоке properties
   if(engine.BuffersPropertyPlotsTotal()!=indicator_plots)
      Alert(TextByLanguage("Внимание! Значение \"indicator_plots\" должно быть ","Attention! Value of \"indicator_plots\" should be "),engine.BuffersPropertyPlotsTotal());
   if(engine.BuffersPropertyBuffersTotal()!=indicator_buffers)
      Alert(TextByLanguage("Внимание! Значение \"indicator_buffers\" должно быть ","Attention! Value of \"indicator_buffers\" should be "),engine.BuffersPropertyBuffersTotal());

Теперь же мы её заменили на вызов соответствующего метода библиотеки.

В обработчике OnCalculate() достаточно только поменять запись данных индикатора Gator Oscillator на запись данных индикатора Accumulation/Distribution в основном цикле программы:

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

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

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

Для mql5-версии индикатора, в отличии от mql4-версии нам потребуется поменять количество рисуемых и расчётных буферов, указываемое в #property:

//+------------------------------------------------------------------+
//|                                             TestDoEasyPart52.mq5 |
//|                        Copyright 2020, MetaQuotes Software Corp. |
//|                             https://mql5.com/ru/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, MetaQuotes Software Corp."
#property link      "https://mql5.com/ru/users/artmedia70"
#property version   "1.00"
//--- includes
#include <DoEasy\Engine.mqh>
//--- properties
#property indicator_separate_window
#property indicator_buffers 3
#property indicator_plots   1

Скомпилируем индикатор и запустим его на графике EURUSD H1 в терминале MetaTrader 4 со значениями во входных параметрах индикатора для символа EURUSD и для периода H4. Таким образом, мы отобразим индикатор AD, рассчитанный для EURUSD H4 на часовом графике EURUSD в терминале MetaTrader 4:


Что дальше

В следующей статье продолжим работу с индикаторами в MetaTrader 5 и неспеша будем работать над кроссплатформенностью библиотеки.

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

Хочу обратить внимание, что вся работа над совместимостью с предыдущей платформой делается только для поддержания мультиплатформенности библиотеки, изначально создаваемой для MQL5, и где она имеет больше преимуществ и функциональных возможностей.
Отдельных статей по работе в MQL4 с данной библиотекой не планировалось и не будет, а всё, чего окажется недостаточно при работе в MetaTrader 4 с данной библиотекой — всё можно доработать самостоятельно для себя каждому читателю. Я и далее буду стараться делать библиотеку совместимой с обеими платформами, но лишь по причине, чтобы пользователь библиотеки мог все свои программы, созданные на её основе, легко перенести на работу в MetaTrader 4.

К содержанию

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

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