English Русский 中文 Español 日本語 Português
Zeitreihen in der Bibliothek DoEasy (Teil 38): Kollektion von Zeitreihen - Aktualisierungen in Echtzeit und Datenzugriff aus dem Programm

Zeitreihen in der Bibliothek DoEasy (Teil 38): Kollektion von Zeitreihen - Aktualisierungen in Echtzeit und Datenzugriff aus dem Programm

MetaTrader 5Beispiele | 7 Juli 2020, 11:25
521 0
Artyom Trishkin
Artyom Trishkin

Inhalt


Konzept

In den vorangegangenen Artikeln, die sich der Erstellung von Zeitreihen beliebiger Chartperioden und beliebiger Symbole widmeten, haben wir eine vollwertige Klasse der Zeitreihenkollektion aller im Programm verwendeten Symbole erstellt und gelernt, Zeitreihen für ihre schnelle Suche und Sortierung mit historischen Daten zu füllen.
Ein solches Mittel wird es uns ermöglichen, verschiedene Kombinationen von Preisdaten in der Historie zu suchen und zu vergleichen. Aber wir müssen auch über eine Aktualisierung der aktuellen Daten nachdenken, die bei jedem neuen Tick für jedes verwendete Symbol erfolgen sollte.

Selbst die einfachste Version erlaubt es uns, alle Zeitreihen in der Funktion OnTimer() in einer Millisekunden-Frequenz des Programms zu aktualisieren. Dies wirft jedoch die Frage auf, ob die Zeitreihendaten immer genau nach dem Timer-Zähler aktualisiert werden sollen. Schließlich werden die Daten bei Ankunft eines neuen Ticks im Programm geändert. Es wäre falsch, die Daten einfach unabhängig vom Eintreffen eines neuen Ticks zu aktualisieren — das wäre leistungsmäßig irrational.

Während wir immer über die Ankunft eines neuen Ticks in OnTick() eines EAs oder in OnCalculate() bei einem Indikator auf dem aktuellen Symbol erkennen, ist dies bei keinem anderen Symbol der Fall, das von dem auf einem anderen Symbol gestarteten Programm verfolgt wird. Diese Aufgabe erfordert die gesonderte Verfolgung der notwendigen Ereignisse in einem EA oder einem Indikator.

Hier ist die einfachste mögliche Option, die den aktuellen Bibliotheksbedarf befriedigt, der Vergleich der vorherigen Tick-Zeit mit der aktuellen. Wenn die vorangegangene Tick-Zeit von der aktuellen abweicht, wird ein neuer Tick als auf einem Symbol angekommen betrachtet, das vom Programm verfolgt wird, aber nicht "nativ" für dieses Symbol ist (das Programm wird auf dem Chart eines anderen Symbols gestartet).

Lassen Sie uns die bestehenden Klassen leicht verbessern, bevor wir die Klasse "New tick" und die Echtzeit-Aktualisierung aller im Programm verwendeten Zeitreihen entwickeln.


Verbesserung der Zeitreihenklassen

Zunächst erhält die Datei Datas.mqh den Index neue Nachricht der Bibliothek :

//--- CTimeSeries
   MSG_LIB_TEXT_TS_TEXT_FIRST_SET_SYMBOL,             // First, set a symbol using SetSymbol()
   MSG_LIB_TEXT_TS_TEXT_IS_NOT_USE,                   // Timeseries is not used. Set the flag using SetAvailable()
   MSG_LIB_TEXT_TS_TEXT_UNKNOWN_TIMEFRAME,            // Unknown timeframe

und der Nachrichtentext entsprechend dem neu hinzugefügten Index:

   {"Сначала нужно установить символ при помощи SetSymbol()","First you need to set Symbol using SetSymbol()"},
   {"Таймсерия не используется. Нужно установить флаг использования при помощи SetAvailable()","Timeseries not used. Need to set usage flag using SetAvailable()"},
   {"Неизвестный таймфрейм","Unknown timeframe"},

Die Klasse CBaseObj des Basisobjekts aller Bibliotheksobjekte verfügt über zwei Variablen:

//+------------------------------------------------------------------+
//| Base object class for all library objects                        |
//+------------------------------------------------------------------+
class CBaseObj : public CObject
  {
protected:
   ENUM_LOG_LEVEL    m_log_level;                              // Logging level
   ENUM_PROGRAM_TYPE m_program;                                // Program type
   bool              m_first_start;                            // First launch flag
   bool              m_use_sound;                              // Flag of playing the sound set for an object
   bool              m_available;                              // Flag of using a descendant object in the program
   int               m_global_error;                           // Global error code
   long              m_chart_id_main;                          // Control program chart ID
   long              m_chart_id;                               // Chart ID
   string            m_name;                                   // Object name
   string            m_folder_name;                            // Name of the folder storing CBaseObj descendant objects 
   string            m_sound_name;                             // Object sound file name
   int               m_type;                                   // Object type (corresponds to the collection IDs)

public:

Die Variable m_chart_id_main speichert die Kontrollprogramm Chart-ID — dies ist die des Charts mit dem Symbol, auf dem das Programm gestartet wurde. Das Chart soll alle in den Kollektionen und Objekten der Bibliothek registrierten Ereignisse erhalten.
Die m_chart_id speichert die ID des Charts, zu dem das von der Klasse CBaseObj abgeleitete Objekt irgendwie in Beziehung steht. Diese Eigenschaft wird noch nirgendwo verwendet. Das kommt später.

Da wir die Variable m_chart_id_main später als m_chart_id hinzugefügt haben, werden alle Nachrichten an die in der Variable m_chart_id festgelegte Chart-ID gesendet. Ich habe dies behoben. Jetzt sind alle aktuellen Chart-IDs in der Variablen m_chart_id_main festgelegt. Alle Klassen, die Nachrichten von der Bibliothek an das Kontrollprogramm auf dem Chart senden, wurden geändert — alle Instanzen von "m_chart_id" wurden durch "m_chart_id_main" ersetzt.
Solche Änderungen wurden an allen Ereignisklassen aus dem Ordner \MQL5\Include\DoEasy\Objects\Events\ sowie an den Dateien der Kollektionsklassen AccountsCollection.mqh, EventsCollection.mqh und SymbolsCollection.mqh vorgenommen.
Sie können alle Änderungen in den angehängten Dateien sehen.

Um die Daten des angegebenen Balkens aus der Zeitreihenkollektion anzuzeigen, fügen Sie die Textbeschreibung der Balkenparameter der Klasse CBar der Datei \MQL5\Include\DoEasy\Objects\Series\Bar.mqh hinzu.

In dem Codeblock, der die Beschreibung der Objekteigenschaften enthält, deklarieren Sie die Methode zur Erstellung der Textbeschreibung der Balkenparameter:

//+------------------------------------------------------------------+
//| Descriptions of bar object properties                            |
//+------------------------------------------------------------------+
//--- Get description of a bar's (1) integer, (2) real and (3) string properties
   string            GetPropertyDescription(ENUM_BAR_PROP_INTEGER property);
   string            GetPropertyDescription(ENUM_BAR_PROP_DOUBLE property);
   string            GetPropertyDescription(ENUM_BAR_PROP_STRING property);

//--- Return the bar type description
   string            BodyTypeDescription(void)  const;
//--- Send description of bar properties to the journal (full_prop=true - all properties, false - only supported ones)
   void              Print(const bool full_prop=false);
//--- Display a short bar description in the journal
   virtual void      PrintShort(void);
//--- Return the (1) short name and (2) description of bar object parameters
   virtual string    Header(void);
   string            ParameterDescription(void);
//---
  };
//+------------------------------------------------------------------+

Außerhalb des Klassenkörpers implementieren wir die die Methode zur Erstellung der Textbeschreibung der Balkenparameter und die geänderte Implementierung der Methode zur Anzeige der kurzen Balkenbeschreibung im Journal:

//+------------------------------------------------------------------+
//| Return the description of the bar object parameters              |
//+------------------------------------------------------------------+
string CBar::ParameterDescription(void)
  {
   int dg=(this.m_digits>0 ? this.m_digits : 1);
   return
     (
      ::TimeToString(this.Time(),TIME_DATE|TIME_MINUTES|TIME_SECONDS)+", "+
      "O: "+::DoubleToString(this.Open(),dg)+", "+
      "H: "+::DoubleToString(this.High(),dg)+", "+
      "L: "+::DoubleToString(this.Low(),dg)+", "+
      "C: "+::DoubleToString(this.Close(),dg)+", "+
      "V: "+(string)this.VolumeTick()+", "+
      (this.VolumeReal()>0 ? "R: "+(string)this.VolumeReal()+", " : "")+
      this.BodyTypeDescription()
     );
  }
//+------------------------------------------------------------------+
//| Display a short bar description in the journal                   |
//+------------------------------------------------------------------+
void CBar::PrintShort(void)
  {
   ::Print(this.Header(),": ",this.ParameterDescription());
  }
