English Русский 中文 Español 日本語 Português
Zeitreihen in der Bibliothek DoEasy (Teil 44): Kollektionsklasse der Objekte von Indikatorpuffern

Zeitreihen in der Bibliothek DoEasy (Teil 44): Kollektionsklasse der Objekte von Indikatorpuffern

MetaTrader 5Beispiele | 28 September 2020, 11:12
301 0
Artyom Trishkin
Artyom Trishkin

Inhalt


Konzept

Mit begann ich darüber nachzudenken, die Bibliothek zur Erstellung nutzerdefinierter Indikatoren zu nutzen. Gegenwärtig habe ich Zeitreihenobjekte und ihre Kollektion sowie Indikatorpufferobjekte vorbereitet — grundlegende, abstrakte Puffer und Indikatorpuffer, die darauf aufbauen.
In diesem Artikel beginne ich mit der Entwicklung der Kollektion von Indikatorpuffern für die schnelle Erstellung von Puffern in beliebiger Menge (bis zu 512) in einem einzigen Programm sowie für deren bequemes Abrufen und den Zugriff auf ihre Daten.

Der Link <"Zeitreihenkollektion> — <Pufferkollektion"> ermöglicht das Erstellen beliebiger Multi-Symbol- und Multi-Zeitrahmen-Indikatoren. Das ist es, was ich ab dem nächsten Artikel tun werde. Im aktuellen Artikel werde ich die Kollektion von Indikatorpuffern zur Erstellung einer beliebigen Anzahl von Puffern unter Verwendung eines der neun Zeichenstile entwickeln und testen. Gegenwärtig darf die maximal mögliche Anzahl der gezeichneten Puffer im Indikator 512 nicht überschreiten. Dies sollte mehr als genug sein, um beliebige komplexe Indikatoren mit einer großen Anzahl von Diagrammen zu erstellen. Die entwickelte Funktionalität soll das Erstellen und die Pflege einer solchen Anzahl grafischer Konstruktionen vereinfachen und den gesamten Prozess auf den bloßen Zugriff auf die erstellten Puffer durch ihren Zeichenstil und ihre Anzahl in der Reihenfolge der Erstellung oder durch den Pufferindex in der Sammlung reduzieren.


Vorbereitung der Daten und Verbesserung der Pufferobjekte

Im vorigen Artikel habe ich "Indikatorpuffer"-Objekte erstellt, die von grundlegenden, abstrakten Puffers abgeleitet wurden. Ergänzen wir sie durch Hilfsmethoden für den Zugriff auf und das Lesen von Daten in/aus Arrays vom Typ double, bestimmt als Indikatorpuffer.
Obwohl die bereits vorhandenen grundlegenden, abstrakten Puffermethoden ausreichend sind, erweist sich die Erstellung nutzerdefinierter Methoden in Nachfolgeobjekten, die nur ihrem Zeichenstil inhärent sind, als nützlich für einen bequemeren Zugriff auf ihre double Arrays beim Zugriff auf den Puffer nach seinem Status (Zeichenstil) und bietet mehr Flexibilität bei der Erstellung benutzerdefinierter Indikatoren.

Fügen wir die neuen Bibliotheksmeldungen hinzu. Die neue Nachrichten-Indizes in \MQL5\Include\DoEasy\Datas.mqh:

   MSG_LIB_SYS_FAILED_COLORS_ARRAY_RESIZE,            // Failed to change the color array size
   MSG_LIB_SYS_FAILED_ADD_BUFFER,                     // Failed to add buffer object to the list
   MSG_LIB_SYS_FAILED_CREATE_BUFFER_OBJ,              // Failed to create \"Indicator buffer\" object
   
   MSG_LIB_TEXT_YES,                                  // Yes

...

   MSG_LIB_TEXT_BUFFER_TEXT_INVALID_PROPERTY_BUFF,    // Invalid number of indicator buffers (#property indicator_buffers)
   MSG_LIB_TEXT_BUFFER_TEXT_MAX_BUFFERS_REACHED,      // Reached maximum possible number of indicator buffers

   MSG_LIB_TEXT_BUFFER_TEXT_STATUS_NONE,              // No drawing

sowie die Nachrichtentexte, die den neu hinzugekommenen Indizes entsprechen:

   {"Не удалось изменить размер массива цветов","Failed to resize color array"},
   {"Не удалось добавить объект-буфер в список","Failed to add buffer object to list"},
   {"Не удалось создать объект \"Индикаторный буфер\"","Failed to create object \"Indicator buffer\""},

   {"Да","Yes"},

...

   {"Неправильно указано количество буферов индикатора (#property indicator_buffers)","Number of indicator buffers incorrect (#property indicator_buffers)"},
   {"Достигнуто максимально возможное количество индикаторных буферов","Maximum number of indicator buffers reached"},
   
   {"Нет отрисовки","No drawing"},

Der Indikator kann maximal 512 Puffer nutzen.
Fügen wir die Makro-Substitution hinzu und geben tragen sie in \MQL5\Include\DoEasy\Defines.mqh ein:

//+------------------------------------------------------------------+
//| Macro substitutions                                              |
//+------------------------------------------------------------------+
//--- Describe the function with the error line number
#define DFUN_ERR_LINE                  (__FUNCTION__+(TerminalInfoString(TERMINAL_LANGUAGE)=="Russian" ? ", Page " : ", Line ")+(string)__LINE__+": ")
#define DFUN                           (__FUNCTION__+": ")        // "Function description"
#define COUNTRY_LANG                   ("Russian")                // Country language
#define END_TIME                       (D'31.12.3000 23:59:59')   // End date for account history data requests
#define TIMER_FREQUENCY                (16)                       // Minimal frequency of the library timer in milliseconds
#define TOTAL_TRY                      (5)                        // Default number of trading attempts
#define IND_COLORS_TOTAL               (64)                       // Maximum possible number of indicator buffer colors
#define IND_BUFFERS_MAX                (512)                      // Maximum possible number of indicator buffers
//--- Standard sounds

Sie können natürlich den Wert "512" verwenden, aber die Makro-Substitution ist bequemer, denn wenn dieser Wert irgendwann von den Entwicklern erhöht wird, dann müssen Sie nicht in allen Dateien, in denen ein Verweis auf diesen Wert vorhanden ist, nach einem neuen Wert suchen und diesen korrigieren. Stattdessen müssen wir nur den Wert für die Makro-Substitution ändern.

Wir werden Pufferobjekte anhand von Eigenschaften suchen und auswählen müssen, die zuvor als unnötig für die Suche und Sortierung identifiziert wurden.

Alle Eigenschaften, die bei der Sortierung nicht angewendet werden, befinden sich immer ganz am Ende der Aufzählungsliste der Eigenschaften. Auch die Makro-Substitution, die die Anzahl der beim Sortieren nicht verwendeten Eigenschaften angibt, ist aktiviert. Um eine Eigenschaft als sortierbar zu setzen, müssen wir sie vom Ende der Enumerationsliste der Eigenschaften näher an diejenigen Eigenschaften heranrücken, die derzeit für die Sortierung zugelassen sind, und eine neue Anzahl unbenutzter Eigenschaften in der Sortierung angeben.
Natürlich sollten wir auch die Enumeration der möglichen Sortierkriterien durch neue Eigenschaften ergänzen — mit denjenigen, die wir für die Sortierung verwenden dürfen. Die Position der neu hinzugefügten Eigenschaften in der Enumerationsliste der Sortierkriterien sollte mit der Position dieser Eigenschaften in der Aufzählungsliste der Eigenschaften übereinstimmen, in der wir die Verwendung dieser Eigenschaften für die Sortierung zugelassen haben.

Das klingt verwirrend, aber in Wirklichkeit ist alles ganz einfach. Öffnen Sie \MQL5\Include\DoEasy\Defines.mqh und nehmen Sie die notwendigen Änderungen vor.

Früher hatten wir ganzzahlige Pufferobjekteigenschaften, die in der folgenden Reihenfolge angeordnet waren:

//+------------------------------------------------------------------+
//| 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_NUM_DATAS,                                   // Number of data buffers
   BUFFER_PROP_INDEX_BASE,                                  // Basic data buffer index
   BUFFER_PROP_INDEX_COLOR,                                 // Color buffer index
   BUFFER_PROP_INDEX_NEXT,                                  // Index of the free array to be assigned as the next indicator buffer
  }; 
#define BUFFER_PROP_INTEGER_TOTAL (19)                      // Total number of integer bar properties
#define BUFFER_PROP_INTEGER_SKIP  (6)                       // Number of buffer properties not used in sorting
//+------------------------------------------------------------------+

Hier sollten zwei Eigenschaften sortierbar gemacht werden. Dafür schieben wir sie nach oben und ändern die Anzahle der nicht zum Sortieren verwendeten Eigenschaften von 6 auf 2:

//+------------------------------------------------------------------+
//| 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,                                  // Basic data buffer index
   BUFFER_PROP_INDEX_NEXT,                                  // Index of the free array to be assigned as the next indicator buffer
   BUFFER_PROP_NUM_DATAS,                                   // Number of data buffers
   BUFFER_PROP_INDEX_COLOR,                                 // Color buffer index
  }; 
#define BUFFER_PROP_INTEGER_TOTAL (19)                      // Total number of integer bar properties
#define BUFFER_PROP_INTEGER_SKIP  (2)                       // Number of buffer properties not used in sorting
//+------------------------------------------------------------------+

Hier habe ich angegeben, dass nur zwei Eigenschaften ganz am Ende der Liste nicht an der Sortierung teilnehmen.
Fügen wir diese neuen Eigenschaften zu den Sortierkriterien hinzu:

//+------------------------------------------------------------------+
//| 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,                               // Sort by the index of the free array to be assigned as the next indicator 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
  };
//+------------------------------------------------------------------+

Wie Sie sehen können, stimmt ihre Position in der Enumerationsliste der Sortierkriterien mit der Position der ganzzahligen Eigenschaften in der Aufzählung überein. Die Kriterienenumeration ist eine obligatorische Bedingung, da die Reihenfolge der Sortiereigenschaften mit der Reihenfolge der Eigenschaften in der Aufzählung der Objekteigenschaften übereinstimmen sollte. Dies wurde im dritten Teil der Bibliotheksbeschreibung diskutiert.

Da wir nun über die Sortierung von Pufferobjekten nach ihren Eigenschaften sprechen, ist es an der Zeit, Werkzeuge für die Suche von Pufferobjekten nach ihren Eigenschaften vorzubereiten. Ich habe diese Möglichkeit bereits für alle in den Sammlungen gespeicherten Bibliotheksobjekte eingeführt. Lassen Sie uns nun auch die Methoden für die Suche nach Pufferobjekten schreiben.

Öffnen wir \MQL5\Include\DoEasy\Services\Select.mqh und binden dazu die Pufferklassendatei ein:

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

Fügen Sie ganz unten im Klassenkörper (nach dem Block, der die Methoden zur Arbeit mit Zeitreihenbalken deklariert) den Block hinzu, der die Methoden zur Arbeit mit Indikatorpuffern deklariert:

//+------------------------------------------------------------------+
//| Methods of working with timeseries bars                          |
//+------------------------------------------------------------------+
   //--- Return the list of bars with one out of (1) integer, (2) real and (3) string properties meeting a specified criterion
   static CArrayObj *ByBarProperty(CArrayObj *list_source,ENUM_BAR_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode);
   static CArrayObj *ByBarProperty(CArrayObj *list_source,ENUM_BAR_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode);
   static CArrayObj *ByBarProperty(CArrayObj *list_source,ENUM_BAR_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode);
   //--- Return the bar index in the list with the maximum value of the order's (1) integer, (2) real and (3) string properties
   static int        FindBarMax(CArrayObj *list_source,ENUM_BAR_PROP_INTEGER property);
   static int        FindBarMax(CArrayObj *list_source,ENUM_BAR_PROP_DOUBLE property);
   static int        FindBarMax(CArrayObj *list_source,ENUM_BAR_PROP_STRING property);
   //--- Return the bar index in the list with the minimum value of the order's (1) integer, (2) real and (3) string properties
   static int        FindBarMin(CArrayObj *list_source,ENUM_BAR_PROP_INTEGER property);
   static int        FindBarMin(CArrayObj *list_source,ENUM_BAR_PROP_DOUBLE property);
   static int        FindBarMin(CArrayObj *list_source,ENUM_BAR_PROP_STRING property);
