Zeitreihen in der Bibliothek DoEasy (Teil 55): Die Kollektionsklasse der Indikatoren

7 Januar 2021, 07:17
Artyom Trishkin
0
116

Inhaltsverzeichnis


Konzept

In diesem Artikel werden wir die abgeleiteten Klassen des abstrakten Indikator-Basisobjekts fertigstellen, mit dessen Entwicklung wir im vorherigen Artikel begonnen haben.
Dem allgemeinen Konzept der Konstruktion von Bibliotheksobjekten folgend und damit sich die Organisation der Indikatorobjekte nicht von anderen Bibliotheksobjekten unterscheidet, müssen wir in die Indikatorobjekte deren Beschreibung einfügen. Nebenbei verbessern wir die Speicherung dieser Objekte in ihrer Kollektion, d. h. wir beseitigen die Mängel, die wir im vorherigen Artikel bei der Erstellung von Kollektionen von Indikatoren und beim Hinzufügen von Objekten zur Kollektion gemacht haben.

Beachten Sie, dass das Basisobjekt des abstrakten Indikators und die Indikatorobjekte, die Nachkommen dieses Objekts sind, unabhängige Subjekte sind, die sich nicht mit den Multi-Symbol-Multi-Perioden-Indikatoren überschneiden, die wir früher erstellt haben, um sie bei der Erstellung unserer benutzerdefinierten Indikatoren mit Hilfe der Bibliothek zu verwenden.

Das abstrakte Indikatorobjekt und seine Nachkommen sind Indikatorobjekte, die wir für Indikator-EAs und für die Suche nach verschiedenen Kombinationen von Daten und Zuständen verschiedener Indikatorwerte verwenden müssen.


Verbesserung der Bibliothek der Klasse

Wie üblich fügen wir bei der Entwicklung von Bibliotheksobjektklassen zunächst die erforderlichen Textmeldungen hinzu, die von den Objekten verwendet werden, wenn ihre Beschreibungen angezeigt werden. Für Indikatorobjekte werden Meldungen zur Anzeige aller möglichen Parameter aller Standardindikatoren benötigt.
Fügen wir in die DateiData.mqh die Indizes der neuen Meldungen hinzu:

   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
   
   MSG_LIB_TEXT_IND_TEXT_IND_PARAMETERS,              // Indicator parameters
   MSG_LIB_TEXT_IND_TEXT_APPLIED_VOLUME,              // Volume type for calculation
   MSG_LIB_TEXT_IND_TEXT_PERIOD,                      // Averaging period
   MSG_LIB_TEXT_IND_TEXT_FAST_PERIOD,                 // Fast MA period
   MSG_LIB_TEXT_IND_TEXT_SLOW_PERIOD,                 // Slow MA period
   MSG_LIB_TEXT_IND_TEXT_SIGNAL,                      // Difference averaging period
   MSG_LIB_TEXT_IND_TEXT_TENKAN_PERIOD,               // Tenkan-sen period
   MSG_LIB_TEXT_IND_TEXT_KIJUN_PERIOD,                // Kijun-sen period
   MSG_LIB_TEXT_IND_TEXT_SPANB_PERIOD,                // Senkou Span B period
   MSG_LIB_TEXT_IND_TEXT_JAW_PERIOD,                  // Period for jaw line calculation
   MSG_LIB_TEXT_IND_TEXT_TEETH_PERIOD,                // Period for teeth line calculation
   MSG_LIB_TEXT_IND_TEXT_LIPS_PERIOD,                 // Period for lips line calculation
   MSG_LIB_TEXT_IND_TEXT_JAW_SHIFT,                   // Horizontal shift of jaws line
   MSG_LIB_TEXT_IND_TEXT_TEETH_SHIFT,                 // Horizontal shift of teeth line
   MSG_LIB_TEXT_IND_TEXT_LIPS_SHIFT,                  // Horizontal shift of lips line
   MSG_LIB_TEXT_IND_TEXT_SHIFT,                       // Horizontal shift of the indicator
   MSG_LIB_TEXT_IND_TEXT_MA_METHOD,                   // Smoothing type
   MSG_LIB_TEXT_IND_TEXT_APPLIED_PRICE,               // Price type or handle
   MSG_LIB_TEXT_IND_TEXT_STD_DEVIATION,               // Number of standard deviations
   MSG_LIB_TEXT_IND_TEXT_DEVIATION,                   // Deviation of channel borders from the central line
   MSG_LIB_TEXT_IND_TEXT_STEP,                        // Price change step — acceleration factor
   MSG_LIB_TEXT_IND_TEXT_MAXIMUM,                     // Maximum step
   MSG_LIB_TEXT_IND_TEXT_KPERIOD,                     // K-period (number of bars for calculation)
   MSG_LIB_TEXT_IND_TEXT_DPERIOD,                     // D-period (primary smoothing period)
   MSG_LIB_TEXT_IND_TEXT_SLOWING,                     // Final smoothing
   MSG_LIB_TEXT_IND_TEXT_PRICE_FIELD,                 // Stochastic calculation method
   MSG_LIB_TEXT_IND_TEXT_CMO_PERIOD,                  // Chande Momentum period
   MSG_LIB_TEXT_IND_TEXT_SMOOTHING_PERIOD,            // Smoothing factor period
   
//--- CIndicatorsCollection
   MSG_LIB_SYS_FAILED_ADD_IND_TO_LIST,                // Error. Failed to add indicator object to the list
   
  };
//+------------------------------------------------------------------+

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

   {"Empty value for plotting, for which there is no drawing"},
   {"Indicator symbol"},
   {"Indicator name"},
   {"Indicator shortname"},
   
   {"Indicator parameters"},
   {"Volume type for calculation"},
   {"Averaging period"},
   {"Fast MA period"},
   {"Slow MA period"},
   {"Averaging period for their difference"},
   {"Tenkan-sen period"},
   {"Kijun-sen period"},
   {"Senkou Span B period"},
   {"Period for the calculation of jaws"},
   {"Period for the calculation of teeth"},
   {"Period for the calculation of lips"},
   {"Horizontal shift of jaws"},
   {"Horizontal shift of teeth"},
   {"Horizontal shift of lips"},
   {"Horizontal shift of the indicator"},
   {"Smoothing type"},
   {"Price type or handle"},
   {"Number of standard deviations"},
   {"Deviation of boundaries from the midline"},
   {"Price increment step - acceleration factor"},
   {"Maximum value of step"},
   {"K-period (number of bars for calculations)"},
   {"D-period (period of first smoothing)"},
   {"Final smoothing"},
   {"Stochastic calculation method"},
   {"Chande Momentum period"},
   {"Smoothing factor period"},
   
   {"Error. Failed to add indicator object to list"},
   
  };
//+---------------------------------------------------------------------+

Um bestimmte Parameter des Indikators anzuzeigen, wie z. B. die Methode der Mittelwertbildung, die Art des Preises und des Volumens für die Berechnung usw., fügen wir einige Funktionen in der Datei der Servicefunktionen der Bibliothek in \MQL5\Include\DoEasy\Services\DELib.mqh hinzu:

//+------------------------------------------------------------------+
//| Return timeframe description                                   |
//+------------------------------------------------------------------+
string TimeframeDescription(const ENUM_TIMEFRAMES timeframe)
  {
   return StringSubstr(EnumToString((timeframe>PERIOD_CURRENT ? timeframe : (ENUM_TIMEFRAMES)Period())),7);
  }
