Der ZigZag-Indikator: Frischer Ansatz und Neue Lösungen

Sergey Pavlov | 27 Juni, 2016

Einleitung

Sicherlich kennt jeder Händler den ZigZag-Indikator, der zur Analyse von Kursbewegungen einer gegebenen oder größeren Amplitude gedacht ist. Eine ZigZag-Linie ist eine gestrichelte Linie, deren Knoten sich an den Hochs und Tiefs des Kurscharts befinden.

Von diesem Indikator gibt es viele Varianten: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16. Und dennoch sind viele MQL5-Programmentwickler scharf darauf, ihren eigenen 'idealen' ZigZag zu erzeugen. Die hauptsächlichen Nachteile eines ZigZag-Indikators sind Verzögerungen, nicht exakte Markierungen fragwürdiger Knoten (externer Bar) und eine nicht zufriedenstellende Leistung.

Meiner Meinung nach hat Yuri Kulikov (Yurich) die eleganteste Implementierung eines ZigZags vorgeschlagen. Übrigens gibt es einige sehr gute MQL4-Beiträge zu diesem Thema, wie "Laymans Anmerkungen: ZigZag..." und "Die Show muss weiter gehen. Oder: Schon wieder was zu ZigZags". Das Thema scheint gründlich ausgeforscht zu sein, da die Anzahl der zur Verfügung stehenden Publikationen weiter zunimmt. Also muss doch irgendetwas Faszinierendes an der Sache dran sein. Und jetzt ist auch mein Interesse geweckt, vor allem an der Möglichkeit, einen fortgeschrittenen ZigZag-Indikator erzeugen zu können.

Dieser Beitrag beschäftigt sich mit der Möglichkeit, einen fortgeschrittenen ZigZag-Indikator mittels des Envelopes-Indikator zu erzeugen. Es wird davon ausgegangen, dass wir eine bestimmte Kombination von Eingabe-Parametern für eine Reihe von Envelopes finden können, bei denen die meisten ZigZag-Knoten innerhalb der Grenzen der Envelopes-Bänder liegen.

 

Eine Methode zur Erzeugung eines fortgeschrittenen ZigZag-Indikators

Setzen wir uns ein Ziel: Wir wollen die Koordinaten von zwei Knoten finden - den aktuellen und den vorhergesagten Knoten (Abb. 1). Der aktuelle Knoten ist derjenige, der noch nicht abgeschlossen ist und nach dessen Koordinaten immer noch gesucht wird oder die angepasst werden. Zudem befindet er sich immer auf dem aktuellen (Null) Bar. Obwohl er in der Zukunft liegt, muss ein vorhergesagter Knoten die geschätzte Ebene des nächsten ZigZag-Knotens zeigen.

Neue ZigZag-Knoten vorhersagen

Abb. 1 Neue ZigZag-Knoten vorhersagen: der aktuelle und der nächste Knoten.

Unser Ziel ist also gesetzt und wir haben eine Vorstellung davon, wie wir Gleitender Mittelwert Envelopes als eine Grundlage zum Bau eines fortgeschrittenen Indikators (Abb. 2) verwenden. Wir suchen also nach Envelopes, die nur minimal von ZigZag-Knoten abweichen. Es erscheint nur logisch, dass nach Envelopes für ZigZag Spitzen und Täler separat gesucht werden muss.

ZigZag-Indikatoren und Gleitender Mittelwert Envelopes

Abb. 2 ZigZag-Indikatoren und Gleitender Mittelwert Envelopes.

Zur Erhöhung der statistischen Bedeutung der Prognose, sollten wir, anstatt nur einen oder sogar 10 Envelopes-Indikatoren zu verwenden, mit einem Pool an 100 oder mehr Indikatoren mit unterschiedlichen Eingabedaten arbeiten. Sie unterscheiden sich im Durchschnittszeitraum der Hauptindikatorlinie und dem verwendeten Kurs (Hoch für Spitzen und Gering für Täler). Führen wir also die folgenden Bezeichnungen und Formeln ein:

Wir haben zwei Indikator-Pools: einen für Spitzen und den anderen für Täler (mit jeweils ca. 100 Indikatoren). Für jeden Indikator im Pool berechnen wir die Abweichung der ZigZag-Knoten von der Hauptlinie des Envelopes-Indikators und finden mit Hilfe der o.g. Formeln das arithmetische Mittel der Abweichungen für jeden Indikator aus dem Pool. Die folgende Abb. zeigt ein Abweichungsdiagramm im Hinblick auf die identifzierten Knoten ZZ von der Hauptlinie ENV für einen Indikator.

Abweichungsdiagramm für ZZ Knoten von ENV

Abb. 3 Abweichungsdiagramm für ZZ Knoten von ENV.

Das arithmetische Mittel der Abweichungen wird zur Feststellung der Ebene benutzt, auf die die Hauptlinie des Envelopes-Indikators verschoben werden sollte, um die Envelopes-Bänder grafisch zeichnen zu können. Daher brauchen wir also das arithmetische Mittel der Abweichungen von den ZigZag-Spitzen zum Zeichnen der oberen Linie und das arithmetische Mittel der Abweichungen von den ZigZag-Tälern zum Zeichnen der unteren Linie des Envelopes-Indikators.

Diese obere und untere Linien der Envelopes werden wir zur Suche der charakteristischen Punkte und zur Prognose der ZigZag-Knoten verwenden. Noch einmal: Wir sind an einem Pool von Envelopes interessiert, der aus einer Reihe von Envelopes.Indikatoren besteht. Für jeden Indikator wird das arithmetische Mittel der Abweichungen der ZigZag-Knoten von der Hauptlinie eines gegebenen Envelopes berechnet wird. Nach der grafischen Darstellung der sich ergebenden Linien (die obere und untere) des Pools auf dem Chart, können wir nun folgendes erkennen:

Die Envelopes-Linien auf der Ebene

Abb. 4 Die Envelopes-Linien auf der Ebene.

Wenn wir annehmen, dass jede Linie auf einer separaten Ebene liegt, und sie alle zusammen eine Oberfläche ergeben, zeigt uns die Abbildung oben nur die Projektion jedes Indikators auf der Kurschart-Ebene. Ein 3D-Bild dieser Linien sieht im Groben ungefähr so aus:

Die Envelopes-Linien in 3D

Abb. 5 Die Envelopes-Linien in 3D.

Nun zu einem kurzen Ausflug in die Geometrie. Stellen Sie sich vor, der Pool der Linien des Envelopes-Indikators ist eine 3D-Oberfläche. Nehmen Sie nun eine Fläche, senkrecht zum Kurschart, die die Oberfläche am aktuellen (Null) Bar durchschneidet.

Als Ergebnis erhalten wir einen Querschnitt der Oberfläche, der einer Kurve ähnelt (die oben gezeigten Abbildungen bilden einen Spezialfall ab, wo die Kurve eine gerade Linie ist). Für unsere Prognose ist es ausreichend, die Koordinaten an jedem Punkt auf der Kurve zu haben, die für weitere Berechnungen verwendet werden.

Wir brauchen die folgenden Querschnitts-Charakteristika: maximaler und minimaler Punkt, sowie den Schwerpunkt jedes Querschnitt (das arithmetische Mittel aller Punktwerte). Die erhaltenen charakteristischen Punkte werden nun auf den aktuellen (Null) Bar projiziert und die relevanten Daten in der History abgelegt. Die charakteristischen Punkte dienen uns dann als Basis für den aktuellen und die nächsten ZigZag-Knoten.

Da ja die Suche nach Envelope-Bändern für Spitzen und Täler separat durchgeführt wird, sollten wir als Ergebnis zwei Querschnitte erhalten - einen für Spuitzen und einen weiteren für Täler.