//+------------------------------------------------------------------+
//| Methods of working with indicator buffers                        |
//+------------------------------------------------------------------+
   //--- Return the list of buffers with one out of (1) integer, (2) real and (3) string properties meeting a specified criterion
   static CArrayObj *ByBufferProperty(CArrayObj *list_source,ENUM_BUFFER_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode);
   static CArrayObj *ByBufferProperty(CArrayObj *list_source,ENUM_BUFFER_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode);
   static CArrayObj *ByBufferProperty(CArrayObj *list_source,ENUM_BUFFER_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode);
   //--- Return the buffer index in the list with the maximum value of the order's (1) integer, (2) real and (3) string properties
   static int        FindBufferMax(CArrayObj *list_source,ENUM_BUFFER_PROP_INTEGER property);
   static int        FindBufferMax(CArrayObj *list_source,ENUM_BUFFER_PROP_DOUBLE property);
   static int        FindBufferMax(CArrayObj *list_source,ENUM_BUFFER_PROP_STRING property);
   //--- Return the buffer index in the list with the minimum value of the order's (1) integer, (2) real and (3) string properties
   static int        FindBufferMin(CArrayObj *list_source,ENUM_BUFFER_PROP_INTEGER property);
   static int        FindBufferMin(CArrayObj *list_source,ENUM_BUFFER_PROP_DOUBLE property);
   static int        FindBufferMin(CArrayObj *list_source,ENUM_BUFFER_PROP_STRING property);
//---
  };
//+------------------------------------------------------------------+

Ganz am Ende der Datei fügen wir alle im Klassenkörper deklarierten Methoden hinzu:

//+------------------------------------------------------------------+
//| Methods of working with buffer lists                             |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Return the list of buffers with one integer                      |
//| property meeting the specified criterion                         |
//+------------------------------------------------------------------+
CArrayObj *CSelect::ByBufferProperty(CArrayObj *list_source,ENUM_BUFFER_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode)
  {
   if(list_source==NULL) return NULL;
   CArrayObj *list=new CArrayObj();
   if(list==NULL) return NULL;
   list.FreeMode(false);
   ListStorage.Add(list);
   int total=list_source.Total();
   for(int i=0; i<total; i++)
     {
      CBuffer *obj=list_source.At(i);
      if(!obj.SupportProperty(property)) continue;
      long obj_prop=obj.GetProperty(property);
      if(CompareValues(obj_prop,value,mode)) list.Add(obj);
     }
   return list;
  }
//+------------------------------------------------------------------+
//| Return the list of buffers with one real                         |
//| property meeting the specified criterion                         |
//+------------------------------------------------------------------+
CArrayObj *CSelect::ByBufferProperty(CArrayObj *list_source,ENUM_BUFFER_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode)
  {
   if(list_source==NULL) return NULL;
   CArrayObj *list=new CArrayObj();
   if(list==NULL) return NULL;
   list.FreeMode(false);
   ListStorage.Add(list);
   for(int i=0; i<list_source.Total(); i++)
     {
      CBuffer *obj=list_source.At(i);
      if(!obj.SupportProperty(property)) continue;
      double obj_prop=obj.GetProperty(property);
      if(CompareValues(obj_prop,value,mode)) list.Add(obj);
     }
   return list;
  }
//+------------------------------------------------------------------+
//| Return the list of buffers with one string                       |
//| property meeting the specified criterion                         |
//+------------------------------------------------------------------+
CArrayObj *CSelect::ByBufferProperty(CArrayObj *list_source,ENUM_BUFFER_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode)
  {
   if(list_source==NULL) return NULL;
   CArrayObj *list=new CArrayObj();
   if(list==NULL) return NULL;
   list.FreeMode(false);
   ListStorage.Add(list);
   for(int i=0; i<list_source.Total(); i++)
     {
      CBuffer *obj=list_source.At(i);
      if(!obj.SupportProperty(property)) continue;
      string obj_prop=obj.GetProperty(property);
      if(CompareValues(obj_prop,value,mode)) list.Add(obj);
     }
   return list;
  }
//+------------------------------------------------------------------+
//| Return the buffer index in the list                              |
//| with the maximum integer property value                          |
//+------------------------------------------------------------------+
int CSelect::FindBufferMax(CArrayObj *list_source,ENUM_BUFFER_PROP_INTEGER property)
  {
   if(list_source==NULL) return WRONG_VALUE;
   int index=0;
   CBuffer *max_obj=NULL;
   int total=list_source.Total();
   if(total==0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      CBuffer *obj=list_source.At(i);
      long obj1_prop=obj.GetProperty(property);
      max_obj=list_source.At(index);
      long obj2_prop=max_obj.GetProperty(property);
      if(CompareValues(obj1_prop,obj2_prop,MORE)) index=i;
     }
   return index;
  }
//+------------------------------------------------------------------+
//| Return the buffer index in the list                              |
//| with the maximum real property value                             |
//+------------------------------------------------------------------+
int CSelect::FindBufferMax(CArrayObj *list_source,ENUM_BUFFER_PROP_DOUBLE property)
  {
   if(list_source==NULL) return WRONG_VALUE;
   int index=0;
   CBuffer *max_obj=NULL;
   int total=list_source.Total();
   if(total==0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      CBuffer *obj=list_source.At(i);
      double obj1_prop=obj.GetProperty(property);
      max_obj=list_source.At(index);
      double obj2_prop=max_obj.GetProperty(property);
      if(CompareValues(obj1_prop,obj2_prop,MORE)) index=i;
     }
   return index;
  }
//+------------------------------------------------------------------+
//| Return the buffer index in the list                              |
//| with the maximum string property value                           |
//+------------------------------------------------------------------+
int CSelect::FindBufferMax(CArrayObj *list_source,ENUM_BUFFER_PROP_STRING property)
  {
   if(list_source==NULL) return WRONG_VALUE;
   int index=0;
   CBuffer *max_obj=NULL;
   int total=list_source.Total();
   if(total==0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      CBuffer *obj=list_source.At(i);
      string obj1_prop=obj.GetProperty(property);
      max_obj=list_source.At(index);
      string obj2_prop=max_obj.GetProperty(property);
      if(CompareValues(obj1_prop,obj2_prop,MORE)) index=i;
     }
   return index;
  }
//+------------------------------------------------------------------+
//| Return the buffer index in the list                              |
//| with the minimum integer property value                          |
//+------------------------------------------------------------------+
int CSelect::FindBufferMin(CArrayObj* list_source,ENUM_BUFFER_PROP_INTEGER property)
  {
   int index=0;
   CBuffer *min_obj=NULL;
   int total=list_source.Total();
   if(total==0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      CBuffer *obj=list_source.At(i);
      long obj1_prop=obj.GetProperty(property);
      min_obj=list_source.At(index);
      long obj2_prop=min_obj.GetProperty(property);
      if(CompareValues(obj1_prop,obj2_prop,LESS)) index=i;
     }
   return index;
  }
//+------------------------------------------------------------------+
//| Return the buffer index in the list                              |
//| with the minimum real property value                             |
//+------------------------------------------------------------------+
int CSelect::FindBufferMin(CArrayObj* list_source,ENUM_BUFFER_PROP_DOUBLE property)
  {
   int index=0;
   CBuffer *min_obj=NULL;
   int total=list_source.Total();
   if(total== 0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      CBuffer *obj=list_source.At(i);
      double obj1_prop=obj.GetProperty(property);
      min_obj=list_source.At(index);
      double obj2_prop=min_obj.GetProperty(property);
      if(CompareValues(obj1_prop,obj2_prop,LESS)) index=i;
     }
   return index;
  }
//+------------------------------------------------------------------+
//| Return the buffer index in the list                              |
//| with the minimum string property value                           |
//+------------------------------------------------------------------+
int CSelect::FindBufferMin(CArrayObj* list_source,ENUM_BUFFER_PROP_STRING property)
  {
   int index=0;
   CBuffer *min_obj=NULL;
   int total=list_source.Total();
   if(total==0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      CBuffer *obj=list_source.At(i);
      string obj1_prop=obj.GetProperty(property);
      min_obj=list_source.At(index);
      string obj2_prop=min_obj.GetProperty(property);
      if(CompareValues(obj1_prop,obj2_prop,LESS)) index=i;
     }
   return index;
  }
//+------------------------------------------------------------------+

Ich habe die Klasse CSelect ausführlich im dritten Artikel der Beschreibung der Bibliothekserstellung beschrieben.

Lassen Sie uns die Klassen des abstrakten Puffers und seiner Nachkommen etwas verbessern.

Da wir die Suche und Sortierung nach den Eigenschaften der Pufferobjekte durchführen, binden wir die Klassendatei CSelect in die abstrakte Pufferklassendatei ein \MQL5\Include\DoEasy\Objects\Indicators\Buffer.mqh:

//+------------------------------------------------------------------+
//|                                                       Buffer.mqh |
//|                        Copyright 2020, MetaQuotes Software Corp. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, MetaQuotes Software Corp."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
#property strict    // Necessary for mql4
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "..\..\Services\Select.mqh"
#include "..\..\Objects\BaseObj.mqh"
//+------------------------------------------------------------------+

Jetzt ist die Klasse CSelect in der Klasse CBuffer und allen ihren Nachkommen sichtbar.

Schreiben wir in den 'public' Bereich der Klasse die Methode, die den nutzerdefinierten Namen für das Pufferobjekt festlegt:

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;  }

//--- Default constructor
                     CBuffer(void){;}
protected:

Das Setzen eines nutzerdefinierten Namens für den Puffer ermöglicht es uns, den Puffer für die nachfolgende Suche in der Kollektionsliste mit seinem Namen zu benennen.

Wir haben sowohl die Methode, die den Pufferwert aus dem angegebenen Zeitreihenindex zurückgibt, als auch die Methode, die eine dem Puffer auf einem angegebenen Zeitreihenindex zugewiesene Farbe zurückgibt. Wir haben jedoch nicht die Methode, die den Index der für den Puffer im angegebenen Zeitreihenindex festgelegten Farbe zurückgibt. Tatsächlich setzen wir den Farbwert im Puffer an sich nicht. Stattdessen legen wir den Farbindex fest — den Wert, der die Seriennummer der Farbe (von denen, die dem Farbpuffer zugeordnet sind) angibt, die verwendet werden soll, um die Linie an der angegebenen Zeitserienposition zu zeichnen.

Bringen wir das in Ordnung. Deklarieren wir eine weitere Methode, die den für den Puffer gesetzten Farbindex an der angegebenen Zeitreihenposition zurückgibt, und wir ändern den Namen der Methode, die die Pufferfarbe an der angegebenen Zeitreihenposition zurückgibt, von GetColorBufferValue() in GetColorBufferValueColor():

//--- 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

Jetzt haben wir zwei Methoden, die mit der deklarierten Pufferfarbe arbeiten. Eine davon gibt die Farbe zurück, während die zweite den Farbindex zurückgibt.

Implementieren wir außerhalb des Klassenkörpers die Methode, die den Farbindex zurückgibt, und korrigieren die Implementierung der Methode, die die Pufferklasse zurückgibt:

//+------------------------------------------------------------------+
//| Return the color index value from the specified timeseries index |
//| of the specified color buffer array                              |
//+------------------------------------------------------------------+
int CBuffer::GetColorBufferValueIndex(const uint series_index) const
  {
   int data_total=this.GetDataTotal(0);
   if(data_total==0)
      return WRONG_VALUE;
   int data_index=((int)series_index<data_total ? (int)series_index : data_total-1);
   return(this.ColorsTotal()==1 ? 0 : (int)this.ColorBufferArray[data_index]);

  }
//+------------------------------------------------------------------+
//| Return the color value from the specified timeseries index       |
//| of the specified color buffer array                              |
//+------------------------------------------------------------------+
color CBuffer::GetColorBufferValueColor(const uint series_index) const
  {
   int data_total=this.GetDataTotal(0);
   if(data_total==0)
      return clrNONE;
   int color_index=this.GetColorBufferValueIndex(series_index);
   return(color_index>WRONG_VALUE ? (color)this.ArrayColors[color_index] : clrNONE);
  }
//+------------------------------------------------------------------+

Früher hatten wir nur eine Methode, während der Farbindex direkt darin erhalten wurde:

//+------------------------------------------------------------------+
//| Return the value from the specified timeseries index             |
//| of the specified color buffer array                              |
//+------------------------------------------------------------------+
color CBuffer::GetColorBufferValue(const uint series_index) const
  {
   int data_total=this.GetDataTotal(0);
   if(data_total==0)
      return clrNONE;
   int data_index=((int)series_index<data_total ? (int)series_index : data_total-1);
   int color_index=(this.ColorsTotal()==1 ? 0 : (int)this.ColorBufferArray[data_index]);
   return (color)this.ArrayColors[color_index];
  }
//+------------------------------------------------------------------+

Nun wurde diese Berechnung in die separate Methode GetColorBufferValueIndex() verschoben, während der Aufruf der neuen Methode nun für die Rückgabe der Balkenfarben statt der Indexberechnung verwendet wird.

Bei der Erstellung der Objektklasse der Pfeilpuffer, die (wie die übrigen Pufferklassen) vom abstrakten Puffers abgeleitet ist, habe ich eine Verbesserung vorgenommen — die virtuellen Methoden zum Setzen und Verschieben der Pfeilcodes wurden in den CBuffer-Klassen deklariert, aber ich habe vergessen, sie in der abgeleiteten Klasse zu implementieren. Bringen wir das in Ordnung.

