Zeitreihen in der Bibliothek DoEasy (Teil 50): Verschieben der Standardindikatoren für mehrere Symbole und Perioden

Artyom Trishkin | 7 Dezember, 2020

Inhaltsverzeichnis


Konzept

Im Laufe mehrerer Artikel hintereinander haben wir Schritt für Schritt die Funktionsweise der Anzeige von Standardindikatoren auf dem aktuellen Symbol/Perioden-Chart im Multi-Symbol-Multi-Perioden-Indikator-Modus geschaffen. Allerdings können nicht alle Standardindikatoren in einem solchen Modus angezeigt werden; aber lassen Sie uns heute vom Thema der Umwandlung von Standardindikatoren in Multimodi etwas abschweifen.
Nein, wir werden nicht etwas anderes machen - alles wird im Rahmen desselben Themas sein, aber... Wenn wir versuchen, einen Mehrperioden-Indikator zu erstellen, dessen Linie auf dem aktuellen Symbol-Chart verschoben ist, werden wir scheitern. Der Grund dafür ist, dass wir bisher den Standardwert verwendet haben, der im Klassenkonstruktor eines Pufferobjekts als Verschiebung der Indikatorlinie eingestellt ist. Und er ist gleich Null. Und wenn wir einfach eine Verschiebung der Mehrperioden-Indikatorlinie als äquivalent zum Wert einer Verschiebung der Standardindikatorlinie festlegen, werden wir ebenfalls scheitern: die Linie wird sich verschieben, aber nur innerhalb der Balken, für die die Standardindikatorlinie verschoben ist.
Ursache: Für die korrekte Darstellung einer mehrperiodigen Indikatorlinie muss berechnet werden, wie viele Balken des aktuellen Charts in einem Balken der Chartperiode enthalten sind, in der der Standardindikator berechnet wird, und um wie viele Balken die Linie verschoben wird. Mit anderen Worten, wenn ein Standardindikator im H4-Chart berechnet wird und wir ihn im H1-Chart anzeigen, müssen wir einen H4-Balken auf vier H1-Balken aufteilen. Dasselbe gilt für die Verschiebung: Wenn die Linie des Standardindikators um einen Balken im H4-Chart verschoben wird, müssen wir sie um vier Balken im H1-Chart verschieben.

Und dies ist noch nicht alles. Ebenso müssen wir beim Ausfüllen des berechneten Pufferfeldes eine Verschiebung der Standardindikatorlinie im Pufferobjekt berücksichtigen; und diese Verschiebung muss den anfänglichen Bezugspunkt einer Reihe von Balken angeben, die wir vom Quellfeld des Standardindikators in das Empfängerfeld des von uns erstellten Pufferobjekts kopieren.

Nach der Implementierung und bei Einhaltung der oben genannten Regeln werden wir in der Lage sein, den Standardindikator im Mehrperiodenmodus mit Zeilenverschiebung korrekt darzustellen.

Lassen Sie mich darauf hinweisen, dass sich nicht alle Standardindikatoren in einen Mehrperiodenmodus mit Linienverschiebung umrechnen lassen. Vorerst versäumen wir es, diejenigen Indikatoren im Mehrperiodenmodus darzustellen, deren Linien nach der Idee der Autoren ursprünglich mit einer Verschiebung gezeichnet wurden (Gator-Oszillator und Ichimoku Kinko Hyo). Dagegen konnte der Indikator Alligator leicht umgewandelt werden. Ich denke, dies ist darauf zurückzuführen, dass beim Indikator Alligator jede gezeichnete Linie mit einer eigenen Verschiebung gesetzt wird. Wir zeigen sie mit berechneter Verschiebung an, und alles wird korrekt angezeigt, und Gator- und Ichimoku-Indikatoren verwenden für ihre Berechnung ursprünglich verschobene Linien. In jedem Fall werden wir die Ursache finden und diese Indikatoren als Mehrperioden- und Mehrsymbol-Indikatoren erstellen. Und in der Zwischenzeit, um nicht auf der Suche nach den Ursachen zu verweilen (ich werde es gleichzeitig mit der Entwicklung tun) - werde ich weitere Bibliotheksfunktionalität schaffen. Je mehr, desto interessanter wird es sehr bald werden - Aufnahme von Standardindikatordaten in Balkenobjekte der Zeitreihenklasse. Dies wird es ermöglichen, schnell beliebige Kombinationen beliebiger Indikatoren bei jedem Chart-Symbol/Zeitraum zu suchen.

Neben der Implementierung der Anzeige von Standardindikatoren mit Linienverschiebung werden wir heute allgemeine Methoden zur Vorbereitung und Anzeige von Standardmethoden in Multimodi erstellen. Vorläufig sind für jeden Standardindikator eigene Darstellungsmethoden implementiert worden. Die schrittweise Schaffung von Mehrsymbol- und Mehrperiodenindikatoren lässt deutlich erkennen, was in allen einzelnen Methoden eigentlich völlig gleich ist. Heute werden wir etwas durchführen, was mit einer einzigen universellen Methode möglich ist, die mit jedem der Standardindikatoren arbeitet. Dies wird die Code-Größe der Kollektionsklasse der Indikatorpuffer erheblich vereinfachen und reduzieren.

Schließlich werden wir heute aus dem endgültigen Indikator die Berechnung und Einstellung der Niveaus der Standardindikatoren im Unterfenster und der Kapazität der Daten der Standardindikatorlinien, die im Terminaldatenfenster angezeigt werden, entfernen. Entfernen wir diese Berechnung in eine Datei mit Bibliotheksdienstfunktionen und rufen sie aus dem Testindikator auf. Dies vereinfacht den endgültigen Code und macht ihn auch visuell umfangreicher und erspart dem Endbenutzer der Bibliothek Routinearbeit.


Verbesserung der Bibliothek der Klasse

Um es zu ermöglichen, verschiedene Indikatoren mit der gleichen Methode zu verarbeiten, müssen wir in der Liste der Zeilennamen jedes einzelnen Indikators dafür sorgen, dass die numerischen Werte der Zeilen verschiedener Indikatoren übereinstimmen. Schließlich kann jede Linie eines beliebigen Indikators drei Typen zugeordnet werden:

  1. Obere Linie, auch genannt Hauptlinie oder Jaws,
  2. Untere Linie, auch genannt Signal eins oder Teeth oder +DI,
  3. Linie des Durchschnitts, auch genannt Lips oder -DI

Ichimoku-Indikator-Linien werden hier noch nicht berücksichtigt, aber dies wird in einer späteren Phase geschehen, wenn sie ebenfalls mehrperiodig gemacht werden.

Und wenn auf diese Weise alle Linien einander entsprechen, werden wir jede Indikatorlinie leicht nach der gleichen Methode behandeln, anstatt sie unterschiedlich zu gestalten.

Öffnen Sie \MQL5\Include\DoEasy\Defines.mqh und nehmen Sie die notwendigen Änderungen vor, um die Arten von Standard-Indikatorzeilen einzubeziehen:

//+------------------------------------------------------------------+
//| Indicator lines                                                  |
//+------------------------------------------------------------------+
enum ENUM_INDICATOR_LINE_MODE
  {
   INDICATOR_LINE_MODE_MAIN      =  0,                      // Main line
   INDICATOR_LINE_MODE_SIGNAL    =  1,                      // Signal line
   INDICATOR_LINE_MODE_UPPER     =  0,                      // Upper line
   INDICATOR_LINE_MODE_LOWER     =  1,                      // Lower line
   INDICATOR_LINE_MODE_MIDDLE    =  2,                      // Middle line
   INDICATOR_LINE_MODE_JAWS      =  0,                      // Jaws line
   INDICATOR_LINE_MODE_TEETH     =  1,                      // Teeth line
   INDICATOR_LINE_MODE_LIPS      =  2,                      // Lips line
   INDICATOR_LINE_MODE_DI_PLUS   =  1,                      // Line +DI
   INDICATOR_LINE_MODE_DI_MINUS  =  2,                      // Line -DI
  };
//+------------------------------------------------------------------+

Da wir Daten vom Standard-Indikator-Quellarray in ein berechnetes Pufferobjekt-Empfängerarray mit einer Verschiebung für die Indikatorlinie kopieren müssen, wird in \MQL5\Include\DoEasy\Objects\Indicators\Buffer.mqh der abstrakten Pufferobjektklasse eine Methode geändert, die eine Verschiebung der grafischen Konstruktion des Indikators setzt - jetzt wird geprüft, ob es sich um einen berechneten Puffer handelt; und in diesem Fall erfolgt die Rückkehr von der Methode - der Wert wird nicht gesetzt. Entfernen wir einfach die Prüfung aus der Methode:

//+------------------------------------------------------------------+
//| Set the indicator graphical construction shift                   |
//+------------------------------------------------------------------+
void CBuffer::SetShift(const int shift)
  {
   if(this.TypeBuffer()==BUFFER_TYPE_CALCULATE)
      return;
   this.SetProperty(BUFFER_PROP_SHIFT,shift);
   ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_SHIFT,shift);
  }
//+------------------------------------------------------------------+

Nun sieht die Methode wie folgt aus:

//+------------------------------------------------------------------+
//| Set the indicator graphical construction shift                   |
//+------------------------------------------------------------------+
void CBuffer::SetShift(const int shift)
  {
   this.SetProperty(BUFFER_PROP_SHIFT,shift);
   if(this.TypeBuffer()!=BUFFER_TYPE_CALCULATE)
      ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_SHIFT,shift);
  }
//+------------------------------------------------------------------+

Zuerst wird Verschiebungswert für einen beliebigen Puffer gesetzt, dann wenn der Puffer kein berechneter ist, wird für ihn eine Verschiebung der zu zeichnenden Linie gesetzt.

In \MQL5\Include\DoEasy\Objects\Indicators\BufferCalculate.mqh des berechneten Pufferobjekts müssen wir den anfänglichen Referenzpunkt (Balken) festlegen, von dem aus das Kopieren von Daten aus dem Standardindikatorpuffer beginnt. Dazu geben wir in der Datenkopiermethode des angegebenen Indikators zum Puffern des Objektarrays einfach den Wert für Datenkopierbeginn mit negativem Vorzeichen an:

//+------------------------------------------------------------------+
//| It copies data of the specified indicator to buffer object array |
//+------------------------------------------------------------------+
int CBufferCalculate::FillAsSeries(const int indicator_handle,const int buffer_num,const int start_pos,const int count)
  {
   return ::CopyBuffer(indicator_handle,buffer_num,-start_pos,count,this.DataBuffer[0].Array);
  }
//+------------------------------------------------------------------+

Es ist ganz einfach: Wir werden den Wert des Offset der Indikatorlinie als Anfangsbalken für das Kopieren von Daten in die Methode übergeben. Wenn die Verschiebung positiv ist, werden die Daten mit einer negativen Verschiebung kopiert, wenn die Verschiebung negativ ist, werden die Daten mit einer positiven Verschiebung kopiert. Auf diese Weise erreichen wir immer den richtigen Startpunkt der erforderlichen Daten des Quellarrays.

Jetzt verbessern wir in \MQL5\Include\DoEasy\Collections\BuffersCollection.mqh der Sammelklasse der Pufferobjekte alle Methoden zur Erstellung von Standardindikatoren, bei denen eine Verschiebung von Indikatorlinien möglich ist.

