Zeitreihen in der Bibliothek DoEasy (Teil 53): Abstrakte Basisklasse der Indikatoren

17 Dezember 2020, 10:00
Artyom Trishkin
0
119

Inhaltsverzeichnis


Konzept

Bei der Entwicklung der DoEasy-Bibliothek ergab sich die Notwendigkeit, ein Indikator-Objekt zu erstellen. Das Vorhandensein eines solchen Objekts wird die bequeme Speicherung und Verwendung aller im Programm erstellten und verwendeten Indikatoren ermöglichen. Das Konzept der Konstruktion des Indikatorobjekts unterscheidet sich nicht von dem Konzept der Hauptbibliotheksobjekte, nämlich: abstraktes Basisobjekt und seine Nachkommen, die die Zugehörigkeit des Objekts durch seinen Status verdeutlichen (für Indikatoren - nutzerdefiniert und Standard). Über die Bildung solcher Objekte habe ich in den allerersten Artikeln erzählt.
Heute erstellen wir das Objekt eines abstrakten Basisindikators und prüfen die Ergebnisse seiner Erstellung. In den folgenden Artikeln werde ich Objekte von Standard- und nutzerdefinierten Indikatoren erstellen.

Zusätzlich zur Zugehörigkeit nach Status (standardmäßig und nutzerdefiniert) wird jedes der erstellten Indikatorobjekte nach Indikatortyp (Gruppe) zugehörig sein:

  • Trendindikatoren
  • Oszillatoren
  • Volumina
  • Pfeil-Indikatoren

So werden wir in der Lage sein, Indikatoren in Programmen nach Gruppen zu sortieren. Wir werden die Indikatoren von Bill Williams nicht in einer separaten Gruppe eingeben, weil jeder von ihnen seine Zugehörigkeit zu einer der angegebenen Gruppen hat. Daher halte ich es für unnötig, eine weitere separate Gruppe einzugeben, die Indikatoren aller oben aufgeführten Gruppen enthält.

Verbesserung der Bibliothek der Klasse

Fügen wir zunächst die benötigten Bibliothekstextmeldungen für Indikatorobjekte hinzu.
Wir fügen der Datei \MQL5\Include\DoEasy\Data.mqh die neuen Nachrichtenindizes hinzu:

//--- CBuffer
//--- removed for the sake of space
//--- ...
//--- ...
//--- ...
   MSG_LIB_TEXT_BUFFER_TEXT_STYLE_SOLID,              // Solid line
   MSG_LIB_TEXT_BUFFER_TEXT_STYLE_DASH,               // Dashed line
   MSG_LIB_TEXT_BUFFER_TEXT_STYLE_DOT,                // Dotted line
   MSG_LIB_TEXT_BUFFER_TEXT_STYLE_DASHDOT,            // Dot-dash line
   MSG_LIB_TEXT_BUFFER_TEXT_STYLE_DASHDOTDOT,         // Dash - two dots
   
//--- CIndicatorDE
   MSG_LIB_TEXT_IND_TEXT_STATUS,                      // Indicator status
   MSG_LIB_TEXT_IND_TEXT_STATUS_STANDART,             // Standard indicator
   MSG_LIB_TEXT_IND_TEXT_STATUS_CUSTOM,               // Custom indicator
   
   MSG_LIB_TEXT_IND_TEXT_TIMEFRAME,                   // Indicator timeframe
   MSG_LIB_TEXT_IND_TEXT_HANDLE,                      // Indicator handle

   MSG_LIB_TEXT_IND_TEXT_GROUP,                       // Indicator group
   MSG_LIB_TEXT_IND_TEXT_GROUP_TREND,                 // Trend indicator
   MSG_LIB_TEXT_IND_TEXT_GROUP_OSCILLATOR,            // Oscillator
   MSG_LIB_TEXT_IND_TEXT_GROUP_VOLUMES,               // Volumes
   MSG_LIB_TEXT_IND_TEXT_GROUP_ARROWS,                // Arrow indicator
   
   MSG_LIB_TEXT_IND_TEXT_EMPTY_VALUE,                 // Empty value for plotting where nothing will be drawn:
   MSG_LIB_TEXT_IND_TEXT_SYMBOL,                      // Indicator symbol
   MSG_LIB_TEXT_IND_TEXT_NAME,                        // Indicator name
   MSG_LIB_TEXT_IND_TEXT_SHORTNAME,                   // Indicator short name
   
  };
//+------------------------------------------------------------------+

... und weiter in der gleichen Datei - die Textmeldungen, die den neu hinzugefügten Indizes entsprechen:

   {"Solid line"},
   {"Broken line"},
   {"Dotted line"},
   {"Dash-dot line"},
   {"Dash - two points"},
   
   {"Indicator status"},
   {"Standard indicator"},
   {"Custom indicator"},
   {"Indicator timeframe"},
   {"Indicator handle"},
   {"Indicator group"},
   {"Trend indicator"},
   {"Solid lineOscillator"},
   {"Volumes"},
   {"Arrow indicator"},
   {"Empty value for plotting, for which there is no drawing"},
   {"Indicator symbol"},
   {"Indicator name"},
   {"Indicator shortname"},
   
  };
//+---------------------------------------------------------------------+

In der Datei E:\MetaQuotes\MetaTrader 5\MQL5\Include\DoEasy\Defines.mqh fügen wir für Bibliotheksobjekte die bereits zum Standard gewordenen Indikator-Objektparameter hinzu.

Da alle diese Objekte am Ende in der Kollektionsliste der Indikatorpuffer gespeichert werden, führen wir eine eigene ID für sie ein:

//--- Collection list IDs
#define COLLECTION_HISTORY_ID          (0x777A)                   // Historical collection list ID
#define COLLECTION_MARKET_ID           (0x777B)                   // Market collection list ID
#define COLLECTION_EVENTS_ID           (0x777C)                   // Event collection list ID
#define COLLECTION_ACCOUNT_ID          (0x777D)                   // Account collection list ID
#define COLLECTION_SYMBOLS_ID          (0x777E)                   // Symbol collection list ID
#define COLLECTION_SERIES_ID           (0x777F)                   // Timeseries collection list ID
#define COLLECTION_BUFFERS_ID          (0x7780)                   // Indicator buffer collection list ID
#define COLLECTION_INDICATORS_ID       (0x7781)                   // Indicator collection list ID

//--- Data parameters for file operations

Und am Ende der Listing-Datei fügen wir alle benötigten Enumerationen von Eigenschaften und Sortierkriterien für Indikatorobjekte in die Kollektionsliste ein:

//+------------------------------------------------------------------+
//| Data for working with indicators                                 |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Abstract indicator status                                        |
//+------------------------------------------------------------------+
enum ENUM_INDICATOR_STATUS
  {
   INDICATOR_STATUS_STANDART,                               // Standard indicator
   INDICATOR_STATUS_CUSTOM,                                 // Custom indicator
  };
//+------------------------------------------------------------------+
//| Indicator group                                                  |
//+------------------------------------------------------------------+
enum ENUM_INDICATOR_GROUP
  {
   INDICATOR_GROUP_TREND,                                   // Trend indicator
   INDICATOR_GROUP_OSCILLATOR,                              // Oscillator
   INDICATOR_GROUP_VOLUMES,                                 // Volumes
   INDICATOR_GROUP_ARROWS,                                  // Arrow indicator
  };
//+------------------------------------------------------------------+
//| Indicator integer properties                                     |
//+------------------------------------------------------------------+
enum ENUM_INDICATOR_PROP_INTEGER
  {
   INDICATOR_PROP_STATUS = 0,                               // Indicator status (from enumeration ENUM_INDICATOR_STATUS)
   INDICATOR_PROP_TIMEFRAME,                                // Indicator timeframe
   INDICATOR_PROP_HANDLE,                                   // Indicator handle
   INDICATOR_PROP_GROUP,                                    // Indicator group
  }; 
#define INDICATOR_PROP_INTEGER_TOTAL (4)                    // Total number of indicator integer properties
#define INDICATOR_PROP_INTEGER_SKIP  (0)                    // Number of indicator properties not used in sorting
//+------------------------------------------------------------------+
//| Indicator real properties                                        |
//+------------------------------------------------------------------+
enum ENUM_INDICATOR_PROP_DOUBLE
  {
   INDICATOR_PROP_EMPTY_VALUE = INDICATOR_PROP_INTEGER_TOTAL,// Empty value for plotting where nothing will be drawn
  }; 
#define INDICATOR_PROP_DOUBLE_TOTAL  (1)                    // Total number of real indicator properties
#define INDICATOR_PROP_DOUBLE_SKIP   (0)                    // Number of indicator properties not used in sorting
//+------------------------------------------------------------------+
//| Indicator string properties                                      |
//+------------------------------------------------------------------+
enum ENUM_INDICATOR_PROP_STRING
  {
   INDICATOR_PROP_SYMBOL = (INDICATOR_PROP_INTEGER_TOTAL+INDICATOR_PROP_DOUBLE_TOTAL), // Indicator symbol
   INDICATOR_PROP_NAME,                                     // Indicator name
   INDICATOR_PROP_SHORTNAME,                                // Indicator short name
  };