Wir öffnen die Datei der Objektklasse der Pfeilpuffer \MQL5\Include\DoEasy\Objects\Indicators\BufferArrow.mqh und deklarieren diese Methoden. Fügen wir außerdem zwei weitere Methoden für das Setzen von und das Zurückgeben von Werten zu/von dem durch den Indikatorpuffer zugewiesenen Array hinzu:

//+------------------------------------------------------------------+
//| Buffer with the "Drawing with arrows" drawing style              |
//+------------------------------------------------------------------+
class CBufferArrow : public CBuffer
  {
private:

public:
//--- Constructor
                     CBufferArrow(const uint index_plot,const uint index_base_array) :
                        CBuffer(BUFFER_STATUS_ARROW,BUFFER_TYPE_DATA,index_plot,index_base_array,1,1,"Arrows") {}
//--- 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 (1) the arrow code, (2) vertical shift of arrows
   virtual void      SetArrowCode(const uchar code);
   virtual void      SetArrowShift(const int shift);
   
//--- 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);  }
   
  };
//+------------------------------------------------------------------+

Außerhalb des Klassenkörpers implementieren wir die Methoden zum Setzen und Verschieben der Pfeilcodes:

//+------------------------------------------------------------------+
//| Set the arrow code                                               |
//+------------------------------------------------------------------+
void CBufferArrow::SetArrowCode(const uchar code)
  {
   this.SetProperty(BUFFER_PROP_ARROW_CODE,code);
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_ARROW,code);
  }
//+------------------------------------------------------------------+
//| Set the vertical shift of the arrows                             |
//+------------------------------------------------------------------+
void CBufferArrow::SetArrowShift(const int shift)
  {
   this.SetProperty(BUFFER_PROP_ARROW_SHIFT,shift);
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_ARROW_SHIFT,shift);
  }
//+------------------------------------------------------------------+

Die Methoden schreiben den übergebenen Wert in die entsprechende Pufferobjekt-Eigenschaft und setzen diese Eigenschaft auf den gezeichneten Pufferobjekt-Puffer.

Fügen wir zwei Methoden zum Setzen/Rückgeben von Werten in/aus dem Puffer-Array des Pufferobjekts hinzu, um Objektdateien mit einem einzigen Balken für die Darstellung zu puffern: Linien (BufferLine.mqh), Abschnitte (BufferSection.mqh) und Histogramme von Null (BufferHistogram.mqh):

//--- 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);  }

Fügen Sie vier Methoden zum Setzen/Zurückgeben von Werten zu/von den Pufferobjekt-Datenpuffer-Arrays hinzu, um Objektdateien mit zwei Puffern für die Darstellung zu puffern: ein Histogramm auf zwei Daten-Arrays (BufferHistogram2.mqh), ein ZigZag (BufferZigZag.mqh) und das Füllen zwischen den beiden Daten-Arrays (BufferFilling.mqh):

//--- Set the value to the (1) zero and (2) the first data buffer array
   void              SetData0(const uint series_index,const double value)              { this.SetBufferValue(0,series_index,value);       }
   void              SetData1(const uint series_index,const double value)              { this.SetBufferValue(1,series_index,value);       }
//--- Return the value from the (1) zero and (2) the first data buffer array
   double            GetData0(const uint series_index)                           const { return this.GetDataBufferValue(0,series_index);  }
   double            GetData1(const uint series_index)                           const { return this.GetDataBufferValue(1,series_index);  }

Fügen wir acht Methoden zum Setzen/Zurückgeben von OHLC-Werten zu/von den Pufferobjekt-Datenpuffer-Arrays zu den Pufferobjektdateien mit vier Puffern für die Darstellung hinzu: Balken (BufferBars.mqh) und Kerzen (BufferCandlts.mqh):

//--- Set (1) Open, (2) High, (3) Low and (4) Close values to the appropriate data buffer array
   void              SetDataOpen(const uint series_index,const double value)           { this.SetBufferValue(0,series_index,value);       }
   void              SetDataHigh(const uint series_index,const double value)           { this.SetBufferValue(1,series_index,value);       }
   void              SetDataLow(const uint series_index,const double value)            { this.SetBufferValue(2,series_index,value);       }
   void              SetDataClose(const uint series_index,const double value)          { this.SetBufferValue(3,series_index,value);       }
//--- Return (1) Open, (2) High, (3) Low and (4) Close value from the appropriate data buffer array
   double            GetDataOpen(const uint series_index)                        const { return this.GetDataBufferValue(0,series_index);  }
   double            GetDataHigh(const uint series_index)                        const { return this.GetDataBufferValue(1,series_index);  }
   double            GetDataLow(const uint series_index)                         const { return this.GetDataBufferValue(2,series_index);  }
   double            GetDataClose(const uint series_index)                       const { return this.GetDataBufferValue(3,series_index);  }

Natürlich können wir ohne die bereits geschriebenen Methoden der grundlegenden abstrakten Pufferobjekte SetBufferValue() und GetBufferValue() auskommen, aber diese Methoden erfordern die Angabe der notwendigen Puffernummer. Ich tue mein Bestes, um die Arbeit eines Endbenutzers zu vereinfachen. Daher werde ich die Möglichkeit implementieren, eine anzuwendende Methode zu wählen.

Jetzt ist alles bereit für die Entwicklung der Kollektionsklasse der Objekte von Indikatorpuffer.

Diese Klasse soll die Liste aller erstellten Pufferobjekte enthalten und die Möglichkeit bieten, jeden beliebigen Puffer für die Arbeit mit ihm im Programm zu erstellen und zu erhalten. Im Gegensatz zu den bisherigen Sammelklassen brauchen wir hier nicht zu prüfen, ob das gleiche Objekt mit den gleichen Eigenschaften vorhanden ist. Stattdessen sind wir in der Lage, die gleichen Puffer für die Visualisierung verschiedener Ereignisse zu verwenden.

Kollektionsklasse der Pufferobjekte

In \MQL5\Include\DoEasy\Collections\ erstellen wir die Datei BuffersCollection.mqh mit der Basisklasse CObject der Standardbibliothek. Wir binden sofort die Klassendateien der Bibliotheksgrundliste und Pufferobjekte darin ein:

//+------------------------------------------------------------------+
//|                                            BuffersCollection.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 "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"
//+------------------------------------------------------------------+
//| Collection of indicator buffers                                  |
//+------------------------------------------------------------------+
class CBuffersCollection : public CObject
  {

Füllen wir den Klassenkörper mit dem notwendigen Inhalt (glücklicherweise gibt es nicht viel davon) und betrachten wir seinen Zweck:

//+------------------------------------------------------------------+
//| Collection of indicator buffers                                  |
//+------------------------------------------------------------------+
class CBuffersCollection : public CObject
  {
private:
   CListObj                m_list;                       // Buffer object list
   
//--- Return the index of the next (1) drawn and (2) basic buffer
   int                     GetIndexNextPlot(void);
   int                     GetIndexNextBase(void);
//--- Create a new buffer object and place it to the collection list
   bool                    CreateBuffer(ENUM_BUFFER_STATUS status);
   
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                     PlotsTotal(void);
   int                     BuffersTotal(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",
   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);    }
   
//--- Return the buffer by the Plot index
   CBuffer                *GetBufferByPlot(const int plot_index);
//--- 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);
   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);
   
//--- Constructor
                           CBuffersCollection();
  };
//+------------------------------------------------------------------+

Also, m_list ist eine Liste, in der wir alle erzeugten Pufferobjekte hinzufügen und speichern. Sie ist abgeleitet von der Klasse des dynamischen Arrays von Zeigern auf die CObject-Klasseninstanzen.

Die Methode 'private' GetIndexNextPlot(), die den Index des nächsten, gezeichneten Puffers zurückgibt, ist notwendig, um den Index des nächsten erstellten Indikatorpuffers zu spezifizieren, während die Methode GetIndexNextBase() (ebenfalls 'private'), die den Index des nächsten Basispuffers zurückgibt, notwendig ist, um den Index des realen Arrays zu spezifizieren, der als Indikatorpuffer für ein neu erstelltes Pufferobjekt zugewiesen werden kann.

Lassen Sie mich das klarstellen. Beim Erstellen des Puffers für einen Indikator geben wir seine Nummer im Datenfenster an (Nummer des gezeichneten Puffers) und binden ihn an das Array vom Typ double (den Index des Arrays des Basispuffers). Warum "basic"? Der Zeichenpuffer ist in der Lage, mehrere Arrays zu verwenden. Nur das allererste als Indikator zugewiesene Array ist "basic". Andere Arrays, die zum Zeichnen verwendet werden, haben einen Index gleich "basic array "+N.

Bei drei farbigen Puffern, die auf zwei Arrays basieren, sehen also die Indizes der Arrays Zeichnen und basic wie folgt aus:

Der erste Puffer:

  • Zeichen-Puffer — Index 0
    • Basic-Array — Index 0
    • Der Index des zweiten Arrays ist 1
    • Farbarray hat Index 2

Der zweite Puffer:

  • Zeichen-Puffer — Index 1
    • Basic-Array — Index 3
    • Zweites Array — Index 4
    • Farbarray — Index 5

Der dritte Puffer:

  • Zeichen-Puffer — Index 2
    • Basic-Array — Index 6
    • Zweites Array — Index 7
    • Farbarray — Index 8

Wie wir sehen können, ist die Indizierung für den gezeichneten Puffer und das Basis-Array für jeden Puffer individuell. Bei mehreren Puffern, die im Indikator angewendet werden, können wir leicht verwirrt werden. Die Kollektionsklasse weist automatisch korrekte Indizes für gezeichnete Puffer und ihre Arrays zu. Das bedeutet, dass wir jederzeit von unserem Programm aus auf sie verweisen können.

Die Methode CreateBuffer() erzeugt einen neuen Puffer und platziert ihn in der Kollektionsliste.
Die Methoden GetObject() und GetList() geben entsprechend die Zeiger auf das Objekt der Kollektionsklasse und die Liste der Pufferobjekte der Kollektionsklasse zurück.
Die Methoden PlotsTotal() und BuffersTotal() geben die Anzahl der erstellten gezeichneten Puffer in der Kollektion und die Gesamtzahl der verwendeten Arrays zum Aufbau aller gezeichneten Puffer entsprechend zurück.

Die 'public' Methoden zum Erstellen von Pufferobjekten mit einem bestimmten Zeichenstil:

//--- 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",
   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);    }

Die Methoden geben das Ergebnis der privaten Methode zum Erstellen des CreateBuffer() Pufferobjekts Spezifizieren eines Zeichenstils des erstellten Puffers zurück.

Die Methode GetBufferByPlot() gibt den Zeiger auf den Puffer durch seinen gezeichneten Pufferindex zurück.

Die Methoden geben Zeiger auf Pufferobjekte anhand ihrer Seriennummer zurück:

//--- 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);
   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);

Sie geben ein Objekt mit einem bestimmten Zeichenstil anhand seiner Nummer in der Reihenfolge seiner Erstellung zurück.
Lassen Sie uns dies am folgenden Beispiel verdeutlichen:

Ich habe vier Pfeilpuffer BufferArrow() mit den gezeichneten Pufferindizes 0, 1, 2 und 3 erstellt.
Als Nächstes habe ich fünf Linienpuffer BufferLine() den gezeichneten Pufferindizes 4, 5, 6, 7 und 8 erstellt.

Jetzt müssen wir mit dem dritten Pfeilpuffer (mit dem Index 2) und dem vierten Zeilenpuffer (mit dem Index 7) arbeiten.
Um den Zeiger auf den dritten Pfeilpuffer zu erhalten, erhalten wir ihn einfach über seine Seriennummer (nicht über den Index). Die Nummer sollte ab Null gezählt werden. Wenn wir zum Beispiel den dritten Pfeilpuffer benötigen, sollten wir ihn wie folgt abrufen:

CBufferArrow *buffer_arrow=GetBufferArrow(2); // the third arrow buffer (0,1,2)

Der Zeiger auf den vierten Zeilenpuffer wird wie folgt ermittelt:

CBufferLine *buffer_line=GetBufferLine(3); // the fourth line buffer (0,1,2,3)


Betrachten wir nun die Implementierung aller deklarierten Methoden.

Der Klassenkonstruktor:

//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CBuffersCollection::CBuffersCollection()
  {
   this.m_list.Clear();
   this.m_list.Sort();
   this.m_list.Type(COLLECTION_BUFFERS_ID);
  }
//+------------------------------------------------------------------+

Löschen Sie die Liste, setzen Sie das Flag zum Sortieren der Liste für die Liste und setzen Sie das Flag der ID der Kollektionsliste des Puffers für den Typ der Kollektion.

Die Methode, die den Index des nächsten gezeichneten Puffers zurückgibt, und die Methode Rückgabe des Index des nächsten Basispuffers:

//+------------------------------------------------------------------+
//| Return the index of the next drawn buffer                        |
//+------------------------------------------------------------------+
int CBuffersCollection::GetIndexNextPlot(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)
      index=0;
