Zeitreihen in der Bibliothek DoEasy (Teil 59): Objekt zum Speichern der Daten eines Ticks

Artyom Trishkin | 8 Februar, 2021

Inhaltsverzeichnis


Konzept

Ab diesem Artikel beginnen Sie mit der Entwicklung der Bibliotheksfunktionalität für die Arbeit mit Tickdaten.

Das Konzept der Speicherung und Verwendung von Tickdaten wird ähnlich sein wie das Konzept der Speicherung von Zeitreihendaten, wobei die kleinste Dateneinheit der Balken ist. Balkenobjekte werden in den Listen gespeichert, die zu entsprechenden Zeitrahmen gehören, die wiederum in den Listen gespeichert werden, die zu Symbolen gehören.

Im Konzept der Tick-Datenspeicherung sollte die kleinste Einheit des Datenvolumens die Werte der Preisstruktur auf einem Tick sein. Solche Werte werden mit Hilfe einer Struktur zur Speicherung der letzten Preise nach Symbol MqlTick beschrieben. Ein Objekt zum Speichern solcher Werte besitzt zusätzliche Eigenschaften: Spread - die Differenz von Ask- und Bid-Preis und das Symbol, dessen Daten ein Tick durch das Objekt beschrieben werden.

Zur bequemeren Strukturierung der Liste werden für jedes der Symbole eigene Listen von Objekten mit Tick-Daten erstellt. Natürlich wird jede der Listen von Tickdaten-Objekten automatisch aktualisiert, neue Daten von neu eintreffenden Ticks werden ihr hinzugefügt. Dabei wird die Größe der Listen in dem vom Nutzer der Bibliothek bestimmten Umfang unterstützt.

Das Konzept der Datenspeicherung in der Bibliothek ermöglicht die Suche und Sortierung nach beliebigen Objekteigenschaften, die in den Listen gespeichert sind, was den Erhalt der benötigten Daten für ihre spätere Verwendung oder die Durchführung analytischer Studien ermöglicht. So werden die Tickdaten die gleiche Funktion besitzen. Dies ermöglicht dem Nutzer eine schnelle Analyse der Tickdatenströme, um sie zu verfolgen, z. B. um Änderungen in der Art ihres Auftretens festzustellen oder um nach bestimmten Mustern zu suchen usw.


Daten vorbereiten

Wie bei allen Bibliotheksobjekten müssen Textnachrichten hinzugefügt werden, um die Beschreibung ihrer Parameter anzuzeigen und eine Aufzählung der Objekteigenschaften zu erstellen, mit der ihre Suche in den Listen oder die Sortierung der Objekte nach diesen Eigenschaften möglich wird.

Fügen wir in der Datei \MQL5\Include\DoEasy\Data.mqh die neuen Nachrichtenindizes hinzu:

//--- CSeriesDataInd
   MSG_LIB_TEXT_METHOD_NOT_FOR_INDICATORS,            // The method is not intended to work with indicator programs
   MSG_LIB_TEXT_IND_DATA_FAILED_GET_SERIES_DATA,      // Failed to get indicator data timeseries
   MSG_LIB_TEXT_IND_DATA_FAILED_GET_CURRENT_DATA,     // Failed to get current data of indicator buffer
   MSG_LIB_SYS_FAILED_CREATE_IND_DATA_OBJ,            // Failed to create indicator data object
   MSG_LIB_TEXT_IND_DATA_FAILED_ADD_TO_LIST,          // Failed to add indicator data object to list
   
//--- CTick
   MSG_TICK_TEXT_TICK,                                // Tick
   MSG_TICK_TIME_MSC,                                 // Time of the last update of prices in milliseconds
   MSG_TICK_TIME,                                     // Time of the last update of prices
   MSG_TICK_VOLUME,                                   // Volume for the current Last price
   MSG_TICK_FLAGS,                                    // Flags
   MSG_TICK_VOLUME_REAL,                              // Volume for the current Last price with greater accuracy
   MSG_TICK_SPREAD,                                   // Spread
   MSG_LIB_TEXT_TICK_CHANGED_DATA,                    // Changed data on tick:
   MSG_LIB_TEXT_TICK_FLAG_BID,                        // Bid price change
   MSG_LIB_TEXT_TICK_FLAG_ASK,                        // Ask price change
   MSG_LIB_TEXT_TICK_FLAG_LAST,                       // Last deal price change
   MSG_LIB_TEXT_TICK_FLAG_VOLUME,                     // Volume change

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

und auch die Nachrichtentexte entsprechend den neu hinzugefügten Indizes:

//--- CSeriesDataInd
   {"The method is not intended for working with indicator programs"},
   {"Failed to get indicator data timeseries"},
   {"Failed to get the current data of the indicator buffer"},
   {"Failed to create indicator data object"},
   {"Failed to add indicator data object to the list"},
   
//--- CTick
   {"Tick"},
   {"Last price update time in milliseconds"},
   {"Last price update time"},
   {"Volume for the current Last price"},
   {"Flags"},
   {"Volume for the current \"Last\" price with increased accuracy"},
   {"Spread"},
   {"Changed data on a tick:"},
   {"Bid price change"},
   {"Ask price change"},
   {"Last price change"},
   {"Volume change"},
   
  };
//+---------------------------------------------------------------------+


In der Datei \MQL5\Include\DoEasy\Defines.mqh werden die Enumerationen zur Angabe der Eigenschaften von integer, real und string des Tick-Datenobjekts hinzugefügt:

//+------------------------------------------------------------------+
//| Data for working with tick data                                  |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Tick integer properties                                          |
//+------------------------------------------------------------------+
enum ENUM_TICK_PROP_INTEGER
  {
   TICK_PROP_TIME_MSC = 0,                                  // Time of the last price update in milliseconds
   TICK_PROP_TIME,                                          // Time of the last update
   TICK_PROP_VOLUME,                                        // Volume for the current Last price
   TICK_PROP_FLAGS,                                         // Tick flags
  }; 