#define INDICATOR_PROP_STRING_TOTAL  (3)                    // Total number of indicator string properties
//+------------------------------------------------------------------+
//| Possible indicator sorting criteria                              |
//+------------------------------------------------------------------+
#define FIRST_INDICATOR_DBL_PROP          (INDICATOR_PROP_INTEGER_TOTAL-INDICATOR_PROP_INTEGER_SKIP)
#define FIRST_INDICATOR_STR_PROP          (INDICATOR_PROP_INTEGER_TOTAL-INDICATOR_PROP_INTEGER_SKIP+INDICATOR_PROP_DOUBLE_TOTAL-INDICATOR_PROP_DOUBLE_SKIP)
enum ENUM_SORT_INDICATOR_MODE
  {
//--- Sort by integer properties
   SORT_BY_INDICATOR_INDEX_STATUS = 0,                      // Sort by indicator status
   SORT_BY_INDICATOR_TIMEFRAME,                             // Sort by indicator timeframe
   SORT_BY_INDICATOR_HANDLE,                                // Sort by indicator handle
   SORT_BY_INDICATOR_GROUP,                                 // Sort by indicator group
//--- Sort by real properties
   SORT_BY_INDICATOR_EMPTY_VALUE = FIRST_INDICATOR_DBL_PROP,// Sort by the empty value for plotting where nothing will be drawn
//--- Sort by string properties
   SORT_BY_INDICATOR_SYMBOL = FIRST_INDICATOR_STR_PROP,     // Sort by indicator symbol
   SORT_BY_INDICATOR_NAME,                                  // Sort by indicator name
   SORT_BY_INDICATOR_SHORTNAME,                             // Sort by indicator short name
  };
//+------------------------------------------------------------------+

Eine Erklärung ist hier nicht erforderlich.

Abstrakte Indikatorklasse

Gehen wir weiter zur Erstellung der abstrakten Indikatorklasse.
Im Ordner \MQL5\Include\DoEasy\Objects\Indicators\ legen wir eine neue Klasse CIndicatorDE in der Datei IndicatorDE.mqh an. Da in der Standardbibliothek die Klasse CIndicator bereits vorhanden ist, habe ich die Klasse und den Dateinamen mit dem Kürzel DE (DoEasy) versehen.

Die neue Klasse ist abgeleitet von Basisobjektklasse aller Objekte der CBaseObj-Bibliothek.
Der Klassenkörper enthält Methoden zum Setzen und Abrufen von Objekteigenschaften, die wir bereits besprochen haben. Schauen wir uns einfach mal den Code an und analysieren einige Klassenmethoden:

//+------------------------------------------------------------------+
//|                                                          Ind.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 "..\..\Services\Select.mqh"
#include "..\..\Objects\BaseObj.mqh"
//+------------------------------------------------------------------+
//| Abstract indicator class                                         |
//+------------------------------------------------------------------+
class CIndicatorDE : public CBaseObj
  {
private:
   long              m_long_prop[INDICATOR_PROP_INTEGER_TOTAL];                  // Integer properties
   double            m_double_prop[INDICATOR_PROP_DOUBLE_TOTAL];                 // Real properties
   string            m_string_prop[INDICATOR_PROP_STRING_TOTAL];                 // String properties
   MqlParam          m_mql_params[];                                             // Array of indicator parameters
   
//--- Return the index of the array the buffer's (1) double and (2) string properties are actually located at
   int               IndexProp(ENUM_INDICATOR_PROP_DOUBLE property)        const { return(int)property-INDICATOR_PROP_INTEGER_TOTAL;                           }
   int               IndexProp(ENUM_INDICATOR_PROP_STRING property)        const { return(int)property-INDICATOR_PROP_INTEGER_TOTAL-INDICATOR_PROP_DOUBLE_TOTAL;}
   
//--- Comare (1) structures MqlParam, (2) array of structures MqlParam between each other
   bool              IsEqualMqlParams(MqlParam &struct1,MqlParam &struct2) const;
   bool              IsEqualMqlParamArrays(MqlParam &compared_struct[])    const;

protected:
public:
//--- Protected parametric constructor
                     CIndicatorDE(ENUM_INDICATOR ind_type,
                                  string symbol,
                                  ENUM_TIMEFRAMES timeframe,
                                  ENUM_INDICATOR_STATUS status,
                                  ENUM_INDICATOR_GROUP group,
                                  string name,
                                  string shortname,
                                  MqlParam &indicator_params[]);
public:  
//--- Default constructor
                     CIndicatorDE(void){;}
//--- Destructor
                    ~CIndicatorDE(void);
                     
//--- Set buffer's (1) integer, (2) real and (3) string properties
   void              SetProperty(ENUM_INDICATOR_PROP_INTEGER property,long value)   { this.m_long_prop[property]=value;                                        }
   void              SetProperty(ENUM_INDICATOR_PROP_DOUBLE property,double value)  { this.m_double_prop[this.IndexProp(property)]=value;                      }
   void              SetProperty(ENUM_INDICATOR_PROP_STRING property,string value)  { this.m_string_prop[this.IndexProp(property)]=value;                      }
//--- Return (1) integer, (2) real and (3) string buffer properties from the properties array
   long              GetProperty(ENUM_INDICATOR_PROP_INTEGER property)        const { return this.m_long_prop[property];                                       }
   double            GetProperty(ENUM_INDICATOR_PROP_DOUBLE property)         const { return this.m_double_prop[this.IndexProp(property)];                     }
   string            GetProperty(ENUM_INDICATOR_PROP_STRING property)         const { return this.m_string_prop[this.IndexProp(property)];                     }
//--- Return description of buffer's (1) integer, (2) real and (3) string properties
   string            GetPropertyDescription(ENUM_INDICATOR_PROP_INTEGER property);
   string            GetPropertyDescription(ENUM_INDICATOR_PROP_DOUBLE property);
   string            GetPropertyDescription(ENUM_INDICATOR_PROP_STRING property);
//--- Return the flag of the buffer supporting the property
   virtual bool      SupportProperty(ENUM_INDICATOR_PROP_INTEGER property)          { return true;       }
   virtual bool      SupportProperty(ENUM_INDICATOR_PROP_DOUBLE property)           { return true;       }
   virtual bool      SupportProperty(ENUM_INDICATOR_PROP_STRING property)           { return true;       }

//--- Compare CIndicatorDE objects by all possible properties (for sorting the lists by a specified indicator object property)
   virtual int       Compare(const CObject *node,const int mode=0) const;
//--- Compare CIndicatorDE objects by all properties (to search for equal indicator objects)
   bool              IsEqual(CIndicatorDE* compared_obj) const;
                     
//--- Set indicator’s (1) group, (2) empty value of buffers, (3) name, (4) short name
   void              SetGroup(const ENUM_INDICATOR_GROUP group)      { this.SetProperty(INDICATOR_PROP_GROUP,group);                         }
   void              SetEmptyValue(const double value)               { this.SetProperty(INDICATOR_PROP_EMPTY_VALUE,value);                   }
   void              SetName(const string name)                      { this.SetProperty(INDICATOR_PROP_NAME,name);                           }
   void              SetShortName(const string shortname)            { this.SetProperty(INDICATOR_PROP_SHORTNAME,shortname);                 }
   
//--- Return indicator’s (1) status, (2) group, (3) timeframe, (4) handle, (5) empty value of buffers, (6) name, (7) short name, (8) symbol
   ENUM_INDICATOR_STATUS Status(void)                          const { return (ENUM_INDICATOR_STATUS)this.GetProperty(INDICATOR_PROP_STATUS);}
   ENUM_INDICATOR_GROUP  Group(void)                           const { return (ENUM_INDICATOR_GROUP)this.GetProperty(INDICATOR_PROP_GROUP);  }
   ENUM_TIMEFRAMES   Timeframe(void)                           const { return (ENUM_TIMEFRAMES)this.GetProperty(INDICATOR_PROP_TIMEFRAME);   }
   int               Handle(void)                              const { return (int)this.GetProperty(INDICATOR_PROP_HANDLE);                  }
   double            EmptyValue(void)                          const { return this.GetProperty(INDICATOR_PROP_EMPTY_VALUE);                  }
   string            Name(void)                                const { return this.GetProperty(INDICATOR_PROP_NAME);                         }
   string            ShortName(void)                           const { return this.GetProperty(INDICATOR_PROP_SHORTNAME);                    }
   string            Symbol(void)                              const { return this.GetProperty(INDICATOR_PROP_SYMBOL);                       }
   
//--- Return description of indicator’s (1) status, (2) group, (3) timeframe, (4) empty value
   string            GetStatusDescription(void)                const;
   string            GetGroupDescription(void)                 const;
   string            GetTimeframeDescription(void)             const;
   string            GetEmptyValueDescription(void)            const;

//--- Display the description of indicator 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 indicator object in the journal (implementation in the descendants)
   virtual void      PrintShort(void) {;}

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

Wir machen den geschlossenen parametrischen Klassenkonstruktor temporär öffentlich — wir wollen heute testen, ob die Erzeugung eines einzelnen Klassenobjekts erfolgreich ist. Deshalb muss dieser Konstruktor für externe Aufrufe offen sein.

Wie man sieht, wiederholen alle Klassenmethoden in ihrer Logik alle zuvor besprochenen Bibliotheksobjekte. Daher muss der Leser mit ihrem Zweck und ihrer Arbeitsweise vertraut sein. Im Gegensatz zu allen anderen Objekten, wo es keinen Destruktor gibt und er implizit erzeugt wird, habe ich hier einen Destruktor hinzugefügt, weil der für das Objekt erzeugte Indikator wieder entfernt werden muss. Analysieren wir die Implementierung der Methoden, die außerhalb des Klassenkörpers durchgeführt wird.

Im Klassen-Destruktor wird das erstellte Indikator-Objekt zerstört:

//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CIndicatorDE::~CIndicatorDE(void)
  {
   ::IndicatorRelease(this.Handle());
  }
//+------------------------------------------------------------------+

Closed parametric class constructor:

//+------------------------------------------------------------------+
//| Closed parametric constructor                                    |
//+------------------------------------------------------------------+
CIndicatorDE::CIndicatorDE(ENUM_INDICATOR ind_type,
                           string symbol,
                           ENUM_TIMEFRAMES timeframe,
                           ENUM_INDICATOR_STATUS status,
                           ENUM_INDICATOR_GROUP group,
                           string name,
                           string shortname,
                           MqlParam &indicator_params[])
  {
//--- Set collection ID to the object
   this.m_type=COLLECTION_INDICATORS_ID;
//--- If parameter array value passed to constructor is more than zero
//--- fill in the array of object parameters with data from the array passed to constructor
   int count=::ArrayResize(m_mql_params,::ArraySize(indicator_params));
   for(int i=0;i<count;i++)
     {
      this.m_mql_params[i].type=indicator_params[i].type;
      this.m_mql_params[i].double_value=indicator_params[i].double_value;
      this.m_mql_params[i].integer_value=indicator_params[i].integer_value;
      this.m_mql_params[i].string_value=indicator_params[i].string_value;
     }
//--- Create indicator handle
   int handle=::IndicatorCreate(symbol,timeframe,ind_type,count,indicator_params);
   
//--- Save integer properties
   this.m_long_prop[INDICATOR_PROP_STATUS]                     = status;
   this.m_long_prop[INDICATOR_PROP_GROUP]                      = group;
   this.m_long_prop[INDICATOR_PROP_TIMEFRAME]                  = timeframe;
   this.m_long_prop[INDICATOR_PROP_HANDLE]                     = handle;
   
//--- Save real properties
   this.m_double_prop[this.IndexProp(INDICATOR_PROP_EMPTY_VALUE)]=EMPTY_VALUE;
//--- Save string properties
   this.m_string_prop[this.IndexProp(INDICATOR_PROP_SYMBOL)]   = (symbol==NULL || symbol=="" ? ::Symbol() : symbol);
   this.m_string_prop[this.IndexProp(INDICATOR_PROP_NAME)]     = name;
   this.m_string_prop[this.IndexProp(INDICATOR_PROP_SHORTNAME)]= shortname;
  }
//+------------------------------------------------------------------+

Alle benötigten Parameter zum Erstellen des Indikatorobjekts werden an den Konstruktor übergeben.
Wenn das Array der Parameter MqlParam &indicator_params[] Null ist, verwendet die Funktion IndicatorCreate() beim Erzeugen eines Indikators nicht die Parameter, die im Array enthalten sein müssen.
Außerdem speichern wir einfach alle Werte, die an die Methode übergeben werden, in den Eigenschaftsfeldern des Objekts.

Um zwei Indikatorobjekte miteinander zu vergleichen, um zwei identische Objekte zu sortieren und zu suchen, müssen alle Felder der beiden Objekte verglichen werden. Das Indikatorobjekt enthält das Array der Strukturen MqlParam — diese müssen ebenfalls miteinander verglichen werden. Sie werden Element für Element verglichen. Dazu gibt es zwei Methoden: eine Methode, um zwei Strukturen MqlParam miteinander zu vergleichen und eine Methode, um zwei Arrays dieser Strukturen zu vergleichen.

//--- Method to compare two MqlParam structures with each other:

//+------------------------------------------------------------------+
//| Compare MqlParam structures with each other                      |
//+------------------------------------------------------------------+
bool CIndicatorDE::IsEqualMqlParams(MqlParam &struct1,MqlParam &struct2) const
  {
   bool res=
     (struct1.type!=struct2.type ? false :
      (struct1.type==TYPE_STRING && struct1.string_value==struct2.string_value) ||
      (struct1.type<TYPE_STRING && struct1.type>TYPE_ULONG && struct1.double_value==struct2.double_value) ||
      (struct1.type<TYPE_FLOAT && struct1.integer_value==struct2.integer_value) ? true : false
     );
   return res;
  }
//+------------------------------------------------------------------+

Hier ist es einfach.
Wir prüfen die Felder der Datentypen der Strukturen und wenn die Datentypen der verglichenen Strukturen nicht übereinstimmen - Rückgabe von false.
Wenn der Datentyp string ist und die Daten der beiden Strukturen identisch sind — Rückgabe von true.
Wenn der Datentyp real ist und die Daten der beiden Strukturen identisch sind — Rückgabe von true.
Wenn der Datentyp Integer ist und die Daten der beiden Strukturen identisch sind — Rückgabe von true.
In jedem anderen Fall — Rückgabe von false.

Die Methode, die Arrays von der Strukturen MqlParam miteinander vergleicht:

//+------------------------------------------------------------------+
//| Compare array of MqlParam structures with each other             |
//+------------------------------------------------------------------+
bool CIndicatorDE::IsEqualMqlParamArrays(MqlParam &compared_struct[]) const
  {
   int total=::ArraySize(this.m_mql_params);
   int size=::ArraySize(compared_struct);
   if(total!=size || total==0 || size==0)
      return false;
   for(int i=0;i<total;i++)
     {
      if(!this.IsEqualMqlParams(this.m_mql_params[i],compared_struct[i]))
         return false;
     }
   return true;
  }
//+------------------------------------------------------------------+

Wenn die Werte von zwei Arrays nicht gleich sind oder einer von ihnen Null ist — wird false zurückgegeben.
Dann, in einer Schleife durch die Anzahl der Strukturen im Array vergleichen wir alle folgenden Strukturen der beiden Arrays und wenn sie nicht gleich sind — wird false zurückgegeben.
Nach erfolgreicher Prüfung aller in zwei Arrays enthaltenen Strukturen — wird true zurückgegeben.

Methode zum Vergleich der Objekte CIndicatorDE anhand aller möglichen Eigenschaften:

//+------------------------------------------------------------------+
//| Compare CIndicatorDE objects with each other                     |
//| by all possible properties                                       |
//+------------------------------------------------------------------+
int CIndicatorDE::Compare(const CObject *node,const int mode=0) const
  {
   const CIndicatorDE *compared_obj=node;
//--- compare integer properties of two indicators
   if(mode<INDICATOR_PROP_INTEGER_TOTAL)
     {
      long value_compared=compared_obj.GetProperty((ENUM_INDICATOR_PROP_INTEGER)mode);
      long value_current=this.GetProperty((ENUM_INDICATOR_PROP_INTEGER)mode);
      return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0);
     }
//--- compare real properties of two indicators
   else if(mode<INDICATOR_PROP_INTEGER_TOTAL+INDICATOR_PROP_DOUBLE_TOTAL)
     {
      double value_compared=compared_obj.GetProperty((ENUM_INDICATOR_PROP_DOUBLE)mode);
      double value_current=this.GetProperty((ENUM_INDICATOR_PROP_DOUBLE)mode);
      return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0);
     }