//--- if the index is not -1,
   else
     {
      //--- 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 index following the Plot index of the buffer object
      index=buffer.IndexPlot()+1;
     }
//--- Return the index value
   return index;
  }
//+------------------------------------------------------------------+
//| Return the index of the next basic buffer                        |
//+------------------------------------------------------------------+
int CBuffersCollection::GetIndexNextBase(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 highest index of the next array that can be assigned as an indicator buffer,
//--- 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_NEXT);
   if(index==WRONG_VALUE)
      index=0;
//--- if the index is not -1,
   else
     {
      //--- 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 index of the next array from the buffer object properties
      index=buffer.IndexNextBuffer();
     }
//--- Return the index value
   return index;
  }
//+------------------------------------------------------------------+

Die Logik dieser beiden Methoden ist identisch. Ich habe sie in den Kommentaren zu den Codezeilen beschrieben.

Die Methode, bei der ein neues Pufferobjekt erstellt und in die Auflistung aufgenommen wird:

//+------------------------------------------------------------------+
//| Create a new buffer object and place it to the collection list   |
//+------------------------------------------------------------------+
bool CBuffersCollection::CreateBuffer(ENUM_BUFFER_STATUS status)
  {
//--- Get the drawn buffer index and the index used to assign the first buffer array as an indicator one
   int index_plot=this.GetIndexNextPlot();
   int index_base=this.GetIndexNextBase();
//--- If any of the indices is not received, return 'false'
   if(index_plot==WRONG_VALUE || index_base==WRONG_VALUE)
      return false;
//--- If the maximum possible number of indicator buffers has already been reached, inform about it and return 'false'
   if(this.m_list.Total()==IND_BUFFERS_MAX)
     {
      ::Print(CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_MAX_BUFFERS_REACHED));
      return false;
     }
//--- Create the buffer drawing style description
   string descript=::StringSubstr(::EnumToString(status),14);
//--- Declare the abstract buffer object
   CBuffer *buffer=NULL;
//--- Create a buffer object depending on the status passed to the method (drawing style)
   switch(status)
     {
      case BUFFER_STATUS_ARROW      : buffer=new CBufferArrow(index_plot,index_base);        break;
      case BUFFER_STATUS_LINE       : buffer=new CBufferLine(index_plot,index_base);         break;
      case BUFFER_STATUS_SECTION    : buffer=new CBufferSection(index_plot,index_base);      break;
      case BUFFER_STATUS_HISTOGRAM  : buffer=new CBufferHistogram(index_plot,index_base);    break;
      case BUFFER_STATUS_HISTOGRAM2 : buffer=new CBufferHistogram2(index_plot,index_base);   break;
      case BUFFER_STATUS_ZIGZAG     : buffer=new CBufferZigZag(index_plot,index_base);       break;
      case BUFFER_STATUS_FILLING    : buffer=new CBufferFilling(index_plot,index_base);      break;
      case BUFFER_STATUS_BARS       : buffer=new CBufferBars(index_plot,index_base);         break;
      case BUFFER_STATUS_CANDLES    : buffer=new CBufferCandles(index_plot,index_base);      break;
      default: break;
     }
//--- Failed to create a buffer, inform of that and return 'false'
   if(buffer==NULL)
     {
      ::Print(CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_BUFFER_OBJ)," ",descript);
      return false;
     }
//--- If failed to add a buffer object to the collection list for some reason,
//--- inform of that, remove the created buffer object and return 'false'
   if(!this.m_list.Add(buffer))
     {
      ::Print(CMessage::Text(MSG_LIB_SYS_FAILED_ADD_BUFFER));
      delete buffer;
      return false;
     }
//--- Set a name for the buffer object and return 'true'
   buffer.SetName("Buffer"+descript+"("+(string)buffer.IndexPlot()+")");
   return true;
  }
//+------------------------------------------------------------------+

Die Logik wird auch hier in den Kommentaren beschrieben. Beachten Sie, dass wir das abstrakte Pufferobjekt CBuffer deklarieren. Tatsächlich erstellen wir jedoch ein neues Objekt mit dem Zeichnungstyp, der durch den Status an die Methode übergeben wird (der Status beschreibt den Zeichenstil). Alle Pufferobjekte sind von dem abstrakten Pufferobjekt abgeleitet, daher ist eine solche Deklaration und Objekterstellung zulässig und einfach.

Die Methode gibt den Puffer über seinen Zeichen-Index zurück (über den Index im DataWindow):

//+------------------------------------------------------------------+
//| Return the buffer by the Plot index                              |
//+------------------------------------------------------------------+
CBuffer *CBuffersCollection::GetBufferByPlot(const int plot_index)
  {
   CArrayObj *list=CSelect::ByBufferProperty(this.GetList(),BUFFER_PROP_INDEX_PLOT,plot_index,EQUAL);
   return(list!=NULL && list.Total()==1 ? list.At(0) : NULL);
  }
//+------------------------------------------------------------------+

Die Klasse CSelect erlaubt es, die Liste zu erhalten, die nur ein Pufferobjekt mit dem angegebenen Index enthält (es wird nur ein Objekt in der Liste geben). Zurückgeben des Pufferobjekts (falls gefunden) aus der erhaltenen Liste. Wenn es kein solches Objekt in der Kollektionsliste gibt, wird NULL zurückgegeben.

Die Methoden, die Pufferobjekte eines bestimmten Typs zurückgeben:

//+------------------------------------------------------------------+
//| Return the "Drawing by arrows" buffer by a serial number         |
//| (0 - the very first arrow buffer, 1,2,N - subsequent ones)       |
//+------------------------------------------------------------------+
CBufferArrow *CBuffersCollection::GetBufferArrow(const int number)
  {
   CArrayObj *list=CSelect::ByBufferProperty(this.GetList(),BUFFER_PROP_STATUS,BUFFER_STATUS_ARROW,EQUAL);
   return(list!=NULL && list.Total()>0 ? list.At(number) : NULL);
  }
//+------------------------------------------------------------------+
//| Return the Line buffer by a serial number                        |
//| (0 - the very first line buffer, 1,2,N - subsequent ones)        |
//+------------------------------------------------------------------+
CBufferLine *CBuffersCollection::GetBufferLine(const int number)
  {
   CArrayObj *list=CSelect::ByBufferProperty(this.GetList(),BUFFER_PROP_STATUS,BUFFER_STATUS_LINE,EQUAL);
   return(list!=NULL && list.Total()>0 ? list.At(number) : NULL);
  }
//+------------------------------------------------------------------+
//| Return the Sections buffer by a serial number                    |
//| (0 - the very first sections buffer, 1,2,N - subsequent ones)    |
//+------------------------------------------------------------------+
CBufferSection *CBuffersCollection::GetBufferSection(const int number)
  {
   CArrayObj *list=CSelect::ByBufferProperty(this.GetList(),BUFFER_PROP_STATUS,BUFFER_STATUS_SECTION,EQUAL);
   return(list!=NULL && list.Total()>0 ? list.At(number) : NULL);
  }
//+------------------------------------------------------------------+
//| Return the "Histogram from the zero line" buffer by number       |
//| (0 - the very first buffer, 1,2,N - subsequent ones)             |
//+------------------------------------------------------------------+
CBufferHistogram *CBuffersCollection::GetBufferHistogram(const int number)
  {
   CArrayObj *list=CSelect::ByBufferProperty(this.GetList(),BUFFER_PROP_STATUS,BUFFER_STATUS_HISTOGRAM,EQUAL);
   return(list!=NULL && list.Total()>0 ? list.At(number) : NULL);
  }
//+------------------------------------------------------------------+
//| Return the "Histogram on two buffers" buffer by number           |
//| (0 - the very first buffer, 1,2,N - subsequent ones)             |
//+------------------------------------------------------------------+
CBufferHistogram2 *CBuffersCollection::GetBufferHistogram2(const int number)
  {
   CArrayObj *list=CSelect::ByBufferProperty(this.GetList(),BUFFER_PROP_STATUS,BUFFER_STATUS_HISTOGRAM2,EQUAL);
   return(list!=NULL && list.Total()>0 ? list.At(number) : NULL);
  }
//+------------------------------------------------------------------+
//| Return the ZigZag buffer by a serial number                      |
//| (0 - the very first zigzag buffer, 1,2,N - subsequent ones)      |
//+------------------------------------------------------------------+
CBufferZigZag *CBuffersCollection::GetBufferZigZag(const int number)
  {
   CArrayObj *list=CSelect::ByBufferProperty(this.GetList(),BUFFER_PROP_STATUS,BUFFER_STATUS_ZIGZAG,EQUAL);
   return(list!=NULL && list.Total()>0 ? list.At(number) : NULL);
  }
//+------------------------------------------------------------------+
//|Return the "Color filling between two levels" buffer by number    |
//| (0 - the very first filling buffer, 1,2,N - subsequent ones)     |
//+------------------------------------------------------------------+
CBufferFilling *CBuffersCollection::GetBufferFilling(const int number)
  {
   CArrayObj *list=CSelect::ByBufferProperty(this.GetList(),BUFFER_PROP_STATUS,BUFFER_STATUS_FILLING,EQUAL);
   return(list!=NULL && list.Total()>0 ? list.At(number) : NULL);
  }
//+------------------------------------------------------------------+
//| Return the "Display as bars" buffer by a serial number           |
//| (0 - the very first bar buffer, 1,2,N - subsequent ones)         |
//+------------------------------------------------------------------+
CBufferBars *CBuffersCollection::GetBufferBars(const int number)
  {
   CArrayObj *list=CSelect::ByBufferProperty(this.GetList(),BUFFER_PROP_STATUS,BUFFER_STATUS_BARS,EQUAL);
   return(list!=NULL && list.Total()>0 ? list.At(number) : NULL);
  }
//+------------------------------------------------------------------+
//|Return the "Display as candles" buffer by a serial number         |
//| (0 - the very first candle buffer, 1,2,N - subsequent ones)      |
//+------------------------------------------------------------------+
CBufferCandles *CBuffersCollection::GetBufferCandles(const int number)
  {
   CArrayObj *list=CSelect::ByBufferProperty(this.GetList(),BUFFER_PROP_STATUS,BUFFER_STATUS_CANDLES,EQUAL);
   return(list!=NULL && list.Total()>0 ? list.At(number) : NULL);
  }


Alle Methoden sind identisch, also besprechen wir nur eine von ihnen.
Wir rufen die Liste, die nur Pufferobjekte mit dem notwendigen Zeichenstil enthält, ab.
Nach dem Erhalt der Liste und wenn sie nicht leer ist, geben wir das Objekt aus der erhaltenen Liste durch einen angegebenen Index zurück.
In der Liste befinden sich die Objekte in aufsteigender Reihenfolge der Indizes, so dass die Indexanpassung nicht erforderlich ist.
Wenn der Index außerhalb der Liste liegt, gibt die Methode NULL zurück.
Wenn die Liste nicht erhalten wurde oder leer ist, ist die Rückgabe NULL.

Die Methoden, die die Anzahl der gezeichneten Puffer und die Anzahl aller Indikator-Arrays zurückgeben:

//+------------------------------------------------------------------+
//| Return the number of drawn buffers                               |
//+------------------------------------------------------------------+
int CBuffersCollection::PlotsTotal(void)
  {
   int index=CSelect::FindBufferMax(this.GetList(),BUFFER_PROP_INDEX_PLOT);
   CBuffer *buffer=this.m_list.At(index);
   return(buffer!=NULL ? buffer.IndexPlot()+1 : WRONG_VALUE);
  }
//+------------------------------------------------------------------+
//| Returns the number of all indicator arrays                       |
//+------------------------------------------------------------------+
int CBuffersCollection::BuffersTotal(void)
  {
   int index=CSelect::FindBufferMax(this.GetList(),BUFFER_PROP_INDEX_NEXT);
   CBuffer *buffer=this.m_list.At(index);
   return(buffer!=NULL ? buffer.IndexNextBuffer() : WRONG_VALUE);
  }
//+------------------------------------------------------------------+

Die Logik der Methoden ist ähnlich: Wir erhalten den Index mit dem höchsten Wert der notwendigen Eigenschaft und erhalten das Pufferobjekt aus der Kollektionsliste durch den erhaltenen Index. Wenn der Puffer erhalten wird, gibt sie die der Methode entsprechenden Eigenschaft zurück, andernfalls -1.

Damit ist die Entwicklung der Kollektionsklasse der Indikatorpuffer abgeschlossen.

Jetzt müssen wir den Zugriff auf Klassenmethoden für bibliotheksbasierte Programme ermöglichen. In meinem Fall geschieht dies in der Basisobjektklasse der Bibliothek CEngine.

Öffnen wir \MQL5\Include\DoEasy\Engine.mqh und nehmen dort die notwendigen Änderungen vor. Hier müssen wir lediglich die bereits erstellten Methoden der Kollektionsklasse der Indikatorpuffer duplizieren und der Einfachheit halber Hilfsmethoden hinzufügen.

