Zeitreihen in der Bibliothek DoEasy (Teil 36): Objekt der Zeitreihe für alle verwendeten Symbolperioden

9 Juni 2020, 14:05
Artyom Trishkin
0
103

Inhalt


Konzept

Im vorigen Artikel haben wir eine neue Serie von DoEasy-Bibliotheksbeschreibungen begonnen und die Erstellung des Bar-Objekts und der Bar-Objektliste besprochen. In Bezug auf die Terminologie der MetaTrader-Plattform erstellten wir eine einzelne Zeitreihe für ein Symbol mit einem Zeitrahmen und füllten sie mit Daten für jeden Balken dieser Zeitreihe.
Im Zusammenhang mit der Terminologie der DoEasy-Bibliotheks haben wir das Balken-Sammelobjekt für ein einzelnes Symbol in einem Zeitrahmen erstellt. Jetzt sind wir in der Lage, jede beliebige Suche und Sortierung innerhalb der erstellten Sammlung (innerhalb der angegebenen Tiefe der Historienkollektion) nach jeder Eigenschaft der in der Sammlung vorhandenen Balkenobjekte durchzuführen. Mit einfachen Worten, wir sind in der Lage, nach verschiedenen Parametern von Zeitreihenbalken und ihren verschiedenen Kombinationen zu suchen (verschiedene Kombinationen von Balken sollen später hinzugefügt werden), sowie einen neuen Balken in der erstellten Sammlung zu erkennen und die aktuellen Daten darin zu aktualisieren.

Dies ist gut, aber nicht ausreichend, wenn wir mehrere Zeitreihen und Symbole für unsere Programme verwenden wollen. Daher sollte die Anzahl der uns zur Verfügung stehenden Symbol-Zeitreihensammlungen der Anzahl ihrer Zeitrahmen entsprechen, die wir in unseren Programmen verwenden müssen.
Die erstellte Zeitreihenkollektionen ermöglicht dies — sie wird in Bezug auf ein Symbol und einen Zeitrahmen erstellt, was bedeutet, dass wir so viele Sammlungen eines Symbols erstellen können, wie wir benötigen.

Es wäre bequem, alle Kollektionen eines Symbols, aber unterschiedlicher Zeitrahmen, in einem Objekt — dem Objekt der Symbolzeitreihen — zu speichern. Aus diesen Objekten wird dann eine einzelne, gemeinsame Zeitreihensammlung für verschiedene Symbole und deren Zeitrahmen erstellt.


Das Objekt der Symbol-Zeitreihen

In absehbarer Zukunft werden viele Bibliothekskurse Kenntnisse über die Art des Programms erfordern, in dem sie durchgeführt werden. Verwenden Sie dazu die Funktion MQLInfoInteger() mit dem Spezifizierer MQL_PROGRAM_TYPE. In diesem Fall gibt die Funktion den Typ des laufenden Mql5-Programms zurück.
Um das Schreiben der Variablen, die den Programmtyp in jeder Klasse speichern, zu vermeiden, werden wir die Variable in der Basisklasse aller Programmobjekte deklarieren. In allen von der Basisklasse abgeleiteten Klassen wird die Variable, die den Typ eines ausgeführten Programms speichert, deklariert.

Im 'protected' Abschnitt der Klasse CBaseObj, der sich in \MQL5\Include\DoEasy\Objects\BaseObj.mqh befindet, deklarieren wir die Klassenvariable, die den Typ des laufenden Programms speichert:

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

und weisen ihr im Klassenkonstruktor einen Wert zu:

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

Jetzt "kennen" alle Objekte, die von der Basisklasse aller Bibliotheksobjekte abgeleitet sind, den Typ des Programms, in dem sie ausgeführt werden.

In der Klassenauflistung CNewBarObj in \MQL5\Include\DoEasy\Objects\Series\NewBarObj.mqh, binden wir die Basisobjekt-Klassendatei ein:

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

und leiten das Objekt "New bar" vom Basisobjekt ab:

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

und entfernen "alle Erwähnungen" der Programmtyp-Variablen aus der Auflistung — jetzt wird der Programmtyp in CBaseObj gesetzt:

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

