English
preview
Entwicklung einer Zone Recovery Martingale Strategie in MQL5

Entwicklung einer Zone Recovery Martingale Strategie in MQL5

MetaTrader 5Handel | 31 Juli 2024, 10:17
21 0
Kikkih25
Kikkih25

Einführung

In diesem Artikel werden wir den Zone Recovery Martingale Forex Trading Strategy Expert Advisor (EA) in MetaQuotes Language 5 (MQL5) für MetaTrader 5 (MT5) Schritt für Schritt erstellen. Die Zone-Recovery-Martingale-Strategie ist eine gängige Strategie, die darauf abzielt, Verlustpositionen durch die Eröffnung von Gegenpositionen mit einem etwas höheren Handelsvolumen auszugleichen, das die Verlustpositionen aufhebt. Im Grunde handelt es sich um eine Trendfolgestrategie, bei der die Richtung des Marktes keine Rolle spielt, in der Hoffnung, dass der Markt irgendwann entweder nach unten oder nach oben tendiert und bestimmte Ziele erreicht werden. Begleiten Sie uns, wenn wir dieses System im MQL5 nicht nur diskutieren, sondern auch automatisieren.

Diese Reise wird die folgenden Themen umfassen;

  1. Definition der Strategie Zone Recovery
  2. Beschreibung der Strategie Zone Recovery
  3. Implementation in MQL5
  4. Backtest-Ergebnisse
  5. Schlussfolgerung


Definition der Strategie Zone Recovery

Die Handelsstrategie Zone Recovery ist eine ausgeklügelte Methode, die meist im Devisenhandel verwendet wird, um Verluste zu kontrollieren und zu reduzieren. Der Kerngedanke dieser Methode besteht darin, genaue Preisspannen festzulegen, innerhalb derer ein Händler die Marktschwankungen erwartet. Der Händler startet eine Reihe von Gegengeschäften, um eine Recovery-Zone (Rückgewinnungszone) aufzubauen, wenn sich ein Handel ungünstig entwickelt und eine vorher festgelegte Verlustzone erreicht, anstatt die Position mit Verlust zu schließen. Idealerweise sollten diese Trades so platziert werden, dass eine Rückkehr in die Recovery-Zone es ermöglicht, die Position als Ganzes zum Breakeven oder sogar mit Gewinn zu schließen.

Diese Technik ist in erster Linie auf die Absicherung und die Durchschnittsbildung ausgerichtet. Wenn sich der Markt gegen das erste Geschäft wendet, baut der Händler eine gleich große Gegenposition auf, die als Absicherung dient. Wenn sich der Markt weiter abwärts bewegt, werden in entscheidenden Abständen neue Geschäfte eröffnet, um den Einstiegspreis zu senken. Die Geschäfte sind so konzipiert, dass der Gesamtgewinn aus den Erholungsgeschäften die Verluste aus dem ursprünglichen Geschäft ausgleichen kann, wenn der Markt zu seinem Mittelwert zurückkehrt. Dieser Ansatz erfordert eine klar definierte Risikomanagementstrategie, da die Möglichkeit der Anhäufung von Beständen zu hohen Margenanforderungen und zur Abhängigkeit von volatilen Marktbedingungen führen kann.

Einer der Hauptvorteile der Zone-Recovery-Strategie besteht darin, dass sie eine Verlusttransaktion in eine erfolgreiche verwandeln kann, ohne dass eine genaue Vorhersage der Marktrichtung erforderlich ist. Er ermöglicht es Händlern, von Marktturbulenzen und -umkehrungen zu profitieren und ungünstige Trends in Chancen für einen Aufschwung umzuwandeln.

Die Zone-Recovery-Handelsmethode eignet sich vor allem für erfahrene Händler, die über ein umfassendes Verständnis von Risikomanagementstrategien und Marktdynamik verfügen. Es handelt sich um ein leistungsfähiges Instrument im Werkzeugkasten eines Händlers, das besonders auf unbeständigen Märkten mit häufigen Kursschwankungen hilfreich ist. Obwohl sie die Möglichkeit bietet, sich von fehlgeschlagenen Handelsgeschäften zu erholen und Gewinne zu erzielen, erfordern ihre Komplexität und die damit verbundenen Gefahren eine sorgfältige Planung und kalkulierte Ausführung.


Beschreibung der Strategie Zone Recovery

Ausgehend von einer Marktanalyse beginnt die Methode des Zone Recovery Trading mit der Eröffnung einer ersten Position. Stellen Sie sich einen Händler vor, der eine Kaufposition eröffnet, weil er glaubt, dass der Markt steigen wird. Das erste Ziel ist es, mit dem steigenden Trend Geld zu verdienen. Der Händler schließt die Position und sichert sich die Gewinne, wenn sich der Markt positiv entwickelt und der Kurs bis zu einem vorher festgelegten Gewinnziel steigt. Mit dieser einfachen Strategie kann der Händler von günstigen Marktbewegungen profitieren, ohne kompliziertere Strategien anwenden zu müssen.

Andererseits wird bei der Zone-Recovery-Methode der Mechanismus zur Verlustbegrenzung ausgelöst, wenn sich der Markt gegen die ursprüngliche Long-Position bewegt und einen vorher festgelegten Verlustpunkt erreicht. An diesem Punkt eröffnet der Händler eine Verkaufsposition mit einer größeren Losgröße, in der Regel doppelt so groß wie die vorherige Kaufposition, anstatt die Kaufposition mit Verlust zu schließen. Indem die Verluste aus dem ersten Geschäft mit den möglichen Gewinnen aus der neuen Position verrechnet werden, wird mit diesem Gegengeschäft eine Absicherung angestrebt. Unter Ausnutzung der dem Markt innewohnenden Oszillation antizipiert die Strategie eine Umkehr oder zumindest eine Stabilisierung innerhalb einer bestimmten Spanne.

Der Händler behält die neue Verkaufsposition im Auge, während sich der Markt weiterentwickelt. Die kombinierte Auswirkung der anfänglichen Kauf- und größeren Verkaufspositionen führt im Idealfall zu einer Breakeven- oder Gewinnsituation, wenn der Markt weiter fällt und einen anderen vorher festgelegten Punkt erreicht. Danach kann der Händler beide Positionen schließen und mit den Gewinnen aus dem größeren Folgegeschäft die Verluste aus dem ersten Geschäft ausgleichen. Um zu gewährleisten, dass die gesamte Position innerhalb der Recovery-Zone gewinnbringend geschlossen werden kann, erfordert diese Strategie eine genaue Berechnung und ein genaues Timing.


Implementation in MQL5

