Trabajando con las series temporales en la biblioteca DoEasy (Parte 36): El objeto de series temporales de todos los periodos utilizados del símbolo

10 junio 2020, 10:47
Artyom Trishkin
0
554

Contenido


Concepto

En el artículo anterior, comenzamos una nueva serie dedicada a la descripción de la biblioteca DoEasy, y también analizamos la creación del objeto "Barra" y la lista de objetos de barra. Hablando en la terminología de la plataforma MetaTrader, hemos creado una serie temporal para un símbolo según un marco temporal, y la hemos rellenado con los datos de cada barra de dicha serie temporal.
Hablando en la terminología de la biblioteca DoEasy, hemos creado un objeto de colección de barras para un símbolo según un marco temporal. Ahora, tenemos la posibilidad de realizar cualquier búsqueda y clasificación en los límites de la colección creada (en los límites de la profundidad establecida para la historia) para cada una de las propiedades disponibles en la colección de objetos de barra. Dicho de una forma más simple: tenemos la posibilidad de buscar diferentes parámetros de las barras de la serie temporal y distintos combinaciones de estos (las distintas combinaciones de las barras las veremos en próximas implementaciones), además de determinar el surgimiento de una nueva barra en la colección creada y actualizar los datos actuales en la misma.

Esto está muy bien, pero se antoja poco, ya que en nuestros programas podemos utilizar más de un marco temporal y un símbolo. Y esto significa que debemos tener tantas colecciones de series temporales de un símbolo, como marcos temporales necesitamos usar en nuestros programas.
La colección de series temporales permite hacer esto, pues se crea con respecto a un símbolo y marco temporal, y eso significa que podemos crear tantas colecciones de este tipo para un símbolo cuantas necesitemos.

También resultaría muy cómodo guardar todas las colecciones de un mismo símbolo pero de marcos temporales distintos en un mismo objeto: el objeto de series temporales del símbolo. A continuación, partiendo de estos objetos, se creará una colección general de series temporales de diferentes símbolos, así como sus marcos temporales.


El objeto de series temporales del símbolo

En un futuro próximo, para muchas clases de la biblioteca será necesario conocer el tipo de programa en el que están iniciadas. Para ello, deberemos utilizar la función MQLInfoInteger() con el especificador MQL_PROGRAM_TYPE. En este caso, la función retornará el tipo del programa mql5 ejecutado.
Para no tener que registrar en cada clase las variables que guarda el tipo de programa, declararemos esta variable en la clase básica de todos los objetos del programa, y todas las clases heredadas de la clase básica tendrán una variable que guarde el tipo del programa ejecutado.

En la sección protegida de la clase CBaseObj, ubicada en la ruta \MQL5\Include\DoEasy\Objects\BaseObj.mqh,
declaramos la variable de miembro de clase que guarda el tipo de programa ejecutado:

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

y, ya en el constructor de la clase, establecemos en ella el valor:

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

Ahora, todos los objetos heredados de la clase básica de todos los objetos de la biblioteca "sabrán" el tipo del programa en el que están iniciados.

En el listado de la clase CNewBarObj, ubicada en el archivo \MQL5\Include\DoEasy\Objects\Series\NewBarObj.mqh, incluimos el archivo de la clase del objeto básico:

//+------------------------------------------------------------------+
//|                                                    NewBarObj.mqh |
//|                        Copyright 2020, MetaQuotes Software Corp. |
//|                             https://mql5.com/es/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, MetaQuotes Software Corp."
#property link      "https://mql5.com/es/users/artmedia70"
#property version   "1.00"
#property strict    // Necessary for mql4
//+------------------------------------------------------------------+
//| Archivos de inclusión                                            |
//+------------------------------------------------------------------+
#include "..\..\Objects\BaseObj.mqh"
//+------------------------------------------------------------------+
//| "New bar" object class                                           |
//+------------------------------------------------------------------+

heredamos el objeto "Nueva barra" del objeto básico:

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

y eliminamos del listado "todas las menciones" respecto a la variable del tipo del programa; ahora, el tipo de programa se establece en 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;
  }
//+------------------------------------------------------------------+

En los archivos adjuntos al final del artículo, podemos ver el listado completo de la clase con las correcciones pertinentes.

Vamos a mejorar ligeramente la clase CSeries, creada en el artículo anterior.