Die korrigierte vollständige Klassenauflistung finden Sie in den an den Artikel angehängten Dateien.

Lassen Sie uns die CSeries-Klasse leicht modifizieren, die im vorherigen Artikel erstellt wurde.

Wirt leiten die Klasse aus dem Basisobjekt CBaseObj anstelle von CObject ab und entfernen die Variable, die den Programmtyp speichert:
//+------------------------------------------------------------------+
//| 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:

Wir deklarieren im 'public' Teil der Klasse die Methoden zum Setzen eines Symbols und des Zeitrahmens, sowie die vollständige Implementierung der Methode, die die Gesamtmenge der verfügbaren Daten zurückgibt:

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

und außerhalb des Klassenkörpers implementieren wir die Methoden zum Setzen eines Symbols und des Zeitrahmens:

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

Die an die Methoden übergebenen Werte werden geprüft und gegebenenfalls angepasst. Dann werden sie an die Variablen gesendet.
Danach wird der Wert im Objekt der Klasse "New bar" gesetzt.

Die Auflistung der Klasse CSelect in \MQL5\Include\DoEasy\Services\Select.mqh soll die Klassendatei CSeries anstelle der Datei Bar.mqh der Klasse CBar enthalten:

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

Nun ist alles bereit für die Erstellung der Klasse des Objekts aller Symbolzeitreihen.

Was ist ein Objekt der Symbolzeitreihen? Im vorigen Artikel haben wir das Zeitreihenobjekt einer Periode für ein einzelnes Symbol erstellt. Jetzt können alle Balkenobjekte innerhalb dieser Liste nach jeder der Eigenschaften des Balkenobjekts sortiert werden, jedes Balkenobjekt kann nach jeder seiner Eigenschaften erkannt werden usw. Die Programme erfordern jedoch oft die Verwendung einer mehrperiodischen Analyse der Geschichte eines oder mehrerer Symbole. Das Objekt der Symbolzeitreihen enthält mehrere Zeitreihen aller möglichen Zeitrahmen eines Symbols. Die Anzahl der Zeitrahmen kann gleich der Anzahl der verfügbaren Diagrammperioden in dem Terminal sein, das in der Enumeration ENUM_TIMEFRAMES beschrieben ist.

Tatsächlich ist das Objekt ein Array von Zeigern auf Objekt CArrayObj, wobei die Objekte Listen von Symbolzeitreihen sind, die ich im vorigen Artikel erstellt habe. Sie enthalten wiederum Bar-Objekte.

Im aktuellen Artikel werden wir das Objekt aller Symbolzeitreihen erstellen, die dies erlauben:

  • die manuelle Verwendung einzustellen:
    • spezifizierte Chartperioden eines einzelnen Symbols
    • alle möglichen Chartperioden eines einzelnen Symbols
  • erstellen:
    • spezifizierte Zeitreihenobjekte eines einzelnen Symbols
    • alle möglichen Zeitreihenobjekte eines einzelnen Symbols
  • Aktualisieren der Daten:
    • spezifizierte Zeitreihenobjekte eines einzelnen Symbols
    • alle Zeitreihenobjekte eines einzelnen Symbols

Die restliche Objektfunktionsweise wird beim Erstellen des Objekts aller Zeitreihen aller verwendeten Symbole in nachfolgenden Artikeln hinzugefügt.

Wie üblich fügen wir zunächst alle für die neue Klasse notwendigen Nachrichten hinzu — Nachrichtenindizes und ihre entsprechenden Texte.

In der Datei Datas.mqh, die sich in \MQL5\Include\DoEasy\Datas.mqh befindet, fügen wir neue Nachrichtenindizes hinzu:

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

und Nachrichtentexte, die den neu hinzugefügten Indizes entsprechen:

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

Erstellen wir in \MQL5\Include\DoEasy\Objects\Series\ die Datei TimeSeries.mqh der CTimeSeries-Klasse mit der damit verbundenen Objektdatei der Series.mqh der Zeitreihen und abgeleitet von CObject aus dem Basisobjekt der Standardbibliothek.
Füllen wir den Klassenkörper mit dem erforderlichen Inhalt und betrachten dann alle Variablen und Methoden getrennt:

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