//+------------------------------------------------------------------+
//| Return volume description for calculation                           |
//+------------------------------------------------------------------+
string AppliedVolumeDescription(const ENUM_APPLIED_VOLUME volume)
  {
   return StringSubstr(EnumToString(volume),7);
  }
//+------------------------------------------------------------------+
//| Return indicator type description                              |
//+------------------------------------------------------------------+
string IndicatorTypeDescription(const ENUM_INDICATOR indicator)
  {
   return StringSubstr(EnumToString(indicator),4);
  }
//+------------------------------------------------------------------+
//| Return averaging method description                            |
//+------------------------------------------------------------------+
string AveragingMethodDescription(const ENUM_MA_METHOD method)
  {
   return StringSubstr(EnumToString(method),5);
  }
//+------------------------------------------------------------------+
//| Return applied price description                            |
//+------------------------------------------------------------------+
string AppliedPriceDescription(const ENUM_APPLIED_PRICE price)
  {
   return StringSubstr(EnumToString(price),6);
  }
//+------------------------------------------------------------------+
//| Return stochastic price calculation description                      |
//+------------------------------------------------------------------+
string StochPriceDescription(const ENUM_STO_PRICE price)
  {
   return StringSubstr(EnumToString(price),4);
  }
//+------------------------------------------------------------------+

Es ist einfach: die Teilzeichenkette wird von der gewünschten Position aus der Textdarstellung eines Enumerationswertes abgerufen und erhält am Ende den Namen des Indikators, die Berechnungsmethode oder Volumentyp und Preis.

Jeder Indikator besitzt einen bestimmten Satz von Parametern. Diese Parameter können für den Indikator mit Hilfe des Arrays der Indikatorparameterstrukturen MqlParam eingestellt werden. Dies geschieht bei der Erstellung eines jeden Indikatorobjekts. Dementsprechend können für jeden Indikator alle Werte des Arrays dieser Strukturen ins Journal geschrieben werden. Die Daten des Wertes der Parameter, die nur zu diesem Indikatortyp gehören, werden für verschiedene Indikatoren in jeder Arrayzelle verfügbar sein. Aber für mehrere Indikatoren desselben Typs werden in jeder Arrayzelle die Eigenschaften angegeben, die durch ihren Zweck gleich sind und sich nur durch ihren Wert unterscheiden.
So kann für jeden Indikator eine Methode geschrieben werden, die im Journal einen Satz von Indikatorparametern mit den für sie eingestellten Werten anzeigt. Dies gilt nur für Standard-Indikatoren, da wir für sie definitiv einen Satz von Parametern für jeden spezifischen Indikator kennen.

Dies wird eine virtuelle Methode sein. Wir schreiben sie in die abstrakte Indikator-Objektklasse in \MQL5\Include\DoEasy\Objects\Indicators\IndicatorDE.mqh:

//--- 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 (1) a short description, (2) description of indicator object parameters in the journal (implementation in the descendants)
   virtual void      PrintShort(void) {;}
   virtual void      PrintParameters(void) {;}

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

In dieser Klasse macht diese Methode nichts. Und die Implementierung der Methode zur Anzeige der Indikatordaten im Journal erfolgt in den abgeleiteten Objekten.
Jede abgeleitete Klasse wird ihre eigene Methode besitzen, da jeder Indikator seinen eigenen Parametersatz hat.

Im geschlossenen parametrischen Konstruktor ersetzen wir die Zeichenkette, um die Beschreibung des Indikatortyps zu erhalten

   this.m_ind_type=::StringSubstr(::EnumToString(ind_type),4);

sodass wir die oben beschriebene Beschreibung mit Hilfe einer neuen Servicefunktion erhalten:

//+------------------------------------------------------------------+
//| 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 &mql_params[])
  {
//--- Set collection ID for the object
   this.m_type=COLLECTION_INDICATORS_ID;
//--- Write description of indicator type
   this.m_ind_type_description=IndicatorTypeDescription(ind_type);
//--- If parameter array size 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(this.m_mql_param,::ArraySize(mql_params));
   for(int i=0;i<count;i++)
     {
      this.m_mql_param[i].type         = mql_params[i].type;
      this.m_mql_param[i].double_value = mql_params[i].double_value;
      this.m_mql_param[i].integer_value= mql_params[i].integer_value;
      this.m_mql_param[i].string_value = mql_params[i].string_value;
     }
//--- Create indicator handle
   int handle=::IndicatorCreate(symbol,timeframe,ind_type,count,this.m_mql_param);
   
//--- Save integer properties
   this.m_long_prop[INDICATOR_PROP_STATUS]                     = status;
   this.m_long_prop[INDICATOR_PROP_TYPE]                       = ind_type;
   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;
  }
//+------------------------------------------------------------------+

In der Methode, die Indikatoreigenschaften ins Journal schreibt, fügen wir am Ende der Auflistung nach der Darstellung aller Eigenschaften des Indikatorobjekts den Aufruf der Methode hinzu, die im Journal die von Ihnen gewählten Werte der Indikatorparameter anzeigt:

//+------------------------------------------------------------------+
//| 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));
     }
   this.PrintParameters();
   ::Print("================== ",CMessage::Text(MSG_LIB_PARAMS_LIST_END),": \"",this.GetStatusDescription(),"\" ==================\n");
  }
//+------------------------------------------------------------------+

Wenn nun die Methode PrintPatameters() in der abgeleiteten Klasse des abstrakten Basisobjekts des Indikators vorhanden ist, wird beim Aufruf der Methode Print() die virtuelle Methode PrintPatameters() von der abgeleiteten Klasse aufgerufen, in der die Proto0kollierung der Indikatorparameter im Journal implementiert wird.

Da jeder Indikatortyp seinen eigenen Parametersatz besitzt, müssen wir in jede abgeleitete Klasse eine eigene Methode PrintPatameters() implementieren.
Solche Methoden sind bereits für jedes Indikatorobjekt geschrieben. Sie sind von der Logik her alle vom gleichen Typ, unterscheiden sich aber im Inhalt. Die Methoden sind für alle Indikatoren geschrieben, außer für einen - den nutzerdefinierten Indikator, weil sich die Methodenimplementierung für ihn aus dem Grund unterscheiden wird, dass wir den Satz der Indikatorparameter im Gegensatz zu allen Standardindikatoren nicht im Voraus kennen können.
Analysieren wir diese Methoden für jedes der abhängigen Objekte.

Die Klasse des Indikatorobjekts Accelerator Oscillator:

//+------------------------------------------------------------------+
//|                                                        IndAC.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"
//+------------------------------------------------------------------+
//| Include files                                                 |
//+------------------------------------------------------------------+
#include "..\\IndicatorDE.mqh"
//+------------------------------------------------------------------+
//| Standard indicator Accelerator Oscillator                     |
//+------------------------------------------------------------------+
class CIndAC : public CIndicatorDE
  {
private:

public:
   //--- Constructor
                     CIndAC(const string symbol,const ENUM_TIMEFRAMES timeframe,MqlParam &mql_param[]) : 
                        CIndicatorDE(IND_AC,symbol,timeframe,
                                     INDICATOR_STATUS_STANDART,
                                     INDICATOR_GROUP_OSCILLATOR,
                                     "Accelerator Oscillator",
                                     "AC("+symbol+","+TimeframeDescription(timeframe)+")",mql_param) {}
   //--- Supported indicator properties (1) real, (2) integer
   virtual bool      SupportProperty(ENUM_INDICATOR_PROP_DOUBLE property);
   virtual bool      SupportProperty(ENUM_INDICATOR_PROP_INTEGER property);

//--- Display (1) a short description, (2) description of indicator object parameters in the journal
   virtual void      PrintShort(void);
   virtual void      PrintParameters(void) {;}
  };