Zuerst müssen wir die Klassendatei einbinden und das Objekt der Kollektionsklasse der Puffer deklarieren:

//+------------------------------------------------------------------+
//|                                                       Engine.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 "Services\TimerCounter.mqh"
#include "Collections\HistoryCollection.mqh"
#include "Collections\MarketCollection.mqh"
#include "Collections\EventsCollection.mqh"
#include "Collections\AccountsCollection.mqh"
#include "Collections\SymbolsCollection.mqh"
#include "Collections\ResourceCollection.mqh"
#include "Collections\TimeSeriesCollection.mqh"
#include "Collections\BuffersCollection.mqh"
#include "TradingControl.mqh"
//+------------------------------------------------------------------+
//| Library basis class                                              |
//+------------------------------------------------------------------+
class CEngine
  {
private:
   CHistoryCollection   m_history;                       // Collection of historical orders and deals
   CMarketCollection    m_market;                        // Collection of market orders and deals
   CEventsCollection    m_events;                        // Event collection
   CAccountsCollection  m_accounts;                      // Account collection
   CSymbolsCollection   m_symbols;                       // Symbol collection
   CTimeSeriesCollection m_time_series;                  // Timeseries collection
   CBuffersCollection   m_buffers;                       // Collection of indicator buffers
   CResourceCollection  m_resource;                      // Resource list
   CTradingControl      m_trading;                       // Trading management object
   CPause               m_pause;                         // Pause object
   CArrayObj            m_list_counters;                 // List of timer counters

Im 'public' Teil der Klasse schreiben wir die Methoden, die die gleichnamigen Methoden der Kollektionsklasse der Puffer aufrufen und die Ergebnisse zurückgeben, und deklarieren zusätzliche Methoden für die Arbeit mit der Kollektionsklasse der Puffer:

//--- 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, (2) the buffer list from the buffer collection and (3) the buffer by the Plot index
   CBuffersCollection  *GetBuffersCollection(void)                                     { return &this.m_buffers;                             }
   CArrayObj           *GetListBuffers(void)                                           { return this.m_buffers.GetList();                    }
   CBuffer             *GetBufferByPlot(const int plot_index)                          { return this.m_buffers.GetBufferByPlot(plot_index);  }
//--- 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);     }

//--- Return the number of (1) drawn buffers and (2) all indicator arrays
   int                  BufferPlotsTotal(void)                                         { return this.m_buffers.PlotsTotal();                 }
   int                  BuffersTotal(void)                                             { return this.m_buffers.BuffersTotal();               }

//--- 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",
   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();              }
   
//--- 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 and (4) histogram from zero
//--- (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);
   void                 BufferSetDataLine(const int number,const int series_index,const double value);
   void                 BufferSetDataSection(const int number,const int series_index,const double value);
   void                 BufferSetDataHistogram(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);
//--- 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);
//--- 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);
//--- 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);
//--- 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);
   