Die Klassenvariable m_symbol speichert einen Symbolnamen, für den die benötigten Zeitreihen erstellt, gespeichert und im Objekt behandelt werden. Anschließend wird der Variablenwert verwendet, um die notwendigen Objekte mit einer Zeitreihe der benötigten Symbole auszuwählen.

Das Array mit Zeigern auf die Instanzen der Klasse CObject m_list_series dient der Speicherung von Zeitreihenobjekten, die im vorherigen Artikel erstellt wurden. Die Anzahl der Objekte in der Liste kann gleich der Anzahl aller in der Plattform verfügbaren Zeitreihen sein, und sie sind in der Liste in der Reihenfolge angeordnet, in der sie in der Aufzählung ENUM_TIMEFRAMES aufgeführt sind, was uns wiederum erlaubt, den Index jedes Zeitreihenobjekts in der Liste genau zu kennen. Es werden zwei Methoden erstellt, um den Index in der Liste der Zeitreihenobjekte zurückzugeben:

Die Methode IndexTimeframe() gibt den Index des Zeitreihenobjekts in der Liste durch einen Zeitrahmenwert zurück.
Seine Implementierung über den Klassenkörper hinaus:

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

Hier ist alles ganz klar. Abhängig von einem an die Methode übergebenen Zeitrahmen wird ihre Seriennummer in der Enumeration ENUM_TIMEFRAMES (und dementsprechend ihr Index in der Liste m_list_series) zurückgegeben.  

Die Methode TimeframeByIndex() gibt einen Zeitrahmen über den Indes des Zeitreihen-Objekts in der Liste zurück.
Seine Implementierung über den Klassenkörper hinaus:

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

Diese Methode ist das Gegenstück von IndexTimeframe(). Je nach dem an die Methode übergebenen Index wird der entsprechende Zeitrahmen in der Reihenfolge seiner Position in der Enumeration ENUM_TIMEFRAMES zurückgegeben.

Die Methode Getlist() gibt die vollständige Liste aller Zeitreihen 'wie besehen' an das Steuerprogramm zurück. Während Sie sich im Programm befinden, können Sie die benötigte Zeitreihe aus der erhaltenen Liste auswählen.

Die Methode GetSeries() gibt das angegebene Zeitreihenobjekt aus der Liste m_list_series mit dem Namen der gewünschten Zeitreihe aus der Enumeration ENUM_TIMEFRAMES zurück. Die zuvor betrachtete Methode IndexTimeframe() wird verwendet, um den Zeitreihenindex in der Liste abzufragen.

Die Methode GetSeriesByIndex() gibt das Zeitreihenobjekt nach seinem Index in der Liste m_list_series zurück.

Implementieren wir die Methode, die die Historientiefe der angegebenen Zeitreihe einstellt:

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

Die Methode erhält die Zeitrahmen der Zeitreihen, dessen Historientiefe eingestellt werden soll, die benötigte Größe der Historien-Zeitreihendaten (Historientiefe; wenn 0, wird die Tiefe von 1000 Balken verwendet) und die Anzahl der aktuellen Zeitreihenbalken (nur für Indikatoren bei der Einstellung der Historientiefe für das aktuelle Symbol auf dem aktuellen Zeitrahmen - übergeben Sie den Parameter rates_total an AufBerechnen(); in anderen Fällen ist der Parameter ohne Bedeutung).

Wenn ein Symbol für das Klassenobjekt noch nicht gesetzt ist, zeigen wir die entsprechende Meldung an und geben false zurück.
Wir holen uns das angeforderte Zeitreihen-Objekt aus der Liste über seinen Index, der durch einen Zeitreihennamen übergeben wird und geben das Ergebnis der Einstellung der Historientiefe mit der gleichnamigen Methode der Zeitreihen-Objektklasse zurück, das wir im vorigen Artikel besprochen haben.

