Trabajando con las series temporales en la biblioteca DoEasy (Parte 52): Concepto multiplataforma de indicadores estándar de período y símbolo múltiples de búfer único

Artyom Trishkin | 7 enero, 2021

Contenido


Concepto

A partir del artículo 39 íbamos diseñando la funcionalidad para crear nuestros propios indicadores de período y símbolo múltiples. Era natural ofrecer a base creada la posibilidad de que los indicadores estándar funcionen en modos múltiples. Conseguimos crear dicha funcionalidad a partir del artículo 47 (hay algunos defectos que iremos detectando y corrigiendo). Pero todo lo que estábamos haciendo, lo hacíamos para la plataforma MetaTrader 5.
Vamos a mejorar un poco las clases de la biblioteca en cuanto a los indicadores para que los programas escritos para la plataforma obsoleta MetaTrader 4 y basados en la biblioteca en cuestión puedan funcionar sin problema cuando los usamos en MetaTrader 5.

A diferencia de MQL5, en MQL4 no podemos tener varios colores para el dibujado de un solo búfer (en MetaTrader 4, todos los búferes de indicador son monocromos). Esta limitación estampa su huella en el concepto de la construcción de búferes múltiples para MetaTrader 4. Aquí, no podemos indicar el color del dibujado para una determinada barra, como podemos hacerlo fácilmente en MetaTrader 5. Tendremos que usar un búfer de indicador monocromo para cada color. Pero no necesitamos crear búferes de cálculo adicionales para almacenar datos del indicador del símbolo/período especificado. Todas las funciones del acceso al indicador en MQL4 devuelven el valor por la barra indicada del símbolo/período especificado, mientras que en MQL5 tenemos que crear el manejador del indicador, y desde este manejador solicitar los datos mediante el copiado de la cantidad necesaria a la matriz receptora (para los indicadores es su búfer de cálculo). Y sólo después, obtenemos los datos del indicador especificado de esta matriz por el índice de la barra necesaria. Además, obtenemos la aceleración del acceso a los datos del indicador en MQL5.

De esta manera, la construcción del objeto de búfer para el indicador estándar en MQL4 es diferente. No tendremos que crear los búferes de cálculo adicionales para almacenar la información de los datos del indicador a mostrar en el gráfico actual. Pero con esta aparente simplificación perdemos en la flexibilidad. Para crear un búfer de color, necesitamos su propio búfer de indicador monocromo para cada color. Y al especificar el color necesario de la barra, ahora tenemos que mostrar el búfer correspondiente al color y ocultar el resto de búferes. ¡Y es una complicación!

En vista de lo expuesto, el concepto de la construcción de búferes múltiples para MQL4 será el siguiente:

En conclusión, tenemos lo siguiente: para el indicador Moving Average de período y símbolo múltiples que tiene tres colores para mostrar su línea:

En MQL5 tendremos tres matrices de datos (tres búferes):

  1. Búfer dibujado (los datos se muestran en la Ventana de datos)
  2. Búfer de color (no se muestra en la Ventana de datos, pero indica con qué color se dibuja la línea del búfer 1 en cada barra)
  3. Búfer de cálculo para almacenar los datos de Moving Average del símbolo/período (no se muestra en la Ventana de datos)

En MQL4 tendremos tres matrices de datos (tres búferes):

  1. Búfer dibujado para el color 1 (los datos se muestran en la Ventana de datos)
  2. Búfer dibujado para el color 2 (los datos se muestran en la Ventana de datos)
  3. Búfer dibujado para el color 3 (los datos se muestran en la Ventana de datos)

Cuando se reduce el número de colores, el número de búferes para MQL4 va a reducirse; cuando se aumenta, va a aumentarse. En MQL5, el número de búferes para este ejemplo siempre va a ser igual a 3. Además, en MQL5 existe la posibilidad de cambiar dinámicamente el número de colores hasta 64. En MQL4, no todo es tan fácil con el coloreado de las líneas de indicadores. Simplemente, para 64 colores tenemos que crear 64 búferes. Y es sólo para una línea. Si el indicador tiene 4 líneas, necesitamos 256 matrices de búferes de indicador. Para ocho líneas, necesitamos 512 búferes, lo cual es el límite. Pues, para MQL5, simplemente indicamos el índice del color correspondiente para cada barra, la línea en esta barra recibe el color especificado. Para MQL4, tendremos que mostrar el búfer correspondiente al color y ocultar el resto. Todos los búferes para cada color en MQL4 estarán disponibles en la Ventana de datos del terminal. En MQL5, para este ejemplo, veremos un búfer en la Ventana de datos, lo cual es correcto ya que para cada línea hay un valor en la Ventana de datos del terminal.

No vamos a corregir y rehacer de inmediato, de una tirada, todo lo que ya está hecho. En vez de eso, paso a paso, a lo largo de varios artículos, de lo más simple a lo más complejo, iremos mejorando las clases de la biblioteca para conseguir la compatibilidad de algunos aspectos de trabajo con indicadores en la biblioteca con MQL4. Usando el indicador Accumulation/Distribution como ejemplo, comprobaremos la creación de un indicador estándar monocromo de período y símbolo múltiples de búfer único en MQL4 con la ayuda de la biblioteca.

Mejorando las clases de la biblioteca

Como siempre, primero añadimos los mensajes de texto necesarios. Antes, en el programa de indicador final, verificábamos la correspondencia de los búferes del indicador creados por la biblioteca con las entradas en el código del indicador sobre el número necesario de búferes de indicador:

#property indicator_buffers 3
#property indicator_plots   1

