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

24 septiembre 2020, 15:06
Artyom Trishkin
0
314

Contenido


Concepto

A partir del artículo 39, hemos comenzado a analizar el uso de la biblioteca para crear indicadores personalizados. En estos momentos, ya tenemos preparados los objetos de series temporales y su colección, así como los objetos de búferes de indicador: el búfer básico abstracto y los búferes de indicador basados en él.
En el presente artículo, comenzaremos a crear una colección de búferes de indicador para crear rápidamente cualquier número de búferes (hasta 512) en un solo programa, y para obtener estos con facilidad, además de acceder al trabajo con sus datos.

La conexión <"colección de series temporales> — <colección de búferes"> nos permitirá crear cualquier indicador de símbolo y periodo múltiples. Nos ocuparemos de ello en el siguiente artículo. Hoy, crearemos y pondremos a prueba una colección de búferes de indicador que nos permitirá crear en un indicador cualquier búfer con uno de los nueve estilos de dibujado y en cualquier cantidad. En estos momentos, el número máximo de búferes a dibujar en el indicador es de 512. Pero esta cifra debería resultar más que suficiente a la hora de crear cualquier indicador complejo con un gran número de construcciones gráficas. Y con la ayuda de la funcionalidad creada, la creación y el servicio de esta cantidad de construcciones gráficas se simplificará hasta el nivel de una simple invocación a los búferes creados según su estilo de dibujado y su orden de creación, o bien según el índice del búfer en la colección.


Preparando los datos y mejorando los objetos de búferes

En el artículo anterior, creamos los objetos "búferes de indicador", herederos del búfer abstracto básico. Vamos a complementarlos con métodos adicionales para acceder al registro y la lectura de datos en/desde las matrices double asignadas como búferes de indicador.
Aunque podríamos arreglárnoslas con los métodos del búfer abstracto básico que ya hemos escrito, la creación de métodos propios en los objetos herederos inherentes al estilo de dibujado será necesaria para recurrir con mayor comodidad a las matrices double al recurrir a un búfer según su estado (estilo de dibujado), ofreciéndonos al mismo tiempo flexibilidad al crear nuestros indicadores.

Vamos a añadir los nuevos mensajes a la biblioteca. En el archivo \MQL5\Include\DoEasy\Datas.mqh, escribimos los índices de los nuevos mensajes:

   MSG_LIB_SYS_FAILED_COLORS_ARRAY_RESIZE,            // Failed to change the color array size
   MSG_LIB_SYS_FAILED_ADD_BUFFER,                     // Failed to add buffer object to the list
   MSG_LIB_SYS_FAILED_CREATE_BUFFER_OBJ,              // Failed to create \"Indicator buffer\" object
   
   MSG_LIB_TEXT_YES,                                  // Yes

...

   MSG_LIB_TEXT_BUFFER_TEXT_INVALID_PROPERTY_BUFF,    // Invalid number of indicator buffers (#property indicator_buffers)
   MSG_LIB_TEXT_BUFFER_TEXT_MAX_BUFFERS_REACHED,      // Reached maximum possible number of indicator buffers

   MSG_LIB_TEXT_BUFFER_TEXT_STATUS_NONE,              // No drawing

y los textos de los mensajes que se corresponden con los índices nuevamente añadidos:

   {"Не удалось изменить размер массива цветов","Failed to resize color array"},
   {"Не удалось добавить объект-буфер в список","Failed to add buffer object to list"},
   {"Не удалось создать объект \"Индикаторный буфер\"","Failed to create object \"Indicator buffer\""},

   {"Да","Yes"},

...

   {"Неправильно указано количество буферов индикатора (#property indicator_buffers)","Number of indicator buffers incorrect (#property indicator_buffers)"},
   {"Достигнуто максимально возможное количество индикаторных буферов","Maximum number of indicator buffers reached"},
   
   {"Нет отрисовки","No drawing"},

En los indicadores se pueden utilizar un máximo de 512 búferes.
Añadimos al archivo \MQL5\Include\DoEasy\Defines.mqh la macrosustitución que indica este tamaño:

//+------------------------------------------------------------------+
//| 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
#define IND_BUFFERS_MAX                (512)                      // Maximum possible number of indicator buffers
//--- Standard sounds

Claro que podemos utilizar el valor "512", pero la macrosustitución resulta cómoda por un buen motivo: si los desarrolladores aumentan alguna vez este valor, para introducir cambios en el código, no necesitaremos buscar y corregir este valor por uno nuevo en todos los archivos donde se recurre a esta magnitud, solo tendremos que cambiar el valor de la macrosustitución.

Necesitaremos realizar la búsqueda y la selección de los objetos de búfer según las propiedades que hemos definido anteriormente como innecesarias para la búsqueda y la clasificación.

Todas las propiedades utilizadas en la clasificación siempre se ubican (en nuestro caso) al final de la lista de enumeración de las propiedades. Asimismo, hemos definido una macrosustitución que indica el número de propiedades usadas en la clasificación. Para establecer una propiedad como clasificable, deberemos acercarla desde el final de la lista de enumeración a las propiedades permitidas en estos momentos para la clasifición, y también indicar el nuevo número de propiedades no utilizadas en la clasificación de las propiedades.
Y, naturalmente, deberemos completar la enumeración de los posibles criterios de clasificación con nuevas propiedades, concretamente, con aquellas que hemos permitido utilizar para la clasificación. La ubicación de las propiedades nuevamente añadidas en la lista de enumeración de criterios de clasificación deberá coincidir con la ubicación de estas propiedades en la lista de enumeración de las propiedades en la que hemos permitido utilizar estas propiedades en la clasificación.

Suena confuso, pero en la práctica todo es sencillo. Abrimos el archivo \MQL5\Include\DoEasy\Defines.mqh e introducimos los cambios necesarios.

Antes, las propiedades de tipo entero del objeto de búfer se ubicaban en este orden:

//+------------------------------------------------------------------+
//| Buffer integer properties                                        |
//+------------------------------------------------------------------+
enum ENUM_BUFFER_PROP_INTEGER
  {
   BUFFER_PROP_INDEX_PLOT = 0,                              // Plotted buffer serial number
   BUFFER_PROP_STATUS,                                      // Buffer status (by drawing style) from the ENUM_BUFFER_STATUS enumeration
   BUFFER_PROP_TYPE,                                        // Buffer type (from the ENUM_BUFFER_TYPE enumeration)
   BUFFER_PROP_TIMEFRAME,                                   // Buffer period data (timeframe)
   BUFFER_PROP_ACTIVE,                                      // Buffer usage flag
   BUFFER_PROP_DRAW_TYPE,                                   // Graphical construction type (from the ENUM_DRAW_TYPE enumeration)
   BUFFER_PROP_ARROW_CODE,                                  // Arrow code for DRAW_ARROW style
   BUFFER_PROP_ARROW_SHIFT,                                 // The vertical shift of the arrows for DRAW_ARROW style
   BUFFER_PROP_LINE_STYLE,                                  // Line style
   BUFFER_PROP_LINE_WIDTH,                                  // Line width
   BUFFER_PROP_DRAW_BEGIN,                                  // The number of initial bars that are not drawn and values in DataWindow
   BUFFER_PROP_SHOW_DATA,                                   // Flag of displaying construction values in DataWindow
   BUFFER_PROP_SHIFT,                                       // Indicator graphical construction shift by time axis in bars
   BUFFER_PROP_COLOR_INDEXES,                               // Number of colors
   BUFFER_PROP_COLOR,                                       // Drawing color
   BUFFER_PROP_NUM_DATAS,                                   // Number of data buffers
   BUFFER_PROP_INDEX_BASE,                                  // Basic data buffer index
   BUFFER_PROP_INDEX_COLOR,                                 // Color buffer index
   BUFFER_PROP_INDEX_NEXT,                                  // Index of the free array to be assigned as the next indicator buffer
  }; 
#define BUFFER_PROP_INTEGER_TOTAL (19)                      // Total number of integer bar properties
#define BUFFER_PROP_INTEGER_SKIP  (6)                       // Number of buffer properties not used in sorting
//+------------------------------------------------------------------+

Aquí, debemos hacer las dos propiedades posibles para la clasificación. Para ello, las desplazamos más arriba y cambiamos el número de propiedades no utilizadas en la clasificación de 6 a 2:

//+------------------------------------------------------------------+
//| Buffer integer properties                                        |
//+------------------------------------------------------------------+
enum ENUM_BUFFER_PROP_INTEGER
  {
   BUFFER_PROP_INDEX_PLOT = 0,                              // Plotted buffer serial number
   BUFFER_PROP_STATUS,                                      // Buffer status (by drawing style) from the ENUM_BUFFER_STATUS enumeration
   BUFFER_PROP_TYPE,                                        // Buffer type (from the ENUM_BUFFER_TYPE enumeration)
   BUFFER_PROP_TIMEFRAME,                                   // Buffer period data (timeframe)
   BUFFER_PROP_ACTIVE,                                      // Buffer usage flag
   BUFFER_PROP_DRAW_TYPE,                                   // Graphical construction type (from the ENUM_DRAW_TYPE enumeration)
   BUFFER_PROP_ARROW_CODE,                                  // Arrow code for DRAW_ARROW style
   BUFFER_PROP_ARROW_SHIFT,                                 // The vertical shift of the arrows for DRAW_ARROW style
   BUFFER_PROP_LINE_STYLE,                                  // Line style
   BUFFER_PROP_LINE_WIDTH,                                  // Line width
   BUFFER_PROP_DRAW_BEGIN,                                  // The number of initial bars that are not drawn and values in DataWindow
   BUFFER_PROP_SHOW_DATA,                                   // Flag of displaying construction values in DataWindow
   BUFFER_PROP_SHIFT,                                       // Indicator graphical construction shift by time axis in bars
   BUFFER_PROP_COLOR_INDEXES,                               // Number of colors
   BUFFER_PROP_COLOR,                                       // Drawing color
   BUFFER_PROP_INDEX_BASE,                                  // Basic data buffer index
   BUFFER_PROP_INDEX_NEXT,                                  // Index of the free array to be assigned as the next indicator buffer
   BUFFER_PROP_NUM_DATAS,                                   // Number of data buffers
   BUFFER_PROP_INDEX_COLOR,                                 // Color buffer index
  }; 
#define BUFFER_PROP_INTEGER_TOTAL (19)                      // Total number of integer bar properties
#define BUFFER_PROP_INTEGER_SKIP  (2)                       // Number of buffer properties not used in sorting
//+------------------------------------------------------------------+

Aquí, hemos indicado que solo las dos propiedades que se encuentran al final de la lista no participarán en la clasificación.
Añadimos estas nuevas propiedades a los criterios de clasificación:

//+------------------------------------------------------------------+
//| Possible buffer sorting criteria                                 |
//+------------------------------------------------------------------+
#define FIRST_BUFFER_DBL_PROP          (BUFFER_PROP_INTEGER_TOTAL-BUFFER_PROP_INTEGER_SKIP)
#define FIRST_BUFFER_STR_PROP          (BUFFER_PROP_INTEGER_TOTAL-BUFFER_PROP_INTEGER_SKIP+BUFFER_PROP_DOUBLE_TOTAL-BUFFER_PROP_DOUBLE_SKIP)
enum ENUM_SORT_BUFFER_MODE
  {
//--- Sort by integer properties
   SORT_BY_BUFFER_INDEX_PLOT = 0,                           // Sort by the plotted buffer serial number
   SORT_BY_BUFFER_STATUS,                                   // Sort by buffer drawing style (status) from the ENUM_BUFFER_STATUS enumeration
   SORT_BY_BUFFER_TYPE,                                     // Sort by buffer type (from the ENUM_BUFFER_TYPE enumeration)
   SORT_BY_BUFFER_TIMEFRAME,                                // Sort by the buffer data period (timeframe)
   SORT_BY_BUFFER_ACTIVE,                                   // Sort by the buffer usage flag
   SORT_BY_BUFFER_DRAW_TYPE,                                // Sort by graphical construction type (from the ENUM_DRAW_TYPE enumeration)
   SORT_BY_BUFFER_ARROW_CODE,                               // Sort by the arrow code for DRAW_ARROW style
   SORT_BY_BUFFER_ARROW_SHIFT,                              // Sort by the vertical shift of the arrows for DRAW_ARROW style
   SORT_BY_BUFFER_LINE_STYLE,                               // Sort by the line style
   SORT_BY_BUFFER_LINE_WIDTH,                               // Sort by the line width
   SORT_BY_BUFFER_DRAW_BEGIN,                               // Sort by the number of initial bars that are not drawn and values in DataWindow
   SORT_BY_BUFFER_SHOW_DATA,                                // Sort by the flag of displaying construction values in DataWindow
   SORT_BY_BUFFER_SHIFT,                                    // Sort by the indicator graphical construction shift by time axis in bars
   SORT_BY_BUFFER_COLOR_INDEXES,                            // Sort by a number of attempts
   SORT_BY_BUFFER_COLOR,                                    // Sort by the drawing color
   SORT_BY_BUFFER_INDEX_BASE,                               // Sort by the basic data buffer index
   SORT_BY_BUFFER_INDEX_NEXT,                               // Sort by the index of the free array to be assigned as the next indicator buffer
//--- Sort by real properties
   SORT_BY_BUFFER_EMPTY_VALUE = FIRST_BUFFER_DBL_PROP,      // Sort by the empty value for plotting where nothing will be drawn
//--- Sort by string properties
   SORT_BY_BUFFER_SYMBOL = FIRST_BUFFER_STR_PROP,           // Sort by the buffer symbol
   SORT_BY_BUFFER_LABEL,                                    // Sort by the name of the graphical indicator series displayed in DataWindow
  };
//+------------------------------------------------------------------+

Como podemos ver, su ubicación en la lista de enumeración de criterios de clasificación coincide con la ubicación en la enumeración de las propiedades de tipo entero. Esta condición —la enumeración de criterios— es de obligado cumplimiento, ya que en ella, el orden secuencial de las propiedades de clasificación debe coincidir con el orden secuencial de las propiedades en la enumeración de las propiedades de tipo entero. Esto ya lo discutimos en la tercera parte de la descripción de la biblioteca.

Y ya que estamos hablando de la clasificación de los objetos de búfer según sus propiedades, significa que ha llegado el momento de preparar el instrumental para realizar la búsqueda de los objetos de búfer también según sus propiedades. También hemos implementado esta posibilidad para todos los objetos de la biblioteca guardados en las colecciones. Ahora, vamos a escribir también los métodos para buscar los objetos de búfer.

