Trabajando con las series temporales en la biblioteca DoEasy (Parte 43): Las clases de los objetos de búferes de indicador

2 septiembre 2020, 10:36
Artyom Trishkin
2
865

Contenido


Concepto

En el artículo anterior, creamos el objeto de búfer de indicador abstracto, que incluye ciertas propiedades de los búferes de indicador y ofrece métodos para trabajar con ellos. Hoy, crearemos objetos derivados de la clase del objeto de búfer abstracto. Cada uno de estos objetos será un objeto independiente de búfer de indicador con su tipo único de construcción gráfica.
Como ya hemos dicho anteriormente, todos nuestros búferes de indicador serán de color, y si necesitamos uno monocolor, solo tendremos que crear los búferes inicialmente con un solo color, o bien podremos establecer el número de búferes de color utilizados directamente durante el funcionamiento del indicador.

Para comenzar, añadiremos los nuevos textos de los mensajes de la biblioteca necesarios para el funcionamiento.
Abrimos el archivo \MQL5\Include\DoEasy\Datas.mqh y añadimos los índices de los nuevos mensajes:

   MSG_LIB_SYS_FAILED_CREATE_BAR_OBJ,                 // Failed to create the \"Bar\" object
   MSG_LIB_SYS_FAILED_SYNC_DATA,                      // Failed to synchronize data with the server
   MSG_LIB_SYS_FAILED_DRAWING_ARRAY_RESIZE,           // Failed to change the array size of drawn buffers
   MSG_LIB_SYS_FAILED_COLORS_ARRAY_RESIZE,            // Failed to change the color array size

...

   MSG_LIB_TEXT_BUFFER_TEXT_LINE_WIDTH,               // Line width
   MSG_LIB_TEXT_BUFFER_TEXT_ARROW_SIZE,               // Arrow size
   MSG_LIB_TEXT_BUFFER_TEXT_COLOR_NUM,                // Number of colors
   MSG_LIB_TEXT_BUFFER_TEXT_COLOR,                    // Drawing color
   MSG_LIB_TEXT_BUFFER_TEXT_EMPTY_VALUE,              // Empty value for plotting where nothing will be drawn
   MSG_LIB_TEXT_BUFFER_TEXT_SYMBOL,                   // Buffer symbol
   MSG_LIB_TEXT_BUFFER_TEXT_LABEL,                    // Name of the graphical indicator series displayed in DataWindow
   MSG_LIB_TEXT_BUFFER_TEXT_STATUS_NAME,              // Indicator buffer with graphical construction type 
   MSG_LIB_TEXT_BUFFER_TEXT_INVALID_PROPERTY_BUFF,    // Invalid number of indicator buffers in #property indicator_buffers

...

   MSG_LIB_TEXT_BUFFER_TEXT_TYPE_CALCULATE,           // Calculated buffer
   MSG_LIB_TEXT_BUFFER_TEXT_TYPE_DATA,                // Colored data buffer
   MSG_LIB_TEXT_BUFFER_TEXT_BUFFER,                   // Buffer
   
   MSG_LIB_TEXT_BUFFER_TEXT_STYLE_SOLID,              // Solid line
   MSG_LIB_TEXT_BUFFER_TEXT_STYLE_DASH,               // Dashed line
   MSG_LIB_TEXT_BUFFER_TEXT_STYLE_DOT,                // Dotted line
   MSG_LIB_TEXT_BUFFER_TEXT_STYLE_DASHDOT,            // Dot-dash line
   MSG_LIB_TEXT_BUFFER_TEXT_STYLE_DASHDOTDOT,         // Dash - two dots
   
  };

Y después añadimos los nuevos textos de los mensajes que se correspodan con los índices nuevamente creados:

   {"Не удалось создать объект \"Бар\"","Failed to create object \"Bar\""},
   {"Не удалось синхронизировать данные с сервером","Failed to sync data with server"},
   {"Не удалось изменить размер массива рисуемых буферов","Failed to resize drawing buffers array"},
   {"Не удалось изменить размер массива цветов","Failed to resize color array"},

...

   {"Толщина линии отрисовки","Thickness of drawing line"},
   {"Размер значка стрелки","Arrow icon size"},
   {"Количество цветов","Number of colors"},
   {"Цвет отрисовки","Index of buffer containing drawing color"},
   {"Пустое значение для построения, для которого нет отрисовки","Empty value for plotting, for which there is no drawing"},
   {"Символ буфера","Buffer Symbol"},
   {"Имя индикаторной графической серии, отображаемое в окне DataWindow","Name of indicator graphical series to display in DataWindow"},
   {"Индикаторный буфер с типом графического построения","Indicator buffer with graphic plot type"},
   {"Неправильно указано количество буферов индикатора (#property indicator_buffers)","Number of indicator buffers is incorrect (#property indicator_buffers)"},

...

   {"Расчётный буфер","Calculated buffer"},
   {"Цветной буфер данных","Colored Data buffer"},
   {"Буфер","Buffer"},
   
   {"Сплошная линия","Solid line"},
   {"Прерывистая линия","Broken line"},
   {"Пунктирная линия","Dotted line"},
   {"Штрих-пунктирная линия","Dash-dot line"},
   {"Штрих - две точки","Dash - two points"},
   
  };
//+---------------------------------------------------------------------+

En el archivo \MQL5\Include\DoEasy\Defines.mqh, en el apartado de macrosustituciones, añadimos la macro que indica el número máximo posible de colores para el búfer de indicador que se pueden establecer:

//+------------------------------------------------------------------+
//| Macrosustituciones                                               |
//+------------------------------------------------------------------+
//--- Describe the function with the error line number
#define DFUN_ERR_LINE                  (__FUNCTION__+(TerminalInfoString(TERMINAL_LANGUAGE)=="Russian" ? ", Page " : ", Line ")+(string)__LINE__+": ")
#define DFUN                           (__FUNCTION__+": ")        // "Function description"
#define COUNTRY_LANG                   ("Russian")                // Country language
#define END_TIME                       (D'31.12.3000 23:59:59')   // End date for account history data requests
#define TIMER_FREQUENCY                (16)                       // Minimal frequency of the library timer in milliseconds
#define TOTAL_TRY                      (5)                        // Default number of trading attempts
#define IND_COLORS_TOTAL               (64)                       // Maximum possible number of indicator buffer colors
//--- Standard sounds

Simplemente por comodidad, para no añadir la comprobación del número de colores establecidos que superen los 64. Especialmente, teniendo en cuenta su posible aumento en algún momento.

Ahora, vamos a mejorar ligeramente la clase del objeto de búfer abstracto.
Los búferes de indicador están actualmente en la sección pública de la clase, en las matrices con el tipo de datos real para el búfer de datos y el búfer de índice de color. Vamos a pasarlos a la sección protegida. Y como los búferes de construcción de los indicadores pueden ser 1, 2 y 4, crearemos una estructura de búfer con una única matriz.
¿Para qué necesitamos esto?
Como el búfer del indicador solo se puede representar con una matriz unidimensional de datos double, vamos a implementar una estructura con una sola matriz para crear la matriz de búfer (puede haber más de una). En este caso, en la matriz de dichas estructuras dispondremos de todas las matrices de datos (búferes de indicador) necesarias para construir el indicador. El acceso a ellas se realziará mediante el índice de la matriz requerida.
Para evitar ir más allá de la matriz al transmitir accidentalmente un índice incorrecto, hemos creado el método ajustando los índices de matriz transmitidos ​en caso de que el índice apunte fuera de la matriz. Así, el índice se ajustará para indicar el último elemento de la matriz de la estructura. Si solo hay una matriz, el índice apuntará a esta.

En el archivo del objeto de búfer abstracto \MQL5\Include\DoEasy\Objects\Indicators\Buffer.mqh, en la sección privada del mismo, declaramos el método que retorna el índice corregido de la matriz del búfer,
mientras que en la sección protegida, declaramos la estructura de matrices de búfer, el objeto de matriz de búferes con el tipo de estructura, la matriz del búfer de color y la matriz de colores utilizados para colorear las construcciones del indicador:

