Trabalhando com séries temporais na biblioteca DoEasy (Parte 57): objeto de dados do buffer do indicador

19 fevereiro 2021, 12:19
Artyom Trishkin
0
462

Sumário


Ideia

Toda a ideia por trás da construção e armazenamento de dados na biblioteca é baseada nas listas-coleções contendo um conjunto com dados do mesmo tipo que podemos selecionar, classificar e filtrar com a sequência que precisamos. Isso torna possível obter os conjuntos de dados necessários para sua posterior comparação e análise. A estrutura dos buffers dos indicadores também não difere da dos objetos em série que criamos anteriormente. Além disso, esses buffers podem estar em suas listas-coleções, onde podemos encontrar rapidamente os objetos com os dados de que precisamos. Mas, para criar uma coleção, devemos ter um objeto de dados - para ela - cujo conjunto completo será armazenado na lista-coleções.
Hoje criaremos um objeto de dados que armazenará todas as informações sobre o buffer do indicador numa barra e, consequentemente, informações sobre o indicador ao qual pertence o buffer cujos dados de uma barra são descritos pelo objeto que estamos criando.

Para cada buffer do indicador e para cada barra da série temporal, será criado um objeto de dados próprio que será adicionado a uma lista-coleção pertencente ao símbolo e período gráfico de tal indicador. Assim, para uma cópia do indicador, teremos um conjunto de dados para cada barra da série temporal de cada buffer.

Até agora, esse conceito de armazenamento de dados parece desnecessário, afinal, sempre podemos simplesmente obter os dados da barra da série temporal do buffer do indicador por meio de uma consulta direta vinda do indicador. Mas, no futuro, quando criarmos coleções com esses dados (nos próximos artigos), seremos capazes de encontrar rapidamente os dados necessários de quaisquer indicadores (para os quais as coleções serão criadas) e realizar uma análise comparativa. E para uma situação assim, o armazenamento de dados no cache da coleção já parece justificado.


Aprimorando as classes da biblioteca

Primeiro, como de costume, adicionamos novas mensagens de biblioteca.
No arquivo \MQL5\Include\DoEasy\Data.mqh inserimos os índices das novas mensagens:

//--- CIndicatorsCollection
   MSG_LIB_SYS_FAILED_ADD_IND_TO_LIST,                // Error. Failed to add indicator object to list
   MSG_LIB_SYS_INVALID_IND_POINTER,                   // Error. Invalid pointer to indicator object is passed
   MSG_LIB_SYS_IND_ID_EXIST,                          // Error. The indicator object with the ID already exists
   
//--- CDataInd
   MSG_LIB_TEXT_IND_DATA_IND_BUFFER_NUM,              // Indicator buffer number
   MSG_LIB_TEXT_IND_DATA_BUFFER_VALUE,                // Indicator buffer value
   
  };
//+------------------------------------------------------------------+

Depois, incluímos os textos das mensagens que correspondem aos índices recém-adicionados:

   {"Ошибка. Не удалось добавить объект-индикатор в список","Error. Failed to add indicator object to list"},
   {"Ошибка. Передан неверный указатель на объект-индикатор","Error. Invalid pointer to indicator object passed"},
   {"Ошибка. Уже существует объект-индикатор с идентификатором","Error. There is already exist an indicator object with ID"},
   
   {"Номер буфера индикатора","Indicator buffer number"},
   {"Значение буфера индикатора","Indicator buffer value"},
   
  };
//+---------------------------------------------------------------------+

Visto vez que o objeto de dados do buffer do indicador será armazenado na lista-coleção, para pesquisa e classificação, precisamos dotar este objeto com todas as propriedades inerentes a outros objetos da biblioteca que também são armazenados em listas.
No arquivo \MQL5\Include\DoEasy\Defines.mqh descrevemos todas as propriedades do novo objeto — propriedades inteiras do objeto:

//+------------------------------------------------------------------+
//| Data for working with indicator data                             |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Integer properties of indicator data                             |
//+------------------------------------------------------------------+
enum ENUM_IND_DATA_PROP_INTEGER
  {
   IND_DATA_PROP_TIME = 0,                                  // Start time of indicator data bar period
   IND_DATA_PROP_PERIOD,                                    // Indicator data period (timeframe)
   IND_DATA_PROP_INDICATOR_TYPE,                            // Indicator type
   IND_DATA_PROP_IND_BUFFER_NUM,                            // Indicator data buffer number
   IND_DATA_PROP_IND_ID,                                    // Indicator ID
  }; 
#define IND_DATA_PROP_INTEGER_TOTAL (5)                     // Total number of indicator data integer properties
#define IND_DATA_PROP_INTEGER_SKIP  (0)                     // Number of indicator data properties not used in sorting
//+------------------------------------------------------------------+

  • A classificação por tempo é o principal tipo de classificação, nela todos os dados serão organizados pela ordem de sequência dos dados no buffer do indicador dentro do terminal.
  • O valor do período gráfico é incluído nas propriedades inteiras para poder comparar posteriormente os valores de dois buffers do indicador em períodos gráficos diferentes.
  • O tipo de indicador conterá um valor do tipo a partir da enumeração ENUM_INDICATOR.
  • Número do buffer do indicador é um número de sequência desde zero e mais para o número de buffers do indicador.
  • Identificador do indicador é a propriedade através da qual será possível encontrar os dados do indicador ao qual foi atribuído um identificador no programa. Nós o discutimos no último artigo.

Propriedades reais do objeto:

//+------------------------------------------------------------------+
//| Indicator data real properties                                   |
//+------------------------------------------------------------------+
enum ENUM_IND_DATA_PROP_DOUBLE
  {
//--- bar data
   IND_DATA_PROP_BUFFER_VALUE = IND_DATA_PROP_INTEGER_TOTAL,// Indicator data value
  }; 
#define IND_DATA_PROP_DOUBLE_TOTAL  (1)                     // Total number of indicator data real properties
#define IND_DATA_PROP_DOUBLE_SKIP   (0)                     // Number of indicator data properties not used in sorting
//+------------------------------------------------------------------+

Aqui, temos apenas uma propriedade, o valor no buffer do indicador correspondente à barra para a qual o objeto de dados do indicador foi criado.

Propriedades de string do objeto:

//+------------------------------------------------------------------+
//| Indicator data string properties                                 |
//+------------------------------------------------------------------+
enum ENUM_IND_DATA_PROP_STRING
  {
   IND_DATA_PROP_SYMBOL = (IND_DATA_PROP_INTEGER_TOTAL+IND_DATA_PROP_DOUBLE_TOTAL), // Indicator data symbol
   IND_DATA_PROP_IND_NAME,                                  // Indicator name
   IND_DATA_PROP_IND_SHORTNAME,                             // Indicator short name
  };
#define IND_DATA_PROP_STRING_TOTAL  (3)                     // Total number of string properties of indicator data
//+------------------------------------------------------------------+

É com base nos valores dessas propriedades que será possível selecionar e ordenar os dados da coleção pelo símbolo (usado para calcular o indicador) e pelo nome do indicador (completo e curto).

Agora adicionamos todas as propriedades do objeto criado à lista de critérios possíveis de classificação:

//+------------------------------------------------------------------+
//| Possible criteria for indicator data sorting                     |
//+------------------------------------------------------------------+
#define FIRST_IND_DATA_DBL_PROP          (IND_DATA_PROP_INTEGER_TOTAL-IND_DATA_PROP_INTEGER_SKIP)
#define FIRST_IND_DATA_STR_PROP          (IND_DATA_PROP_INTEGER_TOTAL-IND_DATA_PROP_INTEGER_SKIP+IND_DATA_PROP_DOUBLE_TOTAL-IND_DATA_PROP_DOUBLE_SKIP)
enum ENUM_SORT_IND_DATA_MODE
  {
//--- Sort by integer properties
   SORT_BY_IND_DATA_TIME = 0,                               // Sort by bar period start time of indicator data
   SORT_BY_IND_DATA_PERIOD,                                 // Sort by indicator data period (timeframe)
   SORT_BY_IND_DATA_INDICATOR_TYPE,                         // Sort by indicator type
   SORT_BY_IND_DATA_IND_BUFFER_NUM,                         // Sort by indicator data buffer number
   SORT_BY_IND_DATA_IND_ID,                                 // Sort by indicator ID
//--- Sort by real properties
   SORT_BY_IND_DATA_BUFFER_VALUE = FIRST_IND_DATA_DBL_PROP, // Sort by indicator data value
//--- Sort by string properties
   SORT_BY_IND_DATA_SYMBOL = FIRST_IND_DATA_STR_PROP,       // Sort by indicator data symbol
   SORT_BY_IND_DATA_IND_NAME,                               // Sort by indicator name
   SORT_BY_IND_DATA_IND_SHORTNAME,                          // Sort by indicator short name
  };