Nehmen wir als Beispiel die Methode zur Erstellung des Objekts des Standardindikators Alligator:

//+------------------------------------------------------------------+
//| Create multi-symbol multi-period Alligator                       |
//+------------------------------------------------------------------+
int CBuffersCollection::CreateAlligator(const string symbol,const ENUM_TIMEFRAMES timeframe,
                                        const int jaw_period,
                                        const int jaw_shift,
                                        const int teeth_period,
                                        const int teeth_shift,
                                        const int lips_period,
                                        const int lips_shift,
                                        const ENUM_MA_METHOD ma_method,
                                        const ENUM_APPLIED_PRICE applied_price,
                                        const int id=WRONG_VALUE)
  {
//--- Calculate and set the number of bars of line shift
   int num_bars=::PeriodSeconds(timeframe)/::PeriodSeconds(PERIOD_CURRENT);
   int shift_jaw=jaw_shift*num_bars;
   int shift_teeth=teeth_shift*num_bars;
   int shift_lips=lips_shift*num_bars;
//--- Create indicator handle and set default indicator
   int handle=::iAlligator(symbol,timeframe,jaw_period,shift_jaw,teeth_period,shift_teeth,lips_period,shift_lips,ma_method,applied_price);
   int identifier=(id==WRONG_VALUE ? IND_ALLIGATOR : id);
   color array_colors[1]={clrBlue};
   CBuffer *buff=NULL;
   if(handle!=INVALID_HANDLE)
     {
      //--- Create line buffer
      this.CreateLine();
      //--- Get the last created buffer object (drawn) and set to it all the necessary parameters of Jaws
      buff=this.GetLastCreateBuffer();
      if(buff==NULL)
         return INVALID_HANDLE;
      buff.SetSymbol(symbol);
      buff.SetTimeframe(timeframe);
      buff.SetShift(shift_jaw);
      buff.SetID(identifier);
      buff.SetIndicatorHandle(handle);
      buff.SetIndicatorType(IND_ALLIGATOR);
      buff.SetShowData(true);
      buff.SetLineMode(INDICATOR_LINE_MODE_JAWS);
      buff.SetIndicatorName("Alligator");
      buff.SetIndicatorShortName("Alligator("+symbol+","+TimeframeDescription(timeframe)+": "+(string)jaw_period+","+(string)teeth_period+","+(string)lips_period+")");
      buff.SetLabel("Jaws("+symbol+","+TimeframeDescription(timeframe)+": "+(string)jaw_period+")");
      buff.SetColors(array_colors);
      
      //--- Create line buffer
      this.CreateLine();
      //--- Get the last created buffer object (drawn) and set to it all the necessary parameters of Teeth
      buff=this.GetLastCreateBuffer();
      if(buff==NULL)
         return INVALID_HANDLE;
      buff.SetSymbol(symbol);
      buff.SetTimeframe(timeframe);
      buff.SetShift(shift_teeth);
      buff.SetID(identifier);
      buff.SetIndicatorHandle(handle);
      buff.SetIndicatorType(IND_ALLIGATOR);
      buff.SetShowData(true);
      buff.SetLineMode(INDICATOR_LINE_MODE_TEETH);
      buff.SetIndicatorName("Alligator");
      buff.SetIndicatorShortName("Alligator("+symbol+","+TimeframeDescription(timeframe)+": "+(string)jaw_period+","+(string)teeth_period+","+(string)lips_period+")");
      buff.SetLabel("Teeth("+symbol+","+TimeframeDescription(timeframe)+": "+(string)teeth_period+")");
      array_colors[0]=clrRed;
      buff.SetColors(array_colors);
      
      //--- Create line buffer
      this.CreateLine();
      //--- Get the last created buffer object (drawn) and set to it all the necessary parameters of Lips
      buff=this.GetLastCreateBuffer();
      if(buff==NULL)
         return INVALID_HANDLE;
      buff.SetSymbol(symbol);
      buff.SetTimeframe(timeframe);
      buff.SetShift(shift_lips);
      buff.SetID(identifier);
      buff.SetIndicatorHandle(handle);
      buff.SetIndicatorType(IND_ALLIGATOR);
      buff.SetShowData(true);
      buff.SetLineMode(INDICATOR_LINE_MODE_LIPS);
      buff.SetIndicatorName("Alligator");
      buff.SetIndicatorShortName("Alligator("+symbol+","+TimeframeDescription(timeframe)+": "+(string)jaw_period+","+(string)teeth_period+","+(string)lips_period+")");
      buff.SetLabel("Lips("+symbol+","+TimeframeDescription(timeframe)+": "+(string)lips_period+")");
      array_colors[0]=clrLime;
      buff.SetColors(array_colors);
      
      //--- Create calculated buffer of Jaws in which standard indicator data will be stored
      this.CreateCalculate();
      //--- Get the last created buffer object (calculated) and set to it all the necessary parameters of Jaws
      buff=this.GetLastCreateBuffer();
      if(buff==NULL)
         return INVALID_HANDLE;
      buff.SetSymbol(symbol);
      buff.SetTimeframe(timeframe);
      buff.SetShift(shift_jaw);
      buff.SetID(identifier);
      buff.SetIndicatorHandle(handle);
      buff.SetIndicatorType(IND_ALLIGATOR);
      buff.SetEmptyValue(EMPTY_VALUE);
      buff.SetLineMode(INDICATOR_LINE_MODE_JAWS);
      buff.SetIndicatorName("Alligator");
      buff.SetLabel("Jaws("+symbol+","+TimeframeDescription(timeframe)+": "+(string)jaw_period+")");
      
      //--- Create calculated buffer of Teeth in which standard indicator data will be stored
      this.CreateCalculate();
      //--- Get the last created buffer object (calculated) and set to it all the necessary parameters of Teeth
      buff=this.GetLastCreateBuffer();
      if(buff==NULL)
         return INVALID_HANDLE;
      buff.SetSymbol(symbol);
      buff.SetTimeframe(timeframe);
      buff.SetShift(shift_teeth);
      buff.SetID(identifier);
      buff.SetIndicatorHandle(handle);
      buff.SetIndicatorType(IND_ALLIGATOR);
      buff.SetEmptyValue(EMPTY_VALUE);
      buff.SetLineMode(INDICATOR_LINE_MODE_TEETH);
      buff.SetIndicatorName("Alligator");
      buff.SetLabel("Teeth("+symbol+","+TimeframeDescription(timeframe)+": "+(string)teeth_period+")");
      
      //--- Create calculated buffer of Lips in which standard indicator data will be stored
      this.CreateCalculate();
      //--- Get the last created buffer object (calculated) and set to it all the necessary parameters of Lips
      buff=this.GetLastCreateBuffer();
      if(buff==NULL)
         return INVALID_HANDLE;
      buff.SetSymbol(symbol);
      buff.SetTimeframe(timeframe);
      buff.SetShift(shift_lips);
      buff.SetID(identifier);
      buff.SetIndicatorHandle(handle);
      buff.SetIndicatorType(IND_ALLIGATOR);
      buff.SetEmptyValue(EMPTY_VALUE);
      buff.SetLineMode(INDICATOR_LINE_MODE_LIPS);
      buff.SetIndicatorName("Alligator");
      buff.SetLabel("Lips("+symbol+","+TimeframeDescription(timeframe)+": "+(string)lips_period+")");
     }
   return handle;
  }
//+------------------------------------------------------------------+

Lassen Sie uns analysieren, was wir hier haben.

Zunächst berechnen wir, wie viele Balken des aktuellen Chart in der Chart-Periode enthalten sind, für die der Indikator berechnet wird.
Und weiter, berechnen wir die Anzahl der Schiebebalken, indem wir (an die Methode übergeben) Verschiebungswerte von drei Linien des Indikators mit der berechneten Anzahl von Balken multiplizieren.
im Falle der Erstellung eines Indikator-Handles als Parameter, der die Anzahl der Schiebebalken jeder Linie angibt über berechnete Werte angeben anstelle derer, die der Methode übergeben wurden.
Um Pufferobjekte (sowohl gezeichnete als auch berechnete) zu erstellen, setzen wir Verschiebungswerte, die ganz am Anfang berechnet wurden - für jedes Paar von Puffern (gezeichnet-berechnet) setzen wir die Werte auf eine entsprechende Indikatorlinie (Jaws, Teeth and Lips).
Somit ist das Objekt des Standardindikators und seiner Puffer vollständig darauf vorbereitet, die Daten mit Indikatorlinienverschiebung zu zeichnen.
Die übrigen Aktionen, die in der Methode der Schaffung des Objekts des Alligator-Standardindikators festgelegt sind, wurden in früheren Artikeln bei der Beschreibung der Methoden der Schaffung von Objekten anderer Standardindikatoren berücksichtigt.

Die oben beschriebenen Änderungen und Ergänzungen für die Berechnung der Verschiebung der Linien des Indikators sind bereits in alle Methoden der Bildung der Standardindikatoren geschrieben, die den Parameter haben, der die Anzahl der Balken der Linienverschiebung bestimmt:
CreateAlligator()
, CreateAMA(), CreateBands(), CreateDEMA(), CreateEnvelopes(), CreateFrAMA(), CreateMA(), CreateStdDev(), CreateTEMA() und CreateVIDYA().

Die Änderungen in jeder Methode sind praktisch identisch mit den oben betrachteten und wir werden sie hier nicht diskutieren.
Den vollständigen Code der Sammelklasse der Pufferobjekte finden Sie in den Anlagen zu diesem Dokument.

In einer Situation, in der mehrere Standardindikator-Pufferobjekte erstellt werden, aber nicht alle gleichzeitig verwendet werden, zeigt das Chart Artefakte von derzeit nicht verwendeten Pufferindikatoren an. Um dies zu vermeiden, füllen wir zuerst alle Indikatorpuffer-Arrays mit ihrem leeren Wert aus und berechnen danach diejenigen, die jetzt berechnet werden müssen. Dazu erstellen wir eine Methode, die alle Puffer ihrer verfügbaren Standardindikatorpufferobjekte durch den angegebenen Zeitreihenindex löscht.

Deklarieren wir die Methode im 'public' Teil der Klasse:

//--- Clears data of the buffer of (1) the specified standard indicator, (2) all the created standard indicators by the timeseries index
   void                    ClearDataBufferStdInd(const ENUM_INDICATOR std_ind,const int id,const int series_index);
   void                    ClearDataAllBuffersStdInd(int series_index);
//--- Set values for the current chart to buffers of the specified standard indicator by the timeseries index in accordance with buffer object symbol/period

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

//+------------------------------------------------------------------+
//| Clear calculated buffer data for all the created                 |
//| standard indicators  by the timeseries index                     |
//+------------------------------------------------------------------+
void CBuffersCollection::ClearDataAllBuffersStdInd(int series_index)
  {
   CArrayObj *list=this.GetListBuffersWithID();
   if(list==NULL || list.Total()==0)
     {
      ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_NO_BUFFER_OBJ));
      return;
     }
   int total=list.Total();
   for(int i=0;i<total;i++)
     {
      CBuffer *buff=list.At(i);
      if(buff==NULL || buff.TypeBuffer()==BUFFER_TYPE_CALCULATE || buff.IndicatorType()==WRONG_VALUE)
         continue;
      this.ClearDataBufferStdInd(buff.IndicatorType(),buff.ID(),series_index);
     }
  }
