Trabalhando com séries temporais na biblioteca DoEasy (Parte 36): objeto das séries temporais de todos os períodos usados do símbolo

16 junho 2020, 17:03
Artyom Trishkin
0
1 135

Sumário


Ideia

No último artigo, iniciamos uma nova série descrevendo a criação da biblioteca DoEasy, e analisamos a criação do objeto 'Barra' e uma lista de objetos-barras. Em relação à terminologia da plataforma MetaTrader, criamos uma série temporal para um símbolo com base num período gráfico e a preenchemos com dados para cada barra dessa série temporal.
Em relação à terminologia da biblioteca DoEasy, criamos um objeto-coleção de barras para determinado símbolo e período gráfico. Portanto, agora podemos realizar pesquisas e classificações dentro da coleção criada (dentro da profundidade especificada no histórico da coleção) para cada uma das propriedades dos objetos-barras presentes nela. Em outras palavras, podemos procurar diferentes parâmetros de barras da série temporal e suas combinações (falaremos das diferentes combinações de barras nas próximas implementações), bem como determinar o surgimento de uma nova barra na coleção criada e atualizar os dados contidos nela.

Isso é bom, mas não é suficiente, afinal, nossos programas podem usar mais de um período gráfico e mais de um símbolo. Isso significa que precisamos que a quantidade de coleções de séries temporais do símbolo e a de períodos gráficos usados em nossos programas se mantenham na mesma proporção.
A coleção-série temporal criada permite que isso seja feito, pois ela é criada em relação ao símbolo e ao período gráfico, o que significa que podemos criar quantas coleções de um símbolo forem necessárias.

Além disso, seria conveniente que todas as coleções do símbolo abrangendo vários períodos gráficos fossem armazenadas num só objeto, em particular no objeto das séries temporais do símbolo. Em seguida, já a partir destes objetos seria bom criar uma coleção de séries temporais grande com base em diferentes símbolos e períodos gráficos.


Objeto das séries temporais do símbolo

No futuro próximo, muitas classes de bibliotecas exigirão conhecimento do tipo de programa em que estarão sendo executadas. Para fazer isso, será necessário utilizar a função MQLInfoInteger() com o qualificador MQL_PROGRAM_TYPE. Neste caso, a função retornará o tipo de programa mql5 em andamento.
Para não registrar em cada classe variáveis que armazenam o tipo de programa, declararemos essa variável na classe base de todos os objetos de programa, e todas as classes herdadas da classe base terão uma variável que armazenará o tipo de programa que estará sendo executado.

Na seção protegida da classe CBaseObj, localizada no caminho \MQL5\Include\DoEasy\Objects\BaseObj.mqh,
declaramos uma variável-membro da classe que armazena o tipo de programa a ser executado:

//+------------------------------------------------------------------+
//| Base object class for all library objects                        |
//+------------------------------------------------------------------+
#define  CONTROLS_TOTAL    (10)
class CBaseObj : public CObject
  {
private:
   int               m_long_prop_total;
   int               m_double_prop_total;
   //--- Fill in the object property array
   template<typename T> bool  FillPropertySettings(const int index,T &array[][CONTROLS_TOTAL],T &array_prev[][CONTROLS_TOTAL],int &event_id);
protected:
   CArrayObj         m_list_events_base;                       // Object base event list
   CArrayObj         m_list_events;                            // Object event list
   ENUM_LOG_LEVEL    m_log_level;                              // Logging level
   ENUM_PROGRAM_TYPE m_program;                                // Program type
   MqlTick           m_tick;                                   // Tick structure for receiving quote data

e no construtor da classe definimos o valor para ela:

//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CBaseObj::CBaseObj() : m_global_error(ERR_SUCCESS),
                       m_hash_sum(0),m_hash_sum_prev(0),
                       m_is_event(false),m_event_code(WRONG_VALUE),
                       m_chart_id_main(::ChartID()),
                       m_chart_id(::ChartID()),
                       m_folder_name(DIRECTORY),
                       m_name(__FUNCTION__),
                       m_long_prop_total(0),
                       m_double_prop_total(0),
                       m_first_start(true)
  {
   ::ArrayResize(this.m_long_prop_event,0,100);
   ::ArrayResize(this.m_double_prop_event,0,100);
   ::ArrayResize(this.m_long_prop_event_prev,0,100);
   ::ArrayResize(this.m_double_prop_event_prev,0,100);
   ::ZeroMemory(this.m_tick);
   this.m_program=(ENUM_PROGRAM_TYPE)::MQLInfoInteger(MQL_PROGRAM_TYPE);
   this.m_digits_currency=(#ifdef __MQL5__ (int)::AccountInfoInteger(ACCOUNT_CURRENCY_DIGITS) #else 2 #endif);
   this.m_list_events.Clear();
   this.m_list_events.Sort();
   this.m_list_events_base.Clear();
   this.m_list_events_base.Sort();
  }
//+------------------------------------------------------------------+

Agora, todos os objetos herdados da classe base de todos os objetos de biblioteca "terão conhecimento" sobre o tipo de programa em que estarão sendo executados.

À listagem da classe CNewBarObj, localizada no arquivo \MQL5\Include\DoEasy\Objects\Series\NewBarObj.mqh, anexamos o arquivo da classe do objeto base:

//+------------------------------------------------------------------+
//|                                                    NewBarObj.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 "..\..\Objects\BaseObj.mqh"
//+------------------------------------------------------------------+
//| "New bar" object class                                           |
//+------------------------------------------------------------------+

herdamos o objeto "Nova barra" a partir do objeto base:

//+------------------------------------------------------------------+
//| "New bar" object class                                           |
//+------------------------------------------------------------------+
class CNewBarObj : public CBaseObj
  {
private:

e removemos da listagem "todas as menções" da variável do tipo de programa, uma vez que agora o tipo de programa é definido em CBaseObj:

//+------------------------------------------------------------------+
//| "New bar" object class                                           |
//+------------------------------------------------------------------+
class CNewBarObj
  {
private:
   ENUM_PROGRAM_TYPE m_program;                                   // Program type
   string            m_symbol;                                    // Symbol
   ENUM_TIMEFRAMES   m_timeframe;                                 // Timeframe
   datetime          m_new_bar_time;                              // New bar time for auto time management
   datetime          m_prev_time;                                 // Previous time for auto time management
   datetime          m_new_bar_time_manual;                       // New bar time for manual time management
   datetime          m_prev_time_manual;                          // Previous time for manual time management
//--- Return the current bar data
   datetime          GetLastBarDate(const datetime time);
public:
//--- Set (1) symbol and (2) timeframe
   void              SetSymbol(const string symbol)               { this.m_symbol=(symbol==NULL || symbol==""   ? ::Symbol() : symbol);                     }
   void              SetPeriod(const ENUM_TIMEFRAMES timeframe)   { this.m_timeframe=(timeframe==PERIOD_CURRENT ? (ENUM_TIMEFRAMES)::Period() : timeframe); }
//--- Save the new bar time during the manual time management
   void              SaveNewBarTime(const datetime time)          { this.m_prev_time_manual=this.GetLastBarDate(time);                                      }
//--- Return (1) symbol and (2) timeframe
   string            Symbol(void)                           const { return this.m_symbol;       }
   ENUM_TIMEFRAMES   Period(void)                           const { return this.m_timeframe;    }
//--- Return the (1) new bar time
   datetime          TimeNewBar(void)                       const { return this.m_new_bar_time; }
//--- Return the new bar opening flag during the time (1) auto, (2) manual management
   bool              IsNewBar(const datetime time);
   bool              IsNewBarManual(const datetime time);
//--- Constructors
                     CNewBarObj(void) : m_program((ENUM_PROGRAM_TYPE)::MQLInfoInteger(MQL_PROGRAM_TYPE)),
                                        m_symbol(::Symbol()),
                                        m_timeframe((ENUM_TIMEFRAMES)::Period()),
                                        m_prev_time(0),m_new_bar_time(0),
                                        m_prev_time_manual(0),m_new_bar_time_manual(0) {}
                     CNewBarObj(const string symbol,const ENUM_TIMEFRAMES timeframe);
  };
//+------------------------------------------------------------------+
//| Parametric constructor                                           |
//+------------------------------------------------------------------+
CNewBarObj::CNewBarObj(const string symbol,const ENUM_TIMEFRAMES timeframe) : m_symbol(symbol),m_timeframe(timeframe)
  {
   this.m_program=(ENUM_PROGRAM_TYPE)::MQLInfoInteger(MQL_PROGRAM_TYPE);
   this.m_prev_time=this.m_prev_time_manual=this.m_new_bar_time=this.m_new_bar_time_manual=0;
  }
//+------------------------------------------------------------------+

A listagem completa contendo as correções pode ser vista nos arquivos anexados no final do artigo.

Modificamos um pouco a classe CSeries, criada no artigo anterior.

Herdamos a classe a partir do objeto base CBaseObj, removemos dele a variável que armazena o tipo de programa:
//+------------------------------------------------------------------+
//| Timeseries class                                                 |
//+------------------------------------------------------------------+
class CSeries : public CBaseObj
  {
private:
   ENUM_PROGRAM_TYPE m_program;                                         // Program type
   ENUM_TIMEFRAMES   m_timeframe;                                       // Timeframe
   string            m_symbol;                                          // Symbol
   uint              m_amount;                                          // Amount of applied timeseries data
   uint              m_bars;                                            // Number of bars in history by symbol and timeframe
   bool              m_sync;                                            // Synchronized data flag
   CArrayObj         m_list_series;                                     // Timeseries list
   CNewBarObj        m_new_bar_obj;                                     // "New bar" object
public:

Na seção pública da classe, declaramos os métodos de definição de símbolo e de período gráfico, e adicionamos o método que retorna a quantidade total de dados disponíveis:

//+------------------------------------------------------------------+
//| Timeseries class                                                 |
//+------------------------------------------------------------------+
class CSeries : public CBaseObj
  {
private:
   ENUM_TIMEFRAMES   m_timeframe;                                       // Timeframe
   string            m_symbol;                                          // Symbol
   uint              m_amount;                                          // Amount of applied timeseries data
   uint              m_bars;                                            // Number of bars in history by symbol and timeframe
   bool              m_sync;                                            // Synchronized data flag
   CArrayObj         m_list_series;                                     // Timeseries list
   CNewBarObj        m_new_bar_obj;                                     // "New bar" object
public:
//--- Return the timeseries list
   CArrayObj        *GetList(void)                                      { return &m_list_series;}
//--- Return the list of bars by selected (1) double, (2) integer and (3) string property fitting a compared condition
   CArrayObj        *GetList(ENUM_BAR_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode=EQUAL){ return CSelect::ByBarProperty(this.GetList(),property,value,mode); }
   CArrayObj        *GetList(ENUM_BAR_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByBarProperty(this.GetList(),property,value,mode); }
   CArrayObj        *GetList(ENUM_BAR_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode=EQUAL){ return CSelect::ByBarProperty(this.GetList(),property,value,mode); }

//--- Set (1) symbol, (2) timeframe, (3) symbol and timeframe, (4) amount of applied timeseries data
   void              SetSymbol(const string symbol);
   void              SetTimeframe(const ENUM_TIMEFRAMES timeframe);
   void              SetSymbolPeriod(const string symbol,const ENUM_TIMEFRAMES timeframe);
   bool              SetAmountUsedData(const uint amount,const uint rates_total);

//--- Return (1) symbol, (2) timeframe, (3) number of applied timeseries data,
//--- (4) number of bars in the timeseries, the new bar flag with the (5) auto, (6) manual time management
   string            Symbol(void)                                          const { return this.m_symbol;                            }
   ENUM_TIMEFRAMES   Timeframe(void)                                       const { return this.m_timeframe;                         }
   uint              AmountUsedData(void)                                  const { return this.m_amount;                            }
   uint              Bars(void)                                            const { return this.m_bars;                              }
   bool              IsNewBar(const datetime time)                               { return this.m_new_bar_obj.IsNewBar(time);        }
   bool              IsNewBarManual(const datetime time)                         { return this.m_new_bar_obj.IsNewBarManual(time);  }
//--- Return the bar object by index (1) in the list and (2) in the timeseries, as well as (3) the real list size
   CBar             *GetBarByListIndex(const uint index);
   CBar             *GetBarBySeriesIndex(const uint index);
   int               DataTotal(void)                                       const { return this.m_list_series.Total();               }
//--- Return (1) Open, (2) High, (3) Low, (4) Close, (5) time, (6) tick volume, (7) real volume, (8) bar spread by index
   double            Open(const uint index,const bool from_series=true);
   double            High(const uint index,const bool from_series=true);
   double            Low(const uint index,const bool from_series=true);
   double            Close(const uint index,const bool from_series=true);
   datetime          Time(const uint index,const bool from_series=true);
   long              TickVolume(const uint index,const bool from_series=true);
   long              RealVolume(const uint index,const bool from_series=true);
   int               Spread(const uint index,const bool from_series=true);

//--- Save the new bar time during the manual time management
   void              SaveNewBarTime(const datetime time)                         { this.m_new_bar_obj.SaveNewBarTime(time);         }
//--- Synchronize symbol and timeframe data with server data
   bool              SyncData(const uint amount,const uint rates_total);
//--- (1) Create and (2) update the timeseries list
   int               Create(const uint amount=0);
   void              Refresh(const datetime time=0,
                             const double open=0,
                             const double high=0,
                             const double low=0,
                             const double close=0,
                             const long tick_volume=0,
                             const long volume=0,
                             const int spread=0);
                             
//--- Constructors
                     CSeries(void);
                     CSeries(const string symbol,const ENUM_TIMEFRAMES timeframe,const uint amount=0);
  };
//+------------------------------------------------------------------+

Fora do corpo da classe, escrevemos a implementação de métodos para definir o símbolo e o período gráfico:

//+------------------------------------------------------------------+
//| Set a symbol                                                     |
//+------------------------------------------------------------------+
void CSeries::SetSymbol(const string symbol)
  {
   this.m_symbol=(symbol==NULL || symbol==""   ? ::Symbol() : symbol);
   this.m_new_bar_obj.SetSymbol(this.m_symbol);
  }
//+------------------------------------------------------------------+
//| Set a timeframe                                                  |
//+------------------------------------------------------------------+
void CSeries::SetTimeframe(const ENUM_TIMEFRAMES timeframe)
  {
   this.m_timeframe=(timeframe==PERIOD_CURRENT ? (ENUM_TIMEFRAMES)::Period() : timeframe);
   this.m_new_bar_obj.SetPeriod(this.m_timeframe);
  }
//+------------------------------------------------------------------+

Primeiro, os valores passados para os métodos são verificados e ajustados, conforme necessário, e são gravados em variáveis,
em seguida, este valor é gravado no objeto da classe "Nova barra".

À listagem da classe CSelect, localizada em \MQL5\Include\DoEasy\Services\Select.mqh,
em vez do arquivo Bar.mqh da classe CBar anexamos o arquivo da classe CSeries:

//+------------------------------------------------------------------+
//|                                                       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\Series.mqh"
//+------------------------------------------------------------------+
//| Storage list                                                     |
//+------------------------------------------------------------------+
CArrayObj   ListStorage; // Storage object for storing sorted collection lists
//+------------------------------------------------------------------+
//| Class for sorting objects meeting the criterion                  |
//+------------------------------------------------------------------+
class CSelect
  {

Agora tudo está pronto para criar uma classe de objeto abrangendo todas as séries temporais de um símbolo.

O que representa o objeto das séries temporais do símbolo? No último artigo, criamos um objeto-série temporal com base no mesmo período para um só símbolo. Agora, estamos lidando com uma lista-série temporal, cujos objetos-barras podem ser classificados e/ou procurados de acordo com qualquer uma das suas propriedades, etc. Porém, os programas geralmente precisam usar uma análise multiperíodo do histórico para um ou mais símbolos. Quando esse é ocaso, o objeto em questão ficará contendo muitas séries temporais abrangendo todos os períodos gráficos possíveis para um só símbolo. A quantidade de períodos gráficos pode ser igual ao número dos mesmos no terminal, descritos na enumeração ENUM_TIMEFRAMES.

Na verdade, o objeto em questão é uma matriz de ponteiros para objetos CArrayObj, objetos esses que são listas das séries temporais do símbolo,
que nós criamos no artigo anterior e que, por sua vez, contêm objetos-barras.

Bem, hoje criaremos um objeto contendo todas as séries temporais do símbolo e que permite:

  • definir manualmente no programa o uso de:
    • períodos gráficos especificados para um símbolo
    • todos os períodos gráficos possíveis para um símbolo
  • criar:
    • objetos-séries temporais especificados para um símbolo
    • todos os objetos-séries temporais possíveis para um símbolo
  • atualizar dados de:
    • objetos-séries temporais especificados para um símbolo
    • todos os objetos-séries temporais criados para um símbolo

Nos próximos artigos, adicionaremos a funcionalidade restante ao criar o objeto de todas as séries temporais abrangendo todos os símbolos usados.

Como já se tornou habitual, primeiro adicionamos as mensagens necessárias para a nova classe (índices de mensagens e textos correspondentes aos índices).

No arquivo Datas.mqh, localizado em \MQL5\Include\DoEasy\Datas.mqh, adicionamos os índices de mensagens novas:

   MSG_LIB_TEXT_BAR_TEXT_FIRS_SET_AMOUNT_DATA,        // First, we need to set the required amount of data using SetAmountUsedData()
  
//--- CTimeSeries
   MSG_LIB_TEXT_TS_TEXT_FIRS_SET_SYMBOL,              // First, set a symbol using SetSymbol()
   MSG_LIB_TEXT_TS_TEXT_UNKNOWN_TIMEFRAME,            // Unknown timeframe
   MSG_LIB_TEXT_TS_FAILED_GET_SERIES_OBJ,             // Failed to receive the timeseries object
  };
//+------------------------------------------------------------------+

e os textos que correspondem aos índices recém-adicionados:

   {"Сначала нужно установить требуемое количество данных при помощи SetAmountUsedData()","First you need to set required amount of data using SetAmountUsedData()"},
   
   {"Сначала нужно установить символ при помощи SetSymbol()","First you need to set Symbol using SetSymbol()"},
   {"Неизвестный таймфрейм","Unknown timeframe"},
   {"Не удалось получить объект-таймсерию ","Failed to get timeseries object "},
   
  };
//+---------------------------------------------------------------------+

No diretório da biblioteca \MQL5\Include\DoEasy\Objects\Series\ criamos um novo arquivo TimeSeries.mqh da classe CTimeSeries contendo o arquivo de objeto-série temporal Series.mqh anexado a ele, e o CObject herdado do objeto base da biblioteca padrão.
Preenchemos imediatamente o corpo da classe com todo o conteúdo necessário e, em seguida, analisamos todas as variáveis e métodos individualmente:

//+------------------------------------------------------------------+
//|                                                   TimeSeries.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 "Series.mqh"
//+------------------------------------------------------------------+
//| Timeseries class                                                 |
//+------------------------------------------------------------------+
class CTimeSeries : public CObject
  {
private:
   string            m_symbol;                                             // Timeseries symbol
   CArrayObj         m_list_series;                                        // List of timeseries by timeframes
//--- Return (1) the timeframe index in the list and (2) the timeframe by index
   char              IndexTimeframe(const ENUM_TIMEFRAMES timeframe) const;
   ENUM_TIMEFRAMES   TimeframeByIndex(const uchar index)             const;
public:
//--- Return (1) the full list of timeseries, (2) specified timeseries object and (3) timeseries object by index
   CArrayObj        *GetList(void)                                         { return &this.m_list_series;                                        }
   CSeries          *GetSeries(const ENUM_TIMEFRAMES timeframe)            { return this.m_list_series.At(this.IndexTimeframe(timeframe));      }
   CSeries          *GetSeriesByIndex(const uchar index)                   { return this.m_list_series.At(index);                               }
//--- Set/return timeseries symbol
   void              SetSymbol(const string symbol)                        { this.m_symbol=(symbol==NULL || symbol=="" ? ::Symbol() : symbol);  }
   string            Symbol(void)                                    const { return this.m_symbol;                                              }
//--- Set the history depth (1) of a specified timeseries and (2) of all applied symbol timeseries
   bool              SetAmountUsedData(const ENUM_TIMEFRAMES timeframe,const uint amount=0,const int rates_total=0);
   bool              SetAmountAllUsedData(const uint amount=0,const int rates_total=0);
//--- Return the flag of data synchronization with the server data of the (1) specified timeseries, (2) all timeseries
   bool              SyncData(const ENUM_TIMEFRAMES timeframe,const uint amount=0,const uint rates_total=0);
   bool              SyncAllData(const uint amount=0,const uint rates_total=0);
   
//--- Create (1) the specified timeseries list and (2) all timeseries lists
   bool              SeriesCreate(const ENUM_TIMEFRAMES timeframe,const uint amount=0);
   bool              SeriesCreateAll(const uint amount=0);
//--- Update (1) the specified timeseries list and (2) all timeseries lists
   void              Refresh(const ENUM_TIMEFRAMES timeframe,
                             const datetime time=0,
                             const double open=0,
                             const double high=0,
                             const double low=0,
                             const double close=0,
                             const long tick_volume=0,
                             const long volume=0,
                             const int spread=0);
   void              RefreshAll(const datetime time=0,
                             const double open=0,
                             const double high=0,
                             const double low=0,
                             const double close=0,
                             const long tick_volume=0,
                             const long volume=0,
                             const int spread=0);
//--- Constructor
                     CTimeSeries(void);
  };
//+------------------------------------------------------------------+

A variável-membro da classe m_symbol armazenará o nome do símbolo para o qual serão criadas, armazenadas e processadas as séries temporais necessárias. Posteriormente, com base no valor dessa variável serão selecionados no programa os objetos com as séries temporais dos símbolos necessários.

O objeto da matriz de ponteiros para instâncias das classes CObject m_list_series se destina a armazenar os objetos-séries temporais criados no artigo anterior. A quantidade de tais objetos nesta lista pode coincidir com o número de períodos gráficos disponíveis na plataforma, além disso, todos têm a mesma ordem que na enumeração ENUM_TIMEFRAMES, que, por sua vez, nos dá a oportunidade de conhecer exatamente o índice de cada objeto de série temporal. Para retornar o índice na lista de objetos-séries temporais foram criados dois métodos:

O método IndexTimeframe() retorna o índice do objeto-série temporal na lista de acordo com o valor do período gráfico.
Sua implementação fora do corpo da classe:

//+------------------------------------------------------------------+
//| Return the timeframe index in the list                           |
//+------------------------------------------------------------------+
char CTimeSeries::IndexTimeframe(ENUM_TIMEFRAMES timeframe) const
  {
   int statement=(timeframe==PERIOD_CURRENT ? ::Period() : timeframe);
   switch(statement)
     {
      case PERIOD_M1    :  return 0;
      case PERIOD_M2    :  return 1;
      case PERIOD_M3    :  return 2;
      case PERIOD_M4    :  return 3;
      case PERIOD_M5    :  return 4;
      case PERIOD_M6    :  return 5;
      case PERIOD_M10   :  return 6;
      case PERIOD_M12   :  return 7;
      case PERIOD_M15   :  return 8;
      case PERIOD_M20   :  return 9;
      case PERIOD_M30   :  return 10;
      case PERIOD_H1    :  return 11;
      case PERIOD_H2    :  return 12;
      case PERIOD_H3    :  return 13;
      case PERIOD_H4    :  return 14;
      case PERIOD_H6    :  return 15;
      case PERIOD_H8    :  return 16;
      case PERIOD_H12   :  return 17;
      case PERIOD_D1    :  return 18;
      case PERIOD_W1    :  return 19;
      case PERIOD_MN1   :  return 20;
      default           :  ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_TS_TEXT_UNKNOWN_TIMEFRAME)); return WRONG_VALUE;
     }
  }
//+------------------------------------------------------------------+

Aqui tudo é claro, dependendo do período gráfico transferido ao método, é retornado seu número de sequência na enumeração ENUM_TIMEFRAMES, e, consequentemente, seu índice na lista m_list_series.  

O método TimeframeByIndex() retorna o período gráfico do índice do objeto-série temporal na lista.
Sua implementação fora do corpo da classe:

//+------------------------------------------------------------------+
//| Return a timeframe by index                                      |
//+------------------------------------------------------------------+
ENUM_TIMEFRAMES CTimeSeries::TimeframeByIndex(const uchar index) const
  {
   switch(index)
     {
      case 0   :  return PERIOD_M1;
      case 1   :  return PERIOD_M2;
      case 2   :  return PERIOD_M3;
      case 3   :  return PERIOD_M4;
      case 4   :  return PERIOD_M5;
      case 5   :  return PERIOD_M6;
      case 6   :  return PERIOD_M10;
      case 7   :  return PERIOD_M12;
      case 8   :  return PERIOD_M15;
      case 9   :  return PERIOD_M20;
      case 10  :  return PERIOD_M30;
      case 11  :  return PERIOD_H1;
      case 12  :  return PERIOD_H2;
      case 13  :  return PERIOD_H3;
      case 14  :  return PERIOD_H4;
      case 15  :  return PERIOD_H6;
      case 16  :  return PERIOD_H8;
      case 17  :  return PERIOD_H12;
      case 18  :  return PERIOD_D1;
      case 19  :  return PERIOD_W1;
      case 20  :  return PERIOD_MN1;
      default  :  ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_NOT_GET_DATAS),"... ",CMessage::Text(MSG_SYM_STATUS_INDEX),": ",(string)index); return WRONG_VALUE;
     }
  }
