Wie man Schnelle Nicht-Nachzeichnende ZigZags Schreibt

Candid | 28 März, 2016

Einführung

Unter allen möglichen Algorithmen von ZigZag Diagrammen, können wir eine Klasse unterscheiden, die von Autoren "ZigZags mit Wechsel auf Durchbruch der Verzögerungsebene". Diese Klasse, vollständig oder in Teilen, beinhaltet die meisten bestehenden ZigZags. Der Name der Klasse selbst, in der Tat, stellt eine algorithmische Vorlage dar. Um einen Indikator aus ihr zu machen, ist es ausreichend einfach die Funktonen, die die Verzögerungsebene erkennen, hinzuzufügen. Die Vielfalt an Algorithmen solcher Funktionen, ist nur begrenzt durch die Vorstellungskraft des Autors über den zukünftigen ZigZag.



Allgemeiner Ansatz

Lassen Sie uns zunächst versuchen den allgemeinen Ansatz zum Schreiben eines Indikators formulieren. Also:

- Die Funktion start() von jedem Indikator (sowie jedem EA) stellt eine Rückruf-Funktion dar, das heißt, eine Funktion, die aufgerufen wird um ein bestimmtes Ereignis zu verarbeiten. Nämlich, um einen Tick zu verarbeiten.

- Das Objekt beim Schreiben eines Indikators ist, in der Regel, die Berechnung von einer oder mehrerer Markt-Eigenschaften. Zusammen mit für die Berechnungen ergänzenden Mengen, bilden sie einen Satz an Schlüsselvariablen des bestimmten Indikators. Bestimmen wir den Zustand des Indikators als eine Reihe von Werten dieser Schlüsselvariablen zu einer bestimmten Zeit. Basierend auf der Definition, können wir das Folgende feststellen:

  • Berechnen der neuen Werte von Variablen bei einem neuen Tick, die Funktion start() berechnet den neuen Zustand des Indikators.
  • Somit ist die Funktion start() tatsächlich ein Operator, der den Zustand des Indikators von dem einem in einem anderen überträgt.

- Unter diesen Umständen reduziert der Prozess des Schreibens eines Indikators sich selbst auf der Bestimmung eines Satzes aus Mengen, die seinen Zustand beschreiben (Status-Variablen) und zum Schreiben eines Operators, der den Indikator in einen neuen Zustand bei der Ankunft eines neuen Ticks übertragen würde. Die Initialisierung von Status-Variablen wird ein wesentlicher Teil des-Indikator-Algorithmus werden. Wir werden an dem Beispiel von ZigZags eines bestimmten Typs zeigen, wie dies getan werden kann.



Welche ZigZags kommen in Frage

Wie weiter oben in diesem Artikel gesagt, sind wir an den ZigZags interessiert, die umschalten bei dem Durchbrechen der Verzögerungslinie. Was ist eine "Verzögerungslinie"? Angenommen wir wollen einen ZigZag schreiben für den das Hoch festgelegt ist, wenn der sich von diesem Hoch durch H Punkte bewegt. Festlegen eines Hochs bedeutet, umschalten der Richtung eines ZigZag-Segments in eine gegenteilige. Lassen Sie uns nur das Tief schon festgelegt haben, und nun in einem Aufwärts-Segment sein. Führen wir eine Variabe ein für das Kurs-Zeit Maximum eines unvollständigen Aufwärts-Segments, TempMax. Wir werde dieses Maximum festlegen (und die Richtung umschalten), wenn der Kurs das Niveau durchbricht von:

SwitchLevel = TempMax - H *Point .

Wenn das Maximum vor dem Umschalten aktualisiert wurde, müssen wir den neuen Wert von SwitchLevel berechnen. Somit wird SwitchLevel dem Zeit-Maximum folgen, H Punkte dahinter.

Die Situation wird für ein Abwärts-Segment absolut symmetrisch sein: SwitchLevel folgt nun dem Zeit-Minimum (TempMin ), die gleichen H Punkte dahinter. Aber jetzt haben wir:

SwitchLevel = TempMin + H *Point .