//+------------------------------------------------------------------+

Hier:
Liste aller Pufferobjekte holen, für die das Kennzeichen ID angegeben ist.
In der Schleife durch die gesamte erhaltene Liste
holen wir uns das nächste Pufferobjekt.
Wenn das Objekt aus irgendeinem Grund nicht erhalten wird oder es sich um einen berechneten Puffer handelt oder ein Standardkennzeichentyp für das Objekt nicht gesetzt ist
(weil jedes Pufferobjekt eine ID haben kann, nicht nur ein Standardkennzeichen) überspringen wir dieses Objekt.
Schließlich wird die Methode zum Löschen der Puffer des spezifizierten Standardindikators aufgerufen, die im vorherigen Artikel besprochen wurde.

Verbessern wir die Methoden zur Vorbereitung und Löschung von berechneten Puffer-Arrays, die Daten von Standardindikatorpuffern speichern. Früher haben wir in diesen Methoden Codeblöcke erstellt, die Einzeltyp-Aktionen für die Indikatoren gruppieren, deren Namen/Zuordnungen ihrer Zeilen identisch waren. Wenn wir nun bei der Rekrutierung von Typen von Standardindikatorzeilen allen gleichartigen Zeilen identische numerische Werte zuwiesen (wir haben das ganz am Anfang der Geschichte diskutiert), können solche Methoden vereinfacht werden, indem wir alles auf drei Codeblöcke bringen - für eine, zwei und drei Zeilen von Standardindikatoren.

Eine Methode, die berechnete Pufferdaten des angegebenen Standardindikators vorbereitet:

//+------------------------------------------------------------------+
//| Prepare calculated buffer data                                   |
//| of the specified standard indicator                              |
//+------------------------------------------------------------------+
int CBuffersCollection::PreparingDataBufferStdInd(const ENUM_INDICATOR std_ind,const int id,const int total_copy)
  {
   CArrayObj *list_ind=this.GetListBufferByTypeID(std_ind,id);
   CArrayObj *list0=NULL,*list1=NULL,*list2=NULL;
   list_ind=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_TYPE,BUFFER_TYPE_CALCULATE,EQUAL);
   if(list_ind==NULL || list_ind.Total()==0)
     {
      ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_NO_BUFFER_OBJ));
      return 0;
     }
   CBufferCalculate *buffer=NULL;
   int copied=WRONG_VALUE;
   int idx0=0,idx1=1,idx2=2;
   switch((int)std_ind)
     {
   //--- Single-buffer standard indicators
      case IND_AC          :
      case IND_AD          :
      case IND_AMA         :
      case IND_AO          :
      case IND_ATR         :
      case IND_BEARS       :
      case IND_BULLS       :
      case IND_BWMFI       :
      case IND_CCI         :
      case IND_CHAIKIN     :
      case IND_DEMA        :
      case IND_DEMARKER    :
      case IND_FORCE       :
      case IND_FRAMA       :
      case IND_MA          :
      case IND_MFI         :
      case IND_MOMENTUM    :
      case IND_OBV         :
      case IND_OSMA        :
      case IND_RSI         :
      case IND_SAR         :
      case IND_STDDEV      :
      case IND_TEMA        :
      case IND_TRIX        :
      case IND_VIDYA       :
      case IND_VOLUMES     :
      case IND_WPR         :
      
        buffer=list_ind.At(0);
        if(buffer==NULL) return 0;
        copied=buffer.FillAsSeries(buffer.IndicatorHandle(),0,buffer.Shift(),total_copy);
        return copied;
      
   //--- Multi-buffer standard indicators
      case IND_ENVELOPES   :
      case IND_FRACTALS    :
      case IND_MACD        :
      case IND_RVI         :
      case IND_STOCHASTIC  :
        idx0=0;
        idx1=1;
        list0=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,0,EQUAL);
        buffer=list0.At(0);
        if(buffer==NULL) return 0;
        copied=buffer.FillAsSeries(buffer.IndicatorHandle(),idx0,buffer.Shift(),total_copy);
        if(copied<total_copy) return 0;
        
        list1=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,1,EQUAL);
        buffer=list1.At(0);
        if(buffer==NULL) return 0;
        copied=buffer.FillAsSeries(buffer.IndicatorHandle(),idx1,buffer.Shift(),total_copy);
        if(copied<total_copy) return 0;
        return copied;
      
      case IND_ALLIGATOR   :
      case IND_ADX         :
      case IND_ADXW        :
      case IND_BANDS       :
        if(std_ind==IND_BANDS)
          {
           idx0=1;
           idx1=2;
           idx2=0;
          }
        else
          {
           idx0=0;
           idx1=1;
           idx2=2;
          }
        list0=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,0,EQUAL);
        buffer=list0.At(0);
        if(buffer==NULL) return 0;
        copied=buffer.FillAsSeries(buffer.IndicatorHandle(),idx0,buffer.Shift(),total_copy);
        if(copied<total_copy) return 0;
        
        list1=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,1,EQUAL);
        buffer=list1.At(0);
        if(buffer==NULL) return 0;
        copied=buffer.FillAsSeries(buffer.IndicatorHandle(),idx1,buffer.Shift(),total_copy);
        if(copied<total_copy) return 0;
        
        list2=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,2,EQUAL);
        buffer=list2.At(0);
        if(buffer==NULL) return 0;
        copied=buffer.FillAsSeries(buffer.IndicatorHandle(),idx2,buffer.Shift(),total_copy);
        if(copied<total_copy) return 0;
        return copied;
      
      case IND_GATOR       :
      case IND_ICHIMOKU    :
        break;
      
      default:
        break;
     }
   return 0;
  }
//+------------------------------------------------------------------+

Im Codeblock für drei Puffer des Indikators haben wir Prüfung der Verarbeitung der Indikatorlinien der Bollinger-Bänder hinzugefügt, weil sich die Indizierung seiner Puffer-Arrays von der Puffer-Indizierung aller übrigen Drei-Puffer-Standardindikatoren unterscheidet. Die Verarbeitung von Gator-Oszillator- und Ichimoku-Kinko-Hyo-Indikatoren ist jedoch aus den zu Beginn des Artikels beschriebenen Gründen nicht möglich.

Eine Methode, bei der die Pufferdaten des angegebenen Standardindikators durch den Zeitreihenindex gelöscht werden:

//+------------------------------------------------------------------+
//| Clear buffer data of the specified standard indicator            |
//| by the timeseries index                                          |
//+------------------------------------------------------------------+
void CBuffersCollection::ClearDataBufferStdInd(const ENUM_INDICATOR std_ind,const int id,const int series_index)
  {
//--- Get the list of buffer objects by type and ID
   CArrayObj *list_ind=this.GetListBufferByTypeID(std_ind,id);
   CArrayObj *list0=NULL,*list1=NULL,*list2=NULL;
   if(list_ind==NULL || list_ind.Total()==0)
      return;
   list_ind=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_TYPE,BUFFER_TYPE_DATA,EQUAL);
   if(list_ind.Total()==0)
      return;
   CBuffer *buffer=NULL;
   switch((int)std_ind)
     {
   //--- Single-buffer standard indicators
      case IND_AC          :
      case IND_AD          :
      case IND_AMA         :
      case IND_AO          :
      case IND_ATR         :
      case IND_BEARS       :
      case IND_BULLS       :
      case IND_BWMFI       :
      case IND_CCI         :
      case IND_CHAIKIN     :
      case IND_DEMA        :
      case IND_DEMARKER    :
      case IND_FORCE       :
      case IND_FRAMA       :
      case IND_MA          :
      case IND_MFI         :
      case IND_MOMENTUM    :
      case IND_OBV         :
      case IND_OSMA        :
      case IND_RSI         :
      case IND_SAR         :
      case IND_STDDEV      :
      case IND_TEMA        :
      case IND_TRIX        :
      case IND_VIDYA       :
      case IND_VOLUMES     :
      case IND_WPR         :
        buffer=list_ind.At(0);
        if(buffer==NULL) return;
        buffer.SetBufferValue(0,series_index,buffer.EmptyValue());
        break;
      
   //--- Multi-buffer standard indicators
      case IND_ENVELOPES   :
      case IND_FRACTALS    :
      case IND_MACD        :
      case IND_RVI         :
      case IND_STOCHASTIC  :
      case IND_GATOR       :
        list0=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,0,EQUAL);
        buffer=list0.At(0);
        if(buffer==NULL) return;
        buffer.SetBufferValue(0,series_index,buffer.EmptyValue());
        
        list1=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,1,EQUAL);
        buffer=list1.At(0);
        if(buffer==NULL) return;
        buffer.SetBufferValue(0,series_index,buffer.EmptyValue());
        break;

      case IND_ALLIGATOR   :
      case IND_ADX         :
      case IND_ADXW        :
      case IND_BANDS       :
        list0=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,0,EQUAL);
        buffer=list0.At(0);
        if(buffer==NULL) return;
        buffer.SetBufferValue(0,series_index,buffer.EmptyValue());
        
        list1=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,1,EQUAL);
        buffer=list1.At(0);
        if(buffer==NULL) return;
        buffer.SetBufferValue(0,series_index,buffer.EmptyValue());
        
        list2=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,2,EQUAL);
        buffer=list2.At(0);
        if(buffer==NULL) return;
        buffer.SetBufferValue(0,series_index,buffer.EmptyValue());
        break;
      
      case IND_ICHIMOKU :
        break;
      
      default:
        break;
     }
  }
//+------------------------------------------------------------------+

Alle Standardindikatoren sind hier in gleicher Weise durch entsprechende Codeblöcke verteilt — Standardindikatoren mit einem, zwei oder drei Puffern. Dabei ist es nicht wichtig, in welcher Reihenfolge die Pufferdaten gelöscht werden, daher sind Bollinger-Bänder und Gator-Oszillator bereits in Codeblöcken der Zwei- und Dreipuffer-Standardindikatoren-Verarbeitung enthalten. Wir verarbeiten Ichimoku vorerst nicht.

Und da wir jetzt die Verarbeitung von Single-, Zwei- und Dreipuffer-Standardindikatoren zusammenfassen, können wir den Code der Methode, die Werte für das aktuelle Chart auf Puffer des angegebenen Standardindikators setzt, durch den Zeitreihenindex gemäß Pufferobjektsymbol/Periode erheblich reduzieren. Hier ist der vollständige Methodencode vor seiner Überarbeitung:

//+------------------------------------------------------------------+
//| Sets values for the current chart to buffers of the specified    |
//| standard indicator by the timeseries index in accordance         |
//| with buffer object symbol/period                                 |
//+------------------------------------------------------------------+
bool CBuffersCollection::SetDataBufferStdInd(const ENUM_INDICATOR ind_type,const int id,const int series_index,const datetime series_time,const char color_index=WRONG_VALUE)
  {
//--- Get the list of buffer objects by type and ID
   CArrayObj *list=this.GetListBufferByTypeID(ind_type,id);
   if(list==NULL || list.Total()==0)
     {
      ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_NO_BUFFER_OBJ));
      return false;
     }
//--- Get the list of drawn buffers with ID
   CArrayObj *list_data=CSelect::ByBufferProperty(list,BUFFER_PROP_TYPE,BUFFER_TYPE_DATA,EQUAL);
   list_data=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_TYPE,ind_type,EQUAL);
