![Modell der Bewegungsfortsetzung - Suche im Chart und Ausführungsstatistik](https://c.mql5.com/i/articles/overlay.png)
Modell der Bewegungsfortsetzung - Suche im Chart und Ausführungsstatistik
- Einführung
- Modellbeschreibung - allgemeine Eigenschaften
- Prinzipien des Modells zur Erkennung auf dem Chart
- Konstruktion und der Code des Algorithmus'
- Eingabeparameter, die Funktion OnInit() und die Anfangsdeklaration der Variablen
- Allgemeine Eigenschaften
- Aktualisierung der Arraydaten
- Eintragungen in die Arrays, wenn eine neue Bar erscheint
- Eintragungen der Daten in die Arrays der Bar 0
- Aktualisierung der Daten der Fraktale
- Suche nach den Extrema
- Suche nach den Extrema bei einem Abwärtstrend
- Suche nach den Extrema bei einem Aufwärtstrend
- Reduktion der Hochs/Tiefs der Korrekturwellen in vereinigte Variablen
- Modell der Erkennungsbedingungen
- Erstellen der Steuerelemente
- Bilden eines Einstiegspunktes im Bereich der Positionseröffnung
- Kontrolle einer Preiskorrektur in den Bereich der Positionseröffnung
- Eliminieren von doppelten Positionen im Singlemodell
- Beschreiben der Bedingungen für die Positionseröffnung
- Handelsbedingungen
- Arbeiten mit den Handelsoperationen
- Sammeln der statistischen Daten
- 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.
Abb. 1. Modell der Bewegungsfortsetzung
Auf dem Chart schaut es dann so aus:
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 |
//+------------------------------------------------------------------+ //| 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.
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:
- Symbol;
- Typ des Deals;
- Eröffnungszeit;
- Eröffnungspreis;
- Stop-Loss Level;
- Stop-Loss Größe;
- Maximaler Gewinn Level;
- Maximaler Gewinn Größe;
- 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).
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:
Abb. 5. Löschen der Zeilen, die sich auf den Bestandsabschluss beziehen, aus dem Bericht
Die zu kopierenden Spalten:
- Eröffnungszeit;
- Symbol;
- Typ;
- Preis;
- S/L.
Im Ergebnis schaut dann die Datei report.csv so aus:
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:
- SL Größe;
- Maximaler Gewinn Level;
- Maximaler Gewinn Größe;
- 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:
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:
Abb. 8. Beispiel einer Bewegungsfortsetzung
Abb. 9. Beispiel einer Bewegungsfortsetzung
Abb. 10. Beispiel einer Bewegungsfortsetzung
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
![Verwendung von Limit-Orders anstelle von Take-Profit, ohne den ursprünglichen Code des EA zu ändern.](https://c.mql5.com/2/34/Limit_TP.png)
![Methoden zur Fernsteuerung von EAs](https://c.mql5.com/2/34/RemoteControl_EA.png)
![Kurslücke - eine profitabele Strategie oder 50/50?](https://c.mql5.com/2/34/GapDown.png)
![Die 100 besten Durchläufe der Optimierung (Teil 1). Entwicklung einer Analyse der Optimierung](https://c.mql5.com/2/34/TOP100passes.png)
![MQL5 - Sprache von Handelsstrategien, eingebaut ins Kundenterminal MetaTrader 5](https://c.mql5.com/i/registerlandings/logo-2.png)
- Freie Handelsapplikationen
- Über 8.000 Signale zum Kopieren
- Wirtschaftsnachrichten für die Lage an den Finanzmärkte
Sie stimmen der Website-Richtlinie und den Nutzungsbedingungen zu.
Dickes Lob , klasse Artikel.
Schade MetaQuotes das man nicht bewerten kann.
Sehr ausführlich und gut auskommentiert !
Die Qualität steigt ....