Luego en el código, después de que la biblioteca creara todos los búferes necesarios, realizábamos la verificación:

//--- Check the number of buffers specified in the 'properties' block
   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());

Pues, precisamente esta verificación —ligeramente modificada para la compatibilidad con MQL4— será transferida a la biblioteca. Y colocaremos los textos mostrados durante la verificación en su debido lugar de la biblioteca, es decir, en el archivo \MQL5\Include\DoEasy\Data.mqh. Escribiremos los índices de nuevos mensajes en él:

//--- CEngine
   MSG_ENG_NO_TRADE_EVENTS,                           // There have been no trade events since the last launch of EA
   MSG_ENG_FAILED_GET_LAST_TRADE_EVENT_DESCR,         // Failed to get description of the last trading event
   MSG_ENG_FAILED_GET_MARKET_POS_LIST,                // Failed to get the list of open positions
   MSG_ENG_FAILED_GET_PENDING_ORD_LIST,               // Failed to get the list of placed orders
   MSG_ENG_NO_OPEN_POSITIONS,                         // No open positions
   MSG_ENG_NO_PLACED_ORDERS,                          // No placed orders
   MSG_ENG_ERR_VALUE_PLOTS,                           // Attention! Value \"indicator_plots\" must be 
   MSG_ENG_ERR_VALUE_ORDERS,                          // Attention! Value \"indicator_buffers\" must be 

así como, los textos de los mensajes que corresponden a los índices recién añadidos:

//--- CEngine
   {"С момента последнего запуска ЕА торговых событий не было","No trade events since the last launch of EA"},
   {"Не удалось получить описание последнего торгового события","Failed to get 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 "},

El archivo que contiene datos para los parámetros de entrada de programas se llama InpDatas.mqh... Vamos a renombrarlo usando el nombre inglés más correcto (me he equivocado con el nombre del archivo). Ahora va a llamarse \MQL5\Include\DoEasy\InpData.mqh.
Simplemente, cambiamos su nombre en la carpeta donde se ubica.

Este archivo se adjunta a la biblioteca en el archivo Data.mqh (el que estamos editando ahora), vamos a corregir la línea de inclusión:

//+------------------------------------------------------------------+
//|                                                         Data.mqh |
//|                        Copyright 2020, MetaQuotes Software Corp. |
//|                             https://mql5.com/es/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, MetaQuotes Software Corp."
#property link      "https://mql5.com/es/users/artmedia70"
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "InpData.mqh"
//+------------------------------------------------------------------+


Procedamos a la implementación del concepto de multiplataforma.

Si ahora intentamos compilar la biblioteca en el editor MetaEditor de MetaTrader 4 (F7 en el archivo Engine.mqh), obtendremos muchos errores:


Pues bien, eso no es sorprendente. Simplemente, empezamos por orden. En primer lugar, veremos que MQL4 no conoce algunas constantes y enumeraciones.
Escribimos las constantes y enumeraciones nuevas en el archivo \MQL5\Include\DoEasy\ToMQL4.mqh:

//+------------------------------------------------------------------+
//|                                                       ToMQL4.mqh |
//|              Copyright 2017, Artem A. Trishkin, Skype artmedia70 |
//|                         https://www.mql5.com/es/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2017, Artem A. Trishkin, Skype artmedia70"
#property link      "https://www.mql5.com/es/users/artmedia70"
#property strict
#ifdef __MQL4__
//+------------------------------------------------------------------+
//| Error codes                                                      |
//+------------------------------------------------------------------+
#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)
//+------------------------------------------------------------------+
//| Order types, execution policy, lifetime, reasons                 |
//+------------------------------------------------------------------+
#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)
//+------------------------------------------------------------------+
//| Flags of allowed order expiration modes                          |
//+------------------------------------------------------------------+
#define SYMBOL_EXPIRATION_GTC             (1)
#define SYMBOL_EXPIRATION_DAY             (2)
#define SYMBOL_EXPIRATION_SPECIFIED       (4)
#define SYMBOL_EXPIRATION_SPECIFIED_DAY   (8)
//+------------------------------------------------------------------+
//| Flags of allowed order filling modes                             |
//+------------------------------------------------------------------+
#define SYMBOL_FILLING_FOK                (1)
#define SYMBOL_FILLING_IOC                (2)
//+------------------------------------------------------------------+
//| Flags of allowed order types                                     |
//+------------------------------------------------------------------+
#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)
//+------------------------------------------------------------------+
//| Indicator lines IDs                                              |
//+------------------------------------------------------------------+
#define TENKANSEN_LINE                    (0)
#define KIJUNSEN_LINE                     (1)
#define SENKOUSPANA_LINE                  (2)
#define SENKOUSPANB_LINE                  (3)
#define CHIKOUSPAN_LINE                   (4)
//+------------------------------------------------------------------+
//| MQL5 deal types                                                  |
//+------------------------------------------------------------------+
//--- the code has been removed for the sake of space
//+------------------------------------------------------------------+
//| Position change method                                           |
//+------------------------------------------------------------------+
//--- the code has been removed for the sake of space
//+------------------------------------------------------------------+
//| Open position direction                                          |
//+------------------------------------------------------------------+
//--- the code has been removed for the sake of space
//+------------------------------------------------------------------+
//| Order state                                                      |
//+------------------------------------------------------------------+
//--- the code has been removed for the sake of space
//+------------------------------------------------------------------+
//| Margin calculation mode                                          |
//+------------------------------------------------------------------+
//--- the code has been removed for the sake of space
//+------------------------------------------------------------------+
//| Prices a symbol chart is based on                                |
//+------------------------------------------------------------------+
//--- the code has been removed for the sake of space
//+------------------------------------------------------------------+
//| The lifetime of pending orders and                               |
//| placed StopLoss/TakeProfit levels                                |
//+------------------------------------------------------------------+
//--- the code has been removed for the sake of space
//+------------------------------------------------------------------+
//| Option types                                                     |
//+------------------------------------------------------------------+
//--- the code has been removed for the sake of space
//+------------------------------------------------------------------+
//| Right provided by an option                                      |
//+------------------------------------------------------------------+
//--- the code has been removed for the sake of space
//+------------------------------------------------------------------+
//| Symbol margin calculation method                                 |
//+------------------------------------------------------------------+
//--- the code has been removed for the sake of space
//+------------------------------------------------------------------+
//| Swap charging methods during a rollover                          |
//+------------------------------------------------------------------+
//--- the code has been removed for the sake of space
//+------------------------------------------------------------------+
//| Trade operation types                                            |
//+------------------------------------------------------------------+
//--- the code has been removed for the sake of space
//+------------------------------------------------------------------+
//| Order filling policies                                           |
//+------------------------------------------------------------------+
//--- the code has been removed for the sake of space
//+------------------------------------------------------------------+
//| Order expiration term                                            |
//+------------------------------------------------------------------+
//--- the code has been removed for the sake of space
//+------------------------------------------------------------------+
//| Integer properties of a selected position                        |
//+------------------------------------------------------------------+
//--- the code has been removed for the sake of space
//+------------------------------------------------------------------+
//| Real properties of a selected position                           |
//+------------------------------------------------------------------+
//--- the code has been removed for the sake of space
//+------------------------------------------------------------------+
//| String properties of a selected position                         |
//+------------------------------------------------------------------+
//--- the code has been removed for the sake of space
//+------------------------------------------------------------------+
//| Technical indicator types                                        |
//+------------------------------------------------------------------+
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,
  };