//+------------------------------------------------------------------+


Objeto de dados do buffer de indicador

Já temos preparadas as propriedades do objeto. Agora criaremos um novo objeto que irá armazenar os dados de um buffer do indicador.

No diretório da biblioteca \MQL5\Include\DoEasy na pasta \Objects\Indicators\ criamos uma nova classe CDataInd no arquivo DataInd.mqh:

A classe será herdada do objeto base de todos os objetos da biblioteca CBaseObj.

Em princípio, o objeto contém campos e métodos padrão para os objetos da biblioteca e é idêntico ao objeto-barra que vimos no artigo 35, mas, ao contrário dele, tem menos propriedades (descrevemos todas as propriedades do objeto-dados do buffer do indicador nas enumerações acima).

Analisemos o corpo da classe do objeto de dados do buffer de indicador:

//+------------------------------------------------------------------+
//|                                                      DataInd.mqh |
//|                        Copyright 2020, MetaQuotes Software Corp. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, MetaQuotes Software Corp."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
#property strict    // Necessary for mql4
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "..\BaseObj.mqh"
//+------------------------------------------------------------------+
//| Indicator data class                                             |
//+------------------------------------------------------------------+
class CDataInd : public CBaseObj
  {
private:
   int               m_digits;                                    // Digits value of indicator data
   int               m_index;                                     // Bar index
   string            m_period_description;                        // Timeframe string description
   long              m_long_prop[IND_DATA_PROP_INTEGER_TOTAL];    // Integer properties
   double            m_double_prop[IND_DATA_PROP_DOUBLE_TOTAL];   // Real properties
   string            m_string_prop[IND_DATA_PROP_STRING_TOTAL];   // String properties

//--- Return the index of the array the object's (1) double and (2) string properties are located at
   int               IndexProp(ENUM_IND_DATA_PROP_DOUBLE property)   const { return(int)property-IND_DATA_PROP_INTEGER_TOTAL;                            }
   int               IndexProp(ENUM_IND_DATA_PROP_STRING property)   const { return(int)property-IND_DATA_PROP_INTEGER_TOTAL-IND_DATA_PROP_DOUBLE_TOTAL; }

public:
//--- Set (1) integer, (2) real and (3) string properties of indicator data
   void              SetProperty(ENUM_IND_DATA_PROP_INTEGER property,long value) { this.m_long_prop[property]=value;                               }
   void              SetProperty(ENUM_IND_DATA_PROP_DOUBLE property,double value){ this.m_double_prop[this.IndexProp(property)]=value;             }
   void              SetProperty(ENUM_IND_DATA_PROP_STRING property,string value){ this.m_string_prop[this.IndexProp(property)]=value;             }
//--- Return (1) integer, (2) real and (3) string property of indicator data from the properties array
   long              GetProperty(ENUM_IND_DATA_PROP_INTEGER property)   const { return this.m_long_prop[property];                                 }
   double            GetProperty(ENUM_IND_DATA_PROP_DOUBLE property)    const { return this.m_double_prop[this.IndexProp(property)];               }
   string            GetProperty(ENUM_IND_DATA_PROP_STRING property)    const { return this.m_string_prop[this.IndexProp(property)];               }

//--- Return the flag of the object supporting this property
   virtual bool      SupportProperty(ENUM_IND_DATA_PROP_INTEGER property)     { return true; }
   virtual bool      SupportProperty(ENUM_IND_DATA_PROP_DOUBLE property)      { return true; }
   virtual bool      SupportProperty(ENUM_IND_DATA_PROP_STRING property)      { return true; }
//--- Return itself
   CDataInd         *GetObject(void)                                          { return &this;}
//--- Set (1) symbol, timeframe and time for the object, (2) indicator type, (3) number of buffers, (4) number of data buffer,
//--- (5) ID, (6) data value, (7) name, (8) indicator short name
   void              SetSymbolPeriod(const string symbol,const ENUM_TIMEFRAMES timeframe,const datetime time);
   void              SetIndicatorType(const ENUM_INDICATOR type)              { this.SetProperty(IND_DATA_PROP_INDICATOR_TYPE,type);               }
   void              SetBufferNum(const int num)                              { this.SetProperty(IND_DATA_PROP_IND_BUFFER_NUM,num);                }
   void              SetIndicatorID(const int id)                             { this.SetProperty(IND_DATA_PROP_IND_ID,id);                         }
   void              SetBufferValue(const double value)                       { this.SetProperty(IND_DATA_PROP_BUFFER_VALUE,value);                }
   void              SetIndicatorName(const string name)                      { this.SetProperty(IND_DATA_PROP_IND_NAME,name);                     }
   void              SetIndicatorShortname(const string shortname)            { this.SetProperty(IND_DATA_PROP_IND_SHORTNAME,shortname);           }
   
//--- Compare CDataInd objects with each other by all possible properties (for sorting the lists by a specified object property)
   virtual int       Compare(const CObject *node,const int mode=0) const;
//--- Compare CDataInd objects with each other by all properties (to search equal objects)
   bool              IsEqual(CDataInd* compared_data) const;
//--- Constructors
                     CDataInd(){;}
                     CDataInd(const ENUM_INDICATOR ind_type,
                              const int ind_id,
                              const int buffer_num,
                              const string symbol,
                              const ENUM_TIMEFRAMES timeframe,
                              const datetime time);
                     
//+------------------------------------------------------------------+ 
//| Methods of simplified access to object properties                |
//+------------------------------------------------------------------+
//--- Return (1) bar period start time, (2) timeframe, (3) indicator type, (4) number of buffers, (5) buffer number, (6) indicator ID
   datetime          Time(void)                                         const { return (datetime)this.GetProperty(IND_DATA_PROP_TIME);                   }
   ENUM_TIMEFRAMES   Timeframe(void)                                    const { return (ENUM_TIMEFRAMES)this.GetProperty(IND_DATA_PROP_PERIOD);          }
   ENUM_INDICATOR    IndicatorType(void)                                const { return (ENUM_INDICATOR)this.GetProperty(IND_DATA_PROP_INDICATOR_TYPE);   }
   int               BufferNum(void)                                    const { return (ENUM_INDICATOR)this.GetProperty(IND_DATA_PROP_IND_BUFFER_NUM);   }
   int               IndicatorID(void)                                  const { return (ENUM_INDICATOR)this.GetProperty(IND_DATA_PROP_IND_ID);           }

//--- Return the price of indicator buffer data
   double            PriceValue(void)                                   const { return this.GetProperty(IND_DATA_PROP_BUFFER_VALUE);                     }
   
//--- Return (1) data symbol, (2) name, (3) indicator short name
   string            Symbol(void)                                       const { return this.GetProperty(IND_DATA_PROP_SYMBOL);                           }
   string            IndicatorName(void)                                const { return this.GetProperty(IND_DATA_PROP_IND_NAME);                         }
   string            IndicatorShortName(void)                           const { return this.GetProperty(IND_DATA_PROP_IND_SHORTNAME);                    }
//--- Return bar index on the specified timeframe the object bar time falls into
   int               Index(const ENUM_TIMEFRAMES timeframe)  const
                       { return ::iBarShift(this.Symbol(),(timeframe==PERIOD_CURRENT ? ::Period() : timeframe),this.Time());                             }
//--- Return Digits set for the object
   int               Digits(void)                                       const { return this.m_digits;                                                    }
//+------------------------------------------------------------------+
//| Description of properties of the object - indicator data         |
//+------------------------------------------------------------------+
//--- Return description of object's (1) integer, (2) real and (3) string property
   string            GetPropertyDescription(ENUM_IND_DATA_PROP_INTEGER property);
   string            GetPropertyDescription(ENUM_IND_DATA_PROP_DOUBLE property);
   string            GetPropertyDescription(ENUM_IND_DATA_PROP_STRING property);

//--- Return indicator type description
   string            IndicatorTypeDescription(void)                     const { return ::IndicatorTypeDescription(this.IndicatorType());                 }
//--- Display the description of object properties in the journal (full_prop=true - all properties, false - supported ones only)
   void              Print(const bool full_prop=false);
//--- Display a short description of the object in the journal
   virtual void      PrintShort(void);
//---
  };
