Zeitreihen in der Bibliothek DoEasy (Teil 57): Das Datenobjekt der Indikatorpuffer

Artyom Trishkin | 12 Januar, 2021

Inhaltsverzeichnis


Konzept

Das gesamte Konzept der Datenkonstruktion und -speicherung in der Bibliothek basiert auf Kollektionslisten, die einen Satz gleichartiger Daten enthalten. Sie können in der gewünschten Reihenfolge ausgewählt, sortiert und gefiltert werden. Dies ermöglicht es, notwendige Datensätze für ihren weiteren Vergleich und ihre Analyse zu erhalten. Die Indikatorpuffer unterscheiden sich in ihrer Struktur nicht wesentlich von den früher erstellten seriellen Objekten und können in ihren Kollektionslisten enthalten sein, wo wir schnell die notwendigen Objekte mit Daten finden können. Aber um eine Kollektion erstellen zu können, müssen Sie ein Datenobjekt für diese Kollektion haben. Der vollständige Satz dieser Daten wird in der Kollektionsliste gespeichert.
Heute werde ich ein Datenobjekt erstellen, das alle Informationen über den Indikatorpuffer auf einem Balken enthält, bzw. es enthält Informationen über den Indikator, zu dem der Puffer gehört, dessen Daten eines Balkens durch das zu erstellende Objekt beschrieben werden.

Für jeden einzelnen Puffer eines Indikators und für jeden Balken der Zeitreihe wird ein eigenes Datenobjekt erstellt und in der Kollektionsliste organisiert, die zum Symbol und Zeitrahmen dieses Indikators gehört. Auf diese Weise werde ich für eine Kopie des Indikators einen Datensatz für jeden Balken der Zeitreihe jedes Puffers dieses Indikators haben.

Im Moment scheint ein solches Konzept der Datenspeicherung übertrieben zu sein, weil man immer die notwendigen Daten des gewünschten Balkens der Zeitreihe des Indikatorpuffers durch eine direkte Anfrage aus dem Indikator bekommen kann. Aber weiter, wenn die Kollektionen dieser Daten erstellt werden (in den folgenden Artikeln), werden wir in der Lage sein, schnell notwendige Daten beliebiger Indikatoren zu finden, für die Sammlungen erstellt werden; sowie, um Benchmarking-Analyse durchzuführen. Für eine solche Situation scheint die Speicherung der Daten im Cache der Kollektion angemessen.


Verbesserung der Bibliothek der Klasse

Fügen wir zunächst, wie üblich, die neuen Bibliotheksmeldungen hinzu.
In der Datei \MQL5\Include\DoEasy\Data.mqh fügen wir die neuer Nachrichtenindizes hinzu:

//--- CIndicatorsCollection
   MSG_LIB_SYS_FAILED_ADD_IND_TO_LIST,                // Error. Failed to add indicator object to the list
   MSG_LIB_SYS_INVALID_IND_POINTER,                   // Error. Invalid pointer to indicator object is passed
   MSG_LIB_SYS_IND_ID_EXIST,                          // Error. Indicator object with ID already exists
   
//--- CDataInd
   MSG_LIB_TEXT_IND_DATA_IND_BUFFER_NUM,              // Indicator buffer number
   MSG_LIB_TEXT_IND_DATA_BUFFER_VALUE,                // Indicator buffer value
   
  };
//+------------------------------------------------------------------+

Und weiter - die Textnachrichten entsprechend den neu hinzugefügten Indices:

   {"Error. Failed to add indicator object to list"},
   {"Error. Invalid pointer to indicator object passed"},
   {"Error. There is already exist an indicator object with ID"},
   
   {"Indicator buffer number"},
   {"Indicator buffer value"},
   
  };
//+---------------------------------------------------------------------+

Da das Indikatorpuffer-Datenobjekt in der Kollektionsliste gespeichert wird, muss dieses Objekt zum Suchen und Sortieren alle Eigenschaften erhalten, die anderen Bibliotheksobjekten eigen sind, die in Listen gespeichert sind.
In der Datei \MQL5\Include\DoEasy\Defines.mqh werden alle notwendigen Eigenschaften eines neuen Objekts beschrieben — ganzzahlige Eigenschaften des Objekts:

//+------------------------------------------------------------------+
//|  Data for working with indicator data                            |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Integer properties of indicator data                             |
//+------------------------------------------------------------------+
enum ENUM_IND_DATA_PROP_INTEGER
  {
   IND_DATA_PROP_TIME = 0,                                  // Start time of indicator data bar period
   IND_DATA_PROP_PERIOD,                                    // Indicator data period (timeframe)
   IND_DATA_PROP_INDICATOR_TYPE,                            // Indicator type
   IND_DATA_PROP_IND_BUFFER_NUM,                            // Indicator data buffer number
   IND_DATA_PROP_IND_ID,                                    // Indicator ID
  }; 
#define IND_DATA_PROP_INTEGER_TOTAL (5)                     // Total number of indicator data integer properties
#define IND_DATA_PROP_INTEGER_SKIP  (0)                     // Number of indicator data properties not used in sorting
//+------------------------------------------------------------------+

Reelle Objekteigenschaften:

//+------------------------------------------------------------------+
//| Indicator data real properties                                   |
//+------------------------------------------------------------------+
enum ENUM_IND_DATA_PROP_DOUBLE
  {
//--- bar data
   IND_DATA_PROP_BUFFER_VALUE = IND_DATA_PROP_INTEGER_TOTAL,// Indicator data value
  }; 
#define IND_DATA_PROP_DOUBLE_TOTAL  (1)                     // Total number of indicator data real properties
#define IND_DATA_PROP_DOUBLE_SKIP   (0)                     // Number of indicator data properties not used in sorting
//+------------------------------------------------------------------+

Hier ist nur eine Eigenschaft verfügbar - ein Wert im Indikatorpuffer, der dem Balken entspricht, für den das Indikatordatenobjekt erstellt wird.

Die Texteigenschaften des Objekts:

//+------------------------------------------------------------------+
//| Indicator data string properties                                 |
//+------------------------------------------------------------------+
enum ENUM_IND_DATA_PROP_STRING
  {
   IND_DATA_PROP_SYMBOL = (IND_DATA_PROP_INTEGER_TOTAL+IND_DATA_PROP_DOUBLE_TOTAL), // Indicator data symbol
   IND_DATA_PROP_IND_NAME,                                  // Indicator name
   IND_DATA_PROP_IND_SHORTNAME,                             // Indicator short name
  };
#define IND_DATA_PROP_STRING_TOTAL  (3)                     // Total number of string properties of indicator data
//+------------------------------------------------------------------+

Durch die Werte dieser Eigenschaften wird es möglich sein, die Daten der Kollektion nach dem Symbol, auf dem der Indikator berechnet wird, sowie nach dem Indikatornamen und der Kurzbezeichnung auszuwählen und zu sortieren.

Fügen wir nun alle erstellten Eigenschaften des Objekts zur Enumeration der möglichen Sortierkriterien hinzu:

//+------------------------------------------------------------------+
//| Possible criteria for indicator data sorting                     |
//+------------------------------------------------------------------+
#define FIRST_IND_DATA_DBL_PROP          (IND_DATA_PROP_INTEGER_TOTAL-IND_DATA_PROP_INTEGER_SKIP)
#define FIRST_IND_DATA_STR_PROP          (IND_DATA_PROP_INTEGER_TOTAL-IND_DATA_PROP_INTEGER_SKIP+IND_DATA_PROP_DOUBLE_TOTAL-IND_DATA_PROP_DOUBLE_SKIP)
enum ENUM_SORT_IND_DATA_MODE
  {
//--- Sort by integer properties
   SORT_BY_IND_DATA_TIME = 0,                               // Sort by bar period start time of indicator data
   SORT_BY_IND_DATA_PERIOD,                                 // Sort by indicator data period (timeframe)
   SORT_BY_IND_DATA_INDICATOR_TYPE,                         // Sort by indicator type
   SORT_BY_IND_DATA_IND_BUFFER_NUM,                         // Sort by indicator data buffer number
   SORT_BY_IND_DATA_IND_ID,                                 // Sort by indicator ID
//--- Sort by real properties
   SORT_BY_IND_DATA_BUFFER_VALUE = FIRST_IND_DATA_DBL_PROP, // Sort by indicator data value
//--- Sort by string properties
   SORT_BY_IND_DATA_SYMBOL = FIRST_IND_DATA_STR_PROP,       // Sort by indicator data symbol
   SORT_BY_IND_DATA_IND_NAME,                               // Sort by indicator name
   SORT_BY_IND_DATA_IND_SHORTNAME,                          // Sort by indicator short name
  };
