Trabalhando com séries temporais na biblioteca DoEasy (Parte 44): classe-coleção de objetos de buffers de indicador

24 setembro 2020, 14:58
Artyom Trishkin
0
316

Sumário


Ideia

Desde o artigo 39 começamos a analisar como usar a biblioteca para criar indicadores personalizados. Até agora foram preparados objetos das séries temporais e suas coleções, bem como objetos de buffers de indicador — buffer abstrato base e buffers de indicador baseados nele.
Neste artigo, começaremos a criar uma coleção de buffers de indicador que nos permitirá não só criar rapidamente qualquer quantidade de buffers (até 512) num programa, mas também obtê-los e acessá-los de maneira conveniente para trabalhar com seus dados.

A ligação <"coleção de séries temporais> — <coleção de buffers"> nos permitirá criar quaisquer indicadores multissímbolos e multiperíodos. Começaremos a lidar com isso no próximo artigo. Hoje criaremos e testaremos uma coleção de buffers de indicador para criar qualquer quantidade de buffers no indicador com um dos nove estilos de plotagem. No momento, o número máximo possível de buffers plotados no indicador não pode ser superior a 512. Mas isso deve ser mais do que suficiente para criar indicadores complexos com um grande número de gráficos. Com a ajuda da funcionalidade criada, a criação e manutenção de tal número de construções gráficas será simplificada para uma simples chamada aos buffers criados por seu estilo de plotagem e ordem de criação, ou pelo índice do buffer na coleção.


Preparando dados e modificando objetos-buffers

No último artigo criamos objetos "buffers de indicador" herdeiros do buffer abstrato base. Vamos complementá-los com métodos auxiliares para acesso à escrita e leitura de dados em/desde matrizes double atribuídas como buffers de indicador.
Apesar de os métodos escritos da classe abstrata base bastarem, a criação nos objeto-herdeiros de métodos próprios inerentes apenas ao seu estilo de plotagem, por um lado, será necessária para um uso mais conveniente das suas matrizes double ao acessar o buffer com base no seu status (estilo de plotagem) e, por outro lado, dará mais flexibilidade ao criar indicadores próprios.

Adicionamos novas mensagens à biblioteca. No arquivo \MQL5\Include\DoEasy\Datas.mqh inserimos os índices das novas mensagens:

   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

e os textos das mensagens correspondentes aos índices recém-adicionados:

   {"Не удалось изменить размер массива цветов","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"},

O indicador pode usar no máximo 512 buffers.
Ao arquivo \MQL5\Include\DoEasy\Defines.mqh adicionamos uma substituição de macros indicando este tamanho:

//+------------------------------------------------------------------+
//| Macro substitutions                                              |
//+------------------------------------------------------------------+
//--- 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

Certamente é possível usar "512" como valor, mas a substituição da macro é mais conveniente porque se algum dia esse valor for aumentado pelos desenvolvedores, então, para fazer alterações no código, não será preciso procurar e corrigir esse valor para um novo em todos os arquivos onde houver uma referência a este valor, mas apenas alterar o valor da substituição da macro.

Precisamos pesquisar e selecionar objetos-buffers por propriedades que identificamos anteriormente como desnecessárias quanto a pesquisa e classificação.

Todas nossas propriedades não utilizadas na classificação estão sempre localizadas no final da enumeração da lista de propriedades, enquanto a substituição de macro indicando o número de propriedades não utilizadas na classificação já está definida. Para definir uma propriedade como classificável, é preciso movê-la do final da lista de enumeração de propriedades para mais perto das propriedades atualmente permitidas para classificação, e especificar um novo número de propriedades não utilizadas na classificação.
E, claro, é necessário complementar a lista de possíveis critérios de classificação com novas propriedades, aquelas que permitimos usar para classificação. A localização das propriedades recém-adicionadas na lista de enumeração de critérios de classificação deve corresponder à localização dessas propriedades na lista de enumeração de propriedades, na qual permitimos que essas propriedades sejam usadas na classificação.

