Trabalhando com preços na biblioteca DoEasy (Parte 61): coleção de séries de ticks para símbolos

Artyom Trishkin | 8 abril, 2021

Sumário


Ideia

No último artigo criamos uma classe do objeto-lista de dados de ticks que coletava e armazenava ticks de um símbolo durante um determinado número de dias. Visto que diferentes símbolos podem ser usados durante a operação do programa, é necessário criar uma lista própria para cada um deles. Hoje vamos combinar essas listas numa coleção de dados de ticks. Na verdade, irá tratar-se de uma lista normal baseada numa classe de array dinâmico de ponteiros para instâncias da classe CObject e seus herdeiros da Biblioteca Padrão, que armazenará ponteiros para as listas de dados de ticks criadas segundo cada símbolo, cuja classe de objetos preparamos no último artigo.

Esta ideia, que é idêntica à de construção das classes-coleções anteriores na biblioteca, nos permitirá posteriormente salvar, armazenar, atualizar, receber e usar em estudos estatísticos dados de ticks de quaisquer símbolos disponíveis no banco de dados da biblioteca.


Classe-coleção de dados de ticks

No diretório da biblioteca \MQL5\Include\DoEasy\Collections\ criamos um novo arquivo da classe-coleção de dados de ticks com o nome TickSeriesCollection.mqh.

A classe será herdeira da classe de objeto base de todos os objetos da biblioteca.

Vamos examinar o corpo da classe e, em seguida, analisaremos suas variáveis e métodos:

//+------------------------------------------------------------------+
//|                                         TickSeriesCollection.mqh |
//|                        Copyright 2021, MetaQuotes Software Corp. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2021, MetaQuotes Software Corp."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "ListObj.mqh"
#include "..\Objects\Ticks\TickSeries.mqh"
#include "..\Objects\Symbols\Symbol.mqh"
//+------------------------------------------------------------------+
//| Collection of symbol tick series                                 |
//+------------------------------------------------------------------+
class CTickSeriesCollection : public CBaseObj
  {
private:
   CListObj                m_list;                                   // List of used symbol tick series
//--- Return the tick series index by symbol name
   int                     IndexTickSeries(const string symbol);
public:
//--- Return (1) itself and (2) tick series collection list and (3) the number of tick series in the list
   CTickSeriesCollection  *GetObject(void)                              { return &this;               }
   CArrayObj              *GetList(void)                                { return &this.m_list;        }
   int                     DataTotal(void)                        const { return this.m_list.Total(); }
//--- Return the pointer to the tick series object (1) by symbol and (2) by index in the list
   CTickSeries            *GetTickseries(const string symbol);
   CTickSeries            *GetTickseries(const int index);
//--- Create a collection list of symbol tick series
   bool                    CreateCollection(const CArrayObj *list_symbols,const uint required=0);
//--- Set the flag of using the tick series of (1) a specified symbol and (2) all symbols
   void                    SetAvailableTickSeries(const string symbol,const bool flag=true);
   void                    SetAvailableTickSeries(const bool flag=true);
//--- Return the flag of using the tick series of (1) a specified symbol and (2) all symbols
   bool                    IsAvailableTickSeries(const string symbol);
   bool                    IsAvailableTickSeries(void);

//--- Set the number of days of the tick history of (1) a specified symbol and (2) all symbols
   bool                    SetRequiredUsedDays(const string symbol,const uint required=0);
   bool                    SetRequiredUsedDays(const uint required=0);

//--- Return the last tick object of a specified symbol (1) by index, (2) by time and (4) by time in milliseconds
   CDataTick              *GetTick(const string symbol,const int index);
   CDataTick              *GetTick(const string symbol,const datetime tick_time);
   CDataTick              *GetTick(const string symbol,const long tick_time_msc);

//--- Return the new tick flag of a specified symbol
   bool                    IsNewTick(const string symbol);

//--- Create a tick series of (1) a specified symbol and (2) all symbols
   bool                    CreateTickSeries(const string symbol,const uint required=0);
   bool                    CreateTickSeriesAll(const uint required=0);
//--- Update (1) a tick series of a specified symbol and (2) all symbols
   void                    Refresh(const string symbol);
   void                    Refresh(void);

//--- Display (1) the complete and (2) short collection description in the journal
   void                    Print(void);
   void                    PrintShort(void);
   
//--- Constructor
                           CTickSeriesCollection();
  };
//+------------------------------------------------------------------+

A variável-membro da classe m_list do tipo CListObj é uma classe herdeira da classe CArrayObj da biblioteca padrão, como muitas listas criadas nesta biblioteca. Tudo o que a classe CListObj faz é implementar o método virtual Type() da Classe CObject, isto é, a classe base de objetos da biblioteca padrão. O método deve retornar o identificador do tipo de classe. Aqui ele é o identificador do tipo de array.
É a implementação do método virtual Type() que é implementado na classe CListObj, que adicionamos à biblioteca há muito tempo:

//+------------------------------------------------------------------+
//|                                                      ListObj.mqh |
//|                        Copyright 2019, MetaQuotes Software Corp. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2019, MetaQuotes Software Corp."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include <Arrays\ArrayObj.mqh>
//+------------------------------------------------------------------+
//| Class of collection lists                                        |
//+------------------------------------------------------------------+
class CListObj : public CArrayObj
  {
private:
   int               m_type;                    // List type
public:
   void              Type(const int type)       { this.m_type=type;     }
   virtual int       Type(void)           const { return(this.m_type);  }
                     CListObj()                 { this.m_type=0x7778;   }
  };