//+------------------------------------------------------------------+

Vamos dar uma olhada rápida na composição da classe.

Na seção privada da classe estão localizados:

Três matrizes que armazenam as propriedades do objeto adequadas — inteiras, reais e de string.
Métodos que calculam o índice verdadeiro de uma propriedade de objeto na matriz correspondente
.
Variáveis-membros da classe para armazenar os valores de propriedades adicionais do objeto.

Na seção pública da classe estão localizados:

Métodos que gravam - nas matrizes das propriedades inteiras, reais e de string - o valor passado da propriedade do objeto.
Métodos que retornam desde a matriz o valor da propriedade inteira, real ou de string solicitada.
Métodos virtuais que retornam para cada propriedade o sinalizador que indica que o objeto mantém essa propriedade. Os métodos devem ser implementados em objetos descendentes do objeto e devem retornar false caso o objeto descendente não suporte a propriedade especificada. Todas as propriedades são suportadas no objeto de dados do buffer do indicador, e os métodos retornam true.

No primeiro artigo nós discutimos em detalhes toda a estrutura dos objetos da biblioteca, já aqui consideramos brevemente a implementação dos restantes métodos da classe.

Todos os métodos auxiliares para definir e retornar propriedades do objeto dão só uma comodidade adicional ao escrever programas e apenas repetem as ações dos métodos de instalação e recepção de propriedades do objeto - para que o usuário da biblioteca não tenha que lembrar de cor os nomes das constantes da enumeração das propriedades do objeto, e, em vez disso, para definir e receber essas propriedades, focando no nome desses métodos auxiliares.

O método virtual Compare() se destina a comparar dois objetos de acordo com a propriedade especificada. É definido na classe de objeto base da biblioteca padrão CObject, e deve retornar zero se os valores forem iguais, bem como 1 ou -1 se um dos valores comparados for maior ou menor, respectivamente. É usado para pesquisar e classificar no método Search() da biblioteca padrão, e deve ser substituído nas classes herdadas:

//+-------------------------------------------------------------------+
//| Compare CDataInd objects with each other by the specified property|
//+-------------------------------------------------------------------+
int CDataInd::Compare(const CObject *node,const int mode=0) const
  {
   const CDataInd *obj_compared=node;
//--- compare integer properties of two objects
   if(mode<IND_DATA_PROP_INTEGER_TOTAL)
     {
      long value_compared=obj_compared.GetProperty((ENUM_IND_DATA_PROP_INTEGER)mode);
      long value_current=this.GetProperty((ENUM_IND_DATA_PROP_INTEGER)mode);
      return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0);
     }
//--- compare real properties of two objects
   else if(mode<IND_DATA_PROP_DOUBLE_TOTAL+IND_DATA_PROP_INTEGER_TOTAL)
     {
      double value_compared=obj_compared.GetProperty((ENUM_IND_DATA_PROP_DOUBLE)mode);
      double value_current=this.GetProperty((ENUM_IND_DATA_PROP_DOUBLE)mode);
      return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0);
     }
//--- compare string properties of two objects
   else if(mode<IND_DATA_PROP_DOUBLE_TOTAL+IND_DATA_PROP_INTEGER_TOTAL+IND_DATA_PROP_STRING_TOTAL)
     {
      string value_compared=obj_compared.GetProperty((ENUM_IND_DATA_PROP_STRING)mode);
      string value_current=this.GetProperty((ENUM_IND_DATA_PROP_STRING)mode);
      return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0);
     }
   return 0;
  }
//+------------------------------------------------------------------+

O método para definir dois objetos de dados idênticos de buffers de indicador serve para comparar dois objetos-dados e retorna true somente se todos os campos dos dois objetos comparados forem iguais:

//+------------------------------------------------------------------+
//| Compare CDataInd objects with each other by all properties       |
//+------------------------------------------------------------------+
bool CDataInd::IsEqual(CDataInd *compared_obj) const
  {
   int beg=0, end=BAR_PROP_INTEGER_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_IND_DATA_PROP_INTEGER prop=(ENUM_IND_DATA_PROP_INTEGER)i;
      if(this.GetProperty(prop)!=compared_obj.GetProperty(prop)) return false; 
     }
   beg=end; end+=IND_DATA_PROP_DOUBLE_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_IND_DATA_PROP_DOUBLE prop=(ENUM_IND_DATA_PROP_DOUBLE)i;
      if(this.GetProperty(prop)!=compared_obj.GetProperty(prop)) return false; 
     }
   beg=end; end+=IND_DATA_PROP_STRING_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_IND_DATA_PROP_STRING prop=(ENUM_IND_DATA_PROP_STRING)i;
      if(this.GetProperty(prop)!=compared_obj.GetProperty(prop)) return false; 
     }
   return true;
  }
//+------------------------------------------------------------------+

Método para definir o símbolo, período gráfico e índice do objeto-dados do buffer na série temporal:

//+------------------------------------------------------------------+
//| Set symbol, timeframe and object bar start time                  |
//+------------------------------------------------------------------+
void CDataInd::SetSymbolPeriod(const string symbol,const ENUM_TIMEFRAMES timeframe,const datetime time)
  {
   this.SetProperty(IND_DATA_PROP_TIME,time);
   this.SetProperty(IND_DATA_PROP_SYMBOL,symbol);
   this.SetProperty(IND_DATA_PROP_PERIOD,timeframe);
  }
//+------------------------------------------------------------------+

Método que imprime no log descrições de todas as propriedades do objeto:

//+------------------------------------------------------------------+
//| Display object properties in the journal                         |
//+------------------------------------------------------------------+
void CDataInd::Print(const bool full_prop=false)
  {
   ::Print("============= ",CMessage::Text(MSG_LIB_PARAMS_LIST_BEG)," (",this.IndicatorShortName(),") =============");
   int beg=0, end=IND_DATA_PROP_INTEGER_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_IND_DATA_PROP_INTEGER prop=(ENUM_IND_DATA_PROP_INTEGER)i;
      if(!full_prop && !this.SupportProperty(prop)) continue;
      ::Print(this.GetPropertyDescription(prop));
     }
   ::Print("------");
   beg=end; end+=IND_DATA_PROP_DOUBLE_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_IND_DATA_PROP_DOUBLE prop=(ENUM_IND_DATA_PROP_DOUBLE)i;
      if(!full_prop && !this.SupportProperty(prop)) continue;
      ::Print(this.GetPropertyDescription(prop));
     }
   ::Print("------");
   beg=end; end+=IND_DATA_PROP_STRING_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_IND_DATA_PROP_STRING prop=(ENUM_IND_DATA_PROP_STRING)i;
      if(!full_prop && !this.SupportProperty(prop)) continue;
      ::Print(this.GetPropertyDescription(prop));
     }
   ::Print("============= ",CMessage::Text(MSG_LIB_PARAMS_LIST_END)," (",this.IndicatorShortName(),") =============\n");
  }
//+------------------------------------------------------------------+

Em três ciclos, matrizes de propriedades de objetos exibem descrições de cada propriedade. Se a propriedade não for suportada, não será exibida se o parâmetro de entrada do método full_prop tiver um valor false (por padrão).

Método virtual que imprime no log uma breve descrição do objeto:

//+------------------------------------------------------------------+
//| Display a short description of the object in the journal         |
//+------------------------------------------------------------------+
void CDataInd::PrintShort(void)
  {
   ::Print
     (
      this.IndicatorShortName(),
      " [",CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_BUFFER)," ",this.BufferNum(),
      ", ",CMessage::Text(MSG_SYM_STATUS_INDEX)," ",this.Index(this.Timeframe()),"]"
     );
  }
//+------------------------------------------------------------------+

O método exibe a descrição dos dados do buffer do indicador padrão no formato:

AMA(EURUSD,H1) [Буфер 0, Индекс 0]

para um indicador personalizado:

Examples\Custom Moving Average.ex5(EURUSD,H1) [Буфер 0, Индекс 1]

Nome curto do indicador que é incluído na descrição dos dados do buffer, pode ser alterado usando o método SetIndicatorShortname(). O método pode ser alterado em objetos herdeiros para exibir outras descrições do objeto-dados correspondente aos dados implementados no objeto herdeiro.

Métodos que retornam descrições de propriedades inteiras, reais e de string de um objeto:

//+------------------------------------------------------------------+
//| Return description of object's integer property                  |
//+------------------------------------------------------------------+
string CDataInd::GetPropertyDescription(ENUM_IND_DATA_PROP_INTEGER property)
  {
   return
     (
      property==IND_DATA_PROP_TIME           ?  CMessage::Text(MSG_LIB_TEXT_BAR_TIME)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::TimeToString(this.GetProperty(property),TIME_DATE|TIME_MINUTES|TIME_SECONDS)
         )  :
      property==IND_DATA_PROP_PERIOD         ?  CMessage::Text(MSG_LIB_TEXT_IND_TEXT_TIMEFRAME)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+this.m_period_description
         )  :
      property==IND_DATA_PROP_INDICATOR_TYPE ?  CMessage::Text(MSG_LIB_TEXT_IND_TEXT_TYPE)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+this.IndicatorTypeDescription()
         )  :
      property==IND_DATA_PROP_IND_BUFFER_NUM ?  CMessage::Text(MSG_LIB_TEXT_IND_DATA_IND_BUFFER_NUM)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==IND_DATA_PROP_IND_ID         ?  CMessage::Text(MSG_LIB_TEXT_IND_TEXT_ID)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      ""
     );
  }
//+------------------------------------------------------------------+
//| Return description of object's real property                     |
//+------------------------------------------------------------------+
string CDataInd::GetPropertyDescription(ENUM_IND_DATA_PROP_DOUBLE property)
  {
   int dg=(this.m_digits>0 ? this.m_digits : 1);
   return
     (
      property==IND_DATA_PROP_BUFFER_VALUE   ?  CMessage::Text(MSG_LIB_TEXT_IND_DATA_BUFFER_VALUE)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::DoubleToString(this.GetProperty(property),dg)
         )  :
      ""
     );
  }
//+------------------------------------------------------------------+
//| Return description of object's string property                   |
//+------------------------------------------------------------------+
string CDataInd::GetPropertyDescription(ENUM_IND_DATA_PROP_STRING property)
  {
   return
     (
      property==IND_DATA_PROP_SYMBOL         ? CMessage::Text(MSG_LIB_TEXT_IND_TEXT_SYMBOL)+": \""+this.GetProperty(property)+"\""     : 
      property==IND_DATA_PROP_IND_NAME       ? CMessage::Text(MSG_LIB_TEXT_IND_TEXT_NAME)+": \""+this.GetProperty(property)+"\""       : 
      property==IND_DATA_PROP_IND_SHORTNAME  ? CMessage::Text(MSG_LIB_TEXT_IND_TEXT_SHORTNAME)+": \""+this.GetProperty(property)+"\""  : 
      ""
     );
  }
//+------------------------------------------------------------------+

Aos métodos são transferidas as propriedades em conformidade e, dependendo de seu valor, são retornadas suas descrições de texto, especificadas no arquivo Datas.mqh e adicionadas por nós acima.

A classe possui dois construtores.

O primeiro construtor é o padrão e cria um objeto de dados vazio que, após ser criado, deve ser preenchido com todos os dados necessários.

O segundo construtor é o paramétrico e a ele são transferidos os dados necessários para criar um objeto com as propriedades básicas especificadas:

//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CDataInd::CDataInd(const ENUM_INDICATOR ind_type,
                   const int ind_id,
                   const int buffer_num,
                   const string symbol,
                   const ENUM_TIMEFRAMES timeframe,
                   const datetime time)
  {
   this.m_type=COLLECTION_INDICATORS_ID;
   this.m_digits=(int)::SymbolInfoInteger(symbol,SYMBOL_DIGITS)+1;
   this.m_period_description=TimeframeDescription(timeframe);
   this.SetSymbolPeriod(symbol,timeframe,time);
   this.SetIndicatorType(ind_type);
   this.SetBufferNum(buffer_num);
   this.SetIndicatorID(ind_id);
  }
//+------------------------------------------------------------------+

Aqui, para o objeto são logo indicados: o tipo de indicador (cujos dados de buffer são descritos pelo objeto); o identificador do indicador (que é uma propriedade pela que podemos obter os dados do buffer do objeto-indicador criado anteriormente, sendo que, para este objeto-indicador é definido um identificador para encontrá-lo rapidamente entre os outros criados); o número do buffer do indicador (cujos dados são descritos pelo objeto); o símbolo e timeframe (no qual é criado o objeto-indicador) e a hora de início da barra (cujos dados do buffer são descritos pelo objeto que está sendo criado).

Além de escrever os parâmetros acima, no construtor é definido o número de dígitos após o ponto decimal por padrão para exibir os valores do buffer do indicador (Digits do símbolo + 1 dígito), já na variável m_period_description é escrita a descrição do período gráfico, que pode ser definida uma vez ao criar um objeto. Incorporamos temporalmente o identificador da coleção de indicadores à variável m_type que declarada na classe pai CObject da biblioteca padrão. Posteriormente, ao criar coleções de dados de buffers de indicador, inserimos nesta variável o identificador dessa nova coleção.

Agora para termos a possibilidade de classificar objetos de dados na coleção (começaremos a fazer coleções a partir do próximo artigo), ao arquivo \MQL5\Include\DoEasy\Services\Select.mqh adicionamos os métodos para trabalhar com um novo objeto para seleção e classificação por propriedades.

Anexamos ao arquivo a classe recém-criada do objeto de dados dos buffers de indicador:

//+------------------------------------------------------------------+
//|                                                       Select.mqh |
//|                        Copyright 2020, MetaQuotes Software Corp. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, MetaQuotes Software Corp."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include <Arrays\ArrayObj.mqh>
#include "..\Objects\Orders\Order.mqh"
#include "..\Objects\Events\Event.mqh"
#include "..\Objects\Accounts\Account.mqh"
#include "..\Objects\Symbols\Symbol.mqh"
#include "..\Objects\PendRequest\PendRequest.mqh"
#include "..\Objects\Series\SeriesDE.mqh"
#include "..\Objects\Indicators\Buffer.mqh"
#include "..\Objects\Indicators\IndicatorDE.mqh"
#include "..\Objects\Indicators\DataInd.mqh"
//+------------------------------------------------------------------+

No final do corpo da classe, declaramos os métodos para trabalhar com a classe recém-criada do objeto de dados dos indicadores:

//+------------------------------------------------------------------+
//| Methods of work with indicator data                              |
//+------------------------------------------------------------------+
   //--- Return the list of indicator data with one out of (1) integer, (2) real and (3) string properties meeting a specified criterion
   static CArrayObj *ByIndicatorDataProperty(CArrayObj *list_source,ENUM_IND_DATA_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode);
   static CArrayObj *ByIndicatorDataProperty(CArrayObj *list_source,ENUM_IND_DATA_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode);
   static CArrayObj *ByIndicatorDataProperty(CArrayObj *list_source,ENUM_IND_DATA_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode);
   //--- Return the indicator data index in the list with the maximum value of (1) integer, (2) real and (3) string property of data
   static int        FindIndDataMax(CArrayObj *list_source,ENUM_IND_DATA_PROP_INTEGER property);
   static int        FindIndDataMax(CArrayObj *list_source,ENUM_IND_DATA_PROP_DOUBLE property);
   static int        FindIndDataMax(CArrayObj *list_source,ENUM_IND_DATA_PROP_STRING property);
   //--- Return the indicator data index in the list with the minimum value of (1) integer, (2) real and (3) string property of data
   static int        FindIndDataMin(CArrayObj *list_source,ENUM_IND_DATA_PROP_INTEGER property);
   static int        FindIndDataMin(CArrayObj *list_source,ENUM_IND_DATA_PROP_DOUBLE property);
   static int        FindIndDataMin(CArrayObj *list_source,ENUM_IND_DATA_PROP_STRING property);
//---
  };
//+------------------------------------------------------------------+

E escrevemos a implementação de todos os métodos declarados no final do arquivo:

//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Methods of work with indicator data lists                        |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Return the list of indicators data with one of integer           |
//| property meeting the specified criterion                         |
//+------------------------------------------------------------------+
CArrayObj *CSelect::ByIndicatorDataProperty(CArrayObj *list_source,ENUM_IND_DATA_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode)
  {
   if(list_source==NULL) return NULL;
   CArrayObj *list=new CArrayObj();
   if(list==NULL) return NULL;
   list.FreeMode(false);
   ListStorage.Add(list);
   int total=list_source.Total();
   for(int i=0; i<total; i++)
     {
      CDataInd *obj=list_source.At(i);
      if(!obj.SupportProperty(property)) continue;
      long obj_prop=obj.GetProperty(property);
      if(CompareValues(obj_prop,value,mode)) list.Add(obj);
     }
   return list;
  }
