English Русский 中文 Español 日本語 Português
Automatische Ermittlung von Extremwerten basierend auf einem angegebenen Kursrückgang

Automatische Ermittlung von Extremwerten basierend auf einem angegebenen Kursrückgang

MetaTrader 5Handel | 27 Januar 2017, 12:58
1 663 0
Sergey Strutinskiy
Sergey Strutinskiy

Einleitung

Viele beliebte Handelsstrategien basieren auf der Verwendung verschiedener grafischer Muster: "Kopf und Schulter", "Doppelte Spitze/Doppelter Boden" u.a. In einigen Strategien wird die Divergenz zwischen den Extremwerten auf den Charts analysiert. Bei der Automatisierung solcher Handelssysteme besteht die Notwendigkeit, Hochs und Tiefs auf den Charts zu ermittlen und anschließend effektiv zu verarbeiten und zu interpretieren. Die bestehenden Tools ermöglichen es nicht immer, Extremwerte anhand angegebenen Kriterien zu ermitteln. In diesem Artikel sind effektive Algorithmen und Lösungen für die Ermittlung und Verarbeitung von Extremwerten auf Preischarts je nach Kursrückgang vorgestellt. 

1. Bestehende Tools für die Ermittlung von Extremwerten

1.1. Fraktale und ähnliche Tools

Fraktale gehören zu beliebten Werkzeuge für die Ermittlung von Extremwerten. Sie erlauben es, Minima und Maxima des Preises für eine Reihe aus 5 Balken zu ermitteln (Abb. 1). Die Extrema werden sowohl bei starken als auch bei schwachen Preisbewegungen ermittelt. Bei der richtigen ausgewählten Timeframe können Fraktale relativ gute Ergebnisse liefern, ihre Leistung hängt aber zu sehr von Marktbedingungen ab.


Abb. 1. Die Ergebnisse der Verwendung von Fraktalen: Extremwerte mit einer relative Größe von 140 bis 420 Pips in einem Trend (a); wenn es keine Preisbewegung zu verzeichnen ist, beträgt die relative Größe der Extremwerte höchstens 50 Pips (b)

Im zweiten Fall kann die relative Größe der Extrema (die Änderung des Preises von einem Extremum zu dem anderen) nur wenige Pips betragen. Solche unbedeutenden Hochs und Tiefs werden in den meisten Fällen beim manuellen Trading nicht berücksichtigt. Die Änderung der Timeframe erlaubt es nicht, unbedeutende Extremwerte auszufiltern — bei einer langen Stagnation werden sie sowieso erkannt. 

Es kann auch eine andere Schwierigkeit entstehen: es werden nicht alle Extremwerte ermittelt. Wenn es innerhalb eines kurzen Zeitraums große Preisschwankungen gab, mit vielen Hochs und Tiefs, werden diese nicht erkannt. Fraktale lassen nur zwei Extremwerte auf einem Intervall ermitteln, die durch 5 Balken der aktuellen Timeframe definiert werden. Aus dem oben gesagten folgt, dass Fraktale für die automatische Ermittlung aller oder der meisten wichtigen Extremwerte beim automatischen Handel nicht empfehlenswert sind. 

Die im Artikel "Thomas DeMarks Beitrag zur technischen Analyse" dargestellten Werkzeuge haben die gleichen Nachteile wie Fraktale. Wenn man ein großes Fenster für die Ermittlung von Extremwerten auswählt, dann können viele von ihnen übersprungen werden. Und wenn das Fester relativ klein ist, werden auch unbedeutende Extremwerte ermittelt. Auf jeden Fall muss man bei der Verarbeitung der Ergebnisse die Parameter entweder manuell optimieren und unbedeutende Minima und Maxima aussortieren oder einen Algorithmus dafür entwickeln.


1.2. Verwendung von gleitenden Durchschnitten bei der Ermittlung von Extremwerten

Bei der Automatisierung der Ermittlung von Extremwerten sieht die Idee der Verwendung irgendeiner Mittellinie, zum Beispiel eines gleitenden Durchschnitts, sehr attraktiv aus. Die Extrema werden innerhalb einer gegebenen Anzahl von Balken ermittelt unter der Bedingung, dass sich der Preis von der Mittellinie um eine angegebene Anzahl von Punkten wegbewegt. Dieses Tool erlaubt es, unbedeutende Tiefs und Hochs auszusortieren, und ist deswegen besser als Fraktale. Es löst aber nicht das Problem, bei dem Maxima und Minima sehr nah zueinander liegen (Abb.2, a).

 

Abb. 2. Die Verwendung der gleitenden Durchschnitte bei der Ermittlung der Extremwerte: 2 Extrema werden wie eins ermittelt (а), Auslassung eines Extremwertes unmittelbar neben dem gleitenden Durchschnitt (b)

Man kann versuchen, gleitende Durchschnitte und Fraktale gleichzeitig zu verwenden. Der gleitende Durchschnitt wird für die Filterung unbedeutender Extremwerten und Fraktale für deren anschließenden Ermittlung im angegebenen Intervall verwendet. Leider beseitigt auch ein solches Schema nicht alle Probleme. Ohnehin muss man das Fenster optimal aussuchen. Egal auf welche Weise wir Maxima und Minima ermitteln, wird eine ständige Anpassung der Parameter des Fensters für die Ermittlung benötigt. Wenn die Parameter nicht optimalen ausgewählt wurden, kann aus zwei zu nah zueinander liegenden Hochs nur eins ermittelt werden (Abb.2, a).

Schauen wir uns noch eine Schwierigkeit an, die für diese Ermittlungsmethode von Extremwerten kennzeichnend ist. In einer Marktsituation mit starken Preisschwankungen kann der gleitende Durchschnitt je nach der Periode nicht auf ein solches Signal reagieren. In einer solchen Situation (Abb.2, b) wird ein Tief, das zwischen zwei Hochs und neben einem gleitenden Durchschnitt liegt, nicht ermittelt. Solche Situationen treten ziemlich selten auf, sie lassen aber einen über die richtige Auswahl des Fensters des gleitenden Durchschnitts nachdenken. 

Die oben beschriebenen Methoden zur Ermittlung von Extremwerten weisen Nachteile auf und erfordern zusätzliche Lösungen. Schauen wir uns die Schwierigkeiten, die bei der Ermittlung von Extremwerten auftreten, sowie Algorithmen, anhand denen man diese Probleme lösen kann, genauer an.

2. Probleme und Mehrdeutigkeiten bei der Ermittlung von Extremwerten

2.1. Auswahl der Spannweite für die Suche von Hochs und Tiefs

Die Extremwerte können in bestehenden Strategien explizit und implizit verwendet werden. Dabei ist die Aufgabe, Extrema zu ermitteln, manchmal sehr subjektiv: auf einem und demselben Chart können zwei Menschen ganz verschiedene Tiefs und Hochs sehen. Betrachten wir eines der bekannten grafischen Modelle "Doppelte Spitze".

Abb. 3. Das grafische Modell "Doppelte Spitze" 

 Abb. 3. Das grafische Modell "Doppelte Spitze"

Auf den zwei Charts (Abb. 3) ist ein und dasselbe Modell dargestellt. Aber je nach dem, wie groß die Spannweite zwischen den Extremwerten ist, können wir das Modell erkennen oder nicht. Auf dem ersten Chart ist nach dem ersten Hoch ein Tief zu sehen, darauf folgt das zweite Hoch. Gäbe es kein Tief zwischen den zwei Hochs, hätten wir das grafische Modell "Doppelte Spitze" nicht erkennen können. Das Modell wäre als ein einfacher Extremum erkannt worden. Eine ähnliche Situation entsteht, wenn ein Tief nicht deutlich sichtbar ist. In diesem Fall ist es eine Sache der subjektiven Einschätzung, ob die "Doppelte Spitze" erkannt oder nicht erkannt wird. In diesem Fall finden wir dieses Modell auf dem ersten Chart schneller als auf dem zweiten, dabei ist der Unterschied zwischen den Charts gleich der Differenz zwischen den benachbarten Extrema. 

Betrachten wir auch ein anderes Beispiel: in einigen Strategien wird ein Trend als Aufwärtstrend erkannt, wenn nachfolgende Extrema (sowohl Hochs als auch Tiefs) über den vorherigen liegen. Ein Abwärtstrend wird auf die gleiche Weise ermittelt. In diesem Beispiel (Abb. 4) kann man die Trendrichtung bestimmen, und in diesem Fall werden die Extremwerte ausdrücklich verwendet.


