English Русский 中文 Español 日本語 Português
Modell der Bewegungsfortsetzung - Suche im Chart und Ausführungsstatistik

Modell der Bewegungsfortsetzung - Suche im Chart und Ausführungsstatistik

MetaTrader 5Beispiele | 4 Dezember 2018, 09:54
902 1
Almat Kaldybay
Almat Kaldybay
  1. Einführung
  2. Modellbeschreibung - allgemeine Eigenschaften
  3. Prinzipien des Modells zur Erkennung auf dem Chart
  4. Konstruktion und der Code des Algorithmus'
    1. Eingabeparameter, die Funktion OnInit() und die Anfangsdeklaration der Variablen
    2. Allgemeine Eigenschaften
    3. Aktualisierung der Arraydaten
      1. Eintragungen in die Arrays, wenn eine neue Bar erscheint
      2. Eintragungen der Daten in die Arrays der Bar 0
      3. Aktualisierung der Daten der Fraktale
    4. Suche nach den Extrema
      1. Suche nach den Extrema bei einem Abwärtstrend
      2. Suche nach den Extrema bei einem Aufwärtstrend
      3. Reduktion der Hochs/Tiefs der Korrekturwellen in vereinigte Variablen
    5. Modell der Erkennungsbedingungen
    6. Erstellen der Steuerelemente
      1. Bilden eines Einstiegspunktes im Bereich der Positionseröffnung
      2. Kontrolle einer Preiskorrektur in den Bereich der Positionseröffnung
      3. Eliminieren von doppelten Positionen im Singlemodell
    7. Beschreiben der Bedingungen für die Positionseröffnung
    8. Handelsbedingungen
    9. Arbeiten mit den Handelsoperationen
      1. Positionseinstellungen
      2. Take-Profit einstellen
      3. Position auf Breakeven bewegen
  5. Sammeln der statistischen Daten
  6. Schlussfolgerung


1. Einführung

Dieser Artikel bietet eine programmtechnische Realisation eines Modells der Bewegungsfortsetzung. Die Hauptidee besteht darin, zwei Wellen zu definieren - die Haupt- und die Korrekturwelle. Für Extrempunkte verwende ich sowohl Fraktale als auch "potenzielle" Fraktale - Extrempunkte, die sich noch nicht als Fraktale gebildet haben. Als nächstes werde ich versuchen, statistische Daten über die Wellenbewegung zu sammeln. Die Daten werden in einer CSV-Datei gespeichert.


2. Modellbeschreibung - allgemeine Eigenschaften

Das im Artikel beschriebene Modelle de Bewegungsfortsetzung besteht aus zwei Wellen: der Haupt- und der Korrekturwelle. Das Modell ist in Abbildung 1 schematisch beschrieben. AB ist die Hauptwelle, BC ist die Korrekturwelle, während CD die Fortsetzung der Bewegung in Richtung des Haupttrends ist.

Modell der Bewegungsfortsetzung

Abb. 1. Modell der Bewegungsfortsetzung

Auf dem Chart schaut es dann so aus:

Modell der Bewegungsfortsetzung mit AUDJPY H4

Abb. 2. Modell der Bewegungsfortsetzung mit AUDJPY H4


3. Prinzipien des Modells zur Erkennung auf dem Chart

Die Prinzipien der Modellerkennung sind in der Tabelle 1 beschrieben.

Tabelle 1. Die Prinzipien des Modells der Bewegungsfortsetzung im Kontext eines Trends

Prinzipien des Modells zur Erkennung in einem Abwärtstrend  # Prinzipien des Modells zur Erkennung in einem Aufwärtstrend
1 Die extreme Bar hat ihre Hoch/Tief über/unter den Hochs/Tiefs der vorherigen Bars 1 Die extreme Bar hat ihre Hoch/Tief über/unter den Hochs/Tiefs der vorherigen Bars
2 Eine Korrekturwelle sollte immer über einen neuen Höchstwert (Punkt С - siehe Abb. 1 und Abb. 2) verfügen. 2 Eine Korrekturwelle sollte immer über einen neuen Tiefstwert (Punkt С - siehe Abb. 1 und Abb. 2) verfügen.
 3 Die Dauer der Korrekturwelle darf nicht lang sein und sollte sich auf einige Bars beschränken.  3 Die Dauer der Korrekturwelle darf nicht lang sein und sollte sich auf einige Bars beschränken.
 4 Das Hoch einer Korrekturbewegung (Punkt С - siehe Abb. 1 und Abb. 2) sollte kleiner sein als das Hoch der Hauptbewegung (Punkt A - siehe Abb. 1 und Abb. 2)  4 Das Tief einer Korrekturbewegung (Punkt С - siehe Abb. 1 und Abb. 2) sollte größer sein als das Tief der Hauptbewegung (Punkt A - siehe Abb. 1 und Abb. 2)
 5 Zeitlinienprinzip der Positionseröffnung - eine Position sollte nur zu einem bestimmten Moment des Eröffnungsmuster eröffnet werden  5 Zeitlinienprinzip der Positionseröffnung - eine Position sollte nur zu einem bestimmten Moment des Eröffnungsmuster eröffnet werden


4. Konstruktion und der Code des Algorithmus'

1. Eingabeparameter, die Funktion OnInit() und die Anfangsdeklaration der Variablen

Zuerst müssen wir die Klasse CTrade für einen vereinfachten Zugriff auf die Handelsoperationen einbinden:

//--- Einbinden der Dateien
#include <Trade\Trade.mqh> 
//--- Objekt zur Durchführung der Handelsoperationen
CTrade  trade;

Als nächstes definieren wir die Eingabeparameter:

//--- Eingabeparameter
input ENUM_TIMEFRAMES base_tf;  //Zeitrahmen der Basisperiode
input ENUM_TIMEFRAMES work_tf;  //Zeitrahmen der Arbeitsperiode
input double SummRisk=100;      //Gesamtrisiko je Position
input double sar_step=0.1;      //Schrittweite der Parabolic
input double maximum_step=0.11; //maximale Schrittweite der Parabolic
input bool TP_mode=true;        //Take-Profit erlauben
input int M=2;                  //Verhältnis von Gewinn zu Risiko
input bool Breakeven_mode=true; //erlauben Positionen auf Breakeven zu schieben
input double breakeven=1;       //Verhältnis von Gewinn zu Stop-Loss

Auf der Basisperiode definiert der EA die Eröffnungsrichtung, während die Arbeitsperiode zur Definition des Eintrittspunkts verwendet wird.

Das Programm berechnet die Losgröße in Abhängigkeit vom Gesamtrisiko pro Position.

Der EA kann auch einen Take-Profit basierend auf dem spezifizierten Verhältnis von Gewinn zu Risiko (Parameter М) festlegen und eine Position auf Breakeven basierend auf dem spezifizierten Verhältnis Gewinn zu Stop-Loss (Break Even Parameter) verschieben.

Nachdem wir die Eingabeparameter beschrieben haben, deklarieren wir die Variablen für die Indikator-Handles und Arrays für die Zeitrahmen base_tf und work_tf:

//--- Deklarieren der Variablen der Handles der Indikatoren
int Fractal_base_tf,Fractal_work_tf;             //Handles des Indikators iFractals
int Sar_base_tf,Sar_work_tf;                     //Handle des Indikators iSar
//--- Deklarieren der Arrays für base_tf
double High_base_tf[],Low_base_tf[];             //Arrays der Hochs und Tiefs
double Close_base_tf[],Open_base_tf[];           //Arrays der Eröffnungs- und Schlusskurse
datetime Time_base_tf[];                         //Array der Eröffnungszeiten
double Sar_array_base_tf[];                      //Array der Indikatorwerte von iSar (Parabolic)
double FractalDown_base_tf[],FractalUp_base_tf[];//Array der Indikatorwerte von iFractals
//--- Deklarieren der Arrays für work_tf
double High_work_tf[],Low_work_tf[];
double Close_work_tf[],Open_work_tf[];
datetime Time_work_tf[];
double Sar_array_work_tf[];
double FractalDown_work_tf[],FractalUp_work_tf[];;

Der EA verwendet zwei Indikatoren: Die Fraktale zur Definition der Extrema und den Parabolic für das Nachziehen des Trailing-Stops Ich verwende den Parabolic auch, um den Einstiegspunkt im Arbeitszeitrahmen work_tf festzulegen.

Danach holen wir uns die Handles für die Indikatoren in der Funktion OnInit() und tragen die Anfangsdaten in die Arrays ein.

int OnInit()
  {
//--- Handle des Indikators iSar
   Sar_base_tf=iSAR(Symbol(),base_tf,sar_step,maximum_step);
   Sar_work_tf=iSAR(Symbol(),work_tf,sar_step,maximum_step);
//--- Handle des Indikators iFractals
   Fractal_base_tf=iFractals(Symbol(),base_tf);
   Fractal_work_tf=iFractals(Symbol(),work_tf);
//--- Festlegen der Laufrichtung der Arrays als Zeitreihen für base_tf
   ArraySetAsSeries(High_base_tf,true);
   ArraySetAsSeries(Low_base_tf,true);
   ArraySetAsSeries(Close_base_tf,true);
   ArraySetAsSeries(Open_base_tf,true);
   ArraySetAsSeries(Time_base_tf,true);;
   ArraySetAsSeries(Sar_array_base_tf,true);
   ArraySetAsSeries(FractalDown_base_tf,true);
   ArraySetAsSeries(FractalUp_base_tf,true);
//--- Erstbefüllung der Arrays für base_tf
   CopyHigh(Symbol(),base_tf,0,1000,High_base_tf);
   CopyLow(Symbol(),base_tf,0,1000,Low_base_tf);
   CopyClose(Symbol(),base_tf,0,1000,Close_base_tf);
   CopyOpen(Symbol(),base_tf,0,1000,Open_base_tf);
   CopyTime(Symbol(),base_tf,0,1000,Time_base_tf);
   CopyBuffer(Sar_base_tf,0,TimeCurrent(),1000,Sar_array_base_tf);
   CopyBuffer(Fractal_base_tf,0,TimeCurrent(),1000,FractalUp_base_tf);
   CopyBuffer(Fractal_base_tf,1,TimeCurrent(),1000,FractalDown_base_tf);
//--- Festlegen der Laufrichtung der Arrays als Zeitreihen für work_tf
   ArraySetAsSeries(High_work_tf,true);
   ArraySetAsSeries(Low_work_tf,true);
   ArraySetAsSeries(Close_work_tf,true);
   ArraySetAsSeries(Open_work_tf,true);
   ArraySetAsSeries(Time_work_tf,true);
   ArraySetAsSeries(Sar_array_work_tf,true);
   ArraySetAsSeries(FractalDown_work_tf,true);
   ArraySetAsSeries(FractalUp_work_tf,true);
//--- Erstbefüllung der Arrays für work_tf
   CopyHigh(Symbol(),work_tf,0,1000,High_work_tf);
   CopyLow(Symbol(),work_tf,0,1000,Low_work_tf);
   CopyClose(Symbol(),work_tf,0,1000,Close_work_tf);
   CopyOpen(Symbol(),work_tf,0,1000,Open_work_tf);
   CopyTime(Symbol(),work_tf,0,1000,Time_work_tf);
   CopyBuffer(Sar_work_tf,0,TimeCurrent(),1000,Sar_array_work_tf);
   CopyBuffer(Fractal_work_tf,0,TimeCurrent(),1000,FractalUp_work_tf);
   CopyBuffer(Fractal_work_tf,1,TimeCurrent(),1000,FractalDown_work_tf);

//---
   return(INIT_SUCCEEDED);
  }