//+------------------------------------------------------------------+
//| Return the list of indicators data with one of real              |
//| property meeting the specified criterion                         |
//+------------------------------------------------------------------+
CArrayObj *CSelect::ByIndicatorDataProperty(CArrayObj *list_source,ENUM_IND_DATA_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode)
  {
   if(list_source==NULL) return NULL;
   CArrayObj *list=new CArrayObj();
   if(list==NULL) return NULL;
   list.FreeMode(false);
   ListStorage.Add(list);
   for(int i=0; i<list_source.Total(); i++)
     {
      CDataInd *obj=list_source.At(i);
      if(!obj.SupportProperty(property)) continue;
      double obj_prop=obj.GetProperty(property);
      if(CompareValues(obj_prop,value,mode)) list.Add(obj);
     }
   return list;
  }
//+------------------------------------------------------------------+
//| Return the list of indicators data with one of string            |
//| property meeting the specified criterion                         |
//+------------------------------------------------------------------+
CArrayObj *CSelect::ByIndicatorDataProperty(CArrayObj *list_source,ENUM_IND_DATA_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode)
  {
   if(list_source==NULL) return NULL;
   CArrayObj *list=new CArrayObj();
   if(list==NULL) return NULL;
   list.FreeMode(false);
   ListStorage.Add(list);
   for(int i=0; i<list_source.Total(); i++)
     {
      CDataInd *obj=list_source.At(i);
      if(!obj.SupportProperty(property)) continue;
      string obj_prop=obj.GetProperty(property);
      if(CompareValues(obj_prop,value,mode)) list.Add(obj);
     }
   return list;
  }
//+------------------------------------------------------------------+
//| Return the indicator data index in the list                      |
//| with the maximum integer property value                          |
//+------------------------------------------------------------------+
int CSelect::FindIndDataMax(CArrayObj *list_source,ENUM_IND_DATA_PROP_INTEGER property)
  {
   if(list_source==NULL) return WRONG_VALUE;
   int index=0;
   CDataInd *max_obj=NULL;
   int total=list_source.Total();
   if(total==0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      CDataInd *obj=list_source.At(i);
      long obj1_prop=obj.GetProperty(property);
      max_obj=list_source.At(index);
      long obj2_prop=max_obj.GetProperty(property);
      if(CompareValues(obj1_prop,obj2_prop,MORE)) index=i;
     }
   return index;
  }
//+------------------------------------------------------------------+
//| Return the indicator data index in the list                      |
//| with the maximum real property value                             |
//+------------------------------------------------------------------+
int CSelect::FindIndDataMax(CArrayObj *list_source,ENUM_IND_DATA_PROP_DOUBLE property)
  {
   if(list_source==NULL) return WRONG_VALUE;
   int index=0;
   CDataInd *max_obj=NULL;
   int total=list_source.Total();
   if(total==0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      CDataInd *obj=list_source.At(i);
      double obj1_prop=obj.GetProperty(property);
      max_obj=list_source.At(index);
      double obj2_prop=max_obj.GetProperty(property);
      if(CompareValues(obj1_prop,obj2_prop,MORE)) index=i;
     }
   return index;
  }
//+------------------------------------------------------------------+
//| Return the indicator data index in the list                      |
//| with the maximum string property value                           |
//+------------------------------------------------------------------+
int CSelect::FindIndDataMax(CArrayObj *list_source,ENUM_IND_DATA_PROP_STRING property)
  {
   if(list_source==NULL) return WRONG_VALUE;
   int index=0;
   CDataInd *max_obj=NULL;
   int total=list_source.Total();
   if(total==0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      CDataInd *obj=list_source.At(i);
      string obj1_prop=obj.GetProperty(property);
      max_obj=list_source.At(index);
      string obj2_prop=max_obj.GetProperty(property);
      if(CompareValues(obj1_prop,obj2_prop,MORE)) index=i;
     }
   return index;
  }
//+------------------------------------------------------------------+
//| Return the indicator data index in the list                      |
//| with the minimum integer property value                          |
//+------------------------------------------------------------------+
int CSelect::FindIndDataMin(CArrayObj* list_source,ENUM_IND_DATA_PROP_INTEGER property)
  {
   int index=0;
   CDataInd *min_obj=NULL;
   int total=list_source.Total();
   if(total==0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      CDataInd *obj=list_source.At(i);
      long obj1_prop=obj.GetProperty(property);
      min_obj=list_source.At(index);
      long obj2_prop=min_obj.GetProperty(property);
      if(CompareValues(obj1_prop,obj2_prop,LESS)) index=i;
     }
   return index;
  }
//+------------------------------------------------------------------+
//| Return the indicator data index in the list                      |
//| with the minimum real property value                             |
//+------------------------------------------------------------------+
int CSelect::FindIndDataMin(CArrayObj* list_source,ENUM_IND_DATA_PROP_DOUBLE property)
  {
   int index=0;
   CDataInd *min_obj=NULL;
   int total=list_source.Total();
   if(total== 0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      CDataInd *obj=list_source.At(i);
      double obj1_prop=obj.GetProperty(property);
      min_obj=list_source.At(index);
      double obj2_prop=min_obj.GetProperty(property);
      if(CompareValues(obj1_prop,obj2_prop,LESS)) index=i;
     }
   return index;
  }
//+------------------------------------------------------------------+
//| Return the indicator data index in the list                      |
//| with the minimum string property value                           |
//+------------------------------------------------------------------+
int CSelect::FindIndDataMin(CArrayObj* list_source,ENUM_IND_DATA_PROP_STRING property)
  {
   int index=0;
   CDataInd *min_obj=NULL;
   int total=list_source.Total();
   if(total==0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      CDataInd *obj=list_source.At(i);
      string obj1_prop=obj.GetProperty(property);
      min_obj=list_source.At(index);
      string obj2_prop=min_obj.GetProperty(property);
      if(CompareValues(obj1_prop,obj2_prop,LESS)) index=i;
     }
   return index;
  }
//+------------------------------------------------------------------+

No terceiro artigo que descreve a criação da biblioteca vimos em detalhes o trabalho dos dados dos métodos.

Um novo objeto de dados de buffers de indicador foi criado e agora podemos testar sua operação.


Teste

Para realizar o teste, pegamos o Expert Advisor do artigo anterior e o salvamos na nova pasta
\MQL5\Experts\TestDoEasy\Part57\ usando o novo nome TestDoEasyPart57.mq5.

No Expert Advisor anterior criamos quatro objetos-indicadores — dois padrão e dois personalizados. Indicadores idênticos diferiam entre si apenas em relação a outros parâmetros. Aqui, criaremos os mesmos quatro indicadores, mas para exibir seus dados, criaremos para cada um deles dois objetos de dados de buffers - para a barra atual (zero) e a última (primeira) barra da série temporal. Exibimos os dados de todos os objetos nos comentários do gráfico de símbolos.

Até que as coleções desses buffers de indicadores tenham sido criadas, geraremos o acesso à classe criada diretamente do Expert Advisor.
Para isso, anexamos dada classe ao arquivo do Expert Advisor:

//+------------------------------------------------------------------+
//|                                             TestDoEasyPart57.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\DataInd.mqh>
//--- enums

Aqui, na escopo das variáveis globais do programa, adicionamos as variáveis-ponteiros para os objetos de dados dos indicadores:

//--- Arrays of custom indicator parameters
MqlParam       param_ma1[];
MqlParam       param_ma2[];
//--- Pointers to indicator data objects
CDataInd      *data_ma1_0=NULL;
CDataInd      *data_ma1_1=NULL;
CDataInd      *data_ma2_0=NULL;
CDataInd      *data_ma2_1=NULL;
CDataInd      *data_ama1_0=NULL;
CDataInd      *data_ama1_1=NULL;
CDataInd      *data_ama2_0=NULL;
CDataInd      *data_ama2_1=NULL;
//+------------------------------------------------------------------+

Ao criar novos objetos de dados, vamos salvar ponteiros para os objetos recém-criados nessas variáveis, para que posteriormente possamos acessá-los.

Visto vez que iremos criar todos os objetos usando o operador new, todos eles devem ser removidos por conta própria, o que faremos no manipulador OnDeinit() do Expert Advisor (após criar uma coleção de dados de buffers de indicador, não haverá necessidade dessas ações no Expert Advisor):

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- Remove EA graphical objects by an object name prefix
   ObjectsDeleteAll(0,prefix);
   Comment("");