//--- 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                BufferColorArrow(const int number,const int series_index);
   color                BufferColorLine(const int number,const int series_index);
   color                BufferColorSection(const int number,const int series_index);
   color                BufferColorHistogram(const int number,const int series_index);
   color                BufferColorHistogram2(const int number,const int series_index);
   color                BufferColorZigZag(const int number,const int series_index);
   color                BufferColorFilling(const int number,const int series_index);
   color                BufferColorBars(const int number,const int series_index);
   color                BufferColorCandles(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                  BufferColorIndexArrow(const int number,const int series_index);
   int                  BufferColorIndexLine(const int number,const int series_index);
   int                  BufferColorIndexSection(const int number,const int series_index);
   int                  BufferColorIndexHistogram(const int number,const int series_index);
   int                  BufferColorIndexHistogram2(const int number,const int series_index);
   int                  BufferColorIndexZigZag(const int number,const int series_index);
   int                  BufferColorIndexFilling(const int number,const int series_index);
   int                  BufferColorIndexBars(const int number,const int series_index);
   int                  BufferColorIndexCandles(const int number,const int series_index);

//--- 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                 BufferSetColorIndexArrow(const int number,const int series_index,const int color_index);
   void                 BufferSetColorIndexLine(const int number,const int series_index,const int color_index);
   void                 BufferSetColorIndexSection(const int number,const int series_index,const int color_index);
   void                 BufferSetColorIndexHistogram(const int number,const int series_index,const int color_index);
   void                 BufferSetColorIndexHistogram2(const int number,const int series_index,const int color_index);
   void                 BufferSetColorIndexZigZag(const int number,const int series_index,const int color_index);
   void                 BufferSetColorIndexFilling(const int number,const int series_index,const int color_index);
   void                 BufferSetColorIndexBars(const int number,const int series_index,const int color_index);
   void                 BufferSetColorIndexCandles(const int number,const int series_index,const int color_index);
   
//--- Set the following for the trading classes:

Die Methoden, die spezifische Pufferdaten nach ihrem Zeichenstil und der Pufferseriennummer mit dem gleichen Zeichenstil zurückgeben:

//+------------------------------------------------------------------+
//| Return arrow buffer data by its serial number                    |
//| (0 - the very first arrow buffer, 1,2,N - subsequent ones)       |
//+------------------------------------------------------------------+
double CEngine::BufferDataArrow(const int number,const int series_index)
  {
   CBufferArrow *buff=this.m_buffers.GetBufferArrow(number);
   return(buff!=NULL ? buff.GetData(series_index) : EMPTY_VALUE);
  }
//+------------------------------------------------------------------+
//| Return line buffer data by its serial number                     |
//| (0 - the very first line buffer, 1,2,N - subsequent ones)        |
//+------------------------------------------------------------------+
double CEngine::BufferDataLine(const int number,const int series_index)
  {
   CBufferLine *buff=this.m_buffers.GetBufferLine(number);
   return(buff!=NULL ? buff.GetData(series_index) : EMPTY_VALUE);
  }
//+------------------------------------------------------------------+
//| Return section buffer data by its serial number                  |
//| (0 - the very first sections buffer, 1,2,N - subsequent ones)    |
//+------------------------------------------------------------------+
double CEngine::BufferDataSection(const int number,const int series_index)
  {
   CBufferSection *buff=this.m_buffers.GetBufferSection(number);
   return(buff!=NULL ? buff.GetData(series_index) : EMPTY_VALUE);
  }
//+------------------------------------------------------------------+
//| Return histogram buffer data from zero                           |
//| by its serial number                                             |
//| (0 - the very first buffer, 1,2,N - subsequent ones)             |
//+------------------------------------------------------------------+
double CEngine::BufferDataHistogram(const int number,const int series_index)
  {
   CBufferHistogram *buff=this.m_buffers.GetBufferHistogram(number);
   return(buff!=NULL ? buff.GetData(series_index) : EMPTY_VALUE);
  }
//+------------------------------------------------------------------+
//| Return histogram zero buffer data on two buffers                 |
//| by its serial number                                             |
//| (0 - the very first buffer, 1,2,N - subsequent ones)             |
//+------------------------------------------------------------------+
double CEngine::BufferDataHistogram20(const int number,const int series_index)
  {
   CBufferHistogram2 *buff=this.m_buffers.GetBufferHistogram2(number);
   return(buff!=NULL ? buff.GetData0(series_index) : EMPTY_VALUE);
  }
//+------------------------------------------------------------------+
//| Return histogram first buffer data on two buffers                |
//| by its serial number                                             |
//| (0 - the very first buffer, 1,2,N - subsequent ones)             |
//+------------------------------------------------------------------+
double CEngine::BufferDataHistogram21(const int number,const int series_index)
  {
   CBufferHistogram2 *buff=this.m_buffers.GetBufferHistogram2(number);
   return(buff!=NULL ? buff.GetData1(series_index) : EMPTY_VALUE);
  }
//+------------------------------------------------------------------+
//| Return zigzag zero buffer data                                   |
//| by its serial number                                             |
//| (0 - the very first zigzag buffer, 1,2,N - subsequent ones)      |
//+------------------------------------------------------------------+
double CEngine::BufferDataZigZag0(const int number,const int series_index)
  {
   CBufferZigZag *buff=this.m_buffers.GetBufferZigZag(number);
   return(buff!=NULL ? buff.GetData0(series_index) : EMPTY_VALUE);
  }
//+------------------------------------------------------------------+
//| Return zigzag first buffer data                                  |
//| by its serial number                                             |
//| (0 - the very first zigzag buffer, 1,2,N - subsequent ones)      |
//+------------------------------------------------------------------+
double CEngine::BufferDataZigZag1(const int number,const int series_index)
  {
   CBufferZigZag *buff=this.m_buffers.GetBufferZigZag(number);
   return(buff!=NULL ? buff.GetData1(series_index) : EMPTY_VALUE);
  }
//+------------------------------------------------------------------+
//| Return filling zero buffer data                                  |
//| by its serial number                                             |
//| (0 - the very first filling buffer, 1,2,N - subsequent ones)     |
//+------------------------------------------------------------------+
double CEngine::BufferDataFilling0(const int number,const int series_index)
  {
   CBufferFilling *buff=this.m_buffers.GetBufferFilling(number);
   return(buff!=NULL ? buff.GetData0(series_index) : EMPTY_VALUE);
  }
//+------------------------------------------------------------------+
//| Return filling first buffer data                                 |
//| by its serial number                                             |
//| (0 - the very first filling buffer, 1,2,N - subsequent ones)     |
//+------------------------------------------------------------------+
double CEngine::BufferDataFilling1(const int number,const int series_index)
  {
   CBufferFilling *buff=this.m_buffers.GetBufferFilling(number);
   return(buff!=NULL ? buff.GetData1(series_index) : EMPTY_VALUE);
  }
//+------------------------------------------------------------------+
//| Return Open data of the bar buffer by its serial number          |
//| (0 - the very first bar buffer, 1,2,N - subsequent ones)         |
//+------------------------------------------------------------------+
double CEngine::BufferDataBarsOpen(const int number,const int series_index)
  {
   CBufferBars *buff=this.m_buffers.GetBufferBars(number);
   return(buff!=NULL ? buff.GetDataOpen(series_index) : EMPTY_VALUE);
  }
//+------------------------------------------------------------------+
//| Return High data of the bar buffer by its serial number          |
//| (0 - the very first bar buffer, 1,2,N - subsequent ones)         |
//+------------------------------------------------------------------+
double CEngine::BufferDataBarsHigh(const int number,const int series_index)
  {
   CBufferBars *buff=this.m_buffers.GetBufferBars(number);
   return(buff!=NULL ? buff.GetDataHigh(series_index) : EMPTY_VALUE);
  }
//+------------------------------------------------------------------+
//| Return Low data of the bar buffer by its serial number           |
//| (0 - the very first bar buffer, 1,2,N - subsequent ones)         |
//+------------------------------------------------------------------+
double CEngine::BufferDataBarsLow(const int number,const int series_index)
  {
   CBufferBars *buff=this.m_buffers.GetBufferBars(number);
   return(buff!=NULL ? buff.GetDataLow(series_index) : EMPTY_VALUE);
  }
//+------------------------------------------------------------------+
//| Return Close data of the bar buffer by its serial number         |
//| (0 - the very first bar buffer, 1,2,N - subsequent ones)         |
//+------------------------------------------------------------------+
double CEngine::BufferDataBarsClose(const int number,const int series_index)
  {
   CBufferBars *buff=this.m_buffers.GetBufferBars(number);
   return(buff!=NULL ? buff.GetDataClose(series_index) : EMPTY_VALUE);
  }
//+------------------------------------------------------------------+
//| Return Open data of the candle buffer by its serial number       |
//| (0 - the very first candle buffer, 1,2,N - subsequent ones)      |
//+------------------------------------------------------------------+
double CEngine::BufferDataCandlesOpen(const int number,const int series_index)
  {
   CBufferCandles *buff=this.m_buffers.GetBufferCandles(number);
   return(buff!=NULL ? buff.GetDataOpen(series_index) : EMPTY_VALUE);
  }
//+------------------------------------------------------------------+
//| Return High data of the candle buffer by its serial number       |
//| (0 - the very first candle buffer, 1,2,N - subsequent ones)      |
//+------------------------------------------------------------------+
double CEngine::BufferDataCandlesHigh(const int number,const int series_index)
  {
   CBufferCandles *buff=this.m_buffers.GetBufferCandles(number);
   return(buff!=NULL ? buff.GetDataHigh(series_index) : EMPTY_VALUE);
  }
//+------------------------------------------------------------------+
//| Return Low data of the candle buffer by its serial number        |
//| (0 - the very first candle buffer, 1,2,N - subsequent ones)      |
//+------------------------------------------------------------------+
double CEngine::BufferDataCandlesLow(const int number,const int series_index)
  {
   CBufferCandles *buff=this.m_buffers.GetBufferCandles(number);
   return(buff!=NULL ? buff.GetDataLow(series_index) : EMPTY_VALUE);
  }
//+------------------------------------------------------------------+
//| Return Close data of the candle buffer by its serial number      |
//| (0 - the very first candle buffer, 1,2,N - subsequent ones)      |
//+------------------------------------------------------------------+
double CEngine::BufferDataCandlesClose(const int number,const int series_index)
  {
   CBufferCandles *buff=this.m_buffers.GetBufferCandles(number);
   return(buff!=NULL ? buff.GetDataClose(series_index) : EMPTY_VALUE);
  }
//+------------------------------------------------------------------+

Alle Methoden sind identisch. Betrachten wir die Methode, die den Wert für den Puffer der Schlusskurse des Pufferobjekts mit dem Zeichenstil Kerzen zurückgibt.
Die Methode empfängt die Seriennummer des Puffers für den Kerzen-Stil aus allen erstellten Puffern des Kerzen-Stils (ich habe oben im Detail besprochen, was die Puffernummer mit einem bestimmten Zeichenstil bedeutet) und den Zeitreihenindex, aus dem die Daten des geschlossenen Kerzenpuffers bezogen werden sollten.

Unter Verwendung der Methode GetBufferCandles() der Kollektionsklasse der Puffer erhalten wir den Zeiger auf den erforderlichen Puffer und, wenn der Puffer empfangen wurde, geben wir Daten aus seinem Puffer der Schlusskurse mit dem angegebenen Zeitreihenindex zurück. Andernfalls wird der "Leerer Wert" zurückgegeben.

Die Methoden sind die Gegenstücke zu den oben beschriebenen. Sie setzen die Werte des angegebenen Zeitreihenindex für einen bestimmten Puffer des entsprechenden Pufferobjekts durch seinen Zeichenstil und seine Seriennummer:

//+------------------------------------------------------------------+
//| 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)
  {
   CBufferArrow *buff=this.m_buffers.GetBufferArrow(number);
   if(buff==NULL)
      return;
   buff.SetData(series_index,value);
  }
//+------------------------------------------------------------------+
//| 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)
  {
   CBufferLine *buff=this.m_buffers.GetBufferLine(number);
   if(buff==NULL)
      return;
   buff.SetData(series_index,value);
  }
//+------------------------------------------------------------------+
//| 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)
  {
   CBufferSection *buff=this.m_buffers.GetBufferSection(number);
   if(buff==NULL)
      return;
   buff.SetData(series_index,value);
  }
//+------------------------------------------------------------------+
//| 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)
  {
   CBufferHistogram *buff=this.m_buffers.GetBufferHistogram(number);
   if(buff==NULL)
      return;
   buff.SetData(series_index,value);
  }
//+------------------------------------------------------------------+
//| Set histogram zero buffer data on two buffers                    |
//| by its serial number                                             |
//| (0 - the very first buffer, 1,2,N - subsequent ones)             |
//+------------------------------------------------------------------+
void CEngine::BufferSetDataHistogram20(const int number,const int series_index,const double value)
  {
   CBufferHistogram2 *buff=this.m_buffers.GetBufferHistogram2(number);
   if(buff==NULL)
      return;
   buff.SetData0(series_index,value);
  }
//+------------------------------------------------------------------+
//| Set histogram first buffer data on two buffers                   |
//| by its serial number                                             |
//| (0 - the very first buffer, 1,2,N - subsequent ones)             |
//+------------------------------------------------------------------+
void CEngine::BufferSetDataHistogram21(const int number,const int series_index,const double value)
  {
   CBufferHistogram2 *buff=this.m_buffers.GetBufferHistogram2(number);
   if(buff==NULL)
      return;
   buff.SetData1(series_index,value);
  }
//+------------------------------------------------------------------+
//| 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 value0,const double value1)
  {
   CBufferHistogram2 *buff=this.m_buffers.GetBufferHistogram2(number);
   if(buff==NULL)
      return;
   buff.SetData0(series_index,value0);
   buff.SetData1(series_index,value1);
  }
//+------------------------------------------------------------------+
//| Set zigzag zero buffer data                                      |
//| by its serial number                                             |
//| (0 - the very first zigzag buffer, 1,2,N - subsequent ones)      |
//+------------------------------------------------------------------+
void CEngine::BufferSetDataZigZag0(const int number,const int series_index,const double value)
  {
   CBufferZigZag *buff=this.m_buffers.GetBufferZigZag(number);
   if(buff==NULL)
      return;
   buff.SetData0(series_index,value);
  }
//+------------------------------------------------------------------+
//| Set zigzag first buffer data                                     |
//| by its serial number                                             |
//| (0 - the very first zigzag buffer, 1,2,N - subsequent ones)      |
//+------------------------------------------------------------------+
void CEngine::BufferSetDataZigZag1(const int number,const int series_index,const double value)
  {
   CBufferZigZag *buff=this.m_buffers.GetBufferZigZag(number);
   if(buff==NULL)
      return;
   buff.SetData1(series_index,value);
  }
//+------------------------------------------------------------------+
//| 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 value0,const double value1)
  {
   CBufferZigZag *buff=this.m_buffers.GetBufferZigZag(number);
   if(buff==NULL)
      return;
   buff.SetData0(series_index,value0);
   buff.SetData1(series_index,value1);
  }
//+------------------------------------------------------------------+
//| Set filling zero buffer data                                     |
//| by its serial number                                             |
//| (0 - the very first filling buffer, 1,2,N - subsequent ones)     |
//+------------------------------------------------------------------+
void CEngine::BufferSetDataFilling0(const int number,const int series_index,const double value)
  {
   CBufferFilling *buff=this.m_buffers.GetBufferFilling(number);
   if(buff==NULL)
      return;
   buff.SetData0(series_index,value);
  }
//+------------------------------------------------------------------+
//| Set filling first buffer data                                    |
//| by its serial number                                             |
//| (0 - the very first filling buffer, 1,2,N - subsequent ones)     |
//+------------------------------------------------------------------+
void CEngine::BufferSetDataFilling1(const int number,const int series_index,const double value)
  {
   CBufferFilling *buff=this.m_buffers.GetBufferFilling(number);
   if(buff==NULL)
      return;
   buff.SetData1(series_index,value);
  }
//+------------------------------------------------------------------+
//| 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 value0,const double value1)
  {
   CBufferFilling *buff=this.m_buffers.GetBufferFilling(number);
   if(buff==NULL)
      return;
   buff.SetData0(series_index,value0);
   buff.SetData1(series_index,value1);
  }
//+------------------------------------------------------------------+
//| Set buffer data of Open bars                                     |
//| by its serial number                                             |
//| (0 - the very first bar buffer, 1,2,N - subsequent ones)         |
//+------------------------------------------------------------------+
void CEngine::BufferSetDataBarsOpen(const int number,const int series_index,const double value)
  {
   CBufferBars *buff=this.m_buffers.GetBufferBars(number);
   if(buff==NULL)
      return;
   buff.SetDataOpen(series_index,value);
  }
//+------------------------------------------------------------------+
//| Set buffer data of High bars                                     |
//| by its serial number                                             |
//| (0 - the very first bar buffer, 1,2,N - subsequent ones)         |
//+------------------------------------------------------------------+
void CEngine::BufferSetDataBarsHigh(const int number,const int series_index,const double value)
  {
   CBufferBars *buff=this.m_buffers.GetBufferBars(number);
   if(buff==NULL)
      return;
   buff.SetDataHigh(series_index,value);
  }
//+------------------------------------------------------------------+
//| Set buffer data of Low bars                                      |
//| by its serial number                                             |
//| (0 - the very first bar buffer, 1,2,N - subsequent ones)         |
//+------------------------------------------------------------------+
void CEngine::BufferSetDataBarsLow(const int number,const int series_index,const double value)
  {
   CBufferBars *buff=this.m_buffers.GetBufferBars(number);
   if(buff==NULL)
      return;
   buff.SetDataLow(series_index,value);
  }
//+------------------------------------------------------------------+
//| Set buffer data of Close bars                                    |
//| by its serial number                                             |
//| (0 - the very first bar buffer, 1,2,N - subsequent ones)         |
//+------------------------------------------------------------------+
void CEngine::BufferSetDataBarsClose(const int number,const int series_index,const double value)
  {
   CBufferBars *buff=this.m_buffers.GetBufferBars(number);
   if(buff==NULL)
      return;
   buff.SetDataClose(series_index,value);
  }
//+------------------------------------------------------------------+
//| Set buffer data of Open candles                                  |
//| by its serial number                                             |
//| (0 - the very first candle buffer, 1,2,N - subsequent ones)      |
//+------------------------------------------------------------------+
void CEngine::BufferSetDataCandlesOpen(const int number,const int series_index,const double value)
  {
   CBufferCandles *buff=this.m_buffers.GetBufferCandles(number);
   if(buff==NULL)
      return;
   buff.SetDataOpen(series_index,value);
  }
//+------------------------------------------------------------------+
//| Set buffer data of High candles                                  |
//| by its serial number                                             |
//| (0 - the very first candle buffer, 1,2,N - subsequent ones)      |
//+------------------------------------------------------------------+
void CEngine::BufferSetDataCandlesHigh(const int number,const int series_index,const double value)
  {
   CBufferCandles *buff=this.m_buffers.GetBufferCandles(number);
   if(buff==NULL)
      return;
   buff.SetDataHigh(series_index,value);
  }
//+------------------------------------------------------------------+
//| Set buffer data of Low candles                                   |
//| by its serial number                                             |
//| (0 - the very first candle buffer, 1,2,N - subsequent ones)      |
//+------------------------------------------------------------------+
void CEngine::BufferSetDataCandlesLow(const int number,const int series_index,const double value)
  {
   CBufferCandles *buff=this.m_buffers.GetBufferCandles(number);
   if(buff==NULL)
      return;
   buff.SetDataLow(series_index,value);
  }
//+------------------------------------------------------------------+
//| Set buffer data of Close candles                                 |
//| by its serial number                                             |
//| (0 - the very first candle buffer, 1,2,N - subsequent ones)      |
//+------------------------------------------------------------------+
void CEngine::BufferSetDataCandlesClose(const int number,const int series_index,const double value)
  {
   CBufferCandles *buff=this.m_buffers.GetBufferCandles(number);
   if(buff==NULL)
      return;
   buff.SetDataClose(series_index,value);
  }
//+------------------------------------------------------------------+

Alle Methoden sind identisch. Betrachten wir die Methode, die den Wert für den Puffer der Schlusskurse des Pufferobjekts mit dem Zeichenstil Kerzen setzt.
Unter Verwendung der Methode GetBufferCandles() der Kollektionsklasse der Puffer rufen wir das Pufferobjekt des Kerzenstils über deren Seriennummer ab.
Wenn das Objekt erhalten werden konnte, beenden wir die Methode. Wir setzen den Wert, der der Methode übergeben wurde, auf den Puffer der Schlusskurse des erhaltenen angeforderten Pufferobjekts durch den Zeitreihenindex.

Es gibt zwei weitere separate Methoden, die OHLC-Werte durch den angegebenen Zeitreihenindex auf alle Puffer von Bars und Candles-Pufferobjekten gleichzeitig setzen:

//+------------------------------------------------------------------+
//| 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)
  {
   CBufferBars *buff=this.m_buffers.GetBufferBars(number);
   if(buff==NULL)
      return;
   buff.SetDataOpen(series_index,open);
   buff.SetDataHigh(series_index,high);
   buff.SetDataLow(series_index,low);
   buff.SetDataClose(series_index,close);
  }
//+------------------------------------------------------------------+
//| 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)
  {
   CBufferCandles *buff=this.m_buffers.GetBufferCandles(number);
   if(buff==NULL)
      return;
   buff.SetDataOpen(series_index,open);
   buff.SetDataHigh(series_index,high);
   buff.SetDataLow(series_index,low);
   buff.SetDataClose(series_index,close);
  }
//+------------------------------------------------------------------+

Hier ist alles so wie oben beschrieben. Es werden jedoch alle Werte aller vier Puffer von Pufferobjekten an die Methoden übergeben und eingetragen.

Die Methoden geben die angegebene Farbe an den angegebenen Index der Farbpuffer-Zeitreihe eines bestimmten Pufferobjekts anhand seines Zeichenstils und seiner Seriennummer zurück:

//+------------------------------------------------------------------+
//| Return the arrow buffer color by its serial number               |
//| (0 - the very first arrow buffer, 1,2,N - subsequent ones)       |
//+------------------------------------------------------------------+
color CEngine::BufferColorArrow(const int number,const int series_index)
  {
   CBufferArrow *buff=this.m_buffers.GetBufferArrow(number);
   return(buff!=NULL ? buff.GetColorBufferValueColor(series_index) : clrNONE);
  }
//+------------------------------------------------------------------+
//| Return the line buffer color by its serial number                |
//| (0 - the very first candle line, 1,2,N - subsequent ones)        |
//+------------------------------------------------------------------+
color CEngine::BufferColorLine(const int number,const int series_index)
  {
   CBufferLine *buff=this.m_buffers.GetBufferLine(number);
   return(buff!=NULL ? buff.GetColorBufferValueColor(series_index) : clrNONE);
  }
//+------------------------------------------------------------------+
//| Return the section buffer color by its serial number             |
//| (0 - the very first sections buffer, 1,2,N - subsequent ones)    |
//+------------------------------------------------------------------+
color CEngine::BufferColorSection(const int number,const int series_index)
  {
   CBufferSection *buff=this.m_buffers.GetBufferSection(number);
   return(buff!=NULL ? buff.GetColorBufferValueColor(series_index) : clrNONE);
  }
//+------------------------------------------------------------------+
//| Return histogram buffer color from zero                          |
//| by its serial number                                             |
//| (0 - the very first buffer, 1,2,N - subsequent ones)             |
//+------------------------------------------------------------------+
color CEngine::BufferColorHistogram(const int number,const int series_index)
  {
   CBufferHistogram *buff=this.m_buffers.GetBufferHistogram(number);
   return(buff!=NULL ? buff.GetColorBufferValueColor(series_index) : clrNONE);
  }
//+------------------------------------------------------------------+
//| Return histogram buffer color on two buffers                     |
//| by its serial number                                             |
//| (0 - the very first buffer, 1,2,N - subsequent ones)             |
//+------------------------------------------------------------------+
color CEngine::BufferColorHistogram2(const int number,const int series_index)
  {
   CBufferHistogram2 *buff=this.m_buffers.GetBufferHistogram2(number);
   return(buff!=NULL ? buff.GetColorBufferValueColor(series_index) : clrNONE);
  }
//+------------------------------------------------------------------+
//| Return the zigzag buffer color by its serial number              |
//| (0 - the very first zigzag buffer, 1,2,N - subsequent ones)      |
//+------------------------------------------------------------------+
color CEngine::BufferColorZigZag(const int number,const int series_index)
  {
   CBufferZigZag *buff=this.m_buffers.GetBufferZigZag(number);
   return(buff!=NULL ? buff.GetColorBufferValueColor(series_index) : clrNONE);
  }
//+------------------------------------------------------------------+
//| Return the filling buffer color by its serial number             |
//| (0 - the very first filling buffer, 1,2,N - subsequent ones)     |
//+------------------------------------------------------------------+
color CEngine::BufferColorFilling(const int number,const int series_index)
  {
   CBufferFilling *buff=this.m_buffers.GetBufferFilling(number);
   return(buff!=NULL ? buff.GetColorBufferValueColor(series_index) : clrNONE);
  }
//+------------------------------------------------------------------+
//| Return the bar buffer color by its serial number                 |
//| (0 - the very first bar buffer, 1,2,N - subsequent ones)         |
//+------------------------------------------------------------------+
color CEngine::BufferColorBars(const int number,const int series_index)
  {
   CBufferBars *buff=this.m_buffers.GetBufferBars(number);
   return(buff!=NULL ? buff.GetColorBufferValueColor(series_index) : clrNONE);
  }
//+------------------------------------------------------------------+
//| Return the candle buffer color by its serial number              |
//| (0 - the very first candle buffer, 1,2,N - subsequent ones)      |
//+------------------------------------------------------------------+
color CEngine::BufferColorCandles(const int number,const int series_index)
  {
   CBufferCandles *buff=this.m_buffers.GetBufferCandles(number);
   return(buff!=NULL ? buff.GetColorBufferValueColor(series_index) : clrNONE);
  }
//+------------------------------------------------------------------+

Hier ist alles ähnlich: Wir erhalten das angeforderte Pufferobjekt durch seine Anzahl, wenn es gelungen ist, das Objekt zu erhalten, erhalten wir die in seinem Farbpuffer gesetzte Farbe durch den angegebenen Zeitreihenindex. Andernfalls wird "Farbe nicht gesetzt" zurückgegeben.

Da der Farbindexwert (und nicht der Farbwert) tatsächlich im Farbpuffer gesetzt wird, haben wir die entsprechenden Methoden, die den Farbindex eines bestimmten Pufferobjekts aus seinem Farbpuffer um den angegebenen Zeitreihenindex zurückgeben:

//+------------------------------------------------------------------+
//| Return the arrow buffer color index by its serial number         |
//| (0 - the very first arrow buffer, 1,2,N - subsequent ones)       |
//+------------------------------------------------------------------+
int CEngine::BufferColorIndexArrow(const int number,const int series_index)
  {
   CBufferArrow *buff=this.m_buffers.GetBufferArrow(number);
   return(buff!=NULL ? buff.GetColorBufferValueIndex(series_index) : WRONG_VALUE);
  }
//+------------------------------------------------------------------+
//| Return the line buffer color index by its serial number          |
//| (0 - the very first candle line, 1,2,N - subsequent ones)        |
//+------------------------------------------------------------------+
int CEngine::BufferColorIndexLine(const int number,const int series_index)
  {
   CBufferLine *buff=this.m_buffers.GetBufferLine(number);
   return(buff!=NULL ? buff.GetColorBufferValueIndex(series_index) : WRONG_VALUE);
  }
//+------------------------------------------------------------------+
//| Return the section buffer color index by its serial number       |
//| (0 - the very first sections buffer, 1,2,N - subsequent ones)    |
//+------------------------------------------------------------------+
int CEngine::BufferColorIndexSection(const int number,const int series_index)
  {
   CBufferSection *buff=this.m_buffers.GetBufferSection(number);
   return(buff!=NULL ? buff.GetColorBufferValueIndex(series_index) : WRONG_VALUE);
  }
//+------------------------------------------------------------------+
//| Return histogram buffer color index from zero                    |
//| by its serial number                                             |
//| (0 - the very first buffer, 1,2,N - subsequent ones)             |
//+------------------------------------------------------------------+
int CEngine::BufferColorIndexHistogram(const int number,const int series_index)
  {
   CBufferHistogram *buff=this.m_buffers.GetBufferHistogram(number);
   return(buff!=NULL ? buff.GetColorBufferValueIndex(series_index) : WRONG_VALUE);
  }
//+------------------------------------------------------------------+
//| Return histogram buffer color index on two buffers               |
//| by its serial number                                             |
//| (0 - the very first buffer, 1,2,N - subsequent ones)             |
//+------------------------------------------------------------------+
int CEngine::BufferColorIndexHistogram2(const int number,const int series_index)
  {
   CBufferHistogram2 *buff=this.m_buffers.GetBufferHistogram2(number);
   return(buff!=NULL ? buff.GetColorBufferValueIndex(series_index) : WRONG_VALUE);
  }
//+------------------------------------------------------------------+
//| Return the zigzag buffer color index by its serial number        |
//| (0 - the very first zigzag buffer, 1,2,N - subsequent ones)      |
//+------------------------------------------------------------------+
int CEngine::BufferColorIndexZigZag(const int number,const int series_index)
  {
   CBufferZigZag *buff=this.m_buffers.GetBufferZigZag(number);
   return(buff!=NULL ? buff.GetColorBufferValueIndex(series_index) : WRONG_VALUE);
  }
//+------------------------------------------------------------------+
//| Return the filling buffer color index by its serial number       |
//| (0 - the very first filling buffer, 1,2,N - subsequent ones)     |
//+------------------------------------------------------------------+
int CEngine::BufferColorIndexFilling(const int number,const int series_index)
  {
   CBufferFilling *buff=this.m_buffers.GetBufferFilling(number);
   return(buff!=NULL ? buff.GetColorBufferValueIndex(series_index) : WRONG_VALUE);
  }
//+------------------------------------------------------------------+
//| Return the bar buffer color index by its serial number           |
//| (0 - the very first bar buffer, 1,2,N - subsequent ones)         |
//+------------------------------------------------------------------+
int CEngine::BufferColorIndexBars(const int number,const int series_index)
  {
   CBufferBars *buff=this.m_buffers.GetBufferBars(number);
   return(buff!=NULL ? buff.GetColorBufferValueIndex(series_index) : WRONG_VALUE);
  }
//+------------------------------------------------------------------+
//| Return the candle buffer color index by its serial number        |
//| (0 - the very first candle buffer, 1,2,N - subsequent ones)      |
//+------------------------------------------------------------------+
int CEngine::BufferColorIndexCandles(const int number,const int series_index)
  {
   CBufferCandles *buff=this.m_buffers.GetBufferCandles(number);
   return(buff!=NULL ? buff.GetColorBufferValueIndex(series_index) : WRONG_VALUE);
  }
//+------------------------------------------------------------------+

Hier ist alles identisch mit den Methoden, die die Farbe zurückgeben, mit dem Unterschied, dass die Methoden den Farbindex zurückgeben.

Dies sind alle Verbesserungen der Klasse CEngine, die für die Prüfung der Kollektionsklasse von Indikatorpuffern erforderlich sind.


Testen der Fähigkeit, einen Indikator unter Verwendung der Kollektion der Puffer zu erstellen

Um die Kollektionsklasse der Puffer zu testen, werden wir den Indikator aus dem vorherigen Artikel verwenden und ihn im neuen Ordner \MQL5\Indikatoren\TestDoEasy\Part44\ als TestDoEasyPart44.mq5 speichern.

Die gesamte Kopfzeile sieht wie folgt aus:

//+------------------------------------------------------------------+
//|                                             TestDoEasyPart44.mq5 |
//|                        Copyright 2020, MetaQuotes Software Corp. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, MetaQuotes Software Corp."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
//--- includes
#include <DoEasy\Engine.mqh>
//--- properties
#property indicator_chart_window
#property indicator_buffers 28
#property indicator_plots   10

//--- classes

//--- enums

//--- defines

//--- structures

//--- input variables
/*sinput*/ ENUM_SYMBOLS_MODE  InpModeUsedSymbols=  SYMBOLS_MODE_CURRENT;            // Mode of used symbols list
/*sinput*/   string               InpUsedSymbols    =  "EURUSD,AUDUSD,EURAUD,EURGBP,EURCAD,EURJPY,EURUSD,GBPUSD,NZDUSD,USDCAD,USDJPY";  // List of used symbols (comma - separator)
/*sinput*/   ENUM_TIMEFRAMES_MODE InpModeUsedTFs    =  TIMEFRAMES_MODE_CURRENT;            // Mode of used timeframes list
/*sinput*/   string               InpUsedTFs        =  "M1,M5,M15,M30,H1,H4,D1,W1,MN1"; // List of used timeframes (comma - separator)
sinput   ENUM_INPUT_YES_NO    InpDrawArrow      =  INPUT_YES;  // Draw Arrow
sinput   ENUM_INPUT_YES_NO    InpDrawLine       =  INPUT_NO;   // Draw Line
sinput   ENUM_INPUT_YES_NO    InpDrawSection    =  INPUT_NO;   // Draw Section
sinput   ENUM_INPUT_YES_NO    InpDrawHistogram  =  INPUT_NO;   // Draw Histogram
sinput   ENUM_INPUT_YES_NO    InpDrawHistogram2 =  INPUT_NO;   // Draw Histogram2
sinput   ENUM_INPUT_YES_NO    InpDrawZigZag     =  INPUT_NO;   // Draw ZigZag
sinput   ENUM_INPUT_YES_NO    InpDrawFilling    =  INPUT_NO;   // Draw Filling
sinput   ENUM_INPUT_YES_NO    InpDrawBars       =  INPUT_NO;   // Draw Bars
sinput   ENUM_INPUT_YES_NO    InpDrawCandles    =  INPUT_YES;  // Draw Candles
 
sinput   bool                 InpUseSounds      =  true; // Use sounds
//--- indicator buffers
CArrayObj     *list_buffers;                    // Pointer to the buffer object list
//--- global variables
CEngine        engine;                          // CEngine library main object
string         prefix;                          // Prefix of graphical object names
int            min_bars;                        // The minimum number of bars for the indicator calculation
int            used_symbols_mode;               // Mode of working with symbols
string         array_used_symbols[];            // The array for passing used symbols to the library
string         array_used_periods[];            // The array for passing used timeframes to the library
//+------------------------------------------------------------------+

Entfernen wir die Einbindung aller Pufferobjektdateien aus dem 'includes'-Block — sie sind bereits in die Bibliothek eingebunden:

//--- includes
#include <DoEasy\Engine.mqh>
#include <DoEasy\Objects\Indicators\BufferArrow.mqh>        // 1 construction buffer + 1 color buffer
#include <DoEasy\Objects\Indicators\BufferLine.mqh>         // 1 construction buffer + 1 color buffer
#include <DoEasy\Objects\Indicators\BufferSection.mqh>      // 1 construction buffer + 1 color buffer
#include <DoEasy\Objects\Indicators\BufferHistogram.mqh>    // 1 construction buffer + 1 color buffer
#include <DoEasy\Objects\Indicators\BufferHistogram2.mqh>   // 2 construction buffers + 1 color buffer
#include <DoEasy\Objects\Indicators\BufferZigZag.mqh>       // 2 construction buffers + 1 color buffer
#include <DoEasy\Objects\Indicators\BufferFilling.mqh>      // 2 construction buffers + 1 color buffer
#include <DoEasy\Objects\Indicators\BufferBars.mqh>         // 4 construction buffer + 1 color buffer
#include <DoEasy\Objects\Indicators\BufferCandles.mqh>      // 4 construction buffer + 1 color buffer
//--- In total: 18 construction buffers + 9 color buffers:       27 indicator buffers, of which 9 are drawn ones
//--- properties

Wir legen die Anzahl der Zeichen- und Indikatorpuffer für den Compiler fest:

#property indicator_buffers 28
#property indicator_plots   10

Wenn in dem Indikator mehrere verschiedene Puffer erwartet werden, können wir in der Anfangsphase einfach beliebige Werte eintragen, anstatt "mit den Fingern zu rechnen". Wenn wir den Indikator zum ersten Mal starten, erhalten wir Warnmeldungen mit der gültigen Anzahl der Zeichen- und der Werte-Indikator-Puffer, falls wir deren Anzahl falsch angegeben haben.

Die Bibliothek setzt den Zugriff auf die erstellten Indikatorpuffer durch ihre Zeichenstile und Nummern in der Reihenfolge ihrer Erstellung voraus. Der Zugriff auf die Puffereigenschaften direkt aus dem Programm heraus ist noch nicht implementiert. Wir können bisher nur auf die Puffer-Arrays zugreifen. Heute werde ich diese Einschränkung umgehen, indem ich die Liste der Pufferobjekte aus der Sammlung erhalte und direkt von der Liste aus auf die Puffer zugreife (eine solche Funktion ist für jede Objektsammlung vorhanden), um direkt mit den erstellten Objekten zu arbeiten. Lassen Sie uns daher das dynamische Array von Zeigern auf CObjektobjekte als "Indikatorpuffer" verwenden.


Bereiten wir in OnInit() die für den Test benötigten Puffer vor und prüfen, ob auf sie auf zwei verschiedene Arten zugegriffen werden kann:

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Initialize DoEasy library
   OnInitDoEasy();

//--- Set indicator global variables
   prefix=engine.Name()+"_";
   //--- Get the index of the maximum used timeframe in the array,
   //--- calculate the number of bars of the current period fitting in the maximum used period
   //--- Use the obtained value if it exceeds 2, otherwise use 2
   int index=ArrayMaximum(ArrayUsedTimeframes);
   int num_bars=NumberBarsInTimeframe(ArrayUsedTimeframes[index]);
   min_bars=(index>WRONG_VALUE ? (num_bars>2 ? num_bars : 2) : 2);

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

//--- Create the button panel

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

//--- indicator buffers mapping
//--- Create all the necessary buffer objects
   engine.BufferCreateArrow();
   engine.BufferCreateLine();
   engine.BufferCreateSection();
   engine.BufferCreateHistogram();
   engine.BufferCreateHistogram2();
   engine.BufferCreateZigZag();
   engine.BufferCreateFilling();
   engine.BufferCreateBars();
   engine.BufferCreateCandles();
   engine.BufferCreateArrow();
   
//--- Check the number of buffers specified in the 'properties' block
   if(engine.BufferPlotsTotal()!=indicator_plots)
      Alert(TextByLanguage("Внимание! Значение \"indicator_plots\" должно быть ","Attention! Value of \"indicator_plots\" should be "),engine.BufferPlotsTotal());
   if(engine.BuffersTotal()!=indicator_buffers)
      Alert(TextByLanguage("Внимание! Значение \"indicator_buffers\" должно быть ","Attention! Value of \"indicator_buffers\" should be "),engine.BuffersTotal());
      
//--- Create color array
   color array_colors[]={clrDodgerBlue,clrRed,clrGray};
//--- Get the pointer to the collection list of buffer objects and
//--- set non-default color values for buffers in a loop according to the list
   list_buffers=engine.GetListBuffers();
   for(int i=0;i<list_buffers.Total();i++)
     {
      CBuffer *buff=list_buffers.At(i);
      buff.SetColors(array_colors);
      //--- Print data on the next buffer
      buff.Print();
     }
//--- 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);
     }