//+------------------------------------------------------------------+

Este método é o oposto do método IndexTimeframe(). Dependendo do índice transferido ao método, é retornado o período gráfico correspondente ao índice de acordo com a ordem de localização na enumeração ENUM_TIMEFRAMES.

O método GetList() retorna uma lista completa contendo todos os períodos gráficos "tal como estão" para o programa de controle. E já no programa, é possível selecionar as séries temporais a partir da lista obtida.

O método GetSeries() retorna o objeto-série temporal especificado a partir da lista m_list_series de acordo com o nome da série temporal a partir da enumeração ENUM_TIMEFRAMES. Obtemos o índice da série temporal na lista com ajuda do método IndexTimeframe() discutido anteriormente.

O método GetSeriesByIndex() retorna o objeto-série temporal de acordo com seu índice na lista m_list_series.

Implementação do método que define a profundidade do histórico da série temporal especificada:

//+------------------------------------------------------------------+
//| Set a history depth of a specified timeseries                    |
//+------------------------------------------------------------------+
bool CTimeSeries::SetAmountUsedData(const ENUM_TIMEFRAMES timeframe,const uint amount=0,const int rates_total=0)
  {
   if(this.m_symbol==NULL)
     {
      ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_TS_TEXT_FIRS_SET_SYMBOL));
      return false;
     }
   CSeries *series_obj=this.m_list_series.At(this.IndexTimeframe(timeframe));
   return series_obj.SetAmountUsedData(amount,rates_total);
  }