//+------------------------------------------------------------------+

Aqui o método Type() define para a variável m_type o valor passado, enquanto o método virtual Type() retorna o valor definido por esta variável.

Por padrão (no construtor da classe) para a variável é definido o valor do identificador de tipo de array, como para CArrayObj - 0x7778.

O objetivo de todos os métodos da classe é descrito nos comentários ao código e, a seguir, veremos a implementação desses métodos.

No construtor da classe limpamos a lista, definimos o sinalizador de lista classificada para a lista e
definimos o identificador da lista de coleção de dados de ticks:

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

O método privado IndexTickSeries() retorna o índice da série de tick pelo nome do símbolo:

//+------------------------------------------------------------------+
//| Return the tick series index by symbol name                      |
//+------------------------------------------------------------------+
int CTickSeriesCollection::IndexTickSeries(const string symbol)
  {
   const CTickSeries *obj=new CTickSeries(symbol==NULL || symbol=="" ? ::Symbol() : symbol);
   if(obj==NULL)
      return WRONG_VALUE;
   this.m_list.Sort();
   int index=this.m_list.Search(obj);
   delete obj;
   return index;
  }
//+------------------------------------------------------------------+

O método recebe o nome do símbolo cujo índice da série de ticks deve ser retornado a partir da lista.
Em seguida, criamos um objeto de série de ticks vazio temporário, enquanto definimos o nome do símbolo passado para o método,
definimos o sinalizador de lista classificada para a lista
e procuramos o índice do objeto na lista.
Depois, removemos o objeto temporário e retornamos o índice resultante. Se o objeto não for encontrado ou não for possível criar um objeto temporário, o método retornará NULL.

Método que retorna um ponteiro para um objeto de séries de ticks com base no símbolo:

//+------------------------------------------------------------------+
//| Return the object of tick series of a specified symbol           |
//+------------------------------------------------------------------+
CTickSeries *CTickSeriesCollection::GetTickseries(const string symbol)
  {
   int index=this.IndexTickSeries(symbol);
   return this.m_list.At(index);
  }
//+------------------------------------------------------------------+

O método recebe o nome do símbolo cujo objeto da série de ticks deve ser retornado a partir da lista.
Usando o método que acabamos de ver encontramos o índice do objeto da série de ticks na lista, obtemos um ponteiro para este objeto com base no índice encontrado e o retornamos. Se o índice não for encontrado, seu valor será -1, e o método At() da classe CArrayObj retornará NULL.

Método que define o sinalizador de uso da série de ticks do símbolo especificado:

//+------------------------------------------------------------------+
//| Set the flag of using the tick series of a specified symbol      |
//+------------------------------------------------------------------+
void CTickSeriesCollection::SetAvailableTickSeries(const string symbol,const bool flag=true)
  {
   CTickSeries *tickseries=this.GetTickseries(symbol);
   if(tickseries==NULL)
      return;
   tickseries.SetAvailable(flag);
  }
//+------------------------------------------------------------------+

O método recebe o nome do símbolo para o objeto da série de ticks para o qual devemos definir o sinalizador de uso.
Usando o método GetTickseries() acima pegamos a partir da lista um ponteiro para um objeto da série de ticks e
definimos o sinalizador passado para o método.

Método que define o sinalizador de uso da série de ticks cobrindo todos os símbolos na coleção:

//+------------------------------------------------------------------+
//| Set the flag of using the tick series of all symbols             |
//+------------------------------------------------------------------+
void CTickSeriesCollection::SetAvailableTickSeries(const bool flag=true)
  {
   for(int i=0;i<this.m_list.Total();i++)
     {
      CTickSeries *tickseries=this.m_list.At(i);
      if(tickseries==NULL)
         continue;
      tickseries.SetAvailable(flag);
     }
  }
//+------------------------------------------------------------------+

Num loop percorrendo o número total de séries de ticks na lista,
obtemos o próximo objeto da série de ticks segundo o índice de loop e
definimos o sinalizador passada para o método.

Método que retorna o sinalizador de uso da série de ticks do símbolo especificado:

//+------------------------------------------------------------------+
//| Return the flag of using the tick series of a specified symbol   |
//+------------------------------------------------------------------+
bool CTickSeriesCollection::IsAvailableTickSeries(const string symbol)
  {
   CTickSeries *tickseries=this.GetTickseries(symbol);
   if(tickseries==NULL)
      return false;
   return tickseries.IsAvailable();
  }
//+------------------------------------------------------------------+

O método recebe o nome do símbolo para o qual devemos retornar o sinalizador de uso do objeto da série ticks.
Usando o método GetTickseries() obtemos um ponteiro para o objeto da série de ticks do símbolo desejado e
retornamos o sinalizador de uso definido para este objeto. Se o objeto não puder ser recuperado da lista, o método retornará false.

Método que retorna o sinalizador de uso da série de ticks de todos os símbolos:

//+------------------------------------------------------------------+
//| Return the flag of using tick series of all symbols              |
//+------------------------------------------------------------------+
bool CTickSeriesCollection::IsAvailableTickSeries(void)
  {
   bool res=true;
   int total=this.m_list.Total();
   for(int i=0;i<total;i++)
     {
      CTickSeries *tickseries=this.m_list.At(i);
      if(tickseries==NULL)
         continue;
      res &=tickseries.IsAvailable();
     }
   return res;
  }
