English 日本語
preview
Automatisieren von Handelsstrategien in MQL5 (Teil 4): Aufbau eines mehrstufigen Zone Recovery Systems

Automatisieren von Handelsstrategien in MQL5 (Teil 4): Aufbau eines mehrstufigen Zone Recovery Systems

MetaTrader 5Handel |
209 0
Allan Munene Mutiiria
Allan Munene Mutiiria

Einführung

Im vorigen Artikel (Teil 3 der Serie) haben wir das Zone Recovery RSI System untersucht und gezeigt, wie man RSI-basierte Signalerzeugung mit einem dynamischen Zone Recovery Mechanismus integriert, um Handelsgeschäfte zu verwalten und sich von ungünstigen Marktbewegungen zu erholen, indem man MetaQuotes Language 5 (MQL5) verwendet. In diesem Artikel (Teil 4) bauen wir auf diesen Grundlagen auf, indem wir ein mehrstufiges Zone Recovery System vorstellen, ein ausgeklügeltes Handelsmanagementkonzept, das mehrere unabhängige Signale gleichzeitig verarbeiten kann.

Dieses System nutzt den Indikator Relative Strength Indicator (RSI) zur Generierung von Handelssignalen und fügt jedes Signal dynamisch in eine Array-Struktur ein, was eine nahtlose Integration mit der Zone Recovery-Logik ermöglicht. Das Hauptziel ist die Skalierung des Rückgewinnungsmechanismus, dem „Recovery“, um mehrere Handelsaufstellungen effizient zu verwalten, die Gesamtauslastung zu verringern und die Handelsergebnisse zu verbessern.

Wir führen Sie durch die Entwicklung des Strategieentwurfs, die Kodierung des Systems in MQL5 und das Backtesting seiner Leistung. Zum besseren Verständnis und zur besseren Organisation haben wir die Schritte wie folgt in Themen unterteilt:

  1. Blaupause der Strategie
  2. Implementation in MQL5
  3. Backtesting
  4. Schlussfolgerung

Am Ende haben Sie ein praktisches Verständnis dafür, wie man ein mehrstufiges Zone Recovery System (Zonenrückgewinnungssystem) für ein dynamisches und robustes Handelsmanagement konstruiert und optimiert.


Blaupause der Strategie

Das mehrstufige Zone Recovery System wird eine gut organisierte Struktur verwenden, um mehrere Handelssignale effektiv zu verwalten. Um dies zu erreichen, wird eine Struktur (struct) definiert, die als Vorlage für die Erstellung der einzelnen „Baskets“ (Körbe) der Handelsgeschäfte dient. Jedes vom RSI-Indikator erzeugte Handelssignal entspricht einem eigenen Basket, der als Element in einem Array gespeichert wird. Wenn das System z.B. Signal 1 generiert, erstellen wir Basket 1, in dem nicht nur die anfänglichen Handelsdetails gespeichert werden, sondern auch alle mit diesem Signal verbundenen Recovery-Positionen verwaltet werden. In ähnlicher Weise wird Signal 2 Basket 2 initiieren, und dieser Basket wird unabhängig alle Handelsgeschäfte der Rückgewinnung auf der Grundlage der Parameter von Signal 2 verfolgen und ausführen. Hier sehen Sie eine Visualisierung der Eigenschaften des Baskets und des Signals.

VISUALISIERUNG DES AUFTRAGSBASKETS

Jeder Basket enthält wichtige Daten wie die Signalrichtung (Kauf oder Verkauf), den Einstiegskurs, die Rückgewinnungsstufen, dynamisch berechnete Losgrößen und andere handelsspezifische Parameter. Sobald neue Signale von RSI identifiziert werden, fügen wir sie dem Array hinzu, um sicherzustellen, dass das System mehrere Signale gleichzeitig verarbeiten kann. Handelsgeschäfte zur Rückgewinnung werden dynamisch berechnet und innerhalb der jeweiligen Baskets ausgeführt, wodurch sichergestellt wird, dass jedes Setup unabhängig und ohne Beeinflussung durch andere verwaltet wird. Hier ist ein Beispiel für die getrennte Behandlung der Signale.

BEISPIEL FÜR GETRENNTE SIGNALE

Durch diese Strukturierung des Systems wird ein hohes Maß an Skalierbarkeit und Flexibilität gewährleistet. Jeder Basket wird als eigenständige Einheit fungieren, sodass das System bei jedem Signal dynamisch auf die Marktbedingungen reagieren kann. Dieses Design vereinfacht die Verfolgung und Verwaltung komplexer Handelseinstellungen, da jedes Signal und die damit verbundenen Rückgewinnungsgeschäfte übersichtlich organisiert sind. Das Array-basierte Basket-System wird als Grundlage für den Aufbau eines robusten und anpassungsfähigen mehrstufigen Zone Recovery Systems dienen, das in der Lage ist, verschiedene Handelsszenarien zu bewältigen und dabei Effizienz und Übersichtlichkeit zu wahren. Fangen wir an.


Implementation in MQL5

Nachdem wir alle Theorien über die mehrstufigen Zone Recovery Handelsstrategie gelernt haben, wollen wir die Theorie automatisieren und einen Expert Advisor (EA) in MetaQuotes Language 5 (MQL5) für MetaTrader 5 erstellen.

Um einen Expert Advisor (EA) zu erstellen, klicken Sie in Ihrem Terminal des MetaTrader 5 auf die Registerkarte Extras und aktivieren Sie MetaQuotes Language Editor oder drücken Sie einfach F4 auf Ihrer Tastatur. Alternativ können Sie auch auf das IDE-Symbol (Integrated Development Environment) in der Symbolleiste klicken. Dadurch wird die Umgebung des MetaQuotes-Spracheditors geöffnet, die das Schreiben von Handelsrobotern, technischen Indikatoren, Skripten und Funktionsbibliotheken ermöglicht. Sobald der MetaEditor geöffnet ist, navigieren wir in der Symbolleiste zur Registerkarte „Datei“ und wählen „Neue Datei“, oder drücken einfach die Tastenkombination STRG + N, um ein neues Dokument zu erstellen. Alternativ könnten wir auch auf das Symbol New auf der Registerkarte Werkzeuge klicken. Daraufhin erscheint ein Popup-Fenster des MQL-Assistenten.

In dem sich öffnenden Assistenten markieren wir die Option Expert Advisor (Template bzw. Vorlage) und klicken auf Weiter (Next). Wir geben in den allgemeinen Eigenschaften des Expert Advisors unter dem Abschnitt Name den Dateinamen Ihres Experten an. Nicht vergessen, den Backslash vor dem Namen des EA verwenden, um einen Ordner anzugeben oder zu erstellen, wenn er nicht existiert. Hier haben wir zum Beispiel standardmäßig „Experts\“. Das bedeutet, dass unser EA im Ordner Experts erstellt wird und wir ihn dort finden können. Die anderen Abschnitte sind ziemlich einfach, aber Sie können dem Link am Ende des Assistenten folgen, um zu erfahren, wie der Prozess genau abläuft.

NEUER EA-NAME

Nachdem Sie den gewünschten Dateinamen des Expert Advisors eingegeben haben, klicken Sie auf Weiter, dann auf Weiter und schließlich auf Fertig stellen. Nachdem wir all dies getan haben, können wir nun unsere Strategie programmieren.

Zunächst definieren wir einige Metadaten über den Expert Advisor (EA). Dazu gehören der Name des EA, die Copyright-Informationen und ein Link zur MetaQuotes-Website. Wir geben auch die Version des EA an, die auf „1.00“ eingestellt ist.

//+------------------------------------------------------------------+
//|                           1. Zone Recovery RSI EA Multi-Zone.mq5 |
//|                                  Copyright 2025, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"

Dadurch werden beim Laden des Programms die System-Metadaten angezeigt. Anschließend können wir einige Eingabevariablen hinzufügen, die auf der Nutzeroberfläche wie folgt angezeigt werden.

sinput group "General EA Settings"
input double inputlot = 0.01;
input double inputzonesizepts = 200;
input double inputzonetragetpts = 400;
input double inputlotmultiplier = 2.0;
input double inputTrailingStopPts = 50; // Trailing stop distance in points
input double inputMinimumProfitPts = 50; // Minimum profit points before trailing stop starts
input bool inputTrailingStopEnabled = true; // Enable or disable trailing stop

Unter der Kategorie „Allgemeine EA-Einstellungen“ definieren wir eine Gruppe von Eingabeparametern, die es dem Nutzer ermöglichen, wesentliche Einstellungen für den Expert Advisor (EA) zu konfigurieren, bevor dieser ausgeführt wird. Diese Eingaben werden unter Verwendung des Eingabedatentyps in MQL5 deklariert, sodass sie direkt über das Eingabeeinstellungsfeld des EAs angepasst werden können, ohne den Code zu ändern. Jeder Eingabeparameter ist spezifisch für die Steuerung des Verhaltens und des Risikomanagements des EA.

Der Parameter „inputlot“ definiert die anfängliche Losgröße für die Eröffnung von Handelsgeschäften mit einem Standardwert von 0,01, was eine genaue Kontrolle des Handelsvolumens ermöglicht. Der Parameter „inputzonesizepts“ gibt die Größe der Rückgewinnungszone in Punkten an, die standardmäßig auf 200 festgelegt ist und den Abstand zwischen den Rückgewinnungsgeschäften bestimmt. Der Parameter „inputzonetragetpts“, mit einem Standardwert von 400, legt den Zielgewinnabstand in Punkten fest, der dem EA vorgibt, wann er Positionen gewinnbringend schließen soll.

Um Handelsgeschäfte zur Rückgewinnung zu handhaben, verwenden wir den Parameter „inputlotmultiplier“, der standardmäßig auf 2.0 gesetzt ist und es dem EA ermöglicht, dynamisch steigende Losgrößen für Handelsgeschäften zur Rückgewinnung auf Basis des Multiplikators zu berechnen. Zusätzlich wird die Funktionsweise eines Trailing-Stop durch drei Parameter eingeführt. Die „inputTrailingStopPts“ definiert den Abstand des Trailing-Stop in Punkten, der auf 50 gesetzt ist und den Stop-Loss anpasst, wenn sich der Markt zu Gunsten des Handelsgeschäfts bewegt. Der Parameter „inputMinimumProfitPts“, der ebenfalls auf 50 gesetzt ist, stellt sicher, dass der Trailing-Stop erst dann aktiviert wird, wenn der Handel eine Mindestgewinnschwelle erreicht.