#define TICK_PROP_INTEGER_TOTAL (4)                         // Total number of tick integer properties
#define TICK_PROP_INTEGER_SKIP  (0)                         // Number of tick properties not used in sorting
//+------------------------------------------------------------------+
//| Real tick properties                                             |
//+------------------------------------------------------------------+
enum ENUM_TICK_PROP_DOUBLE
  {
   TICK_PROP_BID = TICK_PROP_INTEGER_TOTAL,                 // Tick Bid price
   TICK_PROP_ASK,                                           // Tick Ask price
   TICK_PROP_LAST,                                          // Current price of the last trade (Last)
   TICK_PROP_VOLUME_REAL,                                   // Volume for the current Last price with greater accuracy
   TICK_PROP_SPREAD,                                        // Tick spread (Ask - Bid)
  }; 
#define TICK_PROP_DOUBLE_TOTAL  (5)                         // Total number of real tick properties
#define TICK_PROP_DOUBLE_SKIP   (0)                         // Number of tick properties not used in sorting
//+------------------------------------------------------------------+
//| String tick properties                                           |
//+------------------------------------------------------------------+
enum ENUM_TICK_PROP_STRING
  {
   TICK_PROP_SYMBOL = (TICK_PROP_INTEGER_TOTAL+TICK_PROP_DOUBLE_TOTAL), // Tick symbol
  };
#define TICK_PROP_STRING_TOTAL  (1)                         // Total number of string tick properties
//+------------------------------------------------------------------+

Geben Sie für jede der Enumerationen die Gesamtzahl der Eigenschaften verwendet und nicht verwendet beim Sortieren an.

Um die Suche und Sortierung nach den oben angegebenen Eigenschaften zu ermöglichen, fügen wir eine weitere Enumeration hinzu, in der mögliche Kriterien für die Sortierung der Tick-Daten angegeben werden:

//+------------------------------------------------------------------+
//| Possible tick sorting criteria                                   |
//+------------------------------------------------------------------+
#define FIRST_TICK_DBL_PROP          (TICK_PROP_INTEGER_TOTAL-TICK_PROP_INTEGER_SKIP)
#define FIRST_TICK_STR_PROP          (TICK_PROP_INTEGER_TOTAL-TICK_PROP_INTEGER_SKIP+TICK_PROP_DOUBLE_TOTAL-TICK_PROP_DOUBLE_SKIP)
enum ENUM_SORT_TICK_MODE
  {
//--- Sort by integer properties
   SORT_BY_TICK_TIME_MSC = 0,                               // Sort by the time of the last price update in milliseconds
   SORT_BY_TICK_TIM,                                        // Sort by the time of the last price update
   SORT_BY_TICK_VOLUME,                                     // Sort by volume for the current Last price
   SORT_BY_TICK_FLAGS,                                      // Sort by tick flags
//--- Sort by real properties
   SORT_BY_TICK_BID = FIRST_TICK_DBL_PROP,                  // Sort by tick Bid price
   SORT_BY_TICK_ASK,                                        // Sort by tick Ask price
   SORT_BY_TICK_LAST,                                       // Sort by current price of the last trade (Last)
   SORT_BY_TICK_VOLUME_REAL,                                // Sort by volume for the current Last price with greater accuracy
   SORT_BY_TICK_SPREAD,                                     // Sort by tick spread
//--- Sort by string properties
   SORT_BY_TICK_SYMBOL = FIRST_TICK_STR_PROP,               // Sort by tick symbol
  };
//+------------------------------------------------------------------+


Zuvor haben wir bereits im Artikel 38 das Objekt "New tick" erstellt, das es ermöglicht, das Eintreffen eines neuen Ticks auf dem angegebenen Symbol zu verfolgen.
Das Objekt befindet sich im Ordner \MQL5\Include\DoEasy\Objects\Ticks\ im Bibliotheksverzeichnis in der Datei NewTickObj.mqh.
Verbessern wir die Objektklasse so, dass der Wert NULL (oder eine leere Zeichenkette) an die Methode der Symboleinstellung übergeben werden kann, um das aktuelle Symbol für das Objekt anzugeben:

//--- Set a symbol
   void              SetSymbol(const string symbol)   { this.m_symbol=(symbol==NULL || symbol=="" ? ::Symbol() : symbol);  }

Und in der Initialisierungsliste des Klassenkonstruktors weisen wir false der Variablen m_new_tick zu, die das Flag für den neuen Tick speichert:

//+------------------------------------------------------------------+
//| Parametric constructor CNewTickObj                               |
//+------------------------------------------------------------------+
CNewTickObj::CNewTickObj(const string symbol) : m_symbol(symbol),m_new_tick(false)
  {
//--- 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;
     }
  }
//+------------------------------------------------------------------+

Vorher wurde diese Variable noch nicht mit einem Wert initialisiert. Dies ist nicht korrekt.


Klasse des Objekts für die Tickdaten

Wir legen im Klassenverzeichnis des Objekts "New tick" \MQL5\Include\DoEasy\Objects\Ticks\ eine neue Datei der Objektklasse für die Tickdaten DataTick.mqh an.

Das Objekt wird nichts Neues für Bibliotheksklassen sein. Alles ist Standard. Lassen Sie uns den Klassenkörper analysieren:

//+------------------------------------------------------------------+
//|                                                     DataTick.mqh |
//|                        Copyright 2020, MetaQuotes Software Corp. |
//|                             https://mql5.com/ru/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, MetaQuotes Software Corp."
#property link      "https://mql5.com/ru/users/artmedia70"
#property version   "1.00"
#property strict    // Necessary for mql4
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "..\BaseObj.mqh"
#include "..\..\Services\DELib.mqh"
//+------------------------------------------------------------------+
//| “Tick” class                                                     |
//+------------------------------------------------------------------+
class CDataTick : public CBaseObj
  {
private:
   MqlTick           m_tick;                                      // Structure for obtaining current prices
   int               m_digits;                                    // Symbol's digits value
   long              m_long_prop[TICK_PROP_INTEGER_TOTAL];        // Integer properties
   double            m_double_prop[TICK_PROP_DOUBLE_TOTAL];       // Real properties
   string            m_string_prop[TICK_PROP_STRING_TOTAL];       // String properties

//--- Return the index of the array the tick’s (1) double and (2) string properties are actually located at
   int               IndexProp(ENUM_TICK_PROP_DOUBLE property)       const { return(int)property-TICK_PROP_INTEGER_TOTAL;                       }
   int               IndexProp(ENUM_TICK_PROP_STRING property)       const { return(int)property-TICK_PROP_INTEGER_TOTAL-TICK_PROP_DOUBLE_TOTAL;}

public:
//--- Set tick’s (1) integer, (2) real and (3) string property
   void              SetProperty(ENUM_TICK_PROP_INTEGER property,long value)  { this.m_long_prop[property]=value;                               }
   void              SetProperty(ENUM_TICK_PROP_DOUBLE property,double value) { this.m_double_prop[this.IndexProp(property)]=value;             }
   void              SetProperty(ENUM_TICK_PROP_STRING property,string value) { this.m_string_prop[this.IndexProp(property)]=value;             }
//--- Return tick’s (1) integer, (2) real and (3) string property from the properties array
   long              GetProperty(ENUM_TICK_PROP_INTEGER property)    const { return this.m_long_prop[property];                                 }
   double            GetProperty(ENUM_TICK_PROP_DOUBLE property)     const { return this.m_double_prop[this.IndexProp(property)];               }
   string            GetProperty(ENUM_TICK_PROP_STRING property)     const { return this.m_string_prop[this.IndexProp(property)];               }

//--- Return the flag of the tick supporting this property
   virtual bool      SupportProperty(ENUM_TICK_PROP_INTEGER property)      { return true; }
   virtual bool      SupportProperty(ENUM_TICK_PROP_DOUBLE property)       { return true; }
   virtual bool      SupportProperty(ENUM_TICK_PROP_STRING property)       { return true; }
//--- Return itself
   CDataTick        *GetObject(void)                                       { return &this;}

//--- Compare CDataTick objects with each other by the specified property (for sorting the lists by a specified object property)
   virtual int       Compare(const CObject *node,const int mode=0) const;
//--- Compare CDataTick objects with each other by all properties (to search equal objects)
   bool              IsEqual(CDataTick* compared_obj) const;
//--- Constructors
                     CDataTick(){;}
                     CDataTick(const string symbol,const MqlTick &tick);
        
//+------------------------------------------------------------------+
//| Descriptions of object tick data properties                      |
//+------------------------------------------------------------------+
//--- Return description of tick's (1) integer, (2) real and (3) string property
   string            GetPropertyDescription(ENUM_TICK_PROP_INTEGER property);
   string            GetPropertyDescription(ENUM_TICK_PROP_DOUBLE property);
   string            GetPropertyDescription(ENUM_TICK_PROP_STRING property);

//--- Display the description of tick properties in the journal (full_prop=true - all properties, false - supported ones only)
   void              Print(const bool full_prop=false);
//--- Display a short description of the tick in the journal
   virtual void      PrintShort(void);
//---  Return the (1) short name and (2) description of tick data object flags
   virtual string    Header(void);
   string            FlagsDescription(void);
   
//+------------------------------------------------------------------+ 
//| Methods of simplified access to tick data object properties      |
//+------------------------------------------------------------------+
//--- Return tick’s (1) Time, (2) time in milliseconds, (3) volume, (4) flags
   datetime          Time(void)                                         const { return (datetime)this.GetProperty(TICK_PROP_TIME);           }
   long              TimeMSC(void)                                      const { return this.GetProperty(TICK_PROP_TIME_MSC);                 }
   long              Volume(void)                                       const { return this.GetProperty(TICK_PROP_VOLUME);                   }
   uint              Flags(void)                                        const { return (uint)this.GetProperty(TICK_PROP_FLAGS);              }
   
//--- Return tick’s (1) Bid, (2) Ask, (3) Last price, (4) volume with greater accuracy, (5) spread of the tick
//--- size of the (9) candle upper, (10) lower wick
   double            Bid(void)                                          const { return this.GetProperty(TICK_PROP_BID);                      }
   double            Ask(void)                                          const { return this.GetProperty(TICK_PROP_ASK);                      }
   double            Last(void)                                         const { return this.GetProperty(TICK_PROP_LAST);                     }
   double            VolumeReal(void)                                   const { return this.GetProperty(TICK_PROP_VOLUME_REAL);              }
   double            Spread(void)                                       const { return this.GetProperty(TICK_PROP_SPREAD);                   }
   
//--- Return tick symbol
   string            Symbol(void)                                       const { return this.GetProperty(TICK_PROP_SYMBOL);                   }

//--- Return bar (1) time, (2) index on the specified timeframe the tick time falls into
   datetime          TimeBar(const ENUM_TIMEFRAMES timeframe)const
                       { return ::iTime(this.Symbol(),timeframe,this.Index(timeframe));                                                      }
   int               Index(const ENUM_TIMEFRAMES timeframe)  const
                       { return ::iBarShift(this.Symbol(),(timeframe==PERIOD_CURRENT ? ::Period() : timeframe),this.Time());                 }  

//--- Return the flag of (1) Bid, (2) Ask, (3) Last price, (4) volume change; (5) buy and (6) sell trades
   bool              IsChangeBid()                                      const { return((this.Flags() & TICK_FLAG_BID)==TICK_FLAG_BID);       }
   bool              IsChangeAsk()                                      const { return((this.Flags() & TICK_FLAG_ASK)==TICK_FLAG_ASK);       }
   bool              IsChangeLast()                                     const { return((this.Flags() & TICK_FLAG_LAST)==TICK_FLAG_LAST);     }
   bool              IsChangeVolume()                                   const { return((this.Flags() & TICK_FLAG_VOLUME)==TICK_FLAG_VOLUME); }
   bool              IsChangeBuy()                                      const { return((this.Flags() & TICK_FLAG_BUY)==TICK_FLAG_BUY);       }
   bool              IsChangeSell()                                     const { return((this.Flags( )& TICK_FLAG_SELL)==TICK_FLAG_SELL);     }
//---
  };