Abrimos el archivo \MQL5\Include\DoEasy\Services\Select.mqh y le añadimos el archivo de la clase de búfer:

//+------------------------------------------------------------------+
//|                                                       Select.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 <Arrays\ArrayObj.mqh>
#include "..\Objects\Orders\Order.mqh"
#include "..\Objects\Events\Event.mqh"
#include "..\Objects\Accounts\Account.mqh"
#include "..\Objects\Symbols\Symbol.mqh"
#include "..\Objects\PendRequest\PendRequest.mqh"
#include "..\Objects\Series\SeriesDE.mqh"
#include "..\Objects\Indicators\Buffer.mqh"
//+------------------------------------------------------------------+

Al final del cuerpo de clase, después del bloque con la declaración de los métodos de trabajo con las barras de las series temporales, añadimos el bloque con la declaración de los métodos de trabajo con los búferes de indicador:

//+------------------------------------------------------------------+
//| Methods of working with timeseries bars                          |
//+------------------------------------------------------------------+
   //--- Return the list of bars with one out of (1) integer, (2) real and (3) string properties meeting a specified criterion
   static CArrayObj *ByBarProperty(CArrayObj *list_source,ENUM_BAR_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode);
   static CArrayObj *ByBarProperty(CArrayObj *list_source,ENUM_BAR_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode);
   static CArrayObj *ByBarProperty(CArrayObj *list_source,ENUM_BAR_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode);
   //--- Return the bar index in the list with the maximum value of the order's (1) integer, (2) real and (3) string properties
   static int        FindBarMax(CArrayObj *list_source,ENUM_BAR_PROP_INTEGER property);
   static int        FindBarMax(CArrayObj *list_source,ENUM_BAR_PROP_DOUBLE property);
   static int        FindBarMax(CArrayObj *list_source,ENUM_BAR_PROP_STRING property);
   //--- Return the bar index in the list with the minimum value of the order's (1) integer, (2) real and (3) string properties
   static int        FindBarMin(CArrayObj *list_source,ENUM_BAR_PROP_INTEGER property);
   static int        FindBarMin(CArrayObj *list_source,ENUM_BAR_PROP_DOUBLE property);
   static int        FindBarMin(CArrayObj *list_source,ENUM_BAR_PROP_STRING property);
//+------------------------------------------------------------------+
//| Methods of working with indicator buffers                        |
//+------------------------------------------------------------------+
   //--- Return the list of buffers with one out of (1) integer, (2) real and (3) string properties meeting a specified criterion
   static CArrayObj *ByBufferProperty(CArrayObj *list_source,ENUM_BUFFER_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode);
   static CArrayObj *ByBufferProperty(CArrayObj *list_source,ENUM_BUFFER_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode);
   static CArrayObj *ByBufferProperty(CArrayObj *list_source,ENUM_BUFFER_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode);
   //--- Return the buffer index in the list with the maximum value of the order's (1) integer, (2) real and (3) string properties
   static int        FindBufferMax(CArrayObj *list_source,ENUM_BUFFER_PROP_INTEGER property);
   static int        FindBufferMax(CArrayObj *list_source,ENUM_BUFFER_PROP_DOUBLE property);
   static int        FindBufferMax(CArrayObj *list_source,ENUM_BUFFER_PROP_STRING property);
   //--- Return the buffer index in the list with the minimum value of the order's (1) integer, (2) real and (3) string properties
   static int        FindBufferMin(CArrayObj *list_source,ENUM_BUFFER_PROP_INTEGER property);
   static int        FindBufferMin(CArrayObj *list_source,ENUM_BUFFER_PROP_DOUBLE property);
   static int        FindBufferMin(CArrayObj *list_source,ENUM_BUFFER_PROP_STRING property);
//---
  };
//+------------------------------------------------------------------+

Y al final del archivo, añadimos todos los métodos declarados en el cuerpo de la clase:

//+------------------------------------------------------------------+
//| Methods of working with buffer lists                             |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Return the list of buffers with one integer                      |
//| property meeting the specified criterion                         |
//+------------------------------------------------------------------+
CArrayObj *CSelect::ByBufferProperty(CArrayObj *list_source,ENUM_BUFFER_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode)
  {
   if(list_source==NULL) return NULL;
   CArrayObj *list=new CArrayObj();
   if(list==NULL) return NULL;
   list.FreeMode(false);
   ListStorage.Add(list);
   int total=list_source.Total();
   for(int i=0; i<total; i++)
     {
      CBuffer *obj=list_source.At(i);
      if(!obj.SupportProperty(property)) continue;
      long obj_prop=obj.GetProperty(property);
      if(CompareValues(obj_prop,value,mode)) list.Add(obj);
     }
   return list;
  }
//+------------------------------------------------------------------+
//| Return the list of buffers with one real                         |
//| property meeting the specified criterion                         |
//+------------------------------------------------------------------+
CArrayObj *CSelect::ByBufferProperty(CArrayObj *list_source,ENUM_BUFFER_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode)
  {
   if(list_source==NULL) return NULL;
   CArrayObj *list=new CArrayObj();
   if(list==NULL) return NULL;
   list.FreeMode(false);
   ListStorage.Add(list);
   for(int i=0; i<list_source.Total(); i++)
     {
      CBuffer *obj=list_source.At(i);
      if(!obj.SupportProperty(property)) continue;
      double obj_prop=obj.GetProperty(property);
      if(CompareValues(obj_prop,value,mode)) list.Add(obj);
     }
   return list;
  }
//+------------------------------------------------------------------+
//| Return the list of buffers with one string                       |
//| property meeting the specified criterion                         |
//+------------------------------------------------------------------+
CArrayObj *CSelect::ByBufferProperty(CArrayObj *list_source,ENUM_BUFFER_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode)
  {
   if(list_source==NULL) return NULL;
   CArrayObj *list=new CArrayObj();
   if(list==NULL) return NULL;
   list.FreeMode(false);
   ListStorage.Add(list);
   for(int i=0; i<list_source.Total(); i++)
     {
      CBuffer *obj=list_source.At(i);
      if(!obj.SupportProperty(property)) continue;
      string obj_prop=obj.GetProperty(property);
      if(CompareValues(obj_prop,value,mode)) list.Add(obj);
     }
   return list;
  }
//+------------------------------------------------------------------+
//| Return the buffer index in the list                              |
//| with the maximum integer property value                          |
//+------------------------------------------------------------------+
int CSelect::FindBufferMax(CArrayObj *list_source,ENUM_BUFFER_PROP_INTEGER property)
  {
   if(list_source==NULL) return WRONG_VALUE;
   int index=0;
   CBuffer *max_obj=NULL;
   int total=list_source.Total();
   if(total==0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      CBuffer *obj=list_source.At(i);
      long obj1_prop=obj.GetProperty(property);
      max_obj=list_source.At(index);
      long obj2_prop=max_obj.GetProperty(property);
      if(CompareValues(obj1_prop,obj2_prop,MORE)) index=i;
     }
   return index;
  }
//+------------------------------------------------------------------+
//| Return the buffer index in the list                              |
//| with the maximum real property value                             |
//+------------------------------------------------------------------+
int CSelect::FindBufferMax(CArrayObj *list_source,ENUM_BUFFER_PROP_DOUBLE property)
  {
   if(list_source==NULL) return WRONG_VALUE;
   int index=0;
   CBuffer *max_obj=NULL;
   int total=list_source.Total();
   if(total==0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      CBuffer *obj=list_source.At(i);
      double obj1_prop=obj.GetProperty(property);
      max_obj=list_source.At(index);
      double obj2_prop=max_obj.GetProperty(property);
      if(CompareValues(obj1_prop,obj2_prop,MORE)) index=i;
     }
   return index;
  }
//+------------------------------------------------------------------+
//| Return the buffer index in the list                              |
//| with the maximum string property value                           |
//+------------------------------------------------------------------+
int CSelect::FindBufferMax(CArrayObj *list_source,ENUM_BUFFER_PROP_STRING property)
  {
   if(list_source==NULL) return WRONG_VALUE;
   int index=0;
   CBuffer *max_obj=NULL;
   int total=list_source.Total();
   if(total==0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      CBuffer *obj=list_source.At(i);
      string obj1_prop=obj.GetProperty(property);
      max_obj=list_source.At(index);
      string obj2_prop=max_obj.GetProperty(property);
      if(CompareValues(obj1_prop,obj2_prop,MORE)) index=i;
     }
   return index;
  }
//+------------------------------------------------------------------+
//| Return the buffer index in the list                              |
//| with the minimum integer property value                          |
//+------------------------------------------------------------------+
int CSelect::FindBufferMin(CArrayObj* list_source,ENUM_BUFFER_PROP_INTEGER property)
  {
   int index=0;
   CBuffer *min_obj=NULL;
   int total=list_source.Total();
   if(total==0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      CBuffer *obj=list_source.At(i);
      long obj1_prop=obj.GetProperty(property);
      min_obj=list_source.At(index);
      long obj2_prop=min_obj.GetProperty(property);
      if(CompareValues(obj1_prop,obj2_prop,LESS)) index=i;
     }
   return index;
  }
//+------------------------------------------------------------------+
//| Return the buffer index in the list                              |
//| with the minimum real property value                             |
//+------------------------------------------------------------------+
int CSelect::FindBufferMin(CArrayObj* list_source,ENUM_BUFFER_PROP_DOUBLE property)
  {
   int index=0;
   CBuffer *min_obj=NULL;
   int total=list_source.Total();
   if(total== 0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      CBuffer *obj=list_source.At(i);
      double obj1_prop=obj.GetProperty(property);
      min_obj=list_source.At(index);
      double obj2_prop=min_obj.GetProperty(property);
      if(CompareValues(obj1_prop,obj2_prop,LESS)) index=i;
     }
   return index;
  }
//+------------------------------------------------------------------+
//| Return the buffer index in the list                              |
//| with the minimum string property value                           |
//+------------------------------------------------------------------+
int CSelect::FindBufferMin(CArrayObj* list_source,ENUM_BUFFER_PROP_STRING property)
  {
   int index=0;
   CBuffer *min_obj=NULL;
   int total=list_source.Total();
   if(total==0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      CBuffer *obj=list_source.At(i);
      string obj1_prop=obj.GetProperty(property);
      min_obj=list_source.At(index);
      string obj2_prop=min_obj.GetProperty(property);
      if(CompareValues(obj1_prop,obj2_prop,LESS)) index=i;
     }
   return index;
  }
//+------------------------------------------------------------------+

Ya analizamos con detalle el funcionamiento de la clase CSelect en el tercer artículo de la descripción de la biblioteca.

Ahora, vamos a mejorar las clases de búfer abstracto y sus herederos.

Dado que realizamos la búsqueda y la clasificación según las propiedades de los objetos de búfer,
incluiremos el archivo de la clase CSelect en el archivo de la clase de búfer abstracto \MQL5\Include\DoEasy\Objects\Indicators\Buffer.mqh:

//+------------------------------------------------------------------+
//|                                                       Buffer.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"
#property strict    // Necessary for mql4
//+------------------------------------------------------------------+
//| Archivos de inclusión                                            |
//+------------------------------------------------------------------+
#include "..\..\Services\Select.mqh"
#include "..\..\Objects\BaseObj.mqh"
//+------------------------------------------------------------------+

Ahora, la clase CSelect resultará también visible CBuffer en todos sus herederos.

En la sección pública de la clase, escribimos el método que asigna un nombre personalizado al objeto de búfer:

public:
//--- Set buffer's (1) integer, (2) real and (3) string properties
   void              SetProperty(ENUM_BUFFER_PROP_INTEGER property,long value)   { this.m_long_prop[property]=value;                                        }
   void              SetProperty(ENUM_BUFFER_PROP_DOUBLE property,double value)  { this.m_double_prop[this.IndexProp(property)]=value;                      }
   void              SetProperty(ENUM_BUFFER_PROP_STRING property,string value)  { this.m_string_prop[this.IndexProp(property)]=value;                      }
//--- Return (1) integer, (2) real and (3) string buffer properties from the properties array
   long              GetProperty(ENUM_BUFFER_PROP_INTEGER property)        const { return this.m_long_prop[property];                                       }
   double            GetProperty(ENUM_BUFFER_PROP_DOUBLE property)         const { return this.m_double_prop[this.IndexProp(property)];                     }
   string            GetProperty(ENUM_BUFFER_PROP_STRING property)         const { return this.m_string_prop[this.IndexProp(property)];                     }
//--- Get description of buffer's (1) integer, (2) real and (3) string properties
   string            GetPropertyDescription(ENUM_BUFFER_PROP_INTEGER property);
   string            GetPropertyDescription(ENUM_BUFFER_PROP_DOUBLE property);
   string            GetPropertyDescription(ENUM_BUFFER_PROP_STRING property);
//--- Return the flag of the buffer supporting the property
   virtual bool      SupportProperty(ENUM_BUFFER_PROP_INTEGER property)          { return true;       }
   virtual bool      SupportProperty(ENUM_BUFFER_PROP_DOUBLE property)           { return true;       }
   virtual bool      SupportProperty(ENUM_BUFFER_PROP_STRING property)           { return true;       }

//--- Compare CBuffer objects by all possible properties (for sorting the lists by a specified buffer object property)
   virtual int       Compare(const CObject *node,const int mode=0) const;
//--- Compare CBuffer objects by all properties (to search for equal buffer objects)
   bool              IsEqual(CBuffer* compared_obj) const;
                     
//--- Set the buffer name
   void              SetName(const string name)                                  { this.m_name=name;  }

//--- Constructor por defecto
                     CBuffer(void){;}
protected:

Asignando un nombre personalizado al búfer, tenemos la posibilidad de nombrar el búfer para su posterior búsqueda en la lista de colección según este nombre.

Tenemos un método que retorna el valor del búfer desde el índice especificado de la serie temporal, y un método que retorna el color asiganado al búfer en el índice especificado de la serie temporal, pero no tenemos un método que retorne el índice del color asignado al búfer en el índice especificado de la serie temporal. Y, en esencia, no estamos asignando al búfer un color como tal. Estamos estableciendo para el búfer un índice de color, es decir, un número que indica que debemos utilizar uno de los colores asignados al búfer para colorear la línea en la posición indicada de la serie temporal.

Vamos a corregir este despiste declarando otro método más, encargado de retornar el índice del color asignado al búfer en la posición especificada de la serie temporal, mientras que renombramos el método que retorna el color del búfer de la posición indicada de la serie temporal como GetColorBufferValueColor(), en lugar de GetColorBufferValue():