Abb. 4. Entgegengesetzte Preisbewegungen auf einem und demselben Chart: ein Aufwärttrend (a), ein Abwärtstrend (b)

Das heißt, dass man auf einem und demselben Chart sowohl einen Aufwärtstrends als auch einen Abwärtstrend ermitteln kann. Im ersten Fall (Abb. 4, а) weisen die ermittelten Extremwerte 1,2,3,4 auf einen Aufwärtstrend deutlich hin. Wenn wir die Punkte 2,5,6,3 als Extrema auf demselben Chart bestimmen (Abb. 4, b), können wir daraus schließen, dass es ein Abwärtstrend vorliegt. Wenn man ganz andere Extrema verwendet, kann man eine der zwei Varianten bekommen. Daraus schließen wir, dass gerade die Spannweite des Rückgangs die Stellen der Extrema beeinflussen wird.

2.2. Effektive Trennung benachbarter Hochs und Tiefs

Bei der Ermittlung von Extremwerten tritt auch ein weiteres Problem auf. Für eine effektive Ermittlung und Trennung von zwei oder mehreren Hochs muss es zwischen ihnen ein Tief geben. Diese Behauptung gilt sowohl für das erste Beispiel als auch für das zweite, wo das Ganze noch spannender wird. Ein Trend kann laut der oben angeführten Strategie erst nach der Ermittlung der Extremwerte auf den Charts (Abb.5,6), bestimmt werden. 

Abb. 5. Ermittlung von Hochs und Tiefs bei einer langfristigen Investierung 

Abb. 5. Ermittlung von Hochs und Tiefs bei einer langfristigen Investierung 

Abb.6. Ermittlung unbedeutender Hochs und Tiefs 

Abb. 6. Ermittlung unbedeutender Hochs und Tiefs 

Wenn die Tiefs, die Hochs trennen, und die Hochs, die Tiefs trennen, nicht ermittelt werden, kann die Strategie nicht nach gesetzten Kriterien arbeiten, obwohl sich ein Aufwärtstrend auf dem Chart visuell bestimmen lässt. Schauen wir uns ein typisches Beispiel an. Bei einer aufsteigenden Kursbewegung wird zunächst ein Hoch und dann ein noch höheres Hoch gebildet. Wenn es dazwischen gar kein oder ein undeutliches Tief gibt, wird nur das höhere Hoch als Extremum bestimmt. Wenn die Extrema hinsichtlich einer Mittellinie, z.B. des gleitenden Durchschnitts, festgelegt werden, bleibt die Aufgabe bestehen, zwei benachbarte Hochs oder Tiefs voneinander zu trennen. In diesem Fall ist es notwendig, einen Extremwert für die Trennung von zwei Hochs zu verwenden.

Zusammenfassend kann man sagen, dass für alle Strategien, die Extrema implizit oder explizit verwenden, die folgende Annahme gilt: der Preis bewegt sich von einem Hoch zu einem Tief und von einem Tief zu einem Hoch, wie bei der Bewegung von der Vergangenheit zur Zukunft und umgekehrt. Wenn man diese Annahme nicht verwendet, werden zwei Hochs auf einem Preischart je nach der subjektiven Einschätzung:

  • entweder ermittelt,
  • oder es wird nur das höhere Hoch ermittelt,
  • oder keiner der Extrema wird ermittelt.

  Das Gleiche gilt auch für die Tiefs. Aufgrund dieser Annahme kann man einen Algorithmus für eine eindeutige Ermittlung von Extremwerten mit einer ausgewählten Spannweite des Rückgangs entwickeln.

2.3. Ermittlung des ersten Extremwerts

Das dritte Problem ist auch mit dem Kursrückgang verbunden und läuft auf die Ermittlung des ersten Extremwertes hinaus. Für jede Trading Strategie sind die aktuellsten Extremwerte wichtiger als die älteren. Und wie wir bereits festgestellt haben, hängen von der Ermittlung eines Extremwertes die Positionen der benachbarten Tiefs und Hochs ab. Wenn man ein Extremum mit einem Abstand von der aktuellen Zeit auswählt, werden die Ergebnisse stärker von historischen Daten als von den aktuellsten Kursschwankungen abhängig sein. Dieses Problem besteht bei der Verwendung des ZigZag Indikators. Die Stelle der letzten Extremwerten ist wenig abhängig von den letzten Kursschwankungen.  

Eine andere Situation entsteht, wenn die Ermittlung von Extremwerten vom Ende des Charts beginnt. In diesem Fall wird zunächst das erste (das nächstliegende) Hoch oder Tief vom Ende ermittelt, danach werden alle anderen Extrema ermittelt. Je nach der Strategie und nach der Spannweite des Rückgangs können 3 Varianten verwendet werden:

  • das nächste Hoch ermitteln,
  • das nächste Tief ermitteln,
  • den nächsten Extremwert ermitteln (entweder Hoch oder Tief).

Schauen wir uns die Variante, bei der der nächste Extremwert gefunden wird, genauer an. Nachdem die Spannweite des Rückgangs gewählt wurde, kann das erste nächste Extremum ermittelt werden. Die Ermittlung erfolgt aber mit einer Verzögerung, die sich negativ auf die Strategie auswirken kann. Denn um ein Extremum zu "erkennen", müssen wir die durch die Spannweite des Rückgangs gesetzte Preisänderung feststellen. Für die Änderung des Preises braucht man Zeit, dies verursacht die Verzögerung. Als Extremwert kann man auch den letzten bekannten Preis verwenden, es ist aber kaum wahrscheinlich, dass gerade dieser Punkt später zu einem Hoch oder Tief wird. 

Aus diesem Grund lohnt es sich, das erste Extremum mithilfe eines zusätzlichen Koeffizienten zu ermitteln. Es ist sinnvoll, seinen Wert als Bruch von der Spannweite des Rückgangs darzustellen, der für die Ermittlung anderer Extrema verwendet wird. Nehmen wir zum Beispiel den Wert 0.5.  

Der gewählte Wert des zusätzlichen Koeffizienten wird die minimale Preisänderung vom aktuellen Wert bis zum Wert des minimalen Preises für das nächste Tief (maximalen Wert des Preises für das nächste Hoch) bestimmen. Wenn die Spannweite zwischen dem aktuellen Preis und dem Extremwert für das nächste Hoch oder Tief kleiner als der angegebene Wert ist, wird dieses Extremum nicht ermittelt. In diesem Fall gibt es eine gewisse Sicherheit, dass das erste ermittelte Extremum später wirklich zu einem Hoch oder zu einem Tief wird. Gleichzeitig wird man mit der Aufgabe konfrontiert, Extrema möglichst früh zu erkennen, zu analysieren und wenn nötig Trades zu eröffnen.

Betrachten wir ein Beispiel, in welchem die Spannweite an einer von Ebene 140 Pips gesetzt wurde. Für die Ermittlung des ersten Extremwertes wird ein zusätzlicher Koeffizient verwendet. Im ersten Fall beläuft sich sein Wert auf 0.9 (Abb. 7, a), im zweiten – auf 0.7 (Abb. 7, b). Dann wird der Wert des zusätzlichen Koeffizienten den minimalen Kursrückgang in Pips bestimmen, der das erste Extremum zu ermitteln hilft. Für den ersten Fall beträgt der Rückgang 126 Pips, für den zweiten — 98 Pips. In beiden Fällen wird einer und der selbe Chart betrachtet. Die senkrechte Linie markiert den aktuellen Zeitpunkt, für welchen die Berechnung durchgeführt wurde, mit Punkten werden die Extremwerte in diesem Bereich dargestellt. 


 

Abb. 7. Der Einfluss des zusätzlichen Koeffizienten auf die Ermittlung der Extrema: wenn der Koeffizient 0.9 (126 Pips) beträgt, wird das erste Extremum bei einem Rückgang von 205 Pips (a)ermittelt, wenn 0,7 (98 Pips), wird das erste Extremum bereits bei einem Rückgang von 120 Pips ermittelt, die zwei weiteren Extrema werden entsprechend der angegebenen Spannweite ermittelt (b)

Der ausgewählte Wert des zusätzlichen Koeffizienten lies das erste Tief nur bei einem Rückgang von 205 Pips ermitteln, während der minimale Preisrückgang 126 Pips beträgt. Für den zweiten Fall, bei dem der zusätzliche Koeffizient 0,7 (98 Pips) beträgt, wurde das erste Tief bei einem Rückgang von 120 Pips hinsichtlich des aktuellen Preises ermittelt. Die zwei darauf folgenden Extremwerte wurden entsprechend der angegebenen Spannweite von 140 Pips ermittelt. Demnach ist der Kursunterschied zwischen dem ersten Tief und dem auf ihm folgenden Hoch etwas größer als 140 Pips. Das zweite Tief wird auch durch einen Preisrückgang von über 140 Pips festgelegt, aber in Bezug auf das ermittelte Hoch.