//--- Remove created data objects of MA indicators
   if(CheckPointer(data_ma1_0)==POINTER_DYNAMIC)
      delete data_ma1_0;
   if(CheckPointer(data_ma1_1)==POINTER_DYNAMIC)
      delete data_ma1_1;
   if(CheckPointer(data_ma2_0)==POINTER_DYNAMIC)
      delete data_ma2_0;
   if(CheckPointer(data_ma2_1)==POINTER_DYNAMIC)
      delete data_ma2_1;
//--- Remove created data objects of AMA indicators
   if(CheckPointer(data_ama1_0)==POINTER_DYNAMIC)
      delete data_ama1_0;
   if(CheckPointer(data_ama1_1)==POINTER_DYNAMIC)
      delete data_ama1_1;
   if(CheckPointer(data_ama2_0)==POINTER_DYNAMIC)
      delete data_ama2_0;
   if(CheckPointer(data_ama2_1)==POINTER_DYNAMIC)
      delete data_ama2_1;
//--- Deinitialize library
   engine.OnDeinit();
  }
//+------------------------------------------------------------------+

No manipulador OnTick() criamos novos objetos (apenas se ainda não foram criados), os preenchemos com todos os dados e valores necessários e imprimimos as descrições dos objetos no log e no gráfico mostramos os valores dos buffers dos indicadores descritos pelos objetos:

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//--- Handle the NewTick event in the library
   engine.OnTick(rates_data);

//--- If working in the tester
   if(MQLInfoInteger(MQL_TESTER))
     {
      engine.OnTimer(rates_data);   // Working in the timer
      PressButtonsControl();        // Button pressing control
      engine.EventsHandling();      // Working with events
     }
//--- Get custom indicator objects
   CIndicatorDE *ma1=engine.GetIndicatorsCollection().GetIndByID(MA1);
   CIndicatorDE *ma2=engine.GetIndicatorsCollection().GetIndByID(MA2);
   CIndicatorDE *ama1=engine.GetIndicatorsCollection().GetIndByID(AMA1);
   CIndicatorDE *ama2=engine.GetIndicatorsCollection().GetIndByID(AMA2);
   
//--- Write bar start time 0 and 1 to specify it in properties of further created objects
   datetime time0=iTime(ma1.Symbol(),ma1.Timeframe(),0);
   datetime time1=iTime(ma1.Symbol(),ma1.Timeframe(),1);
   if(time0==0 || time1==0)
      return;
      
//--- Create data objects of МА1 and МА2 for bars 0 and 1 (unless the objects are created)
   if(data_ma1_0==NULL) data_ma1_0=new CDataInd(ma1.TypeIndicator(),ma1.ID(),0,ma1.Symbol(),ma1.Timeframe(),time0);
   if(data_ma1_1==NULL) data_ma1_1=new CDataInd(ma1.TypeIndicator(),ma1.ID(),0,ma1.Symbol(),ma1.Timeframe(),time1);
   if(data_ma2_0==NULL) data_ma2_0=new CDataInd(ma2.TypeIndicator(),ma2.ID(),0,ma2.Symbol(),ma2.Timeframe(),time0);
   if(data_ma2_1==NULL) data_ma2_1=new CDataInd(ma2.TypeIndicator(),ma2.ID(),0,ma2.Symbol(),ma2.Timeframe(),time1);
   if(data_ma1_0==NULL || data_ma1_1==NULL || data_ma2_0==NULL || data_ma2_1==NULL) return;
   
//--- Set parameters of data object of indicator МА1, bar 0
//--- and add indicator buffer data to the object
   data_ma1_0.SetIndicatorType(ma1.TypeIndicator());
   data_ma1_0.SetIndicatorName(ma1.Name());
   data_ma1_0.SetIndicatorShortname(ma1.ShortName());
   data_ma1_0.SetBufferValue(ma1.GetDataBuffer(0,time0));
//--- Set parameters of data object of indicator МА1, bar 1
//--- and add indicator buffer data to the object
   data_ma1_1.SetIndicatorType(ma1.TypeIndicator());
   data_ma1_1.SetIndicatorName(ma1.Name());
   data_ma1_1.SetIndicatorShortname(ma1.ShortName());
   data_ma1_1.SetBufferValue(ma1.GetDataBuffer(0,time1));
//--- Set parameters of data object of indicator МА2, bar 0
//--- and add indicator buffer data to the object
   data_ma2_0.SetIndicatorType(ma2.TypeIndicator());
   data_ma2_0.SetIndicatorName(ma2.Name());
   data_ma2_0.SetIndicatorShortname(ma2.ShortName());
   data_ma2_0.SetBufferValue(ma2.GetDataBuffer(0,time0));
//--- Set parameters of data object of indicator МА2, bar 1
//--- and add indicator buffer data to the object
   data_ma2_1.SetIndicatorType(ma2.TypeIndicator());
   data_ma2_1.SetIndicatorName(ma2.Name());
   data_ma2_1.SetIndicatorShortname(ma2.ShortName());
   data_ma2_1.SetBufferValue(ma2.GetDataBuffer(0,time1));
   
//--- Create data objects of АМА1 and АМА2 for bars 0 and 1 (unless the objects are already created)
   if(data_ama1_0==NULL) data_ama1_0=new CDataInd(ama1.TypeIndicator(),ama1.ID(),0,ama1.Symbol(),ama1.Timeframe(),time0);
   if(data_ama1_1==NULL) data_ama1_1=new CDataInd(ama1.TypeIndicator(),ama1.ID(),0,ama1.Symbol(),ama1.Timeframe(),time1);
   if(data_ama2_0==NULL) data_ama2_0=new CDataInd(ama2.TypeIndicator(),ama2.ID(),0,ama2.Symbol(),ama2.Timeframe(),time0);
   if(data_ama2_1==NULL) data_ama2_1=new CDataInd(ama2.TypeIndicator(),ama2.ID(),0,ama2.Symbol(),ama2.Timeframe(),time1);
   if(data_ama1_0==NULL || data_ama1_1==NULL || data_ama2_0==NULL || data_ama2_1==NULL) return;
//--- Set parameters of data object of indicator АМА1, bar 0
//--- and add indicator buffer data to the object
   data_ama1_0.SetIndicatorType(ama1.TypeIndicator());
   data_ama1_0.SetIndicatorName(ama1.Name());
   data_ama1_0.SetIndicatorShortname(ama1.ShortName());
   data_ama1_0.SetBufferValue(ama1.GetDataBuffer(0,time0));
//--- Set parameters of data object of indicator АМА1, bar 1
//--- and add indicator buffer data to the object
   data_ama1_1.SetIndicatorType(ama1.TypeIndicator());
   data_ama1_1.SetIndicatorName(ama1.Name());
   data_ama1_1.SetIndicatorShortname(ama1.ShortName());
   data_ama1_1.SetBufferValue(ama1.GetDataBuffer(0,time1));
//--- Set parameters of data object of indicator АМА2, bar 0
//--- and add indicator buffer data to the object
   data_ama2_0.SetIndicatorType(ama2.TypeIndicator());
   data_ama2_0.SetIndicatorName(ama2.Name());
   data_ama2_0.SetIndicatorShortname(ama2.ShortName());
   data_ama2_0.SetBufferValue(ama2.GetDataBuffer(0,time0));
//--- Set parameters of data object of indicator АМА2, bar 1
//--- and add indicator buffer data to the object
   data_ama2_1.SetIndicatorType(ama2.TypeIndicator());
   data_ama2_1.SetIndicatorName(ama2.Name());
   data_ama2_1.SetIndicatorShortname(ama2.ShortName());
   data_ama2_1.SetBufferValue(ama2.GetDataBuffer(0,time1));

//--- During the first launch, print full and short data of created indicator data objects
   static bool first_start=true;
   if(first_start)
     {
      //--- Full data of buffers МА1 and МА2
      data_ma1_0.Print();
      data_ma1_1.Print();
      data_ma2_0.Print();
      data_ma2_1.Print();
      //--- Full data of buffers АМА1 and АМА2
      data_ama1_0.Print();
      data_ama1_1.Print();
      data_ama2_0.Print();
      data_ama2_1.Print();
      //--- Short data of buffers МА1 and МА2
      data_ma1_0.PrintShort();
      data_ma1_1.PrintShort();
      data_ma2_0.PrintShort();
      data_ma2_1.PrintShort();
      //--- Short data of buffers АМА1 and АМА2
      data_ama1_0.PrintShort();
      data_ama1_1.PrintShort();
      data_ama2_0.PrintShort();
      data_ama2_1.PrintShort();
      //---
      first_start=false;
     }