Zuerst erhalten wir die Handles der Indikatoren, dann definieren wir die Laufrichtung der Arrays als Zeitreihe und tragen die Daten ein. Ich glaube, dass 100 Bars mehr als ausreichend sind für den EA.

2. Allgemeine Eigenschaften

Jetzt wenden wir uns der Funktion OnTick() zu.

Im Teil der "Allgemeinen Parameter" schreibe ich die Daten des Marktes und deklariere die Variablen für die Einstellungen der Positionen.

//+------------------------------------------------------------------+
//| 1. Allgemeinen Parameter (Start)                                 |
//+------------------------------------------------------------------+
//--- Marktdaten
//Anzahl der Dezimalstellen der Preise des Symbols
   int Digit=(int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS);
//Definieren der Kapazität der aktuellen Symbolpreise
   double f=1;
   if(Digit==5) {f=100000;}
   if(Digit==4) {f=10000;}
   if(Digit==3) {f=1000;}
   if(Digit==2) {f=100;}
   if(Digit==1) {f=10;}
//---
   double spread=SymbolInfoInteger(Symbol(),SYMBOL_SPREAD)/f;//Reduzieren der Spanne auf einen Dezimalwert unter Berücksichtigung der Preiskapazität
   double bid=SymbolInfoDouble(_Symbol,SYMBOL_BID);//Daten der Bid-Preise
   double ask=SymbolInfoDouble(_Symbol,SYMBOL_ASK);//Daten der Ask-Preise
   double CostOfPoint=SymbolInfoDouble(Symbol(),SYMBOL_TRADE_TICK_VALUE);//Tickdaten
//--- Lot-Berechnung einer Position
   double RiskSize_points;//Variable für die Größe des Stop-Loss der aktuellen Position
   double CostOfPoint_position;//Variable für den Pointpreis der aktuellen Position unter Berücksichtigung des Risikos je Position
   double Lot;//Variable der Lotgröße für die Positionseröffnung
   double SLPrice_sell,SLPrice_buy;//Variable für die Preislevel von Stop-Loss
//--- Variablen für die Datenspeicherung der Anzahl der Bars
   int bars_base_tf=Bars(Symbol(),base_tf);
   int bars_work_tf=Bars(Symbol(),work_tf);
//--- Variablen für die Datenspeicherung für die Position
   string P_symbol; //Symbol der Position
   int P_type,P_ticket,P_opentime;//Typ, Ticket und Zeitpunkt der Position
//+------------------------------------------------------------------+
//| 1. Allgemeinen Parameter (Ende)                                  |
//+------------------------------------------------------------------+ 

3. Aktualisierung der Arraydaten

Arrays wurden zunächst in der Funktion OnInit() befüllt, aber die Array-Daten sollten jederzeit aktuell bleiben. Das Füllen von Arrays bei jedem eingehenden Tick bedeutet, dass das System zu stark belastet wird, was die Arbeit erheblich verlangsamt. Daher ist es ratsam, die Arrays nachzufüllen, wenn eine neue Bar erscheint.

Dazu verwenden wir die folgende Struktur:

   static datetime LastBar_base_tf=0;//Variable zur Definition der neuen Bar
   datetime ThisBar_base_tf=(datetime)SeriesInfoInteger(_Symbol,base_tf,SERIES_LASTBAR_DATE);//Zeit der aktuellen Bar
   if(LastBar_base_tf!=ThisBar_base_tf)//wenn die Zeiten nicht übereinstimmen, ist eine neue Bar erschienen
     {
         //Arrays werden hier befüllt
     }

Bei diesem Ansatz gehen die Daten der Bar 0 verloren, daher habe ich separate Arrays für Daten der Bar mit dem Index 0 definiert.

Wir sollten auch die Arrays separat mit den fraktalen Daten aktualisieren. Sie sollten jedes Mal nachgefüllt werden, wenn die Extremwerte der Bar 0 höher oder niedriger als die beiden vorherigen sind.

Beispiele für das Befüllen von Arrays finden Sie unten.

1. Eintragungen in die Arrays, wenn eine neue Bar erscheint

Zuerst wird der Array befüllt, wenn eine neue Bar erscheint.

//+------------------------------------------------------------------+
//| 2.1 Befüllen der Arrays beim Erscheinen einer neue bar (Start)   |
//+------------------------------------------------------------------+
//--- für base_tf
//--- Deklarieren der Laufrichtung der Arrays als Zeitreihe
   ArraySetAsSeries(High_base_tf,true);
   ArraySetAsSeries(Low_base_tf,true);
   ArraySetAsSeries(Close_base_tf,true);
   ArraySetAsSeries(Open_base_tf,true);
   ArraySetAsSeries(Time_base_tf,true);
   ArraySetAsSeries(Sar_array_base_tf,true);
   ArraySetAsSeries(FractalDown_base_tf,true);
   ArraySetAsSeries(FractalUp_base_tf,true);
//--- Befüllen der Arrays
   static datetime LastBar_base_tf=0;//Variable zur Definition der neuen Bar
   datetime ThisBar_base_tf=(datetime)SeriesInfoInteger(_Symbol,base_tf,SERIES_LASTBAR_DATE);//Eröffnungszeit der aktuellen Bar
   if(LastBar_base_tf!=ThisBar_base_tf)//wenn die Zeiten nicht übereinstimmen, ist eine neue Bar erschienen
     {
      CopyHigh(Symbol(),base_tf,0,1000,High_base_tf);
      CopyLow(Symbol(),base_tf,0,1000,Low_base_tf);
      CopyClose(Symbol(),base_tf,0,1000,Close_base_tf);
      CopyOpen(Symbol(),base_tf,0,1000,Open_base_tf);
      CopyTime(Symbol(),base_tf,0,1000,Time_base_tf);
      CopyBuffer(Sar_base_tf,0,TimeCurrent(),1000,Sar_array_base_tf);
      CopyBuffer(Fractal_base_tf,0,TimeCurrent(),1000,FractalUp_base_tf);
      CopyBuffer(Fractal_base_tf,1,TimeCurrent(),1000,FractalDown_base_tf);
      LastBar_base_tf=ThisBar_base_tf;
     }
//--- für work_tf
//--- Deklarieren der Laufrichtung der Arrays als Zeitreihe
   ArraySetAsSeries(High_work_tf,true);
   ArraySetAsSeries(Low_work_tf,true);
   ArraySetAsSeries(Close_work_tf,true);
   ArraySetAsSeries(Open_work_tf,true);
   ArraySetAsSeries(Time_work_tf,true);
   ArraySetAsSeries(Sar_array_work_tf,true);
   ArraySetAsSeries(FractalDown_work_tf,true);
   ArraySetAsSeries(FractalUp_work_tf,true);
//--- Befüllen der Arrays
   static datetime LastBar_work_tf=0;//Variable zur Definition der neuen Bar
   datetime ThisBar_work_tf=(datetime)SeriesInfoInteger(_Symbol,work_tf,SERIES_LASTBAR_DATE);//Eröffnungszeit der aktuellen Bar
   if(LastBar_work_tf!=ThisBar_work_tf)//wenn die Zeiten nicht übereinstimmen, ist eine neue Bar erschienen
     {
      CopyHigh(Symbol(),work_tf,0,1000,High_work_tf);
      CopyLow(Symbol(),work_tf,0,1000,Low_work_tf);
      CopyClose(Symbol(),work_tf,0,1000,Close_work_tf);
      CopyOpen(Symbol(),work_tf,0,1000,Open_work_tf);
      CopyTime(Symbol(),work_tf,0,1000,Time_work_tf);
      CopyBuffer(Sar_work_tf,0,TimeCurrent(),1000,Sar_array_work_tf);
      CopyBuffer(Fractal_work_tf,0,TimeCurrent(),1000,FractalUp_work_tf);
      CopyBuffer(Fractal_work_tf,1,TimeCurrent(),1000,FractalDown_work_tf);
      LastBar_work_tf=ThisBar_work_tf;
     }
//+------------------------------------------------------------------+
//| 2.1 Befüllen der Arrays beim Erscheinen einer neue bar (Ende)    |
//+------------------------------------------------------------------+

2. Eintragungen der Daten in die Arrays der Bar 0

Daten der Bars mit dem Index 1 und höher bleiben nun jederzeit aktuell, während die Daten der Bar mit dem Index 0 noch veraltet sind. Ich habe separate Arrays zum Speichern von Daten der Bar 0 eingeführt:

//+------------------------------------------------------------------+
//| 2.2 Befüllen der Arrays mit Daten der Bar 0 (Start)              |
//+------------------------------------------------------------------+
//--- für base_tf
//--- Deklarieren der Arrays
   double High_base_tf_0[],Low_base_tf_0[];
   double Close_base_tf_0[],Open_base_tf_0[];
   datetime Time_base_tf_0[];
   double Sar_array_base_tf_0[];
//--- Deklarieren der Laufrichtung der Arrays als Zeitreihe
   ArraySetAsSeries(High_base_tf_0,true);
   ArraySetAsSeries(Low_base_tf_0,true);
   ArraySetAsSeries(Close_base_tf_0,true);
   ArraySetAsSeries(Open_base_tf_0,true);
   ArraySetAsSeries(Time_base_tf_0,true);
   ArraySetAsSeries(Sar_array_base_tf_0,true);
//--- Befüllen der Arrays
   CopyHigh(Symbol(),base_tf,0,1,High_base_tf_0);
   CopyLow(Symbol(),base_tf,0,1,Low_base_tf_0);
   CopyClose(Symbol(),base_tf,0,1,Close_base_tf_0);
   CopyOpen(Symbol(),base_tf,0,1,Open_base_tf_0);
   CopyTime(Symbol(),base_tf,0,1,Time_base_tf_0);
   CopyBuffer(Sar_base_tf,0,TimeCurrent(),1,Sar_array_base_tf_0);
//--- für work_tf
//--- Deklarieren der Arrays
   double High_work_tf_0[],Low_work_tf_0[];
   double Close_work_tf_0[],Open_work_tf_0[];
   datetime Time_work_tf_0[];
   double Sar_array_work_tf_0[];
//--- Deklarieren der Laufrichtung der Arrays als Zeitreihe
   ArraySetAsSeries(High_work_tf_0,true);
   ArraySetAsSeries(Low_work_tf_0,true);
   ArraySetAsSeries(Close_work_tf_0,true);
   ArraySetAsSeries(Open_work_tf_0,true);
   ArraySetAsSeries(Time_work_tf_0,true);
   ArraySetAsSeries(Sar_array_work_tf_0,true);