Mit dem Parameter „inputTrailingStopEnabled“, einem boolschen Datentyp, kann der Nutzer die Funktion des Trailing-Stop je nach Bedarf aktivieren oder deaktivieren. Diese Flexibilität stellt sicher, dass sich der EA an unterschiedliche Handelsstrategien, Risikoprofile und Marktbedingungen anpassen kann und so einen anpassbaren Rahmen für effizientes Handels- und Risikomanagement bietet. Als Nächstes müssen wir einige zusätzliche Dateien einbinden, damit wir eine Handelsinstanz mit #include einbinden können, da wir Handelsgeschäfte eröffnen werden.  Dadurch erhalten wir Zugriff auf die Klasse „CTrade“, die wir zur Erstellung eines Handelsobjekts verwenden werden. Dies ist von entscheidender Bedeutung, da wir sie zur Eröffnung von Handelsgeschäften benötigen.

#include <Trade/Trade.mqh>
//--- Includes the MQL5 Trade library for handling trading operations.

Der Präprozessor wird die Zeile #include <Trade/Trade.mqh> durch den Inhalt der Datei Trade.mqh ersetzen. Die spitzen Klammern zeigen an, dass die Datei Trade.mqh aus dem Standardverzeichnis entnommen wird (normalerweise ist es das Terminal-Installationsverzeichnis\MQL5\Include). Das aktuelle Verzeichnis wird bei der Suche nicht berücksichtigt. Die Zeile kann an beliebiger Stelle im Programm platziert werden, aber in der Regel werden alle Einbindungen am Anfang des Quellcodes platziert, um den Code besser zu strukturieren und die Referenz zu erleichtern. Hier ist die spezifische Datei aus dem Abschnitt über den Navigator.

DIE KLASSE CTRADE

Danach müssen wir mehrere wichtige globale Variablen deklarieren, die wir im Handelssystem verwenden werden.

//--- Global variables for RSI logic
int rsiPeriod = 14;                //--- The period used for calculating the RSI indicator.
int rsiHandle;                     //--- Handle for the RSI indicator, used to retrieve RSI values.
double rsiBuffer[];                //--- Array to store the RSI values retrieved from the indicator.
datetime lastBarTime = 0;          //--- Holds the time of the last processed bar to prevent duplicate signals.

Hier definieren wir eine Reihe von globalen Variablen, um die Logik des RSI-Indikators zu verwalten, der die Handelssignale des EA steuert. Diese Variablen sind so konzipiert, dass sie die Berechnung, den Abruf und die Verarbeitung von RSI-Werten effizient handhaben. Indem wir sie als global deklarieren, stellen wir sicher, dass sie im gesamten EA zugänglich sind und eine konsistente und effektive Signalerzeugung ermöglichen. Wir setzen die Variable „rsiPeriod“ mit dem int auf 14 und geben damit den Rückblickzeitraum für die Berechnung des RSI-Indikators an. Dieser Wert bestimmt die Anzahl der Balken, die der EA zur Berechnung der RSI-Werte analysiert, und gibt uns die Kontrolle über die Empfindlichkeit des Indikators. Als Nächstes deklarieren wir die Variable „rsiHandle“, eine weitere int-Variable, der wir das Handle für den RSI-Indikator zuweisen. Dieses Handle erhalten wir, wenn wir den RSI mit der Funktion iRSI initialisieren, sodass wir die RSI-Werte direkt aus dem Indikatorpuffer des Terminals abrufen können.

Um diese RSI-Werte zu speichern, erstellen wir „rsiBuffer[]“, ein dynamisches Array mit dem Datentyp double. Dieses Array enthält die berechneten RSI-Werte für jeden Balken, die wir verwenden werden, um überkaufte oder überverkaufte Bedingungen auf dem Markt zu identifizieren. Zusätzlich definieren wir die Variable „lastBarTime“, einen „datetime“-Typ, um die Zeit des letzten verarbeiteten Balkens zu speichern. Indem wir diesen Wert verfolgen, stellen wir sicher, dass der EA nur dann ein neues Signal verarbeitet, wenn ein neuer Balken erscheint, und verhindern so doppelte Signale für denselben Balken. Wir können nun die gemeinsamen Parameter des „Basket“ in einer Struktur definieren, die wir mit jedem erzeugten Signal verknüpfen. Um dies zu erreichen, verwenden wir die Logik von struct, deren allgemeine Syntax wie folgt lautet:

//--- Struct to track individual position recovery states
struct PositionRecovery {
        //--- Member 1
        //--- Member 2
        //--- Member 3

        //--- Method 1
        //...
};

Um zusammengehörige Datenvariablen zu gruppieren, benötigen wir eine Struktur, und hier ist der allgemeine Prototyp dafür. Wir definieren eine Struktur mit dem Namen „PositionRecovery“, die als Blaupause für die Organisation und Verwaltung von Daten im Zusammenhang mit einzelnen Positionszuständen für die Rückgewinnung innerhalb des EA dient. Die Struktur fungiert als nutzerdefinierter Datentyp, der es uns ermöglicht, verwandte Variablen (Mitglieder) und Funktionen (Methoden) in einer einzigen Einheit zu gruppieren.

Erläuterung der Syntax:

„struct“ „PositionRecovery { ... };“

Damit wird eine Struktur mit dem Namen „PositionRecovery“ deklariert. Das Schlüsselwort struct wird verwendet, um die Struktur zu definieren, und die { ... } Klammern umschließen die Mitglieder und Methoden der Struktur. Das Semikolon (;) am Ende der Definition ist in MQL5 obligatorisch.

  • Mitglieder

Mitglieder sind innerhalb der Struktur definierte Variablen, die für jede Instanz von „PositionRecovery“ spezifische Daten speichern.

„//--- Mitglied 1": Platzhalter für eine Variable, z. B. die anfängliche Losgröße oder den Einstiegskurs des Handelsgeschäfts.

„//--- Mitglied 2": Dies können Parameter wie die Größe der Rückgewinnungszone oder der aktuelle Handelsstatus sein.

„//--- Mitglied 3": Zusätzliche Daten wie die Anzahl der ausgeführten Rückgewinnungsvorgänge oder ein Kennzeichen, das den Abschluss der Rückgewinnung anzeigt.

Diese Mitglieder ermöglichen es uns, alle Informationen zu erfassen, die zur Verfolgung und Verwaltung eines individuellen Rückgewinnungsprozesses erforderlich sind.

  • Methoden

Methoden sind Funktionen, die innerhalb der Struktur definiert sind und auf ihre Mitglieder wirken.

„//--- Methode 1": Ein Platzhalter für eine Methode, wie z. B. die Berechnung der nächsten Losgröße des „Recovery Trade“ oder die Überprüfung, ob das Ziel erreicht wurde.

Durch die Kombination von Daten (Mitgliedern) und Logik (Methoden) wird die Struktur vielseitiger und in sich geschlossener. Nachdem wir das verstanden haben, können wir nun mit der Definition der Strukturelemente beginnen.

//--- Struct to track individual position recovery states
struct PositionRecovery {
   CTrade trade;                    //--- Object to handle trading operations.
   double initialLotSize;           //--- Initial lot size for this position.
   double currentLotSize;           //--- Current lot size in the recovery sequence.
   double zoneSize;                 //--- Distance in points defining the recovery zone size.
   double targetSize;               //--- Distance in points defining the profit target range.
   double multiplier;               //--- Lot size multiplier for recovery trades.
   string symbol;                   //--- Trading symbol.
   ENUM_ORDER_TYPE lastOrderType;   //--- Type of the last order (BUY or SELL).
   double lastOrderPrice;           //--- Price of the last executed order.
   double zoneHigh;                 //--- Upper boundary of the recovery zone.
   double zoneLow;                  //--- Lower boundary of the recovery zone.
   double zoneTargetHigh;           //--- Upper boundary of the target range.
   double zoneTargetLow;            //--- Lower boundary of the target range.
   bool isRecovery;                 //--- Whether the recovery is active.
   ulong tickets[];                 //--- Array to store tickets of positions associated with this recovery.
   double trailingStop;             //--- Trailing stop level
   double initialEntryPrice;        //--- Initial entry price for trailing stop calculation

   //---

};

Hier erstellen wir eine Struktur mit dem Namen „PositionRecovery“, um alle Daten zu organisieren und zu verwalten, die für die Verfolgung der einzelnen Positionszustände für die erforderlich sind. Durch diese Struktur stellen wir sicher, dass jeder Rückgewinnungsprozess unabhängig behandelt wird, sodass wir mehrere Signale effektiv verwalten können.

Wir definieren das Objekt „CTrade trade“, das wir zur Ausführung von Handelsoperationen wie Eröffnung, Änderung und Schließung von Aufträgen im Zusammenhang mit dieser Rückgewinnung verwenden werden. Wir richten „initialLotSize“ ein, um die Größe des ersten Handels in der Sequenz zu speichern, während „currentLotSize“ uns hilft, die Losgröße des letzten Handels im Rückgewinnungsprozess zu verfolgen. Um die Rückgewinnungsstrategie zu steuern, geben wir den Abstand der Rückgewinnungszone in Punkten mit „zoneSize“ an und definieren den Bereich des Gewinnziels mit „targetSize“.

Um die dynamische Losgröße zu handhaben, führen wir einen „Multiplikator“ ein, den wir zur Berechnung der Losgröße für jeden nachfolgenden Rückgewinnungshandel verwenden werden. Wir fügen „symbol“ hinzu, um das Handelsinstrument für diese Rückgewinnung zu identifizieren und sicherzustellen, dass der EA Handelsgeschäfte mit dem richtigen Symbol ausführt. Wir verwenden den Typ einer Enumeration ENUM_ORDER_TYPE, um eine Variable „lastOrderType“ zu deklarieren, die den Typ der zuletzt ausgeführten Order (z.B. BUY oder SELL) speichert, und „lastOrderPrice“, um den Ausführungspreis zu erfassen, was uns hilft, den aktuellen Stand der Rückgewinnung zu verfolgen. Für die Überwachung der Rückgewinnungszonen definieren wir „zoneHigh“ und „zoneLow“ als obere und untere Grenze der Rückgewinnungszone, während „zoneTargetHigh“ und „zoneTargetLow“ den Bereich des Gewinnziels markieren.