//+------------------------------------------------------------------+

Das Datenobjekt der Indikatorpuffer

Die Objekteigenschaften sind fertig. Legen wir nun ein neues Objekt an, das die Daten eines Puffers für einen Indikator speichern soll.

Wir legen im Bibliotheksordner \MQL5\Include\DoEasy im Ordner \Objects\Indicators\ eine neue Klasse CDataInd in der Datei DataInd.mqh an:

Die Klasse ist abgeleitet von dem Basisobjekt aller Objekte der CBaseObj-Bibliothek.

Tatsächlich enthält das Objekt Felder und Methoden, die für Bibliotheksobjekte Standard sind, und ist identisch mit dem in Artikel 35 analysierten Balken-Objekt, hat aber im Gegensatz zum Balken-Objekt weniger Eigenschaften (alle Eigenschaften der Objekt-Indikator-Pufferdaten wurden oben in Enumerationen beschrieben).

Analysieren wir nun den Klassenkörper des Objekts Indikatorpufferdaten:

//+------------------------------------------------------------------+
//|                                                      DataInd.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 "..\BaseObj.mqh"
//+------------------------------------------------------------------+
//| Indicator data class                                             |
//+------------------------------------------------------------------+
class CDataInd : public CBaseObj
  {
private:
   int               m_digits;                                    // Digits value of indicator data
   int               m_index;                                     // Bar index
   string            m_period_description;                        // Timeframe string description
   long              m_long_prop[IND_DATA_PROP_INTEGER_TOTAL];    // Integer properties
   double            m_double_prop[IND_DATA_PROP_DOUBLE_TOTAL];   // Real properties
   string            m_string_prop[IND_DATA_PROP_STRING_TOTAL];   // String properties

//--- Return the index of the array the object's (1) double and (2) string properties are located at
   int               IndexProp(ENUM_IND_DATA_PROP_DOUBLE property)   const { return(int)property-IND_DATA_PROP_INTEGER_TOTAL;                            }
   int               IndexProp(ENUM_IND_DATA_PROP_STRING property)   const { return(int)property-IND_DATA_PROP_INTEGER_TOTAL-IND_DATA_PROP_DOUBLE_TOTAL; }

public:
//--- Set (1) integer, (2) real and (3) string properties of indicator data
   void              SetProperty(ENUM_IND_DATA_PROP_INTEGER property,long value) { this.m_long_prop[property]=value;                               }
   void              SetProperty(ENUM_IND_DATA_PROP_DOUBLE property,double value){ this.m_double_prop[this.IndexProp(property)]=value;             }
   void              SetProperty(ENUM_IND_DATA_PROP_STRING property,string value){ this.m_string_prop[this.IndexProp(property)]=value;             }
//--- Return (1) integer, (2) real and (3) string property of indicator data from the properties array
   long              GetProperty(ENUM_IND_DATA_PROP_INTEGER property)   const { return this.m_long_prop[property];                                 }
   double            GetProperty(ENUM_IND_DATA_PROP_DOUBLE property)    const { return this.m_double_prop[this.IndexProp(property)];               }
   string            GetProperty(ENUM_IND_DATA_PROP_STRING property)    const { return this.m_string_prop[this.IndexProp(property)];               }

//--- Return the flag of the object supporting this property
   virtual bool      SupportProperty(ENUM_IND_DATA_PROP_INTEGER property)     { return true; }
   virtual bool      SupportProperty(ENUM_IND_DATA_PROP_DOUBLE property)      { return true; }
   virtual bool      SupportProperty(ENUM_IND_DATA_PROP_STRING property)      { return true; }
//--- Return itself
   CDataInd         *GetObject(void)                                          { return &this;}
//--- Set (1) symbol, timeframe and time for the object, (2) indicator type, (3) number of buffers, (4) number of data buffer,
//--- (5) ID, (6) data value, (7) name, (8) indicator short name
   void              SetSymbolPeriod(const string symbol,const ENUM_TIMEFRAMES timeframe,const datetime time);
   void              SetIndicatorType(const ENUM_INDICATOR type)              { this.SetProperty(IND_DATA_PROP_INDICATOR_TYPE,type);               }
   void              SetBufferNum(const int num)                              { this.SetProperty(IND_DATA_PROP_IND_BUFFER_NUM,num);                }
   void              SetIndicatorID(const int id)                             { this.SetProperty(IND_DATA_PROP_IND_ID,id);                         }
   void              SetBufferValue(const double value)                       { this.SetProperty(IND_DATA_PROP_BUFFER_VALUE,value);                }
   void              SetIndicatorName(const string name)                      { this.SetProperty(IND_DATA_PROP_IND_NAME,name);                     }
   void              SetIndicatorShortname(const string shortname)            { this.SetProperty(IND_DATA_PROP_IND_SHORTNAME,shortname);           }
   
//--- Compare CDataInd objects with each other by all possible properties (for sorting the lists by a specified object property)
   virtual int       Compare(const CObject *node,const int mode=0) const;
//--- Compare CDataInd objects with each other by all properties (to search equal objects)
   bool              IsEqual(CDataInd* compared_data) const;
//--- Constructors
                     CDataInd(){;}
                     CDataInd(const ENUM_INDICATOR ind_type,
                              const int ind_id,
                              const int buffer_num,
                              const string symbol,
                              const ENUM_TIMEFRAMES timeframe,
                              const datetime time);
                     
//+------------------------------------------------------------------+ 
//| Methods of simplified access to object properties                |
//+------------------------------------------------------------------+
//--- Return (1) bar period start time, (2) timeframe, (3) indicator type, (4) number of buffers, (5) buffer number, (6) indicator ID
   datetime          Time(void)                                         const { return (datetime)this.GetProperty(IND_DATA_PROP_TIME);                   }
   ENUM_TIMEFRAMES   Timeframe(void)                                    const { return (ENUM_TIMEFRAMES)this.GetProperty(IND_DATA_PROP_PERIOD);          }
   ENUM_INDICATOR    IndicatorType(void)                                const { return (ENUM_INDICATOR)this.GetProperty(IND_DATA_PROP_INDICATOR_TYPE);   }
   int               BufferNum(void)                                    const { return (ENUM_INDICATOR)this.GetProperty(IND_DATA_PROP_IND_BUFFER_NUM);   }
   int               IndicatorID(void)                                  const { return (ENUM_INDICATOR)this.GetProperty(IND_DATA_PROP_IND_ID);           }

//--- Return the price of indicator buffer data
   double            PriceValue(void)                                   const { return this.GetProperty(IND_DATA_PROP_BUFFER_VALUE);                     }
   
//--- Return (1) data symbol, (2) name, (3) indicator short name
   string            Symbol(void)                                       const { return this.GetProperty(IND_DATA_PROP_SYMBOL);                           }
   string            IndicatorName(void)                                const { return this.GetProperty(IND_DATA_PROP_IND_NAME);                         }
   string            IndicatorShortName(void)                           const { return this.GetProperty(IND_DATA_PROP_IND_SHORTNAME);                    }
//--- Return bar index on the specified timeframe the object bar time falls into
   int               Index(const ENUM_TIMEFRAMES timeframe)  const
                       { return ::iBarShift(this.Symbol(),(timeframe==PERIOD_CURRENT ? ::Period() : timeframe),this.Time());                             }
//--- Return Digits set for the object
   int               Digits(void)                                       const { return this.m_digits;                                                    }
//+------------------------------------------------------------------+
//| Description of properties of the object - indicator data         |
//+------------------------------------------------------------------+
//--- Return description of object's (1) integer, (2) real and (3) string property
   string            GetPropertyDescription(ENUM_IND_DATA_PROP_INTEGER property);
   string            GetPropertyDescription(ENUM_IND_DATA_PROP_DOUBLE property);
   string            GetPropertyDescription(ENUM_IND_DATA_PROP_STRING property);

//--- Return indicator type description
   string            IndicatorTypeDescription(void)                     const { return ::IndicatorTypeDescription(this.IndicatorType());                 }
//--- Display the description of object 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 object in the journal
   virtual void      PrintShort(void);
//---
  };