Um einen Expert Advisor in MQL5 zu erstellen, der auf die Visualisierung der Recovery-Zonen, in der Regel vier, ausgerichtet ist, müssen wir zunächst die Ebenen definieren. Wir tun dies, indem wir sie so früh wie möglich definieren, da sie für die Visualisierung des Handelssystems entscheidend sein werden. Dies wird mit dem Schlüsselwort „#define“ erreicht, einer in MQL5 eingebauten Direktive, mit der Konstanten mnemonische Namen zugewiesen werden können. Dies ist wie unten dargestellt:

#define ZONE_H "ZH"            // Define a constant for the high zone line name
#define ZONE_L "ZL"            // Define a constant for the low zone line name
#define ZONE_T_H "ZTH"         // Define a constant for the target high zone line name
#define ZONE_T_L "ZTL"         // Define a constant for the target low zone line name

Hier legen wir die Konstanten für unsere Zonengrenzen fest: ZONE_H als „ZH“ (Zone High), ZONE_L als „ZL“ (Zone Low), ZONE_T_H als „ZTH“ (Zone Target High), und ZONE_T_L als „ZTL“ (Zone Target Low). Diese Konstanten stellen die jeweiligen Ebenen in unserem System dar.

Nach unseren Definitionen müssen wir Handelspositionen eröffnen. Der einfachste Weg, Positionen zu eröffnen, ist die Einbeziehung einer Handelsinstanz, was in der Regel durch die Einbeziehung einer anderen Datei erreicht wird, die für offene Positionen bestimmt ist. Wir verwenden die Include-Direktive, um die Handelsbibliothek einzubinden, die Funktionen für Handelsoperationen enthält.

#include <Trade/Trade.mqh>
CTrade obj_trade;

Zunächst geben wir mit spitzen Klammern an, dass sich die einzubindende Datei im Include-Ordner befindet, und geben den Ordner Trade an, gefolgt von einem normalen Schrägstrich oder Backslash und dem Namen der Zieldatei, in diesem Fall „Trade.MQH“. CTrade ist eine Klasse für die Abwicklung von Handelsoperationen, und obj_trade ist eine Instanz dieser Klasse, in der Regel ein Zeigerobjekt, das von der Klasse CTrade erstellt wird, um den Zugriff auf die Mitgliedsvariablen der Klasse zu ermöglichen.

Danach benötigen wir eine Steuerlogik, um Signale zur Öffnung der Positionen zu erzeugen. In unserem Fall verwenden wir den RSI (Relative-Stärke-Indikator), aber Sie können jeden beliebigen Indikator verwenden, den Sie für geeignet halten.

int rsi_handle;                // Handle for the RSI indicator
double rsiData[];              // Array to store RSI data
int totalBars = 0;             // Variable to keep track of the total number of bars

Der rsi_handle speichert das Handle für den RSI-Indikator (Relative Strength Index), der in der OnInit-Funktion initialisiert wird, sodass der EA die RSI-Werte abrufen kann. Das Array rsiData speichert diese RSI-Werte, die mit CopyBuffer abgerufen werden, und wird zur Ermittlung von Handelssignalen auf der Grundlage von RSI-Schwellenwerten verwendet. Die Variable totalBars verfolgt die Gesamtzahl der Balken im Chart und stellt sicher, dass die Handelslogik nur einmal pro neuem Balken ausgeführt wird, um Mehrfachausführungen innerhalb eines einzigen Balkens zu verhindern. Zusammen ermöglichen diese Variablen dem EA die Generierung von Handelssignalen auf der Grundlage von RSI-Werten unter Beibehaltung des richtigen Ausführungszeitpunkts.

Nach der Festlegung der Indikatorwerte werden schließlich die Stufen der Indikatorsignalerzeugung und die Zonenstufen definiert.

double overBoughtLevel = 70.0; // Overbought level for RSI
double overSoldLevel = 30.0;   // Oversold level for RSI
double zoneHigh = 0;           // Variable to store the high zone price
double zoneLow = 0;            // Variable to store the low zone price
double zoneTargetHigh = 0;     // Variable to store the target high zone price
double zoneTargetLow = 0;      // Variable to store the target low zone price

Auch hier definieren wir zwei Variablen vom Typ double, overBoughtLevel und overSoldLevel, und initialisieren sie mit 70 bzw. 30. Diese dienen als unsere Extremwerte für die Signalproduktion. Außerdem definieren wir vier zusätzliche Variablen vom Typ Double, zoneHigh, zoneLow, zoneTradegetHigh und zoneTargetLow, und initialisieren sie mit Null. Sie werden später im Code unsere Recovery-Setup-Ebenen enthalten.

Bis zu diesem Punkt haben wir alle globalen Variablen definiert, die für das System entscheidend sind. Wir können nun frei zum OnInit-Ereignishandler übergehen, der immer dann aufgerufen wird, wenn der Expertenberater initialisiert wird. In diesem Fall müssen wir das Indikator-Handle initialisieren, von dem wir später Daten für die weitere Analyse kopieren werden. Um den Indikator zu initialisieren, verwenden wir die eingebaute Funktion, um sein Handle zurückzugeben, indem wir die richtigen Parameter angeben. 

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   //--- Initialize the RSI indicator
   rsi_handle = iRSI(_Symbol, PERIOD_CURRENT, 14, PRICE_CLOSE);
   //--- Return initialization result
   return(INIT_SUCCEEDED);
  }

Im Ereignis-Handler für die Deinitialisierung des Experten müssen wir die Indikatordaten freigeben und die gespeicherten Daten freigeben. Dadurch wird der Indikator in der Regel aus dem Arbeitsspeicher des Computers entfernt, um Ressourcen zu sparen, da er nicht mehr verwendet wird. Falls doch, werden die Handles und Daten des Indikators bei der Initialisierung des Expertenberaters erstellt. 

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   //--- Remove RSI indicator from memory
   IndicatorRelease(rsi_handle);
   ArrayFree(rsiData); // Free the RSI data array
  }

Wir gehen dann zum OnTick über, einer Funktion, die bei jedem Tick, d. h. bei einer Kursänderung, aufgerufen wird. Dies ist wiederum unsere Kernfunktion oder -sektion, da sie alle entscheidenden Codeschnipsel für eine erfolgreiche Umsetzung der Handelsstrategie enthält. Da wir Positionen eröffnen werden, müssen wir unsere Bid- und Ask-Kurse festlegen, damit wir ihre aktuellsten Werte für die Analyse verwenden können. 

   double ask = SymbolInfoDouble(_Symbol,SYMBOL_ASK);
   double bid = SymbolInfoDouble(_Symbol,SYMBOL_BID);

Die Definition der Bid- und Ask-Kurse durch die Verwendung von Symbolinformationen als doppelter Datentyp hilft uns, die neuesten Kursnotierungen für weitere Analysen zu erhalten. Die Funktion, die wir verwenden, ist eine Überladungsfunktion, die zwei Varianten enthält, aber wir verwenden die erste, die nur zwei Parameter benötigt. Der erste Parameter ist das Symbol; wir verwenden _Symbol, um automatisch das Symbol im aktuellen Chart abzurufen und SYMBOL_ASK, um den entsprechenden Preis zu erhalten.