Wie wir sehen, beeinflusst der Wert des zusätzlichen Koeffizienten erheblich die Stelle des ersten ermittelten Extremums und kann auch seinen Typ beeinflussen. Für verschiedene Werte (im Bereich von 0 bis 1) wird als erstes entweder ein Hoch oder ein Tief auf einem und demselben Chart ermittelt. Die zwei ersten Extremwerte, die für den zweiten Fall festgestellt wurden (Abb. 7, b) wurden im ersten Fall gar nicht ermittelt.

Es ist auch anzumerken: Wenn der Koeffizient einen noch niedrigeren Wert hat, wird das erste Extremum etwas schneller ermittelt. Für den zweiten Fall (Abb. 7, b) mit einem zusätzlichen Koeffizienten von 0.4, hätte das erste Extremum 5 Balken früher ermittelt werden können (5 Minuten früher nach der Zeitskala des aktuellen Zeitrahmens).

3. Algorithmische Lösungen für die Ermittlung von Extremwerten und deren Implementierung

3.1 Algorithmen für die Ermittlung von Extremwerten je nach der Spannweite des Rückgangs

Fangen wir mit der Auswahl eines Kursrückgangs für die Zeichnung von Extrema an. Offensichtlich werden sich die Balkengröße und die Parameter der Extrema je nach Zeitrahmen stark voneinander unterscheiden. Darüber hinaus hängen Hochs und Tiefs mit dem Vorhandensein eines Trends, mit der Tageszeit und anderen Faktoren zusammen. Unter Verwendung vorhandener Indikatoren, z.B. Fraktale und ähnlicher Werkzeuge, können wir Extrenwerte auf jeder Timeframe ermitteln sowohl bei einem Trend als auch ohne. Wenn bei der Ermittlung von Hochs und Tiefs ein gleitender Durchschnitt verwendet wird, kann der Wert der Extrema gegenüber dem gleitenden Durchschnitt 2 Punkte oder auch 100 Punkte betragen. Sind Extrema von 2 Punkten für uns beim Intraday-Handel wichtig? Eher nein. Bei einer langfristigen Investierung ziehen wir nicht einmal Extremwerte von weniger als 20 Punkten in Betracht, unabhängig von der Timeframe.

Gerade deswegen ist es notwendig, den Begriff der Spannweite des Rückgangs einzuführen. Darunter verstehen wir einen minimalen Wert der Extrema. Als Ausgangspunkt kann der gleitende Durchschnitt verwendet werden, anhand dessen man den Abstand bis zum Extremum ermitteln und seinen Wert unten beschränken kann. Aber in diesem Fall wird die Periode des gleitenden Durchschnitts wesentlich die Stellen der ermittelten Hochs und Tiefs beeinflussen. Es wird schwierig sein, irgendeine Periode als Standard auszuwählen.

Aus diesem Grund werden wir uns in der Zukunft von der Annahme leiten lassen, dass sich der Preis von einem Hoch zu einem Tief und von einem Tief zu einem Hoch bewegt; als Spannweite des Rückgangs bezeichnen wir die minimale Preisänderung zwischen zwei benachbarten Extrema, einem Tief und einem Hoch, in Punkten. Wenn einer der Extremwerte bereits ermittelt wurde, dann muss der Abstand bis zum nächsten Extremum nicht kleiner als der durch die Spannweite des Rückgangs gesetzte Abstand sein. Wenn wir dieses Kriterium verwenden, können wir Extrema unabhängig von Timeframe und Trend ermitteln. Ein solches Tool passt sowohl für den Intraday-Handel, als auch für die langfristige Investierung.

Schauen wir uns seinen Algorithmus näher an. Zunächst einmal erkennen wir Extrema visuell, dabei verwenden wir einen und denselben Chart, im ersten Fall beträgt aber die Spannweite des Rückgangs 60 Pips (Abb. 8), und im zweiten Fall — 30 Pips (Abb. 9). Nehmen wir an, dass das erste Extremum bereits ermittelt wurde (Punkt 1), und wir ermitteln nun die vorherigen. 

 

Abb. 8. Verwendung der Spannweite eines Rückgangs von 60 Pips


Abb. 9. Verwendung der Spannweite eines Rückgangs von 30 Pips

Die Extremwerte werden vom Ende des Charts (vom Punkt 1) ermittelt. Im ersten Fall wurden 4 Extremwerte im angegebenen Fenster festgestellt, im zweiten Fall - 10. Bei der Erhöhung der Spannweite des Rückgangs auf dem gegebenen Chart-Abschnitt werden gar keine Extrema erkannt. Bezugnehmend auf die Marktvolatilität und die ausgewählte Timeframe muss man realistisch an die Auswahl eines Fensters für die Ermittlung der Extrema herangehen. Mit dem "Fenster" meinen wir die Anzahl der Balken, auf welchen die Ermittlung erfolgt. 

Wir fassen alles oben Beschriebene zusammen und schlagen einen iterativen Algorithmus für die Ermittlung von Extremwerten vor. Warum gerade den iterativen? Denn auf das erste Hoch muss ein Tief folgen, und dann ein zweites Hoch usw. Wenn das zweite Tief nicht ermittelt wurde (die Grafik will nicht nach oben gehen), wird die Stelle des Tiefs neu definiert, und in der Zeitreihe wird weiter und weiter verschoben. Auf die gleiche Weise kann man auch die Stelle des ersten Hochs und jedes anderen Extremums korrigieren. Man muss auch die Varianten aussortieren, in welchen ein Balken gleichzeitig als ein Hoch und als ein Tief erkannt wird.

Natürlich erfordert dieser Ansatz zahlreiche Berechnungen. Es ist praktisch, ihn für die Ermittlung mehrerer Extrema zu verwenden. Je weniger Extrema, desto schneller läuft das Programm. Die Geschwindigkeit der Berechnungen hängt auch mit dem Fenster für die Ermittlung zusammen. Aber dieser Ansatz lohnt sich, denn so kann man Hochs und Tiefs bei maximaler Auswirkung der aktuellsten Kursschwankungen eindeutig ermitteln. Wenn man viele Extrema ermitteln muss, würde ich den ZigZag Indikator empfehlen.

3.2 Programmumsetzung des Indikators 

Der unten angeführte Code des iterativen Algorithmus verwendet zwecks der höchsten Schnelligkeit eine kleine Anzahl von Iterationen. Diese Vereinfachung führt zu keinem wesentlichen Qualitätsverlust bei der Ermittlung von Extrema. Zu den grundlegenden Eingabeparametern gehören das Fenster für die Ermittlung und die Spannweite des Rückgangs.

input int      bars=500;                 //  Fenster für die Ermittlung von Extremwerten
input double   delta_points=160;         //  Spannweite des Rückgangs, die den minimalen Abstand zwischen einem Hoch und einem Tief in Punkten festlegt
input double   first_extrem=0.9;         //  zusätzlicher Koeffizient für die Ermittlung des ersten Extremwertes
input double   reload_time=5;            //  der Wert des Zeitintervalls, anhand welchen die Werte des Indikators in Sekunden neu berechnet werden

Das Programm beinhaltet 3 eingebettete Schleifen, die für die Ermittlung von vier Extremwerten benötigt werden. In diesem Teil des Programms werden nur das erste Tief und die mit ihm verbundenen Extremwerte ermittelt. Die Ermittlung des ersten Hochs und der mit ihm verbundenen Extremwerten wird ähnlich umgesetzt.  

double High[],Low[];
datetime Time[];

ArraySetAsSeries(Low,true);
int copied1=CopyLow(Symbol(),0,0,bars+2,Low);
ArraySetAsSeries(High,true);
int copied2=CopyHigh(Symbol(),0,0,bars+2,High);
ArraySetAsSeries(Time,true);
int copied3=CopyTime(Symbol(),0,0,bars+2,Time);

double delta=delta_points*Point();  //  die Spannweite zwischen den Extrema in absoluten Werten

int j,k,l;
int j2,k2,l2;
double  j1,k1,l1;
int min[6];  // Array, das Tiefs festlegt, der Wert entspricht der Nummer des Balkens für das ermittelte Extremum
int max[6];  // Array, das Hochs bestimmt, der Wert entspricht der Nummer des Balkens für das ermittelte Extremum

int mag1=bars;
int mag2=bars;
int mag3=bars;
int mag4=bars;