//--- Display the values of indicator buffers to comment on the chart from data objects
   Comment
     (
      "ma1(1)=",DoubleToString(data_ma1_1.PriceValue(),6),", ma1(0)=",DoubleToString(data_ma1_0.PriceValue(),data_ma1_0.Digits()),", ",
      "ma2(1)=",DoubleToString(data_ma2_1.PriceValue(),6),", ma2(0)=",DoubleToString(data_ma2_0.PriceValue(),data_ma2_0.Digits()),"\n",
      "ama1(1)=",DoubleToString(data_ama1_1.PriceValue(),6),", ama1(0)=",DoubleToString(data_ama1_0.PriceValue(),data_ama1_0.Digits()),", ",
      "ama2(1)=",DoubleToString(data_ama2_1.PriceValue(),6),", ama2(0)=",DoubleToString(data_ama2_0.PriceValue(),data_ama2_0.Digits())
     );
   
//--- If the trailing flag is set
   if(trailing_on)
     {
      TrailingPositions();          // Trailing positions
      TrailingOrders();             // Trailing pending orders
     }
  }
//+------------------------------------------------------------------+

Posteriormente, faremos todas as ações para criar objetos e preencher suas propriedades com valores na classe-coleção de dados dos buffers dos indicadores. Por enquanto, precisamos apenas verificar o funcionamento dos objetos criados hoje.

Vamos compilar o Expert Advisor e executá-lo no gráfico do símbolo, tendo previamente definido as configurações para usar apenas o símbolo e o período gráfico atuais. O log irá mostrar os dados de todos os objetos-indicadores criados e os objetos-dados:

Account 8550475: Artyom Trishkin (MetaQuotes Software Corp.) 10425.23 USD, 1:100, Hedge, Demo account MetaTrader 5
--- Initializing the "DoEasy" library ---
Work only with the current symbol: "EURUSD"
Work only with the current Period: H1
Symbol time series EURUSD: 
- Timeseries "EURUSD" H1: Required: 1000, Actual: 1000, Created: 1000, On server: 6351
Library initialization time: 00:00:00.000
============= The beginning of the parameter list: "Custom indicator" =============
Indicator status: Custom indicator
Indicator type: CUSTOM
Indicator timeframe: H1
Indicator handle: 10
Indicator group: Trend indicator
Indicator ID: 1
------
Empty value for plotting, for which there is no drawing: EMPTY_VALUE
------
Indicator symbol: EURUSD
Indicator name: "Examples\Custom Moving Average.ex5"
Indicator shortname: "Examples\Custom Moving Average.ex5(EURUSD,H1)"
 --- Indicator parameters --- 
 - [1] Type int: 13
 - [2] Type int: 0
 - [3] Type int: 0
================== End of the parameter list: "Custom indicator" ==================

============= The beginning of the parameter list: "Custom indicator" =============
Indicator status: Custom indicator
Indicator type: CUSTOM
Indicator timeframe: H1
Indicator handle: 11
Indicator group: Trend indicator
Indicator ID: 2
------
Empty value for plotting, for which there is no drawing: EMPTY_VALUE
------
Indicator symbol: EURUSD
Indicator name: "Examples\Custom Moving Average.ex5"
Indicator shortname: "Examples\Custom Moving Average.ex5(EURUSD,H1)"
 --- Indicator parameters --- 
 - [1] Type int: 13
 - [2] Type int: 0
 - [3] Type int: 0
 - [4] Type int: 2
================== End of the parameter list: "Custom indicator" ==================
 
============= The beginning of the parameter list: "Standard indicator" =============
Indicator status: Standard indicator
Indicator type: AMA
Indicator timeframe: H1
Indicator handle: 12
Indicator group: Trend indicator
Indicator ID: 3
------
Empty value for plotting, for which there is no drawing: EMPTY_VALUE
------
Indicator symbol: EURUSD
Indicator name: "Adaptive Moving Average"
Indicator shortname: "AMA(EURUSD,H1)"
 --- Indicator parameters --- 
 - Averaging period: 9
 - Fast MA period: 2
 - Slow MA period: 30
 - Horizontal shift of the indicator: 0
 - Price type or handle: CLOSE
================== End of the parameter list: "Standard indicator" ==================

============= The beginning of the parameter list: "Standard indicator" =============
Indicator status: Standard indicator
Indicator type: AMA
Indicator timeframe: H1
Indicator handle: 13
Indicator group: Trend indicator
Indicator ID: 4
------
Empty value for plotting, for which there is no drawing: EMPTY_VALUE
------
Indicator symbol: EURUSD
Indicator name: "Adaptive Moving Average"
Indicator shortname: "AMA(EURUSD,H1)"
 --- Indicator parameters --- 
 - Averaging period: 14
 - Fast MA period: 2
 - Slow MA period: 30
 - Horizontal shift of the indicator: 0
 - Price type or handle: CLOSE
================== End of the parameter list: "Standard indicator" ==================