//+------------------------------------------------------------------+
//| Abstract indicator buffer class                                  |
//+------------------------------------------------------------------+
class CBuffer : public CBaseObj
  {
private:
   long              m_long_prop[BUFFER_PROP_INTEGER_TOTAL];                     // Integer properties
   double            m_double_prop[BUFFER_PROP_DOUBLE_TOTAL];                    // Real properties
   string            m_string_prop[BUFFER_PROP_STRING_TOTAL];                    // String properties
//--- Return the index of the array the buffer's (1) double and (2) string properties are located at
   int               IndexProp(ENUM_BUFFER_PROP_DOUBLE property)           const { return(int)property-BUFFER_PROP_INTEGER_TOTAL;                           }
   int               IndexProp(ENUM_BUFFER_PROP_STRING property)           const { return(int)property-BUFFER_PROP_INTEGER_TOTAL-BUFFER_PROP_DOUBLE_TOTAL;  }
//--- Set the graphical construction type
   void              SetDrawType(void);
//--- Return the adjusted buffer array index
   int               GetCorrectIndexBuffer(const uint buffer_index) const;

protected:
//--- Structure for storing buffer object buffer arrays
   struct SDataBuffer { double Array[]; };
//--- Array of (1) all indicator buffer object buffers, (2) color buffer, (3) for storing colors
   SDataBuffer       DataBuffer[];
   double            ColorBufferArray[];
   int               ArrayColors[];
   
public:

Elminamos de la sección pública de la clase las matrices de datos y color ya innecesarias:

public:
//--- Array of the (1) drawn indicator buffer and (2) color buffer
   double            DataArray[];
   double            ColorArray[];

En el artículo anterior, para comprobar el funcionamiento del objeto de búfer abstracto, hicimos público su constructor protegido, para poder así tener acceso a él desde el programa-indicador. Ahora, vamos a cerrarlo: el acceso a este se realizará desde los objetos herederos de esta clase al crear los búferes de indicador del tipo indicado. Nos limitaremos a comentar la línea y añadir al constructor protegido algunas variables adicionales necesarias para precisar las propiedades del búfer creado, concretamente, el número de búferes utilizados para la construcción, el grosor de la línea y la descripción del búfer en la ventana de datos:

protected:
//--- Constructor paramétrico protegido
                     CBuffer(ENUM_BUFFER_STATUS status_buffer,
                             ENUM_BUFFER_TYPE buffer_type,
                             const uint index_plot,
                             const uint index_base_array,
                             const int num_datas,
                             const int width,
                             const string label);
public:  

Ahora, tenemos un método para establecer un color único para los búferes de indicador, SetColor(). No obstante, como todos nuestros búferes son de color, deberemos añadir la capacidad de establecer, además de un solo color para todo el búfer, también un nuevo color al ya existente, o bien indicar a la vez todos los colores utilizados por el búfer.
Declaramos los dos métodos adicionales para establecer el color:

   void              SetColor(const color colour);
   void              SetColor(const color colour,const uchar index);
   void              SetColors(const color &array_colors[]);
   void              SetEmptyValue(const double value);
   virtual void      SetLabel(const string label);

El primero de los métodos añadidos agregará a la matriz de colores del objeto de búfer un nuevo color según el índice del nuevo color indicado, mientras que el segundo método establecerá directamente todos los colores utilizados por el objeto de búfer transmitidos al método en la matriz.

En bloque de retorno de los valores de las propiedades del objeto de búfer, renombramos el método NumberColors(), que retorna el número de colores utilizados por el búfer, y añadimos el método que retorna el número de matrices de búfer utilizadas para dibujar los datos del objeto de búfer:

//--- Return (1) the serial number of the drawn buffer, (2) bound array index, (3) color buffer index,
//--- (4) index of the first free bound array, (5) buffer data period (timeframe) (6) buffer status,
//--- (7) buffer type, (8) buffer usage flag, (9) arrow code, (10) arrow shift for DRAW_ARROW style,
//--- (11) Number of initial bars that are not drawn and values in DataWindow, (12) graphical construction type,
//--- (13) flag of displaying construction values in DataWindow, (14) indicator graphical construction shift along the time axis,
//--- (15) drawing line style, (16) drawing line width, (17) number of colors, (18) drawing color, number of buffers for construction
//--- (19) set empty value, (20) buffer symbol and (21) name of the indicator graphical series displayed in DataWindow
   int               IndexPlot(void)                           const { return (int)this.GetProperty(BUFFER_PROP_INDEX_PLOT);              }
   int               IndexBase(void)                           const { return (int)this.GetProperty(BUFFER_PROP_INDEX_BASE);              }
   int               IndexColor(void)                          const { return (int)this.GetProperty(BUFFER_PROP_INDEX_COLOR);             }
   int               IndexNextBuffer(void)                     const { return (int)this.GetProperty(BUFFER_PROP_INDEX_NEXT);              }
   ENUM_TIMEFRAMES   Timeframe(void)                           const { return (ENUM_TIMEFRAMES)this.GetProperty(BUFFER_PROP_TIMEFRAME);   }
   ENUM_BUFFER_STATUS Status(void)                             const { return (ENUM_BUFFER_STATUS)this.GetProperty(BUFFER_PROP_STATUS);   }
   ENUM_BUFFER_TYPE  TypeBuffer(void)                          const { return (ENUM_BUFFER_TYPE)this.GetProperty(BUFFER_PROP_TYPE);       }
   bool              IsActive(void)                            const { return (bool)this.GetProperty(BUFFER_PROP_ACTIVE);                 }
   uchar             ArrowCode(void)                           const { return (uchar)this.GetProperty(BUFFER_PROP_ARROW_CODE);            }
   int               ArrowShift(void)                          const { return (int)this.GetProperty(BUFFER_PROP_ARROW_SHIFT);             }
   int               DrawBegin(void)                           const { return (int)this.GetProperty(BUFFER_PROP_DRAW_BEGIN);              }
   ENUM_DRAW_TYPE    DrawType(void)                            const { return (ENUM_DRAW_TYPE)this.GetProperty(BUFFER_PROP_DRAW_TYPE);    }
   bool              IsShowData(void)                          const { return (bool)this.GetProperty(BUFFER_PROP_SHOW_DATA);              }
   int               Shift(void)                               const { return (int)this.GetProperty(BUFFER_PROP_SHIFT);                   }
   ENUM_LINE_STYLE   LineStyle(void)                           const { return (ENUM_LINE_STYLE)this.GetProperty(BUFFER_PROP_LINE_STYLE);  }
   int               LineWidth(void)                           const { return (int)this.GetProperty(BUFFER_PROP_LINE_WIDTH);              }
   int               ColorsTotal(void)                        const { return (int)this.GetProperty(BUFFER_PROP_COLOR_INDEXES);           }
   color             Color(void)                               const { return (color)this.GetProperty(BUFFER_PROP_COLOR);                 }
   int               BuffersTotal(void)                        const { return (int)this.GetProperty(BUFFER_PROP_NUM_DATAS);               }
   double            EmptyValue(void)                          const { return this.GetProperty(BUFFER_PROP_EMPTY_VALUE);                  }
   string            Symbol(void)                              const { return this.GetProperty(BUFFER_PROP_SYMBOL);                       }
   string            Label(void)                               const { return this.GetProperty(BUFFER_PROP_LABEL);                        }

En el bloque de métodos que retornan la descripción de las propiedades del objeto de búfer, añadimos el método que retorna la descripción de los colores establecidos para el dibujado del búfer:

//--- Return descriptions of the (1) buffer status, (2) buffer type, (3) buffer usage flag, (4) flag of displaying construction values in DataWindow,
//--- (5) drawing line style, (6) set empty value, (7) graphical construction type and (8) used timeframe and (9) specified colors
   string            GetStatusDescription(bool draw_type=false)const;
   string            GetTypeBufferDescription(void)            const;
   string            GetActiveDescription(void)                const;
   string            GetShowDataDescription(void)              const;
   string            GetLineStyleDescription(void)             const;
   string            GetEmptyValueDescription(void)            const;
   string            GetDrawTypeDescription(void)              const;
   string            GetTimeframeDescription(void)             const;
   string            GetColorsDescription(void)                const;

Y luego añadimos al final del cuerpo de la clase el bloque de métodos para trabajar con el objeto de búfer:

//--- Return the size of the data buffer array
   virtual int       GetDataTotal(const uint buffer_index=0)   const;
//--- Return the value from the specified index of the specified (1) data and (2) color buffer arrays
   virtual double    GetDataBufferValue(const uint buffer_index,const uint series_index) const;
   virtual color     GetColorBufferValue(const uint series_index)     const;
//--- Set the value to the specified index of the specified (1) data and (2) color buffer arrays
   virtual void      SetBufferValue(const uint buffer_index,const uint series_index,const double value);
   virtual void      SetBufferColorIndex(const uint series_index,const uchar color_index);
//--- Initialize all object buffers by (1) a specified value, (2) the empty value specified for the object
   virtual void      InitializeBuffers(const double value,const uchar color_index);
   virtual void      InitializeBuffers(void);
//--- Fill all data buffers with empty values in the specified timeseries index
   void              ClearData(const int series_index);
   
  };
//+------------------------------------------------------------------+

La implementación de estos métodos se analizará más tarde.

El constructor paramétrico cerrado de la clase ha sufrido algunos cambios:

//+------------------------------------------------------------------+
//| Constructor paramétrico cerrado                                  |
//+------------------------------------------------------------------+
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 int width,
                 const string label)
  {
   this.m_type=COLLECTION_BUFFERS_ID;
//--- Guardando las propiedades de tipo entero
   this.m_long_prop[BUFFER_PROP_STATUS]                        = buffer_status;
   this.m_long_prop[BUFFER_PROP_TYPE]                          = buffer_type;
   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_FILLING ? 1 : 2);
   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.GetProperty(BUFFER_PROP_NUM_DATAS);
   this.m_long_prop[BUFFER_PROP_INDEX_NEXT]                    = this.GetProperty(BUFFER_PROP_INDEX_COLOR)+(this.Status()!=BUFFER_STATUS_FILLING ? 1 : 0);
   
//--- Guardando las propiedades de tipo real
   this.m_double_prop[this.IndexProp(BUFFER_PROP_EMPTY_VALUE)] = EMPTY_VALUE;
//--- Guardando las propiedades de tipo string
   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);

//--- If failed to change the size of the drawn 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, display the appropriate message indicating the string
   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 drawn 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,INDICATOR_DATA);
      //--- Set indexation flag as in the timeseries to all buffer arrays
      ::ArraySetAsSeries(this.DataBuffer[i].Array,true);
     }
//--- Binding the color buffer with the array
   if(this.Status()!=BUFFER_STATUS_FILLING)
     {
      ::SetIndexBuffer((int)this.GetProperty(BUFFER_PROP_INDEX_COLOR),this.ColorBufferArray,INDICATOR_COLOR_INDEX);
      ::ArraySetAsSeries(this.ColorBufferArray,true);
     }

//--- Set integer buffer parameters
   ::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 buffer parameters
   ::PlotIndexSetDouble((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_EMPTY_VALUE,this.GetProperty(BUFFER_PROP_EMPTY_VALUE));
//--- Set string buffer parameters
   ::PlotIndexSetString((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LABEL,this.GetProperty(BUFFER_PROP_LABEL));
  }
//+------------------------------------------------------------------+

Establecemos el tipo de búfer:

   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;

Para definir los tipos de dibujado, tenemos el método SetDrawType(), descrito con detalle en el artículo anterior:

Todos nuestros búferes serán de color. Por eso, para establecer el estilo de dibujado, comprobamos el estado del búfer y el tipo de dibujado transmitidos al método SetDrawType(), y, dependiendo de ellos, o bien establecemos la ausencia de dibujado, o bien el rellenado a color del espacio entre dos niveles, o bien desplazamos el índice de la enumeración del estado en 8 unidades, de forma que el valor de la constante se corresponda con el valor del búfer de color de la enumeración de estilos de dibujado.
Fuera del cuerpo de la clase, implementamos este método:

//+------------------------------------------------------------------+
//| Set the graphical construction type                              |
//+------------------------------------------------------------------+
void CBuffer::SetDrawType(void)
  {
   ENUM_DRAW_TYPE type=(!this.TypeBuffer() || !this.Status() ? DRAW_NONE : this.Status()==BUFFER_STATUS_FILLING ? DRAW_FILLING : ENUM_DRAW_TYPE(this.Status()+8));
   this.SetProperty(BUFFER_PROP_DRAW_TYPE,type);
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_DRAW_TYPE,type);
  }
//+------------------------------------------------------------------+

Si el tipo de búfer es BUFFER_TYPE_CALCULATE (valor 0) o el estado del búfer es BUFFER_STATUS_NONE (valor 0), el estilo de dibujado se establecerá como "No hay dibujado"; si el estado del búfer es BUFFER_STATUS_FILLING (rellenado de color), el estilo de dibujado se establecerá como el correspondiente. Todos los demás valores simplemente se establecen en 8: precisamente este desplazamiento indicará la constante del estilo de color del dibujado.

Aquí, en el constructor de la clase, utilizamos el mismo modo de cálculo del tipo de búfer.
Por el momento, el método SetDrawType() permanece dentro de la clase, pero probablemente lo quitemos por completo, ya que solo lo necesitamos al crear el objeto, y ya después, cambiar el tipo de dibujado no tiene sentido.
Se quedará en su sitio temporalmente, por si encontramos próximamente algún modo de utilizarlo.


El grosor de la línea ahora se transmite en los parámetros del constructor, por eso, estableceremos como valor por defecto precisamente el valor transmitido al constructor,
mientras que estableceremos un valor igual a la unidad como valor por defecto para todos los tipos de construcciones gráficas, excepto DRAW_FILLING
: en este tipo de dibujado, siempre se utilizan dos colores, que precisamente estableceremos:

   this.m_long_prop[BUFFER_PROP_LINE_WIDTH]                    = width;
   this.m_long_prop[BUFFER_PROP_COLOR_INDEXES]                 = (this.Status()!=BUFFER_STATUS_FILLING ? 1 : 2);

El número de búferes de datos para el dibujado ahora también se transmite en los parámetros del constructor. Vamos añadir estos:

   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.GetProperty(BUFFER_PROP_NUM_DATAS);
   this.m_long_prop[BUFFER_PROP_INDEX_NEXT]                    = this.GetProperty(BUFFER_PROP_INDEX_COLOR)+(this.Status()!=BUFFER_STATUS_FILLING ? 1 : 0);

Y para calcular el índice de la siguiente matriz, para asignar esta como primera matriz de búfer, necesitaremos tener en cuenta que en el búfer con el tipo de construcción gráfica DRAW_FILLING no se usa el búfer de color. Por eso, para un búfer con este tipo de dibujado, deberemos excluir del cálculo el búfer de color, que es precisamente lo que hacemos aquí: para todos los tipos de dibujado, añadimos una unidad al valor calculado del índice del búfer de color, mientras que para el "rellenado con color", añadimos cero, puesto que el índice del siguiente búfer ya es igual al índice calculado del búfer de color ausente en el objeto.