//+------------------------------------------------------------------+

Hier habe ich einfach den Parameterbeschreibungscode aus der Methode entfernt, die die Balkenparameter im Journal anzeigt, und ihn in die neue Methode eingefügt, die die Textnachricht zurückgibt. Bei dem Ausdruck der Balkenparameter im Journal zeigen wir die zusammengesetzte Nachricht bestehend aus einem kurzen Balkenobjektnamen und seinen Parametern an, deren Textbeschreibung jetzt in der neuen Methode ParameterDescription() generiert wird.

Um die "nicht-originären" Zeitreihen (das sind nicht die, auf der das Programm gestartet wird) zu aktualisieren, haben wir uns entschieden, die Klasse "New tick" zu erstellen und die Daten solcher Symbole erst beim Eintreffen des Ereignisses "New tick" für jedes im Programm verwendete Symbol zu aktualisieren.

Klasse "New tick" und Datenaktualisierung

Erstellen Sie im Ordner \MQL5\Include\DoEasy\Objects\ den Ordner Ticks\ mit der Datei NewTickObj.mqh der Klasse CNewTickObj, abgeleitet vom Basisobjekt aller CBaseObj-Bibliotheksobjekte (wessen die Datei in die Klassendatei aufgenommen wird) und tragen Sie die erforderlichen Daten ein:

//+------------------------------------------------------------------+
//|                                                   NewTickObj.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 tick" class                                                 |
//+------------------------------------------------------------------+
class CNewTickObj : public CBaseObj
  {
private:
   MqlTick           m_tick;                          // Structure of the current prices
   MqlTick           m_tick_prev;                     // Structure of the current prices during the previous check
   string            m_symbol;                        // Object symbol
   bool              m_new_tick;                      // New tick flag
public:
//--- Set a symbol
   void              SetSymbol(const string symbol)   { this.m_symbol=symbol;             }
//--- Return the new tick flag
   bool              IsNewTick(void);
//--- Update price data in the tick structure and set the "New tick" event flag if necessary
   void              Refresh(void)                    { this.m_new_tick=this.IsNewTick(); }
//--- Constructors
                     CNewTickObj(void){;}
                     CNewTickObj(const string symbol);
  };
//+------------------------------------------------------------------+

Die Variable m_tick speichert die Preisdaten des letzten angekommenen Tick.

Die Variable m_tick_prev speichert die Preisdaten des letzten Ticks.

Die Variable m_symbol speichert ein Symbol, dessen neuer Tick verfolgt werden soll.

Das neue Tick-Flag in der Variablen m_new_tick soll später verwendet werden.
Für den aktuellen Bibliotheksbedarf wird das Ereignis "New tick" auf einem Symbol durch die Methode IsNewTick() definiert:

//+------------------------------------------------------------------+
//| Return the new tick flag                                         |
//+------------------------------------------------------------------+
bool CNewTickObj::IsNewTick(void)
  {
//--- If failed to get the current prices to the tick structure, return 'false'
   if(!::SymbolInfoTick(this.m_symbol,this.m_tick))
      return false;
//--- If this is the first launch, copy data of the obtained tick to the previous tick data
//--- reset the first launch flag and return 'false'
   if(this.m_first_start)
     {
      this.m_tick_prev=this.m_tick;
      this.m_first_start=false;
      return false;
     }
//--- If the time of a new tick is not equal to the time of a tick during the previous check -
//--- copy data of the obtained tick to the previous tick data and return 'true'
   if(this.m_tick.time_msc!=this.m_tick_prev.time_msc)
     {
      this.m_tick_prev=this.m_tick;
      return true;
     }
//--- In all other cases, return 'false'
   return false;
  }
//+------------------------------------------------------------------+

Die Klasse hat zwei definierte Konstruktoren:

  • der Standardkonstruktor ohne Parameter wird verwendet, um das Objekt "New tick" innerhalb einer anderen Klasse zu definieren. In diesem Fall verwenden Sie die Klassenmethode SetSymbol(), um ein Symbol für das Klassenobjekt CNewTickObj zu setzen, für das die Ereignisse "New tick" definiert sind.
  • der paramterische Konstruktor wird verwendet, um das Klassenobjekt über den Operator new zu erzeugen. In diesem Fall kann ein Symbol, für das das Objekt erzeugt wird, sofort bei der Erzeugung des Objekts angegeben werden.
//+------------------------------------------------------------------+
//| Parametric constructor CNewTickObj                               |
//+------------------------------------------------------------------+
CNewTickObj::CNewTickObj(const string symbol) : m_symbol(symbol)
  {
//--- Reset the structures of the new and previous ticks
   ::ZeroMemory(this.m_tick);
   ::ZeroMemory(this.m_tick_prev);
//--- If managed to get the current prices to the tick structure,
//--- copy data of the obtained tick to the previous tick data and reset the first launch flag
  if(::SymbolInfoTick(this.m_symbol,this.m_tick))
     { 
      this.m_tick_prev=this.m_tick;
      this.m_first_start=false;
     }
  }
//+------------------------------------------------------------------+

Dies ist die gesamte Klasse des neuen Tick-Objekts. Die Idee ist einfach: Man bringt die Preise in die Tickstruktur und vergleicht die Zeit des eingetroffenen Ticks mit der Zeit des vorherigen.
Wenn diese Zeiten nicht gleich sind, dann ist ein neuer Tick eingetroffen.

Ticks können in den EAs übersprungen werden, aber das ist hier nicht wichtig. Wir sind in der Lage, einen neuen Tick auf einem "nicht-originären" Symbol im Timer zu verfolgen, um die Daten nur dann zu aktualisieren, wenn ein neuer Tick eintrifft, und nicht ständig durch den Zeitgeber.
Wenn ein Indikator alle Ticks verfolgt, die in Stapeln eintreffen können, sollte die Aktualisierung der aktuellen Zeitreihendaten für ein Symbol, auf dem der Indikator gestartet wird, in OnCalculate() erfolgen. Die neuen Ticks für "nicht-originäre" Symbole werden im Timer verfolgt (neue Tick-Ereignisse für ein "nicht-originäres" Symbol können in OnOnCalculate() nicht empfangen werden), daher würde es ausreichen, nur die Zeitdifferenz zwischen den neuen und vorherigen Ticks für "nicht-originäre" Symbole zu verfolgen, um die Zeitreihendaten zeitlich zu aktualisieren.

Lassen Sie das Zeitreihenobjekt CSeries sein Ereignis "New bar" an das Steuerprogramm senden. Dies ermöglicht es uns, solche Ereignisse von jeder beliebigen Zeitreihe im Programm zu erhalten und darauf zu reagieren.

Fügen Sie am Ende der Enumerationsdatei Defines.mqh die neue Enumeration mit der Liste der möglichen Ereignisse der Zeitreihenobjekte hinzu:

//+------------------------------------------------------------------+
//| List of possible timeseries events                               |
//+------------------------------------------------------------------+
enum ENUM_SERIES_EVENT
  {
   SERIES_EVENTS_NO_EVENT = SYMBOL_EVENTS_NEXT_CODE,        // no event
   SERIES_EVENTS_NEW_BAR,                                   // "New bar" event
  };
#define SERIES_EVENTS_NEXT_CODE  (SERIES_EVENTS_NEW_BAR+1)  // Code of the next event after the "New bar" event
//+------------------------------------------------------------------+

Hier haben wir bisher nur zwei Zustände der Zeitreihen-Ereignisse: "No event" und "New bar". Wir benötigen diese Enumerationskonstanten, um das Balken-Objekt nach bestimmten Eigenschaften in der Kollektionsliste der Balken (in der Zeitreihen CSeries) zu suchen.

Da die Zeitreihenobjekte im Bibliothekstimer aktualisiert werden, fügen wir die Parameter der Kollektion der Zeitreihen-Objekte für die Aktualisierung zusammen mit der ID der Zeitreihenkollektionsliste zur Datei Defines.mqh hinzu:

//--- Trading class timer parameters
#define COLLECTION_REQ_PAUSE           (300)                      // Trading class timer pause in milliseconds
#define COLLECTION_REQ_COUNTER_STEP    (16)                       // Trading class timer counter increment
#define COLLECTION_REQ_COUNTER_ID      (5)                        // Trading class timer counter ID
//--- Parameters of the timeseries collection timer
#define COLLECTION_TS_PAUSE            (32)                       // Timeseries collection timer pause in milliseconds
#define COLLECTION_TS_COUNTER_STEP     (16)                       // Account timer counter increment
#define COLLECTION_TS_COUNTER_ID       (6)                        // Timeseries timer counter ID
//--- Collection list IDs
#define COLLECTION_HISTORY_ID          (0x777A)                   // Historical collection list ID
#define COLLECTION_MARKET_ID           (0x777B)                   // Market collection list ID
#define COLLECTION_EVENTS_ID           (0x777C)                   // Event collection list ID
#define COLLECTION_ACCOUNT_ID          (0x777D)                   // Account collection list ID
#define COLLECTION_SYMBOLS_ID          (0x777E)                   // Symbol collection list ID
#define COLLECTION_SERIES_ID           (0x777F)                   // Timeseries collection list ID
//--- Data parameters for file operations