//+------------------------------------------------------------------+
//| Drawing styles                                                   |
//+------------------------------------------------------------------+
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
  };
//+------------------------------------------------------------------+
//| Volume type                                                      |
//+------------------------------------------------------------------+
enum ENUM_APPLIED_VOLUME
  {
   VOLUME_TICK,
   VOLUME_REAL
  };
//+------------------------------------------------------------------+
//| Trade request structure                                          |
//+------------------------------------------------------------------+
//--- the code has been further removed for the sake of space
#endif 

Luego, después de realizar la siguiente compilación, vemos el error de la ausencia de las funciones mql5 en MQL4, en particular, BarsCalculated(). Esta función devuelve el número de barras calculadas por el indicador a base de su manejador. Para el lenguaje MQL4, todos estos conceptos son desconocidos. La función más parecida a BarsCalculated() será la función mql4 Bars() que devuelve el número de las barras disponibles para la serie temporal especificada.
Puesto que en MQL4 se considera que el indicador ya ha sido calculado al acceder a él, simplemente podemos sustituir la cantidad de datos calculados del indicador (MQL5 BarsCalculated() ) por el número de barras disponibles de la serie temporal (MQL4 Bars() ). En cualquier caso, cuando los métodos de la biblioteca obtienen los datos del indicador, ellos devuelven los datos recibidos y verifican si son correctos. Por tanto, vamos a considerar que la indicación de las barras disponibles de la serie temporal puede sustituir la cantidad de datos calculados que se desconoce en MQL4.

El método IndicatorBarsCalculated(), que utiliza la función BarsCalculated(), se ubica en el archivo \MQL5\Include\DoEasy\Objects\Indicators\Buffer.mqh. Ahí mismo, tendremos que introducir inmediatamente muchas mejoras en otros métodos de trabajo con indicadores.

Antes, el método estaba escrito completamente en el cuerpo de la clase, donde la cantidad de datos calculados se devolvía en seguida:

   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);              }

Ahora, dejamos sólo la declaración del método

   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);

y pasamos su implementación para fuera del cuerpo de la clase:

//+------------------------------------------------------------------+
//| Return the number of standard indicator calculated bars          |
//+------------------------------------------------------------------+
int CBuffer::IndicatorBarsCalculated(void)
  {
   return(#ifdef __MQL5__ ::BarsCalculated((int)this.GetProperty(BUFFER_PROP_IND_HANDLE)) #else ::Bars(this.Symbol(),this.Timeframe()) #endif);
  }
//+------------------------------------------------------------------+

Aquí, para MQL5 devolvemos la cantidad de datos calculados del indicador, y para MQL4, la cantidad de datos disponibles de la serie temporal.

Dividimos el constructor paramétrico privado de la clase en dos partes.
La primera parte, es la que ya existe, permanecerá sólo para MQL5, y para MQL4 haremos una copia del código mql5 y eliminaremos lo innecesario de él:

//+------------------------------------------------------------------+
//| Closed parametric constructor                                    |
//+------------------------------------------------------------------+
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;
//--- Save integer properties
   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);
   
//--- Save real properties
   this.m_double_prop[this.IndexProp(BUFFER_PROP_EMPTY_VALUE)] = (this.TypeBuffer()>BUFFER_TYPE_CALCULATE ? EMPTY_VALUE : 0);
//--- Save string properties
   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 failed to change the size of the indicator buffer array, display the appropriate message indicating the string
   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 failed to change the size of the color array (only for a non-calculated buffer), display the appropriate message indicating the string
   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());

//--- For DRAW_FILLING, fill in the color array with two default colors
   if(this.Status()==BUFFER_STATUS_FILLING)
     {
      this.SetColor(clrBlue,0);
      this.SetColor(clrRed,1);
     }