Nachdem wir die neuesten Kursnotierungen erhalten haben, die technisch bei jedem Tick generiert werden, sind wir bereit. Wir fahren einfach damit fort, den Zonenbereich und das Zonenziel für den Expert Advisor zu definieren. Wir definieren sie als Variablen vom Datentyp Double und multiplizieren sie mit der Variablen _Point, die die Punktgröße des aktuellen Symbols in der Kurswährung enthält.

   double zoneRange = 200*_Point;
   double zoneTarget = 400*_Point;

Schließlich brauchen wir Hilfsvariablen, die sicherstellen, dass wir die Wiederherstellungspositionen korrekt öffnen. In diesem Fall definieren wir vier davon. LastDirection enthält den Typ der zuletzt eröffneten Position, um sicherzustellen, dass wir zwischen Kauf- und Verkaufsaufträgen abwechseln. Wir beabsichtigen, dass der Verkauf -1 und der Kauf +1 sein soll, ein willkürlicher Wert, der nach Belieben geändert oder implementiert werden kann. Das „recovery_lot“ wird dann verwendet und auf Null initialisiert. Seine Funktion besteht darin, die berechneten Recovery-Punkte zu halten und zu speichern, damit wir den Überblick über die nächsten Handelsvolumina nicht verlieren, vorausgesetzt, ein Zonen-Recovery-System ist noch im Spiel. Dennoch haben wir zwei boolesche Variablen, isBuyDone und isSellDone, die uns dabei helfen, nicht mehrere Positionen gleichzeitig zu eröffnen. Beachten Sie, dass alle unsere Variablen statisch sind, um sicherzustellen, dass sie nicht aktualisiert werden, es sei denn, wir aktualisieren sie selbst. Das liegt daran, dass lokale Variablen, die mit dem Schlüsselwort static deklariert wurden, ihre Werte während der gesamten Ausführungsdauer der Funktion beibehalten. Bei jedem nächsten Aufruf der Funktion OnTick behalten unsere lokalen Variablen also die Werte, die sie beim vorherigen Aufruf hatten.

   static int lastDirection = 0; //-1 = sell, 1 = buy
   static double recovery_lot = 0.0;
   static bool isBuyDone = false, isSellDone = false;

Nachdem wir alle Hilfsvariablen definiert haben, gehen wir zu den offenen Positionen über, von denen aus wir später die Logik der Zonenerholung implementieren werden. Wir beabsichtigen, dies mit Hilfe des RSI-Indikators zu erreichen, der je nach den Präferenzen der Nutzer völlig veränderbar ist, sodass Sie einfach Ihre Einstiegstechnik anwenden können. Um Ressourcen zu sparen, wollen wir die Daten aus dem Indikator bei jeder Kerze und nicht bei jedem Tick abrufen. Dies wird wie folgt erreicht:

   int bars = iBars(_Symbol,PERIOD_CURRENT);
   if (totalBars == bars) return;
   totalBars = bars;

Hier definieren wir die Integer-Variable bars und initialisieren sie mit der Anzahl der aktuellen Balken im Chart, was durch die Verwendung der integrierten Funktion iBars erreicht wird, die zwei Argumente benötigt: ein Symbol und einen Zeitraum. Anschließend wird geprüft, ob die Anzahl der zuvor definierten Balken mit den aktuellen Balken übereinstimmt. Wenn dies der Fall ist, bedeutet dies, dass wir uns immer noch auf dem aktuellen Balken befinden, und daher kehren wir zurück, d. h. wir brechen die Operation ab und geben die Kontrolle an das aufrufende Programm zurück. Wenn die beiden Variablen nicht übereinstimmen, bedeutet dies, dass wir zu einer neuen Kerze übergegangen sind, und wir können fortfahren. Wir aktualisieren also den Wert von totalBars auf die aktuellen Balken, sodass wir beim nächsten Tick einen aktualisierten Wert für die Variable totalBars haben. 

Damit die Zone Recovery Wirkung zeigt und nur eine Position eröffnet und verwaltet werden kann, müssen wir eine Position pro Instanz eröffnen. Wenn also die Anzahl der Positionen größer als eins ist, brauchen wir keine weiteren Positionen hinzuzufügen und kehren einfach vorzeitig zurück. Dies ist wieder wie unten. 

Wenn wir nicht zu diesem Punkt zurückkehren, bedeutet das, dass wir noch keine Position haben, und wir können damit fortfahren, eine zu eröffnen. Daher kopieren wir die Daten aus dem Indikator-Handle und speichern sie zur weiteren Analyse im Indikator-Daten-Array. Dies wird durch die Funktion des Kopierpuffers erreicht. 

   if (PositionsTotal() > 0) return;
   if (!CopyBuffer(rsi_handle,0,1,2,rsiData)) return;

Die Funktion CopyBuffer ist eine überladene Funktion, die eine ganze Zahl zurückgibt. Aus Sicherheitsgründen verwenden wir die if-Anweisung, um zu prüfen, ob die angeforderten Daten zurückgegeben werden, und wenn nicht, haben wir nicht genug Daten, um sie zurückzugeben, da keine weitere Analyse durchgeführt werden kann. Sehen wir uns jedoch an, was die Funktion bewirkt. Er enthält fünf Argumente. Das erste ist das Handle des Indikators, von dem Daten kopiert werden sollen, und das zweite ist die Puffernummer des Indikators; in diesem Fall ist sie 0, kann aber je nach verwendetem Indikator durchaus variieren. Die dritte ist die Startposition oder der Index des Taktes, von dem aus die Daten kopiert werden sollen. Hier verwenden wir die 1, um anzuzeigen, dass wir mit dem Balken vor dem aktuellen Balken im Chart beginnen. Viertens: die Anzahl der zu speichernden Daten. Uns genügen hier zwei, da wir keine detaillierte Analyse durchführen. Schließlich geben wir das Zielfeld des abgerufenen Datenspeichers an.