Wir haben die Kollektionsparameter des Timers bei der Erstellung des CEngine Bibliotheks-Basisobjekts besprochen, während der Zweck der Kollektions-IDs beschrieben wurde, wenn die Bibliotheksstruktur neu geordnet wird.

Weisen Sie dem Balkenobjekt sofort die ID der Zeitreihenkollektion zu, da das Zeitreihen-Objekt eine Liste ist, die Zeiger auf Balkenobjekte enthält, die zu der Liste gehören.
Öffnen Sie \MQL5\Include\DoEasy\Objects\Series\Bar.mqh noch einmal und fügen Sie den Objekttyp zu beiden Konstruktoren hinzu:

//+------------------------------------------------------------------+
//| Constructor 1                                                    |
//+------------------------------------------------------------------+
CBar::CBar(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index)
  {
   this.m_type=COLLECTION_SERIES_ID;
   MqlRates rates_array[1];
   this.SetSymbolPeriod(symbol,timeframe,index);
   ::ResetLastError();
//--- If failed to write bar data to the MqlRates array by index or set the time to the time structure,
//--- display an error message, create and fill the structure with zeros, and write it to the rates_array array
   if(::CopyRates(symbol,timeframe,index,1,rates_array)<1 || !::TimeToStruct(rates_array[0].time,this.m_dt_struct))
     {
      int err_code=::GetLastError();
      ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_BAR_FAILED_GET_BAR_DATA),". ",CMessage::Text(MSG_LIB_SYS_ERROR)," ",CMessage::Text(err_code)," ",CMessage::Retcode(err_code));
      MqlRates err={0};
      rates_array[0]=err;
     }
//--- Set the bar properties
   this.SetProperties(rates_array[0]);
  }
//+------------------------------------------------------------------+
//| Constructor 2                                                    |
//+------------------------------------------------------------------+
CBar::CBar(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index,const MqlRates &rates)
  {
   this.m_type=COLLECTION_SERIES_ID;
   this.SetSymbolPeriod(symbol,timeframe,index);
   ::ResetLastError();
//--- If failed to set time to the time structure, display the error message,
//--- create and fill the structure with zeros, set the bar properties from this structure and exit
   if(!::TimeToStruct(rates.time,this.m_dt_struct))
     {
      int err_code=::GetLastError();
      ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_BAR_FAILED_GET_BAR_DATA),". ",CMessage::Text(MSG_LIB_SYS_ERROR)," ",CMessage::Text(err_code)," ",CMessage::Retcode(err_code));
      MqlRates err={0};
      this.SetProperties(err);
      return;
     }
//--- Set the bar properties
   this.SetProperties(rates);
  }
//+------------------------------------------------------------------+


Verbessern wir nun die Objektklasse der Zeitreihen CSeries, die sich in der Datei \MQL5\Include\DoEasy\Objects\Serie\Serie\Serie.mqh befindet.

Deklarieren Sie im 'public' Teil der Klasse die neue Methode zum Senden eines Ereignisses an die Kontrollprogramm auf dem Chart:

//--- (1) Create and (2) update the timeseries list
   int               Create(const uint required=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);
                             
//--- Create and send the "New bar" event to the control program chart
   void              SendEvent(void);

//--- Return the timeseries name
   string            Header(void);
//--- Display (1) the timeseries description and (2) the brief timeseries description in the journal
   void              Print(void);
   void              PrintShort(void);


//--- Constructors
                     CSeries(void);
                     CSeries(const string symbol,const ENUM_TIMEFRAMES timeframe,const uint required=0);
  };
//+------------------------------------------------------------------+

Implementieren Sie am Ende der Klassen die deklarierte Methode:

//+------------------------------------------------------------------+
//| Create and send the "New bar" event                              |
//| to the control program chart                                     |
//+------------------------------------------------------------------+
void CSeries::SendEvent(void)
  {
   ::EventChartCustom(this.m_chart_id_main,SERIES_EVENTS_NEW_BAR,this.Time(0),this.Timeframe(),this.Symbol());
  }
//+------------------------------------------------------------------+

Hier erzeugen wir und senden ein Ereignis an die Kontrollprogramm auf dem Chart. Das Ereignis besteht aus:
Chart ID des Ereignisempfängers,
Ereignis-ID (New bar),
sendet die neue Eröffnungszeit des Balkens als Ereignisparameter vom Tyo long,
sendet den Zeitrahmen des Charts, in dem das Ereignis aufgetreten ist, als Ereignisparameter vom Typ double
und
sendet den Namen eines Symbols (in dessen Zeitreihe das Ereignis aufgetreten ist) als Parameter vom Typ string.

fügt die Prüfung des Flags, das die Verwendung der Zeitreihe im Programm anzeigt, der Synchronisationsmethode für Zeitreihendaten hinzu:

//+------------------------------------------------------------------+
//|Synchronize symbol and timeframe data with server data            |
//+------------------------------------------------------------------+
bool CSeries::SyncData(const uint required,const uint rates_total)
  {
//--- If the timeseries is not used, notify of that and exit
   if(!this.m_available)
     {
      ::Print(DFUN,this.m_symbol," ",TimeframeDescription(this.m_timeframe),": ",CMessage::Text(MSG_LIB_TEXT_TS_TEXT_IS_NOT_USE));
      return false;
     }
//--- If managed to obtain the available number of bars in the timeseries

Mit anderen Worten, wenn das Flag der Zeitreihennutzung im Programm nicht gesetzt ist, besteht keine Notwendigkeit, es zu synchronisieren. Es kann auch eine Situation geben, in der wir die Zeitreihe benötigen, obwohl das Verwendungsflag nicht gesetzt ist. Daher wird die entsprechende Nachricht an das Journal gesendet.

Implementieren wir die gleiche Prüfung auf die Zeitreihenerstellungsmethode:

//+------------------------------------------------------------------+
//| Create the timeseries list                                       |
//+------------------------------------------------------------------+
int CSeries::Create(const uint required=0)
  {
//--- If the timeseries is not used, notify of that and return zero
   if(!this.m_available)
     {
      ::Print(DFUN,this.m_symbol," ",TimeframeDescription(this.m_timeframe),": ",CMessage::Text(MSG_LIB_TEXT_TS_TEXT_IS_NOT_USE));
      return 0;
     }
//--- If the required history depth is not set for the list yet,

Die Methode in der Klasse, die das Objekt bar durch den Zeitreihenindex zurückgibt, wurde überarbeitet. Zuvor sah die Methode wie folgt aus:

//+------------------------------------------------------------------+
//| Return the bar object by index in the timeseries                 |
//+------------------------------------------------------------------+
CBar *CSeries::GetBarBySeriesIndex(const uint index)
  {
   CArrayObj *list=this.GetList(BAR_PROP_INDEX,index);
   return(list==NULL || list.Total()==0 ? NULL : list.At(0));
  }
//+------------------------------------------------------------------+

Mit anderen Worten: eine neue Liste mit der Kopie des benötigten Balkens wurde erstellt und diese Kopie wurde zurückgegeben. Dies ist ausreichend, wenn wir lediglich Daten eines angeforderten Balkens erhalten wollen, aber wenn wir die Eigenschaften des Balkens ändern müssen, dann funktioniert diese Methode nicht, da die Änderungen an den Eigenschaften der Kopie des Balkens und nicht an den Eigenschaften des Originalobjektes vorgenommen werden.

Da wir wollen, dass der aktuelle Balken in Echtzeit aktualisiert wird, wenn ein neuer Tick ankommt, habe ich die Methode so geändert, dass der Zeiger auf das ursprüngliche Balkenobjekt in der Kollektionsliste der Balken zurückgegeben wird und nicht die Kopie des Balkens aus der Liste:

//+------------------------------------------------------------------+
//| Return the bar object by index in the timeseries                 |
//+------------------------------------------------------------------+
CBar *CSeries::GetBarBySeriesIndex(const uint index)
  {
   CBar *tmp=new CBar(this.m_symbol,this.m_timeframe,index); 
   if(tmp==NULL)
      return NULL;
   this.m_list_series.Sort(SORT_BY_BAR_INDEX);
   int idx=this.m_list_series.Search(tmp);
   delete tmp;
   CBar *bar=this.m_list_series.At(idx);
   return(bar!=NULL ? bar : NULL);
  }
//+------------------------------------------------------------------+

Hier erstellen wir das temporäre Balkenobjekt mit einem Symbol und Periode des aktuellen Zeitreihenobjektchart und dem Balkenindex, der an die Methode übergeben wird. Der Balkenindex in der Zeitreihe des Charts ist notwendig um das gleiche Objekt in der Zeitreihenliste zu suchen sortiert nach Balkenindizes. Bei der Suche nach einem Balken mit dem gleichen Zeitreihenindex holen wir seinen Index in der Liste (dieser Index wird verwendet, um den Zeiger auf das Balkenobjekt in der Liste zu erhalten) und geben den Zeiger auf das Objekt zurück.
Jetzt gibt die Methode den Zeiger auf das ursprüngliche Balkenobjekt in der Zeitreihenliste zurück. Er kann während der Echtzeit-Datenaktualisierung geändert werden.

Verbessern wir jetzt die Objektklasse der Zeitreihen CTimeSeries , um bei der Definition eines solchen Ereignisses neue Ticks zu verfolgen und Daten zu aktualisieren. Das Klassenobjekt ist eine Gruppe von Zeitreihen aller verwendeten Chartperioden eines einzelnen Symbols. Dies bedeutet, dass das Objekt der beste Ort für das Klassenobjekt "New tick" ist, da das Erhalten eines neuen Ticks durch das CTimeSeries Zeitreihen-Objektsymbol die Aktualisierung der Daten des Zeitreihen-Objekts CSeries aller zu dem Objekt gehörenden Perioden startet.

Einfügen der Objektklassendatei "New tick" in die Zeitreihen-Objektklassendatei. Im 'private' Teil der Klasse definieren wir das Objekt der Objektklasse "New tick".
Fügen Sie im 'public' Bereich der Klasse die Methode hinzu, die das neue Tick-Flag zurückgibt auf dem Symbol des aktuellen Zeitreihenobjekts:

//+------------------------------------------------------------------+
//|                                                   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"
#include "..\Ticks\NewTickObj.mqh"
//+------------------------------------------------------------------+
//| Symbol timeseries class                                          |
//+------------------------------------------------------------------+
class CTimeSeries : public CBaseObj
  {
private:
   string            m_symbol;                                             // Timeseries symbol
   CNewTickObj       m_new_tick;                                           // "New tick" object
   CArrayObj         m_list_series;                                        // List of timeseries by timeframes
   datetime          m_server_firstdate;                                   // The very first date in history by a server symbol
   datetime          m_terminal_firstdate;                                 // The very first date in history by a symbol in the client terminal
//--- Return (1) the timeframe index in the list and (2) the timeframe by the list index
   char              IndexTimeframe(const ENUM_TIMEFRAMES timeframe) const { return IndexEnumTimeframe(timeframe)-1;                            }
   ENUM_TIMEFRAMES   TimeframeByIndex(const uchar index)             const { return TimeframeByEnumIndex(uchar(index+1));                       }
//--- Set the very first date in history by symbol on the server and in the client terminal
   void              SetTerminalServerDate(void)
                       {
                        this.m_server_firstdate=(datetime)::SeriesInfoInteger(this.m_symbol,::Period(),SERIES_SERVER_FIRSTDATE);
                        this.m_terminal_firstdate=(datetime)::SeriesInfoInteger(this.m_symbol,::Period(),SERIES_TERMINAL_FIRSTDATE);
                       }
public:
//--- Return (1) oneself, (2) the full list of timeseries, (3) specified timeseries object and (4) timeseries object by index
   CTimeSeries      *GetObject(void)                                       { return &this;                                                      }
   CArrayObj        *GetListSeries(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              SetRequiredUsedData(const ENUM_TIMEFRAMES timeframe,const uint required=0,const int rates_total=0);
   bool              SetRequiredAllUsedData(const uint required=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 required=0,const int rates_total=0);
   bool              SyncAllData(const uint required=0,const int rates_total=0);
//--- Return the very first date in history by symbol (1) on the server, (2) in the client terminal and (3) the new tick flag
   datetime          ServerFirstDate(void)                           const { return this.m_server_firstdate;                                    }
   datetime          TerminalFirstDate(void)                         const { return this.m_terminal_firstdate;                                  }
   bool              IsNewTick(void)                                       { return this.m_new_tick.IsNewTick();                                }
//--- Create (1) the specified timeseries list and (2) all timeseries lists
   bool              Create(const ENUM_TIMEFRAMES timeframe,const uint required=0);
   bool              CreateAll(const uint required=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);

//--- Compare CTimeSeries objects (by symbol)
   virtual int       Compare(const CObject *node,const int mode=0) const;
//--- Display (1) description and (2) short symbol timeseries description in the journal
   void              Print(const bool created=true);
   void              PrintShort(const bool created=true);
   
//--- Constructors
                     CTimeSeries(void){;}
                     CTimeSeries(const string symbol);
  };
//+------------------------------------------------------------------+

Die Methode IsNewTick() gibt das Ergebnis der Anforderung von Daten über den neuen Tick aus dem Objekt "New tick" m_new_tickzurück.

Damit das Objekt der Klasse "New tick" das Symbol kennt, dessen Daten zurückgegeben werden sollen, sollten wir das Symbol für das Objekt der Klasse "New tick" im Klassenkonstruktor setzen und sofort Daten zum Lesen der aktuellen Tick-Preise aktualisieren:

//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CTimeSeries::CTimeSeries(const string symbol) : m_symbol(symbol)
  {
   this.m_list_series.Clear();
   this.m_list_series.Sort();
   for(int i=0;i<21;i++)
     {
      ENUM_TIMEFRAMES timeframe=this.TimeframeByIndex((uchar)i);
      CSeries *series_obj=new CSeries(this.m_symbol,timeframe);
      this.m_list_series.Add(series_obj);
     }
   this.SetTerminalServerDate();
   this.m_new_tick.SetSymbol(this.m_symbol);
   this.m_new_tick.Refresh();
  }
//+------------------------------------------------------------------+

Wir werden jetzt das Zeitreihenverwendungsflags in den Methoden prüfen, die das Datensynchronisationsflag zurückgeben. Wenn das Flag nicht markiert ist, wird die Zeitreihe im Programm nicht verwendet und sollte nicht behandelt werden:

//+------------------------------------------------------------------+
//| Return the flag of data synchronization                          |
//| with the server data                                             |
//+------------------------------------------------------------------+
bool CTimeSeries::SyncData(const ENUM_TIMEFRAMES timeframe,const uint required=0,const int rates_total=0)
  {
   if(this.m_symbol==NULL)
     {
      ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_TS_TEXT_FIRST_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;
     }
   if(!series_obj.IsAvailable())
      return false;
   return series_obj.SyncData(required,rates_total);
  }
//+------------------------------------------------------------------+
//| Return the flag of data synchronization                          |
//| of all timeseries with the server data                           |
//+------------------------------------------------------------------+
bool CTimeSeries::SyncAllData(const uint required=0,const int rates_total=0)
  {
   if(this.m_symbol==NULL)
     {
      ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_TS_TEXT_FIRST_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 || !series_obj.IsAvailable())
         continue;
      res &=series_obj.SyncData(required,rates_total);
     }
   return res;
  }
//+------------------------------------------------------------------+

Erzwingen wir in den Methoden zur Erstellung von Zeitreihen das Setzen der Flag für die Zeitreihenverwendung:

//+------------------------------------------------------------------+
//| Create a specified timeseries list                               |
//+------------------------------------------------------------------+
bool CTimeSeries::Create(const ENUM_TIMEFRAMES timeframe,const uint required=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.RequiredUsedData()==0)
     {
      ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_BAR_TEXT_FIRS_SET_AMOUNT_DATA));
      return false;
     }
   series_obj.SetAvailable(true);
   return(series_obj.Create(required)>0);
  }
//+------------------------------------------------------------------+
//| Create all timeseries lists                                      |
//+------------------------------------------------------------------+
bool CTimeSeries::CreateAll(const uint required=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.RequiredUsedData()==0)
         continue;
      series_obj.SetAvailable(true);
      res &=(series_obj.Create(required)>0);
     }
   return res;
  }
//+------------------------------------------------------------------+

In den Methoden zur Aktualisierung der Zeitreihe (für den Fall, dass das Ereignis "New bar" darin erkannt wird), Hinzufügen einer Nachricht über das Ereignis an das Kontrollprogramm auf dem Chart unter Verwendung der Methode SendEvent() des oben betrachteten Zeitreihenobjekts CSeries:

//+------------------------------------------------------------------+
//| 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);
   if(series_obj.IsNewBar(time))
     {
      series_obj.SendEvent();
      this.SetTerminalServerDate();
     }
  }
//+------------------------------------------------------------------+
//| 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)
  {
   bool upd=false;
   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);
      if(series_obj.IsNewBar(time))
        {
         series_obj.SendEvent();
         upd &=true;
        }
     }
   if(upd)
      this.SetTerminalServerDate();
  }
//+------------------------------------------------------------------+


Verbessern wir die Zeitreihensammlungsklasse CTimeSeriesCollection in \MQL5\Include\DoEasy\Collections\TimeSeriesCollection.mqh.