Um festzustellen, ob eine Rückgewinnung aktiv ist, verwenden wir „isRecovery“, ein Flag, das wir je nach Bedarf auf true oder false setzen. Wir fügen auch „tickets[]“ hinzu, ein Array, in dem wir die Ticketnummern aller Handelsgeschäfte in der Rückgewinnungssequenz speichern, sodass wir sie einzeln verfolgen und verwalten können. Schließlich fügen wir „trailingStop“ ein, um den Abstand der Trailing-Stops festzulegen, und „initialEntryPrice“, um den Einstiegskurs des ersten Handels zu erfassen, den wir für die Berechnung der Trailing-Stops verwenden werden. Diese Komponenten ermöglichen es uns, die Gewinne während des Aufschwungs dynamisch zu schützen.

Nachdem wir die Mitgliedsvariablen definiert haben, müssen wir sie für jede Instanz, die erstellt wird, oder besser gesagt für jeden Basket, initialisieren. Zu diesem Zweck können wir eine Methode erstellen, die die Initialisierung reibungslos abwickelt.

//--- Initialize position recovery
void Initialize(double lot, double zonePts, double targetPts, double lotMultiplier, string _symbol, ENUM_ORDER_TYPE type, double price) {
   initialLotSize = lot;             //--- Assign initial lot size.
   currentLotSize = lot;             //--- Set current lot size equal to initial lot size.
   zoneSize = zonePts * _Point;      //--- Calculate zone size in points.
   targetSize = targetPts * _Point;  //--- Calculate target size in points.
   multiplier = lotMultiplier;       //--- Assign lot size multiplier.
   symbol = _symbol;                 //--- Assign the trading symbol.
   lastOrderType = type;             //--- Set the type of the last order.
   lastOrderPrice = price;           //--- Record the price of the last executed order.
   isRecovery = false;               //--- Set recovery as inactive initially.
   ArrayResize(tickets, 0);          //--- Initialize the tickets array.
   trailingStop = 0;                 //--- Initialize trailing stop
   initialEntryPrice = price;        //--- Set initial entry price
   CalculateZones();                 //--- Calculate recovery and target zones.
}

Wir definieren die Methode „Initialize“, die für die Einstellung aller notwendigen Parameter und die Initialisierung des Zustands für eine individuelle Positionswiederherstellung verantwortlich ist. Diese Methode stellt sicher, dass jede Rückgewinnungsinstanz korrekt konfiguriert und bereit ist, auf der Grundlage der angegebenen Eingabewerte dynamisch Handelsgeschäfte zu verwalten. Wir beginnen damit, dass wir den Wert von „lot“ „initialLotSize“ zuweisen, der die Größe des ersten Handelsgeschäfts in der Rückgewinnungssequenz angibt. Gleichzeitig setzen wir „currentLotSize“ gleich „initialLotSize“, da der erste Handel die gleiche Lotgröße verwendet. Als Nächstes berechnen wir die Größe der Rückgewinnungszone und den Bereich des Gewinnziels in Punkten mit Hilfe der Eingaben „zonePts“ und „targetPts“ und multiplizieren sie mit der Konstante „_Point“, um den Punktwert des Symbols zu berücksichtigen. Mit diesen Berechnungen werden die Entfernungsschwellen für die Verwaltung von Verwertungsgeschäften und deren Ziele festgelegt.

Wir weisen „lotMultiplier“ der Variablen für die „Multiplikation“ zu, die festlegt, wie sich die Losgröße bei nachfolgenden Handelsgeschäften zur Rückgewinnung erhöht. Das Handelssymbol wird „symbol“ zugewiesen, um sicherzustellen, dass alle Handelsgeschäfte in dieser Verwertungsinstanz auf dem richtigen Marktinstrument ausgeführt werden. Wir setzen den „lastOrderType“ auf den angegebenen „type“-Parameter und den „lastOrderPrice“ auf „price“, um die Details der letzten Bestellung zu erfassen. Anhand dieser Werte können wir den Stand des derzeitigen Rückgewinnung verfolgen. Außerdem wird „isRecovery“ auf false initialisiert, was bedeutet, dass der Rückgewinnungsprozess bei der ersten Erstellung nicht aktiv ist.

Mit der Funktion „ArrayResize“ wird die Größe des Arrays „tickets“ auf Null gesetzt, wobei alle vorhandenen Daten gelöscht und das Array darauf vorbereitet wird, die Ticketnummern der mit dieser Rückgewinnungsinstanz verbundenen Handelsgeschäfte zu speichern. Für zusätzliche Flexibilität initialisieren wir „trailingStop“ auf 0 und setzen „initialEntryPrice“ auf „price“, um eine Basislinie für die Berechnung des Trailing-Stops zu erhalten. Schließlich rufen wir die Methode „CalculateZones“ auf, die die oberen und unteren Grenzen für die Rückgewinnungszone und den Zielbereich berechnet. Dieser Schritt stellt sicher, dass der EA über alle erforderlichen Daten verfügt, um den Handel effizient zu verwalten. Mit der Methode „Initialize“ schaffen wir einen vollständigen und genau definierten Ausgangspunkt für jeden Verwertungsprozess und stellen sicher, dass alle relevanten Parameter für ein effektives Handelsmanagement korrekt eingestellt sind. Anschließend können wir die Funktion „CalculateZones“ definieren, die für die Berechnung der Rückgewinnungsbereiche zuständig ist.

//--- Calculate dynamic zones and targets
void CalculateZones() {
   if (lastOrderType == ORDER_TYPE_BUY) { //--- If the last order was a BUY...
      zoneHigh = lastOrderPrice;         //--- Set upper boundary at the last order price.
      zoneLow = zoneHigh - zoneSize;     //--- Set lower boundary below the last order price.
      zoneTargetHigh = zoneHigh + targetSize; //--- Define target range above recovery zone.
      zoneTargetLow = zoneLow - targetSize;   //--- Define target range below recovery zone.
   } else if (lastOrderType == ORDER_TYPE_SELL) { //--- If the last order was a SELL...
      zoneLow = lastOrderPrice;                //--- Set lower boundary at the last order price.
      zoneHigh = zoneLow + zoneSize;           //--- Set upper boundary above the last order price.
      zoneTargetLow = zoneLow - targetSize;    //--- Define target range below recovery zone.
      zoneTargetHigh = zoneHigh + targetSize;  //--- Define target range above recovery zone.
   }
}

Hier definieren wir die Methode „CalculateZones“, die dynamisch die Grenzen der Rückgewinnungszonen und die Bereich des Gewinnzielse auf der Grundlage des Typs des zuletzt ausgeführten Auftrags und seines Preises berechnet. Diese Methode stellt sicher, dass jeder Rückgewinnungsprozess klar definierte Niveaus hat, die die nachfolgenden Handelsentscheidungen leiten, sodass das System angemessen auf Marktbewegungen reagieren kann.

Wir beginnen mit der Überprüfung des „lastOrderType“, um festzustellen, ob der letzte Auftrag ein Kauf oder Verkauf war. Wenn „lastOrderType“ ORDER_TYPE_BUY ist, weisen wir „zoneHigh“ dem „lastOrderPrice“ zu und setzen die obere Grenze der Rückgewinnungszone auf den Eröffnungskurs des letzten Kaufauftrags. Die untere Grenze, „zoneLow“, wird dann durch Subtraktion der „zoneSize“ (umgerechnet in Punkte) von „zoneHigh“ berechnet. Zusätzlich definieren wir den Bereich des Gewinnziels: „zoneTargetHigh“ wird berechnet, indem „targetSize“ zu „zoneHigh“ addiert wird, während „zoneTargetLow“ berechnet wird, indem „targetSize“ von „zoneLow“ subtrahiert wird. Diese Berechnungen stellen sicher, dass die Rückgewinnungszone und der Bereich des Gewinnziels relativ zum Preis der Kauf-Auftrag positioniert sind.

Wenn „lastOrderType“ ORDER_TYPE_SELL ist, kehren wir die Logik um. In diesem Fall weisen wir „zoneLow“ dem „lastOrderPrice“ zu und setzen die untere Grenze der Rückgewinnungszone auf den Eröffnungskurs des letzten Verkaufsauftrags. Die obere Grenze, „zoneHigh“, wird berechnet, indem „zoneSize“ zu „zoneLow“ addiert wird. Für den Bereich des Gewinnziels berechnen wir „zoneTargetLow“, indem wir „targetSize“ von „zoneLow“ subtrahieren und „zoneTargetHigh“, indem wir „targetSize“ zu „zoneHigh“ addieren. Diese Grenzen werden relativ zum Preis des Verkaufsauftrags festgelegt. Diese Definitionen der Stufen würden das unten dargestellte Bild ergeben:

VISUALISIERUNG DER SYSTEMSTUFEN

Nach der Festlegung der Zonenniveaus können wir mit der Eröffnung der Positionen fortfahren. Wir werden die Logik der Positionseröffnung in einer Methode kapseln, um die Verwendung innerhalb der Codestruktur zu erleichtern.

//--- Open a trade with comments for position type
bool OpenTrade(ENUM_ORDER_TYPE type, string comment) {
   if (type == ORDER_TYPE_BUY) { //--- For a BUY order...
      if (trade.Buy(currentLotSize, symbol, 0, 0, 0, comment)) { //--- Attempt to place a BUY trade.
         lastOrderType = ORDER_TYPE_BUY;                        //--- Update the last order type.
         lastOrderPrice = SymbolInfoDouble(symbol, SYMBOL_BID); //--- Record the current price.
         ArrayResize(tickets, ArraySize(tickets) + 1);          //--- Resize the tickets array.
         tickets[ArraySize(tickets) - 1] = trade.ResultOrder(); //--- Store the new ticket.
         CalculateZones();                                      //--- Recalculate zones.
         isRecovery = false;                                    //--- Ensure recovery is inactive for initial trade.
         Print("Opened BUY Position, Ticket: ", tickets[ArraySize(tickets) - 1]);
         return true;                                           //--- Return success.
      }
   } else if (type == ORDER_TYPE_SELL) { //--- For a SELL order...
      if (trade.Sell(currentLotSize, symbol, 0, 0, 0, comment)) { //--- Attempt to place a SELL trade.
         lastOrderType = ORDER_TYPE_SELL;                        //--- Update the last order type.
         lastOrderPrice = SymbolInfoDouble(symbol, SYMBOL_BID);  //--- Record the current price.
         ArrayResize(tickets, ArraySize(tickets) + 1);           //--- Resize the tickets array.
         tickets[ArraySize(tickets) - 1] = trade.ResultOrder();  //--- Store the new ticket.
         CalculateZones();                                       //--- Recalculate zones.
         isRecovery = false;                                     //--- Ensure recovery is inactive for initial trade.
         Print("Opened SELL Position, Ticket: ", tickets[ArraySize(tickets) - 1]);
         return true;                                            //--- Return success.
      }
   }
   return false; //--- If the trade was not placed, return false.
}

