MetaTrader 5 herunterladen

Hat Dir der Artikel gefallen?
Teile ihn mit den anderen -
poste einen Link zu diesem Beitrag!

Nutze neue Möglichkeiten der Plattform MetaTrader 5

Der universelle ZigZag

9 Dezember 2016, 11:54
Dmitry Fedoseev
0
370

Inhalt

Einführung

ZigZag (in Abb. 1) — Einer der beliebtesten Indikatoren bei den Benutzern MetaTrader 5. Bis heute entwickelte man mehrere Variante vom ZigZag. Allerdings sind einige von ihnen zu langsam, so dass sie nicht geeignet für die Erstellung der Experten sind. Andere geben ständig Fehler, so dass es schwierig ist, sie auch für eine visuelle Beobachtung zu verwenden. Allerdings mit den gleichen Indikatoren, die schnell und ohne Fehler funktionieren, entstehen trotzdem Probleme bei der Entwicklung eines Experten oder eines anderen Indikators. Tatsache ist, dass es echt nicht einfach ist, aus dem ZigZag ihre Messwerte zu extrahieren und interpretieren.


in Abb. 1. Der Indikator ZigZag

In diesem Artikel überlegen wir uns darüber, was man für den Bau eines ZigZags braucht, sprechen wir über verschiedene Wege seines Baus, ziehen wir die Schlussfolgerungen und bekommen einen Algorithmus. Auf der Grundlage dieses Algorithmus wird der universelle Indikator erstellt, der ermöglicht, durch die Eigenschaften verschiedene Arten von ZickZack auszuwählen.

Bei der Erstellung des Indikators verwenden wir eine objektorientierte Programmierung. Wir erstellen ein paar Basisklassen für die verschiedenen Erstellungsstufen des Zigzags, von denen für jeden ein paar Tochterklassen entwickelt werden. Die Unterteilung in Basisklassen und Tochterklassen werden deswegen durchgeführt, damit die Erstellung von verschiedenen neuen Varianten ZickZacks maximal einfacher wird.

Zusätzlich zum Bau des ZickZacks wird im Artikel die Verwendung des resultierenden Indikators für die Entwicklung anderer Indikatoren und Experten betrachtet. Unsere Aufgabe - so zu machen, damit die Datenerfassung aus dem ZickZack und seine Verwendung als Teil in anderen Algorithmen nicht kompliziert und zeitaufwendig werden.

Die Besonderheiten des Indikators ZigZag

Der Indikator ZigZag (In Abb. 1) besteht aus einer gestrichelten Linie, die lokalen Maximums und Minimums der Preisen verbindet. Bei Anfängern kann sofort ein Gedanke ankommen: es wäre toll, bei Tiefen kaufen, und bei Spitzen verkaufen! Natürlich sieht diese Idee sehr verlockend aus, aber leider so verlockend sieht es nur in History aus. In Wirklichkeit ist die Situation etwas anders. Darüber zu erfahren, ob die Bildung einer neuen Tiefe oder Spitze stattgefunden hat oder nicht, erfährt man erst nach einer gewissen Anzahl von Bars, nachdem es bekannt wird. in Abb. 2 wird die Situation gezeigt, wenn das letzte Intervall des Indikators seine Bildung (Änderung) aufgehört hat, der Preis dreht sich um und bewegt sich in die entgegengesetzte Richtung (nach oben).

 
in Abb. 2. Der ZigZag ist nach unten gerichtet, und der Preis wendete sich nach oben

Allerdings ein paar Bars später geht der Preis nach unten (in Abb. 3), und das letzte Intervall des Zigzags wird weiter nach unten gezogen.

  
in Abb. 3. Der Preis bewegte sich weiter nach unten, und das letzte Intervall des ZigZags setzte seine Bildung fort

Zu diesem Zeitpunkt erreichte der Indikator sein Minimum, aber das sicherstellen können wir nach ein paar Bars später (Abb. 4).

 
in Abb. 4. Erst zehn Bars später hat der Zickzack ein neues Intervall nach oben gezeichnet und es ist über die Bildung einer Tiefe bekannt geworden

Eine vollständigere Beschreibung dieser Besonderheit von ZigZag kann die Abb. 5. Auf diesem Bild wurden farbige Punkte durch Bars markiert, auf dem es über die Bildung von ihren vorherigen Spitzen oder Tiefen bekannt worden sind. Für Bars mit einem blauen Punkt begann der Indikator neue Intervalle nach oben zu zeichnen, und mit roten Punkten nach unten.


 in Abb. 5. Mit roten und blauen Punkten wurden die Bars markiert, auf den es über die Wende des ZigZags bekannt worden ist   

Trotz dieser Besonderheit verliert der Zigzag nicht seine Popularität und Attraktivität. Zumindest erleichtert er sehr die visuelle Analyse von Charts, hilft den Rest zu filtern und rechnet die Hauptrichtung der Kursbewegung. In einer praktischen Aktivität kann der Indikator für die Rechnung der Unterstützung / Widerstand Levels verwendet werden, auch Muster zu erkennen. Außerdem kann man auf seiner Grundlage Trendlinien zeichnen, sowie alle anderen grafischen Tools für die technische Analyse wie fiboebene, fiboveera u.s.w. Es ist unmöglich, sie alle aufzuzählen, darum hat der Trader-Geist erfunden, den ZickZack zu verwenden.

Die Konstruktionsvarianten des Zigzags

Es ist offensichtlich, dass der ZickZack zwei Zustände hat: er wird entweder nach oben gerichtet oder nach unten. Wenn die Indikatorlinie nach oben gerichtet ist - beobachten wir den Preis, für den Fall der Entstehung eines neuen Maximums, und wenn die Linie nach unten "schaut" - erwarten wir die Entstehung einer neuen Tiefe. Das heißt, um einen ZickZack zu bauen, brauchen wir drei Dinge.

  1. Wie müssen die Anfangsdaten erhalten. 
  2. Man muss die Bedingungen für die Änderung der Linie-Richtung formulieren.  
  3. Man muss auf die Entstehung der neuen Maximums und Minimums aufpassen.

Die Anfangsdaten können in Form von einer Reihe sein - zum Beispiel, der Exit-Preis des Bars oder zwei Reihen - zum Beispiel die maximalen und minimalen Preise der Bar. Wenn nur eine Reihe von Daten verwendet wird, kann dies nicht nur der Exit-Preis sein, sondern praktisch jeder Indikator, ob es um Oszillator geht, oder um gleitenden Mittelwert. Beim Bau des ZickZacks kann man nach den Daten des Indikators auch zwei Reihen von Daten verwenden - eine Reihe von Daten des Indikators, der auf den höchsten Preisen der Bar gebaut wurde, der andere - auf niedrigsten Preisen. 