//+------------------------------------------------------------------+

Declaramos uma variável res e a inicializamos com o valor true.
Em seguida, num loop percorrendo o número total de objetos na lista
obtemos um ponteiro para o próximo objeto da série de ticks
e
ao valor da variável res adicionamos o sinalizador de uso definido para o objeto atual
.
No final do loop retornamos o valor resultante da variável res.

Se o sinalizador de seu uso não estiver definido para pelo menos um dos objetos da lista (false), na variável res no final do loop, será armazenado o valor false. Assim, o método permite saber se o sinalizador de uso está marcado para todas as séries de ticks na coleção, e é retornado true apenas se o sinalizador de uso estiver definido como true para cada objeto de série de ticks na coleção.

Método que define o número de dias do histórico de ticks do símbolo especificado:

//+------------------------------------------------------------------+
//| Set the number of days of the tick history of a specified symbol |
//+------------------------------------------------------------------+
bool CTickSeriesCollection::SetRequiredUsedDays(const string symbol,const uint required=0)
  {
   CTickSeries *tickseries=this.GetTickseries(symbol);
   if(tickseries==NULL)
      return false;
   tickseries.SetRequiredUsedDays(required);
   return true;
  }
//+------------------------------------------------------------------+

O método recebe o nome do símbolo cujo número de dias de dados de ticks deve ser definido.
Obtemos um ponteiro para o objeto da série ticks usando o método discutido anteriormente,
definimos o número de dias para ele e retornamos true .
Se o ponteiro para o objeto da série ticks não puder ser obtido a partir da lista, o método retorna false,

Método que define o número de dias do histórico de ticks de todos os símbolos:

//+------------------------------------------------------------------+
//| Set the number of days of the tick history of all symbols        |
//+------------------------------------------------------------------+
bool CTickSeriesCollection::SetRequiredUsedDays(const uint required=0)
  {
   bool res=true;
   for(int i=0;i<this.m_list.Total();i++)
     {
      CTickSeries *tickseries=this.m_list.At(i);
      if(tickseries==NULL)
        {
         res &=false;
         continue;
        }
      tickseries.SetRequiredUsedDays(required);
     }
   return res;
  }
//+------------------------------------------------------------------+

Declaramos uma variável res e a inicializamos com o valor true.
Em seguida, num loop percorrendo o número total de objetos na lista
obtemos um ponteiro para o próximo objeto da série de ticks
e
se o ponteiro para o objeto não puder ser obtido, então
ao valor da variável res adicionamos o valor false e passamos para o próximo objeto na lista-coleção
.
Caso contrário, definimos o número de dias de dados de ticks para o objeto atual..
No final do loop retornamos o valor resultante da variável res.

Se o número de dias de dados de ticks não for definido para pelo menos um dos objetos da lista, a variável res no final do loop armazenará o valor false. Assim, o método permite definir o número de dias para todas as séries de ticks na coleção e retorna execução bem sucedida apenas se definirmos o número de dias para cada um dos objetos de dados de ticks armazenados na lista.

Método que retorna o objeto-tick do símbolo especificado segundo o índice na lista de séries de ticks:

//+------------------------------------------------------------------+
//| Return the tick object of the specified symbol by index          |
//+------------------------------------------------------------------+
CDataTick *CTickSeriesCollection::GetTick(const string symbol,const int index)
  {
   CTickSeries *tickseries=this.GetTickseries(symbol);
   if(tickseries==NULL)
      return NULL;
   return tickseries.GetTickByListIndex(index);
  }
//+------------------------------------------------------------------+

O método recebe a classe CTickSeries e o índice do objeto-tick armazenado na lista de séries de ticks.
Obtemos um ponteiro para um objeto da série de ticks a partir da lista-coleção segundo o símbolo usando o método GetTickseries() discutido anteriormente e
retornamos um ponteiro para o objeto-tick a partir da lista de séries de ticks usando o método GetTickByListIndex(), que consideramos no último artigo.

Se o objeto da série de ticks não puder ser obtido desde a lista-coleção, o método retorna NULL. Também, o método GetTickByListIndex() da classe CTickSeries também pode retornar NULL.

Método que retorna o último objeto-tick do símbolo especificado segundo o tempo a partir da lista da série de ticks:

//+------------------------------------------------------------------+
//| Return the last tick object of the specified symbol by time      |
//+------------------------------------------------------------------+
CDataTick *CTickSeriesCollection::GetTick(const string symbol,const datetime tick_time)
  {
   CTickSeries *tickseries=this.GetTickseries(symbol);
   if(tickseries==NULL)
      return NULL;
   return tickseries.GetTick(tick_time);
  }
//+------------------------------------------------------------------+

O método recebe o símbolo da série de ticks da classe CTickSeries e a hora do objeto-tick desejado armazenado na lista da série de ticks.
Obtemos um ponteiro para um objeto da série de ticks a partir da lista-coleção segundo o símbolo usando o método GetTickseries() discutido anteriormente e
retornamos um ponteiro para o objeto-tick desde a lista da série de ticks usando o método GetTick(), que discutimos no último artigo.

Se o objeto da série de ticks não puder ser obtido desde a lista-coleção, o método retorna NULL. Além disso, o método GetTick() da classe CTickSeries também pode retornar NULL.