Parece confuso, mas na prática tudo é simples. Abrimos o arquivo \MQL5\Include\DoEasy\Defines.mqh e fazemos as alterações necessárias:

Anteriormente nossas propriedades inteiras do objeto-buffer estavam na seguinte ordem:

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

Aqui precisamos habilitar duas propriedades para classificação. Para fazer isso, vamos colocá-las acima e alterar de 6 para 2 a quantidade de propriedades não usadas na classificação:

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

Aqui, indicamos que apenas duas propriedades no final da lista não participarão da classificação.
Adicionamos essas novas propriedades aos critérios de classificação:

//+------------------------------------------------------------------+
//| 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, sua localização na lista de enumeração de critérios de classificação coincide com a localização na enumeração de propriedades inteiras. Essa condição deve ser atendida (enumeração de critérios) ao mesmo tempo que a ordem das suas propriedades de classificação deve coincidir com a das propriedades na enumeração das propriedades do objeto. Discutimos isso ainda na terceira parte da descrição da biblioteca.

Como já começamos a falar sobre a classificação de objetos-buffers por suas propriedades, significa que é hora de preparar um conjunto de ferramentas para rea lizar a busca de objetos-buffers, o que é realizado com base nas suas propriedades. Já tornamos isso possível para todos os objetos de biblioteca armazenados em coleções. Agora escrevemos os métodos para encontrar os objetos-buffers.

Abrimos o arquivo \MQL5\Include\DoEasy\Services\Select.mqh e anexamos a ele o arquivo da classe-buffer:

//+------------------------------------------------------------------+
//|                                                       Select.mqh |
//|                        Copyright 2020, MetaQuotes Software Corp. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, MetaQuotes Software Corp."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#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"
//+------------------------------------------------------------------+

Na parte inferior do corpo da classe, após o bloco com a declaração de métodos para trabalhar com barras de séries temporais, escrevemos um bloco declarando métodos para trabalhar com buffers 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);
//---
  };
//+------------------------------------------------------------------+

E bem no final do arquivo vamos escrever todos os métodos declarados no corpo da classe:

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

Analisamos o trabalho da classe CSelect no terceiro artigo que descrevia a criação da biblioteca.

Vamos modificar ligeiramente as classes do buffer abstrato e seus herdeiros.

Como estamos fazendo pesquisa e classificação por propriedades de objetos-buffers,
anexamos o arquivo da classe CSelect ao arquivo da classe de buffer abstrato \MQL5\Include\DoEasy\Objects\Indicators\Buffer.mqh:

//+------------------------------------------------------------------+
//|                                                       Buffer.mqh |
//|                        Copyright 2020, MetaQuotes Software Corp. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, MetaQuotes Software Corp."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
#property strict    // Necessary for mql4
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "..\..\Services\Select.mqh"
#include "..\..\Objects\BaseObj.mqh"
//+------------------------------------------------------------------+

Agora a classe CSelect será visível na classe CBuffer em todos seus herdeiros.

Na seção pública da classe, escrevemos um método que define o nome personalizado para o objeto-buffer:

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

//--- Default constructor
                     CBuffer(void){;}
protected:

Atribuir um nome personalizado ao buffer permite nomear o buffer para sua posterior pesquisa na lista-coleção.

Temos um método que retorna o valor do buffer do índice da série temporal especificado, temos um método que retorna a cor definida para o buffer no índice da série temporal especificado, mas não temos nenhum método que retorne o índice de tal cor. Afinal, em essência, não definimos assim o valor de cor para o buffer. No buffer definimos o índice de cor, um número que indica qual das cores atribuídas ao buffer de cores deve ser usada para pintar a linha na posição especificada da série temporal.