//--- compare string properties of two indicators
   else if(mode<INDICATOR_PROP_INTEGER_TOTAL+INDICATOR_PROP_DOUBLE_TOTAL+INDICATOR_PROP_STRING_TOTAL)
     {
      string value_compared=compared_obj.GetProperty((ENUM_INDICATOR_PROP_STRING)mode);
      string value_current=this.GetProperty((ENUM_INDICATOR_PROP_STRING)mode);
      return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0);
     }
   return 0;
  }
//+------------------------------------------------------------------+

Vergleichsmethode für die Objekte CIndicatorDE für alle Eigenschaften

//+------------------------------------------------------------------+
//| Compare CIndicatorDE objects with each other by all properties   |
//+------------------------------------------------------------------+
bool CIndicatorDE::IsEqual(CIndicatorDE *compared_obj) const
  {
   if(!IsEqualMqlParamArrays(compared_obj.m_mql_params))
      return false;
   int beg=0, end=INDICATOR_PROP_INTEGER_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_INDICATOR_PROP_INTEGER prop=(ENUM_INDICATOR_PROP_INTEGER)i;
      if(this.GetProperty(prop)!=compared_obj.GetProperty(prop)) return false; 
     }
   beg=end; end+=INDICATOR_PROP_DOUBLE_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_INDICATOR_PROP_DOUBLE prop=(ENUM_INDICATOR_PROP_DOUBLE)i;
      if(this.GetProperty(prop)!=compared_obj.GetProperty(prop)) return false; 
     }
   beg=end; end+=INDICATOR_PROP_STRING_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_INDICATOR_PROP_STRING prop=(ENUM_INDICATOR_PROP_STRING)i;
      if(this.GetProperty(prop)!=compared_obj.GetProperty(prop)) return false; 
     }
   return true;
  }
//+------------------------------------------------------------------+

Zwei Vergleichsmethoden von CIndicatorDE-Objekten sind von der Logik her identisch mit gleichnamigen Methoden anderer Bibliotheksobjekte und wir haben sie wiederholt besprochen. Der einzige Unterschied in der zweiten Methode (IsEqual) — ist, dass zuerst, Vergleichsmethode von zwei Arrays von MqlParam-Strukturen von Objekten verglichen aufgerufen wird. Wenn sie nicht gleich sind, sind die Objekte nicht gleich — Rückgabe von false. Weiterhin werden die Objekte anhand aller ihrer Felder verglichen.

Die Methoden geben die Beschreibung der Eigenschaften integer, real und string des Indikators zurück:

//+------------------------------------------------------------------+
//| Return description of indicator's integer property               |
//+------------------------------------------------------------------+
string CIndicatorDE::GetPropertyDescription(ENUM_INDICATOR_PROP_INTEGER property)
  {
   return
     (
      property==INDICATOR_PROP_STATUS        ?  CMessage::Text(MSG_LIB_TEXT_IND_TEXT_STATUS)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+this.GetStatusDescription()
         )  :
      property==INDICATOR_PROP_GROUP          ?  CMessage::Text(MSG_LIB_TEXT_IND_TEXT_GROUP)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+this.GetGroupDescription()
         )  :
      property==INDICATOR_PROP_TIMEFRAME     ?  CMessage::Text(MSG_LIB_TEXT_IND_TEXT_TIMEFRAME)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+this.GetTimeframeDescription()
         )  :
      property==INDICATOR_PROP_HANDLE        ?  CMessage::Text(MSG_LIB_TEXT_IND_TEXT_HANDLE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      ""
     );
  }
//+------------------------------------------------------------------+
//| Return description of indicator's real property                  |
//+------------------------------------------------------------------+
string CIndicatorDE::GetPropertyDescription(ENUM_INDICATOR_PROP_DOUBLE property)
  {
   return
     (
      property==INDICATOR_PROP_EMPTY_VALUE    ?  CMessage::Text(MSG_LIB_TEXT_IND_TEXT_EMPTY_VALUE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+this.GetEmptyValueDescription()
         )  :
      ""
     );
  }
//+------------------------------------------------------------------+
//| Return description of indicator's string property                |
//+------------------------------------------------------------------+
string CIndicatorDE::GetPropertyDescription(ENUM_INDICATOR_PROP_STRING property)
  {
   return
     (
      property==INDICATOR_PROP_SYMBOL     ?  CMessage::Text(MSG_LIB_TEXT_IND_TEXT_SYMBOL)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+this.Symbol()
         )  :
      property==INDICATOR_PROP_NAME       ?  CMessage::Text(MSG_LIB_TEXT_IND_TEXT_NAME)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.Name()==NULL || this.Name()=="" ? CMessage::Text(MSG_LIB_PROP_NOT_SET) : "\""+this.Name()+"\"")
         )  :
      property==INDICATOR_PROP_SHORTNAME  ?  CMessage::Text(MSG_LIB_TEXT_IND_TEXT_SHORTNAME)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.ShortName()==NULL || this.ShortName()=="" ? CMessage::Text(MSG_LIB_PROP_NOT_SET) : "\""+this.ShortName()+"\"")
         )  :
      ""
     );
  }
//+------------------------------------------------------------------+

Jedes Bibliotheksobjekt hat die gleichen Methode, die wir schon früher besprochen haben.

Die übrigen Methoden zur Anzeige von Beschreibungen verschiedener Eigenschaften des Indikatorobjekts sind ebenfalls identisch mit den gleichen Methoden in anderen Bibliotheksobjekten. Schauen wir uns daher einfach deren Auflistung zum individuellen Lernen an:

//+------------------------------------------------------------------+
//| Return indicator status description                              |
//+------------------------------------------------------------------+
string CIndicatorDE::GetStatusDescription(void) const
  {
   return
     (
      this.Status()==INDICATOR_STATUS_CUSTOM    ? CMessage::Text(MSG_LIB_TEXT_IND_TEXT_STATUS_CUSTOM)    :
      this.Status()==INDICATOR_STATUS_STANDART  ? CMessage::Text(MSG_LIB_TEXT_IND_TEXT_STATUS_STANDART)  :
      "Unknown"
     );
  }
//+------------------------------------------------------------------+
//| Return indicator group description                               |
//+------------------------------------------------------------------+
string CIndicatorDE::GetGroupDescription(void) const
  {
   return
     (
      this.Group()==INDICATOR_GROUP_TREND       ? CMessage::Text(MSG_LIB_TEXT_IND_TEXT_GROUP_TREND)      :
      this.Group()==INDICATOR_GROUP_OSCILLATOR  ? CMessage::Text(MSG_LIB_TEXT_IND_TEXT_GROUP_OSCILLATOR) :
      this.Group()==INDICATOR_GROUP_VOLUMES     ? CMessage::Text(MSG_LIB_TEXT_IND_TEXT_GROUP_VOLUMES)    :
      this.Group()==INDICATOR_GROUP_ARROWS      ? CMessage::Text(MSG_LIB_TEXT_IND_TEXT_GROUP_ARROWS)     :
      "Any"
     );
  }
//+------------------------------------------------------------------+
//| Return description of timeframe used                             |
//+------------------------------------------------------------------+
string CIndicatorDE::GetTimeframeDescription(void) const
  {
   string timeframe=TimeframeDescription(this.Timeframe());
   return(this.Timeframe()==PERIOD_CURRENT ? CMessage::Text(MSG_LIB_TEXT_PERIOD_CURRENT)+" ("+timeframe+")" : timeframe);
  } 
//+------------------------------------------------------------------+
//| Return description of the set empty value                        |
//+------------------------------------------------------------------+
string CIndicatorDE::GetEmptyValueDescription(void) const
  {
   double value=fabs(this.EmptyValue());
   return(value<EMPTY_VALUE ? ::DoubleToString(this.EmptyValue(),(this.EmptyValue()==0 ? 1 : 8)) : (this.EmptyValue()>0 ? "EMPTY_VALUE" : "-EMPTY_VALUE"));
  }
//+------------------------------------------------------------------+
//| Display indicator properties in the journal                      |
//+------------------------------------------------------------------+
void CIndicatorDE::Print(const bool full_prop=false)
  {
   ::Print("============= ",CMessage::Text(MSG_LIB_PARAMS_LIST_BEG),": \"",this.GetStatusDescription(),"\" =============");
   int beg=0, end=INDICATOR_PROP_INTEGER_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_INDICATOR_PROP_INTEGER prop=(ENUM_INDICATOR_PROP_INTEGER)i;
      if(!full_prop && !this.SupportProperty(prop)) continue;
      ::Print(this.GetPropertyDescription(prop));
     }
   ::Print("------");
   beg=end; end+=INDICATOR_PROP_DOUBLE_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_INDICATOR_PROP_DOUBLE prop=(ENUM_INDICATOR_PROP_DOUBLE)i;
      if(!full_prop && !this.SupportProperty(prop)) continue;
      ::Print(this.GetPropertyDescription(prop));
     }
   ::Print("------");
   beg=end; end+=INDICATOR_PROP_STRING_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_INDICATOR_PROP_STRING prop=(ENUM_INDICATOR_PROP_STRING)i;
      if(!full_prop && !this.SupportProperty(prop)) continue;
      ::Print(this.GetPropertyDescription(prop));
     }
   ::Print("================== ",CMessage::Text(MSG_LIB_PARAMS_LIST_END),": \"",this.GetStatusDescription(),"\" ==================\n");
  }
//+------------------------------------------------------------------+

Dies ist die vollständige Zusammensetzung des Indikatorobjekts. Es ist sehr wahrscheinlich, dass wir es weiter verbessern werden. Aber für den Moment ist dies ausreichend, um die Erstellung von Objekten zu überprüfen.

Jetzt sollten wir es ermöglichen, effizient mit der Liste der neuen Indikatorobjekte in der Bibliothek zu arbeiten: sie zu sortieren und die gewünschten Objekte nach voreingestellten Kriterien auszuwählen. Dazu binden wir in der Datei \MQL5\Include\DoEasy\Services\Select.mqh die Klassendatei des abstrakten Basisindikators 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"
//+------------------------------------------------------------------+

und am Ende des Klassenkörpers deklarieren Sie Methoden für die Arbeit mit der Indikatorobjektliste:

//+------------------------------------------------------------------+
//| Methods of working with indicators                               |
//+------------------------------------------------------------------+
   //--- Return the list of indicators with one out of (1) integer, (2) real and (3) string properties meeting a specified criterion
   static CArrayObj *ByIndicatorProperty(CArrayObj *list_source,ENUM_INDICATOR_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode);
   static CArrayObj *ByIndicatorProperty(CArrayObj *list_source,ENUM_INDICATOR_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode);
   static CArrayObj *ByIndicatorProperty(CArrayObj *list_source,ENUM_INDICATOR_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode);
   //--- Return the indicator index in the list with the maximum value of the indicator's (1) integer, (2) real and (3) string property
   static int        FindIndicatorMax(CArrayObj *list_source,ENUM_INDICATOR_PROP_INTEGER property);
   static int        FindIndicatorMax(CArrayObj *list_source,ENUM_INDICATOR_PROP_DOUBLE property);
   static int        FindIndicatorMax(CArrayObj *list_source,ENUM_INDICATOR_PROP_STRING property);
   //--- Return the indicator index in the list with the minimum value of the indicator's (1) integer, (2) real and (3) string property
   static int        FindIndicatorMin(CArrayObj *list_source,ENUM_INDICATOR_PROP_INTEGER property);
   static int        FindIndicatorMin(CArrayObj *list_source,ENUM_INDICATOR_PROP_DOUBLE property);
   static int        FindIndicatorMin(CArrayObj *list_source,ENUM_INDICATOR_PROP_STRING property);
//---
  };
//+------------------------------------------------------------------+

Alle Methoden der Arbeit mit Indikatorobjekten sind ebenfalls absolut standardisiert und identisch mit den früher eingestellten Methoden zum Suchen und Sortieren in Sammellisten anderer Bibliotheksobjekte. Sie alle wurden früher besprochen. Analysieren wir einfach ihre Implementierung für das individuelle Studium und die Überprüfung des studierten Materials:

//+------------------------------------------------------------------+
//| Methods of working with indicator lists                          |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Return the list of indicators with one of integer                |
//| properties meeting the specified criterion                       |
//+------------------------------------------------------------------+
CArrayObj *CSelect::ByIndicatorProperty(CArrayObj *list_source,ENUM_INDICATOR_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++)
     {
      CIndicatorDE *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 with one of real                   |
//| properties meeting the specified criterion                       |
//+------------------------------------------------------------------+
CArrayObj *CSelect::ByIndicatorProperty(CArrayObj *list_source,ENUM_INDICATOR_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++)
     {
      CIndicatorDE *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 with one of string                 |
//| properties meeting the specified criterion                       |
//+------------------------------------------------------------------+
CArrayObj *CSelect::ByIndicatorProperty(CArrayObj *list_source,ENUM_INDICATOR_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++)
     {
      CIndicatorDE *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 index in the list                           |
//| with the maximum integer property value                          |
//+------------------------------------------------------------------+
int CSelect::FindIndicatorMax(CArrayObj *list_source,ENUM_INDICATOR_PROP_INTEGER property)
  {
   if(list_source==NULL) return WRONG_VALUE;
   int index=0;
   CIndicatorDE *max_obj=NULL;
   int total=list_source.Total();
   if(total==0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      CIndicatorDE *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 index in the list                           |
//| with the maximum real property value                             |
//+------------------------------------------------------------------+
int CSelect::FindIndicatorMax(CArrayObj *list_source,ENUM_INDICATOR_PROP_DOUBLE property)
  {
   if(list_source==NULL) return WRONG_VALUE;
   int index=0;
   CIndicatorDE *max_obj=NULL;
   int total=list_source.Total();
   if(total==0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      CIndicatorDE *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 index in the list                           |
//| with the maximum string property value                           |
//+------------------------------------------------------------------+
int CSelect::FindIndicatorMax(CArrayObj *list_source,ENUM_INDICATOR_PROP_STRING property)
  {
   if(list_source==NULL) return WRONG_VALUE;
   int index=0;
   CIndicatorDE *max_obj=NULL;
   int total=list_source.Total();
   if(total==0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      CIndicatorDE *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 index in the list                           |
//| with the minimum integer property value                          |
//+------------------------------------------------------------------+
int CSelect::FindIndicatorMin(CArrayObj* list_source,ENUM_INDICATOR_PROP_INTEGER property)
  {
   int index=0;
   CIndicatorDE *min_obj=NULL;
   int total=list_source.Total();
   if(total==0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      CIndicatorDE *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 index in the list                           |
//| with the minimum real property value                             |
//+------------------------------------------------------------------+
int CSelect::FindIndicatorMin(CArrayObj* list_source,ENUM_INDICATOR_PROP_DOUBLE property)
  {
   int index=0;
   CIndicatorDE *min_obj=NULL;
   int total=list_source.Total();
   if(total== 0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      CIndicatorDE *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 index in the list                           |
//| with the minimum string property value                           |
//+------------------------------------------------------------------+
int CSelect::FindIndicatorMin(CArrayObj* list_source,ENUM_INDICATOR_PROP_STRING property)
  {
   int index=0;
   CIndicatorDE *min_obj=NULL;
   int total=list_source.Total();
   if(total==0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      CIndicatorDE *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;
  }
//+------------------------------------------------------------------+

Im Moment werden in unserer Bibliothek Indikatoren innerhalb der Sammelklasse CBuffersCollection Indikatorpuffer erstellt und verwendet. Ich erstelle eine eigene Klasse für Indikatorobjekte, die wiederum in ihrer Kollektionsklasse gesammelt werden. Der Zugriff auf die Indikatorobjekte wird über diese Kollektion möglich sein. In der Zwischenzeit arbeiten wir innerhalb der Klasse CBuffersCollection, da wir heute nur ein Indikatorobjekt erzeugen und prüfen müssen, ob es erfolgreich ist.
In den folgenden Artikeln werde ich, wenn Nachfolgeklassen von abstrakten Indikatoren erstellt werden, diese in einer Kollektion sammeln und in der Klasse CBuffersCollection nicht direkt mit Indikatoren arbeiten (wie wir es jetzt tun), sondern mit der Kollektionsklasse des Indikators.

Für heute ist unser Ziel, die Tatsache der Erstellung von Indikatorobjekten zu entwickeln und zu überprüfen. Deshalb werden die Verbesserungen nur eine Methode der Klasse CBuffersCollection betreffen, nämlich die Methode zur Erstellung des Indikators Accelerator Oscillator.
Wir geben die Änderungen in der Methode für die plattformübergreifende Eigenschaft ein und erstellen das Indikatorobjekt, drucken seine Daten im Journal und löschen dieses Objekt sofort. Das ist alles, was getan werden muss, um die Erstellung des abstrakten Indikatorobjekts zu überprüfen.
Wir öffnen die Klassendatei der Indikatorpuffersammlung \MQL5\Include\DoEasy\Collections\BuffersCollection.mqh und nehmen die notwendigen Verbesserungen vor.

Um ein Indikator-Objekt zu erstellen, verwenden wir die Funktion IndicatorCreate(), an die die für viele Indikatoren erforderlichen Parameter übergeben werden müssen. Solche Parameter werden mit dem Array von MqlParam übergeben, einer speziell für diesen Zweck entwickelten Struktur.
Deklaration eines solchen Arrays im 'private' Abschnitt der Klasse:

//+------------------------------------------------------------------+
//| Collection of indicator buffers                                  |
//+------------------------------------------------------------------+
class CBuffersCollection : public CObject
  {
private:
   CListObj                m_list;                       // Buffer object list
   CTimeSeriesCollection  *m_timeseries;                 // Pointer to the timeseries collection object
   MqlParam                m_mql_param[];                // Array of indicator parameters
//--- Return the index of the (1) last, (2) next drawn and (3) basic buffer
   int                     GetIndexLastPlot(void);
   int                     GetIndexNextPlot(void);
   int                     GetIndexNextBase(void);
//--- Create a new buffer object and place it to the collection list
   bool                    CreateBuffer(ENUM_BUFFER_STATUS status);
//--- Get data of the necessary timeseries and bars for working with a single buffer bar, and return the number of bars
   int                     GetBarsData(CBuffer *buffer,const int series_index,int &index_bar_period);

public:

Da die Erstellung des Indikatorobjekts nur in einer Methode überprüft wird, nämlich in der Erstellungsmethode CreateAC() des Indikators Accelerator Oscillator, werden alle Verbesserungen nur diese Methode betreffen. Alle übrigen Methoden zur Erstellung von Indikatorpuffern werden in den folgenden Artikeln verbessert.

Trennen wir die Methode in zwei Blöcke, nämlich für MQL5 und für MQL4 für die plattformübergreifende Eigenschaft. Aber in der Zwischenzeit wird für MQL4 nur der zweite Puffer für die Anzeige der zweiten Farbe (Accelerator Oscillator ist zweifarbig) erstellt. Aber ich mache keine Änderung der Histogramm-Farbe, was in der Tat Abwechslung der Anzeige von zwei verschiedenen Puffern für die visuelle Anzeige von verschiedenen Indikatorfarben in MQL4 bedeutet. Heute mache ich etwas ganz anderes.
Ganz am Anfang der Methode füge ich Zeilen für die Erstellung des Indikatorobjekts, die Ausgabe der Daten für das erstellte Objekt in das Journal und die Entfernung des Objekts hinzu:

//+------------------------------------------------------------------+
//| Create multi-symbol multi-period AC                              |
//+------------------------------------------------------------------+
int CBuffersCollection::CreateAC(const string symbol,const ENUM_TIMEFRAMES timeframe,const int id=WRONG_VALUE)
  {
//--- To check it, create indicator object, print its data and remove it at once
   ::ArrayResize(this.m_mql_param,0);
   CIndicatorDE *indicator=new CIndicatorDE(IND_AC,symbol,timeframe,INDICATOR_STATUS_STANDART,INDICATOR_GROUP_OSCILLATOR,"Accelerator Oscillator","AC("+symbol+","+TimeframeDescription(timeframe)+")",this.m_mql_param);
   indicator.Print();
   delete indicator;

//--- Create indicator handle and set default ID
   int handle= #ifdef __MQL5__ ::iAC(symbol,timeframe) #else 0 #endif ;
   int identifier=(id==WRONG_VALUE ? IND_AC : id);
   color array_colors[3]={clrGreen,clrRed,clrGreen};
   CBuffer *buff=NULL;
   if(handle!=INVALID_HANDLE)
     {
      //--- Create histogram buffer from the zero line
      this.CreateHistogram();
      //--- Get the last created buffer object (drawn) and set to it all necessary parameters
      buff=this.GetLastCreateBuffer();
      if(buff==NULL)
         return INVALID_HANDLE;
      buff.SetSymbol(symbol);
      buff.SetTimeframe(timeframe);
      buff.SetID(identifier);
      buff.SetIndicatorHandle(handle);
      buff.SetIndicatorType(IND_AC);
      buff.SetLineMode(INDICATOR_LINE_MODE_MAIN);
      buff.SetShowData(true);
      buff.SetLabel("AC("+symbol+","+TimeframeDescription(timeframe)+")");
      buff.SetIndicatorName("Accelerator Oscillator");
      buff.SetIndicatorShortName("AC("+symbol+","+TimeframeDescription(timeframe)+")");
      #ifdef __MQL5__ 
         buff.SetColors(array_colors); 
      #else 
         buff.SetColor(array_colors[0]); 
         buff.SetIndicatorLineAdditionalNumber(0);
      #endif 

      //--- MQL5
      #ifdef __MQL5__
         //--- Create calculated buffer, in which standard indicator data will be stored
         this.CreateCalculate();
         //--- Get the last created buffer object (calculated) and set to it all necessary parameters
         buff=this.GetLastCreateBuffer();
         if(buff==NULL)
            return INVALID_HANDLE;
         buff.SetSymbol(symbol);
         buff.SetTimeframe(timeframe);
         buff.SetID(identifier);
         buff.SetIndicatorHandle(handle);
         buff.SetIndicatorType(IND_AC);
         buff.SetLineMode(INDICATOR_LINE_MODE_MAIN);
         buff.SetEmptyValue(EMPTY_VALUE);
         buff.SetLabel("AC("+symbol+","+TimeframeDescription(timeframe)+")");
         buff.SetIndicatorName("Accelerator Oscillator");
         buff.SetIndicatorShortName("AC("+symbol+","+TimeframeDescription(timeframe)+")");
      
      //--- MQL4
      #else 
        //--- Create histogram buffer from the zero line for buffer of the second color
         this.CreateHistogram();
         //--- Get the last created buffer object (drawn) and set to it all necessary parameters
         buff=this.GetLastCreateBuffer();
         if(buff==NULL)
            return INVALID_HANDLE;
         buff.SetSymbol(symbol);
         buff.SetTimeframe(timeframe);
         buff.SetID(identifier);
         buff.SetIndicatorHandle(handle);
         buff.SetIndicatorType(IND_AC);
         buff.SetLineMode(INDICATOR_LINE_MODE_MAIN);
         buff.SetShowData(true);
         buff.SetLabel("AC("+symbol+","+TimeframeDescription(timeframe)+")");
         buff.SetIndicatorName("Accelerator Oscillator");
         buff.SetIndicatorShortName("AC("+symbol+","+TimeframeDescription(timeframe)+")");
         #ifdef __MQL5__ 
            buff.SetColors(array_colors); 
         #else 
            buff.SetColor(array_colors[1]); 
            buff.SetIndicatorLineAdditionalNumber(1);
         #endif 
      #endif 
     }
   return handle;
  }
//+------------------------------------------------------------------+

Da der Standardindikator Accelerator Oscillator keine Parameter besitzt, werde ich beim Erstellen des Indikator-Handles mit der Funktion IndicatorCreate() nicht das Array der Indikatorparameter verwenden.
Wir setzen die Größe des Parameter-Arrays zurück
.
Wir erzeugen ein neues Indikatorobjekt, nachdem seinem Konstruktor alle für die Objekterzeugung erforderlichen Daten übergeben haben
,
geben sofort die Daten des neu erstellten Objekts aus (ich werde nicht prüfen, ob die Erstellung des Objekts erfolgreich war, da dies nur ein Test ist)
und entfernen dieses Objekt — um ein Speicherleck zu vermeiden.

In den folgenden Artikeln werden nach der Erzeugung von Nachfolgeobjekten des abstrakten Basisindikators und deren Platzierung in der Indikatorkollektion bei ihrer Erzeugung die restlichen Methoden für die Erzeugung des Indikatorpuffers hinzugefügt. Heute wird sogar eine solche Prüfung ausreichend sein.

Bei der Methode zum Festlegen des Werts für das aktuelle Diagramm auf die Puffer des angegebenen Standardindikators durch den Zeitreihenindex gemäß Symbol/Periode des Pufferobjekts erstellen wir eine Vorlage zum Organisieren der plattformübergreifenden Eigenschaft, indem wir die Unterteilung in Codeblöcke für MQL5 und MQL4 nur für Einzelpuffer-Standardindikatoren hinzufügen (um den Code für MQL4 nicht zu implementieren) wir werden - wir brauchen es heute nicht):

//+------------------------------------------------------------------+
//| Set values for the current chart to buffers of the specified     |
//| standard indicator by the timeseries index in accordance         |
//| with buffer object symbol/period                                 |
//+------------------------------------------------------------------+
bool CBuffersCollection::SetDataBufferStdInd(const ENUM_INDICATOR ind_type,const int id,const int series_index,const datetime series_time,const char color_index=WRONG_VALUE)
  {
//--- Get the list of buffer objects by type and ID
   CArrayObj *list=this.GetListBufferByTypeID(ind_type,id);
   if(list==NULL || list.Total()==0)
     {
      ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_NO_BUFFER_OBJ));
      return false;
     }
     
//--- Get the list of drawn buffers with ID
   CArrayObj *list_data=CSelect::ByBufferProperty(list,BUFFER_PROP_TYPE,BUFFER_TYPE_DATA,EQUAL);
   list_data=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_TYPE,ind_type,EQUAL);
//--- Get the list of calculated buffers with ID
   CArrayObj *list_calc=CSelect::ByBufferProperty(list,BUFFER_PROP_TYPE,BUFFER_TYPE_CALCULATE,EQUAL);
   list_calc=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_TYPE,ind_type,EQUAL);
 
//--- Leave if any of the lists is empty
   if(list_data.Total()==0 #ifdef __MQL5__ || list_calc.Total()==0 #endif )
      return false;
  
//--- Declare necessary objects and variables
   CBuffer *buffer_data0=NULL,*buffer_data1=NULL,*buffer_data2=NULL,*buffer_data3=NULL,*buffer_data4=NULL,*buffer_tmp0=NULL,*buffer_tmp1=NULL;
   CBuffer *buffer_calc0=NULL,*buffer_calc1=NULL,*buffer_calc2=NULL,*buffer_calc3=NULL,*buffer_calc4=NULL;
   #ifdef __MQL4__ CBuffer *buff_add=NULL; #endif 

   double value00=EMPTY_VALUE, value01=EMPTY_VALUE;
   double value10=EMPTY_VALUE, value11=EMPTY_VALUE;
   double value20=EMPTY_VALUE, value21=EMPTY_VALUE;
   double value30=EMPTY_VALUE, value31=EMPTY_VALUE;
   double value40=EMPTY_VALUE, value41=EMPTY_VALUE;
   double value_tmp0=EMPTY_VALUE,value_tmp1=EMPTY_VALUE;
   long vol0=0,vol1=0;
   int series_index_start=series_index,index_period=0, index=0,num_bars=1;
   uchar clr=0;
//--- Depending on standard indicator type

   switch((int)ind_type)
     {
   //--- Single-buffer standard indicators
      case IND_AC       :
      case IND_AD       :
      case IND_AMA      :
      case IND_AO       :
      case IND_ATR      :
      case IND_BEARS    :
      case IND_BULLS    :
      case IND_BWMFI    :
      case IND_CCI      :
      case IND_CHAIKIN  :
      case IND_DEMA     :
      case IND_DEMARKER :
      case IND_FORCE    :
      case IND_FRAMA    :
      case IND_MA       :
      case IND_MFI      :
      case IND_MOMENTUM :
      case IND_OBV      :
      case IND_OSMA     :
      case IND_RSI      :
      case IND_SAR      :
      case IND_STDDEV   :
      case IND_TEMA     :
      case IND_TRIX     :
      case IND_VIDYA    :
      case IND_VOLUMES  :
      case IND_WPR      :
        list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,0,EQUAL);
        buffer_data0=list.At(0);
      #ifdef __MQL5__
        list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,0,EQUAL);
        buffer_calc0=list.At(0);
      #endif 
        
        if(buffer_data0==NULL #ifdef __MQL5__ || buffer_calc0==NULL || buffer_calc0.GetDataTotal(0)==0 #endif )
           return false;

        series_index_start=PreparingSetDataStdInd(buffer_data0,buffer_data1,buffer_data2,buffer_data3,buffer_data4,
                                                  buffer_calc0,buffer_calc1,buffer_calc2,buffer_calc3,buffer_calc4,
                                                  ind_type,series_index,series_time,index_period,num_bars,
                                                  value00,value01,value10,value11,value20,value21,value30,value31,value40,value41);

        if(series_index_start==WRONG_VALUE)
           return false;
        //--- In a loop, by the number of bars in  num_bars fill in the drawn buffer with a value from the calculated buffer taken by index_period index
        //--- and set the drawn buffer color depending on a proportion of value00 and value01 values
        for(int i=0;i<num_bars;i++)
          {
           index=series_index_start-i;
           buffer_data0.SetBufferValue(0,index,value00);
           if(ind_type!=IND_BWMFI)
              clr=(color_index==WRONG_VALUE ? uchar(value00>value01 ? 0 : value00<value01 ? 1 : 2) : color_index);
           else
             {
              vol0=::iVolume(buffer_data0.Symbol(),buffer_data0.Timeframe(),index_period);
              vol1=::iVolume(buffer_data0.Symbol(),buffer_data0.Timeframe(),index_period+1);
              clr=
                (
                 value00>value01 && vol0>vol1 ? 0 :
                 value00<value01 && vol0<vol1 ? 1 :
                 value00>value01 && vol0<vol1 ? 2 :
                 value00<value01 && vol0>vol1 ? 3 : 4
                );
             }
           #ifdef __MQL5__
              buffer_data0.SetBufferColorIndex(index,clr);
           #else 
              
           #endif 
          }
        return true;
      
   //--- Multi-buffer standard indicators
      case IND_ENVELOPES :
      case IND_FRACTALS  :
        list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,0,EQUAL);
        buffer_data0=list.At(0);
        list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,1,EQUAL);
        buffer_data1=list.At(0);
           
        list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,0,EQUAL);
        buffer_calc0=list.At(0);
        list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,1,EQUAL);
        buffer_calc1=list.At(0);
           
        if(buffer_calc0==NULL || buffer_data0==NULL || buffer_calc0.GetDataTotal(0)==0)
           return false;
        if(buffer_calc1==NULL || buffer_data1==NULL || buffer_calc1.GetDataTotal(0)==0)
           return false;
        
        series_index_start=PreparingSetDataStdInd(buffer_data0,buffer_data1,buffer_data2,buffer_data3,buffer_data4,
                                                  buffer_calc0,buffer_calc1,buffer_calc2,buffer_calc3,buffer_calc4,
                                                  ind_type,series_index,series_time,index_period,num_bars,
                                                  value00,value01,value10,value11,value20,value21,value30,value31,value40,value41);
        if(series_index_start==WRONG_VALUE)
           return false;
        //--- In a loop, by the number of bars in  num_bars fill in the drawn buffer with a value from the calculated buffer taken by index_period index
        //--- and set the drawn buffer color depending on a proportion of value00 and value01 values
        for(int i=0;i<num_bars;i++)
          {
           index=series_index_start-i;
           buffer_data0.SetBufferValue(0,index,value00);
           buffer_data1.SetBufferValue(1,index,value10);
           buffer_data0.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value00>value01 ? 0 : value00<value01 ? 1 : 2) : color_index);
           buffer_data1.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value10>value11 ? 0 : value10<value11 ? 1 : 2) : color_index);
          }
        return true;
      
      case IND_ADX         :
      case IND_ADXW        :
      case IND_BANDS       :
      case IND_MACD        :
      case IND_RVI         :
      case IND_STOCHASTIC  :
      case IND_ALLIGATOR   :
        list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,0,EQUAL);
        buffer_data0=list.At(0);
        list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,1,EQUAL);
        buffer_data1=list.At(0);
        list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,2,EQUAL);
        buffer_data2=list.At(0);
        
        list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,0,EQUAL);
        buffer_calc0=list.At(0);
        list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,1,EQUAL);
        buffer_calc1=list.At(0);
        list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,2,EQUAL);
        buffer_calc2=list.At(0);
        
        if(buffer_calc0==NULL || buffer_data0==NULL || buffer_calc0.GetDataTotal(0)==0)
           return false;
        if(buffer_calc1==NULL || buffer_data1==NULL || buffer_calc1.GetDataTotal(0)==0)
           return false;
        if(buffer_calc2==NULL || buffer_data2==NULL || buffer_calc2.GetDataTotal(0)==0)
           return false;
        
        series_index_start=PreparingSetDataStdInd(buffer_data0,buffer_data1,buffer_data2,buffer_data3,buffer_data4,
                                                  buffer_calc0,buffer_calc1,buffer_calc2,buffer_calc3,buffer_calc4,
                                                  ind_type,series_index,series_time,index_period,num_bars,
                                                  value00,value01,value10,value11,value20,value21,value30,value31,value40,value41);
        if(series_index_start==WRONG_VALUE)
           return false;
        //--- In a loop, by the number of bars in  num_bars fill in the drawn buffer with a value from the calculated buffer taken by index_period index
        //--- and set the drawn buffer color depending on a proportion of value00 and value01 values
        for(int i=0;i<num_bars;i++)
          {
           index=series_index_start-i;
           buffer_data0.SetBufferValue(0,index,value00);
           buffer_data1.SetBufferValue(0,index,value10);
           buffer_data2.SetBufferValue(0,index,value20);
           buffer_data0.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value00>value01 ? 0 : value00<value01 ? 1 : 2) : color_index);
           buffer_data1.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value10>value11 ? 0 : value10<value11 ? 1 : 2) : color_index);
           buffer_data2.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value20>value21 ? 0 : value20<value21 ? 1 : 2) : color_index);
          }
        return true;
      
      case IND_ICHIMOKU :
        list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_TENKAN_SEN,EQUAL);
        buffer_data0=list.At(0);
        list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_KIJUN_SEN,EQUAL);
        buffer_data1=list.At(0);
        list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_SENKOU_SPANA,EQUAL);
        buffer_data2=list.At(0);
        list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_SENKOU_SPANB,EQUAL);
        buffer_data3=list.At(0);
        list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_CHIKOU_SPAN,EQUAL);
        buffer_data4=list.At(0);
        
        //--- Get the list of buffer objects which have ID of auxiliary line, and from it - buffer object with line number as 0
        list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_ADDITIONAL,EQUAL);
        list=CSelect::ByBufferProperty(list,BUFFER_PROP_IND_LINE_ADDITIONAL_NUM,0,EQUAL);
        buffer_tmp0=list.At(0);
        //--- Get the list of buffer objects which have ID of auxiliary line, and from it - buffer object with line number as 1
        list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_ADDITIONAL,EQUAL);
        list=CSelect::ByBufferProperty(list,BUFFER_PROP_IND_LINE_ADDITIONAL_NUM,1,EQUAL);
        buffer_tmp1=list.At(0);
        
        list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_TENKAN_SEN,EQUAL);
        buffer_calc0=list.At(0);
        list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_KIJUN_SEN,EQUAL);
        buffer_calc1=list.At(0);
        list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_SENKOU_SPANA,EQUAL);
        buffer_calc2=list.At(0);
        list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_SENKOU_SPANB,EQUAL);
        buffer_calc3=list.At(0);
        list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_CHIKOU_SPAN,EQUAL);
        buffer_calc4=list.At(0);
        
        if(buffer_calc0==NULL || buffer_data0==NULL || buffer_calc0.GetDataTotal(0)==0)
           return false;
        if(buffer_calc1==NULL || buffer_data1==NULL || buffer_calc1.GetDataTotal(0)==0)
           return false;
        if(buffer_calc2==NULL || buffer_data2==NULL || buffer_calc2.GetDataTotal(0)==0)
           return false;
        if(buffer_calc3==NULL || buffer_data3==NULL || buffer_calc3.GetDataTotal(0)==0)
           return false;
        if(buffer_calc4==NULL || buffer_data4==NULL || buffer_calc4.GetDataTotal(0)==0)
           return false;
        
        series_index_start=PreparingSetDataStdInd(buffer_data0,buffer_data1,buffer_data2,buffer_data3,buffer_data4,
                                                  buffer_calc0,buffer_calc1,buffer_calc2,buffer_calc3,buffer_calc4,
                                                  ind_type,series_index,series_time,index_period,num_bars,
                                                  value00,value01,value10,value11,value20,value21,value30,value31,value40,value41);
        if(series_index_start==WRONG_VALUE)
           return false;
        //--- In a loop, by the number of bars in  num_bars fill in the drawn buffer with a value from the calculated buffer taken by index_period index
        //--- and set the drawn buffer color depending on a proportion of value00 and value01 values
        for(int i=0;i<num_bars;i++)
          {
           index=series_index_start-i;
           buffer_data0.SetBufferValue(0,index,value00);
           buffer_data1.SetBufferValue(0,index,value10);
           buffer_data2.SetBufferValue(0,index,value20);
           buffer_data3.SetBufferValue(0,index,value30);
           buffer_data4.SetBufferValue(0,index,value40);
           buffer_data0.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value00>value01 ? 0 : value00<value01 ? 1 : 2) : color_index);
           buffer_data1.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value10>value11 ? 0 : value10<value11 ? 1 : 2) : color_index);
           buffer_data2.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value20>value21 ? 0 : value20<value21 ? 1 : 2) : color_index);
           buffer_data3.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value30>value31 ? 0 : value30<value31 ? 1 : 2) : color_index);
           buffer_data4.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value40>value41 ? 0 : value40<value41 ? 1 : 2) : color_index);
           
           //--- Set values for indicator auxiliary lines depending on mutual position of  Senkou Span A and Senkou Span B lines
           value_tmp0=buffer_data2.GetDataBufferValue(0,index);
           value_tmp1=buffer_data3.GetDataBufferValue(0,index);
           if(value_tmp0<value_tmp1)
             {
              buffer_tmp0.SetBufferValue(0,index,buffer_tmp0.EmptyValue());
              buffer_tmp0.SetBufferValue(1,index,buffer_tmp0.EmptyValue());
              
              buffer_tmp1.SetBufferValue(0,index,value_tmp0);
              buffer_tmp1.SetBufferValue(1,index,value_tmp1);
             }
           else
             {
              buffer_tmp0.SetBufferValue(0,index,value_tmp0);
              buffer_tmp0.SetBufferValue(1,index,value_tmp1);
              
              buffer_tmp1.SetBufferValue(0,index,buffer_tmp1.EmptyValue());
              buffer_tmp1.SetBufferValue(1,index,buffer_tmp1.EmptyValue());
             }
           
          }
        return true;
      
      case IND_GATOR    :
        list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,0,EQUAL);
        buffer_data0=list.At(0);
        list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,1,EQUAL);
        buffer_data1=list.At(0);
           
        list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,0,EQUAL);
        buffer_calc0=list.At(0);
        list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,1,EQUAL);
        buffer_calc1=list.At(0);
           
        if(buffer_calc0==NULL || buffer_data0==NULL || buffer_calc0.GetDataTotal(0)==0)
           return false;
        if(buffer_calc1==NULL || buffer_data1==NULL || buffer_calc1.GetDataTotal(0)==0)
           return false;
        
        series_index_start=PreparingSetDataStdInd(buffer_data0,buffer_data1,buffer_data2,buffer_data3,buffer_data4,
                                                  buffer_calc0,buffer_calc1,buffer_calc2,buffer_calc3,buffer_calc4,
                                                  ind_type,series_index,series_time,index_period,num_bars,
                                                  value00,value01,value10,value11,value20,value21,value30,value31,value40,value41);
        if(series_index_start==WRONG_VALUE)
           return false;
        //--- In a loop, by the number of bars in  num_bars fill in the drawn buffer with a value from the calculated buffer taken by index_period index
        //--- and set the drawn buffer color depending on a proportion of value00 and value01 values
        for(int i=0;i<num_bars;i++)
          {
           index=series_index_start-i;
           buffer_data0.SetBufferValue(0,index,value00);
           buffer_data1.SetBufferValue(1,index,value10);
           buffer_data0.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value00>value01 ? 0 : value00<value01 ? 1 : 2) : color_index);
           buffer_data1.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value10<value11 ? 0 : value10>value11 ? 1 : 2) : color_index);
          }
        return true;
      
      default:
        break;
     }
   return false;
  }