//--- 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, (2) color index and (3) color buffer arrays
   double            GetDataBufferValue(const uint buffer_index,const uint series_index) const;
   int               GetColorBufferValueIndex(const uint series_index) const;
   color             GetColorBufferValueColor(const uint series_index) const;
//--- Set the value to the specified index of the specified (1) data and (2) color buffer arrays

Ahora, ya hemos declarado dos métodos que trabajan con el color del búfer. Uno retorna el color, el otro, el índice del color.

A continuación, escribimos fuera del cuerpo de la clase el método que retorna el índice del color, y corregimos la implementación del método que retorna el color del búfer:

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

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

Antes, solo teníamos un método, y obteníamos el índice del color directamente dentro de él:

//+------------------------------------------------------------------+
//| 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];
  }
//+------------------------------------------------------------------+

Ahora, hemos sacado este cálculo al método aparte GetColorBufferValueIndex(), mientras que en el método para retornar el color de la barra, en lugar del cálculo del índice, utilizamos la llamada de este nuevo método.

Al crear la clase del objeto de búfer de flechas heredero (como las demás clases de los búferes) del búfer abstracto, hemos cometido una pequeña omisión: en la clase CBuffer hemos declarado los métodos virtuales para establecer el código de las flechas y su desplazamiento, pero hemos olvidado su implementación en la clase heredera. Vamos a corregir la situación.

Abrimos el archivo de la clase del objeto de búfer de flechas \MQL5\Include\DoEasy\Objects\Indicators\BufferArrow.mqh y declaramos estos métodos. Ya de paso, añadimos otros dos métodos para establecer y retornar los valores en/desde la matriz designada por el búfer de indicador:

//+------------------------------------------------------------------+
//| 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);
   
//--- Set (1) the arrow code, (2) vertical shift of arrows
   virtual void      SetArrowCode(const uchar code);
   virtual void      SetArrowShift(const int shift);
   
//--- Set the value to the data buffer array
   void              SetData(const uint series_index,const double value)               { this.SetBufferValue(0,series_index,value);       }
//--- Return the value from the data buffer array
   double            GetData(const uint series_index)                            const { return this.GetDataBufferValue(0,series_index);  }
   
  };
//+------------------------------------------------------------------+

Fuera del cuerpo de la clase, implementamos los métodos para establecer las flechas y su desplazamiento:

//+------------------------------------------------------------------+
//| Set the arrow code                                               |
//+------------------------------------------------------------------+
void CBufferArrow::SetArrowCode(const uchar code)
  {
   this.SetProperty(BUFFER_PROP_ARROW_CODE,code);
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_ARROW,code);
  }
//+------------------------------------------------------------------+
//| Set the vertical shift of the arrows                             |
//+------------------------------------------------------------------+
void CBufferArrow::SetArrowShift(const int shift)
  {
   this.SetProperty(BUFFER_PROP_ARROW_SHIFT,shift);
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_ARROW_SHIFT,shift);
  }
//+------------------------------------------------------------------+

Los métodos registran en la propiedad correspondiente del objeto de búfer el valor que se les ha transmitido y
asignan esta propiedad al búfer dibujado del objeto de búfer
.

Asismismo, también añadimos a los archivos de los objetos de búfer con un búfer para el dibujado, concretamente, a los archivos de líneas (BufferLine.mqh), segmentos (BufferSection.mqh) e histogramas desde cero (BufferHistogram.mqh), dos métodos para establecer/retornar los valores en/desde la matriz del búfer de datos del objeto de búfer:

//--- Set the value to the data buffer array
   void              SetData(const uint series_index,const double value)               { this.SetBufferValue(0,series_index,value);       }
//--- Return the value from the data buffer array
   double            GetData(const uint series_index)                            const { return this.GetDataBufferValue(0,series_index);  }

A los archivos de los objetos de búfer con dos búferes para el dibujado, concretamente, al histograma basado en dos matrices de datos (BufferHistogram2.mqh), al zigzag (BufferZigZag.mqh) y al rellenado entre dos matrices de datos (BufferFilling.mqh), les añadimos cuatro métodos para establecer/retornar los valores en/desde la matriz del búfer de datos del objeto de búfer:

//--- Set the value to the (1) zero and (2) the first data buffer array
   void              SetData0(const uint series_index,const double value)              { this.SetBufferValue(0,series_index,value);       }
   void              SetData1(const uint series_index,const double value)              { this.SetBufferValue(1,series_index,value);       }
//--- Return the value from the (1) zero and (2) the first data buffer array
   double            GetData0(const uint series_index)                           const { return this.GetDataBufferValue(0,series_index);  }
   double            GetData1(const uint series_index)                           const { return this.GetDataBufferValue(1,series_index);  }

A los archivos de los objetos de búfer con cuatro búferes para el dibujado, concretamente, a los archivos de barras (BufferBars.mqh) y velas (BufferCandlts.mqh), les añadimos ocho métodos para establecer/retornar los valores de OHLC en/desde la matriz del búfer de datos del objeto de búfer:

//--- Set (1) Open, (2) High, (3) Low and (4) Close values to the appropriate data buffer array
   void              SetDataOpen(const uint series_index,const double value)           { this.SetBufferValue(0,series_index,value);       }
   void              SetDataHigh(const uint series_index,const double value)           { this.SetBufferValue(1,series_index,value);       }
   void              SetDataLow(const uint series_index,const double value)            { this.SetBufferValue(2,series_index,value);       }
   void              SetDataClose(const uint series_index,const double value)          { this.SetBufferValue(3,series_index,value);       }
//--- Return (1) Open, (2) High, (3) Low and (4) Close value from the appropriate data buffer array
   double            GetDataOpen(const uint series_index)                        const { return this.GetDataBufferValue(0,series_index);  }
   double            GetDataHigh(const uint series_index)                        const { return this.GetDataBufferValue(1,series_index);  }
   double            GetDataLow(const uint series_index)                         const { return this.GetDataBufferValue(2,series_index);  }
   double            GetDataClose(const uint series_index)                       const { return this.GetDataBufferValue(3,series_index);  }

Claro que podríamos apañarnos con los métodos ya escritos del objeto de búfer abstracto básico SetBufferValue() y GetBufferValue(), pero estos métodos requieren que se indique el número del búfer necesario. Y nosotros estamos intentando simplificar el funcionamiento para el usuario final. Por eso, nosotros tendremos la posibilidad de elegir qué modo utilizar entre los propuestos.

Ya está todo listo para crear la clase de colección de los objetos de búferes de indicador.

Esta clase contendrá una lista con todos los objetos de búfer creados y ofrecerá la posibilidad de crear y obtener cualquier búfer para trabajar con él en el programa. A diferencia de las anteriores clases de colección, al añadir un nuevo objeto de búfer, no necesitaremos comprobar la presencia de un objeto idéntico ya existente con las mismas propiedades, ya que podemos utilizar en el indicador búferes absolutamente idénticos para visualizar eventos diferentes.

La clase de colección de los objetos de búferes

En la carpeta \MQL5\Include\DoEasy\Collections\, creamos el nuevo archivo BuffersCollection.mqh con la clase básica CObject de la biblioteca estándar, y le añadimos de inmediato los archivos de las clases de la lista básica de la biblioteca y de los objetos de búfer:

//+------------------------------------------------------------------+
//|                                            BuffersCollection.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 "ListObj.mqh"
#include "..\Objects\Indicators\BufferArrow.mqh"
#include "..\Objects\Indicators\BufferLine.mqh"
#include "..\Objects\Indicators\BufferSection.mqh"
#include "..\Objects\Indicators\BufferHistogram.mqh"
#include "..\Objects\Indicators\BufferHistogram2.mqh"
#include "..\Objects\Indicators\BufferZigZag.mqh"
#include "..\Objects\Indicators\BufferFilling.mqh"
#include "..\Objects\Indicators\BufferBars.mqh"
#include "..\Objects\Indicators\BufferCandles.mqh"
//+------------------------------------------------------------------+
//| Collection of indicator buffers                                  |
//+------------------------------------------------------------------+
class CBuffersCollection : public CObject
  {

Vamos a rellenar el cuerpo de la clase con el contenido necesario (por fortuna, no es mucho), y después analizaremos el papel que juega:

//+------------------------------------------------------------------+
//| Collection of indicator buffers                                  |
//+------------------------------------------------------------------+
class CBuffersCollection : public CObject
  {
private:
   CListObj                m_list;                       // Buffer object list
   
//--- Return the index of the next (1) drawn and (2) basic buffer
   int                     GetIndexNextPlot(void);
   int                     GetIndexNextBase(void);
//--- Create a new buffer object and place it to the collection list
   bool                    CreateBuffer(ENUM_BUFFER_STATUS status);
   
public:
//--- Return (1) oneself and (2) the timeseries list
   CBuffersCollection     *GetObject(void)               { return &this;                                       }
   CArrayObj              *GetList(void)                 { return &this.m_list;                                }
//--- Return the number of (1) drawn buffers, (2) all arrays used to build all buffers in the collection
   int                     PlotsTotal(void);
   int                     BuffersTotal(void);
   
//--- Create the new buffer (1) "Drawing with arrows", (2) "Line", (3) "Sections", (4) "Histogram from the zero line", 
//--- (5) "Histogram on two indicator buffers", (6) "Zigzag", (7) "Color filling between two levels",
//--- (8) "Display as bars", (9) "Display as candles",
   bool                    CreateArrow(void)             { return this.CreateBuffer(BUFFER_STATUS_ARROW);      }
   bool                    CreateLine(void)              { return this.CreateBuffer(BUFFER_STATUS_LINE);       }
   bool                    CreateSection(void)           { return this.CreateBuffer(BUFFER_STATUS_SECTION);    }
   bool                    CreateHistogram(void)         { return this.CreateBuffer(BUFFER_STATUS_HISTOGRAM);  }
   bool                    CreateHistogram2(void)        { return this.CreateBuffer(BUFFER_STATUS_HISTOGRAM2); }
   bool                    CreateZigZag(void)            { return this.CreateBuffer(BUFFER_STATUS_ZIGZAG);     }
   bool                    CreateFilling(void)           { return this.CreateBuffer(BUFFER_STATUS_FILLING);    }
   bool                    CreateBars(void)              { return this.CreateBuffer(BUFFER_STATUS_BARS);       }
   bool                    CreateCandles(void)           { return this.CreateBuffer(BUFFER_STATUS_CANDLES);    }
   
//--- Return the buffer by the Plot index
   CBuffer                *GetBufferByPlot(const int plot_index);
//--- Return buffers by drawing style by a serial number
//--- (0 - the very first created buffer with the ХХХ drawing style, 1,2,N - subsequent ones)
   CBufferArrow           *GetBufferArrow(const int number);
   CBufferLine            *GetBufferLine(const int number);
   CBufferSection         *GetBufferSection(const int number);
   CBufferHistogram       *GetBufferHistogram(const int number);
   CBufferHistogram2      *GetBufferHistogram2(const int number);
   CBufferZigZag          *GetBufferZigZag(const int number);
   CBufferFilling         *GetBufferFilling(const int number);
   CBufferBars            *GetBufferBars(const int number);
   CBufferCandles         *GetBufferCandles(const int number);
   
//--- Constructor
                           CBuffersCollection();
  };
//+------------------------------------------------------------------+

Bien, m_list es una lista en la que iremos "juntando" y guardando todos los objetos de búfer creados. Es heredera de la clase de la matriz dinámica de punteros a las instancias de la clase CObject.

El método privado que retorna el índice del siguiente búfer dibujado GetIndexNextPlot(), es necesario para especificar el índice del siguiente búfer de indicador creado, mientras que el método (igualmente privado) que retorna el siguiente búfer básico GetIndexNextBase(), es necesario para especificar el índice de la matriz real que se puede designar como búfer de indicador para el objeto de búfer nuevamente creado.

Vamos a aclarar este punto. Al crear un búfer para el indicador, le asignamos un número en la ventana de datos (número del búfer dibujado) y lo vinculamos con una matriz double (el índice de la matriz básica del búfer). ¿Por qué "básica"? Porque el búfer para el dibujado puede utilizar varias matrices. Será básica la primera matriz que sea designada como matriz de indicador. Las demás matrices utilizadas para el dibujado tendrán el índice "matriz básica"+N.

De esta forma, para los tres búferes de color construidos sobre las dos matrices, los índices del búfer de dibujado y la matriz básica tendrán el aspecto siguiente:

Primer búfer:

  • Búfer de dibujado — índice 0
    • Matriz básica — índice 0
    • Segunda matriz — índice 1
    • Matriz de color — índice 2

Segundo búfer:

  • Búfer de dibujado — índice 1
    • Matriz básica — índice 3
    • Segunda matriz — índice 4
    • Matriz de color — índice 5

Tercer búfer:

  • Búfer de dibujado — índice 2
    • Matriz básica — índice 6
    • Segunda matriz — índice 7
    • Matriz de color — índice 8

Como podemos ver, la indexación para el búfer de dibujado y la matriz básica para cada búfer es propia, y si el número de búferes utilizados en el indicador es grande, podríamos confundir quién pertenece a quién. La clase de colección asignará automáticamente los índices correctos para los búferes dibujados y sus matrices. Por consiguiente, siempre podremos recurrir a ellos desde nuestro programa.

El método CreateBuffer() crea un nuevo búfer y lo ubica en la lista de colección.
Los métodos GetObject() y GetList() retornan los punteros al objeto de la clase de colección y a la lista de objetos de búfer de la clase de colección, respectivamente.
Los métodos PlotsTotal() y BuffersTotal() retornan el número de búferes de dibujado creados en la colección y el número total de matrices utilizadas para la construcción de los búferes de dibujado, respectivamente.

Métodos públicos para crear objetos de búfer con un estilo de dibujado concreto:

//--- Create the new buffer (1) "Drawing with arrows", (2) "Line", (3) "Sections", (4) "Histogram from the zero line", 
//--- (5) "Histogram on two indicator buffers", (6) "Zigzag", (7) "Color filling between two levels",
//--- (8) "Display as bars", (9) "Display as candles",
   bool                    CreateArrow(void)             { return this.CreateBuffer(BUFFER_STATUS_ARROW);      }
   bool                    CreateLine(void)              { return this.CreateBuffer(BUFFER_STATUS_LINE);       }
   bool                    CreateSection(void)           { return this.CreateBuffer(BUFFER_STATUS_SECTION);    }
   bool                    CreateHistogram(void)         { return this.CreateBuffer(BUFFER_STATUS_HISTOGRAM);  }
   bool                    CreateHistogram2(void)        { return this.CreateBuffer(BUFFER_STATUS_HISTOGRAM2); }
   bool                    CreateZigZag(void)            { return this.CreateBuffer(BUFFER_STATUS_ZIGZAG);     }
   bool                    CreateFilling(void)           { return this.CreateBuffer(BUFFER_STATUS_FILLING);    }
   bool                    CreateBars(void)              { return this.CreateBuffer(BUFFER_STATUS_BARS);       }
   bool                    CreateCandles(void)           { return this.CreateBuffer(BUFFER_STATUS_CANDLES);    }

Los métodos retornan el resultado del funcionamiento del método de creación del objeto de búfer CreateBuffer(), con indicación del estilo de dibujado del búfer creado.

El método GetBufferByPlot() retorna el puntero a un búfer según su índice de búfer de dibujado.

Métdos que retornan los punteros a los objetos de búfer según su número ordinal:

//--- Return buffers by drawing style by a serial number
//--- (0 - the very first created buffer with the ХХХ drawing style, 1,2,N - subsequent ones)
   CBufferArrow           *GetBufferArrow(const int number);
   CBufferLine            *GetBufferLine(const int number);
   CBufferSection         *GetBufferSection(const int number);
   CBufferHistogram       *GetBufferHistogram(const int number);
   CBufferHistogram2      *GetBufferHistogram2(const int number);
   CBufferZigZag          *GetBufferZigZag(const int number);
   CBufferFilling         *GetBufferFilling(const int number);
   CBufferBars            *GetBufferBars(const int number);
   CBufferCandles         *GetBufferCandles(const int number);

Retornan un objeto con un estilo de dibujado concreto según su número en el orden de creación.
Veamos un ejemplo:

Hemos creado tres búferes de flechas BufferArrow() con índices de búferes de dibujado 0, 1, 2 y 3.
A continuación, hemos creado cinco búferes de líneas BufferLine() con índices de búferes de dibujado 4, 5, 6, 7 y 8.

Ahora, debemos trabajar con el tercer búfer de flechas (que tiene el índice 2), y con el cuarto búfer de líneas (con el índice 7).
Para obtener el puntero al tercer búfer de flechas solo tenemos que obtenerlo según el número ordinal (no índice, sino número). El número debe contarse partiendo de cero. Es decir, para obtener el tercer búfer de flechas, deberemos hacerlo así:

CBufferArrow *buffer_arrow=GetBufferArrow(2); // tercer búfer de flechas (0,1,2)

Para obtener el puntero al cuarto búfer de líneas, tenemos que hacerlo así:

CBufferLine *buffer_line=GetBufferLine(3); // cuarto búfer de líneas (0,1,2,3)


Ahora, vamos a analizar la implementación de todos los métodos declarados.

Constructor de la clase:

//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CBuffersCollection::CBuffersCollection()
  {
   this.m_list.Clear();
   this.m_list.Sort();
   this.m_list.Type(COLLECTION_BUFFERS_ID);
  }
//+------------------------------------------------------------------+

Limpiamos la lista, asignamos a la lista la bandera de lista clasificada y asignamos al tipo de colección el indentificador de la lista de colección de búferes de indicador.

Aquí tenemos el método que retorna el índice del siguiente búfer de dibujado y el método que retorna el índice del siguiente búfer básico:

//+------------------------------------------------------------------+
//| Return the index of the next drawn buffer                        |
//+------------------------------------------------------------------+
int CBuffersCollection::GetIndexNextPlot(void)
  {
//--- Return the pointer to the list. If the list is not created for some reason, return -1
   CArrayObj *list=this.GetList();
   if(list==NULL)
      return WRONG_VALUE;
//--- Get the index of the drawn buffer with the highest value. If the FindBufferMax() method returns -1,
//--- the list is empty, return index 0 for the very first buffer in the list
   int index=CSelect::FindBufferMax(list,BUFFER_PROP_INDEX_PLOT);
   if(index==WRONG_VALUE)
      index=0;
//--- if the index is not -1,
   else
     {
      //--- get the buffer object from the list by its index
      CBuffer *buffer=this.m_list.At(index);
      if(buffer==NULL)
         return WRONG_VALUE;
      //--- Return the index following the Plot index of the buffer object
      index=buffer.IndexPlot()+1;
     }
//--- Return the index value
   return index;
  }
//+------------------------------------------------------------------+
//| Return the index of the next basic buffer                        |
//+------------------------------------------------------------------+
int CBuffersCollection::GetIndexNextBase(void)
  {
//--- Return the pointer to the list. If the list is not created for some reason, return -1
   CArrayObj *list=this.GetList();
   if(list==NULL)
      return WRONG_VALUE;
//--- Get the highest index of the next array that can be assigned as an indicator buffer,
//--- if the FindBufferMax() method returns -1,
//--- the list is empty, return index 0 for the very first buffer in the list
   int index=CSelect::FindBufferMax(list,BUFFER_PROP_INDEX_NEXT);
   if(index==WRONG_VALUE)
      index=0;
//--- if the index is not -1,
   else
     {
      //--- get the buffer object from the list by its index
      CBuffer *buffer=this.m_list.At(index);
      if(buffer==NULL)
         return WRONG_VALUE;
      //--- Return the index of the next array from the buffer object properties
      index=buffer.IndexNextBuffer();
     }
//--- Return the index value
   return index;
  }
//+------------------------------------------------------------------+

La lógica de estos dos métodos es idéntica: la hemos detallado en los comentarios a las líneas del código.

Método para crear un nuevo objeto de búfer y ubicarlo en la lista de colección:

//+------------------------------------------------------------------+
//| Create a new buffer object and place it to the collection list   |
//+------------------------------------------------------------------+
bool CBuffersCollection::CreateBuffer(ENUM_BUFFER_STATUS status)
  {
//--- Get the drawn buffer index and the index used to assign the first buffer array as an indicator one
   int index_plot=this.GetIndexNextPlot();
   int index_base=this.GetIndexNextBase();
//--- If any of the indices is not received, return 'false'
   if(index_plot==WRONG_VALUE || index_base==WRONG_VALUE)
      return false;
//--- If the maximum possible number of indicator buffers has already been reached, inform about it and return 'false'
   if(this.m_list.Total()==IND_BUFFERS_MAX)
     {
      ::Print(CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_MAX_BUFFERS_REACHED));
      return false;
     }
//--- Create the buffer drawing style description
   string descript=::StringSubstr(::EnumToString(status),14);
//--- Declare the abstract buffer object
   CBuffer *buffer=NULL;
//--- Create a buffer object depending on the status passed to the method (drawing style)
   switch(status)
     {
      case BUFFER_STATUS_ARROW      : buffer=new CBufferArrow(index_plot,index_base);        break;
      case BUFFER_STATUS_LINE       : buffer=new CBufferLine(index_plot,index_base);         break;
      case BUFFER_STATUS_SECTION    : buffer=new CBufferSection(index_plot,index_base);      break;
      case BUFFER_STATUS_HISTOGRAM  : buffer=new CBufferHistogram(index_plot,index_base);    break;
      case BUFFER_STATUS_HISTOGRAM2 : buffer=new CBufferHistogram2(index_plot,index_base);   break;
      case BUFFER_STATUS_ZIGZAG     : buffer=new CBufferZigZag(index_plot,index_base);       break;
      case BUFFER_STATUS_FILLING    : buffer=new CBufferFilling(index_plot,index_base);      break;
      case BUFFER_STATUS_BARS       : buffer=new CBufferBars(index_plot,index_base);         break;
      case BUFFER_STATUS_CANDLES    : buffer=new CBufferCandles(index_plot,index_base);      break;
      default: break;
     }
//--- Failed to create a buffer, inform of that and return 'false'
   if(buffer==NULL)
     {
      ::Print(CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_BUFFER_OBJ)," ",descript);
      return false;
     }
//--- If failed to add a buffer object to the collection list for some reason,
//--- inform of that, remove the created buffer object and return 'false'
   if(!this.m_list.Add(buffer))
     {
      ::Print(CMessage::Text(MSG_LIB_SYS_FAILED_ADD_BUFFER));
      delete buffer;
      return false;
     }
//--- Set a name for the buffer object and return 'true'
   buffer.SetName("Buffer"+descript+"("+(string)buffer.IndexPlot()+")");
   return true;
  }
//+------------------------------------------------------------------+

La lógica de este también se detalla en los comentarios. Debemos destacar que declaramos el objeto de búfer abstracto CBuffer, pero creamos el nuevo objeto con el estilo de dibujado transmitido al método por el estado (en nuestro caso, precisamente el estado describe el estilo de dibujado). Lo que ocurre es que todos los objetos de búfer son herederos del objeto de búfer abstracto, por eso, esta declaración, así como la creación del objeto, es posible y cómoda.

Método que retorna el búfer según su índice Plot (según el índice en la ventana DataWindow):

//+------------------------------------------------------------------+
//| Return the buffer by the Plot index                              |
//+------------------------------------------------------------------+
CBuffer *CBuffersCollection::GetBufferByPlot(const int plot_index)
  {
   CArrayObj *list=CSelect::ByBufferProperty(this.GetList(),BUFFER_PROP_INDEX_PLOT,plot_index,EQUAL);
   return(list!=NULL && list.Total()==1 ? list.At(0) : NULL);
  }
//+------------------------------------------------------------------+

Con la ayuda de la clase CSelect, obtenemos la lista que contiene solo el objeto de búfer con el índice especificado (el objeto en la lista será solo uno), y retornamos de la lista obtenida el objeto de búfer, si se ha encontrado, o bien NULL, si este objeto no se encuentra en la lista de colección.

Métdos que retornan los objetos de búfer de un tipo concreto:

//+------------------------------------------------------------------+
//| Return the "Drawing by arrows" buffer by a serial number         |
//| (0 - the very first arrow buffer, 1,2,N - subsequent ones)       |
//+------------------------------------------------------------------+
CBufferArrow *CBuffersCollection::GetBufferArrow(const int number)
  {
   CArrayObj *list=CSelect::ByBufferProperty(this.GetList(),BUFFER_PROP_STATUS,BUFFER_STATUS_ARROW,EQUAL);
   return(list!=NULL && list.Total()>0 ? list.At(number) : NULL);
  }
//+------------------------------------------------------------------+
//| Return the Line buffer by a serial number                        |
//| (0 - the very first line buffer, 1,2,N - subsequent ones)        |
//+------------------------------------------------------------------+
CBufferLine *CBuffersCollection::GetBufferLine(const int number)
  {
   CArrayObj *list=CSelect::ByBufferProperty(this.GetList(),BUFFER_PROP_STATUS,BUFFER_STATUS_LINE,EQUAL);
   return(list!=NULL && list.Total()>0 ? list.At(number) : NULL);
  }
//+------------------------------------------------------------------+
//| Return the Sections buffer by a serial number                    |
//| (0 - the very first sections buffer, 1,2,N - subsequent ones)    |
//+------------------------------------------------------------------+
CBufferSection *CBuffersCollection::GetBufferSection(const int number)
  {
   CArrayObj *list=CSelect::ByBufferProperty(this.GetList(),BUFFER_PROP_STATUS,BUFFER_STATUS_SECTION,EQUAL);
   return(list!=NULL && list.Total()>0 ? list.At(number) : NULL);
  }
//+------------------------------------------------------------------+
//| Return the "Histogram from the zero line" buffer by number       |
//| (0 - the very first buffer, 1,2,N - subsequent ones)             |
//+------------------------------------------------------------------+
CBufferHistogram *CBuffersCollection::GetBufferHistogram(const int number)
  {
   CArrayObj *list=CSelect::ByBufferProperty(this.GetList(),BUFFER_PROP_STATUS,BUFFER_STATUS_HISTOGRAM,EQUAL);
   return(list!=NULL && list.Total()>0 ? list.At(number) : NULL);
  }
//+------------------------------------------------------------------+
//| Return the "Histogram on two buffers" buffer by number           |
//| (0 - the very first buffer, 1,2,N - subsequent ones)             |
//+------------------------------------------------------------------+
CBufferHistogram2 *CBuffersCollection::GetBufferHistogram2(const int number)
  {
   CArrayObj *list=CSelect::ByBufferProperty(this.GetList(),BUFFER_PROP_STATUS,BUFFER_STATUS_HISTOGRAM2,EQUAL);
   return(list!=NULL && list.Total()>0 ? list.At(number) : NULL);
  }
//+------------------------------------------------------------------+
//| Return the ZigZag buffer by a serial number                      |
//| (0 - the very first zigzag buffer, 1,2,N - subsequent ones)      |
//+------------------------------------------------------------------+
CBufferZigZag *CBuffersCollection::GetBufferZigZag(const int number)
  {
   CArrayObj *list=CSelect::ByBufferProperty(this.GetList(),BUFFER_PROP_STATUS,BUFFER_STATUS_ZIGZAG,EQUAL);
   return(list!=NULL && list.Total()>0 ? list.At(number) : NULL);
  }
//+------------------------------------------------------------------+
//|Return the "Color filling between two levels" buffer by number    |
//| (0 - the very first filling buffer, 1,2,N - subsequent ones)     |
//+------------------------------------------------------------------+
CBufferFilling *CBuffersCollection::GetBufferFilling(const int number)
  {
   CArrayObj *list=CSelect::ByBufferProperty(this.GetList(),BUFFER_PROP_STATUS,BUFFER_STATUS_FILLING,EQUAL);
   return(list!=NULL && list.Total()>0 ? list.At(number) : NULL);
  }
//+------------------------------------------------------------------+
//| Return the "Display as bars" buffer by a serial number           |
//| (0 - the very first bar buffer, 1,2,N - subsequent ones)         |
//+------------------------------------------------------------------+
CBufferBars *CBuffersCollection::GetBufferBars(const int number)
  {
   CArrayObj *list=CSelect::ByBufferProperty(this.GetList(),BUFFER_PROP_STATUS,BUFFER_STATUS_BARS,EQUAL);
   return(list!=NULL && list.Total()>0 ? list.At(number) : NULL);
  }
//+------------------------------------------------------------------+
//|Return the "Display as candles" buffer by a serial number         |
//| (0 - the very first candle buffer, 1,2,N - subsequent ones)      |
//+------------------------------------------------------------------+
CBufferCandles *CBuffersCollection::GetBufferCandles(const int number)
  {
   CArrayObj *list=CSelect::ByBufferProperty(this.GetList(),BUFFER_PROP_STATUS,BUFFER_STATUS_CANDLES,EQUAL);
   return(list!=NULL && list.Total()>0 ? list.At(number) : NULL);
  }