Las demás acciones se describen en los comentarios al código.
Merece la pena destacar que el establecimiento del color ahora se ejecuta con la ayuda del método de establecimiento de colores, dado que los colores se establecen en las propiedades del objeto dentro del método; asimismo, se rellena la matriz de colores, que posteriormente se usa para obtener el color necesario según su índice:

//--- Set integer buffer parameters
   ::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));

En el método que retorna la descripción de una propiedad de tipo entero, ha cambiado el retorno de la descripción del grosor y el color de la línea del indicador:

//+------------------------------------------------------------------+
//| Return description of a buffer's integer property                |
//+------------------------------------------------------------------+
string CBuffer::GetPropertyDescription(ENUM_BUFFER_PROP_INTEGER property)
  {
   return
     (
   // ... Removed unnecessary strings
   // ...
   // ...

      property==BUFFER_PROP_LINE_STYLE    ?  CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_LINE_STYLE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+this.GetLineStyleDescription()
         )  :
      property==BUFFER_PROP_LINE_WIDTH    ?  
         (this.Status()==BUFFER_STATUS_ARROW ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_ARROW_SIZE) :
          CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_LINE_WIDTH))+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==BUFFER_PROP_COLOR_INDEXES ?  CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_COLOR_NUM)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==BUFFER_PROP_COLOR         ?  CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_COLOR)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+this.GetColorsDescription()
         )  :
      property==BUFFER_PROP_INDEX_BASE    ?  CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_INDEX_BASE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
   
   // ...
   // ...
   // ... Removed unnecessary strings

      ""
     );
  }
//+------------------------------------------------------------------+

Para retornar la descripción del grosor de la línea, se comprueba el estado del búfer, y si se trata de un indicador de flechas, este no tendrá líneas, por lo que se retornará una entrada sobre el tamaño de los inconos de las flechas.
Para retornar el color, ahora se utiliza el método GetColorsDescription(), que devuelve la descripción de todos los colores del búfer utilizados, y que veremos posteriormente.

El método para retornar el valor vacío establecido, ahora tiene en cuenta el valor negativo EMPTY_VALUE, establecido como "Valor vacío" del búfer:

//+------------------------------------------------------------------+
//| Return description of the set empty value                        |
//+------------------------------------------------------------------+
string CBuffer::GetEmptyValueDescription(void) const
  {
   double value=fabs(this.EmptyValue());
   return(value<EMPTY_VALUE ? ::DoubleToString(this.EmptyValue(),(this.EmptyValue()==0 ? 1 : 8)) : (this.EmptyValue()>0 ? "EMPTY_VALUE" : "-EMPTY_VALUE"));
  }
//+------------------------------------------------------------------+

Método que retorna la descripción de los colores establecidos para un objeto de búfer:

//+------------------------------------------------------------------+
//| Return description of specified colors                           |
//+------------------------------------------------------------------+
string CBuffer::GetColorsDescription(void) const
  {
   int total=this.ColorsTotal();
   if(total==1)
      return ::ColorToString(this.Color(),true);
   string res="";
   for(int i=0;i<total;i++)
     {
      res+=::ColorToString(this.ArrayColors[i],true)+(i<total-1 ? "," : "");
     }
   return res;
  }
//+------------------------------------------------------------------+

Si solo se ha establecido un color, se retornará la descripción de línea de la propiedad del objeto de búfer que guarda el valor del color;
si hay varios colores, y estos se encuentran en la matriz de colores, se creará en un ciclo una línea formada por sus descripciones, después de lo cual, se retornará el resultado.

Método que establece el número de colores de un objeto de búfer:

//+------------------------------------------------------------------+
//| Set the number of colors                                         |
//+------------------------------------------------------------------+
void CBuffer::SetColorNumbers(const int number)
  {
   if(number>IND_COLORS_TOTAL)
      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);
  }
//+------------------------------------------------------------------+

Si transmitimos al método un número superior al máximo posible, saldremos del método.
Si el tipo de dibujado de líneas no es DRAW_FILLING, estableceremos un número igual al transmitido, de lo contrario, será dos.
A continuación, cambiamos el tamaño de la matriz de colores y asignamos al búfer el nuevo valor.

Método para asignar el color de dibujado a un objeto de búfer:

//+------------------------------------------------------------------+
//| Set a single specified drawing color for the buffer              |
//+------------------------------------------------------------------+
void CBuffer::SetColor(const color colour)
  {
   if(this.Status()==BUFFER_STATUS_FILLING)
      return;
   this.SetColorNumbers(1);
   this.SetProperty(BUFFER_PROP_COLOR,colour);
   this.ArrayColors[0]=colour;
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LINE_COLOR,0,this.ArrayColors[0]);
  }
//+------------------------------------------------------------------+

Si el estilo de dibujado es DRAW_FILLING, saldremos del método: en este estilo de dibujado, siempre se utilizan dos colores.
A continuación, establecemos un número de colores igual a uno, asignamos a la propiedad del objeto de búfer el color transmitido, registramos en la única celda de la matriz de colores el color transmitido y asignamos este color al búfer de indicador.

Para colorear de nuevo las líneas del indicador, debemos asignar el número del color (su índice en la lista de colores utilizados) al búfer de color del indicador como el índice necesario de la serie temporal donde se debe cambiar el color.
Por ejemplo, si utilizamos tres colores, tendrán los índices 0,1,2. Para colorear la línea del indicador con uno de los tres colores, bastará con anotar en el búfer de color el número que se corresponde con el color necesario. Si en el índice 0 guardamos el color azul, y en el índice 1, el rojo, para colorear la línea, deberemos escribir en el búfer de color del indicador el valor 0 para el color azul, y el valor 1 para el rojo. Y la línea del indicador será nuevamente coloreada.
Para que exista la posibilidad de cambiar los colores asignados a un objeto de búfer, necesitamos un método que registre el nuevo color en el índice de color señalado del búfer de indicador.

Método que asigna el color de dibujado al índice de color indicado:

//+------------------------------------------------------------------+
//| Set the drawing color to the specified color index               |
//+------------------------------------------------------------------+
void CBuffer::SetColor(const color colour,const uchar index)
  {
   if(index>IND_COLORS_TOTAL-1)
      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]);
  }
//+------------------------------------------------------------------+

Transmitimos al método el color y el índice de color según el cual será anotado.
Si se ha transmitido un índice de color superior al número máximo posible de colores del indicador, saldremos.
Si el índice supera el número de colores que ya tenemos para el objeto de búfer, asignaremos un nuevo número de colores al objeto de búfer.
A continuación, registramos en la matriz de colores el color transmitido según el índice indicado; si se ha transmitido un índice cero, estableceremos en la propiedad del búfer el color transmitido al método, y finalmente, asignaremos al búfer de indicador el nuevo valor de color según el índice transmitido al método.

Método para asignar el color al objeto de búfer de la matriz de colores transmitida:

//+------------------------------------------------------------------+
//| Set drawing colors from the color array                          |
//+------------------------------------------------------------------+
void CBuffer::SetColors(const color &array_colors[])
  {
//--- Exit if the passed array is empty
   if(::ArraySize(array_colors)==0)
      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]);
  }
//+------------------------------------------------------------------+

Todas las líneas del método han sido comentadas con detalle. Esperamos que no surjan dudas al respecto.

Método que retorna el índice corregido de la matriz de búfer necesaria:

//+------------------------------------------------------------------+
//| Return the adjusted buffer array index                           |
//+------------------------------------------------------------------+
int CBuffer::GetCorrectIndexBuffer(const uint buffer_index) const
  {
   return int(buffer_index<this.GetProperty(BUFFER_PROP_NUM_DATAS) ? buffer_index : this.GetProperty(BUFFER_PROP_NUM_DATAS)-1);
  }
//+------------------------------------------------------------------+

Como ahora todas las matrices de datos para los búferes de indicador se encuentran en la matriz DataBuffer[] con el tipo de estructura SDataBuffer, para no salir accidentalmente de los límites de la matriz al transmitir un índice incorrecto, utilizaremos un método que corrige los índices de matriz transmitidos de forma incorrecta.
Si el índice transmitido es inferior al número total de búferes utilizados para la construcción, se retornará el índice transmitido.
En cualquier caso, se retornará el índice del último elemento de la matriz.
Si hay una matriz, el último elemento será también el primer elemento.

Método para retornar el tamaño de la matriz del búfer de datos indicado:

//+------------------------------------------------------------------+
//| Return the array size of the specified data buffer               |
//+------------------------------------------------------------------+
int CBuffer::GetDataTotal(const uint buffer_index=0) const
  {
    int index=this.GetCorrectIndexBuffer(buffer_index);
    return(index>WRONG_VALUE ? ::ArraySize(this.DataBuffer[index].Array) : 0);
  }
//+------------------------------------------------------------------+

El método sirve más bien para la depuración y para el uso interno.
Retorna el tamaño de la matriz designada por el búfer de indicador según su índice en la matriz DataBuffer[].
En OnInit(), siempre retorna cero. En OnCalculate(), retorna el tamaño de rates_total.

Método que retorna el valor del búfer de datos indicado según el índice de serie temporal indicado:

//+------------------------------------------------------------------+
//| Return the value from the specified timeseries index             |
//| of the specified data buffer array                               |
//+------------------------------------------------------------------+
double CBuffer::GetDataBufferValue(const uint buffer_index,const uint series_index) const
  {
   int correct_buff_index=this.GetCorrectIndexBuffer(buffer_index);
   int data_total=this.GetDataTotal(correct_buff_index);
   if(data_total==0)
      return this.EmptyValue();
   int data_index=((int)series_index<data_total ? (int)series_index : data_total-1);
   return this.DataBuffer[correct_buff_index].Array[data_index];
  }
//+------------------------------------------------------------------+

El método sirve para obtener y retornar datos según el índice de una serie temporal del búfer de indicador especificado.
En primer lugar, corregimos el índice obtenido del búfer necesario, comprobamos la cantidad de datos en el búfer, y si no los hay, retornamos el valor vacío establecido para el objeto de búfer. A continuación, comprobamos si índice de las series temporales se sale del límite de los datos en el búfer, y si el índice de la serie temporal se sale de los límites de la matriz, retornamos el último elemento del búfer, de lo contrario, retornamos los datos del búfer indicado según el índice de la serie temporal.

Método que retorna el valor del búfer de color según el índice de serie temporal indicado:

//+------------------------------------------------------------------+
//| Return the value from the specified timeseries index             |
//| of the specified color buffer array                              |
//+------------------------------------------------------------------+
color CBuffer::GetColorBufferValue(const uint series_index) const
  {
   int data_total=this.GetDataTotal(0);
   if(data_total==0)
      return clrNONE;
   int data_index=((int)series_index<data_total ? (int)series_index : data_total-1);
   int color_index=(this.ColorsTotal()==1 ? 0 : (int)this.ColorBufferArray[data_index]);
   return (color)this.ArrayColors[color_index];
  }
//+------------------------------------------------------------------+

El método es prácticamente idéntico al que acabamos de ver, salvo que el búfer de color siempre es uno en nuestro caso, y no necesitamos obtener y corregir el índice del búfer necesario. Aquí, comprbamos de inmediato el índice de la serie temporal y retornamos los datos del búfer según el índice corregido (si se ha transmitido un índice no válido).

Método que asigna el valor al búfer de datos indicado según el índice de serie temporal indicado:

//+------------------------------------------------------------------+
//| Set the value to the specified timeseries index                  |
//| for the specified data buffer array                              |
//+------------------------------------------------------------------+
void CBuffer::SetBufferValue(const uint buffer_index,const uint series_index,const double value)
  {
   if(this.GetDataTotal(buffer_index)==0)
      return;
   int correct_buff_index=this.GetCorrectIndexBuffer(buffer_index);
   int data_total=this.GetDataTotal(buffer_index);
   int data_index=((int)series_index<data_total ? (int)series_index : data_total-1);
   this.DataBuffer[correct_buff_index].Array[data_index]=value;
  }