//+------------------------------------------------------------------+

Dies sind alle Änderungen, die wir heute vornehmen mussten.

Tests

Um die Erstellung des Indikatorobjekts zu überprüfen, nehmen wir den Testindikator aus dem vorherigen Artikel,
speichern ihn in einem neuen Ordner \MQL5\Indikatoren\TestDoEasy\TestDoEasyPart53\ unter einem neuen Namen TestDoEasyPart53.mq5 und ersetzen die Zeilen, die die Arbeit mit dem AD-Indikator anzeigen, durch die, die die Arbeit mit dem AC-Indikator anzeigen:

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Write the name of the working timeframe selected in the settings to InpUsedTFs variable
   InpUsedTFs=TimeframeDescription(InpPeriod);
//--- Initialize DoEasy library
   OnInitDoEasy();
   
//--- Set indicator global variables
   prefix=engine.Name()+"_";
   //--- Calculate the number of bars of the current period fitting in the maximum used period
   //--- Use the obtained value if it exceeds 2, otherwise use 2
   int num_bars=NumberBarsInTimeframe(InpPeriod);
   min_bars=(num_bars>2 ? num_bars : 2);

//--- Check and remove remaining indicator graphical objects
   if(IsPresentObectByPrefix(prefix))
      ObjectsDeleteAll(0,prefix);