//+------------------------------------------------------------------+

Die Zusammensetzung von Bibliotheksobjekten und deren Erstellung wurde im ersten Artikel ausführlich besprochen. Schauen wir uns nun die wichtigsten Variablen und Methoden an und analysieren ihre Implementierung in der Klasse weiter.

Der 'private' Abschnitt der Klasse enthält Hilfsvariablen, Arrays zum Speichern von Werten der Integer-, Real- und String-Eigenschaften des Objekts; sowie die Methoden, die die tatsächlichen Eigenschaftsindizes zurückgeben, an denen sie sich physisch in den Arrays befinden.

Der 'public' Abschnitt der Klasse enthält Methoden zum Setzen und Zurückgeben der Werte der angegebenen Eigenschaften an die entsprechenden Arrays der Objekteigenschaften; Methoden, die die Flags der Objekte zurückgeben, die die eine oder andere Eigenschaft unterstützen; Methoden zum Suchen, Vergleichen und Sortieren der Objekte sowie Klassenkonstruktoren.

Methoden zur Anzeige von Objekteigenschaftsbeschreibungen und Methoden zum vereinfachten Zugriff auf Objekteigenschaften befinden sich ebenfalls dort.

Werfen wir nun einen Blick auf die Implementierung der Klassenmethoden.

Das Symbol und eine gefüllte Struktur mit den aktuellen Tick-Daten werden dem parametrischen Klassenkonstruktor übergeben:

//+------------------------------------------------------------------+
//| Parametric constructor                                           |
//+------------------------------------------------------------------+
CDataTick::CDataTick(const string symbol,const MqlTick &tick)
  {
//--- Save symbol’s Digits
   this.m_digits=(int)::SymbolInfoInteger(symbol,SYMBOL_DIGITS);
//--- Save integer tick properties
   this.SetProperty(TICK_PROP_TIME,tick.time);
   this.SetProperty(TICK_PROP_TIME_MSC,tick.time_msc);
   this.SetProperty(TICK_PROP_VOLUME,tick.volume);
   this.SetProperty(TICK_PROP_FLAGS,tick.flags);
//--- Save real tick properties
   this.SetProperty(TICK_PROP_BID,tick.bid);
   this.SetProperty(TICK_PROP_ASK,tick.ask);
   this.SetProperty(TICK_PROP_LAST,tick.last);
   this.SetProperty(TICK_PROP_VOLUME_REAL,tick.volume_real);
//--- Save additional tick properties
   this.SetProperty(TICK_PROP_SPREAD,tick.ask-tick.bid);
   this.SetProperty(TICK_PROP_SYMBOL,(symbol==NULL || symbol=="" ? ::Symbol() : symbol));
  }
//+------------------------------------------------------------------+

Innerhalb des Konstruktors füllen wir einfach die Objekteigenschaften mit den entsprechenden Werten aus der Tick-Struktur und dem Symbol, von dem die Tick-Daten bezogen werden.

Die Methode des Vergleichs zweier Parameter von Objekten anhand der angegebenen Eigenschaft:

//+----------------------------------------------------------------------+
//| Compare CDataTick objects with each other by the specified property  |
//+----------------------------------------------------------------------+
int CDataTick::Compare(const CObject *node,const int mode=0) const
  {
   const CDataTick *obj_compared=node;
//--- compare integer properties of two objects
   if(mode<TICK_PROP_INTEGER_TOTAL)
     {
      long value_compared=obj_compared.GetProperty((ENUM_TICK_PROP_INTEGER)mode);
      long value_current=this.GetProperty((ENUM_TICK_PROP_INTEGER)mode);
      return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0);
     }
//--- compare real properties of two objects
   else if(mode<TICK_PROP_DOUBLE_TOTAL+TICK_PROP_INTEGER_TOTAL)
     {
      double value_compared=obj_compared.GetProperty((ENUM_TICK_PROP_DOUBLE)mode);
      double value_current=this.GetProperty((ENUM_TICK_PROP_DOUBLE)mode);
      return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0);
     }
//--- compare string properties of two objects
   else if(mode<TICK_PROP_DOUBLE_TOTAL+TICK_PROP_INTEGER_TOTAL+TICK_PROP_STRING_TOTAL)
     {
      string value_compared=obj_compared.GetProperty((ENUM_TICK_PROP_STRING)mode);
      string value_current=this.GetProperty((ENUM_TICK_PROP_STRING)mode);
      return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0);
     }
   return 0;
  }
//+------------------------------------------------------------------+

Der Zeiger auf das Objekt, mit dem das aktuelle Objekt verglichen werden muss, und der Typ der Eigenschaft, anhand derer zwei Objekte verglichen werden sollen, werden an die Methode übergeben.

Je nach übergebenem Modus des Objektvergleichs werden diese Eigenschaften der beiden Objekte verglichen und 1/-1/0 zurückgegeben, wenn der Wert der Eigenschaft des aktuellen Objekts größer/kleiner oder gleich dem Wert der Eigenschaft des verglichenen Objekts ist.

