Die Stärke von ZigZag (Teil I). Entwicklung der Basisklasse des Indikators

17 April 2019, 10:03
Anatoli Kazharski
0
367

Inhalt


Einführung

In einem der vorangegangenen Artikel habe ich gezeigt, wie ein solcher Indikator wie Relative Strength Index (RSI) dargestellt werden kann. In einer der Versionen können die Signale für Trend- und Seitwärtsbewegungen gleichzeitig angeboten werden. Dem Indikator fehlt wahrscheinlich nur eines - die Fähigkeit, das Kursverhalten zu erkennen, was auch sehr wichtig für die Entscheidung sein kann, wann gehandelt werden soll und wann nicht. 

Viele Forscher überspringen einfach oder schenken der Bestimmung des Preisverhaltens nicht genügend Aufmerksamkeit. Gleichzeitig werden komplexe Methoden eingesetzt, die sehr oft nur "Black Boxes" sind, wie z.B. maschinelles Lernen oder neuronale Netze. Die wichtigste Frage, die sich in diesem Fall stellt, ist, welche Daten für das Training eines bestimmten Modells vorgelegt werden müssen. In diesem Artikel werden wir die Werkzeuge für solche Studien erweitern. Sie werden erfahren, wie Sie die für den Handel am besten geeigneten Symbole auswählen können, bevor Sie nach den optimalen Parametern suchen. Um dies zu erreichen, werden wir eine modifizierte Version des ZigZag-Indikators und der Codeklasse verwenden, die das Erhalten und Arbeiten mit Daten von Indikatoren dieses Typs erheblich vereinfacht.

In dieser Artikelserie werden wir Folgendes umsetzen:

  • Eine modifizierte Version des ZigZag-Indikators
  • Eine Klasse für den Erhalt der ZigZaga-Daten
  • Ein EA zum Testen des Prozesses zum Erhalt der Daten
  • Indikatoren, die das Preisverhalten definieren
  • Ein EA mit einer grafischen Oberfläche zum Erfassen der Statistik der Preisbewegung
  • Ein EA, der ZigZag-Signale handelt


Erweiterte Version des ZigZag-Indikators

Im Allgemeinen werden ZigZag-Indikatoren basierend auf den Hochs und Tiefs der Balken ohne Berücksichtigung von Spreads (Spreizung) gebaut. Dieser Artikel stellt eine modifizierte Version vor, in der ein Spread bei der Konstruktion von Segmenten für untere ZigZag-Extrema berücksichtigt wird. Es wird davon ausgegangen, dass Positionen innerhalb des Preiskanals vom Handelssystem getätigt werden sollen. Dies ist wichtig, da es oft vorkommt, dass der Kaufpreis (Ask) deutlich höher ist als der Verkaufspreis (Bid). Dies tritt beispielsweise nachts auf. Es wäre also falsch, einen Indikator nur auf der Grundlage von Geldkursen (Bid) zu erstellen. Schließlich macht es keinen Sinn, die unteren Extrema des Indikators auf Basis von Balken-Tiefs zu bilden, wenn es keine Möglichkeit gibt, zu diesen Preisen zu kaufen. Natürlich kann der Spread in den Handelsbedingungen berücksichtigt werden, aber es ist besser, wenn alles sofort auf dem Chart sichtbar ist. Dies vereinfacht die Entwicklung der Handelsstrategie, da zunächst alles plausibler ist.

Darüber hinaus könnten Sie auch alle Punkte sehen wollen, an denen die Extrema des ZigZags aktualisiert wurden. In diesem Fall wird das Bild noch vollständiger. Betrachten wir nun den Indikatorcode. Wir werden nur auf die grundlegenden Merkmale und Funktionen eingehen.

Wir werden zwei Indikatorpuffer benötigen, um Segmente zu bilden. Einer ist für Höchstwerte (Maxima), eine andere für Tiefstwerte (Minima). Sie sind als einzelne Linien auf dem Diagramm darzustellen. Daher benötigen wir sechs Indikatorpuffer, von denen fünf dargestellt werden.

Lassen Sie uns alle Indikatorpuffer auflisten:

  • Kleinster Ask-Preis. Kleinste ZigZag-Werte, die damit ermittelt werden
  • Höchster Bid-Preis. Höchste ZigZag-Werte, die damit ermittelt werden.
  • Hochs
  • Tiefs
  • Alle erkannten Hochs eines Aufwärtssegmentes
  • Alle erkannten Tiefs eines Abwärtssegmentes

#property indicator_chart_window
#property indicator_buffers 6
#property indicator_plots   5
//---
#property indicator_color1  clrRed
#property indicator_color2  clrCornflowerBlue
#property indicator_color3  clrGold
#property indicator_color4  clrOrangeRed
#property indicator_color5  clrSkyBlue

//--- Indicatorpuffer:
double low_ask_buffer[];    // Kleinster Ask-Preis
double high_bid_buffer[];   // höchster Bid-Preis
double zz_H_buffer[];       // Hochs
double zz_L_buffer[];       // Tiefs
double total_zz_h_buffer[]; // Alle Hochs
double total_zz_l_buffer[]; // Alle Tiefs

Fügen wir die Möglichkeit hinzu, die Anzahl der Balken (NumberOfBars) in den externen Parametern einzustellen, um die Indikatorlinien aufzubauen. Null bedeutet, dass alle Daten des Charts verwendet werden sollen. Der Parameter MinImpulseSize legt die Anzahl der Points fest, um die der Preis vom letzten Extremum abweichen soll, um ein gegenläufiges Segment zu konstruieren. Außerdem können wir als zusätzliche Parameter definieren, welche Indikatorenpuffer auf dem Diagramm angezeigt werden sollen, sowie die Farbe der ZigZag-Segmente.

//--- Externe Parameter
input int   NumberOfBars   =0;       // Anzahl der Bars
input int   MinImpulseSize =100;     // Mindestpunkte in einem Strahl
input bool  ShowAskBid     =false;   // Ask/Bid zeigen
input bool  ShowAllPoints  =false;   // Alle Punkte zeigen
input color RayColor       =clrGold; // Strahlfarbe

Wir deklarieren Sie auf globaler Ebene Hilfsvariablen, die für die Berechnung von Extrema erforderlich sind. Wir müssen die Indizes der zuvor berechneten Extrema speichern, die aktuelle Segmentrichtung beachten sowie die niedrigsten Bid- und höchsten Ask-Kurse speichern.

//--- ZZ-Variablen
int    last_zz_max  =0;
int    last_zz_min  =0;
int    direction_zz =0;
double min_low_ask  =0;
double max_high_bid =0;

Die Funktion FillAskBidBuffer() dient zum Füllen von Indikatorpuffern für minimale Ask- und maximale Bid-Kurse. Wir speichern für den Bid-Puffer die Werte aus dem Array high, während wir für den Ask-Puffer die Werte aus dem Array low unter Berücksichtigung des Spread speichern.

//+------------------------------------------------------------------+
//| Füllen der Indikatorpuffer mit High-Bid und Low-Ask              |
//+------------------------------------------------------------------+
void FillAskBidBuffers(const int i,const datetime &time[],const double &high[],const double &low[],const int &spread[])
  {
//--- Beenden, wenn der Anfangszeitpunkt nicht erreicht wurde
   if(time[i]<first_date)
      return;
//---
   high_bid_buffer[i] =high[i];
   low_ask_buffer[i]  =low[i]+(spread[i]*_Point);
  }

Die Funktion FillIndicatorBuffers() dient zur Definition von ZigZag-Extrema. Berechnungen werden erst ab dem angegebenen Datum durchgeführt, abhängig von der Anzahl der Balken, die im externen Parameter MinImpulseSize eingestellt sind. Abhängig von der beim vorherigen Funktionsaufruf definierten Segmentrichtung führt das Programm den entsprechenden Codeblock aus.

Bei der Definition der Richtung werden die folgenden Bedingungen überprüft:

  • Die aktuelle Richtung des Aufwärtssegments
    • Der aktuelle höchste Bid-Preis überschreitet das letzte Maximum:
      • Wenn diese Bedingung erfüllt ist, setzen wir (1) das vorherige Maximum zurück, (2) merken uns den aktuellen Daten-Array-Index und (3) weisen den aktuellen Wert des maximalen Bid-Preises den aktuellen Elementen der Indikatorpuffer zu.
      • Wenn diese Bedingung nicht erfüllt ist, hat sich die Segmentrichtung geändert, und es ist an der Zeit, die Bedingungen der unteren Extremwertbildung zu überprüfen:
        • Der aktuelle minimale Ask-Preis ist kleiner als das letzte Hoch.
        • Der Abstand zwischen dem aktuellen kleinsten Ask-Preis und dem letzten ZigZag-Maximum überschreitet den angegebenen Schwellenwert (MinImpulseSize).
          • Wenn diese Bedingungen erfüllt sind, erinnern wir uns (1) an den aktuellen Daten-Array-Index, speichern (2) die neue (nach unten gerichtete) Segmentrichtung in der Variablen und weisen (3) den aktuellen Wert des minimalen Ask-Preis den aktuellen Elementen der Indikatorpuffer zu.
  • Die aktuelle Segmentrichtung ist nach unten gerichtet.
    • Die aktuelle kleinste Ask-Preis ist niedriger als das letzte Minimum:
      • Wenn diese Bedingung erfüllt ist, setzen wir (1) das vorherige Minimum zurück, (2) merken uns den aktuellen Daten-Array-Index und (3) weisen den aktuellen Wert des minimalen Ask-Preises den aktuellen Elementen der Indikatorpuffer zu.
      • Wenn diese Bedingung nicht erfüllt ist, hat sich die Segmentrichtung geändert, und es ist an der Zeit, die Bedingungen der oberen Extremwertbildung zu überprüfen:
        • Der aktuelle höchste Bid-Preis überschreitet das letzte Minimum:
        • Der Abstand zwischen dem aktuellen kleinsten Bid-Preis und dem letzten ZigZag-Minimum überschreitet den angegebenen Schwellenwert (MinImpulseSize).
          • Wenn diese Bedingungen erfüllt sind, erinnern wir uns (1) an den aktuellen Daten-Array-Index, speichern (2) die neue (nach oben gerichtete) Segmentrichtung in der Variablen und weisen (3) den aktuellen Wert des maximalen Bid-Preises den aktuellen Elementen der Indikatorpuffer zu.