j1=SymbolInfoDouble(Symbol(),SYMBOL_BID)+(1-first_extrem)*delta_points*Point();
// bei der Ermittlung des ersten Extremums bestimmt der zusätzliche Koeffizient den minimalen Preis, unterhalb dessen das erste Tief liegen muss

j2=0; // bei der ersten Iteration beginnt die Ermittlung vom letzten Balken der Historie an

for(j=0;j<=15;j++) // die Schleife, die das erste Tief ermittelt - min[1]
  {
   min[1]=minimum(j2,bars,j1);
   //es wird das nächste Tief im angegebenen Intervall ermittelt

   j2=min[1]+1;     // bei der nächsten Iteration beginnt die Ermittlung von dem bereits ermittelten Tief min[1] an
   j1=Low[min[1]]+delta;
   //der minimale Preis des Tiefs, das bei der nächsten Iteration ermittelt wurde, muss kleiner als der minimale Preis für das Tief sein, das bei der aktuellen Iteration ermittelt wurde

   k1=Low[min[1]];
   //der minimale Preis des Tiefs legt bei der Ermittlung des nächsten Hochs den maximalen Preis fest, oberhalb welchen dieses Hoch liegen muss

   k2=min[1];         // die Ermittlung eines Hochs, das auf ein Tief folgt, beginnt von dem ermittelten Tief min[1] an

   for(k=0;k<=12;k++) // die Schleife, die das erste Hoch max[1] ermittelt
     {
      max[1]=maximum(k2,bars,k1);
      //--- es wird das nächste Hoch im angegebenen Intervall ermittelt
      k1=High[max[1]]-delta;
      // der Preis des Hochs, das bei der nächsten Iteration ermittelt wird, muss höher sein als der maximale Preis des Hochs, das bei der aktuellen Iteration ermittelt wird

      k2=max[1]+1;   // bei der zweiten Iteration beginnt die Ermittlung von dem bereits ermittelten Hoch max[1] an

      l1=High[max[1]];
      //der maximale Preis des Hochs bestimmt bei der Ermittlung des nächsten Tiefs den minimalen Preis, unter welchem dieses Tief liegen muss
      l2=max[1];     // Ermittlung eines Tiefs, das auf das Hoch folgt, erfolgt vom ermittelten Hoch max[1] an
      for(l=0;l<=10;l++) // Schleife, die das zweite Tief min[2] und das zweite Hoch max[2] festlegt
        {
         min[2]=minimum(l2,bars,l1);
         //---es wird das nächste Tief im angegebenen Intervall ermittelt
         l1=Low[min[2]]+delta;
         //der minimale Preis des Tiefs, das bei der nächsten Iteration ermittelt wird, muss kleiner sein als der minimale Preis des Tiefs, das bei der aktuellen Iteration ermittelt wurde

         l2=min[2]+1;     // bei der nächsten Iteration beginnt die Ermittlung vom bereits ermittelten Tief min[2] an
         max[2]=maximum(min[2],bars,Low[min[2]]);
         //es wird das nächste Tief im angegebenen Intervall ermittelt

         if(max[1]>min[1] && min[1]>0 && min[2]>max[1] && min[2]<max[2] && max[2]<mag4)
           //zusammenfallende Extrema und Sonderfälle werden aussortiert
           {
            mag1=min[1];   // wenn die Bedingung erfüllt ist, werden die Stellen der ermittelten Extrema gespeichert
            mag2=max[1];
            mag3=min[2];
            mag4=max[2];
           }
        }
     }
  }
min[1]=mag1; // Extrema sind ermittelt, andernfalls werden alle Variablen auf bars gesetzt
max[1]=mag2;
min[2]=mag3;
max[2]=mag4;

Die Aufgabe, den nächsten Balken zu ermitteln, der minimale Preis für welchen kleiner als der angegebene Wert ist (oder der maximale Preis für welchen größer als der angegebene Wert ist), ist relativ einfach und wird in einer separaten Funktion umgesetzt.

int minimum(int a,int b,double price0)
//die Funktion ermittelt das nächste Tief im angegebenen Intervall, das unterhalb des price0 Preises mit einem Abstand liegt, der größer als die Spannweite des Rückgangs ist
  {
   double High[],Low[];
   ArraySetAsSeries(Low,true);
   int copied4=CopyLow(Symbol(),0,0,bars+2,Low);

   int i,e;
   e=bars;
   double pr=price0-delta_points*Point();    // der Preis, unter welchem ein Hoch addiert mit der Spannweite des Rückgangs liegen muss
   for(i=a;i<=b;i++)                         // Ermittlung eines Hochs im durch die Parameter a und b gesetzten Fenster
     {
      if(Low[i]<pr && Low[i]<Low[i+1])       // es wird das nächste Tief ermittelt, nach welchem der Preis beginnt zu steigen
        {
         e=i;
         break;
        }
     }

   return(e);
  }
int maximum(int a,int b,double price1)
//--- die Funktion bestimmt das nächste Hoch im angegebenen Intervall, das über dem price1 Preis mit einem Abstand liegt, das größer als die Spannweite des Rückgangs ist
  {
   double High[],Low[];
   ArraySetAsSeries(High,true);
   int copied5=CopyHigh(Symbol(),0,0,bars+2,High);

   int i,e;
   e=bars;
   double pr1=price1+delta_points*Point();   // der Preis, über welchen das Hoch addiert mit der Spannweite des Rückgangs liegen muss
   for(i=a;i<=b;i++)                         // Ermittlung eines Hochs im durch die Parameter a und b gesetzten Fenster
     {
      if(High[i]>pr1 && High[i]>High[i+1])   // es wird das nächste Hoch ermittelt, nach welchem der Preis beginnt zu fallen   
        {
         e=i;
         break;
        }
     }
   return(e);
  }

Die Aufgabe der Ermittlung von Extremwerten ist gelöst, aber nur in erster Näherung. Es ist zu berücksichtigen, dass das nach diesem Algorithmus ermittelte Hoch zwischen zwei Tiefs nicht das höchste in diesem Intervall sein kann. Da die Ermittlung vom Ende des Charts begann, müssen die Stellen der Hochs und Tiefs für das erste, zweite, dritte und weitere Extrema auch vom Ende korrigiert werden. Die Überprüfung und die Korrektur der Stellen von Hochs und Tiefs befinden sich in separaten Funktionen. Die Implementierung der Korrektur der Extrema sieht wie folgt aus:

min[1]=check_min(min[1],max[1]); // Prüfung und Korrektur der Stelle des ersten Tiefs im angegebenen Intervall    
max[1]=check_max(max[1],min[2]); // Prüfung und Korrektur der Stelle des ersten Hochs im angegebenen Intervall
min[2]=check_min(min[2],max[2]); // Prüfung und Korrektur der Stelle des zweiten Tiefs im angegebenen Intervall  

 


int check_min(int a,int b)
// die Funktion für die Prüfung und Korrektur der Stelle des Tiefs auf dem gegebenen Zeitintervall    
  {
   double High[],Low[];
   ArraySetAsSeries(Low,true);
   int copied6=CopyLow(Symbol(),0,0,bars+1,Low);
   int i,c;
   c=a;
   for(i=a+1;i<b;i++)                     // bei der Ermittlung des Tiefs werden alle durch das "Fenster" gesetzten Balken überprüft
     {
      if(Low[i]<Low[a] && Low[i]<Low[c])  // wenn ein Tief ermittelt wurde, das tiefer liegt
         c=i;                             // wird die Stelle des Tiefs neu definiert
     }
   return(c);
  }

int check_max(int a,int b)
//--- die Funktion für die Überprüfung und Korrektur der Stelle des Hochs im angegebenen Zeitintervall
  {
   double High[],Low[];
   ArraySetAsSeries(High,true);
   int copied7=CopyHigh(Symbol(),0,0,bars+1,High);
   int i,d;
   d=a;
   for(i=(a+1);i<b;i++)                         // bei der Ermittlung des Tiefs werden alle durch das "Fenster" gesetzten Balken überprüft
     {
      if(High[i]>High[a] && High[i]>High[d])    // wenn ein Hoch ermittelt wurde, das höher liegt,
         d=i;                                   // wird die Position des Hochs neu definiert
     }
   return(d);
  }

Wenn vier Extrema ermittelt wurden, reicht es, die Stellen nur der ersten drei zu prüfen. Denn für die Prüfung und Korrektur arbeitet die Funktion in einem Bereich, der für das aktuelle Extremum durch seine eigene Stelle und durch die Stelle des darauf folgenden Extremums definiert wird. Nach dem die Stellen geprüft wurden, kann man sicher sein, dass die ermittelten Extrema den gesetzten Kriterien entsprechen.