//--- Befüllen der Arrays
   CopyHigh(Symbol(),work_tf,0,1,High_work_tf_0);
   CopyLow(Symbol(),work_tf,0,1,Low_work_tf_0);
   CopyClose(Symbol(),work_tf,0,1,Close_work_tf_0);
   CopyOpen(Symbol(),work_tf,0,1,Open_work_tf_0);
   CopyTime(Symbol(),work_tf,0,1,Time_work_tf_0);
   CopyBuffer(Sar_work_tf,0,TimeCurrent(),1,Sar_array_work_tf_0);
//+------------------------------------------------------------------+
//| 2.2 Befüllen der Arrays mit Daten der Bar 0 (Ende)               |
//+------------------------------------------------------------------+

3. Aktualisierung der Daten der Fraktale

Die Arrays mit den Fraktalen müssen aktualisiert werden Jedes mal, wenn die Bar 0 höher oder niedriger als die vorherigen Bar ist, sollte der Array neu beschrieben werden:

//+------------------------------------------------------------------+
//| 2.3 Aktualisierung der Daten der Fraktale (Start)                |
//+------------------------------------------------------------------+
//--- für base_tf
   if(High_base_tf_0[0]>High_base_tf[1] && High_base_tf_0[0]>High_base_tf[2])
     {
      CopyBuffer(Fractal_base_tf,0,TimeCurrent(),1000,FractalUp_base_tf);
     }
   if(Low_base_tf_0[0]<Low_base_tf[1] && Low_base_tf_0[0]<Low_base_tf[2])
     {
      CopyBuffer(Fractal_base_tf,1,TimeCurrent(),1000,FractalDown_base_tf);
     }
//--- für work_tf
   if(High_work_tf_0[0]>High_work_tf[1] && High_work_tf_0[0]>High_work_tf[2])
     {
      CopyBuffer(Fractal_work_tf,0,TimeCurrent(),1000,FractalUp_work_tf);
     }
   if(Low_work_tf_0[0]<Low_work_tf[1] && Low_work_tf_0[0]<Low_work_tf[2])
     {
      CopyBuffer(Fractal_work_tf,1,TimeCurrent(),1000,FractalDown_work_tf);
     }
//+------------------------------------------------------------------+
//| 2.3 Aktualisierung der Daten der Fraktale (Ende)                 |
//+------------------------------------------------------------------+

4. Suche nach den Extrema

Kommen wir zurück zum Modell der Bewegungsfortsetzung. Schauen wir noch einmal auf die Abbildung 2.

Das Segment АВ ist die Hauptwelle, während ВС eine Korrekturwelle ist. Nach den Prinzipien der Modellerkennung sollte die Korrekturwelle immer mit einem Extremum enden, das ein Fraktal ist. Auf dem Bild ist es als С gekennzeichnet. Die Suche nach den Extrema sollte mit diesem Punkt beginnen, während der Rest anschließend konsequent erkannt wird. Zum Zeitpunkt der Eröffnung ist es jedoch wahrscheinlich, dass das gebildete (bestätigte) Fraktal fehlt. Daher müssen wir nach einer Situation suchen, in der sich das Balkenextremum über/unter den beiden vorherigen Balken befindet - hoch/tief eines solchen Balkens bildet den Punkt С. Beachten Sie auch, dass sich das Hoch/Tief der Korrekturbewegung (Punkt С) zum Zeitpunkt der Eingabe entweder auf einem Nullbalken oder auf einem Balken mit einem Index über Null befinden kann.

Die Tabelle 2 zeigt die Reihenfolge der Definition eines Extremums.

Tabelle 2. Definition eines Extremums

# Für einen Abwärtstrend Für einen Aufwärtstrend
1 Finden des Hochs der Korrekturbewegung (Punkt C) Finden des Tiefs der Korrekturbewegung (Punkt C)
2 Finden des nächsten oberen Extremums des Hochs der Korrekturbewegung (Punkt А) Finden des nächsten unteren Extremums des Tiefs der Korrekturbewegung (Punkt А)
3 Finden von Punkt B (Tief der Korrekturbewegung) zwischen den Punkten C und A Finden von Punkt B (Hoch der Korrekturbewegung) zwischen den Punkten C und A
1. Suche nach den Extrema bei einem Abwärtstrend
//+------------------------------------------------------------------+
//| 3.1 Suche nach Extrema im Abwärtstrend (Start)                   |
//+------------------------------------------------------------------+
//--- Deklarieren der Variablen
   int High_Corr_wave_downtrend_base_tf;//die Bar des Hochs der Korrekturbewegung (Punkt С)
   int UpperFractal_downtrend_base_tf;  //die nächste Bar mit einem oberen Extremum (Punkt А)
   int Low_Corr_wave_downtrend_base_tf; //die nächste Bar mit einem unteren Extremum (Punkt B)
//--- 
//--- Finden des Hochs der Korrekturbewegung (Punkt С)
   if(High_base_tf_0[0]>High_base_tf[1] && High_base_tf_0[0]>High_base_tf[2])
     {
      High_Corr_wave_downtrend_base_tf=0;
     }
   else
     {
      for(n=0; n<(bars_base_tf);n++)
        {
         if(High_base_tf[n]>High_base_tf[n+1] && High_base_tf[n]>High_base_tf[n+2])
            break;
        }
      High_Corr_wave_downtrend_base_tf=n;
     }
//--- 
//--- Finden des nächsten oberen Extremums, das Hoch, der Korrekturbewegung (Punkt А)
   for(n=High_Corr_wave_downtrend_base_tf+1; n<(bars_base_tf);n++)
     {
      // --- bei einem nicht leeren Wert die Schleife beenden
      if(FractalUp_base_tf[n]!=EMPTY_VALUE)
         break;
     }
   UpperFractal_downtrend_base_tf=n;
//---
//--- Finden von Punkt B (Tief der Korrekturbewegung) zwischen Punkt C und A
   int CountToFind_arrmin=UpperFractal_downtrend_base_tf-High_Corr_wave_downtrend_base_tf;
   Low_Corr_wave_downtrend_base_tf=ArrayMinimum(Low_base_tf,High_Corr_wave_downtrend_base_tf,CountToFind_arrmin);
//+------------------------------------------------------------------+
//| 3.1 Suche nach Extrema im Abwärtstrend (Ende)                    |
//+------------------------------------------------------------------+

2. Suche nach den Extrema bei einem Aufwärtstrend

//+------------------------------------------------------------------+
//| 3.2 Suche nach Extrema im Aufwärtstrend (Start)                  |
//+------------------------------------------------------------------+
//--- Deklarieren der Variablen
   int Low_Corr_wave_uptrend_base_tf;//die nächste Bar mit einem unteren Extremum (Punkt С)
   int LowerFractal_uptrend_base_tf;  //die nächste Bar mit einem unteren Extremum (Punkt А)
   int High_Corr_wave_uptrend_base_tf; //das Hoch der Korrekturbewegung (Punkt B)
//--- 
//--- Finden des Tiefs der Korrekturbewegung (Punkt C)
   if(Low_base_tf_0[0]<Low_base_tf[1] && Low_base_tf_0[0]<Low_base_tf[2])
     {
      Low_Corr_wave_uptrend_base_tf=0;
     }
   else
     {
      //Suche nach einer Korrektur
      for(n=0; n<(bars_base_tf);n++)
        {
         if(Low_base_tf[n]<Low_base_tf[n+1] && Low_base_tf[n]<Low_base_tf[n+2])
            break;
        }
      Low_Corr_wave_uptrend_base_tf=n;
     }
//---
//--- Vom Tief der Korrekturbewegung Finden das nächste tiefer Extremum (Punkt А)
   for(n=Low_Corr_wave_uptrend_base_tf+1; n<(bars_base_tf);n++)
     {
      if(FractalDown_base_tf[n]!=EMPTY_VALUE)
         break;
     }
   LowerFractal_uptrend_base_tf=n;
//---
//--- Finden von Punkt B (Hoch der Korrekturbewegung) zwischen Punkt C und A
int CountToFind_arrmax=LowerFractal_uptrend_base_tf-Low_Corr_wave_uptrend_base_tf;
High_Corr_wave_uptrend_base_tf=ArrayMaximum(High_base_tf,Low_Corr_wave_uptrend_base_tf,CountToFind_arrmax);
//+------------------------------------------------------------------+
//| 3.2 Suche nach Extrema im Aufwärtstrend (Ende)                   |
//+------------------------------------------------------------------+

3. Reduktion der Hochs/Tiefs der Korrekturwellen in vereinigte Variablen

Damit haben wir die Indices der Bars mit den Extrema gefunden. Aber wir müssen uns auch auf die Preis- und Zeitwerte der Bars beziehen. Um auf hohe oder niedrige Werte von Korrekturwellen zu verweisen, müssen wir zwei verschiedene Arrays verwenden, da sich das Hoch oder Tief der Korrekturwelle entweder auf der Nullindexleiste oder auf einer Stange mit einem Index über Null befinden kann. Dies ist für die Arbeit nicht sehr bequem, daher ist es sinnvoller, ihre Werte mit dem Operator if in gemeinsame Variablen zu bringen.

//+----------------------------------------------------------------------------------+
//| 3.3 Übertragen der Hoch/Tiefs der Korrekturwelle in gemeinsame Variablen (Start) |
//+----------------------------------------------------------------------------------+
//--- Deklarieren der Variablen
   double High_Corr_wave_downtrend_base_tf_double,Low_Corr_wave_uptrend_base_tf_double;
   datetime High_Corr_wave_downtrend_base_tf_time,Low_Corr_wave_uptrend_base_tf_time;
//--- für High_Corr_wave_downtrend_base_tf
   if(High_Corr_wave_downtrend_base_tf==0)
     {
      High_Corr_wave_downtrend_base_tf_double=High_base_tf_0[High_Corr_wave_downtrend_base_tf];
      High_Corr_wave_downtrend_base_tf_time=Time_base_tf_0[High_Corr_wave_downtrend_base_tf];
     }
   else
     {
      High_Corr_wave_downtrend_base_tf_double=High_base_tf[High_Corr_wave_downtrend_base_tf];
      High_Corr_wave_downtrend_base_tf_time=Time_base_tf[High_Corr_wave_downtrend_base_tf];
     }
//-- für Low_Corr_wave_uptrend_base_tf
   if(Low_Corr_wave_uptrend_base_tf==0)
     {
      Low_Corr_wave_uptrend_base_tf_double=Low_base_tf_0[Low_Corr_wave_uptrend_base_tf];
      Low_Corr_wave_uptrend_base_tf_time=Time_base_tf_0[Low_Corr_wave_uptrend_base_tf];
     }
   else
     {
      Low_Corr_wave_uptrend_base_tf_double=Low_base_tf[Low_Corr_wave_uptrend_base_tf];
      Low_Corr_wave_uptrend_base_tf_time=Time_base_tf[Low_Corr_wave_uptrend_base_tf];
     }