Método que retorna o último objeto-tick do símbolo especificado segundo o tempo em milissegundos a partir da lista de séries de ticks:

//+------------------------------------------------------------------+
//| Return the last tick object of the specified symbol              |
//| by time in milliseconds                                          |
//+------------------------------------------------------------------+
CDataTick *CTickSeriesCollection::GetTick(const string symbol,const long tick_time_msc)
  {
   CTickSeries *tickseries=this.GetTickseries(symbol);
   if(tickseries==NULL)
      return NULL;
   return tickseries.GetTick(tick_time_msc);
  }
//+------------------------------------------------------------------+

O método recebe o símbolo da série de ticks da classe CTickSeries e o tempo em milissegundos do objeto-tick desejado armazenado na lista da série de ticks.
Obtemos um ponteiro para um objeto da série de ticks a partir da lista-coleção segundo o símbolo usando o método GetTickseries() discutido anteriormente e
retornamos um ponteiro para o objeto-tick desde a lista da série de ticks usando o método GetTick(), que discutimos no último artigo.

Se o objeto da série de ticks não puder ser obtido desde a lista-coleção, o método retorna NULL. Além disso, o método GetTick() da classe CTickSeries também pode retornar NULL.

Vale a pena esclarecer que, para os dois últimos métodos que retornam objetos-ticks por tempo, pode haver vários ticks ao mesmo tempo, por isso, o método GetTick() da classe CTickSeries retorna o mais recente deles - com o tempo mais recente como o mais relevante.

Método que retorna o sinalizador do novo tick do símbolo especificado:

//+------------------------------------------------------------------+
//| Return the new tick flag of a specified symbol                   |
//+------------------------------------------------------------------+
bool CTickSeriesCollection::IsNewTick(const string symbol)
  {
   CTickSeries *tickseries=this.GetTickseries(symbol);
   if(tickseries==NULL)
      return false;
   return tickseries.IsNewTick();
  }
//+------------------------------------------------------------------+

O método recebe o nome do símbolo cujo sinalizador de aparecimento de novo tick deve ser retornado.
Obtemos um ponteiro para um objeto da série de ticks a partir da lista-coleção segundo o símbolo usando o método GetTickseries() discutido anteriormente e
retornamos o sinalizador do novo tick da série de ticks usando o método IsNewTick() da classe CTickSeries, que consideramos no último artigo.
Se o objeto da série de ticks não puder ser obtido a partir da lista-coleção, o método retorna false.

Quero observar que esse recurso ainda não foi implementado na classe CTickSeries, e isso será feito em artigos futuros.

Método que cria uma série de ticks do símbolo especificado:

//+------------------------------------------------------------------+
//| Create a tick series of a specified symbol                       |
//+------------------------------------------------------------------+
bool CTickSeriesCollection::CreateTickSeries(const string symbol,const uint required=0)
  {
   CTickSeries *tickseries=this.GetTickseries(symbol);
   if(tickseries==NULL)
      return false;
   return(tickseries.Create(required)>0);
  }
//+------------------------------------------------------------------+

O método recebe o nome do símbolo, cuja série de ticks deve ser criada, e o número de dias de dados de ticks.
Obtemos um ponteiro para um objeto da série de ticks a partir da lista-coleção segundo o símbolo usando o método GetTickseries() discutido anteriormente e
retornamos o sinalizador indicando que o método Create() da classe CTickSeries retornou um valor maior que zero (o número de objetos-tick criados não é zero).

Método que cria uma série de ticks cobrindo todos os símbolos usados:

//+------------------------------------------------------------------+
//| Create tick series of all symbols                                |
//+------------------------------------------------------------------+
bool CTickSeriesCollection::CreateTickSeriesAll(const uint required=0)
  {
   bool res=true;
   int total=this.m_list.Total();
   for(int i=0;i<total;i++)
     {
      CTickSeries *tickseries=this.m_list.At(i);
      if(tickseries==NULL)
         continue;
      res &=(tickseries.Create(required)>0);
     }
   return res;
  }
//+------------------------------------------------------------------+

O número de dias de dados do tick é passado para o método.
Declaramos uma variável res e a inicializamos com valor true.
Em seguida, num loop percorrendo o número total de objetos na lista
obtemos um ponteiro para o próximo objeto da série de ticks
e
ao valor da variável res adicionamos um sinalizador indicando que o valor retornado pelo método Create() da classe CTickSeries, maior que zero (série de ticks criada)

No final do loop, retornamos o valor resultante da variável res.

Se uma série de ticks não for criada para pelo menos um dos objetos na lista, então a variável res no final do loop armazenará o valor false. Assim, o método permite a criação de séries de ticks para todos os símbolos da coleção, e retorna execução bem sucedida apenas se a série de ticks for criada para cada um dos objetos de dados de ticks armazenados na lista.

Método que atualiza a série de ticks do símbolo especificado:

//+------------------------------------------------------------------+
//| Update a tick series of a specified symbol                       |
//+------------------------------------------------------------------+
void CTickSeriesCollection::Refresh(const string symbol)
  {
   CTickSeries *tickseries=this.GetTickseries(symbol);
   if(tickseries==NULL)
      return;
   tickseries.Refresh();
  }
//+------------------------------------------------------------------+

O método recebe o nome do símbolo cuja série de ticks que precisa ser atualizada.
Obtemos um ponteiro para um objeto da série de ticks a partir da lista-coleção segundo o símbolo usando o método GetTickseries() discutido anteriormente e
a atualizamos usando o método Refresh() da classe CTickSeries.