Methode zum Vergleich von zwei Objekten auf Identität:

//+------------------------------------------------------------------+
//| Compare CDataTick objects with each other by all properties      |
//+------------------------------------------------------------------+
bool CDataTick::IsEqual(CDataTick *compared_obj) const
  {
   int beg=0, end=TICK_PROP_INTEGER_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_TICK_PROP_INTEGER prop=(ENUM_TICK_PROP_INTEGER)i;
      if(this.GetProperty(prop)!=compared_obj.GetProperty(prop)) return false; 
     }
   beg=end; end+=TICK_PROP_DOUBLE_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_TICK_PROP_DOUBLE prop=(ENUM_TICK_PROP_DOUBLE)i;
      if(this.GetProperty(prop)!=compared_obj.GetProperty(prop)) return false; 
     }
   beg=end; end+=TICK_PROP_STRING_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_TICK_PROP_STRING prop=(ENUM_TICK_PROP_STRING)i;
      if(this.GetProperty(prop)!=compared_obj.GetProperty(prop)) return false; 
     }
   return true;
  }
//+------------------------------------------------------------------+

Hier: Der Zeiger auf das zu vergleichende Objekt wird an die Methode übergeben. Dann wird in drei Schleifen durch jede Eigenschaftsgruppe jede aufeinanderfolgende Eigenschaft der beiden Objekte verglichen. Wenn mindestens eine der Eigenschaften nicht gleich der gleichen Eigenschaft des verglichenen Objekts ist, wird false zurückgegeben. Nach Abschluss aller drei Schleifen wird true zurückgegeben — alle Eigenschaften der beiden Objekte sind gleich. Dies bedeutet, dass die Objekte identisch sind.

Die Methode zur Anzeige aller Objekteigenschaften im Journal:

//+------------------------------------------------------------------+
//| Display tick properties in the journal                           |
//+------------------------------------------------------------------+
void CDataTick::Print(const bool full_prop=false)
  {
   ::Print("============= ",CMessage::Text(MSG_LIB_PARAMS_LIST_BEG)," (",this.Header(),") =============");
   int beg=0, end=TICK_PROP_INTEGER_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_TICK_PROP_INTEGER prop=(ENUM_TICK_PROP_INTEGER)i;
      if(!full_prop && !this.SupportProperty(prop)) continue;
      ::Print(this.GetPropertyDescription(prop));
     }
   ::Print("------");
   beg=end; end+=TICK_PROP_DOUBLE_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_TICK_PROP_DOUBLE prop=(ENUM_TICK_PROP_DOUBLE)i;
      if(!full_prop && !this.SupportProperty(prop)) continue;
      ::Print(this.GetPropertyDescription(prop));
     }
   ::Print("------");
   beg=end; end+=TICK_PROP_STRING_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_TICK_PROP_STRING prop=(ENUM_TICK_PROP_STRING)i;
      if(!full_prop && !this.SupportProperty(prop)) continue;
      ::Print(this.GetPropertyDescription(prop));
     }
   ::Print("============= ",CMessage::Text(MSG_LIB_PARAMS_LIST_END)," (",this.Header(),") =============\n");
  }
//+------------------------------------------------------------------+

Hier: In drei Schleifen durch jede der Objekteigenschaftsgruppen wird die Beschreibung jeder aufeinanderfolgenden Eigenschaft im Journal mit der entsprechenden Methode GetPropertyDescription() angezeigt. Wenn in Methodenparametern false für den Parameter full_prop übergeben wird, werden nur die vom Objekt unterstützten Eigenschaften angezeigt. Bei einer nicht unterstützten Eigenschaft wird im Journal angezeigt, dass die Eigenschaft nicht unterstützt wird, obwohl alle Eigenschaften in diesem Objekt unterstützt werden. Dies kann aber in von der Klasse abhängigen Objekten geändert werden.

Methoden, die die Beschreibung der Integer-, Real- und String-Eigenschaft des angegebenen Objekts zurückgeben:

//+------------------------------------------------------------------+
//| Return description of tick's integer property                    |
//+------------------------------------------------------------------+
string CDataTick::GetPropertyDescription(ENUM_TICK_PROP_INTEGER property)
  {
   return
     (
      property==TICK_PROP_TIME_MSC           ?  CMessage::Text(MSG_TICK_TIME_MSC)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+TimeMSCtoString(this.GetProperty(property),TIME_DATE|TIME_MINUTES|TIME_SECONDS)
         )  :
      property==TICK_PROP_TIME               ?  CMessage::Text(MSG_TICK_TIME)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::TimeToString(this.GetProperty(property),TIME_DATE|TIME_MINUTES|TIME_SECONDS)
         )  :
      property==TICK_PROP_VOLUME             ?  CMessage::Text(MSG_TICK_VOLUME)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==TICK_PROP_FLAGS              ?  CMessage::Text(MSG_TICK_FLAGS)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)+"\n"+CMessage::Text(MSG_LIB_TEXT_TICK_CHANGED_DATA)+this.FlagsDescription()
         )  :
      ""
     );
  }