In der Tat, wir haben gerade den Algorithmus der Verzögerungsebene beschrieben, für den ZigZag, den wir erstellen werden. Natürlich ist es nicht der einzige mögliche Algorithmus. Zum Beispiel, wenn wir die obere/untere Grenze eines Kanals als die Verzögerungsebene betrachten, erhalten wir genauso viele ZigZags, wie es Methoden zur Kanal-Berechnung gibt. Darüber hinaus, bei näherer Betrachtung, die absolute Mehrheit der dem Autor bekannten ZigZags stellte sich als voll oder teilweise in der betrachteten Klasse eingeschlossen heraus. Aber nicht alle von ihnen. Beispielsweise kann der auf Williams Fractals berechnete ZigZag nicht in diese Klasse eingeschlossen werden.



ZigZag Modell

Bestimmen wir nun die Variablen des ZigZag Status.

Zunächst wird es sie Richtung des aktuellen Segments sein. Wir werden die entsprechende Variable UpZ nennen und ihr die Werte true für Aufwärts-Segmente und false für Abwärts-Segmente zuweisen.

Natürlich, wir sollten der Liste die oben vorgestellten TempMax und TempMin hinzufügen. Wie werden außerdem deren Zeit-Koordinaten hinzufügen. Hier erhalten wir allerdings einen Grad an Freiheit bei der Bestimmung der Maßeinheit. Als Zeit-Koordinate werden wir die Balken-Nummer nehmen, beginnend am Anfang der Charts, das heißt, wir werden das Nummernsystem umgekehrt zu dem in MT4 akzeptierten verwenden. Dies wir den Code vereinfachen und seine Ausführungsrate verbessern. Somit wird die Liste mit den Variablen TempMaxBar und TempMinBar ergänzt.

Wir planen beides, Zeichnen des ZigZag in en Chart und diesen irgendwie zu verwenden. Also werden wir zu der Liste die Koordinaten des letzten festgelegtem ZigZag Hochs hinzufügen: CurMax, CurMaxBar, CurMin, CurMinBar.

Und das ist alles für die Liste. Ein einzelner Autor eines bestimmten ZigZag kann die Liste frei mit Dingen ergänzen, die sie/er mit diesem ZigZag machen wird. Zum Beispiel kann es sich als sinnvoll herausstellen die Koordinaten der vorherigen Hochs hinzuzufügen: PreMax, PreMaxBar, PreMin, PreMinBar. Oder Sie müssen die Anzahl einer vordefinierten Anzahl vorheriger Hochs hinzufügen, in diesem Fall unter Verwendung von Arrays.



Wechsel Operator

In dem vorgeschlagenen Ansatz wird das Schreiben eines Wechsel-Operators eine ziemlich einfache Aufgabe. Wir müssen einfach die Definition der ZigZag Klasse, an der wir interessiert sind, in MQL4 übersetzen. So wird es aussehen:

// First, process the case of an up-segment
    if (UpZ) {
// Check whether the current maximum has changed
      if (High[pos]>TempMax) {
// If yes, then correct the corresponding variables
        TempMax = High[pos];
        TempMaxBar = Bars-pos;  // Here switching to direct numbering
      } else {
// If not, then check whether the slowing level has been broken through
        if (Low[pos]<SwitchLevel()) {
// If yes, then fix the maximum
          CurMax = TempMax;
          CurMaxBar = TempMaxBar;
// And draw a peak
          ZZ[Bars-CurMaxBar]=CurMax;  // Here switching to reverse numbering
// Correct the corresponding variables
          UpZ = false;
          TempMin = Low[pos];
          TempMinBar = Bars-pos;  // Here switching to direct numbering
        }
      }
    }  else {
// Now processing the case of down-segment
// Check whether the current minimum has changed
      if (Low[pos]<TempMin) {
// If yes, then correct the corresponding variables
        TempMin = Low[pos];
        TempMinBar = Bars-pos;  // Here switching to direct numbering
      } else {
// If not, then check whether the slowing level has been broken through
        if (High[pos]>SwitchLevel()) {
// If yes, then fix the minimum
          CurMin = TempMin;
          CurMinBar = TempMinBar;
// And draw a peak
          ZZ[Bars-CurMinBar]=CurMin;  // Here switching to reverse numbering
// Correct the corresponding variables
          UpZ = true;
          TempMax = High[pos];
          TempMaxBar = Bars-pos;  // Here switching to direct numbering
       }
      }
    }

Der Wechsel-Operator ist fertig. Jetzt können wir jederzeit auf die Status-Variablen des Indikators verweisen.

