Zeitreihen in der Bibliothek DoEasy (Teil 45): Puffer für Mehrperiodenindikator

16 Oktober 2020, 07:06
Artyom Trishkin
0
178

Inhalt


Konzept

Im vorherigen Artikel habe ich mit der Entwicklung der Kollektionsklasse für Indikatorpuffer begonnen. Heute werde ich meine Arbeit wieder aufnehmen und die Methode zur Erstellung von Indikatorpuffern und den Zugriff auf ihre Daten arrangieren. Ich werde die Darstellung von Pufferwerten im aktuellen Symbol-/Periodenchart in Abhängigkeit von der im Pufferobjekt eingestellten Periodeneigenschaft (Datenzeitrahmen) implementieren. Wenn für den Puffer ein Zeitrahmenwert eingestellt ist, der nicht dem aktuellen Chart entspricht, sollen alle Pufferdaten im Chart korrekt angezeigt werden. Wenn z.B. der aktuelle Chart den Zeitrahmen M15 hat, während das Pufferobjektchart auf H1 eingestellt ist, werden die Pufferdaten auf jedem Balken des aktuellen M15-Diagramms angezeigt, dessen Zeit sich innerhalb des Balkens des H1-Charts befindet. Im aktuellen Beispiel werden vier Balken des aktuellen Diagramms mit dem gleichen Wert gefüllt, der dem angeforderten Wert aus dem Balken mit der Diagrammperiode H1 entspricht.

Diese Verbesserungen ermöglichen uns das bequeme Erstellen von Mehrperiodenindikatoren, da wir nur noch einen Zeitrahmen für ein Pufferobjekt festlegen müssen. Den Rest erledigt die Bibliothek. Außerdem werde ich die Erstellung von Indikatorpuffern nach und nach erleichtern. Sie soll auf einen einzigen Code-Zeile reduziert werden, in dem das erforderliche Indikatorpufferobjekt mit den erforderlichen Eigenschaften angelegt werden soll.
Auf einen beliebigen Indikatorpuffer soll über den Pufferindex mit einer bestimmten Zeichnungsart zugegriffen werden.

Das Konzept des "Pufferindex" sieht folgendermaßen aus: Wenn zuerst der Pfeilpuffer und dann der Linienpuffer und anschließend der Pfeilpuffer neu angelegt wird, werden die Pufferobjektindizes in der Reihenfolge ihrer Erstellung angeordnet. Jeder Zeichenstil hat seine eigene Indexreihenfolge. Im vorliegenden Beispiel lauten die Indizes wie folgt: Der erste erzeugte Pfeilpuffer hat den Index 0, der zweite den Index 1, während der unmittelbar nach dem ersten Pfeilpuffer erzeugte Linienpuffer den Index 0 hat, weil er der allererste Puffer mit dem Zeichenstil "Linie" ist, obwohl er als zweiter in einer Reihe erzeugt wird.

Lassen Sie uns das Konzept der Pufferobjektindizes veranschaulichen:

  1. Erzeugen des Pfeilpuffers. Sein Index ist 0,
  2. Erzeugen des Zeilenpuffers. Sein Index ist 0,
  3. Erzeugen des Pfeilpuffers. Sein Index ist 1,
  4. Erzeugen des ZigZagpuffers. Sein Index ist 0,
  5. Erzeugen des ZigZagpuffers. Sein Index ist 1,
  6. Erzeugen des Pfeilpuffers. Sein Index ist 2,
  7. Erzeugen des Pfeilpuffers. Sein Index ist 3,
  8. Erzeugen des Zeilenpuffers. Sein Index ist 1,
  9. Erstellung des Kerzenpuffers. Sein Index ist 0,
  10. Erzeugen des Pfeilpuffers. Sein Index ist 4.
Neben dem Zugriff auf den Puffer über seinen seriellen Index kann auf ihn auch über seinen Namen im grafischen Reihen-, Zeit-, Datenfenster- und Kollektionslistenindex zugegriffen werden. Wir können auch das Pufferobjekt ermitteln, das als letztes erstellt und der Kollektionsliste hinzugefügt wurde. Dies vereinfacht die Einstellung von Parametern für das neu erstellte Pufferobjekt — einfach ein Objekt erstellen, es holen und zusätzliche Eigenschaften einstellen.

Verbesserung der Klassen für die Arbeit mit Indikatorpuffern im Mehrperiodenmodus

Ab Build 2430 verfügt MetaTrader 5 über eine erhöhte Anzahl von Symbolen, die im Fenster Marktübersicht platziert werden können. Jetzt sind es 5000 statt 1000 wie vor Build 2430. Um mit der Symbolliste zu arbeiten, führen wir eine neue Makro-Substitution in der Datei Defines.mqh ein:

//+------------------------------------------------------------------+
//| Macro substitutions                                              |
//+------------------------------------------------------------------+
//--- Describe the function with the error line number
....
//--- Symbol parameters
#define CLR_DEFAULT                    (0xFF000000)               // Default symbol background color in the navigator
#ifdef __MQL5__
   #define SYMBOLS_COMMON_TOTAL        (TerminalInfoInteger(TERMINAL_BUILD)<2430 ? 1000 : 5000)   // Total number of MQL5 working symbols
#else 
   #define SYMBOLS_COMMON_TOTAL        (1000)                     // Total number of MQL4 working symbols
#endif 
//--- Pending request type IDs
#define PENDING_REQUEST_ID_TYPE_ERR    (1)                        // Type of a pending request created based on the server return code
#define PENDING_REQUEST_ID_TYPE_REQ    (2)                        // Type of a pending request created by request
//--- Timeseries parameters
#define SERIES_DEFAULT_BARS_COUNT      (1000)                     // Required default amount of timeseries data
#define PAUSE_FOR_SYNC_ATTEMPTS        (16)                       // Amount of pause milliseconds between synchronization attempts
#define ATTEMPTS_FOR_SYNC              (5)                        // Number of attempts to receive synchronization with the server
//+------------------------------------------------------------------+
//| Enumerations                                                     |
//+------------------------------------------------------------------+


Wir ersetzen in \MQL5\Include\DoEasy\Collections\SymbolsCollection.mqh die reservierte Symbolarray-Größe von 1000 durch den neuen Makro-Substitutionswert in der Methode zur Einstellung der Liste der verwendeten Symbole:

//+------------------------------------------------------------------+
//| Set the list of used symbols                                     |
//+------------------------------------------------------------------+
bool CSymbolsCollection::SetUsedSymbols(const string &symbol_used_array[])
  {
   ::ArrayResize(this.m_array_symbols,0,SYMBOLS_COMMON_TOTAL);
   ::ArrayCopy(this.m_array_symbols,symbol_used_array);


Ersetzen Sie in der Methode zum Erstellen einer Symbolliste den Referenzwert von 1000 durch die neue Makro-Substitution im Schleifenindex:

//+------------------------------------------------------------------+
//| Creating the symbol list (Market Watch or the complete list)     |
//+------------------------------------------------------------------+
bool CSymbolsCollection::CreateSymbolsList(const bool flag)
  {
   bool res=true;
   int total=::SymbolsTotal(flag);
   for(int i=0;i<total && i<SYMBOLS_COMMON_TOTAL;i++)
     {


Auf die gleiche Weise ersetzen Sie 1000 bis zur Makro-Substitution in der Methode, alle verwendeten Symbole und Zeitrahmen in \MQL5\Include\DoEasy\Engine.mqh zu schreiben:

//+------------------------------------------------------------------+
//| Write all used symbols and timeframes                            |
//| to the ArrayUsedSymbols and ArrayUsedTimeframes arrays           |
//+------------------------------------------------------------------+
void CEngine::WriteSymbolsPeriodsToArrays(void)
  {
//--- Get the list of all created timeseries (created by the number of used symbols)
   CArrayObj *list_timeseries=this.GetListTimeSeries();
   if(list_timeseries==NULL)
      return;
//--- Get the total number of created timeseries
   int total_timeseries=list_timeseries.Total();
   if(total_timeseries==0)
      return;
//--- Set the size of the array of used symbols equal to the number of created timeseries, while
//--- the size of the array of used timeframes is set equal to the maximum possible number of timeframes in the terminal
   if(::ArrayResize(ArrayUsedSymbols,total_timeseries,SYMBOLS_COMMON_TOTAL)!=total_timeseries || ::ArrayResize(ArrayUsedTimeframes,21,21)!=21)
      return;
//--- Set both arrays to zero
   ::ZeroMemory(ArrayUsedSymbols);
   ::ZeroMemory(ArrayUsedTimeframes);
//--- Reset the number of added symbols and timeframes to zero and,
//--- in a loop by the total number of timeseries,
   int num_symbols=0,num_periods=0;
   for(int i=0;i<total_timeseries;i++)
     {
      //--- get the next object of all timeseries of a single symbol
      CTimeSeriesDE *timeseries=list_timeseries.At(i);
      if(timeseries==NULL || this.IsExistSymbol(timeseries.Symbol()))
         continue;
      //--- increase the number of used symbols and (num_symbols variable), and
      //--- write the timeseries symbol name to the array of used symbols by the num_symbols-1 index
      num_symbols++;
      ArrayUsedSymbols[num_symbols-1]=timeseries.Symbol();
      //--- Get the list of all its timeseries from the object of all symbol timeseries
      CArrayObj *list_series=timeseries.GetListSeries();
      if(list_series==NULL)
         continue;
      //--- In the loop by the total number of symbol timeseries,
      int total_series=list_series.Total();
      for(int j=0;j<total_series;j++)
        {
         //--- get the next timeseries object
         CSeriesDE *series=list_series.At(j);
         if(series==NULL || this.IsExistTimeframe(series.Timeframe()))
            continue;
         //--- increase the number of used timeframes and (num_periods variable), and
         //--- write the timeseries timeframe value to the array of used timeframes by num_periods-1 index
         num_periods++;
         ArrayUsedTimeframes[num_periods-1]=series.Timeframe();
        }
     }
//--- Upon the loop completion, change the size of both arrays to match the exact number of added symbols and timeframes
   ::ArrayResize(ArrayUsedSymbols,num_symbols,SYMBOLS_COMMON_TOTAL);
   ::ArrayResize(ArrayUsedTimeframes,num_periods,21);
  }
//+------------------------------------------------------------------+



Die Parameter des Pufferobjekts enthalten den Parameter, der den Array-Index angibt, dem der nächste Indikatorpuffer zugeordnet werden soll. Auf diese Weise können wir sofort den nächsten Array-Index als erstes (Basis-)Array für den nächsten Puffer festlegen, wenn wir einen einzelnen Puffer erstellen und ihm die erforderliche Anzahl von Symbolen hinzufügen. Im Gegensatz dazu wird im Falle des Indikatorpuffer- (graphischen Konstruktions-) Index dieser Wert verwendet, wenn die erforderlichen Werte für den Puffer mit Hilfe der Funktionen PlotIndexSetDouble(), PlotIndexSetInteger() und PlotIndexSetString() gesetzt werden. Wir haben die Eigenschaft nicht in den Parametern des Pufferobjekts. Um den Index der nächsten grafischen Konstruktion bequem berechnen zu können, fügen wir die neue Eigenschaft zur Enumeration der ganzzahligen Eigenschaften des Pufferobjekts in der Datei Defines.mqh hinzu. Außerdem geben wir der Eigenschaft, die den Index des Basisarrays des nächsten Pufferobjekts speichern, einen anderen Namen:

//+------------------------------------------------------------------+
//| Buffer integer properties                                        |
//+------------------------------------------------------------------+
enum ENUM_BUFFER_PROP_INTEGER
  {
   BUFFER_PROP_INDEX_PLOT = 0,                              // Plotted buffer serial number
   BUFFER_PROP_STATUS,                                      // Buffer status (by drawing style) from the ENUM_BUFFER_STATUS enumeration
   BUFFER_PROP_TYPE,                                        // Buffer type (from the ENUM_BUFFER_TYPE enumeration)
   BUFFER_PROP_TIMEFRAME,                                   // Buffer period data (timeframe)
   BUFFER_PROP_ACTIVE,                                      // Buffer usage flag
   BUFFER_PROP_DRAW_TYPE,                                   // Graphical construction type (from the ENUM_DRAW_TYPE enumeration)
   BUFFER_PROP_ARROW_CODE,                                  // Arrow code for DRAW_ARROW style
   BUFFER_PROP_ARROW_SHIFT,                                 // The vertical shift of the arrows for DRAW_ARROW style
   BUFFER_PROP_LINE_STYLE,                                  // Line style
   BUFFER_PROP_LINE_WIDTH,                                  // Line width
   BUFFER_PROP_DRAW_BEGIN,                                  // The number of initial bars that are not drawn and values in DataWindow
   BUFFER_PROP_SHOW_DATA,                                   // Flag of displaying construction values in DataWindow
   BUFFER_PROP_SHIFT,                                       // Indicator graphical construction shift by time axis in bars
   BUFFER_PROP_COLOR_INDEXES,                               // Number of colors
   BUFFER_PROP_COLOR,                                       // Drawing color
   BUFFER_PROP_INDEX_BASE,                                  // Base data buffer index
   BUFFER_PROP_INDEX_NEXT_BASE,                             // Index of the array to be assigned as the next indicator buffer
   BUFFER_PROP_INDEX_NEXT_PLOT,                             // Index of the next drawn buffer
   BUFFER_PROP_NUM_DATAS,                                   // Number of data buffers
   BUFFER_PROP_INDEX_COLOR,                                 // Color buffer index
  }; 
#define BUFFER_PROP_INTEGER_TOTAL (20)                      // Total number of integer bar properties
#define BUFFER_PROP_INTEGER_SKIP  (2)                       // Number of buffer properties not used in sorting
//+------------------------------------------------------------------+


Erhöhen wir die Anzahl der ganzzahligen Eigenschaften des Pufferobjekts von 19 auf 20.

Während Sie mögliche Puffersortierkriterien aufzählen, fügen wir das Sortieren nach der neu hinzugefügten Eigenschaft hinzu und benennen den Konstantennamen der Sortierung nach dem Array-Index für die Zuweisung als nächstes Basispufferarray um:

//+------------------------------------------------------------------+
//| Possible buffer sorting criteria                                 |
//+------------------------------------------------------------------+
#define FIRST_BUFFER_DBL_PROP          (BUFFER_PROP_INTEGER_TOTAL-BUFFER_PROP_INTEGER_SKIP)
#define FIRST_BUFFER_STR_PROP          (BUFFER_PROP_INTEGER_TOTAL-BUFFER_PROP_INTEGER_SKIP+BUFFER_PROP_DOUBLE_TOTAL-BUFFER_PROP_DOUBLE_SKIP)
enum ENUM_SORT_BUFFER_MODE
  {
//--- Sort by integer properties
   SORT_BY_BUFFER_INDEX_PLOT = 0,                           // Sort by the plotted buffer serial number
   SORT_BY_BUFFER_STATUS,                                   // Sort by buffer drawing style (status) from the ENUM_BUFFER_STATUS enumeration
   SORT_BY_BUFFER_TYPE,                                     // Sort by buffer type (from the ENUM_BUFFER_TYPE enumeration)
   SORT_BY_BUFFER_TIMEFRAME,                                // Sort by the buffer data period (timeframe)
   SORT_BY_BUFFER_ACTIVE,                                   // Sort by the buffer usage flag
   SORT_BY_BUFFER_DRAW_TYPE,                                // Sort by graphical construction type (from the ENUM_DRAW_TYPE enumeration)
   SORT_BY_BUFFER_ARROW_CODE,                               // Sort by the arrow code for DRAW_ARROW style
   SORT_BY_BUFFER_ARROW_SHIFT,                              // Sort by the vertical shift of the arrows for DRAW_ARROW style
   SORT_BY_BUFFER_LINE_STYLE,                               // Sort by the line style
   SORT_BY_BUFFER_LINE_WIDTH,                               // Sort by the line width
   SORT_BY_BUFFER_DRAW_BEGIN,                               // Sort by the number of initial bars that are not drawn and values in DataWindow
   SORT_BY_BUFFER_SHOW_DATA,                                // Sort by the flag of displaying construction values in DataWindow
   SORT_BY_BUFFER_SHIFT,                                    // Sort by the indicator graphical construction shift by time axis in bars
   SORT_BY_BUFFER_COLOR_INDEXES,                            // Sort by a number of attempts
   SORT_BY_BUFFER_COLOR,                                    // Sort by the drawing color
   SORT_BY_BUFFER_INDEX_BASE,                               // Sort by the basic data buffer index
   SORT_BY_BUFFER_INDEX_NEXT_BASE,                          // Sort by the index of the array to be assigned as the next indicator buffer
   SORT_BY_BUFFER_INDEX_NEXT_PLOT,                          // Sort by the index of the next drawn buffer
//--- Sort by real properties
   SORT_BY_BUFFER_EMPTY_VALUE = FIRST_BUFFER_DBL_PROP,      // Sort by the empty value for plotting where nothing will be drawn
//--- Sort by string properties
   SORT_BY_BUFFER_SYMBOL = FIRST_BUFFER_STR_PROP,           // Sort by the buffer symbol
   SORT_BY_BUFFER_LABEL,                                    // Sort by the name of the graphical indicator series displayed in DataWindow
  };
//+------------------------------------------------------------------+

Wenn wir nun ein beliebiges Pufferobjekt erstellen, schreiben wir die grundlegenden Array-Indizes und den Index der nächsten grafischen Reihe in die aktuellen Pufferparameter. Um also herauszufinden, welche Indizes in dem neu erstellten Pufferobjekt gesetzt werden sollen, brauchen wir nur auf das vorherige (das zuletzt erstellte Pufferobjekt) zuzugreifen und die erforderlichen Werte zu erhalten.
Dies ist einfacher und besser, da jedem Puffer ein bis fünf Arrays zugewiesen werden können und wir nicht alle Werte der erforderlichen Array-Indizes für einen neuen Indikatorpuffer neu berechnen müssen — alles wird direkt in den Eigenschaften des zuletzt erstellten Pufferobjekts festgelegt. Diese Werte werden mit jedem neu hinzugefügten Indikatorpuffer in Abhängigkeit von der Anzahl der von ihm verwendeten Arrays neu berechnet.

In \MQL5\Include\DoEasy\Datas.mqh fügen wir den neuen Nachrichtenindex für die neu hinzugefügte Puffereigenschaft hinzu und benennen den Namen der Indexkonstanten der Nachricht bezüglich der Eigenschaft des nächsten Arrays für den Indikatorpuffer um:

//--- CBuffer
   MSG_LIB_TEXT_BUFFER_TEXT_INDEX_BASE,               // Base data buffer index
   MSG_LIB_TEXT_BUFFER_TEXT_INDEX_PLOT,               // Plotted buffer serial number
   MSG_LIB_TEXT_BUFFER_TEXT_INDEX_COLOR,              // Color buffer index
   MSG_LIB_TEXT_BUFFER_TEXT_NUM_DATAS,                // Number of data buffers
   MSG_LIB_TEXT_BUFFER_TEXT_INDEX_NEXT_BASE,          // Index of the array to be assigned as the next indicator buffer
   MSG_LIB_TEXT_BUFFER_TEXT_INDEX_NEXT_PLOT,          // Index of the next drawn buffer
   MSG_LIB_TEXT_BUFFER_TEXT_TIMEFRAME,                // Buffer (timeframe) data period


Fügen wir auch den Nachrichtentext hinzu, der dem neu hinzugefügten Index entspricht:

   {"Индекс базового буфера данных","Index of Base data buffer"},
   {"Порядковый номер рисуемого буфера","Plot buffer sequence number"},
   {"Индекс буфера цвета","Color buffer index"},
   {"Количество буферов данных","Number of data buffers"},
   {"Индекс массива для назначения следующим индикаторным буфером","Array index for assignment as the next indicator buffer"},
   {"Индекс следующего по счёту рисуемого буфера","Index of the next drawable buffer"},
   {"Период данных буфера (таймфрейм)","Buffer data Period (Timeframe)"},


Da wir den Namen der Konstante geändert haben, die den Index des nächsten grundlegenden Pufferobjektarrays speichert, ersetzen wir alle Instanzen der Zeichenfolge BUFFER_PROP_INDEX_NEXT in allen Dateien der Pufferobjektklassen (BufferArrow.mqh, BufferBars. mqh, BufferCandles.mqh, BufferFilling.mqh, BufferHistogram.mqh, BufferHistogram2.mqh, BufferLine.mqh, BufferArrow.mqh, BufferSection.mqh und BufferZigZag.mqh) und verbessern die virtuelle Methode zur Anzeige der kurzen Pufferobjektbeschreibung im Journal.
Betrachten wir die Änderungen der Methode unter Verwendung der Klasse CBufferArrow in \MQL5\Include\DoEasy\Objects\Indicators\BufferArrow.mqh als Beispiel:

//+------------------------------------------------------------------+
//| Display short buffer description in the journal                  |
//+------------------------------------------------------------------+
void CBufferArrow::PrintShort(void)
  {
   ::Print
     (
      CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_BUFFER),"(P",(string)this.IndexPlot(),"/B",(string)this.IndexBase(),"/C",(string)this.IndexColor(),"): ",
      this.GetStatusDescription(true)," ",this.Symbol()," ",TimeframeDescription(this.Timeframe())
     );
  }
//+------------------------------------------------------------------+


The message text has been made as informative as possible. For example, the short description of the arrow buffer class displayed by the improved method looks as follows:

Buffer(P0/B0/C1): Drawing with arrows EURUSD H1


wobei:

  • "P" bedeutet "Plot" — grafischer Konstruktionsindex,
  • "B" bedeutet "Base" — Index des Basisarrays, das als erstes dem Puffer zugeordnet wird (die übrigen Puffer des Arrays sind in aufsteigender Reihenfolge angeordnet, ausgehend vom Index des Basisarrays), 
  • "C" bedeutet "Color" — Index des dem Puffer zugeordneten Farbarrays.

Wenn wir also alle möglichen Pufferobjekte erstellen und ihre Kurzbeschreibungen im Journal anzeigen, sehen wir dort die folgenden Einträge:

Buffer(P0/B0/C1): Drawing with arrows EURUSD H1
Buffer(P1/B2/C3): EURUSD H1 line
Buffer(P2/B4/C5): EURUSD H1 sections
Buffer(P3/B6/C7): Histogram from the zero line EURUSD H1
Buffer(P4/B8/C10): Histogram on two indicator buffers EURUSD H1
Buffer(P5/B11/C13): EURUSD H1 zigzag
Buffer(P6/B14/C16): Color filling between two levels EURUSD H1
Buffer(P7/B16/C20): Display as EURUSD H1 bars
Buffer(P8/B21/C25): Display as EURUSD H1 candles
Buffer[P8/B26/C27]: Calculated buffer


Jetzt können wir die Indikatorpuffer und Arrays mit den ihnen zugeordneten Indizes (B und C) sowie die den Puffern zugeordneten grafischen Serienindizes (P) deutlich sehen.
Hier sehen wir, dass der grafische Serienindex, der dem Berechnungspuffer zugeordnet ist (wird später in diesem Artikel vorgestellt), mit demjenigen übereinstimmt, der dem vorherigen Puffer zugeordnet ist. Tatsächlich hat der Berechnungspuffer keinen grafischen Serien- und Farbpuffer-Array. Derzeit enthält er nur Debugging-Daten. P8 und C27 zeigen, für welche Indizes der grafischen Serie und des Farbpuffer-Arrays der nächste Puffer nach dem berechneten erstellt werden sollte.
Nachdem ich die Indikatorpuffer erstellt habe, werde ich die Daten z.B. in PN, CN oder PX, CX ändern, was bedeutet, dass der Berechnungspuffer keine graphischen Reihen und kein Farbpuffer-Array hat, oder ihn sogar entfernen, so dass nur B übrig bleibt — der Index des dem Puffer zugeordneten Arrays.

Wir haben das abgeleitete Objekt des abstrakten Basispufferobjekts, das den Typ des erzeugten Indikatorpuffers anhand seines Zeichentyps verdeutlichen. Uns fehlt jedoch noch ein weiteres Pufferobjekt — das berechnete, das zur Durchführung von Berechnungen verwendet wird, die das Array benötigen, ohne dass auf dem Chart etwas angezeigt wird. Ein solches Array ist ein Indikatorpuffer. Das Terminal-Subsystem ist für seine Verteilung zusammen mit den gezeichneten Indikator-Puffer-Arrays verantwortlich.
Erstellen Sie ein solches abgeleitetes Objekt mit dem Klassennamen CBufferCalculate. Erstellen wir in \MQL5\Include\DoEasy\Objects\Indicators\ die neue Datei BufferCalculate.mqh und füllen sie sofort mit dem notwendigen Inhalt:

//+------------------------------------------------------------------+
//|                                              BufferCalculate.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 "Buffer.mqh"
//+------------------------------------------------------------------+
//| Calculated buffer                                                |
//+------------------------------------------------------------------+
class CBufferCalculate : public CBuffer
  {
private:

public:
//--- Constructor
                     CBufferCalculate(const uint index_plot,const uint index_array) :
                        CBuffer(BUFFER_STATUS_NONE,BUFFER_TYPE_CALCULATE,index_plot,index_array,1,0,"Calculate") {}
//--- Supported integer properties of a buffer
   virtual bool      SupportProperty(ENUM_BUFFER_PROP_INTEGER property);
//--- Supported real properties of a buffer
   virtual bool      SupportProperty(ENUM_BUFFER_PROP_DOUBLE property);
//--- Supported string properties of a buffer
   virtual bool      SupportProperty(ENUM_BUFFER_PROP_STRING property);
//--- Display a short buffer description in the journal
   virtual void      PrintShort(void);
   
//--- Set the value to the data buffer array
   void              SetData(const uint series_index,const double value)               { this.SetBufferValue(0,series_index,value);       }
//--- Return the value from the data buffer array
   double            GetData(const uint series_index)                            const { return this.GetDataBufferValue(0,series_index);  }
   
  };
//+------------------------------------------------------------------+
//| Return 'true' if a buffer supports a passed                      |
//| integer property, otherwise return 'false'                       |
//+------------------------------------------------------------------+
bool CBufferCalculate::SupportProperty(ENUM_BUFFER_PROP_INTEGER property)
  {
   if(
      property==BUFFER_PROP_INDEX_PLOT       || 
      property==BUFFER_PROP_STATUS           ||  
      property==BUFFER_PROP_TYPE             || 
      property==BUFFER_PROP_INDEX_BASE       || 
      property==BUFFER_PROP_INDEX_NEXT_BASE
     ) return true; 
   return false;
  }
//+------------------------------------------------------------------+
//| Return 'true' if a buffer supports a passed                      |
//| real property, otherwise return 'false'                          |
//+------------------------------------------------------------------+
bool CBufferCalculate::SupportProperty(ENUM_BUFFER_PROP_DOUBLE property)
  {
   return false;
  }
//+------------------------------------------------------------------+
//| Return 'true' if a buffer supports a passed                      |
//| string property, otherwise return 'false'                        |
//+------------------------------------------------------------------+
bool CBufferCalculate::SupportProperty(ENUM_BUFFER_PROP_STRING property)
  {
   return false;
  }
//+------------------------------------------------------------------+
//| Display short buffer description in the journal                  |
//+------------------------------------------------------------------+
void CBufferCalculate::PrintShort(void)
  {
   ::Print
     (
      CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_BUFFER),"[P",(string)this.IndexPlot(),"/B",(string)this.IndexBase(),"/C",(string)this.IndexColor(),"]: ",
      this.GetTypeBufferDescription()
     );
  }
//+------------------------------------------------------------------+


Die Struktur und die Funktionsprinzipien der nachkommenden Klassen des abstrakten Grundpuffers habe ich bereits im Artikel 43 besprochen. Sie sind ähnlich wie die hier beschriebenen.

Nun, da wir einen vollständigen Satz aller abgeleiteten Klassen des Basispufferobjekts (CBuffer) haben, wollen wir ihn auch modifizieren.

Manchmal ist es notwendig, einige einmalige Aktionen mit dem Indikatorpuffer nach Anforderung oder Bedingungen durchzuführen. Zum Beispiel können Sie die Pufferdaten durch Drücken einer Taste löschen oder aktualisieren. Um dies zu tun, lassen Sie uns das Flag einführen, das angibt, dass noch keine erforderliche Aktion durchgeführt wurde.
Im 'private' Teil der Klasse deklarieren wir die Klassenvariable zum Speichern eines solchen Flags, während im 'public' Abschnitt zwei Methoden zum Setzen von und zum Abrufen der Wertvariable deklarieren:

//+------------------------------------------------------------------+
//| Abstract indicator buffer class                                  |
//+------------------------------------------------------------------+
class CBuffer : public CBaseObj
  {
private:
   long              m_long_prop[BUFFER_PROP_INTEGER_TOTAL];                     // Integer properties
   double            m_double_prop[BUFFER_PROP_DOUBLE_TOTAL];                    // Real properties
   string            m_string_prop[BUFFER_PROP_STRING_TOTAL];                    // String properties
   bool              m_act_state_trigger;                                        // Auxiliary buffer status switch flag
//--- Return the index of the array the buffer's (1) double and (2) string properties are located at
   int               IndexProp(ENUM_BUFFER_PROP_DOUBLE property)           const { return(int)property-BUFFER_PROP_INTEGER_TOTAL;                           }
   int               IndexProp(ENUM_BUFFER_PROP_STRING property)           const { return(int)property-BUFFER_PROP_INTEGER_TOTAL-BUFFER_PROP_DOUBLE_TOTAL;  }
//--- Set the graphical construction type by buffer type and status
   void              SetDrawType(void);
//--- Return the adjusted buffer array index
   int               GetCorrectIndexBuffer(const uint buffer_index) const;

protected:
   struct SDataBuffer { double Array[]; };                                       // Structure for storing buffer object buffer arrays
   SDataBuffer       DataBuffer[];                                               // Array of all object indicator buffers
   double            ColorBufferArray[];                                         // Color buffer array
   int               ArrayColors[];                                              // Array for storing colors



public:
//--- Set buffer's (1) integer, (2) real and (3) string properties
   void              SetProperty(ENUM_BUFFER_PROP_INTEGER property,long value)   { this.m_long_prop[property]=value;                                        }
   void              SetProperty(ENUM_BUFFER_PROP_DOUBLE property,double value)  { this.m_double_prop[this.IndexProp(property)]=value;                      }
   void              SetProperty(ENUM_BUFFER_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_BUFFER_PROP_INTEGER property)        const { return this.m_long_prop[property];                                       }
   double            GetProperty(ENUM_BUFFER_PROP_DOUBLE property)         const { return this.m_double_prop[this.IndexProp(property)];                     }
   string            GetProperty(ENUM_BUFFER_PROP_STRING property)         const { return this.m_string_prop[this.IndexProp(property)];                     }
//--- Get description of buffer's (1) integer, (2) real and (3) string properties
   string            GetPropertyDescription(ENUM_BUFFER_PROP_INTEGER property);
   string            GetPropertyDescription(ENUM_BUFFER_PROP_DOUBLE property);
   string            GetPropertyDescription(ENUM_BUFFER_PROP_STRING property);
//--- Return the flag of the buffer supporting the property
   virtual bool      SupportProperty(ENUM_BUFFER_PROP_INTEGER property)          { return true;       }
   virtual bool      SupportProperty(ENUM_BUFFER_PROP_DOUBLE property)           { return true;       }
   virtual bool      SupportProperty(ENUM_BUFFER_PROP_STRING property)           { return true;       }

//--- Compare CBuffer objects by all possible properties (for sorting the lists by a specified buffer object property)
   virtual int       Compare(const CObject *node,const int mode=0) const;
//--- Compare CBuffer objects by all properties (to search for equal buffer objects)
   bool              IsEqual(CBuffer* compared_obj) const;
                     
//--- Set the buffer name
   void              SetName(const string name)                                  { this.m_name=name;  }
//--- (1) Set and (2) return the buffer status switch flag
   void              SetActStateFlag(const bool flag)                            { this.m_act_state_trigger=flag;    }
   bool              GetActStateFlag(void)                                 const { return this.m_act_state_trigger;  }
   
//--- Default constructor
                     CBuffer(void){;}


Wenn wir zum Beispiel die Pufferdarstellung durch Drücken der Taste deaktivieren, können wir zusätzlich zum Löschen aller Array-Daten auch den Zeichenstil des Puffers auf DRAW_NONE setzen. Um dies zu erreichen, führen Sie im öffentlichen Bereich der Klasse die zusätzliche Methode ein, die den Zeichnungstyp auf das Pufferobjekt setzt:

public:  
//--- Send description of buffer properties to the journal (full_prop=true - all properties, false - only supported ones)
   void              Print(const bool full_prop=false);
//--- Display a short buffer description in the journal (implementation in the descendants)
   virtual void      PrintShort(void) {;}
   
//--- Set (1) the arrow code, (2) vertical shift of arrows, (3) symbol, (4) timeframe, (5) buffer activity flag
//--- (6) drawing type, (7) number of initial bars without drawing, (8) flag of displaying construction values in DataWindow,
//--- (9) shift of the indicator graphical construction along the time axis, (10) line style, (11) line width,
//--- (12) total number of colors, (13) one drawing color, (14) color of drawing in the specified color index,
//--- (15) drawing colors from the color array, (16) empty value, (17) name of the graphical series displayed in DataWindow
   virtual void      SetArrowCode(const uchar code)                  { return;                                                            }
   virtual void      SetArrowShift(const int shift)                  { return;                                                            }
   void              SetSymbol(const string symbol)                  { this.SetProperty(BUFFER_PROP_SYMBOL,symbol);                       }
   void              SetTimeframe(const ENUM_TIMEFRAMES timeframe)   { this.SetProperty(BUFFER_PROP_TIMEFRAME,timeframe);                 }
   void              SetActive(const bool flag)                      { this.SetProperty(BUFFER_PROP_ACTIVE,flag);                         }
   void              SetDrawType(const ENUM_DRAW_TYPE draw_type);
   void              SetDrawBegin(const int value);
   void              SetShowData(const bool flag);
   void              SetShift(const int shift);
   void              SetStyle(const ENUM_LINE_STYLE style);
   void              SetWidth(const int width);
   void              SetColorNumbers(const int number);
   void              SetColor(const color colour);
   void              SetColor(const color colour,const uchar index);
   void              SetColors(const color &array_colors[]);
   void              SetEmptyValue(const double value);
   virtual void      SetLabel(const string label);


Schreiben wir seine Implementierung außerhalb des Klassenkörpers:

//+------------------------------------------------------------------+
//| Set the passed graphical construction type                       |
//+------------------------------------------------------------------+
void CBuffer::SetDrawType(const ENUM_DRAW_TYPE draw_type)
  {
   if(this.TypeBuffer()==BUFFER_TYPE_CALCULATE)
      return;
   this.SetProperty(BUFFER_PROP_DRAW_TYPE,draw_type);
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_DRAW_TYPE,draw_type);
  }
//+------------------------------------------------------------------+


Hier ist der Puffertyp Berechnung, hat er kein Rendering — wir verlassen die Methode .
Setzen wir den Zeichentyp (an die Methode übergeben) auf die Objekteigenschaft und setzen den Stil für den Puffer.

Da ich die Konstante BUFFER_PROP_INDEX_NEXT durch BUFFER_PROP_INDEX_NEXT_BASE ersetzt habe, benennen wir die entsprechende Methode IndexNextBuffer() in IndexNextBaseBuffer() um und fügen eine weitere Methode hinzu, die den Index des nächsten gezeichneten Puffers zurückgibt:

//--- Return (1) the serial number of the drawn buffer, (2) bound array index, (3) color buffer index,
//--- (4) index of the first free bound array, (5) index of the next drawn buffer, (6) buffer data period, (7) buffer status,
//--- (8) buffer type, (9) buffer usage flag, (10) arrow code, (11) arrow shift for DRAW_ARROW style,
//--- (12) number of initial bars that are not drawn and values in DataWindow, (13) graphical construction type,
//--- (14) flag of displaying construction values in DataWindow, (15) indicator graphical construction shift along the time axis,
//--- (16) drawing line style, (17) drawing line width, (18) number of colors, (19) drawing color, number of buffers for construction
//--- (20) set empty value, (21) buffer symbol, (22) name of the indicator graphical series displayed in DataWindow
   int               IndexPlot(void)                           const { return (int)this.GetProperty(BUFFER_PROP_INDEX_PLOT);              }
   int               IndexBase(void)                           const { return (int)this.GetProperty(BUFFER_PROP_INDEX_BASE);              }
   int               IndexColor(void)                          const { return (int)this.GetProperty(BUFFER_PROP_INDEX_COLOR);             }
   int               IndexNextBaseBuffer(void)                 const { return (int)this.GetProperty(BUFFER_PROP_INDEX_NEXT_BASE);         }
   int               IndexNextPlotBuffer(void)                 const { return (int)this.GetProperty(BUFFER_PROP_INDEX_NEXT_PLOT);         }
   ENUM_TIMEFRAMES   Timeframe(void)                           const { return (ENUM_TIMEFRAMES)this.GetProperty(BUFFER_PROP_TIMEFRAME);   }


Fügen wir schließlich vier Methoden ganz am Ende des Klassenkörpers hinzu — zwei Methoden zur Initialisierung von Objekt-Arrays und zwei Methoden zum Füllen dieser Arrays:

//--- Return the size of the data buffer array
   virtual int       GetDataTotal(const uint buffer_index=0)   const;
//--- Return the value from the specified index of the specified (1) data, (2) color index and (3) color buffer arrays
   double            GetDataBufferValue(const uint buffer_index,const uint series_index) const;
   int               GetColorBufferValueIndex(const uint series_index) const;
   color             GetColorBufferValueColor(const uint series_index) const;
//--- Set the value to the specified index of the specified (1) data and (2) color buffer arrays
   void              SetBufferValue(const uint buffer_index,const uint series_index,const double value);
   void              SetBufferColorIndex(const uint series_index,const uchar color_index);
//--- Initialize all object buffers by (1) a specified value, (2) the empty value specified for the object
   void              InitializeAll(const double value,const uchar color_index);
   void              InitializeAll(void);
//--- Fill all data buffers with empty values in the specified timeseries index
   void              ClearData(const int series_index);
//--- Fill the specified buffer with data from (1) the passed timeseries and (2) the array
   void              FillAsSeries(const int buffer_index,CSeriesDE *series,const ENUM_SORT_BAR_MODE property);
   void              FillAsSeries(const int buffer_index,const double &array[]);
   
  };