Nachdem wir die Daten aus dem Indikator-Handle abgerufen haben, verwenden wir sie für Handelszwecke, oder besser gesagt, für die Signalgenerierung. Zunächst suchen wir nach Kaufsignalen. Wir erreichen dies, indem wir eine if-Anweisung verwenden und eine Kaufposition eröffnen. 

   if (rsiData[1] < overSoldLevel && rsiData[0] > overSoldLevel){
      obj_trade.Buy(0.01);

Wenn die Daten bei Index 1 des gespeicherten Arrays unter dem definierten überverkauften Niveau liegen und die Daten bei Index 0 über dem überverkauften Niveau liegen, bedeutet dies, dass RSI-Linie das überverkaufte Niveau gekreuzt hatten, was ein Kaufsignal bedeutet. Anschließend verwenden wir das Handels-Objekt und den Punkt-Operator, um Zugriff auf die Buy-Methode in der CTrade-Klasse zu erhalten. In diesem Fall eröffnen wir eine Kaufposition mit einem Volumen von 0,01 und ignorieren die übrigen Parameter wie Stop-Loss und Take-Profit, da wir sie nicht verwenden werden, da sie dazu führen würden, dass unser System nicht wie beabsichtigt funktioniert, da wir die x=Zone Recovery-Strategie implementieren, bei der die Positionen nicht auf Stop-Loss-Niveau geschlossen werden müssen.

Zum Einstellen der Zonenwiederherstellungsstufen benötigen wir jedoch das Ticket für die Position, damit wir auf deren Eigenschaften zugreifen können. Um das Ticket zu erhalten, verwenden wir die Ergebnisreihenfolge der zuvor eröffneten Position. Nachdem wir das Ticket erhalten haben, wollen wir prüfen, ob es größer als 0 ist, was bedeutet, dass die Position erfolgreich geöffnet wurde, und dann die Position nach Ticket auswählen. Wenn wir sie über das Ticket auswählen können, erhalten wir die Eigenschaften der Position, aber wir interessieren uns nur für den Recovery-Kurs. Ausgehend vom Recovery-Kurs legen wir die Positionen wie folgt fest: Der Recovery-Kurs der Kaufposition ist unser Zonenhoch, und um das Zonentief zu erhalten, ziehen wir einfach den Zonenbereich vom Zonenhoch ab. Für die Zonenziele Hoch und Tief addieren wir einfach das Zonenziel zum Zonenhoch und subtrahieren das Zonenziel vom Zonentief. Zum Schluss müssen wir nur noch die Werte auf die Ziffern des Symbols normalisieren, um die Genauigkeit zu gewährleisten, und schon sind wir fertig.

      ulong pos_ticket = obj_trade.ResultOrder();
      if (pos_ticket > 0){
         if (PositionSelectByTicket(pos_ticket)){
            double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
            zoneHigh = NormalizeDouble(openPrice,_Digits);
            zoneLow = NormalizeDouble(zoneHigh - zoneRange,_Digits);
            zoneTargetHigh = NormalizeDouble(zoneHigh + zoneTarget,_Digits);
            zoneTargetLow = NormalizeDouble(zoneLow - zoneTarget,_Digits);

Bis zu diesem Punkt können wir die Niveaus einstellen, aber sie sind nicht auf dem Chart sichtbar. Um dies zu erreichen, erstellen wir eine Funktion, mit der wir die vier definierten Ebenen im Chart visualisieren.

            drawZoneLevel(ZONE_H,zoneHigh,clrGreen,2);
            drawZoneLevel(ZONE_L,zoneLow,clrRed,2);
            drawZoneLevel(ZONE_T_H,zoneTargetHigh,clrBlue,3);
            drawZoneLevel(ZONE_T_L,zoneTargetLow,clrBlue,3);

Wir erstellen eine einfache void-Funktion, die vier Eingabeparameter oder Argumente benötigt, nämlich LevelName, Preis, CLR und Breite. Wir verwenden die integrierte Funktion ObjectCreate, um eine horizontale Linie zu erstellen, die sich über die gesamte Länge des Charts erstreckt und mit der angegebenen Zeit und dem Preis verbunden ist. Schließlich verwenden wir ObjectSetInteger, um die Farbe des Objekts für die Eindeutigkeit und die Breite für die leichter einstellbare Sichtbarkeit festzulegen. 

void drawZoneLevel(string levelName, double price, color clr, int width) {
   ObjectCreate(0, levelName, OBJ_HLINE, 0, TimeCurrent(), price); // Create a horizontal line object
   ObjectSetInteger(0, levelName, OBJPROP_COLOR, clr); // Set the line color
   ObjectSetInteger(0, levelName, OBJPROP_WIDTH, width); // Set the line width
}

Schließlich setzen wir die letzte Richtung auf den Wert 1, um zu zeigen, dass wir eine Kaufposition eröffnet haben, setzen das nächste Recovery-Volumen als das Ausgangsvolumen multipliziert mit einer Multiplikatorkonstante, in diesem Fall 2, was bedeutet, dass wir das Volumen verdoppeln, und schließlich setzen wir das Flag isBuyDone auf true und isSellDone auf false. 

            lastDirection = 1;
            recovery_lot = 0.01*2;
            isBuyDone = true; isSellDone = false;

Der vollständige Code zum Eröffnen der Position und zum Einrichten der Recovery-Zonen lautet wie folgt. 

   //--- Check for oversold condition and open a buy position
   if (rsiData[1] < overSoldLevel && rsiData[0] > overSoldLevel) {
      obj_trade.Buy(0.01); // Open a buy trade with 0.01 lots
      ulong pos_ticket = obj_trade.ResultOrder();
      if (pos_ticket > 0) {
         if (PositionSelectByTicket(pos_ticket)) {
            double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
            zoneHigh = NormalizeDouble(openPrice, _Digits); // Set the high zone price
            zoneLow = NormalizeDouble(zoneHigh - zoneRange, _Digits); // Set the low zone price
            zoneTargetHigh = NormalizeDouble(zoneHigh + zoneTarget, _Digits); // Set the target high zone price
            zoneTargetLow = NormalizeDouble(zoneLow - zoneTarget, _Digits); // Set the target low zone price
            drawZoneLevel(ZONE_H, zoneHigh, clrGreen, 2); // Draw the high zone line
            drawZoneLevel(ZONE_L, zoneLow, clrRed, 2); // Draw the low zone line
            drawZoneLevel(ZONE_T_H, zoneTargetHigh, clrBlue, 3); // Draw the target high zone line
            drawZoneLevel(ZONE_T_L, zoneTargetLow, clrBlue, 3); // Draw the target low zone line
            
            lastDirection = 1;       // Set the last direction to buy
            recovery_lot = 0.01 * 2; // Set the initial recovery lot size
            isBuyDone = true;        // Mark buy trade as done
            isSellDone = false;      // Reset sell trade flag
         }
      }
   }

Um die Verkaufsposition zu eröffnen und die Recovery-Zonen einzurichten, bleibt die Kontrolllogik bestehen, jedoch mit umgekehrten Bedingungen wie unten.

   else if (rsiData[1] > overBoughtLevel && rsiData[0] < overBoughtLevel) {
      obj_trade.Sell(0.01); // Open a sell trade with 0.01 lots
      ulong pos_ticket = obj_trade.ResultOrder();
      if (pos_ticket > 0) {
         if (PositionSelectByTicket(pos_ticket)) {
            double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
            zoneLow = NormalizeDouble(openPrice, _Digits); // Set the low zone price
            zoneHigh = NormalizeDouble(zoneLow + zoneRange, _Digits); // Set the high zone price
            zoneTargetHigh = NormalizeDouble(zoneHigh + zoneTarget, _Digits); // Set the target high zone price
            zoneTargetLow = NormalizeDouble(zoneLow - zoneTarget, _Digits); // Set the target low zone price
            drawZoneLevel(ZONE_H, zoneHigh, clrGreen, 2); // Draw the high zone line
            drawZoneLevel(ZONE_L, zoneLow, clrRed, 2); // Draw the low zone line
            drawZoneLevel(ZONE_T_H, zoneTargetHigh, clrBlue, 3); // Draw the target high zone line
            drawZoneLevel(ZONE_T_L, zoneTargetLow, clrBlue, 3); // Draw the target low zone line
            
            lastDirection = -1;      // Set the last direction to sell
            recovery_lot = 0.01 * 2; // Set the initial recovery lot size
            isBuyDone = false;       // Reset buy trade flag
            isSellDone = true;       // Mark sell trade as done
         }
      }
   }

Hier prüfen wir, ob die Bedingungen für das Verkaufssignal erfüllt sind, und wenn ja, eröffnen wir sofort eine Verkaufsposition. Dann rufen wir das Ticket ab und verwenden es, um den Recovery-Kurs der Position zu ermitteln, den wir zur Festlegung der Recovery-Zonen verwenden. Da es sich um eine Verkaufsposition handelt, wird ihr Preis zum Zonentief, und um das Zonenhoch zu erhalten, addieren wir einfach den Zonenbereich zum Zonenhoch. In ähnlicher Weise addieren wir das Zonenziel zum Zonenhoch, um das Zonenzielhoch zu erhalten, und subtrahieren das Zonenziel vom Zonentief, um das Zonenzieltief zu erhalten. Zur Veranschaulichung zeichnen wir wieder die vier Ebenen mit Hilfe der Funktionen. Zum Schluss richten wir noch unsere Hilfsvariablen ein.

Bis zu diesem Zeitpunkt ist es uns gelungen, die Positionen auf der Grundlage des vorgelegten Signals zu eröffnen und das System der Zone Recovery einzurichten. Hier ist der vollständige Code, der dies ermöglicht.

void OnTick()
  {
   int bars = iBars(_Symbol,PERIOD_CURRENT);
   if (totalBars == bars) return;
   totalBars = bars;
   
   if (PositionsTotal() > 0) return;
   
   if (!CopyBuffer(rsi_handle,0,1,2,rsiData)) return;
   
   //--- Check for oversold condition and open a buy position
   if (rsiData[1] < overSoldLevel && rsiData[0] > overSoldLevel) {
      obj_trade.Buy(0.01); // Open a buy trade with 0.01 lots
      ulong pos_ticket = obj_trade.ResultOrder();
      if (pos_ticket > 0) {
         if (PositionSelectByTicket(pos_ticket)) {
            double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
            zoneHigh = NormalizeDouble(openPrice, _Digits); // Set the high zone price
            zoneLow = NormalizeDouble(zoneHigh - zoneRange, _Digits); // Set the low zone price
            zoneTargetHigh = NormalizeDouble(zoneHigh + zoneTarget, _Digits); // Set the target high zone price
            zoneTargetLow = NormalizeDouble(zoneLow - zoneTarget, _Digits); // Set the target low zone price
            drawZoneLevel(ZONE_H, zoneHigh, clrGreen, 2); // Draw the high zone line
            drawZoneLevel(ZONE_L, zoneLow, clrRed, 2); // Draw the low zone line
            drawZoneLevel(ZONE_T_H, zoneTargetHigh, clrBlue, 3); // Draw the target high zone line
            drawZoneLevel(ZONE_T_L, zoneTargetLow, clrBlue, 3); // Draw the target low zone line
            
            lastDirection = 1;       // Set the last direction to buy
            recovery_lot = 0.01 * 2; // Set the initial recovery lot size
            isBuyDone = true;        // Mark buy trade as done
            isSellDone = false;      // Reset sell trade flag
         }
      }
   }
   //--- Check for overbought condition and open a sell position
   else if (rsiData[1] > overBoughtLevel && rsiData[0] < overBoughtLevel) {
      obj_trade.Sell(0.01); // Open a sell trade with 0.01 lots
      ulong pos_ticket = obj_trade.ResultOrder();
      if (pos_ticket > 0) {
         if (PositionSelectByTicket(pos_ticket)) {
            double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
            zoneLow = NormalizeDouble(openPrice, _Digits); // Set the low zone price
            zoneHigh = NormalizeDouble(zoneLow + zoneRange, _Digits); // Set the high zone price
            zoneTargetHigh = NormalizeDouble(zoneHigh + zoneTarget, _Digits); // Set the target high zone price
            zoneTargetLow = NormalizeDouble(zoneLow - zoneTarget, _Digits); // Set the target low zone price
            drawZoneLevel(ZONE_H, zoneHigh, clrGreen, 2); // Draw the high zone line
            drawZoneLevel(ZONE_L, zoneLow, clrRed, 2); // Draw the low zone line
            drawZoneLevel(ZONE_T_H, zoneTargetHigh, clrBlue, 3); // Draw the target high zone line
            drawZoneLevel(ZONE_T_L, zoneTargetLow, clrBlue, 3); // Draw the target low zone line
            
            lastDirection = -1;      // Set the last direction to sell
            recovery_lot = 0.01 * 2; // Set the initial recovery lot size
            isBuyDone = false;       // Reset buy trade flag
            isSellDone = true;       // Mark sell trade as done
         }
      }
   }
}   

Wir können das System der Zone Recovery einrichten, aber wir müssen es überwachen und sicherstellen, dass wir es verlassen, sobald wir entweder die Zielniveaus erreichen oder entsprechende Positionen eröffnen, sobald wir die vordefinierten Niveaus erreichen. Wir müssen dies bei jedem Tick tun, und daher muss die Logik vor der Einschränkung der Kerzen-Schleife implementiert werden. Prüfen wir zunächst die Bedingung, unter der die Kurse die Zielniveaus erreichen, und anschließend die Bedingung, unter der die Kurse die Zonenniveaus erreichen. Lasst es uns also tanzen.

         if (zoneTargetHigh > 0 && zoneTargetLow > 0){
            if (bid > zoneTargetHigh || bid < zoneTargetLow){
               obj_trade.PositionClose(_Symbol);
               deleteZoneLevels();
               ...
            }
         }

Hier überprüfen wir, ob das System der Zone Recovery eingestellt ist, indem wir die Logik anwenden, dass die Zielwerte über Null liegen, was bedeutet, dass wir bereits ein System im Einsatz haben. Wenn dies der Fall ist, prüfen wir, ob der Bid-Kurs über dem Zielhoch oder unter dem Zieltief liegt, was darauf hindeutet, dass wir die Erholung der Zone problemlos zerstören können, da ihr Ziel erreicht wurde. Daher löschen wir die Zonenebenen mit der Funktion deleteZoneLevels. Die Funktion, die wir verwenden, ist vom Typ void, da wir nichts zurückgeben müssen, und die eingebaute Funktion ObjectDelete ist implementiert, um die Ebenen zu löschen, indem sie zwei Argumente aufnimmt, den Chartindex und den Objektnamen.

void deleteZoneLevels(){
   ObjectDelete(0,ZONE_H);
   ObjectDelete(0,ZONE_L);
   ObjectDelete(0,ZONE_T_H);
   ObjectDelete(0,ZONE_T_L);
}

Um die Positionen zu schließen, da es zu diesem Zeitpunkt mehrere davon geben könnte, verwenden wir eine Schleife, die alle Positionen berücksichtigt und sie dann einzeln löscht. Dies wird durch den folgenden Code erreicht.

               for (int i = PositionsTotal()-1; i >= 0; i--){
                  ulong ticket = PositionGetTicket(i);
                  if (ticket > 0){
                     if (PositionSelectByTicket(ticket)){
                        obj_trade.PositionClose(ticket);
                     }
                  }
               }

Nachdem wir alle Positionen geschlossen und die Ebenen gelöscht hatten, setzten wir das System auf die Standardeinstellungen zurück, die kein System der Zone Recovery enthalten.

               //closed all, reset all
               zoneHigh=0;zoneLow=0;zoneTargetHigh=0;zoneTargetLow=0;
               lastDirection=0;
               recovery_lot = 0;

Dies wird dadurch erreicht, dass die Zonenniveaus und -ziele auf Null gesetzt werden, abgesehen von der letzten Richtung und dem Erholungslos. Es handelt sich um statische Variablen, weshalb wir sie manuell zurücksetzen müssen. Für die dynamischen Variablen besteht keine Notwendigkeit, da sie häufig automatisch aktualisiert werden.

Der vollständige Code, der für die Zerstörung des Rückgewinnungssystems verantwortlich ist, nachdem seine Ziele erreicht wurden, lautet wie folgt: 

   //--- Close all positions if the bid price is outside target zones
   if (zoneTargetHigh > 0 && zoneTargetLow > 0) {
      if (bid > zoneTargetHigh || bid < zoneTargetLow) {
         obj_trade.PositionClose(_Symbol); // Close the current position
         deleteZoneLevels();               // Delete all drawn zone levels
         for (int i = PositionsTotal() - 1; i >= 0; i--) {
            ulong ticket = PositionGetTicket(i);
            if (ticket > 0) {
               if (PositionSelectByTicket(ticket)) {
                  obj_trade.PositionClose(ticket); // Close positions by ticket
               }
            }
         }
         //--- Reset all zone and direction variables
         zoneHigh = 0;
         zoneLow = 0;
         zoneTargetHigh = 0;
         zoneTargetLow = 0;
         lastDirection = 0;
         recovery_lot = 0;
      }
   }

Wenn wir nun zu den offenen Erholungspositionen übergehen, müssen wir prüfen, ob das System noch im Spiel ist, wie es sich zeigt, wenn die Zonenniveaus über Null liegen. Ist dies der Fall, wird eine Variable lots_rec deklariert, in der die Lose für die Wiederherstellung gespeichert werden. Anschließend normalisieren wir sie auf 3 Dezimalstellen, um die Genauigkeit zu gewährleisten, da es sich bei dem von uns verwendeten Handelskonto um ein Mikrolot-Konto handelt. Dieser Wert kann sich je nach der von Ihnen verwendeten Kontoart ändern. Wenn Sie z. B. ein Standardkonto verwenden, beträgt das Mindestlos 1, und Ihr Wert wird daher 0 sein, um die Nachkommastellen loszuwerden. Die meisten haben 2 Dezimalstellen, aber Sie könnten einen Kontotyp von 0,001 haben, und daher ist Ihr Wert 3, um die Lose auf die nächsten 3 Dezimalstellen zu runden.

   if (zoneHigh > 0 && zoneLow > 0){   
      double lots_Rec = 0;
      lots_Rec = NormalizeDouble(recovery_lot,2);
      ...
   }

Dann prüfen wir, ob der Bid-Kurs über dem Höchststand der Zone liegt, und wenn entweder das vorherige isBuyDone-Flag falsch ist oder der Wert der letzten Richtung kleiner als Null ist, eröffnen wir eine Erholungskaufposition. Nach dem Öffnen der Position setzen wir lastDirection auf 1, was bedeutet, dass es sich bei der zuvor geöffneten Position um eine Kaufposition handelt, berechnen die Recovery-Lots und speichern sie in der Variablen recovery_lot, um sie beim nächsten Aufruf der Recovery Position zu verwenden, und setzen dann das Flag isBuyDone auf true und isSellDone auf false, was anzeigt, dass bereits eine Kaufposition geöffnet wurde. 

  if (bid > zoneHigh) {
         if (isBuyDone == false || lastDirection < 0) {
            obj_trade.Buy(lots_Rec); // Open a buy trade
            
            lastDirection = 1;       // Set the last direction to buy
            recovery_lot = recovery_lot * 2; // Double the recovery lot size
            isBuyDone = true;        // Mark buy trade as done
            isSellDone = false;      // Reset sell trade flag
         }
      }

Andernfalls, wenn der Bid-Kurs unter dem Tiefstkurs der Zone liegt, eröffnen wir die Erholungsverkaufsposition, wie dargestellt.

else if (bid < zoneLow) {
         if (isSellDone == false || lastDirection > 0) {
            obj_trade.Sell(lots_Rec); // Open a sell trade
            
            lastDirection = -1;      // Set the last direction to sell
            recovery_lot = recovery_lot * 2; // Double the recovery lot size
            isBuyDone = false;       // Reset buy trade flag
            isSellDone = true;       // Mark sell trade as done
         }
      }
   }

Der vollständige Code, der nun für das Öffnen der Wiederherstellungspositionen verantwortlich ist, lautet wie folgt:

   //--- Check if price is within defined zones and take action
   if (zoneHigh > 0 && zoneLow > 0) {   
      double lots_Rec = NormalizeDouble(recovery_lot, 2); // Normalize the recovery lot size to 2 decimal places
      if (bid > zoneHigh) {
         if (isBuyDone == false || lastDirection < 0) {
            obj_trade.Buy(lots_Rec); // Open a buy trade
            
            lastDirection = 1;       // Set the last direction to buy
            recovery_lot = recovery_lot * 2; // Double the recovery lot size
            isBuyDone = true;        // Mark buy trade as done
            isSellDone = false;      // Reset sell trade flag
         }
      } else if (bid < zoneLow) {
         if (isSellDone == false || lastDirection > 0) {
            obj_trade.Sell(lots_Rec); // Open a sell trade
            
            lastDirection = -1;      // Set the last direction to sell
            recovery_lot = recovery_lot * 2; // Double the recovery lot size
            isBuyDone = false;       // Reset buy trade flag
            isSellDone = true;       // Mark sell trade as done
         }
      }
   }

Dies ist der Meilenstein, den wir bis jetzt erreicht haben.

   Aktueller Meilenstein

Der vollständige Code, der für die Automatisierung des System der Zone Recovery benötigt wird, ist nachstehend aufgeführt:

//+------------------------------------------------------------------+
//|                                                MARTINGALE EA.mq5 |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"

//--- Define utility variables for later use

#define ZONE_H "ZH"            // Define a constant for the high zone line name
#define ZONE_L "ZL"            // Define a constant for the low zone line name
#define ZONE_T_H "ZTH"         // Define a constant for the target high zone line name
#define ZONE_T_L "ZTL"         // Define a constant for the target low zone line name

//--- Include trade instance class

#include <Trade/Trade.mqh>     // Include the trade class for trading functions
CTrade obj_trade;              // Create an instance of the CTrade class for trading operations

//--- Declare variables to hold indicator data

int rsi_handle;                // Handle for the RSI indicator
double rsiData[];              // Array to store RSI data
int totalBars = 0;             // Variable to keep track of the total number of bars

double overBoughtLevel = 70.0; // Overbought level for RSI
double overSoldLevel = 30.0;   // Oversold level for RSI
double zoneHigh = 0;           // Variable to store the high zone price
double zoneLow = 0;            // Variable to store the low zone price
double zoneTargetHigh = 0;     // Variable to store the target high zone price
double zoneTargetLow = 0;      // Variable to store the target low zone price

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   //--- Initialize the RSI indicator
   rsi_handle = iRSI(_Symbol, PERIOD_CURRENT, 14, PRICE_CLOSE);
   //--- Return initialization result
   return(INIT_SUCCEEDED);
  }

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   //--- Remove RSI indicator from memory
   IndicatorRelease(rsi_handle);
   ArrayFree(rsiData); // Free the RSI data array
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   //--- Retrieve the current Ask and Bid prices
   double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
   double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   double zoneRange = 200 * _Point;       // Define the range for the zones
   double zoneTarget = 400 * _Point;      // Define the target range for the zones
   
   //--- Variables to track trading status
   static int lastDirection = 0;          // -1 = sell, 1 = buy
   static double recovery_lot = 0.0;      // Lot size for recovery trades
   static bool isBuyDone = false, isSellDone = false; // Flags to track trade completion
   
   //--- Close all positions if the bid price is outside target zones
   if (zoneTargetHigh > 0 && zoneTargetLow > 0) {
      if (bid > zoneTargetHigh || bid < zoneTargetLow) {
         obj_trade.PositionClose(_Symbol); // Close the current position
         deleteZoneLevels();               // Delete all drawn zone levels
         for (int i = PositionsTotal() - 1; i >= 0; i--) {
            ulong ticket = PositionGetTicket(i);
            if (ticket > 0) {
               if (PositionSelectByTicket(ticket)) {
                  obj_trade.PositionClose(ticket); // Close positions by ticket
               }
            }
         }
         //--- Reset all zone and direction variables
         zoneHigh = 0;
         zoneLow = 0;
         zoneTargetHigh = 0;
         zoneTargetLow = 0;
         lastDirection = 0;
         recovery_lot = 0;
      }
   }

   //--- Check if price is within defined zones and take action
   if (zoneHigh > 0 && zoneLow > 0) {   
      double lots_Rec = NormalizeDouble(recovery_lot, 2); // Normalize the recovery lot size to 2 decimal places
      if (bid > zoneHigh) {
         if (isBuyDone == false || lastDirection < 0) {
            obj_trade.Buy(lots_Rec); // Open a buy trade
            
            lastDirection = 1;       // Set the last direction to buy
            recovery_lot = recovery_lot * 2; // Double the recovery lot size
            isBuyDone = true;        // Mark buy trade as done
            isSellDone = false;      // Reset sell trade flag
         }
      } else if (bid < zoneLow) {
         if (isSellDone == false || lastDirection > 0) {
            obj_trade.Sell(lots_Rec); // Open a sell trade
            
            lastDirection = -1;      // Set the last direction to sell
            recovery_lot = recovery_lot * 2; // Double the recovery lot size
            isBuyDone = false;       // Reset buy trade flag
            isSellDone = true;       // Mark sell trade as done
         }
      }
   }
   
   //--- Update bars and check for new bars
   int bars = iBars(_Symbol, PERIOD_CURRENT);
   if (totalBars == bars) return; // Exit if no new bars
   totalBars = bars; // Update the total number of bars
   
   //--- Exit if there are open positions
   if (PositionsTotal() > 0) return;
   
   //--- Copy RSI data and check for oversold/overbought conditions
   if (!CopyBuffer(rsi_handle, 0, 1, 2, rsiData)) return;
   
   //--- Check for oversold condition and open a buy position
   if (rsiData[1] < overSoldLevel && rsiData[0] > overSoldLevel) {
      obj_trade.Buy(0.01); // Open a buy trade with 0.01 lots
      ulong pos_ticket = obj_trade.ResultOrder();
      if (pos_ticket > 0) {
         if (PositionSelectByTicket(pos_ticket)) {
            double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
            zoneHigh = NormalizeDouble(openPrice, _Digits); // Set the high zone price
            zoneLow = NormalizeDouble(zoneHigh - zoneRange, _Digits); // Set the low zone price
            zoneTargetHigh = NormalizeDouble(zoneHigh + zoneTarget, _Digits); // Set the target high zone price
            zoneTargetLow = NormalizeDouble(zoneLow - zoneTarget, _Digits); // Set the target low zone price
            drawZoneLevel(ZONE_H, zoneHigh, clrGreen, 2); // Draw the high zone line
            drawZoneLevel(ZONE_L, zoneLow, clrRed, 2); // Draw the low zone line
            drawZoneLevel(ZONE_T_H, zoneTargetHigh, clrBlue, 3); // Draw the target high zone line
            drawZoneLevel(ZONE_T_L, zoneTargetLow, clrBlue, 3); // Draw the target low zone line
            
            lastDirection = 1;       // Set the last direction to buy
            recovery_lot = 0.01 * 2; // Set the initial recovery lot size
            isBuyDone = true;        // Mark buy trade as done
            isSellDone = false;      // Reset sell trade flag
         }
      }
   }
   //--- Check for overbought condition and open a sell position
   else if (rsiData[1] > overBoughtLevel && rsiData[0] < overBoughtLevel) {
      obj_trade.Sell(0.01); // Open a sell trade with 0.01 lots
      ulong pos_ticket = obj_trade.ResultOrder();
      if (pos_ticket > 0) {
         if (PositionSelectByTicket(pos_ticket)) {
            double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
            zoneLow = NormalizeDouble(openPrice, _Digits); // Set the low zone price
            zoneHigh = NormalizeDouble(zoneLow + zoneRange, _Digits); // Set the high zone price
            zoneTargetHigh = NormalizeDouble(zoneHigh + zoneTarget, _Digits); // Set the target high zone price
            zoneTargetLow = NormalizeDouble(zoneLow - zoneTarget, _Digits); // Set the target low zone price
            drawZoneLevel(ZONE_H, zoneHigh, clrGreen, 2); // Draw the high zone line
            drawZoneLevel(ZONE_L, zoneLow, clrRed, 2); // Draw the low zone line
            drawZoneLevel(ZONE_T_H, zoneTargetHigh, clrBlue, 3); // Draw the target high zone line
            drawZoneLevel(ZONE_T_L, zoneTargetLow, clrBlue, 3); // Draw the target low zone line
            
            lastDirection = -1;      // Set the last direction to sell
            recovery_lot = 0.01 * 2; // Set the initial recovery lot size
            isBuyDone = false;       // Reset buy trade flag
            isSellDone = true;       // Mark sell trade as done
         }
      }
   }
}