Heredamos la clase del objeto básico CBaseObj en lugar de CObject, y eliminamos de ella la variable que guarda el 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:

En la sección pública de la clase, declaramos los métodos para establecer el símbolo y el marco temporal, y añadimos el método que retorna la cantidad total de datos disponibles:

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

Fuera del cuerpo de la clase, implementamos los métodos para establecer el símbolo y el marco temporal:

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

Primero se comprueban los valores transmitidos a los métodos, y, si fuera necesario, se corrigen; después, estos se registran en las variables.
A continuación, este valor se registra en el objeto de clase "Nueva barra".

En el listado de la clase CSelect, ubicada en la dirección \MQL5\Include\DoEasy\Services\Select.mqh,
en lugar del archivo Bar.mqh de la clase CBar, incluimos el archivo de la clase CSeries:

//+------------------------------------------------------------------+
//|                                                       Select.mqh |
//|                        Copyright 2020, MetaQuotes Software Corp. |
//|                             https://mql5.com/es/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, MetaQuotes Software Corp."
#property link      "https://mql5.com/es/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Archivos de inclusión                                            |
//+------------------------------------------------------------------+
#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
  {

Ahora, ya tenemos todo preparado para crear la clase de objeto de todas las series temporales de un símbolo.

¿Qué supone el objeto de series temporales de un símbolo? En el artículo anterior, creamos el objeto de serie temporal de un periodo para un símbolo. Ahora, esta lista de series temporales (todos los objetos de barra que se encuentran en ella), se puede clasificar de acuerdo con cualquiera de las propiedades de un objeto de barra. También es posible buscar cualquier objeto de barra de acuerdo con cualquiera de sus propiedades, etcétera. No obstante, con frecuencia es necesario usar en los programas el análisis multiperiodo de la historia de uno o varios símbolos. Pues, en relación con esos casos, el objeto de serie temporal contiene multitud de series temporales de todos los marcos temporales posibles de un símbolo. El número de marcos temporales en él puede ser exactamente igual al número de periodos disponibles para el gráfico en el terminal, descritos en la enumeración ENUM_TIMEFRAMES.

En esencia, este objeto supone una matriz de punteros al objeto CArrayObj, en la que los objetos son las listas de las series temporales del símbolo
que hemos creado en el artículo anterior, y que a su vez contienen los objetos de barra.

Bien, hoy vamos a crear el objeto de todas las series temporales del símbolo, que nos permitirá:

  • establecer manualmente el uso en el programa de:
    • los periodos indicados del gráfico de un símbolo
    • todos los periodos posibles del gráfico de un símbolo
  • crear:
    • los objetos de serie temporal indicados para un símbolo
    • todos los objetos de serie temporal posibles para un símbolo
  • actualizar los datos de:
    • los objetos de serie temporal indicados para un símbolo
    • todos los objetos de serie temporal para un símbolo

El resto de la funcionalidad del objeto será añadido al crear el objeto de todas las series temporales de todos los símbolos utilizados en los próximos artículos.

Como viene siendo costumbre, primero escribiremos los mensajes necesarios para la nueva clase: los índices de los mensajes y los textos correspondientes a los índices.

En el archivo Datas.mqh, ubicado en la dirección \MQL5\Include\DoEasy\Datas.mqh, añadimos los índices de los nuevos mensajes:

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

y los textos de los mensajes que se corresponden con los índices nuevamente añadidos:

   {"Сначала нужно установить требуемое количество данных при помощи 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 "},
   
  };
//+---------------------------------------------------------------------+

Creamos en el directorio de la biblioteca \MQL5\Include\DoEasy\Objects\Series\ el nuevo archivo TimeSeries.mqh de la clase CTimeSeries con el archivo de objeto de serie temporal Series.mqh incluido en la misma, y con el CObject heredado del objeto básico de la biblioteca estándar.
Rellenamos de inmediato el cuerpo de la clase con el contenido necesario, y a continuación analizamos todas las variables y métodos individualmente:

//+------------------------------------------------------------------+
//|                                                   TimeSeries.mqh |
//|                        Copyright 2020, MetaQuotes Software Corp. |
//|                             https://mql5.com/es/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, MetaQuotes Software Corp."
#property link      "https://mql5.com/es/users/artmedia70"
#property version   "1.00"
#property strict    // Necessary for mql4
//+------------------------------------------------------------------+
//| Archivos de inclusión                                            |
//+------------------------------------------------------------------+
#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);
  };