Ein solcher Operator hat jedoch ein besonderes Merkmal, das als Fehler beim Zeichnen des ZigZag wahrgenommen werden kann. Betrachten wir das folgende Fragment detaillierter:

      if (High[pos]>TempMax) {
// If yes, then correct the corresponding variables
        TempMax = High[pos];
        TempMaxBar = Bars-pos;  // Here switching to direct numbering
      } else {
// If not, then check whether the slowing level has been broken through
        if (Low[pos]<SwitchLevel()) {
// If yes, then fix the maximum
          CurMax = TempMax;
          CurMaxBar = TempMaxBar;

Die Verwendung des Paares if - else bedeutet, dass das Tief des Balkens mit TempMax nicht berücksichtigt wird. Die Situationen, in denen sich dieser Kurs als unter dem nächsten festgelegten Minimum herausstellt, kann als Fehler in der Zeichnung des ZigZag wahrgenommen werden. Ist das in der Tat ein Fehler?

Unter Berücksichtigung, dass die Arbeit in der Historie und in Echtzeit identisch sein muss, ist der Autor der Meinung, dass dies kein Fehler ist. In der Tat, innerhalb eines Zeitrahmens werden wir nie wissen was früher in der Historie passierte, Das Balken Minimum oder Maximum. Hier die Konstruktion von if - else zu verwenden bedeutet eine bewusste Entscheidung: Wir bevorzugen Dynamik. Es bedeutet, dass wir das Minimum für ein Aufwärts-Segment und das Maximum für ein Abwärts-Segment opfern. Es liegt nahe, dass je kleiner der Zeitrahmen, umso weniger häufig wird dieses Dilemma auftreten.

Ein weiteres Fragment braucht einige Kommentare:

// Correct the corresponding variables
          UpZ = false;
          TempMin = Low[pos];
          TempMinBar = Bars-pos;  // Here switching to direct numbering
        }
      }

Hier ist tatsächlich der Anfangspunkt der Zeit-Minimum Prüfung festgelegt auf den Moment des Wechsels zwischen Segmenten. Es ist für das angegebene Beispiel gerechtfertigt, aber, im allgemeinen Fall, müssen Sie es nicht so machen. Es wäre sinnvoller das temporäre Minimum für den minimalen Intervall von dem festgelegten Maximum zu der aktuellen Position festzulegen (d.h. den Moment des Wechsels). Der Code kann wie folgt aussehen:

   // Correct the corresponding variables
          UpZ = false;
          TempMinBar = CurMaxBar+1;
          TempExtPos = Bars - TempMinBar;  // Here switching to reverse numbering
          TempMin = Low[TempExtPos];
          for (i=TempExtPos-1;i>=pos;i--) {
            if (Low[i]<TempMin) {
              TempMin = Low[i];
              TempMinBar = Bars-i;  // Here switching to direct numbering
            }
          }

Hier ist das Tief des Balken, wo das Minimum festgelegt war, wieder von der Berücksichtigung ausgeschlossen.

Diese zwe Anmerkungen betreffen auch Abwärts-Segmente.



Indikator

Es bleibt nur noch den Indikator zu kompletieren, damit er funktioniert. Es ist nicht notwendig auf init() und deinit() zu kommentieren, alles dort ist ziemlich klar und Standard. Allerdings treffe wir eine wichtige Entscheidung bei der Funktion start(). Wir werden nur mit vollständigen Balken arbeiten. Der wichtigste Grund dafür ist, dass es uns ermöglicht eine einfache und kompakte Struktur zu erreichen.

Es gibt einen weiteren wichtigen Aspekt dazu. Eine ernsthafte Arbeit an einem Handelssystem impliziert das Sammeln von Statistiken zu der Historie. Die Statistiken werden nur gültig (richtig) sein. wenn die ermittelten Eigenschaften in Echtzeit vollständig den in der Historie ermittelten entsprechen. Wir haben keine Historie von echten Ticks, so dass wir diese volle Übereinstimmung nur erreichen können, wenn wir in Echtzeit mit kompletten Balken arbeiten. Das einzige, das wir machen können um die Verzögerung zu verringern, ist das Arbeiten auf kleineren Zeitrahmen, runter bis M1.