//+------------------------------------------------------------------+
//| Return 'true' if indicator supports a passed        |
//| integer property, otherwise return 'false'       |
//+------------------------------------------------------------------+
bool CIndAC::SupportProperty(ENUM_INDICATOR_PROP_INTEGER property)
  {
   return true;
  }
//+------------------------------------------------------------------+
//| Return 'true' if indicator supports a passed        |
//| real property, otherwise return 'false'        |
//+------------------------------------------------------------------+
bool CIndAC::SupportProperty(ENUM_INDICATOR_PROP_DOUBLE property)
  {
   return true;
  }
//+------------------------------------------------------------------+
//| Display a short description of indicator object in the journal             |
//+------------------------------------------------------------------+
void CIndAC::PrintShort(void)
  {
   ::Print(GetStatusDescription()," ",this.Name()," ",this.Symbol()," ",TimeframeDescription(this.Timeframe())," [",this.Handle(),"]");
  }
//+------------------------------------------------------------------+

Hier wird die virtuelle Methode für die Parameteranzeige nur deklariert, sie ist leer, da der AC-Indikator keine Eingabeparameter hat. Sie könnte hier weggelassen werden, denn wenn die virtuelle Methode in der Nachfolgeklasse fehlt, wird die virtuelle Methode der Elternklasse aufgerufen. Aber hier haben wir es so geschrieben, dass alle abgeleiteten Klassen die gleiche Struktur ihrer Methoden haben.
In der Anzeigemethode der Indikator-Kurzbeschreibung fügen wir die Anzeige des erstellten Indikator-Handles für dieses Objekt hinzu. Solche Änderungen in dieser Methode werden für alle Indikatorobjekte vorgenommen. Wir werden sie nicht weiter analysieren. Stattdessen werden wir nur die Methoden zur Anzeige der Beschreibung für die Indikatorparameter analysieren.

Die Klasse des Indikatorobjekts Akkumulation/Ausschüttung und seine Methode der Beschreibungsanzeige der Indikatorparameter:

//+------------------------------------------------------------------+
//|                                                        IndAD.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"
//+------------------------------------------------------------------+
//| Include files                                                 |
//+------------------------------------------------------------------+
#include "..\\IndicatorDE.mqh"
//+------------------------------------------------------------------+
//| Standard indicator Accumulation/Distribution                  |
//+------------------------------------------------------------------+
class CIndAD : public CIndicatorDE
  {
private:

public:
   //--- Constructor
                     CIndAD(const string symbol,const ENUM_TIMEFRAMES timeframe,MqlParam &mql_param[]) :
                        CIndicatorDE(IND_AD,symbol,timeframe,
                                     INDICATOR_STATUS_STANDART,
                                     INDICATOR_GROUP_VOLUMES,
                                     "Accumulation/Distribution",
                                     "AD("+symbol+","+TimeframeDescription(timeframe)+")",mql_param) {}
   //--- Supported indicator properties (1) real, (2) integer
   virtual bool      SupportProperty(ENUM_INDICATOR_PROP_DOUBLE property);
   virtual bool      SupportProperty(ENUM_INDICATOR_PROP_INTEGER property);
   
//--- Display (1) a short description, (2) description of indicator object parameters in the journal
   virtual void      PrintShort(void);
   virtual void      PrintParameters(void);
  };
//+------------------------------------------------------------------+
//| Return 'true' if indicator supports a passed        |
//| integer property, otherwise return 'false'       |
//+------------------------------------------------------------------+
bool CIndAD::SupportProperty(ENUM_INDICATOR_PROP_INTEGER property)
  {
   return true;
  }
//+------------------------------------------------------------------+
//| Return 'true' if indicator supports a passed        |
//| real property, otherwise return 'false'        |
//+------------------------------------------------------------------+
bool CIndAD::SupportProperty(ENUM_INDICATOR_PROP_DOUBLE property)
  {
   return true;
  }
//+------------------------------------------------------------------+
//| Display a short description of indicator object in the journal             |
//+------------------------------------------------------------------+
void CIndAD::PrintShort(void)
  {
   ::Print(GetStatusDescription()," ",this.Name()," ",this.Symbol()," ",TimeframeDescription(this.Timeframe())," [",this.Handle(),"]");
  }
//+------------------------------------------------------------------+
//| Display parameter description of indicator object in the journal          |
//+------------------------------------------------------------------+
void CIndAD::PrintParameters(void)
  {
   ::Print(" --- ",CMessage::Text(MSG_LIB_TEXT_IND_TEXT_IND_PARAMETERS)," --- ");
   //--- applied_volume
   ::Print(" - ",CMessage::Text(MSG_LIB_TEXT_IND_TEXT_APPLIED_VOLUME),": ",AppliedVolumeDescription((ENUM_APPLIED_VOLUME)m_mql_param[0].integer_value));
  }
//+------------------------------------------------------------------+

Der Indikator Akkumulation/Verteilung besitzt nur einen Eingabeparameter - den Volumentyp für die Berechnung. Deshalb besitzt das Array der Struktur der Eingabeparameter nur eine Zelle, die diesen Parameter speichert. Um ihn im Journal anzuzeigen, holen wir ihn aus dem Array mit dem Index 0 aus den ganzzahligen Daten der Struktur und senden ihn mit der oben beschriebenen Servicefunktion zum Journal. Beschreiben wir den Parameterwert vor der Anzeige aus früher geschriebenen Textmeldungen der Bibliothek.

Analysieren wir ferner nur die Methoden der Beschreibungsanzeige der Eingabeparameter des Indikators.

Die Methode der Beschreibungsdarstellung für Klassenindikatorparameter des Indikatorobjekts Average Directional Index:

//+------------------------------------------------------------------+
//| Display parameter description of indicator object in the journal          |
//+------------------------------------------------------------------+
void CIndADX::PrintParameters(void)
  {
   ::Print(" --- ",CMessage::Text(MSG_LIB_TEXT_IND_TEXT_IND_PARAMETERS)," --- ");
   //--- adx_period
   ::Print(" - ",CMessage::Text(MSG_LIB_TEXT_IND_TEXT_PERIOD),": ",(string)m_mql_param[0].integer_value);
  }
//+------------------------------------------------------------------+

Der ADX-Indikator besitzt nur einen Eingabeparameter, nämlich den Berechnungszeitraum. Deshalb wird auf die gleiche Weise die Beschreibung nur einer Array-Zelle mit dem Index 0 aus den Integer-Daten der Struktur angezeigt.

Die Methode der Anzeige der Beschreibung für die Parameter der Klasse Indikator des Indikatorobjekts Alligator:

//+------------------------------------------------------------------+
//| Display parameter description of indicator object in the journal          |
//+------------------------------------------------------------------+
void CIndAlligator::PrintParameters(void)
  {
   ::Print(" --- ",CMessage::Text(MSG_LIB_TEXT_IND_TEXT_IND_PARAMETERS)," --- ");
   //--- jaw_period
   ::Print(" - ",CMessage::Text(MSG_LIB_TEXT_IND_TEXT_JAW_PERIOD),": ",(string)m_mql_param[0].integer_value);
   //--- jaw_shift
   ::Print(" - ",CMessage::Text(MSG_LIB_TEXT_IND_TEXT_JAW_SHIFT),": ",(string)m_mql_param[1].integer_value);
   //--- teeth_period
   ::Print(" - ",CMessage::Text(MSG_LIB_TEXT_IND_TEXT_TEETH_PERIOD),": ",(string)m_mql_param[2].integer_value);
   //--- teeth_shift
   ::Print(" - ",CMessage::Text(MSG_LIB_TEXT_IND_TEXT_TEETH_SHIFT),": ",(string)m_mql_param[3].integer_value);
   //--- lips_period
   ::Print(" - ",CMessage::Text(MSG_LIB_TEXT_IND_TEXT_LIPS_PERIOD),": ",(string)m_mql_param[4].integer_value);
   //--- lips_shift
   ::Print(" - ",CMessage::Text(MSG_LIB_TEXT_IND_TEXT_LIPS_SHIFT),": ",(string)m_mql_param[5].integer_value);
   //--- ma_method
   ::Print(" - ",CMessage::Text(MSG_LIB_TEXT_IND_TEXT_MA_METHOD),": ",AveragingMethodDescription((ENUM_MA_METHOD)m_mql_param[6].integer_value));
   //--- applied_price
   ::Print(
           " - ",CMessage::Text(MSG_LIB_TEXT_IND_TEXT_APPLIED_PRICE),": ",
           (m_mql_param[7].integer_value<10 ? AppliedPriceDescription((ENUM_APPLIED_PRICE)m_mql_param[7].integer_value) : (string)m_mql_param[7].integer_value)
          );
  }
//+------------------------------------------------------------------+

Der Indikator Alligator verfügt über acht Eingabeparameter, deshalb werden sie nacheinander in der Reihenfolge der Erstellung des Indikators angezeigt:

  • Berechnungszeitraum der "Jaws"-Linie - Arrayindex 0
  • Horizontale Verschiebung der Linie "Jaws" - Arrayindex 1
  • Berechnungszeitraum der Linie "Teeth" - Arrayindex 2
  • Horizontale Verschiebung der Linie "Teeth" - Arrayindex 3
  • Berechnungszeitraum der Linie "Lips" - Arrayindex 4
  • Horizontale Verschiebung der Linie "Lips" - Arrayindex 5
  • Glättungsart - Arrayindex 6
  • Preistyp oder Indikatorhandles - Arrayindex 7

Alle Daten besitzen den Typ Integer, daher wird die Beschreibung der Struktur Integer-Daten aus den entsprechenden Array-Zellen angezeigt.
Der letzte Parameter kann entweder den Typ des Preises speichern, nach dem der Indikator konstruiert ist, oder das Indikator-Handle, auf dem die Daten Alligator konstruiert sind. Prüfen wir daher zuerst den Wert in der Arrayzelle. Wenn der Wert kleiner als 10 ist, bedeutet das, dass der Indikator auf einem der möglichen Preistypen aufgebaut ist und wir zeigen die Beschreibung des Preistyps an. Wenn der Wert 10 und mehr ist, bedeutet es, dass der Indikator auf den Daten eines anderen Indikators konstruiert ist, dessen Handle im Array geschrieben ist - Anzeige des Handle-Wertes dieses Indikators.

Die Methode der Anzeige der Beschreibung für die Parameter der Klasse Indikator des Indikatorobjekts Hüllkurven:

//+------------------------------------------------------------------+
//| Display parameter description of indicator object in the journal          |
//+------------------------------------------------------------------+
void CIndEnvelopes::PrintParameters(void)
  {
   ::Print(" --- ",CMessage::Text(MSG_LIB_TEXT_IND_TEXT_IND_PARAMETERS)," --- ");
   //--- ma_period
   ::Print(" - ",CMessage::Text(MSG_LIB_TEXT_IND_TEXT_PERIOD),": ",(string)m_mql_param[0].integer_value);
   //--- ma_shift
   ::Print(" - ",CMessage::Text(MSG_LIB_TEXT_IND_TEXT_SHIFT),": ",(string)m_mql_param[1].integer_value);
   //--- ma_method
   ::Print(" - ",CMessage::Text(MSG_LIB_TEXT_IND_TEXT_MA_METHOD),": ",AveragingMethodDescription((ENUM_MA_METHOD)m_mql_param[2].integer_value));
   //--- applied_price
   ::Print(
           " - ",CMessage::Text(MSG_LIB_TEXT_IND_TEXT_APPLIED_PRICE),": ",
           (m_mql_param[3].integer_value<10 ? AppliedPriceDescription((ENUM_APPLIED_PRICE)m_mql_param[3].integer_value) : (string)m_mql_param[3].integer_value)
          );
   //--- deviation
   ::Print(" - ",CMessage::Text(MSG_LIB_TEXT_IND_TEXT_DEVIATION),": ",::DoubleToString(m_mql_param[4].double_value,3));
   
  }
//+------------------------------------------------------------------+

Der Indikator Envelopes besitzt fünf Eingabeparameter. Der letzte Parameter "Abweichung der Kanalgrenzen von der Mittellinie" ist vom Typ double. Deshalb, um die Beschreibung dieses Parameters anzuzeigen, nehmen wir den Wert aus reellen Daten der Struktur der Eingabeparameter.

Alle übrigen Methoden der Indikatorobjektklassen wiederholen die Logik der oben besprochenen und wir werden ihre Methoden nicht analysieren. Sie können sie in den Dateien im Anhang zu diesem Artikel studieren.

Wir tragen alle erstellten Indikatorobjekte in die Kollektionsliste der Indikatoren ein. Im Terminal wird beim Erstellen einer beliebigen Anzahl von Indikatoren mit identischen Parametern tatsächlich ein Indikator erstellt und alle Aufrufe gehen zu ihm. Deshalb müssen wir bei der Erstellung von Indikatorobjekten und deren Eintrag in der Kollektionsliste kontrollieren, ob in der Kollektionsliste derselbe Indikator mit demselben Typ, Symbol/Zeitrahmen und denselben Parametern wie derjenige, den wir in die Liste einfügen wollen, vorhanden ist. Das liegt einfach daran, dass absolut gleiche Indikatorobjekte für alle das gleiche Handle des erstellten Indikators haben werden. Das bedeutet, dass es sich um ein und dasselbe Indikatorobjekt handelt.

Wir fügen die notwendigen Änderungen in die Kollektionsklasse der Indikatorobjekte in der Datei \MQL5\Include\DoEasy\Collections\IndicatorsCollection.mqh hinzu, mit deren Implementierung wir im vorherigen Artikel begonnen haben. Um zwei Indikatorobjekte zu suchen und zu vergleichen, haben wir die Methode Search() der Klasse des dynamischen Arrays von Zeigern auf Instanzen der Klasse CObject und ihrer Abkömmlinge aus der Standardauslieferung der Bibliothek verwendet. Diese Methode kann aber die Gleichheit der beiden Objekte, die Strukturen enthalten, nicht eindeutig feststellen. Der Zweck dieser Methode ist es, eine bestimmte Eigenschaft von zwei gleichartigen Objekten zu vergleichen. In Indikatorobjekten wird aktiv das Array von Parameterstrukturen MqlParam des Indikators verwendet, in dem jede Eigenschaft der Struktur im Array Element für Element verglichen werden muss. Glücklicherweise gibt es in allen Bibliotheksobjekten eine Standardmethode IsEqual() für den präzisen Vergleich von zwei gleichartigen Objekten. Um zwei gleichartige Objekte auf Gleichheit zu vergleichen, wird diese Methode verwendet.