//+---------------------------------------------------------------------------------+
//| 3.3 Übertragen der Hoch/Tiefs der Korrekturwelle in gemeinsame Variablen (Ende) |
//+---------------------------------------------------------------------------------+

So werden die Werte von Hoch/Tief und der Zeit der Korrekturwellen in Variablen geschrieben. Es ist nicht erforderlich, jedes Mal auf verschiedene Arrays zuzugreifen.

Fasst man die Arbeiten zur Suche nach Extremen zusammen, stellt sich heraus, dass die Punkte A, B und C nach dem Konzept der Modellerkennung gefunden wurden (siehe Tabellen 4 und 5).

Tabelle 4. Werte der Punkte А, В und С für einen Abwärtstrend

Parameter Werte von Punkt A Werte von Punkt B Werte von Punkt V
Bar-Index UpperFractal_downtrend_base_tf Low_Corr_wave_downtrend_base_tf High_Corr_wave_downtrend_base_tf
Zeitpunkt Time_base_tf[UpperFractal_downtrend_base_tf] Time_base_tf[Low_Corr_wave_downtrend_base_tf] High_Corr_wave_downtrend_base_tf_time
Preis High_base_tf[UpperFractal_downtrend_base_tf] Low_base_tf[Low_Corr_wave_downtrend_base_tf] High_Corr_wave_downtrend_base_tf_double

Tabelle 5. Werte der Punkte А, В und С für einen Aufwärtstrend

Parameter Werte von Punkt A Werte von Punkt B Werte von Punkt V
Bar-Index LowerFractal_uptrend_base_tf High_Corr_wave_uptrend_base_tf Low_Corr_wave_uptrend_base_tf
Zeitpunkt Time_base_tf[LowerFractal_uptrend_base_tf] Time_base_tf[High_Corr_wave_uptrend_base_tf] Low_Corr_wave_uptrend_base_tf_time
Preis Low_base_tf[LowerFractal_uptrend_base_tf] High_base_tf[High_Corr_wave_uptrend_base_tf] Low_Corr_wave_uptrend_base_tf_double


5. Modell der Erkennungsbedingungen

In diesem Abschnitt werde ich nur die notwendigsten Rahmenbedingungen beschreiben, die für das in diesem Artikel beschriebene Modell charakteristisch sind.

Tabelle 6. Minimaler Satz von Bedingungen für das Erkennen des Modell der Bewegungsfortsetzung

# Abwärtsbedingungen Aufwärtsbedingungen
1 Korrekturwelle Hoch (Punkt C) liegt unter dem Hoch des darauf folgenden Extremums (Punkt А). Das Tief der Korrekturwelle (Punkt C) liegt über dem Tief des darauf folgenden Extremums (Punkt А).
2 Korrekturwelle Index des Tiefs (Punkt В) überschreitet Index des Hochs (Punkt С) Korrekturwelle Index des Hochs (Punkt В) überschreitet den Index des Tiefs (Punkt С)
3 Dauer der Korrekturbewegung von 2 bis 6 Bar (Anzahl der Bars ab Punkt В) Dauer der Korrekturbewegung von 2 bis 6 Bar (Anzahl der Bars ab Punkt В)

Der Code zur Beschreibung der Modellerkennungsbedingungen ist nachfolgend aufgeführt. Die Bedingungen werden in den beiden logischen Variablen gesammelt: eine für einen Abwärtstrend, eine andere für einen Aufwärtstrend:

//+------------------------------------------------------------------+
//| 4. Bedingungen der Modelerkennung (Start)                        |
//+------------------------------------------------------------------+
//--- für einen Abwärtstrend
/*1. Correction wave High (point C) is below the high of the extremum that follows it (point А)*/
/*2. Correction wave low index (point В) exceeds high index (point С)*/
/*3. Correction movement duration from 2 to 6 bars (number of bars from point В)*/
   bool Model_downtrend_base_tf=(
                                 /*1.*/High_Corr_wave_downtrend_base_tf_double<High_base_tf[UpperFractal_downtrend_base_tf] && 
                                 /*2.*/Low_Corr_wave_downtrend_base_tf>High_Corr_wave_downtrend_base_tf && 
                                 /*3.*/Low_Corr_wave_downtrend_base_tf>=1 && Low_Corr_wave_downtrend_base_tf<=6
                                 );
//--- für einen Aufwärtstrend
/*1. Correction wave low (point C) is above the low of the extremum that follows it (point А)*/
/*2. Correction wave high index (point В) exceeds low index (point С)*/
/*3. Correction movement duration from 2 to 6 bars (number of bars from point В)*/
   bool Model_uptrend_base_tf=(
                               /*1.*/Low_Corr_wave_uptrend_base_tf_double>Low_base_tf[LowerFractal_uptrend_base_tf] && 
                               /*2.*/High_Corr_wave_uptrend_base_tf>Low_Corr_wave_uptrend_base_tf && 
                               /*3.*/High_Corr_wave_uptrend_base_tf>=1 && High_Corr_wave_uptrend_base_tf<=6
                               );
//+------------------------------------------------------------------+
//| 4. Bedingungen der Modelerkennung (Ende)                         |
//+------------------------------------------------------------------+

6. Erstellen der Steuerelemente

Der EA sollte mindestens drei Prüfungen durchführen.

Die ersten beiden Prüfungen dienen der rechtzeitigen Eröffnung. Die dritte bestätigt, dass innerhalb eines Modells nur eine Position eröffnet wird, d.h. es wird sichergestellt, dass es keine doppelten Positionen gibt.

Siehe Abb. 3. Gestrichelte Linien markieren die Position der Eröffnungsbereiche, in denen sich die Einstiegspunkte befinden - irgendwo zwischen den Punkten В und С. Es wird nicht empfohlen, später zu eröffnen, wenn der Preis das Niveau von Punkt B durchbricht, da dies die Risiken erhöht. Dies ist die erste Überprüfung, die das Programm durchführen sollte.

Modell der Bewegungsfortsetzung

Abb. 3. Modell der Bewegungsfortsetzung mit AUDJPY H4

In einigen Fällen kann der Preis den Punkt В durchbrechen und in den Eröffnungsbereich der Position zurückkehren. Diese Situation kann nicht als Handelssituation betrachtet werden. Dies ist die zweite Überprüfung, die das Programm durchführen sollte. Um Mehrfachbesetzungen zu vermeiden, müssen wir schließlich die Einschränkung einführen: 1 Modell - 1 eine offene Position. Dies ist die dritte Überprüfung, die das Programm durchführen sollte.

1. Bilden eines Einstiegspunktes im Bereich der Positionseröffnung

Hier ist alles ganz einfach: Für einen Verkauf sollte der Bid-Preis über dem Tief der Korrekturbewegung liegen (Punkt В). Für einen Kauf sollte der Bid-Preis unter dem Hoch der Korrekturbewegung sein (Punkt В).

//+------------------------------------------------------------------------+
//| 5.1 Die Einstiegskontrolle im Bereich der Positionsöffnung (Start)     |
//+------------------------------------------------------------------------+
//--- für einen Abwärtstrend
bool First_downtrend_control_bool=(bid>=Low_base_tf[Low_Corr_wave_downtrend_base_tf]);
//--- für einen Aufwärtstrend
bool First_uptrend_control_bool=(bid<=High_base_tf[High_Corr_wave_uptrend_base_tf]);
//+------------------------------------------------------------------------+
//| 5.1 Die Einstiegskontrolle im Bereich der Positionsöffnung (Ende)      |
//+------------------------------------------------------------------------+

2. Kontrolle einer Preiskorrektur in den Bereich der Positionseröffnung

Um diese Kontrolle zu implementieren, sollten wir die Bar mit dem tiefsten "Tief" (für Verkäufe) oder die Bar mit dem höchsten "Hoch" (für Käufe) definieren, beginnend mit dem aktuellen Index und bis zur Bar mit Hoch/Tief der Korrekturbewegung (Punkt В). Um dies zu erreichen, wird für das Verkaufsmodell die Funktion ArrayMinimum() und für das Kaufmodell die Funktion ArrayMaximum() verwendet.

Weiterhin werden die Indizes verglichen, der Index von Hoch/Tief der Korrekturbewegung (Punkt В) und die Indizes, die durch die Funktionen ArrayMinimum() und ArrayMaximum() ermittelt werden. Wenn sie übereinstimmen, gab es keinen niedrigen/hohen Durchbruch der Korrekturbewegung, und der gesamte Fall kann als eine Handelssituation betrachtet werden. Wenn die Indizes nicht übereinstimmen, hat die Bewegung früher begonnen, und es ist zu spät, eine Position zu eröffnen.

//+------------------------------------------------------------------------------+
//| 5.2 Steuerung einer Preiskorrektur im Bereich der Positionsöffnung (Start)   |
//+------------------------------------------------------------------------------+
//--- für einen Abwärtstrend
//Finden der Bar mit dem Tief zwischen der Bar 0 un dem Tief der Korrekturbewegung
   int Second_downtrend_control_int=ArrayMinimum(Low_base_tf,0,Low_Corr_wave_downtrend_base_tf+1);
//wenn das Tief der aktuellen Bar unter dem Tief der Korrekturbewegung liegt
   if(Low_base_tf_0[0]<Low_base_tf[Second_downtrend_control_int])
     {
      Second_downtrend_control_int=0; //das heißt, das Minimum ist auf Bar 0
     }
//Wenn die Bar mit dem niedrigsten Preis und der Korrekturbewegung niedrigste Übereinstimmung hat, ist dies dieselbe Bar.
//Das bedeutet, dass sich der Preis nicht über den Bereich der Positionsöffnung hinaus bewegt hat.
   bool Second_downtrend_control_bool=(Second_downtrend_control_int==Low_Corr_wave_downtrend_base_tf);
//---
//--- für einen Aufwärtstrend
//Finden der Bar mit dem Hoch zwischen der Bar 0 und dem Hoch der Korrekturbewegung
   int Second_uptrend_control_int=ArrayMaximum(High_base_tf,0,High_Corr_wave_uptrend_base_tf+1);
   //wenn das Hoch der aktuellen Bar das Hoch der Korrekturbewegung überschreitet
   if(High_base_tf_0[0]>High_base_tf[Second_uptrend_control_int])
     {
      Second_uptrend_control_int=0;//this means maximum on bar 0
     }
//Wenn die Bar mit dem Hoch gleich dem Hoch der Korrekturbewegung ist, ist das dieselbe Bar.
//Das bedeutet, dass sich der Preis nicht über den Bereich der Positionsöffnung hinaus bewegt hat.
   bool Second_uptrend_control_bool=(Second_uptrend_control_int==High_Corr_wave_uptrend_base_tf);
//+-----------------------------------------------------------------------------+
//| 5.2 Steuerung einer Preiskorrektur im Bereich der Positionsöffnung (Ende)   |
//+-----------------------------------------------------------------------------+