//+------------------------------------------------------------------+

Lassen Sie uns kurz den Inhalt der Klasse analysieren.

Die Eigenschaften im 'private' Abschnitt der Klasse:

Die drei Arrays, die die entsprechenden Objekteigenschaften speichern — ganzzahlige, reelle und Text-Eigenschaften.
Die Methoden, die den wahren Index der Objekteigenschaft im entsprechenden Array berechnen
.
Die Variablen der Klasse zum Speichern von Werten weiterer Objekteigenschaften.

Der 'public' Teil der Klasseneigenschaften:

Die Methoden, die den übergebenen Objekt-Eigenschaftswert in die Arrays der Integer-, Real- und String-Eigenschaften schreiben.
Die Methoden, die den Wert einer angeforderten ganzzahligen, reelen oder Text-Eigenschaft aus den Arrays zurückgeben.
Die virtuellen Methoden, die das Flag der Unterstützung der Eigenschaft durch das Objekt für jede der Eigenschaften zurückgeben. Die Methoden sollen in Nachfolgeobjekten des Objekts implementiert werden und sollten false zurückgeben, falls das Nachfolgeobjekt die angegebene Eigenschaft nicht unterstützt. Im Objekt der Indikatorpufferdaten werden alle Eigenschaften unterstützt und die Methoden geben true zurück.

Wir diskutierten die gesamte Struktur der Bibliotheksobjekte im ersten Artikel. Hier werden wir kurz auf die Implementierung der übrigen Klassenmethoden eingehen.

Alle zusätzlichen Methoden zum Setzen und Zurückgeben von Objekteigenschaften dienen nur der zusätzlichen Bequemlichkeit beim Schreiben eines Programms; sie duplizieren lediglich die Aktionen von Setzmethoden und Empfang von Objekteigenschaften — so dass der Bibliotheksbenutzer sich nicht an die Namen von Konstanten aus Enumerationen von Objekteigenschaften erinnern muss, sondern sich beim Setzen und Empfangen dieser Eigenschaften am Namen dieser zusätzlichen Methoden orientieren kann.

Die virtuelle Methode Compare() ist für den Vergleich zweier Objekte anhand der angegebenen Eigenschaft gedacht. Sie ist in der Basisobjektklasse CObject der Standardbibliothek definiert und sollte Null zurückgeben, wenn die Werte gleich sind und 1 oder -1, wenn einer der verglichenen Werte höher bzw. niedriger ist. Die wird in der Methode Search() der Standardbibliothek zum Suchen und Sortieren verwendet. Die Methode sollte in den abgeleiteten Klassen neu definiert werden:

//+-------------------------------------------------------------------+
//| Compare CDataInd objects with each other by the specified property|
//+-------------------------------------------------------------------+
int CDataInd::Compare(const CObject *node,const int mode=0) const
  {
   const CDataInd *obj_compared=node;
//--- compare integer properties of two objects
   if(mode<IND_DATA_PROP_INTEGER_TOTAL)
     {
      long value_compared=obj_compared.GetProperty((ENUM_IND_DATA_PROP_INTEGER)mode);
      long value_current=this.GetProperty((ENUM_IND_DATA_PROP_INTEGER)mode);
      return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0);
     }
//--- compare real properties of two objects
   else if(mode<IND_DATA_PROP_DOUBLE_TOTAL+IND_DATA_PROP_INTEGER_TOTAL)
     {
      double value_compared=obj_compared.GetProperty((ENUM_IND_DATA_PROP_DOUBLE)mode);
      double value_current=this.GetProperty((ENUM_IND_DATA_PROP_DOUBLE)mode);
      return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0);
     }
//--- compare string properties of two objects
   else if(mode<IND_DATA_PROP_DOUBLE_TOTAL+IND_DATA_PROP_INTEGER_TOTAL+IND_DATA_PROP_STRING_TOTAL)
     {
      string value_compared=obj_compared.GetProperty((ENUM_IND_DATA_PROP_STRING)mode);
      string value_current=this.GetProperty((ENUM_IND_DATA_PROP_STRING)mode);
      return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0);
     }
   return 0;
  }
//+------------------------------------------------------------------+

Die Methode zur Ermittlung zweier identischer Objekte von Indikatorpufferdaten dient dem Vergleich zweier Datenobjekte und liefert true nur dann, wenn alle Felder der beiden verglichenen Objekte gleich sind:

//+------------------------------------------------------------------+
//|Compare CDataInd objects with each other by all properties        |
//+------------------------------------------------------------------+
bool CDataInd::IsEqual(CDataInd *compared_obj) const
  {
   int beg=0, end=BAR_PROP_INTEGER_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_IND_DATA_PROP_INTEGER prop=(ENUM_IND_DATA_PROP_INTEGER)i;
      if(this.GetProperty(prop)!=compared_obj.GetProperty(prop)) return false; 
     }
   beg=end; end+=IND_DATA_PROP_DOUBLE_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_IND_DATA_PROP_DOUBLE prop=(ENUM_IND_DATA_PROP_DOUBLE)i;
      if(this.GetProperty(prop)!=compared_obj.GetProperty(prop)) return false; 
     }
   beg=end; end+=IND_DATA_PROP_STRING_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_IND_DATA_PROP_STRING prop=(ENUM_IND_DATA_PROP_STRING)i;
      if(this.GetProperty(prop)!=compared_obj.GetProperty(prop)) return false; 
     }
   return true;
  }
//+------------------------------------------------------------------+

Die Methode zur Platzierung eines Symbols, Zeitrahmens und Datenobjektindex in der Zeitreihe:

//+------------------------------------------------------------------+
//| Set symbol, timeframe and object bar start time                  |
//+------------------------------------------------------------------+
void CDataInd::SetSymbolPeriod(const string symbol,const ENUM_TIMEFRAMES timeframe,const datetime time)
  {
   this.SetProperty(IND_DATA_PROP_TIME,time);
   this.SetProperty(IND_DATA_PROP_SYMBOL,symbol);
   this.SetProperty(IND_DATA_PROP_PERIOD,timeframe);
  }
//+------------------------------------------------------------------+

Die Methode, die Beschreibungen aller Objekteigenschaften im Journal ausdruckt:

//+------------------------------------------------------------------+
//| Display object properties in the journal                         |
//+------------------------------------------------------------------+
void CDataInd::Print(const bool full_prop=false)
  {
   ::Print("============= ",CMessage::Text(MSG_LIB_PARAMS_LIST_BEG)," (",this.IndicatorShortName(),") =============");
   int beg=0, end=IND_DATA_PROP_INTEGER_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_IND_DATA_PROP_INTEGER prop=(ENUM_IND_DATA_PROP_INTEGER)i;
      if(!full_prop && !this.SupportProperty(prop)) continue;
      ::Print(this.GetPropertyDescription(prop));
     }
   ::Print("------");
   beg=end; end+=IND_DATA_PROP_DOUBLE_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_IND_DATA_PROP_DOUBLE prop=(ENUM_IND_DATA_PROP_DOUBLE)i;
      if(!full_prop && !this.SupportProperty(prop)) continue;
      ::Print(this.GetPropertyDescription(prop));
     }
   ::Print("------");
   beg=end; end+=IND_DATA_PROP_STRING_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_IND_DATA_PROP_STRING prop=(ENUM_IND_DATA_PROP_STRING)i;
      if(!full_prop && !this.SupportProperty(prop)) continue;
      ::Print(this.GetPropertyDescription(prop));
     }
   ::Print("============= ",CMessage::Text(MSG_LIB_PARAMS_LIST_END)," (",this.IndicatorShortName(),") =============\n");
  }