//--- Create the button panel

//--- Check playing a standard sound using macro substitutions
   engine.PlaySoundByDescription(SND_OK);
//--- Wait for 600 milliseconds
   engine.Pause(600);
   engine.PlaySoundByDescription(SND_NEWS);

//--- indicator buffers mapping
//--- Create all necessary buffer objects to construct the selected standard indicator
   if(!engine.BufferCreateAC(InpUsedSymbols,InpPeriod,1))
     {
      Print(TextByLanguage("Error. Indicator not created"));
      return INIT_FAILED;
     }
//--- Check the number of buffers specified in the 'properties' block
   engine.CheckIndicatorsBuffers(indicator_buffers,indicator_plots);
      
//--- Create the color array and set non-default colors to all buffers within the collection
//--- (commented out since default colors are already set in methods of standard indicator creation)
//--- (we can always set required colors either for all indicators like here or for each one individually)
   //color array_colors[]={clrGreen,clrRed,clrGray};
   //engine.BuffersSetColors(array_colors);

//--- Display short descriptions of created indicator buffers
   engine.BuffersPrintShort();

//--- Set a short name for the indicator, data capacity and levels
   string label=engine.BufferGetIndicatorShortNameByTypeID(IND_AC,1);
   IndicatorSetString(INDICATOR_SHORTNAME,label);
   SetIndicatorLevels(InpUsedSymbols,IND_AC);