//--- Get the second arrow buffer (created last).
//--- The first arrow buffer has the number of 0, while the second one has the number of 1
//--- Set the arrow size of 2 and the code of 161
   CBuffer *buff=engine.GetBufferArrow(1);
   if(buff!=NULL)
     {
      buff.SetWidth(2);
      buff.SetArrowCode(161);
     }
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+

Jetzt ist das Erstellen der Puffer einfach geworden — wir verwenden die Bibliotheksmethoden, die entwickelt wurden, um einen Indikatorpuffer des einen oder anderen Typs zu erstellen. Die Bibliothek übernimmt alle Array-Funktionen, und wir sind in der Lage, die Eigenschaften der erstellten Puffer zu ändern, nachdem sie erstellt wurden. Lassen Sie uns die angegebene Anzahl von Puffern in #property im Indikator-Kopf sofort überprüfen. Wenn wir bei der Angabe der Anzahl der Puffer einen Fehler gemacht haben, wird eine entsprechende Warnung angezeigt. Eine solche Prüfung ist praktisch bei der Entwicklung eines Indikators. Danach kann sie aus dem Code entfernt werden.

Verwenden wir zwei Methoden, um den Zugriff auf den Puffer zu testen:
Zunächst holen wir uns den Zugriff auf den ZigZag-Puffer durch dessen Zeichenpuffer-Index unter Verwendung der Methode GetBufferByPlot(), in der der Zeichenpuffer-Index angegeben werden soll (hier ist es der Index 5 für den ZigZag-Puffer),
dann den Zugriff auf den allerletzten Pfeilpuffer, der ganz am Ende erstellt wurde. Es handelt sich um den zweiten Pfeilpuffer. Greifen wir auf ihn mit der Methode GetBufferArrow() zu. Der Methode übergeben wir die Seriennummer des erforderlichen Pfeilpuffers an (hier ist die Zahl 1, da die Zählung bei Null beginnt)

