Trabalhando com séries temporais na biblioteca DoEasy (Parte 43): classes de objetos de buffers de indicador

11 setembro 2020, 09:59
Artyom Trishkin
0
579

Sumário


Ideia

No último artigo, criamos um objeto de buffer de indicador abstrato que continha algumas propriedades comuns de buffers de indicador e apresentava métodos para trabalhar com eles. Hoje vamos criar objetos que derivam da classe de objeto de buffer abstrato. Cada um desses objetos será um objeto buffer de indicador independente com seu próprio tipo único de plotagem.
Como mencionamos anteriormente, todos os buffers de indicador serão coloridos e, se for necessária uma única cor, inicialmente os buffers serão simplesmente criados com uma cor. Também será possível definir o número de cores usadas pelo buffer durante a operação do indicador.

Para começar, vamos adicionar novos textos para as mensagens da biblioteca, textos esses necessários para o trabalho.
Abrimos o arquivo \MQL5\Include\DoEasy\Datas.mqh e inserimos os índices das novas mensagens:

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

...

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

...

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

Em seguida, inserimos os novos textos das mensagens correspondendo aos índices recém-adicionados:

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

...

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

...

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

No arquivo \MQL5\Include\DoEasy\Defines.mqh, à seção de substituição de macro inserimos uma macro indicando o número máximo possível de cores de buffer de indicador que pode ser definido:

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

Apenas por conveniência, de modo a não inserir uma verificação de se o número de cores especificadas é mais de 64. Especialmente considerando seu possível aumento no futuro.

Agora vamos modificar ligeiramente a classe do objeto do buffer abstrato.
No momento, nossos buffers de indicador estão na seção pública da classe em matrizes com um tipo de dados real, para o buffer de dados e o buffer de índice de cor. Vamos colocá-los numa seção protegida. Como pode haver 1, 2 e 4 buffers para construir o indicador, criaremos uma estrutura de buffers contendo uma matriz.
Por que isso é necessário?
Uma vez que apenas uma matriz unidimensional de dados-double pode atuar como buffer do indicador, então, para criar a matriz dos buffers (pode haver mais de um), criaremos uma estrutura com uma única matriz. Nesse momento, na matriz de tais estruturas, teremos todas as matrizes de dados necessárias - buffers do indicador - para construir o indicador. Eles serão acessados usando o índice da matriz requerida.
Para não ultrapassar os limites de tal matriz, passando acidentalmente o índice errado, criaremos um método que corrige o índice de matriz passado, caso o índice aponte para fora da matriz. Assim, o índice será ajustado e apontará para o último elemento da matriz das estruturas. No caso de uma única matriz, o índice apontará para ela.

No arquivo do objeto do buffer abstrato \MQL5\Include\DoEasy\Objects\Indicators\Buffer.mqh na seção privada, declaramos o método que retorna o índice corrigido da matriz do buffer,
enquanto na seção privada declaramos a estrutura de matrizes-buffers, o objeto-matriz de buffers com esse tipo de estrutura, a matriz de buffer de cor e a matriz de cores usadas para colorir a plotagem do indicador:

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

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

Da seção pública da classe removemos as agora desnecessárias matrizes de dados e de cores:

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

No artigo anterior, para testar a operação do objeto do buffer abstrato, tornamos público seu construtor protegido para que pudéssemos acessá-lo a partir do programa-indicador. Agora vamos fechá-lo, porque ele será acessado a partir de objetos-herdeiros desta classe ao criar buffers de indicador do tipo especificado. Somente descomentamos a string e adicionamos ao construtor protegido mais algumas variáveis necessárias (para esclarecer as propriedades do buffer criado), nomeadamente o número de buffers usados para construção, a espessura de linha e a descrição de buffer na janela de dados:

protected:
//--- Protected parametric constructor
                     CBuffer(ENUM_BUFFER_STATUS status_buffer,
                             ENUM_BUFFER_TYPE buffer_type,
                             const uint index_plot,
                             const uint index_base_array,
                             const int num_datas,
                             const int width,
                             const string label);
public:  

Agora temos um método para definir uma única cor para os buffers de indicador SetColor(). Mas, como todos os nossos buffers são coloridos, precisamos adicionar uma maneira de definir não apenas uma cor para todo o buffer, mas também adicionar uma nova cor aos existentes ou especificar todas as cores usadas pelo buffer de uma vez.
Vamos declarar dois métodos adicionais para definir a cor:

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

O primeiro dos métodos adicionados adicionará à matriz de cores do objeto-buffer uma nova cor segundo o índice especificado para a nova cor, enquanto o segundo método definirá imediatamente todas as cores usadas pelo objeto-buffer, passadas para o método na matriz.

No bloco de métodos de retorno de valores de propriedades de objeto-buffer renomeamos o método NumberColors() que retorna o número de cores usadas pelo buffer, e adicionamos um método que retorne o número de matrizes-buffer usadas para renderizar dados do objeto-buffer:

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

No bloco de métodos que retornam uma descrição de propriedades de objeto-buffer, adicionamos um método que retorna uma descrição das cores de renderização do buffer especificadas:

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

E no final do corpo da classe adicionamos um bloco de métodos para trabalhar com um objeto-buffer:

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

Vamos considerar a implementação desses métodos a seguir.

O construtor da classe paramétrica privada teve algumas mudanças:

//+------------------------------------------------------------------+
//| Closed parametric constructor                                    |
//+------------------------------------------------------------------+
CBuffer::CBuffer(ENUM_BUFFER_STATUS buffer_status,
                 ENUM_BUFFER_TYPE buffer_type,
                 const uint index_plot,
                 const uint index_base_array,
                 const int num_datas,
                 const int width,
                 const string label)
  {
   this.m_type=COLLECTION_BUFFERS_ID;
//--- Save integer properties
   this.m_long_prop[BUFFER_PROP_STATUS]                        = buffer_status;
   this.m_long_prop[BUFFER_PROP_TYPE]                          = buffer_type;
   ENUM_DRAW_TYPE type=
     (
      !this.TypeBuffer() || !this.Status() ? DRAW_NONE      : 
      this.Status()==BUFFER_STATUS_FILLING ? DRAW_FILLING   : 
      ENUM_DRAW_TYPE(this.Status()+8)
     );
   this.m_long_prop[BUFFER_PROP_DRAW_TYPE]                     = type;
   this.m_long_prop[BUFFER_PROP_TIMEFRAME]                     = PERIOD_CURRENT;
   this.m_long_prop[BUFFER_PROP_ACTIVE]                        = true;
   this.m_long_prop[BUFFER_PROP_ARROW_CODE]                    = 0x9F;
   this.m_long_prop[BUFFER_PROP_ARROW_SHIFT]                   = 0;
   this.m_long_prop[BUFFER_PROP_DRAW_BEGIN]                    = 0;
   this.m_long_prop[BUFFER_PROP_SHOW_DATA]                     = (buffer_type>BUFFER_TYPE_CALCULATE ? true : false);
   this.m_long_prop[BUFFER_PROP_SHIFT]                         = 0;
   this.m_long_prop[BUFFER_PROP_LINE_STYLE]                    = STYLE_SOLID;
   this.m_long_prop[BUFFER_PROP_LINE_WIDTH]                    = width;
   this.m_long_prop[BUFFER_PROP_COLOR_INDEXES]                 = (this.Status()!=BUFFER_STATUS_FILLING ? 1 : 2);
   this.m_long_prop[BUFFER_PROP_COLOR]                         = clrRed;
   this.m_long_prop[BUFFER_PROP_NUM_DATAS]                     = num_datas;
   this.m_long_prop[BUFFER_PROP_INDEX_PLOT]                    = index_plot;
   this.m_long_prop[BUFFER_PROP_INDEX_BASE]                    = index_base_array;
   this.m_long_prop[BUFFER_PROP_INDEX_COLOR]                   = this.GetProperty(BUFFER_PROP_INDEX_BASE)+this.GetProperty(BUFFER_PROP_NUM_DATAS);
   this.m_long_prop[BUFFER_PROP_INDEX_NEXT]                    = this.GetProperty(BUFFER_PROP_INDEX_COLOR)+(this.Status()!=BUFFER_STATUS_FILLING ? 1 : 0);
   
//--- Save real properties
   this.m_double_prop[this.IndexProp(BUFFER_PROP_EMPTY_VALUE)] = EMPTY_VALUE;
//--- Save string properties
   this.m_string_prop[this.IndexProp(BUFFER_PROP_SYMBOL)]      = ::Symbol();
   this.m_string_prop[this.IndexProp(BUFFER_PROP_LABEL)]       = (this.TypeBuffer()>BUFFER_TYPE_CALCULATE ? label : NULL);

//--- If failed to change the size of the drawn buffer array, display the appropriate message indicating the string
   if(::ArrayResize(this.DataBuffer,(int)this.GetProperty(BUFFER_PROP_NUM_DATAS))==WRONG_VALUE)
      ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_SYS_FAILED_DRAWING_ARRAY_RESIZE),". ",CMessage::Text(MSG_LIB_SYS_ERROR),": ",(string)::GetLastError());
      