Die Bedingungen der Richtungswende - ist der wichtigste Punkt, der die verschiedenen Arten von Zickzacks definiert. Diese Bedingungen können sehr verschiedene sein. Zum Beispiel, als eine solche Bedingung kann die Entstehung eines Maximums/Minimums auf einer bildenden Bar gelten. In anderen Worten, wenn der Wert der ursprünglichen Reihe auf der bildenden Bar maximale oder minimale in den letzten n Bars sind, dann bestimmt es die Richtung des ZickZacks. Nach diesem Prinzip arbeitet der klassische Indikator ZigZag. Eine andere Methode - ist basierend auf dem Rollback des fixierenden maximalen oder minimalen Wertes. Die Größe des Rollbacks kann in Punkten (wenn die ursprüngliche Reihe der Preis ist) gemessen werden oder in herkömmlichen Einheiten (falls es ein Indikator ist). Mit diesen beiden Methoden ist es nicht beschränkt, es ist möglich, die Richtung von einem Indikator zu bestimmen - sowie auch über Stochastic, sowie über ADX. Wenn der Stochastik über 50 ist, bedeutet dies, dass Zickzack nach oben gerichtet ist, wenn es unter 50 ist - nach unten. Jetzt werden wir versuchen, die Richtung über ADX zu finden: die ZickZack-Linie ist nach oben gerichtet, wenn die PDI-Linie über der Linie MDI ist, und wenn es umgekehrt ist - nach unten. 

Auf diese Weise erhalten wir durch die Kombination von verschiedenen Varianten nach dem Punkt 1 und Punkt 2 eine lange Reihe von verschiedenen Varianten des ZigZags, denn nichts stoppt uns gemäß Punkt 1 die Daten zu verwenden, zum Beispiel von RSI, und die Richtung durch die Stochastik zu finden, usw. Den Punkt 3 braucht man nur deswegen, damit der Indikator ein Aussehen des Zigzags hat, obwohl die Varianten der Übernahme sehr unterschiedlich sein können. 

Da unser Ziel einen universellen Indikator zu bekommen ist, müssen wir möglichst sorgfältig den Algorithmus in zwei Teilen teilen: ein Teil, der für alle ZickZacks identisch ist (nennen wir es als ein allgemeiner Teil), und ein Teil, der abhängig von der Art des ZickZacks ist (nennen wir es als ein persönlicher Teil). Im persönlichen Teil werden die Indikator-Puffer mit Anfangsdaten ausgefüllt: mit Preis-Daten oder Indikator-Daten, und noch ein anderer Puffer (der die Richtung der Zigzag-Linie bestimmt) wird mit den Werten von 1 oder -1 ausgefüllt. Diese drei Puffer werden im allgemeinen Teil geordnet, der auf ihrer Grundlage selbst den Indikator zeichnet.

Um das deutlicher zu machen, erstellen wir zuerst einen separaten Indikator, der über die Preise high/low Bars funktioniert und der seine Richtung durch die Bedingung der n-Bar Maximums/Minimums ändert.

Der einfache ZigZag über high/low

Im Editor MetaEditor erstellen Sie einen neuen Indikator (Menü — Datei — Neu oder die Taster Ctrl+N). Im Assistenten geben Sie den Namen des neuen Indikators "iHighLowZigZag", erstellen Sie einen externen Parameter "period" (ein int-Typ, der Wert 12), wählen Sie den Ereignisbearbeiter OnCalculate (..., open,high,low,close), erstellen Sie einen Puffer namens "ZigZag"(Typ Section, die Farbe Red) und drei Puffer namens "Direction","LastHighBar" und "LastLowBar"(Typ line, die Farbe none).

Der Puffer "ZigZag" wird für die Anzeige des ZickZacks verwendet, die anderen Puffer sind Hilfspuffer. In der Funktion OnInit() für alle Hilfspuffer beim Aufruf der Funktion SetIndexBuffer() ersetzen Sie den Typ INDICATOR_DATA um INDICATOR_CALCULATIONS. Am oberen Teil der Datei ändern Sie den Wert Eigenschaften indicator_plots: Stellen Sie den Wert 1. Danach wird der Indikator nur einen Puffer "ZigZag" zeichnen und im Chart werden keine übrigen Linien, aber zugleich werden zusätzliche Puffer verfügbar sein, um sie durch die Funktion iCustom() aufzurufen. 

Zunächst in der Funktion OnCalculate() berechnen wir den Index der Bar so, von dem die Berechnung anfangen muss (die Variable start), damit die Berechnung aller Bars nur beim Start des Indikators ausgeführt wird, und weiterhin würde nur jede neu Bar berechnet. Außerdem initialisieren wir die Anfangselementen des Puffers:

  int start; // die Variable für das Index der Bar, von dem die Berechnung anfangen muss
  if(prev_calculated==0)
    { //beim Aufruf
     // Das Initialisieren der Anfangselementen des Puffers
     DirectionBuffer[0]=0;
     LastHighBarBuffer[0]=0;
     LastLowBarBuffer[0]=0;
     start=1; // Die Berechnung von nächsten Elementen nach den initialisierenden Elementen
    }
  else
    { // Während des Betriebs
     start=prev_calculated-1;
    }
}

Nun, die wichtigste Indikatorsloop:

for(int i=start;i<rates_total;i++)
     {

Wie oben beschrieben wurde, um die Universalität des Codes zu erreichen, sollte man den Code auf die Berechnung der Richtung des ZickZacks und auf seine Zeichnung teilen. Wir werden heute dieses Prinzip halten. Schreiben wir zuerst den Code, um die Richtung zu bestimmen. Um die Richtung zu bestimmen, verwenden wir die Funktionen ArrayMaximum() und ArrayMinimum(). Wenn das Maximum oder Minimum auf der berechneten Bar gefunden wird, wird dem Element des Puffer Direction der Wert 1 oder -1 zugeordnet. Im jeder Bar Informationen über die aktuelle ZickZack-Richtung zu haben, bevor eine bestimmte Richtung gefunden wird, nehmen wir den Wert aus dem vorherigen Pufferelement Direction und ordnen ihn auf das aktuelle Element zu:

// Aus dem vorherigen Pufferelement erhalten wir
// den Wert der frühen definierten Richtung
   DirectionBuffer[i]=DirectionBuffer[i-1];

// Die Berechnung der Anfangs-Bar für die Funktionen
// ArrayMaximum() und ArrayMinimum()
   int ps=i-period+1;
// Die Bestimmung der Bars Maximums und Minimums auf
// period Bars
   int hb=ArrayMaximum(high,ps,period);
   int lb=ArrayMinimum(low,ps,period);

// wenn Maximums und Minimums gefunden wurden
   if(hb==i && lb!=i)
     { // Maximum gefunden
      DirectionBuffer[i]=1;
     }
   else if(lb==i && hb!=i)
     { // Minimum gefunden
      DirectionBuffer[i]=-1;
     }

Achten Sie auf das letzte Stück des Codes: da wird ein Minimum oder Maximum gefunden, wird eine Überprüfung durchgeführt, um sicherzustellen, damit auf der aktuellen Bar ein Maximum wird, dabei kein Minimum oder umgekehrt: damit auf der aktuellen Bar ein Minimum wird, dabei kein Maximum. Manchmal gibt es sehr lange Bars, und auf sie werden beide Richtungen bestimmt. In diesem Fall wird im Puffer Direction die zuvor definierte Richtung sein.

Im Allgemeinen kann das Terminal MetaTrader5 einen ZickZack erstellen, der vertikale Intervalle zeichnet, und auf einer Bar können zwei Richtungsänderungen des Indikators angezeigt werden. Allerdings haben wir in diesem Artikel nicht diese Art von ZigZags betrachten. 

Wir werden weiterhin den Code in der Hauptloop schreiben: das folgende Fragment wird für das Zeichnen der Zigzag-Linien verantwortlich sein. Genauso, wie wir den Puffer Direction verwendet haben, verwenden wir die anderen zwei Puffer:

LastHighBarBuffer[i]=LastHighBarBuffer[i-1];
LastLowBarBuffer[i]=LastLowBarBuffer[i-1];  

In diesen Puffern werden die Daten über Index der Bars mit den letzten Maximums oder Minimums des Zigzags platziert. Neben der Tatsache, dass die Indexe dieser Bars für das Zeichnen des Indikators nötig sind, machen diese Puffer erheblich leichter den Aufruf des ZickZacks aus dem Experten. Wir müssen nicht mehr Bars auf der Suche nach der letzten Spitze sortieren.

Nötig ist, dass wir den Puffer für ZigZag räumen:

ZigZagBuffer[i]=EMPTY_VALUE;  

Dies muss gemacht werden, weil die volle Berechnung des Indikators nicht nur beim Start stattfindet, sondern auch bei einigen anderen Ereignissen - zum Beispiel, wenn die History geladen wird. Im Puffer können alte Daten sein, die den Blick auf die Indikator-Linien stören werden.  

Wir gehen nun direkt auf die Zeichnung. Hier wird der Algorithmus in vier Zweigen unterteilt: der Beginn einer neuen Aufwärtsbewegung, der Beginn einer neuen Abwärtsbewegung, die Fortsetzung der Aufwärtsbewegung, die Fortsetzung der Abwärtsbewegung. Um die Werte der Richtung des vorherigen und berechneten Bars zu überprüfen, verwenden wir die Operators switch:

switch((int)DirectionBuffer[i])
  {
   case 1:
      switch((int)DirectionBuffer[i-1])
        {
         case 1:
            // die Fortsetzung der Aufwärtsbewegung
            ...
            break;
         case -1:
            // der Beginn der neuen Aufwärtsbewegung
            ...
            break;
        }
      break;
   case -1:
      switch((int)DirectionBuffer[i-1])
        {
         case -1:
            // die Fortsetzung der Abwärtsbewegung
            ...
            break;
         case 1:
            // der Beginn der neuen Abwärtsbewegung    
            ...
            break;
        }
      break;

Jetzt müssen wir noch 4 Codeblöcke schreiben. Betrachten wir genauer 2 von ihnen: der Beginn einer neuen Aufwärtsbewegung und die Fortsetzung der Aufwärtsbewegung. Der Beginn der neuen Aufwärtsbewegung findet statt, wenn der Wert im Puffer Direction von -1 auf 1 ändert. Dabei zeichnen wir einen neuen Punkt des ZickZacks und wir speichern die Informationen über den Index der Bar, auf dem die neue Richtung begonnen hat:

ZigZagBuffer[i]=high[i];
LastHighBarBuffer[i]=i;

Eine fortgesetzte Bewegung ein wenig komplizierter. Es prüft, ob der Wert auf der aktuellen Bar nicht größer des bisherigen bekannten maximalen Wertes ZickZack sei. Wenn es mehr ist, dann muss das Ende des letzten Intervalls bewegt werden, das heißt, es muss der zuvor gezeichnete Punkt gelöscht werden und ein neuer Punkt gesetzt werden. Hier speichern wir auch Informationen über die Bar, auf dem ein neuer Punkt gezeichnet wird:

// die Fortsetzung der Aufwärtsbewegung
   if(high[i]>high[(int)LastHighBarBuffer[i]])
     { // das neue Maximum
      // den alten Punkt des Zigzags löschen
      ZigZagBuffer[(int)LastHighBarBuffer[i]]=EMPTY_VALUE;
      // Setzen Wir den neuen Punkt
      ZigZagBuffer[i]=high[i];
      // Der Index der Bar mit der neuen Spitze
      LastHighBarBuffer[i]=i;
     }

Und das war es. Vergessen Sie nicht die Loop mit den schließenden Klammern zu schließen. Der Indikator muss jetzt im Tester in einem visuellen Modus getestet werden. Der Indikator "iHighLowZigZag"ist einsatzbereit, und den kann man im Anhang finden.

Der einfache ZigZag über close

Nun wandeln wir den eben erstellten Indikator für die Arbeit über den Preis close um. Es ist nicht notwendig, alles neu zu schreiben: Speichern wir den Indikator  "iHighLowZigZag" namens "iCloseZigZag" und ersetzen den Aufruf zu Arrays high und low um den Aufruf zu Arrays close. Es scheint, dass man damit die Arbeit beenden kann, aber der Test zeigt die Fehlfunktion des Indikators (in Abb. 6).

 
in Abb. 6. Die Fehlfunktion Zigzag über close, der aus dem Indikator über high/low umgewandelt wurde

Denken wir nach, wieso dies so geschieht ist. Wenn der Preis high der bildenden Bar ein bestimmtes Maximum auf einem Intervall von Bars gebildet hat, dann, egal wie sich der Schlusskurs der Bar geändert hat, wird dieses Maximum als Maximum bleiben. Wenn das Maximum einen Schlusskurs gebildet hat, kann das sich während der weiteren Bildung des Bars ändern und das Maximum wird nicht mehr existieren. Bei der Bestimmung eines neuen Maximums / Minimums der gleichen Richtung wird die Löschung des alten Punktes durchgeführt - hier lauert auf uns ein Problem. Ein neues Maximum wurde abgebrochen, ein neuer Punkt wird gelöscht, aber der alte auch gelöscht. So müssen wir die Position des alten Punktes wiederherstellen. Die Information über den Standort der letzten Extremes in Puffer sind: LastHighBarBuffer und LastLowBarBuffer. Von diesen werden wir die letzten beiden Punkte wiederherstellen. Zur Hauptloop des Indikators vor dem Operator switch fügen wir zwei Zeilen des Codes hinzu:

ZigZagBuffer[(int)LastHighBarBuffer[i]]=close[(int)LastHighBarBuffer[i]];
ZigZagBuffer[(int)LastLowBarBuffer[i]]=close[(int)LastLowBarBuffer[i]];  

Nach dieser Aktion wird der Indikator korrekt funktionieren. Der erhaltende Indikator "iCloseZigZag" finden Sie im Anhang dieses Artikels. 

Der Anfang der Erstellung des universellen ZigZags

Die Universalität des Zigzags wird durch die getrennte Lösung von drei Aufgaben erreicht:
  1. Die Erfüllung der Puffer den Anfangsdaten. Es werden 2 Puffer benutzt. Wir benötigen sie, um sie mit den Preisen high und low erfüllen zu können. Um den Zigzag über close oder über einen anderen Indikator zu erhalten, können beide Puffer mit den gleichen Werten gefüllt werden. 
  2. Die Erfüllung des Puffers Direction basierend auf der Analyse der Anfangsdaten.
  3. Das Zeichen des ZigZags.
Jede Aufgabe wird mit ihrer individuellen Basisklasse gelöst und mit zusätzlichen Tochterklassen, was eine Auswahl von verschiedenen Varianten und Kombinationen durch das Fenster der Indikator-Eigenschaften anbietet.

Die Klasse der ursprünglichen Daten

Erstellen Sie eine hinzugefügte Datei "CSorceData.mqh" und fügen Sie zu dieser Datei die Klasse CSorceData hinzu. Er wird die Elternklasse sein. Er wird eine virtuelle Methode Calculate enthalten, die ähnliche Funktion OnCalculate() des Indikators, aber mit einigen Änderungen. Die Methode werden von zwei zusätzlichen Arrays übergeben: BufferHigh[] und BufferLow[]. Diese Puffer werden mit Daten erfüllt, nach denen in Zukunft Zigzag berechnet wird. Da die Anfangsdaten nicht nur der Preis sein kann, sondern auch der Wert eines anderen Indikators, müssen Sie dann die Ladung des Indikators kontrollieren. Dafür fügen wir die virtuelle Methode CheckHandle() (der Typ bool) hinzu:

class CSorceData
  {
private:
public:
   virtual int Calculate(const int rates_total,
                         const int prev_calculated,
                         const datetime &time[],
                         const double &open[],
                         const double &high[],
                         const double &low[],
                         const double &close[],
                         const long &tick_volume[],
                         const long &volume[],
                         const int &spread[],
                         double &BufferHigh[],
                         double &BufferLow[])
     {
      return(0);
     }
   virtual bool CheckHandle()
     {
      return(true);
     }

  };

Jetzt erstellen wir ein paar Tochterklassen. Eine — für den Preis high/low:

class CHighLow:public CSorceData
  {
private:
public:
   int Calculate(const int rates_total,
                 const int prev_calculated,
                 const datetime &time[],
                 const double &open[],
                 const double &high[],
                 const double &low[],
                 const double &close[],
                 const long &tick_volume[],
                 const long &volume[],
                 const int &spread[],
                 double &BufferHigh[],
                 double &BufferLow[])
     {
      int start=0;
      if(prev_calculated!=0)
        {
         start=prev_calculated-1;
        }
      for(int i=start;i<rates_total;i++)
        {
         BufferHigh[i]=high[i];
         BufferLow[i]=low[i];
        }
      return(rates_total);
     }
  };

 die Zweite — für den Preis close. Er wird sich nur mit dem Code in der Loop unterscheiden:

for(int i=start;i<rates_total;i++)
  {
   BufferHigh[i]=close[i];
   BufferLow[i]=close[i];
  }

Der Name dieser Klasse ist "CClose:public CSorceData". Die Methode CheckHandle() wird noch nicht verwendet.

Erstellen Sie noch ein paar Klassen, um Daten von diesen Indikatoren zu erhalten. Wir wählen Indikatoren mit einer unterschiedlichen Anzahl von Parametern und verschiedenen Standorten (am Chart oder in einem separaten Fenster) - RSI und gleitender Mittelwert. Unter ihnen schreiben wir dann eben unsere Klassen.

Erstellen wir eine Klasse für RSI, geben wir ihm der Name "CRSI:public CSorceData". Im Feld private fügen wir die Variable für "Handel" des Indikators hinzu:

   private:
      int m_handle;

Fügen wir einen Konstruktor hinzu: darin wird die Parameter RSI übergeben, und in ihm wird die Ladung des Indikators durchgeführt:

void CRSI(int period,ENUM_APPLIED_PRICE price)
  {
   m_handle=iRSI(Symbol(),Period(),period,price);
  }

Jetzt die Methode CheckHandle():

bool CheckHandle()
  {
   return(m_handle!=INVALID_HANDLE);
  }

In der Methode Calculate werden wir die Loop nicht verwenden, kopieren wir einfach die Puffer:

int to_copy;
   if(prev_calculated==0)
     {
      to_copy=rates_total;
     }
   else
     {
      to_copy=rates_total-prev_calculated;
      to_copy++;
     }

   if(CopyBuffer(m_handle,0,0,to_copy,BufferHigh)<=0)
     {
      return(0);
     }

   if(CopyBuffer(m_handle,0,0,to_copy,BufferLow)<=0)
     {
      return(0);
     }
   return(rates_total);

Bitte beachten Sie: Im Falle einer fehlgeschlagenen Kopie (des Aufrufs der Funktion CopyBuffer()) setzt die Methode 0 zurück, und falls es erfolgreich war — rates_total. Dies wird getan, um dann die Neuberechnung des Indikators machen zu können, falls es fehlerhaft durchgeführt wird. 

Auf ähnlicher Weise erstellen wir eine Klasse für den gleitenden Mittelwert namens "CMA:public CSorceData". Die Unterschiede werden nur im Konstruktor:

void CMA(int period,int shift,ENUM_MA_METHOD method,ENUM_APPLIED_PRICE price)
   {
    m_handle=iMA(Symbol(),Period(),period,shift,method,price);
   }

Die Methoden Calculate() sind in diesem Fall gleich geworden, aber auch für andere Indikatoren können einige Unterschiede sein, insbesondere mit den Zahlen der Puffer. Die Datei "CSorceData.mqh" ist einsatzbereit, und die kann man im Anhang zum Artikel finden. Zwar sie einsatzbereit zu sagen, kann man nur relativ, da sie durch das Hinzufügen neuer Tochtermethoden für andere Indikatoren eine weitere Expansion impliziert.

Die Klasse der Richtung

Die Klasse wird in der Datei "CZZDirection.mqh" sein, der Name der Basisklasse — "CZZDirection". Bei dieser Klasse wird die virtuelle Methode Calculate() sein, in dem die Parameter übertragen werden, die die Bar für die Berechnung (Variablen rates_total, prev_calculated) bestimmen lassen, die Puffer mit Anfangsdaten und Puffer für die Richtung. Zuvor wurde es bereits erwähnt, dass die Richtung des ZickZacks durch den Indikator gefunden werden kann, von daher bieten wir die Möglichkeit an, Indikatoren zu verwenden. Dafür fügen wir die virtuelle Methode CheckHandle()hinzu:

class CZZDirection
  {
private:
public:
   virtual int Calculate(const int rates_total,
                         const int prev_calculated,
                         double &BufferHigh[],
                         double &BufferLow[],
                         double &BufferDirection[])
     {
      return(0);
     }
   virtual bool CheckHandle()
     {
      return(true);
     }
  };

Schreiben wir nun die Tochterklasse, um die Richtung zu bestimmen, wie es im Indikator "iHighLowZigZag" ist. Um die Richtung dieser Methode zu bestimmen, wird der Parameter "period" erforderlich, von daher fügen wir im Feld private eine Variable m_period und den Konstruktor mit dem Parameter der Periode hinzu:

class CNBars:public CZZDirection
  {
private:
   int               m_period;
public:
   void CNBars(int period)
     {
      m_period=period;
     }
   int Calculate(const int rates_total,
                 const int prev_calculated,
                 double &BufferHigh[],
                 double &BufferLow[],
                 double &BufferDirection[]
                 )
     {
      int start;

      if(prev_calculated==0)
        {
         BufferDirection[0]=0;
         start=1;
        }
      else
        {
         start=prev_calculated-1;
        }

      for(int i=start;i<rates_total;i++)
        {

         BufferDirection[i]=BufferDirection[i-1];

         int ps=i-m_period+1;
         int hb=ArrayMaximum(BufferHigh,ps,m_period);
         int lb=ArrayMinimum(BufferLow,ps,m_period);

         if(hb==i && lb!=i)
           { // Maximum gefunden
            BufferDirection[i]=1;
           }
         else if(lb==i && hb!=i)
           { // Minimum gefunden
            BufferDirection[i]=-1;
           }

        }
      return(rates_total);
     }

Eine weitere Tochterklasse erstellen wir für die Bestimmung der Richtung vom Indikator CCI. Die Position CCI über null entspricht der Richtung ZigZag nach oben, die Position unter Null - nach unten:

class CCCIDir:public CZZDirection
   {
private:
    int               m_handle;
public:
    void CCCIDir(int period,ENUM_APPLIED_PRICE price)
      {
       m_handle=iCCI(Symbol(),Period(),period,price);
      }
    bool CheckHandle()
      {
       return(m_handle!=INVALID_HANDLE);
      }
    int Calculate(const int rates_total,
                  const int prev_calculated,
                  double &BufferHigh[],
                  double &BufferLow[],
                  double &BufferDirection[]
                  )
      {
       int start;
       if(prev_calculated==0)
         {
          BufferDirection[0]=0;
          start=1;
         }
       else
         {
          start=prev_calculated-1;
         }

       for(int i=start;i<rates_total;i++)
         {

          BufferDirection[i]=BufferDirection[i-1];

          double buf[1];
          if(CopyBuffer(m_handle,0,rates_total-i-1,1,buf)<=0)return(0);

          if(buf[0]>0)
            {
             BufferDirection[i]=1;
            }
          else if(buf[0]<0)
            {
             BufferDirection[i]=-1;
            }
         }
       return(rates_total);
      }
   };

Im Konstruktor der Klasse werden Parameter CCI übergeben und es wird seine Ladung ausgeführt. Es wird die Methode CheckHandle() benutzt, die nach der Erstellung des Objektes aufgerufen wird. In der Hauptloop wird die Überprüfung CCI durchgeführt und die Erfüllung des Puffers BufferDirection.

Die Datei "CZZDirection.mqh" kann man im Anhang zum Artikel finden. 

Die Zeichenklasse

Es gibt verschiedene Varianten der Zeichnung des Zickzacks. Sie können es mit einer Linie zeichnen, es ist auch möglich, zu malen, Punkte auf Spitzen zu setzen usw. In diesem Artikel werden wir uns auf eine Art und Weise der Zeichnung beschränken, aber wir erstellen auch Basisklassen und Tochterklassen, im Falle einer weiteren Bearbeitung. Die Klasse wird in der Datei "CZZDraw.mqh" sein, der Name der Klasse — "CZZDraw". Die Klasse wird eine virtuelle Methode Calculate() mit den gleichen Parametern haben, wie die Klasse der Richtung. Darüber hinaus wird es darin drei Arrays für Zigzag übertragen: InufferLastHighBar (für den Index des letzten Maximums), BufferLastLowBar (für den Index des letzten Minimums), BufferZigZag (eigentlich,selbst ZickZack). 

class CZZDraw
  {
private:
public:
   virtual int Calculate(const int rates_total,
                         const int prev_calculated,
                         double &BufferHigh[],
                         double &BufferLow[],
                         double &BufferDirection[],
                         double &BufferLastHighBar[],
                         double &BufferLastLowBar[],
                         double &BufferZigZag[]
                         )
     {
      return(0);
     }
  };
Tochterklasse: 
class CSimpleDraw:public CZZDraw
   {
private:
public:
    virtual int Calculate(const int rates_total,
                          const int prev_calculated,
                          double &BufferHigh[],
                          double &BufferLow[],
                          double &BufferDirection[],
                          double &BufferLastHighBar[],
                          double &BufferLastLowBar[],
                          double &BufferZigZag[]
                          )
      {
       int start;
       if(prev_calculated==0)
         {
          BufferLastHighBar[0]=0;
          BufferLastLowBar[0]=0;
          start=1;
         }
       else
         {
          start=prev_calculated-1;
         }

       for(int i=start;i<rates_total;i++)
         {
          BufferLastHighBar[i]=BufferLastHighBar[i-1];
          BufferLastLowBar[i]=BufferLastLowBar[i-1];

          BufferZigZag[i]=EMPTY_VALUE;

          BufferZigZag[(int)BufferLastHighBar[i]]=BufferHigh[(int)BufferLastHighBar[i]];
          BufferZigZag[(int)BufferLastLowBar[i]]=BufferLow[(int)BufferLastLowBar[i]];

          switch((int)BufferDirection[i])
            {
             case 1:
                switch((int)BufferDirection[i-1])
                  {
                   case 1:
                      if(BufferHigh[i]>BufferHigh[(int)BufferLastHighBar[i]])
                        {
                         BufferZigZag[(int)BufferLastHighBar[i]]=EMPTY_VALUE;
                         BufferZigZag[i]=BufferHigh[i];
                         BufferLastHighBar[i]=i;
                        }
                      break;
                   case -1:
                      BufferZigZag[i]=BufferHigh[i];
                      BufferLastHighBar[i]=i;
                      break;
                  }
                break;
             case -1:
                switch((int)BufferDirection[i-1])
                  {
                   case -1:
                      if(BufferLow[i]<BufferLow[(int)BufferLastLowBar[i]])
                        {
                         BufferZigZag[(int)BufferLastLowBar[i]]=EMPTY_VALUE;
                         BufferZigZag[i]=BufferLow[i];
                         BufferLastLowBar[i]=i;
                        }
                      break;
                   case 1:
                      BufferZigZag[i]=BufferLow[i];
                      BufferLastLowBar[i]=i;
                      break;
                  }
                break;
            }
         }
       return(rates_total);
      }
   };
Die Klasse in Details werden wir nicht betrachten, dies alles wurde schon in Abschnitten "Der einfache ZigZag über high/low" und "Der einfache ZigZag close" beschrieben. Die Datei "CZZDraw.mqh" kann man im Anhang zum Artikel finden.

Wir verbinden drei Klassen zusammen

Schließlich muss man einen Indikator mit drei Klassen schreiben, die oben erstellt wurden. Die Klasse der Anfangsdaten bietet die Möglichkeit an, diese Preise und die RSI-Indikator-Daten zu verwenden, der in der Regel in einem Unterfenster arbeitet. Die Preisdaten können im Fensterbereich angezeigt werden, aber den Indikator RSI am Chart nicht. So werden wir einen Indikator für das Unterfenster erstellen. 

Im Editor MetaEditor erstellen Sie einen neuen Indikator (Menü — Datei — Neu oder die Taster Ctrl+N). Im Assistenten geben Sie den Namen des neuen Indikators "iUniZigZagSW", erstellen Sie einen externen Parameter "period" (ein int-Typ, der Wert 12), wählen Sie den Ereignisbearbeiter OnCalculate (..., open,high,low,close), erstellen Sie die folgenden Puffer: 

TitelStilFarbe
HighLineGreen
LowLineGreen
ZigZagSectionRed
DirectionLinenone
LastHighBarLine none 
LastLowBarLinenone
Nachdem Sie einen neuen Indikator erstellt haben, verbinden Sie zu ihm drei Klassen: 
#include <CSorceData.mqh>
#include <CZZDirection.mqh>
#include <CZZDraw.mqh>

Beim Indikator sollten die Parameter für die Auswahl des Typs der Anfangsdaten sein und des Bestimmungstyps der Richtung. Erstellen wir dafür zwei Auflistungen:

enum ESorce
  {
   Src_HighLow=0,
   Src_Close=1,
   Src_RSI=2,
   Src_MA=3
  };
enum EDirection
  {
   Dir_NBars=0,
   Dir_CCI=1
  };

Erstellen wir zwei externe Parameter dieser Typen: 

input ESorce      SrcSelect=Src_HighLow;
input EDirection  DirSelect=Dir_NBars;

Für die Anfangsdaten RSI und MA müssen die entsprechenden Parameter sein, sowie für den Indikator CCI. Fügen wir sie hinzu:

input int                  RSIPeriod   =  14;
input ENUM_APPLIED_PRICE   RSIPrice    =  PRICE_CLOSE;
input int                  MAPeriod    =  14;
input int                  MAShift     =  0;
input ENUM_MA_METHOD       MAMethod    =  MODE_SMA;
input ENUM_APPLIED_PRICE   MAPrice     =  PRICE_CLOSE;
input int                  CCIPeriod   =  14;
input ENUM_APPLIED_PRICE   CCIPrice    =  PRICE_TYPICAL;

Wir brauchen noch einen Parameter für die Bestimmung der Richtung über n-bars:

input int                  ZZPperiod   =  14;

Jetzt ist es was Interessantes - drei Anzeiger sind entsprechend mit Typs der Basisklassen (unter der externen Parameter):

CSorceData * src;
CZZDirection * dir;
CZZDraw * zz;

In der Funktion OnInit, entsprechend mit der Auswahl der Variablen SrcSelect und DirSelect, laden wir die entsprechenden Tochterklassen hoch. Zunächst SrcSelect:

switch(SrcSelect)
  {
   case Src_HighLow:
      src=new CHighLow();
      break;
   case Src_Close:
      src=new CClose();
      break;
   case Src_RSI:
      src=new CRSI(RSIPeriod,RSIPrice);
      break;
   case Src_MA:
      src=new CMA(MAPeriod,MAShift,MAMethod,MAPrice);
      break;
  }

Nach der Ladung überprüfen wir "Handel":

if(!src.CheckHandle())
  {
   Alert("Fehler bei der Ladung des Indikators");
   return(INIT_FAILED);
  }

weiter DirSelect:

switch(DirSelect)
  {
   case Dir_NBars:
      dir=new CNBars(ZZPeriod);
      break;
   case Dir_CCI:
      dir=new CCCIDir(CCIPeriod,CCIPrice);
      break;
  }

Überprüfung "Handel":

if(!dir.CheckHandle())
  {
   Alert("Fehler bei der Ladung des Indikators 2");
   return(INIT_FAILED);
  }

Die dritte Klasse:

zz = new CSimpleDraw();

In der Funktion OnDeinit() löschen wir Objekten:

void OnDeinit(const int reason)
  {
   if(CheckPointer(src)==POINTER_DYNAMIC)
     {
      delete(src);
     }
   if(CheckPointer(dir)==POINTER_DYNAMIC)
     {
      delete(dir);
     }
   if(CheckPointer(zz)==POINTER_DYNAMIC)
     {
      delete(zz);
     }
  }

Schließlich ist der letzte Schritt - Umstieg in die Funktion OnCalculate(). Die Methoden Calculate() der Klassen CSorceData und CZZDirection können auf 0 zurücksetzen, deshalb überprüfen wir das Ergebnis. Beim Fehler (es wurde 0 erhalten), setzen wir 0 zurück, damit es am nächsten Tick die Neuberechnung stattfindet:

int rv;

rv=src.Calculate(rates_total,
                 prev_calculated,
                 time,
                 open,
                 high,
                 low,
                 close,
                 tick_volume,
                 volume,
                 spread,
                 HighBuffer,
                 LowBuffer);

if(rv==0)return(0);

rv=dir.Calculate(rates_total,
                 prev_calculated,
                 HighBuffer,
                 LowBuffer,
                 DirectionBuffer);

if(rv==0)return(0);

zz.Calculate(rates_total,
             prev_calculated,
             HighBuffer,
             LowBuffer,
             DirectionBuffer,
             LastHighBarBuffer,
             LastLowBarBuffer,
             ZigZagBuffer);

return(rates_total);

Der Indikator "iUniZigZagSW" finden Sie im Anhang dieses Artikels.

Die Variante für das Preischart

Im resultierenden Indikator sind alle vorher erstellten Varianten verfügbar, sowie mit Datenquellen, die dem Preischart entsprechen, auch für Unterfenster, deshalb wurde er für das Unterfenster erstellt. Ich möchte den ZickZack auch im Preischart sehen können. In diesem Fall ist es notwendig, die Datenquelle von RSI zu opfern. Erstellen wir eine Kopie des Indikators mit dem Namen "iUniZigZag", ändern die Eigenschaften von indicator_separate_window um indicator_chart_window, aus der Aufzählungs ESorce löschen wir die Variante Src_RSI, löschen wir die Variante aus dem RSI von der Funktion OnInit() und erhalten die Variante für das Preis-Chart. Der Indikator "iUniZigZag" finden Sie im Anhang dieses Artikels.   

Die Variante für price

Für das Terminal MetaTrader ist es möglich, die Indikatoren zu erstellen, die nicht auf genau definierten Quelldaten arbeiten, sondern auf einem anderen Indikator, der im Chart ist. Bei diesem Indikator, wenn Sie ihn zum Chart oder zum Unterfenster der Parameter hinzufügen, müssen Sie die Variante "Die Daten des vorherigen Indikators" auswählen oder "Daten des ersten Indikators". Ändern wir den Indikator "iUniZigZagSW" so, damit er zu einem anderen Indikator hinzugefügt werden kann. Speichern Sie den Indikator unter dem Namen "iUniZigZagPriceSW" und löschen Sie alles, was mit der Klasse CSorceData zu tun hat, ändern Sie den Typ der Funktion OnCalculate und am Anfang der Funktionen schreiben Sie die Loop für die Erfüllung der Puffer HighBuffer und LowBuffer mit den Werten des Arrays price:

int OnCalculate(const int rates_total,
                const int prev_calculated,
                const int begin,
                const double &price[]
                )
  {
   int start;
   if(prev_calculated==0)
     {
      start=0;
     }
   else
     {
      start=prev_calculated-1;
     }

   for(int i=start;i<rates_total;i++)
     {
      HighBuffer[i]=price[i];
      LowBuffer[i]=price[i];
     }
   int rv;
   rv=dir.Calculate(rates_total,
                    prev_calculated,
                    HighBuffer,
                    LowBuffer,
                    DirectionBuffer);

   if(rv==0)return(0);
   zz.Calculate(rates_total,
                prev_calculated,
                HighBuffer,
                LowBuffer,
                DirectionBuffer,
                LastHighBarBuffer,
                LastLowBarBuffer,
                ZigZagBuffer);
   return(rates_total);
  }

Ebenso können Sie eine Variante erstellen, die aus dem price am Chart läuft. Dafür ist es genung, beim Indikator "iUniZigZagPriceSW" die Eigenschaft  indicator_separate_window на indicator_chart_window zu ändern. Der Indikator "iUniZigZagPriceSW" finden Sie im Anhang, da finden Sie auch den Indikator iUniZigZagPrice - eine Variante von price für das Preis-Chart. 

Der Aufruf von einem Experten

Normalerweise beim Aufruf ZigZag aus dem Experten wird die Suche der letzten Spitze oder Tiefe über die Loop durchgeführt, es werden Bars sortiert und die Werte im Puffer überprüft, der Zigzag zeichnet. All dies zusammen ist sehr langsam. Der Zigzag, der in diesem Artikel entwickelt wurde, hat zusätzliche Puffer, die schnell alle notwendigen Daten erhalten lässt. Im Puffer DirectionBuffer gibt es Daten über die Richtung des letzten Intervalls Zigzags. In Puffern LastHighBarBuffer und LastLowBarBuffer sind Index der Bars, auf denen die letzte Spitze und die letzte Tiefe gezeichnet sind. Mit dem Wissen des Index der Bar kann man beim Rechnen einerseits die Anzahl der Bars berechnen, anderseits kann man beim Rechnen den Index der Bar finden (Im Indikator zählt man von links nach rechts, und die Funktion CopyBuffer() funktioniert von rechts nach links). Mit dem Index der Bar können Sie den Wert des ZickZack auf dieser Bar erhalten.

Um die Daten vom Indikator zu erhalten, kann der folgende Code verwendet werden. Wir experimentieren mit dem Indikator "iUniZigZagSW". In der Funktion OnInit() laden wir den Indikator:

handle=iCustom(Symbol(),Period(),"iUniZigZagSW",SrcSelect,
               DirSelect,
               RSIPeriod,
               RSIPrice,
               MAPeriod,
               MAShift,
               MAMethod,
               MAPrice,
               CCIPeriod,
               CCIPrice,
               ZZPeriod);
In der Funktion OnTick() erhalten wir die Richtung und liefern ihn in die Kommentaren des Charts:
   string cs="";
// Richtung
   double dir[1];
   if(CopyBuffer(handle,3,0,1,dir)<=0)
     {
      Print("Der Empfangsfehler der Daten vom ZigZag");
      return;
     }
   if(dir[0]==1)
     {
      cs=cs+"Die Richtung nach oben";
     }
   if(dir[0]==-1)
     {
      cs=cs+"Die Richtung nach unten";
     }
   Comment(cs,"\n",GetTickCount());

Jetzt haben wir die Werte der letzten Spitzen /Tiefen bekommen. Wenn die Indikatorlinie nach oben zeigt, erhalten wir den Index des Bars aus der letzten Spitze, vom Puffer LastHighBarBuffer. Dann berechnen wir durch seine Verwendung den Index der Bar bei der Berechnung von rechts nach links. Mit der Verwendung dieses Index erhalten wir den Wert des Puffers ZigZagBuffer. Wir können noch weiter gehen: für die gleiche Bar, auf dem wir den Wert Zigzags bekommen haben, den Wert aus dem Puffer LastLowBarBuffer erhalten. Dies wird der Index der Bar aus der vorhergehenden Tiefe. Und so weiter, durch die Abwechslung zu Puffern LastHighBarBuffer und LastLowBarBuffer, können wir Daten über alle Spitzen / Tiefen der Indikator-Linie sammeln. Unten ist ein Beispiel des Codes, um die letzten beiden Punkte des ZickZacks zu erhalten, wenn es nach oben gerichtet ist: 

if(dir[0]==1)
  {
   // Der Index der Bar bei der Zählung von null Links
   if(CopyBuffer(handle,4,0,1,lhb)<=0)
     {
      Print("Der Empfangsfehler der Daten vom ZigZag 2");
      return;
     }
   // Der Index der Bar bei der Zählung rechts von null
   ind=bars-(int)lhb[0]-1;

   // Der Wert des ZickZacks auf der Bar ind
   if(CopyBuffer(handle,2,ind,1,zz)<=0)
     {
      Print("Der Empfangsfehler der Daten vom ZigZag 3");
      return;
     }
   //===
   // Der Index der Tiefe, die diese Spitze vorzeichnet
   if(CopyBuffer(handle,5,ind,1,llb)<=0)
     {
      Print("Der Empfangsfehler der Daten vom ZigZag 4");
      return;
     }
   // Der Index der Bar bei der Zählung rechts von null
   ind=bars-(int)llb[0]-1;

   // Der Wert des ZickZacks auf der Bar ind
   if(CopyBuffer(handle,2,ind,1,zz1)<=0)
     {
      Print("Der Empfangsfehler der Daten vom ZigZag 5");
      return;
     }

   cs=cs+"\n"+(string)zz1[0]+" "+(string)zz[0];
  }
else if(dir[0]==-1)
  {

  }

Ein vollständiges Beispiel finden Sie im Anhang zum Experten namens "eUniZigZagSW". Der Experten zeigt die Meldung in Kommentaren des Charts über die Richtung des ZickZacks und mit dem zweiten Satz - zwei Zahlen mit den Werten der letzten beiden Punkte des ZickZacks (Abbildung. 7). Der dritte Satz ist eine einfache Zahl, die von der Funktion GetTickCount() zurückgeliefert wird, damit es sichtbar wird, dass der Experte funktioniert.

 
in Abb. 7. In der linken Ecke der Rückmeldung, die von dem Experten angezeigt wird

Natürlich können die Daten über den letzten Punkte des Indikators von den Puffern LastHighBarBuffer und LastLowBarBuffer erhalten werden, wenn wir deren Werte beim nullwertigen oder bei der ersten Bar nehmen, aber der Sinn dieses Beispiels besteht darin - in einer sequentiellen Datenextraktion aus einer beliebigen Anzahl von Punkten Zigzag durchzuführen.

Der Aufruf von einem anderen Indikator

Wenn auf der Grundlage eines Indikators ein anderer Indikator gemacht werden muss, dann im Falle des ZickZacks ist es leichter, ohne Aufruf des Indikators durch iCustom() zu machen, sondern durch eine seine Kopie, die verarbeitet wird. In einigen Fällen lohnt sich dieser Ansatz (in Bezug auf Geschwindigkeit und Einfachheit der Ausführung dieser Verarbeitung), in einigen - nicht (in Bezug auf die Wiederverwendung und Flexibilität des Codes). Die Indikatoren, die in diesem Artikel erstellt wurden, können Sie über die Funktion iCustom bei der Entwicklung anderer Indikatoren aufrufen.

An sich selbst ist der Zickzack auf der History - ist es nicht das gleiche, wie es bei der Bildung dieser History war, aber wir haben die Puffer  LastHighBarBuffer und LastLowBarBuffer, in den die Daten über Zwischenzustände des Zickzacks gespeichert werden. Aus Gründen der Klarheit schreiben wir einen Indikator, der die Pfeile bei der Wende der Indikator-Linien zeichnet (die Änderung des Wertes DirectionBuffer) und der Markierungspunkte auf Bars, in denen neue Mximums/Minimums des Zigzags (Änderungen der Werte der Puffer  LastHighBarBuffer и LastLowBarBuffer) aufgenommen wurden. In Details betrachten wir den Code dieses Indikators nicht, im Anhang zu dem Artikel finden Sie ihn unter dem Namen "iUniZigZagSWEvents". Die Ansicht ist in Abb. 8.

 
in Abb. 8. Der Indikator iUniZigZagSWEvents

Fazit

Da der Artikel Trainingsmaterial erfasst, und keine Eingabe von fertigen und vollständigen Lösungen ist, die alle im Artikel erstellten Indikatoren haben eine minimale Auswahl von Anfangsdaten und der Typs-Bestimmungen dieser Richtung. Doch der Prozess der Erstellung der Indikatoren wurde in Details betrachtet, deswegen, wenn man den Artikel versteht, kann man selbstständig die nötigen Tochterklassen erstellen. Darüber hinaus kommen beim Versuch der Erstellung eines universellen Indikators einige Probleme, und nicht bei der Erstellung, sondern bei der Verwendung. Beim Hinzufügen der verschiedenen Indikatoren als Daten-Quellen oder für die Bestimmung der Richtung, muss man in Eigenschaften-Fenster diese Indikatoren hinzufügen. Am Ende ist die Anzahl der Parameter wird zu groß, und die Verwendung eines solchen Indikators wird sehr unbequem. Ergonomischer wird die Erstellung eigener Indikatoren sein, die durch universelle Indikatoren im Artikel erhalten wurden.       

Anwendungsdateien

  • iHighLowZigZag.mq5 — Der einfache ZigZag über high/low.
  • iCloseZigZag.mq5 — Der einfache ZigZag close.
  • CSorceData.mqh — Die Klasse für die Auswahl der ursprünglichen Daten.
  • CZZDirection.mqh — Die Klasse für die Bestimmung der Richtung vom Indikator ZigZag.
  • CZZDraw.mqh — Die Zeichenklasse ZigZag.
  • iUniZigZagSW.mq5 — Der universelle ZigZag für das Unterfenster.
  • iUniZigZag.mq5 — Der universelle ZigZag für das Preischart.
  • iUniZigZagPriceSW.mq5 — Der universelle ZigZag von price für das Unterfenster.
  • iUniZigZagPrice.mq5 — Der universelle ZigZag von price für das Preischart. 
  • eUniZigZagSW — das Beispiel des Aufrufs des Indikators "iUniZigZagSW" aus dem Experten durch die Funktion iCustom().
  • iUniZigZagSWEvents — das Beispiel der Erstellung eines anderen Indikators "iUniZigZagSW" durch die Funktion iCustom(). 

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

Beigefügte Dateien |
mql5.zip (18.42 KB)
Das Handelssystem 'Turtle Soup' und seine Modifikation 'Turtle Soup Plus One' Das Handelssystem 'Turtle Soup' und seine Modifikation 'Turtle Soup Plus One'

In diesem Artikel wurden Regeln der Handelsstrategien Turtle Soup und Turtle Soup Plus One aus dem Buch Street Smarts: High Probability Short-Term Trading Strategies von Linda Raschke und Laurence Connors formuliert und programmiert. Die im Buch beschriebenen Strategien sind relativ populär, man sollte aber beachten, dass die Autoren diese Strategien anhand eines 15...20 Jahre alten Marktverhaltens entwickelt haben.

Grundlagen der Programmierung in MQL5: Globale Variablen des Terminals Grundlagen der Programmierung in MQL5: Globale Variablen des Terminals

Globale Variablen des Terminals sind ein unverzichtbares Hilfsmittel für die Entwicklung komplexer und zuverlässiger Experten und Berater. Sobald Sie die Verwendung globaler Variablen beherrschen, können Sie sich die Entwicklung von EAs ohne sie nicht mehr vorstellen.

Die Trading-Strategie '80-20' Die Trading-Strategie '80-20'

Im Artikel wird die Erstellung der Instrumente (des Indikators und des EAs) für die Forschung der Handelsstrategie '80-20' beschrieben. Die Regeln der TS sind aus dem Buch Lindy Raschke und Lawrance Konnorsa "Street Smarts High Probability Short-Term Trading Strategies" genommen. In der Sprache MQL5 wurden die Regeln dieser Strategie formalisiert, und die auf ihrer Grundlage erstellten Indikatoren und der EA auf der aktuellen History des Marktes geprüft.

Die statistische Verteilung in Form von einem Histogramm ohne Indikator-Puffer und Arrays Die statistische Verteilung in Form von einem Histogramm ohne Indikator-Puffer und Arrays

Im Artikel wird die Bildungsmöglichkeit der Histogramme der statistischen Verteilungen der Markt-Charakteristiken unter Verwendung des graphischen Gedächtnisses betrachtet, das heißt ohne Verwendung der Indikator-Puffer und Arrays. Es wurden die ausführlichen Beispiele des Aufbaus solcher Histogramme aufgeführt und wurde die sogenannte "verborgene" Funktional der graphischen Objekte der Sprache MQL5 vorgeführt.