Der Code der Funktion FillIndicatorBuffers() ist im Detail unten aufgeführt:

//+------------------------------------------------------------------+
//| Ausfüllen der ZZ Indikatorpuffer                                 |
//+------------------------------------------------------------------+
void FillIndicatorBuffers(const int i,const datetime &time[])
  {
   if(time[i]<first_date)
      return;
//--- Wenn ZZ steigt
   if(direction_zz>0)
     {
      //--- bei einem neuen Hoch
      if(high_bid_buffer[i]>=max_high_bid)
        {
         zz_H_buffer[last_zz_max] =0;
         last_zz_max              =i;
         max_high_bid             =high_bid_buffer[i];
         zz_H_buffer[i]           =high_bid_buffer[i];
         total_zz_h_buffer[i]     =high_bid_buffer[i];
        }
      //--- Bei einer Trendwende (abwärts)
      else
        {
         if(low_ask_buffer[i]<max_high_bid && 
            fabs(low_ask_buffer[i]-zz_H_buffer[last_zz_max])>MinImpulseSize*_Point)
           {
            last_zz_min          =i;
            direction_zz         =-1;
            min_low_ask          =low_ask_buffer[i];
            zz_L_buffer[i]       =low_ask_buffer[i];
            total_zz_l_buffer[i] =low_ask_buffer[i];
           }
        }
     }
//--- Wenn ZZ fällt
   else
     {
      //--- Bei einem neuen Tief
      if(low_ask_buffer[i]<=min_low_ask)
        {
         zz_L_buffer[last_zz_min] =0;
         last_zz_min              =i;
         min_low_ask              =low_ask_buffer[i];
         zz_L_buffer[i]           =low_ask_buffer[i];
         total_zz_l_buffer[i]     =low_ask_buffer[i];
        }
      //--- Bei einer Trendwende (aufwärts)
      else
        {
         if(high_bid_buffer[i]>min_low_ask && 
            fabs(high_bid_buffer[i]-zz_L_buffer[last_zz_min])>MinImpulseSize*_Point)
           {
            last_zz_max          =i;
            direction_zz         =1;
            max_high_bid         =high_bid_buffer[i];
            zz_H_buffer[i]       =high_bid_buffer[i];
            total_zz_h_buffer[i] =high_bid_buffer[i];
           }
        }
     }
  }

Der Code der Indikator-Hauptfunktion wird im Folgenden gezeigt. Der Indikator wird nur bei fertig ausgebildete Balken berechnet. Danach werden (1) Felder und Variablen auf Null gesetzt, (2) die Anzahl der Balken für die Berechnung und der Anfangsindex definiert. Zunächst werden Daten für alle Elemente der Indikatorpuffer berechnet, während danach jedes Mal nur Daten für den letzten Balken berechnet werden. Nach Vorberechnungen und Prüfungen werden Indikatorpuffer berechnet und ausgefüllt.

//+------------------------------------------------------------------+
//| Funktion des nutzerdefinierten Indikators                        |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,const int prev_calculated,const datetime &time[],
                const double &open[],const double &high[],const double &low[],const double &close[],
                const long &tick_volume[],const long &volume[],const int &spread[])
  {
//--- Berechnung bei jedem neuen Tick vermeiden
   if(prev_calculated==rates_total)
      return(rates_total);
//--- Bei der Erstberechnung
   if(prev_calculated==0)
     {
      //--- Indikatorpuffer auf Null setzen
      ZeroIndicatorBuffers();
      //--- Variablen auf Null setzen
      ZeroIndicatorData();
      //--- Verfügbaren Datenumfang prüfen
      if(!CheckDataAvailable())
         return(0);
      //--- Falls mehr Daten zu kopieren sind, wird der aktuelle Umfang verwendet
      DetermineNumberData();
      //--- Definition der zu zeichnenden Bars für jedes Symbol ab Beginn 
      DetermineBeginForCalculate(rates_total);
     }
   else
     {
      //--- Berechnen nur des letzten Wertes
      start=prev_calculated-1;
     }
//--- Ausfüllen der Indikatorpuffer mit dem Hoch des Bid-Preises und dem Tief des Ask-Preises
   for(int i=start; i<rates_total; i++)
      FillAskBidBuffers(i,time,high,low,spread);
//--- Ausfüllen des Indikatorpuffers mit Daten
   for(int i=start; i<rates_total-1; i++)
      FillIndicatorBuffers(i,time);
//--- Rückgabe der Größe des Datenarrays
   return(rates_total);
  }

Der Indikator mit EURUSD D1 ist unten dargestellt:

 Abb. 1. Modifizierter ZigZag-Indikator mit EURUSD D1

Abb. 1. Modifizierter ZigZag-Indikator mit EURUSD D1

Der nächste Screenshot zeigt den Indikator mit EURMXN M5. Hier sehen wir, wie sich der Spread nachts deutlich ausdehnt. Dennoch wird der Indikator unter Berücksichtigung der Spreads berechnet.

 Abb. 2. Modifizierter ZigZag-Indikator mit EURUSD M5

Abb. 2. Modifizierter ZigZag-Indikator mit EURUSD M5

Im nächsten Abschnitt werden wir eine Klasse mit Methoden betrachten, die uns helfen, alle notwendigen Daten zu erhalten, um das aktuelle Preisverhalten zu definieren.


Klasse zur Klärung der Daten des ZigZag-Indikators

Der Preis bewegt sich chaotisch und unvorhersehbar. Seitwärtsbewegungen, während denen der Preis oft seine Richtung ändert, können abrupt durch lange unidirektionale Trends ohne Rücksetzer ersetzt werden. Es ist notwendig, immer den aktuellen Zustand zu kontrollieren, aber es ist auch wichtig, über Werkzeuge zur korrekten Interpretation des Preisverhaltens zu verfügen. Dies kann durch die Klasse CZigZagModule mit allen notwendigen Methoden für die Arbeit mit ZigZag-Daten erreicht werden. Mal sehen, wie es funktioniert.

Da wir in der Lage sind, mit mehreren Klasseninstanzen gleichzeitig zu arbeiten, z.B. mit ZigZag-Daten aus verschiedenen Zeiträumen, kann es notwendig sein, die erhaltenen Segmente mit Trendlinien verschiedener Farben zu visualisieren. Binden Sie daher die Datei ChartObjectsLines.mqh aus der Standardbibliothek mit der Datei mit der Klasse CZigZagModule ein. Aus dieser Datei benötigen wir die Klasse CChartObjectTrend für die Arbeit mit Trendlinien. Die Farbe der Trendlinien kann durch die 'public' Methode CZigZagModule::LinesColor() festgelegt werden. Grau (clrGray) ist standardmäßig eingestellt.

//+------------------------------------------------------------------+
//|                                                 ZigZagModule.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#include <ChartObjects\ChartObjectsLines.mqh>
//+------------------------------------------------------------------+
//| Klasse für die Daten des ZigZag-Indikators                       |
//+------------------------------------------------------------------+
class CZigZagModule
  {
protected:
   //--- Segmentlinie
   CChartObjectTrend m_trend_lines[];

   //--- Farbe der Segmentlinie
   color             m_lines_color;
   //---
public:
                     CZigZagModule(void);
                    ~CZigZagModule(void);
   //---
public:
   //--- Linienfarbe
   void              LinesColor(const color clr) { m_lines_color=clr; }
  };