//+------------------------------------------------------------------+

Die Beschreibung jeder nächsten Eigenschaft wird durch Objekteigenschaften-Arrays in drei Schleifen angezeigt. Wenn die Eigenschaft nicht unterstützt wird, wird sie nicht im Journal ausgedruckt, wenn der Eingabeparameter der Methode full_prop false ist (standardmäßig).

Virtuelle Methode, die die kurze Objektbeschreibung im Journal ausdruckt:

//+------------------------------------------------------------------+
//| Display a short description of the object in the journal         |
//+------------------------------------------------------------------+
void CDataInd::PrintShort(void)
  {
   ::Print
     (
      this.IndicatorShortName(),
      " [",CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_BUFFER)," ",this.BufferNum(),
      ", ",CMessage::Text(MSG_SYM_STATUS_INDEX)," ",this.Index(this.Timeframe()),"]"
     );
  }
//+------------------------------------------------------------------+

Die Methode zeigt eine Standard-Indikatorpufferdatenbeschreibung im folgenden Format an:

AMA(EURUSD,H1) [Buffer 0, Index 0]

für nutzerdefinierte Indikatoren:

Examples\Custom Moving Average.ex5(EURUSD,H1) [Buffer 0, Index 1]

Der Kurzname des Indikators, der in der Datenbeschreibung des Puffers enthalten ist, kann mit der Methode SetIndicatorShortname() geändert werden. Die Methode kann in Nachfolgeobjekten geändert werden, um andere Beschreibungen des Datenobjekts anzuzeigen, die den im Nachfolgeobjekt implementierten Daten entsprechen.

Die Methoden geben Beschreibungen der ganzzahliegn, reellen und Text-Eigenschaften des Objekts zurück:

//+------------------------------------------------------------------+
//| Return description of object's integer property                  |
//+------------------------------------------------------------------+
string CDataInd::GetPropertyDescription(ENUM_IND_DATA_PROP_INTEGER property)
  {
   return
     (
      property==IND_DATA_PROP_TIME           ?  CMessage::Text(MSG_LIB_TEXT_BAR_TIME)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::TimeToString(this.GetProperty(property),TIME_DATE|TIME_MINUTES|TIME_SECONDS)
         )  :
      property==IND_DATA_PROP_PERIOD         ?  CMessage::Text(MSG_LIB_TEXT_IND_TEXT_TIMEFRAME)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+this.m_period_description
         )  :
      property==IND_DATA_PROP_INDICATOR_TYPE ?  CMessage::Text(MSG_LIB_TEXT_IND_TEXT_TYPE)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+this.IndicatorTypeDescription()
         )  :
      property==IND_DATA_PROP_IND_BUFFER_NUM ?  CMessage::Text(MSG_LIB_TEXT_IND_DATA_IND_BUFFER_NUM)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==IND_DATA_PROP_IND_ID         ?  CMessage::Text(MSG_LIB_TEXT_IND_TEXT_ID)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      ""
     );
  }
//+------------------------------------------------------------------+
//| Return description of object's real property                     |
//+------------------------------------------------------------------+
string CDataInd::GetPropertyDescription(ENUM_IND_DATA_PROP_DOUBLE property)
  {
   int dg=(this.m_digits>0 ? this.m_digits : 1);
   return
     (
      property==IND_DATA_PROP_BUFFER_VALUE   ?  CMessage::Text(MSG_LIB_TEXT_IND_DATA_BUFFER_VALUE)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::DoubleToString(this.GetProperty(property),dg)
         )  :
      ""
     );
  }
//+------------------------------------------------------------------+
//| Return description of object's string property                   |
//+------------------------------------------------------------------+
string CDataInd::GetPropertyDescription(ENUM_IND_DATA_PROP_STRING property)
  {
   return
     (
      property==IND_DATA_PROP_SYMBOL         ? CMessage::Text(MSG_LIB_TEXT_IND_TEXT_SYMBOL)+": \""+this.GetProperty(property)+"\""     : 
      property==IND_DATA_PROP_IND_NAME       ? CMessage::Text(MSG_LIB_TEXT_IND_TEXT_NAME)+": \""+this.GetProperty(property)+"\""       : 
      property==IND_DATA_PROP_IND_SHORTNAME  ? CMessage::Text(MSG_LIB_TEXT_IND_TEXT_SHORTNAME)+": \""+this.GetProperty(property)+"\""  : 
      ""
     );
  }
//+------------------------------------------------------------------+

Die Methoden erhalten entsprechende Eigenschaften und abhängig von deren Wert ihre Textbeschreibungen, die in der oben eingefügten Datei Daten.mqh eingestellt sind, zurück.

Die Klasse verfügt über zwei Konstruktoren.

Der erste Konstruktor ist voreingestellt und erzeugt ein leeres Datenobjekt, das nach der Erzeugung mit allen notwendigen Daten gefüllt werden muss.

Der zweite Konstruktor ist parametrisch; er erhält die notwendigen Daten zur Erzeugung eines Objekts mit den angegebenen Grundeigenschaften:

//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CDataInd::CDataInd(const ENUM_INDICATOR ind_type,
                   const int ind_id,
                   const int buffer_num,
                   const string symbol,
                   const ENUM_TIMEFRAMES timeframe,
                   const datetime time)
  {
   this.m_type=COLLECTION_INDICATORS_ID;
   this.m_digits=(int)::SymbolInfoInteger(symbol,SYMBOL_DIGITS)+1;
   this.m_period_description=TimeframeDescription(timeframe);
   this.SetSymbolPeriod(symbol,timeframe,time);
   this.SetIndicatorType(ind_type);
   this.SetBufferNum(buffer_num);
   this.SetIndicatorID(ind_id);
  }
//+------------------------------------------------------------------+

Geben Sie als Erstes für das Objekt an: Indikator-Typ, welche Pufferdaten durch das Objekt beschrieben werden, Indikator-ID - durch diese Eigenschaft können wir Pufferdaten eines früher erstellten Indikatorobjekts erhalten, für das die ID für seine schnelle Suche unter anderen erstellten Indikatorobjekten festgelegt ist. Indikatorpuffernummer, welche Daten durch das Objekt beschrieben werden, Symbol und Zeitrahmen, auf dem das Indikatorobjekt erstellt wurde, und Balkenstartzeit, welche Pufferdaten durch das erstellte Objekt beschrieben werden.

Neben dem Hinzufügen der oben genannten Parameter legt der Konstruktor eine Standardanzahl von Dezimalstellen für die Anzeige von Indikatorpufferwerten fest (Stellen des Symbols + 1 Stelle) und fügt die Beschreibung des Zeitrahmens zur Variablen m_period_description hinzu — eine Beschreibung kann einmalig beim Erstellen eines Objekts festgelegt werden. In die Variable m_type, die in der CObject-Elternklasse der Standardbibliothek deklariert ist, schreiben wir temporär die ID der Indikator-Kollektion. Im Weiteren werde ich beim Erstellen von Indikatorpuffer-Datensammlungen die ID dieser neuen Kollektion zu dieser Variablen hinzufügen.

Um nun Datenobjekte in ihrer Kollektion sortieren zu können (ich werde ab dem nächsten Artikel mit der Erstellung von Kollektionen beginnen), fügen wir in die Datei \MQL5\Include\DoEasy\Services\Select.mqh die Methoden der Arbeit mit dem neuen Objekt ein, um es auszuwählen und nach seinen Eigenschaften zu sortieren.

Fügen wir die soeben erstellte Klasse des Indikatorpuffer-Datenobjekts in die Datei ein:

//+------------------------------------------------------------------+
//|                                                       Select.mqh |
//|                        Copyright 2020, MetaQuotes Software Corp. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, MetaQuotes Software Corp."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include <Arrays\ArrayObj.mqh>
#include "..\Objects\Orders\Order.mqh"
#include "..\Objects\Events\Event.mqh"
#include "..\Objects\Accounts\Account.mqh"
#include "..\Objects\Symbols\Symbol.mqh"
#include "..\Objects\PendRequest\PendRequest.mqh"
#include "..\Objects\Series\SeriesDE.mqh"
#include "..\Objects\Indicators\Buffer.mqh"
#include "..\Objects\Indicators\IndicatorDE.mqh"
#include "..\Objects\Indicators\DataInd.mqh"
//+------------------------------------------------------------------+

Am Ende des Klassenkörpers deklarieren wir Arbeitsmethoden mit der gerade erstellten Klasse des Indikator-Datenobjekts:

//+------------------------------------------------------------------+
//| Methods of work with indicator data                              |
//+------------------------------------------------------------------+
   //--- Return the list of indicator data with one out of (1) integer, (2) real and (3) string properties meeting a specified criterion
   static CArrayObj *ByIndicatorDataProperty(CArrayObj *list_source,ENUM_IND_DATA_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode);
   static CArrayObj *ByIndicatorDataProperty(CArrayObj *list_source,ENUM_IND_DATA_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode);
   static CArrayObj *ByIndicatorDataProperty(CArrayObj *list_source,ENUM_IND_DATA_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode);
   //--- Return the indicator data index in the list with the maximum value of (1) integer, (2) real and (3) string property of data
   static int        FindIndDataMax(CArrayObj *list_source,ENUM_IND_DATA_PROP_INTEGER property);
   static int        FindIndDataMax(CArrayObj *list_source,ENUM_IND_DATA_PROP_DOUBLE property);
   static int        FindIndDataMax(CArrayObj *list_source,ENUM_IND_DATA_PROP_STRING property);
   //--- Return the indicator data index in the list with the minimum value of (1) integer, (2) real and (3) string property of data
   static int        FindIndDataMin(CArrayObj *list_source,ENUM_IND_DATA_PROP_INTEGER property);
   static int        FindIndDataMin(CArrayObj *list_source,ENUM_IND_DATA_PROP_DOUBLE property);
   static int        FindIndDataMin(CArrayObj *list_source,ENUM_IND_DATA_PROP_STRING property);
//---
  };
//+------------------------------------------------------------------+

Und implementieren alle deklarierten Methoden bis zum Dateiende:

//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Methods of work with indicator data lists                        |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Return the list of indicators data with one of integer           |
//| properties meeting the specified criterion                       |
//+------------------------------------------------------------------+
CArrayObj *CSelect::ByIndicatorDataProperty(CArrayObj *list_source,ENUM_IND_DATA_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode)
  {
   if(list_source==NULL) return NULL;
   CArrayObj *list=new CArrayObj();
   if(list==NULL) return NULL;
   list.FreeMode(false);
   ListStorage.Add(list);
   int total=list_source.Total();
   for(int i=0; i<total; i++)
     {
      CDataInd *obj=list_source.At(i);
      if(!obj.SupportProperty(property)) continue;
      long obj_prop=obj.GetProperty(property);
      if(CompareValues(obj_prop,value,mode)) list.Add(obj);
     }
   return list;
  }
//+------------------------------------------------------------------+
//| Return the list of indicators data with one of real              |
//| properties meeting the specified criterion                       |
//+------------------------------------------------------------------+
CArrayObj *CSelect::ByIndicatorDataProperty(CArrayObj *list_source,ENUM_IND_DATA_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode)
  {
   if(list_source==NULL) return NULL;
   CArrayObj *list=new CArrayObj();
   if(list==NULL) return NULL;
   list.FreeMode(false);
   ListStorage.Add(list);
   for(int i=0; i<list_source.Total(); i++)
     {
      CDataInd *obj=list_source.At(i);
      if(!obj.SupportProperty(property)) continue;
      double obj_prop=obj.GetProperty(property);
      if(CompareValues(obj_prop,value,mode)) list.Add(obj);
     }
   return list;
  }
//+------------------------------------------------------------------+
//| Return the list of indicators data with one of string            |
//| properties meeting the specified criterion                       |
//+------------------------------------------------------------------+
CArrayObj *CSelect::ByIndicatorDataProperty(CArrayObj *list_source,ENUM_IND_DATA_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode)
  {
   if(list_source==NULL) return NULL;
   CArrayObj *list=new CArrayObj();
   if(list==NULL) return NULL;
   list.FreeMode(false);
   ListStorage.Add(list);
   for(int i=0; i<list_source.Total(); i++)
     {
      CDataInd *obj=list_source.At(i);
      if(!obj.SupportProperty(property)) continue;
      string obj_prop=obj.GetProperty(property);
      if(CompareValues(obj_prop,value,mode)) list.Add(obj);
     }
   return list;
  }
//+------------------------------------------------------------------+
//| Return the indicator data index in the list                      |
//| with the maximum integer property value                          |
//+------------------------------------------------------------------+
int CSelect::FindIndDataMax(CArrayObj *list_source,ENUM_IND_DATA_PROP_INTEGER property)
  {
   if(list_source==NULL) return WRONG_VALUE;
   int index=0;
   CDataInd *max_obj=NULL;
   int total=list_source.Total();
   if(total==0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      CDataInd *obj=list_source.At(i);
      long obj1_prop=obj.GetProperty(property);
      max_obj=list_source.At(index);
      long obj2_prop=max_obj.GetProperty(property);
      if(CompareValues(obj1_prop,obj2_prop,MORE)) index=i;
     }
   return index;
  }
//+------------------------------------------------------------------+
//| Return the indicator data index in the list                      |
//| with the maximum real property value                             |
//+------------------------------------------------------------------+
int CSelect::FindIndDataMax(CArrayObj *list_source,ENUM_IND_DATA_PROP_DOUBLE property)
  {
   if(list_source==NULL) return WRONG_VALUE;
   int index=0;
   CDataInd *max_obj=NULL;
   int total=list_source.Total();
   if(total==0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      CDataInd *obj=list_source.At(i);
      double obj1_prop=obj.GetProperty(property);
      max_obj=list_source.At(index);
      double obj2_prop=max_obj.GetProperty(property);
      if(CompareValues(obj1_prop,obj2_prop,MORE)) index=i;
     }
   return index;
  }
//+------------------------------------------------------------------+
//| Return the indicator data index in the list                      |
//| with the maximum string property value                           |
//+------------------------------------------------------------------+
int CSelect::FindIndDataMax(CArrayObj *list_source,ENUM_IND_DATA_PROP_STRING property)
  {
   if(list_source==NULL) return WRONG_VALUE;
   int index=0;
   CDataInd *max_obj=NULL;
   int total=list_source.Total();
   if(total==0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      CDataInd *obj=list_source.At(i);
      string obj1_prop=obj.GetProperty(property);
      max_obj=list_source.At(index);
      string obj2_prop=max_obj.GetProperty(property);
      if(CompareValues(obj1_prop,obj2_prop,MORE)) index=i;
     }
   return index;
  }
//+------------------------------------------------------------------+
//| Return the indicator data index in the list                      |
//| with the minimum integer property value                          |
//+------------------------------------------------------------------+
int CSelect::FindIndDataMin(CArrayObj* list_source,ENUM_IND_DATA_PROP_INTEGER property)
  {
   int index=0;
   CDataInd *min_obj=NULL;
   int total=list_source.Total();
   if(total==0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      CDataInd *obj=list_source.At(i);
      long obj1_prop=obj.GetProperty(property);
      min_obj=list_source.At(index);
      long obj2_prop=min_obj.GetProperty(property);
      if(CompareValues(obj1_prop,obj2_prop,LESS)) index=i;
     }
   return index;
  }