//+------------------------------------------------------------------+

Ao método é transferido o período gráfico da série temporal, a profundidade do histórico a ser definida, o tamanho dos dados históricos da série temporal (profundidade do histórico; se o valor for 0, será usada uma profundidade de 1000 barras) e o número de barras da série temporal (apenas para indicadores ao definir a profundidade do histórico para o símbolo atual no período gráfico atual — transferimos o parâmetro rates_total em OnCalculate(), em outros casos, o parâmetro não importa).

Se o símbolo do objeto da classe ainda não estiver definido, exibimos uma mensagem sobre isso e retornamos false.
Obtemos o objeto-série temporal solicitado a partir da lista de acordo com seu índice que obtido pelo nome do período gráfico, e retornamos o resultado da definição da profundidade do histórico com ajuda do método - que tem o mesmo nome - do objeto-série temporal que vimos no artigo anterior.

Implementação do método que define a profundidade do histórico para todas as séries temporais de símbolo usadas:

//+------------------------------------------------------------------+
//| Set the history depth of all applied symbol timeseries           |
//+------------------------------------------------------------------+
bool CTimeSeries::SetAmountAllUsedData(const uint amount=0,const int rates_total=0)
  {
   if(this.m_symbol==NULL)
     {
      ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_TS_TEXT_FIRS_SET_SYMBOL));
      return false;
     }
   bool res=true;
   for(int i=0;i<21;i++)
     {
      CSeries *series_obj=this.m_list_series.At(i);
      if(series_obj==NULL)
         continue;
      res &=series_obj.SetAmountUsedData(amount,rates_total);
     }
   return res;
  }