In dieser booleschen Funktion „OpenTrade“ wird die Logik zur Eröffnung eines neuen Handelsgeschäfts eines bestimmten Typs (BUY oder SELL) behandelt und die erforderlichen Aktualisierungen des Rückgewinnungssystems verwaltet. Diese Funktion stellt sicher, dass die Handelsgeschäfte korrekt geöffnet werden und dass alle zugehörigen Daten aktualisiert werden, um die Synchronisation mit dem Rückgewinnungsprozess aufrechtzuerhalten. Wenn der Parameter „type“ ORDER_TYPE_BUY lautet, wird versucht, eine Kaufposition mit der Methode „trade.Buy“ zu eröffnen. Die Methode verwendet die Parameter „currentLotSize“, „symbol“ und „comment“, um den Handel auszuführen, wobei Stop-Loss und Take-Profit auf Null (nicht angegeben) belassen werden, da wir diese dynamisch gemäß den Zielbereichen der Zone definieren. Wenn die Kaufposition erfolgreich platziert wurde, aktualisieren wir „lastOrderType“ auf ORDER_TYPE_BUY, was den Typ des letzten Handelsgeschäfts angibt, und „lastOrderPrice“ wird auf den aktuellen Marktpreis gesetzt, der mit der Funktion SymbolInfoDouble mit dem Parameter „SYMBOL_BID“ abgerufen wird.

Als Nächstes wird die Größe des Arrays „tickets“ mit der Funktion ArrayResize angepasst, um Platz für den neuen Handel zu schaffen, und die Ticketnummer des erfolgreich platzierten Handels wird mit „trade.ResultOrder()“ gespeichert. Dadurch wird sichergestellt, dass alle mit dieser Verwertungsinstanz zusammenhängenden Vorgänge effizient verfolgt und gespeichert werden. Anschließend rufen wir die Funktion „CalculateZones“ auf, um die Rückgewinnungs- und Zielzonen auf der Grundlage des letzten Handels neu zu berechnen. Schließlich setzen wir „isRecovery“ auf false, da dies der erste Handel ist und nicht Teil eines Rückgewinnungsprozesses. Eine Erfolgsmeldung wird in das Protokoll gedruckt, und die Funktion gibt true zurück, um anzuzeigen, dass der Handel erfolgreich eröffnet wurde.

Wenn „type“ „ORDER_TYPE_SELL“ ist, folgen wir einer ähnlichen Logik. Die Methode „trade.Sell“ wird aufgerufen, um einen Verkaufsauftrag mit den angegebenen Parametern abzusenden. Bei Erfolg wird „lastOrderType“ auf ORDER_TYPE_SELL aktualisiert und der „lastOrderPrice“ als aktueller Marktpreis erfasst. Die Größe des Arrays „tickets“ wird geändert, und das neue Ticket wird gespeichert, genau wie bei einem Kaufauftrag. Die Zonen werden mit „CalculateZones“ neu berechnet, und „isRecovery“ wird auf false gesetzt. Es wird eine Erfolgsmeldung ausgegeben, und die Funktion gibt true zurück.

Wenn der Handel für eine der beiden Auftragsarten fehlschlägt, gibt die Funktion den Wert false zurück und signalisiert damit, dass der Vorgang nicht erfolgreich war. Diese Struktur stellt sicher, dass die Handelsgeschäfte systematisch verwaltet werden und dass alle verwertungsbezogenen Daten korrekt aktualisiert werden, um eine nahtlose Verwaltung der Handelsgeschäfte zu gewährleisten. Nachdem die Positionen eröffnet und die Zonenniveaus berechnet wurden, können wir diese Zonen bei jedem Tick weiter verwalten, um die Rückgewinnungspositionen zu öffnen, wenn eines der Niveaus erreicht wird.

//--- Manage zone recovery
void ManageZones() {
   double currentPrice = SymbolInfoDouble(symbol, SYMBOL_BID); //--- Get the current price.
   if (lastOrderType == ORDER_TYPE_BUY && currentPrice <= zoneLow) { //--- If price drops below the recovery zone for a BUY...
      double previousLotSize = currentLotSize;                       //--- Store the current lot size temporarily.
      currentLotSize *= multiplier;                                 //--- Tentatively increase lot size.
      if (OpenTrade(ORDER_TYPE_SELL, "Recovery Position")) {        //--- Attempt to open a SELL recovery trade.
         isRecovery = true;                                         //--- Mark recovery as active if trade is successful.
      } else {
         currentLotSize = previousLotSize;                          //--- Revert the lot size if the trade fails.
      }
   } else if (lastOrderType == ORDER_TYPE_SELL && currentPrice >= zoneHigh) { //--- If price rises above the recovery zone for a SELL...
      double previousLotSize = currentLotSize;                       //--- Store the current lot size temporarily.
      currentLotSize *= multiplier;                                 //--- Tentatively increase lot size.
      if (OpenTrade(ORDER_TYPE_BUY, "Recovery Position")) {         //--- Attempt to open a BUY recovery trade.
         isRecovery = true;                                         //--- Mark recovery as active if trade is successful.
      } else {
         currentLotSize = previousLotSize;                          //--- Revert the lot size if the trade fails.
      }
   }
}

Wir deklarieren die Funktion „ManageZones“, um den Marktpreis der Rückgewinnungszonen zu überwachen und Maßnahmen zu ergreifen, wenn sich der Preis gegen den ursprünglichen Handel bewegt. Zunächst wird der aktuelle Marktpreis mit der Funktion SymbolInfoDouble abgefragt, um den letzten Geldkurs zu erhalten. Anschließend wird geprüft, ob der Preis die Grenzen der Rückgewinnungszone verlassen hat, die durch „zoneLow“ für einen Kaufauftrag und „zoneHigh“ für einen Verkaufsauftrag definiert ist.

Wenn zuletzt ein Kaufauftrag (angezeigt durch „lastOrderType“ == ORDER_TYPE_BUY) erteilt wurde und der aktuelle Preis unter „zoneLow“ fällt, erhöhen wir die Losgröße für den Rückgewinnungshandel. Wir speichern die aktuelle Losgröße in „previousLotSize“ und multiplizieren dann „currentLotSize“ mit dem „Multiplikator“, um sie zu erhöhen. Danach versuchen wir, über die Funktion „OpenTrade“ eine Verkaufsposition für den Rückgewinnungshandel zu eröffnen. Wenn der Rückgewinnungshandel erfolgreich platziert wurde, setzen wir „isRecovery“ auf true, um zu markieren, dass die Rückgewinnung aktiv ist. Wenn der Handel fehlschlägt, setzen wir die Losgröße auf den ursprünglichen Wert zurück, der in „previousLotSize“ gespeichert ist.

Wenn der letzte Auftrag ein VERKAUF war (angezeigt durch „lastOrderType“ == ORDER_TYPE_SELL) und der Preis über „zoneHigh“ steigt, wenden wir die gleiche Logik an, um einen Rückgewinnungshandel durch eine Kaufposition zu eröffnen. Die Losgröße wurde erhöht, und wir versuchen, eine Kaufposition zu eröffnen. Bei Erfolg wird „isRecovery“ auf true gesetzt, aber wenn der Handel fehlschlägt, wird die Losgröße zurückgesetzt. Dadurch wird sichergestellt, dass das System Handelsgeschäfte zur Rückgewinnung effektiv verwaltet, Positionsgrößen anpasst und Korrekturmaßnahmen auf der Grundlage der Marktbedingungen ergreift. Schließlich müssen wir die Positionen schließen, wenn der Kurs die festgelegten Zielmarken erreicht.

//--- Check and close trades at targets
void CheckCloseAtTargets() {
   double currentPrice = SymbolInfoDouble(symbol, SYMBOL_BID); //--- Get the current price.
   if (lastOrderType == ORDER_TYPE_BUY && currentPrice >= zoneTargetHigh) { //--- If price reaches the target for a BUY...
      ClosePositionsAtTarget();                               //--- Close positions that meet the target criteria.
   } else if (lastOrderType == ORDER_TYPE_SELL && currentPrice <= zoneTargetLow) { //--- If price reaches the target for a SELL...
      ClosePositionsAtTarget();                               //--- Close positions that meet the target criteria.
   }
}

Hier definieren wir eine ungültige Funktion „CheckCloseAtTargets“, um zu prüfen, ob der Marktpreis die vordefinierten Zielwerte erreicht hat, und die Positionen zu schließen, die die Zielkriterien erfüllen. Zunächst wird der aktuelle Geldkurs mit „SymbolInfoDouble (symbol, SYMBOL_BID)“ abgefragt. Dann vergleichen wir diesen Preis mit den durch „zoneTargetHigh“ für einen Kaufauftrag und „zoneTargetLow“ für einen Verkaufsauftrag definierten Zielniveaus.

Wenn der letzte Handel ein KAUFEN war (angezeigt durch „lastOrderType“ == ORDER_TYPE_BUY) und der aktuelle Kurs auf oder über „zoneTargetHigh“ steigt, hat die Position das gewünschte Gewinnziel erreicht. In diesem Fall rufen wir die Funktion „ClosePositionsAtTarget“ auf, um alle Positionen zu schließen, die die Zielkriterien erfüllen. Ähnlich verhält es sich, wenn der letzte Auftrag ein VERKAUF war (angezeigt durch „lastOrderType“ == ORDER_TYPE_SELL) und der Preis auf oder unter „zoneTargetLow“ fällt, ruft das System erneut „ClosePositionsAtTarget“ auf, um die Positionen zu schließen. Diese Funktion stellt sicher, dass die Positionen geschlossen werden, wenn der Markt das festgelegte Gewinnziel erreicht, wodurch die Gewinne gesichert und der Rückgewinnungsprozess abgeschlossen wird.

Um die Positionen zu schließen, haben wir eine Funktion namens „ClosePositionsAtTarget“ verwendet, damit wir sie wiederverwenden können. Hier ist der Funktionsausschnitt.