Wir setzen den Typ der Kollektionsliste der Zeitreihen auf den Klassentyp CListObj.
Dazu müssen wir die Klassendatei CListObj einbinden und den Typ der Kollektionsliste von CArrayObj in CListObj ändern:

//+------------------------------------------------------------------+
//|                                         TimeSeriesCollection.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 "ListObj.mqh"
#include "..\Objects\Series\TimeSeries.mqh"
#include "..\Objects\Symbols\Symbol.mqh"
//+------------------------------------------------------------------+
//| Symbol timeseries collection                                     |
//+------------------------------------------------------------------+
class CTimeSeriesCollection : public CObject
  {
private:
   CListObj                m_list;                    // List of applied symbol timeseries
//--- Return the timeseries index by symbol name
   int                     IndexTimeSeries(const string symbol);
public:

Wir deklarieren im 'public' Teil der Klasse die Methode zum Zurückgeben des angegebenen Zeitreihenbalkens durch den Zeitreihenindex des Charts, die Methode zum Zurückgeben des Flags zum Öffnen eines neuen Balkens einer angegebenen Zeitreihe und die Methode zum Aktualisieren von Zeitreihen, die nicht zum aktuellen Symbol gehören:

//--- Return the flag of data synchronization with the server data of the (1) specified timeseries of the specified symbol,
//--- (2) the specified timeseries of all symbols, (3) all timeseries of the specified symbol and (4) all timeseries of all symbols
   bool                    SyncData(const string symbol,const ENUM_TIMEFRAMES timeframe,const uint required=0,const int rates_total=0);
   bool                    SyncData(const ENUM_TIMEFRAMES timeframe,const uint required=0,const int rates_total=0);
   bool                    SyncData(const string symbol,const uint required=0,const int rates_total=0);
   bool                    SyncData(const uint required=0,const int rates_total=0);

//--- Return the bar of the specified timeseries of the specified symbol of the specified position 
   CBar                   *GetBar(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index,const bool from_series=true);
//--- Return the flag of opening a new bar of the specified timeseries of the specified symbol
   bool                    IsNewBar(const string symbol,const ENUM_TIMEFRAMES timeframe,const datetime time=0);

//--- Create (1) the specified timeseries of the specified symbol, (2) the specified timeseries of all symbols,
//--- (3) all timeseries of the specified symbol and (4) all timeseries of all symbols

   void                    RefreshOther(void);

//--- Display (1) the complete and (2) short collection description in the journal
   void                    Print(const bool created=true);
   void                    PrintShort(const bool created=true);
   
   
//--- Constructor
                           CTimeSeriesCollection();
  };
//+------------------------------------------------------------------+

Legen Sie im Klassenkonstruktor die ID der Kollektion der Zeitreihen für die Liste der Zeitreihenobjekte fest:

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

Implementierung der Methoden zur Rückgabe des Balkenobjekts durch den Zeitreihenindex und des neuen Balkenereignisses aus der angegebenen Zeitreihenliste:

//+-----------------------------------------------------------------------+
//| Return the bar of the specified timeseries                            |
//| of the specified symbol of the specified position                     |
//| from_series=true - by the timeseries index, false - by the list index |
//+-----------------------------------------------------------------------+
CBar *CTimeSeriesCollection::GetBar(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index,const bool from_series=true)
  {
//--- Get the timeseries object index in the timeseries collection list by a symbol name
   int idx=this.IndexTimeSeries(symbol);
   if(idx==WRONG_VALUE)
      return NULL;
//--- Get the pointer to the timeseries object from the collection list of timeseries objects by the obtained index
   CTimeSeries *timeseries=this.m_list.At(idx);
   if(timeseries==NULL)
      return NULL;
//--- Get the specified timeseries from the symbol timeseries object by the specified timeframe
   CSeries *series=timeseries.GetSeries(timeframe);
   if(series==NULL)
      return NULL;
//--- Depending on the from_series flag, return the pointer to the bar
//--- either by the chart timeseries index or by the bar index in the timeseries list
   return(from_series ? series.GetBarBySeriesIndex(index) : series.GetBarByListIndex(index));
  }
//+------------------------------------------------------------------+
//| Return new bar opening flag                                      |
//| for a specified timeseries of a specified symbol                 |
//+------------------------------------------------------------------+
bool CTimeSeriesCollection::IsNewBar(const string symbol,const ENUM_TIMEFRAMES timeframe,const datetime time=0)
  {
//--- Get the timeseries object index in the timeseries collection list by a symbol name
   int index=this.IndexTimeSeries(symbol);
   if(index==WRONG_VALUE)
      return false;
//--- Get the pointer to the timeseries object from the collection list of timeseries objects by the obtained index
   CTimeSeries *timeseries=this.m_list.At(index);
   if(timeseries==NULL)
      return false;
//--- Get the specified timeseries from the symbol timeseries object by the specified timeframe
   CSeries *series=timeseries.GetSeries(timeframe);
   if(series==NULL)
      return false;
//--- Return the result of checking the new bar of the specified timeseries
   return series.IsNewBar(time);
  }
//+------------------------------------------------------------------+

Implementierung der Methode zur Aktualisierung aller Zeitreihen mit Ausnahme der aktuellen Symbolzeitreihen:

//+------------------------------------------------------------------+
//| Update all timeseries except the current symbol                  |
//+------------------------------------------------------------------+
void CTimeSeriesCollection::RefreshOther(void)
  {
   int total=this.m_list.Total();
   for(int i=0;i<total;i++)
     {
      CTimeSeries *timeseries=this.m_list.At(i);
      if(timeseries==NULL)
         continue;
      if(timeseries.Symbol()==::Symbol() || !timeseries.IsNewTick())
         continue;
      timeseries.RefreshAll();
     }
  }
//+------------------------------------------------------------------+

In der Schleife durch die Liste aller Zeitreihenobjekte, holen wir uns das nächste Zeitreihenobjekt. Wenn das Objektsymbol gleich einem Symbol eines Charts ist, auf dem das Programm auf gestartet wird, wird ein solches Zeitreihenobjekt übersprungen.

Diese Methode sowie die unten beschriebenen Methoden zur Aktualisierung von Zeitreihen erlauben die Prüfung auf das neue Tick-Flag. Wenn es kein neues Tick-Flag gibt, wird die Zeitreihe übersprungen und ihre Daten werden nicht aktualisiert:

//+------------------------------------------------------------------+
//| Update the specified timeseries of the specified symbol          |
//+------------------------------------------------------------------+
void CTimeSeriesCollection::Refresh(const string symbol,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)
  {
   int index=this.IndexTimeSeries(symbol);
   if(index==WRONG_VALUE)
      return;
   CTimeSeries *timeseries=this.m_list.At(index);
   if(timeseries==NULL)
      return;
   if(!timeseries.IsNewTick())
      return;
   timeseries.Refresh(timeframe,time,open,high,low,close,tick_volume,volume,spread);
  }
//+------------------------------------------------------------------+
//| Update the specified timeseries of all symbols                   |
//+------------------------------------------------------------------+
void CTimeSeriesCollection::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)
  {
   int total=this.m_list.Total();
   for(int i=0;i<total;i++)
     {
      CTimeSeries *timeseries=this.m_list.At(i);
      if(timeseries==NULL)
         continue;
      if(!timeseries.IsNewTick())
         continue;
      timeseries.Refresh(timeframe,time,open,high,low,close,tick_volume,volume,spread);
     }
  }
//+------------------------------------------------------------------+
//| Update all timeseries of the specified symbol                    |
//+------------------------------------------------------------------+
void CTimeSeriesCollection::Refresh(const string symbol,
                                    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)
  {
   int index=this.IndexTimeSeries(symbol);
   if(index==WRONG_VALUE)
      return;
   CTimeSeries *timeseries=this.m_list.At(index);
   if(timeseries==NULL)
      return;
   if(!timeseries.IsNewTick())
      return;
   timeseries.RefreshAll(time,open,high,low,close,tick_volume,volume,spread);
  }
//+------------------------------------------------------------------+
//| Update all timeseries of all symbols                             |
//+------------------------------------------------------------------+
void CTimeSeriesCollection::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)
  {
   int total=this.m_list.Total();
   for(int i=0;i<total;i++)
     {
      CTimeSeries *timeseries=this.m_list.At(i);
      if(timeseries==NULL)
         continue;
      if(!timeseries.IsNewTick())
         continue;
      timeseries.RefreshAll(time,open,high,low,close,tick_volume,volume,spread);
     }
  }
//+------------------------------------------------------------------+


Der letzte Schritt besteht darin, die notwendigen Verbesserungen an der Datei der Hauptobjektklasse der CEngine-Bibliothek vorzunehmen.

Öffnen Sie die Klassendatei in \MQL5\Include\DoEasy\Engine.mqh.
In der 'private' Teil der Klasse deklarieren wir die Variable zur Speicherung des Programmtyps basierend auf der Bibliothek:

//+------------------------------------------------------------------+
//| Library basis class                                              |
//+------------------------------------------------------------------+
class CEngine
  {
private:
   CHistoryCollection   m_history;                       // Collection of historical orders and deals
   CMarketCollection    m_market;                        // Collection of market orders and deals
   CEventsCollection    m_events;                        // Event collection
   CAccountsCollection  m_accounts;                      // Account collection
   CSymbolsCollection   m_symbols;                       // Symbol collection
   CTimeSeriesCollection m_series;                       // Timeseries collection
   CResourceCollection  m_resource;                      // Resource list
   CTradingControl      m_trading;                       // Trading management object
   CArrayObj            m_list_counters;                 // List of timer counters
   int                  m_global_error;                  // Global error code
   bool                 m_first_start;                   // First launch flag
   bool                 m_is_hedge;                      // Hedge account flag
   bool                 m_is_tester;                     // Flag of working in the tester
   bool                 m_is_market_trade_event;         // Account trading event flag
   bool                 m_is_history_trade_event;        // Account history trading event flag
   bool                 m_is_account_event;              // Account change event flag
   bool                 m_is_symbol_event;               // Symbol change event flag
   ENUM_TRADE_EVENT     m_last_trade_event;              // Last account trading event
   int                  m_last_account_event;            // Last event in the account properties
   int                  m_last_symbol_event;             // Last event in the symbol properties
   ENUM_PROGRAM_TYPE    m_program;                       // Program type

Im 'public' Teil der Klasse deklarieren wir die Methode zur Behandlung der EA-Ereignisse von NewTick:

//--- (1) NewTick event timer and (2) handler
   void                 OnTimer(void);
   void                 OnTick(void);

Im gleichen 'public' Teil deklarieren wir die Methode, die das Balkenobjekt der angegebenen Zeitreihe des angegebenen Symbols durch den Zeitreihenindex des Charts zurückgibt, und die Methode, die das Flag zum Öffnen eines neuen Balkens der angegebenen Zeitreihe des angegebenen Symbols zurückgibt:

//--- Create (1) the specified timeseries of the specified symbol, (2) the specified timeseries of all symbols,
//--- (3) all timeseries of the specified symbol and (4) all timeseries of all symbols
   bool                 SeriesCreate(const string symbol,const ENUM_TIMEFRAMES timeframe,const uint required=0)
                          { return this.m_series.CreateSeries(symbol,timeframe,required);          }
   bool                 SeriesCreate(const ENUM_TIMEFRAMES timeframe,const uint required=0)
                          { return this.m_series.CreateSeries(timeframe,required);                 }
   bool                 SeriesCreate(const string symbol,const uint required=0)
                          { return this.m_series.CreateSeries(symbol,required);                    }
   bool                 SeriesCreate(const uint required=0)
                          { return this.m_series.CreateSeries(required);                           }

//--- Return the bar of the specified timeseries of the specified symbol of the specified position
   CBar                *SeriesGetBar(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index,const bool from_series=true)
                          { return this.m_series.GetBar(symbol,timeframe,index,from_series);                   }
//--- Return the flag of opening a new bar of the specified timeseries of the specified symbol
   bool                 SeriesIsNewBar(const string symbol,const ENUM_TIMEFRAMES timeframe,const datetime time=0)
                          { return this.m_series.IsNewBar(symbol,timeframe,time);                  }

//--- Update (1) the specified timeseries of the specified symbol, (2) the specified timeseries of all symbols,
//--- (3) all timeseries of the specified symbol and (4) all timeseries of all symbols

Im gleichen Klassenabschnitt deklarieren wir die Methoden, die Standardbalkeneigenschaften für ein bestimmtes Symbol, eine Zeitreihe und seine Position in der Zeitreihe (Balkenindex) des Charts zurückgeben:

//--- Return (1) Open, (2) High, (3) Low, (4) Close, (5) Time, (6) TickVolume,
//--- (7) RealVolume, (8) Spread of the specified bar of the specified symbol of the specified timeframe
   double               SeriesOpen(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index);
   double               SeriesHigh(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index);
   double               SeriesLow(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index);
   double               SeriesClose(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index);
   datetime             SeriesTime(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index);
   long                 SeriesTickVolume(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index);
   long                 SeriesRealVolume(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index);
   int                  SeriesSpread(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index);

//--- Set the following for the trading classes:
//--- (1) correct filling policy, (2) filling policy,
//--- (3) correct order expiration type, (4) order expiration type,
//--- (5) magic number, (6) comment, (7) slippage, (8) volume, (9) order expiration date,
//--- (10) the flag of asynchronous sending of a trading request, (11) logging level, (12) number of trading attempts

Wir legen im Klassenkonstruktor den Typ des laufenden Programms fest und erzeugen den Zähler des Timers für die Kollektion der Zeitreihen:

//+------------------------------------------------------------------+
//| CEngine constructor                                              |
//+------------------------------------------------------------------+
CEngine::CEngine() : m_first_start(true),
                     m_last_trade_event(TRADE_EVENT_NO_EVENT),
                     m_last_account_event(WRONG_VALUE),
                     m_last_symbol_event(WRONG_VALUE),
                     m_global_error(ERR_SUCCESS)
  {
   this.m_is_hedge=#ifdef __MQL4__ true #else bool(::AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING) #endif;
   this.m_is_tester=::MQLInfoInteger(MQL_TESTER);
   this.m_program=(ENUM_PROGRAM_TYPE)::MQLInfoInteger(MQL_PROGRAM_TYPE);
   
   this.m_list_counters.Sort();
   this.m_list_counters.Clear();
   this.CreateCounter(COLLECTION_ORD_COUNTER_ID,COLLECTION_ORD_COUNTER_STEP,COLLECTION_ORD_PAUSE);
   this.CreateCounter(COLLECTION_ACC_COUNTER_ID,COLLECTION_ACC_COUNTER_STEP,COLLECTION_ACC_PAUSE);
   this.CreateCounter(COLLECTION_SYM_COUNTER_ID1,COLLECTION_SYM_COUNTER_STEP1,COLLECTION_SYM_PAUSE1);
   this.CreateCounter(COLLECTION_SYM_COUNTER_ID2,COLLECTION_SYM_COUNTER_STEP2,COLLECTION_SYM_PAUSE2);
   this.CreateCounter(COLLECTION_REQ_COUNTER_ID,COLLECTION_REQ_COUNTER_STEP,COLLECTION_REQ_PAUSE);
   this.CreateCounter(COLLECTION_TS_COUNTER_ID,COLLECTION_TS_COUNTER_STEP,COLLECTION_TS_PAUSE);
   
   ::ResetLastError();
   #ifdef __MQL5__
      if(!::EventSetMillisecondTimer(TIMER_FREQUENCY))
        {
         ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_TIMER),(string)::GetLastError());
         this.m_global_error=::GetLastError();
        }
   //---__MQL4__
   #else 
      if(!this.IsTester() && !::EventSetMillisecondTimer(TIMER_FREQUENCY))
        {
         ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_TIMER),(string)::GetLastError());
         this.m_global_error=::GetLastError();
        }
   #endif 
   //---
  }
//+------------------------------------------------------------------+

Fügen wir im OnTimer() der Bibliothek die Arbeit mit dem Timer für die Zeitreihenkollektion hinzu (überflüssiger Code entfernt):

//+------------------------------------------------------------------+
//| CEngine timer                                                    |
//+------------------------------------------------------------------+
void CEngine::OnTimer(void)
  {
//--- Timer of the collections of historical orders and deals, as well as of market orders and positions
//...

//--- Account collection timer
//...
     
//--- Timer 1 of the symbol collection (updating symbol quote data in the collection)
//...

//--- Timer 2 of the symbol collection (updating all data of all symbols in the collection and tracking symbl and symbol search events in the market watch window)
//...

//--- Trading class timer
//...
     
//--- Timeseries collection timer
   index=this.CounterIndex(COLLECTION_TS_COUNTER_ID);
   if(index>WRONG_VALUE)
     {
      CTimerCounter* counter=this.m_list_counters.At(index);
      if(counter!=NULL)
        {
         //--- If this is not a tester
         if(!this.IsTester())
           {
            //--- If the pause is over, work with the timeseries list (except for the current symbol timeseries)
            if(counter.IsTimeDone())
               this.m_series.RefreshOther();
           }
         //--- In case of the tester, work with the timeseries list by tick (except for the current symbol timeseries)
         else
            this.m_series.RefreshOther();
        }
     }
  }
//+------------------------------------------------------------------+

Bei der Arbeit mit den Timer für die Kollektion und mit dem Timer selbst wurde bei der Erstellung des Bibliothekshauptobjekts CEngine berücksichtigt. Alles andere wird in den Kommentaren des Codes beschrieben.
Beachten Sie, dass der Timer nur die Zeitreihen verarbeitet, deren Symbol nicht mit dem Symbol eines Charts übereinstimmt, auf dem das Programm gestartet wird.
In der Zeitschaltuhr aktualisieren wir die Zeitreihen bei der Registrierung der Ereignisse von "New tick" für "nicht-originäre" Symbole. Daher sind dies die Ereignisse, die wir im Timer erkennen.
Die Methode OnTick(), die aus der EA-Funktion OnTick() gestartet wird, wird verwendet, um die aktuelle Symbolzeitreihe zu aktualisieren:

//+------------------------------------------------------------------+
//| NewTick event handler                                            |
//+------------------------------------------------------------------+
void CEngine::OnTick(void)
  {
//--- If this is not a EA, exit
   if(this.m_program!=PROGRAM_EXPERT)
      return;
//--- Update the current symbol timeseries
   this.SeriesRefresh(NULL,PERIOD_CURRENT);
  }
//+------------------------------------------------------------------+

Implementieren der Methoden zum Empfang der Haupteigenschaften des angegebenen Balkens der angegebenen Zeitreihe:

//+------------------------------------------------------------------+
//| Return the specified bar's Open                                  |
//| of the specified symbol of the specified timeframe               |
//+------------------------------------------------------------------+
double CEngine::SeriesOpen(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index)
  {
   CBar *bar=this.m_series.GetBar(symbol,timeframe,index);
   return(bar!=NULL ? bar.Open() : 0);
  }
//+------------------------------------------------------------------+
//| Return the specified bar's High                                  |
//| of the specified symbol of the specified timeframe               |
//+------------------------------------------------------------------+
double CEngine::SeriesHigh(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index)
  {
   CBar *bar=this.m_series.GetBar(symbol,timeframe,index);
   return(bar!=NULL ? bar.High() : 0);
  }
//+------------------------------------------------------------------+
//| Return the specified bar's Low                                   |
//| of the specified symbol of the specified timeframe               |
//+------------------------------------------------------------------+
double CEngine::SeriesLow(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index)
  {
   CBar *bar=this.m_series.GetBar(symbol,timeframe,index);
   return(bar!=NULL ? bar.Low() : 0);
  }
//+------------------------------------------------------------------+
//| Return the specified bar's Close                                 |
//| of the specified symbol of the specified timeframe               |
//+------------------------------------------------------------------+
double CEngine::SeriesClose(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index)
  {
   CBar *bar=this.m_series.GetBar(symbol,timeframe,index);
   return(bar!=NULL ? bar.Close() : 0);
  }
//+------------------------------------------------------------------+
//| Return the specified bar's Time                                  |
//| of the specified symbol of the specified timeframe               |
//+------------------------------------------------------------------+
datetime CEngine::SeriesTime(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index)
  {
   CBar *bar=this.m_series.GetBar(symbol,timeframe,index);
   return(bar!=NULL ? bar.Time() : 0);
  }
//+------------------------------------------------------------------+
//| Return the specified bar's TickVolume                            |
//| of the specified symbol of the specified timeframe               |
//+------------------------------------------------------------------+
long CEngine::SeriesTickVolume(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index)
  {
   CBar *bar=this.m_series.GetBar(symbol,timeframe,index);
   return(bar!=NULL ? bar.VolumeTick() : WRONG_VALUE);
  }
//+------------------------------------------------------------------+
//| Return the specified bar's RealVolume                            |
//| of the specified symbol of the specified timeframe               |
//+------------------------------------------------------------------+
long CEngine::SeriesRealVolume(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index)
  {
   CBar *bar=this.m_series.GetBar(symbol,timeframe,index);
   return(bar!=NULL ? bar.VolumeReal() : WRONG_VALUE);
  }
//+------------------------------------------------------------------+
//| Return the specified bar's Spread                                |
//| of the specified symbol of the specified timeframe               |
//+------------------------------------------------------------------+
int CEngine::SeriesSpread(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index)
  {
   CBar *bar=this.m_series.GetBar(symbol,timeframe,index);
   return(bar!=NULL ? bar.Spread() : INT_MIN);
  }
//+------------------------------------------------------------------+

Hier ist alles einfach: Wir erhalten das Balkenobjekt durch Zeitreihensymbol und Zeitrahmen aus dem spezifizierten Index der Chartzeitreihe (0 - aktueller Balken) und Rückgabe der entsprechenden Balkeneigenschaft.

Dies alles sind die Verbesserungen, die heute erforderlich sind, um eine automatische Aktualisierung der im Programm verwendeten Zeitreihen-Preisdaten zu erstellen, Ereignisse an das Kontrollprogramm auf dem Chart zu senden und Daten aus der erstellten Zeitreihe im Programm zu empfangen.

Tests

Lassen Sie uns den Test folgendermaßen durchführen:
Wir erstellen drei Zeitreihen für die aktuellen Zeitrahmen von drei Symbolen, das Balkenobjekt Null (Klasse CBar) Zeitreihenkollektion (CTimeSeriesCollection) abrufen und die Balkendaten als Kommentar im Chart mit den Methoden anzeigen, die den Kurznamen des Balkenobjekts + Beschreibung der Parameter des Balkenobjekts zurückgeben. Die zweite Kommentarzeile soll die Balkendaten von Null in einem ähnlichen Format enthalten. In diesem Fall werden die Daten jedoch mit den Methoden des Hauptobjekts der CEngine-Bibliothek erzeugt, die die Daten des angegebenen Balkens des angegebenen Symbols des angegebenen Zeitrahmens zurückgeben.

Die Daten sind im Tester und auf dem Chart, auf dem der EA gestartet wird, in Echtzeit zu aktualisieren.
Wir werden auch die Behandlung des Empfangs von Ereignissen aus den Objekten der Klasse CSeries implementieren, die das Ereignis "New bar" an das Kontrollprogramm auf dem Chart senden, und den Empfang dieser Ereignisse im Programm beobachten, das auf einer Chartsymbol gestartet wird.

Um den Test durchzuführen, werden wir den EA aus dem vorherigen Artikel verwenden und ihn in \MQL5\Experts\TestDoEasy\Part38\ unter dem Namen TestDoEasyPart38.mq5 speichern.

Prüfen wir OnTick() des EAs wie folgt:

//+------------------------------------------------------------------+
//| 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
     }
//--- Handle the NewTick event in the library
   engine.OnTick();

//--- If the trailing flag is set
   if(trailing_on)
     {
      TrailingPositions();    // Trailing positions
      TrailingOrders();       // Trailing of pending orders
     }
   
//--- Bet the zero bar of the current timeseries
   CBar *bar=engine.SeriesGetBar(NULL,PERIOD_CURRENT,0);
   if(bar==NULL)
      return;
//--- Create a string of parameters of the current bar similar to the one
//--- displayed by the bar object description:
//--- bar.Header()+": "+bar.ParameterDescription()
   string parameters=
     (TextByLanguage("Бар \"","Bar \"")+Symbol()+"\" "+TimeframeDescription((ENUM_TIMEFRAMES)Period())+"[0]: "+TimeToString(bar.Time(),TIME_DATE|TIME_MINUTES|TIME_SECONDS)+
      ", O: "+DoubleToString(engine.SeriesOpen(NULL,PERIOD_CURRENT,0),Digits())+
      ", H: "+DoubleToString(engine.SeriesHigh(NULL,PERIOD_CURRENT,0),Digits())+
      ", L: "+DoubleToString(engine.SeriesLow(NULL,PERIOD_CURRENT,0),Digits())+
      ", C: "+DoubleToString(engine.SeriesClose(NULL,PERIOD_CURRENT,0),Digits())+
      ", V: "+(string)engine.SeriesTickVolume(NULL,PERIOD_CURRENT,0)+
      ", Real: "+(string)engine.SeriesRealVolume(NULL,PERIOD_CURRENT,0)+
      ", Spread: "+(string)engine.SeriesSpread(NULL,PERIOD_CURRENT,0)
     );
//--- Display the data received from the bar object in the first line of the chart comment,
//--- while the second line contains the methods of receiving timeseries price data
   Comment(bar.Header(),": ",bar.ParameterDescription(),"\n",parameters);
  }
//+------------------------------------------------------------------+

Hier ist alles einfach: der Codeblock ist eine Standardvorlage bei der Arbeit mit der DoEasy-Bibliothek. Die aktuelle Implementierung funktioniert den Aufruf des NewTick-Ereignishandlers , der von der Bibliothek bei jedem Tick behandelt wird (derzeit führt sie die Aktualisierung der erzeugten Zeitreihen durch). Alle fehlenden Zeitreihen (die mit den Create()-Methoden deklariert, aber nicht erzeugt wurden) werden übersprungen (nicht von der Bibliothek aktualisiert). In Zukunft wird der Aufruf dieser Methode aus OnTick() der EAs erforderlich sein, um die aktuellen Zeitreihendaten zu aktualisieren.