Im 'private' Teil der Klasse deklarieren wir die Methode, die den Index des Indikatorobjekts in der Kollektionsliste zurückgibt:

//+------------------------------------------------------------------+
//| Indicator collection                                            |
//+------------------------------------------------------------------+
class CIndicatorsCollection : public CObject
  {
private:
   CListObj                m_list;                       // Indicator object list
   MqlParam                m_mql_param[];                // Array of indicator parameters

//--- Create a new indicator object
   CIndicatorDE           *CreateIndicator(const ENUM_INDICATOR ind_type,MqlParam &mql_param[],const string symbol_name=NULL,const ENUM_TIMEFRAMES period=PERIOD_CURRENT);
//--- Return the indicator index in the list
   int                     Index(CIndicatorDE *compared_obj);

public:

Ganz am Ende des Codes des Klassenkörpers deklarieren wir zwei 'public' Methoden - zur Anzeige von vollständigen und kurzen Beschreibungen der Indikatorobjekte, die sich in der Kollektionsliste befinden:

//--- Display (1) the complete and (2) short collection description in the journal
   void                    Print(void);
   void                    PrintShort(void);

//--- Constructor
                           CIndicatorsCollection();

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

Implementieren wir die deklarierten Methoden außerhalb des Klassenkörpers.

Die Methode, die die vollständige Beschreibung der Kollektion in der Zeitschrift anzeigt:

//+------------------------------------------------------------------+
//| Display full collection description in the journal                       |
//+------------------------------------------------------------------+
void CIndicatorsCollection::Print(void)
  {
   int total=this.m_list.Total();
   for(int i=0;i<total;i++)
     {
      CIndicatorDE *ind=m_list.At(i);
      if(ind==NULL)
         continue;
      ind.Print();
     }
  }
//+------------------------------------------------------------------+

In einer Schleife über die Kollektionsliste holen wir uns ein anderes Indikatorobjekt und schreiben dessen vollständige Beschreibung ins Journal.

Die Methode, die die Kurzbeschreibung der Kollektion ins Journal schreibt:

//+------------------------------------------------------------------+
//| Display the short collection description in the journal                      |
//+------------------------------------------------------------------+
void CIndicatorsCollection::PrintShort(void)
  {
   int total=this.m_list.Total();
   for(int i=0;i<total;i++)
     {
      CIndicatorDE *ind=m_list.At(i);
      if(ind==NULL)
         continue;
      ind.PrintShort();
     }
  }
//+------------------------------------------------------------------+

In einer Schleife über die Kollektionsliste holen wir uns ein anderes Indikatorobjekt und schreiben dessen Kurzbeschreibung ins Journal.

Die Methode gibt den Index des Indikatorobjekts in der Kollektionsliste zurück
:

//+------------------------------------------------------------------+
//| Return the indicator index in the list                            |
//+------------------------------------------------------------------+
int CIndicatorsCollection::Index(CIndicatorDE *compared_obj)
  {
   int total=this.m_list.Total();
   for(int i=0;i<total;i++)
     {
      CIndicatorDE *indicator=m_list.At(i);
      if(indicator==NULL)
         continue;
      if(indicator.IsEqual(compared_obj))
         return i;
     }
   return WRONG_VALUE;
  }
//+------------------------------------------------------------------+

In einer Schleife über Kollektionsliste holen wir uns ein anderes Indikatorobjekt, vergleichen es mit dem Indikatorobjekt, übergeben den Zeiger der Methode und geben den Schleifenindex zurück, wenn die Objekte gleich sind. Nach Beendigung der Schleife (wenn alle Objekte ungleich sind) wird -1 zurückgegeben.

In allen Methoden der Erzeugung neuer Indikatorobjekte und deren Platzierung in der Kollektionsliste wurden die für jede Methode identischen Änderungen vorgenommen. Dies wird gemacht, um mögliche Speicherlecks bei erfolgloser Erstellung des Objekts oder seiner erfolglosen Platzierung in die Liste auszuschließen.

Nehmen wir als Beispiel die Methode der Erstellung des Indikators Accelerator Oscillator:

//+------------------------------------------------------------------+
//| Create a new indicator object Accelerator Oscillator            |
//| and place it to the collection list                                |
//+------------------------------------------------------------------+
int CIndicatorsCollection::CreateAC(const string symbol,const ENUM_TIMEFRAMES timeframe)
  {
//--- AC indicator possesses no parameters - resize the array of parameter structures
   ::ArrayResize(this.m_mql_param,0);
//--- Create indicator object
   CIndicatorDE *indicator=this.CreateIndicator(IND_AC,this.m_mql_param,symbol,timeframe);
   if(indicator==NULL)
      return INVALID_HANDLE;
//--- If such indicator is already in the list
   int index=this.Index(indicator);
   if(index!=WRONG_VALUE)
     {
      //--- Remove created object, get indicator object from the list and return indicator handle
      delete indicator;
      indicator=this.m_list.At(index);
      return indicator.Handle();
     }
//--- If such indicator is not in the list
   else
     {
      //--- If failed to add indicator object to the list
      //--- display the appropriate message, remove object and return INVALID_HANDLE
      if(!this.m_list.Add(indicator))
        {
         ::Print(CMessage::Text(MSG_LIB_SYS_FAILED_ADD_IND_TO_LIST));
         delete indicator;
         return INVALID_HANDLE;
        }
      //--- Return the handle of a new indicator added to the list
      return indicator.Handle();
     }
//--- Return INVALID_HANDLE
   return INVALID_HANDLE;
  }
//+------------------------------------------------------------------+

Verfügbarkeit des Indikatorobjekts in der Liste anhand seines Index prüfen.
Wenn der Index größer als -1
ist, bedeutet das, dass es in der Liste ist und das neu erstellte Objekt muss entfernt werden.
Wenn ein solcher Indikator nicht in der Liste ist und wir ihn aus irgendeinem Grund nicht in die Kollektionsliste aufnehmen konnten,
wird das neu erstellte Objekt entfernt.
Dadurch werden Speicherlecks im Falle einer erfolglosen Platzierung des neuen Objekts in die Kollektionsliste ausgeschlossen.

Solche Änderungen wurden in allen Methoden der Erzeugung von Indikatorobjekten vorgenommen, und wir werden sie nicht analysieren. Sie können sie in den Dateien im Anhang zu diesem Artikel studieren.

Um ein Indikatorobjekt in der Kollektionsliste zu suchen und einen Zeiger darauf aus dieser Liste zu erhalten, benötigen wir die Methoden, die Zeiger auf das gewünschte Objekt zurückgeben. An die Methode übergeben wir den Typ des benötigten Indikators, sein Symbol, Zeitrahmen und Parameter (für jeden Indikator entsprechen die Parameter dem Indikatortyp). Am Ende muss der Zeiger auf das in der Liste gefundene Indikatorobjekt empfangen werden.

Im vorherigen Artikel habe ich eine solche Methode geschrieben, um den Zeiger auf den Indikator Accelerator Oscillator abzurufen. Es ist die einfachste, da der AC-Indikator keine Eingänge besitzt und wir müssen nur das notwendige Objekt nach Symbol und Zeitrahmen finden:

//+------------------------------------------------------------------+
//| Return pointer to indicator object Accelerator Oscillator  |
//+------------------------------------------------------------------+
CIndicatorDE *CIndicatorsCollection::GetIndAC(const string symbol,const ENUM_TIMEFRAMES timeframe)
  {
   CArrayObj *list=GetListAC(symbol,timeframe);
   return(list==NULL || list.Total()==0 ? NULL : list.At(0));
  }
//+------------------------------------------------------------------+

Um Indikatoren zu suchen, die Eingabeparameter besitzen, werde ich ein temporäres Indikatorobjekt mit eingestellten Parametern erstellen und eine Übereinstimmung in der Kollektionsliste suchen:

//+------------------------------------------------------------------+
//| Return pointer to indicator object                         |
//| Accumulation/Distribution                                        |
//+------------------------------------------------------------------+
CIndicatorDE *CIndicatorsCollection::GetIndAD(const string symbol,const ENUM_TIMEFRAMES timeframe,const ENUM_APPLIED_VOLUME applied_volume)
  {
   MqlParam param[1];
   param[0].type=TYPE_INT;
   param[0].integer_value=applied_volume;
   CIndicatorDE *tmp=this.CreateIndicator(IND_AD,param,symbol,timeframe);
   if(tmp==NULL)
      return NULL;
   int index=this.Index(tmp);
   delete tmp;
   return(index>WRONG_VALUE ? this.m_list.At(index) : NULL);
  }
//+------------------------------------------------------------------+

Wenn ein solches Objekt in der Liste gefunden wird, wird sein Index zurückgegeben. Wenn nicht - wird -1 zurückgegeben.

Die übrigen Methoden für die Zeigerrückgabe auf Indikatorobjekte in der Liste sind identisch mit den oben besprochenen, besitzen aber andere Parameter zur Erstellung des Indikatorobjekts. Um z. B. den Zeiger auf das Indikatorobjekt Alligator zurückzugeben, erstellen wir das Array, das aus acht Parametern besteht:

//+------------------------------------------------------------------+
//| Return pointer to indicator object Alligator               |
//+------------------------------------------------------------------+
CIndicatorDE *CIndicatorsCollection::GetIndAlligator(const string symbol,const ENUM_TIMEFRAMES timeframe,
                                                     const int jaw_period,
                                                     const int jaw_shift,
                                                     const int teeth_period,
                                                     const int teeth_shift,
                                                     const int lips_period,
                                                     const int lips_shift,
                                                     const ENUM_MA_METHOD ma_method,
                                                     const ENUM_APPLIED_PRICE applied_price)
  {
   MqlParam param[8];
   param[0].type=TYPE_INT;
   param[0].integer_value=jaw_period;
   param[1].type=TYPE_INT;
   param[1].integer_value=jaw_shift;
   param[2].type=TYPE_INT;
   param[2].integer_value=teeth_period;
   param[3].type=TYPE_INT;
   param[3].integer_value=teeth_shift;
   param[4].type=TYPE_INT;
   param[4].integer_value=lips_period;
   param[5].type=TYPE_INT;
   param[5].integer_value=lips_shift;
   param[6].type=TYPE_INT;
   param[6].integer_value=ma_method;
   param[7].type=TYPE_INT;
   param[7].integer_value=applied_price;
   CIndicatorDE *tmp=this.CreateIndicator(IND_ALLIGATOR,param,symbol,timeframe);
   if(tmp==NULL)
      return NULL;
   int index=this.Index(tmp);
   delete tmp;
   return(index>WRONG_VALUE ? this.m_list.At(index) : NULL);
  }
//+------------------------------------------------------------------+

Alle übrigen Dinge sind identisch mit der oben besprochenen Methode der Rückgabe des Zeigers auf das Indikatorobjekt Akkumulation/Verteilung.
In jeder der Methoden wird obligatorisch das temporäre Indikatorobjekt entfernt. Es dient als Referenz für die Suche einer Übereinstimmung in der Kollektionsliste.

Die übrigen ähnlichen Methoden werde ich nicht analysieren. Sie sind identisch mit den beiden Methoden, die wir gerade besprochen haben.

Damit ist die Verbesserung der Klassen im Rahmen dieses Artikels abgeschlossen.


Test-EA

Um den Test der Indikatorerstellung in EAs durchzuführen, nehmen wir den Test EA und speichern ihn in einem neuen Ordner \MQL5\Experts\TestDoEasy\Part55\ unter dem neuen Namen TestDoEasyPart55.mq5.

Im Wesentlichen handelt es sich dabei um kleinere Verbesserungen. In einem der vorherigen Artikel habe ich die Funktion der Arbeit mit Ereignissen im Tester EventsHandling() in die Bibliothek verschoben - in die Datei Engine.mqh. Entfernen wir daher diese Funktion aus dem EA-Code und ersetzen im Handler OnTick() dessen Aufruf aus der EA-Datei

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

durch den Aufruf aus der Bibliothek:

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

Entfernen wir den Codeblock aus OnTick(), der einen Kommentar mit Daten zum aktuellen Balken im Diagramm anzeigt:

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

Die Ereignisbehandlung eines "neuen Ticks" wird also wie folgt aussehen:

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

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

//--- If the trailing flag is set
   if(trailing_on)
     {
      TrailingPositions();          // Trailing positions
      TrailingOrders();             // Trailing of pending orders
     }
  }