3. Eliminieren von doppelten Positionen im Singlemodell

Mit dieser Kontrolle wird die Anzahl der geöffneten Positionen begrenzt. Die Idee dahinter: ein Modell - eine offene Position. Offene Positionen werden einzeln analysiert. Wenn eine Position auf dem aktuellen Chart geöffnet wird, wird die Extrem-Bar definiert, der dieser Position vom Einstiegspunkt aus am nächsten liegt - Korrekturbewegung Hoch/Tief (Punkt С vom Einstiegspunkt) abhängig von der Handelsart.

Danach wird die Zeit der erfassten Bar - Korrekturbewegung Hoch/Tief (Punkt С vom Einstiegspunkt) - mit der Zeit der aktuellen Korrekturbewegung Hoch/Tief (aktueller Punkt С) verglichen. Wenn sie übereinstimmen, sollte keine Position geöffnet werden, da es keine Position gibt, die diesem Modell entspricht.

Erstellen der Verkaufskontrolle:

//+---------------------------------------------------------------------------+
//| 5.3.1 Für den Verkauf (Start)                                             |
//+---------------------------------------------------------------------------+
//--- Deklarieren der Variablen
   int Bar_sell_base_tf,High_Corr_wave_downtrend_base_tf_sell;
   bool Third_downtrend_control_bool=false;
//--- Iterieren über die offenen Position
   if(PositionsTotal()>0)
     {
      for(i=0;i<=PositionsTotal();i++)
        {
         if(PositionGetTicket(i))
           {
            //--- Definieren des Symbols der Position, der Zeit und des Typs
            P_symbol=string(PositionGetString(POSITION_SYMBOL));
            P_type=int(PositionGetInteger(POSITION_TYPE));
            P_opentime=int(PositionGetInteger(POSITION_TIME));
            //--- wenn das Symbol einer Position mit dem aktuellen Chart übereinstimmt und die Handelsrichtung ist "sell"
            if(P_symbol==Symbol() && P_type==1)
              {
               //--- Finden der Bar der Positionseröffnung
               Bar_sell_base_tf=iBarShift(Symbol(),base_tf,P_opentime);
               //--- Suche nach der Korrekturbewegung vom Hoch
               //Wenn die Positionseröffnung auf der aktuellen Bar passierte,
               if(Bar_sell_base_tf==0)
                 {
                  //und die aktuellen Bar ein Extremum hat
                  if(High_base_tf_0[Bar_sell_base_tf]>High_base_tf[Bar_sell_base_tf+1] && High_base_tf_0[Bar_sell_base_tf]>High_base_tf[Bar_sell_base_tf+2])
                    {
                     High_Corr_wave_downtrend_base_tf_sell=Bar_sell_base_tf;//das Hoch der Korrekturbewegung ist gleich der aktuellen Bar
                    }
                  else
                    {
                     //Wenn die aktuellen Bar kein Extremum hat, starten wir die Schleife, um ein Extremum zu suchen
                     for(n=Bar_sell_base_tf; n<(bars_base_tf);n++)
                       {
                        if(High_base_tf[n]>High_base_tf[n+1] && High_base_tf[n]>High_base_tf[n+2])//wenn das Extremum gefunden wurde
                           break;//Abbrechen der Schleife
                       }
                     High_Corr_wave_downtrend_base_tf_sell=n;
                    }
                  //--- Beschreiben der Kontrollbedingungen
                  Third_downtrend_control_bool=(
                                                /*1. Zeitpunkt des Hochs der Korrekturbewegung von der Positionseröffnung
                                                 stimmt mit dem Zeitpunkt des Hochs der aktuellen Korrekturbewegung*/Time_base_tf[High_Corr_wave_downtrend_base_tf_sell]==High_Corr_wave_downtrend_base_tf_time
                                                );
                 }
               //--- wenn die Position nicht auf der aktuellen Bar eröffnet wurde
               if(Bar_sell_base_tf!=0 && Bar_sell_base_tf!=1000)
                 {
                  //--- starte die Schleife für die Ermittlung der Bar mit dem Extremum
                  for(n=Bar_sell_base_tf; n<(bars_base_tf);n++)
                    {
                     //--- wenn das Extremum gefunden wurde
                     if(High_base_tf[n]>High_base_tf[n+1] && High_base_tf[n]>High_base_tf[n+2])
                        break;//Abbrechen der Schleife
                    }
                  High_Corr_wave_downtrend_base_tf_sell=n;
                 }
               Third_downtrend_control_bool=(
                                             /*1. Zeitpunkt des Hochs der Korrekturbewegung von der Positionseröffnung
                                                 stimmt mit dem Zeitpunkt des Hochs der aktuellen Korrekturbewegung*/Time_base_tf[High_Corr_wave_downtrend_base_tf_sell]==High_Corr_wave_downtrend_base_tf_time
                                             );
              }
           }
        }
     }
//+---------------------------------------------------------------------------+
//| 5.3.1 Für den Verkauf (Ende)                                              |
//+---------------------------------------------------------------------------+
Erstellen der Kaufkontrolle:
//+---------------------------------------------------------------------------+
//| 5.3.1 Für den Kauf (Start)                                                |
//+---------------------------------------------------------------------------+
//--- Deklarieren der Variablen
   int Bar_buy_base_tf,Low_Corr_wave_uptrend_base_tf_buy;
   bool Third_uptrend_control_bool=false;
//--- Iterieren über die offenen Position
   if(PositionsTotal()>0)
     {
      for(i=0;i<=PositionsTotal();i++)
        {
         if(PositionGetTicket(i))
           {
            //Definieren von Symbol, Typ und Zeitpunkt der Position
            P_symbol=string(PositionGetString(POSITION_SYMBOL));
            P_type=int(PositionGetInteger(POSITION_TYPE));
            P_opentime=int(PositionGetInteger(POSITION_TIME));
            //wenn das Symbol einer Position mit dem aktuellen Chart übereinstimmt und die Handelsrichtung ist "buy"
            if(P_symbol==Symbol() && P_type==0)
              {
               //Finden der Bar auf der die Position eröffnet wurde
               Bar_buy_base_tf=iBarShift(Symbol(),base_tf,P_opentime);
               //Suche nach dem Tief der Korrekturbewegung
               //Wenn die Positionseröffnung auf der aktuellen Bar passierte,
               if(Bar_buy_base_tf==0)
                 {
                 //und die aktuellen Bar ein Extremum hat
                  if(Low_base_tf_0[Bar_buy_base_tf]<Low_base_tf[Bar_buy_base_tf+1] && Low_base_tf_0[Bar_buy_base_tf]<Low_base_tf[Bar_buy_base_tf+2])
                    {
                     Low_Corr_wave_uptrend_base_tf_buy=Bar_buy_base_tf;
                    }
                  else
                    {
                    //Wenn die aktuellen Bar kein Extremum hat, starten wir die Schleife, um ein Extremum zu suchen
                     for(n=Bar_buy_base_tf; n<(bars_base_tf);n++)
                       {
                        if(Low_base_tf[n]<Low_base_tf[n+1] && Low_base_tf[n]<Low_base_tf[n+2])//wenn das Extremum gefunden wurde
                           break;//Abbrechen der Schleife
                       }
                     Low_Corr_wave_uptrend_base_tf_buy=n;
                    }
                  //--- Beschreiben der Kontrollbedingungen  
                  Third_uptrend_control_bool=(
                                               /*1. Zeitpunkt des Tiefs der Korrekturbewegung gefunden von der Positionseröffnung
                                                 passt zum Zeitpunkt des Tiefs der Korrekturbewegung*/Time_base_tf[Low_Corr_wave_uptrend_base_tf_buy]==Low_Corr_wave_uptrend_base_tf_time
                                               );
                 }
               //--- wenn die Position nicht auf der aktuellen Bar eröffnet wurde
               if(Bar_buy_base_tf!=0 && Bar_buy_base_tf!=1000)
                 {
                  //--- starte die Schleife für die Ermittlung der Bar mit dem Extremum
                  for(n=Bar_buy_base_tf; n<(bars_base_tf);n++)
                    {
                     //--- wenn das Extremum gefunden wurde
                     if(Low_base_tf[n]<Low_base_tf[n+1] && Low_base_tf[n]<Low_base_tf[n+2])
                        break;//Abbrechen der Schleife
                    }
                  Low_Corr_wave_uptrend_base_tf_buy=n;
                 }
                 //--- Beschreiben der Kontrollbedingungen  
               Third_uptrend_control_bool=(
                                            /*1. Zeitpunkt des Tiefs der Korrekturbewegung gefunden von der Positionseröffnung
                                                 passt zum Zeitpunkt des Tiefs der Korrekturbewegung*/Time_base_tf[Low_Corr_wave_uptrend_base_tf_buy]==Low_Corr_wave_uptrend_base_tf_time
                                            );
              }
           }
        }
     }
//+---------------------------------------------------------------------------+
//| 5.3.1 Für den Kauf (Ende)                                                 |
//+---------------------------------------------------------------------------+

7. Beschreiben der Bedingungen für die Positionseröffnung

Der Einstiegspunkt sollte auf der Arbeitsperiode definiert werden - work_tf. Dies ist notwendig, um rechtzeitig in den Markt zu gelangen und, wenn möglich, das Risiko in Punkten zu reduzieren. Die Indikatorwerte der Parabolic werden als Signal verwendet: Wenn der Indikatorwert auf dem aktuellen Balken über dem Hoch des aktuellen Balken liegt, während auf dem vorherigen Balken der Indikatorwert niedriger ist als das Tief des gleichen Balkens, dann ist es Zeit zu verkaufen. Beim Kauf wird es umgekehrt gemacht.

//+------------------------------------------------------------------+
//| 6. Beschreibung der Markteintrittsbedingungen (Ende)             |
//+------------------------------------------------------------------+
//--- Verkaufen
   bool PointSell_work_tf_bool=(
                                /*1. Bar 1 low exceeds iSar[1]*/Low_work_tf[1]>Sar_array_work_tf[1] && 
                                /*2. Bar 0 high is lower than iSar[0]*/High_work_tf_0[0]<Sar_array_work_tf_0[0]
                                );
//--- Kaufen
   bool PointBuy_work_tf_bool=(
                               /*1. Das Hoch der Bar 1 ist kleiner als iSar*/High_work_tf[1]<Sar_array_work_tf[1] && 
                               /*2. das Tief der Bar 0 ist größer als iSar[0]*/Low_work_tf_0[0]>Sar_array_work_tf_0[0]
                               );
//+------------------------------------------------------------------+
//| 6. Beschreibung der Markteintrittsbedingungen (Ende)             |
//+------------------------------------------------------------------+

8. Handelsbedingungen

Jetzt kombinieren wir alle vorher erstellen Bedingungen und Kontrollen in einer einzigen logischen Variablen.