Implementation der Methode, die eine Historientiefe für alle verwendeten Symbolzeitreihen festlegt:

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

Die Methode erhält die benötigte Größe der historischen Zeitreihendaten (Tiefe der Historie; wenn 0, wird eine Tiefe von 1000 Balken verwendet) und die Anzahl der Balken der aktuellen Zeitreihe (nur für Indikatoren, wenn die Tiefe der Historie für das aktuelle Symbol auf den aktuellen Zeitrahmen gesetzt wird — übergeben Sie den Parameter rates_total an OnCalculate(), in anderen Fällen ist der Parameter ohne Bedeutung).

Wenn ein Symbol für das Klassenobjekt noch nicht gesetzt ist, zeigen wir die entsprechende Meldung an und geben false zurück.
In einer Schleife durch die vollständige Liste aller existierenden Zeitrahmen holen wir uns die nächste Zeitreihenliste durch den Schleifenindex aus der Liste und schreiben das Ergebnis der Einstellung der Historientiefe mit der gleichnamigen Zeitreihen-Objektklassenmethode, im vorigen Artikel besprochen, in die lokale Variable res. Wenn mindestens eine der Methoden zur Einstellung der Historientiefe eines der verfügbaren Zeitreihenobjekte false ergibt, wird false der Variablen zugewiesen.
Nach Abschluss der Schleife geben wir das Ergebnis aller Einstellungen zurück, die der Variablen res zugewiesen wurden.

Implementation der Methode, die das Flag der angegebenen Zeitreihensynchronisation mit den Serverdaten zurückgibt:

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

Die Methode erhält den Zeitreihen-Zeitrahmen, dessen Synchronisationsflag zurückgegeben werden soll, die erforderliche Größe der Historien-Zeitreihendaten (Historientiefe; wenn 0, wird die Tiefe von 1000 Balken verwendet) und die Anzahl der aktuellen Zeitreihenbalken (nur für Indikatoren, wenn die Historientiefe für das aktuelle Symbol auf dem aktuellen Zeitrahmen eingestellt wird, übergeben wir den Parameter rates_total an OnCalculate(); in anderen Fällen ist der Parameter ohne Bedeutung).

Wenn ein Symbol für das Klassenobjekt noch nicht gesetzt ist, zeigen wir die entsprechende Meldung an und geben false zurück.
Wir fragen das angeforderte Zeitreihenobjekt aus der Liste durch seinen Index ab, der durch einen Zeitreihennamen erhalten wurde, und geben das Ergebnis der Überprüfung des Datensynchronisationsergebnisses unter Verwendung der gleichnamigen Methode der Zeitreihenobjektklasse zurück, die wir im vorherigen Artikel betrachtet haben.

Implementation der Methode, die das Flag der Datensynchronisation mit den Serverdaten für alle Zeitreihen zurückgibt:

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

Die Methode erhält die benötigte Größe der historischen Zeitreihendaten (Tiefe der Historie; wenn 0, wird eine Tiefe von 1000 Balken verwendet) und die Anzahl der Balken der aktuellen Zeitreihe (nur für Indikatoren, wenn die Tiefe der Historie für das aktuelle Symbol auf den aktuellen Zeitrahmen gesetzt wird — übergeben Sie den Parameter rates_total an OnCalculate(), in anderen Fällen ist der Parameter ohne Bedeutung).

Wenn ein Symbol für das Klassenobjekt noch nicht gesetzt ist, zeigen wir die entsprechende Meldung an und geben false zurück.
In einer Schleife durch die vollständige Liste aller existierenden Zeitrahmen, holen wir uns die nächste Zeitreihenliste durch den Schleifenindex aus der Liste und schreiben das Flag zur Überprüfung der Datensynchronisation mit dem Server unter Verwendung der gleichnamigen Methode der Zeitreihenobjektklasse, die wir im vorigen Artikel betrachteten, in die lokale Variable res. Wenn mindestens eine der Methoden zur Überprüfung der Synchronisierung des Zeitreihenobjekts false zurückgibt, wird false der Variablen zugewiesen.
Nach Abschluss der Schleife geben wir das Ergebnis aller Prüfungen zurück, die in der Variablen res
gesetzt wurde.