Um die Prognose machen zu können, verwenden wir den nächstliegenden charakteristischen Punkt. Wenn wir also z.B. nach einer ZigZag-Spitze suchen, nehmen wir die charakteristischen Punkte des Querschnitts her, der sich aus der Kreuzung der Oberfläche der oberen Linien des Envelopes-Indikators mit einer Schnittebene ergibt. Und umgekehrt: Um ein ZigZag-Tal zu finden, nehmen wir die charakteristischen Punkte des Querschnitts her, der sich aus der Kreuzung der Oberfläche der unteren Linien des Envelopes-Indikators mit einer Schnittebene ergibt.

 

Den neuen Indikator testen

Da wir jetzt die Methode definiert haben, erzeugen wir also den Indikator. Zuerst finden wir die letzten Knoten des ZigZag-Indikators und zeichnen sie auf das Chart. Dazu verwenden wir die AdvancedZigZag Klasse, die für anstehende Aufgabe geschrieben wurde:

//+------------------------------------------------------------------+
//|                                               AdvancedZigZag.mqh |
//|                                           Copyright 2013, DC2008 |
//|                           https://www.mql5.com/ru/users/DC2008 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2013, DC2008"
#property link      "https://www.mql5.com/ru/users/DC2008"
#property version   "1.00"
//+------------------------------------------------------------------+
//|                                                 GetExtremums.mqh |
//+------------------------------------------------------------------+
#include <GetExtremums.mqh>   // author of the code Yurich
#property copyright "Copyright 2012, Yurich"
#property link      "https://www.mql5.com/ru/users/Yurich"
//+------------------------------------------------------------------+
//| ZigZag node structure                                            |
//+------------------------------------------------------------------+
struct MqlZigZag
  {
   double            price;   // Node coordinate
   datetime          t;       // Time
  };
//+------------------------------------------------------------------+
//| The AdvancedZigZag class                                         |
//+------------------------------------------------------------------+
class AdvancedZigZag
  {
private:
   MqlRates          rt[];
   dextremum         zz[];
   int               history;
   double            amplitude;
public:
   dextremum         zHL[];
   MqlZigZag         zzH[],zzL[];
   int               Count(const double range);
   int               Read(const int nodes);
                     AdvancedZigZag(const int bars);
                    ~AdvancedZigZag();
  };
//+------------------------------------------------------------------+
//| Class constructor                                                |
//+------------------------------------------------------------------+
AdvancedZigZag::AdvancedZigZag(const int bars)
  {
   history=bars;
   amplitude=0;
  }
//+------------------------------------------------------------------+
//| The Read method of the class                                     |
//+------------------------------------------------------------------+
int AdvancedZigZag::Read(const int nodes)
  {
   CopyRates(NULL,0,TimeCurrent(),history,rt);
   int cnt=GetExtremums(amplitude,rt,zHL,nodes);
   return(cnt);
  }