OnCalculate() bleibt nahezu unverändert, außer dass die Daten in den Kerzen- und Balkenpuffern mit den Objektmethoden des Kerzenpuffers gesetzt werden (Schreiben der Daten nacheinander in Open, High, Low und Close). Wir schreiben außerdem alle OHLC-Werte auf einmal in die Balkenpuffer-Arrays. Auf diese Weise sind wir in der Lage, die Arbeit aller erstellten Methoden zur Arbeit mit Pufferobjekten zu überprüfen:

//--- Calculate the indicator
   for(int i=limit; i>WRONG_VALUE && !IsStopped(); i--)
     {
      //--- In a loop by the number of buffers in the list
      for(int j=0;j<total;j++)
        {
         //--- get the next buffer and
         CBuffer *buff=list_buffers.At(j);
         //--- clear its current data
         buff.ClearData(0);
         //--- If drawing is not used, move on to the next one
         if(!IsUse(buff.Status()))
            continue;
         //--- Depending on the number of buffers, fill them with price data
         //--- one buffer
         if(buff.BuffersTotal()==1)
            buff.SetBufferValue(0,i,close[i]);
         //--- two buffers - the first one is to store the bar open price, while the second one is to store the bar close price
         else if(buff.BuffersTotal()==2)
           {
            buff.SetBufferValue(0,i,open[i]);
            buff.SetBufferValue(1,i,close[i]);
           }
         //--- four buffers - each buffer is to store OHLC bar prices
         else if(buff.BuffersTotal()==4)
           {
            //--- If this is the candle buffer
            if(buff.Status()==BUFFER_STATUS_CANDLES)
              {
               //--- create the pointer to the candle buffer object by assigning the pointer to the abstract buffer object to it
               //--- and write the appropriate timeseries data to the "candle" object buffers
               CBufferCandles *candle=buff;
               candle.SetDataOpen(i,open[i]);
               candle.SetDataHigh(i,high[i]);
               candle.SetDataLow(i,low[i]);
               candle.SetDataClose(i,close[i]);
              }
            //--- If this is the bar buffer, use access to the first (and only) created bar buffer object
            //--- and write the appropriate timeseries data to the "bars" object buffers in one go
            else
              {
               engine.BufferSetDataBars(0,i,open[i],high[i],low[i],close[i]);
              }
           }
         //--- Set the buffer color depending on the candle direction
         if(open[i]<close[i])
            buff.SetBufferColorIndex(i,0);
         else if(open[i]>close[i])
            buff.SetBufferColorIndex(i,1);
         else
            buff.SetBufferColorIndex(i,2);
        }
     }
//--- return value of prev_calculated for next call

Der vollständige Indikatorcode ist in den unten angehängten Dateien enthalten.

Kompilieren wir den Indikator und starten ihn auf dem Chart eines Symbols. Richten wir in den Einstellungen die Anzeige eines einzelnen Pfeilpuffers ein. Dieser Puffer soll im Chart als Punkte angezeigt werden. Wir haben aber auch den zweiten Pfeilpuffer, der zuletzt erstellt wurde. Wir haben auf den zweiten Pfeilpuffer in OnInit() zugegriffen, um seinen Code und die Symbolgröße zu ändern:

//--- Get the second arrow buffer (created last).
//--- The first arrow buffer has the number of 0, while the second one has the number of 1
//--- Set the arrow size of 2 and the code of 161
   CBuffer *buff=engine.GetBufferArrow(1);
   if(buff!=NULL)
     {
      buff.SetWidth(2);
      buff.SetArrowCode(161);
     }

Wenn es uns gelungen ist, das Objekt durch Zugriff auf einen bestimmten Puffertyp anhand seiner Nummer zu holen, dann sollten zwei Pfeilpuffer auf dem Chart angezeigt werden — der erste wird in Punkten, der zweite in Kreisen mit der Größe 2 dargestellt.

Wir haben die Linienbreite des ZigZag eingestellt, indem wir den ZigZag-Puffer mit der Methode erhalten, die das Pufferobjekt über seinen Zeichen-Index zurückgibt. Verbinden wir die Darstellung des ZigZag und stellen dabei sicher, dass die Linienbreite der in OnInit() eingestellten entspricht:

//--- 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);
     }

Zum Schluss wollen wir sehen, wie Balken und Kerzen dargestellt werden. Wenn die Methoden zum Schreiben von Preiswerten in die Puffer-Arrays funktionieren, dann sollten Balken und Kerzen im Chart korrekt angezeigt werden.

Prüfen wir es


Wie wir sehen können, funktioniert alles wie erwartet.


Was kommt als Nächstes?

Im nächsten Artikel werden wir die Entwicklung der Kollektionsklasse der Indikatorpuffer im Hinblick auf die Indikatorausführung im Multi-Symbol- und Multi-Zeitrahmen-Modus fortsetzen.

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


Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/7886

Beigefügte Dateien |
MQL5.zip (3735.87 KB)
Ein manuelles Chart- und Handelswerkzeug (Teil I). Vorbereitung: Strukturbeschreibung und die Hilfsklasse Ein manuelles Chart- und Handelswerkzeug (Teil I). Vorbereitung: Strukturbeschreibung und die Hilfsklasse
Dies ist der erste Artikel in einer Serie, in der ich ein Werkzeug beschreiben werde, das die manuelle Verwendung von Chartgrafiken mit Hilfe von Tastaturkürzeln ermöglicht. Es ist sehr praktisch: man drückt eine Taste und eine Trendlinie erscheint, drückt man eine andere Taste — so entsteht ein Fibonacci-Fächer mit den benötigten Parametern. Es wird auch möglich sein, den Zeitrahmen zu wechseln, die Ebenen neu anzuordnen oder alle Objekte aus dem Diagramm zu löschen.
Nativer Twitter-Client: Teil 2 Nativer Twitter-Client: Teil 2
Ein als MQL-Klasse implementierter Twitter-Client, mit dem Sie Tweets mit Fotos versenden können. Alles, was Sie brauchen, ist eine einzige, in sich geschlossene Include-Datei und schon können Sie all Ihre wunderbaren Charts und Signale twittern.
Praktische Anwendung von neuronalen Netzen im Handel Praktische Anwendung von neuronalen Netzen im Handel
In diesem Artikel werden wir die Hauptaspekte der Integration von neuronalen Netzen und dem Handelsterminal betrachten, mit dem Ziel, einen voll ausgestatteten Handelsroboter zu schaffen.
Nativer Twitter-Client für MT4 und MT5 ohne DLL Nativer Twitter-Client für MT4 und MT5 ohne DLL
Wollten Sie schon immer auf Tweets zugreifen und/oder Ihre Handelssignale auf Twitter posten? Suchen Sie nicht mehr, diese fortlaufenden Artikelserien zeigen Ihnen, wie Sie es ohne die Verwendung einer DLL machen können. Genießen Sie die Reise der Implementierung der Tweeter-API mit MQL. In diesem ersten Teil werden wir dem glorreichen Weg der Authentifizierung und Autorisierung beim Zugriff auf die Twitter-API folgen.