//+------------------------------------------------------------------+
//| Return description of tick's real property                       |
//+------------------------------------------------------------------+
string CDataTick::GetPropertyDescription(ENUM_TICK_PROP_DOUBLE property)
  {
   int dg=(this.m_digits>0 ? this.m_digits : 1);
   return
     (
      property==TICK_PROP_BID                ?  CMessage::Text(MSG_LIB_PROP_BID)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::DoubleToString(this.GetProperty(property),dg)
         )  :
      property==TICK_PROP_ASK                ?  CMessage::Text(MSG_LIB_PROP_ASK)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::DoubleToString(this.GetProperty(property),dg)
         )  :
      property==TICK_PROP_LAST               ?  CMessage::Text(MSG_LIB_PROP_LAST)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::DoubleToString(this.GetProperty(property),dg)
         )  :
      property==TICK_PROP_VOLUME_REAL        ?  CMessage::Text(MSG_TICK_VOLUME_REAL)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::DoubleToString(this.GetProperty(property),2)
         )  :
      property==TICK_PROP_SPREAD             ?  CMessage::Text(MSG_TICK_SPREAD)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::DoubleToString(this.GetProperty(property),dg)
         )  :
      ""
     );
  }
//+------------------------------------------------------------------+
//| Return description of tick's string property                     |
//+------------------------------------------------------------------+
string CDataTick::GetPropertyDescription(ENUM_TICK_PROP_STRING property)
  {
   return(property==TICK_PROP_SYMBOL ? CMessage::Text(MSG_LIB_PROP_SYMBOL)+": \""+this.GetProperty(property)+"\"" : "");
  }
//+------------------------------------------------------------------+

Hier: Abhängig von der an die Methode übergebenen Eigenschaft wird deren Stringbeschreibung zurückgegeben.

Methode, die den String mit der Beschreibung aller Flags des Ticks zurückgibt:

//+------------------------------------------------------------------+
//| Display the description of flags                                 |
//+------------------------------------------------------------------+
string CDataTick::FlagsDescription(void)
  {
   string flags=
     (
      (this.IsChangeAsk()     ? "\n - "+CMessage::Text(MSG_LIB_TEXT_TICK_FLAG_ASK)     : "")+
      (this.IsChangeBid()     ? "\n - "+CMessage::Text(MSG_LIB_TEXT_TICK_FLAG_BID)     : "")+
      (this.IsChangeLast()    ? "\n - "+CMessage::Text(MSG_LIB_TEXT_TICK_FLAG_LAST)    : "")+
      (this.IsChangeVolume()  ? "\n - "+CMessage::Text(MSG_LIB_TEXT_TICK_FLAG_VOLUME)  : "")+
      (this.IsChangeBuy()     ? "\n - "+CMessage::Text(MSG_DEAL_TO_BUY)                : "")+
      (this.IsChangeSell()    ? "\n - "+CMessage::Text(MSG_DEAL_TO_SELL)               : "")
     );
  return flags; 
  }
//+------------------------------------------------------------------+

Jeder Tick hat in seinen Eigenschaften eine Variable, die einen Satz von Flags enthält. Diese Flags beschreiben die Ereignisse, die zu diesem Tick geführt haben:

Aus dem Nutzer-Handbuch MqlTick

Um herauszufinden, welche spezifischen Daten sich mit dem aktuellen Tick geändert haben, analysieren Sie dessen Flags:

Wir haben sechs Methoden (nach der Anzahl der Flags), die das Anwesenheitsflag innerhalb der Variablen jedes Flags zurückgeben:

//--- Return the flag of (1) Bid, (2) Ask, (3) Last price, (4) volume change; (5) buy and (6) sell trades
   bool IsChangeBid()    const { return((this.Flags() & TICK_FLAG_BID)==TICK_FLAG_BID);       }
   bool IsChangeAsk()    const { return((this.Flags() & TICK_FLAG_ASK)==TICK_FLAG_ASK);       }
   bool IsChangeLast()   const { return((this.Flags() & TICK_FLAG_LAST)==TICK_FLAG_LAST);     }
   bool IsChangeVolume() const { return((this.Flags() & TICK_FLAG_VOLUME)==TICK_FLAG_VOLUME); }
   bool IsChangeBuy()    const { return((this.Flags() & TICK_FLAG_BUY)==TICK_FLAG_BUY);       }
   bool IsChangeSell()   const { return((this.Flags() & TICK_FLAG_SELL)==TICK_FLAG_SELL);     }

In der beschriebenen Methode wird zunächst das Vorhandensein des Flags innerhalb der Variablen geprüft. Je nachdem, ob es vorhanden ist oder nicht, wird der resultierende Textstring mit Newline-Zeichen + Flag-Beschreibung oder einem leeren String angefügt. Nach der Überprüfung aller Flags haben wir schließlich den kompilierten String, der Beschreibungen der in der Variablen vorhandenen Flags enthält. Wenn mehrere Flags vorhanden sind, beginnt die Beschreibung jedes Flags ab einer neuen Zeile im Journal.

Methode zur Anzeige der Kurzbeschreibung des Tick-Datenobjekts im Journal:

//+------------------------------------------------------------------+
//| Display a short description of the tick in the journal           |
//+------------------------------------------------------------------+
void CDataTick::PrintShort(void)
  {
   ::Print(this.Header());
  }
//+------------------------------------------------------------------+

Die Methode zeigt einfach im Journal den eigenen Kurznamen an, der von der folgenden Methode zurückgegeben wird:

//+------------------------------------------------------------------+
//| Return a short name of tick data object                          |
//+------------------------------------------------------------------+
string CDataTick::Header(void)
  {
   return
     (
      CMessage::Text(MSG_TICK_TEXT_TICK)+" \""+this.Symbol()+"\" "+TimeMSCtoString(TimeMSC())
     );
  }
//+------------------------------------------------------------------+

In dieser Phase wird die Erstellung des Tick-Datenobjekts beendet.


Testen des Objekts für Tickdaten

Um den Test durchzuführen, verwenden wir den EA aus dem vorherigen Artikel und speichern ihn im neuen Ordner \MQL5\Experts\TestDoEasy\Part59\ unter dem neuen Namen TestDoEasyPart59.mq5.