//+------------------------------------------------------------------+

La variable de miembro de clase m_symbol guardará la denominación del símbolo para el cual, dentro del objeto, se crearán, guardarán y procesarán las series temporales necesarias. Como consecuencia, según el valor de esta variable se seleccionarán en el programa los objetos necesarios con la series temporales de los símbolos requeridos.

El objeto de la matriz de punteros a los ejemplares de las clases CObject m_list_series ha sido diseñado para guardar los objetos de series temporales creados en el artículo anterior. El número de estos objetos en la lista podrá ser exactamente igual al de todos los marcos temporales disponibles en la plataforma. Los objetos se ubicarán en la lista en el mismo orden secuencial de la enumeración ENUM_TIMEFRAMES, lo que a su vez nos dará la posibilidad de conocer exactamente el índice de cada objeto de serie temporal en esta lista. Para retornar el índice, hemos creado dos métodos en la lista del objeto de serie temporal:

El método IndexTimeframe() retorna el índice de un objeto de serie temporal en la lista según el valor del marco temporal.
Aquí tenemos su implementación fuera del cuerpo de la clase:

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

Aquí todo resulta muy visual: dependiendo del marco temporal transmitido al método, se retorna su número ordinal de ubicación en la enumeración ENUM_TIMEFRAMES, y, por consiguiente, su índice de ubicación en la lista m_list_series.  

El método TimeframeByIndex() retorna el marco temporal según el índice de un objeto de serie temporal en la lista.
Aquí tenemos su implementación fuera del cuerpo de la clase:

//+------------------------------------------------------------------+
//| 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 es el inverso del método IndexTimeframe(). Dependiendo del índice transmitido al método, se retorna el marco temporal correspondiente al índice en el orden de ubicación de la enumeración ENUM_TIMEFRAMES.

El método GetList() retorna al programa de control la lista completa de todas las series temporales "como es". Y ya en el programa, será posible seleccionar de la lista obtenida la serie temporal necesaria para trabajar con ella.

El método GetSeries() retorna el objeto de serie temporal indicado de la lista m_list_series según la denominación de la serie temporal necesaria de la enumeración ENUM_TIMEFRAMES. El índice de la serie temporal en la lista lo obtendremos con el el método IndexTimeframe(), anteriormente analizado.

El método GetSeriesByIndex() retorna un objeto de serie temporal según su índice en la lista m_list_series.

Implementamos el método que establece la profundidad de la historia de la serie temporal indicada:

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

Transmitimos al método el marco temporal de la serie temporal, la profundidad de la historia que debemos establecer, el tamaño necesario de los datos históricos de la serie temporal (profundidad de la historia; con un valor igual a 0, se usará una profundidad de 1000 barras) y el número de barras de la serie temporal actual (solo para los indicadores, al establecer la profundidad de la historia para el símbolo actual en el marco temporal actual, transmitimos el parámetro rates_total a OnCalculate(), en el resto de los casos, el parámetro no tiene importancia).

Si el símbolo no ha sido establecido para el objeto de la clase, mostramos un mensaje sobre ello y retornamos false.
Obtenemos el objeto de serie temporal solicitado de la lista según el índice obtenido de acuerdo con la denominación del marco temporal, yretornamos el resultado del establecimiento de la profundidad de la historia con la ayuda del método homónimo de la clase de objeto de serie temporal que hemos analizado en el artículo anterior.

Implementamos el método que establece la profundidad de la historia para todas las series temporales del símbolo utilizadas:

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

Transmitimos al método el tamaño necesario de los datos históricos de la serie tmeporal (profundidad de la historia; con un valor igual a 0, se usará una profundidad de 1000 barras) y el número de barras de la serie temporal actual (solo para los indicadores, al establecer la profundidad de la historia para el símbolo actual en el marco temporal actual, transmitimos el parámetro rates_total a OnCalculate(), en el resto de los casos, el parámetro no tiene importancia).