//+------------------------------------------------------------------+

Ao método é transferido o tamanho de dados históricos de série temporal necessário (profundidade do histórico; se o valor for 0, será usada uma profundidade de 1000 barras) e o número de barras da série temporal atual (apenas para indicadores ao definir a profundidade do histórico para o símbolo atual no período gráfico atual — transferimos o parâmetro rates_total em OnCalculate(), em outros casos, o parâmetro não importa).

Se o símbolo do objeto da classe ainda não estiver definido, exibimos uma mensagem sobre isso e retornamos false.
Num ciclo percorrendo a lista que contém todos os períodos gráficos existentes obtemos o próximo objeto-série temporal de acordo com o índice de ciclo e registramos o resultado da definição da profundidade do histórico com ajuda do método - que tem o mesmo nome - do objeto-série temporal que vimos no artigo anterior, na variável local res. Se pelo menos um dos métodos para definir a profundidade do histórico de qualquer um dos objetos-séries temporais disponíveis retornar false, na variável será registrado o valor false.
No final do ciclo, retornamos o resultado de todas as definições registradas na variável res.

Implementação do método que retorna um sinalizador para sincronização de dados com o servidor da série temporal especificada:

//+------------------------------------------------------------------+
//| Return the flag of data synchronization                          |
//| with the server data                                             |
//+------------------------------------------------------------------+
bool CTimeSeries::SyncData(const ENUM_TIMEFRAMES timeframe,const uint amount=0,const uint rates_total=0)
  {
   if(this.m_symbol==NULL)
     {
      ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_TS_TEXT_FIRS_SET_SYMBOL));
      return false;
     }
   CSeries *series_obj=this.m_list_series.At(this.IndexTimeframe(timeframe));
   if(series_obj==NULL)
     {
      ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_TS_FAILED_GET_SERIES_OBJ),this.m_symbol," ",TimeframeDescription(timeframe));
      return false;
     }
   return series_obj.SyncData(amount,rates_total);
  }
//+------------------------------------------------------------------+

Ao método é transferido o período gráfico da série temporal, o sinalizador a ser retornado, o tamanho dos dados históricos da série temporal (profundidade do histórico; se o valor for 0, será usada uma profundidade de 1000 barras) e o número de barras da série temporal (apenas para indicadores ao definir a profundidade do histórico para o símbolo atual no período gráfico atual — transferimos o parâmetro rates_total em OnCalculate(), em outros casos, o parâmetro não importa).

Se o símbolo do objeto da classe ainda não estiver definido, exibimos uma mensagem sobre isso e retornamos false.
Obtemos o objeto-série temporal solicitado a partir da lista de acordo com seu índice que obtido pelo nome do período gráfico, e retornamos o resultado da verificação da sincronização de dados com ajuda do método - que tem o mesmo nome - do objeto-série temporal que vimos no artigo anterior.

Implementação do método que retorna um sinalizador para sincronização de dados com o servidor para todas as séries temporais:

//+------------------------------------------------------------------+
//| Return the flag of data synchronization                          |
//| of all timeseries with the server data                           |
//+------------------------------------------------------------------+
bool CTimeSeries::SyncAllData(const uint amount=0,const uint rates_total=0)
  {
   if(this.m_symbol==NULL)
     {
      ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_TS_TEXT_FIRS_SET_SYMBOL));
      return false;
     }
   bool res=true;
   for(int i=0;i<21;i++)
     {
      CSeries *series_obj=this.m_list_series.At(i);
      if(series_obj==NULL)
         continue;
      res &=series_obj.SyncData(amount,rates_total);
     }
   return res;
  }
//+------------------------------------------------------------------+

Ao método é transferido o tamanho de dados históricos de série temporal necessário (profundidade do histórico; se o valor for 0, será usada uma profundidade de 1000 barras) e o número de barras da série temporal atual (apenas para indicadores ao definir a profundidade do histórico para o símbolo atual no período gráfico atual — transferimos o parâmetro rates_total em OnCalculate(), em outros casos, o parâmetro não importa).

Se o símbolo do objeto da classe ainda não estiver definido, exibimos uma mensagem sobre isso e retornamos false.
Num ciclo percorrendo a lista que contém todos os períodos gráficos existentes obtemos o próximo objeto-série temporal de acordo com o índice de ciclo e registramos o sinalizador de verificação de sincronização de dados com ajuda do método - que tem o mesmo nome - do objeto-série temporal que vimos no artigo anterior, na variável local res. Se pelo menos um dos métodos para verificar a sincronização dos objetos-séries temporais retornar false, na variável será registrado o valor false.
No final do ciclo, retornamos o resultado de todas as definições registradas na variável res
.

Implementação do método que cria a lista-série temporal especificada:

//+------------------------------------------------------------------+
//| Create a specified timeseries list                               |
//+------------------------------------------------------------------+
bool CTimeSeries::Create(const ENUM_TIMEFRAMES timeframe,const uint amount=0)
  {
   CSeries *series_obj=this.m_list_series.At(this.IndexTimeframe(timeframe));
   if(series_obj==NULL)
     {
      ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_TS_FAILED_GET_SERIES_OBJ),this.m_symbol," ",TimeframeDescription(timeframe));
      return false;
     }
   if(series_obj.AmountUsedData()==0)
     {
      ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_BAR_TEXT_FIRS_SET_AMOUNT_DATA));
      return false;
     }
   return(series_obj.Create(amount)>0);
  }
//+------------------------------------------------------------------+

Ao método são transferidos o período gráfico, cuja série temporal é necessário criar, e a profundidade do histórico da série temporal criada (por padrão, é zero, quer dizer: é criada uma série temporal com uma profundidade de histórico definida anteriormente para o objeto-série temporal por meio do método SetAmountUsedData(), se for maior que zero e menor que a quantidade de barras disponíveis da série temporal do período gráfico especificado, será usado o valor de profundidade de histórico criado, valor esse que transferido ao método)