//+------------------------------------------------------------------+
//| Return the indicator data index in the list                      |
//| with the minimum real property value                             |
//+------------------------------------------------------------------+
int CSelect::FindIndDataMin(CArrayObj* list_source,ENUM_IND_DATA_PROP_DOUBLE property)
  {
   int index=0;
   CDataInd *min_obj=NULL;
   int total=list_source.Total();
   if(total== 0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      CDataInd *obj=list_source.At(i);
      double obj1_prop=obj.GetProperty(property);
      min_obj=list_source.At(index);
      double obj2_prop=min_obj.GetProperty(property);
      if(CompareValues(obj1_prop,obj2_prop,LESS)) index=i;
     }
   return index;
  }
//+------------------------------------------------------------------+
//| Return the indicator data index in the list                      |
//| with the minimum string property value                           |
//+------------------------------------------------------------------+
int CSelect::FindIndDataMin(CArrayObj* list_source,ENUM_IND_DATA_PROP_STRING property)
  {
   int index=0;
   CDataInd *min_obj=NULL;
   int total=list_source.Total();
   if(total==0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      CDataInd *obj=list_source.At(i);
      string obj1_prop=obj.GetProperty(property);
      min_obj=list_source.At(index);
      string obj2_prop=min_obj.GetProperty(property);
      if(CompareValues(obj1_prop,obj2_prop,LESS)) index=i;
     }
   return index;
  }
//+------------------------------------------------------------------+

Die Funktionsweise dieser Methoden wurde im dritten Artikel der Beschreibung der Bibliothekserstellung im Detail analysiert.

Damit ist das neue Indikatorpuffer-Datenobjekt erstellt und kann nun getestet werden.


Tests

Um den Test durchzuführen, verwenden wir den EA aus dem vorigen Artikel und speichern ihn in dem neuen Ordner
\MQL5\Experts\TestDoEasy\Part57\ unter dem neuen Namen TestDoEasyPart57.mq5.

Im vorherigen EA habe ich vier Indikatorobjekte erstellt: zwei Standard- und zwei nutzerdefinierte. Die identischen Indikatoren unterschieden sich nur durch andere Parameter voneinander. Hier erstellen wir die gleichen vier Indikatoren, aber um ihre Daten anzuzeigen, erstellen wir für jeden von ihnen zwei Pufferdatenobjekte - für den aktuellen (Null) und den vorherigen (ersten) Balken der Zeitreihe. Die Daten aller Objekte werden im Kommentar auf dem Symbol-Chart angezeigt.

Bis die Kollektionen der Indikatorpuffer erstellt sind, organisieren wir den Zugriff auf die erstellte Klasse direkt aus dem EA.
Um dies zu tun, binden wir diese Klasse in die EA-Datei ein:

//+------------------------------------------------------------------+
//|                                             TestDoEasyPart57.mq5 |
//|                        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"
//--- includes
#include <DoEasy\Engine.mqh>
#include <DoEasy\Objects\Indicators\DataInd.mqh>
//--- enums

Fügen wir an der gleichen Stelle im Bereich der globalen Variablen des Programms Zeiger auf die Indikator-Datenobjekte ein:

//--- Arrays of custom indicator parameters
MqlParam       param_ma1[];
MqlParam       param_ma2[];
//--- Pointers to indicator data objects
CDataInd      *data_ma1_0=NULL;
CDataInd      *data_ma1_1=NULL;
CDataInd      *data_ma2_0=NULL;
CDataInd      *data_ma2_1=NULL;
CDataInd      *data_ama1_0=NULL;
CDataInd      *data_ama1_1=NULL;
CDataInd      *data_ama2_0=NULL;
CDataInd      *data_ama2_1=NULL;
//+------------------------------------------------------------------+

Wenn ich neue Datenobjekte anlege, speichere ich in diese Zeiger auf die neu angelegten Objekte in diesen Variablen, damit später auf sie zugegriffen werden kann.

Da alle Objekte mit dem Operator new erzeugt werden, müssen sie alle einzeln entfernt werden. Ich werde das in OnDeinit() des EAs machen (nach der Erstellung der Kollektion von Indikatorpufferdaten wird es keine Notwendigkeit in diesen Aktionen in EA geben):

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- Remove EA graphical objects by an object name prefix
   ObjectsDeleteAll(0,prefix);
   Comment("");
//--- Remove created data objects of MA indicators
   if(CheckPointer(data_ma1_0)==POINTER_DYNAMIC)
      delete data_ma1_0;
   if(CheckPointer(data_ma1_1)==POINTER_DYNAMIC)
      delete data_ma1_1;
   if(CheckPointer(data_ma2_0)==POINTER_DYNAMIC)
      delete data_ma2_0;
   if(CheckPointer(data_ma2_1)==POINTER_DYNAMIC)
      delete data_ma2_1;
//--- Remove created data objects of AMA indicators
   if(CheckPointer(data_ama1_0)==POINTER_DYNAMIC)
      delete data_ama1_0;
   if(CheckPointer(data_ama1_1)==POINTER_DYNAMIC)
      delete data_ama1_1;
   if(CheckPointer(data_ama2_0)==POINTER_DYNAMIC)
      delete data_ama2_0;
   if(CheckPointer(data_ama2_1)==POINTER_DYNAMIC)
      delete data_ama2_1;
//--- Deinitialize library
   engine.OnDeinit();
  }
//+------------------------------------------------------------------+

In OnTick() erzeugen wir neue Objekte (sofern sie nicht schon erzeugt sind), füllen sie mit allen notwendigen Daten und Werten und zeigen die Beschreibungen der Objekte im Journal an; dagegen zeigen wir im Chart die Pufferwerte der Indikatoren an, die durch die Objekte beschrieben werden:

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

//--- If work in tester
   if(MQLInfoInteger(MQL_TESTER))
     {
      engine.OnTimer(rates_data);   // Work in timer
      PressButtonsControl();        // Button press control
      engine.EventsHandling();      // Work with events
     }
//--- Get custom indicator objects
   CIndicatorDE *ma1=engine.GetIndicatorsCollection().GetIndByID(MA1);
   CIndicatorDE *ma2=engine.GetIndicatorsCollection().GetIndByID(MA2);
   CIndicatorDE *ama1=engine.GetIndicatorsCollection().GetIndByID(AMA1);
   CIndicatorDE *ama2=engine.GetIndicatorsCollection().GetIndByID(AMA2);
   
//--- Write bar start time 0 and 1 to specify it in properties of further created objects
   datetime time0=iTime(ma1.Symbol(),ma1.Timeframe(),0);
   datetime time1=iTime(ma1.Symbol(),ma1.Timeframe(),1);
   if(time0==0 || time1==0)
      return;
      
//--- Create data objects of МА1 and МА2 for bars 0 and 1 (unless the objects are created)
   if(data_ma1_0==NULL) data_ma1_0=new CDataInd(ma1.TypeIndicator(),ma1.ID(),0,ma1.Symbol(),ma1.Timeframe(),time0);
   if(data_ma1_1==NULL) data_ma1_1=new CDataInd(ma1.TypeIndicator(),ma1.ID(),0,ma1.Symbol(),ma1.Timeframe(),time1);
   if(data_ma2_0==NULL) data_ma2_0=new CDataInd(ma2.TypeIndicator(),ma2.ID(),0,ma2.Symbol(),ma2.Timeframe(),time0);
   if(data_ma2_1==NULL) data_ma2_1=new CDataInd(ma2.TypeIndicator(),ma2.ID(),0,ma2.Symbol(),ma2.Timeframe(),time1);
   if(data_ma1_0==NULL || data_ma1_1==NULL || data_ma2_0==NULL || data_ma2_1==NULL) return;
   
//--- Set parameters of data object of indicator МА1, bar 0
//--- and add indicator buffer data to the object
   data_ma1_0.SetIndicatorType(ma1.TypeIndicator());
   data_ma1_0.SetIndicatorName(ma1.Name());
   data_ma1_0.SetIndicatorShortname(ma1.ShortName());
   data_ma1_0.SetBufferValue(ma1.GetDataBuffer(0,time0));