//--- Close positions that have reached the target
void ClosePositionsAtTarget() {
   for (int i = ArraySize(tickets) - 1; i >= 0; i--) {              //--- Iterate through all tickets.
      ulong ticket = tickets[i];                                    //--- Get the position ticket.
      int retries = 10;                                             //--- Set retry count.
      while (retries > 0) {                                         //--- Retry until successful or retries exhausted.
         if (trade.PositionClose(ticket)) {                         //--- Attempt to close the position.
            Print("CLOSED # ", ticket, " Trailed and closed: ", (trailingStop != 0));
            ArrayRemove(tickets, i);                                //--- Remove the ticket from the array on success.
            retries = 0;                                            //--- Exit the loop on success.
         } else {
            retries--;                                              //--- Decrement retries on failure.
            Sleep(100);                                             //--- Wait before retrying.
         }
      }
   }
   if (ArraySize(tickets) == 0) {                                   //--- If all tickets are closed...
      Reset();                                                      //--- Reset recovery state after closing the target positions.
   }
}

In der Funktion „ClosePositionsAtTarget“ werden alle offenen Positionen, die im Array „tickets“ gespeichert sind, durchlaufen und versucht, diejenigen zu schließen, die das Zielniveau erreicht haben. Wir beginnen mit einer Schleife durch das Array „Tickets“ in umgekehrter Reihenfolge, um sicherzustellen, dass wir keine Positionen überspringen, während wir sie nach dem Schließen entfernen. Für jedes Ticket legen wir eine Anzahl von Wiederholungen fest, um sicherzustellen, dass das System einen weiteren Versuch unternimmt, wenn eine Position beim ersten Versuch nicht geschlossen werden kann.

Für jede Position wird versucht, sie mit der Funktion „trade.PositionClose(ticket)“ zu schließen. Wenn die Position erfolgreich geschlossen wurde, drucken wir eine Meldung aus, die angibt, dass das Ticket geschlossen wurde und ob es ein Trailing-Stop war oder nicht, wobei „trailingStop != 0“ verwendet wird, um zu prüfen, ob ein Trailing-Stop angewendet wurde. Sobald die Position geschlossen ist, entfernen wir das Ticket mit der Funktion ArrayRemove aus dem Array „tickets“ und beenden die Wiederholungsschleife, indem wir „retries“ auf 0 setzen. Wenn die Position nicht geschlossen werden kann, wird der Zähler für Wiederholungsversuche dekrementiert, eine kurze Zeit mit der Sleep-Funktion gewartet und dann erneut versucht, die Position zu schließen, um sicher zu gehen, dass die Funktion nicht über Gebühr verwendet wird.

Nachdem wir versucht haben, alle Positionen zu schließen, prüfen wir mit der Funktion ArraySize, ob das Array „tickets“ leer ist. Wenn alle Positionen geschlossen sind, rufen wir die Funktion „Reset“ auf, um den Status der Rückgewinnung zurückzusetzen, alle verbleibenden diesbezüglichen Daten zu löschen und zukünftige Handelsgeschäfte vorzubereiten. Das ist alles. Da wir jedoch nicht sicher sind, dass der Markt unsere Zielmarken erreichen wird, können wir das System verbessern, indem wir die Positionen, die unseren Mindestgewinn erreichen, nachziehen, anstatt zu warten, bis die Marken erreicht sind. Wir haben diese Logik wieder in einer Methode.

//--- Apply trailing stop logic to initial positions
void ApplyTrailingStop() {
   if (inputTrailingStopEnabled && ArraySize(tickets) == 1) { // Ensure trailing stop is enabled and there is only one position (initial position)
      ulong ticket = tickets[0]; // Get the ticket of the initial position
      double entryPrice = GetPositionEntryPrice(ticket); // Get the entry price of the position by ticket
      double currentPrice = SymbolInfoDouble(symbol, SYMBOL_BID); // Get the current price
      double newTrailingStop;

      if (lastOrderType == ORDER_TYPE_BUY) {
         if (currentPrice > entryPrice + (inputMinimumProfitPts + inputTrailingStopPts) * _Point) {
            newTrailingStop = currentPrice - inputTrailingStopPts * _Point; // Calculate new trailing stop for BUY
            if (newTrailingStop > trailingStop) {
               trailingStop = newTrailingStop; // Update trailing stop if the new one is higher
               Print("Trailing BUY Position, Ticket: ", ticket, " New Trailing Stop: ", trailingStop);
            }
         }

         if (trailingStop != 0 && currentPrice <= trailingStop) {
            Print("Trailed and closing BUY Position, Ticket: ", ticket);
            ClosePositionsAtTarget(); // Close position if the price falls below the trailing stop
         }
      } else if (lastOrderType == ORDER_TYPE_SELL) {
         if (currentPrice < entryPrice - (inputMinimumProfitPts + inputTrailingStopPts) * _Point) {
            newTrailingStop = currentPrice + inputTrailingStopPts * _Point; // Calculate new trailing stop for SELL
            if (newTrailingStop < trailingStop) {
               trailingStop = newTrailingStop; // Update trailing stop if the new one is lower
               Print("Trailing SELL Position, Ticket: ", ticket, " New Trailing Stop: ", trailingStop);
            }
         }

         if (trailingStop != 0 && currentPrice >= trailingStop) {
            Print("Trailed and closing SELL Position, Ticket: ", ticket);
            ClosePositionsAtTarget(); // Close position if the price rises above the trailing stop
         }
      }
   }
}

In der Methode „ApplyTrailingStop“ ohne Rückgabewert (void), die wir definieren, implementieren wir die Trailing-Stop-Logik für die Anfangsposition basierend darauf, ob Trailing-Stops aktiviert sind und ob es nur eine aktive Position gibt. Zunächst wird mit „inputTrailingStopEnabled“ geprüft, ob die Trailing-Stop-Funktion aktiviert ist und ob es nur eine offene Position gibt (sichergestellt durch „ArraySize(tickets) == 1“). Wir rufen dann das Ticket der Anfangsposition ab und verwenden es, um den Einstiegspreis über die Funktion „GetPositionEntryPrice“ zu erhalten. Mit der Funktion SymbolInfoDouble holen wir uns auch den aktuellen Marktpreis.

Bei einer Kaufposition prüfen wir, ob der aktuelle Kurs um einen bestimmten Betrag über den Einstiegskurs gestiegen ist (unter Berücksichtigung sowohl des Mindestgewinns als auch des Trailing-Stop-Abstands, der mit „inputMinimumProfitPts + inputTrailingStopPts“ berechnet wird) und setzen den neuen Trailing-Stop entsprechend. Wenn der berechnete Trailing-Stop höher ist als der aktuelle „trailingStop“, aktualisieren wir den Trailing-Stop-Wert und geben eine Meldung aus, die das neue Trailing-Stop-Niveau angibt. Wenn der aktuelle Kurs auf oder unter das Trailing-Stop-Niveau fällt, schließen wir die Position mit der Funktion „ClosePositionsAtTarget“.

Bei einer Verkaufsposition gehen wir ähnlich vor, allerdings umgekehrt. Wir prüfen, ob der aktuelle Kurs um einen bestimmten Betrag unter dem Einstiegskurs liegt und setzen den Trailing-Stop gegebenenfalls niedriger. Wenn der berechnete Trailing-Stop niedriger ist als der aktuelle „trailingStop“, aktualisieren wir den Trailing-Stop und geben eine Meldung aus, die das neue Niveau angibt. Steigt der aktuelle Kurs auf oder über den Trailing-Stop, wird die Position geschlossen. Diese Funktion stellt sicher, dass der Trailing-Stop dynamisch auf der Grundlage der Marktbedingungen angewendet wird, was die Sicherung von Gewinnen ermöglicht und gleichzeitig die Position vor erheblichen Verlusten schützt. Wenn sich der Kurs günstig entwickelt, wird der Trailing-Stop angepasst; wenn sich der Kurs umkehrt und den Trailing-Stop trifft, wird die Position geschlossen.

Sie haben vielleicht bemerkt, dass wir eine nutzerdefinierte Funktion verwenden, um die Einstiegspreise zu ermitteln. Hier ist die Logik der Funktion.

//--- Get the entry price of a position by ticket
double GetPositionEntryPrice(ulong ticket) {
   if (PositionSelectByTicket(ticket)) {
      return PositionGetDouble(POSITION_PRICE_OPEN);
   } else {
      Print("Failed to select position by ticket: ", ticket);
      return 0.0;
   }
}

Hier definieren wir die Funktion „GetPositionEntryPrice“, mit der wir den Einstiegspreis einer Position anhand der angegebenen Ticketnummer abrufen. Zunächst versuchen wir, mit der Funktion PositionSelectByTicket die Position auszuwählen, die mit dem angegebenen Ticket verbunden ist. Wenn die Position erfolgreich ausgewählt wurde, rufen wir den Einstiegskurs der Position ab, indem wir „PositionGetDouble(POSITION_PRICE_OPEN)“ aufrufen, was uns den Preis liefert, zu dem die Position eröffnet wurde. Wenn die Position nicht ausgewählt werden kann (z. B. wenn das Ticket ungültig ist oder die Position nicht mehr existiert), wird eine Fehlermeldung ausgegeben und der Wert 0,0 zurückgegeben, um anzuzeigen, dass der Einstiegskurs nicht abgerufen werden konnte.

Nach dem Öffnen und Schließen der Positionen müssen wir nun das System zurücksetzen und den zugehörigen Handels-Basket als Bereinigungsmethode entfernen. Hier sehen Sie, wie wir diese Bereinigungslogik in einer „Reset“-Funktion behandeln.

//--- Reset recovery state
void Reset() {
   currentLotSize = inputlot; //--- Reset lot size to initial value.
   lastOrderType = -1;              //--- Clear the last order type.
   lastOrderPrice = 0.0;            //--- Reset the last order price.
   isRecovery = false;              //--- Mark recovery as inactive.
   ArrayResize(tickets, 0);         //--- Clear the tickets array.
   trailingStop = 0;                //--- Reset trailing stop
   initialEntryPrice = 0.0;         //--- Reset initial entry price
   Print("Strategy BASKET reset after closing trades.");
}