//--- Get the list of calculated buffers with ID
   CArrayObj *list_calc=CSelect::ByBufferProperty(list,BUFFER_PROP_TYPE,BUFFER_TYPE_CALCULATE,EQUAL);
   list_calc=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_TYPE,ind_type,EQUAL);
//--- Leave if any of the lists is empty
   if(list_data.Total()==0 || list_calc.Total()==0)
      return false;
//--- Declare necessary objects and variables
   CBuffer *buffer_data0=NULL;
   CBuffer *buffer_data1=NULL;
   CBuffer *buffer_data2=NULL;
   CBuffer *buffer_calc0=NULL;
   CBuffer *buffer_calc1=NULL;
   CBuffer *buffer_calc2=NULL;
   int index_period=0;
   int series_index_start=0;
   int num_bars=1,index=0;
   uchar clr=color_index;
   long vol0=0,vol1=0;
   datetime time_period=0,time_shift=0;
   double value00=EMPTY_VALUE, value01=EMPTY_VALUE;
   double value10=EMPTY_VALUE, value11=EMPTY_VALUE;
   double value20=EMPTY_VALUE, value21=EMPTY_VALUE;

//--- Depending on standard indicator type
   switch((int)ind_type)
     {
   //--- Single-buffer standard indicators
      case IND_AC       :
      case IND_AD       :
      case IND_AMA      :
      case IND_AO       :
      case IND_ATR      :
      case IND_BEARS    :
      case IND_BULLS    :
      case IND_BWMFI    :
      case IND_CCI      :
      case IND_CHAIKIN  :
      case IND_DEMA     :
      case IND_DEMARKER :
      case IND_FORCE    :
      case IND_FRAMA    :
      case IND_MA       :
      case IND_MFI      :
      case IND_MOMENTUM :
      case IND_OBV      :
      case IND_OSMA     :
      case IND_RSI      :
      case IND_SAR      :
      case IND_STDDEV   :
      case IND_TEMA     :
      case IND_TRIX     :
      case IND_VIDYA    :
      case IND_VOLUMES  :
      case IND_WPR      :
        //--- Get objects of drawn and calculated buffers
        buffer_data0=list_data.At(0);
        buffer_calc0=list_calc.At(0);
        if(buffer_calc0==NULL || buffer_data0==NULL || buffer_calc0.GetDataTotal(0)==0)
           return false;
        //--- Find bar index on a period of indicator buffer chart which corresponds to the time of current bar beginning
        index_period=::iBarShift(buffer_calc0.Symbol(),buffer_calc0.Timeframe(),series_time,true);
        if(index_period==WRONG_VALUE || index_period>buffer_calc0.GetDataTotal()-1)
           return false;
        //--- Get the value by this index from indicator buffer
        value00=buffer_calc0.GetDataBufferValue(0,index_period);
        if(buffer_calc0.Symbol()==::Symbol() && buffer_calc0.Timeframe()==::Period())
          {
           series_index_start=series_index;
           num_bars=1;
          }
        else
          {
           //--- Calculate bar shift depending on a direction of indicator line shift
           //index_period+=buffer_data0.Shift();
           //--- Get the bar time which the bar with index_period index falls into on a period and symbol of calculated buffer
           time_period=::iTime(buffer_calc0.Symbol(),buffer_calc0.Timeframe(),index_period); // -buffer_calc0.Shift()+index_shift
           if(time_period==0) return false;
           //--- Get the current chart bar which corresponds to the time
           series_index_start=::iBarShift(::Symbol(),::Period(),time_period,true);
           if(series_index_start==WRONG_VALUE) return false;
           //--- Calculate the number of bars on the current chart which are to be filled in with calculated buffer data
           num_bars=::PeriodSeconds(buffer_calc0.Timeframe())/::PeriodSeconds(PERIOD_CURRENT);
           if(num_bars==0) num_bars=1;
          }
        //--- Take values for color calculation
        value01=(series_index_start+num_bars>buffer_data0.GetDataTotal()-1 ? value00 : buffer_data0.GetDataBufferValue(0,series_index_start+num_bars+(num_bars*buffer_calc0.Shift())));
        //--- In a loop, by the number of bars in  num_bars fill in the drawn buffer with a value from the calculated buffer taken by index_period index
        //--- and set the drawn buffer color depending on a proportion of value00 and value01 values
        for(int i=0;i<num_bars;i++)
          {
           index=series_index_start-i;
           buffer_data0.SetBufferValue(0,index,value00);
           if(ind_type!=IND_BWMFI)
              clr=(color_index==WRONG_VALUE ? uchar(value00>value01 ? 0 : value00<value01 ? 1 : 2) : color_index);
           else
             {
              vol0=::iVolume(buffer_calc0.Symbol(),buffer_calc0.Timeframe(),index_period);
              vol1=::iVolume(buffer_calc0.Symbol(),buffer_calc0.Timeframe(),index_period+1);
              clr=
                (
                 value00>value01 && vol0>vol1 ? 0 :
                 value00<value01 && vol0<vol1 ? 1 :
                 value00>value01 && vol0<vol1 ? 2 :
                 value00<value01 && vol0>vol1 ? 3 : 4
                );
             }
           buffer_data0.SetBufferColorIndex(index,clr);
          }
        return true;
      
   //--- Multi-buffer standard indicators
      case IND_ADX   :
      case IND_ADXW  :
        //--- Get objects of drawn and calculated buffers
        list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_MAIN,EQUAL);
        buffer_data0=list.At(0);
        list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_DI_PLUS,EQUAL);
        buffer_data1=list.At(0);
        list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_DI_MINUS,EQUAL);
        buffer_data2=list.At(0);
        
        list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_MAIN,EQUAL);
        buffer_calc0=list.At(0);
        list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_DI_PLUS,EQUAL);
        buffer_calc1=list.At(0);
        list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_DI_MINUS,EQUAL);
        buffer_calc2=list.At(0);
        
        if(buffer_calc0==NULL || buffer_data0==NULL || buffer_calc0.GetDataTotal(0)==0)
           return false;
        if(buffer_calc1==NULL || buffer_data1==NULL || buffer_calc1.GetDataTotal(0)==0)
           return false;
        if(buffer_calc2==NULL || buffer_data2==NULL || buffer_calc2.GetDataTotal(0)==0)
           return false;
        //--- Find bar index on a period which corresponds to the time of current bar beginning
        index_period=::iBarShift(buffer_calc0.Symbol(),buffer_calc0.Timeframe(),series_time,true);
        if(index_period==WRONG_VALUE || index_period>buffer_calc0.GetDataTotal()-1)
           return false;
        //--- Get the value by this index from indicator buffer
        value00=buffer_calc0.GetDataBufferValue(0,index_period);
        value10=buffer_calc1.GetDataBufferValue(0,index_period);
        value20=buffer_calc2.GetDataBufferValue(0,index_period);
        if(buffer_calc0.Symbol()==::Symbol() && buffer_calc0.Timeframe()==::Period())
          {
           series_index_start=series_index;
           num_bars=1;
          }
        else
          {
           //--- Get the bar time which the bar with index_period index falls into on a period and symbol of calculated buffer
           time_period=::iTime(buffer_calc0.Symbol(),buffer_calc0.Timeframe(),index_period);
           if(time_period==0) return false;
           //--- Get the current chart bar which corresponds to the time
           series_index_start=::iBarShift(::Symbol(),::Period(),time_period,true);
           if(series_index_start==WRONG_VALUE) return false;
           //--- Calculate the number of bars on the current chart which are to be filled in with calculated buffer data
           num_bars=::PeriodSeconds(buffer_calc0.Timeframe())/::PeriodSeconds(PERIOD_CURRENT);
           if(num_bars==0) num_bars=1;
          }
        //--- Take values for color calculation
        value01=(series_index_start+num_bars>buffer_data0.GetDataTotal()-1 ? value00 : buffer_data0.GetDataBufferValue(0,series_index_start+num_bars));
        value11=(series_index_start+num_bars>buffer_data1.GetDataTotal()-1 ? value10 : buffer_data1.GetDataBufferValue(0,series_index_start+num_bars));
        value21=(series_index_start+num_bars>buffer_data2.GetDataTotal()-1 ? value20 : buffer_data2.GetDataBufferValue(0,series_index_start+num_bars));
        //--- In a loop, by the number of bars in  num_bars fill in the drawn buffer with a value from the calculated buffer taken by index_period index
        //--- and set the drawn buffer color depending on a proportion of value00 and value01 values
        for(int i=0;i<num_bars;i++)
          {
           index=series_index_start-i;
           buffer_data0.SetBufferValue(0,index,value00);
           buffer_data1.SetBufferValue(1,index,value10);
           buffer_data2.SetBufferValue(2,index,value20);
           buffer_data0.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value00>value01 ? 0 : value00<value01 ? 1 : 2) : color_index);
           buffer_data1.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value10>value11 ? 0 : value10<value11 ? 1 : 2) : color_index);
           buffer_data2.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value20>value21 ? 0 : value20<value21 ? 1 : 2) : color_index);
          }
        return true;
      
      case IND_BANDS    :
        //--- Get objects of drawn and calculated buffers
        list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_UPPER,EQUAL);
        buffer_data0=list.At(0);
        list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_LOWER,EQUAL);
        buffer_data1=list.At(0);
        list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_MIDDLE,EQUAL);
        buffer_data2=list.At(0);
        
        list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_UPPER,EQUAL);
        buffer_calc0=list.At(0);
        list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_LOWER,EQUAL);
        buffer_calc1=list.At(0);
        list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_MIDDLE,EQUAL);
        buffer_calc2=list.At(0);
        
        if(buffer_calc0==NULL || buffer_data0==NULL || buffer_calc0.GetDataTotal(0)==0)
           return false;
        if(buffer_calc1==NULL || buffer_data1==NULL || buffer_calc1.GetDataTotal(0)==0)
           return false;
        if(buffer_calc2==NULL || buffer_data2==NULL || buffer_calc2.GetDataTotal(0)==0)
           return false;
        //--- Find bar index on a period which corresponds to the time of current bar beginning
        index_period=::iBarShift(buffer_calc0.Symbol(),buffer_calc0.Timeframe(),series_time,true);
        if(index_period==WRONG_VALUE || index_period>buffer_calc0.GetDataTotal()-1)
           return false;
        //--- Get the value by this index from indicator buffer
        value00=buffer_calc0.GetDataBufferValue(0,index_period);
        value10=buffer_calc1.GetDataBufferValue(0,index_period);
        value20=buffer_calc2.GetDataBufferValue(0,index_period);
        if(buffer_calc0.Symbol()==::Symbol() && buffer_calc0.Timeframe()==::Period())
          {
           series_index_start=series_index;
           num_bars=1;
          }
        else
          {
           //--- Get the bar time which the bar with index_period index falls into on a period and symbol of calculated buffer
           time_period=::iTime(buffer_calc0.Symbol(),buffer_calc0.Timeframe(),index_period);
           if(time_period==0) return false;
           //--- Get the current chart bar which corresponds to the time
           series_index_start=::iBarShift(::Symbol(),::Period(),time_period,true);
           if(series_index_start==WRONG_VALUE) return false;
           //--- Calculate the number of bars on the current chart which are to be filled in with calculated buffer data
           num_bars=::PeriodSeconds(buffer_calc0.Timeframe())/::PeriodSeconds(PERIOD_CURRENT);
           if(num_bars==0) num_bars=1;
          }
        //--- Take values for color calculation
        value01=(series_index_start+num_bars>buffer_data0.GetDataTotal()-1 ? value00 : buffer_data0.GetDataBufferValue(0,series_index_start+num_bars));
        value11=(series_index_start+num_bars>buffer_data1.GetDataTotal()-1 ? value10 : buffer_data1.GetDataBufferValue(0,series_index_start+num_bars));
        value21=(series_index_start+num_bars>buffer_data2.GetDataTotal()-1 ? value20 : buffer_data2.GetDataBufferValue(0,series_index_start+num_bars));
        //--- In a loop, by the number of bars in  num_bars fill in the drawn buffer with a value from the calculated buffer taken by index_period index
        //--- and set the drawn buffer color depending on a proportion of value00 and value01 values
        for(int i=0;i<num_bars;i++)
          {
           index=series_index_start-i;
           buffer_data0.SetBufferValue(0,index,value00);
           buffer_data1.SetBufferValue(0,index,value10);
           buffer_data2.SetBufferValue(0,index,value20);
           buffer_data0.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value00>value01 ? 0 : value00<value01 ? 1 : 2) : color_index);
           buffer_data1.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value10>value11 ? 0 : value10<value11 ? 1 : 2) : color_index);
           buffer_data2.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value20>value21 ? 0 : value20<value21 ? 1 : 2) : color_index);
          }
        return true;
      
      case IND_ENVELOPES :
      case IND_FRACTALS  :
        //--- Get objects of drawn and calculated buffers
        list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_UPPER,EQUAL);
        buffer_data0=list.At(0);
        list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_LOWER,EQUAL);
        buffer_data1=list.At(0);
        
        list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_UPPER,EQUAL);
        buffer_calc0=list.At(0);
        list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_LOWER,EQUAL);
        buffer_calc1=list.At(0);
        
        if(buffer_calc0==NULL || buffer_data0==NULL || buffer_calc0.GetDataTotal(0)==0)
           return false;
        if(buffer_calc1==NULL || buffer_data1==NULL || buffer_calc1.GetDataTotal(0)==0)
           return false;
        //--- Find bar index on a period which corresponds to the time of current bar beginning
        index_period=::iBarShift(buffer_calc0.Symbol(),buffer_calc0.Timeframe(),series_time,true);
        if(index_period==WRONG_VALUE || index_period>buffer_calc0.GetDataTotal()-1)
           return false;
        //--- Get the value by this index from indicator buffer
        value00=buffer_calc0.GetDataBufferValue(0,index_period);
        value10=buffer_calc1.GetDataBufferValue(0,index_period);
        if(buffer_calc0.Symbol()==::Symbol() && buffer_calc0.Timeframe()==::Period())
          {
           series_index_start=series_index;
           num_bars=1;
          }
        else
          {
           //--- Get the bar time which the bar with index_period index falls into on a period and symbol of calculated buffer
           time_period=::iTime(buffer_calc0.Symbol(),buffer_calc0.Timeframe(),index_period);
           if(time_period==0) return false;
           //--- Get the current chart bar which corresponds to the time
           series_index_start=::iBarShift(::Symbol(),::Period(),time_period,true);
           if(series_index_start==WRONG_VALUE) return false;
           //--- Calculate the number of bars on the current chart which are to be filled in with calculated buffer data
           num_bars=::PeriodSeconds(buffer_calc0.Timeframe())/::PeriodSeconds(PERIOD_CURRENT);
           if(num_bars==0) num_bars=1;
          }
        //--- Take values for color calculation
        value01=(series_index_start+num_bars>buffer_data0.GetDataTotal()-1 ? value00 : buffer_data0.GetDataBufferValue(0,series_index_start+num_bars));
        value11=(series_index_start+num_bars>buffer_data1.GetDataTotal()-1 ? value10 : buffer_data1.GetDataBufferValue(0,series_index_start+num_bars));
        //--- In a loop, by the number of bars in  num_bars fill in the drawn buffer with a value from the calculated buffer taken by index_period index
        //--- and set the drawn buffer color depending on a proportion of value00 and value01 values
        for(int i=0;i<num_bars;i++)
          {
           index=series_index_start-i;
           buffer_data0.SetBufferValue(0,index,value00);
           buffer_data1.SetBufferValue(1,index,value10);
           buffer_data0.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value00>value01 ? 0 : value00<value01 ? 1 : 2) : color_index);
           buffer_data1.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value10>value11 ? 0 : value10<value11 ? 1 : 2) : color_index);
          }
        return true;
      
      case IND_MACD        :
      case IND_RVI         :
      case IND_STOCHASTIC  :
        //--- Get objects of drawn and calculated buffers
        list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_MAIN,EQUAL);
        buffer_data0=list.At(0);
        list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_SIGNAL,EQUAL);
        buffer_data1=list.At(0);
        
        list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_MAIN,EQUAL);
        buffer_calc0=list.At(0);
        list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_SIGNAL,EQUAL);
        buffer_calc1=list.At(0);
        
        if(buffer_calc0==NULL || buffer_data0==NULL || buffer_calc0.GetDataTotal(0)==0)
           return false;
        if(buffer_calc1==NULL || buffer_data1==NULL || buffer_calc1.GetDataTotal(0)==0)
           return false;
        //--- Find bar index on a period which corresponds to the time of current bar beginning
        index_period=::iBarShift(buffer_calc0.Symbol(),buffer_calc0.Timeframe(),series_time,true);
        if(index_period==WRONG_VALUE || index_period>buffer_calc0.GetDataTotal()-1)
           return false;
        //--- Get the value by this index from indicator buffer
        value00=buffer_calc0.GetDataBufferValue(0,index_period);
        value10=buffer_calc1.GetDataBufferValue(0,index_period);
        if(buffer_calc0.Symbol()==::Symbol() && buffer_calc0.Timeframe()==::Period())
          {
           series_index_start=series_index;
           num_bars=1;
          }
        else
          {
           //--- Get the bar time which the bar with index_period index falls into on a period and symbol of calculated buffer
           time_period=::iTime(buffer_calc0.Symbol(),buffer_calc0.Timeframe(),index_period);
           if(time_period==0) return false;
           //--- Get the current chart bar which corresponds to the time
           series_index_start=::iBarShift(::Symbol(),::Period(),time_period,true);
           if(series_index_start==WRONG_VALUE) return false;
           //--- Calculate the number of bars on the current chart which are to be filled in with calculated buffer data
           num_bars=::PeriodSeconds(buffer_calc0.Timeframe())/::PeriodSeconds(PERIOD_CURRENT);
           if(num_bars==0) num_bars=1;
          }
        //--- Take values for color calculation
        value01=(series_index_start+num_bars>buffer_data0.GetDataTotal()-1 ? value00 : buffer_data0.GetDataBufferValue(0,series_index_start+num_bars));
        value11=(series_index_start+num_bars>buffer_data1.GetDataTotal()-1 ? value10 : buffer_data1.GetDataBufferValue(0,series_index_start+num_bars));
        //--- In a loop, by the number of bars in  num_bars fill in the drawn buffer with a value from the calculated buffer taken by index_period index
        //--- and set the drawn buffer color depending on a proportion of value00 and value01 values
        for(int i=0;i<num_bars;i++)
          {
           index=series_index_start-i;
           buffer_data0.SetBufferValue(0,index,value00);
           buffer_data1.SetBufferValue(1,index,value10);
           buffer_data0.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value00>value01 ? 0 : value00<value01 ? 1 : 2) : color_index);
           buffer_data1.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value10>value11 ? 0 : value10<value11 ? 1 : 2) : color_index);
          }
        return true;
      
      case IND_ALLIGATOR:
        //--- Get objects of drawn and calculated buffers
        list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_JAWS,EQUAL);
        buffer_data0=list.At(0);
        list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_TEETH,EQUAL);
        buffer_data1=list.At(0);
        list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_LIPS,EQUAL);
        buffer_data2=list.At(0);
        
        list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_JAWS,EQUAL);
        buffer_calc0=list.At(0);
        list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_TEETH,EQUAL);
        buffer_calc1=list.At(0);
        list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_LIPS,EQUAL);
        buffer_calc2=list.At(0);
        
        if(buffer_calc0==NULL || buffer_data0==NULL || buffer_calc0.GetDataTotal(0)==0)
           return false;
        if(buffer_calc1==NULL || buffer_data1==NULL || buffer_calc1.GetDataTotal(0)==0)
           return false;
        if(buffer_calc2==NULL || buffer_data2==NULL || buffer_calc2.GetDataTotal(0)==0)
           return false;
        //--- Find bar index on a period which corresponds to the time of current bar beginning
        index_period=::iBarShift(buffer_calc0.Symbol(),buffer_calc0.Timeframe(),series_time,true);
        if(index_period==WRONG_VALUE || index_period>buffer_calc0.GetDataTotal()-1)
           return false;
        //--- Get the value by this index from indicator buffer
        value00=buffer_calc0.GetDataBufferValue(0,index_period);
        value10=buffer_calc1.GetDataBufferValue(0,index_period);
        value20=buffer_calc2.GetDataBufferValue(0,index_period);
        if(buffer_calc0.Symbol()==::Symbol() && buffer_calc0.Timeframe()==::Period())
          {
           series_index_start=series_index;
           num_bars=1;
          }
        else
          {
           //--- Get the bar time which the bar with index_period index falls into on a period and symbol of calculated buffer
           time_period=::iTime(buffer_calc0.Symbol(),buffer_calc0.Timeframe(),index_period);
           if(time_period==0) return false;
           //--- Get the current chart bar which corresponds to the time
           series_index_start=::iBarShift(::Symbol(),::Period(),time_period,true);
           if(series_index_start==WRONG_VALUE) return false;
           //--- Calculate the number of bars on the current chart which are to be filled in with calculated buffer data
           num_bars=::PeriodSeconds(buffer_calc0.Timeframe())/::PeriodSeconds(PERIOD_CURRENT);
           if(num_bars==0) num_bars=1;
          }
        //--- Take values for color calculation
        value01=(series_index_start+num_bars>buffer_data0.GetDataTotal()-1 ? value00 : buffer_data0.GetDataBufferValue(0,series_index_start+num_bars));
        value11=(series_index_start+num_bars>buffer_data1.GetDataTotal()-1 ? value10 : buffer_data1.GetDataBufferValue(0,series_index_start+num_bars));
        value21=(series_index_start+num_bars>buffer_data2.GetDataTotal()-1 ? value20 : buffer_data2.GetDataBufferValue(0,series_index_start+num_bars));
        //--- In a loop, by the number of bars in  num_bars fill in the drawn buffer with a value from the calculated buffer taken by index_period index
        //--- and set the drawn buffer color depending on a proportion of value00 and value01 values
        for(int i=0;i<num_bars;i++)
          {
           index=series_index_start-i;
           buffer_data0.SetBufferValue(0,index,value00);
           buffer_data1.SetBufferValue(0,index,value10);
           buffer_data2.SetBufferValue(0,index,value20);
           buffer_data0.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value00>value01 ? 0 : value00<value01 ? 1 : 2) : color_index);
           buffer_data1.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value10>value11 ? 0 : value10<value11 ? 1 : 2) : color_index);
           buffer_data2.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value20>value21 ? 0 : value20<value21 ? 1 : 2) : color_index);
          }
        return true;
      
      case IND_GATOR    :
        //--- Get objects of drawn and calculated buffers
        list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_UPPER,EQUAL);
        buffer_data0=list.At(0);
        list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_LOWER,EQUAL);
        buffer_data1=list.At(0);
        
        list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_UPPER,EQUAL);
        buffer_calc0=list.At(0);
        list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_LOWER,EQUAL);
        buffer_calc1=list.At(0);
        
        if(buffer_calc0==NULL || buffer_data0==NULL || buffer_calc0.GetDataTotal(0)==0)
           return false;
        if(buffer_calc1==NULL || buffer_data1==NULL || buffer_calc1.GetDataTotal(0)==0)
           return false;
        //--- Find bar index on a period which corresponds to the time of current bar beginning
        index_period=::iBarShift(buffer_calc0.Symbol(),buffer_calc0.Timeframe(),series_time,true);
        if(index_period==WRONG_VALUE || index_period>buffer_calc0.GetDataTotal()-1)
           return false;
        //--- Get the value by this index from indicator buffer
        value00=buffer_calc0.GetDataBufferValue(0,index_period);
        value10=buffer_calc1.GetDataBufferValue(0,index_period);
        if(buffer_calc0.Symbol()==::Symbol() && buffer_calc0.Timeframe()==::Period())
          {
           series_index_start=series_index;
           num_bars=1;
          }
        else
          {
           //--- Get the bar time which the bar with index_period index falls into on a period and symbol of calculated buffer
           time_period=::iTime(buffer_calc0.Symbol(),buffer_calc0.Timeframe(),index_period);
           if(time_period==0) return false;
           //--- Get the current chart bar which corresponds to the time
           series_index_start=::iBarShift(::Symbol(),::Period(),time_period,true);
           if(series_index_start==WRONG_VALUE) return false;
           //--- Calculate the number of bars on the current chart which are to be filled in with calculated buffer data
           num_bars=::PeriodSeconds(buffer_calc0.Timeframe())/::PeriodSeconds(PERIOD_CURRENT);
           if(num_bars==0) num_bars=1;
          }
        //--- Take values for color calculation
        value01=(series_index_start+num_bars>buffer_data0.GetDataTotal()-1 ? value00 : buffer_data0.GetDataBufferValue(0,series_index_start+num_bars));
        value11=(series_index_start+num_bars>buffer_data1.GetDataTotal()-1 ? value10 : buffer_data1.GetDataBufferValue(0,series_index_start+num_bars));
        //--- In a loop, by the number of bars in  num_bars fill in the drawn buffer with a value from the calculated buffer taken by index_period index
        //--- and set the drawn buffer color depending on a proportion of value00 and value01 values
        for(int i=0;i<num_bars;i++)
          {
           index=series_index_start-i;
           buffer_data0.SetBufferValue(0,index,value00);
           buffer_data1.SetBufferValue(1,index,value10);
           buffer_data0.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value00>value01 ? 0 : value00<value01 ? 1 : 2) : color_index);
           buffer_data1.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value10<value11 ? 0 : value10>value11 ? 1 : 2) : color_index);
          }
        return true;
      
      case IND_ICHIMOKU :
        break;
      
      default:
        break;
     }
   return false;
  }