//+------------------------------------------------------------------+
//|      FUNCTION TO DRAW HORIZONTAL ZONE LINES                      |
//+------------------------------------------------------------------+

void drawZoneLevel(string levelName, double price, color clr, int width) {
   ObjectCreate(0, levelName, OBJ_HLINE, 0, TimeCurrent(), price); // Create a horizontal line object
   ObjectSetInteger(0, levelName, OBJPROP_COLOR, clr); // Set the line color
   ObjectSetInteger(0, levelName, OBJPROP_WIDTH, width); // Set the line width
}

//+------------------------------------------------------------------+
//|       FUNCTION TO DELETE DRAWN ZONE LINES                        |
//+------------------------------------------------------------------+

void deleteZoneLevels() {
   ObjectDelete(0, ZONE_H); // Delete the high zone line
   ObjectDelete(0, ZONE_L); // Delete the low zone line
   ObjectDelete(0, ZONE_T_H); // Delete the target high zone line
   ObjectDelete(0, ZONE_T_L); // Delete the target low zone line
}

Bis zu diesem Punkt haben wir das Forex-Handelssystem „Zone Recovery“ wie vorgesehen erfolgreich automatisiert, und wir werden nun seine Leistung, wie unten gezeigt, testen und sehen, ob es die vorgesehenen Ziele erfüllt.