Implementation der Methode zur Erstellung einer angegebenen Zeitserienliste:

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

Die Methode erhält den Zeitrahmen, dessen Zeitreihen erstellt werden sollen und die Historientiefe der erstellten Zeitreihen (der Standardwert ist Null — wir erstellen eine Zeitreihe mit der Historientiefe, die zuvor für das Zeitreihenobjekt mit der Methode SetAmountUsedData() festgelegt wurde; wenn der Wert Null überschreitet und kleiner als der Wert der verfügbaren Zeitreihenbalken der angegebenen Diagrammperiode ist, wird die erstellte Historientiefe verwendet, die an die Methode übergeben wird).

Erhält das notwendige Zeitreihenobjekt durch seinen Index, der durch den Zeitreihennamen empfangen wird. Wenn es nicht gelang, das Objekt zu erhalten oder die Historientiefe dafür noch nicht eingestellt ist, zeigen wir die entsprechenden Meldungen an und geben false zurück.

Die Rückgabe des Ergebnisses der Zeitreihenerstellung durch die Methode mit einer gleichnamigen Zeitreihen-Objektmethode, die wir im vorigen Artikel betrachtet haben. Da die im vorherigen Artikel beschriebene Methode zur Zeitserienerstellung die Anzahl der Objektbalken zurückgibt, die der Zeitserienliste hinzugefügt wurden, während die aktuelle Methode den booleschen Wert zurückgibt, reicht es aus, das Ergebnis des Vergleichs der Anzahl der hinzugefügten Elemente mit der Liste "über Null" zurückzugeben, um true oder false zurückzugeben. Genau das machen wir hier.

Implementation der Methode zur Erstellung aller Zeitreihenlisten:

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

Die Methode erhält die Historientiefe einer erstellten Zeitreihe (der Standardwert ist Null — das erstellt eine Zeitreihe mit der Historientiefe, die zuvor mit der Methode SetAmountUsedData() für das Zeitreihenobjekt festgelegt wurde; wenn der Wert größer als Null und kleiner als der Wert der verfügbaren Zeitreihenbalken der angegebenen Diagrammperiode ist, wird die erstellte Historientiefe verwendet, die an die Methode übergeben wurde).

In einer Schleife durch die Liste aller Zeitrahmen, holen wir das nächste Zeitreihenobjekt durch den Schleifenindex. Wenn das Objekt nicht erhalten werden konnte oder für das Objekt noch keine Historientiefe eingestellt wurde, gehen wir zum nächsten Zeitreihenobjekt über.

Die lokale Variable res erhält das Ergebnis der Erstellung der Zeitreihe unter Verwendung der gleichnamigen Methode der Zeitreihen-Objektklasse, die wir im vorherigen Artikel als Ergebnis eines Vergleiches "über Null" der Anzahl der zur Liste hinzugefügten Elemente besprochen haben. Wenn mindestens eine der Methoden zur Erzeugung von Zeitreihenobjekten false zurückgibt, wird die Variable auf false gesetzt.
Nach Abschluss der Schleife geben wir das Ergebnis der Erstellung aller Zeitreihen zurück, die der Variablen res
zugewiesen wurde.

Implementation der Methode Aktualisieren einer angegebenen Zeitserienliste:

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

Die Methode erhält den aktualisierten Zeitreihen-Zeitrahmen und die aktuellen Balkenpreisdaten (nur für Indikatoren, wenn die Daten des aktuellen Symbols auf den aktuellen Zeitrahmen aktualisiert werden — Daten der Preisfelder an OnCalculate() übergeben; in anderen Fällen spielen die Werte der übergebenen Parameter keine Rolle).

Erhält das notwendige Zeitreihenobjekt durch seinen Index, der durch den Zeitreihennamen empfangen wird. Wenn das Objekt nicht erhalten wurde oder die Größe einer erzeugten Zeitreihengeschichte Null ist (der Zeitrahmen wird nicht verwendet oder wurde nicht mit der Methode Create() erzeugt), beenden wir die Methode.
Als Nächstes rufen wir die Methode zur Aktualisierung des gleichnamigen Zeitreihenobjekts auf, die wir im vorigen Artikel besprochen haben.