Si el símbolo no ha sido establecido para el objeto de la clase, mostramos un mensaje sobre ello y retornamos false.
En un ciclo por la lista completa de todos los marcos temporales disponibles, obtenemos de la lista el siguiente objeto de serie temporal según el índice del ciclo y registramos en la variable local res el resultado del establecimiento de la profundidad de la historia con la ayuda del método homónimo de la clase de objeto de serie temporal que hemos analizado en el artículo anterior. Si aunque sea uno solo de los métodos para establecer la profundidad de la historia de cualquiera de los objetos de serie temporal disponibles retorna false, en la variable se registrará el valor false.
Al finalizar el ciclo, retornamos el resultado de todos los establecimientos, registrado en la varible res.

Implementamos el método que retorna la bandera de sincronización de los datos con los datos en el servidor de la serie temporal indicada:

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

Transmitimos al método el marco temporal de la serie temporal, la bandera de sincronización que debemos retornar, el tamaño necesario de los datos históricos de la serie temporal (profundidad de la historia; con un valor igual a 0, se usará una profundidad de 1000 barras) y el número de barras de la serie temporal actual (solo para los indicadores, al establecer la profundidad de la historia para el símbolo actual en el marco temporal actual, transmitimos el parámetro rates_total a OnCalculate(), en el resto de los casos, el parámetro no tiene importancia).

Si el símbolo no ha sido establecido para el objeto de la clase, mostramos un mensaje sobre ello y retornamos false.
Obtenemos el objeto de serie temporal solicitado de la lista según el índice obtenido de acuerdo con la denominación del marco temporal, yretornamos el resultado de la comprobación de la sincronización de los datos con la ayuda del método homónimo de la clase de serie temporal que hemos analizado en el artículo anterior.

Implementamos el método que retorna la bandera de sincronización de los datos con los datos en el servidor para todas las series temporales:

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

Transmitimos al método el tamaño necesario de los datos históricos de la serie tmeporal (profundidad de la historia; con un valor igual a 0, se usará una profundidad de 1000 barras) y el número de barras de la serie temporal actual (solo para los indicadores, al establecer la profundidad de la historia para el símbolo actual en el marco temporal actual, transmitimos el parámetro rates_total a OnCalculate(), en el resto de los casos, el parámetro no tiene importancia).

Si el símbolo no ha sido establecido para el objeto de la clase, mostramos un mensaje sobre ello y retornamos false.
En un ciclo por la lista completa de todos los marcos temporales disponibles, obtenemos de la lista el siguiente objeto de serie temporal según el índice del ciclo y registramos en la variable local res la bandera de comprobación de la sincronización de los datos con el servidor con la ayuda del método homónimo de la clase de objeto de serie temporal que hemos analizado en el artículo anterior. Si aunque sea uno solo de los métodos para comprobar la sincronización de los objetos de serie temporal retorna false, en la variable se registrará el valor false.
Al finalizar el ciclo, retornamos el resultado de todas las comprobaciones, registrado en la varible res</s13>.

Implementación del método que crea la lista de serie temporal indicada:

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

Transmitimos al método el marco temporal cuya serie temporal debemos crear, y la profundidad de la historia de la serie temporal creada (por defecto, cero: se crea la serie temporal con la profundidad de historia anteriormente establecida para el objeto de serie temporal mediante el método SetAmountUsedData(), si es mayor a cero e inferior al número de barras disponibles de la serie temporal del periodo indicado para el gráfico, se usará el valor de profundidad de la historia creada tansmitido al método)

Obtenemos el objeto de serie temporal necesario según su índice, obtenido de acuerdo con la denominación del marco temporal. Si no hemos logrado obtener el objeto, o no se ha establecido para el mismo a profundidad de la historia, mostramos los mensajes correspondientes sobre ello y retornamos false.

Retornamos desde el método el resultado de la creación de la serie temporal mediante el método homónimo del objeto de serie temporal que hemos analizado en el artículo anterior. Dado que el método de creación de la serie temporal del artículo anterior retorna el número de objetos de barra añadidos a la lista de serie temporal, y este método retorna un valor booleano, para devolver true o false bastará con retornar el resultado de la comparación del número de elementos añadidos a la lista de "mayor que cero". Eso es precisamente lo que vamos a hacer aquí.

Implementamos el método que crea todas las listas de series temporales:

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