//+------------------------------------------------------------------+
//| The Count method of the class                                    |
//+------------------------------------------------------------------+
int AdvancedZigZag::Count(const double range)
  {
   amplitude=range;
   CopyRates(NULL,0,TimeCurrent(),history,rt);
   int cnt=GetExtremums(amplitude,rt,zz);
   ArrayResize(zzH,cnt);
   ArrayResize(zzL,cnt);
   int h=0;
   int l=0;
   for(int i=0; i<cnt; i++)
     {
      if(zz[i].type>0)
        {
         zzH[h]=(MqlZigZag)zz[i];
         h++;
        }
      else
        {
         zzL[l]=(MqlZigZag)zz[i];
         l++;
        }
     }
   ArrayResize(zzH,h);
   ArrayResize(zzL,l);
   return(cnt);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
AdvancedZigZag::~AdvancedZigZag()
  {
  }

Insgesamt gibt es zwei Methoden:

Die GetExtremums Library (von Yury Kulikov) ist für die Suche nach Knoten ebenfalls notwendig.

Setzen wir den berücksichtigten Indikator in einen Expert Advisor. Warum ein Expert Advisor und kein Indikator? Das ist selbstverständlich Geschmackssache, doch für mich scheint das so weitaus effizienter. Die grafischen Features des Expert Advisors sind zweifellos schwächer, doch wir gewinnen an Leistung, da Indikatoren mit dem gleichen Symbol in einem einzigen Strom arbeiten - wohingegen jeder EA in seinem eigenen, extra Strom arbeitet. Werfen wir einen Blick auf den Code:

//+------------------------------------------------------------------+
//|                                                   two_Comets.mq5 |
//|                                           Copyright 2013, DC2008 |
//|                           https://www.mql5.com/ru/users/DC2008 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2013, DC2008"
#property link      "https://www.mql5.com/ru/users/DC2008"
#property version   "1.00"
#include <AdvancedZigZag.mqh>
//--- Depth of history for the indicator calculation
input int      depth_stories=5000;  // Depth stories for calculating the indicator [bars]
//--- Minimum ZigZag amplitude value
input int      amplitude=100;        // The minimum value of the amplitude of the indicator [points]
//--- Declaring the class
AdvancedZigZag Azz(depth_stories);
//---
#define NUMBER_MA   227
#define START_MA    5
//--- macros
#define SIZE(i)                     (double)i*0.3<1?1:(int)(i*0.25)
#define ObjF1                       ObjectSetString(0,name,OBJPROP_FONT,"Wingdings")
#define ObjF2                       ObjectSetInteger(0,name,OBJPROP_ANCHOR,ANCHOR_CENTER)
#define ObjF3(T)                    ObjectSetInteger(0,name,OBJPROP_TIME,T)
#define ObjF4(P)                    ObjectSetDouble(0,name,OBJPROP_PRICE,P)
#define ObjF5(size)                 ObjectSetInteger(0,name,OBJPROP_FONTSIZE,size)
#define ObjF6(code)                 ObjectSetString(0,name,OBJPROP_TEXT,CharToString(code))
#define ObjF7(clr)                  ObjectSetInteger(0,name,OBJPROP_COLOR,clr)
#define ObjF8                       ObjectSetInteger(0,name,OBJPROP_COLOR,clrMagenta)
#define ObjF9                       ObjectSetInteger(0,name,OBJPROP_WIDTH,3)
#define ObjF10                      ObjectSetInteger(0,name,OBJPROP_BACK,true) 
#define ObjFont                     ObjF1;ObjF2;
#define ObjCoordinates(T,P)         ObjF3(T);ObjF4(P);
#define ObjProperty(size,code,clr)  ObjF5(size);ObjF6(code);ObjF7(clr);
#define ObjZZ                       ObjF8;ObjF9;ObjF10;
//---
double      MA[1],sumHi[NUMBER_MA],sumLo[NUMBER_MA];
int         handle_MA_H[NUMBER_MA],handle_MA_L[NUMBER_MA];
datetime    t[1];
int         H,L;
int         t_min,t_max;
int         err=-1;
double      sumH[2],maxH[2],minH[2];
double      sumL[2],maxL[2],minL[2];
string      name;
int         count;
int         shift;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   shift=PeriodSeconds()/30;
//--- calculation of ZigZag nodes using historical data
   Azz.Count(amplitude*Point());
   H=ArraySize(Azz.zzH);
   L=ArraySize(Azz.zzL);
   if(H<30 || L<30)
     {
      Print("Not enough data to calculate ZigZag nodes: "+
            "increase the depth of history; "+
            "or decrease the amplitude value.");
      return(-1);
     }
//---
   for(int i=0; i<NUMBER_MA; i++)
     {
      handle_MA_H[i]=iMA(NULL,0,i+START_MA,0,MODE_SMA,PRICE_HIGH);
      handle_MA_L[i]=iMA(NULL,0,i+START_MA,0,MODE_SMA,PRICE_LOW);
     }
//---
   return(0);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   ObjectsDeleteAll(0,-1,-1);
   for(int i=0; i<NUMBER_MA; i++)
     {
      IndicatorRelease(handle_MA_H[i]);
      IndicatorRelease(handle_MA_L[i]);
     }
//---
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+

void OnTick()
  {
//--- get the current bar's opening time value
   CopyTime(NULL,0,0,1,t);
//--- ZigZag: last 7 nodes
   count=Azz.Read(7);
   for(int i=1; i<count; i++)
     {
      name="ZZ"+(string)i;
      ObjectCreate(0,name,OBJ_TREND,0,0,0);
      ObjectSetInteger(0,name,OBJPROP_COLOR,clrRed);
      ObjectSetInteger(0,name,OBJPROP_WIDTH,10);
      ObjectSetInteger(0,name,OBJPROP_BACK,true);
      ObjectSetDouble(0,name,OBJPROP_PRICE,0,Azz.zHL[i-1].value);
      ObjectSetInteger(0,name,OBJPROP_TIME,0,Azz.zHL[i-1].time);
      ObjectSetDouble(0,name,OBJPROP_PRICE,1,Azz.zHL[i].value);
      ObjectSetInteger(0,name,OBJPROP_TIME,1,Azz.zHL[i].time);
     }
//--- check for integrity of preliminary calculations
   if(err<0)
     {
      //--- calculate the sums of deviations of the nodes from MA for ZigZag peaks
      ArrayInitialize(sumHi,0.0);
      for(int j=H-1; j>=0; j--)
        {
         for(int i=0; i<NUMBER_MA; i++)
           {
            err=CopyBuffer(handle_MA_H[i],0,Azz.zzH[j].t,1,MA);
            if(err<0) return;
            sumHi[i]+=Azz.zzH[j].price-MA[0];
           }
        }
      //--- calculate the sums of deviations of the nodes from MA for ZigZag troughs
      ArrayInitialize(sumLo,0.0);
      for(int j=L-1; j>=0; j--)
        {
         for(int i=0; i<NUMBER_MA; i++)
           {
            err=CopyBuffer(handle_MA_L[i],0,Azz.zzL[j].t,1,MA);
            if(err<0) return;
            sumLo[i]+=MA[0]-Azz.zzL[j].price;
           }
        }
     }
  }
//+------------------------------------------------------------------+

Wir müssen hier einiges klarstellen:

Also stellt der so entstandene Indikator die letzten sieben ZigZag-Knoten grafisch dar und berechnet die Koordinaten aller anderen Knoten über eine gegebene History (Abb. 6). Die Berechnung wird nur einmal durchgeführt, und wir arbeiten mit den errechneten Daten weiter. Sie können das jedoch auch so implementieren, dass die Daten regelmäßig aktualisiert werden können, doch in diesem Beitrag beschränken wir uns auf eine einmalige Übertragung.

Der ZigZag-Indikator (7 Knoten)

Abb. 6. Der ZigZag-Indikator (7 Knoten).

Stellen wir des Weiteren die Querschnitte der Oberflächen der Envelopes-Indikatoren grafisch dar. Dazu ergänzen wir die OnTick() Methode um folgendes:

//--- PEAKS
   sumH[0]=0.0;
   maxH[0]=0.0;
   minH[0]=0.0;
   for(int i=0; i<NUMBER_MA; i++)
     {
      CopyBuffer(handle_MA_H[i],0,t[0],1,MA);
      double envelope=MA[0]+sumHi[i]/H;
      if(i==0 || envelope<minH[0])
        {
         minH[0]=envelope;
         t_min=SIZE(i);
        }
      if(envelope>maxH[0])
        {
         maxH[0]=envelope;
         t_max=SIZE(i);
        }
      sumH[0]+=envelope;
      name="H"+(string)i;
      ObjectCreate(0,name,OBJ_TEXT,0,0,0);
      ObjFont
      ObjCoordinates(t[0]-(NUMBER_MA-i*2)*shift,envelope)
      ObjProperty(SIZE(i),158,clrBlue)
     }
//--- TROUGHS
   sumL[0]=0.0;
   maxL[0]=0.0;
   minL[0]=0.0;
   for(int i=0; i<NUMBER_MA; i++)
     {
      CopyBuffer(handle_MA_L[i],0,t[0],1,MA);
      double envelope=MA[0]-sumLo[i]/L;
      if(i==0 || envelope<minL[0])
        {
         minL[0]=envelope;
         t_min=SIZE(i);
        }
      if(envelope>maxL[0])
        {
         maxL[0]=envelope;
         t_max=SIZE(i);
        }
      sumL[0]+=envelope;
      name="L"+(string)i;
      ObjectCreate(0,name,OBJ_TEXT,0,0,0);
      ObjFont
      ObjCoordinates(t[0]+(NUMBER_MA-i*2)*shift,envelope)
      ObjProperty(SIZE(i),158,clrGold)
     }
Hinweis für Neulinge unter den Programmierern: Operatoren am Ende des Spitzen- und Tal-Blocks besitzen am Ende des Strings kein ';'. Das ist kein Fehler oder Tippfehler. Es handelt sich hier um Makros (vgl. der Datenbereich wo sie deklariert wurden) - sie sind extrem hilfreich! Ich empfehle dringend, sie in Ihren Programmen zu verwenden.

Zur Unterscheidung der Querschnittspunkte der durch die Envelopes-Linien gebildeten Oberfläche sind die Punkte unterschiedlich groß: je größer der durchschnittliche Zeitraum der Hauptlinie der Envelopes-Indikatoren ist, umso größer sind die Punkte (Abb. 7). Darüber hinaus werden die Querschnitte um eine senkrechte Achse gedreht, die den aktuellen (Null) Bar in unterschiedlichen Richtungen durchläuft: Spitzen befinden sich um 90 Grad nach rechts und Täler um 90 Grad nach links.

Jetzt kann man sie in der Fläche des Kurscharts erkennen. Anfangs lagen sie nämlich in der Schnittebene (Abb. 5) und konnten nicht beobachtet werden. Wir konnten sie uns nur so vorstellen, ohne eine Ahnung von ihrer Form zu haben. Die Querschnitt-Linien haben eine sehr eigenartige Form angenommen. Dies dient auch der Vereinfachung und bequemen grafischen Analyse. Die Querschnitte ähneln visuell zwei fliegenden Kometen:

Querschnitt des Envelopes-Indikator Pools

Abb. 7 Querschnitt des Envelopes-Indikator Pools.

Gehen wir nun zur Berechnung der Querschnitts-Charakteristika weiter: das Maximum und Minimum, sowie den Schwerpunkt (das arithmetisches Mittel). Die sich ergebenden Werte werden als Punkte auf einem aktuellen Bar dargestellt, wobei die Punktegröße der Größe des jeweilig relevanten Merkmals entspricht. Zudem speichern wir sie in der History für die weitere Analyse. Also ergänzen wir den bestehenden Code um folgendes:

//--- PEAKS

...

//--- midi
   string str=(string)t[0];
   name="Hmidi"+str;
   ObjectCreate(0,name,OBJ_TEXT,0,0,0);
   ObjFont
   ObjCoordinates(t[0],sumH[0]/NUMBER_MA)
   ObjProperty(10,119,clrBlue)
//--- max
   name="Hmax"+str;
   ObjectCreate(0,name,OBJ_TEXT,0,0,0);
   ObjFont
   ObjCoordinates(t[0],maxH[0])
   ObjProperty(t_max,158,clrBlue)
//--- min
   name="Hmin"+str;
   ObjectCreate(0,name,OBJ_TEXT,0,0,0);
   ObjFont
   ObjCoordinates(t[0],minH[0])
   ObjProperty(t_min,158,clrBlue)

...

//--- TROUGHS

...

//--- midi
   name="Lmidi"+str;
   ObjectCreate(0,name,OBJ_TEXT,0,0,0);
   ObjFont
   ObjCoordinates(t[0],sumL[0]/NUMBER_MA)
   ObjProperty(10,119,clrGold)
//--- max
   name="Lmax"+str;
   ObjectCreate(0,name,OBJ_TEXT,0,0,0);
   ObjFont
   ObjCoordinates(t[0],maxL[0])
   ObjProperty(t_max,158,clrGold)
//--- min
   name="Lmin"+str;
   ObjectCreate(0,name,OBJ_TEXT,0,0,0);
   ObjFont
   ObjCoordinates(t[0],minL[0])
   ObjProperty(t_min,158,clrGold)

Sehen wir uns jetzt mal an, wie das grafisch dargestellt aussieht:

Querschnitts-Charakteristika

Abb. 8 Querschnitts-Charakteristika: das Maximum und Minimum sowie der Schwerpunkt, für Spitzen und Täler separat dargestellt.

Jetzt müssen wir nur noch den letzten Feinschliff machen, indem wir die fortgeschrittenen ZigZag-Knoten finden und grafisch darstellen. Wir verbessern den Code durch Hinzufügen von:

//--- ZigZag: advanced nodes
   if(Azz.zHL[0].type>0) // peak
     {
      ObjectDelete(0,"MIN");
      ObjectDelete(0,"MINfuture");
      name="MAX";
      ObjectCreate(0,name,OBJ_TREND,0,0,0);
      ObjZZ
      ObjectSetDouble(0,name,OBJPROP_PRICE,0,Azz.zHL[1].value);
      ObjectSetInteger(0,name,OBJPROP_TIME,0,Azz.zHL[1].time);
      ObjectSetInteger(0,name,OBJPROP_TIME,1,t[0]);
      double price=minH[0];
      ObjectSetDouble(0,name,OBJPROP_PRICE,1,price);
      if(Azz.zHL[0].value>minH[0])
        {
         price=sumH[0]/NUMBER_MA;
         ObjectSetDouble(0,name,OBJPROP_PRICE,1,price);
        }
      if(Azz.zHL[0].value>sumH[0]/NUMBER_MA)
        {
         price=maxH[0];
         ObjectSetDouble(0,name,OBJPROP_PRICE,1,price);
        }
      //--- into the future
      name="MAXfuture";
      ObjectCreate(0,name,OBJ_TREND,0,0,0);
      ObjZZ
      ObjectSetDouble(0,name,OBJPROP_PRICE,0,price);
      ObjectSetInteger(0,name,OBJPROP_TIME,0,t[0]);
      ObjectSetDouble(0,name,OBJPROP_PRICE,1,maxL[0]);
      ObjectSetInteger(0,name,OBJPROP_TIME,1,t[0]+NUMBER_MA*shift);
      if(price<maxL[0])
         ObjectSetDouble(0,name,OBJPROP_PRICE,1,sumL[0]/NUMBER_MA);
      if(price<sumL[0]/NUMBER_MA)
         ObjectSetDouble(0,name,OBJPROP_PRICE,1,minL[0]);
     }
   if(Azz.zHL[0].type<0) // trough
     {
      ObjectDelete(0,"MAX");
      ObjectDelete(0,"MAXfuture");
      name="MIN";
      ObjectCreate(0,name,OBJ_TREND,0,0,0);
      ObjZZ
      ObjectSetDouble(0,name,OBJPROP_PRICE,0,Azz.zHL[1].value);
      ObjectSetInteger(0,name,OBJPROP_TIME,0,Azz.zHL[1].time);
      ObjectSetInteger(0,name,OBJPROP_TIME,1,t[0]);
      double price=maxL[0];
      ObjectSetDouble(0,name,OBJPROP_PRICE,1,price);
      if(Azz.zHL[0].value<maxL[0])
        {
         price=sumL[0]/NUMBER_MA;
         ObjectSetDouble(0,name,OBJPROP_PRICE,1,price);
        }
      if(Azz.zHL[0].value<sumL[0]/NUMBER_MA)
        {
         price=minL[0];
         ObjectSetDouble(0,name,OBJPROP_PRICE,1,price);
        }
      //--- into the future
      name="MINfuture";
      ObjectCreate(0,name,OBJ_TREND,0,0,0);
      ObjZZ
      ObjectSetDouble(0,name,OBJPROP_PRICE,0,price);
      ObjectSetInteger(0,name,OBJPROP_TIME,0,t[0]);
      ObjectSetDouble(0,name,OBJPROP_PRICE,1,minH[0]);
      ObjectSetInteger(0,name,OBJPROP_TIME,1,t[0]+NUMBER_MA*shift);
      if(price>minH[0])
         ObjectSetDouble(0,name,OBJPROP_PRICE,1,sumH[0]/NUMBER_MA);
      if(price>sumH[0]/NUMBER_MA)
         ObjectSetDouble(0,name,OBJPROP_PRICE,1,maxH[0]);
     }

Jetzt haben wir also den neuen, fortgeschrittenen ZigZag-Indikator, der die Position neuer Knoten vorhersagt (Abb. 9). Die Knoten ihrerseits befinden sich in den charakteristischen Querschnittspunkten: dem Maximum, dem Minimum und dem Schwerpunkt. Der Arbeitstitel des Indikators lautet "Zwei Kometen".

Es sei darauf hingewiesen, dass die Zeit für den Abschluss des nächsten Knotens, der sich in der Zukunft befindet, nicht bekannt ist. Wir können also eigentlich nur eine Koordinate des Knotens vorhersagen - den Kurs.

Vorhergesagte ZigZag-Knoten

Abb. 9 Der fortgeschrittene ZigZag-Indikator sagt die Knoten vorher: den aktuellen und den folgenden.

 

Analyse der Ergebnisse und Empfehlungen für Entwickler

Aufgrund der Beobachtungen des Indikators hat sich gezeigt, dass:

  1. Abweichungen der Koordinaten des ZigZag-Knotens von den vorhergesagten Knoten innerhalb des Toleranzbereichs liegen. Die große Zahl der Knoten befindet sich im Schatten des entsprechenden Querschnitts. Dies ist sicherlich nur eine qualitative Beurteilung Exaktere Ergebnisse wird es in zukünftigen Beiträgen geben.
  2. Querschnitte der Envelope-Linien zeigen das Marktverhalten und die erwartete Kursdynamik an! Achten Sie auf den Kometenschwanz, der aus Punkten mit dem kleinsten Durchschnittszeitraum (größenmäßig am kleinsten) besteht. Er zeigt in die Richtung des Kurses. Der Kometenschwanz biegt sich auf höchst komplexe Weisen, und je mehr er in die entgegengesetzte Richtung weist, umso größer ist die Chance, dass eine Trendwende eintritt. Beobachten Sie einfach das Indikator-Verhalten auf unterschiedlichen Zeitrahmen mit anderen Amplituden. Das ist wahnsinnig interessant!
  3. Die charakteristischen Querschnittspunkte bilden Linien, die einen starken Widerstand gegenüber der Kursbewegung zeigen. Sie können daher als Stütz- und Widerstandslinien betrachtet werden.
  4. Wenn die Punkte des Schwerpunkts weiter als der Querschnitt reichen (wie die Spitzen in Abb. 9), ist dies ein Zeichen dafür, das ein Aufwärtstrend vorhanden ist.

Was wir also als Ergebnis dieses Beitrags haben ist ein sehr interessanter Indikator, der in einer Handelsstrategie ausprobiert werden kann!

 

Fazit