Vamos corrigir esse descuido e, para tal, declaramos mais um método que retorna o índice da cor definida para o buffer na posição da série temporal especificada, e renomeamos o método que retorna a cor de buffer na posição especificada da série temporal de GetColorBufferValue() para GetColorBufferValueColor():

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

Agora temos dois métodos declarados que trabalham com a cor do buffer. Um retorna a cor, o outro, o índice da cor.

Fora do corpo da classe, escrevemos a implementação do método que retorna o índice de cor e corrigimos a implementação do método que retorna a cor do buffer:

//+------------------------------------------------------------------+
//| 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, tínhamos apenas um método e recebíamos o índice da cor diretamente dentro dele:

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

Agora, este cálculo foi movido para um método GetColorBufferValueIndex() separado, e no método de retorno da cor da barra, em vez de calcular o índice, usamos a chamada deste novo método.

Ao criar a classe do objeto de buffer de seta, que (como todas as outras classes de buffer) é herdeira do buffer abstrato, tive um descuido, quer dizer, na classe CBuffer foram declarados métodos virtual para definir o código das setas e seus deslocamentos, mas esqueci sua implementação na classe herdada. Vamos consertar a situação.

Abrimos o arquivo da classe do objeto do buffer de setas \MQL5\Include\DoEasy\Objects\Indicators\BufferArrow.mqh e declaramos estes métodos. Ao mesmo tempo, adicionamos mais dois métodos para definir e retornar os valores para/desde a matriz atribuída pelo buffer do 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);  }
   
  };
//+------------------------------------------------------------------+

Fora do corpo da classe, escrevemos uma implementação dos métodos para definir o código das setas e seu deslocamento:

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

O métodos registram o valor passado para eles de acordo com a propriedade do objeto-buffer e
definem esta propriedade para o buffer plotado do objeto-buffer
.

Nos arquivos dos objetos-buffers com um buffer para plotagem, isto é: linhas (BufferLine.mqh), segmentos (BufferSection.mqh) histogramas de zero (BufferHistogram.mqh), também inserimos dois métodos para definir/retornar os valores em/desde a matriz do buffer de dados do objeto-buffer:

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

Aos arquivos dos objetos-buffers com dois buffers para plotagem, isto é: histogramas em duas matrizes de dados (BufferHistogram2.mqh), zigzag (BufferZigZag.mqh) e preenchimento entre duas matrizes de dados(BufferFilling.mqh), inserimos quatro métodos para definir/retornar valores em/desde matrizes de buffers de dados do objeto-buffer:

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

Aos arquivos dos objetos-buffers com quatro buffers para plotagem, isto é: barras (BufferBars.mqh) e velas (BufferCandlts.mqh), inserimos oito métodos para definir/retornar valores OHLC em/desde matrizes de buffers de dados do objeto-buffer:

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

Talvez os métodos de buffer-objeto abstrato base SetBufferValue() e GetBufferValue() bastariam, mas esses métodos requerem especificação do número do buffer necessário. Porém, fazemos o possível para facilitar o trabalho do usuário final. Portanto, teremos uma escolha de qual método usar a partir dos propostos.

Estamos prontos para criar uma classe-coleção de objetos de buffer de indicador.

Essa classe terá uma lista contendo todos os objetos de buffer criados e fornecerá a capacidade de criar e receber qualquer buffer para trabalhar com ele no programa. Ao contrário das classes da coleção anterior, aqui, ao adicionar outro objeto buffer, não precisaremos verificar a presença do mesmo objeto com exatamente as mesmas propriedades, pois podemos usar buffers absolutamente idênticos no indicador para visualizar eventos diferentes.

Classe-coleção de objetos-buffers

Na pasta \MQL5\Include\DoEasy\Collections\ criamos um novo arquivo BuffersCollection.mqh com classe base CObject da biblioteca padrão, e imediatamente anexamos a ela os arquivos das classes da lista base da biblioteca e dos objetos-buffers:

//+------------------------------------------------------------------+
//|                                            BuffersCollection.mqh |
//|                        Copyright 2020, MetaQuotes Software Corp. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, MetaQuotes Software Corp."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#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
  {

Preencheremos o corpo da classe com todo o conteúdo necessário (felizmente, não há muito), e log consideraremos o propósito de tudo que é necessário:

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

Bem, m_list é uma lista na qual iremos "reunir" e armazenar todos os objetos-buffers criados. É uma herdeira de uma classe de matriz dinâmica de ponteiros para instâncias da classe CObject.

O método privado que retorna o índice do próximo buffer plotado GetIndexNextPlot() é necessário para especificar o índice do próximo buffer de indicador a ser criado, e o método (também privado) que retorna o índice do próximo buffer base GetIndexNextBase() é necessário para indicar o índice de uma matriz real, que pode ser atribuída como um buffer de indicador para um objeto de buffer recém-criado.

Deixe-me explicar. Ao criar um buffer para o indicador, atribuímos a ele um número na janela de dados (o número do buffer plotado) e o associamos com a matriz double (índice do array base do buffer). Por que "base"? É que apenas um buffer para plotagem pode usar várias matrizes. A primeira matriz atribuída como indicador será a base. As matrizes restantes usadas para renderização terão o índice "matriz base"+N.

Assim, para três buffers coloridos construídos em duas matrizes, os índices do plotado e do base ficará assim:

Primeiro buffer:

  • Buffer plotado — índice 0
    • Buffer base — índice 0
    • Segundo buffer — índice 1
    • Matriz de cor — índice 2

Segundo buffer:

  • Buffer plotado — índice 1
    • Matriz base — índice 3
    • Segunda matriz — índice 4
    • Matriz de cor — índice 5

Terceiro buffer:

  • Buffer plotado — índice 2
    • Matriz base — índice 6
    • Segunda matriz — índice 7
    • Matriz de cor — índice 8

Como podemos ver, a indexação do buffer plotado e da matriz base é diferente para cada buffer, portanto, com um número suficientemente grande de buffers usados no indicador, podemos facilmente ficar confusos sobre o que se refere a quê. A classe-coleção atribuirá automaticamente os índices corretos para buffers plotados e suas matrizes. Consequentemente, sempre podemos acessá-los a partir do nosso programa.

O método CreateBuffer() cria um novo buffer e o coloca na lista de coleção.
Os métodos GetObject() e GetList() retornam ponteiros para o objeto da classe-coleção e para a lista de objetos-buffers da classe-coleção, respectivamente .
Os métodos PlotsTotal() e BuffersTotal() retornam o número de buffers plotados criados na coleção e o número total de matrizes usadas para construir todos os buffers plotados, respectivamente.

Métodos públicos para criar objetos-buffers com um estilo de plotagem específico:

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

Os métodos retornam o resultado do método privado para criar um objeto-buffer CreateBuffer() com um estilo de plotagem específico.

O método GetBufferByPlot() retorna um ponteiro para o buffer com base em seu índice de buffer plotado.

Métodos que retornam ponteiros para objetos-buffers por seu 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);

Retornam um objeto com um estilo de plotagem específico por seu número na ordem de criação.
Vou explicar com um exemplo:

Criamos quatro buffers de seta BufferArrow() com índices de buffer plotado 0, 1, 2 e 3.
Em seguida, criamos cinco buffers de linha BufferLine() com índices de buffer plotado 4, 5, 6, 7 e 8.

Agora precisamos trabalhar com o terceiro buffer de seta (que está no índice 2) e o quarto buffer de linha (no índice 7).
Para obter um ponteiro para o terceiro buffer de seta, basta obtê-lo pelo ordinal (não pelo índice, mas, sim, pelo número). O número deve ser calculado desde zero. Ou seja, para obter o terceiro buffer de setas, devemos obtê-lo assim:

CBufferArrow *buffer_arrow=GetBufferArrow(2); // terceiro buffer de setas (0,1,2)