//+------------------------------------------------------------------+


Tatsächlich handelt es sich dabei um zwei überladene Methoden, die für verschiedene Situationen unterschiedliche Parametersätze akzeptieren.

Die parametrische Methode zur Initialisierung aller Objektpuffer als Parameter nimmt den Wert an, der zur Initialisierung aller Pufferobjekt-Arrays verwendet wird, die von Indikatorpuffern zugewiesen wurden, sowie den Farbindex, der zur Initialisierung des Arrays verwendet wird, das vom Farbpuffer im Pufferobjekt zugewiesen wurde.
Die Methode ohne Eingaben initialisiert alle Arrays des Pufferobjekts mit dem in den Eigenschaften des Pufferobjekts als "leer" gesetzten Wert, während das Farb-Array mit Null initialisiert wird — die allererste Farbe, die auf das Pufferobjekt gesetzt wird (oder die einzige, wenn es nur eine Farbe gibt).

Implementieren wir die Methoden außerhalb des Klassenkörpers:

//+------------------------------------------------------------------+
//| Initialize all object buffers by the specified value             |
//+------------------------------------------------------------------+
void CBuffer::InitializeAll(const double value,const uchar color_index)
  {
   for(int i=0;i<this.GetProperty(BUFFER_PROP_NUM_DATAS);i++)
      ::ArrayInitialize(this.DataBuffer[i].Array,value);
   if(this.Status()!=BUFFER_STATUS_FILLING && this.TypeBuffer()!=BUFFER_TYPE_CALCULATE)
      ::ArrayInitialize(this.ColorBufferArray,(color_index>this.ColorsTotal()-1 ? 0 : color_index));
  }
//+------------------------------------------------------------------+
//| Initialize all object buffers                                    |
//| by the empty value set for the object                            |
//+------------------------------------------------------------------+
void CBuffer::InitializeAll(void)
  {
   for(int i=0;i<this.GetProperty(BUFFER_PROP_NUM_DATAS);i++)
      ::ArrayInitialize(this.DataBuffer[i].Array,this.EmptyValue());
   if(this.Status()!=BUFFER_STATUS_FILLING && this.TypeBuffer()!=BUFFER_TYPE_CALCULATE)
      ::ArrayInitialize(this.ColorBufferArray,0);
  }
//+------------------------------------------------------------------+


Zunächst initialisieren wir in der Schleife über die Anzahl der von Indikatorpuffern zugewiesenen Pufferobjekt-Arrays jedes nächste Array mit dem Wert, der in der ersten Methode an die Methode übergeben wurde, und dem für den Puffer in der zweiten Methode gesetzten Wert. Wenn das Pufferobjekt nicht den Zeichenstil "Farbfüllung zwischen zwei Ebenen" hat und kein Berechnungspuffer ist (die Puffer haben keinen Farbpuffer), wird als Nächstes das Farb-Array mit dem Wert initialisiert, der der Methode in der ersten Methode übergeben wurde, und mit dem Wert Null in der zweiten Methode.

//+-----------------------------------------------------------------------+
//| Fill the specified buffer with data from the passed timeseries' bars  |
//+-----------------------------------------------------------------------+
void CBuffer::FillAsSeries(const int buffer_index,CSeriesDE *series,const ENUM_SORT_BAR_MODE property)
  {
//--- If an empty timeseries is passed or a property to fill the array is a string one, leave the method
   if(series==NULL || property>FIRST_BAR_STR_PROP-1)
      return;
//--- Get the list of all its timeseries from the passed timeseries object
   CArrayObj *list=series.GetList();
   if(list==NULL || list.Total()==0)
      return;
//--- Get the number of bars in the timeseries list
   int total=list.Total();
//--- In the loop from the very last timeseries list element (from the current bar)
   int n=0;
   for(int i=total-1;i>WRONG_VALUE && !::IsStopped();i--)
     {
      //--- get the next bar object by the loop index,
      CBar *bar=list.At(i);
      //--- Set the value of the copied property
      double value=
        (bar==NULL ? this.EmptyValue()                                                    :
         property<FIRST_BAR_DBL_PROP ? bar.GetProperty((ENUM_BAR_PROP_INTEGER)property)   :
         property<FIRST_BAR_STR_PROP ? bar.GetProperty((ENUM_BAR_PROP_DOUBLE)property)    :
         this.EmptyValue()
        );
      //--- calculate the index, based on which the bar property is saved to the buffer, and
      //--- write the value of the obtained bar property to the buffer array using the calculated index
      n=total-1-i;
      this.SetBufferValue(buffer_index,n,value);
     }
  }
//+------------------------------------------------------------------+
//| Fill the specified buffer with data from the passed array        |
//+------------------------------------------------------------------+
void CBuffer::FillAsSeries(const int buffer_index,const double &array[])
  {
//--- Get the copied array size
   int total=::ArraySize(array);
   if(total==0)
      return;
//--- In the loop from the very last array element (from the current bar)
   int n=0;
   for(int i=total-1;i>WRONG_VALUE && !::IsStopped();i--)
     {
      //--- calculate the index, based on which the array value is saved to the buffer, and
      //--- write the value of the array cell by i index using the calculated index
      n=total-1-i;
      this.SetBufferValue(buffer_index,n,array[i]);
     }
  }
//+------------------------------------------------------------------+

Diese beiden Methoden werden in der Codeliste ausführlich kommentiert. Die erste Methode empfängt den Zeiger auf das Zeitreihenobjekt, dessen Daten zum Füllen des Indikatorpuffers verwendet werden sollen, während die zweite Methode ein double Array erhält. Ihre Daten werden auch zum Füllen des Indikatorpuffers verwendet.

Lassen Sie uns den geschlossenen Klassenkonstruktor anpassen:

//+------------------------------------------------------------------+
//| Closed parametric constructor                                    |
//+------------------------------------------------------------------+
CBuffer::CBuffer(ENUM_BUFFER_STATUS buffer_status,
                 ENUM_BUFFER_TYPE buffer_type,
                 const uint index_plot,
                 const uint index_base_array,
                 const int num_datas,
                 const int width,
                 const string label)
  {
   this.m_type=COLLECTION_BUFFERS_ID;
   this.m_act_state_trigger=true;
//--- Save integer properties
   this.m_long_prop[BUFFER_PROP_STATUS]                        = buffer_status;
   this.m_long_prop[BUFFER_PROP_TYPE]                          = buffer_type;
   ENUM_DRAW_TYPE type=
     (
      !this.TypeBuffer() || !this.Status() ? DRAW_NONE      : 
      this.Status()==BUFFER_STATUS_FILLING ? DRAW_FILLING   : 
      ENUM_DRAW_TYPE(this.Status()+8)
     );
   this.m_long_prop[BUFFER_PROP_DRAW_TYPE]                     = type;
   this.m_long_prop[BUFFER_PROP_TIMEFRAME]                     = PERIOD_CURRENT;
   this.m_long_prop[BUFFER_PROP_ACTIVE]                        = true;
   this.m_long_prop[BUFFER_PROP_ARROW_CODE]                    = 0x9F;
   this.m_long_prop[BUFFER_PROP_ARROW_SHIFT]                   = 0;
   this.m_long_prop[BUFFER_PROP_DRAW_BEGIN]                    = 0;
   this.m_long_prop[BUFFER_PROP_SHOW_DATA]                     = (buffer_type>BUFFER_TYPE_CALCULATE ? true : false);
   this.m_long_prop[BUFFER_PROP_SHIFT]                         = 0;
   this.m_long_prop[BUFFER_PROP_LINE_STYLE]                    = STYLE_SOLID;
   this.m_long_prop[BUFFER_PROP_LINE_WIDTH]                    = width;
   this.m_long_prop[BUFFER_PROP_COLOR_INDEXES]                 = (this.Status()>BUFFER_STATUS_NONE ? (this.Status()!=BUFFER_STATUS_FILLING ? 1 : 2) : 0);
   this.m_long_prop[BUFFER_PROP_COLOR]                         = clrRed;
   this.m_long_prop[BUFFER_PROP_NUM_DATAS]                     = num_datas;
   this.m_long_prop[BUFFER_PROP_INDEX_PLOT]                    = index_plot;
   this.m_long_prop[BUFFER_PROP_INDEX_BASE]                    = index_base_array;
   this.m_long_prop[BUFFER_PROP_INDEX_COLOR]                   = this.GetProperty(BUFFER_PROP_INDEX_BASE)+this.GetProperty(BUFFER_PROP_NUM_DATAS);
   this.m_long_prop[BUFFER_PROP_INDEX_NEXT_BASE]               = this.GetProperty(BUFFER_PROP_INDEX_COLOR)+
                                                                    (this.Status()==BUFFER_STATUS_FILLING || this.TypeBuffer()==BUFFER_TYPE_CALCULATE ? 0 : 1);
   this.m_long_prop[BUFFER_PROP_INDEX_NEXT_PLOT]               = (this.TypeBuffer()>BUFFER_TYPE_CALCULATE ? index_plot+1 : index_plot);
   
//--- Save real properties
   this.m_double_prop[this.IndexProp(BUFFER_PROP_EMPTY_VALUE)] = (this.TypeBuffer()>BUFFER_TYPE_CALCULATE ? EMPTY_VALUE : 0);
//--- Save string properties
   this.m_string_prop[this.IndexProp(BUFFER_PROP_SYMBOL)]      = ::Symbol();
   this.m_string_prop[this.IndexProp(BUFFER_PROP_LABEL)]       = (this.TypeBuffer()>BUFFER_TYPE_CALCULATE ? label : NULL);

//--- If failed to change the size of the indicator buffer array, display the appropriate message indicating the string
   if(::ArrayResize(this.DataBuffer,(int)this.GetProperty(BUFFER_PROP_NUM_DATAS))==WRONG_VALUE)
      ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_SYS_FAILED_DRAWING_ARRAY_RESIZE),". ",CMessage::Text(MSG_LIB_SYS_ERROR),": ",(string)::GetLastError());
      
//--- If failed to change the size of the color array (only for a non-calculated buffer), display the appropriate message indicating the string
   if(this.TypeBuffer()>BUFFER_TYPE_CALCULATE)
      if(::ArrayResize(this.ArrayColors,(int)this.ColorsTotal())==WRONG_VALUE)
         ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_SYS_FAILED_COLORS_ARRAY_RESIZE),". ",CMessage::Text(MSG_LIB_SYS_ERROR),": ",(string)::GetLastError());

//--- For DRAW_FILLING, fill in the color array with two default colors
   if(this.Status()==BUFFER_STATUS_FILLING)
     {
      this.SetColor(clrBlue,0);
      this.SetColor(clrRed,1);
     }

//--- Bind indicator buffers with arrays
//--- In a loop by the number of indicator buffers
   int total=::ArraySize(DataBuffer);
   for(int i=0;i<total;i++)
     {
      //--- calculate the index of the next array and
      //--- bind the indicator buffer by the calculated index with the dynamic array
      //--- located by the i loop index in the DataBuffer array
      int index=(int)this.GetProperty(BUFFER_PROP_INDEX_BASE)+i;
      ::SetIndexBuffer(index,this.DataBuffer[i].Array,(this.TypeBuffer()==BUFFER_TYPE_DATA ? INDICATOR_DATA : INDICATOR_CALCULATIONS));
      //--- Set indexation flag as in the timeseries to all buffer arrays
      ::ArraySetAsSeries(this.DataBuffer[i].Array,true);
     }
//--- Bind the color buffer with the array (only for a non-calculated buffer and not for the filling buffer)
   if(this.Status()!=BUFFER_STATUS_FILLING && this.TypeBuffer()!=BUFFER_TYPE_CALCULATE)
     {
      ::SetIndexBuffer((int)this.GetProperty(BUFFER_PROP_INDEX_COLOR),this.ColorBufferArray,INDICATOR_COLOR_INDEX);
      ::ArraySetAsSeries(this.ColorBufferArray,true);
     }
//--- If this is a calculated buffer, all is done
   if(this.TypeBuffer()==BUFFER_TYPE_CALCULATE)
      return;
//--- Set integer parameters of the drawn buffer
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_DRAW_TYPE,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_DRAW_TYPE));
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_ARROW,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_ARROW_CODE));
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_ARROW_SHIFT,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_ARROW_SHIFT));
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_DRAW_BEGIN,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_DRAW_BEGIN));
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_SHOW_DATA,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_SHOW_DATA));
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_SHIFT,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_SHIFT));
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LINE_STYLE,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_LINE_STYLE));
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LINE_WIDTH,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_LINE_WIDTH));
   this.SetColor((color)this.GetProperty(BUFFER_PROP_COLOR));
//--- Set real buffer parameters
   ::PlotIndexSetDouble((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_EMPTY_VALUE,this.GetProperty(BUFFER_PROP_EMPTY_VALUE));
//--- Set string buffer parameters
   ::PlotIndexSetString((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LABEL,this.GetProperty(BUFFER_PROP_LABEL));
  }
//+------------------------------------------------------------------+


Die Änderungen haben sich auf das Setzen der Variablen m_act_state_trigger auf true ausgewirkt, was bedeutet, dass der Puffer bereits während der Erstellung behandelt wurde (z.B. werden wir beim Knopfdruck prüfen, "ob die notwendige Aktion mit dem Pufferobjekt durchgeführt wurde", wenn nicht, dann sollte sie durchgeführt werden. Das gesetzte Flag erlaubt nicht, dass eine falsche Aktion ausgeführt wird).
Die übrigen Verbesserungen hängen damit zusammen, dass wir bereits einen Berechnungspuffer ohne Farb-Array haben. Es besteht also keine Notwendigkeit, dafür grafische Reiheneigenschaften festzulegen. Daher wird bei der Berechnung von Array-Indizes jetzt der Objekttyp des Puffers berücksichtigt. Wenn es sich um einen Berechnungspuffer handelt, werden die entsprechenden Anpassungen in die Indexberechnung eingeführt. Bevor wir die grafischen Reihenwerte für Indikatorpuffer einstellen, überprüfen wir den Pufferobjekttyp. Handelt es sich um einen Berechnungspuffer, verlassen wir die Methode — ein Berechnungspuffer hat keine graphischen Reihen.

Bei Methoden zur Einstellung der grafischen Reiheneigenschaften von Pufferobjekten führen wir die Prüfung für den Puffertyp ein. Wenn es sich um einen Berechnungspuffer handelt, verlassen wir die Methode sofort:

//+------------------------------------------------------------------+
//| Set the number of initial bars                                   |
//| without drawing and values in DataWindow                         |
//+------------------------------------------------------------------+
void CBuffer::SetDrawBegin(const int value)
  {
   if(this.TypeBuffer()==BUFFER_TYPE_CALCULATE)
      return;
   this.SetProperty(BUFFER_PROP_DRAW_BEGIN,value);
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_DRAW_BEGIN,value);
  }
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
//| Set the number of colors                                         |
//+------------------------------------------------------------------+
void CBuffer::SetColorNumbers(const int number)
  {
   if(number>IND_COLORS_TOTAL || this.TypeBuffer()==BUFFER_TYPE_CALCULATE)
      return;
   int n=(this.Status()!=BUFFER_STATUS_FILLING ? number : 2);
   this.SetProperty(BUFFER_PROP_COLOR_INDEXES,n);
   ::ArrayResize(this.ArrayColors,n);
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_COLOR_INDEXES,n);
  }
//+------------------------------------------------------------------+


Ähnliche Prüfungen wurden für alle Methoden eingeführt, die für den Berechnungspuffer nicht benötigt werden — wir werden sie hier nicht berücksichtigen.

Manchmal, wenn bei der Berechnung des Balkenindexes der Indikatorpufferwert gesetzt werden sollte, stellt sich heraus, dass der errechnete Wert kleiner als Null ist. Dies passiert, wenn der Balkenindex auf dem aktuellen Chart berechnet wird, während die Daten einer Chartperiode, die nicht gleich der aktuellen ist, darauf angezeigt werden. Um zusätzliche Prüfungen für die Gültigkeit des berechneten Index zu vermeiden, wäre es viel einfacher, den übergebenen Index auf einen negativen Wert zu prüfen und die Methode in den Methoden zu verlassen, die für das Schreiben von Werten in Arrays durch einen angegebenen Index zuständig sind:

//+------------------------------------------------------------------+
//| Set the value to the specified timeseries index                  |
//| for the specified data buffer array                              |
//+------------------------------------------------------------------+
void CBuffer::SetBufferValue(const uint buffer_index,const uint series_index,const double value)
  {
   if(this.GetDataTotal(buffer_index)==0)
      return;
   int correct_buff_index=this.GetCorrectIndexBuffer(buffer_index);
   int data_total=this.GetDataTotal(buffer_index);
   int data_index=((int)series_index<data_total ? (int)series_index : data_total-1);
   if(data_index<0)
      return;
   this.DataBuffer[correct_buff_index].Array[data_index]=value;
  }
//+------------------------------------------------------------------+
//| Set the color index to the specified timeseries index            |
//| of the color buffer array                                        |
//+------------------------------------------------------------------+
void CBuffer::SetBufferColorIndex(const uint series_index,const uchar color_index)
  {
   if(this.GetDataTotal(0)==0 || color_index>this.ColorsTotal()-1 || this.Status()==BUFFER_STATUS_FILLING || this.Status()==BUFFER_STATUS_NONE)
      return;
   int data_total=this.GetDataTotal(0);
   int data_index=((int)series_index<data_total ? (int)series_index : data_total-1);
   if(::ArraySize(this.ColorBufferArray)==0)
      ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR),": ",CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_INVALID_PROPERTY_BUFF));
   if(data_index<0)
      return;
   this.ColorBufferArray[data_index]=color_index;
  }
//+------------------------------------------------------------------+


Ich habe auch einige kleinere Änderungen am Code der Klasse vorgenommen. Es macht keinen Sinn, sie hier zu beschreiben, da sie sich nicht auf die Leistung des Codes auswirken. Sie können alle Änderungen in den unten angehängten Dateien sehen.