Custom indicator Examples\Custom Moving Average.ex5 EURUSD H1 [handle 10, id #1]
Custom indicator Examples\Custom Moving Average.ex5 EURUSD H1 [handle 11, id #2]
Standard indicator Adaptive Moving Average EURUSD H1 [handle 12, id #3]
Standard indicator Adaptive Moving Average EURUSD H1 [handle 13, id #4]

============= The beginning of the parameter list (Examples\Custom Moving Average.ex5(EURUSD,H1)) =============
Period start time: 2020.11.18 10:00:00
Indicator timeframe: H1
Indicator type: CUSTOM
Indicator buffer number: 0
Indicator ID: 1
------
Indicator buffer value: 1.186694
------
Indicator symbol: "EURUSD"
Indicator name: "Examples\Custom Moving Average.ex5"
Indicator shortname: "Examples\Custom Moving Average.ex5(EURUSD,H1)"
============= End of the parameter list (Examples\Custom Moving Average.ex5(EURUSD,H1)) =============

============= The beginning of the parameter list (Examples\Custom Moving Average.ex5(EURUSD,H1)) =============
Period start time: 2020.11.18 09:00:00
Indicator timeframe: H1
Indicator type: CUSTOM
Indicator buffer number: 0
Indicator ID: 1
------
Indicator buffer value: 1.186535
------
Indicator symbol: "EURUSD"
Indicator name: "Examples\Custom Moving Average.ex5"
Indicator shortname: "Examples\Custom Moving Average.ex5(EURUSD,H1)"
============= End of the parameter list (Examples\Custom Moving Average.ex5(EURUSD,H1)) =============

============= The beginning of the parameter list (Examples\Custom Moving Average.ex5(EURUSD,H1)) =============
Period start time: 2020.11.18 10:00:00
Indicator timeframe: H1
Indicator type: CUSTOM
Indicator buffer number: 0
Indicator ID: 2
------
Indicator buffer value: 1.186552
------
Indicator symbol: "EURUSD"
Indicator name: "Examples\Custom Moving Average.ex5"
Indicator shortname: "Examples\Custom Moving Average.ex5(EURUSD,H1)"
============= End of the parameter list (Examples\Custom Moving Average.ex5(EURUSD,H1)) =============

============= The beginning of the parameter list (Examples\Custom Moving Average.ex5(EURUSD,H1)) =============
Period start time: 2020.11.18 09:00:00
Indicator timeframe: H1
Indicator type: CUSTOM
Indicator buffer number: 0
Indicator ID: 2
------
Indicator buffer value: 1.186438
------
Indicator symbol: "EURUSD"
Indicator name: "Examples\Custom Moving Average.ex5"
Indicator shortname: "Examples\Custom Moving Average.ex5(EURUSD,H1)"
============= End of the parameter list (Examples\Custom Moving Average.ex5(EURUSD,H1)) =============

============= The beginning of the parameter list (AMA(EURUSD,H1)) =============
Period start time: 2020.11.18 10:00:00
Indicator timeframe: H1
Indicator type: AMA
Indicator buffer number: 0
Indicator ID: 3
------
Indicator buffer value: 1.186992
------
Indicator symbol: "EURUSD"
Indicator name: "Adaptive Moving Average"
Indicator shortname: "AMA(EURUSD,H1)"
============= End of the parameter list (AMA(EURUSD,H1)) =============

============= The beginning of the parameter list (AMA(EURUSD,H1)) =============
Period start time: 2020.11.18 09:00:00
Indicator timeframe: H1
Indicator type: AMA
Indicator buffer number: 0
Indicator ID: 3
------
Indicator buffer value: 1.186725
------
Indicator symbol: "EURUSD"
Indicator name: "Adaptive Moving Average"
Indicator shortname: "AMA(EURUSD,H1)"
============= End of the parameter list (AMA(EURUSD,H1)) =============

============= The beginning of the parameter list (AMA(EURUSD,H1)) =============
Period start time: 2020.11.18 10:00:00
Indicator timeframe: H1
Indicator type: AMA
Indicator buffer number: 0
Indicator ID: 4
------
Indicator buffer value: 1.186548
------
Indicator symbol: "EURUSD"
Indicator name: "Adaptive Moving Average"
Indicator shortname: "AMA(EURUSD,H1)"
============= End of the parameter list (AMA(EURUSD,H1)) =============

============= The beginning of the parameter list (AMA(EURUSD,H1)) =============
Period start time: 2020.11.18 09:00:00
Indicator timeframe: H1
Indicator type: AMA
Indicator buffer number: 0
Indicator ID: 4
------
Indicator buffer value: 1.186403
------
Indicator symbol: "EURUSD"
Indicator name: "Adaptive Moving Average"
Indicator shortname: "AMA(EURUSD,H1)"
============= End of the parameter list (AMA(EURUSD,H1)) =============

Examples\Custom Moving Average.ex5(EURUSD,H1) [Buffer 0, Index 0]
Examples\Custom Moving Average.ex5(EURUSD,H1) [Buffer 0, Index 1]
Examples\Custom Moving Average.ex5(EURUSD,H1) [Buffer 0, Index 0]
Examples\Custom Moving Average.ex5(EURUSD,H1) [Buffer 0, Index 1]
AMA(EURUSD,H1) [Buffer 0, Index 0]
AMA(EURUSD,H1) [Buffer 0, Index 1]
AMA(EURUSD,H1) [Buffer 0, Index 0]
AMA(EURUSD,H1) [Buffer 0, Index 1]

já o gráfico mostrará, no comentário, os dados correspondentes aos dados nos buffers de indicador na primeira e na barra zero:


O que vem agora?

No próximo artigo, criaremos uma classe-coleção para dados de buffers de indicador.

Todos os arquivos da versão atual da biblioteca e o arquivo do EA de teste para MQL5 estão anexados abaixo. Você pode baixá-los e testar tudo sozinho.
Quero ressaltar que a classe-coleção de indicadores está atualmente em desenvolvimento, portanto, não é recomendável usá-la em nossos programas.
Se você tiver perguntas, comentários e sugestões, poderá expressá-los nos comentários do artigo.

Complementos

Artigos desta série:

Trabalhando com séries temporais na biblioteca DoEasy (Parte 35): Objeto "Barra" e lista-série temporal do símbolo
Trabalhando com séries temporais na biblioteca DoEasy (Parte 36): objeto das séries temporais de todos os períodos usados do símbolo
Trabalhando com séries temporais na biblioteca DoEasy (Parte 37): coleção de séries temporais - banco de dados de séries temporais para símbolos e períodos
Trabalhando com séries temporais na biblioteca DoEasy (Parte 38): coleção de séries temporais - atualização em tempo real e acesso aos dados do programa
Trabalhando com séries temporais na biblioteca DoEasy (Parte 39): indicadores com base na biblioteca - preparação de dados e eventos das séries temporais
Trabalhando com séries temporais na biblioteca DoEasy (Parte 40): indicadores com base na biblioteca - atualização de dados em tempo real
Trabalhando com séries temporais na biblioteca DoEasy (Parte 41): exemplo de indicador multissímbolo multiperíodo
Trabalhando com séries temporais na biblioteca DoEasy (Parte 42): classe de um objeto de buffer abstrato de indicador
Trabalhando com séries temporais na biblioteca DoEasy (Parte 43): classes de objetos de buffers de indicador
Trabalhando com séries temporais na biblioteca DoEasy (Parte 44): classe-coleção de objetos de buffers de indicador
Trabalhando com séries temporais na biblioteca DoEasy (Parte 45): buffers de indicador multiperíodo
Trabalhando com séries temporais na biblioteca DoEasy (Parte 46): buffers de indicador multiperíodos multissímbolos
Trabalhando com séries temporais na biblioteca DoEasy (Parte 47): indicadores padrão multiperíodos multissímbolos
Trabalhando com séries temporais na biblioteca DoEasy (Parte 48): indicadores multissímbolos multiperíodos num buffer de uma subjanela
Trabalhando com séries temporais na biblioteca DoEasy (Parte 49): indicadores padrão multiperíodos multissímbolos multibuffer
Trabalhando com séries temporais na biblioteca DoEasy (Parte 50): indicadores padrão multiperíodos multissímbolos com deslocamento
Trabalhando com séries temporais na biblioteca DoEasy (Parte 51): indicadores padrão multiperíodos multissímbolos compostos
Trabalhando com séries temporais na biblioteca DoEasy (Parte 52): natureza multiplataforma de indicadores padrão multiperíodos multissímbolos de buffer único
Trabalhando com séries temporais na biblioteca DoEasy (Parte 53): classe do indicador base abstrato
Trabalhando com séries temporais na biblioteca DoEasy (Parte 54): classes herdeiras do indicador base abstrato
Trabalhando com séries temporais na biblioteca DoEasy (Parte 55): classe-coleção de indicadores
Trabalhando com séries temporais na biblioteca DoEasy (Parte 56): objeto de indicador personalizado, obtenção de dados a partir de objetos-indicadores numa coleção


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

Arquivos anexados |
MQL5.zip (3858.21 KB)
Trabalhando com séries temporais na biblioteca DoEasy (Parte 56): objeto de indicador personalizado, obtenção de dados a partir de objetos-indicadores numa coleção Trabalhando com séries temporais na biblioteca DoEasy (Parte 56): objeto de indicador personalizado, obtenção de dados a partir de objetos-indicadores numa coleção

Neste artigo, veremos a criação de um objeto de indicador personalizado para ser usado em Expert Advisors. Vamos modificar ligeiramente as classes da biblioteca e escrever métodos para receber dados desde objetos-indicadores em Expert Advisors.

Exemplos de análise de gráficos usando o TD Sequential e os níveis de Murray-Gann Exemplos de análise de gráficos usando o TD Sequential e os níveis de Murray-Gann

O TD Sequential mostra perfeitamente as mudanças no equilíbrio durante o movimento do preço. Isso é especialmente evidente se usarmos seus sinais juntamente com um indicador de nível, como com os níveis de Murray. Este artigo falará sobre essa combinação. O texto é destinado principalmente a iniciantes e àqueles que ainda não conseguiram encontrar seu "Graal", embora eu mostre alguns recursos de construção de níveis que não vi em outros fóruns. Sendo assim, algumas partes podem ser úteis também para usuários avançados. Por outra parte, quanto aos gurus, eu os convido ao diálogo e à crítica...

Caminhe em novos trilhos: Personalize indicadores no MQL5 Caminhe em novos trilhos: Personalize indicadores no MQL5

Vou agora listar todas as possibilidades novas e recursos do novo terminal e linguagem. Elas são várias, e algumas novidades valem a discussão em um artigo separado. Além disso, não há códigos aqui escritos com programação orientada ao objeto, é um tópico muito importante para ser simplesmente mencionado em um contexto como vantagens adicionais para os desenvolvedores. Neste artigo vamos considerar os indicadores, sua estrutura, desenho, tipos e seus detalhes de programação em comparação com o MQL4. Espero que este artigo seja útil tanto para desenvolvedores iniciantes quanto para experientes, talvez alguns deles encontrem algo novo.

Está chegando o novo MetaTrader 5 e MQL5 Está chegando o novo MetaTrader 5 e MQL5

Esta é apenas uma breve resenha do MetaTrader 5. Eu não posso descrever todos os novos recursos do sistema por um período tão curto de tempo - os testes começaram em 09.09.2009. Esta é uma data simbólica, e tenho certeza que será um número de sorte. Alguns dias passaram-se desde que eu obtive a versão beta do terminal MetaTrader 5 e MQL5. Eu ainda não consegui testar todos os seus recursos, mas já estou impressionado.