Backtest-Ergebnisse

Nach dem Test mit dem Strategietester sind die Ergebnisse wie folgt.

Grafik:

   Grafik

Ergebnisse:

Ergebnisse


Schlussfolgerung

In diesem Artikel haben wir uns die grundlegenden Schritte angesehen, die zur Automatisierung der berühmten Zone Recovery Martingale-Strategie in MQL5 durchgeführt werden müssen. Wir haben die grundlegende Definition und Beschreibung der Strategie geliefert und gezeigt, wie sie in MQL5 umgesetzt werden kann. Die Händler können nun das gezeigte Wissen nutzen, um komplexere Systeme zur Zonenrückgewinnung zu entwickeln, die später optimiert werden können, um am Ende bessere Ergebnisse zu erzielen.

Haftungsausschluss: Dieser Code soll Ihnen nur helfen, die Grundlagen für die Erstellung eines Forex-Handelssystems mit Zonenerholung zu erlernen, und die gezeigten Ergebnisse sind keine Garantie für die zukünftige Performance. Setzen Sie also das Wissen sorgfältig ein, um Ihre Systeme zu erstellen und zu optimieren, damit sie zu Ihrem Handelsstil passen.

Der Artikel enthält alle Schritte zur Erstellung des Systems in regelmäßigen Abständen. Wir hoffen, dass diese Informationen für Sie nützlich sind und Ihnen dabei helfen, ein besseres und vollständig optimiertes Zone Recovery System zu entwickeln. Die zur Veranschaulichung dieser Beispiele erforderlichen Dateien sind als Anlagen beigefügt. Sie sollten in der Lage sein, den Code zu studieren und ihn auf Ihre spezifische Strategie anzuwenden, um optimale Ergebnisse zu erzielen.