Obtemos o objeto-série temporal de acordo com seu índice, obtido pelo nome do período gráfico. Se não for possível obter o objeto ou se para ele ainda não tiver sido definida uma profundidade de histórico, exibiremos uma mensagem sobre isto e retornaremos false.

Desde o método retornaremos o resultado de criação da série temporal gerada por meio do método de objeto-série temporal que tem o mesmo nome e que consideramos no artigo anterior. Como, ao contrário do método do artigo anterior (que retorna o número de objetos-barras adicionado à lista-série temporal), este método retorna um valor booleano, para retornar true ou false basta retornar o resultado de quando se compara se a quantidade de elementos adicionados à lista é "maior que zero", que é o que estamos fazendo aqui.

Implementação do método que cria todas as listas-séries temporais:

//+------------------------------------------------------------------+
//| Create all timeseries lists                                      |
//+------------------------------------------------------------------+
bool CTimeSeries::CreateAll(const uint amount=0)
  {
   bool res=true;
   for(int i=0;i<21;i++)
     {
      CSeries *series_obj=this.m_list_series.At(i);
      if(series_obj==NULL || series_obj.AmountUsedData()==0)
         continue;
      res &=(series_obj.Create(amount)>0);
     }
   return res;
  }
//+------------------------------------------------------------------+

Ao método são transferidos a profundidade do histórico da série temporal criada (por padrão, é zero, quer dizer: é criada uma série temporal com uma profundidade de histórico definida anteriormente para o objeto-série temporal por meio do método SetAmountUsedData(), se for maior que zero e menor que a quantidade de barras disponíveis da série temporal do período gráfico especificado, será usado o valor de profundidade de histórico criado, valor esse que transferido ao método)

Num ciclo percorrendo a lista que contém todos os períodos gráficos obtemos o próximo objeto-série temporal de acordo com o índice de ciclo. Se não for possível obter o objeto ou se para ele ainda não tiver sido definida uma profundidade de histórico, continuamos com o seguinte período gráfico.

Na variável local res registramos o resultado da criação da série temporal da classe do objeto-série temporal , que consideramos no artigo anterior, expresso como resultado da comparação de se a quantidade de elementos adicionados à lista é "maior que zero". Se pelo menos um dos métodos para criar objetos-séries temporais retornar false, na variável será registrado o valor false.
No final do ciclo, retornamos o resultado da criação de todas as séries temporais registradas na variável res
.

Implementação do método que atualiza a lista-série temporal especificada:

//+------------------------------------------------------------------+
//| Update a specified timeseries list                               |
//+------------------------------------------------------------------+
void CTimeSeries::Refresh(const ENUM_TIMEFRAMES timeframe,
                          const datetime time=0,
                          const double open=0,
                          const double high=0,
                          const double low=0,
                          const double close=0,
                          const long tick_volume=0,
                          const long volume=0,
                          const int spread=0)
  {
   CSeries *series_obj=this.m_list_series.At(this.IndexTimeframe(timeframe));
   if(series_obj==NULL || series_obj.DataTotal()==0)
      return;
   series_obj.Refresh(time,open,high,low,close,tick_volume,volume,spread);
  }
//+------------------------------------------------------------------+

Ao método é transferido o período gráfico da série temporal atualizada e os dados de preço da barra atual (apenas para indicadores ao atualizar os dados do símbolo atual no período gráfico atual — transferimos os dados a partir das matrizes de preços em OnCalculate(), em outros casos, os valores dos parâmetros transferidos não importam).

Obtemos o objeto-série temporal de acordo com seu índice, obtido pelo nome do período gráfico. Se não for possível obter o objeto ou se o tamanho do histórico criado para o período gráfico for zero (o período gráfico não é usado ou não foi criado através do método Create()), sairemos do método.
Em seguida, chamamos o mesmo método de atualização do objeto-série temporal que revisamos no último artigo.

Implementação do método que atualiza todas as listas-séries temporais:

//+------------------------------------------------------------------+
//| Update all timeseries lists                                      |
//+------------------------------------------------------------------+
void CTimeSeries::RefreshAll(const datetime time=0,
                             const double open=0,
                             const double high=0,
                             const double low=0,
                             const double close=0,
                             const long tick_volume=0,
                             const long volume=0,
                             const int spread=0)
  {
   for(int i=0;i<21;i++)
     {
      CSeries *series_obj=this.m_list_series.At(i);
      if(series_obj==NULL || series_obj.DataTotal()==0)
         continue;
      series_obj.Refresh(time,open,high,low,close,tick_volume,volume,spread);
     }
  }
//+------------------------------------------------------------------+

Ao método são transferidos os dados de preço da barra atual apenas para indicadores ao atualizar os dados do símbolo atual no período gráfico atual — transferimos os dados a partir das matrizes de preços em OnCalculate(), em outros casos, os valores dos parâmetros transferidos não importam).

Num ciclo percorrendo a lista que contém todos os períodos gráficos obtemos o próximo objeto-série temporal de acordo com o índice de ciclo. Se não for possível obter o objeto ou se o tamanho do histórico criado para o período gráfico for zero (o período gráfico não é usado ou não foi criado através do método Create()), sairemos do método.

Em seguida, chamamos o mesmo método de atualização do objeto-série temporal que revisamos no último artigo.

Assim, concluímos a primeira versão da classe do objeto que abrange todas as séries temporais de um símbolo. A funcionalidade criada é suficiente para trabalhar com vários períodos gráficos de um símbolo. No futuro, certamente, vamos refiná-la ao criar uma classe-coleção de séries temporais geral para muitos símbolos.

No arquivo da classe CEngine, localizado em \MQL5\Include\DoEasy\Engine.mqh, substituímos a linha para anexar o arquivo da classe do objeto-série temporal:

//+------------------------------------------------------------------+
//| 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 "TradingControl.mqh"
#include "Objects\Series\Series.mqh"
//+------------------------------------------------------------------+

pelo arquivo da classe do objeto das séries temporais de todos os períodos usados do símbolo:

//+------------------------------------------------------------------+
//| 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 "TradingControl.mqh"
#include "Objects\Series\TimeSeries.mqh"
//+------------------------------------------------------------------+

Agora, nos programas baseados nesta biblioteca ficará visível a classe criada hoje.

Teste

Para testar o trabalho com séries temporais de diferentes períodos de um símbolo, tomamos um EA do artigo anterior e o armazenamos na nova pasta \MQL5\Experts\TestDoEasy\Part36\ usando o novo nome TestDoEasyPart36.mq5.