Para obter um ponteiro para o quarto buffer de linha, devemos obtê-lo assim:

CBufferLine *buffer_line=GetBufferLine(3); // the fourth line buffer (0,1,2,3)


Agora, vamos examinar a implementação de todos os métodos declarados.

Construtor de classe:

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

Limpamos a lista, definimos o sinalizador de lista classificada para a lista e definimos o tipo de coleção para o identificador de lista da coleção de buffers de indicador.

Método que retorna o índice do próximo buffer plotado e método que retorna o índice do próximo buffer base:

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

A lógica desses dois métodos é idêntica e eu a descrevi nos comentários às linhas de código.

Método que cria um novo objeto-buffer e o coloca numa lista-coleção:

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

Aqui, também, nos comentários é descrita a lógica. Observe que estamos declarando um objeto do buffer abstrato CBuffer, mas criamos um novo objeto com o tipo de plotagem passado para o método pelo status (nosso status apenas descreve o estilo de plotagem). Acontece que todos os objetos-buffer são herdeiros de um objeto-buffer abstrato, por isso, tal declaração e criação de objeto é permissível e conveniente.

Método que retorna o buffer pelo seu índice Plot (pelo índice na janela 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);
  }
//+------------------------------------------------------------------+

Com ajuda da classe CSelect, obtemos uma lista contendo apenas o objeto-buffer com o índice especificado (haverá apenas um objeto na lista), e retornamos um objeto-buffer da lista resultante, se encontrado, ou NULL , se não houver tal objeto na lista-coleção.

Métodos que retornam objetos-buffers de um tipo específico:

//+------------------------------------------------------------------+
//| 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 os métodos são idênticos entre si, por isso, vamos considerar um.
Obtemos uma lista contendo apenas objetos-buffers com o estilo de plotagem desejado.
Se a lista for recebida e não estiver vazia, retornamos um objeto desde a lista resultante com base no índice especificado.
Os objetos na lista são organizados em ordem crescente de índice, portanto, não é necessário nenhum ajuste de índice.
Se o índice estiver fora da lista, o método At() da classe CArrayObj retornará NULL.
Se a lista não for recebida ou estiver vazia, retornamos NULL.

Métodos que retornam o número de buffers plotados e o número de todas as matrizes 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);
  }
//+------------------------------------------------------------------+

A lógica dos métodos é a mesma: obtemos o índice com o maior valor da propriedade desejada, e com base no índice recebido, obtemos um objeto-buffer desde a lista-coleção. Se o buffer for recebido, retornamos sua propriedade, em conformidade com o método, caso contrário, retornamos -1.

Assim fica concluída a criação da classe-coleção de buffers de indicador.

Agora precisamos fornecer acesso aos métodos de classe para programas baseados na biblioteca. Isso é feito em nossa classe do objeto base da biblioteca CEngine.

Abrimos o arquivo \MQL5\Include\DoEasy\Engine.mqh e fazemos as alterações necessárias: Aqui, praticamente só precisamos duplicar os métodos já criados da classe da coleção de buffers de indicador e adicionar métodos auxiliares para facilitar o trabalho.

Primeiramente anexamos o arquivo da classe e declaramos um objeto da classe-coleção de buffers:

//+------------------------------------------------------------------+
//|                                                       Engine.mqh |
//|                        Copyright 2020, MetaQuotes Software Corp. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, MetaQuotes Software Corp."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#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

Na seção pública da classe escrevemos métodos que chamam e retornam o resultado do trabalho dos métodos com o mesmo nome da classe-coleção de buffers,
e declaramos métodos adicionais para trabalhar com a classe de coleção de buffers:

//--- 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 retornam dados para um buffer específico com base no seu estilo de plotagem e número ordinal do buffer com o seguinte estilo plotagem:

//+------------------------------------------------------------------+
//| 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 os métodos são idênticos. Consideremos um método que retorna o valor do buffer Close de um objeto-buffer com estilo de plotagem "Vela".
Ao método é passado o número ordinal do buffer com o estilo Candles a partir de todos os buffers criados com o estilo Candles (acima, examinamos em detalhes o que significa o número do buffer com um estilo de plotagem específico), o índice da série temporal a partir do qual devem ser obtidos os dados do buffer Close da vela.

Com ajuda do método GetBufferCandles() da classe-coleção de buffers obtemos o ponteiro para o buffer necessário, e se o buffer for obtido, retornamos os dados desde seu buffer Close com base no índice de série temporal especificado. Caso contrário, retornamos "Valor vazio".

Métodos, opostos aos que acabamos de discutir, que definem valores para o índice de série temporal especificado para um buffer específico do objeto-buffer correspondente de acordo com seu estilo de plotagem e seu 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 os métodos são idênticos. Consideremos o método que define o valor pelo índice da série temporal para o buffer Close do objeto-buffer com estilo de plotagem "Vela" com base no seu número ordinal.
Com ajuda do método GetBufferCandles() da classe-coleção de buffers obtemos o objeto-buffer com estilo de plotagem "Vela" com base no seu número ordinal.
Se o objeto não for obtido, saímos do método. Definimos com base no índice da série temporal o valor passado para o método no buffer Close do objeto-buffer necessário recebido.

Existem mais dois métodos separados que definem simultaneamente para todos os buffers dos objetos-buffer "Barras" e "Velas" os valores OHLC com base no índice da série temporal especificado:

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

Aqui tudo é exatamente igual ao descrito acima, mas aos métodos são transferidos todos os valores para todos os quatro buffers de objetos-buffers e são configurados.

Métodos que retornam a cor definida para o índice da série temporal especificada do buffer de cor de um objeto-buffer específico com base no seu estilo de plotagem e número de sequência:

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

Aqui tudo é igual: obtemos o objeto-buffer necessário com base no seu número, se conseguirmos obter o objeto, retornamos a cor definida no seu buffer de cor pelo índice de série temporal especificado. Caso contrário, retornamos "Cor não definida".

Como realmente o que é escrito no buffer de cor não é o valor de cor, mas, sim, seu índice a partir das cores definidas para o buffer, temos
os métodos correspondente que retornam o índice de cor de um objeto-buffer específico a partir de seu buffer de cor no índice de série temporal especificado:

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

Tudo aqui é idêntico aos métodos que retornam cor, exceto que os métodos retornam o índice de cor.

Essas são todas as modificações da classe CEngine necessárias para testar a classe-coleção de buffers de indicador.


Testando a criação de um indicador com uso da coleção de buffers

Para testar a classe-coleção de buffers criada, usamos o indicador do último artigo
e o salvamos na nova pasta \MQL5\Indicators\TestDoEasy\Part44\ com o novo nome TestDoEasyPart44.mq5.

Todo o nosso cabeçalho ficará assim:

//+------------------------------------------------------------------+
//|                                             TestDoEasyPart44.mq5 |
//|                        Copyright 2020, MetaQuotes Software Corp. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, MetaQuotes Software Corp."
#property link      "https://mql5.com/en/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
//+------------------------------------------------------------------+

Do bloco includes removemos o anexo de arquivo de todos os objetos-buffers - agora eles já estão anexados à biblioteca e não há necessidade de fazer isso aqui:

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

Dizendo ao compilador quantos buffers plotados e de indicador estão planejados:

#property indicator_buffers 28
#property indicator_plots   10

Aqui, se for assumido uma grande quantidade de buffers no indicador, então, na etapa inicial poderemos, em vez de contar com os dedos, apresentar quaisquer valores, assim, em seguida, na primeira inicialização sugiram alertas indicando o valor certo de buffers plotados e de indicador, caso nos enganemos ao escrever sua quantidade.