//+------------------------------------------------------------------+

In der Bibliotheksinitialisierungsfunktion OnInitDoEasy() im Block der Anzeige der verwendeten Symbolliste ersetzen wir eine Anzahl von Symbolen, die bestimmt wurden

//--- Implement displaying the list of used symbols only for MQL5 - MQL4 has no ArrayPrint() function
#ifdef __MQL5__
   if(InpModeUsedSymbols!=SYMBOLS_MODE_CURRENT)
     {
      string array_symbols[];
      CArrayObj* list_symbols=engine.GetListAllUsedSymbols();
      for(int i=0;i<list_symbols.Total();i++)
        {
         CSymbol *symbol=list_symbols.At(i);
         if(symbol==NULL)
            continue;
         ArrayResize(array_symbols,ArraySize(array_symbols)+1,1000);
         array_symbols[ArraySize(array_symbols)-1]=symbol.Name();
        }
      ArrayPrint(array_symbols);
     }
#endif   

durch die Reihe von Symbolen die in der Makro-Substitution angegeben sind:

//--- Implement displaying the list of used symbols only for MQL5 - MQL4 has no ArrayPrint() function
#ifdef __MQL5__
   if(InpModeUsedSymbols!=SYMBOLS_MODE_CURRENT)
     {
      string array_symbols[];
      CArrayObj* list_symbols=engine.GetListAllUsedSymbols();
      for(int i=0;i<list_symbols.Total();i++)
        {
         CSymbol *symbol=list_symbols.At(i);
         if(symbol==NULL)
            continue;
         ArrayResize(array_symbols,ArraySize(array_symbols)+1,SYMBOLS_COMMON_TOTAL);
         array_symbols[ArraySize(array_symbols)-1]=symbol.Name();
        }
      ArrayPrint(array_symbols);
     }
#endif   

da in MetaTrader 5, beginnend mit Version 2430 die Gesamtzahl der Arbeitssymbole geändert wird und diese Zahl von der Bibliothek geprüft und automatisch von der Makro-Substitution SYMBOLS_COMMON_TOTAL, deklariert in Datei \MQL5\Include\DoEasy\Defines.mqh, gesetzt wird.

Vorübergehend und nur zur Überprüfung der Erstellung von Indikatorobjekten werden zwei Indikatoren desselben Typs, aber mit unterschiedlichen Parametern erstellt. Bis die normale Erstellung von Indikatoren in unseren Programmen nicht implementiert ist, erstellen wir sie einfach in der Initialisierungsfunktion der Bibliothek:

//+------------------------------------------------------------------+
//| Initializing DoEasy library                                  |
//+------------------------------------------------------------------+
void OnInitDoEasy()
  {
//--- Check if working with the full list is selected
   used_symbols_mode=InpModeUsedSymbols;
   if((ENUM_SYMBOLS_MODE)used_symbols_mode==SYMBOLS_MODE_ALL)
     {
      int total=SymbolsTotal(false);
      string ru_n="\nКоличество символов на сервере "+(string)total+".\nМаксимальное количество: "+(string)SYMBOLS_COMMON_TOTAL+" символов.";
      string en_n="\nThe number of symbols on server "+(string)total+".\nMaximal number: "+(string)SYMBOLS_COMMON_TOTAL+" symbols.";
      string caption=TextByLanguage("Attention!");
      string ru="Выбран режим работы с полным списком.\nВ этом режиме первичная подготовка списков коллекций символов и таймсерий может занять длительное время."+ru_n+"\nПродолжить?\n\"Нет\" - работа с текущим символом \""+Symbol()+"\"";
      string en="Full list mode selected.\nIn this mode, the initial preparation of lists of symbol collections and timeseries can take a long time."+en_n+"\nContinue?\n\"No\" - working with the current symbol \""+Symbol()+"\"";
      string message=TextByLanguage(ru,en);
      int flags=(MB_YESNO | MB_ICONWARNING | MB_DEFBUTTON2);
      int mb_res=MessageBox(message,caption,flags);
      switch(mb_res)
        {
         case IDNO : 
           used_symbols_mode=SYMBOLS_MODE_CURRENT; 
           break;
         default:
           break;
        }
     }
//--- Set the counter start point to measure the approximate library initialization time
   ulong begin=GetTickCount();
   Print(TextByLanguage("--- Initializing the \"DoEasy\" library ---"));
//--- Fill in the array of used symbols
   CreateUsedSymbolsArray((ENUM_SYMBOLS_MODE)used_symbols_mode,InpUsedSymbols,array_used_symbols);
//--- Set the type of the used symbol list in the symbol collection and fill in the list of symbol timeseries
   engine.SetUsedSymbols(array_used_symbols);
//--- Displaying the selected mode of working with the symbol object collection in the journal
   string num=
     (
      used_symbols_mode==SYMBOLS_MODE_CURRENT ? ": \""+Symbol()+"\"" : 
      TextByLanguage(". Number of used symbols: ",". The number of symbols used: ")+(string)engine.GetSymbolsCollectionTotal()
     );
   Print(engine.ModeSymbolsListDescription(),num);
//--- Implement displaying the list of used symbols only for MQL5 - MQL4 has no ArrayPrint() function
#ifdef __MQL5__
   if(InpModeUsedSymbols!=SYMBOLS_MODE_CURRENT)
     {
      string array_symbols[];
      CArrayObj* list_symbols=engine.GetListAllUsedSymbols();
      for(int i=0;i<list_symbols.Total();i++)
        {
         CSymbol *symbol=list_symbols.At(i);
         if(symbol==NULL)
            continue;
         ArrayResize(array_symbols,ArraySize(array_symbols)+1,SYMBOLS_COMMON_TOTAL);
         array_symbols[ArraySize(array_symbols)-1]=symbol.Name();
        }
      ArrayPrint(array_symbols);
     }
#endif   
//--- Set used timeframes
   CreateUsedTimeframesArray(InpModeUsedTFs,InpUsedTFs,array_used_periods);
//--- Display the selected mode of working with the timeseries object collection
   string mode=
     (
      InpModeUsedTFs==TIMEFRAMES_MODE_CURRENT   ? 
         TextByLanguage("Work only with the current Period: ")+TimeframeDescription((ENUM_TIMEFRAMES)Period())   :
      InpModeUsedTFs==TIMEFRAMES_MODE_LIST      ? 
         TextByLanguage("Work with a predefined list of Periods:")                                              :
      TextByLanguage("Work with the full list of all Periods:")
     );
   Print(mode);
//--- Implement displaying the list of used timeframes only for MQL5 - MQL4 has no ArrayPrint() function
#ifdef __MQL5__
   if(InpModeUsedTFs!=TIMEFRAMES_MODE_CURRENT)
      ArrayPrint(array_used_periods);
#endif 
//--- Create timeseries of all used symbols
   engine.SeriesCreateAll(array_used_periods);
//--- Check created timeseries - display descriptions of all created timeseries in the journal
//--- (true - only created ones, false - created and declared ones)
   engine.GetTimeSeriesCollection().PrintShort(false); // Short descriptions
   //engine.GetTimeSeriesCollection().Print(true);      // Full descriptions

//--- Create indicators
   engine.GetIndicatorsCollection().CreateAMA(Symbol(),Period(),9,2,30,0,PRICE_CLOSE);
   engine.GetIndicatorsCollection().CreateAMA(Symbol(),Period(),10,3,32,5,PRICE_CLOSE);
   engine.GetIndicatorsCollection().Print();
   engine.GetIndicatorsCollection().PrintShort();

//--- Create resource text files
   engine.CreateFile(FILE_TYPE_WAV,"sound_array_coin_01",TextByLanguage("The sound of a falling coin 1"),sound_array_coin_01);
   engine.CreateFile(FILE_TYPE_WAV,"sound_array_coin_02",TextByLanguage("Sound fallen coins"),sound_array_coin_02);
   engine.CreateFile(FILE_TYPE_WAV,"sound_array_coin_03",TextByLanguage("Sound of coins"),sound_array_coin_03);
   engine.CreateFile(FILE_TYPE_WAV,"sound_array_coin_04",TextByLanguage("The sound of a falling coin 2"),sound_array_coin_04);
   engine.CreateFile(FILE_TYPE_WAV,"sound_array_click_01",TextByLanguage("Click on the button sound 1"),sound_array_click_01);
   engine.CreateFile(FILE_TYPE_WAV,"sound_array_click_02",TextByLanguage("Click on the button sound 1"),sound_array_click_02);
   engine.CreateFile(FILE_TYPE_WAV,"sound_array_click_03",TextByLanguage("Click on the button sound 1"),sound_array_click_03);
   engine.CreateFile(FILE_TYPE_WAV,"sound_array_cash_machine_01",TextByLanguage("The sound of the cash machine"),sound_array_cash_machine_01);
   engine.CreateFile(FILE_TYPE_BMP,"img_array_spot_green",TextByLanguage("Image \"Green Spot lamp\""),img_array_spot_green);
   engine.CreateFile(FILE_TYPE_BMP,"img_array_spot_red",TextByLanguage("Image \"Red Spot lamp\""),img_array_spot_red);

//--- Pass all existing collections to the main library class
   engine.CollectionOnInit();

//--- Set the default magic number for all used symbols
   engine.TradingSetMagic(engine.SetCompositeMagicNumber(magic_number));
//--- Set synchronous passing of orders for all used symbols
   engine.TradingSetAsyncMode(false);
//--- Set the number of trading attempts in case of an error
   engine.TradingSetTotalTry(InpTotalAttempts);
//--- Set correct order expiration and filling types to all trading objects
   engine.TradingSetCorrectTypeExpiration();
   engine.TradingSetCorrectTypeFilling();

//--- Set standard sounds for trading objects of all used symbols
   engine.SetSoundsStandart();
//--- Set the general flag of using sounds
   engine.SetUseSounds(InpUseSounds);
//--- Set the spread multiplier for symbol trading objects in the symbol collection
   engine.SetSpreadMultiplier(InpSpreadMultiplier);
      
//--- Set controlled values for symbols
   //--- Get the list of all collection symbols
   CArrayObj *list=engine.GetListAllUsedSymbols();
   if(list!=NULL && list.Total()!=0)
     {
      //--- In a loop by the list, set the necessary values for tracked symbol properties
      //--- By default, the LONG_MAX value is set to all properties, which means "Do not track this property” 
      //--- It can be enabled or disabled (set the value less than LONG_MAX or vice versa - set the LONG_MAX value) at any time and anywhere in the program
      /*
      for(int i=0;i<list.Total();i++)
        {
         CSymbol* symbol=list.At(i);
         if(symbol==NULL)
            continue;
        //--- Set control of the symbol price increase to 100 points
         symbol.SetControlBidInc(100000*symbol.Point());
        //--- Set control of the symbol price decrease to 100 points
         symbol.SetControlBidDec(100000*symbol.Point());
        //--- Set control of the symbol spread increase to 40 points
         symbol.SetControlSpreadInc(400);
        //--- Set control of the symbol spread decrease to 40 points
         symbol.SetControlSpreadDec(400);
        //--- Set control of the current spread by the value of 40 points
         symbol.SetControlSpreadLevel(400);
        }
      */
     }
//--- Set controlled values for the current account
   CAccount* account=engine.GetAccountCurrent();
   if(account!=NULL)
     {
      //--- Set control of the profit increase to 10
      account.SetControlledValueINC(ACCOUNT_PROP_PROFIT,10.0);
      //--- Set control of the funds increase to 15
      account.SetControlledValueINC(ACCOUNT_PROP_EQUITY,15.0);
      //--- Set profit control level to 20
      account.SetControlledValueLEVEL(ACCOUNT_PROP_PROFIT,20.0);
     }
//--- Get the end of the library initialization time counting and display it in the journal
   ulong end=GetTickCount();
   Print(TextByLanguage("Library initialization time: "),TimeMSCtoString(end-begin,TIME_MINUTES|TIME_SECONDS));
  }