Para testar o funcionamento da classe, criamos duas versões do EA de teste usando diretivas de compilação condicional.

  • A primeira versão do EA criará duas séries temporais do símbolo atual:
    uma para o período M15 consistindo em apenas duas barras,
    outra composta por dez barras, para o período gráfico atual no qual está sendo executado o EA.
  • A segunda versão do EA criará todas as séries temporais do símbolo atual com valores padrão:
    serão criadas 1000 barras para cada série temporal ou será gerado um máximo possível, desde que o número disponível de barras para a série temporal seja menor que 1000.

No bloco de variáveis globais do Expert Advisor, deixaremos apenas um objeto da classe de séries temporais, em vez da classe CSeries, definimos a variável de classe CTimeSeries, pois os objetos da classe CSeries agora são parte integrante do objeto de classe CTimeSeries.
Excluímos um objeto de CSeries:

//--- global variables
CEngine        engine;
CSeries        series;
CSeries        series_m1;
SDataButt      butt_data[TOTAL_BUTT];

e renomeamos o segundo objeto e definimos seu tipo como CTimeSeries:

//--- global variables
CEngine        engine;
CTimeSeries    timeseries;
SDataButt      butt_data[TOTAL_BUTT];

No final do manipulador OnInit() do EA, escreveremos um bloco de código para criar séries temporais, dependendo da existência/ausência de um determinado identificador TIMESERIES_ALL:

//--- Check playing a standard sound by macro substitution and a custom sound by description
   engine.PlaySoundByDescription(SND_OK);
   Sleep(600);
   engine.PlaySoundByDescription(TextByLanguage("Звук упавшей монетки 2","Falling coin 2"));

//--- Set a symbol for created timeseries
   timeseries.SetSymbol(Symbol());
//#define TIMESERIES_ALL
//--- Create two timeseries
   #ifndef TIMESERIES_ALL
      timeseries.SyncData(PERIOD_CURRENT,10);
      timeseries.Create(PERIOD_CURRENT);
      timeseries.SyncData(PERIOD_M15,2);
      timeseries.Create(PERIOD_M15);
//--- Create all timeseries
   #else 
      timeseries.SyncAllData();
      timeseries.CreateAll();
   #endif 
//--- Check created timeseries
   CArrayObj *list=timeseries.GetList();
   Print(TextByLanguage("Данные созданных таймсерий:","Data of created timeseries:"));
   for(int i=0;i<list.Total();i++)
     {
      CSeries *series_obj=timeseries.GetSeriesByIndex((uchar)i);
      if(series_obj==NULL || series_obj.AmountUsedData()==0 || series_obj.DataTotal()==0)
         continue;
      Print(
            DFUN,i,": ",series_obj.Symbol()," ",TimeframeDescription(series_obj.Timeframe()),
            ": AmountUsedData=",series_obj.AmountUsedData(),", DataTotal=",series_obj.DataTotal(),", Bars=",series_obj.Bars()
           );
     }
   Print("");
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+

Primeiro de tudo, definimos o nome do símbolo para a classe das séries temporais, em seguida, o identificador aparecerá definido e comentado na forma de uma substituição de macros, cuja ausência/presença definirá qual versão do código deverá ser compilada: criação de duas séries temporais (se não houver identificador) ou criação de todas as séries temporais (se houver identificador)

Em geral, para criar uma série temporal com base na versão atual da classe do objeto de todas as séries temporais do símbolo, é necessário :

  1. definir o símbolo do objeto de todas as séries temporais do símbolo,
  2. chamar o método usado para definir a profundidade do histórico da série temporal e para verificar a sincronização de dados da série temporal com o servidor,
  3. criar uma série temporal com base na profundidade do histórico estabelecida.

Em condições normais, é necessário verificar o resultado retornado com base no método usado para definir a profundidade do histórico e para verificar a sincronização com o servidor. Afinal, o método pode retornar false se não for possível definir a profundidade do histórico ou se os dados ainda não estiverem sincronizados com o servidor.
Mas, quanto ao teste com base no símbolo atual, podemos ignorar dada verificação, pois provavelmente todos os dados estarão disponíveis. Se não estiverem disponíveis, a série temporal simplesmente não será criada. Bastará reiniciar o EA, afinal, o primeiro uso das funções desencadeia o processo de carregamento de dados históricos, e na seguinte inicialização do EA os dados deverão estar sincronizados.

Após as séries temporais necessárias serem criadas, exibiremos no log a lista completa de séries temporais criadas, a fim de verificar se foram criadas com sucesso.
Para fazer isso, obtemos uma lista completa contendo todas as séries temporais e, no ciclo, obtemos o próximo objeto-série temporal da lista e, se essa série temporária for criada (possui um histórico profundo e é preenchida com dados), imprimimos esses dados no log.

Agora, no final do manipulador OnTick(), colocamos o bloco do código para atualização de dados de todas as séries temporais do símbolo:

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//--- If working in the tester
   if(MQLInfoInteger(MQL_TESTER))
     {
      engine.OnTimer();       // Working in the timer
      PressButtonsControl();  // Button pressing control
      EventsHandling();       // Working with events
     }
//--- If the trailing flag is set
   if(trailing_on)
     {
      TrailingPositions();    // Trailing positions
      TrailingOrders();       // Trailing of pending orders
     }
//--- Update created timeseries
   CArrayObj *list=timeseries.GetList();
   for(int i=0;i<list.Total();i++)
     {
      CSeries *series_obj=timeseries.GetSeriesByIndex((uchar)i);
      if(series_obj==NULL || series_obj.DataTotal()==0)
         continue;
      series_obj.Refresh();
      if(series_obj.IsNewBar(0))
        {
         Print(TextByLanguage("Новый бар на ","New bar on "),series_obj.Symbol()," ",TimeframeDescription(series_obj.Timeframe())," ",TimeToString(series_obj.Time(0)));
         if(series_obj.Timeframe()==Period())
            engine.PlaySoundByDescription(SND_NEWS);
        }
     }
  }
//+------------------------------------------------------------------+

Aqui, obtemos a lista de séries temporais a partir do objeto de todas as séries temporais do símbolo, num ciclo percorrendo a lista de séries temporais obtemos o próximo objeto-série temporal de acordo com o índice de ciclo, se não for possível obter a série temporal ou não houver dados (barras) nela, continuaremos com a seguinte série temporal do próximo período gráfico. Se o objeto-série temporal for obtido, iremos atualizá-lo. Se para a série temporal estiver definido o sinalizador de nova barra, exibiremos uma mensagem sobre isso (para a série temporal do período atual, tocamos adicionalmente o som "news.wav")