//+------------------------------------------------------------------+

Identische Code-Blöcke sind in der Methodenliste farblich hervorgehoben. Es liegt auf der Hand, dass wir all diese ähnlichen Verarbeitungen in einer einzigen Methode zusammenfassen können, was eine Reduzierung des Methodencodes ermöglicht.

Im 'public' Teil der Klasse deklarieren wir die 'private' Methode, die die Daten des angegebenen Standardindikators für das Setzen von Werten auf dem aktuellen Symbol Chart vorbereitet (zu dieser Methode verschieben alle ähnlichen Verarbeitungen von der oben betrachteten Methode aus dem vorherigen Artikel):

//--- Clear buffer data of (1) the specified standard indicator, (2) all the created standard indicators by the timeseries index
   void                    ClearDataBufferStdInd(const ENUM_INDICATOR std_ind,const int id,const int series_index);
   void                    ClearDataAllBuffersStdInd(int series_index);
//--- Set values for the current chart to buffers of the specified standard indicator by the timeseries index in accordance with buffer object symbol/period
   bool                    SetDataBufferStdInd(const ENUM_INDICATOR std_ind,const int id,const int series_index,const datetime series_time,const char color_index=WRONG_VALUE);
private:
//--- Prepare data of the specified standard indicator for setting values on the current symbol chart
   int                     PreparingSetDataStdInd(CBuffer *buffer_data0,CBuffer *buffer_data1,CBuffer *buffer_data2,
                                                  CBuffer *buffer_calc0,CBuffer *buffer_calc1,CBuffer *buffer_calc2,
                                                  const ENUM_INDICATOR ind_type,
                                                  const int series_index,
                                                  const datetime series_time,
                                                  int &index_period,
                                                  int &num_bars,
                                                  double &value00,
                                                  double &value01,
                                                  double &value10,
                                                  double &value11,
                                                  double &value20,
                                                  double &value21);