Todos los métodos son idénticos, así que analizaremos solo uno de ellos.
Obtenemos la lista que contiene solo los objetos de búfer con el estilo de dibujado necesario.
Si hemos obtenido la lista, y no está vacía, retornamos el objeto de la lista obtenida según el índice especificado.
En la lista, los objetos se ubican en orden ascendente según el índice, por eso, no hace falta corregir este.
Si el índice se sale de los límites de la lista, el método At() de la clase CArrayObj retornará NULL.
Si no hemos obtenido la lista, o bien se encuentra vacía, retornamos NULL.

Aquí tenemos los métodos que retornan el número de búferes de dibujado y el número total de matrices de indicador:

//+------------------------------------------------------------------+
//| Return the number of drawn buffers                               |
//+------------------------------------------------------------------+
int CBuffersCollection::PlotsTotal(void)
  {
   int index=CSelect::FindBufferMax(this.GetList(),BUFFER_PROP_INDEX_PLOT);
   CBuffer *buffer=this.m_list.At(index);
   return(buffer!=NULL ? buffer.IndexPlot()+1 : WRONG_VALUE);
  }
//+------------------------------------------------------------------+
//| Returns the number of all indicator arrays                       |
//+------------------------------------------------------------------+
int CBuffersCollection::BuffersTotal(void)
  {
   int index=CSelect::FindBufferMax(this.GetList(),BUFFER_PROP_INDEX_NEXT);
   CBuffer *buffer=this.m_list.At(index);
   return(buffer!=NULL ? buffer.IndexNextBuffer() : WRONG_VALUE);
  }
//+------------------------------------------------------------------+

La lógica de los métodos es idéntica: obtenemos el índice con el mayor valor de la propiedad necesaria, y, según el índice recibido, obtenemos el objeto de búfer de la lista de colección. Si hemos obtenido el búfer, retornamos la propiedad de este que se corresponda con el método, de lo contrario, retornamos -1.

Con esto, podemos dar por finalizada la creación de la clase de colección de búferes de indicador.

Ahora, debemos proporcionar acceso a los métodos de la clase para los programas que funcionan usando como base la biblioteca. Esto se hace en la clase del objeto básico de la biblioteca CEngine.

Abrimos el archivo \MQL5\Include\DoEasy\Engine.mqh e introducimos los cambios necesarios. Aquí, solo necesitaremos duplicar los métodos ya creados de la clase de colección de búferes de indicador y añadir los métodos auxiliares para que resulte más cómodo trabajar.

En primer lugar, incluimos el archivo de la clase y declaramos el objeto de la clase de colección de búferes:

//+------------------------------------------------------------------+
//|                                                       Engine.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 "Services\TimerCounter.mqh"
#include "Collections\HistoryCollection.mqh"
#include "Collections\MarketCollection.mqh"
#include "Collections\EventsCollection.mqh"
#include "Collections\AccountsCollection.mqh"
#include "Collections\SymbolsCollection.mqh"
#include "Collections\ResourceCollection.mqh"
#include "Collections\TimeSeriesCollection.mqh"
#include "Collections\BuffersCollection.mqh"
#include "TradingControl.mqh"
//+------------------------------------------------------------------+
//| Library basis class                                              |
//+------------------------------------------------------------------+
class CEngine
  {
private:
   CHistoryCollection   m_history;                       // Collection of historical orders and deals
   CMarketCollection    m_market;                        // Collection of market orders and deals
   CEventsCollection    m_events;                        // Event collection
   CAccountsCollection  m_accounts;                      // Account collection
   CSymbolsCollection   m_symbols;                       // Symbol collection
   CTimeSeriesCollection m_time_series;                  // Timeseries collection
   CBuffersCollection   m_buffers;                       // Collection of indicator buffers
   CResourceCollection  m_resource;                      // Resource list
   CTradingControl      m_trading;                       // Trading management object
   CPause               m_pause;                         // Pause object
   CArrayObj            m_list_counters;                 // List of timer counters

En la sección pública de la clase, escribimos los métodos que llaman y retornan el resultado del funcionamiento de los métodos homónimos de la clase de colección de búferes,
y declaramos los métodos adicionales para trabajar con la clase de colección de búferes:

//--- Copy the specified double property of the specified timeseries of the specified symbol to the array
//--- Regardless of the array indexing direction, copying is performed the same way as copying to a timeseries array
   bool                 SeriesCopyToBufferAsSeries(const string symbol,const ENUM_TIMEFRAMES timeframe,const ENUM_BAR_PROP_DOUBLE property,
                                                   double &array[],const double empty=EMPTY_VALUE)
                          { return this.m_time_series.CopyToBufferAsSeries(symbol,timeframe,property,array,empty);}

//--- Return (1) the buffer collection, (2) the buffer list from the buffer collection and (3) the buffer by the Plot index
   CBuffersCollection  *GetBuffersCollection(void)                                     { return &this.m_buffers;                             }
   CArrayObj           *GetListBuffers(void)                                           { return this.m_buffers.GetList();                    }
   CBuffer             *GetBufferByPlot(const int plot_index)                          { return this.m_buffers.GetBufferByPlot(plot_index);  }
//--- Return buffers by drawing style by a serial number
//--- (0 - the very first created buffer with the ХХХ drawing style, 1,2,N - subsequent ones)
   CBufferArrow        *GetBufferArrow(const int number)                               { return this.m_buffers.GetBufferArrow(number);       }
   CBufferLine         *GetBufferLine(const int number)                                { return this.m_buffers.GetBufferLine(number);        }
   CBufferSection      *GetBufferSection(const int number)                             { return this.m_buffers.GetBufferSection(number);     }
   CBufferHistogram    *GetBufferHistogram(const int number)                           { return this.m_buffers.GetBufferHistogram(number);   }
   CBufferHistogram2   *GetBufferHistogram2(const int number)                          { return this.m_buffers.GetBufferHistogram2(number);  }
   CBufferZigZag       *GetBufferZigZag(const int number)                              { return this.m_buffers.GetBufferZigZag(number);      }
   CBufferFilling      *GetBufferFilling(const int number)                             { return this.m_buffers.GetBufferFilling(number);     }
   CBufferBars         *GetBufferBars(const int number)                                { return this.m_buffers.GetBufferBars(number);        }
   CBufferCandles      *GetBufferCandles(const int number)                             { return this.m_buffers.GetBufferCandles(number);     }

//--- Return the number of (1) drawn buffers and (2) all indicator arrays
   int                  BufferPlotsTotal(void)                                         { return this.m_buffers.PlotsTotal();                 }
   int                  BuffersTotal(void)                                             { return this.m_buffers.BuffersTotal();               }

//--- Create the new buffer (1) "Drawing with arrows", (2) "Line", (3) "Sections", (4) "Histogram from the zero line", 
//--- (5) "Histogram on two indicator buffers", (6) "Zigzag", (7) "Color filling between two levels",
//--- (8) "Display as bars", (9) "Display as candles",
   bool                 BufferCreateArrow(void)                                        { return this.m_buffers.CreateArrow();                }
   bool                 BufferCreateLine(void)                                         { return this.m_buffers.CreateLine();                 }
   bool                 BufferCreateSection(void)                                      { return this.m_buffers.CreateSection();              }
   bool                 BufferCreateHistogram(void)                                    { return this.m_buffers.CreateHistogram();            }
   bool                 BufferCreateHistogram2(void)                                   { return this.m_buffers.CreateHistogram2();           }
   bool                 BufferCreateZigZag(void)                                       { return this.m_buffers.CreateZigZag();               }
   bool                 BufferCreateFilling(void)                                      { return this.m_buffers.CreateFilling();              }
   bool                 BufferCreateBars(void)                                         { return this.m_buffers.CreateBars();                 }
   bool                 BufferCreateCandles(void)                                      { return this.m_buffers.CreateCandles();              }
   
//--- Return buffer data by its serial number of (1) arrows, (2) line, (3) sections and (4) histogram from zero
//--- (0 - the very first created buffer with the ХХХ drawing style, 1,2,N - subsequent ones)
   double               BufferDataArrow(const int number,const int series_index);
   double               BufferDataLine(const int number,const int series_index);
   double               BufferDataSection(const int number,const int series_index);
   double               BufferDataHistogram(const int number,const int series_index);
//--- Return buffer data by its serial number of (1) the zero and (2) the first histogram buffer on two buffers
//--- (0 - the very first created buffer with the ХХХ drawing style, 1,2,N - subsequent ones)
   double               BufferDataHistogram20(const int number,const int series_index);
   double               BufferDataHistogram21(const int number,const int series_index);
//--- Return buffer data by its serial number of (1) the zero and (2) the first zigzag buffer
//--- (0 - the very first created buffer with the ХХХ drawing style, 1,2,N - subsequent ones)
   double               BufferDataZigZag0(const int number,const int series_index);
   double               BufferDataZigZag1(const int number,const int series_index);
//--- Return buffer data by its serial number of (1) the zero and (2) the first filling buffer
//--- (0 - the very first created buffer with the ХХХ drawing style, 1,2,N - subsequent ones)
   double               BufferDataFilling0(const int number,const int series_index);
   double               BufferDataFilling1(const int number,const int series_index);
//--- Return buffer data by its serial number of (1) Open, (2) High, (3) Low and (4) Close bar buffers
//--- (0 - the very first created buffer with the ХХХ drawing style, 1,2,N - subsequent ones)
   double               BufferDataBarsOpen(const int number,const int series_index);
   double               BufferDataBarsHigh(const int number,const int series_index);
   double               BufferDataBarsLow(const int number,const int series_index);
   double               BufferDataBarsClose(const int number,const int series_index);
//--- Return buffer data by its serial number of (1) Open, (2) High, (3) Low and (4) Close candle buffers
//--- (0 - the very first created buffer with the ХХХ drawing style, 1,2,N - subsequent ones)
   double               BufferDataCandlesOpen(const int number,const int series_index);
   double               BufferDataCandlesHigh(const int number,const int series_index);
   double               BufferDataCandlesLow(const int number,const int series_index);
   double               BufferDataCandlesClose(const int number,const int series_index);

//--- Set buffer data by its serial number of (1) arrows, (2) line, (3) sections and (4) histogram from zero
//--- (0 - the very first created buffer with the ХХХ drawing style, 1,2,N - subsequent ones)
   void                 BufferSetDataArrow(const int number,const int series_index,const double value);
   void                 BufferSetDataLine(const int number,const int series_index,const double value);
   void                 BufferSetDataSection(const int number,const int series_index,const double value);
   void                 BufferSetDataHistogram(const int number,const int series_index,const double value);
//--- Set data of the (1) zero, (2) first and (3) all histogram buffers on two buffers by a serial number of a created buffer
//--- (0 - the very first created buffer with the HISTOGRAM2 drawing style, 1,2,N - subsequent ones)
   void                 BufferSetDataHistogram20(const int number,const int series_index,const double value);
   void                 BufferSetDataHistogram21(const int number,const int series_index,const double value);
   void                 BufferSetDataHistogram2(const int number,const int series_index,const double value0,const double value1);
//--- Set data of the (1) zero, (2) first and (3) all zigzag buffers by a serial number of a created buffer
//--- (0 - the very first created buffer with the ZIGZAG drawing style, 1,2,N - subsequent ones)
   void                 BufferSetDataZigZag0(const int number,const int series_index,const double value);
   void                 BufferSetDataZigZag1(const int number,const int series_index,const double value);
   void                 BufferSetDataZigZag(const int number,const int series_index,const double value0,const double value1);
//--- Set data of the (1) zero, (2) first and (3) all filling buffers by a serial number of a created buffer
//--- (0 - the very first created buffer with the FILLING drawing style, 1,2,N - subsequent ones)
   void                 BufferSetDataFilling0(const int number,const int series_index,const double value);
   void                 BufferSetDataFilling1(const int number,const int series_index,const double value);
   void                 BufferSetDataFilling(const int number,const int series_index,const double value0,const double value1);
//--- Set data of the (1) Open, (2) High, (3) Low, (4) Close and (5) all bar buffers by a serial number of a created buffer
//--- (0 - the very first created buffer with the BARS drawing style, 1,2,N - subsequent ones)
   void                 BufferSetDataBarsOpen(const int number,const int series_index,const double value);
   void                 BufferSetDataBarsHigh(const int number,const int series_index,const double value);
   void                 BufferSetDataBarsLow(const int number,const int series_index,const double value);
   void                 BufferSetDataBarsClose(const int number,const int series_index,const double value);
   void                 BufferSetDataBars(const int number,const int series_index,const double open,const double high,const double low,const double close);
//--- Set data of the (1) Open, (2) High, (3) Low, (4) Close and (5) all candle buffers by a serial number of a created buffer
//--- (0 - the very first created buffer with the CANDLES drawing style, 1,2,N - subsequent ones)
   void                 BufferSetDataCandlesOpen(const int number,const int series_index,const double value);
   void                 BufferSetDataCandlesHigh(const int number,const int series_index,const double value);
   void                 BufferSetDataCandlesLow(const int number,const int series_index,const double value);
   void                 BufferSetDataCandlesClose(const int number,const int series_index,const double value);
   void                 BufferSetDataCandles(const int number,const int series_index,const double open,const double high,const double low,const double close);
   
//--- Return buffer color by its serial number of (1) arrows, (2) line, (3) sections, (4) histogram from zero
//--- (5) histogram on two buffers, (6) zigzag, (7) filling, (8) bars and (9) candles
//--- (0 - the very first created buffer with the ХХХ drawing style, 1,2,N - subsequent ones)
   color                BufferColorArrow(const int number,const int series_index);
   color                BufferColorLine(const int number,const int series_index);
   color                BufferColorSection(const int number,const int series_index);
   color                BufferColorHistogram(const int number,const int series_index);
   color                BufferColorHistogram2(const int number,const int series_index);
   color                BufferColorZigZag(const int number,const int series_index);
   color                BufferColorFilling(const int number,const int series_index);
   color                BufferColorBars(const int number,const int series_index);
   color                BufferColorCandles(const int number,const int series_index);
   
//--- Return buffer color index by its serial number of (1) arrows, (2) line, (3) sections, (4) histogram from zero
//--- (5) histogram on two buffers, (6) zigzag, (7) filling, (8) bars and (9) candles
//--- (0 - the very first created buffer with the ХХХ drawing style, 1,2,N - subsequent ones)
   int                  BufferColorIndexArrow(const int number,const int series_index);
   int                  BufferColorIndexLine(const int number,const int series_index);
   int                  BufferColorIndexSection(const int number,const int series_index);
   int                  BufferColorIndexHistogram(const int number,const int series_index);
   int                  BufferColorIndexHistogram2(const int number,const int series_index);
   int                  BufferColorIndexZigZag(const int number,const int series_index);
   int                  BufferColorIndexFilling(const int number,const int series_index);
   int                  BufferColorIndexBars(const int number,const int series_index);
   int                  BufferColorIndexCandles(const int number,const int series_index);

//--- Set the color index to the color buffer by its serial number of (1) arrows, (2) line, (3) sections, (4) histogram from zero
//--- (5) histogram on two buffers, (6) zigzag, (7) filling, (8) bars and (9) candles
//--- (0 - the very first created buffer with the ХХХ drawing style, 1,2,N - subsequent ones)
   void                 BufferSetColorIndexArrow(const int number,const int series_index,const int color_index);
   void                 BufferSetColorIndexLine(const int number,const int series_index,const int color_index);
   void                 BufferSetColorIndexSection(const int number,const int series_index,const int color_index);
   void                 BufferSetColorIndexHistogram(const int number,const int series_index,const int color_index);
   void                 BufferSetColorIndexHistogram2(const int number,const int series_index,const int color_index);
   void                 BufferSetColorIndexZigZag(const int number,const int series_index,const int color_index);
   void                 BufferSetColorIndexFilling(const int number,const int series_index,const int color_index);
   void                 BufferSetColorIndexBars(const int number,const int series_index,const int color_index);
   void                 BufferSetColorIndexCandles(const int number,const int series_index,const int color_index);
   
//--- Set the following for the trading classes:

Métodos que retornan los datos de un búfer concreto según su estilo de dibujado y según el número ordinal del búfer con el mismo estilo de dibujado:

//+------------------------------------------------------------------+
//| Return arrow buffer data by its serial number                    |
//| (0 - the very first arrow buffer, 1,2,N - subsequent ones)       |
//+------------------------------------------------------------------+
double CEngine::BufferDataArrow(const int number,const int series_index)
  {
   CBufferArrow *buff=this.m_buffers.GetBufferArrow(number);
   return(buff!=NULL ? buff.GetData(series_index) : EMPTY_VALUE);
  }
//+------------------------------------------------------------------+
//| Return line buffer data by its serial number                     |
//| (0 - the very first line buffer, 1,2,N - subsequent ones)        |
//+------------------------------------------------------------------+
double CEngine::BufferDataLine(const int number,const int series_index)
  {
   CBufferLine *buff=this.m_buffers.GetBufferLine(number);
   return(buff!=NULL ? buff.GetData(series_index) : EMPTY_VALUE);
  }
//+------------------------------------------------------------------+
//| Return section buffer data by its serial number                  |
//| (0 - the very first sections buffer, 1,2,N - subsequent ones)    |
//+------------------------------------------------------------------+
double CEngine::BufferDataSection(const int number,const int series_index)
  {
   CBufferSection *buff=this.m_buffers.GetBufferSection(number);
   return(buff!=NULL ? buff.GetData(series_index) : EMPTY_VALUE);
  }
//+------------------------------------------------------------------+
//| Return histogram buffer data from zero                           |
//| by its serial number                                             |
//| (0 - the very first buffer, 1,2,N - subsequent ones)             |
//+------------------------------------------------------------------+
double CEngine::BufferDataHistogram(const int number,const int series_index)
  {
   CBufferHistogram *buff=this.m_buffers.GetBufferHistogram(number);
   return(buff!=NULL ? buff.GetData(series_index) : EMPTY_VALUE);
  }
//+------------------------------------------------------------------+
//| Return histogram zero buffer data on two buffers                 |
//| by its serial number                                             |
//| (0 - the very first buffer, 1,2,N - subsequent ones)             |
//+------------------------------------------------------------------+
double CEngine::BufferDataHistogram20(const int number,const int series_index)
  {
   CBufferHistogram2 *buff=this.m_buffers.GetBufferHistogram2(number);
   return(buff!=NULL ? buff.GetData0(series_index) : EMPTY_VALUE);
  }
//+------------------------------------------------------------------+
//| Return histogram first buffer data on two buffers                |
//| by its serial number                                             |
//| (0 - the very first buffer, 1,2,N - subsequent ones)             |
//+------------------------------------------------------------------+
double CEngine::BufferDataHistogram21(const int number,const int series_index)
  {
   CBufferHistogram2 *buff=this.m_buffers.GetBufferHistogram2(number);
   return(buff!=NULL ? buff.GetData1(series_index) : EMPTY_VALUE);
  }
//+------------------------------------------------------------------+
//| Return zigzag zero buffer data                                   |
//| by its serial number                                             |
//| (0 - the very first zigzag buffer, 1,2,N - subsequent ones)      |
//+------------------------------------------------------------------+
double CEngine::BufferDataZigZag0(const int number,const int series_index)
  {
   CBufferZigZag *buff=this.m_buffers.GetBufferZigZag(number);
   return(buff!=NULL ? buff.GetData0(series_index) : EMPTY_VALUE);
  }
//+------------------------------------------------------------------+
//| Return zigzag first buffer data                                  |
//| by its serial number                                             |
//| (0 - the very first zigzag buffer, 1,2,N - subsequent ones)      |
//+------------------------------------------------------------------+
double CEngine::BufferDataZigZag1(const int number,const int series_index)
  {
   CBufferZigZag *buff=this.m_buffers.GetBufferZigZag(number);
   return(buff!=NULL ? buff.GetData1(series_index) : EMPTY_VALUE);
  }
//+------------------------------------------------------------------+
//| Return filling zero buffer data                                  |
//| by its serial number                                             |
//| (0 - the very first filling buffer, 1,2,N - subsequent ones)     |
//+------------------------------------------------------------------+
double CEngine::BufferDataFilling0(const int number,const int series_index)
  {
   CBufferFilling *buff=this.m_buffers.GetBufferFilling(number);
   return(buff!=NULL ? buff.GetData0(series_index) : EMPTY_VALUE);
  }
//+------------------------------------------------------------------+
//| Return filling first buffer data                                 |
//| by its serial number                                             |
//| (0 - the very first filling buffer, 1,2,N - subsequent ones)     |
//+------------------------------------------------------------------+
double CEngine::BufferDataFilling1(const int number,const int series_index)
  {
   CBufferFilling *buff=this.m_buffers.GetBufferFilling(number);
   return(buff!=NULL ? buff.GetData1(series_index) : EMPTY_VALUE);
  }
//+------------------------------------------------------------------+
//| Return Open data of the bar buffer by its serial number          |
//| (0 - the very first bar buffer, 1,2,N - subsequent ones)         |
//+------------------------------------------------------------------+
double CEngine::BufferDataBarsOpen(const int number,const int series_index)
  {
   CBufferBars *buff=this.m_buffers.GetBufferBars(number);
   return(buff!=NULL ? buff.GetDataOpen(series_index) : EMPTY_VALUE);
  }
//+------------------------------------------------------------------+
//| Return High data of the bar buffer by its serial number          |
//| (0 - the very first bar buffer, 1,2,N - subsequent ones)         |
//+------------------------------------------------------------------+
double CEngine::BufferDataBarsHigh(const int number,const int series_index)
  {
   CBufferBars *buff=this.m_buffers.GetBufferBars(number);
   return(buff!=NULL ? buff.GetDataHigh(series_index) : EMPTY_VALUE);
  }
//+------------------------------------------------------------------+
//| Return Low data of the bar buffer by its serial number           |
//| (0 - the very first bar buffer, 1,2,N - subsequent ones)         |
//+------------------------------------------------------------------+
double CEngine::BufferDataBarsLow(const int number,const int series_index)
  {
   CBufferBars *buff=this.m_buffers.GetBufferBars(number);
   return(buff!=NULL ? buff.GetDataLow(series_index) : EMPTY_VALUE);
  }
//+------------------------------------------------------------------+
//| Return Close data of the bar buffer by its serial number         |
//| (0 - the very first bar buffer, 1,2,N - subsequent ones)         |
//+------------------------------------------------------------------+
double CEngine::BufferDataBarsClose(const int number,const int series_index)
  {
   CBufferBars *buff=this.m_buffers.GetBufferBars(number);
   return(buff!=NULL ? buff.GetDataClose(series_index) : EMPTY_VALUE);
  }
//+------------------------------------------------------------------+
//| Return Open data of the candle buffer by its serial number       |
//| (0 - the very first candle buffer, 1,2,N - subsequent ones)      |
//+------------------------------------------------------------------+
double CEngine::BufferDataCandlesOpen(const int number,const int series_index)
  {
   CBufferCandles *buff=this.m_buffers.GetBufferCandles(number);
   return(buff!=NULL ? buff.GetDataOpen(series_index) : EMPTY_VALUE);
  }
//+------------------------------------------------------------------+
//| Return High data of the candle buffer by its serial number       |
//| (0 - the very first candle buffer, 1,2,N - subsequent ones)      |
//+------------------------------------------------------------------+
double CEngine::BufferDataCandlesHigh(const int number,const int series_index)
  {
   CBufferCandles *buff=this.m_buffers.GetBufferCandles(number);
   return(buff!=NULL ? buff.GetDataHigh(series_index) : EMPTY_VALUE);
  }
//+------------------------------------------------------------------+
//| Return Low data of the candle buffer by its serial number        |
//| (0 - the very first candle buffer, 1,2,N - subsequent ones)      |
//+------------------------------------------------------------------+
double CEngine::BufferDataCandlesLow(const int number,const int series_index)
  {
   CBufferCandles *buff=this.m_buffers.GetBufferCandles(number);
   return(buff!=NULL ? buff.GetDataLow(series_index) : EMPTY_VALUE);
  }
//+------------------------------------------------------------------+
//| Return Close data of the candle buffer by its serial number      |
//| (0 - the very first candle buffer, 1,2,N - subsequent ones)      |
//+------------------------------------------------------------------+
double CEngine::BufferDataCandlesClose(const int number,const int series_index)
  {
   CBufferCandles *buff=this.m_buffers.GetBufferCandles(number);
   return(buff!=NULL ? buff.GetDataClose(series_index) : EMPTY_VALUE);
  }
//+------------------------------------------------------------------+

Todos los métodos son idénticos. Echemos un vistazo al método que retorna el valor Close de un objeto de búfer con el estilo de dibujado "Velas".
Transmitimos al método el número ordinal del búfer con el estilo Candles de todos los búferes creados con el estilo Candles (ya hemos analizado anteriormente qué indica el número de búfer con un estilo de dibujado concreto), y el índice de la serie temporal del que debemos obtener los datos del búfer Close de la vela.

Con la ayuda del método GetBufferCandles() de la clase de colección de búferes, obtenemos el puntero al búfer necesario , y, si el búfer ha sido obtenido, retornamos los datos de su búfer Close según el índice especificado de la serie temporal. De lo contrario, retornamos un "valor vacío".

Aquí tenemos los métodos opuestos a los que acabamos de analizar. Estos asignan los valores -en el índice especificado de la serie temporal- a un búfer concreto del objeto de búfer correspondiente según su estilo de dibujado y su número ordinal:

//+------------------------------------------------------------------+
//| Set arrow buffer data by its serial number                       |
//| (0 - the very first arrow buffer, 1,2,N - subsequent ones)       |
//+------------------------------------------------------------------+
void CEngine::BufferSetDataArrow(const int number,const int series_index,const double value)
  {
   CBufferArrow *buff=this.m_buffers.GetBufferArrow(number);
   if(buff==NULL)
      return;
   buff.SetData(series_index,value);
  }
//+------------------------------------------------------------------+
//| Set line buffer data by its serial number                        |
//| (0 - the very first line buffer, 1,2,N - subsequent ones)        |
//+------------------------------------------------------------------+
void CEngine::BufferSetDataLine(const int number,const int series_index,const double value)
  {
   CBufferLine *buff=this.m_buffers.GetBufferLine(number);
   if(buff==NULL)
      return;
   buff.SetData(series_index,value);
  }
//+------------------------------------------------------------------+
//| Set section buffer data by its serial number                     |
//| (0 - the very first sections buffer, 1,2,N - subsequent ones)    |
//+------------------------------------------------------------------+
void CEngine::BufferSetDataSection(const int number,const int series_index,const double value)
  {
   CBufferSection *buff=this.m_buffers.GetBufferSection(number);
   if(buff==NULL)
      return;
   buff.SetData(series_index,value);
  }
//+------------------------------------------------------------------+
//| Set histogram buffer data from zero                              |
//| by its serial number                                             |
//| (0 - the very first buffer, 1,2,N - subsequent ones)             |
//+------------------------------------------------------------------+
void CEngine::BufferSetDataHistogram(const int number,const int series_index,const double value)
  {
   CBufferHistogram *buff=this.m_buffers.GetBufferHistogram(number);
   if(buff==NULL)
      return;
   buff.SetData(series_index,value);
  }
//+------------------------------------------------------------------+
//| Set histogram zero buffer data on two buffers                    |
//| by its serial number                                             |
//| (0 - the very first buffer, 1,2,N - subsequent ones)             |
//+------------------------------------------------------------------+
void CEngine::BufferSetDataHistogram20(const int number,const int series_index,const double value)
  {
   CBufferHistogram2 *buff=this.m_buffers.GetBufferHistogram2(number);
   if(buff==NULL)
      return;
   buff.SetData0(series_index,value);
  }
//+------------------------------------------------------------------+
//| Set histogram first buffer data on two buffers                   |
//| by its serial number                                             |
//| (0 - the very first buffer, 1,2,N - subsequent ones)             |
//+------------------------------------------------------------------+
void CEngine::BufferSetDataHistogram21(const int number,const int series_index,const double value)
  {
   CBufferHistogram2 *buff=this.m_buffers.GetBufferHistogram2(number);
   if(buff==NULL)
      return;
   buff.SetData1(series_index,value);
  }
//+------------------------------------------------------------------+
//| Set data of all histogram buffers on two buffers                 |
//| by its serial number                                             |
//| (0 - the very first buffer, 1,2,N - subsequent ones)             |
//+------------------------------------------------------------------+
void CEngine::BufferSetDataHistogram2(const int number,const int series_index,const double value0,const double value1)
  {
   CBufferHistogram2 *buff=this.m_buffers.GetBufferHistogram2(number);
   if(buff==NULL)
      return;
   buff.SetData0(series_index,value0);
   buff.SetData1(series_index,value1);
  }
//+------------------------------------------------------------------+
//| Set zigzag zero buffer data                                      |
//| by its serial number                                             |
//| (0 - the very first zigzag buffer, 1,2,N - subsequent ones)      |
//+------------------------------------------------------------------+
void CEngine::BufferSetDataZigZag0(const int number,const int series_index,const double value)
  {
   CBufferZigZag *buff=this.m_buffers.GetBufferZigZag(number);
   if(buff==NULL)
      return;
   buff.SetData0(series_index,value);
  }
//+------------------------------------------------------------------+
//| Set zigzag first buffer data                                     |
//| by its serial number                                             |
//| (0 - the very first zigzag buffer, 1,2,N - subsequent ones)      |
//+------------------------------------------------------------------+
void CEngine::BufferSetDataZigZag1(const int number,const int series_index,const double value)
  {
   CBufferZigZag *buff=this.m_buffers.GetBufferZigZag(number);
   if(buff==NULL)
      return;
   buff.SetData1(series_index,value);
  }
//+------------------------------------------------------------------+
//| Set data of all zizag buffers                                    |
//| by its serial number                                             |
//| (0 - the very first zigzag buffer, 1,2,N - subsequent ones)      |
//+------------------------------------------------------------------+
void CEngine::BufferSetDataZigZag(const int number,const int series_index,const double value0,const double value1)
  {
   CBufferZigZag *buff=this.m_buffers.GetBufferZigZag(number);
   if(buff==NULL)
      return;
   buff.SetData0(series_index,value0);
   buff.SetData1(series_index,value1);
  }
//+------------------------------------------------------------------+
//| Set filling zero buffer data                                     |
//| by its serial number                                             |
//| (0 - the very first filling buffer, 1,2,N - subsequent ones)     |
//+------------------------------------------------------------------+
void CEngine::BufferSetDataFilling0(const int number,const int series_index,const double value)
  {
   CBufferFilling *buff=this.m_buffers.GetBufferFilling(number);
   if(buff==NULL)
      return;
   buff.SetData0(series_index,value);
  }
//+------------------------------------------------------------------+
//| Set filling first buffer data                                    |
//| by its serial number                                             |
//| (0 - the very first filling buffer, 1,2,N - subsequent ones)     |
//+------------------------------------------------------------------+
void CEngine::BufferSetDataFilling1(const int number,const int series_index,const double value)
  {
   CBufferFilling *buff=this.m_buffers.GetBufferFilling(number);
   if(buff==NULL)
      return;
   buff.SetData1(series_index,value);
  }
//+------------------------------------------------------------------+
//| Set data of all filling buffers                                  |
//| by its serial number                                             |
//| (0 - the very first filling buffer, 1,2,N - subsequent ones)     |
//+------------------------------------------------------------------+
void CEngine::BufferSetDataFilling(const int number,const int series_index,const double value0,const double value1)
  {
   CBufferFilling *buff=this.m_buffers.GetBufferFilling(number);
   if(buff==NULL)
      return;
   buff.SetData0(series_index,value0);
   buff.SetData1(series_index,value1);
  }
//+------------------------------------------------------------------+
//| Set buffer data of Open bars                                     |
//| by its serial number                                             |
//| (0 - the very first bar buffer, 1,2,N - subsequent ones)         |
//+------------------------------------------------------------------+
void CEngine::BufferSetDataBarsOpen(const int number,const int series_index,const double value)
  {
   CBufferBars *buff=this.m_buffers.GetBufferBars(number);
   if(buff==NULL)
      return;
   buff.SetDataOpen(series_index,value);
  }
//+------------------------------------------------------------------+
//| Set buffer data of High bars                                     |
//| by its serial number                                             |
//| (0 - the very first bar buffer, 1,2,N - subsequent ones)         |
//+------------------------------------------------------------------+
void CEngine::BufferSetDataBarsHigh(const int number,const int series_index,const double value)
  {
   CBufferBars *buff=this.m_buffers.GetBufferBars(number);
   if(buff==NULL)
      return;
   buff.SetDataHigh(series_index,value);
  }
//+------------------------------------------------------------------+
//| Set buffer data of Low bars                                      |
//| by its serial number                                             |
//| (0 - the very first bar buffer, 1,2,N - subsequent ones)         |
//+------------------------------------------------------------------+
void CEngine::BufferSetDataBarsLow(const int number,const int series_index,const double value)
  {
   CBufferBars *buff=this.m_buffers.GetBufferBars(number);
   if(buff==NULL)
      return;
   buff.SetDataLow(series_index,value);
  }
//+------------------------------------------------------------------+
//| Set buffer data of Close bars                                    |
//| by its serial number                                             |
//| (0 - the very first bar buffer, 1,2,N - subsequent ones)         |
//+------------------------------------------------------------------+
void CEngine::BufferSetDataBarsClose(const int number,const int series_index,const double value)
  {
   CBufferBars *buff=this.m_buffers.GetBufferBars(number);
   if(buff==NULL)
      return;
   buff.SetDataClose(series_index,value);
  }
//+------------------------------------------------------------------+
//| Set buffer data of Open candles                                  |
//| by its serial number                                             |
//| (0 - the very first candle buffer, 1,2,N - subsequent ones)      |
//+------------------------------------------------------------------+
void CEngine::BufferSetDataCandlesOpen(const int number,const int series_index,const double value)
  {
   CBufferCandles *buff=this.m_buffers.GetBufferCandles(number);
   if(buff==NULL)
      return;
   buff.SetDataOpen(series_index,value);
  }
//+------------------------------------------------------------------+
//| Set buffer data of High candles                                  |
//| by its serial number                                             |
//| (0 - the very first candle buffer, 1,2,N - subsequent ones)      |
//+------------------------------------------------------------------+
void CEngine::BufferSetDataCandlesHigh(const int number,const int series_index,const double value)
  {
   CBufferCandles *buff=this.m_buffers.GetBufferCandles(number);
   if(buff==NULL)
      return;
   buff.SetDataHigh(series_index,value);
  }
//+------------------------------------------------------------------+
//| Set buffer data of Low candles                                   |
//| by its serial number                                             |
//| (0 - the very first candle buffer, 1,2,N - subsequent ones)      |
//+------------------------------------------------------------------+
void CEngine::BufferSetDataCandlesLow(const int number,const int series_index,const double value)
  {
   CBufferCandles *buff=this.m_buffers.GetBufferCandles(number);
   if(buff==NULL)
      return;
   buff.SetDataLow(series_index,value);
  }
//+------------------------------------------------------------------+
//| Set buffer data of Close candles                                 |
//| by its serial number                                             |
//| (0 - the very first candle buffer, 1,2,N - subsequent ones)      |
//+------------------------------------------------------------------+
void CEngine::BufferSetDataCandlesClose(const int number,const int series_index,const double value)
  {
   CBufferCandles *buff=this.m_buffers.GetBufferCandles(number);
   if(buff==NULL)
      return;
   buff.SetDataClose(series_index,value);
  }
//+------------------------------------------------------------------+

Todos los métodos son idénticos. Vamos a analizar el método que retorna un valor según el índice de la serie temporal en el búfer Close del objeto de búfer con el estilo de dibujado "Velas" según su número ordinal.
Con la ayuda del método GetBufferCandles() de la clase de colección de búferes, obtenemos el objeto de búfer "Velas" según su número ordinal.
Si no hemos logrado obtener el objeto, salimos del método. Según el índice de la serie temporal, asignamos el valor que se ha transmitido al método al búfer Close del objeto de búfer necesario obtenido.

Hay otros dos métodos aparte que asignan al mismo tiempo para todos los búferes de los objetos de búfer "Barras" y "Velas" los valores OHLC según el índice especificado de la serie temporal:

//+------------------------------------------------------------------+
//| Set data of all bar buffers                                      |
//| by its serial number                                             |
//| (0 - the very first bar buffer, 1,2,N - subsequent ones)         |
//+------------------------------------------------------------------+
void CEngine::BufferSetDataBars(const int number,const int series_index,const double open,const double high,const double low,const double close)
  {
   CBufferBars *buff=this.m_buffers.GetBufferBars(number);
   if(buff==NULL)
      return;
   buff.SetDataOpen(series_index,open);
   buff.SetDataHigh(series_index,high);
   buff.SetDataLow(series_index,low);
   buff.SetDataClose(series_index,close);
  }
//+------------------------------------------------------------------+
//| Set data of all candle buffers                                   |
//| by its serial number                                             |
//| (0 - the very first candle buffer, 1,2,N - subsequent ones)      |
//+------------------------------------------------------------------+
void CEngine::BufferSetDataCandles(const int number,const int series_index,const double open,const double high,const double low,const double close)
  {
   CBufferCandles *buff=this.m_buffers.GetBufferCandles(number);
   if(buff==NULL)
      return;
   buff.SetDataOpen(series_index,open);
   buff.SetDataHigh(series_index,high);
   buff.SetDataLow(series_index,low);
   buff.SetDataClose(series_index,close);
  }
//+------------------------------------------------------------------+

Aquí todo es exactamente igual que lo descrito arriba, pero en los métodos se transmiten y establecen todos los valores para los cuatro búferes de los objetos de búfer.

Métodos que retornan el color asignado al índice especificado de la serie temporal del búfer de color de un objeto de búfer concreto según su estilo de dibujado y su número ordinal:

//+------------------------------------------------------------------+
//| Return the arrow buffer color by its serial number               |
//| (0 - the very first arrow buffer, 1,2,N - subsequent ones)       |
//+------------------------------------------------------------------+
color CEngine::BufferColorArrow(const int number,const int series_index)
  {
   CBufferArrow *buff=this.m_buffers.GetBufferArrow(number);
   return(buff!=NULL ? buff.GetColorBufferValueColor(series_index) : clrNONE);
  }
//+------------------------------------------------------------------+
//| Return the line buffer color by its serial number                |
//| (0 - the very first candle line, 1,2,N - subsequent ones)        |
//+------------------------------------------------------------------+
color CEngine::BufferColorLine(const int number,const int series_index)
  {
   CBufferLine *buff=this.m_buffers.GetBufferLine(number);
   return(buff!=NULL ? buff.GetColorBufferValueColor(series_index) : clrNONE);
  }
//+------------------------------------------------------------------+
//| Return the section buffer color by its serial number             |
//| (0 - the very first sections buffer, 1,2,N - subsequent ones)    |
//+------------------------------------------------------------------+
color CEngine::BufferColorSection(const int number,const int series_index)
  {
   CBufferSection *buff=this.m_buffers.GetBufferSection(number);
   return(buff!=NULL ? buff.GetColorBufferValueColor(series_index) : clrNONE);
  }
//+------------------------------------------------------------------+
//| Return histogram buffer color from zero                          |
//| by its serial number                                             |
//| (0 - the very first buffer, 1,2,N - subsequent ones)             |
//+------------------------------------------------------------------+
color CEngine::BufferColorHistogram(const int number,const int series_index)
  {
   CBufferHistogram *buff=this.m_buffers.GetBufferHistogram(number);
   return(buff!=NULL ? buff.GetColorBufferValueColor(series_index) : clrNONE);
  }
//+------------------------------------------------------------------+
//| Return histogram buffer color on two buffers                     |
//| by its serial number                                             |
//| (0 - the very first buffer, 1,2,N - subsequent ones)             |
//+------------------------------------------------------------------+
color CEngine::BufferColorHistogram2(const int number,const int series_index)
  {
   CBufferHistogram2 *buff=this.m_buffers.GetBufferHistogram2(number);
   return(buff!=NULL ? buff.GetColorBufferValueColor(series_index) : clrNONE);
  }
//+------------------------------------------------------------------+
//| Return the zigzag buffer color by its serial number              |
//| (0 - the very first zigzag buffer, 1,2,N - subsequent ones)      |
//+------------------------------------------------------------------+
color CEngine::BufferColorZigZag(const int number,const int series_index)
  {
   CBufferZigZag *buff=this.m_buffers.GetBufferZigZag(number);
   return(buff!=NULL ? buff.GetColorBufferValueColor(series_index) : clrNONE);
  }
//+------------------------------------------------------------------+
//| Return the filling buffer color by its serial number             |
//| (0 - the very first filling buffer, 1,2,N - subsequent ones)     |
//+------------------------------------------------------------------+
color CEngine::BufferColorFilling(const int number,const int series_index)
  {
   CBufferFilling *buff=this.m_buffers.GetBufferFilling(number);
   return(buff!=NULL ? buff.GetColorBufferValueColor(series_index) : clrNONE);
  }
//+------------------------------------------------------------------+
//| Return the bar buffer color by its serial number                 |
//| (0 - the very first bar buffer, 1,2,N - subsequent ones)         |
//+------------------------------------------------------------------+
color CEngine::BufferColorBars(const int number,const int series_index)
  {
   CBufferBars *buff=this.m_buffers.GetBufferBars(number);
   return(buff!=NULL ? buff.GetColorBufferValueColor(series_index) : clrNONE);
  }
//+------------------------------------------------------------------+
//| Return the candle buffer color by its serial number              |
//| (0 - the very first candle buffer, 1,2,N - subsequent ones)      |
//+------------------------------------------------------------------+
color CEngine::BufferColorCandles(const int number,const int series_index)
  {
   CBufferCandles *buff=this.m_buffers.GetBufferCandles(number);
   return(buff!=NULL ? buff.GetColorBufferValueColor(series_index) : clrNONE);
  }
//+------------------------------------------------------------------+

Aquí, todo es idéntico: obtenemos el objeto de búfer necesario según su número. Si hemos logrado obtener el objeto, retonamos el color establecido en su búfer de color según el índice especificado de la serie temporal. De lo contrario, retornamos "el color no ha sido establecido".

Como en el búfer de color en realidad no se registra el valor del color, sino su índice entre los colores establecidos para el búfer, tendremos los métodos correspondientes
que retornan el índice de color de un objeto de búfer concreto desde su búfer de color según el índice especificado de la serie temporal:

//+------------------------------------------------------------------+
//| Return the arrow buffer color index by its serial number         |
//| (0 - the very first arrow buffer, 1,2,N - subsequent ones)       |
//+------------------------------------------------------------------+
int CEngine::BufferColorIndexArrow(const int number,const int series_index)
  {
   CBufferArrow *buff=this.m_buffers.GetBufferArrow(number);
   return(buff!=NULL ? buff.GetColorBufferValueIndex(series_index) : WRONG_VALUE);
  }
//+------------------------------------------------------------------+
//| Return the line buffer color index by its serial number          |
//| (0 - the very first candle line, 1,2,N - subsequent ones)        |
//+------------------------------------------------------------------+
int CEngine::BufferColorIndexLine(const int number,const int series_index)
  {
   CBufferLine *buff=this.m_buffers.GetBufferLine(number);
   return(buff!=NULL ? buff.GetColorBufferValueIndex(series_index) : WRONG_VALUE);
  }
//+------------------------------------------------------------------+
//| Return the section buffer color index by its serial number       |
//| (0 - the very first sections buffer, 1,2,N - subsequent ones)    |
//+------------------------------------------------------------------+
int CEngine::BufferColorIndexSection(const int number,const int series_index)
  {
   CBufferSection *buff=this.m_buffers.GetBufferSection(number);
   return(buff!=NULL ? buff.GetColorBufferValueIndex(series_index) : WRONG_VALUE);
  }
//+------------------------------------------------------------------+
//| Return histogram buffer color index from zero                    |
//| by its serial number                                             |
//| (0 - the very first buffer, 1,2,N - subsequent ones)             |
//+------------------------------------------------------------------+
int CEngine::BufferColorIndexHistogram(const int number,const int series_index)
  {
   CBufferHistogram *buff=this.m_buffers.GetBufferHistogram(number);
   return(buff!=NULL ? buff.GetColorBufferValueIndex(series_index) : WRONG_VALUE);
  }
//+------------------------------------------------------------------+
//| Return histogram buffer color index on two buffers               |
//| by its serial number                                             |
//| (0 - the very first buffer, 1,2,N - subsequent ones)             |
//+------------------------------------------------------------------+
int CEngine::BufferColorIndexHistogram2(const int number,const int series_index)
  {
   CBufferHistogram2 *buff=this.m_buffers.GetBufferHistogram2(number);
   return(buff!=NULL ? buff.GetColorBufferValueIndex(series_index) : WRONG_VALUE);
  }
//+------------------------------------------------------------------+
//| Return the zigzag buffer color index by its serial number        |
//| (0 - the very first zigzag buffer, 1,2,N - subsequent ones)      |
//+------------------------------------------------------------------+
int CEngine::BufferColorIndexZigZag(const int number,const int series_index)
  {
   CBufferZigZag *buff=this.m_buffers.GetBufferZigZag(number);
   return(buff!=NULL ? buff.GetColorBufferValueIndex(series_index) : WRONG_VALUE);
  }
//+------------------------------------------------------------------+
//| Return the filling buffer color index by its serial number       |
//| (0 - the very first filling buffer, 1,2,N - subsequent ones)     |
//+------------------------------------------------------------------+
int CEngine::BufferColorIndexFilling(const int number,const int series_index)
  {
   CBufferFilling *buff=this.m_buffers.GetBufferFilling(number);
   return(buff!=NULL ? buff.GetColorBufferValueIndex(series_index) : WRONG_VALUE);
  }
//+------------------------------------------------------------------+
//| Return the bar buffer color index by its serial number           |
//| (0 - the very first bar buffer, 1,2,N - subsequent ones)         |
//+------------------------------------------------------------------+
int CEngine::BufferColorIndexBars(const int number,const int series_index)
  {
   CBufferBars *buff=this.m_buffers.GetBufferBars(number);
   return(buff!=NULL ? buff.GetColorBufferValueIndex(series_index) : WRONG_VALUE);
  }
//+------------------------------------------------------------------+
//| Return the candle buffer color index by its serial number        |
//| (0 - the very first candle buffer, 1,2,N - subsequent ones)      |
//+------------------------------------------------------------------+
int CEngine::BufferColorIndexCandles(const int number,const int series_index)
  {
   CBufferCandles *buff=this.m_buffers.GetBufferCandles(number);
   return(buff!=NULL ? buff.GetColorBufferValueIndex(series_index) : WRONG_VALUE);
  }
//+------------------------------------------------------------------+

Aquí, todo es idéntico a los métodos que retornan el color, con la salvedad de que los métodos retornan el índice del color.

Estas son todas las mejoras de la clase CEngine necesarias para poner a prueba la clase de colección de los búferes de indicador.


Analizando la creación de un indicador que utilice la colección de búferes

Para poner a prueba la clase de colección de búferes creada, tomaremos el indicador del artículo anterior
y lo guardaremos en la carpeta \MQL5\Indicators\TestDoEasy\Part44\ con el nuevo nombre TestDoEasyPart44.mq5.

El encabezado completo tendrá el aspecto que sigue:

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

//--- 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
//--- indicator buffers
CArrayObj     *list_buffers;                    // Pointer to the buffer object list
//--- 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
//+------------------------------------------------------------------+

Elminamos del bloque includes la inclusión de los archivos de todos los objetos de búfer. Ahora, estos ya están incluidos en la biblioteca, por lo que no tenemos que incluirlos aquí:

//--- 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

Indicamos al compilador con cuántos búferes de dibujado y de indicador vamos a trrabajar:

#property indicator_buffers 28
#property indicator_plots   10

Aquí, si presuponemos un gran número de distintos búferes en el indicador, podemos no calcular esta cifra "al dedillo" en la etapa inicial, estableciendo en cambio cualquier valor. A continuación, al realizar el primer inicio del indicador, recibiremos varias alertas en las que se indicará el número correcto de búferes de dibujado y de indicador, si es que escribimos de forma incorrecta su número.

La biblioteca supone que recurriremos a los búferes de indicador creados según sus estilos de dibujado y sus números en el orden de creación. Aún no hemos implementado la invocación directa de las propiedades del búfer (solo de sus matrices) desde el programa, pero hoy podremos sortear esta limitación obteniendo la lista de objetos de búfer desde la colección y recurriendo a los búferes desde la lista —esta posibilidad existe en cualquiera de las colecciones de objetos—, para trabajar directamente con los objetos creados. Por eso, utilizaremos como "búfer de indicador" una matriz dinámica de punteros a los objetos CObject.


En el manjejador OnInit(), preparamos los búferes necesarios para la simulación y comprobamos el acceso a estos de dos formas distintas:

//+------------------------------------------------------------------+
//| 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 the necessary buffer objects
   engine.BufferCreateArrow();
   engine.BufferCreateLine();
   engine.BufferCreateSection();
   engine.BufferCreateHistogram();
   engine.BufferCreateHistogram2();
   engine.BufferCreateZigZag();
   engine.BufferCreateFilling();
   engine.BufferCreateBars();
   engine.BufferCreateCandles();
   engine.BufferCreateArrow();
   
//--- Check the number of buffers specified in the 'properties' block
   if(engine.BufferPlotsTotal()!=indicator_plots)
      Alert(TextByLanguage("Внимание! Значение \"indicator_plots\" должно быть ","Attention! Value of \"indicator_plots\" should be "),engine.BufferPlotsTotal());
   if(engine.BuffersTotal()!=indicator_buffers)
      Alert(TextByLanguage("Внимание! Значение \"indicator_buffers\" должно быть ","Attention! Value of \"indicator_buffers\" should be "),engine.BuffersTotal());
      
//--- Create color array
   color array_colors[]={clrDodgerBlue,clrRed,clrGray};
//--- Get the pointer to the collection list of buffer objects and
//--- set non-default color values for buffers in a loop according to the list
   list_buffers=engine.GetListBuffers();
   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 line width for ZigZag (the sixth drawn buffer)
//--- It has the index of 5 considering that the starting point is zero
   CBuffer *buff_zz=engine.GetBufferByPlot(5);
   if(buff_zz!=NULL)
     {
      buff_zz.SetWidth(2);
     }
//--- Get the second arrow buffer (created last).
//--- The first arrow buffer has the number of 0, while the second one has the number of 1
//--- Set the arrow size of 2 and the code of 161
   CBuffer *buff=engine.GetBufferArrow(1);
   if(buff!=NULL)
     {
      buff.SetWidth(2);
      buff.SetArrowCode(161);
     }
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+

Ahora, la creación de los búferes tiene lugar de manera muy simple, pues utilizamos los métodos de la biblioteca diseñados para crear un búfer de indicador de uno u otro tipo. La biblioteca se encarga de designar las matrices, mientras que nosotros podemos modificar las propiedades de los búferes una vez estos hayan sido creados. A continuación, comprobamos el número de búferes indicado en #property en el encabezado del indicador, y si nos hemos equivocado al indicar el número de búferes, recibiremos una advertencia sobre ello. Esta comprobación resulta cómoda al crear el indicador, y después de ello, podemos eliminarla del código.

Son dos las formas de comprobar si se ha obtenido acceso al búfer
la primera consiste en obtener acceso al búfer del zigzag según el índice de su búfer de dibujado con la ayuda del método GetBufferByPlot(), en el que se debe indicar el índice del búfer de dibujado (en este caso, se trata del índice 5 para el zigzag),
la segunda, consiste en obtener acceso al último búfer de flechas, creado al final del todo, que supone ordinalmente el segundo búfer de flechas. El acceso al mismo lo conseguimos con la ayuda del método GetBufferArrow(), en el que debemos indicar el número ordinal del búfer de flechas necesario (en este caso, el número 1, dado que la cuenta comienza desde cero)

En el manejador OnCalculate() todo permanece prácticamente sin cambios, salvo por los búferes de velas y barras, en los que registraremos los datos con la ayuda de los métodos del objeto de búfer de velas (registraremos por turno los datos en Open, High, Low y Close), mientras que en las matrices del búfer de barras registraremos todos los valores OHLC de golpe. De esta forma, comprobaremos el funcionamiento de todos los métodos creados para trabajar con los objetos de búfer:

//--- 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<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)
           {
            //--- If this is the candle buffer
            if(buff.Status()==BUFFER_STATUS_CANDLES)
              {
               //--- create the pointer to the candle buffer object by assigning the pointer to the abstract buffer object to it
               //--- and write the appropriate timeseries data to the "candle" object buffers
               CBufferCandles *candle=buff;
               candle.SetDataOpen(i,open[i]);
               candle.SetDataHigh(i,high[i]);
               candle.SetDataLow(i,low[i]);
               candle.SetDataClose(i,close[i]);
              }
            //--- If this is the bar buffer, use access to the first (and only) created bar buffer object
            //--- and write the appropriate timeseries data to the "bars" object buffers in one go
            else
              {
               engine.BufferSetDataBars(0,i,open[i],high[i],low[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 next call

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

Vamos a compilar el indicador e iniciarlo en el gráfico del símbolo, configurando sus ajustes para representar solo un búfer de flechas. Este búfer debe representarse con puntos en el gráfico. Pero también tenemos un segundo búfer de flechas creado en último lugar, y además hemos recurrido al segundo búfer de flechas en OnInit() para modificar su código y el tamaño de su icono:

//--- Get the second arrow buffer (created last).
//--- The first arrow buffer has the number of 0, while the second one has the number of 1
//--- Set the arrow size of 2 and the code of 161
   CBuffer *buff=engine.GetBufferArrow(1);
   if(buff!=NULL)
     {
      buff.SetWidth(2);
      buff.SetArrowCode(161);
     }

Si la obtención de un objeto mediante el método de invoación de un tipo concreto de búfer según su número funciona correctamente, en el gráfico se representarán dos búferes de flechas: el primero con puntos, y el segundo con círculos de una tamaño igual a 2.

El grosor de la línea del zigzag ya lo hemos establecido obteniendo el búfer del zigzag con la ayuda del método que retorna un objeto de búfer según su índice Plot. Activamos la representación del zigzag, y nos aseguramos de que el grosor de su línea se corresponda con el establecido en OnInit():

//--- Set the line width for ZigZag (the sixth drawn buffer)
//--- It has the index of 5 considering that the starting point is zero
   CBuffer *buff_zz=engine.GetBufferByPlot(5);
   if(buff_zz!=NULL)
     {
      buff_zz.SetWidth(2);
     }

Y, finalmente, analizamos cómo se representan las barras y velas: si los métodos para registrar los valores de los precios en las matrices de los búferes funcionan, las barras y las velas se representarán correctamente en el gráfico.

Comprobémoslo:


Como podemos ver, todo lo que hemos diseñado funciona como suponíamos.


¿Qué es lo próximo?

En el próximo artículo, continuaremos desarrollando la clase de colección de los búferes de indicador, centrándonos en el organización del funcionamiento de los indicadores en los modos de símbolo y periodo múltiples.

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
Trabajando con las series temporales en la biblioteca DoEasy (Parte 43): Las clases de los objetos de búferes de indicador


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

Archivos adjuntos |
MQL5.zip (3731.86 KB)
Aplicación práctica de las redes neuronales en el trading Aplicación práctica de las redes neuronales en el trading

En el presente artículo, analizaremos los momentos esenciales de la implementación de las redes neuronales y el terminal comercial para crear un robot comercial plenamente funcinal.

Discretización de series temporales con generación aleatoria de "ruidos" Discretización de series temporales con generación aleatoria de "ruidos"

Nos hemos acostumbrado a analizar el mercado con la ayuda de barras o velas que "hacen cortes" en la serie temporal a intervalos regulares de tiempo. Pero, ¿cuánto deforma realmente este método de discretización la estructura real de los movimientos de mercado? Discretizar una señal sonora a intervalos temporales iguales resulta una solución aceptable, porque una función sonora supone una función que cambia con el tiempo. En sí misma, una señal es una amplitud que depende del tiempo, y esta propiedad en ella es fundamental.

Aplicación práctica de las redes neuronales en el trading. Pasamos a la práctica Aplicación práctica de las redes neuronales en el trading. Pasamos a la práctica

En el presente artículo, ofrecemos la descripción y las instrucciones del uso práctico de los módulos de red neuronal en la plataforma Matlab. Asimismo, comentaremos los aspectos principales de la construcción de un sistema comercial con uso de modelos de redes neuronales (RN). Para que resulte más fácil familiarizarse con el complejo de elementos comprimidos para el presente artículo, hemos tenido que modernizarlo de forma que se puedan compatibilizar varias funciones del modelo de RN.

Conjunto de instrumentos para el marcado manual de gráficos y comercio (Parte I). Preparación - descripción de la estructura y clase de funciones auxiliares Conjunto de instrumentos para el marcado manual de gráficos y comercio (Parte I). Preparación - descripción de la estructura y clase de funciones auxiliares

En este artículo, comenzaremos a describir el conjunto para el marcado gráfico con la ayuda de atajos de teclado. Es un herramienta muy cómoda: con solo pulsar un botón, aparecerá una línea de tendencia, el abanico de Fibonacci con los parámetros necesarios, etcétera. Asimismo, tendremos la posibilidad de alternar marcos temporales, cambiar el orden de las "capas" de los objetos o eliminar todos los objetos de un gráfico.