Als Nächstes empfangen wir das Balkenobjekt vom aktuellen Symbol und der Periodenzeitreihe, erzeugen den String mit der Beschreibung der erhaltenen Taktdaten und zeigen zwei Zeilen im Kommentar an:
die erste Zeile wird mit den Methoden des Balkenobjekts angezeigt,
die zweite besteht aus Daten, die mit den Methoden des Hauptobjekts der Bibliothek erhalten wurden, die die angeforderten Balken-Daten zurückgeben.

Die Bibliotheksinitialisierungsfunktion OnInitDoEasy() verfügt über den leicht veränderten Codeblock zur Erstellung der Zeitreihe aller verwendeten Symbole:

//--- Implement displaying the list of used timeframes only for MQL5 - MQL4 has no ArrayPrint() function
#ifdef __MQL5__
   if(InpModeUsedTFs!=TIMEFRAMES_MODE_CURRENT)
      ArrayPrint(array_used_periods);
#endif 
//--- Create timeseries of all used symbols
   CArrayObj *list_timeseries=engine.GetListTimeSeries();
   if(list_timeseries!=NULL)
     {
      int total=list_timeseries.Total();
      for(int i=0;i<total;i++)
        {
         CTimeSeries *timeseries=list_timeseries.At(i);
         int total_periods=ArraySize(array_used_periods);
         for(int j=0;j<total_periods;j++)
           {
            ENUM_TIMEFRAMES timeframe=TimeframeByDescription(array_used_periods[j]);
            timeseries.SyncData(timeframe);
            timeseries.Create(timeframe);
           }
        }
     }
//--- Check created timeseries - display descriptions of all created timeseries in the journal
//--- (true - only created ones, false - created and declared ones)
   engine.GetTimeSeriesCollection().PrintShort(true); // Short descriptions
   //engine.GetTimeSeriesCollection().Print(true);      // Full descriptions


Hier erhalten wir die Liste aller Zeitreihen und, in der Schleife durch die Zeitreihenliste, das nächste Zeitreihenobjekt durch den Schleifenindex. Dann in der Schleife durch die Anzahl der verwendeten Zeitrahmen, erzeugen wir die benötigte Zeitreihenliste nach dem Synchronisieren der Zeitreihen- und der historischen Daten.

In der Funktion OnDoEasyEvent(), die die Bibliotheksereignisse behandelt, fügen wir den Codeblock für die Behandlung von Zeitreihen-Ereignissen hinzu (der redundante Code wurde entfernt):

//+------------------------------------------------------------------+
//| Handling DoEasy library events                                   |
//+------------------------------------------------------------------+
void OnDoEasyEvent(const int id,
                   const long &lparam,
                   const double &dparam,
                   const string &sparam)
  {
   int idx=id-CHARTEVENT_CUSTOM;
//--- Retrieve (1) event time milliseconds, (2) reason and (3) source from lparam, as well as (4) set the exact event time
   ushort msc=engine.EventMSC(lparam);
   ushort reason=engine.EventReason(lparam);
   ushort source=engine.EventSource(lparam);
   long time=TimeCurrent()*1000+msc;
   
//--- Handling symbol events
//...  
     
//--- Handling account events
//...
     
//--- Handling market watch window events
//...
     
//--- Handling timeseries events
   else if(idx>SERIES_EVENTS_NO_EVENT && idx<SERIES_EVENTS_NEXT_CODE)
     {
      //--- "New bar" event
      if(idx==SERIES_EVENTS_NEW_BAR)
        {
         Print(TextByLanguage("Новый бар на ","New Bar on "),sparam," ",TimeframeDescription((ENUM_TIMEFRAMES)dparam),": ",TimeToString(lparam));
        }
     }
     
//--- Handling trading events
//...

  }
//+------------------------------------------------------------------+

Hier wenn die erhaltene Ereignis-ID innerhalb der Zeitreihen-Ereignis-IDs liegt und wenn es sich um das Ereignis "New bar" handelt, zeigen wir die Nachricht über das Ereignis im Terminal-Journal an.

Kompilieren Sie den EA und stellen Sie seine Parameter wie folgt ein:

  • setzen Sie Mode of used symbols list für die Verwendung einer bestimmten Symbolliste,
  • lassen Sie in List of used symbols (comma - separator) nur drei Symbole stehen, eines davon ist EURUSD und
  • wählen Sie z.B. aus der Liste Mode of used timeframes list, dass Sie nur mit dem aktuellen Zeitrahmen arbeiten möchten:


Starten Sie den EA auf dem Chart. Nach einer Weile zeigt das Journal die Ereignismeldungen "New bar" zu den für das aktuelle Chartsymbol verwendeten Symbolen an:

New bar on EURUSD M5: 2020.03.11 12:55
New bar on EURAUD M5: 2020.03.11 12:55
New bar on AUDUSD M5: 2020.03.11 12:55
New bar on EURUSD M5: 2020.03.11 13:00
New bar on AUDUSD M5: 2020.03.11 13:00
New bar on EURAUD M5: 2020.03.11 13:00

Starten Sie den EA im visuellen Tester-Modus auf dem Chart eines der in den Einstellungen ausgewählten Symbole, z.B. auf EURUSD, und sehen Sie, wie sich die Nullbalken-Daten im Kommentar des Charts ändern:


Wie wir sehen können, enthalten beide Zeilen Daten, die auf unterschiedliche Weise erhalten wurden, identische Werte der empfangenen Nullbalkeneigenschaften und werden bei jedem Tick in Echtzeit aktualisiert.

Was kommt als Nächstes?

Im nächsten Artikel werden wir einige Unzulänglichkeiten der aktuellen Bibliotheksversion beheben, die nach Fertigstellung des aktuellen Artikels festgestellt wurden, und die Entwicklung des Konzepts der Arbeit mit Zeitreihen fortsetzen, indem wir die Bibliothek auf die Arbeit als Teil von Indikatoren vorbereiten.

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
Zeitreihen in der Bibliothek DoEasy (Teil 36): Objekt der Zeitreihe für alle verwendeten Symbolperioden
Zeitreihen in der Bibliothek DoEasy (Teil 37): Kollektion von Zeitreihen - Datenbank der Zeitreihen nach Symbolen und Zeitrahmen


Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/7695

Beigefügte Dateien |
MQL5.zip (3698.04 KB)
MQL4.zip (3697.98 KB)
MQL als Darstellungsmittel für graphische Schnittstellen von MQL-Programmen. Teil 1 MQL als Darstellungsmittel für graphische Schnittstellen von MQL-Programmen. Teil 1
Dieser Artikel schlägt ein neues Konzept zur Beschreibung der Fenster-Schnittstelle von MQL-Programmen vor, wobei die Strukturen von MQL verwendet werden. Spezielle Klassen transformieren das sichtbare MQL-Markup in die GUI-Elemente und erlauben es, diese zu verwalten, ihre Eigenschaften einzustellen und die Ereignisse in einer einheitlichen Weise zu verarbeiten. Es enthält auch einige Beispiele für die Verwendung des Markups für die Dialoge und Elemente einer Standardbibliothek.
Die Handelssignale mehrerer Währungen überwachen (Teil 3): Einführung von Suchalgorithmen Die Handelssignale mehrerer Währungen überwachen (Teil 3): Einführung von Suchalgorithmen
Im vorherigen Artikel haben wir den visuellen Teil der Anwendung sowie die grundlegende Interaktion der GUI-Elementen entwickelt. Diesmal fügen wir die interne Logik und den Algorithmus der Vorbereitung der Handelssignaldaten hinzu, sowie die Fähigkeit, Signale einzurichten, sie zu durchsuchen und auf dem Monitor zu visualisieren.
Die Handelssignale mehrerer Währungen überwachen (Teil 4): Erweiterung der Funktionsweise und Verbesserung des Signalsuchsystems Die Handelssignale mehrerer Währungen überwachen (Teil 4): Erweiterung der Funktionsweise und Verbesserung des Signalsuchsystems
In diesem Teil erweitern wir das System zur Suche und Bearbeitung von Handelssignalen und führen die Möglichkeit ein, benutzerdefinierte Indikatoren zu verwenden und Programmlokalisierung hinzuzufügen. Zuvor haben wir ein Basissystem für die Suche nach Signalen geschaffen, das jedoch auf einem kleinen Satz von Indikatoren und einem einfachen Satz von Suchregeln basierte.
Zeitreihen in der Bibliothek DoEasy (Teil 37): Kollektion von Zeitreihen - Datenbank der Zeitreihen nach Symbolen und Zeitrahmen Zeitreihen in der Bibliothek DoEasy (Teil 37): Kollektion von Zeitreihen - Datenbank der Zeitreihen nach Symbolen und Zeitrahmen
Der Artikel befasst sich mit der Entwicklung der Zeitreihenkollektion spezifizierter Zeitrahmen für alle im Programm verwendeten Symbole. Wir werden die Zeitreihenkollektion, die Methoden zur Parametereinstellung der Zeitreihenkollektion und das anfängliche Ausfüllen der entwickelten Zeitreihen mit historischen Daten erstellen.