//--- If failed to change the size of the color array, display the appropriate message indicating the string
   if(::ArrayResize(this.ArrayColors,(int)this.ColorsTotal())==WRONG_VALUE)
      ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_SYS_FAILED_COLORS_ARRAY_RESIZE),". ",CMessage::Text(MSG_LIB_SYS_ERROR),": ",(string)::GetLastError());

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

//--- Bind indicator buffers with arrays
//--- In a loop by the number of drawn buffers
   int total=::ArraySize(DataBuffer);
   for(int i=0;i<total;i++)
     {
      //--- calculate the index of the next array and
      //--- bind the indicator buffer by the calculated index with the dynamic array
      //--- located by the i loop index in the DataBuffer array
      int index=(int)this.GetProperty(BUFFER_PROP_INDEX_BASE)+i;
      ::SetIndexBuffer(index,this.DataBuffer[i].Array,INDICATOR_DATA);
      //--- Set indexation flag as in the timeseries to all buffer arrays
      ::ArraySetAsSeries(this.DataBuffer[i].Array,true);
     }
//--- Binding the color buffer with the array
   if(this.Status()!=BUFFER_STATUS_FILLING)
     {
      ::SetIndexBuffer((int)this.GetProperty(BUFFER_PROP_INDEX_COLOR),this.ColorBufferArray,INDICATOR_COLOR_INDEX);
      ::ArraySetAsSeries(this.ColorBufferArray,true);
     }

//--- Set integer buffer parameters
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_DRAW_TYPE,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_DRAW_TYPE));
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_ARROW,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_ARROW_CODE));
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_ARROW_SHIFT,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_ARROW_SHIFT));
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_DRAW_BEGIN,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_DRAW_BEGIN));
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_SHOW_DATA,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_SHOW_DATA));
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_SHIFT,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_SHIFT));
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LINE_STYLE,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_LINE_STYLE));
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LINE_WIDTH,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_LINE_WIDTH));
   this.SetColor((color)this.GetProperty(BUFFER_PROP_COLOR));

//--- Set real buffer parameters
   ::PlotIndexSetDouble((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_EMPTY_VALUE,this.GetProperty(BUFFER_PROP_EMPTY_VALUE));
//--- Set string buffer parameters
   ::PlotIndexSetString((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LABEL,this.GetProperty(BUFFER_PROP_LABEL));
  }
//+------------------------------------------------------------------+

Definição do tipo de buffer:

   ENUM_DRAW_TYPE type=
     (
      !this.TypeBuffer() || !this.Status() ? DRAW_NONE      : 
      this.Status()==BUFFER_STATUS_FILLING ? DRAW_FILLING   : 
      ENUM_DRAW_TYPE(this.Status()+8)
     );
   this.m_long_prop[BUFFER_PROP_DRAW_TYPE]                     = type;

Para determinar o tipo de plotagem, temos o método SetDrawType(), descrito em detalhes no último artigo:

Todos os nossos buffers serão coloridos. Por isso, para definir o estilo de plotagem, verificamos o status do buffer e o tipo de plotagem passados para o método SetDrawType(), e, dependendo deles, quer não definimos nenhuma plotagem, quer preenchemos com cor o espaço entre dois níveis, quer deslocamos o índice de enumeração de status em 8 unidades para que o valor da constante corresponda ao valor da do buffer de cor a partir da enumeração de estilos de plotagem.
Fora do corpo da classe vamos escrever a implementação deste método:

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

Se o tipo de buffer for BUFFER_TYPE_CALCULATE (valor 0) ou o status do buffer for BUFFER_STATUS_NONE (valor 0), o estilo de plotagem será definido como "Sem plotagem", se o status do buffer for BUFFER_STATUS_FILLING (preenchimento com cor), o estilo de plotagem será definido como corresponder. Todos os outros valores são simplesmente incrementados em 8, é este deslocamento que apontará para a constante de estilo de plotagem de cor.

Aqui, no construtor da classe, usamos a mesma maneira de calcular o tipo de buffer.
Por enquanto, o método SetDrawType() permanece na classe, mas provavelmente iremos removê-lo por completo, já que ele só é necessário ao criar um objeto e, portanto, não faz sentido alterar o tipo de plotagem.
Ele ficará temporariamente no caso de encontrarmos em breve alguma outra maneira de usá-lo.


A largura da linha agora é passada nos parâmetros do construtor, por isso o valor passado para o construtor é definido como o padrão, e
o número de cores por padrão para todos os tipos de construções gráficas é definido como igual a um, excluindo DRAW_FILLING
, porque este tipo de renderização sempre usa duas cores que vamos definir:

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

O número de buffers de dados para plotagem também é passado nos parâmetros do construtor, também vamos inseri-los:

   this.m_long_prop[BUFFER_PROP_NUM_DATAS]                     = num_datas;
   this.m_long_prop[BUFFER_PROP_INDEX_PLOT]                    = index_plot;
   this.m_long_prop[BUFFER_PROP_INDEX_BASE]                    = index_base_array;
   this.m_long_prop[BUFFER_PROP_INDEX_COLOR]                   = this.GetProperty(BUFFER_PROP_INDEX_BASE)+this.GetProperty(BUFFER_PROP_NUM_DATAS);
   this.m_long_prop[BUFFER_PROP_INDEX_NEXT]                    = this.GetProperty(BUFFER_PROP_INDEX_COLOR)+(this.Status()!=BUFFER_STATUS_FILLING ? 1 : 0);

Ao calcular o índice da próxima matriz e atribui-la como a primeira matriz-buffer de dados do seguinte objeto-buffer, precisamos levar em conta que no buffer com plotagem do tipo DRAW_FILLING não é usado buffer de cor. Por isso, para um buffer com esse tipo de plotagem, é necessário excluirmos do cálculo o buffer de cor — para todos os tipos de plotagem, adicionamos um ao valor calculado do índice do buffer de cor, enquanto para o "Preenchimento de cor" adicionamos zero — o índice do seguinte buffer é igual ao do calculado ausente no objeto do buffer de cor.

As ações restantes são descritas nos comentários do código.
Vale ressaltar que a definição de cor agora é realizada com ajuda do método de definição de cores, porque diretamente no método são definidas as cores para as propriedades do objeto, adicionalmente, é preenchida a matriz de cores, que, posteriormente, é usada para obter a cor desejada pelo índice:

//--- Set integer buffer parameters
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_DRAW_TYPE,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_DRAW_TYPE));
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_ARROW,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_ARROW_CODE));
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_ARROW_SHIFT,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_ARROW_SHIFT));
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_DRAW_BEGIN,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_DRAW_BEGIN));
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_SHOW_DATA,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_SHOW_DATA));
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_SHIFT,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_SHIFT));
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LINE_STYLE,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_LINE_STYLE));
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LINE_WIDTH,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_LINE_WIDTH));
   this.SetColor((color)this.GetProperty(BUFFER_PROP_COLOR));