//+------------------------------------------------------------------+

El método es idéntico al método de obtención de datos del búfer indicado -analizado más arriba- según el índice de serie temporal, salvo que, en lugar de retornar el valor, el método registra en el búfer de datos indicado el valor transmitido según el índice de serie temporal indicado.

Método que asigna el valor al búfer de color según el índice de serie temporal indicado:

//+------------------------------------------------------------------+
//| 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)
  {
   if(this.GetDataTotal(0)==0 || color_index>this.ColorsTotal()-1 || this.Status()==BUFFER_STATUS_FILLING)
      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));
   this.ColorBufferArray[data_index]=color_index;
  }
//+------------------------------------------------------------------+

El método es idéntico al método de obtención del índice de color del índice de serie temporal especificado, salvo que, en lugar de retornar el valor, el método registra en el búfer de color el valor del índice de color transmitido según el índice de serie temporal indicado.
Además, si no existen los datos correspondientes en la matriz de datos (esta se encuentra vacía), o el índice de color transmitido es mayor que el tamaño de la matriz de colores, o el estilo de dibujado del objeto de búfer DRAW_FILLING (где нет буфера цвета), saldremos del método.
Y si el tamaño del búfer de color es igual a cero, los más probale es que no se hayan establecido correctamente los valores en #property indicator_buffers, por lo que se mostrará un mensaje sobre ello; a continuación, obtendremos el error de salida de los ímites de la matriz, que no se procesará a propósito para que podamos comprender por las entradas en el diario que debemos colocar correctamente el número de búferes en las propiedades del indicador.

Método de inicialización de todos los búferes del objeto de búfer con los valores indicador del búfer de datos y el búfer de color:

//+------------------------------------------------------------------+
//| Initialize all object buffers by the specified value             |
//+------------------------------------------------------------------+
void CBuffer::InitializeBuffers(const double value,const uchar color_index)
  {
   for(int i=0;i<this.GetProperty(BUFFER_PROP_NUM_DATAS);i++)
      ::ArrayInitialize(this.DataBuffer[i].Array,value);
   if(this.Status()!=BUFFER_STATUS_FILLING)
      ::ArrayInitialize(this.ColorBufferArray,(color_index>this.ColorsTotal()-1 ? 0 : color_index));
  }
//+------------------------------------------------------------------+

En un ciclo por la lista completa con todos los búferes de datos, inicializamos cada nueva matriz con el valor indicado.
Si el estilo de dibujado del objeto de búfer no es DRAW_FILLING, inicializamos el búfer de color con el valor indicado, corregido en el caso de que se haya salido de los límites de la matriz de colores.

Método de inicialización de todas las matrices de búferes existentes en el objeto de búfer, con el valor "vacío" asignado al objeto de búfer:

//+------------------------------------------------------------------+
//| Initialize all object buffers                                    |
//| by the empty value set for the object                            |
//+------------------------------------------------------------------+
void CBuffer::InitializeBuffers(void)
  {
   for(int i=0;i<this.GetProperty(BUFFER_PROP_NUM_DATAS);i++)
      ::ArrayInitialize(this.DataBuffer[i].Array,this.EmptyValue());
   if(this.Status()!=BUFFER_STATUS_FILLING)
      ::ArrayInitialize(this.ColorBufferArray,0);
  }
//+------------------------------------------------------------------+

El método es idéntico al que hemos analizado anteriormente, salvo que el valor de inicialización se toma del valor "vacío" establecido para el objeto, mientras que como índice de color para la inicialización se toma el primero.

Con mucha frecuencia, resulta necesario limpiar en los indicadores los valores de todos los búferes antes de su rellenado, para evitar la aparición de elementos innecesarios en el gráfico.
El siguiente método se encarga precisamente de limpiar todos los búferes en el índice especificado de la serie temporal.
Método que rellena con un valor vacío todos los búferes de indicador del objeto de búfer:

//+------------------------------------------------------------------+
//| Fill all data buffers with the empty value                       |
//| in the specified timeseries index                                |
//+------------------------------------------------------------------+
void CBuffer::ClearData(const int series_index)
  {
   for(int i=0;i<this.GetProperty(BUFFER_PROP_NUM_DATAS);i++)
      this.SetBufferValue(i,series_index,this.EmptyValue());
   this.SetBufferColorIndex(series_index,0);
  }
//+------------------------------------------------------------------+

En un ciclo por todas las matrices de búfer del objeto de búfer, registramos el valor vacío asignado al objeto de búfer según el índice de serie temporal especificado con el método SetBufferValue(), que hemos analizado antes, y con la ayuda del método SetBufferColorIndex(), igualmente analizado más arriba, establecemos el índice de color en el índice de serie temporal especificado.

Estos son todos los cambios a realizar en la clase del objeto de búfer abstracto en lo que respecta a la creación y el trabajo de los objetos herederos de esta clase.

Los objetos de búfer de indicador como herederos del objeto de búfer abstracto

Bien. Como ya hemos visto, casi todos los objetos de la biblioteca tienen una estructura similar, es decir, un objeto bastracto común con propiedades y métodos comunes para sus herederos, cuyos objetos derivados precisan y concretan los datos del objeto abstracto. Ya hemos analizado el concepto de los objetos de la biblioteca : en el primer artículo, el objeto abstracto, y en el segundo, los objetos herederos y sus colecciones.

Los objetos de búfer no serán una excepción. Cada uno de estos objetos puede suponer un búfer de indicador con un estilo de dibujado concreto para sí mismo. Precisamente del estilo de dibujado tomará su nombre.

El primero objeto de búfer tendrá el estilo de dibujado "Flechas", adoptando así el nombre correspondiente.

Vamos a crear en la carpeta del terminal \MQL5\Include\DoEasy\Objects\Indicators\ el nuevo archivo BufferArrow.mqh de la clase CBufferArrow, cuya clase básica será el objeto de búfer abstracto CBuffer:

//+------------------------------------------------------------------+
//|                                                  BufferArrow.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"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Archivos de inclusión                                            |
//+------------------------------------------------------------------+
#include "Buffer.mqh"
//+------------------------------------------------------------------+
//| Buffer with the "Drawing with arrows" drawing style              |
//+------------------------------------------------------------------+
class CBufferArrow : public CBuffer
  {
private:

public:
//--- Constructor
                     CBufferArrow(const uint index_plot,const uint index_base_array) :
                        CBuffer(BUFFER_STATUS_ARROW,BUFFER_TYPE_DATA,index_plot,index_base_array,1,1,"Arrows") {}
//--- Supported integer properties of a buffer
   virtual bool      SupportProperty(ENUM_BUFFER_PROP_INTEGER property);
//--- Supported real properties of a buffer
   virtual bool      SupportProperty(ENUM_BUFFER_PROP_DOUBLE property);
//--- Supported string properties of a buffer
   virtual bool      SupportProperty(ENUM_BUFFER_PROP_STRING property);
//--- Display a short buffer description in the journal
   virtual void      PrintShort(void);
  };
//+------------------------------------------------------------------+

Como podemos ver, en el listado de la clase no sobra nada.
Hemos declarado los métodos virtuales, y retornado las banderas que indican el soporte de las propiedades de tipo entero, real y string por parte del objeto, así como un método virtual que muestra en el diario la descripción breve del búfer.
Transmitimos al constructor de la clase el índice del búfer de indicador creado para el dibujado (se trata del número ordinal del búfer en la lista de todos los búferes de indicador) y el índice de la matriz de bufer básica con la que estará verdaderamente vinculada la primera matriz double del nuevo búfer creado.
Por otra parte, hemos escrito en la lista de inicialización del constructor la inicialización del constructor protegido de la clase padre:

CBufferArrow(const uint index_plot,const uint index_base_array) :
   CBuffer(BUFFER_STATUS_ARROW,BUFFER_TYPE_DATA,index_plot,index_base_array,1,1,"Arrows") {}

donde indicamos como primer parámetro el estado de búfer "Búfer con estilo de dibujado "Dibujado con flechas", como segundo parámetro, el tipo de búfer "Búfer de datos de color"; asimismo, transmitimos los valores del índice del búfer dibujado y la matriz básica, el número de búferes para la construcción, el grosor de la línea (aquí, este parámetro indicará el tamaño de la flecha) y el nombre de la serie gráfica representado en la ventana de datos. De esta forma, al crear un nuevo objeto de búfer, indicamos a la clase básica el tipo de búfer y con qué parámetros tiene que crearlo.

Método que retorna la bandera de soporte por parte del búfer de propiedades de tipo entero:

//+------------------------------------------------------------------+
//| Return 'true' if a buffer supports a passed                      |
//| integer property, otherwise return 'false'                       |
//+------------------------------------------------------------------+
bool CBufferArrow::SupportProperty(ENUM_BUFFER_PROP_INTEGER property)
  {
   if(property==BUFFER_PROP_LINE_STYLE || (this.TypeBuffer()==BUFFER_TYPE_CALCULATE && property!=BUFFER_PROP_TYPE && property!=BUFFER_PROP_INDEX_NEXT))
      return false;
   return true;
  }
//+------------------------------------------------------------------+

Si hemos transmitido al método la propiedad "Estilo de la línea" o se trata de un búfer de cálculo, y además no se trata de la propiedad "índice del búfer", ni de la propiedad "índice del siguiente búfer de dibujado", esta propiedad no tendrá soporte y se retornará false. En los demás casos, se retornará true.

Método que retorna la bandera de soporte por parte del búfer de propiedades de tipo real:

//+------------------------------------------------------------------+
//| Return 'true' if a buffer supports a passed                      |
//| real property, otherwise return 'false'                          |
//+------------------------------------------------------------------+
bool CBufferArrow::SupportProperty(ENUM_BUFFER_PROP_DOUBLE property)
  {
   if(this.TypeBuffer()==BUFFER_TYPE_CALCULATE)
      return false;
   return true;
  }
//+------------------------------------------------------------------+

Si se trata de un búfer de cálculo, no tendrá soporte ninguna propiedad de tipo real (de todas las propiedades de tipo entero solo hay una propiedad: "Valor vacío para la construcción, para la que no hay dibujado"), por lo que retornamos false. En los demás casos, retornaremos true.

Método que retorna la bandera de soporte por parte del búfer de propiedades de tipo string:

//+------------------------------------------------------------------+
//| Return 'true' if a buffer supports a passed                      |
//| string property, otherwise return 'false'                        |
//+------------------------------------------------------------------+
bool CBufferArrow::SupportProperty(ENUM_BUFFER_PROP_STRING property)
  {
   if(this.TypeBuffer()==BUFFER_TYPE_CALCULATE)
      return false;
   return true;
  }
//+------------------------------------------------------------------+

Si se trata del búfer de cálculo, no tendrá soporte ninguna propiedad de tipo string, por lo que retornamos false. En los demás casos, retornaremos true.

Método para mostrar en el diario la descripción breve de un objeto de búfer:

//+------------------------------------------------------------------+
//| Display short buffer description in the journal                  |
//+------------------------------------------------------------------+
void CBufferArrow::PrintShort(void)
  {
   ::Print
     (
      CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_BUFFER),"(",(string)this.IndexPlot(),"): ",
      this.GetStatusDescription(true)," ",this.Symbol()," ",TimeframeDescription(this.Timeframe())
     );
  }
//+------------------------------------------------------------------+