Método que atualiza a série de ticks de todos os símbolos:

//+------------------------------------------------------------------+
//| Update tick series of all symbols                                |
//+------------------------------------------------------------------+
void CTickSeriesCollection::Refresh(void)
  {
   for(int i=0;i<this.m_list.Total();i++)
     {
      CTickSeries *tickseries=this.m_list.At(i);
      if(tickseries==NULL)
         continue;
      tickseries.Refresh();
     }
  }
//+------------------------------------------------------------------+

No ciclo sobre o número total de objetos na lista
obtemos um ponteiro para o próximo objeto da série de ticks
por índice de ciclo e
atualizamos a série usando o método Refresh() da classe CTickSeries.

Gostaria de ressaltar que ainda não desenvolvemos o recurso para atualizar a série de ticks na classe CTickSeries ainda, e isso será feito nos próximos artigos.

O método que registra no log uma descrição completa da coleção:

//+------------------------------------------------------------------+
//| Display complete collection description to the journal           |
//+------------------------------------------------------------------+
void CTickSeriesCollection::Print(void)
  {
   for(int i=0;i<this.m_list.Total();i++)
     {
      CTickSeries *tickseries=this.m_list.At(i);
      if(tickseries==NULL)
         continue;
      tickseries.Print();
     }
  }
//+------------------------------------------------------------------+

No ciclo sobre o número total de objetos na lista
obtemos um ponteiro para o próximo objeto da série de ticks/s3> por índice de ciclo e
exibimos uma descrição completa da série de ticks no log.

O método que registra no log uma breve descrição da coleção:

//+------------------------------------------------------------------+
//| Display the short collection description in the journal          |
//+------------------------------------------------------------------+
void CTickSeriesCollection::PrintShort(void)
  {
   for(int i=0;i<this.m_list.Total();i++)
     {
      CTickSeries *tickseries=this.m_list.At(i);
      if(tickseries==NULL)
         continue;
      tickseries.PrintShort();
     }
  }
//+------------------------------------------------------------------+

No ciclo sobre o número total de objetos na lista
obtemos um ponteiro para o próximo objeto da série de ticks
por índice de ciclo e
exibimos uma breve descrição da série de ticks no log.

Os métodos discutidos acima são projetados para trabalhar com a já criada lista-coleção de ponteiros para objetos de dados de ticks de símbolos diferentes. Para que eles funcionem em nossos programas, podemos usar diferentes símbolos. Para criar o próprio objeto-coleção, de forma que possamos colocar todas as séries de ticks necessárias nele e, depois, obter ponteiros para elas a partir da lista-coleção, usaremos o seguinte método.

Método que cria uma lista-coleção de séries de ticks de símbolos:

//+------------------------------------------------------------------+
//| Create a collection list of symbol tick series                   |
//+------------------------------------------------------------------+
bool CTickSeriesCollection::CreateCollection(const CArrayObj *list_symbols,const uint required=0)
  {
//--- If an empty list of symbol objects is passed, exit
   if(list_symbols==NULL)
      return false;
//--- Get the number of symbol objects in the passed list
   int total=list_symbols.Total();
//--- Clear the tick series collection list
   this.m_list.Clear();
//--- In a loop by all symbol objects
   for(int i=0;i<total;i++)
     {
      //--- get the next symbol object
      CSymbol *symbol_obj=list_symbols.At(i);
      //--- if failed to get a symbol object, move on to the next one in the list
      if(symbol_obj==NULL)
         continue;
      //--- Create a new empty tick series object
      CTickSeries *tickseries=new CTickSeries();
      //--- If failed to create the tick series object, move on to the next symbol in the list
      if(tickseries==NULL)
         continue;
      //--- Set a symbol name for a tick series object
      tickseries.SetSymbol(symbol_obj.Name());
      //--- Set the sorted list flag for the tick series collection list
      this.m_list.Sort();
      //--- If the object with the same symbol name is already present in the tick series collection list, remove the tick series object
      if(this.m_list.Search(tickseries)>WRONG_VALUE)
         delete tickseries;
      //--- otherwise, there is no object with such a symbol name in the collection yet
      else
        {
         //--- Set the number of tick data days for a tick series object
         tickseries.SetRequiredUsedDays(required);
         //--- if failed to add the tick series object to the collection list, remove the tick series object
         if(!this.m_list.Add(tickseries))
            delete tickseries;
        } 
     }
//--- Return the flag indicating that the created collection list has a size greater than zero
   return this.m_list.Total()>0;
  }
//+------------------------------------------------------------------+

O método é simples - ele recebe uma lista de símbolos usados no programa (temos essa lista há muito tempo e é usada para criar uma coleção de séries temporais de símbolos), em seguida, num loop percorrendo o número total de símbolos, criamos outro novo objeto-série de ticks e definimos o nome do símbolo a partir da lista de símbolos na posição atual do loop. Se um objeto-série de ticks com esse símbolo ainda não estiver na lista, definimos para ele o número de dias de dados de ticks passados para o método e adicionamos o objeto à lista-coleção. E fazemos assim para cada símbolo da lista. Isso é em poucas palavras. Na verdade, isso se torna um pouco diferente quando verificamos se são bem-sucedidos a criação/adição de objetos-séries de ticks à lista e remoção de objetos desnecessários. Toda a lógica do método é descrita em detalhes em sua listagem, vamos deixá-la para uma análise independente.