Ein weiteres wesentliches Merkmal ist, die Funktion IndicatorCounted() nicht zu verwenden. Der Hauptgrund dies zu tun ist, dass der verwendete Code eine andere wichtige Aktion erfordert - die Initialisierung der Status-Variablen des Indikators. Dies kann nicht in der Fukntion init() gemacht werden, da die Verwendung der direkten Nummerierung die Neuberechnung des Indikators beim Historiepumpen erfordert und deshalb, die Status-Variablen zu re-initialisieren. Die Funktion init() ist beim Historiepumpen nicht gestartet.

Somit müssen wir eine weitere "Standard"-Funktion hinzufügen, Reset(). Schließlich hilft der Wunsch der Verwendung von IndicatorCounted() nicht so viel, wie er daran hindert die notwendige Prüfung der Neuberechnung für einen Indikator dieses Typs zu organisieren. Die Prüfung wird wie folgt umgesetzt:

int start() {
//  Work with completed bars only
  if (Bars == PreBars) return(0);  
//  Check whether there are enough bars on the chart
  if (Bars < MinBars) {
    Alert(": Not enough bars on the chart");
    return(0);
  }  
//  If the history was not pumped, make calculations for the bar just completed
  if (Bars-PreBars == 1 && BarTime==Time[1]) StartPos = 1;
//  Otherwise, count the number of bars specified in function Reset() 
  else StartPos = Reset();
// Modify check variables
  PreBars = Bars;  
  BarTime=Time[0];
// Cycle on history
  for (pos=StartPos;pos>0;pos--) {

Die Funktion Reset() erscheint wie folgt:

int Reset() {
  if (MinBars == 0) MinBars = Bars-1;
  StartPos = MinBars;
  PreBars = 0;
  BarTime = 0;
  dH = H*Point;
  UpZ = true;
  TempMaxBar = Bars-StartPos;
  TempMinBar = Bars-StartPos;
  TempMax = High[StartPos];
  TempMin = Low[StartPos];
  StartPos++;
  return(StartPos);
}

Hier können wir besonderes Augenmerk auf die zusätzliche Variable legen, dH, zu der wir einmal den ZigZag-Wechsel Schwellenwert (H ) umgewandelt in eine Kurs-Skala. Eine Frage kann aufkommen: Warum ist UpZ = true, nicht false ? Die Antwort ist einfach: Nach einer kleinen Anzahl an Segmenten, wird der Indikator zu dem gleichen Diagramm führen, unabhängig von den ursprünglichen Wert von UpZ.

Nun, abschließend die Berechnung für die Verzögerungsebene:

double SwitchLevel() {
  double SwLvl;
  if (UpZ) SwLvl = TempMax - dH;
  else SwLvl = TempMin + dH;
  return(SwLvl);
}

Bis hier muss alles klar sein.



Fazit

Eine Vorlage zum Schreiben von ZigZags, ZZTemplate, ist an diesen Artikel angehangen. Alles was Sie tun müssen, ist den erforderlichen Code zu Funktion SwitchLevel() hinzuzufügen. Um die Vorlage zu dem hier als Beispiel verwendeten ZigZag zu machen, müssen Sie nur die folgenden Zeilen finden und einkommentieren:

//extern int H = 33;

//double dH;

//  dH = H*Point;

//  if (UpZ) SwLvl = TempMax - dH;
//  else SwLvl = TempMin + dH;

Die letzte Anmerkung betrifft die ZigZag Geschwindigkeit. Die Vorlage impliziert Allgemeingültigkeit. Darüber hinaus wollen wir die Struktur so transparent wie möglich haben. Ich denke, die meisten spezifischen Umsetzungen optimiert werden, um ihren Betrieb zu verbessern.

Die allgemeine Empfehlung ist wie folgt: Wo es möglich ist, platzieren Sie Operationen in if Operatoren. Als ein Beispiel (aber nicht ideales Modell) von Optimierung, finden Sie den angehangenen Indikator HZZ, eine alternative Umsetzung zu dem in diesem Artikel genutzten ZigZag. Die Einfachheit des Problems ermöglicht es uns sowohl auf die Funktion SwitchLevel() als auch auf einige Status-Variablen zu verzichten. Als kleinen Bonus, habe ich zu HZZ Schreiben von ZigZag Hochs in eine Datei und "on-the-Fly" Prüfung einiger statistischer Eigenschaften von ZigZag.