Implementation der Methode zur Aktualisierung aller Zeitreihenlisten:

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

Die Methode erhält die aktuellen Balkenpreisdaten (nur für Indikatoren, wenn die Daten des aktuellen Symbols im aktuellen Zeitrahmen aktualisiert werden — Daten der Preisfelder an OnCalculate() übergeben; in anderen Fällen spielen die Werte der übergebenen Parameter keine Rolle).

In einer Schleife durch die Liste aller Zeitrahmen, holen wir uns das nächste Zeitreihenobjekt durch den SchleifenindexWenn es nicht gelang, das Objekt zu erhalten oder die Größe einer erstellten Zeitreihengeschichte Null ist (die Zeitreihe wird nicht verwendet oder wurde nicht mit der Methode Create() erstellt), gehen wir zum nächsten Zeitreihenobjekt.

Als Nächstes rufen wir die Methode zur Aktualisierung des gleichnamigen Zeitreihenobjekts auf, die wir im vorigen Artikel besprochen haben.

Die erste Version der Objektklasse aller Zeitreihen eines einzelnen Symbols ist fertig. Die aktuelle Klassenfunktionalität ist ausreichend, um das Arbeiten mit mehreren Zeitreihen eines einzelnen Symbols zu testen. In Zukunft werden wir sie beim Erstellen einer gemeinsamen Zeitreihensammlungsklasse für mehrere Symbole verfeinern.

In der Klassendatei der CEngine, die sich in \MQL5\Include\DoEasy\Engine.mqh befindet, ersetzen wir die Zeichenfolge zum Einfügen der Zeitreihen-Objektklassendatei:

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

mit der Datei des Objekts der Zeitreihe für alle verwendeten Symbolperioden:

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

Jetzt ist die neu erstellte Klasse von einem bibliotheksbasierten Programm aus sichtbar.

Tests

Um die Arbeit mit einem einzigen Symbol auf Zeitreihen verschiedener Perioden zu testen, werden wir den EA aus dem vorherigen Artikel verwenden und ihn in \MQL5\Experts\TestDoEasy\Part36\ unter dem Namen TestDoEasyPart36.mq5 speichern.

Um die Klasse zu testen, erstellen wir zwei Versionen des Test-EAs unter Verwendung der bedingten Kompilierungsanweisungen.

  • Die erste EA-Version erzeugt zwei Zeitreihen des aktuellen Symbols:
    die erste ist für M15, die nur aus zwei Balken besteht,
    ein weiterer besteht aus zehn Balken — für die aktuelle Chart-Periode, auf der der EA gestartet wird.
  • Die zweite EA-Version erstellt alle Zeitreihen des aktuellen Symbols mit den Standardwerten:
    entweder 1000 Balken für jede Zeitreihe oder den maximal möglichen Wert, vorausgesetzt, die verfügbare Anzahl von Balken für die Zeitreihe ist kleiner als 1000.

Lassen Sie im Block der globalen Variablen EA ein einzelnes Objekt der Klasse Zeitreihe stehen. Definieren wir anstelle der Klasse CSeries die Klassenvariable CTimeSeries, da die Objekte der Klasse CSeries nun Teil des Objektes der Klasse CTimeSeries sind.
Entfernen wir ein CSeries-Objekt:

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

außerdem nennen wir das zweite Objekt anders und definieren seinen Typ als CTimeSeries:

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

Ganz am Ende von OnInit() des EAs schreiben wir den Codeblock zum Erstellen von Zeitreihen in Abhängigkeit vom Vorhandensein oder Fehlen der angegebenen TIMESERIES_ALL ID:

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

Wir stellen zunächst sicher, dass ein Symbolnamen für die Symbolzeitreihenklasse gesetzt wird. Als Nächstes haben wir die definierte und auskommentierte ID als eine Makro-Substitution. Ihr Vorhandensein/Fehlen definiert die Version des zu kompilierenden Codes — die für die Erstellung von zwei Zeitreihen (wenn es keine ID gibt) oder die für die Erstellung aller Zeitreihen (wenn es eine ID gibt).