//--- Set parameters of data object of indicator МА1, bar 1
//--- and add indicator buffer data to the object
   data_ma1_1.SetIndicatorType(ma1.TypeIndicator());
   data_ma1_1.SetIndicatorName(ma1.Name());
   data_ma1_1.SetIndicatorShortname(ma1.ShortName());
   data_ma1_1.SetBufferValue(ma1.GetDataBuffer(0,time1));
//--- Set parameters of data object of indicator МА2, bar 0
//--- and add indicator buffer data to the object
   data_ma2_0.SetIndicatorType(ma2.TypeIndicator());
   data_ma2_0.SetIndicatorName(ma2.Name());
   data_ma2_0.SetIndicatorShortname(ma2.ShortName());
   data_ma2_0.SetBufferValue(ma2.GetDataBuffer(0,time0));
//--- Set parameters of data object of indicator МА2, bar 1
//--- and add indicator buffer data to the object
   data_ma2_1.SetIndicatorType(ma2.TypeIndicator());
   data_ma2_1.SetIndicatorName(ma2.Name());
   data_ma2_1.SetIndicatorShortname(ma2.ShortName());
   data_ma2_1.SetBufferValue(ma2.GetDataBuffer(0,time1));
   
//--- Create data objects of АМА1 and АМА2 for bars 0 and 1 (unless the objects are already created)
   if(data_ama1_0==NULL) data_ama1_0=new CDataInd(ama1.TypeIndicator(),ama1.ID(),0,ama1.Symbol(),ama1.Timeframe(),time0);
   if(data_ama1_1==NULL) data_ama1_1=new CDataInd(ama1.TypeIndicator(),ama1.ID(),0,ama1.Symbol(),ama1.Timeframe(),time1);
   if(data_ama2_0==NULL) data_ama2_0=new CDataInd(ama2.TypeIndicator(),ama2.ID(),0,ama2.Symbol(),ama2.Timeframe(),time0);
   if(data_ama2_1==NULL) data_ama2_1=new CDataInd(ama2.TypeIndicator(),ama2.ID(),0,ama2.Symbol(),ama2.Timeframe(),time1);
   if(data_ama1_0==NULL || data_ama1_1==NULL || data_ama2_0==NULL || data_ama2_1==NULL) return;
//--- Set parameters of data object of indicator АМА1, bar 0
//--- and add indicator buffer data to the object
   data_ama1_0.SetIndicatorType(ama1.TypeIndicator());
   data_ama1_0.SetIndicatorName(ama1.Name());
   data_ama1_0.SetIndicatorShortname(ama1.ShortName());
   data_ama1_0.SetBufferValue(ama1.GetDataBuffer(0,time0));
//--- Set parameters of data object of indicator АМА1, bar 1
//--- and add indicator buffer data to the object
   data_ama1_1.SetIndicatorType(ama1.TypeIndicator());
   data_ama1_1.SetIndicatorName(ama1.Name());
   data_ama1_1.SetIndicatorShortname(ama1.ShortName());
   data_ama1_1.SetBufferValue(ama1.GetDataBuffer(0,time1));
//--- Set parameters of data object of indicator АМА2, bar 0
//--- and add indicator buffer data to the object
   data_ama2_0.SetIndicatorType(ama2.TypeIndicator());
   data_ama2_0.SetIndicatorName(ama2.Name());
   data_ama2_0.SetIndicatorShortname(ama2.ShortName());
   data_ama2_0.SetBufferValue(ama2.GetDataBuffer(0,time0));
//--- Set parameters of data object of indicator АМА2, bar 1
//--- and add indicator buffer data to the object
   data_ama2_1.SetIndicatorType(ama2.TypeIndicator());
   data_ama2_1.SetIndicatorName(ama2.Name());
   data_ama2_1.SetIndicatorShortname(ama2.ShortName());
   data_ama2_1.SetBufferValue(ama2.GetDataBuffer(0,time1));

//--- During the first launch, print full and short data of created indicator data objects
   static bool first_start=true;
   if(first_start)
     {
      //--- Full data of buffers МА1 and МА2
      data_ma1_0.Print();
      data_ma1_1.Print();
      data_ma2_0.Print();
      data_ma2_1.Print();
      //--- Full data of buffers АМА1 and АМА2
      data_ama1_0.Print();
      data_ama1_1.Print();
      data_ama2_0.Print();
      data_ama2_1.Print();
      //--- Short data of buffers МА1 and МА2
      data_ma1_0.PrintShort();
      data_ma1_1.PrintShort();
      data_ma2_0.PrintShort();
      data_ma2_1.PrintShort();
      //--- Short data of buffers АМА1 and АМА2
      data_ama1_0.PrintShort();
      data_ama1_1.PrintShort();
      data_ama2_0.PrintShort();
      data_ama2_1.PrintShort();
      //---
      first_start=false;
     }
//--- Display the values of indicator buffers to comment on the chart from data objects
   Comment
     (
      "ma1(1)=",DoubleToString(data_ma1_1.PriceValue(),6),", ma1(0)=",DoubleToString(data_ma1_0.PriceValue(),data_ma1_0.Digits()),", ",
      "ma2(1)=",DoubleToString(data_ma2_1.PriceValue(),6),", ma2(0)=",DoubleToString(data_ma2_0.PriceValue(),data_ma2_0.Digits()),"\n",
      "ama1(1)=",DoubleToString(data_ama1_1.PriceValue(),6),", ama1(0)=",DoubleToString(data_ama1_0.PriceValue(),data_ama1_0.Digits()),", ",
      "ama2(1)=",DoubleToString(data_ama2_1.PriceValue(),6),", ama2(0)=",DoubleToString(data_ama2_0.PriceValue(),data_ama2_0.Digits())
     );
   
//--- If the trailing flag is set
   if(trailing_on)
     {
      TrailingPositions();          // Trailing positions
      TrailingOrders();             // Trailing of pending orders
     }
  }
//+------------------------------------------------------------------+

Darüber hinaus werden alle Aktionen zum Erstellen von Objekten und zum Füllen ihrer Eigenschaften mit Werten in der Sammelklasse der Indikatorpufferdaten durchgeführt. In der Zwischenzeit muss die Funktion der heute erstellten Objekte einfach überprüft werden.

Kompilieren Sie den EA und starten Sie ihn im Symbol-Chart, wobei in den Einstellungen vorläufig festgelegt wurde, dass nur das aktuelle Symbol und die aktuelle Periode des Charts verwendet werden. Das Journal wird die Daten aller erstellten Indikatorobjekte und Datenobjekte anzeigen:

Account 8550475: Artyom Trishkin (MetaQuotes Software Corp.) 10425.23 USD, 1:100, Hedge, Demo account MetaTrader 5
--- Initializing the "DoEasy" library ---
Work only with the current symbol: "EURUSD"
Work only with the current Period: H1
Symbol time series EURUSD: 
- Timeseries "EURUSD" H1: Required: 1000, Actual: 1000, Created: 1000, On server: 6351
Library initialization time: 00:00:00.000
============= The beginning of the parameter list: "Custom indicator" =============
Indicator status: Custom indicator
Indicator type: CUSTOM
Indicator timeframe: H1
Indicator handle: 10
Indicator group: Trend indicator
Indicator ID: 1
------
Empty value for plotting, for which there is no drawing: EMPTY_VALUE
------
Indicator symbol: EURUSD
Indicator name: "Examples\Custom Moving Average.ex5"
Indicator shortname: "Examples\Custom Moving Average.ex5(EURUSD,H1)"
 --- Indicator parameters --- 
 - [1] Type int: 13
 - [2] Type int: 0
 - [3] Type int: 0
================== End of the parameter list: "Custom indicator" ==================