Compilaremos o EA (a string 176 com definição de substituição de macros #define TIMESERIES_ALL deverá ficar comentada): será compilada uma versão do EA que cria duas séries temporais.
Vamos iniciá-lo no terminal usando o período gráfico M30. No log serão exibidas entradas sobre os parâmetros de duas séries temporais criadas e, depois de algum tempo, aparecerão entradas sobre a abertura de novas barras nos períodos gráficos M15 e M30:

Account 8550475: Artyom Trishkin (MetaQuotes Software Corp.) 10425.23 USD, 1:100, Hedge, Demo account MetaTrader 5
Work only with the current symbol. The number of symbols used: 1
Data of created timeseries:
OnInit: 8: EURUSD M15: AmountUsedData=2, DataTotal=2, Bars=5000
OnInit: 10: EURUSD M30: AmountUsedData=10, DataTotal=10, Bars=5030
 
New bar on EURUSD M15 2020.02.20 20:45
New bar on EURUSD M15 2020.02.20 21:00
New bar on EURUSD M30 2020.02.20 21:00
New bar on EURUSD M15 2020.02.20 21:15
New bar on EURUSD M15 2020.02.20 21:30
New bar on EURUSD M30 2020.02.20 21:30

Agora, descomentaremos a string 176 com definição de substituição de macros #define TIMESERIES_ALL e compilaremos o EA: será criado um EA que criará todas as séries temporais com valores por padrão.

Iremos iniciá-lo no gráfico do símbolo. No log serão exibidas entradas sobre os parâmetros de todas as séries temporais criadas e, depois de algum tempo, aparecerão entradas sobre a abertura de novas barras nos períodos gráficos dessas séries:

Account 8550475: Artyom Trishkin (MetaQuotes Software Corp.) 10425.23 USD, 1:100, Hedge, Demo account MetaTrader 5
Work only with the current symbol. The number of symbols used: 1
Data of created timeseries:
OnInit: 0: EURUSD M1: AmountUsedData=1000, DataTotal=1000, Bars=5140
OnInit: 1: EURUSD M2: AmountUsedData=1000, DataTotal=1000, Bars=4010
OnInit: 2: EURUSD M3: AmountUsedData=1000, DataTotal=1000, Bars=3633
OnInit: 3: EURUSD M4: AmountUsedData=1000, DataTotal=1000, Bars=3445
OnInit: 4: EURUSD M5: AmountUsedData=1000, DataTotal=1000, Bars=3332
OnInit: 5: EURUSD M6: AmountUsedData=1000, DataTotal=1000, Bars=3256
OnInit: 6: EURUSD M10: AmountUsedData=1000, DataTotal=1000, Bars=3106
OnInit: 7: EURUSD M12: AmountUsedData=1000, DataTotal=1000, Bars=3068
OnInit: 8: EURUSD M15: AmountUsedData=1000, DataTotal=1000, Bars=5004
OnInit: 9: EURUSD M20: AmountUsedData=1000, DataTotal=1000, Bars=2993
OnInit: 10: EURUSD M30: AmountUsedData=1000, DataTotal=1000, Bars=5032
OnInit: 11: EURUSD H1: AmountUsedData=1000, DataTotal=1000, Bars=5352
OnInit: 12: EURUSD H2: AmountUsedData=1000, DataTotal=1000, Bars=6225
OnInit: 13: EURUSD H3: AmountUsedData=1000, DataTotal=1000, Bars=6212
OnInit: 14: EURUSD H4: AmountUsedData=1000, DataTotal=1000, Bars=5292
OnInit: 15: EURUSD H6: AmountUsedData=1000, DataTotal=1000, Bars=5182
OnInit: 16: EURUSD H8: AmountUsedData=1000, DataTotal=1000, Bars=5443
OnInit: 17: EURUSD H12: AmountUsedData=1000, DataTotal=1000, Bars=5192
OnInit: 18: EURUSD D1: AmountUsedData=1000, DataTotal=1000, Bars=5080
OnInit: 19: EURUSD W1: AmountUsedData=1000, DataTotal=1000, Bars=2562
OnInit: 20: EURUSD MN1: AmountUsedData=589, DataTotal=589, Bars=589

New bar on EURUSD M1 2020.02.20 21:41
New bar on EURUSD M1 2020.02.20 21:42
New bar on EURUSD M2 2020.02.20 21:42
New bar on EURUSD M3 2020.02.20 21:42
New bar on EURUSD M6 2020.02.20 21:42
New bar on EURUSD M1 2020.02.20 21:43
New bar on EURUSD M1 2020.02.20 21:44
New bar on EURUSD M2 2020.02.20 21:44
New bar on EURUSD M4 2020.02.20 21:44
New bar on EURUSD M1 2020.02.20 21:45
New bar on EURUSD M3 2020.02.20 21:45
New bar on EURUSD M5 2020.02.20 21:45
New bar on EURUSD M15 2020.02.20 21:45

Executamos no testador no modo visual no período gráfico M5:


Primeiro, o testador carrega os dados históricos de todos os períodos gráficos, depois o EA exibe os dados das séries temporais criadas e, em seguida, no log são exibidas as mensagens sobre a abertura de novas barras nas séries temporais criadas durante o progresso do teste.

Tudo funciona como planejado neste estágio de criação de funcional para trabalhar com séries temporais de um único símbolo.

O que vem agora?

No próximo artigo, criaremos uma classe-coleção geral de séries temporais que armazena a quantidade necessária de dados para diferentes símbolos e seus períodos gráficos.

Abaixo estão anexados todos os arquivos da versão atual da biblioteca e os arquivos do EA de teste. Você pode baixá-los e testar tudo sozinho.
Se você tiver 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


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

Arquivos anexados |
MQL5.zip (3686.04 KB)
MQL4.zip (3686.04 KB)
Otimização Walk Forward Contínua (Parte 4): Gerenciamento de Otimização (Otimizador Automático) Otimização Walk Forward Contínua (Parte 4): Gerenciamento de Otimização (Otimizador Automático)

O principal objetivo do artigo é descrever o mecanismo de trabalho com nosso aplicativo e seus recursos. Assim, o artigo pode ser tratado como instruções sobre como utilizar o aplicativo. Ele cobre todas as possíveis dificuldades e detalhes do uso do aplicativo.

Os projetos permitem que criar robôs de negociação lucrativos!  Mas não é exatamente isso Os projetos permitem que criar robôs de negociação lucrativos! Mas não é exatamente isso

Um programa grande começa com um arquivo pequeno que, por sua vez, gradualmente se torna maior, sendo preenchido com conjuntos de funções e objetos. A maioria dos desenvolvedores de robôs lida com esse problema por meio de arquivos de inclusão. Mas, o melhor é começar imediatamente a escrever os programas de negociação em projetos, pois isso é benéfico em todos os aspectos.

Implementado OLAP na negociação (Parte 4): análise quantitativa e visual dos relatórios do testador Implementado OLAP na negociação (Parte 4): análise quantitativa e visual dos relatórios do testador

O artigo oferece ferramentas básicas para análise OLAP dos relatórios do testador sobre execuções únicas e resultados de otimização em formatos padrão (tst e opt), bem como uma interface gráfica interativa. Os códigos fonte MQL são anexados ao final artigo.

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 37): coleção de séries temporais - banco de dados de séries temporais para símbolos e períodos

Este artigo é dedicado à criação de uma coleção de séries temporais com base nos períodos gráficos especificados para todos os símbolos usados no programa. Criaremos uma coleção de séries temporais, os métodos para definir os parâmetros dessas séries e inicialmente as preencheremos com dados históricos.