//--- Bind indicator buffers with arrays
//--- In a loop by the number of indicator buffers
   int total=::ArraySize(DataBuffer);
   for(int i=0;i<total;i++)
     {
      //--- calculate the index of the next array and
      //--- bind the indicator buffer by the calculated index with the dynamic array
      //--- located by the i loop index in the DataBuffer array
      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));
      //--- Set indexation flag as in the timeseries to all buffer arrays
      ::ArraySetAsSeries(this.DataBuffer[i].Array,true);
     }
//--- Bind the color buffer with the array (only for a non-calculated buffer and not for the filling buffer)
   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 is a calculated buffer, all is done
   if(this.TypeBuffer()==BUFFER_TYPE_CALCULATE)
      return;
//--- Set integer parameters of the graphical series
   ::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));
//--- Set real parameters of the graphical series
   ::PlotIndexSetDouble((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_EMPTY_VALUE,this.GetProperty(BUFFER_PROP_EMPTY_VALUE));
//--- Set string parameters of the graphical series
   ::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;
//--- Save integer properties
   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);
   
//--- Save real properties
   this.m_double_prop[this.IndexProp(BUFFER_PROP_EMPTY_VALUE)] = (this.TypeBuffer()>BUFFER_TYPE_CALCULATE ? EMPTY_VALUE : 0);
//--- Save string properties
   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 failed to change the size of the indicator buffer array, display the appropriate message indicating the string
   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());
      
//--- Bind indicator buffers with arrays
//--- In a loop by the number of indicator buffers
   int total=::ArraySize(DataBuffer);
   for(int i=0;i<total;i++)
     {
      //--- calculate the index of the next array and
      //--- bind the indicator buffer by the calculated index with the dynamic array
      //--- located by the i loop index in the DataBuffer array
      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));
      //--- Set indexation flag as in the timeseries to all buffer arrays
      ::ArraySetAsSeries(this.DataBuffer[i].Array,true);
     }

//--- If this is a calculated buffer, all is done
   if(this.TypeBuffer()==BUFFER_TYPE_CALCULATE)
      return;
//--- Set integer parameters of the graphical series
   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));
   
//--- Set real parameters of the graphical series
   ::SetIndexEmptyValue((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),this.GetProperty(BUFFER_PROP_EMPTY_VALUE));
   
//--- Set string parameters of the graphical series
   ::SetIndexLabel((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),this.GetProperty(BUFFER_PROP_LABEL));
   
#endif 
  }
//+------------------------------------------------------------------+

La principal diferencia consiste en el cálculo del tipo del dibujado. Para MQL5, lo calculamos a partir del tipo del búfer (su estatus), mientras que aquí era más fácil simplemente asignar los valores necesarios. Para establecer los valores necesarios para el búfer de indicador, usamos las funciones mql4 correspondientes, porque a pesar de que las funciones mql5 PlotIndexSetInteger(), PlotIndexSetDouble() y PlotIndexSetString() no provocan errores de compilación, pero tampoco establecen en MQL4 los valores necesarios para el búfer de indicador.

En los métodos para definir algunas propiedades del búfer de indicador, hacemos de la misma manera la división en el código mql5 y en el código mql4 usando las funciones correspondientes para cada uno de los lenguajes:

//+------------------------------------------------------------------+
//| Set the graphical construction type by type and status           |
//+------------------------------------------------------------------+
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 
  }
//+------------------------------------------------------------------+
//| Set the passed graphical construction type                       |
//+------------------------------------------------------------------+
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 
  }
//+------------------------------------------------------------------+
//| Set the number of initial bars                                   |
//| without drawing and values in 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 
  }
//+------------------------------------------------------------------+
//| Set the flag of displaying                                       |
//| construction values in 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);
  }
//+------------------------------------------------------------------+
//| Set the indicator graphical construction shift                   |
//+------------------------------------------------------------------+
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 
  }
//+------------------------------------------------------------------+
//| Set the line style                                               |
//+------------------------------------------------------------------+
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 
  }
//+------------------------------------------------------------------+
//| Set the line width                                               |
//+------------------------------------------------------------------+
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 
  }
//+------------------------------------------------------------------+
//| Set the number of colors                                         |
//+------------------------------------------------------------------+
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);
  }
//+------------------------------------------------------------------+
//| Set a single specified drawing color for the buffer              |
//+------------------------------------------------------------------+
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 
  }
//+------------------------------------------------------------------+
//| Set the drawing color to the specified color index               |
//+------------------------------------------------------------------+
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 
  }
//+------------------------------------------------------------------+
//| Set drawing colors from the color array                          |
//+------------------------------------------------------------------+
void CBuffer::SetColors(const color &array_colors[])
  {
#ifdef __MQL5__
//--- Exit if the passed array is empty
   if(::ArraySize(array_colors)==0 || this.TypeBuffer()==BUFFER_TYPE_CALCULATE)
      return;
//--- Copy the passed array to the array of buffer object colors
   ::ArrayCopy(this.ArrayColors,array_colors,0,0,IND_COLORS_TOTAL);
//--- Exit if the color array was empty and not copied for some reason
   int total=::ArraySize(this.ArrayColors);
   if(total==0)
      return;
//--- If the drawing style is not DRAW_FILLING
   if(this.Status()!=BUFFER_STATUS_FILLING)
     {
      //--- if the new number of colors exceeds the currently set one, 
      //--- set the new value for the number of colors
      if(total>this.ColorsTotal())
         this.SetColorNumbers(total);
     }
   //--- If the drawing style is DRAW_FILLING, set the number of colors equal to 2
   else
      total=2;
//--- Set the very first color from the color array (for a single color) to the buffer object color property
   this.SetProperty(BUFFER_PROP_COLOR,(color)this.ArrayColors[0]);
//--- Set the new number of colors for the indicator buffer
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_COLOR_INDEXES,total);
//--- In the loop by the new number of colors, set all colors by their indices for the indicator buffer
   for(int i=0;i<total;i++)
      ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LINE_COLOR,i,this.ArrayColors[i]);