En el método se crea una línea a partir del encabezado "Búfer", especificando el índice del búfer dibujado (número ordinal del búfer en la ventana DataWindow) con la descripción del estado del búfer (estilo de dibujado), el símbolo y el marco temporal.
De la siguiente forma, por ejemplo:

Búfer(0): Dibujado con flechas de EURUSD H1

Y esta es la clase completa del objeto de búfer con el tipo de dibujado "Dibujado con flechas".


Vamos a crear un nuevo objeto heredero del objeto de búfer abstracto CBufferLine, con el estilo de dibujado "Línea"
en el archivo \MQL5\Include\DoEasy\Objects\Indicators\BufferLine.mqh:

//+------------------------------------------------------------------+
//|                                                   BufferLine.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"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Archivos de inclusión                                            |
//+------------------------------------------------------------------+
#include "Buffer.mqh"
//+------------------------------------------------------------------+
//| Buffer of the Line drawing style                                 |
//+------------------------------------------------------------------+
class CBufferLine : public CBuffer
  {
private:

public:
//--- Constructor
                     CBufferLine(const uint index_plot,const uint index_base_array) :
                        CBuffer(BUFFER_STATUS_LINE,BUFFER_TYPE_DATA,index_plot,index_base_array,1,1,"Line") {}
//--- Supported integer properties of a buffer
   virtual bool      SupportProperty(ENUM_BUFFER_PROP_INTEGER property);
//--- Supported real properties of a buffer
   virtual bool      SupportProperty(ENUM_BUFFER_PROP_DOUBLE property);
//--- Supported string properties of a buffer
   virtual bool      SupportProperty(ENUM_BUFFER_PROP_STRING property);
//--- Display a short buffer description in the journal
   virtual void      PrintShort(void);
  };
//+------------------------------------------------------------------+
//| Return 'true' if a buffer supports a passed                      |
//| integer property, otherwise return 'false'                       |
//+------------------------------------------------------------------+
bool CBufferLine::SupportProperty(ENUM_BUFFER_PROP_INTEGER property)
  {
   if((property==BUFFER_PROP_ARROW_CODE || property==BUFFER_PROP_ARROW_SHIFT) || 
      (this.TypeBuffer()==BUFFER_TYPE_CALCULATE && property!=BUFFER_PROP_TYPE && property!=BUFFER_PROP_INDEX_NEXT)
     ) return false;
   return true; 
  }
//+------------------------------------------------------------------+
//| Return 'true' if a buffer supports a passed                      |
//| real property, otherwise return 'false'                          |
//+------------------------------------------------------------------+
bool CBufferLine::SupportProperty(ENUM_BUFFER_PROP_DOUBLE property)
  {
   if(this.TypeBuffer()==BUFFER_TYPE_CALCULATE)
      return false;
   return true;
  }
//+------------------------------------------------------------------+
//| Return 'true' if a buffer supports a passed                      |
//| string property, otherwise return 'false'                        |
//+------------------------------------------------------------------+
bool CBufferLine::SupportProperty(ENUM_BUFFER_PROP_STRING property)
  {
   if(this.TypeBuffer()==BUFFER_TYPE_CALCULATE)
      return false;
   return true;
  }
//+------------------------------------------------------------------+
//| Display short buffer description in the journal                  |
//+------------------------------------------------------------------+
void CBufferLine::PrintShort(void)
  {
   ::Print
     (
      CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_BUFFER),"(",(string)this.IndexPlot(),"): ",
      this.GetStatusDescription(true)," ",this.Symbol()," ",TimeframeDescription(this.Timeframe())
     );
  }
//+------------------------------------------------------------------+

En comparación con la clase de búfer de flechas, aquí, en la lista de inicialización del constructor de la clase, transmitimos al constructor del objeto de búfer básico el estado "Línea" y el nombre de la serie gráfica "Line". Los demás parámetros son iguales que en el objeto de búfer de flechas:

CBufferLine(const uint index_plot,const uint index_base_array) :
   CBuffer(BUFFER_STATUS_LINE,BUFFER_TYPE_DATA,index_plot,index_base_array,1,1,"Line") {}

Asismismo, hemos modificado el método que retorna la bandera de soporte por parte del búfer de propiedades de tipo entero:

//+------------------------------------------------------------------+
//| Return 'true' if a buffer supports a passed                      |
//| integer property, otherwise return 'false'                       |
//+------------------------------------------------------------------+
bool CBufferLine::SupportProperty(ENUM_BUFFER_PROP_INTEGER property)
  {
   if((property==BUFFER_PROP_ARROW_CODE || property==BUFFER_PROP_ARROW_SHIFT) || 
      (this.TypeBuffer()==BUFFER_TYPE_CALCULATE && property!=BUFFER_PROP_TYPE && property!=BUFFER_PROP_INDEX_NEXT)
     ) return false;
   return true; 
  }
//+------------------------------------------------------------------+

Si la propiedad es "código de flecha" o "desplazamiento vertical de la flecha" o se trata de un búfer de cálculo, y además no se trata de la propiedad "índice del búfer", ni de la propiedad "índice del siguiente búfer de dibujado, esta propiedad no tendrá soporte y se retornará false. En los demás casos, se retornará true.


Vamos a crear un nuevo objeto heredero del objeto de búfer abstracto CBufferSection, con el estilo de dibujado "Segmentos"
en el archivo \MQL5\Include\DoEasy\Objects\Indicators\BufferSection.mqh.
A continuación, solo vamos a analizar los constructores de las nuevas clases y los métodos de soporte de propiedades de tipo entero, ya que todo lo demás es idéntico a lo ya analizado en las dos clases.

En la lista de inicialización del constructor de la clase, transmitimos al constructor del objeto de búfer básico el estado "Segmentos" y el nombre de la serie gráfica "Section".

CBufferSection(const uint index_plot,const uint index_base_array) :
   CBuffer(BUFFER_STATUS_SECTION,BUFFER_TYPE_DATA,index_plot,index_base_array,1,1,"Section") {}

Método que retorna la bandera de soporte por parte del búfer de propiedades de tipo entero:

//+------------------------------------------------------------------+
//| Return 'true' if a buffer supports a passed                      |
//| integer property, otherwise return 'false'                       |
//+------------------------------------------------------------------+ 
bool CBufferSection::SupportProperty(ENUM_BUFFER_PROP_INTEGER property)
  {
   if((property==BUFFER_PROP_ARROW_CODE || property==BUFFER_PROP_ARROW_SHIFT) || 
      (this.TypeBuffer()==BUFFER_TYPE_CALCULATE && property!=BUFFER_PROP_TYPE && property!=BUFFER_PROP_INDEX_NEXT)
     ) return false;
   return true; 
  }
//+------------------------------------------------------------------+

Si la propiedad es "código de flecha" o "desplazamiento vertical de la flecha" o se trata de un búfer de cálculo, y además no se trata de la propiedad "índice del búfer", ni de la propiedad "índice del siguiente búfer de dibujado, esta propiedad no tendrá soporte y se retornará false. En los demás casos, se retornará true.


Vamos a crear un nuevo objeto heredero del objeto de búfer abstracto CBufferHistogram, con el estilo de dibujado "Histograma desde la línea cero"
en el archivo \MQL5\Include\DoEasy\Objects\Indicators\BufferHistogram.mqh.

En la lista de inicialización del constructor de la clase, transmitimos al constructor del objeto de búfer básico el estado "Histograma desde la línea cero" y el nombre de la serie gráfica "Histogram", mientras que el grosor de la línea del histograma será por defecto igual a 2.

CBufferHistogram(const uint index_plot,const uint index_base_array) :
   CBuffer(BUFFER_STATUS_HISTOGRAM,BUFFER_TYPE_DATA,index_plot,index_base_array,1,2,"Histogram") {}

Los demás métodos son idénticos a los de la clase del objeto de búfer "Segmentos".


Vamos a crear un nuevo objeto heredero del objeto de búfer abstracto CBufferHistogram2, con el estilo de dibujado "Histograma basado en dos búferes de indicador"
en el archivo \MQL5\Include\DoEasy\Objects\Indicators\BufferHistogram2.mqh.

En la lista de inicialización del constructor de la clase, transmitimos al constructor del objeto de búfer básico el estado "Histograma basado en dos búferes de indicador,
el nombre de la serie gráfica "Histogram2 0;Histogram2 1"
, los dos búferes para la construcción y un grosor de la línea del histograma por defecto igual a 8.

CBufferHistogram2(const uint index_plot,const uint index_base_array) :
   CBuffer(BUFFER_STATUS_HISTOGRAM2,BUFFER_TYPE_DATA,index_plot,index_base_array,2,8,"Histogram2 0;Histogram2 1") {}

Por eso, el grosor de la línea del histograma es de 8.
En cuanto a los indicadores de histograma construidos con dos búferes, en MQL5, se trata del tamaño máximo de anchura de la columna del histograma, y dicha anchura depende de la escala horizontal del gráfico:


Con una escala máxima del gráfico (5), la anchura de la columna del histograma tiene su tamaño máximo. Si necesitamos un tamaño menor, después de crear el búfer, podemos asignarle un nuevo valor de anchura con la ayuda del método SetWidth().

¿Por qué el nombre de la serie gráfica es "Histogram2 0;Histogram2 1"?
En los indicadores que usan varios búferes para su construcción, el nombre para cada búfer se indica en la denominación de la serie gráfica. Si necesitamos asignar a cada búfer su propia denominación única, todos ellos se deberá indicar usando punto y coma (;) al especificar el nombre de la serie gráfica.
Aquí, para el nombre del primer búfer, tenemos la denomiación "Histogram2 0" (el cero especifica el índice del primer búfer), y para el segundo búfer, hemos establecido la denominación "Histogram2 1" (el uno especifica el índice del segundo búfer).
En la ventana DataWindow, se representará de la forma siguiente:


Si necesitamos otras denominaciones, después de crear el búfer, podemos asignarle nuevas denominaciones a los búferes con la ayuda del método SetLabel().

Los demás métodos son idénticos a los de la clase del objeto de búfer "Segmentos".


Vamos a crear un nuevo objeto heredero del objeto de búfer abstracto CBufferZigZag, con el estilo de dibujado "Zigzag"
en el archivo \MQL5\Include\DoEasy\Objects\Indicators\BufferZigZag.mqh.

En la lista de inicialización del constructor de la clase, transmitimos al constructor del objeto de búfer básico el estado "Zigzag",
el nombre de la serie gráfica "ZigZag 0;ZigZag 1", los dos búferes para la construcción y un grosor de la línea del zigzag por defecto igual a 1.

CBufferZigZag(const uint index_plot,const uint index_base_array) :
   CBuffer(BUFFER_STATUS_ZIGZAG,BUFFER_TYPE_DATA,index_plot,index_base_array,2,1,"ZigZag 0;ZigZag 1") {}

Los demás métodos son idénticos a los de la clase del objeto de búfer "Segmentos".


Vamos a crear un nuevo objeto heredero del objeto de búfer abstracto CBufferFilling, con el estilo de dibujado "Rellenado de color entre dos niveles"
en el archivo \MQL5\Include\DoEasy\Objects\Indicators\BufferFilling.mqh.