public:

//--- Return the buffer (1) by the graphical series name, (2) timeframe,
//--- (3) Plot index, (4) object index in the collection list, (5) the last created,
//--- buffer list (6) by ID, (7) standard indicator type, (8) type and ID

An die Methode übergeben wir Zeiger auf Pufferobjekte und Zeitreihendaten sowie Variablen durch die Verknüpfung, welche Werte von der Methode für ihre weitere Verarbeitung in der von uns verkleinerten Methode zurückgegeben werden müssen.

Implementation der Methode außerhalb des Klassenhauptteils:

//+------------------------------------------------------------------+
//| Prepare data of the specified standard indicator                 |
//| for setting values on the current symbol chart                   |
//+------------------------------------------------------------------+
int CBuffersCollection::PreparingSetDataStdInd(CBuffer *buffer_data0,CBuffer *buffer_data1,CBuffer *buffer_data2,
                                               CBuffer *buffer_calc0,CBuffer *buffer_calc1,CBuffer *buffer_calc2,
                                               const ENUM_INDICATOR ind_type,
                                               const int series_index,
                                               const datetime series_time,
                                               int &index_period,
                                               int &num_bars,
                                               double &value00,
                                               double &value01,
                                               double &value10,
                                               double &value11,
                                               double &value20,
                                               double &value21)
  {
     //--- Find bar index on a period which corresponds to the time of current bar beginning
     index_period=::iBarShift(buffer_calc0.Symbol(),buffer_calc0.Timeframe(),series_time,true);
     if(index_period==WRONG_VALUE || index_period>buffer_calc0.GetDataTotal()-1)
        return WRONG_VALUE;
     
     //--- Get the value by this index from indicator buffer
     if(buffer_calc0!=NULL)
        value00=buffer_calc0.GetDataBufferValue(0,index_period);
     if(buffer_calc1!=NULL)
        value10=buffer_calc1.GetDataBufferValue(0,index_period);
     if(buffer_calc2!=NULL)
        value20=buffer_calc2.GetDataBufferValue(0,index_period);
     
     int series_index_start=series_index;
     //--- For the current chart we don’t need to calculate a number of bars processed - only one bar is available
     if(buffer_calc0.Symbol()==::Symbol() && buffer_calc0.Timeframe()==::Period())
       {
        series_index_start=series_index;
        num_bars=1;
       }
     else
       {
        //--- Get the bar time which the bar with index_period index falls into on a period and symbol of calculated buffer
        datetime time_period=::iTime(buffer_calc0.Symbol(),buffer_calc0.Timeframe(),index_period);
        if(time_period==0) return false;
        //--- Get the current chart bar which corresponds to the time
        series_index_start=::iBarShift(::Symbol(),::Period(),time_period,true);
        if(series_index_start==WRONG_VALUE) return WRONG_VALUE;
        //--- Calculate the number of bars on the current chart which are to be filled in with calculated buffer data
        num_bars=::PeriodSeconds(buffer_calc0.Timeframe())/::PeriodSeconds(PERIOD_CURRENT);
        if(num_bars==0) num_bars=1;
       }
     //--- Take values for color calculation
     if(buffer_calc0!=NULL)
        value01=(series_index_start+num_bars>buffer_data0.GetDataTotal()-1 ? value00 : buffer_data0.GetDataBufferValue(0,series_index_start+num_bars));
     if(buffer_calc1!=NULL)
        value11=(series_index_start+num_bars>buffer_data1.GetDataTotal()-1 ? value10 : buffer_data1.GetDataBufferValue(0,series_index_start+num_bars));
     if(buffer_calc2!=NULL)
        value21=(series_index_start+num_bars>buffer_data2.GetDataTotal()-1 ? value20 : buffer_data2.GetDataBufferValue(0,series_index_start+num_bars));
   
   return series_index_start;
  }
//+------------------------------------------------------------------+

Das ist klar - wir haben einen wiederholten Codeblock von einer Methode in eine andere, neue Methode verschoben. Da es in der reduzierten Methode notwendig ist, mehrere Daten zu verwenden, die innerhalb dieser privaten Methode berechnet werden, übergeben wir einfach Variablen über die Verknüpfung an die neue Methode, und alle Änderungen der Werte dieser Variablen stehen in der aufrufenden Methode zur Verfügung. Abgesehen vom Setzen von Werten an Variablen durch die Verknüpfung gibt diese Methode den Balkenindex zurück, aus dem in einer Schleife Pufferdaten über das aktuelle Chart in der aufrufenden Methode gefüllt werden müssen. Im Falle eines Fehlers in der Datenverarbeitung gibt die Methode den Wert -1 zurück.

Nun wollen wir sehen, wie sehr wir die Methode, die Werte für das aktuelle Chart auf Puffer des angegebenen Standardindikators setzt, um den Zeitreihenindex gemäß Pufferobjektsymbol/Periode reduziert haben:

//+------------------------------------------------------------------+
//| Sets values for the current chart to buffers of the specified    |
//| standard indicator by the timeseries index in accordance         |
//| with buffer object symbol/period                                 |
//+------------------------------------------------------------------+
bool CBuffersCollection::SetDataBufferStdInd(const ENUM_INDICATOR ind_type,const int id,const int series_index,const datetime series_time,const char color_index=WRONG_VALUE)
  {
//--- Get the list of buffer objects by type and ID
   CArrayObj *list=this.GetListBufferByTypeID(ind_type,id);
   if(list==NULL || list.Total()==0)
     {
      ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_NO_BUFFER_OBJ));
      return false;
     }
//--- Get the list of drawn buffers with ID
   CArrayObj *list_data=CSelect::ByBufferProperty(list,BUFFER_PROP_TYPE,BUFFER_TYPE_DATA,EQUAL);
   list_data=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_TYPE,ind_type,EQUAL);
//--- Get the list of calculated buffers with ID
   CArrayObj *list_calc=CSelect::ByBufferProperty(list,BUFFER_PROP_TYPE,BUFFER_TYPE_CALCULATE,EQUAL);
   list_calc=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_TYPE,ind_type,EQUAL);
//--- Leave if any of the lists is empty
   if(list_data.Total()==0 || list_calc.Total()==0)
      return false;
  