Transmitimos al método la profundidad de la historia de la serie temporal creada (por defecto, cero: se crea la serie temporal con la profundidad de historia anteriormente establecida para el objeto de serie temporal mediante el método SetAmountUsedData(), si es mayor a cero e inferior al número de barras disponibles de la serie temporal del periodo indicado para el gráfico, se usará el valor de profundidad de la historia creada tansmitido al método)

En un ciclo por la lista de todos los marcos temporales, obtenemos el siguiente objeto de la serie temporal según el índice del ciclo. Si no hemos logrado obtener el objeto, o no se ha establecido para el mismo a profundidad de la historia, pasamos al siguiente marco temporal.

Registramos en la variable local res el resultado de la creación de la serie temporal mediante el método homónimo del objeto de serie temporal que hemos analizado en el artículo anterior, expresado como el resultado de la comparación "mayor que cero" del número de elementos añadidos a la lista. Si aunque sea uno solo de los métodos para crear los objetos de serie temporal retorna false, en la variable se registrará el valor false.
Al finalizar el ciclo, retornamos el resultado de la creación de todas las series temporales, registrado en la varible res
.

Implementamos el método que actualiza la lista de serie temporal indicada:

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

Transmitimos al método el marco temporal de la serie temporal actualizada y los datos de precio de la barra actual (solo para los indicadores, al actualizar los datos del símbolo actual en el marco temporal actual, transmitiremos los datos de las matrices de precio a OnCalculate(), en el resto de los casos, las magnitudes de los parámetros transmitidos no tienen importancia).

Obtenemos el objeto de serie temporal necesario según su índice, obtenido de acuerdo con la denominación del marco temporal. Si no hemos logrado obtener el objeto, o el tamaño de la historia creada para el marco temporal es igual a cero (el marco temporal no se usa, o no ha sido creado con la ayuda del método Create()), salimos del método.
A continuación, llamamos al método homónimo de actualización del objeto temporal que hemos analizado en el artículo anterior.

Implementamos el método que actualiza todas las listas de series temporales:

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

Transmitimos al método los datos de precio de la barra acutal (solo para los indicadores, al actualizar los datos del símbolo actual en el marco temporal actual, transmitiremos los datos de las matrices de precio a OnCalculate(), en el resto de los casos, las magnitudes de los parámetros transmitidos no tienen importancia).

En un ciclo por la lista de todos los marcos temporales, obtenemos el siguiente objeto de serie temporal según el índice del cicloSi no hemos logrado obtener el objeto, o el tamaño de la historia creada para el marco temporal es igual a cero (el marco temporal no se usa, o no ha sido creado con la ayuda del método Create()), pasamos al siguiente marco temporal.

A continuación, llamamos al método homónimo de actualización del objeto temporal que hemos analizado en el artículo anterior.

La primera versión de la clase de objeto de todas las series temporales de un símbolo ya está preparada: la funcionalidad creada de dicha clase es en estos momentos suficiente para probar el trabajo con varios marcos temporales de un mismo símbolo. Ahora, naturalmente, vamos a mejorarla al crear la clase de colección general de las series temporales para multitud de símbolos.

En el archivo de clase CEngine ubicado en la dirección \MQL5\Include\DoEasy\Engine.mqh, sustituimos la línea de inclusión del archivo de la clase de objeto de serie temporal:

//+------------------------------------------------------------------+
//| Archivos de inclusión                                            |
//+------------------------------------------------------------------+
#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"
//+------------------------------------------------------------------+

por el archivo de clase del objeto de series temporales de todos los periodos utilizados del símbolo:

//+------------------------------------------------------------------+
//| Archivos de inclusión                                                 |
//+------------------------------------------------------------------+
#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"
//+------------------------------------------------------------------+

Ahora, en el programa que funciona usando como base esta biblioteca, podremos ver la clase que hemos creado hoy.

Prueba

Para poner a prueba el trabajo con series temporales de diferentes periodos de un mismo símbolo, vamos a tomar el asesor del artículo anterior y guardarlo en la nueva carpeta \MQL5\Experts\TestDoEasy\Part36\ con el nuevo nombre TestDoEasyPart36.mq5.