#else 
#endif 
  }
//+------------------------------------------------------------------+
//| Set the "empty" value for construction                           |
//| without drawing                                                  |
//+------------------------------------------------------------------+
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 
  }
//+------------------------------------------------------------------+
//| Set the indicator graphical series name                          |
//+------------------------------------------------------------------+
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 
  }
//+------------------------------------------------------------------
//+------------------------------------------------------------------+
//| Set the color index to the specified timeseries index            |
//| of the color buffer array                                        |
//+------------------------------------------------------------------+
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;
  }
//+------------------------------------------------------------------+

En el búfer de cálculo de la clase CBufferCalculate del archivo \MQL5\Include\DoEasy\Objects\Indicators\BufferCalculate.mqh tenemos tres métodos que copian los datos desde el manejador del indicador a la matriz de datos del objeto del búfer de cálculo. Los métodos devuelven la cantidad de datos copiados. Como para MQL4 no necesitamos copiar los datos del manejador del indicador, sino que vamos a recibirlos usando las funciones mql4 estándar correspondientes que indican el símbolo, marco temporal y el número de la barra, entonces necesitamos devolver algún valor ficticio con estos métodos que confirma el éxito del copiado.

A estos métodos les transferimos el número de barras a copiar y devolvemos la bandera que indica en que los datos copiados coinciden con este valor.
Para MQL4, simplemente devolvemos el mismo:

//+------------------------------------------------------------------+
//| Copy data of the specified indicator to the buffer object array  |
//+------------------------------------------------------------------+
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 
     );
  }
//+------------------------------------------------------------------+

Para el último método, en el que no especificamos la cantidad de datos a copiar, sino especificamos la hora de inicio y la hora final de datos requeridos,
para MQL4, calculamos la cantidad de datos entre los valores de la hora inicial y la hora final para los datos requeridos, y devolvemos este valor calculado.

Creamos todos los objetos de búfer para los indicadores estándar en la clase de colección de búferes de indicador
en el archivo \MQL5\Include\DoEasy\Collections\BuffersCollection.mqh.
Ya hemos mejorado esta clase para que sea compatible con MQL4.

En MQL5, en los métodos para crear los objetos de indicadores estándar, primero, se crea el manejador del indicador necesario, y en caso del éxito, se construye el propio objeto. En MQL4 no es necesario crear ningún manejador, por eso, añadimos el manejador ficticio del indicador creado a todos estos métodos:

//+------------------------------------------------------------------+
//| Create multi-symbol multi-period AC                              |
//+------------------------------------------------------------------+
int CBuffersCollection::CreateAC(const string symbol,const ENUM_TIMEFRAMES timeframe,const int id=WRONG_VALUE)
  {
//--- Create the indicator handle and set the default ID
   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)
     {
      //--- Create the histogram buffer from the zero line
      this.CreateHistogram();

Por ahora, hemos añadido un cero como valor del manejador. Luego, probablemente emularemos la creación de los manejadores de indicadores de tal manera que no sea posible crear dos objetos iguales del indicador estándar con los mismos parámetros de entrada. Pero la práctica nos mostrará si es realmente necesario hacer eso.

La línea con emulación de la creación del manejador ya fue añadida a todos los métodos de la creación de objetos de indicadores estándar. No vamos a considerarlos aquí, mejor, usando el método para crear el indicador AD como ejemplo, analizaremos los cambios necesarios para crear un indicador estándar monocromo de búfer único para MQL4:

//+------------------------------------------------------------------+
//| Create multi-symbol multi-period AD                              |
//+------------------------------------------------------------------+
int CBuffersCollection::CreateAD(const string symbol,const ENUM_TIMEFRAMES timeframe,const ENUM_APPLIED_VOLUME applied_volume,const int id=WRONG_VALUE)
  {
//--- Create the indicator handle and set the default ID
   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)
     {
      //--- Create the line buffer
      this.CreateLine();
      //--- Get the last created (drawn) buffer object and set all the necessary parameters to it
      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__
         //--- Create a calculated buffer storing standard indicator data
         this.CreateCalculate();
         //--- Get the last created (calculated) buffer object and set all the necessary parameters to it
         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;
  }
//+------------------------------------------------------------------+

Aquí, para MQL5, definimos para el búfer el conjunto de sus colores a través del método de su transferencia al objeto usando la matriz de colores, y para MQL4, definimos sólo un color, es decir, el primero en la matriz de colores. El búfer de cálculo es necesario solamente para MQL5. Ahí van a almacenarse los datos del indicador AD creado a base del símbolo y período del gráfico especificados. Para MQL4, este búfer no es necesario, ya que vamos a recibir todos los datos directamente de la función de acceso al indicador iAD()

El método privado que prepara los datos del indicador estándar indicado para definir los valores en el gráfico actual del símbolo para MQL5 lee los datos del búfer de cálculo. Para MQL4, será suficiente obtener los datos solicitados de la función de acceso a los datos del indicador estándar:

//+------------------------------------------------------------------+
//| Prepare data of the specified standard indicator                 |
//| for setting values on the current symbol chart                   |
//+------------------------------------------------------------------+
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)
  {
     //--- Find the bar index corresponding to the current bar start time
     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;
     
     //--- For MQL5
     #ifdef __MQL5__
        //--- Get the value by the index from the indicator buffer
        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);
     
     //--- for MQL4
     #else 
        switch((int)ind_type)
          {
           //--- Single-buffer standard indicators
           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;
            
           //--- Multi-buffer standard indicators
           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;
     //--- The current chart requires no calculation of the number of handled bars since there is only one bar
     if(buffer_data0.Symbol()==::Symbol() && buffer_data0.Timeframe()==::Period())
       {
        series_index_start=series_index;
        num_bars=1;
       }
     else
       {
        //--- Get the bar time the bar with the index_period index falls into on the calculated buffer period and symbol
        datetime time_period=::iTime(buffer_data0.Symbol(),buffer_data0.Timeframe(),index_period);
        if(time_period==0) return false;
        //--- Get the appropriate current chart bar
        series_index_start=::iBarShift(::Symbol(),::Period(),time_period,true);
        if(series_index_start==WRONG_VALUE) return WRONG_VALUE;
        //--- Calculate the number of bars on the current chart which should be filled with calculated buffer data
        num_bars=::PeriodSeconds(buffer_data0.Timeframe())/::PeriodSeconds(PERIOD_CURRENT);
        if(num_bars==0) num_bars=1;
       }
     //--- Set values to calculate colors
     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;
  }