Para conectar a coleção criada com o "mundo externo", usamos a classe da biblioteca principal CEngine,
localizada em \MQL5\Include\DoEasy\Engine.mqh.

Anexamos o arquivo da classe recém-criada a ele:

//+------------------------------------------------------------------+
//|                                                       Engine.mqh |
//|                        Copyright 2020, MetaQuotes Software Corp. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, MetaQuotes Software Corp."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "Services\TimerCounter.mqh"
#include "Collections\HistoryCollection.mqh"
#include "Collections\MarketCollection.mqh"
#include "Collections\EventsCollection.mqh"
#include "Collections\AccountsCollection.mqh"
#include "Collections\SymbolsCollection.mqh"
#include "Collections\ResourceCollection.mqh"
#include "Collections\TimeSeriesCollection.mqh"
#include "Collections\BuffersCollection.mqh"
#include "Collections\IndicatorsCollection.mqh"
#include "Collections\TickSeriesCollection.mqh"
#include "TradingControl.mqh"
//+------------------------------------------------------------------+

Na seção privada da classe declaramos um objeto da classe-coleção da série de ticks:

//+------------------------------------------------------------------+
//| Library basis class                                              |
//+------------------------------------------------------------------+
class CEngine
  {
private:
   CHistoryCollection   m_history;                       // Collection of historical orders and deals
   CMarketCollection    m_market;                        // Collection of market orders and deals
   CEventsCollection    m_events;                        // Event collection
   CAccountsCollection  m_accounts;                      // Account collection
   CSymbolsCollection   m_symbols;                       // Symbol collection
   CTimeSeriesCollection m_time_series;                  // Timeseries collection
   CBuffersCollection   m_buffers;                       // Collection of indicator buffers
   CIndicatorsCollection m_indicators;                   // Indicator collection
   CTickSeriesCollection m_tick_series;                  // Collection of tick series
   CResourceCollection  m_resource;                      // Resource list
   CTradingControl      m_trading;                       // Trading management object
   CPause               m_pause;                         // Pause object
   CArrayObj            m_list_counters;                 // List of timer counters

A classe tem um método SetUsedSymbols(), que permite definir na biblioteca uma lista de símbolos especificados para uso no programa.
Adicionamos a ele a transferência do número de dias para os quais é necessário ter dados de ticks na biblioteca:

//--- Set the list of used symbols in the symbol collection and create the collection of symbol timeseries
   bool                 SetUsedSymbols(const string &array_symbols[],const uint required=0);

Por padrão, é passado zero, o que significa um dia, e é especificado no arquivo \MQL5\Include\DoEasy\Defines.mqh pela constante TICKSERIES_DEFAULT_DAYS_COUNT.

Na implementação do método adicionamos a criação de uma coleção de séries de ticks.

//+------------------------------------------------------------------+
//| Set the list of used symbols in the symbol collection            |
//| and create the symbol timeseries collection                      |
//+------------------------------------------------------------------+
bool CEngine::SetUsedSymbols(const string &array_symbols[],const uint required=0)
  {
   bool res=this.m_symbols.SetUsedSymbols(array_symbols);
   CArrayObj *list=this.GetListAllUsedSymbols();
   if(list==NULL)
      return false;
   res&=this.m_time_series.CreateCollection(list);
   res&=this.m_tick_series.CreateCollection(list,required);
   return res;
  }
//+------------------------------------------------------------------+

Agora, quando este método é chamado, duas coleções serão criadas a partir do programa - uma coleção de séries temporais e uma de séries de ticks.

Na seção pública da classe adicionamos métodos para acessar a classe da coleção da séries de ticks a partir de nossos programas:

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


//--- Return (1) the tick series collection, (2) the list of tick series from the tick series collection
   CTickSeriesCollection *GetTickSeriesCollection(void)                       { return &this.m_tick_series;                                     }
   CArrayObj           *GetListTickSeries(void)                               { return this.m_tick_series.GetList();                            }


//--- Return (1) the buffer collection and (2) the buffer list from the collection 

Por enquanto, basta retornar ao programa o próprio objeto de coleção das séries de ticks e a lista-coleção a partir dele.

Por hoje, isso é tudo de que precisamos para criar uma coleção de séries de ticks.


Teste

Para testar a criação de uma coleção de séries de ticks, pegaremos um Expert Advisor do artigo anterior e salvá-lo-emos numa nova pasta \MQL5\Experts\TestDoEasy\Part61\ com o nome TestDoEasyPart61.mq5.

Como agora todas as séries de ticks estão disponíveis na própria biblioteca, removemos a anexação de seu arquivo da classe a o programa:

//+------------------------------------------------------------------+
//|                                             TestDoEasyPart60.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\Ticks\TickSeries.mqh>
//--- enums

Na área de variáveis globais do programa excluímos as variáveis do objeto "Novo Tick" e o objeto de dados da série de ticks do símbolo atual:

//--- global variables
CEngine        engine;
SDataButt      butt_data[TOTAL_BUTT];
string         prefix;
double         lot;
double         withdrawal=(InpWithdrawal<0.1 ? 0.1 : InpWithdrawal);
ushort         magic_number;
uint           stoploss;
uint           takeprofit;
uint           distance_pending;
uint           distance_stoplimit;
uint           distance_pending_request;
uint           bars_delay_pending_request;
uint           slippage;
bool           trailing_on;
bool           pressed_pending_buy;
bool           pressed_pending_buy_limit;
bool           pressed_pending_buy_stop;
bool           pressed_pending_buy_stoplimit;
bool           pressed_pending_close_buy;
bool           pressed_pending_close_buy2;
bool           pressed_pending_close_buy_by_sell;
bool           pressed_pending_sell;
bool           pressed_pending_sell_limit;
bool           pressed_pending_sell_stop;
bool           pressed_pending_sell_stoplimit;
bool           pressed_pending_close_sell;
bool           pressed_pending_close_sell2;
bool           pressed_pending_close_sell_by_buy;
bool           pressed_pending_delete_all;
bool           pressed_pending_close_all;
bool           pressed_pending_sl;
bool           pressed_pending_tp;
double         trailing_stop;
double         trailing_step;
uint           trailing_start;
uint           stoploss_to_modify;
uint           takeprofit_to_modify;
int            used_symbols_mode;
string         array_used_symbols[];
string         array_used_periods[];
bool           testing;
uchar          group1;
uchar          group2;
double         g_point;
int            g_digits;

//--- "New tick" object
CNewTickObj    check_tick;
//--- Object of the current symbol tick series data
CTickSeries    tick_series;
//+------------------------------------------------------------------+

No final do manipulador OnInit() removemos a configuração do símbolo atual para o objeto "Novo Tick":

//--- Wait for 600 milliseconds
   engine.Pause(600);
   engine.PlaySoundByDescription(TextByLanguage("Звук упавшей монетки 2","Falling coin 2"));

//--- Set the current symbol for "New tick" object
   check_tick.SetSymbol(Symbol());
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+

Removemos na função OnInitDoEasy() o bloco de código usado para verificar a criação de uma série de ticks do símbolo atual:

//--- Check created timeseries - display descriptions of all created timeseries in the journal
//--- (true - only created ones, false - created and declared ones)
   engine.GetTimeSeriesCollection().PrintShort(false); // Short descriptions
   //engine.GetTimeSeriesCollection().Print(true);      // Full descriptions

//--- Code block for checking the tick list creation and working with it
   Print("");
//--- Since the tick series object is created with the default constructor,
//--- set a symbol, usage flag and the number of days (the default is 1) to copy the ticks
//--- Create the tick series and printed data in the journal
   tick_series.SetSymbol(Symbol());
   tick_series.SetAvailable(true);
   tick_series.SetRequiredUsedDays();
   tick_series.Create();
   tick_series.Print();
   
   Print("");
//--- Get and display in the journal the data of an object with the highest Ask price in the daily price range
   int index_max=CSelect::FindTickDataMax(tick_series.GetList(),TICK_PROP_ASK);
   CDataTick *tick_max=tick_series.GetList().At(index_max);
   if(tick_max!=NULL)
      tick_max.Print();
//--- Get and display in the journal the data of an object with the lowest Bid price in the daily price range
   int index_min=CSelect::FindTickDataMin(tick_series.GetList(),TICK_PROP_BID);
   CDataTick *tick_min=tick_series.GetList().At(index_min);
   if(tick_min!=NULL)
      tick_min.Print();

//--- Create resource text files

Agora, neste lugar, precisamos colocar a criação de séries de ticks para todos os símbolos da coleção criada de dados de ticks:

//--- Check created timeseries - display descriptions of all created timeseries in the journal
//--- (true - only created ones, false - created and declared ones)
   engine.GetTimeSeriesCollection().PrintShort(false); // Short descriptions
   //engine.GetTimeSeriesCollection().Print(true);      // Full descriptions

//--- Create tick series of all used symbols
   engine.GetTickSeriesCollection().CreateTickSeriesAll();
//--- Check created tick series - display descriptions of all created tick series in the journal
   engine.GetTickSeriesCollection().Print();

//--- Create resource text files

No manipulador OnTick(), quando um novo tick chegar, tentaremos encontrar na coleção de séries de ticks para cada símbolo um objeto-tick com o preço máximo Ask e preço mínimo Bid nas listas de dados de ticks, e exibir os parâmetros de cada objeto-tick encontrado no log:

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

//--- If the trailing flag is set
   if(trailing_on)
     {
      TrailingPositions();          // Trailing positions
      TrailingOrders();             // Trailing pending orders
     }
     
//--- Check created tick data on the first tick
//--- Get and display in the journal the data of an object with the highest Ask price and the lowest Bid price in the daily price range
   static bool check=false;
   if(!check)
     {
      Print("");
      //--- Get the pointer to the list of tick data of all symbols from the tick collection
      CArrayObj* list=engine.GetTickSeriesCollection().GetList();
      int total=engine.GetTickSeriesCollection().DataTotal();
      //--- In the loop by the number of tick series in the collection
      for(int i=0;i<list.Type();i++)
        {
         //--- Get the next tick series from the collection by index
         CTickSeries *tick_series=engine.GetTickSeriesCollection().GetTickseries(i);
         if(tick_series!=NULL)
           {
            //--- In the obtained tick series, find the indices of tick objects with the highest Ask and the lowest Bid
            int index_max=CSelect::FindTickDataMax(tick_series.GetList(),TICK_PROP_ASK);
            int index_min=CSelect::FindTickDataMin(tick_series.GetList(),TICK_PROP_BID);
            //--- Display the data of the tick objects obtained from the tick series in the journal
            engine.GetTickSeriesCollection().GetTick(tick_series.Symbol(),index_max).Print();
            engine.GetTickSeriesCollection().GetTick(tick_series.Symbol(),index_min).Print();
           }
        }
      check=true;
     }
  }