En la lista de inicialización del constructor de la clase, transmitimos al constructor del objeto de búfer básico el estado "Rellenado de color entre dos niveles",
el nombre de la serie gráfica "Filling 0;Filling 1", los dos búferes para la construcción y un grosor de la línea por defecto igual a 1 (en este estilo de dibujado no hay líneas, así que esta propiedad no importa.

CBufferFilling(const uint index_plot,const uint index_base_array) :
   CBuffer(BUFFER_STATUS_FILLING,BUFFER_TYPE_DATA,index_plot,index_base_array,2,1,"Filling 0;Filling 1") {}

Método que retorna la bandera de soporte por parte del búfer de propiedades de tipo entero:

//+------------------------------------------------------------------+
//| Return 'true' if a buffer supports a passed                      |
//| integer property, otherwise return 'false'                       |
//+------------------------------------------------------------------+ 
bool CBufferFilling::SupportProperty(ENUM_BUFFER_PROP_INTEGER property)
  {
   if((property==BUFFER_PROP_ARROW_CODE || property==BUFFER_PROP_ARROW_SHIFT) || property==BUFFER_PROP_LINE_STYLE || 
       property==BUFFER_PROP_LINE_WIDTH || property==BUFFER_PROP_INDEX_COLOR ||
      (this.TypeBuffer()==BUFFER_TYPE_CALCULATE && property!=BUFFER_PROP_TYPE && property!=BUFFER_PROP_INDEX_NEXT)
     ) return false;
   return true; 
  }
//+------------------------------------------------------------------+

Si la propiedad es "código de flecha" o "desplazamiento vertical de la flecha", o "estilo de la línea", o "anchura de la línea", o se trata del búfer de color o de un búfer de cálculo, y además no se trata de la propiedad "tipo del búfer", ni de la propiedad "índice del siguiente búfer dibujado", esta propiedad no tendrá soporte y se retornará false. En los demás casos, se retornará true.

Los demás métodos son idénticos a los de la clase del objeto de búfer "Segmentos".


Vamos a crear un nuevo objeto heredero del objeto de búfer abstracto CBufferBars, con el estilo de dibujado "Representación en forma de barras"
en el archivo \MQL5\Include\DoEasy\Objects\Indicators\BufferBars.mqh.

En la lista de inicialización del constructor de la clase, transmitimos al constructor del objeto de búfer básico el estado "Representación en forma de barras",
el nombre de la serie gráfica "Bar Open;Bar High;Bar Low;Bar Close", los cuatro búferes para la construcción y un grosor de las barras por defecto igual a 2.

CBufferBars(const uint index_plot,const uint index_base_array) :
   CBuffer(BUFFER_STATUS_BARS,BUFFER_TYPE_DATA,index_plot,index_base_array,4,2,"Bar Open;Bar High;Bar Low;Bar Close") {}

Método que retorna la bandera de soporte por parte del búfer de propiedades de tipo entero:

//+------------------------------------------------------------------+
//| Return 'true' if a buffer supports a passed                      |
//| integer property, otherwise return 'false'                       |
//+------------------------------------------------------------------+ 
bool CBufferBars::SupportProperty(ENUM_BUFFER_PROP_INTEGER property)
  {
   if((property==BUFFER_PROP_ARROW_CODE || property==BUFFER_PROP_ARROW_SHIFT) || property==BUFFER_PROP_LINE_STYLE ||
      (this.TypeBuffer()==BUFFER_TYPE_CALCULATE && property!=BUFFER_PROP_TYPE && property!=BUFFER_PROP_INDEX_NEXT)
     ) return false;
   return true; 
  }
//+------------------------------------------------------------------+

Si la propiedad es "código de flecha" o "desplazamiento vertical de la flecha", o "estilo de la línea", o se trata de un búfer de cálculo, y además no se trata de la propiedad "tipo del búfer", ni de la propiedad "índice del siguiente búfer de dibujado, esta propiedad no tendrá soporte y se retornará false. En los demás casos, se retornará true.

Los demás métodos son idénticos a los de la clase del objeto de búfer "Segmentos".


Vamos a crear un nuevo objeto heredero del objeto de búfer abstracto CBufferCandles, con el estilo de dibujado "Representación en forma de velas"
en el archivo \MQL5\Include\DoEasy\Objects\Indicators\BufferCandles.mqh.

En la lista de inicialización del constructor de la clase, transmitimos al constructor del objeto de búfer básico el estado "Representación en forma de velas",
el nombre de la serie gráfica "Candle Open;Candle High;Candle Low;Candle Close", los cuatro búferes para la construcción y un grosor de las velas por defecto igual a 1 (el parámetro no influye en la anchura de dibujado real: la anchura de las velas siempre es igual a la anchura de las velas del gráfico, dependiendo de su escala horizontal).

CBufferCandles(const uint index_plot,const uint index_base_array) : 
   CBuffer(BUFFER_STATUS_CANDLES,BUFFER_TYPE_DATA,index_plot,index_base_array,4,1,"Candle Open;Candle High;Candle Low;Candle Close") {}

Método que retorna la bandera de soporte por parte del búfer de propiedades de tipo entero:

//+------------------------------------------------------------------+
//| Return 'true' if a buffer supports a passed                      |
//| integer property, otherwise return 'false'                       |
//+------------------------------------------------------------------+ 
bool CBufferCandles::SupportProperty(ENUM_BUFFER_PROP_INTEGER property)
  {
   if((property==BUFFER_PROP_ARROW_CODE || property==BUFFER_PROP_ARROW_SHIFT) ||  
       property==BUFFER_PROP_LINE_STYLE || property==BUFFER_PROP_LINE_WIDTH   ||
      (this.TypeBuffer()==BUFFER_TYPE_CALCULATE && property!=BUFFER_PROP_TYPE && property!=BUFFER_PROP_INDEX_NEXT)
     ) return false;
   return true; 
  }
//+------------------------------------------------------------------+

Si la propiedad es "código de flecha" o "desplazamiento vertical de la flecha", o "estilo de la línea", o "anchura de la línea", o se trata de un búfer de cálculo, y además no se trata de la propiedad "tipo del búfer", ni de la propiedad "índice del siguiente búfer de dibujado, esta propiedad no tendrá soporte y se retornará false. En los demás casos, se retornará true.

Los demás métodos son idénticos a los de la clase del objeto de búfer "Segmentos".

Ya hemos creado todas las clases de los objetos de búfer de los herederos del objeto de búfer abstracto.
Podrá ver el listado completo de las clases en los archivos adjuntos al artículo.

Para poner a prueba las clases creadas, no vamos a inventarnos ningún control con botones para mostrar las líneas de los búferes en el gráfico; en lugar de ello, estableceremos las construcciones gráficas necesarias en los ajustes del indicador, donde se podrá seleccionar qué búferes representar, y también iniciar el indicador.

Simulación

Para poder seleccionar las construcciones gráficas necesarias, añadiremos al archivo con las enumeraciones de los parámetros de entrada \MQL5\Include\DoEasy\InpDatas.mqh nuevas enumeraciones para los lenguajes de compilación en inglés y ruso:

//+------------------------------------------------------------------+
//|                                                     InpDatas.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"
//+------------------------------------------------------------------+
//| Macrosustituciones                                               |
//+------------------------------------------------------------------+
#define COMPILE_EN // Comment out the string for compilation in Russian 
//+------------------------------------------------------------------+
//| Input enumerations                                               |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| English language inputs                                          |
//+------------------------------------------------------------------+
#ifdef COMPILE_EN
//+------------------------------------------------------------------+
//| Modes of working with symbols                                    |
//+------------------------------------------------------------------+
enum ENUM_SYMBOLS_MODE
  {
   SYMBOLS_MODE_CURRENT,                              // Work only with the current Symbol
   SYMBOLS_MODE_DEFINES,                              // Work with a given list of Symbols
   SYMBOLS_MODE_MARKET_WATCH,                         // Working with Symbols from the "Market Watch" window
   SYMBOLS_MODE_ALL                                   // Work with a complete list of Symbols
  };
//+------------------------------------------------------------------+
//| Mode of working with timeframes                                  |
//+------------------------------------------------------------------+
enum ENUM_TIMEFRAMES_MODE
  {
   TIMEFRAMES_MODE_CURRENT,                           // Work only with the current timeframe
   TIMEFRAMES_MODE_LIST,                              // Work with a given list of timeframes
   TIMEFRAMES_MODE_ALL                                // Work with a complete list of timeframes
  };
//+------------------------------------------------------------------+
//| "Yes"/"No"                                                       |
//+------------------------------------------------------------------+
enum ENUM_INPUT_YES_NO
  {
   INPUT_NO  = 0,                                     // No
   INPUT_YES = 1                                      // Yes
  };
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Russian language inputs                                          |
//+------------------------------------------------------------------+
#else  
//+------------------------------------------------------------------+
//| Modes of working with symbols                                    |
//+------------------------------------------------------------------+
enum ENUM_SYMBOLS_MODE
  {
   SYMBOLS_MODE_CURRENT,                              // Work with the current symbol only
   SYMBOLS_MODE_DEFINES,                              // Work with the specified symbol list
   SYMBOLS_MODE_MARKET_WATCH,                         // Work with the Market Watch window symbols
   SYMBOLS_MODE_ALL                                   // Work with the full symbol list
  };
//+------------------------------------------------------------------+
//| Mode of working with timeframes                                  |
//+------------------------------------------------------------------+
enum ENUM_TIMEFRAMES_MODE
  {
   TIMEFRAMES_MODE_CURRENT,                           // Work with the current timeframe only
   TIMEFRAMES_MODE_LIST,                              // Work with the specified timeframe list
   TIMEFRAMES_MODE_ALL                                // Work with the full timeframe list
  };
//+------------------------------------------------------------------+
//| "Yes"/"No"                                                       |
//+------------------------------------------------------------------+
enum ENUM_INPUT_YES_NO
  {
   INPUT_NO  = 0,                                     // No
   INPUT_YES = 1                                      // Yes
  };
//+------------------------------------------------------------------+
#endif 
//+------------------------------------------------------------------+

Esto nos dará la posibilidad de elegir en los ajustes:


Para la simulación, vamos a tomar el indicador del artículo anterior y guardarlo en la nueva carpeta
\MQL5\Indicators\TestDoEasy\Part43\ con el nuevo nombre TestDoEasyPart43.mq5.

Añadimos al archivo del indicador los archivos de todos los búferes creados, establecemos un número de búferes utilizados igual a 27, y un número de búferes dibujados igual a 9:

//+------------------------------------------------------------------+
//|                                             TestDoEasyPart43.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>
#include <DoEasy\Objects\Indicators\BufferArrow.mqh>        // 1 construction buffer + 1 color buffer
#include <DoEasy\Objects\Indicators\BufferLine.mqh>         // 1 construction buffer + 1 color buffer
#include <DoEasy\Objects\Indicators\BufferSection.mqh>      // 1 construction buffer + 1 color buffer
#include <DoEasy\Objects\Indicators\BufferHistogram.mqh>    // 1 construction buffer + 1 color buffer
#include <DoEasy\Objects\Indicators\BufferHistogram2.mqh>   // 2 construction buffers + 1 color buffer
#include <DoEasy\Objects\Indicators\BufferZigZag.mqh>       // 2 construction buffers + 1 color buffer
#include <DoEasy\Objects\Indicators\BufferFilling.mqh>      // 2 construction buffers + 1 color buffer
#include <DoEasy\Objects\Indicators\BufferBars.mqh>         // 4 construction buffer + 1 color buffer
#include <DoEasy\Objects\Indicators\BufferCandles.mqh>      // 4 construction buffer + 1 color buffer
//--- In total: 18 construction buffers + 9 color buffers:       27 indicator buffers, of which 9 are drawn ones
//--- properties
#property indicator_chart_window
#property indicator_buffers 27
#property indicator_plots   9 

En el bloque de parámetros de entrada, comentamos los modificadores sinput para seleccionar el modo de trabajo con los símbolos (por defecto, será el actual). La lista de símbolos utilizados también estará oculta en los ajustes, y el modo de selección del marco temporal de trabajo será por defecto el actual. Ocultamos de los ajustes la lista de marcos temporales utilizados y añadimos la posibilidad de seleccionar en los ajustes los búferes que debemos representar en el gráfico:

//--- classes

//--- enums

//--- defines

//--- structures

//--- input variables
/*sinput*/ ENUM_SYMBOLS_MODE  InpModeUsedSymbols=  SYMBOLS_MODE_CURRENT;            // Mode of used symbols list
/*sinput*/   string               InpUsedSymbols    =  "EURUSD,AUDUSD,EURAUD,EURGBP,EURCAD,EURJPY,EURUSD,GBPUSD,NZDUSD,USDCAD,USDJPY";  // List of used symbols (comma - separator)
/*sinput*/   ENUM_TIMEFRAMES_MODE InpModeUsedTFs    =  TIMEFRAMES_MODE_CURRENT;            // Mode of used timeframes list
/*sinput*/   string               InpUsedTFs        =  "M1,M5,M15,M30,H1,H4,D1,W1,MN1"; // List of used timeframes (comma - separator)
sinput   ENUM_INPUT_YES_NO    InpDrawArrow      =  INPUT_YES;  // Draw Arrow
sinput   ENUM_INPUT_YES_NO    InpDrawLine       =  INPUT_NO;   // Draw Line
sinput   ENUM_INPUT_YES_NO    InpDrawSection    =  INPUT_NO;   // Draw Section
sinput   ENUM_INPUT_YES_NO    InpDrawHistogram  =  INPUT_NO;   // Draw Histogram
sinput   ENUM_INPUT_YES_NO    InpDrawHistogram2 =  INPUT_NO;   // Draw Histogram2
sinput   ENUM_INPUT_YES_NO    InpDrawZigZag     =  INPUT_NO;   // Draw ZigZag
sinput   ENUM_INPUT_YES_NO    InpDrawFilling    =  INPUT_NO;   // Draw Filling
sinput   ENUM_INPUT_YES_NO    InpDrawBars       =  INPUT_NO;   // Draw Bars
sinput   ENUM_INPUT_YES_NO    InpDrawCandles    =  INPUT_YES;  // Draw Candles
 
sinput   bool                 InpUseSounds      =  true; // Use sounds

En lugar de la declaración de las matrices double de los búferes de indicador, añadimos la declaración de una matriz dinámica de punteros a las instancias de los objetos CObject. En esta lista, guardaremos los objetos de los búferes de indicador creados para la simulación:

//--- indicator buffers
CArrayObj      list_buffers;                    // Temporary list for storing buffer objects
//--- global variables
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() del indicador, creamos todos los objetos de búfer, los añadimos a la lista, establecemos los colores que no son por defecto (tres) e imprimimos en el diario los datos de cada objeto de búfer creado:

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Initialize DoEasy library
   OnInitDoEasy();

//--- Set indicator global variables
   prefix=engine.Name()+"_";
   //--- Get the index of the maximum used timeframe in the array,
   //--- 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 index=ArrayMaximum(ArrayUsedTimeframes);
   int num_bars=NumberBarsInTimeframe(ArrayUsedTimeframes[index]);
   min_bars=(index>WRONG_VALUE ? (num_bars>2 ? num_bars : 2) : 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 buffer objects
   CBuffer *buffer0=new CBufferArrow(0,0);
   CBuffer *buffer1=new CBufferLine(1,buffer0.IndexNextBuffer());
   CBuffer *buffer2=new CBufferSection(2,buffer1.IndexNextBuffer());
   CBuffer *buffer3=new CBufferHistogram(3,buffer2.IndexNextBuffer());
   CBuffer *buffer4=new CBufferHistogram2(4,buffer3.IndexNextBuffer());
   CBuffer *buffer5=new CBufferZigZag(5,buffer4.IndexNextBuffer());
   CBuffer *buffer6=new CBufferFilling(6,buffer5.IndexNextBuffer());
   CBuffer *buffer7=new CBufferBars(7,buffer6.IndexNextBuffer());
   CBuffer *buffer8=new CBufferCandles(8,buffer7.IndexNextBuffer());
//--- Add buffers to the list of indicator buffers
   list_buffers.Add(buffer0);
   list_buffers.Add(buffer1);
   list_buffers.Add(buffer2);
   list_buffers.Add(buffer3);
   list_buffers.Add(buffer4);
   list_buffers.Add(buffer5);
   list_buffers.Add(buffer6);
   list_buffers.Add(buffer7);
   list_buffers.Add(buffer8);
//--- Create color array
   color array_colors[]={clrDodgerBlue,clrRed,clrGray};
//--- Set non-default buffer color values
   for(int i=0;i<list_buffers.Total();i++)
     {
      CBuffer *buff=list_buffers.At(i);
      buff.SetColors(array_colors);
      //--- Print data on the next buffer
      buff.Print();
     }
//--- Set the label size for the arrow buffer and the line width for ZigZag
   buffer0.SetWidth(2);
   buffer5.SetWidth(2);
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+

En el manejador OnCalculate()
Al inicializar o modificar los datos históricos, inicializamos todos los búferes creados y mostramos sus denominaciones breves
.
Al abrir una nueva barra, o en el tick actual, registramos en los búferes los valores de los precios:
para un búfer,
el precio Close,
para dos búferes,
en el primero, el precio Open, en el segundo, el precio Close,
para cuatro
búferes, en cada búfer registraremos los precios de OHLC, respectivamente:

//+------------------------------------------------------------------+
//| 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
   CopyData(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.0)
      return 0;
   
//--- If working in the tester
   if(MQLInfoInteger(MQL_TESTER)) 
     {
      engine.OnTimer(rates_data);   // Working in the library timer
      EventsHandling();             // Working with library events
     }
//+------------------------------------------------------------------+
//| OnCalculate code block for working with the indicator:           |
//+------------------------------------------------------------------+
//--- Set OnCalculate arrays as timeseries
   ArraySetAsSeries(open,true);
   ArraySetAsSeries(high,true);
   ArraySetAsSeries(low,true);
   ArraySetAsSeries(close,true);
   ArraySetAsSeries(time,true);
   ArraySetAsSeries(tick_volume,true);
   ArraySetAsSeries(volume,true);
   ArraySetAsSeries(spread,true);

//--- 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)
     {
      //--- In a loop by the number of buffers in the list
      for(int i=0;i<list_buffers.Total();i++)
        {
         //--- get the next buffer and display the type of its graphical construction to the journal
         //--- together with the double array assigned to the buffer (if all is correct, the size is equal to rates_total)
         CBuffer *buff=list_buffers.At(i);
         buff.PrintShort();
         buff.InitializeBuffers();
        }
      limit=rates_total-1;
     }
//--- Prepare data

//--- Calculate the indicator
   for(int i=limit; i>WRONG_VALUE && !IsStopped(); i--)
     {
      //--- In a loop by the number of buffers in the list
      for(int j=0;j<list_buffers.Total();j++)
        {
         //--- get the next buffer and
         CBuffer *buff=list_buffers.At(j);
         //--- clear its current data
         buff.ClearData(0);
         //--- If drawing is not used, move on to the next one
         if(!IsUse(buff.Status()))
            continue;
         //--- Depending on the number of buffers, fill them with price data
         //--- one buffer
         if(buff.BuffersTotal()==1)
            buff.SetBufferValue(0,i,close[i]);
         //--- two buffers - the first one is to store the bar open price, while the second one is to store the bar close price
         else if(buff.BuffersTotal()==2)
           {
            buff.SetBufferValue(0,i,open[i]);
            buff.SetBufferValue(1,i,close[i]);
           }
         //--- four buffers - each buffer is to store OHLC bar prices
         else if(buff.BuffersTotal()==4)
           {
            buff.SetBufferValue(0,i,open[i]);
            buff.SetBufferValue(1,i,high[i]);
            buff.SetBufferValue(2,i,low[i]);
            buff.SetBufferValue(3,i,close[i]);
           }
         //--- Set the buffer color depending on the candle direction
         if(open[i]<close[i])
            buff.SetBufferColorIndex(i,0);
         else if(open[i]>close[i])
            buff.SetBufferColorIndex(i,1);
         else
            buff.SetBufferColorIndex(i,2);
        }
     }
//--- return value of prev_calculated for the next call
   return(rates_total);
  }
//+------------------------------------------------------------------+

Para definir la resolución en el dibujado de las líneas del indicador, utilizaremos la función:

//+------------------------------------------------------------------+
//|Return the flag indicating the appropriate drawing style is enabled
//+------------------------------------------------------------------+
bool IsUse(const ENUM_BUFFER_STATUS status)
  {
   switch(status)
     {
      case BUFFER_STATUS_FILLING    : return (bool)InpDrawFilling;
      case BUFFER_STATUS_LINE       : return (bool)InpDrawLine;
      case BUFFER_STATUS_HISTOGRAM  : return (bool)InpDrawHistogram;
      case BUFFER_STATUS_ARROW      : return (bool)InpDrawArrow;
      case BUFFER_STATUS_SECTION    : return (bool)InpDrawSection;
      case BUFFER_STATUS_HISTOGRAM2 : return (bool)InpDrawHistogram2;
      case BUFFER_STATUS_ZIGZAG     : return (bool)InpDrawZigZag;
      case BUFFER_STATUS_BARS       : return (bool)InpDrawBars;
      case BUFFER_STATUS_CANDLES    : return (bool)InpDrawCandles;
      default: return false;
     }
  }
//+------------------------------------------------------------------+

Transmitimos a la función el estado del búfer y retornamos el valor del parámetro de entrada que se corresponde con el estado transmitido a la función.

Podrá ver el código completo del indicador en los archivos adjuntos al artículo.

Compilamos el indicador y lo iniciamos en el gráfico.
Durante la inicialización del diario, se mostrarán todas las propiedades de los nueve búferes de indicador creados:

Счёт 8550475: Artyom Trishkin (MetaQuotes Software Corp.) 10425.23 USD, 1:100, Hedge, Демонстрационный счёт MetaTrader 5
--- Initializing "DoEasy" library ---
Working with the current symbol only: "EURUSD"
Working with the current timeframe only: H1
EURUSD symbol timeseries: 
- Timeseries "EURUSD" H1: Requested: 1000, Actual: 0, Created: 0, On the server: 0
Library initialization time: 00:00:00.156

============= Parameter list start: Colored data buffer[0] "Drawing with arrows" ==================
Plotted buffer serial number: 0
Buffer status: Indicator buffer with graphical construction type "Drawing with arrows"
Buffer type: Colored data buffer
Buffer data period (timeframe): Current chart period (H1)
Active: Yes
Graphical construction type: Drawing with arrows
Arrow code: 159
The vertical shift of the arrows: 0
Arrow size: 1
The number of initial bars that are not drawn and values in DataWindow: 0
Display construction values in DataWindow: Yes
Indicator graphical construction shift by time axis in bars: 0
Number of colors: 3
Drawing color: clrDodgerBlue,clrRed,clrGray
Number of data buffers: 1
Base data buffer index: 0
Color buffer index: 1
Index of the array to be assigned as the next indicator buffer: 2
------
Empty value for plotting where nothing will be drawn: EMPTY_VALUE
------
Buffer symbol: EURUSD
Name of the graphical indicator series displayed in DataWindow: Arrows
================== Parameter list end: Colored data buffer[0] "Drawing with arrows" ==================

============= Parameter list start: Colored data buffer[1] "Line" ==================
Plotted buffer serial number: 1
Buffer status: Indicator buffer with graphical construction type "Line"
Buffer type: Colored data buffer
Buffer data period (timeframe): Current chart period (H1)
Active: Yes
Graphical construction type: Line
Line style: Solid line
Line width: 1
The number of initial bars that are not drawn and values in DataWindow: 0
Display construction values in DataWindow: Yes
Indicator graphical construction shift by time axis in bars: 0
Number of colors: 3
Drawing color: clrDodgerBlue,clrRed,clrGray
Number of data buffers: 1
Base data buffer index: 2
Color buffer index: 3
Index of the array to be assigned as the next indicator buffer: 4
------
Empty value for plotting where nothing will be drawn: EMPTY_VALUE
------
Buffer symbol: EURUSD
Name of the graphical indicator series displayed in DataWindow: Line
================== Parameter list end: Colored data buffer[1] "Line" ==================

============= Parameter list start: Colored data buffer[2] "Section" ==================
Plotted buffer serial number: 2
Buffer status: Indicator buffer with graphical construction type "Section"
Buffer type: Colored data buffer
Buffer data period (timeframe): Current chart period (H1)
Active: Yes
Graphical construction type: Section
Line style: Solid line
Line width: 1
The number of initial bars that are not drawn and values in DataWindow: 0
Display construction values in DataWindow: Yes
Indicator graphical construction shift by time axis in bars: 0
Number of colors: 3
Drawing color: clrDodgerBlue,clrRed,clrGray
Number of data buffers: 1
Base data buffer index: 4
Color buffer index: 5
Index of the array to be assigned as the next indicator buffer: 6
------
Empty value for plotting where nothing will be drawn: EMPTY_VALUE
------
Buffer symbol: EURUSD
Name of the graphical indicator series displayed in DataWindow: Section
================== Parameter list end: Colored data buffer[2] "Section" ==================

============= Parameter list start: Colored data buffer[3] "Histogram from the zero line" ==================
Plotted buffer serial number: 3
Buffer status: Indicator buffer with graphical construction type "Histogram from the zero line"
Buffer type: Colored data buffer
Buffer data period (timeframe): Current chart period (H1)
Active: Yes
Graphical construction type: Histogram from the zero line
Line style: Solid line
Line width: 2
The number of initial bars that are not drawn and values in DataWindow: 0
Display construction values in DataWindow: Yes
Indicator graphical construction shift by time axis in bars: 0
Number of colors: 3
Drawing color: clrDodgerBlue,clrRed,clrGray
Number of data buffers: 1
Base data buffer index: 6
Color buffer index: 7
Index of the array to be assigned as the next indicator buffer: 8
------
Empty value for plotting where nothing will be drawn: EMPTY_VALUE
------
Buffer symbol: EURUSD
Name of the graphical indicator series displayed in DataWindow: Histogram
================== Parameter list end: Colored data buffer[3] "Histogram from the zero line" ==================

============= Parameter list start: Colored data buffer[4] "Histogram on two indicator buffers" ==================
Plotted buffer serial number: 4
Buffer status: Indicator buffer with graphical construction type "Histogram on two indicator buffers"
Buffer type: Colored data buffer
Buffer data period (timeframe): Current chart period (H1)
Active: Yes
Graphical construction type: Histogram on two indicator buffers
Line style: Solid line
Line width: 8
The number of initial bars that are not drawn and values in DataWindow: 0
Display construction values in DataWindow: Yes
Indicator graphical construction shift by time axis in bars: 0
Number of colors: 3
Drawing color: clrDodgerBlue,clrRed,clrGray
Number of data buffers: 2
Base data buffer index: 8
Color buffer index: 10
Index of the array to be assigned as the next indicator buffer: 11
------
Empty value for plotting where nothing will be drawn: EMPTY_VALUE
------
Buffer symbol: EURUSD
Name of the graphical indicator series displayed in DataWindow: Histogram2 0;Histogram2 1
================== Parameter list end: Colored data buffer[4] "Histogram on two indicator buffers" ==================

============= Parameter list start: Colored data buffer[5] "Zigzag" ==================
Plotted buffer serial number: 5
Buffer status: Indicator buffer with graphical construction type "Zigzag"
Buffer type: Colored data buffer
Buffer data period (timeframe): Current chart period (H1)
Active: Yes
Graphical construction type: Zigzag
Line style: Solid line
Line width: 1
The number of initial bars that are not drawn and values in DataWindow: 0
Display construction values in DataWindow: Yes
Indicator graphical construction shift by time axis in bars: 0
Number of colors: 3
Drawing color: clrDodgerBlue,clrRed,clrGray
Number of data buffers: 2
Base data buffer index: 11
Color buffer index: 13
Index of the array to be assigned as the next indicator buffer: 14
------
Empty value for plotting where nothing will be drawn: EMPTY_VALUE
------
Buffer symbol: EURUSD
Name of the graphical indicator series displayed in DataWindow: ZigZag 0;ZigZag 1
================== Parameter list end: Colored data buffer[5] "Zigzag" ==================

============= Parameter list start: Colored data buffer[6] "Color filling between two levels" ==================
Plotted buffer serial number: 6
Buffer status: Indicator buffer with graphical construction type "Color filling between two levels"
Buffer type: Colored data buffer
Buffer data period (timeframe): Current chart period (H1)
Active: Yes
Graphical construction type: Color filling between two levels
The number of initial bars that are not drawn and values in DataWindow: 0
Display construction values in DataWindow: Yes
Indicator graphical construction shift by time axis in bars: 0
Number of colors: 2
Drawing color: clrDodgerBlue,clrRed
Number of data buffers: 2
Base data buffer index: 14
Index of the array to be assigned as the next indicator buffer: 16
------
Empty value for plotting where nothing will be drawn: EMPTY_VALUE
------
Buffer symbol: EURUSD
Name of the graphical indicator series displayed in DataWindow: Filling 0;Filling 1
================== Parameter list end: Colored data buffer[6] "Color filling between two levels" ==================

============= Parameter list start: Colored data buffer[7] "Display as bars" ==================
Plotted buffer serial number: 7
Buffer status: Indicator buffer with graphical construction type "Display as bars"
Buffer type: Colored data buffer
Buffer data period (timeframe): Current chart period (H1)
Active: Yes
Graphical construction type: Display as bars
Line width: 2
The number of initial bars that are not drawn and values in DataWindow: 0
Display construction values in DataWindow: Yes
Indicator graphical construction shift by time axis in bars: 0
Number of colors: 3
Drawing color: clrDodgerBlue,clrRed,clrGray
Number of data buffers: 4
Base data buffer index: 16
Color buffer index: 20
Index of the array to be assigned as the next indicator buffer: 21
------
Empty value for plotting where nothing will be drawn: EMPTY_VALUE
------
Buffer symbol: EURUSD
Name of the graphical indicator series displayed in DataWindow: Bar Open;Bar High;Bar Low;Bar Close
================== Parameter list end: Colored data buffer[7] "Display as bars" ==================

============= Parameter list start: Colored data buffer[8] "Display as candles" ==================
Plotted buffer serial number: 8
Buffer status: Indicator buffer with graphical construction type "Display as candles"
Buffer type: Colored data buffer
Buffer data period (timeframe): Current chart period (H1)
Active: Yes
Graphical construction type: Display as candles
The number of initial bars that are not drawn and values in DataWindow: 0
Display construction values in DataWindow: Yes
Indicator graphical construction shift by time axis in bars: 0
Number of colors: 3
Drawing color: clrDodgerBlue,clrRed,clrGray
Number of data buffers: 4
Base data buffer index: 21
Color buffer index: 25
Index of the array to be assigned as the next indicator buffer: 26
------
Empty value for plotting where nothing will be drawn: EMPTY_VALUE
------
Buffer symbol: EURUSD
Name of the graphical indicator series displayed in DataWindow: Candle Open;Candle High;Candle Low;Candle Close
================== Parameter list end: Colored data buffer[8] "Display as candles" ==================


Después del primer inicio, al realizar la inicialización de la biblioteca y de todos los búferes de indicador, en el diario se mostrarán las entradas:

"EURUSD" H1 timeseries created successfully:
- Timeseries "EURUSD" H1: Requested: 1000, Actual: 1000, Created: 1000, On the server: 6230
Búfer(0): Dibujado con flechas de EURUSD H1
Buffer(1): EURUSD H1 line
Buffer(2): EURUSD H1 sections
Buffer(3): Histogram from the zero line EURUSD H1
Buffer(4): Histogram on two indicator buffers EURUSD H1
Buffer(5): EURUSD H1 zigzag
Buffer(6): Color filling between two levels EURUSD H1
Buffer(7): Display as EURUSD H1 bars
Buffer(8): Display as EURUSD H1 candles

Probamos a cambiar en los ajustes la resolución de muestra de las líneas del indicador:


¿Qué es lo próximo?

En el siguiente artículo, comenzaremos a crear la clase de colección de los búferes de indicador. Precisamente esta clase ofrecerá una cierta flexibilidad y algo de comodidad al crear los búferes de indicador y trabajar con ellos en cualquier símbolo y periodo de los gráficos al crear nuestros propios programas-indicadores con la ayuda de esta biblioteca.

Más abajo se adjuntan todos los archivos de la versión actual de la biblioteca y los archivos del asesor 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.
Querríamos recordar al lector que en este artículo hemos creado un indicador de prueba en MQL5 para MetaTrader 5.
Los archivos adjuntos han sido diseñados solo para MetaTrader 5, y en MetaTrader 4, la biblioteca en su versión actual no ha sido puesta a prueba.
Después de crear todas las colecciones de los búferes de los indicadores y poner estas a prueba, trataremos también de implementar algunas cosas 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



Traducción del ruso hecha por MetaQuotes Software Corp.
Artículo original: https://www.mql5.com/ru/articles/7868

Archivos adjuntos |
MQL5.zip (3722.34 KB)
Enrique Enguix Vino
Enrique Enguix Vino | 17 sep. 2020 en 10:17
Gracias por estos artículos y por compartir tus conocimientos! Son verdaderamente interesantes! 
Artyom Trishkin
Artyom Trishkin | 17 sep. 2020 en 11:12
Enrique Enguix Vino:
Gracias por estos artículos y por compartir tus conocimientos! Son verdaderamente interesantes! 
You are welcome :)
Monitoreo multidivisas de las señales comerciales (Parte 5): Señales compuestas Monitoreo multidivisas de las señales comerciales (Parte 5): Señales compuestas