//+------------------------------------------------------------------+

Para este momento, hablando de MQL4, ha sido implementado el recibimiento de datos sólo de los indicadores estándar de búfer único, los que no disponen de los parámetros de entrada, excepto el símbolo y período del gráfico. Los demás indicadores estándar serán implementados en los siguientes artículos.

En el método que define valores para el gráfico actual en los búferes del indicador estándar indicado según el índice de la serie temporal de acuerdo con el símbolo/período del objeto de búfer, fueron hechas pequeñas modificaciones para excluir la verificación del búfer de cálculo para MQL4:

//+------------------------------------------------------------------+
//| Set values for the current chart to the buffers of the specified |
//| standard indicator by the timeseries index according to          |
//| the buffer object symbol/period                                  |
//+------------------------------------------------------------------+
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)
  {
//--- Get the list of buffer objects by type and ID
   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;
     }
     
//--- Get the list of drawn objects with ID
   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);
//--- Get the list of calculated buffers with ID
   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);
 
//--- Exit if any of the lists is empty
   if(list_data.Total()==0 #ifdef __MQL5__ || list_calc.Total()==0 #endif )
      return false;
  
//--- Declare the necessary objects and variables
   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;
//--- Depending on the standard indicator type

   switch((int)ind_type)
     {
   //--- Single-buffer standard indicators
      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;
        //--- In the loop by the number of bars in num_bars, fill in the drawn buffer with the calculated buffer value taken by the index_period index
        //--- and set the color of the drawn buffer depending on the value00 and value01 values ratio
        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;
      
   //--- Multi-buffer standard indicators
      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;
        //--- In the loop by the number of bars in num_bars, fill in the drawn buffer with the calculated buffer value taken by the index_period index
        //--- and set the color of the drawn buffer depending on the value00 and value01 values ratio
        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;
        //--- In the loop by the number of bars in num_bars, fill in the drawn buffer with the calculated buffer value taken by the index_period index
        //--- and set the color of the drawn buffer depending on the value00 and value01 values ratio
        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);
        
        //--- Get the list of buffer objects which have ID of auxiliary line, and from it - buffer object with line number as 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);
        //--- Get the list of buffer objects which have ID of auxiliary line, and from it - buffer object with line number as 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;
        //--- In the loop by the number of bars in num_bars, fill in the drawn buffer with the calculated buffer value taken by the index_period index
        //--- and set the color of the drawn buffer depending on the value00 and value01 values ratio
        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);
           
           //--- Set values for indicator auxiliary lines depending on mutual position of  Senkou Span A and Senkou Span B lines
           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;
        //--- In the loop by the number of bars in num_bars, fill in the drawn buffer with the calculated buffer value taken by the index_period index
        //--- and set the color of the drawn buffer depending on the value00 and value01 values ratio
        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;
  }
//+------------------------------------------------------------------+

Como hemos decidido transferir a la biblioteca la verificación de que si el número de objetos de búfer creados por la biblioteca corresponde con los valores en #property del programa, vamos a añadir este método a la clase del objeto principal de la biblioteca CEngine, en el archivo \MQL5\Include\DoEasy\Engine.mqh.

En la sección pública de la clase, declaramos el método:

//--- Display short description of all indicator buffers of the buffer collection
   void                 BuffersPrintShort(void);

//--- Specify the required number of buffers for indicators
   void                 CheckIndicatorsBuffers(const int buffers,const int plots #ifdef __MQL4__ =1 #endif );
 
//--- Return the bar index on the specified timeframe chart by the current chart's bar index

Escribimos su implementación fuera del cuerpo de la clase:

//+------------------------------------------------------------------+
//| Specify the required number of buffers for indicators            |
//+------------------------------------------------------------------+
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 
  }
//+------------------------------------------------------------------+

Para MQL5, simplemente mostramos los mensajes de aviso sobre la discrepancia entre el número creado de búferes de indicador (dibujados y de cálculo) y el valor especificado en #property del programa de indicador.
Para MQL4, si el valor especificado en #property indicator_buffers no coincide, mostramos un mensaje sobre ello y definimos el número total de todos los búferes de indicador (dibujados y de cálculo) de acuerdo con el número total de todos los búferes creados por la biblioteca
.