Mit der Funktion „Zurücksetzen“ setzen wir den Rückgewinnungszustand zurück, um einen neuen Handelszyklus vorzubereiten. Zunächst setzen wir die „currentLotSize“ auf den durch „inputlot“ definierten Anfangswert zurück, um sicherzustellen, dass die Losgröße auf den nutzerdefinierten Anfangsbetrag zurückgesetzt wird. Wir löschen auch die Details des letzten Auftrags, indem wir „lastOrderType“ auf -1 setzen (was bedeutet, dass es keine aktive Positionsart gibt) und „lastOrderPrice“ auf 0,0 zurücksetzen, wodurch alle vorherigen Preisinformationen der Positionen entfernt werden.

Als Nächstes kennzeichnen wir die Rückgewinnung als inaktiv, indem wir „isRecovery“ auf false setzen, wodurch sichergestellt wird, dass keine Rückgewinnungslogik angewendet wird, wenn der Reset erfolgt. Anschließend wird das Array „tickets“ mit der Funktion ArrayResize gelöscht, wobei alle gespeicherten Positions-Tickets, die Teil des vorherigen Rückgewinnungsprozesses waren, entfernt werden. Außerdem setzen wir „trailingStop“ auf 0 und „initialEntryPrice“ auf 0,0 zurück, wodurch alle Trailing-Stop-Einstellungen und Einstiegskurswerte aus früheren Handelsgeschäften gelöscht werden. Abschließend wird die Meldung „Strategy BASKET reset after closing trades“ ausgegeben, um darauf hinzuweisen, dass die Rückgewinnung abgeschlossen und der Rückgewinnungszustand gelöscht wurde. Diese Funktion stellt sicher, dass sich das System in einem sauberen Zustand befindet und für den nächsten Handelszyklus bereit ist.

Nachdem wir die Struktureigenschaften definiert haben, können wir nun Signale erzeugen und sie der definierten Struktur hinzufügen. Da wir jedoch viele dynamische Signale verwalten müssen, müssen wir eine Array-Struktur definieren, die als ein ganzer Basket fungiert, in dem wir die Unterbaskets für jedes erzeugte Signal definieren. Wie wir das erreichen, erfahren Sie hier.

//--- Dynamic list to track multiple positions
PositionRecovery recoveryArray[]; //--- Dynamic array for recovery instances.

Hier wird ein dynamisches Array mit dem Namen „recoveryArray“ deklariert, das dazu dient, mehrere Positionswiederherstellungsinstanzen zu verfolgen und zu verwalten. Das Array basiert auf der Struktur „PositionRecovery“, die es ermöglicht, individuelle Rückgewinnungszustände für mehrere Handelsgeschäfte unabhängig voneinander zu speichern. Jedes Element des Arrays repräsentiert ein bestimmtes Rückgewinnungs-Setup, komplett mit allen relevanten Attributen wie Losgröße, Zonengrenzen und zugehörigen Handelstickets.

Indem wir das Array dynamisch machen, können wir es während der Laufzeit mit Funktionen wie ArrayResize nach Bedarf erweitern oder verkleinern. So können wir dynamisch neue Rückgewinnungsinstanzen für neue Handelssignale hinzufügen oder abgeschlossene Rückgewinnungen entfernen, was eine effiziente Speichernutzung und Anpassungsfähigkeit an unterschiedliche Handelsszenarien gewährleistet. Dieser Ansatz ist für die gleichzeitige Verwaltung mehrerer Handelsgeschäfte von entscheidender Bedeutung, da er es ermöglicht, dass die Verwertungslogik für jedes Handelsgeschäft unabhängig in seinem eigenen „Basket“ der Daten funktioniert.

Nachdem wir das Array definiert haben, können wir nun mit der Logik der Signalerzeugung beginnen. Wir müssen den Indikator-Handle in der Funktion OnInit initialisieren, die immer dann aufgerufen wird, wenn das Programm gestartet wird.

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit() {
   rsiHandle = iRSI(_Symbol, PERIOD_CURRENT, rsiPeriod, PRICE_CLOSE); //--- Create RSI indicator handle.
   if (rsiHandle == INVALID_HANDLE) {                                 //--- Check if handle creation failed.
      Print("Failed to create RSI handle. Error: ", GetLastError());  //--- Print error message.
      return(INIT_FAILED);                                            //--- Return initialization failure.
   }
   ArraySetAsSeries(rsiBuffer, true); //--- Set RSI buffer as a time series.
   Print("Multi-Zone Recovery Strategy initialized."); //--- Log initialization success.
   return(INIT_SUCCEEDED); //--- Return initialization success.
}

In OnInit initialisieren wir die wesentlichen Komponenten, die für das Funktionieren des Expert Advisors (EA) erforderlich sind. Wir beginnen mit der Erstellung des Handles des RSI-Indikators mit Hilfe der Funktion iRSI, die den Relative Strength Index für das aktuelle Symbol und den aktuellen Zeitraum berechnet. Mit diesem Handle kann der EA dynamisch auf RSI-Werte zugreifen. Wenn die Handle-Erstellung fehlschlägt, was durch den Wert INVALID_HANDLE angezeigt wird, protokollieren wir mit der Funktion Print eine Fehlermeldung mit Details und geben INIT_FAILED zurück, um den Initialisierungsprozess zu beenden. Anschließend wird das Array „rsiBuffer“ mit ArraySetAsSeries als Zeitreihe konfiguriert, damit die Daten für eine genaue Verarbeitung chronologisch geordnet sind. Nach erfolgreicher Initialisierung drucken wir eine Bestätigungsmeldung aus, die anzeigt, dass die Strategie bereit ist, und geben INIT_SUCCEEDED zurück, um zu signalisieren, dass der EA einsatzbereit ist. In OnDeinit wird das Handle dann zerstört, um Ressourcen zu sparen.

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason) {
   if (rsiHandle != INVALID_HANDLE)             //--- Check if RSI handle is valid.
      IndicatorRelease(rsiHandle);              //--- Release the RSI handle.
   Print("Multi-Zone Recovery Strategy deinitialized."); //--- Log deinitialization.
}

Hier kümmern wir uns um die Bereinigung und das Ressourcenmanagement für den Expert Advisor (EA), wenn er entfernt oder deaktiviert wird. Zunächst prüfen wir, ob das „rsiHandle“ für den RSI-Indikator gültig ist, indem wir sicherstellen, dass es nicht gleich „INVALID_HANDLE“ ist. Wenn das Handle gültig ist, wird es mit der Funktion IndicatorRelease freigegeben, um Ressourcen freizugeben und Speicherlecks zu vermeiden. Abschließend wird mit Print eine Meldung protokolliert, die besagt, dass die Multi-Zone Recovery Strategy erfolgreich deinitialisiert wurde. Diese Funktion sorgt für ein sauberes und geordnetes Herunterfahren des Programms, sodass keine Ressourcen oder Prozesse zurückbleiben.

Danach können wir zu OnTick übergehen, wo die gesamte Logik des Hauptsystems unter Verwendung der zuvor definierten Struktur abwickeln wird. Zunächst müssen wir die Daten des Indikators abrufen, damit wir sie für weitere Analysen verwenden können.

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick() {
   if (CopyBuffer(rsiHandle, 0, 1, 2, rsiBuffer) <= 0) { //--- Copy the RSI buffer values.
      Print("Failed to copy RSI buffer. Error: ", GetLastError()); //--- Print error on failure.
      return;                                                     //--- Exit on failure.
   }

   //---

}

Hier, in der Funktion OnTick, behandeln wir die Logik, die bei jedem neuen Tick ausgeführt wird, der eine Preisaktualisierung für das Handelssymbol darstellt. Der erste Schritt besteht darin, die Werte des RSI-Indikators mit der Funktion CopyBuffer in das Array „rsiBuffer“ zu kopieren. Wir geben den „rsiHandle“ an, um den RSI-Indikator zu identifizieren, setzen den Pufferindex auf 0 und fordern zwei Werte ab dem letzten Balken an. Wenn der Vorgang fehlschlägt (d. h. der zurückgegebene Wert ist kleiner oder gleich 0), wird eine Fehlermeldung mit Print ausgegeben, um den Nutzer über das Problem zu informieren, und es werden die von der Funktion GetLastError erhaltenen Fehlerdetails angegeben. Nachdem der Fehler protokolliert wurde, wird die Funktion sofort mit return beendet. Dadurch wird sichergestellt, dass der Rest der Logik nicht ausgeführt wird, wenn der Abruf der RSI-Daten fehlschlägt, wodurch die Integrität und Stabilität des Expert Advisors erhalten bleibt.

Wenn wir die Daten erfolgreich abrufen, können wir sie wie folgt für die Signalerzeugungslogik verwenden.

datetime currentBarTime = iTime(_Symbol, PERIOD_CURRENT, 0); //--- Get the time of the current bar.
if (currentBarTime != lastBarTime) {                         //--- Check if a new bar has formed.
   lastBarTime = currentBarTime;                             //--- Update the last processed bar time.
   if (rsiBuffer[1] > 30 && rsiBuffer[0] <= 30) {            //--- Check for oversold RSI crossing up.
      Print("BUY SIGNAL");
      PositionRecovery newRecovery;                          //--- Create a new recovery instance.
      newRecovery.Initialize(inputlot, inputzonesizepts, inputzonetragetpts, inputlotmultiplier, _Symbol, ORDER_TYPE_BUY, SymbolInfoDouble(_Symbol, SYMBOL_BID)); //--- Initialize the recovery.
      newRecovery.OpenTrade(ORDER_TYPE_BUY, "Initial Position"); //--- Open an initial BUY position.
      ArrayResize(recoveryArray, ArraySize(recoveryArray) + 1); //--- Resize the recovery array.
      recoveryArray[ArraySize(recoveryArray) - 1] = newRecovery; //--- Add the new recovery to the array.
   } else if (rsiBuffer[1] < 70 && rsiBuffer[0] >= 70) {      //--- Check for overbought RSI crossing down.
      Print("SELL SIGNAL");
      PositionRecovery newRecovery;                          //--- Create a new recovery instance.
      newRecovery.Initialize(inputlot, inputzonesizepts, inputzonetragetpts, inputlotmultiplier, _Symbol, ORDER_TYPE_SELL, SymbolInfoDouble(_Symbol, SYMBOL_BID)); //--- Initialize the recovery.
      newRecovery.OpenTrade(ORDER_TYPE_SELL, "Initial Position"); //--- Open an initial SELL position.
      ArrayResize(recoveryArray, ArraySize(recoveryArray) + 1); //--- Resize the recovery array.
      recoveryArray[ArraySize(recoveryArray) - 1] = newRecovery; //--- Add the new recovery to the array.
   }
}