//+------------------------------------------------------------------+
//| 7. Bedingung der Handelsbedingungen (Start)                      |
//+------------------------------------------------------------------+
//--- Verkaufen
   bool OpenSell=(
                  /*1. Model gebildet*/Model_downtrend_base_tf==true && 
                  /*2. Kontrolle 1 erlaubt die Positionseröffnung*/First_downtrend_control_bool==true && 
                  /*3. Kontrolle 2 erlaubt die Positionseröffnung*/Second_downtrend_control_bool==true && 
                  /*4. Kontrolle 3 erlaubt die Positionseröffnung*/Third_downtrend_control_bool==false && 
                  /*5. Einstiegspunkt für work_tf*/PointSell_work_tf_bool==true
                  );
//--- Verkaufen
   bool OpenBuy=(
                 /*1. Modell gebildet*/Model_uptrend_base_tf==true && 
                 /*2. Kontrolle 1 erlaubt die Positionseröffnung*/First_uptrend_control_bool==true && 
                 /*3. Kontrolle 2 erlaubt die Positionseröffnung*/Second_uptrend_control_bool==true && 
                 /*4. Kontrolle 3 erlaubt die Positionseröffnung*/Third_uptrend_control_bool==false && 
                 /*5. Einstiegspunkt für work_tf*/PointBuy_work_tf_bool==true
                 );
//+------------------------------------------------------------------+
//| 7. Bedingung der Handelsbedingungen (Ende)                       |
//+------------------------------------------------------------------+

9. Arbeiten mit den Handelsoperationen

Die Arbeit mit den Handelsoperationen kann folgendermaßen aufgeteilt werden

  • Einstellungen der Positionen
  • Erstellen von Take-Profit
  • Verschieben der Position auf Breakeven.

1. Positionseinstellungen

//+------------------------------------------------------------------+
//| 8. Working with trading operations (start)                       |
//+------------------------------------------------------------------+
//--- Definieren von Stop-Loss
   SLPrice_sell=High_Corr_wave_downtrend_base_tf_double+spread;
   SLPrice_buy=Low_Corr_wave_uptrend_base_tf_double-spread;
//+------------------------------------------------------------------+
//| 8.1 Einstellungen der Positionen (Start)                         |
//+------------------------------------------------------------------+
//--- Verkaufen
   if(OpenSell==true)
     {
      RiskSize_points=(SLPrice_sell-bid)*f;//Definieren von SL in Points als Ganzzahl
      if(RiskSize_points==0)//Prüfen auf Division durch Null
        {
         RiskSize_points=1;
        }
      CostOfPoint_position=SummRisk/RiskSize_points;//Definieren des Positionspreises in Points unter Berücksichtigung von SL
      Lot=CostOfPoint_position/CostOfPoint;//Lotberechnung für die Positionseröffnung
      //Positionseröffnung
      trade.PositionOpen(_Symbol,ORDER_TYPE_SELL,NormalizeDouble(Lot,2),bid,NormalizeDouble(SLPrice_sell,5),0,"");
     }
//--- Kaufen
   if(OpenBuy==true)
     {
      RiskSize_points=(bid-SLPrice_buy)*f;//Definieren von SL in Points als Ganzzahl
      if(RiskSize_points==0)//Prüfen auf Division durch Null
        {
         RiskSize_points=1;
        }
      CostOfPoint_position=SummRisk/RiskSize_points;//Definieren des Positionspreises in Points unter Berücksichtigung von SL
      Lot=CostOfPoint_position/CostOfPoint;//Lotberechnung für die Positionseröffnung
      //Positionseröffnung
      trade.PositionOpen(_Symbol,ORDER_TYPE_BUY,NormalizeDouble(Lot,2),ask,NormalizeDouble(SLPrice_buy,5),0,"");
     }
//+------------------------------------------------------------------+
//| 8.1 Einstellungen der Positionen (Ende)                          |
//+------------------------------------------------------------------+

2. Take-Profit einstellen

//+------------------------------------------------------------------+
//| 8.2 Einstellungen von Take-Profit (start)                        |
//+------------------------------------------------------------------+
   if(TP_mode==true)
     {
      if(PositionsTotal()>0)
        {
         for(i=0;i<=PositionsTotal();i++)
           {
            if(PositionGetTicket(i))
              {
              //Abfrage der Positionswerte
               SL_double=double (PositionGetDouble(POSITION_SL));
               OP_double=double (PositionGetDouble(POSITION_PRICE_OPEN));
               TP_double=double (PositionGetDouble(POSITION_TP));
               P_symbol=string(PositionGetString(POSITION_SYMBOL));
               P_type=int(PositionGetInteger(POSITION_TYPE));
               P_profit=double (PositionGetDouble(POSITION_PROFIT));
               P_ticket=int (PositionGetInteger(POSITION_TICKET));
               P_opentime=int(PositionGetInteger(POSITION_TIME));
               if(P_symbol==Symbol())
                 {
                  if(P_type==0 && TP_double==0)
                    {
                     double SL_size_buy=OP_double-SL_double;//Definieren von SL in Points
                     double TP_size_buy=SL_size_buy*M;//Multiplizieren von SL mit dem Verhältnis des Eingabeparameters
                     double TP_price_buy=OP_double+TP_size_buy;//Definieren des Levels von TP
                     //Modifikation einer Position
                     trade.PositionModify(PositionGetInteger(POSITION_TICKET),SL_double,NormalizeDouble(TP_price_buy,5));
                    }
                  if(P_type==1 && TP_double==0)
                    {
                     double SL_size_sell=SL_double-OP_double;//Definieren von SL in Points
                     double TP_size_sell=SL_size_sell*M;//Multiplizieren von SL mit dem Verhältnis des Eingabeparameters
                     double TP_price_sell=OP_double-TP_size_sell;//Definieren des Levels von TP
                     //Modifikation einer Position
                     trade.PositionModify(PositionGetInteger(POSITION_TICKET),SL_double,NormalizeDouble(TP_price_sell,5));
                    }
                 }
              }
           }
        }
     }
//+------------------------------------------------------------------+
//| 8.2 Einstellungen von Take-Profit (Ende)                         |
//+------------------------------------------------------------------+

3. Position auf Breakeven bewegen

//+------------------------------------------------------------------+
//| 8.3 Verschieben der Position auf Breakeven (Start)               |
//+------------------------------------------------------------------+
   double Size_Summ=breakeven*SummRisk;//Definieren des Gewinnlevels, bei dem die Position auf Breakeven verschoben werden soll
   if(Breakeven_mode==true && breakeven!=0)
     {
      if(PositionsTotal()>0)
        {
         for(i=0;i<=PositionsTotal();i++)
           {
            if(PositionGetTicket(i))
              {
              //Abfrage der Positionswerte
               SL_double=double (PositionGetDouble(POSITION_SL));
               OP_double=double (PositionGetDouble(POSITION_PRICE_OPEN));
               TP_double=double (PositionGetDouble(POSITION_TP));
               P_symbol=string(PositionGetString(POSITION_SYMBOL));
               P_type=int(PositionGetInteger(POSITION_TYPE));
               P_profit=double (PositionGetDouble(POSITION_PROFIT));
               P_ticket=int (PositionGetInteger(POSITION_TICKET));
               P_opentime=int(PositionGetInteger(POSITION_TIME));
               if(P_symbol==Symbol())
                 {
                  if(P_type==0 && P_profit>=Size_Summ && SL_double<OP_double)
                    {
                     trade.PositionModify(PositionGetInteger(POSITION_TICKET),OP_double,TP_double);
                    }
                  if(P_type==1 && P_profit>=Size_Summ && SL_double>OP_double)
                    {
                     trade.PositionModify(PositionGetInteger(POSITION_TICKET),OP_double,TP_double);
                    }
                 }
              }
           }
        }
     }
//+------------------------------------------------------------------+
//| 8.3 Verschieben der Position auf Breakeven (Ende)                |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| 8. Die Arbeit mit den Handelsoperationen (Ende)                  |
//+------------------------------------------------------------------+

5. Sammeln der statistischen Daten

Als erstes muss entschieden werden, welche Daten erfasst werden sollen:

  1. Symbol;
  2. Typ des Deals;
  3. Eröffnungszeit;
  4. Eröffnungspreis;
  5. Stop-Loss Level;
  6. Stop-Loss Größe;
  7. Maximaler Gewinn Level;
  8. Maximaler Gewinn Größe;
  9. Positionsdauer.

Es ist notwendig, davon auszugehen, dass der maximale Gewinnpunkt das Hoch/Tief des ersten oberen/unteren Fraktals auf der Hauptperiode ist, die nach dem Balken gebildet wurde, an dem die Position eröffnet wurde.

Zuerst müssen wir die EA-Operation im Strategietester testen. Für einen Test habe ich AUDJPY für den Zeitraum vom 01.01.2018-29.08.2018 ausgewählt. D1 wurde als Hauptperiode gewählt, während H6 als Arbeitszeitfenster verwendet wurde. Risiko pro Position - $100. Position, die sich auf einen Breakeven bewegt 1/2, Einstellung von Take Profit - 1/3 (Risiko/Gewinn).

EA Eingabeparameter

Abb. 4. EA Eingabeparameter

Nach dem Test wird der Bericht als CSV-Datei gespeichert. Wir erstellen im lokalen Ordner des Terminals die neue Datei report.csv und kopieren die Berichtsdaten in diese (aus dem Auftragsteil). Wir sollten die Linien löschen, die sich auf das Schließen der Position beziehen, wie in Abbildung 5 dargestellt:

Löschen der Zeilen, die sich auf den Bestandsabschluss beziehen, aus dem Bericht

Abb. 5. Löschen der Zeilen, die sich auf den Bestandsabschluss beziehen, aus dem Bericht

Die zu kopierenden Spalten:

  1. Eröffnungszeit;
  2. Symbol;
  3. Typ;
  4. Preis;
  5. S/L.

Im Ergebnis schaut dann die Datei report.csv so aus:

Inhalt von report.csv

Abb. 6. Inhalt von report.csv

Jetzt benötigen wir noch ein Statistik, das die Daten aus der Datei report.csv liest und die neue Datei file_stat.csv mit weiteren statistischen Informationen erstellt:

  1. SL Größe;
  2. Maximaler Gewinn Level;
  3. Maximaler Gewinn Größe;
  4. Positionsdauer in Bars.

Dafür verwende ich die Fertiglösung aus dem Abschnitt Lesen einer Datei mit Trennzeichen in ein Array des Artikels MQL5 Grundlagen der Programmierung: Dateien. Ich fügte auch die Arrays und deren Befüllung zum Abspeichern der Spaltenwerte in der Datei file_stat.csv hinzu.

Wi erstellen ein neues Skript und schreiben den Code der Funktion zum Lesen der Dateien in das Array in der Funktion OnStart():