Ahora, nos falta definir el rango decimal de los datos de salida para indicadores en MQL4. Para eso, vamos a modificar la función de definición del rango decimal y de los niveles para los indicadores estándar en el archivo de funciones de servicio de la biblioteca \MQL5\Include\DoEasy\Services\DELib.mqh:

//+------------------------------------------------------------------+
//| Set standard indicator's decimal capacity and levels             |
//+------------------------------------------------------------------+
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 
  }
//+------------------------------------------------------------------+

Aquí, para MQL4, con el fin de definir el rango decimal de los datos mostrados del indicador, usamos la función mql4 estándar IndicatorDigits().

Pues bien, así terminamos la mejora de las clases de la biblioteca para la creación de los indicadores estándar de período y símbolo múltiples de búfer único.


Prueba

Para la simulación, vamos a usar el segundo indicador (TestDoEasyPart51_2.mq5) del artículo anterior y vamos a guardarlo en la carpeta de los indicadores del terminal MetaTrader 4 \MQL4\Indicators\TestDoEasy\Part52\ con el nombre TestDoEasyPart52.mq4.

El indicador de prueba anterior creaba el indicador estándar de período y símbolo múltiples Gator Oscillator. Tenemos que crear el indicador Accumulation/Distribution.

En el encabezado del archivo, definimos la cantidad necesaria de búferes de indicador para MQL4:

//+------------------------------------------------------------------+
//|                                             TestDoEasyPart52.mq4 |
//|                        Copyright 2020, MetaQuotes Software Corp. |
//|                             https://mql5.com/es/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, MetaQuotes Software Corp."
#property link      "https://mql5.com/es/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;                                  // List of used timeframes
CEngine              engine;                                      // CEngine library main object
string               prefix;                                      // Prefix of graphical object names
int                  min_bars;                                    // The minimum number of bars for the indicator calculation
int                  used_symbols_mode;                           // Mode of working with symbols
string               array_used_symbols[];                        // The array for passing used symbols to the library
string               array_used_periods[];                        // The array for passing used timeframes to the library
//+------------------------------------------------------------------+

En el manejador OnInit() creamos un objeto del indicador estándar Accumulation/Distribution e indicamos el tipo del indicador AD donde es necesario:

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Write the name of the working timeframe selected in the settings to the InpUsedTFs variable
   InpUsedTFs=TimeframeDescription(InpPeriod);
//--- Initialize DoEasy library
   OnInitDoEasy();
   
//--- Set indicator global variables
   prefix=engine.Name()+"_";
   //--- Calculate the number of bars of the current period fitting in the maximum used period
   //--- Use the obtained value if it exceeds 2, otherwise use 2
   int num_bars=NumberBarsInTimeframe(InpPeriod);
   min_bars=(num_bars>2 ? num_bars : 2);

//--- Check and remove remaining indicator graphical objects
   if(IsPresentObectByPrefix(prefix))
      ObjectsDeleteAll(0,prefix);

//--- Create the button panel

//--- Check playing a standard sound using macro substitutions
   engine.PlaySoundByDescription(SND_OK);
//--- Wait for 600 milliseconds
   engine.Pause(600);
   engine.PlaySoundByDescription(SND_NEWS);

//--- indicator buffers mapping
//--- Create all the necessary buffer objects for constructing a selected standard indicator
   if(!engine.BufferCreateAD(InpUsedSymbols,InpPeriod,VOLUME_TICK,1))
     {
      Print(TextByLanguage("Ошибка. Индикатор не создан","Error. Indicator not created"));
      return INIT_FAILED;
     }
//--- Check the number of buffers specified in the 'properties' block
   engine.CheckIndicatorsBuffers(indicator_buffers,indicator_plots);

//--- Create the color array and set non-default colors to all buffers within the collection
//--- (commented out since the colors have already been set in the methods of creating default standard indicators)
//--- (we can always set necessary colors either for all indicators, like here, or for each of them individually)
   //color array_colors[]={clrGreen,clrRed,clrGray};
   //engine.BuffersSetColors(array_colors);

//--- Display short descriptions of created indicator buffers
   engine.BuffersPrintShort();

//--- Set the indicator short name, digital capacity and levels
   string label=engine.BufferGetIndicatorShortNameByTypeID(IND_AD,1);
   IndicatorSetString(INDICATOR_SHORTNAME,label);
   SetIndicatorLevels(InpUsedSymbols,IND_AD);

//--- Successful
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+

Antes, la verificación de la correspondencia entre la cantidad de búferes de indicador especificados y creados se realizaba en el manejador OnInit():

//--- Check the number of buffers specified in the 'properties' block
   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());

Ahora, la hemos sustituido por la llamada al método correspondiente de la biblioteca.

En el manejador OnCalculate(), basta con reemplazar el registro de datos del indicador Gator Oscillator por el registro de los datos del indicador Accumulation/Distribution en el ciclo principal del programa:

//+------------------------------------------------------------------+
//| 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 code block for working with the library:             |
//+------------------------------------------------------------------+
//--- Pass the current symbol data from OnCalculate() to the price structure and set the "as timeseries" flag to the arrays
   CopyDataAsSeries(rates_total,prev_calculated,time,open,high,low,close,tick_volume,volume,spread);

//--- Check for the minimum number of bars for calculation
   if(rates_total<min_bars || Point()==0) return 0;