In diesem EA sind Indikatoren und deren Daten mit ihren Zeitreihen nicht mehr notwendig. Hier wird einfach ein Tick-Datenobjekt erzeugt, dessen vollständige Beschreibung im Journal und die Kurzbeschreibung im Kommentar auf dem Chart angezeigt. Mit jedem neuen Tick ändert sich seine Beschreibung im Chart, während im Journal der erste Tick angezeigt wird, der nach dem Start des EA kam.

Da es noch keine Verbindung des EA mit diesem neuen Bibliotheksobjekt gibt, werde ich die Datei seiner Klasse mit dem EA verbinden:

//+------------------------------------------------------------------+
//|                                             TestDoEasyPart59.mq5 |
//|                        Copyright 2020, MetaQuotes Software Corp. |
//|                             https://mql5.com/ru/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, MetaQuotes Software Corp."
#property link      "https://mql5.com/ru/users/artmedia70"
#property version   "1.00"
//--- includes
#include <DoEasy\Engine.mqh>
#include <DoEasy\Objects\Ticks\DataTick.mqh>

Im Bereich der globalen Variablen von EA anstelle von nutzerdefinierten Indikatorparameter-Arrays

//--- Arrays of custom indicator parameters
MqlParam       param_ma1[];
MqlParam       param_ma2[];
//+------------------------------------------------------------------+

Deklarieren wir ein Objekt der Klasse "New tick" — es wird benötigt, um die Arbeit innerhalb zukünftiger Kollektionsklasse von Bibliotheks-Tick-Daten zu simulieren:

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

//--- "New tick" object
CNewTickObj    check_tick;
//+------------------------------------------------------------------+

In OnInit() entfernen wir den Indikator-Erstellungsblock:

//--- Create indicators
   ArrayResize(param_ma1,4);
   //--- Name of indicator 1
   param_ma1[0].type=TYPE_STRING;
   param_ma1[0].string_value="Examples\\Custom Moving Average.ex5";
   //--- Calculation period
   param_ma1[1].type=TYPE_INT;
   param_ma1[1].integer_value=13;
   //--- Horizontal shift
   param_ma1[2].type=TYPE_INT;
   param_ma1[2].integer_value=0;
   //--- Smoothing method
   param_ma1[3].type=TYPE_INT;
   param_ma1[3].integer_value=MODE_SMA;
   //--- Create indicator 1
   engine.GetIndicatorsCollection().CreateCustom(NULL,PERIOD_CURRENT,MA1,1,INDICATOR_GROUP_TREND,param_ma1);
   
   ArrayResize(param_ma2,5);
   //--- Name of indicator 2
   param_ma2[0].type=TYPE_STRING;
   param_ma2[0].string_value="Examples\\Custom Moving Average.ex5";
   //--- Calculation period
   param_ma2[1].type=TYPE_INT;
   param_ma2[1].integer_value=13;
   //--- Horizontal shift
   param_ma2[2].type=TYPE_INT;
   param_ma2[2].integer_value=0;
   //--- Smoothing method
   param_ma2[3].type=TYPE_INT;
   param_ma2[3].integer_value=MODE_SMA;
   //--- Calculation price
   param_ma2[4].type=TYPE_INT;
   param_ma2[4].integer_value=PRICE_OPEN;
   //--- Create indicator 2
   engine.GetIndicatorsCollection().CreateCustom(NULL,PERIOD_CURRENT,MA2,1,INDICATOR_GROUP_TREND,param_ma2);
   
   //--- Create indicator 3
   engine.GetIndicatorsCollection().CreateAMA(NULL,PERIOD_CURRENT,AMA1);
   //--- Create indicator 4
   engine.GetIndicatorsCollection().CreateAMA(NULL,PERIOD_CURRENT,AMA2,14);
   
   //--- Display descriptions of created indicators
   engine.GetIndicatorsCollection().Print();
   engine.GetIndicatorsCollection().PrintShort();

Ganz am Ende von OnInit() setzen wir für das Objekt "Neuer Tick" das aktuelle Symbol als das, mit dem es arbeitet:

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

In OnTick() des EAs fügen wir einen Codeblock zur Ermittlung eines neuen Ticks (als Simulation der Arbeit innerhalb der zukünftigen Sammelklasse von Ticks) und zur Erzeugung eines neuen Tick-Datenobjekts mit der Anzeige seiner Beschreibung im Chart und im Journal hinzu:

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//--- Handle the NewTick event in the library
   engine.OnTick(rates_data);

//--- If working in the tester
   if(MQLInfoInteger(MQL_TESTER))
     {
      engine.OnTimer(rates_data);   // Working in the timer
      PressButtonsControl();        // Button pressing control
      engine.EventsHandling();      // Working with events
     }

//--- Create a temporary list for storing “Tick data” objects,
//--- a variable for obtaining tick data and
//--- a variable for calculating incoming ticks
   static int tick_count=0;
   CArrayObj list;
   MqlTick tick_struct;
//--- Check a new tick on the current symbol
   if(check_tick.IsNewTick())
     {
      //--- If failed to get the price - exit
      if(!SymbolInfoTick(Symbol(),tick_struct))
         return;
      //--- Create a new tick data object
      CDataTick *tick_obj=new CDataTick(Symbol(),tick_struct);
      if(tick_obj==NULL)
         return;
      //--- Increase tick counter (simply to display on the screen, no other purpose is provided)
      tick_count++;
      //--- Limit the number of ticks in the counting as one hundred thousand (again, no purpose is provided)
      if(tick_count>100000) tick_count=1;
      //--- In the comment on the chart display the tick number and its short description
      Comment("--- №",IntegerToString(tick_count,5,'0'),": ",tick_obj.Header());
      //--- If this is the first tick (which follows the first launch of EA) display its full description in the journal
      if(tick_count==1)
         tick_obj.Print();
      //--- Remove if failed to put the created tick data object in the list
      if(!list.Add(tick_obj))
         delete tick_obj;
     }
   