Danach wird das erste Hoch vom Ende des Charts ermittelt, anschließend werden die Stellen des ersten Tiefs und des ersten Hochs verglichen. Als Ergebnis der durchgeführten Berechnungen erhalten wir die Stelle des ersten Extremums vom Ende des Charts und weiterer mit ihm verbundenen Extrema.

Ich gehe noch einmal auf die Ermittlung des ersten Extremums ein. Es wurde bereits vorgeschlagen, einen zusätzlichen Koeffizienten für seine Ermittlung einzuführen — einen Bruch von der Spannweite des Rückgangs, zum Beispiel 0.7. Dabei erlauben hohe Werte (0.8…0.9), das erste Extremum mit hoher Genauigkeit, aber mit Verzögerung zu ermitteln, und die kleineren Werte (0.1…0.25) reduzieren zwar die Verzögerung, die Genauigkeit geht aber verloren. Demzufolge muss der Wert des zusätzlichen Koeffizienten entsprechend der Strategie ausgewählt werden.

Die ermittelten Hochs und Tiefs werden mit Pfeilen markiert. Die Pfeile markieren Koordinaten der Extrema (eine Koordinate zeigt die Stelle in der Zeitreihe an, die andere — den Wert des maximalen/minimalen Preises für das ermittelte Hoch/Tief). Da dabei zahlreiche Berechnungen durchgeführt werden, ist ein Eingabeparameter im Programm vorgesehen, der das Intervall setzt, mithilfe dessen die Werte des Indikators neu berechnet werden. Wenn es keine Hochs oder Tiefs im ausgewählten Fester ermittelt wurden, gibt der Indikator die entsprechende Meldung aus. Die Implementierung der grafischen Darstellung der Extrema sieht wie folgt aus:

if(min[1]<Max[1]) // wenn das Tief näher liegt, werden seine Stelle und die Stelle der mit ihm verbundenen Extrema markiert
  {
   ObjectDelete(0,"id_1");       // Markierungen, die beim vorherigen Schritt vorgenommen wurden, werden gelöscht
   ObjectDelete(0,"id_2");
   ObjectDelete(0,"id_3");
   ObjectDelete(0,"id_4");
   ObjectDelete(0,"id_5");
   ObjectDelete(0,"id_6");

   ObjectCreate(0,"id_1",OBJ_ARROW_UP,0,Time[min[1]],Low[min[1]]);         // markieren wir das erste Tief
   ObjectSetInteger(0,"id_1",OBJPROP_ANCHOR,ANCHOR_TOP);
   //--- das ermittelte erste Tief wird nach der Stelle in der Zeitreihe und dem minimalen Preis verankert    

   ObjectCreate(0,"id_2",OBJ_ARROW_DOWN,0,Time[max[1]],High[max[1]]);      // markieren wir das erste Hoch
   ObjectSetInteger(0,"id_2",OBJPROP_ANCHOR,ANCHOR_BOTTOM);
   //--- das ermittelte Hoch wird nach der Stelle in der Zeitreihe und dem maximalen Preis verankert    

   ObjectCreate(0,"id_3",OBJ_ARROW_UP,0,Time[min[2]],Low[min[2]]);         // markieren wir das zweite Tief
   ObjectSetInteger(0,"id_3",OBJPROP_ANCHOR,ANCHOR_TOP);
   //--- das ermittelte zweite Tief wird nach der Stelle in der Zeitreihe und dem minimalen Preis verankert
  }

 


if(min[1]>Max[1]) // wenn das Hoch näher liegt, werden seine Stelle und die Stelle der mit ihm verbundenen Extrema markiert
  {
   ObjectDelete(0,"id_1");  // die beim vorherigen Schritt vorgenommenen Markierungen werden gelöscht
   ObjectDelete(0,"id_2");
   ObjectDelete(0,"id_3");
   ObjectDelete(0,"id_4");
   ObjectDelete(0,"id_5");
   ObjectDelete(0,"id_6");

   ObjectCreate(0,"id_4",OBJ_ARROW_DOWN,0,Time[Max[1]],High[Max[1]]);         // markieren wir das erste Hoch
   ObjectSetInteger(0,"id_4",OBJPROP_ANCHOR,ANCHOR_BOTTOM);
   //das ermittelte erste Hoch wird nach der Stelle in der Zeitreihe und dem maximalen Preis verankert

   ObjectCreate(0,"id_5",OBJ_ARROW_UP,0,Time[Min[1]],Low[Min[1]]);            // markieren wir das erste Tief
   ObjectSetInteger(0,"id_5",OBJPROP_ANCHOR,ANCHOR_TOP);
   //das ermittelte Tief wird nach der Stelle in der Zeitreihe und dem minimalen Preis verankert

   ObjectCreate(0,"id_6",OBJ_ARROW_DOWN,0,Time[Max[2]],High[Max[2]]);         // markieren wir das zweite Hoch
   ObjectSetInteger(0,"id_6",OBJPROP_ANCHOR,ANCHOR_BOTTOM);
   //das zweite ermittelte Hoch wird nach der Stelle in der Zeitreihe und dem maximalen Preis verankert
  }

if(min[1]==Max[1]) Alert("Im angegebenen Fester von ",bars," Balken wurden keine Extrema ermittelt");
// wenn keine Extrema ermittelt wurden, wird eine entsprechende Meldung ausgegeben

Während der Deinitalisierung des Indikators werden die Objekte, die Hochs und Tiefs markieren, gelöscht. 

Anhand der vorgestellten Algorithmen wurde ein benutzerdefinierter Indikator entwickelt, der Extrema ermittelt und diese auf dem Chart markiert (Abb. 10).


 

Abb. 10. Die Ergebnisse des Indikators: Spannweite des Rückgangs 120 Pips (a), Spannweite 160 Pips (b)

Die Ergebnisse hängen mit der Spannweite des Rückgangs zusammen. Beim Wert 120 Pips und weniger (Abb. 10, a) liegen die Extrema relativ nah beieinander, und die Fenstergröße ist nicht so wichtig. Beim Wert 160 Pips und mehr (Abb. 10, b)  liegen die Extrema relativ weit voneinander. Das muss man bei der Auswahl des Fensters für die Ermittlung berücksichtigen. Bei der Stagnation auf dem Markt erlaubt ein optimal ausgewähltes Fenster nicht nur Hochs und Tiefs bei einer geringen Bewegung automatisch zu erkennen, sondern auch Extrema auszufiltern (auszulassen), die durch große Zeitintervalle voneinander getrennt sind.

3.3 Expert Advisor, der die Strategie der Divergenz zwischen dem MACD-Histogramm und Kursen umsetzt

Die vorgestellten Algorithmen können für die Umsetzung verschiedener Strategien verwendet werden. Die Ergebnisse des Indikators scale_factor sind für das Arbeiten mit solchen grafischen Mustern wie "Kopf und Schulter", "Doppelte Spitze", "Doppelter Boden" u.a. gut geeignet. Man kann diese auch in den Strategien einsetzen, die die Divergenz zwischen Hochs und Tiefs für Preischarts und Indikatoren verwenden. Zum Beispiel: ein Expert Advisor, der auf der Strategie der Divergenz zwischen dem Preischart und dem MACD-Histogramm basiert. Diese Strategie wurde im Buch von Alexander Elder "Trading for a Living: Psychology, Trading Tactics, Money Management" ausführlich beschrieben.

Laut dieser Strategie: wenn der Kurs beim Steigen ein neues Hoch bildet, das oberhalb des vorherigen Hochs liegt, und das durch das MACD-Histogramm gebildete Hoch unter dem vorherigen liegt, – ist das ein Sell-Signal. 

Wenn der Kurs fällt, und dabei ein neues Tief gebildet wird, das unterhalb des nächsten Tiefs liegt, aber das vom MACD-Histogramm gebildete Tief sich oberhalb des vorherigen Tiefs des Histogramms findet, wird ein Signal erzeugt, das auf eine Trendwende hinweist sowie auf die Notwendigkeit zu kaufen. 

Der Expert Advisor, der den oben beschriebenen Algorithmus umsetzt, ermittelt Hochs und Tiefs eindeutig entsprechend der angegebenen Spannweite des Rückgangs, und berücksichtigt in erster Linie die letzten Änderungen im Preischart. 