//+------------------------------------------------------------------+

Vamos compilar o Expert Advisor e iniciá-lo no gráfico de qualquer símbolo, tendo previamente definido nas configurações o uso do timeframe atual e dos símbolos de uma lista predefinida, na qual deixaremos os dois primeiros:


Após um curto período de tempo necessário para criar dados de tick para os dois símbolos usados no manipulador OnInit(), o log exibirá os dados sobre os parâmetros do programa, séries de tempo criadas e dados de ticks gerados, enquanto após a chegada do primeiro ticko log mostrará os dados de quatro ticks encontrados com máximo Ask e preço mínimo Bid para cada um dos dois símbolos:

Account 8550475: Artyom Trishkin (MetaQuotes Software Corp.) 10426.13 USD, 1:100, Hedge, MetaTrader 5 demo
--- Initializing "DoEasy" library ---
Working with predefined symbol list. The number of used symbols: 2
"AUDUSD" "EURUSD"
Working with the current timeframe only: H1
AUDUSD symbol timeseries: 
- Timeseries "AUDUSD" H1: Requested: 1000, Actual: 1000, Created: 1000, On the server: 6194
EURUSD symbol timeseries: 
- Timeseries "EURUSD" H1: Requested: 1000, Actual: 1000, Created: 1000, On the server: 5675
Tick series "AUDUSD": Requested number of days: 1, Historical data created: 142712
Tick series "EURUSD": Requested number of days: 1, Historical data created: 113985
Library initialization time: 00:00:06.156
 