============= The beginning of the parameter list: "Custom indicator" =============
Indicator status: Custom indicator
Indicator type: CUSTOM
Indicator timeframe: H1
Indicator handle: 11
Indicator group: Trend indicator
Indicator ID: 2
------
Empty value for plotting, for which there is no drawing: EMPTY_VALUE
------
Indicator symbol: EURUSD
Indicator name: "Examples\Custom Moving Average.ex5"
Indicator shortname: "Examples\Custom Moving Average.ex5(EURUSD,H1)"
 --- Indicator parameters --- 
 - [1] Type int: 13
 - [2] Type int: 0
 - [3] Type int: 0
 - [4] Type int: 2
================== End of the parameter list: "Custom indicator" ==================
 
============= The beginning of the parameter list: "Standard indicator" =============
Indicator status: Standard indicator
Indicator type: AMA
Indicator timeframe: H1
Indicator handle: 12
Indicator group: Trend indicator
Indicator ID: 3
------
Empty value for plotting, for which there is no drawing: EMPTY_VALUE
------
Indicator symbol: EURUSD
Indicator name: "Adaptive Moving Average"
Indicator shortname: "AMA(EURUSD,H1)"
 --- Indicator parameters --- 
 - Averaging period: 9
 - Fast MA period: 2
 - Slow MA period: 30
 - Horizontal shift of the indicator: 0
 - Price type or handle: CLOSE
================== End of the parameter list: "Standard indicator" ==================

============= The beginning of the parameter list: "Standard indicator" =============
Indicator status: Standard indicator
Indicator type: AMA
Indicator timeframe: H1
Indicator handle: 13
Indicator group: Trend indicator
Indicator ID: 4
------
Empty value for plotting, for which there is no drawing: EMPTY_VALUE
------
Indicator symbol: EURUSD
Indicator name: "Adaptive Moving Average"
Indicator shortname: "AMA(EURUSD,H1)"
 --- Indicator parameters --- 
 - Averaging period: 14
 - Fast MA period: 2
 - Slow MA period: 30
 - Horizontal shift of the indicator: 0
 - Price type or handle: CLOSE
================== End of the parameter list: "Standard indicator" ==================

Custom indicator Examples\Custom Moving Average.ex5 EURUSD H1 [handle 10, id #1]
Custom indicator Examples\Custom Moving Average.ex5 EURUSD H1 [handle 11, id #2]
Standard indicator Adaptive Moving Average EURUSD H1 [handle 12, id #3]
Standard indicator Adaptive Moving Average EURUSD H1 [handle 13, id #4]

============= The beginning of the parameter list (Examples\Custom Moving Average.ex5(EURUSD,H1)) =============
Period start time: 2020.11.18 10:00:00
Indicator timeframe: H1
Indicator type: CUSTOM
Indicator buffer number: 0
Indicator ID: 1
------
Indicator buffer value: 1.186694
------
Indicator symbol: "EURUSD"
Indicator name: "Examples\Custom Moving Average.ex5"
Indicator shortname: "Examples\Custom Moving Average.ex5(EURUSD,H1)"
============= End of the parameter list (Examples\Custom Moving Average.ex5(EURUSD,H1)) =============

============= The beginning of the parameter list (Examples\Custom Moving Average.ex5(EURUSD,H1)) =============
Period start time: 2020.11.18 09:00:00
Indicator timeframe: H1
Indicator type: CUSTOM
Indicator buffer number: 0
Indicator ID: 1
------
Indicator buffer value: 1.186535
------
Indicator symbol: "EURUSD"
Indicator name: "Examples\Custom Moving Average.ex5"
Indicator shortname: "Examples\Custom Moving Average.ex5(EURUSD,H1)"
============= End of the parameter list (Examples\Custom Moving Average.ex5(EURUSD,H1)) =============

============= The beginning of the parameter list (Examples\Custom Moving Average.ex5(EURUSD,H1)) =============
Period start time: 2020.11.18 10:00:00
Indicator timeframe: H1
Indicator type: CUSTOM
Indicator buffer number: 0
Indicator ID: 2
------
Indicator buffer value: 1.186552
------
Indicator symbol: "EURUSD"
Indicator name: "Examples\Custom Moving Average.ex5"
Indicator shortname: "Examples\Custom Moving Average.ex5(EURUSD,H1)"
============= End of the parameter list (Examples\Custom Moving Average.ex5(EURUSD,H1)) =============

============= The beginning of the parameter list (Examples\Custom Moving Average.ex5(EURUSD,H1)) =============
Period start time: 2020.11.18 09:00:00
Indicator timeframe: H1
Indicator type: CUSTOM
Indicator buffer number: 0
Indicator ID: 2
------
Indicator buffer value: 1.186438
------
Indicator symbol: "EURUSD"
Indicator name: "Examples\Custom Moving Average.ex5"
Indicator shortname: "Examples\Custom Moving Average.ex5(EURUSD,H1)"
============= End of the parameter list (Examples\Custom Moving Average.ex5(EURUSD,H1)) =============

============= The beginning of the parameter list (AMA(EURUSD,H1)) =============
Period start time: 2020.11.18 10:00:00
Indicator timeframe: H1
Indicator type: AMA
Indicator buffer number: 0
Indicator ID: 3
------
Indicator buffer value: 1.186992
------
Indicator symbol: "EURUSD"
Indicator name: "Adaptive Moving Average"
Indicator shortname: "AMA(EURUSD,H1)"
============= End of the parameter list (AMA(EURUSD,H1)) =============

============= The beginning of the parameter list (AMA(EURUSD,H1)) =============
Period start time: 2020.11.18 09:00:00
Indicator timeframe: H1
Indicator type: AMA
Indicator buffer number: 0
Indicator ID: 3
------
Indicator buffer value: 1.186725
------
Indicator symbol: "EURUSD"
Indicator name: "Adaptive Moving Average"
Indicator shortname: "AMA(EURUSD,H1)"
============= End of the parameter list (AMA(EURUSD,H1)) =============

============= The beginning of the parameter list (AMA(EURUSD,H1)) =============
Period start time: 2020.11.18 10:00:00
Indicator timeframe: H1
Indicator type: AMA
Indicator buffer number: 0
Indicator ID: 4
------
Indicator buffer value: 1.186548
------
Indicator symbol: "EURUSD"
Indicator name: "Adaptive Moving Average"
Indicator shortname: "AMA(EURUSD,H1)"
============= End of the parameter list (AMA(EURUSD,H1)) =============

============= The beginning of the parameter list (AMA(EURUSD,H1)) =============
Period start time: 2020.11.18 09:00:00
Indicator timeframe: H1
Indicator type: AMA
Indicator buffer number: 0
Indicator ID: 4
------
Indicator buffer value: 1.186403
------
Indicator symbol: "EURUSD"
Indicator name: "Adaptive Moving Average"
Indicator shortname: "AMA(EURUSD,H1)"
============= End of the parameter list (AMA(EURUSD,H1)) =============

Examples\Custom Moving Average.ex5(EURUSD,H1) [Buffer 0, Index 0]
Examples\Custom Moving Average.ex5(EURUSD,H1) [Buffer 0, Index 1]
Examples\Custom Moving Average.ex5(EURUSD,H1) [Buffer 0, Index 0]
Examples\Custom Moving Average.ex5(EURUSD,H1) [Buffer 0, Index 1]
AMA(EURUSD,H1) [Buffer 0, Index 0]
AMA(EURUSD,H1) [Buffer 0, Index 1]
AMA(EURUSD,H1) [Buffer 0, Index 0]
AMA(EURUSD,H1) [Buffer 0, Index 1]

während das Chart (als Kommentar) die Daten anzeigt, die den Daten in den Indikatorpuffern des ersten und des nullten Balkens entsprechen:


Was kommt als Nächstes?

Im nächsten Artikel werden wir die Kollektionsklasse der Indikatorpufferdaten erstellen.

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.
Bitte beachten Sie, dass sich die Kollektionsklasse der Indikatoren derzeit noch in der Entwicklung befindet, daher wird dringend davon abgeraten, sie in Ihren Programmen zu verwenden.
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