No método que retorna a descrição de número inteiro, o retorno de descrição de espessurae de cor de linha do indicador foi alterado:

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

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

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

Para retornar a descrição de espessura de linha, é verificado o status do buffer, e, se for um indicador de seta, não terá linhas, e será retornado uma entrada sobre o tamanho dos ícones das setas.
Para retorno da cor agora é usado o método GetColorsDescription(), que retorna a descrição de todas as cores de buffer usadas, buffer esse que será examinado abaixo.

O método de retorno de descrição de valor vazio definido agora leva em consideração o EMPTY_VALUE negativo definido como "Valor vazio" do buffer:

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

O método que retorna uma descrição das cores definidas para o objeto-buffer:

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

Se apenas uma cor for definida, será retornada uma descrição de string da propriedade do objeto-buffer que armazena o valor da cor,
se houver várias cores e estas estiverem na matriz de cores, num loop será criada uma string a partir de suas descrições e será retornado o valor resultante.

Método que define o número de cores do objeto-buffer:

//+------------------------------------------------------------------+
//| Set the number of colors                                         |
//+------------------------------------------------------------------+
void CBuffer::SetColorNumbers(const int number)
  {
   if(number>IND_COLORS_TOTAL)
      return;
   int n=(this.Status()!=BUFFER_STATUS_FILLING ? number : 2);
   this.SetProperty(BUFFER_PROP_COLOR_INDEXES,n);
   ::ArrayResize(this.ArrayColors,n);
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_COLOR_INDEXES,n);
  }
//+------------------------------------------------------------------+

Se para o método for passado mais do que o valor máximo possível, sairemos do método.
Se o tipo de plotagem de linha não for DRAW_FILLING, definimos um número igual ao passado, caso contrário, 2.
Em seguida, alteramos o tamanho da matriz de cores e definimos para o buffer um novo valor.

Método que define para o objeto-buffer uma cor de plotagem:

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

Se o estilo de plotagem for DRAW_FILLING, saímos do método — este estilo de plotagem sempre usa duas cores de destaque.
Em seguida, definimos o número de cores como igual a um, definimos a cor passada para a propriedade do objeto-buffer, na única célula da matriz de cores escrevemos a cor transmitida e definimos esta cor para o buffer do indicador.

Para redesenhar as linhas do indicador, é necessário definir o número da cor (seu índice da lista de cores usadas) para o buffer de cor do indicador com base no índice da série temporal desejada, onde é preciso alterar a cor.
Por exemplo, se usarmos três cores, elas terão os índices 0, 1 e 2. Para colorir a linha do indicador com qualquer uma das três cores, basta escrever o número correspondente à cor desejada no buffer de cores. Se armazenarmos a cor azul no índice 0 e vermelho no índice 1, para colorir a linha, precisamos inserir o valor 0 para a cor azul, 1, para a cor vermelha no buffer de cor do indicador. E a linha do indicador será mudada de cor.
Para poder alterar as cores definidas para o objeto-buffer, precisamos de um método que registrar a nova cor no índice de cor especificado do buffer do indicador.

Método que define a cor de plotagem para o índice de cor especificado:

//+------------------------------------------------------------------+
//| Set the drawing color to the specified color index               |
//+------------------------------------------------------------------+
void CBuffer::SetColor(const color colour,const uchar index)
  {
   if(index>IND_COLORS_TOTAL-1)
      return;
   if(index>this.ColorsTotal()-1)
      this.SetColorNumbers(index+1);
   this.ArrayColors[index]=colour;
   if(index==0)
      this.SetProperty(BUFFER_PROP_COLOR,(color)this.ArrayColors[0]);
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LINE_COLOR,index,this.ArrayColors[index]);
  }
//+------------------------------------------------------------------+

Ao método é transmitida cor e o índice de cor pelo qual será registrado.
Se o índice de cor transmitido for maior que o número máximo possível de cores de indicador, sairemos.
Se o índice exceder o número de cores já existentes no objeto-buffer , definiremos uma nova quantidade de cores para o objeto-buffer.
Em seguida, na matriz de cores gravamos a cor transmitida pelo índice especificado, se um índice zero for passado, para a propriedade de cor do buffer definimos a cor passada ao método e, finalmente, para o buffer do indicador definimos um novo valor de cor pelo índice passado para o método.

Método que define as cores para o objeto-buffer a partir da matriz de cores passada:

//+------------------------------------------------------------------+
//| Set drawing colors from the color array                          |
//+------------------------------------------------------------------+
void CBuffer::SetColors(const color &array_colors[])
  {
//--- Exit if the passed array is empty
   if(::ArraySize(array_colors)==0)
      return;
//--- Copy the passed array to the array of buffer object colors
   ::ArrayCopy(this.ArrayColors,array_colors,0,0,IND_COLORS_TOTAL);
//--- Exit if the color array was empty and not copied for some reason
   int total=::ArraySize(this.ArrayColors);
   if(total==0)
      return;
//--- If the drawing style is not DRAW_FILLING
   if(this.Status()!=BUFFER_STATUS_FILLING)
     {
      //--- if the new number of colors exceeds the currently set one, 
      //--- set the new value for the number of colors
      if(total>this.ColorsTotal())
         this.SetColorNumbers(total);
     }
   //--- If the drawing style is DRAW_FILLING, set the number of colors equal to 2
   else
      total=2;
//--- Set the very first color from the color array (for a single color) to the buffer object color property
   this.SetProperty(BUFFER_PROP_COLOR,(color)this.ArrayColors[0]);
//--- Set the new number of colors for the indicator buffer
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_COLOR_INDEXES,total);
//--- In the loop by the new number of colors, set all colors by their indices for the indicator buffer
   for(int i=0;i<total;i++)
      ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LINE_COLOR,i,this.ArrayColors[i]);
  }
//+------------------------------------------------------------------+

Todas as linhas do método estão detalhadas nos comentários. Acho que não haverá perguntas.

Método que retorna o índice ajustado da matriz de buffer necessária:

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

Como agora todas as matrizes de dados para buffers de indicador estão na matriz DataBuffer[] com o tipo de estrutura SDataBuffer, para não sair acidentalmente fora da matriz ao passar um índice incorreto, usaremos um método que corrige o índice de matriz passado incorretamente.
Se for passado um índice menos do que o número total de buffers usados para a construção, é retornado o índice passado.
Em qualquer outro caso, é retornado o índice do último elemento da matriz é retornado.
No caso de uma única matriz, o primeiro elemento será o último elemento.

Método que retorna o tamanho da matriz do buffer de dados especificado:

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

O método é mais para depuração e para uso interno.
Retorna o tamanho da matriz atribuída pelo buffer do indicador com base no seu índice na matriz DataBuffer[].
OnInit() sempre retorna zero. OnCalculate() retorna o tamanho rates_total.

Método que retorna o valor do buffer de dados especificado com base no índice da série temporal especificado:

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

O método é usado para receber e retornar dados pelo índice da série temporal desde o buffer do indicador especificado.
Primeiro ajustamos o índice resultante do buffer necessário, verificamos a quantidade de dados no buffer e, se não houver nada, retornamos o valor vazio definido para o objeto-buffer. Em seguida, verificamos que o índice da série temporal não ultrapasse a quantidade de dados no buffer, e se esse for o caso, retornamos o último elemento do buffer; caso contrário, retornamos dados desde o buffer especificado com base no índice da série temporal.