Zu den Eingabeparametern gehören das Fenster für die Ermittlung von Extrema und die Spannweite des Kursrückgangs. Darüber hinaus muss die minimale Divergenz für die letzten zwei Hochs festgelegt werden, wenn der Kurs steigt (für die letzten zwei Tiefs, wenn der Kurs fällt) sowie die minimale Divergenz des MACD-Histogramms für Extrema. Für jeden Trade werden das Risiko in der Anlagewährung und ein zusätzlicher Koeffizient festgelegt. Der guard_points Parameter bestimmt die zusätzliche Verschiebung von Stoploss nach unten hinsichtlich des minimalen Preises für das nächste Tief, wenn eine Long-Position eröffnet wird. Dementsprechend wird Stoploss bei der Eröffnung einer Short-Position nach oben verschoben. Darüber hinaus gibt es die Option, Parameter ermittelter Extrema bei der Eröffnung von Trades auszugeben (show_info=1).

input int      bars=500;                 //  Fenster für die Ermittlung von Extrema
input double   delta_points=160;         //  Spannweite des Rückgangs, die den minimalen Abstand zwischen einem Hoch und einem Tief in Punkten festlegt
input double   first_extrem=0.9;         //  zusätzlicher Koeffizient für die Ermittlung des ersten Extremwertes
input int      orderr_size=10;           //  Risiko für jeden Trade
input double   macd_t=0.00002;           //  minimale Divergenz des MACD-Histogramms
input double   trend=100;                //  minimale Divergenz der Preise für zwei nächste Hochs/Tiefs
input double   guard_points=30;          //  Verschiebung von Stoploss
input int      time=0;                   //  Verzögerung in Sekunden
input int      show_info=0;              //  Ausgabe der Information über Extrema

Die Berechnungen können auf jedem Tick ausgeführt werden genau wie auch Handelsoperationen. Die Strategie wird aber auch bei der Einführung einer Verzögerung gut funktionieren. Nachdem die grundlegenden Parameter in absoluten Werten festgelegt sind, fangen wir mit der Ermittlung der Extrema an. Der erste Teil des Programms erlaubt es, Extrema für den Fall zu ermitteln, wenn das erste der Extrema ein Tief ist, danach werden die Stellen der Extrema geprüft. Der zweite Teil des Codes ermittelt Extrema für den Fall, wenn das erste Extremum vom Ende des Charts ein Hoch ist. Beim nächsten Schritt werden die Parameter der Hochs und Tiefs geprüft und korrigiert wenn nötig.