//--- Successful
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- Remove indicator graphical objects by an object name prefix
   ObjectsDeleteAll(0,prefix);
   Comment("");
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//+------------------------------------------------------------------+
//| OnCalculate code block for working with the library:             |
//+------------------------------------------------------------------+
//--- Pass the current symbol data from OnCalculate() to the price structure and set the "as timeseries" flag to the arrays
   CopyDataAsSeries(rates_total,prev_calculated,time,open,high,low,close,tick_volume,volume,spread);

//--- Check for the minimum number of bars for calculation
   if(rates_total<min_bars || Point()==0) return 0;
//--- Handle the Calculate event in the library
//--- If the OnCalculate() method of the library returns zero, not all timeseries are ready - leave till the next tick
   if(engine.OnCalculate(rates_data)==0)
      return 0;
   
//--- If work in the tester
   if(MQLInfoInteger(MQL_TESTER))
     {
      engine.OnTimer(rates_data);   // Work in the library timer
      engine.EventsHandling();      // Work with library events
     }
//+------------------------------------------------------------------+
//| OnCalculate code block for working with the indicator:           |
//+------------------------------------------------------------------+
//--- Check and calculate the number of calculated bars
//--- If limit = 0, there are no new bars - calculate the current one
//--- If limit = 1, a new bar has appeared - calculate the first and the current ones
//--- If limit > 1 means the first launch or changes in history - the full recalculation of all data
   int limit=rates_total-prev_calculated;
   
//--- Recalculate the entire history
   if(limit>1)
     {
      limit=rates_total-1;
      engine.BuffersInitPlots();
      engine.BuffersInitCalculates();
     }
//--- Prepare data
//--- Fill in calculated buffers of all created standard indicators with data
   int bars_total=engine.SeriesGetBarsTotal(InpUsedSymbols,InpPeriod);
   int total_copy=(limit<min_bars ? min_bars : fmin(limit,bars_total));
   if(!engine.BufferPreparingDataAllBuffersStdInd())
      return 0;

//--- Calculate the indicator
//--- Main calculation loop of the indicator
   for(int i=limit; i>WRONG_VALUE && !IsStopped(); i--)
     {
      engine.GetBuffersCollection().SetDataBufferStdInd(IND_AC,1,i,time[i]);
     }
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+

Das brauchen wir im Moment, um die Funktion der neu erstellten Klasse des abstrakten Indikatorobjekts im Testindikator zu überprüfen.
Der vollständige Indikatorcode ist in den unten angehängten Dateien enthalten.
Kompilieren Sie den Indikator und starten Sie ihn. Das Journal "Experten" zeigt die Daten zum erstellten Indikatorobjekt an:

Account 8550475: Artyom Trishkin (MetaQuotes Software Corp.) 10425.23 USD, 1:100, Hedge, Demo account MetaTrader 5
--- Initializing "DoEasy" library ---
Working with the current symbol only. Number of used symbols: 1
"EURUSD"
Working with the specified timeframe list:
"H4" "H1"
EURUSD symbol timeseries: 
- "EURUSD" H1 timeseries: Requested: 1000, Actually: 0, Created: 0, On the server: 0
- "EURUSD" H4 timeseries: Requested: 1000, Actually: 1000, Created: 1000, On the server: 6237
Time of library initializing: 00:00:00.156
 
============= Beginning of the parameter list: "Standard indicator" =============
Indicator status: Standard indicator
Indicator timeframe: H4
Indicator handle: 10
Indicator group: Oscillator 
------
Empty value for plotting where nothing will be drawn: EMPTY_VALUE
------
Indicator symbol: EURUSD
Indicator name: "Accelerator Oscillator"
Indicator short name: "AC(EURUSD,H4)"
================== End of the parameter list "Standard indicator" ==================
 
Buffer(P0/B0/C1): Histogram from the zero line EURUSD H4
Buffer[P0/B2/C2]: Calculated buffer
"EURUSD" H1 timeseries created successfully:
- "EURUSD" H1 timeseries: Requested: 1000, Actually: 1000, Created: 1000, On the server: 6256


Was kommt als Nächstes?

Im folgenden Artikel beginne ich mit der Erstellung von Klassen von abgeleiteten Objekten des heute erstellten abstrakten Indikator-Basisobjekts.

Alle Dateien der aktuellen Version der Bibliothek sind unten angehängt, zusammen mit der Testindikator-Datei für MQL5. Sie können sie herunterladen und alles testen.
Hinterlassen Sie Ihre Kommentare, Fragen und Anregungen in den Kommentaren zum Artikel.

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

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

Beigefügte Dateien |
MQL5.zip (3779.29 KB)
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 52): Plattformübergreifende Eigenschaft für Standardindikatoren mit einem Puffer für mehrere Symbole und Perioden

In diesem Artikel wird das Erstellen des Standardindikators Akkumulation/Distribution mehrere Symbole und Perioden behandelt. Wir verbessern die Bibliotheksklassen in Bezug auf die Indikatoren ein wenig, damit die für die veraltete Plattform MetaTrader 4 entwickelten Programme, die auf dieser Bibliothek basieren, beim Umstieg auf MetaTrader 5 normal funktionieren können.

Neuronale Netze leicht gemacht (Teil 3): Convolutional Neurale Netzwerke Neuronale Netze leicht gemacht (Teil 3): Convolutional Neurale Netzwerke

Als Fortsetzung des Themas Neuronale Netze schlage ich vor, Convolutional Neurale Netzwerke (faltende Neuronale Netzwerke) zu besprechen. Diese Art von Neuronalen Netzwerken wird in der Regel für die Analyse von visuellen Bildern verwendet. In diesem Artikel werden wir die Anwendung dieser Netzwerke auf den Finanzmärkten besprechen.

Brute-Force-Ansatz zur Mustersuche Brute-Force-Ansatz zur Mustersuche

In diesem Artikel werden wir nach Marktmustern suchen, Expert Advisors basierend auf den identifizierten Mustern erstellen und prüfen, wie lange diese Muster gültig bleiben, wenn sie überhaupt ihre Gültigkeit behalten.

Grid und Martingale: was sind sie und wie verwendet man sie? Grid und Martingale: was sind sie und wie verwendet man sie?

In diesem Artikel werde ich versuchen, im Detail zu erklären, was Grid und Martingale sind, sowie was sie gemeinsam haben. Außerdem werde ich versuchen zu analysieren, wie praktikabel diese Strategien wirklich sind. Der Artikel enthält mathematische und praktische Teile.