Generell und ab der aktuellen Version der Objektklasse aller Symbolzeitreihen benötigen wir zur Erstellung einer Zeitreihe folgendes:

  1. Setzen eines Symbols aller Zeitreihen-Objekte,
  2. Aufruf der Methode zur Einstellung der Tiefe der Zeitreihenhistorie und zur Überprüfung der Synchronisation der Zeitreihendaten mit dem Server,
  3. Erstellen einer Zeitreihe basierend auf der angegebenen Historientiefe.

Normalerweise sollte die Methode zur Einstellung der Historientiefe und zur Überprüfung der Serversynchronisation für ein Rückgabeergebnis überprüft werden. Die Methode kann false zurückgeben, wenn die Historientiefe nicht eingestellt wurde oder wenn die Daten noch nicht mit dem Server synchronisiert sind.
Wir können diese Prüfung jedoch vorerst überspringen, um einen Test für das aktuelle Symbol durchzuführen. Höchstwahrscheinlich werden die gesamten Daten verfügbar sein. Selbst wenn die Daten nicht verfügbar sind, wird die Zeitreihe einfach nicht erstellt. Es wird möglich sein, den EA einfach neu zu starten, da der erste Zugriff auf die Funktionen, der das Laden der historischen Daten einleitet, das Laden startet, und die Daten sollten während des ersten EA-Starts synchronisiert werden.

Nachdem die erforderlichen Zeitreihen erstellt wurden, wird die vollständige Liste der erstellten Zeitreihen im Journal angezeigt, um zu überprüfen, ob die Erstellung erfolgreich war.
Um dies zu tun, erhalten Sie die vollständige Liste aller Zeitreihen. In einer Schleife erhalten Sie das nächste Zeitreihenobjekt aus der Liste. Wenn die Zeitreihe angelegt ist (mit der Historientiefe und mit Daten gefüllt), zeigen wir die Daten im Journal.

Fügen wir ganz am Ende von OnTick() den Codeblock ein, um die Daten aller erstellten Symbolzeitserien zu aktualisieren:

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

Hier erhalten wir die Liste der Zeitreihen aus dem Objekt aller Symbolzeitreihen. In einer Schleife durch die Liste der Zeitreihen, holen wir uns das nächste Zeitreihenobjekt durch den Schleifenindex. Wenn die Zeitreihe nicht empfangen werden konnte oder sie keine Daten (Balken) hat, gehen wir zur nächsten Zeitreihe des nächsten Zeitrahmens über. Wenn das Zeitreihenobjekt empfangen wird, aktualisieren wir es. Wenn das neue Balken-Flag für die Zeitreihe gesetzt ist, zeigen wir die entsprechende Meldung an (spielen auch den news.wav-Sound für die Zeitreihe der aktuellen Periode ab)