Lassen Sie uns nun Ergänzungen zur Kollektionsklasse der Indikatorpuffer CBuffersCollection in \MQL5\Include\DoEasy\Collections\BuffersCollection.mqh vornehmen.
Da ich die Möglichkeit einführe, mit Chart-Perioden zu arbeiten, die sich von der aktuellen Periode unterscheiden, sollte die Kollektionsklasse der Puffer Zugriff auf die Zeitreihensammlungsklasse haben. Holen Sie sich aus der Zeitreihensammlung alle Zeitreihen, die für die Berechnung des Indikator-Pufferaufbaus im aktuellen Chart unter Verwendung der Daten aus anderen Periodendiagrammen erforderlich sind.
Damit die Kollektionsklasse der Puffer auf die Kollektionsklasse der Zeitreihen zugreifen kann, übergeben wir einfach den Zeiger auf die Zeitreihensammlung an die Sammlung von Indikatorpuffern. Lassen Sie uns diesen Zeiger verwenden, um die notwendigen Daten und Methoden auszuwählen, um sie in der Klasse zu empfangen.

Ähnlich wie bei der Änderung des Konstantennamens in allen Pufferobjekt-Klassendateien wurden hier bereits alle Instanzen der Zeichenfolge BUFFER_PROP_INDEX_PLOT durch BUFFER_PROP_INDEX_NEXT_PLOT ersetzt, während der Zugriff auf die Methode IndexNextBuffer() durch ihre umbenannte Version IndexNextBaseBuffer() ersetzt wurde.

Wir schließen die Berechnungspufferobjektklassendatei und die Datei der Kollektionsklasse der Zeitreihen in die Datei der Kollektionsklasse der Indikatorpuffer ein und definieren die 'private' Klassenvariable zum Speichern des Zeigers auf das Objekt der Kollektionsklasse der Zeitreihen:

//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "ListObj.mqh"
#include "..\Objects\Indicators\BufferArrow.mqh"
#include "..\Objects\Indicators\BufferLine.mqh"
#include "..\Objects\Indicators\BufferSection.mqh"
#include "..\Objects\Indicators\BufferHistogram.mqh"
#include "..\Objects\Indicators\BufferHistogram2.mqh"
#include "..\Objects\Indicators\BufferZigZag.mqh"
#include "..\Objects\Indicators\BufferFilling.mqh"
#include "..\Objects\Indicators\BufferBars.mqh"
#include "..\Objects\Indicators\BufferCandles.mqh"
#include "..\Objects\Indicators\BufferCalculate.mqh"
#include "TimeSeriesCollection.mqh"
//+------------------------------------------------------------------+
//| Collection of indicator buffers                                  |
//+------------------------------------------------------------------+
class CBuffersCollection : public CObject
  {
private:
   CListObj                m_list;                       // Buffer object list
   CTimeSeriesCollection  *m_timeseries;                 // Pointer to the timeseries collection object


Obwohl es mehrere Methoden gibt, die hinzugefügt werden können, haben die meisten von ihnen ähnliche Funktionen für verschiedene Pufferobjekte. Um also die Beschreibung der einzelnen Methoden zu vermeiden (zumal sie alle in den entsprechenden Kommentaren beschrieben sind), deklarieren wir alle neuen Methoden im Klassenkörper und analysieren einzeln ihre Struktur und Prinzipien:

//+------------------------------------------------------------------+
//| Collection of indicator buffers                                  |
//+------------------------------------------------------------------+
class CBuffersCollection : public CObject
  {
private:
   CListObj                m_list;                       // Buffer object list
   CTimeSeriesCollection  *m_timeseries;                 // Pointer to the timeseries collection object
   
//--- 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:
//--- Return (1) oneself and (2) the timeseries list
   CBuffersCollection     *GetObject(void)               { return &this;                                       }
   CArrayObj              *GetList(void)                 { return &this.m_list;                                }
//--- Return the number of (1) drawn buffers, (2) all arrays used to build all buffers in the collection
   int                     PropertyPlotsTotal(void);
   int                     PropertyBuffersTotal(void);
   
//--- Create the new buffer (1) "Drawing with arrows", (2) "Line", (3) "Sections", (4) "Histogram from the zero line", 
//--- (5) "Histogram on two indicator buffers", (6) "Zigzag", (7) "Color filling between two levels",
//--- (8) "Display as bars", (9) "Display as candles", calculated buffer
   bool                    CreateArrow(void)             { return this.CreateBuffer(BUFFER_STATUS_ARROW);      }
   bool                    CreateLine(void)              { return this.CreateBuffer(BUFFER_STATUS_LINE);       }
   bool                    CreateSection(void)           { return this.CreateBuffer(BUFFER_STATUS_SECTION);    }
   bool                    CreateHistogram(void)         { return this.CreateBuffer(BUFFER_STATUS_HISTOGRAM);  }
   bool                    CreateHistogram2(void)        { return this.CreateBuffer(BUFFER_STATUS_HISTOGRAM2); }
   bool                    CreateZigZag(void)            { return this.CreateBuffer(BUFFER_STATUS_ZIGZAG);     }
   bool                    CreateFilling(void)           { return this.CreateBuffer(BUFFER_STATUS_FILLING);    }
   bool                    CreateBars(void)              { return this.CreateBuffer(BUFFER_STATUS_BARS);       }
   bool                    CreateCandles(void)           { return this.CreateBuffer(BUFFER_STATUS_CANDLES);    }
   bool                    CreateCalculate(void)         { return this.CreateBuffer(BUFFER_STATUS_NONE);       }
   
//--- Return the buffer by (1) the graphical series name, (2) timeframe, (2) Plot index and (3) collection list object
   CBuffer                *GetBufferByLabel(const string plot_label);
   CBuffer                *GetBufferByTimeframe(const ENUM_TIMEFRAMES timeframe);
   CBuffer                *GetBufferByPlot(const int plot_index);
   CBuffer                *GetBufferByListIndex(const int index_list);
//--- Return buffers by their status by the specified serial number
//--- (0 - the very first created buffer with the ХХХ drawing style, 1,2,N - subsequent ones)
   CBufferArrow           *GetBufferArrow(const int number);
   CBufferLine            *GetBufferLine(const int number);
   CBufferSection         *GetBufferSection(const int number);
   CBufferHistogram       *GetBufferHistogram(const int number);
   CBufferHistogram2      *GetBufferHistogram2(const int number);
   CBufferZigZag          *GetBufferZigZag(const int number);
   CBufferFilling         *GetBufferFilling(const int number);
   CBufferBars            *GetBufferBars(const int number);
   CBufferCandles         *GetBufferCandles(const int number);
   CBufferCalculate       *GetBufferCalculate(const int number);
   
//--- Initialize all drawn buffers by a (1) specified value, (2) empty value set for the buffer object
   void                    InitializePlots(const double value,const uchar color_index);
   void                    InitializePlots(void);
//--- Initialize all calculated buffers by a (1) specified value, (2) empty value set for the buffer object
   void                    InitializeCalculates(const double value);
   void                    InitializeCalculates(void);
//--- Set color values from the passed color array for all indicator buffers within the collection
   void                    SetColors(const color &array_colors[]);
   
//--- Set the value by the timeseries index for the (1) arrow, (2) line, (3) section, (4) zero line histogram,
//--- (5) two buffer histogram, (6) zigzag, (7) filling, (8) bar, (9) candle, (10) calculated buffer
   void                    SetBufferArrowValue(const int number,const int series_index,const double value,const uchar color_index,bool as_current=false);
   void                    SetBufferLineValue(const int number,const int series_index,const double value,const uchar color_index,bool as_current=false);
   void                    SetBufferSectionValue(const int number,const int series_index,const double value,const uchar color_index,bool as_current=false);
   void                    SetBufferHistogramValue(const int number,const int series_index,const double value,const uchar color_index,bool as_current=false);
   void                    SetBufferHistogram2Value(const int number,const int series_index,const double value1,const double value2,const uchar color_index,bool as_current=false);
   void                    SetBufferZigZagValue(const int number,const int series_index,const double value1,const double value2,const uchar color_index,bool as_current=false);
   void                    SetBufferFillingValue(const int number,const int series_index,const double value1,const double value2,bool as_current=false);
   void                    SetBufferBarsValue(const int number,const int series_index,const double open,const double high,const double low,const double close,const uchar color_index,bool as_current=false);
   void                    SetBufferCandlesValue(const int number,const int series_index,const double open,const double high,const double low,const double close,const uchar color_index,bool as_current=false);
   void                    SetBufferCalculateValue(const int number,const int series_index,const double value);
   
//--- Set the color index to the color buffer by its serial number of
//--- (1) arrows, (2) lines, (3) sections, (4) zero histogram
//--- (5) histogram on two buffers, (6) zigzag, (7) filling, (8) bars and (9) candles
//--- (0 - the very first created buffer with the ХХХ drawing style, 1,2,N - subsequent ones)
   void                    SetBufferArrowColorIndex(const int number,const int series_index,const uchar color_index);
   void                    SetBufferLineColorIndex(const int number,const int series_index,const uchar color_index);
   void                    SetBufferSectionColorIndex(const int number,const int series_index,const uchar color_index);
   void                    SetBufferHistogramColorIndex(const int number,const int series_index,const uchar color_index);
   void                    SetBufferHistogram2ColorIndex(const int number,const int series_index,const uchar color_index);
   void                    SetBufferZigZagColorIndex(const int number,const int series_index,const uchar color_index);
   void                    SetBufferFillingColorIndex(const int number,const int series_index,const uchar color_index);
   void                    SetBufferBarsColorIndex(const int number,const int series_index,const uchar color_index);
   void                    SetBufferCandlesColorIndex(const int number,const int series_index,const uchar color_index);
   
//--- Clear buffer data by its index in the list in the specified timeseries bar
   void                    Clear(const int buffer_list_index,const int series_index);
//--- Clear data by the timeseries index for the (1) arrow, (2) line, (3) section, (4) zero line histogram,
//--- (5) histogram on two buffers, (6) zigzag, (7) filling, (8) bars and (9) candles
   void                    ClearBufferArrow(const int number,const int series_index);
   void                    ClearBufferLine(const int number,const int series_index);
   void                    ClearBufferSection(const int number,const int series_index);
   void                    ClearBufferHistogram(const int number,const int series_index);
   void                    ClearBufferHistogram2(const int number,const int series_index);
   void                    ClearBufferZigZag(const int number,const int series_index);
   void                    ClearBufferFilling(const int number,const int series_index);
   void                    ClearBufferBars(const int number,const int series_index);
   void                    ClearBufferCandles(const int number,const int series_index);
   
//--- Constructor
                           CBuffersCollection();
//--- Get pointers to the timeseries collection (the method is called in the CollectionOnInit() method of the CEngine object)
   void                    OnInit(CTimeSeriesCollection *timeseries) { this.m_timeseries=timeseries;  }
  };
//+------------------------------------------------------------------+


Betrachten wir die Aufgaben der neu hinzugekommenen Methoden. Während ich die Methodenzuordnung beschreibe, werde ich sofort ihre Implementierung besprechen, die, wie die meisten Methoden von Bibliotheksklassen, aus dem Klassenkörper heraus verschoben wurde.

GetIndexLastPlot() gibt den Index der grafischen Reihe des zuletzt erzeugten Pufferobjekts zurück:

//+------------------------------------------------------------------+
//| Return the index of the last drawn buffer                        |
//+------------------------------------------------------------------+
int CBuffersCollection::GetIndexLastPlot(void)
  {
//--- Return the pointer to the list. If the list is not created for some reason, return -1
   CArrayObj *list=this.GetList();
   if(list==NULL)
      return WRONG_VALUE;
//--- Get the index of the drawn buffer with the highest value. If the FindBufferMax() method returns -1,
//--- the list is empty, return index 0 for the very first buffer in the list
   int index=CSelect::FindBufferMax(list,BUFFER_PROP_INDEX_PLOT);
   if(index==WRONG_VALUE)
      return 0;
//--- if the index is not -1,
//--- get the buffer object from the list by its index
   CBuffer *buffer=this.m_list.At(index);
   if(buffer==NULL)
      return WRONG_VALUE;
//--- Return the Plot index of the buffer object
   return buffer.IndexPlot();
  }
//+------------------------------------------------------------------+


Die Methode ist recht spezifisch und ist vor allem für die Methode zur Erstellung eines neuen Puffers gedacht, da der von ihr zurückgegebene Wert an die Größe der Pufferliste gebunden ist (wenn die Liste leer ist, wird Null statt -1 zurückgegeben). Sollte sich diese Methode später für andere Zwecke als notwendig erweisen, so werde ich sie leicht korrigieren. Wenn sie außer für die aktuelle Anwendung nicht mehr benötigt wird, dann wird die Methode privat gemacht.

Die 'private' Methode GetBarsData() wird verwendet, um Daten aus anderen Zeitreihen (nicht nativ für das Pufferobjekt) zu erhalten, und gibt die Anzahl der Balken des aktuellen Zeitrahmens zurück, die in einem Balken der Chart-Periode des Pufferobjekts enthalten sind:

//+------------------------------------------------------------------+
//| Get data of the necessary timeseries and bars                    |
//| for working with a single bar of the buffer                      |
//+------------------------------------------------------------------+
int CBuffersCollection::GetBarsData(CBuffer *buffer,const int series_index,int &index_bar_period)
  {
//--- Get timeseries of the current chart and the chart of the buffer timeframe
   CSeriesDE *series_current=this.m_timeseries.GetSeries(buffer.Symbol(),PERIOD_CURRENT);
   CSeriesDE *series_period=this.m_timeseries.GetSeries(buffer.Symbol(),buffer.Timeframe());
   if(series_current==NULL || series_period==NULL)
      return WRONG_VALUE;
//--- Get the bar object of the current timeseries corresponding to the required timeseries index
   CBar *bar_current=series_current.GetBar(series_index);
   if(bar_current==NULL)
      return WRONG_VALUE;
//--- Get the timeseries bar object of the buffer chart period corresponding to the time the timeseries bar of the current chart falls into
   CBar *bar_period=m_timeseries.GetBarSeriesFirstFromSeriesSecond(NULL,PERIOD_CURRENT,bar_current.Time(),NULL,series_period.Timeframe());
   if(bar_period==NULL)
      return WRONG_VALUE;
//--- Write down the bar index on the current timeframe which falls into the bar start time of the buffer object chart 
   index_bar_period=bar_period.Index(PERIOD_CURRENT);
//--- Calculate the amount of bars of the current timeframe included into one bar of the buffer object chart period
//--- and return this value (1 if the result is 0)
   int num_bars=::PeriodSeconds(bar_period.Timeframe())/::PeriodSeconds(bar_current.Timeframe());
   return(num_bars>0 ? num_bars : 1);
  }
//+------------------------------------------------------------------+


Die Methode wird häufig für die Berechnung der Balken des aktuellen Charts verwendet werden, wobei Sie den Wert des Indikatorpuffers mit Daten aus der Chartperiode schreiben müssen, die vom aktuellen Zeitrahmen abweichen.

Die Methoden PropertyPlotsTotal() und PropertyBuffersTotal() sind die früheren Methoden PlotsTotal() und BuffersTotal(), die zur besseren Übersichtlichkeit umbenannt wurden. Sie übergeben an #property indicator_plots und #property indicator_buffers korrekte Werte, um sie als Programmeigenschaften des Indikators zu spezifizieren.
In der Methode PropertyBuffersTotal() wurden auch die Konstantennamen durch die umbenannten ersetzt (BUFFER_PROP_INDEX_NEXT wurden durch BUFFER_PROP_INDEX_NEXT_BASE ersetzt).
Die Methode PropertyPlotsTotal() wurde aufgrund der Entstehung eines Berechnungspuffers umgeschrieben. Daher sollten die Daten jetzt auf eine andere Art und Weise abgerufen werden:

//+------------------------------------------------------------------+
//| Return the number of drawn buffers                               |
//+------------------------------------------------------------------+
int CBuffersCollection::PropertyPlotsTotal(void)
  {
   CArrayObj *list=CSelect::ByBufferProperty(this.GetList(),BUFFER_PROP_TYPE,BUFFER_TYPE_DATA,EQUAL);
   return(list!=NULL ? list.Total() : WRONG_VALUE);
  }
//+------------------------------------------------------------------+


Holen wir uns die Liste der zu zeichnenden Pufferobjekte und geben deren Größe zurück. Wenn die Liste leer ist, wird -1 zurückgegeben.

CreateCalculate() dient zur Erzeugung des Objektes des Berechnungspuffers.
Die Methode wird direkt im Klassenkörper implementiert und liefert das Ergebnis der Methode zur Erstellung eines neuen Puffers (besprochen im vorhergehenden Artikel), an den der Berechnungspufferstatus übergeben wird:

bool CreateCalculate(void) { return this.CreateBuffer(BUFFER_STATUS_NONE); }

Die Methode GetBufferByLabel() gibt den Zeiger auf das Pufferobjekt durch den grafischen Reihennamen zurück:

//+------------------------------------------------------------------+
//| Return the buffer by the graphical series name                   |
//+------------------------------------------------------------------+
CBuffer *CBuffersCollection::GetBufferByLabel(const string plot_label)
  {
   CArrayObj *list=CSelect::ByBufferProperty(this.GetList(),BUFFER_PROP_LABEL,plot_label,EQUAL);
   return(list!=NULL && list.Total()>0 ? list.At(list.Total()-1) : NULL);
  }
//+------------------------------------------------------------------+

Wir holen uns die Liste der Pufferobjekte, in der der "Grafische Reihenname" den an die Methode übergebenen Werten entspricht, und geben den Zeiger auf das letzte Objekt aus der Liste zurück. Wenn die Liste leer ist, geben wir NULL zurück. Wenn es mehrere Pufferobjekte mit dem gleichen grafischen Reihennamen gibt, gibt die Methode das allerletzte Pufferobjekt zurück, das mit einem solchen Namen erstellt wurde.

Die Methode GetBufferByTimeframe() gibt den Zeiger auf das Pufferobjekt um den eingestellten Zeitrahmen zurück:

//+------------------------------------------------------------------+
//| Return the buffer by timeframe                                   |
//+------------------------------------------------------------------+
CBuffer *CBuffersCollection::GetBufferByTimeframe(const ENUM_TIMEFRAMES timeframe)
  {
   CArrayObj *list=CSelect::ByBufferProperty(this.GetList(),BUFFER_PROP_TIMEFRAME,timeframe,EQUAL);
   return(list!=NULL && list.Total()>0 ? list.At(list.Total()-1) : NULL);
  }
//+------------------------------------------------------------------+

Holen wir uns die Liste der Pufferobjekte, in der der Zeitrahmen den an die Methode übergebenen Werten entspricht, und geben den Zeiger auf das letzte Objekt aus der Liste zurück. Wenn die Liste leer ist, geben wir NULL zurück. Wenn es mehrere Pufferobjekte mit dem gleichen Zeitrahmen gibt, gibt die Methode das allerletzte Pufferobjekt zurück, das mit der gleichen Chart-Periode erstellt wurde.

Die Methode GetBufferByListIndex() gibt den Pointer auf das Pufferobjekt gemäß dem Index in der Kollektionsliste zurück:

//+------------------------------------------------------------------+
//| Return the buffer by the collection list index                   |
//+------------------------------------------------------------------+
CBuffer *CBuffersCollection::GetBufferByListIndex(const int index_list)
  {
   return this.m_list.At(index_list);
  }
//+------------------------------------------------------------------+

Es wird einfach der Zeiger auf das allerletzte Objekt in der Kollektionsliste der Puffer zurück. Wenn die Liste leer ist, gibt NULL zurück.


Die Methode GetBufferCalculate() gibt den Zeiger auf das Pufferobjekt anhand seiner Seriennummer (in der Reihenfolge der Erstellung Berechnungspuffer) zurück:

//+------------------------------------------------------------------+
//|Return the calculated buffer by serial number                     |
//| (0 - the very first candle buffer, 1,2,N - subsequent ones)      |
//+------------------------------------------------------------------+
CBufferCalculate *CBuffersCollection::GetBufferCalculate(const int number)
  {
   CArrayObj *list=CSelect::ByBufferProperty(this.GetList(),BUFFER_PROP_TYPE,BUFFER_TYPE_CALCULATE,EQUAL);
   return(list!=NULL && list.Total()>0 ? list.At(number) : NULL);
  }
//+------------------------------------------------------------------+


Holen wir uns aus der Kollektionsliste der Puffer nur die Puffer vom Typ "Berechneter Puffer" und geben den Zeiger auf das Objekt über den Index zurück, der der Methode aus der erhaltenen Liste übergeben wurde. Wenn die resultierende sortierte Liste leer ist oder der Index des notwendigen Objekts, das der Methode übergeben wurde, die erhaltene Liste überschreitet, gibt die NULL zurück.

Die überladenen Methoden InitializePlots() werden verwendet, um alle gezeichneten Puffer in der Kollektion zu initialisieren:

//+------------------------------------------------------------------+
//| Initialize all drawn buffers by a specified empty value          |
//+------------------------------------------------------------------+
void CBuffersCollection::InitializePlots(const double value,const uchar color_index)
  {
   CArrayObj *list=CSelect::ByBufferProperty(this.GetList(),BUFFER_PROP_TYPE,BUFFER_TYPE_DATA,EQUAL);
   if(list==NULL || list.Total()==0)
      return;
   int total=list.Total();
   for(int i=0;i<total;i++)
     {
      CBuffer *buff=list.At(i);
      if(buff==NULL)
         continue;
      buff.InitializeAll(value,color_index);
     }
  }
//+------------------------------------------------------------------+


Die Methode initialisiert alle zu zeichnenden Puffer der Kollektion mit den in den Parametern übergebenen Puffer- und Farbindexwerten.

Nur zu zeichnende Puffer werden zuerst für die Liste ausgewählt. Dann holen wir das nächste Pufferobjekt der erhaltenen Liste in einer Schleife und initialisieren alle seine Arrays unter Verwendung der Pufferobjektklassenmethode InitializeAll(), wobei die Methodeneingaben an sie übergeben werden.

//+------------------------------------------------------------------+
//| Initialize all drawn buffers using                               |
//| the empty value set for the buffer object                        |
//+------------------------------------------------------------------+
void CBuffersCollection::InitializePlots(void)
  {
   CArrayObj *list=CSelect::ByBufferProperty(this.GetList(),BUFFER_PROP_TYPE,BUFFER_TYPE_DATA,EQUAL);
   if(list==NULL || list.Total()==0)
      return;
   int total=list.Total();
   for(int i=0;i<total;i++)
     {
      CBuffer *buff=list.At(i);
      if(buff==NULL)
         continue;
      buff.InitializeAll();
     }
  }  
//+------------------------------------------------------------------+


Die Methode initialisiert alle zu zeichnenden Puffer der Kollektion unter Verwendung des für jedes Pufferobjekt eingestellten Pufferwertes und eines Farbindexwertes gleich Null.

Nur zu zeichnende Puffer werden zuerst für die Liste ausgewählt. Dann holen wir das nächste Pufferobjekt anhand der erhaltenen Liste in einer Schleife und initialisieren alle seine Arrays mit der Pufferobjektklassenmethode InitializeAll() ohne Parameter.

Die überladenen Methoden InitializeCalculates() werden verwendet, um alle Berechnungspuffer in der Sammlung zu initialisieren:

//+------------------------------------------------------------------+
//| Initialize all calculated buffers by a specified empty value     |
//+------------------------------------------------------------------+
void CBuffersCollection::InitializeCalculates(const double value)
  {
   CArrayObj *list=CSelect::ByBufferProperty(this.GetList(),BUFFER_PROP_TYPE,BUFFER_TYPE_CALCULATE,EQUAL);
   if(list==NULL || list.Total()==0)
      return;
   int total=list.Total();
   for(int i=0;i<total;i++)
     {
      CBuffer *buff=list.At(i);
      if(buff==NULL)
         continue;
      buff.InitializeAll(value,0);
     }
  }
//+------------------------------------------------------------------+


Die Methode initialisiert alle Berechnungspufferwert.

Nur Berechnungspuffer werden zuerst für die Liste ausgewählt. Dann holen wir das nächste Pufferobjekt durch die erhaltene Liste in einer Schleife und initialisieren sein gesamtes Array mit Hilfe der Pufferobjektklassenmethode InitializeAll(), wobei die Eingabe der Methode an sie übergeben wird.

//+------------------------------------------------------------------+
//| Initialize all calculated buffers using                          |
//| the empty value set for the buffer object                        |
//+------------------------------------------------------------------+
void CBuffersCollection::InitializeCalculates(void)
  {
   CArrayObj *list=CSelect::ByBufferProperty(this.GetList(),BUFFER_PROP_TYPE,BUFFER_TYPE_CALCULATE,EQUAL);
   if(list==NULL || list.Total()==0)
      return;
   int total=list.Total();
   for(int i=0;i<total;i++)
     {
      CBuffer *buff=list.At(i);
      if(buff==NULL)
         continue;
      buff.InitializeAll();
     }
  }
//+------------------------------------------------------------------+


Die Methode initialisiert alle Berechnungspufferwert.

Nur Berechnungspuffer werden zuerst für die Liste ausgewählt. Dann holen wir das nächste Pufferobjekt der erhaltenen Liste in einer Schleife und initialisieren sein Array mit der Pufferobjektklassenmethode InitializeAll() ohne Parameter.

Die Methode SetColors() setzt Farbwerte aus dem Color Array für alle zu zeichnenden Kollektionspuffer.

//+------------------------------------------------------------------+
//| Set color values from the passed color array                     |
//| for all collection indicator buffers                             |
//+------------------------------------------------------------------+
void CBuffersCollection::SetColors(const color &array_colors[])
  {
   CArrayObj *list=CSelect::ByBufferProperty(this.GetList(),BUFFER_PROP_TYPE,BUFFER_TYPE_DATA,EQUAL);
   if(list==NULL || list.Total()==0)
      return;
   int total=list.Total();
   for(int i=0;i<total;i++)
     {
      CBuffer *buff=list.At(i);
      if(buff==NULL)
         continue;
      buff.SetColors(array_colors);
     }
  }
//+------------------------------------------------------------------+


Die Methode legt Farbwerte fest, die in den Array-Parametern für alle gezeichneten Sammelpuffer übergeben werden.

Nur zu zeichnende Puffer werden zuerst für die Liste ausgewählt. Dann holen wir das nächste Pufferobjekt der erhaltenen Liste in einer Schleife und spezifizieren die Farbeinstellung dafür mit der Pufferobjektklassenmethode SetColors(), indem wir das Array an die Methodeneingaben übergeben.

Die Methode Clear() löscht die Pufferdaten anhand ihres Index in der Kollektionsliste in dem angegebenen Balken der Zeitreihe:

//+------------------------------------------------------------------+
//| Clear buffer data by its index in the list                       |
//| in the specified timeseries bar                                  |
//+------------------------------------------------------------------+
void CBuffersCollection::Clear(const int buffer_list_index,const int series_index)
  {
   CBuffer *buff=this.GetBufferByListIndex(buffer_list_index);
   if(buff==NULL)
      return;
   buff.ClearData(series_index);
  }
//+------------------------------------------------------------------+


Holen wir uns zunächst den Zeiger auf das Pufferobjekt anhand seines Index in der Liste mit der oben beschriebenen Methode GetBufferByListIndex(). Dann löschen wir alle seine Arrays mit der Methode des erhaltenen Pufferobjekts ClearData().

Um die notwendigen Balkenindexdaten beim Zeichnen von Indikatorpufferlinien auf dem aktuellen Chart unter Verwendung von Daten aus anderen Zeiträumen berechnen zu können, müssen wir den Zeiger auf die Kollektionsliste aller verwendeten Zeitreihen, die sich in der Kollektionsklasse der Zeitreihen befinden, an die Kollektionsklasse
übergeben. Am einfachsten geht dies bei der Programminitialisierung, wenn alle Objekte der Bibliotheksklassen bereits erstellt sind.
Die Klassenmethode OnInit() weist den übergebenen Zeiger auf die Klasse der Zeitreihensammlung der m_Zeitreihe Variablen zu:

void OnInit(CTimeSeriesCollection *timeseries) { this.m_timeseries=timeseries; }

Im weiteren Verlauf werde ich die Methode in den Bibliotheksinitialisierungsfunktionen innerhalb von Programmen aufrufen. Dafür gibt es bereits alle notwendigen Funktionen. Weiter unten werde ich einfach den Aufruf der Methode in der erforderlichen Klassenmethode der CEngine hinzufügen.

Die Klasse enthält bereits die Methoden zum Setzen der Werte für Indikatorpuffer nach Puffertyp (Pfeile, Linie usw.) sowie zum Setzen des Farbindex für diese Puffer und zum Löschen aller Puffer-Arrays. Insgesamt habe ich 28 Methoden hinzugefügt. Es macht keinen Sinn, jede einzelne hier zu beschreiben — alle Methoden werden in den unten angehängten Dateien vorgestellt. Sie sind von der Logik her identisch. Der einzige Unterschied ist die Anzahl der Arrays, die Puffer verschiedener Typen haben. Daher werden wir hier nur die Pfeilpuffer-Methoden besprechen.

Die Methode, die den Wert durch den Pfeilpuffer-Zeitreihenindex setzt:

//+------------------------------------------------------------------+
//| Set the value to the arrow buffer by the timeseries index        |
//+------------------------------------------------------------------+
void CBuffersCollection::SetBufferArrowValue(const int number,const int series_index,const double value,const uchar color_index,bool as_current=false)
  {
//--- Get the arrow buffer object
   CBufferArrow *buff=this.GetBufferArrow(number);
   if(buff==NULL)
      return;
//--- If the buffer usage flag is set only as for the current timeframe,
//--- write the passed value to the current buffer bar and exit (the color is not used)
   if(as_current)
     {
      buff.SetBufferValue(0,series_index,value);
      return;
     }
//--- Get data on the necessary timeseries and bars, and calculate the amount of bars of the current timeframe included into one bar of the buffer object chart period
   int index_bar_period=series_index;
   int num_bars=this.GetBarsData(buff,series_index,index_bar_period);
   if(num_bars==WRONG_VALUE)
      return;
//--- Calculate the index of the next bar for the current chart in the loop by the number of bars and
//--- set the value and color passed to the method by the calculated index
   for(int i=0;i<num_bars;i++)
     {
      int index=index_bar_period-i;
      if(index<0)
         break;
      buff.SetBufferValue(0,index,value);
      buff.SetBufferColorIndex(index,color_index);
     }
  }  
//+------------------------------------------------------------------+


Die Logik der Methode wird in den Code-Kommentaren beschrieben. Lassen Sie mich jedoch einige Nuancen verdeutlichen.

Die Methode erhält die Nummer des Pfeilpufferobjekts. Die Nummer bedeutet die Seriennummer, die aus allen erstellten Pfeilpuffern besteht. Der allererste Pfeilpuffer, den wir im benutzerdefinierten Programm erstellt haben, hat die Nummer 0, der zweite ist 1, der dritte ist 2 usw. Verwechseln Sie die Nummern der erstellten Pufferobjekte nicht mit den Pufferobjektindizes in der Kollektionsliste. Indizes von neu hinzugefügten Puffern gehen immer in aufsteigender Reihenfolge und sind nicht von einem Pufferobjekttyp abhängig. Die Anzahl der Pufferobjekte hängt vom Puffertyp ab. Ich habe das Konzept ganz am Anfang des Artikels ausführlich beschrieben.

Die Methode erhält den Balkenindex der Zeitreihen und akzeptiert den Wert, der auch durch den nächsten Parameter übergeben wird, Index der Farbe, mit der die Linie des Indikatorpuffers durch den angegebenen Zeitreihenindex gezeichnet wird, sowie den zusätzlichen Parameter "wie für die aktuelle Zeitreihe".

Die Methoden sind so angeordnet, dass, wenn das Pufferobjekt den Eigenschaftswert "timeframe" (Zeitrahmen) hat, der sich von der aktuellen Chart-Periode unterscheidet, die Indikatorlinie auf dem aktuellen Chart unter Berücksichtigung der Zeitrahmendaten des Pufferobjekts gezeichnet wird, unabhängig vom an die Methode übergebenen Zeitreihenindex. D.h. die Methode zeigt Daten aus anderen Chart-Perioden auf der aktuellen korrekt an, vorausgesetzt, dass das Flag as_current den Wert false (standardmäßig) hat. Wenn das Flag auf true gesetzt ist, arbeitet die Methode nur mit den aktuellen Zeitrahmendaten, unabhängig davon, ob das Pufferobjekt in seinen Eigenschaften eine andere angegebene Chart-Periode hat.

Die Methode, bei der der Farbindex für das Pfeilpufferobjekt anhand seiner Seriennummer in das Farbpuffer-Array: gesetzt wird.

//+------------------------------------------------------------------+
//| Set the color index to the color buffer                          |
//| of the arrow buffer by the timeseries index                      |
//+------------------------------------------------------------------+
void CBuffersCollection::SetBufferArrowColorIndex(const int number,const int series_index,const uchar color_index)
  {
   CBufferArrow *buff=this.GetBufferArrow(number);
   if(buff==NULL)
      return;
   buff.SetBufferColorIndex(series_index,color_index);
  }
//+------------------------------------------------------------------+


Hier ist alles einfach: Holen Sie das Pfeilpufferobjekt über seinen Index und setzen Sie den angegebenen Farbindex auf den übergebenen Zeitreihenindex.

Die Methode zum Löschen der Daten durch den Pfeilpuffer Zeitreihenindex durch seinen Index:

//+------------------------------------------------------------------+
//| Clear the arrow buffer data by the timeseries index              |
//+------------------------------------------------------------------+
void CBuffersCollection::ClearBufferArrow(const int number,const int series_index)
  {
   CBufferArrow *buff=this.GetBufferArrow(number);
   if(buff==NULL)
      return;
   buff.SetBufferValue(0,series_index,buff.EmptyValue());
  }
//+------------------------------------------------------------------+


Hier erhalten wir das Pfeilpufferobjekt durch seinen Index und setzen den für das Pufferobjekt angegebenen "leeren" Wert auf den übergebenen Zeitreihenindex.

Alle übrigen Methoden zur Arbeit mit anderen Pufferobjekttypen unterscheiden sich bis auf die Anzahl der Arrays und den Pufferobjekttyp nicht grundlegend von den oben besprochenen. Sie können sie selbst untersuchen.

Damit sind die Verbesserungen der Pufferobjektsammelklasse abgeschlossen.
Die vollständige Liste mit allen Änderungen finden Sie in den unten angehängten Dateien.

Ergänzen und Verbessern wir noch die Hauptobjektklasse der Bibliothek CEngine in \MQL5\Include\DoEasy\Engine.mqh . Ich werde auf die gleiche Weise vorgehen wie bei der Beschreibung der Verbesserungen der Kollektionsklasse der Puffer: Zuerst werden wir uns die Ergänzungen und Änderungen im Klassenkörper ansehen, und dann werde ich jede der Methoden analysieren.
Es macht keinen Sinn, die vollständige Liste hier anzuzeigen, da sie recht umfangreich ist. Stattdessen werde ich Auszüge mit implementierten Änderungen und Ergänzungen vorstellen:

//+------------------------------------------------------------------+
//| Library basis class                                              |
//+------------------------------------------------------------------+
class CEngine
  {
private:
//--- The code has been removed for the sake of space
//--- ...
public:
//--- The code has been removed for the sake of space
//--- ...
//--- Return the bar type of the specified timeframe's symbol by (1) index and (2) time
   ENUM_BAR_BODY_TYPE   SeriesBarType(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index);
   ENUM_BAR_BODY_TYPE   SeriesBarType(const string symbol,const ENUM_TIMEFRAMES timeframe,const datetime time);

//--- Copy the specified double property of the specified timeseries of the specified symbol to the array
//--- Regardless of the array indexing direction, copying is performed the same way as copying to a timeseries array
   bool                 SeriesCopyToBufferAsSeries(const string symbol,const ENUM_TIMEFRAMES timeframe,const ENUM_BAR_PROP_DOUBLE property,
                                                   double &array[],const double empty=EMPTY_VALUE)
                          { return this.m_time_series.CopyToBufferAsSeries(symbol,timeframe,property,array,empty);}

//--- Return (1) the buffer collection and (2) the buffer list from the collection 
   CBuffersCollection  *GetBuffersCollection(void)                                     { return &this.m_buffers;                             }
   CArrayObj           *GetListBuffers(void)                                           { return this.m_buffers.GetList();                    }
//--- Return the buffer by (1) the graphical series name, (2) timeframe, (3) Plot index, (4) collection list and (5) the last one in the list
   CBuffer             *GetBufferByLabel(const string plot_label)                      { return this.m_buffers.GetBufferByLabel(plot_label); }
   CBuffer             *GetBufferByTimeframe(const ENUM_TIMEFRAMES timeframe)          { return this.m_buffers.GetBufferByTimeframe(timeframe);}
   CBuffer             *GetBufferByPlot(const int plot_index)                          { return this.m_buffers.GetBufferByPlot(plot_index);  }
   CBuffer             *GetBufferByListIndex(const int index_list)                     { return this.m_buffers.GetBufferByListIndex(index_list);}
   CBuffer             *GetLastBuffer(void);
//--- Return buffers by drawing style by a serial number
//--- (0 - the very first created buffer with the ХХХ drawing style, 1,2,N - subsequent ones)
   CBufferArrow        *GetBufferArrow(const int number)                               { return this.m_buffers.GetBufferArrow(number);       }
   CBufferLine         *GetBufferLine(const int number)                                { return this.m_buffers.GetBufferLine(number);        }
   CBufferSection      *GetBufferSection(const int number)                             { return this.m_buffers.GetBufferSection(number);     }
   CBufferHistogram    *GetBufferHistogram(const int number)                           { return this.m_buffers.GetBufferHistogram(number);   }
   CBufferHistogram2   *GetBufferHistogram2(const int number)                          { return this.m_buffers.GetBufferHistogram2(number);  }
   CBufferZigZag       *GetBufferZigZag(const int number)                              { return this.m_buffers.GetBufferZigZag(number);      }
   CBufferFilling      *GetBufferFilling(const int number)                             { return this.m_buffers.GetBufferFilling(number);     }
   CBufferBars         *GetBufferBars(const int number)                                { return this.m_buffers.GetBufferBars(number);        }
   CBufferCandles      *GetBufferCandles(const int number)                             { return this.m_buffers.GetBufferCandles(number);     }
   CBufferCalculate    *GetBufferCalculate(const int number)                           { return this.m_buffers.GetBufferCalculate(number);   }
   
//--- Return the number of (1) drawn buffers and (2) all indicator arrays
   int                  BuffersPropertyPlotsTotal(void)                                { return this.m_buffers.PropertyPlotsTotal();         }
   int                  BuffersPropertyBuffersTotal(void)                              { return this.m_buffers.PropertyBuffersTotal();       }

//--- Create the new buffer (1) "Drawing with arrows", (2) "Line", (3) "Sections", (4) "Histogram from the zero line", 
//--- (5) "Histogram on two indicator buffers", (6) "Zigzag", (7) "Color filling between two levels",
//--- (8) "Display as bars", (9) "Display as candles", calculated buffer
   bool                 BufferCreateArrow(void)                                        { return this.m_buffers.CreateArrow();                }
   bool                 BufferCreateLine(void)                                         { return this.m_buffers.CreateLine();                 }
   bool                 BufferCreateSection(void)                                      { return this.m_buffers.CreateSection();              }
   bool                 BufferCreateHistogram(void)                                    { return this.m_buffers.CreateHistogram();            }
   bool                 BufferCreateHistogram2(void)                                   { return this.m_buffers.CreateHistogram2();           }
   bool                 BufferCreateZigZag(void)                                       { return this.m_buffers.CreateZigZag();               }
   bool                 BufferCreateFilling(void)                                      { return this.m_buffers.CreateFilling();              }
   bool                 BufferCreateBars(void)                                         { return this.m_buffers.CreateBars();                 }
   bool                 BufferCreateCandles(void)                                      { return this.m_buffers.CreateCandles();              }
   bool                 BufferCreateCalculate(void)                                    { return this.m_buffers.CreateCalculate();            }
   
//--- Initialize all drawn buffers by a (1) specified value, (2) empty value set for the buffer object
   void                 BuffersInitPlots(const double value,const uchar color_index)   { this.m_buffers.InitializePlots(value,color_index);  }
   void                 BuffersInitPlots(void)                                         { this.m_buffers.InitializePlots();                   }
//--- Initialize all calculated buffers by a (1) specified value, (2) empty value set for the buffer object
   void                 BuffersInitCalculates(const double value)                      { this.m_buffers.InitializeCalculates(value);         }
   void                 BuffersInitCalculates(void)                                    { this.m_buffers.InitializeCalculates();              }

//--- Return buffer data by its serial number of (1) arrows, (2) line, (3) sections and (4) histogram from zero
//--- (0 - the very first created buffer with the ХХХ drawing style, 1,2,N - subsequent ones)
   double               BufferDataArrow(const int number,const int series_index);
   double               BufferDataLine(const int number,const int series_index);
   double               BufferDataSection(const int number,const int series_index);
   double               BufferDataHistogram(const int number,const int series_index);
//--- Return buffer data by its serial number of (1) the zero and (2) the first histogram buffer on two buffers
//--- (0 - the very first created buffer with the ХХХ drawing style, 1,2,N - subsequent ones)
   double               BufferDataHistogram20(const int number,const int series_index);
   double               BufferDataHistogram21(const int number,const int series_index);
//--- Return buffer data by its serial number of (1) the zero and (2) the first zigzag buffer
//--- (0 - the very first created buffer with the ХХХ drawing style, 1,2,N - subsequent ones)
   double               BufferDataZigZag0(const int number,const int series_index);
   double               BufferDataZigZag1(const int number,const int series_index);
//--- Return buffer data by its serial number of (1) the zero and (2) the first filling buffer
//--- (0 - the very first created buffer with the ХХХ drawing style, 1,2,N - subsequent ones)
   double               BufferDataFilling0(const int number,const int series_index);
   double               BufferDataFilling1(const int number,const int series_index);
//--- Return buffer data by its serial number of (1) Open, (2) High, (3) Low and (4) Close bar buffers
//--- (0 - the very first created buffer with the ХХХ drawing style, 1,2,N - subsequent ones)
   double               BufferDataBarsOpen(const int number,const int series_index);
   double               BufferDataBarsHigh(const int number,const int series_index);
   double               BufferDataBarsLow(const int number,const int series_index);
   double               BufferDataBarsClose(const int number,const int series_index);
//--- Return buffer data by its serial number of (1) Open, (2) High, (3) Low and (4) Close candle buffers
//--- (0 - the very first created buffer with the ХХХ drawing style, 1,2,N - subsequent ones)
   double               BufferDataCandlesOpen(const int number,const int series_index);
   double               BufferDataCandlesHigh(const int number,const int series_index);
   double               BufferDataCandlesLow(const int number,const int series_index);
   double               BufferDataCandlesClose(const int number,const int series_index);

//--- Set buffer data by its serial number of (1) arrows, (2) line, (3) sections, (4) histogram from zero and the (5) calculated buffer
//--- (0 - the very first created buffer with the ХХХ drawing style, 1,2,N - subsequent ones)
   void                 BufferSetDataArrow(const int number,const int series_index,const double value,const uchar color_index,bool as_current=false);
   void                 BufferSetDataLine(const int number,const int series_index,const double value,const uchar color_index,bool as_current=false);
   void                 BufferSetDataSection(const int number,const int series_index,const double value,const uchar color_index,bool as_current=false);
   void                 BufferSetDataHistogram(const int number,const int series_index,const double value,const uchar color_index,bool as_current=false);
   void                 BufferSetDataCalculate(const int number,const int series_index,const double value);
//--- Set data of the (1) zero, (2) first and (3) all histogram buffers on two buffers by a serial number of a created buffer
//--- (0 - the very first created buffer with the HISTOGRAM2 drawing style, 1,2,N - subsequent ones)
   void                 BufferSetDataHistogram20(const int number,const int series_index,const double value);
   void                 BufferSetDataHistogram21(const int number,const int series_index,const double value);
   void                 BufferSetDataHistogram2(const int number,const int series_index,const double value0,const double value1,const uchar color_index,bool as_current=false);
//--- Set data of the (1) zero, (2) first and (3) all zigzag buffers by a serial number of a created buffer
//--- (0 - the very first created buffer with the ZIGZAG drawing style, 1,2,N - subsequent ones)
   void                 BufferSetDataZigZag0(const int number,const int series_index,const double value);
   void                 BufferSetDataZigZag1(const int number,const int series_index,const double value);
   void                 BufferSetDataZigZag(const int number,const int series_index,const double value0,const double value1,const uchar color_index,bool as_current=false);
//--- Set data of the (1) zero, (2) first and (3) all filling buffers by a serial number of a created buffer
//--- (0 - the very first created buffer with the FILLING drawing style, 1,2,N - subsequent ones)
   void                 BufferSetDataFilling0(const int number,const int series_index,const double value);
   void                 BufferSetDataFilling1(const int number,const int series_index,const double value);
   void                 BufferSetDataFilling(const int number,const int series_index,const double value0,const double value1,bool as_current=false);
//--- Set data of the (1) Open, (2) High, (3) Low, (4) Close and (5) all bar buffers by a serial number of a created buffer
//--- (0 - the very first created buffer with the BARS drawing style, 1,2,N - subsequent ones)
   void                 BufferSetDataBarsOpen(const int number,const int series_index,const double value);
   void                 BufferSetDataBarsHigh(const int number,const int series_index,const double value);
   void                 BufferSetDataBarsLow(const int number,const int series_index,const double value);
   void                 BufferSetDataBarsClose(const int number,const int series_index,const double value);
   void                 BufferSetDataBars(const int number,const int series_index,const double open,const double high,const double low,const double close,const uchar color_index,bool as_current=false);
//--- Set data of the (1) Open, (2) High, (3) Low, (4) Close and (5) all candle buffers by a serial number of a created buffer
//--- (0 - the very first created buffer with the CANDLES drawing style, 1,2,N - subsequent ones)
   void                 BufferSetDataCandlesOpen(const int number,const int series_index,const double value);
   void                 BufferSetDataCandlesHigh(const int number,const int series_index,const double value);
   void                 BufferSetDataCandlesLow(const int number,const int series_index,const double value);
   void                 BufferSetDataCandlesClose(const int number,const int series_index,const double value);
   void                 BufferSetDataCandles(const int number,const int series_index,const double open,const double high,const double low,const double close,const uchar color_index,bool as_current=false);
   
//--- Return buffer color by its serial number of (1) arrows, (2) line, (3) sections, (4) histogram from zero
//--- (5) histogram on two buffers, (6) zigzag, (7) filling, (8) bars and (9) candles
//--- (0 - the very first created buffer with the ХХХ drawing style, 1,2,N - subsequent ones)
   color                BufferArrowColor(const int number,const int series_index);
   color                BufferLineColor(const int number,const int series_index);
   color                BufferSectionColor(const int number,const int series_index);
   color                BufferHistogramColor(const int number,const int series_index);
   color                BufferHistogram2Color(const int number,const int series_index);
   color                BufferZigZagColor(const int number,const int series_index);
   color                BufferFillingColor(const int number,const int series_index);
   color                BufferBarsColor(const int number,const int series_index);
   color                BufferCandlesColor(const int number,const int series_index);
   
//--- Return buffer color index by its serial number of (1) arrows, (2) line, (3) sections, (4) histogram from zero
//--- (5) histogram on two buffers, (6) zigzag, (7) filling, (8) bars and (9) candles
//--- (0 - the very first created buffer with the ХХХ drawing style, 1,2,N - subsequent ones)
   int                  BufferArrowColorIndex(const int number,const int series_index);
   int                  BufferLineColorIndex(const int number,const int series_index);
   int                  BufferSectionColorIndex(const int number,const int series_index);
   int                  BufferHistogramColorIndex(const int number,const int series_index);
   int                  BufferHistogram2ColorIndex(const int number,const int series_index);
   int                  BufferZigZagColorIndex(const int number,const int series_index);
   int                  BufferFillingColorIndex(const int number,const int series_index);
   int                  BufferBarsColorIndex(const int number,const int series_index);
   int                  BufferCandlesColorIndex(const int number,const int series_index);

//--- Set color values from the passed color array for all indicator buffers within the collection
   void                 BuffersSetColors(const color &array_colors[])                     { this.m_buffers.SetColors(array_colors);                   } 
//--- Set the color index to the color buffer by its serial number of (1) arrows, (2) line, (3) sections, (4) histogram from zero
//--- (5) histogram on two buffers, (6) zigzag, (7) filling, (8) bars and (9) candles
//--- (0 - the very first created buffer with the ХХХ drawing style, 1,2,N - subsequent ones)
   void                 BufferArrowSetColorIndex(const int number,const int series_index,const uchar color_index)
                          { this.m_buffers.SetBufferArrowColorIndex(number,series_index,color_index);       }
   void                 BufferLineSetColorIndex(const int number,const int series_index,const uchar color_index)
                          { this.m_buffers.SetBufferLineColorIndex(number,series_index,color_index);        }
   void                 BufferSectionSetColorIndex(const int number,const int series_index,const uchar color_index)
                          { this.m_buffers.SetBufferSectionColorIndex(number,series_index,color_index);     }
   void                 BufferHistogramSetColorIndex(const int number,const int series_index,const uchar color_index)
                          { this.m_buffers.SetBufferHistogramColorIndex(number,series_index,color_index);   }
   void                 BufferHistogram2SetColorIndex(const int number,const int series_index,const uchar color_index)
                          { this.m_buffers.SetBufferHistogram2ColorIndex(number,series_index,color_index);  }
   void                 BufferZigZagSetColorIndex(const int number,const int series_index,const uchar color_index)
                          { this.m_buffers.SetBufferZigZagColorIndex(number,series_index,color_index);      }
   void                 BufferFillingSetColorIndex(const int number,const int series_index,const uchar color_index)
                          { this.m_buffers.SetBufferFillingColorIndex(number,series_index,color_index);     }
   void                 BufferBarsSetColorIndex(const int number,const int series_index,const uchar color_index)
                          { this.m_buffers.SetBufferBarsColorIndex(number,series_index,color_index);        }
   void                 BufferCandlesSetColorIndex(const int number,const int series_index,const uchar color_index)
                          { this.m_buffers.SetBufferCandlesColorIndex(number,series_index,color_index);     }

//--- Clear buffer data by its index in the list in the specified timeseries bar
   void                 BufferClear(const int buffer_list_index,const int series_index)   { this.m_buffers.Clear(buffer_list_index,series_index);     }
//--- Clear data by the timeseries index for the (1) arrow, (2) line, (3) section, (4) zero line histogram,
//--- (5) histogram on two buffers, (6) zigzag, (7) filling, (8) bars and (9) candles
   void                 BufferArrowClear(const int number,const int series_index)         { this.m_buffers.ClearBufferArrow(number,series_index);     }
   void                 BufferLineClear(const int number,const int series_index)          { this.m_buffers.ClearBufferLine(number,series_index);      }
   void                 BufferSectionClear(const int number,const int series_index)       { this.m_buffers.ClearBufferSection(number,series_index);   }
   void                 BufferHistogramClear(const int number,const int series_index)     { this.m_buffers.ClearBufferHistogram(number,series_index); }
   void                 BufferHistogram2Clear(const int number,const int series_index)    { this.m_buffers.ClearBufferHistogram2(number,series_index);}
   void                 BufferZigZagClear(const int number,const int series_index)        { this.m_buffers.ClearBufferZigZag(number,series_index);    }
   void                 BufferFillingClear(const int number,const int series_index)       { this.m_buffers.ClearBufferFilling(number,series_index);   }
   void                 BufferBarsClear(const int number,const int series_index)          { this.m_buffers.ClearBufferBars(number,series_index);      }
   void                 BufferCandlesClear(const int number,const int series_index)       { this.m_buffers.ClearBufferCandles(number,series_index);   }

//--- Display short description of all indicator buffers of the buffer collection
   void                 BuffersPrintShort(void);
   
//--- Set the following for the trading classes:
//--- (1) correct filling policy, (2) filling policy,
//--- (3) correct order expiration type, (4) order expiration type,
//--- (5) magic number, (6) comment, (7) slippage, (8) volume, (9) order expiration date,
//--- (10) the flag of asynchronous sending of a trading request, (11) logging level, (12) number of trading attempts
   void                 TradingSetCorrectTypeFilling(const ENUM_ORDER_TYPE_FILLING type=ORDER_FILLING_FOK,const string symbol_name=NULL);
   void                 TradingSetTypeFilling(const ENUM_ORDER_TYPE_FILLING type=ORDER_FILLING_FOK,const string symbol_name=NULL);
   void                 TradingSetCorrectTypeExpiration(const ENUM_ORDER_TYPE_TIME type=ORDER_TIME_GTC,const string symbol_name=NULL);
   void                 TradingSetTypeExpiration(const ENUM_ORDER_TYPE_TIME type=ORDER_TIME_GTC,const string symbol_name=NULL);
   void                 TradingSetMagic(const uint magic,const string symbol_name=NULL);
   void                 TradingSetComment(const string comment,const string symbol_name=NULL);
   void                 TradingSetDeviation(const ulong deviation,const string symbol_name=NULL);
   void                 TradingSetVolume(const double volume=0,const string symbol_name=NULL);
   void                 TradingSetExpiration(const datetime expiration=0,const string symbol_name=NULL);
   void                 TradingSetAsyncMode(const bool async_mode=false,const string symbol_name=NULL);
   void                 TradingSetLogLevel(const ENUM_LOG_LEVEL log_level=LOG_LEVEL_ERROR_MSG,const string symbol_name=NULL);
   void                 TradingSetTotalTry(const uchar attempts)                       { this.m_trading.SetTotalTry(attempts);                     }
   
//--- Return the logging level of a trading class symbol trading object
   ENUM_LOG_LEVEL       TradingGetLogLevel(const string symbol_name)                   { return this.m_trading.GetTradeObjLogLevel(symbol_name);   }
   
//--- Set standard sounds (symbol==NULL) for a symbol trading object, (symbol!=NULL) for trading objects of all symbols
   void                 SetSoundsStandart(const string symbol=NULL)
                          {
                           this.m_trading.SetSoundsStandart(symbol);
                          }
//--- Set the flag of using sounds
   void                 SetUseSounds(const bool flag) { this.m_trading.SetUseSounds(flag);   }
//--- Set a sound for a specified order/position type and symbol. 'mode' specifies an event a sound is set for
//--- (symbol=NULL) for trading objects of all symbols, (symbol!=NULL) for a trading object of a specified symbol
   void                 SetSound(const ENUM_MODE_SET_SOUND mode,const ENUM_ORDER_TYPE action,const string sound,const string symbol=NULL)
                          {
                           this.m_trading.SetSound(mode,action,sound,symbol);
                          }
//--- Play a sound by its description
   bool                 PlaySoundByDescription(const string sound_description);

//--- Pass the pointers to all the necessary collections to the trading class and the indicator buffer collection class
   void                 CollectionOnInit(void)
                          {
                           this.m_trading.OnInit(this.GetAccountCurrent(),m_symbols.GetObject(),m_market.GetObject(),m_history.GetObject(),m_events.GetObject());
                           this.m_buffers.OnInit(this.m_time_series.GetObject());
                          }


Die überladene Methode SeriesBarType() gibt den Typ des angegebenen Balkens auf dem angegebenen Symbol und der angegebenen Chart-Periode zurück durch den angegebenen Index der entsprechenden Zeitreihe:

//+------------------------------------------------------------------+
//| Return the bar type of the specified symbol                      |
//| of the specified timeframe by index                              |
//+------------------------------------------------------------------+
ENUM_BAR_BODY_TYPE CEngine::SeriesBarType(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index)
  {
   CBar *bar=this.m_time_series.GetBar(symbol,timeframe,index);
   return(bar!=NULL ? bar.TypeBody() : (ENUM_BAR_BODY_TYPE)WRONG_VALUE);
  }  
//+------------------------------------------------------------------+


Wir rufen das erforderliche Balkenobjekt aus der Zeitreihensammlung über den angegebenen Zeitreihenindex ab und geben seinen Typ zurück (bullish/bearish/zero/mit einem Nullkörper). Wenn es nicht gelungen ist, einen Balken zu erhalten, wird -1 zurückgegeben, andernfalls

nach der Zeit:

//+------------------------------------------------------------------+
//| Return the bar type of the specified symbol                      |
//| of the specified timeframe by time                               |
//+------------------------------------------------------------------+
ENUM_BAR_BODY_TYPE CEngine::SeriesBarType(const string symbol,const ENUM_TIMEFRAMES timeframe,const datetime time)
  {
   CBar *bar=this.m_time_series.GetBar(symbol,timeframe,time);
   return(bar!=NULL ? bar.TypeBody() : (ENUM_BAR_BODY_TYPE)WRONG_VALUE);
  }  
//+------------------------------------------------------------------+


Holen Sie das erforderliche Balkenobjekt aus der Zeitreihensammlung bis zum angegebenen Zeitpunkt und geben Sie seinen Typ zurück (auf-, abwärts oder Null ohne Körper).
Wenn es nicht gelungen ist, einen Balken zu erhalten, wird -1 zurückgegeben, andernfalls

Es ist praktisch, die Methoden in Indikatoren zu verwenden, um eine Farbe des erforderlichen Balkens (oder der erforderlichen Balken, wenn Daten aus anderen Zeitreihen gezeichnet werden) zu bestimmen.

Die Methode GetBufferByLabel() gibt den Zeiger auf das Pufferobjekt mit dem Namen seiner grafischen Reihe zurück:

CBuffer *GetBufferByLabel(const string plot_label) { return this.m_buffers.GetBufferByLabel(plot_label); }

Es wird das Ergebnis der oben besprochenen Methode der gleichnamigen Puffersammelklasse zurückgegeben.

Die Methode GetBufferByTimeframe() gibt den Zeiger auf das Pufferobjekt durch seine "timeframe"-Eigenschaft zurück:

CBuffer *GetBufferByTimeframe(const ENUM_TIMEFRAMES timeframe) { return this.m_buffers.GetBufferByTimeframe(timeframe);}

Es wird das Ergebnis der oben besprochenen Methode der gleichnamigen Puffersammelklasse zurückgegeben.

Die Methode GetBufferByListIndex() gibt den Zeiger auf das Pufferobjekt anhand seines Indexes in der Kollektionsliste der Puffer der Kollektionsklasse der Puffer zurück:

CBuffer *GetBufferByListIndex(const int index_list) { return this.m_buffers.GetBufferByListIndex(index_list);}

Es wird das Ergebnis der oben besprochenen Methode der gleichnamigen Puffersammelklasse zurückgegeben.

Die Methode GetLastBuffer() gibt den Zeiger auf das zuletzt erstellte Pufferobjekt zurück:

//+------------------------------------------------------------------+
//| Return the last indicator buffer                                 |
//| in the indicator buffer collection list                          |
//+------------------------------------------------------------------+
CBuffer *CEngine::GetLastBuffer(void)
  {
   CArrayObj *list=this.GetListBuffers();
   if(list==NULL)
      return NULL;
   return list.At(list.Total()-1);
  }
//+------------------------------------------------------------------+


Wir holen uns den Zeiger auf die Kollektionsliste der Puffer und geben das letzte Pufferobjekt aus der Liste zurück.

Die Methode GetBufferCalculate() gibt das Ergebnis der oben besprochenen Methode der gleichnamigen Kollektionsklasse der Puffer zurück:

CBufferCalculate *GetBufferCalculate(const int number) { return this.m_buffers.GetBufferCalculate(number); }

Die Methoden BuffersPropertyPlotsTotal() und BuffersPropertyBuffersTotal() wurden von den Methoden BufferPlotsTotal() und BuffersTotal() entsprechend umbenannt. Sie geben die Ergebnisse der Methoden PropertyPlotsTotal() und PropertyBuffersTotal() der Puffersammelklasse zurück, die ich oben betrachtet habe:

int BuffersPropertyPlotsTotal(void)   { return this.m_buffers.PropertyPlotsTotal();   }
int BuffersPropertyBuffersTotal(void) { return this.m_buffers.PropertyBuffersTotal(); }


Die Methode BufferCreateCalculate() gibt das Ergebnis der Methode CreateCalculate() der Kollektionsklasse der Puffer zurück, die ich oben besprochen habe:

bool BufferCreateCalculate(void) { return this.m_buffers.CreateCalculate(); }

Gegenwärtig gibt es eine Einschränkung bei der Verwendung der Methode zum Erstellen eines Berechnungspuffers: Alle Berechnungspuffer sollten erstellt werden, wenn alle erforderlichen gezeichneten Puffer bereits vorhanden sind. Wenn wir einen Berechnungspuffer zwischen mehreren gezeichneten Puffern erstellen, verstößt dies gegen die Reihenfolge der Einstellung von Arrays für Indikatorpuffer. Infolgedessen werden alle gezeichneten Indikatorpuffer, die nach dem Berechnungspuffer erstellt wurden, nicht angezeigt. Ich habe den Grund für dieses Verhalten noch nicht gefunden, aber ich habe die feste Absicht, dies später zu korrigieren.

Die überladene Methode BuffersInitPlots() initialisiert alle gezeichneten Puffer mit der Methode InitializePlots() der Puffersammlungsklasse, die ich oben besprochen habe:

void BuffersInitPlots(const double value,const uchar color_index) { this.m_buffers.InitializePlots(value,color_index); }
void BuffersInitPlots(void)                                       { this.m_buffers.InitializePlots();                  }


Die überladene Methode BuffersInitCalculates() initialisiert alle Berechnungspuffer mit der Methode InitializeCalculates() der Puffersammlungsklasse, die ich oben betrachtet habe:

void BuffersInitCalculates(const double value) { this.m_buffers.InitializeCalculates(value); }
void BuffersInitCalculates(void)               { this.m_buffers.InitializeCalculates();      }


Die Methode BufferSetDataCalculate() setzt Daten für den Berechnungspuffer unter Verwendung der Methode SetBufferCalculateValue() der Kollektionsklasse der Puffer, die ich oben betrachtet habe:

//+------------------------------------------------------------------+
//| Set the calculated buffer data by its serial number              |
//| (0 - the very first buffer, 1,2,N - subsequent ones)             |
//+------------------------------------------------------------------+
void CEngine::BufferSetDataCalculate(const int number,const int series_index,const double value)
  {
   this.m_buffers.SetBufferCalculateValue(number,series_index,value);
  }
//+------------------------------------------------------------------+


Die Methoden BufferSetDataArrow(), BufferSetDataLine(), BufferSetDataSection(), BufferSetDataHistogram(), BufferSetDataHistogram2(), Die Methoden BufferSetDataZigZag(), BufferSetDataFilling(), BufferSetDataBars() und BufferSetDataCandles() wurden geändert. Jetzt setzen sie die Daten der entsprechenden Puffer unter Verwendung der oben betrachteten Methoden der Kollektionsklasse der Puffer:

//+------------------------------------------------------------------+
//| Set arrow buffer data by its serial number                       |
//| (0 - the very first arrow buffer, 1,2,N - subsequent ones)       |
//+------------------------------------------------------------------+
void CEngine::BufferSetDataArrow(const int number,const int series_index,const double value,const uchar color_index,bool as_current=false)
  {
   this.m_buffers.SetBufferArrowValue(number,series_index,value,color_index,as_current);
  }
//+------------------------------------------------------------------+
//| Set line buffer data by its serial number                        |
//| (0 - the very first line buffer, 1,2,N - subsequent ones)        |
//+------------------------------------------------------------------+
void CEngine::BufferSetDataLine(const int number,const int series_index,const double value,const uchar color_index,bool as_current=false)
  {
   this.m_buffers.SetBufferLineValue(number,series_index,value,color_index,as_current);
  }
//+------------------------------------------------------------------+
//| Set section buffer data by its serial number                     |
//| (0 - the very first sections buffer, 1,2,N - subsequent ones)    |
//+------------------------------------------------------------------+
void CEngine::BufferSetDataSection(const int number,const int series_index,const double value,const uchar color_index,bool as_current=false)
  {
   this.m_buffers.SetBufferSectionValue(number,series_index,value,color_index,as_current);
  }
//+------------------------------------------------------------------+
//| Set histogram buffer data from zero                              |
//| by its serial number                                             |
//| (0 - the very first buffer, 1,2,N - subsequent ones)             |
//+------------------------------------------------------------------+
void CEngine::BufferSetDataHistogram(const int number,const int series_index,const double value,const uchar color_index,bool as_current=false)
  {
   this.m_buffers.SetBufferHistogramValue(number,series_index,value,color_index,as_current);
  }
//+------------------------------------------------------------------+


...

//+------------------------------------------------------------------+
//| Set data of all histogram buffers on two buffers                 |
//| by its serial number                                             |
//| (0 - the very first buffer, 1,2,N - subsequent ones)             |
//+------------------------------------------------------------------+
void CEngine::BufferSetDataHistogram2(const int number,const int series_index,const double value1,const double value2,const uchar color_index,bool as_current=false)
  {
   this.m_buffers.SetBufferHistogram2Value(number,series_index,value1,value2,color_index,as_current);
  }
//+------------------------------------------------------------------+


...

//+------------------------------------------------------------------+
//| Set data of all zizag buffers                                    |
//| by its serial number                                             |
//| (0 - the very first zigzag buffer, 1,2,N - subsequent ones)      |
//+------------------------------------------------------------------+
void CEngine::BufferSetDataZigZag(const int number,const int series_index,const double value1,const double value2,const uchar color_index,bool as_current=false)
  {
   this.m_buffers.SetBufferZigZagValue(number,series_index,value1,value2,color_index,as_current);
  }
//+------------------------------------------------------------------+


...

//+------------------------------------------------------------------+
//| Set data of all filling buffers                                  |
//| by its serial number                                             |
//| (0 - the very first filling buffer, 1,2,N - subsequent ones)     |
//+------------------------------------------------------------------+
void CEngine::BufferSetDataFilling(const int number,const int series_index,const double value1,const double value2,bool as_current=false)
  {
   this.m_buffers.SetBufferFillingValue(number,series_index,value1,value2,as_current);
  }
//+------------------------------------------------------------------+


...

//+------------------------------------------------------------------+
//| Set data of all bar buffers                                      |
//| by its serial number                                             |
//| (0 - the very first bar buffer, 1,2,N - subsequent ones)         |
//+------------------------------------------------------------------+
void CEngine::BufferSetDataBars(const int number,const int series_index,const double open,const double high,const double low,const double close,const uchar color_index,bool as_current=false)
  {
   CBufferBars *buff=this.m_buffers.GetBufferBars(number);
   if(buff==NULL)
      return;
   this.m_buffers.SetBufferBarsValue(number,series_index,open,high,low,close,color_index,as_current);
  }
//+------------------------------------------------------------------+


...

//+------------------------------------------------------------------+
//| Set data of all candle buffers                                   |
//| by its serial number                                             |
//| (0 - the very first candle buffer, 1,2,N - subsequent ones)      |
//+------------------------------------------------------------------+
void CEngine::BufferSetDataCandles(const int number,const int series_index,const double open,const double high,const double low,const double close,const uchar color_index,bool as_current=false)
  {
   CBufferCandles *buff=this.m_buffers.GetBufferCandles(number);
   if(buff==NULL)
      return;
   this.m_buffers.SetBufferCandlesValue(number,series_index,open,high,low,close,color_index,as_current);
  }
//+------------------------------------------------------------------+


Alle diese Methoden sind Mehrperiodenverfahren, die es uns ermöglichen, angeforderte Daten anderer Perioden auf dem aktuellen Chart innerhalb eines Aufrufs anzuzeigen.

Die Methoden PufferPfeilFarbe(), PufferZeileFarbe(), PufferAbschnittFarbe(), PufferHistogrammFarbe(), PufferHistogramm2Farbe(), PufferZigZagColor(), PufferFillingColor(), PufferBarsColor(), PufferKerzenColor(), PufferPfeilColorIndex(), PufferLineColorIndex(), BufferSectionColorIndex(), BufferHistogramColorIndex(), BufferHistogram2ColorIndex(), BufferZigZagColorIndex(), Die Methoden BufferFillingColorIndex(), BufferBarsColorIndex() und BufferCandlesColorIndex() wurden lediglich zur besseren Lesbarkeit innerhalb des Programms umbenannt.

Zum Beispiel hieß die Methode BufferArrowColor() früher BufferColorArrow(), was bei der Bestimmung ihres Zwecks zu Verwirrung führen konnte. Jetzt beschreiben all diese Methoden zuerst den Puffertyp, danach geben sie das zurück, was visuell umfassender und korrekter zu sein scheint.

Die Methode BuffersSetColors() setzt Farbwerte für alle Indikatorpuffer aus dem übergebenen Farbarray mit Hilfe der Methode SetColors() der Kollektionsklasse der Puffer, die ich oben besprochen habe:

void BuffersSetColors(const color &array_colors[]) { this.m_buffers.SetColors(array_colors); }

Die Methoden zum Setzen des Farbindex auf bestimmte Pufferobjekte wurden überarbeitet. Jetzt rufen sie die entsprechenden Methoden der Kollektionsklasse der Puffer des Objekts auf, die oben besprochen wurden:

void BufferArrowSetColorIndex(const int number,const int series_index,const uchar color_index)
       { this.m_buffers.SetBufferArrowColorIndex(number,series_index,color_index);       }
void BufferLineSetColorIndex(const int number,const int series_index,const uchar color_index)
       { this.m_buffers.SetBufferLineColorIndex(number,series_index,color_index);        }
void BufferSectionSetColorIndex(const int number,const int series_index,const uchar color_index)
       { this.m_buffers.SetBufferSectionColorIndex(number,series_index,color_index);     }
void BufferHistogramSetColorIndex(const int number,const int series_index,const uchar color_index)
       { this.m_buffers.SetBufferHistogramColorIndex(number,series_index,color_index);   }
void BufferHistogram2SetColorIndex(const int number,const int series_index,const uchar color_index)
       { this.m_buffers.SetBufferHistogram2ColorIndex(number,series_index,color_index);  }
void BufferZigZagSetColorIndex(const int number,const int series_index,const uchar color_index)
       { this.m_buffers.SetBufferZigZagColorIndex(number,series_index,color_index);      }
void BufferFillingSetColorIndex(const int number,const int series_index,const uchar color_index)
       { this.m_buffers.SetBufferFillingColorIndex(number,series_index,color_index);     }
void BufferBarsSetColorIndex(const int number,const int series_index,const uchar color_index)
       { this.m_buffers.SetBufferBarsColorIndex(number,series_index,color_index);        }
void BufferCandlesSetColorIndex(const int number,const int series_index,const uchar color_index)
       { this.m_buffers.SetBufferCandlesColorIndex(number,series_index,color_index);     }


Die Methode BufferClear() löscht die Pufferdaten anhand ihres Index in der Liste in der angegebenen Zeitreihenleiste mit Hilfe der Methode Clear() der Kollektionsklasse der Pufferobjekte:

void BufferClear(const int buffer_list_index,const int series_index) { this.m_buffers.Clear(buffer_list_index,series_index); }

Die Methoden zum Löschen von Pufferobjekten löschen die Daten aller Arrays des angegebenen Puffers unter Verwendung der geeigneten Methoden der oben besprochenen Pufferobjektsammelklasse:

void BufferArrowClear(const int number,const int series_index)      { this.m_buffers.ClearBufferArrow(number,series_index);     }
void BufferLineClear(const int number,const int series_index)       { this.m_buffers.ClearBufferLine(number,series_index);      }
void BufferSectionClear(const int number,const int series_index)    { this.m_buffers.ClearBufferSection(number,series_index);   }
void BufferHistogramClear(const int number,const int series_index)  { this.m_buffers.ClearBufferHistogram(number,series_index); }
void BufferHistogram2Clear(const int number,const int series_index) { this.m_buffers.ClearBufferHistogram2(number,series_index);}
void BufferZigZagClear(const int number,const int series_index)     { this.m_buffers.ClearBufferZigZag(number,series_index);    }
void BufferFillingClear(const int number,const int series_index)    { this.m_buffers.ClearBufferFilling(number,series_index);   }
void BufferBarsClear(const int number,const int series_index)       { this.m_buffers.ClearBufferBars(number,series_index);      }
void BufferCandlesClear(const int number,const int series_index)    { this.m_buffers.ClearBufferCandles(number,series_index);   }


Die Methode BuffersPrintShort() zeigt die Kurzbeschreibung aller im Programm angelegten und in der Pufferobjektsammlung gespeicherten Indikatorpuffer an:

//+-------------------------------------------------------------------------+
//| Display short description of all indicator buffers within the collection|
//+-------------------------------------------------------------------------+
void CEngine::BuffersPrintShort(void)
  {
//--- Get the pointer to the collection list of buffer objects
   CArrayObj *list=this.GetListBuffers();
   if(list==NULL)
      return;
   int total=list.Total();
//--- In a loop by the number of buffers in the list,
//--- get the next buffer and display its brief description in the journal
   for(int i=0;i<total;i++)
     {
      CBuffer *buff=list.At(i);
      if(buff==NULL)
         continue;
      buff.PrintShort();
     }
  }
//+------------------------------------------------------------------+


Die Logik der Methode wird in den Code-Kommentaren beschrieben und ist recht einfach: In einer Schleife durch die Liste aller Kollektionsliste der Puffer holen wir uns den nächsten Puffer und lassen sich dessen Kurzbeschreibung mit der Methode PrintShort() der Pufferobjektklasse anzeigen.

Ich habe bereits erwähnt, dass wir den Zeiger auf das Objekt der Zeitreihensammlungsklasse an die Kollektionsklasse des Pufferobjekts übergeben müssen.
Wir verfügen bereits über die Methode CollectionOnInit(). Wir übergeben alle benötigten Kollektionen der Bibliothek, indem wir die Methode aufrufen. Jetzt ist es an der Zeit, zu der Methode den benötigten Zeiger hinzuzufügen. Wir tun dies, indem wir die zuvor besprochene Methode OnInit() der Klasse der Kollektionsklasse der Puffer aufrufen:

//--- Pass the pointers to all the necessary collections to the trading class and the indicator buffer collection class
   void                 CollectionOnInit(void)
                          {
                           this.m_trading.OnInit(this.GetAccountCurrent(),m_symbols.GetObject(),m_market.GetObject(),m_history.GetObject(),m_events.GetObject());
                           this.m_buffers.OnInit(this.m_time_series.GetObject());
                          }


Diese Methode wird in OnInit() von bibliotheksbasierten Programmen aufgerufen.
Um genauer zu sein, wird die Funktion zum Initialisieren der OnInitDoEasy() Bibliothek in OnInit() aufgerufen. Die Bibliothek OnInitDoEasy() wiederum enthält den Aufruf dieser Methode zusammen mit anderen konfigurierenden Aktionen.

Damit ist die Verbesserung der Bibliotheksklassen abgeschlossen.
Lassen Sie uns die Erstellung des Mehrperiodenindikators testen.

Tests

Um den Test durchzuführen, nehmen wir den Indikator Testindikator aus dem vorherigen Artikel und speichern ihn in \MQL5\Indikatoren\TestDoEasy\Teil45\ als TestDoEasyPart45.mq5.

Der Indikator weist in seinen Einstellungen einen Zeitrahmen auf, der in seiner Arbeit verwendet werden sollte. Außerdem wird er auch die Möglichkeit haben, verschiedene Zeichentypen auszuwählen. Die auf dem Chart angezeigten Puffer sollen von den gewählten Zeichentypen abhängen.

Die Zeichenfolge des Indikators aus dem vorherigen Artikel

/*sinput*/   ENUM_TIMEFRAMES_MODE InpModeUsedTFs    =  TIMEFRAMES_MODE_CURRENT;            // Mode of used timeframes list


wird ersetzt durch die neue:

/*sinput*/   ENUM_TIMEFRAMES_MODE InpModeUsedTFs    =  TIMEFRAMES_MODE_LIST;            // Mode of used timeframes list


Hier habe ich den Indikator, der mit dem aktuellen Zeitrahmen arbeitet, durch den Indikator ersetzt, der mit der Liste der Zeitrahmen arbeitet. Jetzt wartet der Indikator darauf, dass die Liste der Zeitrahmen, die sich in dem für ihre Speicherung vorgesehenen Array befindet, zu arbeiten beginnt.
Als Nächstes fügen wir die Eingabe, wo der benötigte Zeitrahmen festgelegt ist, zu den Einstellungen hinzu (Standard ist die aktuelle Einstellung). Dann wird der gewählte Zeitrahmen in OnInit() in das Array geschrieben. Auf diese Weise werden wir die erforderliche Chart-Periode festlegen, aus der der Indikator Daten für die Anzeige auf dem aktuellen Chart entnehmen soll.

sinput   ENUM_TIMEFRAMES      InpPeriod         =  PERIOD_CURRENT;                  // Used chart period

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Write the name of the working timeframe selected in the settings to the InpUsedTFs variable
   InpUsedTFs=TimeframeDescription(InpPeriod);


Nachdem der Zeitrahmen in das Array geschrieben wurde, verwendet die Bibliothek diesen Wert, um die erforderlichen Zeitreihen zu erstellen.

Als Nächstes erstellen wir einfach Puffer mit allen Zeichnungstypen , setzen für sie Anzeige-Flags im Datenfenster in Abhängigkeit von den entsprechenden Eingabeeinstellungen und setzen den in den Einstellungen für sie angegebenen Zeitrahmen:

//--- indicator buffers mapping
//--- Create all the necessary buffer objects
   engine.BufferCreateArrow();         // 2 arrays
   engine.BufferCreateLine();          // 2 arrays
   engine.BufferCreateSection();       // 2 arrays
   engine.BufferCreateHistogram();     // 2 arrays
   engine.BufferCreateHistogram2();    // 3 arrays
   engine.BufferCreateZigZag();        // 3 arrays
   engine.BufferCreateFilling();       // 2 arrays
   engine.BufferCreateBars();          // 5 arrays
   engine.BufferCreateCandles();       // 5 arrays
   engine.BufferCreateCalculate();     // 1 array

//--- Check the number of buffers specified in the 'properties' block
   if(engine.BuffersPropertyPlotsTotal()!=indicator_plots)
      Alert(TextByLanguage("Внимание! Значение \"indicator_plots\" должно быть ","Attention! Value of \"indicator_plots\" should be "),engine.BuffersPropertyPlotsTotal());
   if(engine.BuffersPropertyBuffersTotal()!=indicator_buffers)
      Alert(TextByLanguage("Внимание! Значение \"indicator_buffers\" должно быть ","Attention! Value of \"indicator_buffers\" should be "),engine.BuffersPropertyBuffersTotal());
      
//--- Create the color array and set non-default colors to all buffers within the collection
   color array_colors[]={clrDodgerBlue,clrRed,clrGray};
   engine.BuffersSetColors(array_colors);
//--- Set the line width for ZigZag (the sixth drawn buffer)
//--- It has the index of 5 considering that the starting point is zero
   CBuffer *buff_zz=engine.GetBufferByPlot(5);
   if(buff_zz!=NULL)
     {
      buff_zz.SetWidth(2);
     }
     
//--- In a loop by the list of collection buffer objects,
   for(int i=0;i<engine.GetListBuffers().Total();i++)
     {
      //--- get the next buffer
      CBuffer *buff=engine.GetListBuffers().At(i);
      if(buff==NULL)
         continue;
      //--- and set its display in the data window depending on its specified usage
      //--- and also the chart period selected in the settings
      buff.SetShowData(IsUse(buff.Status()));
      buff.SetTimeframe(InpPeriod);
     }


Da ich die Indizierungsrichtung wie in der Zeitreihe innerhalb von Indikatoren verwenden werde (aufgrund der Art und Weise, wie die Speicherung von Zeitreihen in der Bibliothek angeordnet ist), erstellen wir eine weitere Funktion zur Übergabe aller Daten-Arrays von OnCalculate() an die Bibliothek. Zuvor wurde dies durch die Funktion CopyData() durchgeführt, in der das Flag "wie in der Zeitreihe" für Arrays gesetzt wurde und sein vorheriger Status dann zurückgegeben wurde:

//+------------------------------------------------------------------+
//| Copy data from the second OnCalculate() form to the structure    |
//+------------------------------------------------------------------+
void CopyData(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[])
  {
//--- Get the array indexing flags as in the timeseries. If failed,
//--- set the indexing direction or the arrays as in the timeseries
   bool as_series_time=ArrayGetAsSeries(time);
   if(!as_series_time)
      ArraySetAsSeries(time,true);
   bool as_series_open=ArrayGetAsSeries(open);
   if(!as_series_open)
      ArraySetAsSeries(open,true);
   bool as_series_high=ArrayGetAsSeries(high);
   if(!as_series_high)
      ArraySetAsSeries(high,true);
   bool as_series_low=ArrayGetAsSeries(low);
   if(!as_series_low)
      ArraySetAsSeries(low,true);
   bool as_series_close=ArrayGetAsSeries(close);
   if(!as_series_close)
      ArraySetAsSeries(close,true);
   bool as_series_tick_volume=ArrayGetAsSeries(tick_volume);
   if(!as_series_tick_volume)
      ArraySetAsSeries(tick_volume,true);
   bool as_series_volume=ArrayGetAsSeries(volume);
   if(!as_series_volume)
      ArraySetAsSeries(volume,true);
   bool as_series_spread=ArrayGetAsSeries(spread);
   if(!as_series_spread)
      ArraySetAsSeries(spread,true);
//--- Copy the arrays' zero bar to the OnCalculate() SDataCalculate data structure
   rates_data.rates_total=rates_total;
   rates_data.prev_calculated=prev_calculated;
   rates_data.rates.time=time[0];
   rates_data.rates.open=open[0];
   rates_data.rates.high=high[0];
   rates_data.rates.low=low[0];
   rates_data.rates.close=close[0];
   rates_data.rates.tick_volume=tick_volume[0];
   rates_data.rates.real_volume=(#ifdef __MQL5__ volume[0] #else 0 #endif);
   rates_data.rates.spread=(#ifdef __MQL5__ spread[0] #else 0 #endif);
//--- Return the arrays' initial indexing direction
   if(!as_series_time)
      ArraySetAsSeries(time,false);
   if(!as_series_open)
      ArraySetAsSeries(open,false);
   if(!as_series_high)
      ArraySetAsSeries(high,false);
   if(!as_series_low)
      ArraySetAsSeries(low,false);
   if(!as_series_close)
      ArraySetAsSeries(close,false);
   if(!as_series_tick_volume)
      ArraySetAsSeries(tick_volume,false);
   if(!as_series_volume)
      ArraySetAsSeries(volume,false);
   if(!as_series_spread)
      ArraySetAsSeries(spread,false);
  }
//+------------------------------------------------------------------+


Als Nächstes habe ich "diese Arrays noch einmal umgedreht", damit sie mit der Bibliothekszeitreihe in OnCalculate() korrekt funktionieren:

//+------------------------------------------------------------------+
//| OnCalculate code block for working with the indicator:           |
//+------------------------------------------------------------------+
//--- Set OnCalculate arrays as timeseries
   ArraySetAsSeries(open,true);
   ArraySetAsSeries(high,true);
   ArraySetAsSeries(low,true);
   ArraySetAsSeries(close,true);
   ArraySetAsSeries(time,true);
   ArraySetAsSeries(tick_volume,true);
   ArraySetAsSeries(volume,true);
   ArraySetAsSeries(spread,true);


Um unnötige Arbeit zu vermeiden, lassen Sie uns noch eine weitere Funktion in der Datei der Bibliotheksdienstfunktionen erstellen, bei der die an sie übergebenen Arrays wie in der Zeitreihe die Indexierungsrichtung erhalten. Öffnen Sie \MQL5\Include\DoEasy\Services\DELib.mqh und fügen Sie die neue Funktion hinzu:

//+------------------------------------------------------------------+
//| Copy data from the second OnCalculate() form to the structure    |
//| and set the "as timeseries" flag to all arrays                   |
//+------------------------------------------------------------------+
void CopyDataAsSeries(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[])
  {
//--- set the indexing direction or the arrays as in the timeseries
   ArraySetAsSeries(time,true);
   ArraySetAsSeries(open,true);
   ArraySetAsSeries(high,true);
   ArraySetAsSeries(low,true);
   ArraySetAsSeries(close,true);
   ArraySetAsSeries(tick_volume,true);
   ArraySetAsSeries(volume,true);
   ArraySetAsSeries(spread,true);
//--- Copy the arrays' zero bar to the OnCalculate() SDataCalculate data structure
   rates_data.rates_total=rates_total;
   rates_data.prev_calculated=prev_calculated;
   rates_data.rates.time=time[0];
   rates_data.rates.open=open[0];
   rates_data.rates.high=high[0];
   rates_data.rates.low=low[0];
   rates_data.rates.close=close[0];
   rates_data.rates.tick_volume=tick_volume[0];
   rates_data.rates.real_volume=(#ifdef __MQL5__ volume[0] #else 0 #endif);
   rates_data.rates.spread=(#ifdef __MQL5__ spread[0] #else 0 #endif);
  }
//+------------------------------------------------------------------+


Die Funktion macht dasselbe wie die vorhergehende, aber sie setzt die Arrays nicht auf ihre ursprüngliche Indexierung zurück. Anstatt die Funktion CopyData() vom Indikator aus aufzurufen, rufen wir nun die neue Funktion auf — CopyDataAsSeries(). Dies erspart uns die Neueinstellung der gewünschten Indizierung für die Arrays:

//+------------------------------------------------------------------+
//| 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.0)
      return 0;
   
//--- If working in the tester
   if(MQLInfoInteger(MQL_TESTER)) 
     {
      engine.OnTimer(rates_data);   // Working in the library timer
      EventsHandling();             // Working 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
//--- 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

//--- Calculate the indicator
   CBar *bar=NULL;         // Bar object for defining the candle direction
   uchar color_index=0;    // Color index to be set for the buffer depending on the candle direction

//--- Main calculation loop of the indicator
   for(int i=limit; i>WRONG_VALUE && !IsStopped(); i--)
     {
      //--- Clear the current bar of all created buffers
      engine.BufferArrowClear(0,0);
      engine.BufferLineClear(0,0);
      engine.BufferSectionClear(0,0);
      engine.BufferHistogramClear(0,0);
      engine.BufferHistogram2Clear(0,0);
      engine.BufferZigZagClear(0,0);
      engine.BufferFillingClear(0,0);
      engine.BufferBarsClear(0,0);
      engine.BufferCandlesClear(0,0);
      
      //--- Get the timeseries bar corresponding to the loop index time on the chart period specified in the settings
      bar=engine.SeriesGetBar(NULL,InpPeriod,time[i]);
      if(bar==NULL)
         continue;
      //--- Calculate the color index depending on the candle direction on the timeframe specified in the settings
      color_index=(bar.TypeBody()==BAR_BODY_TYPE_BULLISH ? 0 : bar.TypeBody()==BAR_BODY_TYPE_BEARISH ? 1 : 2);
      //--- Check the settings and calculate the arrow buffer
      if(IsUse(BUFFER_STATUS_ARROW))
         engine.BufferSetDataArrow(0,i,bar.Close(),color_index);
      //--- Check the settings and calculate the line buffer
      if(IsUse(BUFFER_STATUS_LINE))
         engine.BufferSetDataLine(0,i,bar.Open(),color_index);
      //--- Check the settings and calculate the section buffer
      if(IsUse(BUFFER_STATUS_SECTION))
         engine.BufferSetDataSection(0,i,bar.Close(),color_index);
      //--- Check the settings and calculate the histogram from zero buffer
      if(IsUse(BUFFER_STATUS_HISTOGRAM))
         engine.BufferSetDataHistogram(0,i,open[i],color_index);
      //--- Check the settings and calculate the 'histogram on two buffers' buffer
      if(IsUse(BUFFER_STATUS_HISTOGRAM2))
         engine.BufferSetDataHistogram2(0,i,bar.Open(),bar.Close(),color_index);
      //--- Check the settings and calculate the zigzag buffer
      if(IsUse(BUFFER_STATUS_ZIGZAG))
        {
         //--- Set the bar's Low value value for the zigzag (for bullish candles)
         double value1=bar.Low();
         double value2=value1;
         //--- If the candle is bearish (color index = 1), set the bar's High value for the zigzag
         if(color_index==1)
           {
            value1=value2=bar.High();
           }
         engine.BufferSetDataZigZag(0,i,value1,value2,color_index);
        }
      //--- Check the settings and calculate the filling buffer
      if(IsUse(BUFFER_STATUS_FILLING))
        {
         //--- Set filling border values for bullish candles
         double value1=bar.High();
         double value2=bar.Low();
         //--- In case of the bearish candle (color index = 1), swap the filling borders to change the color
         if(color_index==1)
           {
            value1=bar.Low();
            value2=bar.High();
           }
         engine.BufferSetDataFilling(0,i,value1,value2,color_index);
        }
      //--- Check the settings and calculate the bar buffer
      if(IsUse(BUFFER_STATUS_BARS))
         engine.BufferSetDataBars(0,i,bar.Open(),bar.High(),bar.Low(),bar.Close(),color_index);
      //--- Check the settings and calculate the candle buffer
      if(IsUse(BUFFER_STATUS_CANDLES))
         engine.BufferSetDataCandles(0,i,bar.Open(),bar.High(),bar.Low(),bar.Close(),color_index);
     }
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+


Die übrigen Aktionen und die Logik von OnCalculate() werden in den Code-Kommentaren ausführlich beschrieben. Die Arbeit mit dem Mehrperiodenindikator ist viel einfacher geworden. Wir brauchen nichts mehr selbst zu berechnen. Stattdessen können wir einfach Daten in den Puffer schreiben, damit die Bibliothek definieren kann, wohin die Daten gestellt und wie sie angezeigt werden sollen:



Was kommt als Nächstes?

Im nächsten Artikel werden wir die Entwicklung der Indikatorpuffer-Kollektionsklasse fortsetzen und den Betrieb des Indikators im Multisymbolmodus gestalten.

Alle Dateien der aktuellen Version der Bibliothek sind unten zusammen mit den Dateien der Test-EAs angehängt, die Sie testen und herunterladen können.
Hinterlassen Sie Ihre Fragen, Kommentare und Anregungen in den Kommentaren.
Bitte bedenken Sie, dass ich hier den MQL5-Testindikator für MetaTrader 5 entwickelt habe.
Die angehängten Dateien sind nur für MetaTrader 5 bestimmt. Die aktuelle Bibliotheksversion wurde nicht mit dem MetaTrader 4 getestet.
Nachdem ich die Kollektion der Indikatorpuffer erstellt und getestet habe, werde ich versuchen, einige MQL5-Funktionen in MetaTrader 4 zu implementieren.

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


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

Beigefügte Dateien |
MQL5.zip (3739.84 KB)
Berechnung mathematischer Ausdrücke (Teil 1). Ein Parser mit rekursivem Abstieg Berechnung mathematischer Ausdrücke (Teil 1). Ein Parser mit rekursivem Abstieg

Der Artikel behandelt die Grundprinzipien der Analyse und Berechnung mathematischer Ausdrücke. Wir werden Parser mit rekursivem Abstieg implementieren, die im Interpreter- und beschleunigtem Berechnungsmodus arbeiten und auf einem vorgefertigten Syntaxbaum basieren.

Schnelle Werkzeuge für den manuellen Handel: Grundlegende Funktionsweise Schnelle Werkzeuge für den manuellen Handel: Grundlegende Funktionsweise

Heutzutage wechseln viele Händler zu automatisierten Handelssystemen, die eine zusätzliche Einrichtung erfordern oder vollständig automatisiert und einsatzbereit sein können. Es gibt jedoch einen beträchtlichen Teil der Händler, die es vorziehen, auf die altmodische Art und Weise manuell zu handeln. In diesem Artikel erstellen wir "Market Order" (Marktorder) das Toolkit für den schnellen manuellen Handel, für die Verwendung von Hotkeys und für die Durchführung typischer Handelsaktionen mit einem Klick.

Berechnung mathematischer Ausdrücke (Teil 2). Parser nach Pratt und dem Shunting-yard-Algorithmus Berechnung mathematischer Ausdrücke (Teil 2). Parser nach Pratt und dem Shunting-yard-Algorithmus

In diesem Artikel betrachten wir die Prinzipien der Analyse und Auswertung mathematischer Ausdrücke unter Verwendung von Parsern, die auf der Operator-Priorität basieren. Wir werden Parser nach Pratt und dem Shunting-yard-Algorithmus, Bytecode-Generierung und Auswertungen mit diesem Code implementieren und uns ansehen, wie Indikatoren als Funktionen in Ausdrücken verwendet und wie Handelssignale in Expert Advisors auf der Grundlage dieser Indikatoren eingerichtet werden können.

Zeitreihen in der Bibliothek DoEasy (Teil 46): Mehrperioden-Multisymbol-Indikatorpuffer Zeitreihen in der Bibliothek DoEasy (Teil 46): Mehrperioden-Multisymbol-Indikatorpuffer

In diesem Artikel werde ich die Klassen der Objekte der Indikatorpuffer verbessern, um im Multisymbolmodus arbeiten zu können. Dies wird den Weg für die Erstellung von Multisymbol- und Mehrperioden-Indikatoren in benutzerdefinierten Programmen ebnen. Ich werde den berechneten Pufferobjekten die fehlende Funktionalität hinzufügen, die es uns ermöglicht, multisymbol- und mehrperiodische Standardindikatoren zu erstellen.