void OnTick()
  {
   Sleep(1000*time);                //  Einführung einer Verzögerung

   double High[],Low[];

   ArraySetAsSeries(Low,true);
   int copied1=CopyLow(Symbol(),0,0,bars+2,Low);
   ArraySetAsSeries(High,true);
   int copied2=CopyHigh(Symbol(),0,0,bars+2,High);
   ArraySetAsSeries(Time,true);
   int copied3=CopyTime(Symbol(),0,0,bars+2,Time);

   MqlTick last_tick;
   double Bid=last_tick.bid;
   double Ask=last_tick.ask;

   double delta=delta_points*Point();  // Spannweite des Rückgangs in absoluten Werten
   double trendd=trend*Point();        // minimaler Preisunterschied für 2 nächste Hochs/Tiefs in absoluten Werten
   double guard=guard_points*Point();  // Verschiebung von Stoploss in absoluten Werten


   int j,k,l;
   int j2,k2,l2;
   double  j1,k1,l1;
   int min[6];  // ein Array, das Tiefs ermittelt, wenn das erste ermittelte Extremum ein Tief ist. Der Wert entspricht der Nummer des Balkens des ermittelten Extremums
   int max[6];  // ein Array, das Hochs ermittelt, wenn das erste ermittelte Extremum ein Tief ist. Der Wert entspricht der Nummer des Balkens des ermittelten Extremums
   int Min[6];  // ein Array, das Tiefs ermittelt, wenn das erste ermittelte Extremum ein Hoch ist. Der Wert entspricht der Nummer des Balkens des ermittelten Extremums
   int Max[6];  // ein Array, das Hochs ermittelt, wenn das erste ermittelte Extremum ein Hoch ist. Der Wert entspricht der Nummer des Balkens des ermittelten Extremums

   int mag1=bars;
   int mag2=bars;
   int mag3=bars;
   int mag4=bars;

   j1=SymbolInfoDouble(Symbol(),SYMBOL_BID)+(1-first_extrem)*delta_points*Point();
   // bei der Ermittlung des ersten Extremums bestimmt der zusätzliche Koeffizient den minimalen Preis, unterhalb dessen das erste Tief liegen muss

   j2=0;                         // bei der ersten Iteration beginnt die Ermittlung vom letzten Balken der Historie an
   for(j=0;j<=15;j++)            // die Schleife, die das erste Tief min[1] ermittelt
     {
      min[1]=minimum(j2,bars,j1);
      // es wird das nächste Tief im angegebenen Intervall ermittelt

      j2=min[1]+1;              //bei der nächsten Iteration beginnt die Ermittlung von dem bereits ermittelten Tief min[1] an
      j1=Low[min[1]]+delta;
      //--- der minimale Preis des Tiefs, das bei der nächsten Iteration ermittelt wird muss kleiner sein als der minimale Preis für das Tief, das bei der aktuellen Iteration ermittelt wurde
      k1=Low[min[1]];
      //der minimale Preis des Tiefs definiert bei der Ermittlung des nächsten Hochs den maximalen Preis, über welchen dieses Hoch liegen muss

      k2=min[1];                 // die Ermittlung des Hochs, das auf das Tief folgt, erfolgt vom bereits ermittelten Tief min[1]

      for(k=0;k<=12;k++)         // die Schleife, die das erste Hoch max[1] ermittelt
        {
         max[1]=maximum(k2,bars,k1);
         //--- es wird das nächste Hoch im gegebenen Intervall ermittelt
         k1=High[max[1]]-delta;
         //--- der maximale Preis des Hochs, das bei der nächsten Iteration ermittelt wird, muss größer sein, als der maximale Preis des Hochs, das bei der aktuellen Iteration ermittelt wurde
         k2=max[1]+1;            // bei der nächsten Iteration fängt die Ermittlung vom bereits ermittelten Hoch max[1] an
         l1=High[max[1]];
         //--- der maximale Preis des Hochs bei der Ermittlung des nächsten Tiefs bestimmt den minimalen Preis, unter welchem dieses Tief liegen muss
         l2=max[1];              // die Ermittlung eines Tiefs, das auf ein Hoch folgt, erfolgt vom bereits ermittelten Hoch max[1] an
         for(l=0;l<=10;l++)      // die Schleife, die das zweite Tief min[2] und das zweite Hoch max[2] ermittelt
           {
            min[2]=minimum(l2,bars,l1);
            //--- es wird das nächste Tief im angegebenen Intervall ermittelt
            l1=Low[min[2]]+delta;
            //der minimaler Preis des Tiefs, das bei der nächsten Iteration ermittelt wurde, muss kleiner sein als der minimale Preis des Tiefs, das bei der aktuellen Iteration ermittelt wurde

            l2=min[2]+1;         //bei der nächsten Iteration fängt die Ermittlung vom bereits ermittelten Tief min[2] an

            max[2]=maximum(min[2],bars,Low[min[2]]);
            //es wird das nächste Hoch im angegebenen Intervall ermittelt
            if(max[1]>min[1] && min[1]>0 && min[2]>max[1] && min[2]<max[2] && max[2]<mag4)
              //--- zusammenfallende Extrema und Sonderfälle werden aussortiert
              {
               mag1=min[1];      // wenn die Bedingung erfüllt ist, werden die Stellen der Extrema bei jeder Iteration gespeichert
               mag2=max[1];
               mag3=min[2];
               mag4=max[2];

              }
           }
        }

     }

//--- Extrema sind ermittelt, andernfalls werden alle Variablen auf bars gesetzt
   min[1]=mag1;
   max[1]=mag2;
   min[2]=mag3;
   max[2]=mag4;
//--- Prüfung und Korrektur der Stellen der Extrema im angegebenen Intervall

   min[1]=check_min(min[1],max[1]);  
   max[1]=check_max(max[1],min[2]);
   min[2]=check_min(min[2],max[2]);

//---------------------------------------------------------------------------------------------------------------
   mag1=bars;
   mag2=bars;
   mag3=bars;
   mag4=bars;

   j1=SymbolInfoDouble(Symbol(),SYMBOL_BID)-(1-first_extrem)*delta_points*Point();
   // bei der Ermittlung des ersten Extremums bestimmt der zusätzliche Koeffizient den maximalen Preis, oberhalb dessen das erste Hoch liegen muss

   j2=0;  // bei der ersten Iteration erfolgt die Ermittlung von dem letzten Balken der Historie an

   for(j=0;j<=15;j++)      // die Schleife, die das erste Hoch Max[1] ermittelt
     {
      Max[1]=maximum(j2,bars,j1);
      //es wird das nächste Hoch im angegebenen Intervall ermittelt

      j1=High[Max[1]]-delta;
      //der maximale Preis des Hochs, das bei der nächsten Iteration ermittelt wird, muss höher sein als der maximale Preis des Hochs, das bei der aktuellen Iteration ermittelt wurde
      j2=Max[1]+1;         // bei der nächsten Iteration fängt die Ermittlung von dem bereits ermittelten Hoch Max[1] an

      k1=High[Max[1]];
      //der maximale Preis des Hochs bestimmt bei der Ermittlung des nächsten Tiefs den minimalen Preis, unter welchem dieses Tief liegen muss

      k2=Max[1];           // die Ermittlung des Tiefs, das das auf ein Hoch folgt, fängt vom bereits ermittelten Hoch Max[1] an

      for(k=0;k<=12;k++)   //die Schleife, die das erste Hoch Min[1] ermittelt
        {
         Min[1]=minimum(k2,bars,k1);
         //--- es wird das nächste Tief im angegebenen Intervall ermittelt
         k1=Low[Min[1]]+delta;
         //der minimale Preis des Tiefs, das bei der nächsten Iteration ermittelt wird, muss kleiner sein als der minimale Preis des Tiefs, das bei der aktuellen Iteration ermittelt wurde
         k2=Min[1]+1;      // bei der nächsten Iteration beginnt die Ermittlung von dem bereits ermittelten Tief Min[1] an
         l1=Low[Min[1]];
         //---der minimale Preis des Tiefs bestimmt bei der Ermittlung des nächsten Hochs den maximalen Preis, über welchem dieses Hoch liegen muss
         l2=Min[1];        // Ermittlung des Hochs, das auf ein Tief folgt, erfolgt vom Tief Min[1] an
         for(l=0;l<=10;l++)//die Schleife, die das zweite Hoch Max[2] und das zweite Tief Min[2] ermittelt
           {
            Max[2]=maximum(l2,bars,l1);
            //es wird das nächste Hoch im angegebenen Intervall ermittelt

            l1=High[Max[2]]-delta;
            //der maximale Preis des Hochs, das bei der nächsten Iteration ermittelt wird, muss größer sein als der maximale Preis des Hochs, das bei der aktuellen Iteration ermittelt wurde
            l2=Max[2]+1;  //bei der nächsten Iteration beginnt die Ermittlung von dem bereits ermittelten Hoch Max[2] an

            Min[2]=minimum(Max[2],bars,High[Max[2]]);
            //---es wird das nächste Tief im angegebenen Intervall ermittelt
            if(Max[2]>Min[1] && Min[1]>Max[1] && Max[1]>0 && Max[2]<Min[2] && Min[2]<bars)
              //--- übereinstimmende Extrema und Sonderfälle werden ausgefiltert
              {
               mag1=Max[1];  // wenn die Bedingung erfüllt ist, werden die Stellen der ermittelten Extrema bei jeder Iteration gespeichert
               mag2=Min[1];
               mag3=Max[2];
               mag4=Min[2];
              }
           }
        }
     }
   Max[1]=mag1;  // Extrema sind ermittelt, andernfalls werden alle Variablen auf bars gesetzt
   Min[1]=mag2;
   Max[2]=mag3;
   Min[2]=mag4;

   Max[1]=check_max(Max[1],Min[1]);  // Prüfung und Korrektur der Stellen der Extrema im angegebenen Intervall
   Min[1]=check_min(Min[1],Max[2]);
   Max[2]=check_max(Max[2],Min[2]);

Später kann man sowohl das erste ermittelte Hoch als auch das erste Tief verwenden. Aber besonders sinnvoll scheint die Variante zu sein, bei der das möglichst nahe Extremum sowie die auf ihm basierenden Hochs und Tiefs verwendet werden. 

Für beide Varianten werden die Lotgröße sowie die Werte des Indikators entsprechend den Stellen der Extrema berechnet. Es wird die Bedingung für die richtige Ermittlung von Extrema und das Ausbleiben offener Positionen überprüft.

Wenn es eine Divergenz zwischen den Preisen von Extrema und MACD-Histogramm gibt, und diese Divergenz nicht kleiner als die in den Eingabeparametern gesetzten Werte ist, wird eine entsprechende Position eröffnet. Die Divergenzen müssen dabei entgegengerichtet sein.

   double lot_buy=NormalizeDouble(0.1*orderr_size/(NormalizeDouble(((SymbolInfoDouble(Symbol(),SYMBOL_BID)-Low[min[1]]+guard)*10000),0)+0.00001),2);
   //es wird die Lotgröße beim Kauf berechnet

   double lot_sell=NormalizeDouble(0.1*orderr_size/(NormalizeDouble(((High[Max[1]]-SymbolInfoDouble(Symbol(),SYMBOL_ASK)+guard)*10000),0)+0.00001),2);
   //--- es wird die Lotgröße beim Verkauf berechnet
   int index_handle=iMACD(NULL,PERIOD_CURRENT,12,26,9,PRICE_MEDIAN);
   double MACD_all[];
   ArraySetAsSeries(MACD_all,true);
   int copied4=CopyBuffer(index_handle,0,0,bars+2,MACD_all);
   double index_min1=MACD_all[min[1]];
   double index_min2=MACD_all[min[2]];
   //--- es werden die Werte des Indikators berechnet, die den Stellen der Extrema entsprechen, wenn das erste Extremum ein Tief ist
   double index_Max1=MACD_all[Max[1]];
   double index_Max2=MACD_all[Max[2]];
   //es werden dir Werte des Indikators berechnet, die den Stellen der Extrema entsprechen, wenn das erste Extremum ein Hoch ist
   bool flag_1=(min[2]<bars && min[2]!=0 && max[1]<bars && max[1]!=0 && max[2]<bars && max[2]!=0); //Die Bedingung der korrekten Ermittlung der Extrema wird überprüft
   bool flag_2=(Min[1]<bars && Min[1]!=0 && Max[2]<bars && Max[2]!=0  && Min[2]<bars && Min[2]!=0);
   bool trend_down=(Low[min[1]]<(Low[min[2]]-trendd));
   bool trend_up=(High[Max[1]]>(High[Max[2]]+trendd));
   //---der Unterschied zwischen den Preisen der Extrema darf nicht kleiner der festgelegten Wert sein
   openedorder=PositionSelect(Symbol());  //überprüfen, ob die Bedingung erfüllt ist, dass es keine offenen Positionen gibt
S   if(min[1]<Max[1] && trend_down && flag_1 && !openedorder && (index_min1>(index_min2+macd_t)))
      //wenn das erste Extremum ein Tief ist, wird ein Long-Position eröffnet
      //der Unterschied zwischen den Werten des MACD Indikators für Extrema ist nicht kleiner als der vom Inputparameter macd_t gesetzte Wert
      // ein Trade wird im Fall einer entgegengerichteten Bewegung für den Preis und den Indikator eröffnet, berechnet basierend auf die Extremwerten
     {
      if(show_info==1) Alert("Innerhalb der letzten",bars," Balken beträgt der Abstand in Balken bis zum nächsten Tief und Extrema ",min[1]," ",max[1]," ",min[2]);
      //--- Information über die Extremwerte ausgeben
      MqlTradeResult result={0};
      MqlTradeRequest request={0};
      request.action=TRADE_ACTION_DEAL;
      request.magic=123456;
      request.symbol=_Symbol;
      request.volume=lot_buy;
      request.price=SymbolInfoDouble(Symbol(),SYMBOL_ASK);
      request.sl=Low[min[1]]-guard;
      request.tp=MathAbs(2*SymbolInfoDouble(Symbol(),SYMBOL_BID)-Low[min[1]])+guard;
      request.type=ORDER_TYPE_BUY;
      request.deviation=50;
      request.type_filling=ORDER_FILLING_FOK;

      OrderSend(request,result);
     }

   if(min[1]>Max[1] && trend_up && flag_2 && !openedorder && (index_Max1<(index_Max2-macd_t)))
      //wenn das erste Extremum ein Hoch ist, wird ein Short-Position eröffnet
      //der Unterschied zwischen den Werten des MACD-Indikators darf nicht kleiner als der vom Inputparameter macd_t gesetzte Wert sein
      // ein Trade wird im Fall einer entgegengerichteten Bewegung für den Preis und den Indikator eröffnet, berechnet basierend auf den Extremwerten
     {
      if(show_info==1) Alert("Innerhalb der letzten ",bars," Balken beträgt der Abstand in Balken bis zum nächsten Hoch und Extrema",Max[1]," ",Min[1]," ",Max[2]);
      //---Ausgabe der Information über die Extrema
      MqlTradeResult result={0};
      MqlTradeRequest request={0};
      request.action=TRADE_ACTION_DEAL;
      request.magic=123456;
      request.symbol=_Symbol;
      request.volume=lot_sell;
      request.price=SymbolInfoDouble(Symbol(),SYMBOL_BID);
      request.sl=High[Max[1]]+guard;
      request.tp=MathAbs(High[Max[1]]-2*(High[Max[1]]-SymbolInfoDouble(Symbol(),SYMBOL_ASK)))-guard;
      request.type=ORDER_TYPE_SELL;
      request.deviation=50;
      request.type_filling=ORDER_FILLING_FOK;

      OrderSend(request,result);
     }

Bei der Eröffnung einer Short-Position wird Stoploss nach der Stelle des nächsten Hochs gesetzt, bei der Eröffnung einer Long-Position — nach der Stelle des nächsten Tiefs. Dies erlaubt es, realistische Ziele sowohl bei Schwankungen als auch bei Flaute auf dem Markt zu setzen. In beiden Fällen wird Takeprofit symmetrisch zu Stoploss hinsichtlich des aktuellen Preises gesetzt. Beim Intraday-Handel wählt man eine kleinere Spannweite aus. Bei einer langfristigen Inverstierung sollte man eine größere Spannweite auswählen.

Betrachten wir den Expert Advisor anhand des folgenden Beispiels (Abb. 11). Die grundlegenden Eingabeparameter, die verwendet wurden: Spannweite — 160 Pips, minimale Divergenz MACD – 0,0004; minimaler Preisunterschied für zwei nächste Hochs/Tiefs – 120 Pips und zusätzlicher Koeffizient – 0.9. 


Abb. 11. Die Ergebnisse des Expert Advisors

Zunächst wurden drei letzte Extrema ermittelt. In dem Moment, als die Entscheidung über die Eröffnung einer Long-Position getroffen wurde, wurden ein Hoch und zwei Tiefs (mit Pfeilen markiert) erkannt. Im Gegensatz zum Indikator markiert der Expert Advisor die Stellen der Extrema nicht, aber wenn nötig können wir den Parameter show_info verwenden und ihn auf 1 setzen. So können wir Informationen über die Stellen der Extrema beim Eröffnen von Positionen erhalten.

Der Preisunterschied für 2 nächste Tiefs betrug 148 Pips. Dieser Wert ist größer als der angegebene Wert. Die Divergenz des MACD beträgt für diese Extrema 0.00062, und dieser Wert ist auch größer. Unter Berücksichtigung der gegenläufigen Änderung der Preise und der Werte des Indikators, die anhand der letzten zwei Tiefs ermittelt wurde, wurde eine Long-Position an der vom zusätzlichen Koeffizienten (er beträgt 150 Pips) bestimmten Stelle eröffnet. Bei der Verwendung kleinerer Werte des zusätzlichen Koeffizienten hätte die Position auch früher eröffnet und der Gewinn auch früher gesichert werden können.

Und zum Schluss präsentiere ich die Testergebnisse des Expert Advisors (Abb. 12). Während des Testens wurde die maximale Auswirkung der Parameter macd_t und trend auf die Profitabilität festgestellt. Je größer die Werte dieser Parameter sind, desto höher ist die Anzahl profitabler Trades (in Prozent). Aber während die Profitabilität steigt, sinkt gleichzeitig die Gesamtzahl der Trades. 

Bei den Parametern macd_t = 0,0006 und trend=160 (Abb. 12), waren 56% der 44 Trades profitabel. Bei macd_t = 0,0004 und trend=120 wurden 84 Trades ausgeführt, unter denen 51% profitabel waren.


 Abb. 12. Testergebnisse des Expert Advisors

Bei der Optimierung der Strategie, ist es in erster Linie wichtig, optimale Parameter macd_t und trend zu finden. Auf die Parameter der Trades wird sich auch die Spannweite des Rückgangs und des zusätzlichen Koeffizienten auswirken. Die Spannweite des Rückgangs wird die Anzahl der ermittelten Extrema und somit die Anzahl von Trades bestimmen. Der zusätzliche Koeffizient bestimmt, wie nah Takeprofit und Stoploss bei der Eröffnung einer Position gesetzt werden.

Diese und eine Reihe anderer Strategien können nur bei der Verwendung der vorgestellten Tools möglichst korrekt funktionieren. Sonst kann es zu Situationen kommen, dass ein Trade mit Takeprofit und Stoploss, gesetzt mit einem Abstand von 200 Punkten vom aktuellen Preis, durch Signale eröffnet wird, die unter der Verwendung von Extrema von 5 Punkten und kleiner erzeugt wurden. Die Bedeutung solcher Extrema ist in diesem Fall äußerst niedrig. In diesen und vielen anderen Situationen ermitteln traditionelle Tools entweder viel zu viele unbedeutende Extrema oder erlauben es, gar keine Hochs und Tief zu ermitteln. Darüber hinaus fällt den traditionellen Werkzeugen schwer, Extrema am Ende einer Zeitreihe zu erkennen.

Fazit

Die im Artikel vorgestellten Algorithmen und Lösungen ermöglichen es, je nach Kursrückgang Extrema auf Preischarts eindeutig zu ermitteln. Die erhaltenen Ergebnisse sind sowohl für die Erkennung grafischer Muster, als auch für die Umsetzung von Handelsstrategien, die auf grafischen Mustern und Indikatoren basieren, zur Anwendung geeignet. Die entwickelten Werkzeuge weisen eine Reihe von Vorteilen gegenüber altbekannten Lösungen auf. Es werden nur wichtige Extrema ermittelt, unabhängig von der Situation auf dem Markt: sowohl in einem Trend, als auch in einem Seitwärtsmarkt. Es werden nur die Extrema ermittelt, die größer als ein vorgegebener Wert sind, andere Hochs und Tiefs werden ignoriert. Die Ermittlung beginnt vom Ende eines Charts an. Auf diese Weise kann man Ergebnisse erhalten, die von den aktuellsten Kursschwankungen stark abhängen. Die erhaltenen Ergebnisse sind fast unabhängig von der Timeframe und werden nur durch den angegebenen Kursrückgang bestimmt. 

Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/2817

Beigefügte Dateien |
report.zip (200.04 KB)
3D-Modellierung in MQL5 3D-Modellierung in MQL5
Eine Zeitreihe stellt ein dynamisches System dar, in welches Werte einer zufälligen Größe einer nach dem anderen eintreffen: kontinuierlich oder in gewissen Zeitabständen. Der Übergang von einer flachen zur dreidimensionalen Analyse des Marktes ermöglicht es, komplexe Prozesse und Erscheinungen aus einer neuen Perspektive zu betrachten. In diesem Artikel werden Visualisierungsfunktionen für eine 3D-Darstellung zweidimensionaler Daten beschrieben.
Grafische Interfaces X: Text Edit Box, Bild Slider und einfache Controls (build 5) Grafische Interfaces X: Text Edit Box, Bild Slider und einfache Controls (build 5)
In diesem Artikel besprechen wir neue Controls: Text Edit Box, Bild-Slider, sowie weitere zusätzliche einfache Controls: Text-Label und Bild. Die Bibliothek wächst weiter, und neben der Einführung der neuen Steuerelemente, werden auch die zuvor erstellten verbessert.
Grafische Interfaces X: Elemente der Zeit, Listen von Kontrollkästchen und das Sortieren von Tabellen (build 6) Grafische Interfaces X: Elemente der Zeit, Listen von Kontrollkästchen und das Sortieren von Tabellen (build 6)
Weiterentwicklung der Bibliothek zum Erstellen grafischer Benutzeroberflächen. Zeit und Listen von Kontrollkästchen werden diesmal behandelt. Weiters verfügt die Klasse CTable jetzt über die Möglichkeit, Daten auf- oder absteigend zu sortieren.
Entwickeln und testen einer Strategie für Binäre Optionen mit dem Strategie-Tester des MetaTrader 4 Entwickeln und testen einer Strategie für Binäre Optionen mit dem Strategie-Tester des MetaTrader 4
Eine Anleitung für das Erstellen und Testen einer Strategie für Binäre Optionen im Strategie-Tester des MetaTrader 4 mit dem "Binary-Options-Strategy-Tester" aus dem Market.