Para poner a prueba el funionamiento de la clase, vamos a crear dos versiones del asesor de prueba con la ayuda de las directivas de compilación condicional.

  • La primea versión del asesor creará dos series temporales del símbolo actual:
    una para el periodo M15, que constará de solo dos barras,
    y otra que constará de diez barras, para el periodo actual del gráfico en el que está iniciado el asesor.
  • La segunda versión del asesor creará dos series temporales del símbolo actual con los valores por defecto:
    se crearán, o bien 1000 barras para cada serie temporal, o bien el máximo posible, con la condición de que el número de barras disponible para la serie temporal sea inferior a 1000.

En el bloque de variables globales del asesor, dejamos solo un objeto de la clase de series temporales: en lugar de la clase CSeries, determinamos la variable de clase CTimeSeries, dado que los objetos de la clase CSeries ahora constituyen la parte principal del objeto de clase CTimeSeries.
Eliminamos uno de los objetos CSeries:

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

mientras que renombramos el segundo y definimos su tipo como CTimeSeries:

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

Al final del manejador OnInit() del asesor, escribimos el bloque de código para crear las series temporales dependiendo de la presencia/ausencia del indentificador TIMESERIES_ALL establecido:

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

En primer lugar, establecemos necesariamente la denominación del símbolo para la clase de serie temporal del símbolo. A continuación, tenemos definido y comentado el identificador como macrosustitución. Su presencia/ausencia determinará qué versión del código debemos compilar: para crear dos series temporales (si no hay identificador), o para crear todas las series temporales (si hay identificador)

Normalmente, y dependiendo del estado de la versión actual de la clase de objeto de todas las series temporales del símbolo, para crear una serie temporal, necesitaremos:

  1. establecer el símbolo del objeto de todas la series temporales del símbolo,
  2. llamar al método para establecer la profundidad de la historia de la serie temporal y comprobar la sincronización de los datos de la serie temporal con el servidor,
  3. crear una serie temporal usando como base la profundidad de la historia establecida.

En condiciones normales, debemos comprobar el resultado retornado por el método que establece la profundidad de la historia y comprueba la sincronización con el servidor. Y es que el método puede retornar false si no se ha logrado establecer la profundidad de la historia o si los datos no están sincronizados con el servidor.
No obstante, para realizar las simulaciones con el símbolo actual, podemos obviar esta comprobación: seguramente, todos los datos estarán disponibles. Si no fuera el caso, tampoco pasaría nada, simplemente la serie temporal no sería creada. Solo tenemos que reiniciar el asesor, ya que el primer acceso a las funciones que inician la carga de los datos históricos pondrá en marcha el proceso de carga, y en el siguiente inicio del asesor, los datos ya deberían estar sincronizados.

Después de que las series temporales necesarias hayan sido creadas, mostraremos en el diario la lista completa con las series temporales creadas, precisamente para comprobar si se han creado con éxito.
Para ello, obtendremos la lista completa de todas las series temporales, y luego obtendremos en un ciclo el siguiente objeto de serie temporal. Si esta serie temporal ha sido creada (tiene un valor para la profundidad de la historia y ha sido rellanada con datos), imprimiremos estos datos en el diario.

Ahora, al final del manejador OnTick(), insertamos el bloque de código para actualizar los datos de todas las series temporales del 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);
        }
     }
  }
//+------------------------------------------------------------------+

Aquí, obtenemos la lista de series temporales del objeto de todas las series temporales del símbolo, y obtenemos en un ciclo por la lista de series temporalesel siguiente objeto de serie temporal según el índice del ciclo. Si no hemos logrado obtener la serie temporal o no hay datos (barras) en la misma, pasaremos a la siguiente serie temporal del próximo marco temporal. Si hemos obtenido el objeto de serie temporal, lo actualizamos. Si la bandera de nueva barra ha sido establecida para la serie temporal, mostramos un mensaje sobre ello (para la serie temporal del periodo actual, reproduciremos adicionalmente el sonido "news.wav")