//+------------------------------------------------------------------+
//| Konstruktor                                                      |
//+------------------------------------------------------------------+
CZigZagModule::CZigZagModule(void) : m_lines_color(clrGray)
  {
// ...
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CZigZagModule::~CZigZagModule(void)
  {
  }

Bevor wir ZigZag-Indikatorendaten erhalten, müssen wir die Anzahl der für die Arbeit notwendigen Extrema festlegen. Um dies zu erreichen, sollten wir die Methode CZigZagModule::CopyExtremums() aufrufen. Separate dynamische Arrays wurden deklariert, um (1) Extrempreise, (2) Indizes der Extrembalken, (3) die Zeit ihrer Balken und die (4) Anzahl der Segmente für den Aufbau von Trendlinien in einem Chart zu speichern. Die Größe der Arrays wird nach dem gleichen Verfahren eingestellt.

Die Anzahl der Segmente wird automatisch aus der Anzahl der angegebenen Extremwerte berechnet. Wenn wir beispielsweise 1 an die Methode CZigZagModule::CopyExtremums() übergeben, erhalten wir Daten über ein Hoch und ein Tief. In diesem Fall ist es nur ein Segment des ZigZag-Indikators. Wenn ein Wert größer als 1 übergeben wird, ist die Anzahl der Segmente immer gleich der Anzahl der kopierten Extremwerte multipliziert mit 2 minus 1. Mit anderen Worten, die Anzahl der Segmente ist immer ungerade:

  • Ein Extremum - 1 Segment
  • Zwei Extrema - 3 Segmente
  • Drei Extrema - 5 Segmente, etc.
class CZigZagModule
  {
protected:
   int               m_copy_extremums;    // Anzahl der gespeicherten Hochs/Tiefs
   int               m_segments_total;    // Anzahl der Segmente
   //--- Extremum
   double            m_zz_low[];
   double            m_zz_high[];
   //--- Index des Balkens mit Extremum
   int               m_zz_low_bar[];
   int               m_zz_high_bar[];
   //--- Zeit des Balkens mit Extremum
   datetime          m_zz_low_time[];
   datetime          m_zz_high_time[];
   //---
  };
//+------------------------------------------------------------------+
//| Konstruktor                                                      |
//+------------------------------------------------------------------+
CZigZagModule::CZigZagModule(void) : m_copy_extremums(1),
                                     m_segments_total(1)
  {
   CopyExtremums(m_copy_extremums);
  }

//+------------------------------------------------------------------+
//| Anzahl der zu verwendeten der Extrema                            |
//+------------------------------------------------------------------+
void CZigZagModule::CopyExtremums(const int total)
  {
   if(total<1)
      return;
//---
   m_copy_extremums =total;
   m_segments_total =total*2-1;
//---
   ::ArrayResize(m_zz_low,total);
   ::ArrayResize(m_zz_high,total);
   ::ArrayResize(m_zz_low_bar,total);
   ::ArrayResize(m_zz_high_bar,total);
   ::ArrayResize(m_zz_low_time,total);
   ::ArrayResize(m_zz_high_time,total);
   ::ArrayResize(m_trend_lines,m_segments_total);
  }

Bevor wir mit den ZigZag-Indikatordaten beginnen zu arbeiten, platzieren wir diese zur besseren Übersicht in den oben beschriebenen Klassenarrays. Wir werden Hilfsfelder benötigen, die als Zähler der Extrema verwendet werden können. 

Um die Daten zu erhalten, benötigen wir die Methode CZigZagModule::GetZigZagData(). Initiale Felder des ZigZag-Indikators zusammen mit dem Zeitfeld sollten ihr übergeben werden. Diese Quelldaten können mit den Funktionen CopyBuffer() und CopyTime() abgerufen werden. Bevor die notwendigen Werte aus den Quelldaten gewonnen werden, sollten alle Felder und Arrays zurückgesetzt werden. Dann erhalten wir die angegebene Anzahl von (1) der Extrema, (2) der Indizes der Extrembalken und (3) die Extremzeit. 

Die Richtung des aktuellen Segments wird am Ende der Methode definiert. Wenn hier die aktuelle Zeit des Hochs des Segments größer ist als die des Tiefs, zeigt die Richtung nach oben. Ansonsten zeigt es nach unten.

class CZigZagModule
  {
protected:
   int               m_direction;         // Richtung
   int               m_counter_lows;      // Zähler der Tiefs
   int               m_counter_highs;     // Zähler der Hochs
   //---
public:
   //--- Datenabruf
   void              GetZigZagData(const double &zz_h[],const double &zz_l[],const datetime &time[]);
   //--- Rücksetzen der Struktur
   void              ZeroZigZagData(void);
  };
//+------------------------------------------------------------------+
//| ZigZag Datenabruf                                                |
//+------------------------------------------------------------------+
void CZigZagModule::GetZigZagData(const double &zz_h[],const double &zz_l[],const datetime &time[])
  {
   int h_total =::ArraySize(zz_h);
   int l_total =::ArraySize(zz_l);
   int total   =h_total+l_total;
//--- Rücksetzen der ZZ-Variablen
   ZeroZigZagData();
//--- Iterieren über die ZZ-Werte
   for(int i=0; i<total; i++)
     {
      //--- Ist die benötigte Anzahl der Hochs und Tiefs von ZZ erreicht, Schleife beenden
      if(m_counter_highs==m_copy_extremums && m_counter_lows==m_copy_extremums)
         break;
      //--- Kontrolle des Arrayüberlauf
      if(i>=h_total || i>=l_total)
         break;
      //--- Ausfüllen des Arrays mit den Hochs im benötigten Umfang
      if(zz_h[i]>0 && m_counter_highs<m_copy_extremums)
        {
         m_zz_high[m_counter_highs]      =zz_h[i];
         m_zz_high_bar[m_counter_highs]  =i;
         m_zz_high_time[m_counter_highs] =time[i];
         //---
         m_counter_highs++;
        }
      //--- Ausfüllen des Arrays mit den Tiefs im benötigten Umfang
      if(zz_l[i]>0 && m_counter_lows<m_copy_extremums)
        {
         m_zz_low[m_counter_lows]      =zz_l[i];
         m_zz_low_bar[m_counter_lows]  =i;
         m_zz_low_time[m_counter_lows] =time[i];
         //---
         m_counter_lows++;
        }
     }
//--- Erkennen der Richtung der Preisbewegung
   m_direction=(m_zz_high_time[0]>m_zz_low_time[0])? 1 : -1;
  }

Nun, da die Daten empfangen wurden, können wir die anderen Methoden dieser Klasse besprechen. Um die Extrema, deren Balkenindizes und die -zeit, bei denen diese Extrema gebildet wurden, zu erhalten, rufen wir einfach die entsprechende Methode (siehe Codeliste unten) auf, indem wir den Index eines Extremums angeben. Als Beispiel wird hier nur der Methodencode CZigZagModule::LowPrice() aufgelistet, da sie alle nahezu identisch sind.

class CZigZagModule
  {
public:
   //--- Extremum des angegebenen Index
   double            LowPrice(const int index);
   double            HighPrice(const int index);
   //--- Index des Extremum-Balkens nach dem angegebenen Index
   int               LowBar(const int index);
   int               HighBar(const int index);
   //--- Zeit des Extremum-Balkens nach dem angegebenen Index
   datetime          LowTime(const int index);
   datetime          HighTime(const int index);
  };
//+------------------------------------------------------------------+
//| Tief des angegebenen Index                                       |
//+------------------------------------------------------------------+
double CZigZagModule::LowPrice(const int index)
  {
   if(index>=::ArraySize(m_zz_low))
      return(0.0);
//---
   return(m_zz_low[index]);
  }

Wenn wir eine Segmentgröße benötigen, rufen wir die Methode CZigZagModule::SegmentSize() auf, die den Segmentindex als einzigen Parameter verlangt. Je nachdem, ob es sich bei dem angegebenen Index um einen geraden oder ungeraden Wert handelt, werden die Indizes der Extrema, bei denen die Segmentgröße berechnet wird, entsprechend definiert. Wenn der Indexwert gerade ist, passen die Indizes der Extrem zusammen und müssen nicht abhängig von der Segmentrichtung berechnet werden.

class CZigZagModule
  {
public:
   //--- Segmentgröße nach dem angegebenen Index
   double            SegmentSize(const int index);
  };
//+------------------------------------------------------------------+
//| Rückgabe der Segmentgröße nach Index                             |
//+------------------------------------------------------------------+
double CZigZagModule::SegmentSize(const int index)
  {
   if(index>=m_segments_total)
      return(-1);
//---
   double size=0;
//--- Bei einem geraden Wert
   if(index%2==0)
     {
      int i=index/2;
      size=::fabs(m_zz_high[i]-m_zz_low[i]);
     }
//--- Bei einem ungeraden Wert
   else
     {
      int l=0,h=0;
      //---
      if(Direction()>0)
        {
         h=(index-1)/2+1;
         l=(index-1)/2;
        }
      else
        {
         h=(index-1)/2;
         l=(index-1)/2+1;
        }
      //---
      size=::fabs(m_zz_high[h]-m_zz_low[l]);
     }
//---
   return(size);
  }

Die Methode CZigZagModule::SegmentsSum() wird verwendet, um die Summe aller Segmente zu ermitteln. Hier ist alles einfach, da die oben beschriebene Methode CZigZagModule::SegmentSize() beim Iterieren über alle Segmente in einer Schleife aufgerufen wird.

class CZigZagModule
  {
public:
   //--- Summe aller Segmente
   double            SegmentsSum(void);
  };
//+------------------------------------------------------------------+
//| Gesamtgröße aller Segmente                                       |
//+------------------------------------------------------------------+
double CZigZagModule::SegmentsSum(void)
  {
   double sum=0.0;
//---
   for(int i=0; i<m_segments_total; i++)
      sum+=SegmentSize(i);
//---
   return(sum);
  }

Außerdem könnten wir möglicherweise die Summe aller Segmente benötigen, die nur nach oben oder unten gerichtet sind. Der Code für Aufwärtssegmente wird im Folgenden als Beispiel dargestellt. Es hängt alles von der Richtung des aktuellen Segments ab. Wenn sie nach oben gerichtet ist, werden die aktuellen Indizes in einer Schleife für die Berechnungen verwendet. Wenn die aktuelle Richtung nach unten gerichtet ist, sollten die Berechnungen ab dem ersten Index mit einem Offset von einem Element zurück für die Hochs begonnen werden. Wenn wir die Summe aller Segmente nach unten erhalten möchten, verwenden wir die gleiche Methode mit dem einzigen Unterschied, dass bei steigender Richtung der Offset für die Tiefs ausgeführt wird.

class CZigZagModule
  {
public:
   //--- Summe der (1) auf- und (2) abwärts Segmente
   double            SumSegmentsUp(void);
   double            SumSegmentsDown(void);
  };
//+------------------------------------------------------------------+
//| Rückgabe der Größe aller Aufwärtssegmente                        |
//+------------------------------------------------------------------+
double CZigZagModule::SumSegmentsUp(void)
  {
   double sum=0.0;
//---
   for(int i=0; i<m_copy_extremums; i++)
     {
      if(Direction()>0)
         sum+=::fabs(m_zz_high[i]-m_zz_low[i]);
      else
        {
         if(i>0)
            sum+=::fabs(m_zz_high[i-1]-m_zz_low[i]);
        }
     }
//---
   return(sum);
  }

Es kann sinnvoll sein, ein prozentuales Verhältnis der Summen der unidirektionalen Segmente zur Gesamtzahl der Segmente in der Menge zu erhalten. Wir verwenden dazu die Methoden CZigZagModule::PercentSumSegmentsUp() und CZigZagModule::PercentSumSegmentsDown(). Mit ihnen können die prozentualen Differenzen dieser Verhältnisse ermittelt werden - die CZigZagModule::PercentSumSegmentsDifference() Methode, die uns wiederum die aktuelle Preis(trend)richtung anzeigen kann. Ist die Differenz unwesentlich, schwankt der Preis gleichmäßig in beide Richtungen (flach). 

class CZigZagModule
  {
public:
   //--- prozentuales Verhältnis der Summe der Segmente zur Anzahl aller Segmente im Set
   double            PercentSumSegmentsUp(void);
   double            PercentSumSegmentsDown(void);
   //--- Differenz der Summen der Segmente
   double            PercentSumSegmentsDifference(void);
  };
//+------------------------------------------------------------------+
//| Rückgabe des Prozentsatzes der Aufwärtssegmente                  |
//+------------------------------------------------------------------+
double CZigZagModule::PercentSumSegmentsUp(void)
  {
   double sum=SegmentsSum();
   if(sum<=0)
      return(0);
//---
   return(SumSegmentsDown()/sum*100);
  }
//+------------------------------------------------------------------+
//| Rückgabe des Prozentsatzes der Abwärtssegmente                   |
//+------------------------------------------------------------------+
double CZigZagModule::PercentSumSegmentsDown(void)
  {
   double sum=SegmentsSum();
   if(sum<=0)
      return(0);
//---
   return(SumSegmentsUp()/sum*100);
  }
//+------------------------------------------------------------------+
//| Rückgabe der Differenz der Summen aller Segmente in Prozent      |
//+------------------------------------------------------------------+
double CZigZagModule::PercentSumSegmentsDifference(void)
  {
   return(::fabs(PercentSumSegmentsUp()-PercentSumSegmentsDown()));
  }

Um die Preisberwegungen zu erkennen, benötigen wir Methoden zur Ermittlung der Dauer einzelner Segmente und der gesamten Ergebnismenge. Die Methode CZigZagModule::SegmentBars() dient zum Erhalten der Anzahl der Balken im angegebenen Segment. Die Logik des Codes der Methode ist die gleiche wie die der Methoden CZigZagModule::SegmentSize() zum Erhalten einer Segmentgröße. Daher ist es überflüssig, seinen Code hier anzugeben. 

Um die Gesamtzahl der Balken im erhaltenen Datensatz zu erhalten, verwenden wir die Methode CZigZagModule::SegmentsTotalBars(). Hier werden die Indizes der Anfangs- und Endbalken im Set definiert und die Differenz zurückgegeben. Die Methode CZigZagModule::SegmentsTotalSeconds() folgt dem gleichen Prinzip. Der einzige Unterschied besteht darin, dass sie die Anzahl der Sekunden im Set zurückgibt. 

class CZigZagModule
  {
public:
   //--- Anzahl der Bars im angegebenen Segment
   int               SegmentBars(const int index);
   //--- (1) Anzahl der Bars und (2) Sekunden im Set der Segmente
   int               SegmentsTotalBars(void);
   long              SegmentsTotalSeconds(void);
  };
//+------------------------------------------------------------------+
//| Anzahl der Bars aller Segmente                                   |
//+------------------------------------------------------------------+
int CZigZagModule::SegmentsTotalBars(void)
  {
   int begin =0;
   int end   =0;
   int l     =m_copy_extremums-1;
//---
   begin =(m_zz_high_bar[l]>m_zz_low_bar[l])? m_zz_high_bar[l] : m_zz_low_bar[l];
   end   =(m_zz_high_bar[0]>m_zz_low_bar[0])? m_zz_low_bar[0] : m_zz_high_bar[0];
//---
   return(begin-end);
  }
//+------------------------------------------------------------------+
//| Anzahl der Sekunden aller Segmente                               |
//+------------------------------------------------------------------+
long CZigZagModule::SegmentsTotalSeconds(void)
  {
   datetime begin =NULL;
   datetime end   =NULL;
   int l=m_copy_extremums-1;
//---
   begin =(m_zz_high_time[l]<m_zz_low_time[l])? m_zz_high_time[l] : m_zz_low_time[l];
   end   =(m_zz_high_time[0]<m_zz_low_time[0])? m_zz_low_time[0] : m_zz_high_time[0];
//---
   return(long(end-begin));
  }

Oftmals kann es notwendig sein, die Preisspanne innerhalb des beobachteten Datensatzes herauszufinden. Für diese Zwecke bietet die Klasse Methoden, um die Maxima und die Minima sowie der Differenz zwischen ihnen (Preisspanne) zu ermitteln.

class CZigZagModule
  {
public:
   //--- (1) Minimum und (2) Maximum im Set
   double            LowMinimum(void);
   double            HighMaximum(void);
   //--- Preisspanne
   double            PriceRange(void);
  };
//+------------------------------------------------------------------+
//| Minimum im Set                                                   |
//+------------------------------------------------------------------+
double CZigZagModule::LowMinimum(void)
  {
   return(m_zz_low[::ArrayMinimum(m_zz_low)]);
  }
//+------------------------------------------------------------------+
//| Maximum im Set                                                   |
//+------------------------------------------------------------------+
double CZigZagModule::HighMaximum(void)
  {
   return(m_zz_high[::ArrayMaximum(m_zz_high)]);
  }
//+------------------------------------------------------------------+
//| Preisspanne                                                      |
//+------------------------------------------------------------------+
double CZigZagModule::PriceRange(void)
  {
   return(HighMaximum()-LowMinimum());
  }

Noch ein weiterer Satz der Klassenmethoden CZigZagModule erlaubt das Abrufen von Werten wie:

  • SmallestSegment() - gibt das kleinste Segment der erhaltenen Daten zurück.
  • LargegestSegment() - gibt das größte Segment der erhaltenen Daten zurück. 
  • LeastNumberOfSegmentBars() - gibt die kleinste Anzahl von Balken in einem Segment in den erhaltenen Daten zurück.
  • MostNumberOfSegmentBars() - liefert die höchste Anzahl von Balken in einem Segment in den erhaltenen Daten.

Die Klasse verfügt bereits über Methoden, um die Größe der Segmente und die Anzahl der Segmentbalken um den angegebenen Index zu ermitteln. Daher wird es leicht sein, den Code der Methoden aus der obigen Liste zu verstehen. Alle von ihnen unterscheiden sich nur in den in ihnen aufgerufenen Methoden, daher werde ich die Codes von nur zwei von ihnen besprechen - CZigZagModule::SmallestSegmen() und CZigZagModule::MostNumberOfSegmentBars().

class CZigZagModule
  {
public:
   //--- Kleinstes Segment im Set
   double            SmallestSegment(void);
   //--- Größtes Segment im Set
   double            LargestSegment(void);
   //--- Kleinste Balkenzahl eines Segments im Set
   int               LeastNumberOfSegmentBars(void);
   //--- Größte Balkenzahl eines Segments im Set 
   int               MostNumberOfSegmentBars(void);
  };
//+------------------------------------------------------------------+
//| Kleinstes Segment im Set                                         |
//+------------------------------------------------------------------+
double CZigZagModule::SmallestSegment(void)
  {
   double min_size=0;
   for(int i=0; i<m_segments_total; i++)
     {
      if(i==0)
        {
         min_size=SegmentSize(0);
         continue;
        }
      //---
      double size=SegmentSize(i);
      min_size=(size<min_size)? size : min_size;
     }
//---
   return(min_size);
  }
//+------------------------------------------------------------------+
//| Größte Balkenzahl eines Segments im Set                          |
//+------------------------------------------------------------------+
int CZigZagModule::MostNumberOfSegmentBars(void)
  {
   int max_bars=0;
   for(int i=0; i<m_segments_total; i++)
     {
      if(i==0)
        {
         max_bars=SegmentBars(0);
         continue;
        }
      //---
      int bars=SegmentBars(i);
      max_bars=(bars>max_bars)? bars : max_bars;
     }
//---
   return(max_bars);
  }

Bei der Suche nach Mustern müssen wir möglicherweise definieren, wie stark sich ein bestimmtes Segment in der Größe (in %) vom vorherigen unterscheidet. Um solche Aufgaben zu lösen, verwenden wir die Methode CZigZagModule::PercentDeviation().

class CZigZagModule
  {
public:
   //--- Abweichung in Prozent
   double            PercentDeviation(const int index);
  };
//+------------------------------------------------------------------+
//| Abweichung in Prozent                                            |
//+------------------------------------------------------------------+
double CZigZagModule::PercentDeviation(const int index)
  {
   return(SegmentSize(index)/SegmentSize(index+1)*100);
  }

Nun wollen wir sehen, wie man die erhaltenen Daten visualisieren und die Klasse CZigZagModule in eigne Projekten verwenden kann.


Darstellung der erhaltenen Daten

Nachdem wir die Handles der ZigZag-Indikators für verschiedenen Zeitrahmen erhalten haben, können wir Segmente auf dem aktuellen Diagramm visualisieren, auf dem der EA gestartet wird. Verwenden wir grafische Objekte vom Typ Trendlinie zur Visualisierung. Die private Methode CZigZagModule::CreateSegment() wird zum Erstellen von Objekten verwendet. Sie empfängt den Segmentindex und das Suffix (optionaler Parameter), die verwendet werden, um einen eindeutigen Namen des grafischen Objekts zu bilden, um Doppelarbeit zu vermeiden, falls wir ZigZag-Indikatordaten mit unterschiedlichen Parametern und aus unterschiedlichen Zeitrahmen anzeigen müssen. 

Die 'public' Methoden CZigZagModule::ShowSegments() und CZigZagModule::DeleteSegments() ermöglichen das Anzeigen und Entfernen von grafischen Objekten.

class CZigZagModule
  {
public:
   //--- Objekte (1) anzeigen und (2) löschen
   void              ShowSegments(const string suffix="");
   void              DeleteSegments(void);
   //---
private:
   //--- Objekt erstellen
   void              CreateSegment(const int segment_index,const string suffix="");
  };
//+------------------------------------------------------------------+
//| Anzeigen der ZZ-Segmente auf dem Chart                           |
//+------------------------------------------------------------------+
void CZigZagModule::ShowSegments(const string suffix="")
  {
   for(int i=0; i<m_segments_total; i++)
      CreateSegment(i,suffix);
  }
//+------------------------------------------------------------------+
//| Segment entfernen                                                |
//+------------------------------------------------------------------+
void CZigZagModule::DeleteSegments(void)
  {
   for(int i=0; i<m_segments_total; i++)
     {
      string name="zz_"+string(::ChartID())+"_"+string(i);
      ::ObjectDelete(::ChartID(),name);
     }
  }

Die Klasse wurde um Methoden zur Anzeige von Kommentaren in einem Diagramm erweitert, um schnell grundlegende Informationen über die erhaltenen Indikatordaten zu erhalten. Der Code des Verfahrens, der kurz die berechneten Indikatordaten anzeigt, wird unten angezeigt.

 class CZigZagModule
  {
public:
   //--- Kommentar auf dem Chart
   void              CommentZigZagData();
   void              CommentShortZigZagData();
  };
//+------------------------------------------------------------------+
//| Datenanzeige von ZigZag im Kommentar des Charts                  |
//+------------------------------------------------------------------+
void CZigZagModule::CommentShortZigZagData(void)
  {
   string comment="Current direction : "+string(m_direction)+"\n"+
                  "Copy extremums: "+string(m_copy_extremums)+
                  "\n---\n"+
                  "SegmentsTotalBars(): "+string(SegmentsTotalBars())+"\n"+
                  "SegmentsTotalSeconds(): "+string(SegmentsTotalSeconds())+"\n"+
                  "SegmentsTotalMinutes(): "+string(SegmentsTotalSeconds()/60)+"\n"+
                  "SegmentsTotalHours(): "+string(SegmentsTotalSeconds()/60/60)+"\n"+
                  "SegmentsTotalDays(): "+string(SegmentsTotalSeconds()/60/60/24)+
                  "\n---\n"+
                  "PercentSumUp(): "+::DoubleToString(SumSegmentsUp()/SegmentsSum()*100,2)+"\n"+
                  "PercentSumDown(): "+::DoubleToString(SumSegmentsDown()/SegmentsSum()*100,2)+"\n"+
                  "PercentDifference(): "+::DoubleToString(PercentSumSegmentsDifference(),2)+
                  "\n---\n"+
                  "SmallestSegment(): "+::DoubleToString(SmallestSegment()/_Point,0)+"\n"+
                  "LargestSegment(): "+::DoubleToString(LargestSegment()/_Point,0)+"\n"+
                  "LeastNumberOfSegmentBars(): "+string(LeastNumberOfSegmentBars())+"\n"+
                  "MostNumberOfSegmentBars(): "+string(MostNumberOfSegmentBars());
//---
   ::Comment(comment);
  }

Entwickeln wir eine Anwendung für die Übernahme und Darstellung der erhaltenen Daten.


EA zur Überprüfung der erzielten Ergebnisse

Entwickeln wir einen einfachen Test EA zur Übernahmen und Visualisieren von ZigZag-Indikatorendaten. Wir werden keine zusätzlichen Kontrollen durchführen, um den Code so einfach wie möglich zu halten. Der Hauptzweck des Beispiels besteht darin, das Prinzip der Datenbeschaffung zu demonstrieren. 

Wir fügen die Datei mit der Klasse CZigZagModule in die EA-Datei ein und deklarieren deren Instanz. Es gibt hier zwei externe Parameter, mit denen Sie die Anzahl der zu kopierenden Extreme und den Mindestabstand für ein neues ZigZag-Indikatorsegment festlegen können. Auf globaler Ebene deklarieren wir auch dynamische Arrays zur Gewinnung von Quelldaten und eine Variable für das Indikatorhandle. 

//+------------------------------------------------------------------+
//|                                                    TestZZ_01.mq5 |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#include <ZigZagModule.mqh>
CZigZagModule zz_current;

//--- Externe Parameter
input int CopyExtremum   =3;
input int MinImpulseSize =0;

//--- Arrays der Anfangsdaten
double   l_zz[];
double   h_zz[];
datetime t_zz[];

//--- Handles des ZZ-Iindikators
int zz_handle_current=WRONG_VALUE;

In der Funktion OnInit() erhalten wir (1) das Indikator-Handle, setzen (2) die Anzahl der Extremwerte, um die endgültigen Daten und eine Farbe der Segmentlinien aus dem erhaltenen Satz zu bilden, und (3) eine umgekehrte Indexierungsreihenfolge für Quelldaten-Arrays.

//+------------------------------------------------------------------+
//| Initialisierungsfunktion des Experten                            |
//+------------------------------------------------------------------+
int OnInit(void)
  {
//--- Pfad zum ZZ-Indikator
   string zz_path="Custom\\ZigZag\\ExactZZ_Plus.ex5";
//--- Abfrage des Handles des Indikators
   zz_handle_current=::iCustom(_Symbol,_Period,zz_path,10000,MinImpulseSize,true,true);
//--- Setzen der Segmentfarbe und der Anzahl der benötigten Extrema
   zz_current.LinesColor(clrRed);
   zz_current.CopyExtremums(CopyExtremum);
//--- Setzen der umgekehrten Indexierung (... 3 2 1 0)
   ::ArraySetAsSeries(l_zz,true);
   ::ArraySetAsSeries(h_zz,true);
   ::ArraySetAsSeries(t_zz,true);
   return(INIT_SUCCEEDED);
  }

In der Funktion OnTick() holen wir uns die Quelldaten des Indikators mit seinem Handle und der Öffnungszeit der Balken. Dann bereiten wir die endgültigen Daten vor, indem wir die Methode CZigZagModule::GetZigZagData() aufrufen. Abschließend visualisieren wir Segmente der erhaltenen ZigZag-Indikatordaten und zeigen diese Daten auf dem Chart als Kommentar an.

//+------------------------------------------------------------------+
//| Experten Funktion OnTick                                         |
//+------------------------------------------------------------------+
void OnTick(void)
  {
//--- Abrufen der Quelldaten
   int copy_total=1000;
   ::CopyTime(_Symbol,_Period,0,copy_total,t_zz);
   ::CopyBuffer(zz_handle_current,2,0,copy_total,h_zz);
   ::CopyBuffer(zz_handle_current,3,0,copy_total,l_zz);
//--- Abrufen der endgültigen Daten
   zz_current.GetZigZagData(h_zz,l_zz,t_zz);
//--- Segmente auf dem Chart darstellen
   zz_current.ShowSegments();
//--- Anzeige der Daten im Kommentar auf dem Chart
   zz_current.CommentZigZagData();
  }

Wenn wir den EA im Strategietester im Visualisierungsmodus starten, werden wir folgendes sehen. In diesem Fall wurden 5 hohe und 5 niedrige Extrema ermittelt. Infolgedessen wurden 9 Segmente in der Grafik rot hervorgehoben.

 Abb. 3. Demonstration im Visualisierungsmodus (ein ZigZag)

Abb. 3. Demonstration im Visualisierungsmodus (ein ZigZag)

Wenn wir ZigZag-Indikatorendaten aus verschiedenen Zeiträumen gleichzeitig erhalten müssen, sollte der Code des Tests EA leicht verbessert werden. Betrachten wir ein Beispiel, in dem wir Daten aus drei Zeitrahmen beziehen müssen. In diesem Fall müssen wir drei Instanzen der Klasse CZigZagModule deklarieren. Der erste Zeitrahmen wird aus dem aktuellen Diagramm entnommen, in dem der EA gestartet wird. Zwei weitere sind z.B. M15 und H1.

#include <Addons\Indicators\ZigZag\ZigZagModule.mqh>
CZigZagModule zz_current;
CZigZagModule zz_m15;
CZigZagModule zz_h1;

Jeder Indikator hat seine eigene Variable für das Handle:

//--- Handle des ZZ-Indikators
int zz_handle_current =WRONG_VALUE;
int zz_handle_m15     =WRONG_VALUE;
int zz_handle_h1      =WRONG_VALUE;

Als nächstes erhalten wir in der Funktion OnInit() die Handles für jeden Indikator separat und legen die Farben und die Anzahl der Extreme fest:

//+------------------------------------------------------------------+
//| Initialisierungsfunktion des Experten                            |
//+------------------------------------------------------------------+
int OnInit(void)
  {
//--- Pfad zum ZZ-Indikator
   string zz_path="Custom\\ZigZag\\ExactZZ_Plus.ex5";
//--- Abrufen des Indikatorhandles
   zz_handle_current =::iCustom(_Symbol,_Period,zz_path,10000,MinImpulseSize,false,false);
   zz_handle_m15     =::iCustom(_Symbol,PERIOD_M15,zz_path,10000,MinImpulseSize,false,false);
   zz_handle_h1      =::iCustom(_Symbol,PERIOD_H1,zz_path,10000,MinImpulseSize,false,false);
//--- Setzen der Segmentfarbe
   zz_current.LinesColor(clrRed);
   zz_m15.LinesColor(clrCornflowerBlue);
   zz_h1.LinesColor(clrGreen);
//--- Setzen der benötigten Anzahl der Extrema
   zz_current.CopyExtremums(CopyExtremum);
   zz_m15.CopyExtremums(CopyExtremum);
   zz_h1.CopyExtremums(CopyExtremum);
//--- Setzen der umgekehrten Indexierung (... 3 2 1 0)
   ::ArraySetAsSeries(l_zz,true);
   ::ArraySetAsSeries(h_zz,true);
   ::ArraySetAsSeries(t_zz,true);
   return(INIT_SUCCEEDED);
  }

Die Daten werden in der Funktion OnTick() empfangen, wie oben für jede ZigZag-Indikatorinstanz separat dargestellt. Kommentare zu nur einem Indikator können im Chart angezeigt werden. In diesem Fall werfen wir einen Blick auf die kurzen Daten für den Indikator des aktuellen Zeitrahmens.

//+------------------------------------------------------------------+
//| Experten Funktion OnTick                                         |
//+------------------------------------------------------------------+
void OnTick(void)
  {
   int copy_total=1000;
   ::CopyTime(_Symbol,_Period,0,copy_total,t_zz);
   ::CopyBuffer(zz_handle_current,2,0,copy_total,h_zz);
   ::CopyBuffer(zz_handle_current,3,0,copy_total,l_zz);
   zz_current.GetZigZagData(h_zz,l_zz,t_zz);
   zz_current.ShowSegments("_current");
   zz_current.CommentShortZigZagData();
//---
   ::CopyTime(_Symbol,PERIOD_M15,0,copy_total,t_zz);
   ::CopyBuffer(zz_handle_m15,2,0,copy_total,h_zz);
   ::CopyBuffer(zz_handle_m15,3,0,copy_total,l_zz);
   zz_m15.GetZigZagData(h_zz,l_zz,t_zz);
   zz_m15.ShowSegments("_m15");
//---
   ::CopyTime(_Symbol,PERIOD_H1,0,copy_total,t_zz);
   ::CopyBuffer(zz_handle_h1,2,0,copy_total,h_zz);
   ::CopyBuffer(zz_handle_h1,3,0,copy_total,l_zz);
   zz_h1.GetZigZagData(h_zz,l_zz,t_zz);
   zz_h1.ShowSegments("_h1");
  }

Hier ist, wie es aussieht:

 Abb. 4. Demonstration im Visualisierungsmodus (drei ZigZags)

Abb. 4. Demonstration im Visualisierungsmodus (drei ZigZags)

Wir sehen, dass die Extremwerte der Indikatoren aus höheren Zeiträumen leicht nach links verschoben sind. Der Grund dafür ist, dass Ober- und Unterseite durch die Öffnungszeit der Balken des Zeitraums, die über das Handle empfangen wurde, festgelegt werden. 


Weiterentwicklung der Klasse CZigZagModule

Betrachtet man die bereits erzielten Ergebnisse, könnte man meinen, dass sie ausreichen, um die Arbeit mit dem ZigZag-Indikator abzuschließen. Aber in der Tat ist das nicht der Fall. Wir müssen die Entwicklung der Codeklasse CZigZagModule fortsetzen und sie mit neuen nützlichen Methoden ergänzen. 

Bisher haben wir Daten aus dem ZigZag-Indikator erhalten, beginnend mit dem neuesten Balken und bis tief in die historischen Daten hinein. Möglicherweise müssen wir aber auch Daten in einem bestimmten Zeitrahmen beschaffen. Um dies zu erreichen, schreiben wir eine andere Methode CZigZagModule::GetZigZagData() mit einem anderen Parametersatz. In dieser Version erhalten wir die ersten Daten innerhalb der Methode, daher benötigen wir das Indikator-Handle, Symbol, Zeitrahmen und Zeitbereich (Start- und Enddatum) als Parameter.

Weiterhin müssen wir die Anzahl der Höhen und Tiefen in den erhaltenen Daten separat zählen. In diesem Fall ist die Anzahl der Extremwerte für die weitere Arbeit durch den Minimalbetrag zwischen diesen Zählern zu definieren. 

Die gleichnamige Methode CZigZagModule::GetZigZagData() wird hier mit einem anderen Parametersatz ganz am Ende aufgerufen. Wir haben diesen Set oben betrachtet, während wir beschreiben, wie Arrays mit Quelldaten als Parameter übergeben werden sollten, um die endgültigen Daten zu erhalten.

class CZigZagModule
  {
private:
   //--- Arrays für die Übernahme der Quelldaten
   double            m_zz_lows_temp[];
   double            m_zz_highs_temp[];
   datetime          m_zz_time_temp[];
   //---
public:
   //--- Datenabruf
   void              GetZigZagData(const int handle,const string symbol,const ENUM_TIMEFRAMES period,const datetime start_time,const datetime stop_time);
  };
//+------------------------------------------------------------------+
//| Abrufen der ZZ-Daten mit dem übergebenen Handle                  |
//+------------------------------------------------------------------+
void CZigZagModule::GetZigZagData(const int handle,const string symbol,const ENUM_TIMEFRAMES period,const datetime start_time,const datetime stop_time)
  {
//--- Abrufen der Quelldaten
   ::CopyTime(symbol,period,start_time,stop_time,m_zz_time_temp);
   ::CopyBuffer(handle,2,start_time,stop_time,m_zz_highs_temp);
   ::CopyBuffer(handle,3,start_time,stop_time,m_zz_lows_temp);
//--- Zähler
   int lows_counter  =0;
   int highs_counter =0;
//--- Zählen der Hochs
   int h_total=::ArraySize(m_zz_highs_temp);
   for(int i=0; i<h_total; i++)
     {
      if(m_zz_highs_temp[i]>0)
         highs_counter++;
     }
//--- Zählen der Tiefs
   int l_total=::ArraySize(m_zz_lows_temp);
   for(int i=0; i<l_total; i++)
     {
      if(m_zz_lows_temp[i]>0)
         lows_counter++;
     }
//--- Abfragen der Anzahl der Extrema
   int copy_extremums=(int)::fmin((double)highs_counter,(double)lows_counter);
   CopyExtremums(copy_extremums);
//--- Iterieren über die ZZ-Werte
   GetZigZagData(m_zz_highs_temp,m_zz_lows_temp,m_zz_time_temp);
  }

Wir verwenden die Methoden CZigZagModule::SmallestMinimumTime() und CZigZagModule::LargestMaximumTime(), um den Zeitpunkt des niedrigsten und des höchsten Extremums im erhaltenen Datensatz zu ermitteln. 

class CZigZagModule
  {
public:
   //--- Kleinste Zeit der Minima
   datetime          SmallestMinimumTime(void);
   //--- Größte Zeit der Maxima
   datetime          LargestMaximumTime(void);
  };
//+------------------------------------------------------------------+
//| Kleinste Zeit der Minima                                         |
//+------------------------------------------------------------------+
datetime CZigZagModule::SmallestMinimumTime(void)
  {
   return(m_zz_low_time[::ArrayMinimum(m_zz_low)]);
  }
//+------------------------------------------------------------------+
//| Größte Zeit der Maxima                                           |
//+------------------------------------------------------------------+
datetime CZigZagModule::LargestMaximumTime(void)
  {
   return(m_zz_high_time[::ArrayMaximum(m_zz_high)]);
  }

Außerdem möchten wir die Liste der Methoden für die Arbeit mit ZigZag-Segmenten erweitern. Es kann bequem sein, mehrere Werte in Variablen zu bekommen, die von Links gleichzeitig übergeben werden. Die Klasse verfügt über drei solcher Methoden:

  • SegmentBars() gibt den Start- und Endbalkenindex eines bestimmten Segments zurück.
  • Segmentpreise() gibt die Start- und Endpreise eines bestimmten Segments zurück.
  • SegmentTimes() gibt die Start- und Endzeit eines bestimmten Segments zurück.

Eine ähnliche Struktur ist bei anderen zuvor betrachteten Methoden vorhanden, weshalb im Folgenden nur ein Beispielcode angegeben wird. 

class CZigZagModule
  {
public:
   //--- Rückgabe des ersten und letzten Balkens im angegebenen Segment
   bool              SegmentBars(const int index,int &start_bar,int &stop_bar);
   //--- Rückgabe des ersten und letzten Preises im angegebenen Segment
   bool              SegmentPrices(const int index,double &start_price,double &stop_price);
   //--- Rückgabe des ersten und letzten Zeitpunktes im angegebenen Segment
   bool              SegmentTimes(const int index,datetime &start_time,datetime &stop_time);
  };
//+------------------------------------------------------------------+
//| Rückgabe des ersten und letzten Balkens im angegebenen Segment   |
//+------------------------------------------------------------------+
bool CZigZagModule::SegmentBars(const int index,int &start_bar,int &stop_bar)
  {
   if(index>=m_segments_total)
      return(false);
//--- Bei einer geraden Anzahl
   if(index%2==0)
     {
      int i=index/2;
      //---
      start_bar =(Direction()>0)? m_zz_low_bar[i] : m_zz_high_bar[i];
      stop_bar  =(Direction()>0)? m_zz_high_bar[i] : m_zz_low_bar[i];
     }
//--- Bei einer ungeraden Anzahl
   else
     {
      int l=0,h=0;
      //---
      if(Direction()>0)
        {
         h=(index-1)/2+1;
         l=(index-1)/2;
         //---
         start_bar =m_zz_high_bar[h];
         stop_bar  =m_zz_low_bar[l];
        }
      else
        {
         h=(index-1)/2;
         l=(index-1)/2+1;
         //---
         start_bar =m_zz_low_bar[l];
         stop_bar  =m_zz_high_bar[h];
        }
     }
//---
   return(true);
  }

Angenommen, wir haben ein M5-Diagramm und erhalten Daten von H1. Wir suchen nach Mustern aus dem H1-Zeitrahmen und müssen das Preisverhalten eines bestimmten ZigZag-Segments aus dem H1-Zeitraum gegenüber dem aktuellen definieren. Mit anderen Worten, wir wollen wissen, wie sich das angegebene Segment in einem niedrigeren Zeitrahmen gebildet hat.

Wie im vorherigen Abschnitt gezeigt, werden Extremwerte von Segmenten aus höheren Zeitrahmen auf dem aktuellen durch die offene Zeitspanne der höheren Zeitrahmen angezeigt. Wir haben bereits die Methode CZigZagModule::SegmentTimes(), die die Start- und Endzeit eines bestimmten Segments zurückgibt. Wenn wir diesen Zeitbereich nutzen, um ZigZag-Daten aus einem niedrigeren Zeitrahmen zu erhalten, dann erhalten wir in den meisten Fällen viele redundante Segmente, die tatsächlich zu anderen Segmenten eines höheren Zeitrahmens gehören. Schreiben wir noch die weitere Methode CZigZagModule::SegmentTimes() mit einem anderen Parametersatz, falls mehr Genauigkeit benötigt wird. Darüber hinaus benötigen wir mehrere private Hilfsmethoden zum Empfangen von (1) Quelldaten und (2) Indizes von Minimal- und Maximalwerten in den übergebenen Arrays. 

class CZigZagModule
  {
private:
   //--- Kopieren der Quelldaten in das übergebene Array
   void              CopyData(const int handle,const int buffer_index,const string symbol,
                              const ENUM_TIMEFRAMES period,datetime start_time,datetime stop_time,
                              double &zz_array[],datetime &time_array[]);
   //--- Rückgabe des Index vom (1) Minimum und (2) Maximum des übergebenen Arrays
   int               GetMinValueIndex(double &zz_lows[]);
   int               GetMaxValueIndex(double &zz_highs[]);
  };
//+------------------------------------------------------------------+
//| Kopieren der Quelldaten in das übergebene Array                  |
//+------------------------------------------------------------------+
void CZigZagModule::CopyData(const int handle,const int buffer_index,const string symbol,
                             const ENUM_TIMEFRAMES period,datetime start_time,datetime stop_time,
                             double &zz_array[],datetime &time_array[])
  {
   ::CopyBuffer(handle,buffer_index,start_time,stop_time,zz_array);
   ::CopyTime(symbol,period,start_time,stop_time,time_array);
  }
//+------------------------------------------------------------------+
//| Rückgabe des Index des Maximums im übergebenen Array             |
//+------------------------------------------------------------------+
int CZigZagModule::GetMaxValueIndex(double &zz_highs[])
  {
   int    max_index =0;
   double max_value =0;
   int total=::ArraySize(zz_highs);
   for(int i=0; i<total; i++)
     {
      if(zz_highs[i]>0)
        {
         if(zz_highs[i]>max_value)
           {
            max_index =i;
            max_value =zz_highs[i];
           }
        }
     }
//---
   return(max_index);
  }
//+------------------------------------------------------------------+
//| Rückgabe des Index des Minimums im übergebenen Array             |
//+------------------------------------------------------------------+
int CZigZagModule::GetMinValueIndex(double &zz_lows[])
  {
   int    min_index =0;
   double min_value =INT_MAX;
   int total=::ArraySize(zz_lows);
   for(int i=0; i<total; i++)
     {
      if(zz_lows[i]>0)
        {
         if(zz_lows[i]<min_value)
           {
            min_index =i;
            min_value =zz_lows[i];
           }
        }
     }
//---
   return(min_index);
  }

Eine weitere Methode CZigZagModule::SegmentTimes() ist implementiert, um die Start- und Endzeit eines bestimmten Segments unter Berücksichtigung eines niedrigeren Zeitrahmens zu empfangen. Dies erfordert einige Erklärungen. Die folgenden Parameter werden der Methode übergeben:

  • handle — Handle des ZigZag-Indikators aus einem niedrigeren Zeitrahmen.
  • highs_buffer_index — Index des Indikatorpuffers mit den Maxima.
  • lows_buffer_index — Index des Indikatorpuffers mit den Minima.
  • Symbol — unteres Zeitrahmensymbol.
  • Periode — Periodenlänge des höheren Zeitrahmens.
  • in_period — Periodenlänge des niedrigeren Zeitrahmens.
  • index — Segmentindex des höheren Zeitrahmens.

Rückgabe von Parameterwerten, die durch Referenz übergeben werden:

  • start_zeit - Segmentstartzeit unter Berücksichtigung eines niedrigeren Zeitrahmens.
  • stop_time — Segmentendzeit unter Berücksichtigung eines niedrigeren Zeitrahmens.

Zuerst müssen wir die Öffnungszeit des ersten und letzten Balkens eines bestimmten Segments ermitteln. Dazu rufen wir die die erste Methode CZigZagModule::SegmentTimes() wie oben beschrieben auf. 

Anschließend verwenden wir die Methode CZigZagModule::CopyData(), um Daten über Extremwerte und die Zeit der Balken zu empfangen. Abhängig von der Segmentrichtung erhalten wir Daten in einer bestimmten Reihenfolge. Im Falle der Aufwärtsrichtung erhalten wir zunächst Daten über die Minima des unteren Zeitrahmens ZigZag, die Teil des Segments des ersten Balkens auf einem höheren Zeitrahmen sind. Danach erhalten wir Daten über die Maxima des unteren Zeitrahmens ZigZag, die Teil des Segments des letzten Balkens auf einem höheren Zeitrahmen sind. Bei der Abwärtsrichtung wird die Reihenfolge der Aktionen umgekehrt. Erstens müssen wir Daten über die Maxima erhalten, gefolgt von Informationen über die Minima. 

Nach dem Empfangen der Quelldaten finden wir die Indizes der Maximal- und Minimalwerte. Anhand dieser Indizes können Sie die Start- und Endzeit des analysierten Segments in einem niedrigeren Zeitrahmen ermitteln.

class CZigZagModule
  {
public:
   //--- Rückgabe des ersten und letzten Zeitpunktes im angegebenen Segment des niedrigeren Zeitrahmens
   bool              SegmentTimes(const int handle,const int highs_buffer_index,const int lows_buffer_index,
                                  const string symbol,const ENUM_TIMEFRAMES period,const ENUM_TIMEFRAMES in_period,
                                  const int index,datetime &start_time,datetime &stop_time);
  };
//+------------------------------------------------------------------+
//| Rückgabe des ersten und letzten Zeitpunktes im Segment           |
//| des niedrigeren Zeitrahmens                                      |
//+------------------------------------------------------------------+
bool CZigZagModule::SegmentTimes(const int handle,const int highs_buffer_index,const int lows_buffer_index,
                                 const string symbol,const ENUM_TIMEFRAMES period,const ENUM_TIMEFRAMES in_period,
                                 const int index,datetime &start_time,datetime &stop_time)
  {
//--- Abrufen der Zeit ohne Beachtung auf den abrufen Zeitrahmen
   datetime l_start_time =NULL;
   datetime l_stop_time  =NULL;
   if(!SegmentTimes(index,l_start_time,l_stop_time))
      return(false);
//---
   double   zz_lows[];
   double   zz_highs[];
   datetime zz_lows_time[];
   datetime zz_highs_time[];
   datetime start =NULL;
   datetime stop  =NULL;
   int      period_seconds=::PeriodSeconds(period);
//--- Abrufen der Quelldaten im Falle einer Aufwärtsrichtung
   if(SegmentDirection(index)>0)
     {
      //--- Daten des ersten Balkens des höheren Zeitrahmens
      start =l_start_time;
      stop  =l_start_time+period_seconds;
      CopyData(handle,lows_buffer_index,symbol,in_period,start,stop,zz_lows,zz_lows_time);
      //--- Daten des letzten Balkens des höheren Zeitrahmens
      start =l_stop_time;
      stop  =l_stop_time+period_seconds;
      CopyData(handle,highs_buffer_index,symbol,in_period,start,stop,zz_highs,zz_highs_time);
     }
//--- Abrufen der Quelldaten im Falle einer Abwärtsrichtung
   else
     {
      //--- Daten des ersten Balkens des höheren Zeitrahmens
      start =l_start_time;
      stop  =l_start_time+period_seconds;
      CopyData(handle,highs_buffer_index,symbol,in_period,start,stop,zz_highs,zz_highs_time);
      //--- Daten des letzten Balkens des höheren Zeitrahmens
      start =l_stop_time;
      stop  =l_stop_time+period_seconds;
      CopyData(handle,lows_buffer_index,symbol,in_period,start,stop,zz_lows,zz_lows_time);
     }
//--- Suche nach Index des Maximums
   int max_index =GetMaxValueIndex(zz_highs);
//--- Suche nach Index des Minimums
   int min_index =GetMinValueIndex(zz_lows);
//--- Abrufen der Anfangs- und Endzeit des Segments
   start_time =(SegmentDirection(index)>0)? zz_lows_time[min_index] : zz_highs_time[max_index];
   stop_time  =(SegmentDirection(index)>0)? zz_highs_time[max_index] : zz_lows_time[min_index];
//--- Erfolgreich
   return(true);
  }

Jetzt schreiben wir einen EA für die Tests. Der aktuelle Zeitrahmen ist M5. Betätigen Sie den Button, um das EA im Visualisierungsmodus des Strategietesters zu starten. Wir werden sowohl Daten aus dem ersten Halbjahr als auch aus dem aktuellen Zeitrahmen erhalten. Der EA-Code ist ähnlich wie der zuvor betrachtete, daher werde ich hier nur den Inhalt der Funktion OnTick() zeigen.

Zuerst werden wir die Daten für H1 mit der ersten Methode erhalten und die Segmente zur besseren Übersichtlichkeit auf dem Diagramm anzeigen. Als nächstes erhalten wir ZigZag-Daten aus dem aktuellen Zeitrahmen (M5) über den Zeitbereich des dritten (Index 2) ZigZag-Segments von H1. Dazu erhalten wir den Anfang und das Ende des Segments unter Berücksichtigung des aktuellen Zeitrahmens.

Dann rufen wir die Daten für den aktuellen Zeitrahmen mit der zweiten Methode ab und auch die Segmente auf dem Chart anzeigen, um sicherzustellen, dass alles in Ordnung ist.

//+------------------------------------------------------------------+
//| Experten Funktion OnTick                                         |
//+------------------------------------------------------------------+
void OnTick(void)
  {
   int copy_total=1000;
   int h_buff=2,l_buff=3;
//--- Erste Methode zum Abrufen der Daten
   ::CopyTime(_Symbol,PERIOD_H1,0,copy_total,t_zz);
   ::CopyBuffer(zz_handle_h1,h_buff,0,copy_total,h_zz);
   ::CopyBuffer(zz_handle_h1,l_buff,0,copy_total,l_zz);
   zz_h1.GetZigZagData(h_zz,l_zz,t_zz);
   zz_h1.ShowSegments("_h1");
//---
   int      segment_index =2;
   int      start_bar     =0;
   int      stop_bar      =0;
   double   start_price   =0.0;
   double   stop_price    =0.0;
   datetime start_time    =NULL;
   datetime stop_time     =NULL;
   datetime start_time_in =NULL;
   datetime stop_time_in  =NULL;
//---
   zz_h1.SegmentBars(segment_index,start_bar,stop_bar);
   zz_h1.SegmentPrices(segment_index,start_price,stop_price);
   zz_h1.SegmentTimes(segment_index,start_time,stop_time);
   zz_h1.SegmentTimes(zz_handle_current,h_buff,l_buff,_Symbol,PERIOD_H1,_Period,segment_index,start_time_in,stop_time_in);
   
//--- Zweite Methode zum Abrufen der Daten
   zz_current.GetZigZagData(zz_handle_current,_Symbol,_Period,start_time_in,stop_time_in);
   zz_current.ShowSegments("_current");
   
//--- Datenanzeige im Kommentar des Charts
   string comment="Current direction : "+string(zz_h1.Direction())+"\n"+
                  "\n---\n"+
                  "Direction > segment["+string(segment_index)+"]: "+string(zz_h1.SegmentDirection(segment_index))+
                  "\n---\n"+
                  "Start bar > segment["+string(segment_index)+"]: "+string(start_bar)+"\n"+
                  "Stop bar > segment["+string(segment_index)+"]: "+string(stop_bar)+
                  "\n---\n"+
                  "Start price > segment["+string(segment_index)+"]: "+::DoubleToString(start_price,_Digits)+"\n"+
                  "Stop price > segment["+string(segment_index)+"]: "+::DoubleToString(stop_price,_Digits)+
                  "\n---\n"+
                  "Start time > segment["+string(segment_index)+"]: "+::TimeToString(start_time,TIME_DATE|TIME_MINUTES)+"\n"+
                  "Stop time > segment["+string(segment_index)+"]: "+::TimeToString(stop_time,TIME_DATE|TIME_MINUTES)+
                  "\n---\n"+
                  "Start time (in tf) > segment["+string(segment_index)+"]: "+::TimeToString(start_time_in,TIME_DATE|TIME_MINUTES)+"\n"+
                  "Stop time (in tf) > segment["+string(segment_index)+"]: "+::TimeToString(stop_time_in,TIME_DATE|TIME_MINUTES)+
                  "\n---\n"+
                  "Extremums copy: "+string(zz_current.CopyExtremums())+"\n"+
                  "SmallestMinimumTime(): "+string(zz_current.SmallestMinimumTime())+"\n"+
                  "LargestMaximumTime(): "+string(zz_current.LargestMaximumTime());
//---
   ::Comment(comment);
  }

Das schaut dann so aus:

 Abb. 5. Empfangen von Daten innerhalb des angegebenen Segments

Abb. 5. Empfangen von Daten innerhalb des angegebenen Segments

Als Nächstes entwickeln wir einen weiteren EA für den Empfang von Daten aus den drei Segmenten eines höheren Zeitrahmens.

Wir sollten nun vier Instanzen der Klasse CZigZagModule am Anfang der Datei deklarieren. Eine davon ist für den höheren Zeitrahmen (H1) gedacht, während die restlichen drei für den aktuellen Zeitrahmen bestimmt sind. In diesem Fall führen wir Tests an M5 durch. 

CZigZagModule zz_h1;
CZigZagModule zz_current0;
CZigZagModule zz_current1;
CZigZagModule zz_current2;

Zur besseren Übersichtlichkeit werden die Segmente des unteren Zeitrahmens innerhalb der Segmente des höheren Zeitrahmens in verschiedenen Farben dargestellt:

//---- Setzt die Segmentfarbe
   zz_current0.LinesColor(clrRed);
   zz_current1.LinesColor(clrLimeGreen);
   zz_current2.LinesColor(clrMediumPurple);
   zz_h1.LinesColor(clrCornflowerBlue);

In der Funktion OnTick() empfangen wir zuerst H1-Zeitrahmendaten und erhalten dann Daten aus dem unteren Zeitrahmen für das erste, zweite und dritte Segment in Folge. Wir zeigen die Daten zu jeder Gruppe der erhaltenen Segmente des unteren Zeitrahmens und des höheren Zeitrahmens separat in einem Chart-Kommentar an. In diesem Fall ist dies die Differenz zwischen den Prozentsätzen der Segmentbeträge. Sie kann mit der Methode CZigZagModule::PercentSumSegmentsDifference() ermittelt werden. 

//+------------------------------------------------------------------+
//| Experten Funktion OnTick                                         |
//+------------------------------------------------------------------+
void OnTick(void)
  {
   int copy_total=1000;
   int h_buff=2,l_buff=3;
//--- Erste Methode zum Abrufen der Daten
   ::CopyTime(_Symbol,PERIOD_H1,0,copy_total,t_zz);
   ::CopyBuffer(zz_handle_h1,h_buff,0,copy_total,h_zz);
   ::CopyBuffer(zz_handle_h1,l_buff,0,copy_total,l_zz);
   zz_h1.GetZigZagData(h_zz,l_zz,t_zz);
   zz_h1.ShowSegments("_h1");
//---
   datetime start_time_in =NULL;
   datetime stop_time_in  =NULL;
//--- Erste Daten des Segments
   zz_h1.SegmentTimes(zz_handle_current,h_buff,l_buff,_Symbol,PERIOD_H1,_Period,0,start_time_in,stop_time_in);
   zz_current0.GetZigZagData(zz_handle_current,_Symbol,_Period,start_time_in,stop_time_in);
   zz_current0.ShowSegments("_current0");
//--- Zweite Daten des Segments
   zz_h1.SegmentTimes(zz_handle_current,h_buff,l_buff,_Symbol,PERIOD_H1,_Period,1,start_time_in,stop_time_in);
   zz_current1.GetZigZagData(zz_handle_current,_Symbol,_Period,start_time_in,stop_time_in);
   zz_current1.ShowSegments("_current1");
//--- Dritte Daten des Segments
   zz_h1.SegmentTimes(zz_handle_current,h_buff,l_buff,_Symbol,PERIOD_H1,_Period,2,start_time_in,stop_time_in);
   zz_current2.GetZigZagData(zz_handle_current,_Symbol,_Period,start_time_in,stop_time_in);
   zz_current2.ShowSegments("_current2");
//--- Datenanzeige im Kommentar des Charts
   string comment="H1: "+::DoubleToString(zz_h1.PercentSumSegmentsDifference(),2)+"\n"+
                  "segment[0]: "+::DoubleToString(zz_current0.PercentSumSegmentsDifference(),2)+"\n"+
                  "segment[1]: "+::DoubleToString(zz_current1.PercentSumSegmentsDifference(),2)+"\n"+
                  "segment[2]: "+::DoubleToString(zz_current2.PercentSumSegmentsDifference(),2);
//---
   ::Comment(comment);
  }

Hier ist, wie es auf dem Chart aussieht:

 Abb. 6. Empfangen von Daten innerhalb der drei angegebenen Segmente

Abb. 6. Empfangen von Daten innerhalb der drei angegebenen Segmente

Dieser Ansatz bietet zusätzliche Möglichkeiten, die Art des Preisverhaltens innerhalb von Mustern zu analysieren. Angenommen, wir definieren das Muster für H1 und analysieren, wie sich der Preis in jedem Segment verhalten hat. Die Klassenmethoden CZigZagModule ermöglichen das Erhalten aller Eigenschaften von Extremen und Segmenten, wie z.B.:

  • Preis, Zeit und Index eines Balkens von jedem einzelnen Extremum.
  • Größe der einzelnen Segmente.
  • Dauer der einzelnen Segmente in Balken.
  • Größe der Preisspanne des gesamten Satzes der erhaltenen Segmente.
  • Das gesamte Segment setzt die Formierungsdauer (in Balken).
  • Summen von unidirektionalen Segmenten.
  • Verhältnisse der Summen der gegenläufigen Segmente usw. 

Dieser Basissatz kann als Ausgangspunkt für die Entwicklung mehrerer benutzerdefinierter Parameter verwendet werden, um daraus Indikatoren zu erstellen. Die Tests werden zeigen, welche Vorteile sich daraus ableiten lassen. Diese Website enthält eine Reihe von Artikeln, die bei der Durchführung Ihrer eigenen Forschung zu diesem Thema hilfreich sein können. 


Schlussfolgerung

Die Vorstellung, dass der ZigZag nicht geeignet ist, Handelssignale zu erzeugen, ist in Handelsforen weit verbreitet. Das ist ein großes Missverständnis. Tatsächlich liefert kein anderer Indikator so viele Informationen, um die Art des Preisverhaltens zu bestimmen. Jetzt haben Sie ein Werkzeug, mit dem Sie auf einfache Weise alle notwendigen ZigZag-Indikatorendaten für eine detailliertere Analyse erhalten.

In den kommenden Artikeln der Serie werde ich zeigen, welche Indikatoren mit der Klasse CZigZagModule entwickelt werden können, sowie welche EAs zum Erhalten von Statistiken über verschiedene Symbole aus dem ZigZag-Indikator und zum Überprüfen einiger ZigZag-basierter Handelsstrategien entwickelt werden können.

Dateiname Kommentar
MQL5\Indicators\Custom\ZigZag\ExactZZ_Plus.mq5 Modifizierter ZigZag-Indikator
MQL5\Experts\ZigZag\TestZZ_01.mq5 EA zum Testen eines einzelnen Datensatzes
MQL5\Experts\ZigZag\TestZZ_02.mq5 EA zum Testen von drei Datensets von unterschiedlichen Zeitrahmen
MQL5\Experts\ZigZag\TestZZ_03.mq5 EA zum Testen der Datenakquisition innerhalb des angegebenen Segments des höheren Zeitrahmens
MQL5\Experts\ZigZag\TestZZ_04.mq5 EA zum Testen der Datenakquisition innerhalb von drei angegebenen Segmenten des höheren Zeitrahmens


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

Beigefügte Dateien |
MQL5.zip (17.45 KB)
Martingale als Basis für eine langfristige Handelsstrategie Martingale als Basis für eine langfristige Handelsstrategie

In diesem Artikel werden wir uns ausführlich mit dem Martingal-System befassen. Wir werden prüfen, ob dieses System im Handel eingesetzt werden kann und wie es zur Risikominimierung eingesetzt werden kann. Der Hauptnachteil dieses einfachen Systems ist die Wahrscheinlichkeit, dass die gesamte Einlage verloren geht. Diese Tatsache muss berücksichtigt werden, wenn Sie sich entscheiden, mit der Martingaltechnik zu handeln.

Die praktische Anwendung von Korrelationen im Handel Die praktische Anwendung von Korrelationen im Handel

In diesem Artikel werden wir das Konzept der Korrelation zwischen Variablen analysieren, sowie Methoden zur Berechnung von Korrelationskoeffizienten und deren praktische Anwendung im Handel. Eine Korrelation ist eine statistische Beziehung zwischen zwei oder mehreren zufälligen Variablen (oder Größen, die mit einem gewissen Maß an Genauigkeit als zufällig angesehen werden können). Ändert sich eine oder ändern sich mehrere Variablen, führt das zu systematischen Änderungen der anderen gekoppelten Variablen.

Untersuchung von Techniken zur Analyse der Kerzen (Teil I): Überprüfen vorhandener Muster Untersuchung von Techniken zur Analyse der Kerzen (Teil I): Überprüfen vorhandener Muster

In diesem Artikel werden wir uns mit den beliebten Kerzenmustern beschäftigen und versuchen herauszufinden, ob sie in den heutigen Märkten noch relevant und effektiv sind. Die Analyse von Kerzen ist vor mehr als 20 Jahren erschienen und erfreut sich inzwischen großer Beliebtheit. Viele Händler halten die japanischen Kerzen für die bequemste und leicht verständlichste Form der Visualisierung von Wertpapierpreisen.

Die Entwicklung von grafischen Oberflächen für Expert Advisors und Indikatoren auf Basis von .Net Framework und C# Die Entwicklung von grafischen Oberflächen für Expert Advisors und Indikatoren auf Basis von .Net Framework und C#

Der Artikel stellt eine einfache und schnelle Methode zur Erstellung von grafischen Fenstern mit Visual Studio mit anschließender Integration in den MQL-Code des Expert Advisors vor. Der Artikel richtet sich an ein nicht spezialisiertes Publikum und erfordert keine Kenntnisse der Technologie von C# oder .Net.