En la parte 5 del desarrollo de la aplicación para monitorear las señales comerciales, introduciremos el concepto de la señal compuesta en nuestro sistema e implementaremos la funcionalidad necesaria para ello. Antes usábamos las señales simples en nuestra aplicación (RSI, WPR, CCI), también podíamos usar nuestro propio indicador personalizado.

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 42): La clase del objeto de búfer de indicador abstracto

En este artículo, comenzamos a construir las clases de los búferes de indicador para la biblioteca DoEasy. En esta parte, crearemos la clase básica de búfer abstracto, que será la principal para crear los diferentes tipos de clases de los búferes de indicador.

Cómo escribir un cliente nativo de Twitter para MetaTrader 4 y MetaTrader 5 sin usar DLL Cómo escribir un cliente nativo de Twitter para MetaTrader 4 y MetaTrader 5 sin usar DLL

¿Quiere usted recibir tweets o publicar sus señales comerciales en Twitter? Ya no tendrá que buscar soluciones para ello: en esta serie de artículos, analizaremos cómo trabajar con Twitter sin usar DLL. Juntos, implementaremos una Tweeter API con ayuda de MQL. En el primer artículo, hablaremos de las posibilidades de autenticación y autorización a través de Twitter API.

Cliente Nativo de Twitter: Parte 2 Cliente Nativo de Twitter: Parte 2

Un cliente de Twitter implementado como clase MQL para permitirle a usted enviar tweets con fotos. Todo lo que necesita es agregar un solo archivo de inclusión autónomo y listo para tuitear todos sus maravillosos gráficos y señales.