Compilamos el asesor (la línea 176 con la definición de la macrosustitución #define TIMESERIES_ALL deberá haber sido comentada): con ello, se compilará la versión del asesor que crea dos series temporales.
Lo iniciamos en el terminal, en el gráfico de M30. En el diario se mostrarán las entradas sobre los parámetros de las dos series temporales credas y, transcurrido cierto tiempo, aparecerán las entradas sobre la creación de nuevas barras en los periodos de los gráficos M15 y 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

Ahora, vamos a comentar la línea 176 con la definición de la macrosustitución #define TIMESERIES_ALL y compilar el asesor: se creará la versión del asesor que crea dos series temporales con los valores por defecto.

Lo iniciamos en el gráfico del símbolo. En el diario se mostrarán las entradas sobre los parámetros de todas las series temporales creadas y, transcurrido cierto tiempo, aparecerán las entradas sobre la apertura de nuevas barras en los periodos de los gráficos de las series temporales creadas:

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

Iniciamos el asesor en el modo visual del simulador en el periodo del gráfico M5:


Primero, el simulador carga los datos históricos de todos los marcos temporales; a continuación, el asesor muestra los datos de las series temporales creadas, y por último, en el diario se muestran mensajes sobre la apertura de nuevas barras en las series temporales creadas durante la prueba.

Todo funciona como habíamos planeado en esta etapa de creación de la funcionalidad para trabajar con las series temporales de un mismo símbolo.

¿Qué es lo próximo?

En el próximo artículo, crearemos una clase de colección general de la series temporales que guardará la cantidad necesaria de datos de símbolos distintos y de sus marcos temporales.

Más abajo se adjuntan todos los archivos de la versión actual de la biblioteca y los archivos del asesor de prueba. Puede descargarlo todo y ponerlo a prueba por sí mismo.
Si tiene preguntas, observaciones o sugerencias, podrá concretarlas en los comentarios al artículo.

Volver al contenido

Artículos de esta serie:

Trabajando con las series temporales en la biblioteca DoEasy (Parte 35): El objeto "Barra" y la lista de serie temporal del símbolo


Traducción del ruso hecha por MetaQuotes Software Corp.
Artículo original: https://www.mql5.com/ru/articles/7627

Archivos adjuntos |
MQL5.zip (3686.04 KB)
MQL4.zip (3686.04 KB)
Pronosticación de series temporales (Parte 2): el método de los mínimos cuadrados de los vectores de soporte (LS-SVM) Pronosticación de series temporales (Parte 2): el método de los mínimos cuadrados de los vectores de soporte (LS-SVM)

En el artículo se analiza la teoría y el uso práctico del algoritmo de pronosticación de series temporales usando como base el método de vectores de soporte. Asimismo, presentamos su implementación en MQL, además de varios indicadores de prueba y expertos. Esta tecnología todavía no ha sido implementada en MQL. Vamos a comenzar familiarizándonos con el aparato matemático.

Trabajando con las series temporales en la biblioteca DoEasy (Parte 35): El objeto "Barra" y la lista de serie temporal del símbolo Trabajando con las series temporales en la biblioteca DoEasy (Parte 35): El objeto "Barra" y la lista de serie temporal del símbolo

Con este artículo, comenzamos una nueva serie en la descripción de la biblioteca "DoEasy" para la creación rápida y sencilla de programas. Hoy, empezaremos a preparar la funcionalidad de la biblioteca para acceder a los datos de las series temporales de los símbolos y trabajar con los mismos. Asimismo, crearemos el objeto "Barra", encargado de guardar los datos tanto básicos como ampliados de la barra de la serie temporal, y también ubicaremos los objetos de barra en la lista de serie temporal para que resulte más cómodo buscar y clasificar dichos objetos.

¡Los proyectos ayudan a crear robots rentables! O eso parece ¡Los proyectos ayudan a crear robots rentables! O eso parece

Un gran programa comienza con un pequeño archivo, que a su vez va creciendo y llenándose con multitud de funciones y objetos. La mayoría de los programadores de robots afronta este problema con la ayuda de archivos de inclusión. Pero resulta mejor comenzar a escribir cualquier programa para trading directamente en un proyecto: es más rentable en todos los sentidos.

Optimización móvil continua (Parte 5): Panorámica del proyecto del optimizador automático, creación de la interfaz gráfica Optimización móvil continua (Parte 5): Panorámica del proyecto del optimizador automático, creación de la interfaz gráfica

Continuamos con la descripción de la optimización móvil en el terminal MetaTrader 5. Tras analizar en los artículos anteriores los métodos de formación del informe de optimización y su método de filtrado, hemos procedido a describir la estructura interna de la aplicación encargada del propio proceso de optimización. El optimizador automático, ejecutado como una aplicación en C#, tiene su propia interfaz gráfica. Este artículo está dedicado precisamente a esta interfaz gráfica.