Übersetzt aus dem Englischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/en/articles/15067

Beigefügte Dateien |
Die Übertragung der Trading-Signale in einem universalen Expert Advisor. Die Übertragung der Trading-Signale in einem universalen Expert Advisor.
In diesem Artikel wurden die verschiedenen Möglichkeiten beschrieben, um die Trading-Signale von einem Signalmodul des universalen EAs zum Steuermodul der Positionen und Orders zu übertragen. Es wurden die seriellen und parallelen Interfaces betrachtet.
Erstellung von Zeitreihenvorhersagen mit neuronalen LSTM-Netzen: Normalisierung des Preises und Tokenisierung der Zeit Erstellung von Zeitreihenvorhersagen mit neuronalen LSTM-Netzen: Normalisierung des Preises und Tokenisierung der Zeit
In diesem Artikel wird eine einfache Strategie zur Normalisierung der Marktdaten anhand der täglichen Spanne und zum Training eines neuronalen Netzes zur Verbesserung der Marktprognosen beschrieben. Die entwickelten Modelle können in Verbindung mit einem bestehenden technischen Analysesystem oder auf eigenständiger Basis verwendet werden, um die allgemeine Marktrichtung vorherzusagen. Der in diesem Artikel skizzierte Rahmen kann von jedem technischen Analysten weiter verfeinert werden, um Modelle zu entwickeln, die sowohl für manuelle als auch für automatisierte Handelsstrategien geeignet sind.
Eine alternative Log-datei mit der Verwendung der HTML und CSS Eine alternative Log-datei mit der Verwendung der HTML und CSS
In diesem Artikel werden wir eine sehr einfache, aber leistungsfähige Bibliothek zur Erstellung der HTML-Dateien schreiben, dabei lernen wir auch, wie man eine ihre Darstellung einstellen kann (nach seinem Geschmack) und sehen wir, wie man es leicht in seinem Expert Advisor oder Skript hinzufügen oder verwenden kann.
Aufbau des Kerzenmodells Trend-Constraint (Teil 5): Nachrichtensystem (Teil II) Aufbau des Kerzenmodells Trend-Constraint (Teil 5): Nachrichtensystem (Teil II)
Heute besprechen wir eine funktionierende Telegram-Integration für MetaTrader 5 Indikator-Benachrichtigungen, die die Leistungsfähigkeit von MQL5 in Zusammenarbeit mit Python und der Telegram Bot API nutzt. Wir werden alles im Detail erklären, damit niemand etwas verpasst. Am Ende dieses Projekts werden Sie wertvolle Erkenntnisse gewonnen haben, die Sie in Ihren Projekten anwenden können.