//--- If the trailing flag is set
   if(trailing_on)
     {
      TrailingPositions();          // Trailing positions
      TrailingOrders();             // Trailing of pending orders
     }
  }
//+------------------------------------------------------------------+

Die Logik ist in den Kommentaren des Codes detailliert beschrieben; ich glaube, sie ist klar und einfach.

Kompilieren Sie den EA und starten Sie ihn auf dem Chart, der in den Einstellungen vorläufig so eingestellt ist, dass er das aktuelle Symbol und den aktuellen Zeitrahmen verwendet. Nach dem Start und der Ankunft des neuen Ticks wird die Beschreibung des Tick-Datenobjekts (angekommener Tick) im Journal angezeigt:

Account 8550475: Artyom Trishkin (MetaQuotes Software Corp.) 10426.13 USD, 1:100, Hedge, Demo account MetaTrader 5
--- Initialize "DoEasy" library ---
Work with the current symbol only: "EURUSD"
Work with the current timeframe only: H1
EURUSD symbol timeseries: 
- "EURUSD" H1 timeseries: Requested: 1000, Actually: 1000, Created: 1000, On the server: 5153
Library initialize time: 00:00:00.000
============= Beginning of parameter list (Tick "EURUSD" 2020.12.16 13:22:32.822) =============
Last price update time in milliseconds: 2020.12.16 13:22:32.822
Last price update time: 2020.12.16 13:22:32
Volume for the current Last price: 0
Flags: 6
Changed data on the tick:
 - Ask price change
 - Bid price change
------
Bid price: 1.21927
Ask price: 1.21929
Last price: 0.00000
Volume for the current Last price with greater accuracy: 0.00
Spread: 0.00002
------
Symbol: "EURUSD"
============= End of parameter list (Tick "EURUSD" 2020.12.16 13:22:32.822) =============

und bei jedem neuen Tick wird ein Kommentar mit seiner Kurzbeschreibung im Diagramm angezeigt:



Was kommt als Nächstes?

Im nächsten Artikel werden wir mit der Erstellung der Tick-Datensammlung für ein Symbol beginnen.

Alle Dateien der aktuellen Bibliotheksversion sind unten angehängt, zusammen mit der Test-EA-Datei für MQL5. Sie können sie herunterladen und alles testen.
Hinterlassen Sie Ihre Kommentare, Fragen und Anregungen im Kommentarteil dieses Artikels.

Zurück zum Inhalt

Frühere Artikel dieser Serie:

Zeitreihen in der Bibliothek DoEasy (Teil 35): das Balkenobjekt und die Liste der Zeitreihen eines Symbols
Zeitreihen in der Bibliothek DoEasy (Teil 36): Objekt der Zeitreihe für alle verwendeten Symbolzeitrahmen
Zeitreihen in der Bibliothek DoEasy (Teil 37): Kollektion von Zeitreihen - Datenbank der Zeitreihen nach Symbolen und Zeitrahmen
Zeitreihen in der Bibliothek DoEasy (Teil 38): Kollektion von Zeitreihen - Aktualisierungen in Echtzeit und Datenzugriff aus dem Programm

Zeitreihen in der Bibliothek DoEasy (Teil 40): Bibliotheksbasierte Indikatoren - Aktualisierung der Daten in Echtzeit
Zeitreihen in der Bibliothek DoEasy (Teil 41): Beispiel eines Multi-Symbol- und Multi-Zeitrahmen-Indikators
Zeitreihen in der Bibliothek DoEasy (Teil 42): Abstrakte Objektklasse der Indikatorpuffer
Zeitreihen in der Bibliothek DoEasy (Teil 43): Klassen der Objekte von Indikatorpuffern
Zeitreihen in der Bibliothek DoEasy (Teil 44): Kollektionsklasse der Objekte von Indikatorpuffern
Zeitreihen in der Bibliothek DoEasy (Teil 45): Puffer für Mehrperiodenindikator
Zeitreihen in der Bibliothek DoEasy (Teil 46): Mehrperioden-Multisymbol-Indikatorpuffer
Zeitreihen in der Bibliothek DoEasy (Teil 47): Standardindikatoren für mehrere Symbole und Perioden
Zeitreihen in der Bibliothek DoEasy (Teil 48): Mehrperioden-Multisymbol-Indikatoren mit einem Puffer in einem Unterfenster
Zeitreihen in der Bibliothek DoEasy (Teil 49): Standardindikatoren mit mehreren Puffern für mehrere Symbole und Perioden
Zeitreihen in der Bibliothek DoEasy (Teil 50): Verschieben der Standardindikatoren für mehrere Symbole und Perioden
Zeitreihen in der Bibliothek DoEasy (Teil 51): Zusammengesetzte Standardindikatoren für mehrere Symbole und Perioden
Zeitreihen in der Bibliothek DoEasy (Teil 52): Plattformübergreifende Eigenschaft für Standardindikatoren mit einem Puffer für mehrere Symbole und Perioden
Zeitreihen in der Bibliothek DoEasy (Teil 53): Abstrakte Basisklasse der Indikatoren
Zeitreihen in der Bibliothek DoEasy (Teil 54): Abgeleitete Klassen des abstrakten Basisindikators
Zeitreihen in der Bibliothek DoEasy (Teil 55): Die Kollektionsklasse der Indikatoren
Zeitreihen in der Bibliothek DoEasy (Teil 56): Nutzerdefiniertes Indikatorobjekt, das die Daten von Indikatorobjekten aus der Kollektion holt
Zeitreihen in der Bibliothek DoEasy (Teil 57): Das Datenobjekt der Indikatorpuffer
Zeitreihen in der Bibliothek DoEasy (Teil 58): Zeitreihen der Datenpuffer von Indikatoren