//--- Declare necessary objects and variables
   CBuffer *buffer_data0=NULL;
   CBuffer *buffer_data1=NULL;
   CBuffer *buffer_data2=NULL;
   CBuffer *buffer_calc0=NULL;
   CBuffer *buffer_calc1=NULL;
   CBuffer *buffer_calc2=NULL;
   double value00=EMPTY_VALUE, value01=EMPTY_VALUE;
   double value10=EMPTY_VALUE, value11=EMPTY_VALUE;
   double value20=EMPTY_VALUE, value21=EMPTY_VALUE;
   long vol0=0,vol1=0;
   int series_index_start=series_index,index_period=0, index=0,num_bars=1;
   uchar clr=0;
//--- Depending on standard indicator type

   switch((int)ind_type)
     {
   //--- Single-buffer standard indicators
      case IND_AC       :
      case IND_AD       :
      case IND_AMA      :
      case IND_AO       :
      case IND_ATR      :
      case IND_BEARS    :
      case IND_BULLS    :
      case IND_BWMFI    :
      case IND_CCI      :
      case IND_CHAIKIN  :
      case IND_DEMA     :
      case IND_DEMARKER :
      case IND_FORCE    :
      case IND_FRAMA    :
      case IND_MA       :
      case IND_MFI      :
      case IND_MOMENTUM :
      case IND_OBV      :
      case IND_OSMA     :
      case IND_RSI      :
      case IND_SAR      :
      case IND_STDDEV   :
      case IND_TEMA     :
      case IND_TRIX     :
      case IND_VIDYA    :
      case IND_VOLUMES  :
      case IND_WPR      :
        list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,0,EQUAL);
        buffer_data0=list.At(0);
        list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,0,EQUAL);
        buffer_calc0=list.At(0);
        
        if(buffer_calc0==NULL || buffer_data0==NULL || buffer_calc0.GetDataTotal(0)==0)
           return false;
        
        series_index_start=PreparingSetDataStdInd(buffer_data0,buffer_data1,buffer_data2,buffer_calc0,buffer_calc1,buffer_calc2,
                                                  ind_type,series_index,series_time,index_period,num_bars,value00,value01,value10,value11,value20,value21);
        if(series_index_start==WRONG_VALUE)
           return false;
        //--- In a loop, by the number of bars in  num_bars fill in the drawn buffer with a value from the calculated buffer taken by index_period index
        //--- and set the drawn buffer color depending on a proportion of value00 and value01 values
        for(int i=0;i<num_bars;i++)
          {
           index=series_index_start-i;
           buffer_data0.SetBufferValue(0,index,value00);
           if(ind_type!=IND_BWMFI)
              clr=(color_index==WRONG_VALUE ? uchar(value00>value01 ? 0 : value00<value01 ? 1 : 2) : color_index);
           else
             {
              vol0=::iVolume(buffer_calc0.Symbol(),buffer_calc0.Timeframe(),index_period);
              vol1=::iVolume(buffer_calc0.Symbol(),buffer_calc0.Timeframe(),index_period+1);
              clr=
                (
                 value00>value01 && vol0>vol1 ? 0 :
                 value00<value01 && vol0<vol1 ? 1 :
                 value00>value01 && vol0<vol1 ? 2 :
                 value00<value01 && vol0>vol1 ? 3 : 4
                );
             }
           buffer_data0.SetBufferColorIndex(index,clr);
          }
        return true;
      
   //--- Multi-buffer standard indicators
      case IND_ENVELOPES :
      case IND_FRACTALS  :
        list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,0,EQUAL);
        buffer_data0=list.At(0);
        list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,1,EQUAL);
        buffer_data1=list.At(0);
           
        list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,0,EQUAL);
        buffer_calc0=list.At(0);
        list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,1,EQUAL);
        buffer_calc1=list.At(0);
           
        if(buffer_calc0==NULL || buffer_data0==NULL || buffer_calc0.GetDataTotal(0)==0)
           return false;
        if(buffer_calc1==NULL || buffer_data1==NULL || buffer_calc1.GetDataTotal(0)==0)
           return false;
        
        series_index_start=PreparingSetDataStdInd(buffer_data0,buffer_data1,buffer_data2,buffer_calc0,buffer_calc1,buffer_calc2,
                                                  ind_type,series_index,series_time,index_period,num_bars,value00,value01,value10,value11,value20,value21);
        if(series_index_start==WRONG_VALUE)
           return false;
        //--- In a loop, by the number of bars in  num_bars fill in the drawn buffer with a value from the calculated buffer taken by index_period index
        //--- and set the drawn buffer color depending on a proportion of value00 and value01 values
        for(int i=0;i<num_bars;i++)
          {
           index=series_index_start-i;
           buffer_data0.SetBufferValue(0,index,value00);
           buffer_data1.SetBufferValue(1,index,value10);
           buffer_data0.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value00>value01 ? 0 : value00<value01 ? 1 : 2) : color_index);
           buffer_data1.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value10>value11 ? 0 : value10<value11 ? 1 : 2) : color_index);
          }
        return true;
      
      case IND_ADX         :
      case IND_ADXW        :
      case IND_BANDS       :
      case IND_MACD        :
      case IND_RVI         :
      case IND_STOCHASTIC  :
      case IND_ALLIGATOR   :
        list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,0,EQUAL);
        buffer_data0=list.At(0);
        list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,1,EQUAL);
        buffer_data1=list.At(0);
        list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,2,EQUAL);
        buffer_data2=list.At(0);
        
        list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,0,EQUAL);
        buffer_calc0=list.At(0);
        list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,1,EQUAL);
        buffer_calc1=list.At(0);
        list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,2,EQUAL);
        buffer_calc2=list.At(0);
        
        if(buffer_calc0==NULL || buffer_data0==NULL || buffer_calc0.GetDataTotal(0)==0)
           return false;
        if(buffer_calc1==NULL || buffer_data1==NULL || buffer_calc1.GetDataTotal(0)==0)
           return false;
        if(buffer_calc2==NULL || buffer_data2==NULL || buffer_calc2.GetDataTotal(0)==0)
           return false;
        
        series_index_start=PreparingSetDataStdInd(buffer_data0,buffer_data1,buffer_data2,buffer_calc0,buffer_calc1,buffer_calc2,
                                                  ind_type,series_index,series_time,index_period,num_bars,value00,value01,value10,value11,value20,value21);
        if(series_index_start==WRONG_VALUE)
           return false;
        //--- In a loop, by the number of bars in  num_bars fill in the drawn buffer with a value from the calculated buffer taken by index_period index
        //--- and set the drawn buffer color depending on a proportion of value00 and value01 values
        for(int i=0;i<num_bars;i++)
          {
           index=series_index_start-i;
           buffer_data0.SetBufferValue(0,index,value00);
           buffer_data1.SetBufferValue(1,index,value10);
           buffer_data2.SetBufferValue(2,index,value20);
           buffer_data0.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value00>value01 ? 0 : value00<value01 ? 1 : 2) : color_index);
           buffer_data1.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value10>value11 ? 0 : value10<value11 ? 1 : 2) : color_index);
           buffer_data2.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value20>value21 ? 0 : value20<value21 ? 1 : 2) : color_index);
          }
        return true;
      
      case IND_GATOR    :
      case IND_ICHIMOKU :
        break;
      
      default:
        break;
     }
   return false;
  }
//+------------------------------------------------------------------+

Alle Berechnungen, die sich von einem Block zum anderen wiederholen, werden nun durch den Aufruf der Methode und Prüfung des von ihr zurückgegebenen Ergebnisses ersetzt. Außerdem verarbeiten wir hier aus den oben genannten Gründen Gator-Oszillator und Ichimoku Kinko Hyo nicht.

Test

Um den Test durchzuführen, nehmen wir den Testindikator aus dem vorherigen Artikel und speichern ihn in einem neuen Ordner \MQL5\Indikatoren\TestDoEasy\TestDoEasyPart50\ unter dem neuen Namen TestDoEasyPart50.mq5.

Fügen wir die Verschiebung der Indikatorlinie zu den externen Parametern des Indikators hinzu:

//--- input variables
sinput   string               InpUsedSymbols    =  "GBPUSD";      // Used symbol (one only)
sinput   ENUM_TIMEFRAMES      InpPeriod         =  PERIOD_M30;    // Used chart period
sinput   ENUM_INDICATOR       InpIndType        =  IND_AC;        // Type standard indicator
sinput   int                  InpShift          =  0;             // Indicator line shift
//---
sinput   bool                 InpUseSounds      =  true;          // Use sounds
//--- indicator buffers

Wir müssen überprüfen, wie die Verschiebung der Indikatorlinien funktioniert. Standardindikatoren, die eine Funktion zur Darstellung einer verschobenen Linie haben, zeigen alle ihre Daten im Hauptfenster des Charts an (mit Ausnahme des Gator-Oszillators, der in einem Unterfenster angezeigt wird, aber noch nicht von uns implementiert wurde). Fügen wir daher in OnInit() einen Codeblock zum Erstellen von Standardindikatorobjekten hinzu, die im Hauptfenster arbeiten:

//--- indicator buffers mapping
//--- Create all the necessary buffer objects for constructing the selected standard indicator
   bool success=false;
   switch(InpIndType)
     {
//--- Single-buffer standard indicators in the main window
      case IND_AMA         :  success=engine.BufferCreateAMA(InpUsedSymbols,InpPeriod,9,2,30,InpShift,PRICE_CLOSE,1);                  break;
      case IND_DEMA        :  success=engine.BufferCreateDEMA(InpUsedSymbols,InpPeriod,14,InpShift,PRICE_CLOSE,1);                     break;
      case IND_FRAMA       :  success=engine.BufferCreateFrAMA(InpUsedSymbols,InpPeriod,14,InpShift,PRICE_CLOSE,1);                    break;
      case IND_MA          :  success=engine.BufferCreateMA(InpUsedSymbols,InpPeriod,10,InpShift,MODE_SMA,PRICE_CLOSE,1);              break;
      case IND_SAR         :  success=engine.BufferCreateSAR(InpUsedSymbols,InpPeriod,0.02,0.2,1);                                     break;
      case IND_TEMA        :  success=engine.BufferCreateTEMA(InpUsedSymbols,InpPeriod,14,InpShift,PRICE_CLOSE,1);                     break;
      case IND_VIDYA       :  success=engine.BufferCreateVIDYA(InpUsedSymbols,InpPeriod,9,12,InpShift,PRICE_CLOSE,1);                  break;
      
//--- Multi-buffer standard indicators in the main window
      case IND_ALLIGATOR   :  success=engine.BufferCreateAlligator(InpUsedSymbols,InpPeriod,13,8,8,5,5,3,MODE_SMMA,PRICE_MEDIAN,1);    break;
      case IND_BANDS       :  success=engine.BufferCreateBands(InpUsedSymbols,InpPeriod,20,InpShift,2.0,PRICE_CLOSE,1);                break;
      case IND_ENVELOPES   :  success=engine.BufferCreateEnvelopes(InpUsedSymbols,InpPeriod,14,InpShift,MODE_SMA,PRICE_CLOSE,0.1,1);   break;
      case IND_FRACTALS    :  success=engine.BufferCreateFractals(InpUsedSymbols,InpPeriod,1);                                         break;
      
      default:
        break;
     }
   if(!success)
     {
      Print(TextByLanguage("Error. Indicator not created"));
      return INIT_FAILED;
     }