A biblioteca assume o acesso aos buffers de indicadores criados por seus estilos de desenho e números na ordem de criação. Até agora não fizemos um acesso desde o programa diretamente às propriedades do buffer (apenas às suas matrizes), porém, hoje vamos pular essa parte, após obter a lista de objetos-buffers desde a coleção e acessar os buffers diretamente desde a lista; isso pode ser feito para qualquer um dos objetos da coleção, trabalhar diretamente com os objetos criados. Por isso, como "buffer de indicador" vamos usar uma matriz dinâmica de ponteiros para o objeto CObject.


No manipulador OnInit() preparamos os buffers necessários para teste e verificamos o acesso de duas maneiras diferentes:

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

Agora a criação de buffers acontece de maneira simples — usamos os métodos da biblioteca projetados para criar um buffer de indicador de determinado tipo. A biblioteca é responsável por todas as atribuições das matrizes, já só podemos alterar as propriedades dos buffers criados após sua criação. Imediatamente verificamos o número especificado de buffers em #property no cabeçalho do indicador, e se cometermos um erro ao especificar o número de buffers, receberemos um aviso sobre isso. Essa verificação é conveniente ao criar o indicador e, depois dela, pode ser removida do código.

Para verificar o acesso ao buffer, usaremos dois métodos:
primeiro acessamos o buffer do zigzag pelo seu índice de buffer plotado com ajuda do método GetBufferByPlot(), em que é necessário especificar o índice de buffer plotado (neste caso, é o índice 5 para o zigzag),
em seguida acessamos o último buffer de setas, que foi criado no final, e que é o segundo buffer de setas. Iremos acessá-lo usando o método GetBufferArrow(), em que é necessário especificar o número de sequência do buffer de setas desejado (neste caso, número 1, já que a contagem começa do zero)

No manipulador OnCalculate() tudo permanece praticamente inalterado, exceto que nos buffers das velas e barras gravaremos os dados com ajuda dos métodos do objeto-buffer de velas (alternativamente escrevemos os dados e Open, High, Low e Close), já nas matrizes do buffer de barras escrevemos todos os valores OHLC de uma vez. Assim, verificamos o funcionamento de todos os métodos criados para trabalhar com objetos-buffers:

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

O código completo do indicador pode ser encontrado nos arquivos anexados ao artigo.

Vamos compilar o indicador e executá-lo no gráfico do símbolo. Vamos configurá-lo para exibir apenas um buffer de seta. Este buffer deve ser exibido como pontos no gráfico. Mas também temos um segundo buffer de setas criado mais recentemente, e acessamos o segundo buffer de setas em OnInit() para alterar seu código e tamanho de ícone:

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

Se a obtenção de um objeto acessando um tipo específico de buffer por seu número funcionar, nosso gráfico deverá exibir dois buffers de setas, o primeiro com pontos e o segundo com círculos de tamanho 2.

Definimos a largura da linha zagzag após receber o buffer do zigzag usando o método que retorna o objeto-buffer por seu índice Plot. Anexamos a exibição de zigzag e certificamo-nos de que a espessura de sua linha corresponda àquela configurada em 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);
     }

E, finalmente, vamos ver como são exibidas as barras e velas, se os métodos para escrever valores de preços em matrizes de buffer funcionarem, as barras e velas deveram ser exibidas corretamente no gráfico.

Vamos checar:


Como podemos ver, tudo que foi concebido e feito funciona conforme o esperado.


O que vem agora?

No próximo artigo, continuaremos o desenvolvimento da classe-coleção de buffers de indicador do ponto de vista da organização da operação do indicador nos modos multissímbolo e multiperíodo.