//--- Handle the Calculate event in the library
//--- If the OnCalculate() method of the library returns zero, not all timeseries are ready - leave till the next tick
   if(engine.OnCalculate(rates_data)==0)
      return 0;
   
//--- If working in the tester
   if(MQLInfoInteger(MQL_TESTER)) 
     {
      engine.OnTimer(rates_data);   // Working in the library timer
      engine.EventsHandling();      // Working with library events
     }
//+------------------------------------------------------------------+
//| OnCalculate code block for working with the indicator:           |
//+------------------------------------------------------------------+
//--- Check and calculate the number of calculated bars
//--- If limit = 0, there are no new bars - calculate the current one
//--- If limit = 1, a new bar has appeared - calculate the first and the current ones
//--- limit > 1 means the first launch or changes in history - the full recalculation of all data
   int limit=rates_total-prev_calculated;
   
//--- Recalculate the entire history
   if(limit>1)
     {
      limit=rates_total-1;
      engine.BuffersInitPlots();
      engine.BuffersInitCalculates();
     }
//--- Prepare data 
//--- Fill in calculated buffers of all created standard indicators with data
   int bars_total=engine.SeriesGetBarsTotal(InpUsedSymbols,InpPeriod);
   int total_copy=(limit<min_bars ? min_bars : fmin(limit,bars_total));
   if(!engine.BufferPreparingDataAllBuffersStdInd())
      return 0;

//--- Calculate the indicator
//--- Main calculation loop of the indicator
   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);
  }
//+------------------------------------------------------------------+

Para la versión mql5 del indicador, a diferencia de la versión mql4, tendremos que alterar el número de los búferes dibujados y calculados, especificado en #property:

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

Vamos a compilar el indicador y ejecutarlo en el gráfico EURUSD H1 en el terminal MetaTrader 4 con los valores en los parámetros de entrada del indicador para el símbolo EURUSD y período H4. Así, visualizaremos el indicador AD calculado para EURUSD H4 en el gráfico de una hora EURUSD en el terminal MetaTrader 4:


¿Qué es lo próximo?

En el siguiente artículo, continuaremos trabajando con indicadores en MetaTrader 5, desarrollando poco a poco el concepto multiplataforma de la biblioteca.

Más abajo se adjuntan todos los archivos de la versión actual de la biblioteca y los archivos de los indicadores de prueba. Puede descargarlo todo y ponerlo a prueba por sí mismo.
Si tiene preguntas, observaciones o sugerencias, podrá concretarlas en los comentarios al artículo.

Me gustaría prestar su atención en el hecho de que todo el trabajo relacionado con la compatibilidad con la plataforma anterior se realiza solamente para mantener el concepto multiplataforma de la biblioteca, que fue desarrollada desde el principio para MQL5, y donde tiene más ventajas y capacidades funcionales.
No tenía planeado antes, y no tengo en la mente ahora crear más artículos sobre cómo trabajar con esta biblioteca en MQL4. Cada lector puede modificar y mejorar por sí mismo lo que le haga falta para trabajar con esta biblioteca en MetaTrader 4. Seguiré intentando hacer que la biblioteca sea compatible con ambas plataformas, pero solamente para que el usuario de la biblioteca pueda transferir fácilmente todos sus programas creados a su base en MetaTrader 4.

Volver al contenido

Artículos de esta serie:

Trabajando con las series temporales en la biblioteca DoEasy (Parte 35): El objeto "Barra" y la lista de serie temporal del símbolo
Trabajando con las series temporales en la biblioteca DoEasy (Parte 36): El objeto de series temporales de todos los periodos utilizados del símbolo
Trabajando con las series temporales en la biblioteca DoEasy (Parte 37): Colección de series temporales - Base de datos de series temporales según el símbolo y el periodo
Trabajando con las series temporales en la biblioteca DoEasy (Parte 38): Colección de series temporales - Actualización en tiempo real y acceso a los datos desde el programa
Trabajando con las series temporales en la biblioteca DoEasy (Parte 39): Indicadores basados en la biblioteca - Preparación de datos y eventos de la series temporales
Trabajando con las series temporales en la biblioteca DoEasy (Parte 40): Indicadores basados en la biblioteca - actualización de datos en tiempo real
Trabajando con las series temporales en la biblioteca DoEasy (Parte 41): Ejemplo de indicador de símbolo y periodo múltiples
Trabajando con las series temporales en la biblioteca DoEasy (Parte 42): La clase del objeto de búfer de indicador abstracto
Trabajando con las series temporales en la biblioteca DoEasy (Parte 43): Las clases de los objetos de búferes de indicador
Trabajando con las series temporales en la biblioteca DoEasy (Parte 44): Las clases de colección de los objetos de búferes de indicador
Trabajando con las series temporales en la biblioteca DoEasy (Parte 45): Búferes de indicador de periodo múltiple
Trabajando con las series temporales en la biblioteca DoEasy (Parte 46): Búferes de indicador de periodo y símbolos múltiples
Trabajando con las series temporales en la biblioteca DoEasy (Parte 47): Indicadores estándar de periodo y símbolo múltiples
Trabajando con las series temporales en la biblioteca DoEasy (Parte 48): Indicadores de periodo y símbolo múltiples en un búfer en una subventana
Trabajando con las series temporales en la biblioteca DoEasy (Parte 49): Indicadores estándar de periodo, símbolo y búfer múltiples
Trabajando con las series temporales en la biblioteca DoEasy (Parte 50): Indicadores estándar de periodo y símbolo múltiples con desplazamiento
Trabajando con las series temporales en la biblioteca DoEasy (Parte 51): Indicadores estándar compuestos de período y símbolo múltiples