Übergeben wir den externen Parameter, der die Linienverschiebung auf Methoden zur Erstellung von Standardindikatorobjekten festlegt. Für den Indikator Alligator werden die Verschiebungen für jede seiner drei Linien angegeben, und diese Werte sind schon in die Logik der Indikatoridee von seinem Autor einbezogen.

In früheren Testindikatoren hatten wir einen Codeblock, der Ebenen für Indikatoren im Unterfenster und die Kapazität der Indikator-Datenanzeige im Terminaldatenfenster zuweist und einstellt:

//--- Set the levels where they are required and determine data capacity
   int digits=(int)SymbolInfoInteger(InpUsedSymbols,SYMBOL_DIGITS);
   switch(InpIndType)
     {
      case IND_AD          :
      case IND_CHAIKIN     :
      case IND_OBV         :
      case IND_VOLUMES     : digits=0;    break;
      
      case IND_AO          :
      case IND_BEARS       :
      case IND_BULLS       :
      case IND_FORCE       :
      case IND_STDDEV      :
      case IND_AMA         :
      case IND_DEMA        :
      case IND_FRAMA       :
      case IND_MA          :
      case IND_TEMA        :
      case IND_VIDYA       :
      case IND_BANDS       :
      case IND_ENVELOPES   :
      case IND_MACD        : digits+=1;   break;
      
      case IND_AC          :
      case IND_OSMA        : digits+=2;   break;
      
      case IND_MOMENTUM    : digits=2;    break;
      
      case IND_CCI         :
        IndicatorSetInteger(INDICATOR_LEVELS,2);
        IndicatorSetDouble(INDICATOR_LEVELVALUE,0,100);
        IndicatorSetDouble(INDICATOR_LEVELVALUE,1,-100);
        digits=2;
        break;
      case IND_DEMARKER    :
        IndicatorSetInteger(INDICATOR_LEVELS,2);
        IndicatorSetDouble(INDICATOR_LEVELVALUE,0,0.7);
        IndicatorSetDouble(INDICATOR_LEVELVALUE,1,0.3);
        digits=3;
        break;
      case IND_MFI         :
        IndicatorSetInteger(INDICATOR_LEVELS,2);
        IndicatorSetDouble(INDICATOR_LEVELVALUE,0,80);
        IndicatorSetDouble(INDICATOR_LEVELVALUE,1,20);
        break;
      case IND_RSI         :
        IndicatorSetInteger(INDICATOR_LEVELS,3);
        IndicatorSetDouble(INDICATOR_LEVELVALUE,0,70);
        IndicatorSetDouble(INDICATOR_LEVELVALUE,1,50);
        IndicatorSetDouble(INDICATOR_LEVELVALUE,2,30);
        digits=2;
        break;
      case IND_STOCHASTIC  :
        IndicatorSetInteger(INDICATOR_LEVELS,2);
        IndicatorSetDouble(INDICATOR_LEVELVALUE,0,80);
        IndicatorSetDouble(INDICATOR_LEVELVALUE,1,20);
        digits=2;
        break;
      case IND_WPR         :
        IndicatorSetInteger(INDICATOR_LEVELS,2);
        IndicatorSetDouble(INDICATOR_LEVELVALUE,0,-80);
        IndicatorSetDouble(INDICATOR_LEVELVALUE,1,-20);
        digits=2;
        break;
     
      case IND_ATR         :              break;
      case IND_SAR         :              break;
      case IND_TRIX        :              break;
      
      default:
        IndicatorSetInteger(INDICATOR_LEVELS,0);
        break;
     }
//--- Set a short name for the indicator and data capacity
   string label=engine.BufferGetIndicatorShortNameByTypeID(InpIndType,1);
   IndicatorSetString(INDICATOR_SHORTNAME,label);
   IndicatorSetInteger(INDICATOR_DIGITS,digits);

Beseitigen wir diesen Codeblock im letzten Indikator. Dazu schreiben wir in die Datei der Dienstfunktionen der Bibliothek \MQL5\Include\DoEasy\Services\DELib.mqh eine neue Funktion, durch die dieser Codeblock entfernt wird:

//+------------------------------------------------------------------+
//| Set capacity and levels to standard indicator                    |
//+------------------------------------------------------------------+
void SetIndicatorLevels(const string symbol,const ENUM_INDICATOR ind_type)
  {
   int digits=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS);
   switch(ind_type)
     {
      case IND_AD          :
      case IND_CHAIKIN     :
      case IND_OBV         :
      case IND_VOLUMES     : digits=0;    break;
      
      case IND_AO          :
      case IND_BEARS       :
      case IND_BULLS       :
      case IND_FORCE       :
      case IND_STDDEV      :
      case IND_AMA         :
      case IND_DEMA        :
      case IND_FRAMA       :
      case IND_MA          :
      case IND_TEMA        :
      case IND_VIDYA       :
      case IND_BANDS       :
      case IND_ENVELOPES   :
      case IND_MACD        : digits+=1;   break;
      
      case IND_AC          :
      case IND_OSMA        : digits+=2;   break;
      
      case IND_MOMENTUM    : digits=2;    break;
      
      case IND_CCI         :
        IndicatorSetInteger(INDICATOR_LEVELS,2);
        IndicatorSetDouble(INDICATOR_LEVELVALUE,0,100);
        IndicatorSetDouble(INDICATOR_LEVELVALUE,1,-100);
        digits=2;
        break;
      case IND_DEMARKER    :
        IndicatorSetInteger(INDICATOR_LEVELS,2);
        IndicatorSetDouble(INDICATOR_LEVELVALUE,0,0.7);
        IndicatorSetDouble(INDICATOR_LEVELVALUE,1,0.3);
        digits=3;
        break;
      case IND_MFI         :
        IndicatorSetInteger(INDICATOR_LEVELS,2);
        IndicatorSetDouble(INDICATOR_LEVELVALUE,0,80);
        IndicatorSetDouble(INDICATOR_LEVELVALUE,1,20);
        break;
      case IND_RSI         :
        IndicatorSetInteger(INDICATOR_LEVELS,3);
        IndicatorSetDouble(INDICATOR_LEVELVALUE,0,70);
        IndicatorSetDouble(INDICATOR_LEVELVALUE,1,50);
        IndicatorSetDouble(INDICATOR_LEVELVALUE,2,30);
        digits=2;
        break;
      case IND_STOCHASTIC  :
        IndicatorSetInteger(INDICATOR_LEVELS,2);
        IndicatorSetDouble(INDICATOR_LEVELVALUE,0,80);
        IndicatorSetDouble(INDICATOR_LEVELVALUE,1,20);
        digits=2;
        break;
      case IND_WPR         :
        IndicatorSetInteger(INDICATOR_LEVELS,2);
        IndicatorSetDouble(INDICATOR_LEVELVALUE,0,-80);
        IndicatorSetDouble(INDICATOR_LEVELVALUE,1,-20);
        digits=2;
        break;
     
      case IND_ATR         :              break;
      case IND_SAR         :              break;
      case IND_TRIX        :              break;
      
      default:
        IndicatorSetInteger(INDICATOR_LEVELS,0);
        break;
     }
   IndicatorSetInteger(INDICATOR_DIGITS,digits);
  }
//+------------------------------------------------------------------+

Jetzt, in OnInit() des Indikators, reicht es ganz zum Schluss aus, einfach diese Funktion aufzurufen, was den Code einfacher und visuell umfassender macht:

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

//--- Succeeded
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+

Wir lassen OnCalculate() unverändert. Den gesamten Code des Testindikators finden Sie in den unten angehängten Dateien.

Kompilieren wir den Indikator und starten ihn auf dem EURUSD H1 Chart, nachdem wir in den Einstellungen die Verwendung des EURUSD H4-Symbols vorab festgelegt haben. Wir stellen die Linienverschiebung des Indikators auf 4 Balken ein und wählen die Bollinger Bänder als Indikator. Dann wählen wir in den Einstellungen den Indikator Alligator:


Wie wir sehen, werden Bollinger Bänder korrekt mit der spezifizierten Verschiebung von 4 Balken angezeigt aber Alligator reagiert nicht auf eine Verschiebung von 4 Balken - er besitzt sofort beim Erstellen in OnInit() gesetzte Standardwerte gleich den Werten im Standardindikator:

//--- Multi-buffer standard indicators in the main window
      case IND_ALLIGATOR   :  success=engine.BufferCreateAlligator(InpUsedSymbols,InpPeriod,13,8,8,5,5,3,MODE_SMMA,PRICE_MEDIAN,1);    break;

Und auch die Linien des Alligators werden mit der Standardverschiebung korrekt dargestellt.


Was kommt als Nächstes?

Im nächsten Artikel werden wir die Entwicklung von Bibliotheksmethoden für die Arbeit mit Standardindikatoren im Mehrsymbol- und Mehrperiodenmodus fortsetzen.

Alle Dateien der aktuellen Version der Bibliothek sind unten zusammen mit der Testindikator-Datei zum Testen und Herunterladen angehängt.
Hinterlassen Sie Ihre Kommentare, Fragen und Vorschläge in den Kommentaren zum Artikel.
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 Funktionalität für die Arbeit mit Indikatorpuffern entwickelt und getestet habe, werde ich versuchen, einige MQL5-Funktionen in MetaTrader 4 zu implementieren.

Zurück zum Inhalt

Frühere Artikel dieser Serie:

Zeitreihen in der Bibliothek DoEasy (Teil 35): das Balkenobjekt und die Liste der Zeitreihen eines Symbols
Zeitreihen in der Bibliothek DoEasy (Teil 36): Objekt der Zeitreihe für alle verwendeten Symbolzeitrahmen
Zeitreihen in der Bibliothek DoEasy (Teil 37): Kollektion von Zeitreihen - Datenbank der Zeitreihen nach Symbolen und Zeitrahmen
Zeitreihen in der Bibliothek DoEasy (Teil 38): Kollektion von Zeitreihen - Aktualisierungen in Echtzeit und Datenzugriff aus dem Programm

Zeitreihen in der Bibliothek DoEasy (Teil 40): Bibliotheksbasierte Indikatoren - Aktualisierung der Daten in Echtzeit
Zeitreihen in der Bibliothek DoEasy (Teil 41): Beispiel eines Multi-Symbol- und Multi-Zeitrahmen-Indikators
Zeitreihen in der Bibliothek DoEasy (Teil 42): Abstrakte Objektklasse der Indikatorpuffer
Zeitreihen in der Bibliothek DoEasy (Teil 43): Klassen der Objekte von Indikatorpuffern
Zeitreihen in der Bibliothek DoEasy (Teil 44): Kollektionsklasse der Objekte von Indikatorpuffern
Zeitreihen in der Bibliothek DoEasy (Teil 45): Puffer für Mehrperiodenindikator
Zeitreihen in der Bibliothek DoEasy (Teil 46): Mehrperioden-Multisymbol-Indikatorpuffer
Zeitreihen in der Bibliothek DoEasy (Teil 47): Standardindikatoren für mehrere Symbole und Perioden
Zeitreihen in der Bibliothek DoEasy (Teil 48): Mehrperioden-Multisymbol-Indikatoren mit einem Puffer in einem Unterfenster
Zeitreihen in der Bibliothek DoEasy (Teil 49): Standardindikatoren mit mehreren Puffern für mehrere Symbole und Perioden