//+------------------------------------------------------------------+

Hier haben wir zwei Indikatoren, Adaptive Moving Average, erstellt, auf dem aktuellen Symbol und Zeitrahmen, aber mit unterschiedlichen Eingabewerten.

Kompilieren Sie den EA und starten Sie ihn auf dem Chart im Terminal.
Nach seiner Initialisierung zeigt das Journal "Experts" Bibliotheksmeldungen zur Initialisierung an. Darunter befinden sich vollständige und kurze Listen der Parameter von zwei erstellten Indikatoren:

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 with a predefined list of Periods:
"H1" "H4"
Symbol time series EURUSD: 
- Timeseries "EURUSD" H1: Required: 1000, Actual: 1000, Created: 1000, On server: 6350
- Timeseries "EURUSD" H4: Required: 1000, Actual: 1000, Created: 1000, On server: 6255
============= The beginning of the event parameter list: "Standard indicator" =============
Indicator status: Standard indicator
Indicator type: AMA
Indicator timeframe: H1
Indicator handle: 10
Indicator group: Trend indicator
------
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 event parameter list: "Standard indicator" =============
Indicator status: Standard indicator
Indicator type: AMA
Indicator timeframe: H1
Indicator handle: 11
Indicator group: Trend indicator
------
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: 10
- Fast MA period: 3
- Slow MA period: 32
- Horizontal shift of the indicator: 5
- Price type or handle: CLOSE
================== End of the parameter list: "Standard indicator" ==================
 
Standard indicator Adaptive Moving Average EURUSD H1 [10]
Standard indicator Adaptive Moving Average EURUSD H1 [11]
Library initialization time: 00:00:00.000

Es ist zwar nur ein Indikatortyp - AMA - aber es wurden zwei Handles dieses Indikators erstellt, da die Parameter der erstellten Indikatoren unterschiedlich waren. Daher handelt es sich um zwei verschiedene Indikatoren - jeder hat sein Handle. Dementsprechend werden zwei Indikatorobjekte erstellt und in der Kollektion der Indikatoren abgelegt.

Mittlerweile können wir nur noch unterschiedliche Indikatoren mit unterschiedlichen Parametern erstellen. Aber um sie in EAs anzuwenden, muss ein Bereich für die Speicherung ihrer Daten vorbereitet werden. Aus diesem Bereich können Daten in beliebigen Kombinationen von Parametern empfangen und in Programmen verwendet werden, um Entscheidungen zu treffen oder um statistische Daten zu erhalten. Mit all diesen Dingen werde ich mich ab dem nächsten Artikel beschäftigen.

Was kommt als Nächstes?

Im folgenden Artikel wird mit der Implementierung des Speicherns und Empfangens von Daten aus Indikatorobjekten in EAs begonnen.

Alle Dateien der aktuellen Version der Bibliothek 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

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

Beigefügte Dateien |
MQL5.zip (3856.21 KB)
Neuronale Netze leicht gemacht (Teil 5): Parallele Berechnungen mit OpenCL Neuronale Netze leicht gemacht (Teil 5): Parallele Berechnungen mit OpenCL

Wir haben bereits einige Arten von Implementierungen neuronaler Netze besprochen. In den betrachteten Netzwerken werden die gleichen Operationen für jedes Neuron wiederholt. Ein logischer weiterer Schritt ist die Nutzung der parallelen Berechnung, die die moderne Technologie bietet, um den Lernprozess des neuronalen Netzwerks zu beschleunigen. Eine der möglichen Implementierungen wird in diesem Artikel beschrieben.

Zeitreihen in der Bibliothek DoEasy (Teil 54): Abgeleitete Klassen des abstrakten Basisindikators Zeitreihen in der Bibliothek DoEasy (Teil 54): Abgeleitete Klassen des abstrakten Basisindikators

Der Artikel betrachtet das Erstellen von Klassen von abgeleiteten Objekten des abstrakten Basisindikators. Solche Objekte ermöglichen den Zugriff auf die Funktionen der Erstellung von Indikator-EAs, das Sammeln und Abrufen von Datenwertstatistiken verschiedener Indikatoren und Preise. Außerdem wird eine Kollektion von Indikatorobjekten erstellt, von der aus der Zugriff auf die Eigenschaften und Daten jedes im Programm erstellten Indikators möglich sein wird.

Praktische Anwendung von neuronalen Netzen im Handel. Python (Teil I) Praktische Anwendung von neuronalen Netzen im Handel. Python (Teil I)

In diesem Artikel werden wir die schrittweise Implementierung eines Handelssystems analysieren, das auf der Programmierung von tiefen neuronalen Netzen in Python basiert. Dies wird unter Verwendung der von Google entwickelten TensorFlow-Bibliothek für maschinelles Lernen durchgeführt. Außerdem werden wir die Keras-Bibliothek zur Beschreibung von neuronalen Netzen verwenden.

Optimale Vorgehensweise für Entwicklung und Analyse von Handelssystemen Optimale Vorgehensweise für Entwicklung und Analyse von Handelssystemen

In diesem Artikel zeige ich Ihnen die Kriterien, die Sie bei der Auswahl eines Systems oder Signals für die Investition Ihrer Gelder berücksichtigen sollten. Außerdem beschreibe ich die optimale Vorgehensweise bei der Entwicklung von Handelssystemen und zeige auf, wie wichtig diese Angelegenheit im Forex-Handel ist.