Hier konzentrieren wir uns auf die Erkennung neuer Balken und die Generierung von Handelssignalen, wenn der Indikator bestimmte Niveaus überschreitet. Zunächst wird mit der Funktion iTime die Zeit des aktuellen Balkens ermittelt, die in der Variablen „currentBarTime“ gespeichert ist. Wir vergleichen dann „currentBarTime“ mit „lastBarTime“, um zu prüfen, ob sich ein neuer Balken gebildet hat. Wenn sich die beiden Werte unterscheiden, deutet dies darauf hin, dass sich ein neuer Balken gebildet hat. Daher aktualisieren wir „lastBarTime“ auf den Wert von „currentBarTime“, um zu verhindern, dass derselbe Balken mehrmals verarbeitet wird.

Als Nächstes bewerten wir die Bedingungen für RSI-basierte Signale. Wenn der RSI-Wert im „rsiBuffer[1]“ (vorheriger Balken) größer als 30 ist und der aktuelle Wert im „rsiBuffer[0]“ (aktueller Balken) kleiner oder gleich 30 ist, bedeutet dies eine überverkaufte Situation mit einem Aufwärtskreuz. In diesem Fall drucken wir die Meldung „BUY SIGNAL“ und initiieren eine neue Instanz von „PositionRecovery“ mit dem Namen „newRecovery“. Anschließend rufen wir die Methode „Initialize“ von „newRecovery“ auf, um die Parameter für eine Rückgewinnung einzurichten, einschließlich „inputlot“, „inputzonesizepts“, „inputzonetragetpts“, „inputlotmultiplier“, Symbol, Ordertyp als ORDER_TYPE_BUY und den aktuellen Geldkurs aus der Funktion „SymbolInfoDouble“. Nach der Initialisierung eröffnen wir mit der Methode „OpenTrade“ eine erste Kaufposition, wobei wir „ORDER_TYPE_BUY“ und einen beschreibenden Kommentar übergeben.

Wenn der „RSI“ in „rsiBuffer[1]“ unter 70 liegt und der aktuelle Wert in „rsiBuffer[0]“ größer oder gleich 70 ist, deutet dies auf einen überkauften Zustand mit einem Abwärtskreuz hin. In diesem Szenario drucken wir die Nachricht „SELL SIGNAL“ und erstellen eine neue Instanz von „PositionRecovery“. Nach der Initialisierung mit denselben Parametern, aber der Einstellung des Auftragstyps auf ORDER_TYPE_SELL, eröffnen wir eine erste Verkaufsposition mit der Methode „OpenTrade“.

Schließlich fügen wir sowohl für die „BUY“- als auch für die „SELL“-Signale die initialisierte Instanz von „PositionRecovery“ zum „recoveryArray“ hinzu. Die Größe des Arrays wird mit der Funktion ArrayResize geändert, und die neue Instanz wird der letzten Position des Arrays zugewiesen, um sicherzustellen, dass jede Rückgewinnung unabhängig verfolgt wird. Diese Logik ist nun für die Initiierung der Positionsbaskets mit Anfangspositionen und -konditionen verantwortlich. Um das Positionsmanagement zu handhaben, müssen wir eine Schleife über die Körbe im HauptBasket ziehen und die Managementlogik wie in der Hauptstruktur bei jedem Tick anwenden. Hier ist die Logik.

for (int i = 0; i < ArraySize(recoveryArray); i++) { //--- Iterate through all recovery instances.
   recoveryArray[i].ManageZones();                 //--- Manage zones for each recovery instance.
   recoveryArray[i].CheckCloseAtTargets();         //--- Check and close positions at targets.
   recoveryArray[i].ApplyTrailingStop();           //--- Apply trailing stop logic to initial positions.
}

Um die Positionsverwaltung unabhängig zu handhaben, verwenden wir eine for-Schleife, um alle im „recoveryArray“ gespeicherten Rückgewinnungsinstanzen zu durchlaufen. Diese Schleife stellt sicher, dass jede Rückgewinnungsinstanz separat verwaltet wird, sodass das System eine unabhängige Kontrolle über mehrere Rückgewinnungsszenarien behält. Die Schleife beginnt mit dem Index „i“, der auf 0 gesetzt ist, und wird fortgesetzt, bis alle Elemente im „recoveryArray“ verarbeitet worden sind, wie durch die Funktion ArraySize bestimmt.

Innerhalb der Schleife werden für jede Rückgewinnungsinstanz drei wesentliche Methoden aufgerufen. Zunächst wird die Methode „ManageZones“ mit Hilfe des Punkt-Operators aufgerufen, die die Kursbewegungen in Bezug auf die festgelegten Rückgewinnungszonen überwacht. Wenn der Preis die Zonengrenzen verlässt, versucht diese Methode, eine Rückgewinnungsposition zu eröffnen, wobei die Losgröße dynamisch entsprechend dem angegebenen Multiplikator angepasst wird.

Anschließend wird die Methode „CheckCloseAtTargets“ ausgeführt, um zu prüfen, ob der Kurs die Zielwerte für die Rückgewinnungsinstanz erreicht hat. Wenn die Zielbedingungen erfüllt sind, schließt diese Methode alle zugehörigen Positionen und setzt die Rückgewinnungsinstanz zurück, um sicherzustellen, dass die Gewinne gesichert sind und die Instanz für einen neuen Zyklus bereit ist.

Schließlich wird die Methode „ApplyTrailingStop“ angewendet, die eine Trailing-Stop-Logik für die Anfangsposition der Rückgewinnungsinstanz erzwingt. Bei dieser Methode wird das Trailing-Stop-Niveau dynamisch angepasst, wenn sich der Kurs günstig entwickelt, wodurch Gewinne gesichert werden. Wenn sich der Kurs umkehrt und den Trailing-Stop erreicht, sorgt die Methode dafür, dass die Position geschlossen wird, um sich vor möglichen Verlusten zu schützen.

Indem das System jede Rückgewinnungsinstanz auf diese Weise verarbeitet, verwaltet es effektiv mehrere unabhängige Positionen und stellt sicher, dass alle Rückgewinnungsszenarien dynamisch und in Übereinstimmung mit vordefinierten Strategien gehandhabt werden. Um sicherzugehen, dass das Programm richtig funktioniert, führen wir es aus, und hier ist das Ergebnis.

BEISPIEL FÜR DAS ZURÜCKSETZEN EINER STRATEGIE

Aus dem Bild geht hervor, dass die Strategie nach dem Schließen einer Rückgewinnungsinstanz zurückgesetzt wird, was eines der Hauptziele des Systems ist. Die Schließung greift jedoch nicht in die anderen laufenden Instanzen ein, was bedeutet, dass die Instanz unabhängig von den anderen Instanzen im Array behandelt wird. Um dies zu bestätigen, wechseln wir zur Registerkarte „Handel“ (Trade) und sehen, dass es aktive Instanzen gibt.

HANDELSNIEDERLASSUNGEN

Aus dem Bild ist ersichtlich, dass es noch Rückgewinnungsinstanzen gibt, von denen sich zwei bereits im Rückgewinnungsmodus befinden. Sie sind durch die Kommentare im rechten Bereich, die angeben, ob es sich um eine Ausgangs- oder eine Wiedereinstellungsposition handelt, klar erkennbar. Damit ist sichergestellt, dass wir unser Ziel erreicht haben, und es bleibt nur noch, das Programm einem Backtest zu unterziehen und seine Leistung zu analysieren. Dies wird im nächsten Abschnitt behandelt.


Backtests

Um die Leistung und Robustheit des Programms zu bewerten, müssen wir zunächst die historischen Marktbedingungen simulieren. Auf diese Weise können wir feststellen, wie gut das Programm mit Rückgewinnungsszenarien umgeht, sich an Preisbewegungen anpasst und Handelsgeschäfte verwaltet. Der Backtest liefert uns wichtige Informationen über die Rentabilität der Strategie, die Höhe des Drawdowns und das Risikomanagement. Das Programm generiert Kauf- und Verkaufssignale auf der Grundlage der festgelegten Schwellenwerte (z. B. überverkauft bei 30 und überkauft bei 70; ganz nach den Wünschen des Nutzers), indem es die historischen Kursdaten Tick für Tick verarbeitet, um reale Marktbedingungen nachzubilden. Wenn ein Signal generiert wird, initialisiert der EA eine neue Rückgewinnungsinstanz, führt den ersten Handel aus und verfolgt die Kursbewegungen innerhalb der festgelegten Rückgewinnungszonen. 

Wir testen den dynamischen Rückgewinnungsmechanismus des Systems, der die Losgrößen anhand des Multiplikators anpasst und bei Bedarf Hedging-Positionen eröffnet, unter verschiedenen Marktbedingungen gründlich. Das Programm wertet die Rückgewinnungsszenarien für jedes Signal unabhängig voneinander aus, um sicherzustellen, dass alle Abschlüsse isoliert verwaltet werden, was sich in der Handhabung des „recoveryArray“ widerspiegelt. Dadurch wird sichergestellt, dass die Strategie auch bei mehreren aktiven Rückgewinnungsinstanzen organisiert und anpassungsfähig bleibt. Wir haben das Programm in den letzten 5 Monaten mit den folgenden Einstellungen getestet:

BACKTEST-EINSTELLUNGEN

Nach der Fertigstellung haben wir folgende Ergebnisse:

Diagramm des Strategietesters:

DIAGRAMM

Bericht des Strategietesters:

BERICHT

Aus den obigen Abbildungen ist ersichtlich, dass die Kurven glatt sind, wenn auch holprig, wenn es eine Korrelation zwischen dem Saldo und dem Eigenkapital gibt, die durch den kontinuierlichen Anstieg der Anzahl der Rückgewinnungsstufen, die das Programm pro Instanz ausführt, und der Anzahl der erzeugten Signale verursacht wird. So können wir die Anzahl der Rückgewinnungspositionen begrenzen, indem wir die Trailing-Stop-Funktion aktivieren. Hier sind die Ergebnisse, die wir erhalten.

REPORT_TRAILING AKTIVIERT

Aus dem Bild ist ersichtlich, dass die Anzahl der Handelsgeschäfte sinkt und die Gewinnrate steigt, wenn die Trailing-Stop-Funktion aktiviert ist. Wir können die Anzahl der Positionen weiter einschränken, indem wir eine Logik zur Beschränkung der Handelsanzahl einbauen, bei der, wenn bereits mehrere Positionen offen sind, die Eröffnung weiterer Aufträge auf der Grundlage generierter Signale nicht in Betracht gezogen wird. Um dies zu erreichen, definieren wir zusätzliche Eingabevariablen wie folgt:

input bool inputEnablePositionsRestriction = true; // Enable Maximum positions restriction
input int inputMaximumPositions = 11; // Maximum number of positions

Diese Eingabevariablen enthalten ein Flag zum Aktivieren oder Deaktivieren der Beschränkungsoption und die zweite enthält die maximale Anzahl von Positionen, die im System initiiert werden können, wenn die Beschränkung aktiviert ist. Wir übernehmen dann die Logik in OnTick, wenn ein Signal bestätigt wird, und fügen die zusätzliche Schicht der Handelsbeschränkung hinzu.

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick() {
   if (CopyBuffer(rsiHandle, 0, 1, 2, rsiBuffer) <= 0) { //--- Copy the RSI buffer values.
      Print("Failed to copy RSI buffer. Error: ", GetLastError()); //--- Print error on failure.
      return;                                                     //--- Exit on failure.
   }

   datetime currentBarTime = iTime(_Symbol, PERIOD_CURRENT, 0); //--- Get the time of the current bar.
   if (currentBarTime != lastBarTime) {                         //--- Check if a new bar has formed.
      lastBarTime = currentBarTime;                             //--- Update the last processed bar time.
      if (rsiBuffer[1] > 30 && rsiBuffer[0] <= 30) {            //--- Check for oversold RSI crossing up.
         Print("BUY SIGNAL");
         if (inputEnablePositionsRestriction == false || inputMaximumPositions > PositionsTotal()){
            PositionRecovery newRecovery;                          //--- Create a new recovery instance.
            newRecovery.Initialize(inputlot, inputzonesizepts, inputzonetragetpts, inputlotmultiplier, _Symbol, ORDER_TYPE_BUY, SymbolInfoDouble(_Symbol, SYMBOL_BID)); //--- Initialize the recovery.
            newRecovery.OpenTrade(ORDER_TYPE_BUY, "Initial Position"); //--- Open an initial BUY position.
            ArrayResize(recoveryArray, ArraySize(recoveryArray) + 1); //--- Resize the recovery array.
            recoveryArray[ArraySize(recoveryArray) - 1] = newRecovery; //--- Add the new recovery to the array.
         }
         else {
            Print("FAILED: Maximum positions threshold hit!");
         }
      } else if (rsiBuffer[1] < 70 && rsiBuffer[0] >= 70) {      //--- Check for overbought RSI crossing down.
         Print("SELL SIGNAL");
         if (inputEnablePositionsRestriction == false || inputMaximumPositions > PositionsTotal()){
            PositionRecovery newRecovery;                          //--- Create a new recovery instance.
            newRecovery.Initialize(inputlot, inputzonesizepts, inputzonetragetpts, inputlotmultiplier, _Symbol, ORDER_TYPE_SELL, SymbolInfoDouble(_Symbol, SYMBOL_BID)); //--- Initialize the recovery.
            newRecovery.OpenTrade(ORDER_TYPE_SELL, "Initial Position"); //--- Open an initial SELL position.
            ArrayResize(recoveryArray, ArraySize(recoveryArray) + 1); //--- Resize the recovery array.
            recoveryArray[ArraySize(recoveryArray) - 1] = newRecovery; //--- Add the new recovery to the array.
         }
         else {
            Print("FAILED: Maximum positions threshold hit!");
         }
      }
   }

   for (int i = 0; i < ArraySize(recoveryArray); i++) { //--- Iterate through all recovery instances.
      recoveryArray[i].ManageZones();                 //--- Manage zones for each recovery instance.
      recoveryArray[i].CheckCloseAtTargets();         //--- Check and close positions at targets.
      recoveryArray[i].ApplyTrailingStop();           //--- Apply trailing stop logic to initial positions.
   }
}

Hier implementieren wir einen Mechanismus zur Verwaltung von Positionsbeschränkungen und zur Kontrolle der Anzahl von Handelsgeschäften, die der Expert Advisor (EA) zu einem bestimmten Zeitpunkt eröffnen kann. Die Logik beginnt mit der Auswertung, ob Positionsbeschränkungen deaktiviert sind (“inputEnablePositionsRestriction“ == false) oder ob die Gesamtzahl der aktuell offenen Positionen (PositionsTotal) unter dem nutzerdefinierten Maximum “inputMaximumPositions“ liegt. Ist eine der beiden Bedingungen erfüllt, eröffnet der EA ein neues Handelsgeschäft, wobei er sicherstellt, dass er mit den Präferenzen des Nutzers für uneingeschränkten oder eingeschränkten Handel übereinstimmt.

Wenn jedoch beide Bedingungen nicht erfüllt sind - was bedeutet, dass die Positionsbeschränkungen aktiviert sind und die maximal zulässige Anzahl von Positionen erreicht wurde - wird der EA kein neues Handelsgeschäft eröffnen. Stattdessen wird eine Fehlermeldung auf dem Terminal protokolliert: “FAILED: Maximum positions threshold hit!" (FEHLGESCHLAGEN, Maximale Positionsschwelle erreicht). Diese Nachricht dient als informativer Feedback-Mechanismus, der dem Nutzer hilft zu verstehen, warum zusätzliche Handelsgeschäfte nicht ausgeführt wurden. Zur Verdeutlichung haben wir die Änderungen in hellgelber Farbe hervorgehoben. Bei der Prüfung erhalten wir die folgenden Ergebnisse.

REPORT_MAXIMALE AUFTRÄGE BESCHRÄNKUNG

Aus dem Bild ist ersichtlich, dass die Anzahl der Handelsgeschäfte weiter sinkt und die Gewinnrate weiter steigt. Dies bestätigt, dass wir unser Ziel, ein mehrstufiges Zone Recovery System zu schaffen, erreicht haben. In einer Visualisierung im Graphic Interchange Format (GIF) haben wir die folgende Simulation, die das Erreichen unseres Ziels bestätigt.

MEHRSTUFEN-GIF


Schlussfolgerung

Abschließend hat dieser Artikel den Prozess der Konstruktion eines robusten Expert Advisors in MQL5 auf der Grundlage einer mehrstufigen Zonenwiederherstellungsstrategie dargestellt. Durch die Nutzung von Kernkonzepten wie automatischer Signalerkennung, dynamischem Rückgewinnungsmanagement und Gewinnsicherungsmechanismen wie Trailing Stops haben wir ein flexibles System geschaffen, das mehrere unabhängige Rückgewinnungsinstanzen handhaben kann. Zu den wichtigsten Komponenten dieser Implementierung gehören die Generierung von Handelssignalen, die Logik der Positionsbeschränkung und die effiziente Handhabung von Rückgewinnungs- und Ausstiegsstrategien.

Haftungsausschluss: Dieser Artikel ist als Lehrmittel für die MQL5-Programmierung gedacht. Das vorgestellte mehrstufige System der Rückgewinnung bietet zwar einen strukturierten Rahmen für das Handelsmanagement, doch ist das Marktverhalten von Natur aus unsicher. Der Handel ist mit einem finanziellen Risiko verbunden, und Erfolge in der Vergangenheit sind keine Garantie für zukünftige Ergebnisse. Umfassende Tests und ein effektives Risikomanagement sind unabdingbar, bevor eine Strategie auf realen Märkten eingesetzt wird.

Wenn Sie die in diesem Leitfaden besprochenen Methoden befolgen, können Sie Ihr Fachwissen im algorithmischen Handel erweitern und diese Prinzipien anwenden, um noch ausgefeiltere Handelssysteme zu erstellen. Viel Spaß beim Kodieren und viel Erfolg bei Ihren Handelsbestrebungen!

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

Generative Adversarial Networks (GANs) für synthetische Daten in der Finanzmodellierung (Teil 2): Erstellen eines synthetischen Symbols für Tests Generative Adversarial Networks (GANs) für synthetische Daten in der Finanzmodellierung (Teil 2): Erstellen eines synthetischen Symbols für Tests
In diesem Artikel erstellen wir ein synthetisches Symbol mit Hilfe eines Generative Adversarial Network (GAN), das realistische Finanzdaten generiert, die das Verhalten tatsächlicher Marktinstrumente, wie z. B. EURUSD, nachahmen. Das GAN-Modell lernt Muster und Volatilität aus historischen Marktdaten und erstellt synthetische Preisdaten mit ähnlichen Merkmalen.
Gating-Mechanismen beim Ensemblelernen Gating-Mechanismen beim Ensemblelernen
In diesem Artikel setzen wir unsere Untersuchung von Ensemblemodellen fort, indem wir das Konzept der Gates erörtern, insbesondere wie sie bei der Kombination von Modellergebnissen nützlich sein können, um entweder die Vorhersagegenauigkeit oder die Modellgeneralisierung zu verbessern.
Erstellen von selbstoptimierenden Expert Advisor in MQL5 (Teil 5): Selbstanpassende Handelsregeln Erstellen von selbstoptimierenden Expert Advisor in MQL5 (Teil 5): Selbstanpassende Handelsregeln
Die besten Praktiken, die festlegen, wie ein Indikator sicher zu verwenden ist, sind nicht immer leicht zu befolgen. Bei ruhigen Marktbedingungen kann der Indikator überraschenderweise Werte anzeigen, die nicht als Handelssignal gelten, was dazu führt, dass algorithmischen Händlern Chancen entgehen. In diesem Artikel wird eine mögliche Lösung für dieses Problem vorgeschlagen, da wir erörtern, wie Handelsanwendungen entwickelt werden können, die ihre Handelsregeln an die verfügbaren Marktdaten anpassen.
Einführung in MQL5 (Teil 11): Eine Anleitung für Anfänger zur Arbeit mit integrierten Indikatoren in MQL5 (II) Einführung in MQL5 (Teil 11): Eine Anleitung für Anfänger zur Arbeit mit integrierten Indikatoren in MQL5 (II)
Entdecken Sie, wie man einen Expert Advisor (EA) in MQL5 entwickelt, der mehrere Indikatoren wie RSI, MA und Stochastik-Oszillator verwendet, um versteckte steigende und fallende Divergenzen zu erkennen. Lernen Sie, ein effektives Risikomanagement zu implementieren und den Handel zu automatisieren - mit detaillierten Beispielen und vollständig kommentiertem Quellcode für Ausbildungszwecke!