Método que retorna o valor do buffer de cor com base no índice da série temporal especificado:

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

O método é quase idêntico ao que acabamos de considerar, exceto que sempre temos um buffer de cor e não precisamos obter e ajustar o índice do buffer desejado. Aqui, verificamos imediatamente o índice da série temporal e retornamos dados desde o buffer com base no índice ajustado (se for passado um índice inválido).

Método que define o valor para o buffer de dados especificado com base no índice de série temporal especificado:

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

O método é idêntico ao método acima de receber dados do buffer especificado pelo índice da série temporal. Exceto que, em vez de retornar um valor, o método grava no buffer de dados especificado o valor passado com base no índice de série temporal especificado.

Método que define o valor para o buffer de cor com base no índice da série temporal especificado:

//+------------------------------------------------------------------+
//| Set the color index to the specified timeseries index            |
//| of the color buffer array                                        |
//+------------------------------------------------------------------+
void CBuffer::SetBufferColorIndex(const uint series_index,const uchar color_index)
  {
   if(this.GetDataTotal(0)==0 || color_index>this.ColorsTotal()-1 || this.Status()==BUFFER_STATUS_FILLING)
      return;
   int data_total=this.GetDataTotal(0);
   int data_index=((int)series_index<data_total ? (int)series_index : data_total-1);
   if(::ArraySize(this.ColorBufferArray)==0)
      ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR),": ",CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_INVALID_PROPERTY_BUFF));
   this.ColorBufferArray[data_index]=color_index;
  }
//+------------------------------------------------------------------+

O método é idêntico ao método para obter o índice de cor a partir do índice de série temporal especificado. Exceto que, em vez de retornar um valor, o método grava no buffer de cor o valor de índice de cor passado com base no índice de série temporal especificado.
Uma vantagem é que se quer não houver dados em conformidade na matriz de dados (estiver vazia), quer o índice de cor passado for maior que o tamanho da matriz de cores, quer o estilo de desenho do objeto buffer DRAW_FILLING (onde não há buffer de cor), então saímos do método.
E se o tamanho do buffer de cor é zero, provavelmente os valores foram mal definidos em #property indicator_buffers, portanto, é exibida a respectiva mensagem , e depois disso haverá um erro de saída da matriz que não será processada intencionalmente, para, assim, entender com base nas entradas no log que é preciso definir corretamente o número de buffers nas propriedades do indicador.

Método de inicialização de todos os buffers do objeto-buffer com os valores especificados do buffer de dados e do buffer de cor:

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

Num loop percorrendo a lista completa de todos os buffers de dados inicializamos cada matriz sucessivo com o valor especificado.
Se o estilo de plotagem do objeto-buffer não for DRAW_FILLING, inicializaremos o buffer de cores com o valor especificado, ajustado em caso de sair da matriz de cores.

Método de inicialização de todas as matrizes de buffers contidas no objeto-buffer para o objeto-buffer definido como "vazio":

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

O método é idêntico ao anterior, exceto que o valor de inicialização é obtido do valor "vazio" definido para o objeto, e o índice de cor para inicialização é obtido desde o primeiro.

Muitas vezes nos indicadores é necessário limpar os valores de todos os buffers antes de preenchê-los para evitar o aparecimento de "lixo" no gráfico.
O próximo método é projetado para limpar todos os buffers no índice de série temporal especificado.
Método que preenche todos os buffers de indicador do objeto-buffer com um valor vazio:

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

Num loop percorrendo todas as matrizes-buffers do objeto-buffer, escrevemos o valor vazio especificado para o objeto-buffer com base no índice especificado da série temporal através do método SetBufferValue(), que consideramos acima, e usando o método SetBufferColorIndex(), também descrito acima, definimos o índice de cor para o índice especificado da série temporal.

Essas são todas as mudanças na classe do objeto do buffer abstrato que são necessárias para criar e trabalhar com objetos-herdeiros de dada classe.

Objetos-buffers de indicador são herdeiros do objeto de buffer abstrato

Como já ficou sabido, quase todos os objetos de biblioteca têm uma estrutura semelhante: um objeto abstrato comum com propriedades e métodos comuns a seus herdeiros, e cada objeto derivado especifica e concretiza os dados do objeto abstrato. Consideramos o conceito de objetos de biblioteca: no primeiro artigo sobre um objeto abstrato, no segundo sobre objetos-herdeiros e sua coleção.

Objetos-buffers não serão exceção. Cada um desses objetos será um buffer de indicador com um estilo de plotagem específico. Ele terá seu nome a partir do estilo de plotagem.

O primeiro objeto-buffer tem o estilo de plotagem "Setas", e terá um nome em conformidade.

Na pasta do terminal \MQL5\Include\DoEasy\Objects\Indicators\ criamos o novo arquivo BufferArrow.mqh da classe CBufferArrow, a classe base para a qual será o objeto de buffer abstrato CBuffer:

//+------------------------------------------------------------------+
//|                                                  BufferArrow.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 "Buffer.mqh"
//+------------------------------------------------------------------+
//| Buffer with the "Drawing with arrows" drawing style              |
//+------------------------------------------------------------------+
class CBufferArrow : public CBuffer
  {
private:

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

Como podemos ver, na lista de classes não há nada a mais.
Os métodos virtuais declarados que retornam sinalizadores de que o objeto mantém propriedades inteiras, reais e de string, e um método virtual que imprime no log uma breve descrição do buffer.
Ao construtor da classe é transferido o índice do buffer plotado (ele é o número de sequência do buffer na lista contendo todos os buffers do indicador) e o índice da buffer-matriz base com o qual será realmente vinculada a primeira matriz double do novo buffer a ser criado.
Na lista de inicialização do construtor é escrita a inicialização do construtor protegido da classe pai:

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") {}

onde como primeiro parâmetro definimos o status do buffer "Buffer com o estilo de plotagem "Renderização de setas", como o segundo parâmetro especificamos o tipo de buffer "buffer de dados de cor", passamos o valor do índice do buffer plotado e a matriz de base, o número de buffers para plotagem, a espessura da linha (aqui este parâmetro irá indicar o tamanho da seta) e nome da série gráfica exibido na janela de dados. Assim, ao criar um novo objeto-buffer, indicamos para a classe base qual o tipo de buffer e com quais parâmetros o criar.

Método que retorna o sinalizador indicando que o buffer suporta propriedades inteiras:

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

Se ao método for transferida a propriedade "Estilo de linha" ou se ele for um buffer de cálculo, e, além disso, ele não for nem a propriedade "tipo de buffer" nem "índice do seguinte buffer plotado", tal propriedade não será suportada e será retornado false. Em outros casos, é retornado true.

Método que retorna o sinalizador indicando que o buffer suporta propriedades reais:

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

Se ele for um buffer de cálculo, não serão suportadas nenhumas propriedades reais (das propriedades reais há apenas uma propriedade: "Valor vazio para plotagem, para o qual não há plotagem:") — retornamos false. Em outros casos, nós retornamos true.

Método que retorna o sinalizador indicando que o buffer suporta propriedades de string:

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

Se ele for um buffer de cálculo, nenhuma propriedade de string será suportada — retornamos false. Em outros casos, nós retornamos true.

Método para registrar uma breve descrição do objeto-buffer:

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

No método é criada uma string a partir do cabeçalho "Buffer" indicando o índice do buffer plotado (o número de sequência do buffer na janela DataWindow) com uma descrição do status do buffer (estilo de desenho), símbolo e período gráfico.
Assim:

Buffer(0): Renderização de setas EURUSD H1

Isto é toda a classe do objeto-buffer com o tipo de plotagem "Renderização de setas".


Criamos uma nova classe-herdeira do objeto de buffer abstrato CBufferLine — com estilo de plotagem "Linha"
no arquivo \MQL5\Include\DoEasy\Objects\Indicators\BufferLine.mqh:

//+------------------------------------------------------------------+
//|                                                   BufferLine.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 "Buffer.mqh"
//+------------------------------------------------------------------+
//| Buffer of the Line drawing style                                 |
//+------------------------------------------------------------------+
class CBufferLine : public CBuffer
  {
private:

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

В сравнении с классом буфера стрелок, aqui ao construtor do buffer-objeto base na lista de inicialização dentro do construtor da classe transferimos o status "Linha" e nome da série gráfica "Line". Os parâmetros restantes são idênticos aos do objeto-buffer de setas.

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

Também alteramos o método que retorna o sinalizador indicando que o buffer suporta propriedades inteiras:

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

Se a propriedade quer "código de seta", quer "deslocamento vertical de seta" ou se for um buffer de cálculo, e, ademais, não a for nem a propriedade "tipo de buffer" nem "índice do seguinte buffer plotado", tal propriedade não será suportada e será retornado false. Em outros casos, é retornado true.


Vamos criar uma nova classe-herdeiro do buffer abstrato CBufferSection, com o estilo de plotagem "Segmentos"
no arquivo \MQL5\Include\DoEasy\Objects\Indicators\BufferSection.mqh.
Consideraremos posteriormente apenas os construtores de novas classes e métodos para manter propriedades inteiras, uma vez que todo o resto é idêntico às duas classes já consideradas.

No construtor da classe, na lista de inicialização, ao construtor do buffer-buffer base transferimos o status "Segmentos" e nome da série gráfica "Section"...

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

Método que retorna o sinalizador indicando que o buffer suporta propriedades inteiras:

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

Se a propriedade quer "código de seta", quer "deslocamento vertical de seta" ou se for um buffer de cálculo, e, ademais, não a for nem a propriedade "tipo de buffer" nem "índice do seguinte buffer plotado", tal propriedade não será suportada e será retornado false. Em outros casos, é retornado true.


Criamos uma nova classe-herdeira do objeto de buffer abstrato CBufferHistogram — com estilo de plotagem "Histograma a partir da linha zero"
no arquivo \MQL5\Include\DoEasy\Objects\Indicators\BufferHistogram.mqh.

No construtor da classe, na lista de inicialização, ao construtor do buffer-buffer base transferimos o status "Histograma a partir da linha zero" e nome da série gráfica "Histogram", já a espessura da linha do histograma por padrão será igual a 2.

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

Os métodos restantes são idênticos à classe de objeto-buffer "Segmentos".


Criamos uma nova classe-herdeira do objeto de buffer abstrato CBufferHistogram2 — com estilo de plotagem "Histograma em dois buffers de indicador"
no arquivo \MQL5\Include\DoEasy\Objects\Indicators\BufferHistogram2.mqh.

No construtor da classe, na lista de inicialização, ao construtor do objeto-buffer base transferimos o status "Histograma em dois buffers de indicador" e
nome da série gráfica "Histogram2 0;Histogram2 1"
, dois buffers para plotagem e espessura da linha do histograma por padrão será igual a 8.

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

Por que a largura da linha do histograma é 8.
Nos indicadores-histogramas com base em dois buffers, em MQL5 esta é a largura máxima da coluna do histograma, e essa largura depende da escala horizontal do gráfico:


Na escala máxima do gráfico (5), a largura da coluna do histograma tem seu tamanho máximo. Se for preciso um tamanho menor, após criar o buffer, é possível defini-lo com um novo valor de largura usando o método SetWidth().

Por que esse nome de série gráfica "Histogram2 0;Histogram2 1".
Nos indicadores que utilizam vários buffers para sua construção, os nomes de cada buffer são indicados no nome da série gráfica. Se for preciso dar a cada buffer um nome exclusivo, todos eles serão separados por ponto e vírgula (;) ao especificar o nome da série gráfica.
Aqui, para o primeiro bufer é definido o nome "Histogram2 0" (zero é uma indicação do índice do primeiro buffer), já para o segundo buffer será definido o nome "Histogram2 1" (um é uma indicação do índice do segundo buffer).
Na janela DataWindow, ele será exibido assim:


Se for preciso de outros nomes, após criar um buffer, será possível definir novos valores para nomes de buffer usando o método SetLabel().

Os métodos restantes são idênticos à classe de objeto-buffer "Segmentos".


Vamos criar uma nova classe-herdeiro do buffer abstrato CBufferZigZag, com o estilo de plotagem "ZigZag"
no arquivo \MQL5\Include\DoEasy\Objects\Indicators\BufferZigZag.mqh.

No construtor da classe, na lista de inicialização, ao construtor do objeto-buffer base transferimos o status "ZigZag" e
nome da série gráfica "ZigZag 0;ZigZag 1", dois buffers para plotagem e espessura de linha do zigzag por padrão será igual a 1.

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

Os métodos restantes são idênticos à classe de objeto-buffer "Segmentos".


Vamos criar uma nova classe-herdeiro do objeto de buffer abstrato CBufferFilling, com o estilo de plotagem "Preenchimento de cor entre dois níveis"
no arquivo \MQL5\Include\DoEasy\Objects\Indicators\BufferFilling.mqh.

No construtor da classe, na lista de inicialização, ao construtor do objeto-buffer base transferimos o status "Preenchimento de cor entre dois níveis" e
nome da série gráfica "Filling 0;Filling 1", dois buffers para plotagem e espessura de linha do zigzag por padrão será igual a 1 (não há linhas neste estilo de desenho e esta propriedade é irrelevante).

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

Método que retorna o sinalizador indicando que o buffer suporta propriedades inteiras:

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

Se a propriedade quer "código de seta", quer "deslocamento vertical de seta", quer "estilo da linha", quer "espessura de linha", quer o índice de buffer de cor ou se for um buffer de cálculo, e, ademais, não a for nem a propriedade "tipo de buffer" nem "índice do seguinte buffer plotado", tal propriedade não é suportada e é retornado false. Em outros casos, é retornado true.

Os métodos restantes são idênticos à classe de objeto-buffer "Segmentos".


Criamos uma nova classe-herdeiro do objeto abstrato do buffer CBufferBars — com estilo de plotagem "Exibição na forma de barras"
no arquivo \MQL5\Include\DoEasy\Objects\Indicators\BufferBars.mqh.

No construtor da classe, na lista de inicialização, ao construtor do objeto-buffer base transferimos o status "Exibição na forma de barras" e
nome padrão da série gráfica "Bar Open;Bar High;Bar Low;Bar Close", quatro buffers para plotagem e espessura de barra por padrão será igual a 2.

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

Método que retorna o sinalizador indicando que o buffer suporta propriedades inteiras:

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

Se a propriedade quer "código de seta", quer "deslocamento vertical de seta", quer "estilo da linha" ou se for um buffer de cálculo, e, ademais, não a for nem a propriedade "tipo de buffer" nem "índice do seguinte buffer plotado", tal propriedade não é suportada e é retornado false. Em outros casos, é retornado true.

Os métodos restantes são idênticos à classe de objeto-buffer "Segmentos".


Vamos criar uma nova classe-herdeiro do objeto de buffer abstrato CBufferCandles, com o estilo de plotagem "Exibição na forma de vela"
no arquivo \MQL5\Include\DoEasy\Objects\Indicators\BufferCandles.mqh.

No construtor da classe, na lista de inicialização, ao construtor do objeto-buffer base transferimos o status "Exibição na forma de vela" e
nome da série gráfica "Candle Open;Candle High;Candle Low;Candle Close", quatro buffers para plotagem e largura da vela por padrão será igual a 1 (o parâmetro não afeta a espessura real de plotagem — a largura das velas é sempre igual à das do gráfico, dependendo de sua escala horizontal).

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

Método que retorna o sinalizador indicando que o buffer suporta propriedades inteiras:

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

Se a propriedade quer "código de seta", quer "deslocamento vertical de seta", quer "estilo da linha", quer "espessura de linha" ou se for um buffer de cálculo, e, ademais, não a for nem a propriedade "tipo de buffer" nem "índice do seguinte buffer plotado", tal propriedade não é suportada e é retornado false. Em outros casos, é retornado true.

Os métodos restantes são idênticos à classe de objeto-buffer "Segmentos".

Criamos todas as classes de objetos-buffers para os herdeiros do objeto de buffer abstrato.
A listagem completa das classes pode ser encontrada nos arquivos anexados ao artigo.

Para testar as classes criadas, não iremos criar nenhum controle de botão para exibir linhas de buffer no gráfico, em vez disso, definiremos os gráficos necessários nas configurações do indicador, assim, será possível selecionar quais buffers exibir e iniciar o indicador.

Teste

Para poder selecionar as construções gráficas necessárias, ao arquivo de enumerações de parâmetros de entrada \MQL5\Include\DoEasy\InpDatas.mqh adicionamos novas enumerações para os idiomas inglês e russo da compilação:

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

Isso permite que nas configurações existam opções assim:


Para teste, vamos pegar o indicador do artigo anterior e salvá-lo numa nova pasta.
\MQL5\Indicators\TestDoEasy\Part43\ com o novo nome TestDoEasyPart43.mq5.

Ao arquivo do indicador anexamos os arquivos de todos os buffers criados, definimos o número de buffers como 27, enquanto a quantidade de buffers plotados, como 9:

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

No bloco de parâmetros de entrada comentamos os modificadores sinput para selecionar o modo de trabalho com os símbolos (o padrão será o atual), a lista de símbolos usados também ficará oculta nas configurações, o modo de escolha do período gráfico será o atual por padrão, ocultamos a lista de períodos gráficos usados nas configurações e adicionamos uma opção nas configurações para selecionar os buffers que devem ser exibidos no gráfico:

//--- classes

//--- enums

//--- defines

//--- structures

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

Em vez de declarar matrizes-double de buffers de indicador, escrevemos a declaração de uma matriz dinâmica de ponteiros para instâncias CObject, nesta lista vamos armazenar os objetos de buffers de indicador criados para teste:

//--- indicator buffers
CArrayObj      list_buffers;                    // Temporary list for storing buffer objects
//--- global variables
CEngine        engine;                          // CEngine library main object
string         prefix;                          // Prefix of graphical object names
int            min_bars;                        // The minimum number of bars for the indicator calculation
int            used_symbols_mode;               // Mode of working with symbols
string         array_used_symbols[];            // The array for passing used symbols to the library
string         array_used_periods[];            // The array for passing used timeframes to the library
//+------------------------------------------------------------------+

No manipulador OnInit() do indicador vamos criar todos os objetos-buffers, adicioná-los à lista, definir cores não padrão (três cores) e imprimir no log os dados de cada objeto-buffer criado:

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

//--- Set indicator global variables
   prefix=engine.Name()+"_";
   //--- Get the index of the maximum used timeframe in the array,
   //--- calculate the number of bars of the current period fitting in the maximum used period
   //--- Use the obtained value if it exceeds 2, otherwise use 2
   int index=ArrayMaximum(ArrayUsedTimeframes);
   int num_bars=NumberBarsInTimeframe(ArrayUsedTimeframes[index]);
   min_bars=(index>WRONG_VALUE ? (num_bars>2 ? num_bars : 2) : 2);

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

//--- Create the button panel

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

//--- indicator buffers mapping

//--- Create all buffer objects
   CBuffer *buffer0=new CBufferArrow(0,0);
   CBuffer *buffer1=new CBufferLine(1,buffer0.IndexNextBuffer());
   CBuffer *buffer2=new CBufferSection(2,buffer1.IndexNextBuffer());
   CBuffer *buffer3=new CBufferHistogram(3,buffer2.IndexNextBuffer());
   CBuffer *buffer4=new CBufferHistogram2(4,buffer3.IndexNextBuffer());
   CBuffer *buffer5=new CBufferZigZag(5,buffer4.IndexNextBuffer());
   CBuffer *buffer6=new CBufferFilling(6,buffer5.IndexNextBuffer());
   CBuffer *buffer7=new CBufferBars(7,buffer6.IndexNextBuffer());
   CBuffer *buffer8=new CBufferCandles(8,buffer7.IndexNextBuffer());
//--- Add buffers to the list of indicator buffers
   list_buffers.Add(buffer0);
   list_buffers.Add(buffer1);
   list_buffers.Add(buffer2);
   list_buffers.Add(buffer3);
   list_buffers.Add(buffer4);
   list_buffers.Add(buffer5);
   list_buffers.Add(buffer6);
   list_buffers.Add(buffer7);
   list_buffers.Add(buffer8);
//--- Create color array
   color array_colors[]={clrDodgerBlue,clrRed,clrGray};
//--- Set non-default buffer color values
   for(int i=0;i<list_buffers.Total();i++)
     {
      CBuffer *buff=list_buffers.At(i);
      buff.SetColors(array_colors);
      //--- Print data on the next buffer
      buff.Print();
     }
//--- Set the label size for the arrow buffer and the line width for ZigZag
   buffer0.SetWidth(2);
   buffer5.SetWidth(2);
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+

No manipulador OnCalculate()
Ao inicializar ou alterar os dados históricos, iniciamos todos os buffers criados e exibimos seus nomes curtos
.
Ao abrir uma nova barra o no tick atual, registramos no buffer os valores de preços:
para um buffer
— preço Close,
para dois buffers
— no primeiro Open, no segundo — Close,
para quatro
buffers em cada buffer registramos os devidos preços OHLC:

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//+------------------------------------------------------------------+
//| OnCalculate code block for working with the library:             |
//+------------------------------------------------------------------+
   
//--- Pass the current symbol data from OnCalculate() to the price structure
   CopyData(rates_total,prev_calculated,time,open,high,low,close,tick_volume,volume,spread);

//--- Check for the minimum number of bars for calculation
   if(rates_total<min_bars || Point()==0) return 0;
   
//--- Handle the Calculate event in the library
//--- If the OnCalculate() method of the library returns zero, not all timeseries are ready - leave till the next tick
   if(engine.0)
      return 0;
   
//--- If working in the tester
   if(MQLInfoInteger(MQL_TESTER)) 
     {
      engine.OnTimer(rates_data);   // Working in the library timer
      EventsHandling();             // Working with library events
     }
//+------------------------------------------------------------------+
//| OnCalculate code block for working with the indicator:           |
//+------------------------------------------------------------------+
//--- Set OnCalculate arrays as timeseries
   ArraySetAsSeries(open,true);
   ArraySetAsSeries(high,true);
   ArraySetAsSeries(low,true);
   ArraySetAsSeries(close,true);
   ArraySetAsSeries(time,true);
   ArraySetAsSeries(tick_volume,true);
   ArraySetAsSeries(volume,true);
   ArraySetAsSeries(spread,true);

//--- Checking and calculating the number of calculated bars
//--- If limit = 0, there are no new bars - calculate the current one
//--- If limit = 1, a new bar has appeared - calculate the first and the current ones
//--- limit > 1 means the first launch or changes in history - the full recalculation of all data
   int limit=rates_total-prev_calculated;
   
//--- Recalculate the entire history
   if(limit>1)
     {
      //--- In a loop by the number of buffers in the list
      for(int i=0;i<list_buffers.Total();i++)
        {
         //--- get the next buffer and display the type of its graphical construction to the journal
         //--- together with the double array assigned to the buffer (if all is correct, the size is equal to rates_total)
         CBuffer *buff=list_buffers.At(i);
         buff.PrintShort();
         buff.InitializeBuffers();
        }
      limit=rates_total-1;
     }
//--- Prepare data

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

Para determinar se é permitido desenhar as linhas do indicador, usaremos a função:

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

O status do buffer é passado para a função e é retornado o valor do parâmetro de entrada, correspondente ao status passado para a função.

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

Vamos compilar o indicador e executá-lo no gráfico.
Durante a inicialização do log, serão exibidas todas as propriedades dos nove buffers de indicador criados:

Conta 8550475: Artyom Trishkin (MetaQuotes Software Corp.) 10425.23 USD, 1:100, Hedge, Conta de demonstração MetaTrader 5
--- Inicialização da biblioteca "DoEasy" ---
Trabalho apenas com o símbolo atual: "EURUSD"
Trabalho apenas com o período gráfico atual: H1
Série temporal do símbolo EURUSD: 
- Série temporal "EURUSD" H1: Solicitado: 1000, Real: 0, Criado: 0, No servidor: 0
Hora de inicialização da biblioteca: 00:00:00.156

============= Iníco da lista de parámetros: Buffer de dados de cor[0] "Renderização de setas" ==================
Número de sequência do buffer plotado: 0
Status do buffer: Buffer de indicador com tipo de plotagem "Renderização de setas"
Tipo de buffer: Buffer de dados de cor
Período do buffer de dados (período gráfico): Périodo gráfico atual (H1)
Ativo: Sim
Tipo de plotagem gráfica: Renderização de setas
Código de seta: 159
Deslocamento vertical de setas: 0
Tamanho de ícone de seta: 1
Número de barras iniciais sem plotagem e valores em DataWindow: 0
Exibição de valores de plotagem na janela DataWindow: Sim
Deslocamento da plotagem do indicador ao longo do eixo do tempo em barras: 0
Número de cores: 3
Cor de plotagem: clrDodgerBlue,clrRed,clrGray
Quantidade de buffers de dados: 1
Índice de buffer básico de dados: 0
Índice de bufer de cor: 1
Índice de matriz para atribuição ao seguinte buffer de indicador: 2
------
Valor vazio para plotagem, para o qual não há plotagem: EMPTY_VALUE
------
Símbolo do buffer: EURUSD
Nome da série gráfica de indicador exibida na janela DataWindow: Arrows
================== Final da lista de parâmetro: Buffer de dados de cor[0] "Renderização de setas" ==================

============= Iníco da lista de parámetros: Buffer de dados de cor[1] "Linha" ==================
Número de sequência do buffer plotado: 1
Status do buffer: Buffer de indicador com tipo de plotagem "Linha"
Tipo de buffer: Buffer de dados de cor
Período do buffer de dados (período gráfico): Périodo gráfico atual (H1)
Ativo: Sim
Tipo de plotagem gráfica: Linha
Estilo de linha de plotagem: Linha sólida
Espessura de lina de plotagem: 1
Número de barras iniciais sem plotagem e valores em DataWindow: 0
Exibição de valores de plotagem na janela DataWindow: Sim
Deslocamento da plotagem do indicador ao longo do eixo do tempo em barras: 0
Número de cores: 3
Cor de plotagem: clrDodgerBlue,clrRed,clrGray
Quantidade de buffers de dados: 1
Índice de buffer básico de dados: 2
Índice de bufer de cor: 3
Índice de matriz para atribuição ao seguinte buffer de indicador: 4
------
Valor vazio para plotagem, para o qual não há plotagem: EMPTY_VALUE
------
Símbolo do buffer: EURUSD
Nome da série gráfica de indicador exibida na janela DataWindow: Line
================== Final da lista de parâmetro: Buffer de dados de cor[1] "Linha" ==================

============= Iníco da lista de parámetros: Buffer de dados de cor[2] "Отрезки" ==================
Número de sequência do buffer plotado: 2
Status do buffer: Buffer de indicador com tipo de plotagem "Отрезки"
Tipo de buffer: Buffer de dados de cor
Período do buffer de dados (período gráfico): Périodo gráfico atual (H1)
Ativo: Sim
Tipo de plotagem gráfica: Отрезки
Estilo de linha de plotagem: Linha sólida
Espessura de lina de plotagem: 1
Número de barras iniciais sem plotagem e valores em DataWindow: 0
Exibição de valores de plotagem na janela DataWindow: Sim
Deslocamento da plotagem do indicador ao longo do eixo do tempo em barras: 0
Número de cores: 3
Cor de plotagem: clrDodgerBlue,clrRed,clrGray
Quantidade de buffers de dados: 1
Índice de buffer básico de dados: 4
Índice de bufer de cor: 5
Índice de matriz para atribuição ao seguinte buffer de indicador: 6
------
Valor vazio para plotagem, para o qual não há plotagem: EMPTY_VALUE
------
Símbolo do buffer: EURUSD
Nome da série gráfica de indicador exibida na janela DataWindow: Section
================== Final da lista de parâmetro: Buffer de dados de cor[2] "Segmentos" ==================

============= Iníco da lista de parámetros: Buffer de dados de cor[3] "Histograma a partir da linha zero" ==================
Número de sequência do buffer plotado: 3
Status do buffer: Buffer de indicador com tipo de plotagem "Histograma a partir da linha zero"
Tipo de buffer: Buffer de dados de cor
Período do buffer de dados (período gráfico): Périodo gráfico atual (H1)
Ativo: Sim
Tipo de plotagem gráfica: Histograma a partir da linha zero
Estilo de linha de plotagem: Linha sólida
Espessura de lina de plotagem: 2
Número de barras iniciais sem plotagem e valores em DataWindow: 0
Exibição de valores de plotagem na janela DataWindow: Sim
Deslocamento da plotagem do indicador ao longo do eixo do tempo em barras: 0
Número de cores: 3
Cor de plotagem: clrDodgerBlue,clrRed,clrGray
Quantidade de buffers de dados: 1
Índice de buffer básico de dados: 6
Índice de bufer de cor: 7
Índice de matriz para atribuição ao seguinte buffer de indicador: 8
------
Valor vazio para plotagem, para o qual não há plotagem: EMPTY_VALUE
------
Símbolo do buffer: EURUSD
Nome da série gráfica de indicador exibida na janela DataWindow: Histogram
================== Final da lista de parâmetro: Buffer de dados de cor[3] "Histograma a partir da linha zero" ==================

============= Iníco da lista de parámetros: Buffer de dados de cor[4] "Histograma em dois buffer de indicador" ==================
Número de sequência do buffer plotado: 4
Status do buffer: Buffer de indicador com tipo de plotagem "Histograma em dois buffer de indicador"
Tipo de buffer: Buffer de dados de cor
Período do buffer de dados (período gráfico): Périodo gráfico atual (H1)
Ativo: Sim
Tipo de plotagem gráfica: Histograma em dois buffer de indicador
Estilo de linha de plotagem: Linha sólida
Espessura de lina de plotagem: 8
Número de barras iniciais sem plotagem e valores em DataWindow: 0
Exibição de valores de plotagem na janela DataWindow: Sim
Deslocamento da plotagem do indicador ao longo do eixo do tempo em barras: 0
Número de cores: 3
Cor de plotagem: clrDodgerBlue,clrRed,clrGray
Quantidade de buffers de dados: 2
Índice de buffer básico de dados: 8
Índice de bufer de cor: 10
Índice de matriz para atribuição ao seguinte buffer de indicador: 11
------
Valor vazio para plotagem, para o qual não há plotagem: EMPTY_VALUE
------
Símbolo do buffer: EURUSD
Nome da série gráfica de indicador exibida na janela DataWindow: Histogram2 0;Histogram2 1
================== Final da lista de parâmetro: Buffer de dados de cor[4] "Histograma em dois buffer de indicador" ==================

============= Iníco da lista de parámetros: Buffer de dados de cor[5] "ZigZag" ==================
Número de sequência do buffer plotado: 5
Status do buffer: Buffer de indicador com tipo de plotagem "ZigZag"
Tipo de buffer: Buffer de dados de cor
Período do buffer de dados (período gráfico): Périodo gráfico atual (H1)
Ativo: Sim
Tipo de plotagem gráfica: ZigZag
Estilo de linha de plotagem: Linha sólida
Espessura de lina de plotagem: 1
Número de barras iniciais sem plotagem e valores em DataWindow: 0
Exibição de valores de plotagem na janela DataWindow: Sim
Deslocamento da plotagem do indicador ao longo do eixo do tempo em barras: 0
Número de cores: 3
Cor de plotagem: clrDodgerBlue,clrRed,clrGray
Quantidade de buffers de dados: 2
Índice de buffer básico de dados: 11
Índice de bufer de cor: 13
Índice de matriz para atribuição ao seguinte buffer de indicador: 14
------
Valor vazio para plotagem, para o qual não há plotagem: EMPTY_VALUE
------
Símbolo do buffer: EURUSD
Nome da série gráfica de indicador exibida na janela DataWindow: ZigZag 0;ZigZag 1
================== Final da lista de parâmetro: Buffer de dados de cor[5] "ZigZag" ==================

============= Iníco da lista de parámetros: Buffer de dados de cor[6] "Preenchimento colorido entre dois níveis" ==================
Número de sequência do buffer plotado: 6
Status do buffer: Buffer de indicador com tipo de plotagem "Preenchimento colorido entre dois níveis"
Tipo de buffer: Buffer de dados de cor
Período do buffer de dados (período gráfico): Périodo gráfico atual (H1)
Ativo: Sim
Tipo de plotagem gráfica: Preenchimento colorido entre dois níveis
Número de barras iniciais sem plotagem e valores em DataWindow: 0
Exibição de valores de plotagem na janela DataWindow: Sim
Deslocamento da plotagem do indicador ao longo do eixo do tempo em barras: 0
Número de cores: 2
Cor de plotagem: clrDodgerBlue,clrRed
Quantidade de buffers de dados: 2
Índice de buffer básico de dados: 14
Índice de matriz para atribuição ao seguinte buffer de indicador: 16
------
Valor vazio para plotagem, para o qual não há plotagem: EMPTY_VALUE
------
Símbolo do buffer: EURUSD
Nome da série gráfica de indicador exibida na janela DataWindow: Filling 0;Filling 1
================== Final da lista de parâmetro: Buffer de dados de cor[6] "Preenchimento colorido entre dois níveis" ==================

============= Iníco da lista de parámetros: Buffer de dados de cor[7] "Exibição na forma de barras" ==================
Número de sequência do buffer plotado: 7
Status do buffer: Buffer de indicador com tipo de plotagem "Exibição na forma de barras"
Tipo de buffer: Buffer de dados de cor
Período do buffer de dados (período gráfico): Périodo gráfico atual (H1)
Ativo: Sim
Tipo de plotagem gráfica: Exibição na forma de barras
Espessura de lina de plotagem: 2
Número de barras iniciais sem plotagem e valores em DataWindow: 0
Exibição de valores de plotagem na janela DataWindow: Sim
Deslocamento da plotagem do indicador ao longo do eixo do tempo em barras: 0
Número de cores: 3
Cor de plotagem: clrDodgerBlue,clrRed,clrGray
Quantidade de buffers de dados: 4
Índice de buffer básico de dados: 16
Índice de bufer de cor: 20
Índice de matriz para atribuição ao seguinte buffer de indicador: 21
------
Valor vazio para plotagem, para o qual não há plotagem: EMPTY_VALUE
------
Símbolo do buffer: EURUSD
Nome da série gráfica de indicador exibida na janela DataWindow: Bar Open;Bar High;Bar Low;Bar Close
================== Final da lista de parâmetro: Buffer de dados de cor[7] "Exibição na forma de barras" ==================

============= Iníco da lista de parámetros: Buffer de dados de cor[8] "Exibição na forma de velas" ==================
Número de sequência do buffer plotado: 8
Status do buffer: Buffer de indicador com tipo de plotagem "Exibição na forma de velas"
Tipo de buffer: Buffer de dados de cor
Período do buffer de dados (período gráfico): Périodo gráfico atual (H1)
Ativo: Sim
Tipo de plotagem gráfica: Exibição na forma de velas
Número de barras iniciais sem plotagem e valores em DataWindow: 0
Exibição de valores de plotagem na janela DataWindow: Sim
Deslocamento da plotagem do indicador ao longo do eixo do tempo em barras: 0
Número de cores: 3
Cor de plotagem: clrDodgerBlue,clrRed,clrGray
Quantidade de buffers de dados: 4
Índice de buffer básico de dados: 21
Índice de bufer de cor: 25
Índice de matriz para atribuição ao seguinte buffer de indicador: 26
------
Valor vazio para plotagem, para o qual não há plotagem: EMPTY_VALUE
------
Símbolo do buffer: EURUSD
Nome da série gráfica de indicador exibida na janela DataWindow: Candle Open;Candle High;Candle Low;Candle Close
================== Final da lista de parâmetro: Buffer de dados de cor[8] "Exibição na forma de velas" ==================


Após a primeira inicialização, quando for inicializada a biblioteca e todos os buffers do indicador, as seguintes entradas serão registradas no log:

Série "EURUSD" H1 criada com sucesso:
- Série temporal "EURUSD" H1: Solicitado: 1000, Real: 1000, Criado: 1000, No servidor: 6230
Buffer(0): Renderização de setas EURUSD H1
Buffer(1): Linhas EURUSD H1
Buffer(2): Segmentos EURUSD H1
Buffer(3): Histograma a partir da linha zero EURUSD H1
Buffer(4): Histograma nos dois buffers de indicador EURUSD H1
Buffer(5): ZigZag EURUSD H1
Buffer(6): Preenchimento colorido entre dois níveis EURUSD H1
Buffer(7): Exibição na forma de barras EURUSD H1
Buffer(8): Exibição na forma de velas EURUSD H1

Mudamos as configurações para permitir a exibição das linhas do indicador:


O que vem agora?

No próximo artigo, começaremos a criar uma classe-coleção de buffers de indicador. É esta classe que trará uma maior flexibilidade e facilitará criar buffers de indicador para trabalhar com eles com base em quaisquer símbolos e períodos gráficos ao criar programas-indicadores próprios usando esta biblioteca.

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



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

Arquivos anexados |
MQL5.zip (3722.34 KB)
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 42): classe de um objeto de buffer abstrato de indicador

Com este artigo começaremos a criar classes de buffers de indicador para a biblioteca DoEasy. Hoje, criaremos uma classe base de buffer abstrato que será o alicerce para a criação de diversos tipos de classes de buffer de indicador.

Linguagem MQL como um meio de marcação da interface gráfica de programas MQL (Parte 3). Designer de formulários Linguagem MQL como um meio de marcação da interface gráfica de programas MQL (Parte 3). Designer de formulários

Este artigo complementa a descrição da ideia de como construir uma interface de programa MQL com ajuda das construções da linguagem MQL. Um editor gráfico especial nos permitirá configurar interativamente um layout consistindo nas principais classes de elementos da GUI e, em seguida, as exportará para uma descrição MQL que será usada em nosso projeto MQL. Aqui são apresentados detalhes internos do editor e o manual do usuário. Códigos fonte estão anexados ao artigo.

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.

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.