============= Beginning of parameter list (Tick "AUDUSD" 2021.01.19 10:06:53.387) =============
Last price update time in milliseconds: 2021.01.19 10:06:53.387
Last price update time: 2021.01.19 10:06:53
Volume for the current Last price: 0
Flags: 6
Changed data on the tick:
 - Ask price change
 - Bid price change
------
Bid price: 0.77252
Ask price: 0.77256
Last price: 0.00000
Volume for the current Last price with greater accuracy: 0.00
Spread: 0.00004
------
Symbol: "AUDUSD"
============= End of parameter list (Tick "AUDUSD" 2021.01.19 10:06:53.387) =============
 
============= Beginning of parameter list (Tick "AUDUSD" 2021.01.18 11:51:48.662) =============
Last price update time in milliseconds: 2021.01.18 11:51:48.662
Last price update time: 2021.01.18 11:51:48
Volume for the current Last price: 0
Flags: 130
Changed data on the tick:
 - Bid price change
------
Bid price: 0.76589
Ask price: 0.76593
Last price: 0.00000
Volume for the current Last price with greater accuracy: 0.00
Spread: 0.00004
------
Symbol: "AUDUSD"
============= End of parameter list (Tick "AUDUSD" 2021.01.18 11:51:48.662) =============
 
============= Beginning of parameter list (Tick "EURUSD" 2021.01.19 10:05:07.246) =============
Last price update time in milliseconds: 2021.01.19 10:05:07.246
Last price update time: 2021.01.19 10:05:07
Volume for the current Last price: 0
Flags: 6
Changed data on the tick:
 - Ask price change
 - Bid price change
------
Bid price: 1.21189
Ask price: 1.21189
Last price: 0.00000
Volume for the current Last price with greater accuracy: 0.00
Spread: 0.00000
------
Symbol: "EURUSD"
============= End of parameter list (Tick "EURUSD" 2021.01.19 10:05:07.246) =============

============= Beginning of parameter list (Tick "EURUSD" 2021.01.18 14:57:53.847) =============
Last price update time in milliseconds: 2021.01.18 14:57:53.847
Last price update time: 2021.01.18 14:57:53
Volume for the current Last price: 0
Flags: 134
Changed data on the tick:
 - Ask price change
 - Bid price change
------
Bid price: 1.20536
Ask price: 1.20536
Last price: 0.00000
Volume for the current Last price with greater accuracy: 0.00
Spread: 0.00000
------
Symbol: "EURUSD"
============= End of parameter list (Tick "EURUSD" 2021.01.18 14:57:53.847) =============

No log podemos ver que inicializar a biblioteca com a criação de listas de dados de ticks para dois símbolos demorou 16 segundos, e depois, após a chagada de um novo tick, encontramos dois ticks para cada um dos símbolos usados - com o preço máximo Ask e o preço mínimo Bid para o dia atual.

O que vem agora?

No próximo artigo, começaremos a criar atualizações em tempo real e monitorar eventos de dados na coleção de ticks criados hoje.

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.
Gostaria de ressaltar que a classe-coleção das séries de tick está em desenvolvimento e, por isso, implementar essas classes neste estágio é extremamente desfavorável.
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
Trabalhando com séries temporais na biblioteca DoEasy (Parte 57): objeto de dados do buffer do indicador
Trabalhando com séries temporais na biblioteca DoEasy (Parte 58): séries temporais de dados de buffers de indicadores
Trabalhando com preços na biblioteca DoEasy (Parte 59): objeto para armazenar dados de um tick
Trabalhando com preços na biblioteca DoEasy (Parte 60): lista-série de dados de tick do símbolo