Abaixo estão anexados todos os arquivos da versão atual da biblioteca e os arquivos do EA de teste. Você pode baixá-los e testar tudo sozinho.
Se você tiver dúvidas, comentários e sugestões, pode expressá-los nos comentários do artigo.
Gostaria de chamar sua atenção para o fato de que neste artigo fizemos um indicador de teste em MQL5 para MetaTrader 5.
Os arquivos anexados estão destinados apenas ao MetaTrader 5 e a versão atual da biblioteca ainda não foi testada no MetaTrader 4.
Depois de criar uma coleção de buffers de indicador e testá-la, tentaremos implementar algumas coisas do MQL5 para MetaTrader 4.

Complementos

Artigos desta série:

Trabalhando com séries temporais na biblioteca DoEasy (Parte 35): Objeto "Barra" e lista-série temporal do símbolo
Trabalhando com séries temporais na biblioteca DoEasy (Parte 36): objeto das séries temporais de todos os períodos usados do símbolo
Trabalhando com séries temporais na biblioteca DoEasy (Parte 37): coleção de séries temporais - banco de dados de séries temporais para símbolos e períodos
Trabalhando com séries temporais na biblioteca DoEasy (Parte 38): coleção de séries temporais - atualização em tempo real e acesso aos dados do programa
Trabalhando com séries temporais na biblioteca DoEasy (Parte 39): indicadores com base na biblioteca - preparação de dados e eventos das séries temporais
Trabalhando com séries temporais na biblioteca DoEasy (Parte 40): indicadores com base na biblioteca - atualização de dados em tempo real
Trabalhando com séries temporais na biblioteca DoEasy (Parte 41): exemplo de indicador multissímbolo multiperíodo
Trabalhando com séries temporais na biblioteca DoEasy (Parte 42): classe de um objeto de buffer abstrato de indicador
Trabalhando com séries temporais na biblioteca DoEasy (Parte 43): classes de objetos de buffers de indicador


Traduzido do russo pela MetaQuotes Software Corp.
Artigo original: https://www.mql5.com/ru/articles/7886

Arquivos anexados |
MQL5.zip (3731.86 KB)
Como escrever um cliente nativo Twitter para MetaTrader: 2º parte Como escrever um cliente nativo Twitter para MetaTrader: 2º parte

Vamos implementar o cliente Twitter como uma classe MQL que nos permitirá enviar tweets com imagens. Depois de anexar apenas um arquivo include autônomo, poderemos publicar tweets e colocar nossos gráficos e sinais.

Como escrever um cliente nativo Twitter para MetaTrader 4 e MetaTrader 5 sem usar DLL Como escrever um cliente nativo Twitter para MetaTrader 4 e MetaTrader 5 sem usar DLL

Quer receber tweets ou postar seus sinais de negociação no Twitter? Você já não precisará procurar soluções, já que nesta série de artigos, veremos como trabalhar com o Twitter sem usar uma DLL. Juntos implementaremos a Tweeter API usando MQL. No primeiro artigo, começaremos com os recursos de autenticação e autorização da Twitter API.

Luta pela velocidade: QLUA vs MQL5 - por que o MQL5 é 50 a 600 vezes mais rápido? Luta pela velocidade: QLUA vs MQL5 - por que o MQL5 é 50 a 600 vezes mais rápido?

Para comparar as linguagens MQL5 e QLUA, escrevemos vários testes que medem a velocidade de execução de operações básicas. Nos testes, usamos um computador com Windows 7 Professional 64 bits, MetaTrader 5 build 1340 e QUIK versão 7.2.0.45.

Métodos para localizar zonas de sobrecompra/sobrevenda. Parte I Métodos para localizar zonas de sobrecompra/sobrevenda. Parte I

As zonas de sobrecompra/sobrevenda caracterizam uma determinada situação do mercado que se distingue por um enfraquecimento da dinâmica dos preços dos instrumentos financeiros. Além disso, essa mudança negativa da dinâmica é mais pronunciada na parte final da tendência, independentemente do tamanho desta última. E como o lucro depende diretamente da capacidade de cobrir a máxima amplitude da tendência, identificar com precisão essas zonas é o mais importante ao negociar qualquer instrumento financeiro.