Kompilieren wir also den EA (Zeile 176 mit der Makro-Substitution #define TIMESERIES_ALL sollte auskommentiert werden) — die EA-Version, die zwei Zeitreihen erzeugt, wird kompiliert.
Wir starten ihn im Terminal auf einem M30-Chart. Die Einträge zu den Parametern der beiden erstellten Zeitreihen werden im Journal angezeigt. Die Einträge über die Eröffnung neuer Balken auf M15- und M30-Charts erscheinen nach einiger Zeit:

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

Entkommentieren wir nun die Zeile 176, wo die Makro-Substitution #define TIMESERIES_ALL definiert ist, und kompilieren den EA — die EA-Version, die alle Zeitreihen mit den Standardwerten erzeugt, wird generiert.

Wir starten ihn auf dem Chart des Symbols. Die Einträge über die Parameter aller erzeugten Zeitreihen werden im Journal angezeigt. Die Einträge über das Öffnen neuer Balken auf den erstellten Zeitreihen-Diagrammperioden erscheinen nach einiger Zeit:

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

Starten wir den EA im visuellen Modus des Testers auf M5:


Zuerst lädt der Tester historische Daten für alle Zeiträume herunter, dann zeigt der EA die Daten der erstellten Zeitreihen an. Die Nachrichten werden dann an das Journal gesendet, das über das Öffnen neuer Balken auf den erstellten Zeitserien während des Tests informiert.

In dieser Phase der Erstellung der Funktionalität für die Arbeit mit einer Zeitserie mit einem einzigen Symbol funktioniert alles wie vorgesehen.

Was kommt als Nächstes?

Im nächsten Artikel werden wir die gemeinsame Klasse der Zeitreihenkollektion erstellen, die die erforderliche Datenmenge für verschiedene Symbole und deren Zeitrahmen speichert.

Alle Dateien der aktuellen Version der Bibliothek sind unten zusammen mit den Dateien der Test-EAs angehängt, die Sie testen und herunterladen können.
Schreiben Sie Ihre Fragen und Vorschläge in den Kommentaren.

Zurück zum Inhalt

Frühere Artikel dieser Serie:

Zeitreihen in der Bibliothek DoEasy (Teil 35): das Bar-Objekt und die Liste der Zeitreihen eines Symbols


Übersetzt aus dem Russischen von MetaQuotes Software Corp.
Originalartikel: https://www.mql5.com/ru/articles/7627

Beigefügte Dateien |
MQL5.zip (3686.04 KB)
MQL4.zip (3686.04 KB)
Prognose von Zeitreihen (Teil 1): Methode der Empirischen Modus Dekomposition (Empirical Mode Decomposition, EMD) Prognose von Zeitreihen (Teil 1): Methode der Empirischen Modus Dekomposition (Empirical Mode Decomposition, EMD)

Dieser Artikel befasst sich mit der Theorie und der praktischen Anwendung des Algorithmus zur Vorhersage von Zeitreihen, basierend auf der empirischen Moduszerlegung. Er schlägt die MQL-Implementierung dieser Methode vor und stellt Testindikatoren und Expert Advisors vor.

Zeitreihen in der Bibliothek DoEasy (Teil 35): das Bar-Objekt und die Liste der Zeitreihen eines Symbols Zeitreihen in der Bibliothek DoEasy (Teil 35): das Bar-Objekt und die Liste der Zeitreihen eines Symbols

Dieser Artikel startet eine neue Serie über das Erstellen der Bibliothek DoEasy zur einfachen und schnellen Programmentwicklung. Im aktuellen Artikel werden wir die Bibliotheksfunktionen für den Zugriff auf und die Arbeit mit den Zeitreihen der Symbole implementieren. Wir werden das Bar-Objekt erstellen, das die Haupt- und erweiterten Zeitreihendaten speichert, und Bar-Objekte in der Zeitreihenliste platzieren, um eine bequeme Suche und Sortierung der Objekte zu ermöglichen.

Kontinuierliche Walk-Forward-Optimierung (Teil 4): Optimierungsmanager (automatische Optimierung) Kontinuierliche Walk-Forward-Optimierung (Teil 4): Optimierungsmanager (automatische Optimierung)

Der Hauptzweck des Artikels besteht darin, den Mechanismus der Arbeit mit unserer Anwendung und deren Möglichkeiten zu beschreiben. Daher kann der Artikel als eine Anleitung zur Benutzung der Anwendung betrachtet werden. Er behandelt alle möglichen Fallstricke und Besonderheiten bei der Verwendung der Anwendung.

Projekte helfen beim Entwickeln profitabler Handelsroboter! Zumindest scheint es so Projekte helfen beim Entwickeln profitabler Handelsroboter! Zumindest scheint es so

Ein großes Programm beginnt mit einer kleinen Datei, die dann immer größer wird, je mehr Funktionen und Objekte Sie hinzufügen. Die meisten Roboterentwickler verwenden Include-Dateien, um dieses Problem zu lösen. Es gibt jedoch eine bessere Lösung: Beginnen Sie mit der Entwicklung einer beliebigen Handelsanwendung in einem Projekt. Es gibt so viele Gründe, dies zu tun.