//+------------------------------------------------------------------+
//| Einlesen in einen Array (Start)                                  |
//+------------------------------------------------------------------+
bool ReadFileToArrayCSV(string FileName,SLine  &Lines[])
  {
   ResetLastError();
   int h=FileOpen(FileName,FILE_READ|FILE_ANSI|FILE_CSV,";");
   if(h==INVALID_HANDLE)
     {
      int ErrNum=GetLastError();
      printf("File open error %s # %i",FileName,ErrNum);
      return(false);
     }
   int lcnt=0; // Variable zur Behandlung von Zeichenketten
   int fcnt=0; // Variable zur Behandlung von Feldern von Zeichenketten    
   while(!FileIsEnding(h))
     {
      string str=FileReadString(h);
      // neue Zeichenkette (neues Element im Arrays der Struktur)
      if(lcnt>=ArraySize(Lines))
        { // Voll befülltes Arrays der Struktur
         ArrayResize(Lines,ArraySize(Lines)+1024); // Vergrößern des Arrays um 1024 Elemente
        }
      ArrayResize(Lines[lcnt].field,64);// Ändern der Arraygröße der Struktur
      Lines[lcnt].field[0]=str; // Wertzuweisung zum ersten Feld
                                // Beginn des Lesens der verbleibenden Felder in eine Zeichenkette
      fcnt=1; // während ein Element im Feld des Arrays belegt ist
      while(!FileIsLineEnding(h))
        { // lesen der verbleibenden Felder in eine Zeichenkette
         str=FileReadString(h);
         if(fcnt>=ArraySize(Lines[lcnt].field))
           { // Array der Felder komplett ausgefüllt
            ArrayResize(Lines[lcnt].field,ArraySize(Lines[lcnt].field)+64); // Erhöhen der Arraygröße um 64 Elemente
           }
         Lines[lcnt].field[fcnt]=str; // Wertzuweisung zum nächsten Feld
         fcnt++; // Erhöhen des Feldzählers
        }
      ArrayResize(Lines[lcnt].field,fcnt); // Ändern der Größe des Feldarrays nach der aktuellen Anzahl von Feldern
      lcnt++; // Erhöhen des Zählers der Zeichenketten
     }
   ArrayResize(Lines,lcnt); // Ändern der Größe des Arrays der Strukturen (Zeichenketten) gemäß der Anzahl der Zeichenketten
   FileClose(h);
   return(true);
  }
//+------------------------------------------------------------------+
//| Einlesen in einen Array (Ende)                                   |
//+------------------------------------------------------------------+

Danach folgt die Spezifikation der Eingabeparameter:

#property script_show_inputs 
//--- Eingabeparameter
input ENUM_TIMEFRAMES base_tf;  //Zeitrahmen der Basisperiode
input double sar_step=0.1;      //Schrittweite der Parabolic
input double maximum_step=0.11; //maximale Schrittweite der Parabolic
//--- Deklarieren der Variablen der Handles der Indikatoren
int Fractal_base_tf;             //Handles des Indikators iFractals
//--- Deklarieren der Variablen für base_tf
double High_base_tf[],Low_base_tf[];             //Arrays der Hochs und Tiefs
double FractalDown_base_tf[],FractalUp_base_tf[];//Array der Indikatorwerte von iFractal
//--- Struktur der Arrays
struct SLine
  {
   string            field[];
  };

Innerhalb der Funktion OnStart(), holen wir uns das Handle des Indikators iFractals, deklarieren und befüllen das Array mit Hoch-/Tiefpreisen. Wir benötigen auch die Variable bars_base_tf, die in der for-Schleife verwendet wird, und die Variable f für die Größe der Preiszahl in Abhängigkeit der Anzahl der Dezimalstellen des Symbolpreises zu speichern. Diese Variable wird verwendet, um Stop-Loss und den Wert des maximalen Gewinns in Ganzzahlen umzuwandeln.

//--- Holen des Handles des Indikators iFractal
   Fractal_base_tf=iFractals(Symbol(),base_tf);
//--- Festlegen der Laufrichtung der Arrays als Zeitreihen für base_tf
   ArraySetAsSeries(High_base_tf,true);
   ArraySetAsSeries(Low_base_tf,true);
   ArraySetAsSeries(FractalDown_base_tf,true);
   ArraySetAsSeries(FractalUp_base_tf,true);
//--- Erstbefüllung der Arrays für base_tf
   CopyHigh(Symbol(),base_tf,0,1000,High_base_tf);
   CopyLow(Symbol(),base_tf,0,1000,Low_base_tf);
   CopyBuffer(Fractal_base_tf,0,TimeCurrent(),1000,FractalUp_base_tf);
   CopyBuffer(Fractal_base_tf,1,TimeCurrent(),1000,FractalDown_base_tf);
//--- Variablen für die Datenspeicherung der Anzahl der Bars
   int bars_base_tf=Bars(Symbol(),base_tf);
//Anzahl der Dezimalstellen der Preise des Symbols
   int Digit=(int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS);
//Definieren der Kapazität der aktuellen Symbolpreise
   double f=1;
   if(Digit==5) {f=100000;}
   if(Digit==4) {f=10000;}
   if(Digit==3) {f=1000;}
   if(Digit==2) {f=100;}
   if(Digit==1) {f=10;}

Next, declare arrays and variables:

//--- Deklarieren der Variablen und Arrays
   int i,j,n; //Schleifenvariablen
   datetime opentime[];//Array der Zeitpunkte der Position
   string symbol[];//Array der Symbole
   string type[];////array der Positionstypen
   string openprice[];//Array der Eröffnungspreise
   string  sl_price[];//Array der Stop-Loss Level
   int index[];//Array der Indices der Bars der Positionen
   int down_fractal[];//Array der unteren Fraktale
   int up_fractal[];//Array der oberen Fraktale
   double sl_size_points[];//Array der Stop-Loss Größen in Points
   string maxprofit_price[];//Array der maximalen Gewinnlevel
   double maxprofit_size_points[];//Array der Maximalgewinne
   int duration[];//Array für die Dauer der Wellen in Bars
   bool maxprofit_bool[];//Array für die Sicherstellung, dass die Position nicht mit SL geschlossen wurde
   int maxprofit_int[];//Array zur Definition von Minimum/Maximum Bars. Wind in Verbindung mit maxprofit_bool[] verwendet

Danach gehen wir weiter und lesen die Daten aus der Datei in das Array:

   SLine lines[];
   int size=0;
   if(!ReadFileToArrayCSV("report.csv",lines))
     {
      Alert("Error, see details in the \"Experts\"" tab);
     }
   else
     {
      size=ArraySize(lines);
      ArrayResize(opentime,ArraySize(lines));
      ArrayResize(symbol,ArraySize(lines));
      ArrayResize(type,ArraySize(lines));
      ArrayResize(openprice,ArraySize(lines));
      ArrayResize(sl_price,ArraySize(lines));
      ArrayResize(index,ArraySize(lines));
      ArrayResize(down_fractal,ArraySize(lines));
      ArrayResize(up_fractal,ArraySize(lines));
      ArrayResize(sl_size_points,ArraySize(lines));
      ArrayResize(maxprofit_price,ArraySize(lines));
      ArrayResize(maxprofit_size_points,ArraySize(lines));
      ArrayResize(duration,ArraySize(lines));
      ArrayResize(maxprofit_bool,ArraySize(lines));
      ArrayResize(maxprofit_int,ArraySize(lines));
      for(i=0;i<size;i++)
        {
         for(j=0;j<ArraySize(lines[i].field);j=j+5)//Auswahl der Felder durch die Spalte der Eröffnungszeit der Postionen
           {
            opentime[i]=(datetime)(lines[i].field[j]);//Schreiben der Daten in das Array
           }
         for(j=1;j<ArraySize(lines[i].field);j=j+4)//Feldauswahl durch die Spalte der Symbole
           {
            symbol[i]=(lines[i].field[j]);//Schreiben der Daten in das Array
           }
         for(j=2;j<ArraySize(lines[i].field);j=j+3)//Feldauswahl durch die Spalte der Typen
           {
            type[i]=(lines[i].field[j]);//Schreiben der Daten in das Array
           }
         for(j=3;j<ArraySize(lines[i].field);j=j+2)//Auswahl der Felder durch die Spalte der Eröffnungspreise
           {
            openprice[i]=(lines[i].field[j]);//Schreiben der Daten in das Array
           }
         for(j=4;j<ArraySize(lines[i].field);j=j+1)//Feldauswahl durch die Spalte der Stop-Loss
           {
            sl_price[i]=(lines[i].field[j]);//Schreiben der Daten in das Array
           }
        }
     }
//-----------------------------------------------------

Die Arrays openrpice[] und sl_price[] sind vom Typ Zeichenkette. Für die Berechnungen müssen wir sie in Werte vom Typ double unter Verwendung der Funktion StringToDouble() umwandeln. Damit gehen jedoch die Zahlen nach den Kommas verloren. Um dies zu verhindern, ersetzen wir die Kommata durch Punkte mit der Funktion StringReplace():

   for(i=0;i<size;i++)
     {
      StringReplace(openprice[i],",",".");
      StringReplace(sl_price[i],",",".");
     }

Als Nächstes müssen wir die Indizes der Bars bestimmen, auf denen die Positionen installiert wurden:

//--- Definieren der Indices der Bars auf denen Positionen eröffnet wurden
   for(i=0;i<size;i++)
     {
      index[i]=iBarShift(Symbol(),PERIOD_D1,opentime[i]);//Schreiben der Daten in das Array
     }

Danach finden wir die oberen und unteren Fraktale, die am nächsten der platzierten Position sind.

//--- Suche nach einem unteren Fraktal für einen Verkauf
   for(i=0;i<size;i++)
     {
      if(type[i]=="sell")
        {
         for(n=index[i];n>0;n--)
           {
            if(FractalDown_base_tf[n]!=EMPTY_VALUE)
               break;
           }
         down_fractal[i]=n;
        }
     }
//--- Suche nach einem oberen Fraktal für einen Kauf
   for(i=0;i<size;i++)
     {
      if(type[i]=="buy")
        {
         for(n=index[i];n>0;n--)
           {
            if(FractalUp_base_tf[n]!=EMPTY_VALUE)
               break;
           }
         up_fractal[i]=n;
        }
     }

Als nächstes definieren wir Stop-Loss in Points und konvertieren die Anzahl der Points in eine Ganzzahl.

//--- Stop-Loss in Points
   for(i=0;i<size;i++)
     {
      if(type[i]=="sell")
        {
         sl_size_points[i]=(StringToDouble(sl_price[i])-StringToDouble(openprice[i]))*f;
        }
      if(type[i]=="buy")
        {
         sl_size_points[i]=(StringToDouble(openprice[i])-StringToDouble(sl_price[i]))*f;
        }
     }

Basierend auf den zuvor erkannten Fraktalen können wir die maximale Gewinnspanne bestimmen. Aber zuerst müssen wir sicherstellen, dass die Position nicht vorzeitig durch Stop-Loss geschlossen wird. Code der Prüfung:

//--- Sicherstellen, dass die Position nicht vor dem Erreichen des Gewinnmaximums geschlossen wird
//--- für Verkaufspositionen
   for(i=0;i<size;i++)
     {
      if(type[i]=="sell")
        {
         for(n=index[i];n>down_fractal[i];n--)
           {
            if(High_base_tf[n]>=StringToDouble(sl_price[i]))
               break;
           }
         maxprofit_int[i]=n;
         maxprofit_bool[i]=(n==down_fractal[i]);
        }
     }
//--- für Kaufpositionen
   for(i=0;i<size;i++)
     {
      if(type[i]=="buy")
        {
         for(n=index[i];n>up_fractal[i];n--)
           {
            if(Low_base_tf[n]<=StringToDouble(sl_price[i]))
               break;
           }
         maxprofit_int[i]=n;
         maxprofit_bool[i]=(n==up_fractal[i]);
        }
     }

Jetzt können wir den Code schreiben, um den Preis des Maximalgewinns zu bestimmen:

//--- Level des Maximalgewinns
   for(i=0;i<size;i++)
     {
      if(type[i]=="sell" && maxprofit_bool[i]==true)
        {
         maxprofit_price[i]=(string)Low_base_tf[down_fractal[i]];
        }
      if(type[i]=="sell" && maxprofit_bool[i]==false)
        {
         maxprofit_price[i]="";
        }
      if(type[i]=="buy" && maxprofit_bool[i]==true)
        {
         maxprofit_price[i]=(string)High_base_tf[up_fractal[i]];
        }
      if(type[i]=="buy" && maxprofit_bool[i]==false)
        {
         maxprofit_price[i]="";
        }
     }

Damit können wir die Größe des Maximalgewinns bestimmen. Der Gewinn wird durch Stop-Loss negativ, wenn die Kontrolle aktiviert ist:

   for(i=0;i<size;i++)
     {
      if(type[i]=="sell" && maxprofit_bool[i]==true)
        {
         maxprofit_size_points[i]=(StringToDouble(openprice[i])-Low_base_tf[down_fractal[i]])*f;
        }
      if(type[i]=="sell" && maxprofit_bool[i]==false)
        {
         maxprofit_size_points[i]=sl_size_points[i]*-1;
        }
      if(type[i]=="buy" && maxprofit_bool[i]==true)
        {
         maxprofit_size_points[i]=(High_base_tf[up_fractal[i]]-StringToDouble(openprice[i]))*f;
        }
      if(type[i]=="buy" && maxprofit_bool[i]==false)
        {
         maxprofit_size_points[i]=sl_size_points[i]*-1;
        }
     }

Abschließend definieren wir die Dauer zwischen der Bar, an dem sich die Position befindet, und dem maximalen Gewinn (in Bars). Wenn die Kontrolle der Positionsschließung durch SL aktiviert ist, ist die Dauer definiert als eine Differenz zwischen der Bar, auf den die Position eingestellt ist, und der, bei der SL ausgelöst wird:

//---- Berechnen der Positionsdauer in Bars
   for(i=0;i<size;i++)
     {
      if(type[i]=="sell" && maxprofit_bool[i]==true)
        {
         duration[i]=index[i]-(int)down_fractal[i];
        }
      if(type[i]=="sell" && maxprofit_bool[i]==false)
        {
         duration[i]=index[i]-maxprofit_int[i];
        }
      if(type[i]=="buy" && maxprofit_bool[i]==true)
        {
         duration[i]=index[i]-(int)up_fractal[i];
        }
      if(type[i]=="buy" && maxprofit_bool[i]==false)
        {
         duration[i]=index[i]-maxprofit_int[i];
        }
     }

Danach können wir die Punkte wieder in Kommata wechseln für die korrekte Darstellung der Parameter:

   for(i=0;i<size;i++)
     {
      StringReplace(openprice[i],".",",");
      StringReplace(sl_price[i],".",",");
      StringReplace(maxprofit_price[i],".",",");
     }

Jetzt bleibt nur noch, die erhaltenen Daten in die Datei file_stat.csv zu schreiben:

//--- Schreiben der Daten in die neue Datei für die Statistik
   int h=FileOpen("file_stat.csv",FILE_READ|FILE_WRITE|FILE_ANSI|FILE_CSV,";");
//--- Prüfen auf Positionseröffnung
   if(h==INVALID_HANDLE)
     {
      Alert("Error opening file!");
      return;
     }
   else
     {
      FileWrite(h,
                /*1 Symbol*/"Symbol",
                /*2 Dealtyp*/"Deal type",
                /*3 Eröffnungszeit*/"Open time",
                /*4 Eröffnungspreis*/"Open price",
                /*5 SL-Level*/"SL",
                /*6 SL-Größe*/"SL size",
                /*7 max. Gewinnlevel*/"Max profit level",
                /*8 max, Gewinnwert*/"Max profit value",
                /*9 Dauer*/"Duration in bars");
      //--- Sprung ans Ende
      FileSeek(h,0,SEEK_END);
      for(i=0;i<size;i++)
        {
         FileWrite(h,
                   /*1 Symbol*/symbol[i],
                   /*2 Dealtyp*/type[i],
                   /*3 Eröffnungszeit*/TimeToString(opentime[i]),
                   /*4 Eröffnungspreis*/openprice[i],
                   /*5 SL-Level*/sl_price[i],
                   /*6 SL-Größe*/NormalizeDouble(sl_size_points[i],2),
                   /*7 max. Gewinn-Level*/maxprofit_price[i],
                   /*8 max. Gewinnwert*/NormalizeDouble(maxprofit_size_points[i],2),
                   /*9 Dauer*/duration[i]);
        }
     }
   FileClose(h);
   Alert("file_stat.csv file created");

Überprüfen wir: Wir starten das Skript auf dem Chart, nachdem wir den Basiszeitrahmen in den Eingaben festgelegt haben (was in meinem Fall D1 ist). Danach erscheint die neue Datei file_stat.csv mit dem folgenden Parametersatz im lokalen Ordner des Terminals:

Dateiinhalt von file_stat.csv
 

Abb. 7. Dateiinhalt von file_stat.csv

6. Schlussfolgerung

In diesem Artikel haben wir die Methode der programmatischen Bestimmung eines Modells der Bewegungsfortsetzung analysiert. Die Grundidee der Methode ist die Suche nach einer Korrekturbewegung mit den Extrema Hoch/Tief ohne Verwendung von Indikatoren. Die aufeinanderfolgenden Punkte des Modells werden dann basierend auf dem gefundenen Extremum erfasst.

Wir diskutierten auch die Methode der Erhebung statistischer Daten auf der Grundlage der Testergebnisse im Strategietester, indem wir die Testergebnisse in ein Array schreiben und anschließend verarbeiten. Ich glaube, dass es möglich ist, eine effizientere Art der Erhebung und Verarbeitung statistischer Daten zu entwickeln. Diese Methode erscheint mir jedoch am einfachsten und umfassendsten.

Beachten Sie, dass der Artikel die Mindestanforderungen für die Definition des Modells und vor allem den minimalsten Satz von Kontrollen beschreibt, die von der EA bereitgestellt werden. Für den realen Handel sollte das Kontrollsystem erweitert werden.

Nachfolgend finden Sie Beispiele für die Erkennung von Bewegungsmodellen:

Modellerkennung

Abb. 8. Beispiel einer Bewegungsfortsetzung

Modellerkennung

Abb. 9. Beispiel einer Bewegungsfortsetzung

Beispiel einer Trendfortsetzung

Abb. 10. Beispiel einer Bewegungsfortsetzung

Beispiel einer Trendfortsetzung

Abb. 11. Beispiel einer Bewegungsfortsetzung

Die dieses Artikels

# Name Typ Beschreibung
1 Trade.mqh Klassenbibliothek Klasse der Handelsoperationen
2 MQL5_POST_final Expert Advisor EA, der das Modell der Bewegungsfortsetzung umsetzt
3 Report_read Skript Skript für das Erfassen der Statistik

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

Beigefügte Dateien |
MQL5.zip (128.44 KB)
Letzte Kommentare | Zur Diskussion im Händlerforum (1)
Christian
Christian | 4 Dez. 2018 in 12:28

Dickes Lob , klasse Artikel.

Schade MetaQuotes das man nicht bewerten kann.

Sehr ausführlich und gut auskommentiert !

Die Qualität steigt ....

Verwendung von Limit-Orders anstelle von Take-Profit, ohne den ursprünglichen Code des EA zu ändern. Verwendung von Limit-Orders anstelle von Take-Profit, ohne den ursprünglichen Code des EA zu ändern.
Die Verwendung von Limit-Orders anstelle von herkömmlichen Take-Profits ist seit langem ein Diskussionsthema im Forum. Was ist der Vorteil dieses Ansatzes und wie kann er in Ihrem Handel umgesetzt werden? In diesem Artikel möchte ich Ihnen meine Vision zu diesem Thema vorstellen.
Methoden zur Fernsteuerung von EAs Methoden zur Fernsteuerung von EAs
Der Hauptvorteil der Handelsroboter liegt in der Möglichkeit, dass sie 24 Stunden am Tag auf einem entfernten VPS-Server arbeiten. Aber manchmal ist es notwendig, in ihre Arbeit einzugreifen, ohne dass es einen direkten Zugriff auf den Server gibt. Ist es möglich, EAs fernzusteuern? Der Artikel schlägt eine der Möglichkeiten vor, EAs über externe Befehle zu steuern.
Kurslücke - eine profitabele Strategie oder 50/50? Kurslücke - eine profitabele Strategie oder 50/50?
Der Artikel beschäftigt sich mit Kurslücken (gaps) - signifikante Unterschiede zwischen dem Schlusskurs des vorherigen Balkens und dem Eröffnungskurs des darauf folgenden sowie auf der Prognose der Richtung des Tagesbalkens. Die Anwendung der Funktion GetOpenFileName durch die System-DLL wird ebenfalls besprochen.
Die 100 besten Durchläufe der Optimierung (Teil 1). Entwicklung einer Analyse der Optimierung Die 100 besten Durchläufe der Optimierung (Teil 1). Entwicklung einer Analyse der Optimierung
Der Artikel beschäftigt sich mit der Entwicklung einer Anwendung zur Auswahl der besten Optimierungsdurchläufe unter Verwendung mehrerer möglicher Optionen. Die Anwendung ist in der Lage, die Optimierungsergebnisse nach einer Vielzahl von Faktoren zu sortieren. Optimierungsläufe werden immer in eine Datenbank geschrieben, so dass Sie jederzeit neue Roboterparameter ohne erneute Optimierung auswählen können. Außerdem können Sie alle Optimierungsdurchläufe in einem einzigen Diagramm sehen, parametrische VaR-Kennzahlen berechnen und die Grafik der Normalverteilung von Durchgängen und Handelsergebnissen einer Reihe bestimmter Verhältnisse erstellen. Außerdem werden die Diagramme einiger berechneter Verhältnisse dynamisch erstellt, beginnend mit dem Optimierungsstart (oder von einem ausgewählten Datum zu einem anderen ausgewählten Datum).