English
preview
Automatisieren von Handelsstrategien in MQL5 (Teil 22): Erstellen eines Zone Recovery Systems für den Trendhandel mit Envelopes

Automatisieren von Handelsstrategien in MQL5 (Teil 22): Erstellen eines Zone Recovery Systems für den Trendhandel mit Envelopes

MetaTrader 5Handel |
41 2
Allan Munene Mutiiria
Allan Munene Mutiiria

Einführung

In unserem letzten Artikel (Teil 21) haben wir eine Handelsstrategie auf Basis der Neuronalen Netzwerke untersucht, die mit adaptiven Lernraten erweitert wurde, um die Vorhersagegenauigkeit für Marktbewegungen in MetaQuotes Language 5 (MQL5) zu verbessern. In Teil 22 verlagern wir den Schwerpunkt auf die Entwicklung eines Zone Recovery System, das in eine Envelopes-Trendhandelsstrategie integriert ist und den Relative Strength Index (RSI) und die Envelopes-Indikatoren kombiniert, um den Handel zu automatisieren und Verluste effektiv zu verwalten. Wir werden die folgenden Themen behandeln:

  1. Verstehen der Architektur der Zone Recovery und dem Trend von Envelopes
  2. Implementation in MQL5
  3. Backtests
  4. Schlussfolgerung

Am Ende werden Sie ein robustes MQL5-Handelssystem haben, das für dynamische Marktbedingungen entwickelt wurde und zur Implementierung und zum Testen bereit ist - legen wir los!


Verstehen der Architektur der Zone Recovery und dem Trend von Envelopes

Die Zone Recovery ist eine intelligente Handelsstrategie, die uns dabei hilft, potenzielle Verluste in Gewinne umzuwandeln, indem wir zusätzliche Handelsgeschäfte platzieren, wenn sich der Markt gegen uns bewegt, mit dem Ziel, einen Gewinn oder ein ausgeglichenes Ergebnis zu erzielen. Stellen Sie sich vor, Sie kaufen ein Währungspaar in der Erwartung, dass es steigt, aber es fällt – dann greift die Zone Recovery ein, indem wir eine Preisspanne oder „Zone“ festlegen, in der wir gegenläufige Handelsgeschäfte platzieren, um Verluste auszugleichen, wenn der Kurs wieder steigt. Wir haben vor, ein automatisiertes System in MetaQuotes Language 5 (MQL5) zu entwickeln, das dieses Konzept für den Handel auf den Devisenmärkten bei geringem Risiko und maximiertem Gewinn nutzt.

Damit dies funktioniert, werden wir zwei technische Indikatoren verwenden, um die optimalen Zeitpunkte für den Einstieg in den Handel zu ermitteln. Ein Indikator prüft die Energie des Marktes und stellt sicher, dass wir nur handeln, wenn es einen starken Schub in eine Richtung gibt, und vermeidet schwache oder chaotische Signale. Der andere, Envelopes, zeichnet einen Kanal um den Durchschnittspreis des Marktes und zeigt uns an, wenn die Preise zu weit nach oben oder unten gehen, was einen wahrscheinlichen Moment für einen Rücksetzer signalisiert. Diese Indikatoren arbeiten zusammen, um chancenreiche Handelsgeschäfte zu finden, bei denen der Preis bereit ist, innerhalb eines Trends umzukehren.

Und so wollen wir vorgehen: Wir beginnen mit einem Handelsgeschäft, wenn unsere Indikatoren eine Umkehr signalisieren, z. B. wenn der Kurs mit starkem Momentum den Rand des Envelopes-Kanals erreicht. Wenn sich der Markt in die falsche Richtung bewegt, aktivieren wir die Zonenerholung, indem wir innerhalb unserer festgelegten Preiszone entgegengesetzte Handelsgeschäfte eröffnen, die sorgfältig ausgewählt werden, um Risiko und Erholung auszugleichen. Wir begrenzen die Anzahl der Handelsgeschäfte, damit das System diszipliniert bleibt und sich nicht verzettelt. Mit diesem Setup können wir Trendchancen nutzen und haben gleichzeitig ein Sicherheitsnetz für den Fall, dass die Dinge nicht so laufen wie geplant, das sowohl an wilde als auch an ruhige Märkte angepasst werden kann. Bleiben Sie dabei, wenn wir diesen Plan in die Tat umsetzen und ausprobieren! Siehe den nachstehenden Durchführungsplan.

STRATEGIEPLAN


Implementation in MQL5

Um das Programm in MQL5 zu erstellen, öffnen Sie den MetaEditor, gehen zum Navigator, suchen den Ordner Indikatoren, klicken auf die Registerkarte „Neu“ und folgen Sie den Anweisungen, um die Datei zu erstellen. Sobald das Programm erstellt ist, beginnen wir in der Programmierumgebung mit der Deklaration einiger Eingabevariablen, die uns helfen werden, die Schlüsselwerte des Programms leicht zu kontrollieren.

//+------------------------------------------------------------------+
//|                 Envelopes Trend Bounce with Zone Recovery EA.mq5 |
//|                           Copyright 2025, Allan Munene Mutiiria. |
//|                                   https://t.me/Forex_Algo_Trader |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, Allan Munene Mutiiria."
#property link      "https://t.me/Forex_Algo_Trader"
#property version   "1.00"
#property strict

#include <Trade/Trade.mqh>                                             //--- Include trade library

enum TradingLotSizeOptions { FIXED_LOTSIZE, UNFIXED_LOTSIZE };         //--- Define lot size options

input group "======= EA GENERAL SETTINGS ======="
input TradingLotSizeOptions lotOption = UNFIXED_LOTSIZE;               // Lot Size Option
input double initialLotSize = 0.01;                                    // Initial Lot Size
input double riskPercentage = 1.0;                                     // Risk Percentage (%)
input int    riskPoints = 300;                                         // Risk Points
input int    magicNumber = 123456789;                                  // Magic Number
input int    maxOrders = 1;                                            // Maximum Initial Positions
input double zoneTargetPoints = 600;                                   // Zone Target Points
input double zoneSizePoints = 300;                                     // Zone Size Points
input bool   restrictMaxOrders = true;                                 // Apply Maximum Orders Restriction

Hier legen wir den Grundstein für unser Zone Recovery System für Envelopes Trendhandel in MQL5, indem wir wesentliche Komponenten und nutzerkonfigurierbare Einstellungen einrichten. Wir beginnen mit der Einbindung der Bibliothek „<Trade/Trade.mqh>“, die die Klasse „CTrade“ für die Ausführung von Handelsoperationen wie das Öffnen und Schließen von Positionen enthält. Diese Einbeziehung ist von entscheidender Bedeutung, da sie unseren Expert Advisor (EA) mit den erforderlichen Instrumenten ausstattet, um nahtlos mit dem Markt zu interagieren, insbesondere bei der Einleitung von Aufträgen. Siehe unten, wie man die Datei öffnet.

MQL5 HANDELSOPERATIONEN DATEI

Wir definieren dann die Enumeration „TradingLotSizeOptions“ mit zwei Werten: „FIXED_LOTSIZE“ und „UNFIXED_LOTSIZE“. Dadurch können wir den Nutzern die Wahl zwischen einer konstanten Losgröße und einer dynamisch an die Risikoparameter angepassten Losgröße bieten, was die Flexibilität bei der Handelsgröße für unterschiedliche Handelsstile erhöht. Als Nächstes konfigurieren wir die Eingabeparameter in der Gruppe „EA GENERAL SETTINGS“, die der Nutzer in der MetaTrader 5-Plattform anpassen kann.

Die Eingabe „lotOption“, die standardmäßig auf „UNFIXED_LOTSIZE“ eingestellt ist, legt fest, ob für den Handel eine feste oder risikobasierte Losgröße verwendet wird. Die „initialLotSize“ (0.01) legt die Lotgröße für feste Handelsgeschäfte fest, während „riskPercentage“ (1.0%) und „riskPoints“ (300) den Prozentsatz des Kontostandes und den Stop-Loss-Abstand für die dynamische Lot-Größe definieren. Diese Einstellungen steuern, wie viel Risiko wir pro Handel eingehen, um sicherzustellen, dass der EA mit der Risikotoleranz des Nutzers übereinstimmt.

Wir vergeben eine eindeutige „magicNumber“ (123456789), um die Handelsgeschäfte unserer EAs zu identifizieren, damit wir sie von anderen Handelsgeschäfte auf demselben Konto unterscheiden können. Die Eingaben „maxOrders“ (1) und „restrictMaxOrders“ (true) begrenzen die Anzahl der Anfangspositionen und verhindern, dass der EA zu viele Handelsgeschäfte auf einmal eröffnet. Schließlich legen „zoneTargetPoints“ (600) und „zoneSizePoints“ (300) das Gewinnziel und die Größe der Erholungszone in Punkten fest und definieren damit die Grenzen für unsere Zonenerholungsstrategie. Nach dem Kompilieren erhalten wir die folgende Ausgabe.

GELADENE EINGABEN

Nachdem die Eingänge geladen sind, können wir nun mit der Deklaration der Kernlogik für das gesamte System beginnen. Wir beginnen mit der Deklaration einiger Strukturen und Klassen, die wir verwenden werden, da wir einen objektorientierten Programmieransatz (OOP) anwenden wollen.

class MarketZoneTrader {
private:
   //--- Trade State Definition
   enum TradeState { INACTIVE, RUNNING, TERMINATING };                 //--- Define trade lifecycle states

   //--- Data Structures
   struct TradeMetrics {
      bool   operationSuccess;                                         //--- Track operation success
      double totalVolume;                                              //--- Sum closed trade volumes
      double netProfitLoss;                                            //--- Accumulate profit/loss
   };

   struct ZoneBoundaries {
      double zoneHigh;                                                 //--- Upper recovery zone boundary
      double zoneLow;                                                  //--- Lower recovery zone boundary
      double zoneTargetHigh;                                           //--- Upper profit target
      double zoneTargetLow;                                            //--- Lower profit target
   };

   struct TradeConfig {
      string         marketSymbol;                                     //--- Trading symbol
      double         openPrice;                                        //--- Position entry price
      double         initialVolume;                                    //--- Initial trade volume
      long           tradeIdentifier;                                  //--- Magic number
      string         tradeLabel;                                       //--- Trade comment
      ulong          activeTickets[];                                  //--- Active position tickets
      ENUM_ORDER_TYPE direction;                                       //--- Trade direction
      double         zoneProfitSpan;                                   //--- Profit target range
      double         zoneRecoverySpan;                                 //--- Recovery zone range
      double         accumulatedBuyVolume;                             //--- Total buy volume
      double         accumulatedSellVolume;                            //--- Total sell volume
      TradeState     currentState;                                     //--- Current trade state
   };

   struct LossTracker {
      double tradeLossTracker;                                         //--- Track cumulative profit/loss
   };
};

Hier definieren wir die Kernstruktur unseres Systems für den Envelopes-Trendhandel in MQL5, indem wir die Klasse „MarketZoneTrader“ implementieren, wobei wir uns auf den privaten Teil mit den Handelsstatus-Definitionen und Datenstrukturen konzentrieren. Diese Logik hilft bei der Organisation der kritischen Komponenten, die für die Verwaltung von Handelsgeschäften, die Verfolgung von Erholungszonen und die Leistungsüberwachung erforderlich sind. Wir beginnen mit der Definition der Klasse „MarketZoneTrader“, die als Rückgrat unseres Expert Advisors (EA) dient und die Logik für unsere Handelsstrategie kapselt.

In seinem privaten Bereich führen wir die Enumeration „TradeState“ mit drei Werten ein: „INACTIVE“, „RUNNING“, und „TERMINATING“. Anhand dieser Zustände können wir den Lebenszyklus unserer Handelsoperationen verfolgen und so sicherstellen, dass wir wissen, ob der EA im Leerlauf ist, aktiv Handelsgeschäfte verwaltet oder Positionen schließt. Dies ist entscheidend, um die Kontrolle über den Handelsprozess zu behalten, da es uns hilft, Aktionen wie die Eröffnung von Wiederherstellungsgeschäften oder die Schließung von Positionen zu koordinieren.

Als Nächstes erstellen wir die Struktur „TradeMetrics“, um wichtige Leistungsdaten für unsere Handelsgeschäfte zu speichern. Sie umfasst „operationSuccess“, um zu verfolgen, ob Handelsaktionen (wie das Schließen von Positionen) erfolgreich waren, „totalVolume“, um die Volumina der geschlossenen Handelsgeschäfte zu summieren, und „netProfitLoss“, um den Gewinn oder Verlust aus diesen Handelsgeschäften zu kumulieren. Diese Struktur hilft uns bei der Bewertung der Ergebnisse unserer Handelsaktionen und liefert ein klares Bild der Leistung während der Einziehung oder Schließung.

Anschließend definieren wir die Struktur „ZoneBoundaries“, die die Preisniveaus für unsere Zonenerholungsstrategie enthält. Die Variablen „zoneHigh“ und „zoneLow“ markieren die obere und untere Grenze der Erholungszone, in der wir Gegengeschäfte platzieren, um Verluste zu mindern. Die „zoneTargetHigh“ und „zoneTargetLow“ legen die Gewinnziele oberhalb und unterhalb der Zone fest und bestimmen, wann wir den Handel gewinnbringend beenden. Diese Grenzen sind für unsere Strategie von entscheidender Bedeutung, da sie den Zeitpunkt bestimmen, an dem wir Einziehungsmaßnahmen einleiten oder Positionen schließen. Hier sehen Sie, wie sie in der Visualisierung aussehen würden, damit Sie ein klares Bild davon haben, warum wir die Struktur brauchen.

ZONEN-BEISPIEL

In der Struktur „TradeConfig“ werden die Handelseinstellungen gespeichert. Es enthält „marketSymbol“ für das Währungspaar, „openPrice“ für den Einstiegskurs und „initialVolume“ für die Handelsgröße. Der „tradeIdentifier“ enthält unsere eindeutige magische Nummer, und „tradeLabel“ fügt einen Kommentar zur Identifizierung des Handelsgeschäfts hinzu. Das Array „activeTickets“ erfasst offene Positionstickets, während „direction“ angibt, ob es sich um einen Kauf oder Verkauf handelt. Wir fügen auch „zoneProfitSpan“ und „zoneRecoverySpan“ ein, um die Größe des Gewinnziels und der Erholungszone in Preiseinheiten zu definieren, sowie „accumulatedBuyVolume“ und „accumulatedSellVolume“, um die Gesamtvolumina für jeden Handelstyp zu überwachen. Die Variable „currentState“, die die Enumeration „TradeState“ verwendet, verfolgt den Handelsstatus und bindet alles zusammen.

Schließlich fügen wir die Struktur „LossTracker“ mit einer einzigen Variablen „tradeLossTracker“ hinzu, um den kumulierten Gewinn oder Verlust über die einzelnen Handelsgeschäfte hinweg zu überwachen. Dies hilft uns, die finanziellen Auswirkungen unserer Einziehungsmaßnahmen abzuschätzen, damit wir unsere Strategie anpassen können, falls die Verluste zu groß werden. Wir können dann einige Mitgliedsvariablen definieren, um die anderen, weniger wichtigen, aber notwendigen Handelsinformationen zu speichern.

//--- Member Variables
TradeConfig           m_tradeConfig;                                //--- Store trade configuration
ZoneBoundaries        m_zoneBounds;                                 //--- Store zone boundaries
LossTracker           m_lossTracker;                                //--- Track profit/loss
string                m_lastError;                                  //--- Store error message
int                   m_errorStatus;                                //--- Store error code
CTrade                m_tradeExecutor;                              //--- Manage trade execution
int                   m_handleRsi;                                  //--- RSI indicator handle
int                   m_handleEnvUpper;                             //--- Upper Envelopes handle
int                   m_handleEnvLower;                             //--- Lower Envelopes handle
double                m_rsiBuffer[];                                //--- RSI data buffer
double                m_envUpperBandBuffer[];                       //--- Upper Envelopes buffer
double                m_envLowerBandBuffer[];                       //--- Lower Envelopes buffer
TradingLotSizeOptions m_lotOption;                                  //--- Lot size option
double                m_initialLotSize;                             //--- Fixed lot size
double                m_riskPercentage;                             //--- Risk percentage
int                   m_riskPoints;                                 //--- Risk points
int                   m_maxOrders;                                  //--- Maximum positions
bool                  m_restrictMaxOrders;                          //--- Position restriction flag
double                m_zoneTargetPoints;                           //--- Profit target points
double                m_zoneSizePoints;                             //--- Recovery zone points

Im privaten Bereich der Klasse „MarketZoneTrader“ definieren wir wichtige Mitgliedsvariablen zur Verwaltung von Handelseinstellungen, Erholungszonen und Indikatordaten. Wir verwenden „m_tradeConfig“ (Struktur „TradeConfig“), um Handelsdetails wie Symbol und Richtung zu speichern, „m_zoneBounds“ (“ZoneBoundaries“-Struktur) für Erholungszonen und Gewinnzielpreise und „m_lossTracker“ (“LossTracker“-Struktur), um Gewinne oder Verluste zu verfolgen. Für die Fehlerbehandlung protokollieren „m_lastError“ (String) und „m_errorStatus“ (Integer) Probleme, während „m_tradeExecutor“ (Klasse „CTrade“) Handelsoperationen abwickelt.

Indikator-Handles - „m_handleRsi“, „m_handleEnvUpper“, „m_handleEnvLower“ - greifen auf RSI- und Daten von Envelopes zu, wobei die Arrays „m_rsiBuffer“, „m_envUpperBandBuffer“ und „m_envLowerBandBuffer“ ihre Werte speichern. Wir speichern Eingabeeinstellungen in „m_lotOption“ (“TradingLotSizeOptions“), „m_initialLotSize“, „m_riskPercentage“, „m_riskPoints““m_maxOrders“, „m_restrictMaxOrders“, „m_zoneTargetPoints“ und „m_zoneSizePoints“ zur Steuerung der Losgröße, der Positionslimits und der Zonengrößen. Diese Variablen bilden das Rückgrat für die Verwaltung von Handelsgeschäfte und Indikatoren und bereiten uns auf die kommende Handelslogik vor. Anschließend müssen wir einige Hilfsfunktionen definieren, die wir im Programm häufig verwenden werden.

//--- Error Handling
void logError(string message, int code) {
   //--- Error Logging Start
   m_lastError = message;                                           //--- Store error message
   m_errorStatus = code;                                            //--- Store error code
   Print("Error: ", message);                                       //--- Log error to Experts tab
   //--- Error Logging End
}

//--- Market Data Access
double getMarketVolumeStep() {
   //--- Volume Step Retrieval Start
   return SymbolInfoDouble(m_tradeConfig.marketSymbol, SYMBOL_VOLUME_STEP); //--- Retrieve broker's volume step
   //--- Volume Step Retrieval End
}

double getMarketAsk() {
   //--- Ask Price Retrieval Start
   return SymbolInfoDouble(m_tradeConfig.marketSymbol, SYMBOL_ASK); //--- Retrieve ask price
   //--- Ask Price Retrieval End
}

double getMarketBid() {
   //--- Bid Price Retrieval Start
   return SymbolInfoDouble(m_tradeConfig.marketSymbol, SYMBOL_BID); //--- Retrieve bid price
   //--- Bid Price Retrieval End
}

Hier fügen wir wichtige Funktionen für die Fehlerbehandlung und den Zugriff auf Marktdaten hinzu. Die Funktion „logError“ speichert „message“ in „m_lastError“, „code“ in „m_errorStatus“ und protokolliert die Meldung über „Print“ in der Registerkarte „Experts“ zur Fehlersuche. Die Funktion „getMarketVolumeStep“ verwendet SymbolInfoDouble mit SYMBOL_VOLUME_STEP, um das Volumeninkrement des Brokers für „m_tradeConfig.marketSymbol“ zu ermitteln und gültige Handelsgrößen zu gewährleisten. Die Funktionen „getMarketAsk“ und „getMarketBid“ rufen Brief- und Geldkurse unter Verwendung von „SymbolInfoDouble“ mit SYMBOL_ASK bzw. „SYMBOL_BID“ ab, um eine genaue Preisfindung für den Handel zu ermöglichen.

Wir können nun die wichtigsten Funktionen für die Ausführung von Handelsgeschäften definieren. Beginnen wir mit denjenigen, die uns bei der Initialisierung, der Speicherung von Handelstickets für die Verfolgung und Überwachung von Vorgängen und der Schließung von Handelsgeschäften helfen, da dies die weniger komplexe Logik ist.

//--- Trade Initialization
bool configureTrade(ulong ticket) {
   //--- Trade Configuration Start
   if (!PositionSelectByTicket(ticket)) {                               //--- Select position by ticket
      logError("Failed to select ticket " + IntegerToString(ticket), INIT_FAILED); //--- Log selection failure
      return false;                                                     //--- Return failure
   }
   m_tradeConfig.marketSymbol = PositionGetString(POSITION_SYMBOL);     //--- Set symbol
   m_tradeConfig.tradeLabel = __FILE__;                                 //--- Set trade comment
   m_tradeConfig.tradeIdentifier = PositionGetInteger(POSITION_MAGIC);  //--- Set magic number
   m_tradeConfig.direction = (ENUM_ORDER_TYPE)PositionGetInteger(POSITION_TYPE);   //--- Set direction
   m_tradeConfig.openPrice = PositionGetDouble(POSITION_PRICE_OPEN);    //--- Set entry price
   m_tradeConfig.initialVolume = PositionGetDouble(POSITION_VOLUME);    //--- Set initial volume
   m_tradeExecutor.SetExpertMagicNumber(m_tradeConfig.tradeIdentifier); //--- Set magic number for executor
   return true;                                                         //--- Return success
   //--- Trade Configuration End
}

//--- Trade Ticket Management
void storeTradeTicket(ulong ticket) {
   //--- Ticket Storage Start
   int ticketCount = ArraySize(m_tradeConfig.activeTickets);        //--- Get ticket count
   ArrayResize(m_tradeConfig.activeTickets, ticketCount + 1);       //--- Resize ticket array
   m_tradeConfig.activeTickets[ticketCount] = ticket;               //--- Store ticket
   //--- Ticket Storage End
}

//--- Trade Execution
ulong openMarketTrade(ENUM_ORDER_TYPE tradeDirection, double tradeVolume, double price) {
   //--- Trade Opening Start
   ulong ticket = 0;                                                //--- Initialize ticket
   if (m_tradeExecutor.PositionOpen(m_tradeConfig.marketSymbol, tradeDirection, tradeVolume, price, 0, 0, m_tradeConfig.tradeLabel)) { //--- Open position
      ticket = m_tradeExecutor.ResultOrder();                       //--- Get ticket
   } else {
      Print("Failed to open trade: Direction=", EnumToString(tradeDirection), ", Volume=", tradeVolume); //--- Log failure
   }
   return ticket;                                                   //--- Return ticket
   //--- Trade Opening End
}

//--- Trade Closure
void closeActiveTrades(TradeMetrics &metrics) {
   //--- Trade Closure Start
   for (int i = ArraySize(m_tradeConfig.activeTickets) - 1; i >= 0; i--) {    //--- Iterate tickets in reverse
      if (m_tradeConfig.activeTickets[i] > 0) {                               //--- Check valid ticket
         if (m_tradeExecutor.PositionClose(m_tradeConfig.activeTickets[i])) { //--- Close position
            m_tradeConfig.activeTickets[i] = 0;                               //--- Clear ticket
            metrics.totalVolume += m_tradeExecutor.ResultVolume();            //--- Accumulate volume
            if ((ENUM_ORDER_TYPE)PositionGetInteger(POSITION_TYPE) == ORDER_TYPE_BUY) { //--- Check buy position
               metrics.netProfitLoss += m_tradeExecutor.ResultVolume() * (m_tradeExecutor.ResultPrice() - PositionGetDouble(POSITION_PRICE_OPEN)); //--- Calculate buy profit
            } else {                                                          //--- Handle sell position
               metrics.netProfitLoss += m_tradeExecutor.ResultVolume() * (PositionGetDouble(POSITION_PRICE_OPEN) - m_tradeExecutor.ResultPrice()); //--- Calculate sell profit
            }
         } else {
            metrics.operationSuccess = false;                                  //--- Mark failure
            Print("Failed to close ticket: ", m_tradeConfig.activeTickets[i]); //--- Log failure
         }
      }
   }
   //--- Trade Closure End
}

//--- Bar Detection
bool isNewBar() {
   //--- New Bar Detection Start
   static datetime previousTime = 0;                                      //--- Store previous bar time
   datetime currentTime = iTime(m_tradeConfig.marketSymbol, Period(), 0); //--- Get current bar time
   bool result = (currentTime != previousTime);                           //--- Check for new bar
   previousTime = currentTime;                                            //--- Update previous time
   return result;                                                         //--- Return new bar status
   //--- New Bar Detection End
}

Hier tauchen wir in die Kernlogik unseres Programms ein und entwickeln Funktionen zum Einrichten von Handelsgeschäften, zum Verfolgen von Positionen, zum Ausführen von Aufträgen, zum Schließen von Handelsgeschäften und zum Festlegen des Zeitpunkts unserer Aktionen. Wir beginnen mit der Erstellung der Funktion „configureTrade“, um einen Handel für ein bestimmtes „Ticket“ vorzubereiten. Zunächst versuchen wir, die Position mit der Funktion PositionSelectByTicket auszuwählen. Wenn es nicht funktioniert, protokollieren wir das Problem mit „logError“ und beenden es mit false. Wenn dies gelingt, füllen wir „m_tradeConfig“ mit Details: Wir holen uns „marketSymbol“ mit der Funktion PositionGetString, setzen „tradeLabel“ auf __FILE__ und holen uns „tradeIdentifier“ und „direction“ von PositionGetInteger, wobei letzteres auf ENUM_ORDER_TYPE gecastet wird. Dann setzen wir „openPrice“ und „initialVolume“ mit PositionGetDouble und markieren „m_tradeExecutor“ mit „SetExpertMagicNumber“, um sicherzustellen, dass unser Handel einsatzbereit ist.

Als Nächstes erstellen wir die Funktion „storeTradeTicket“, um unsere offenen Positionen zu organisieren. Wir überprüfen die Größe von „m_tradeConfig.activeTickets“ mit der Funktion ArraySize, dehnen das Array mit der Funktion ArrayResize um einen Slot und setzen das neue „Ticket“ ein, sodass wir immer wissen, welche Handelsgeschäfte aktiv sind. Im nächsten Schritt erstellen wir die Funktion „openMarketTrade“, um den Handel auf dem Markt zu platzieren. Wir rufen „m_tradeExecutor.PositionOpen“ mit den Angaben „tradeDirection“, „tradeVolume“, „price“ und „m_tradeConfig“ auf. Wenn es klappt, weisen wir das „Ticket“ mit „ResultOrder“ zu; wenn nicht, protokollieren wir den Fehler mit „Print“ und halten unsere Handelsausführung fest.

Anschließend werden die Positionen mit der Funktion „closeActiveTrades“ geschlossen. Wir gehen in einer Schleife rückwärts durch „m_tradeConfig.activeTickets“ und schließen jedes gültige Ticket mit „m_tradeExecutor.PositionClose“. Wenn eine Schließung funktioniert, löschen wir das Ticket, fügen „ResultVolume“ zu „metrics.totalVolume“ hinzu und berechnen „metrics.netProfitLoss“ mithilfe der Funktionen „PositionGetInteger“ und „PositionGetDouble“, um die Handelsrichtung zu überprüfen. Wenn etwas fehlschlägt, kennzeichnen wir „metrics.operationSuccess“ als „false“ und protokollieren es mit Print, um sicherzustellen, dass wir jedes Ergebnis verfolgen.

Schließlich fügen wir die Funktion „isNewBar“ hinzu, um den Handel einmal pro Balken zu unterstützen und den Ressourcenverbrauch zu reduzieren. Wir holen die aktuelle Zeit des Balkens für „m_tradeConfig.marketSymbol“ mit der Funktion iTime, vergleichen sie mit „previousTime“ und aktualisieren „previousTime“, wenn sie sich unterscheidet, damit wir wissen, wann ein neuer Balken eintrifft, um auf Handelssignale zu prüfen. Schließlich benötigen wir eine Funktion zur Berechnung des Handelsvolumens und eine Funktion zur Eröffnung der Handelsgeschäfte.

//--- Lot Size Calculation
double calculateLotSize(double riskPercent, int riskPips) {
   //--- Lot Size Calculation Start
   double riskMoney = AccountInfoDouble(ACCOUNT_BALANCE) * riskPercent / 100;                //--- Calculate risk amount
   double tickSize = SymbolInfoDouble(m_tradeConfig.marketSymbol, SYMBOL_TRADE_TICK_SIZE);   //--- Get tick size
   double tickValue = SymbolInfoDouble(m_tradeConfig.marketSymbol, SYMBOL_TRADE_TICK_VALUE); //--- Get tick value
   if (tickSize == 0 || tickValue == 0) {                           //--- Validate tick data
      Print("Invalid tick size or value");                          //--- Log invalid data
      return -1;                                                    //--- Return invalid lot
   }
   double lotValue = (riskPips * _Point) / tickSize * tickValue;    //--- Calculate lot value
   if (lotValue == 0) {                                             //--- Validate lot value
      Print("Invalid lot value");                                   //--- Log invalid lot
      return -1;                                                    //--- Return invalid lot
   }
   return NormalizeDouble(riskMoney / lotValue, 2);                 //--- Return normalized lot size
   //--- Lot Size Calculation End
}

//--- Order Execution
int openOrder(ENUM_ORDER_TYPE orderType, double stopLoss, double takeProfit) {
   //--- Order Opening Start
   int ticket;                                                      //--- Initialize ticket
   double openPrice;                                                //--- Initialize open price
   
   if (orderType == ORDER_TYPE_BUY) {                               //--- Check buy order
      openPrice = NormalizeDouble(getMarketAsk(), Digits());        //--- Set buy price
   } else if (orderType == ORDER_TYPE_SELL) {                       //--- Check sell order
      openPrice = NormalizeDouble(getMarketBid(), Digits());        //--- Set sell price
   } else {
      Print("Invalid order type");                                  //--- Log invalid type
      return -1;                                                    //--- Return invalid ticket
   }
   
   double lotSize = 0;                                              //--- Initialize lot size
   
   if (m_lotOption == FIXED_LOTSIZE) {                              //--- Check fixed lot
      lotSize = m_initialLotSize;                                   //--- Use fixed lot size
   } else if (m_lotOption == UNFIXED_LOTSIZE) {                     //--- Check dynamic lot
      lotSize = calculateLotSize(m_riskPercentage, m_riskPoints);   //--- Calculate risk-based lot
   }
   
   if (lotSize <= 0) {                                              //--- Validate lot size
      Print("Invalid lot size: ", lotSize);                         //--- Log invalid lot
      return -1;                                                    //--- Return invalid ticket
   }
   
   if (m_tradeExecutor.PositionOpen(m_tradeConfig.marketSymbol, orderType, lotSize, openPrice, 0, 0, __FILE__)) { //--- Open position
      ticket = (int)m_tradeExecutor.ResultOrder();                  //--- Get ticket
      Print("New trade opened: Ticket=", ticket, ", Type=", EnumToString(orderType), ", Volume=", lotSize); //--- Log success
   } else {
      ticket = -1;                                                  //--- Set invalid ticket
      Print("Failed to open order: Type=", EnumToString(orderType), ", Volume=", lotSize); //--- Log failure
   }
   
   return ticket;                                                   //--- Return ticket
   //--- Order Opening End
}

Wir beginnen mit der Funktion „calculateLotSize“, um die Handelsgröße auf der Grundlage der Risikoparameter zu bestimmen. Zunächst berechnen wir das „riskMoney“, indem wir einen Prozentsatz des Kontostands mit Hilfe von AccountInfoDouble mit ACCOUNT_BALANCE und „riskPercent“ ermitteln. Dann holen wir „tickSize“ und „tickValue“ für „m_tradeConfig.marketSymbol“ mit SymbolInfoDouble mit „SYMBOL_TRADE_TICK_SIZE“ und „SYMBOL_TRADE_TICK_VALUE“. Wenn einer der beiden Werte Null ist, wird ein Fehler mit „Print“ protokolliert und -1 zurückgegeben, um ungültige Berechnungen zu vermeiden. Wir berechnen den „lotValue“ mit „riskPips“, _Point, „tickSize“ und „tickValue“, und wenn er Null ist, protokollieren wir einen weiteren Fehler und geben -1 zurück. Schließlich geben wir die Losgröße mit NormalizeDouble auf zwei Dezimalstellen zurück, um sicherzustellen, dass sie den Anforderungen des Brokers entspricht.

Als Nächstes erstellen wir die Funktion „openOrder“ zur Platzierung von Handelsgeschäften. Wir initialisieren „ticket“ und „openPrice“, dann prüfen wir „orderType“. Für ORDER_TYPE_BUY setzen wir „openPrice“ mit „getMarketAsk“ und „NormalizeDouble“ mit Digits; für „ORDER_TYPE_SELL“ verwenden wir „getMarketBid“. Wenn „orderType“ ungültig ist, wird dies mit „Print“ protokolliert und -1 zurückgegeben. Wir bestimmen „lotSize“ auf der Grundlage von „m_lotOption“: für „FIXED_LOTSIZE“ verwenden wir „m_initialLotSize“; für „UNFIXED_LOTSIZE“ rufen wir „calculateLotSize“ mit „m_riskPercentage“ und „m_riskPoints“ auf. Wenn „lotSize“ ungültig ist, protokollieren wir den Fehler mit „Print“ und geben -1 zurück. Dann öffnen wir die Position mit „m_tradeExecutor.PositionOpen“ unter Verwendung von „m_tradeConfig.marketSymbol“, „orderType“, „lotSize“, „openPrice“ und „FILE“ als Kommentar. Bei Erfolg setzen wir „ticket“ auf „ResultOrder“ und protokollieren es mit „Print“; bei Misserfolg setzen wir „ticket“ auf -1 und protokollieren den Fehler. Schließlich geben wir den Wert des Tickets zurück.

Danach müssen wir die Systemwerte initialisieren. Wir können dies über eine eigene Funktion erreichen, aber um alles einfach zu halten, werden wir den Konstruktor verwenden. Es ist ratsam, den Konstruktor in einem Public Access Modifier zu definieren, damit er überall im Programm verfügbar ist. Definieren wir auch hier den Destruktor.

public:
   //--- Constructor
   MarketZoneTrader(TradingLotSizeOptions lotOpt, double initLot, double riskPct, int riskPts, int maxOrds, bool restrictOrds, double targetPts, double sizePts) {
      //--- Constructor Start
      m_tradeConfig.currentState = INACTIVE;                           //--- Set initial state
      ArrayResize(m_tradeConfig.activeTickets, 0);                     //--- Initialize ticket array
      m_tradeConfig.zoneProfitSpan = targetPts * _Point;               //--- Set profit target
      m_tradeConfig.zoneRecoverySpan = sizePts * _Point;               //--- Set recovery zone
      m_lossTracker.tradeLossTracker = 0.0;                            //--- Initialize loss tracker
      m_lotOption = lotOpt;                                            //--- Set lot size option
      m_initialLotSize = initLot;                                      //--- Set initial lot
      m_riskPercentage = riskPct;                                      //--- Set risk percentage
      m_riskPoints = riskPts;                                          //--- Set risk points
      m_maxOrders = maxOrds;                                           //--- Set max positions
      m_restrictMaxOrders = restrictOrds;                              //--- Set restriction flag
      m_zoneTargetPoints = targetPts;                                  //--- Set target points
      m_zoneSizePoints = sizePts;                                      //--- Set zone points
      m_tradeConfig.marketSymbol = _Symbol;                            //--- Set symbol
      m_tradeConfig.tradeIdentifier = magicNumber;                     //--- Set magic number
      //--- Constructor End
   }

   //--- Destructor
   ~MarketZoneTrader() {
      //--- Destructor Start
      cleanup();                                                       //--- Release resources
      //--- Destructor End
   }

Wir fahren fort, indem wir den Konstruktor und den Destruktor für die Klasse „MarketZoneTrader“ in ihrem öffentlichen Abschnitt definieren. Wir beginnen mit dem Konstruktor „MarketZoneTrader“, der die Parameter „lotOpt“, „initLot“, „riskPct“, „riskPts“, „maxOrds“, „restrictOrds“, „targetPts“ und „sizePts“ enthält. Wir initialisieren die Handelsumgebung, indem wir „m_tradeConfig.currentState“ auf „INACTIVE“ setzen, um anzuzeigen, dass keine aktiven Handelsgeschäfte vorliegen. Als Nächstes wird das Array „m_tradeConfig.activeTickets“ mit ArrayResize auf Null gesetzt, um es für neue Tickets vorzubereiten. Wir berechnen „m_tradeConfig.zoneProfitSpan“ und „m_tradeConfig.zoneRecoverySpan“, indem wir „targetPts“ und „sizePts“ mit „_Point“ multiplizieren und die Größe des Gewinnziels und der Erholungszone in Preiseinheiten angeben. Wir setzen „m_lossTracker.tradeLossTracker“ auf 0,0 zurück, um mit der Verfolgung von Gewinnen oder Verlusten von Grund auf zu beginnen.

Dann weisen wir die Eingabeparameter den Mitgliedsvariablen zu: „m_lotOption“ auf „lotOpt“, „m_initialLotSize“ auf „initLot“, „m_riskPercentage“ auf „riskPct“, „m_riskPoints“ auf „riskPts“, „m_maxOrders“ zu „maxOrds“, „m_restrictMaxOrders“ zu „restrictOrds“, „m_zoneTargetPoints“ zu „targetPts“, und „m_zoneSizePoints“ zu „sizePts“. Wir setzen „m_tradeConfig.marketSymbol“ auf _Symbol, um das Symbol des aktuellen Charts zu handeln, und weisen „m_tradeConfig.tradeIdentifier“ auf „magicNumber“ für eine eindeutige Handelsidentifikation zu. Dieses Setup stellt sicher, dass unser EA die Nutzereinstellungen widerspiegelt und bereit für den Handel ist.

Als Nächstes definieren wir den Destruktor „~MarketZoneTrader“, um die Ressourcen aufzuräumen. Wir rufen die Funktion „cleanup“ auf, um alle zugewiesenen Ressourcen, wie z. B. Indikator-Handles, freizugeben und so sicherzustellen, dass der EA sauber und ohne Speicherlecks beendet wird. Es ist gut zu wissen, dass der Konstruktor und der Destruktor den gleichen Wortlaut des Klassennamens haben, nur dass der Destruktor ein Tilde (~) vor sich hat. Genau das. Hier ist die Funktion, um die Klasse zu zerstören, wenn sie nicht benötigt wird.

//--- Cleanup
void cleanup() {
   //--- Cleanup Start
   IndicatorRelease(m_handleRsi);                                   //--- Release RSI handle
   ArrayFree(m_rsiBuffer);                                          //--- Free RSI buffer
   IndicatorRelease(m_handleEnvUpper);                              //--- Release upper Envelopes handle
   ArrayFree(m_envUpperBandBuffer);                                 //--- Free upper Envelopes buffer
   IndicatorRelease(m_handleEnvLower);                              //--- Release lower Envelopes handle
   ArrayFree(m_envLowerBandBuffer);                                 //--- Free lower Envelopes buffer
   //--- Cleanup End
}

Wir verwenden einfach die Funktion IndicatorRelease, um die Indikator-Handles freizugeben, und die Funktion ArrayFree, um die Speicher-Arrays freizugeben. Da wir die Indikatoren berührt haben, sollten wir eine Initialisierungsfunktion definieren, die wir beim Start des Programms aufrufen.

//--- Getters
TradeState getCurrentState() {
   //--- Get Current State Start
   return m_tradeConfig.currentState;                               //--- Return trade state
   //--- Get Current State End
}

double getZoneTargetHigh() {
   //--- Get Target High Start
   return m_zoneBounds.zoneTargetHigh;                              //--- Return profit target high
   //--- Get Target High End
}

double getZoneTargetLow() {
   //--- Get Target Low Start
   return m_zoneBounds.zoneTargetLow;                               //--- Return profit target low
   //--- Get Target Low End
}

double getZoneHigh() {
   //--- Get Zone High Start
   return m_zoneBounds.zoneHigh;                                    //--- Return recovery zone high
   //--- Get Zone High End
}

double getZoneLow() {
   //--- Get Zone Low Start
   return m_zoneBounds.zoneLow;                                     //--- Return recovery zone low
   //--- Get Zone Low End
}

//--- Initialization
int initialize() {
   //--- Initialization Start
   m_tradeExecutor.SetExpertMagicNumber(m_tradeConfig.tradeIdentifier); //--- Set magic number
   int totalPositions = PositionsTotal();                               //--- Get total positions
   
   for (int i = 0; i < totalPositions; i++) {                           //--- Iterate positions
      ulong ticket = PositionGetTicket(i);                              //--- Get ticket
      if (PositionSelectByTicket(ticket)) {                             //--- Select position
         if (PositionGetString(POSITION_SYMBOL) == m_tradeConfig.marketSymbol && PositionGetInteger(POSITION_MAGIC) == m_tradeConfig.tradeIdentifier) { //--- Check symbol and magic
            if (activateTrade(ticket)) {                                //--- Activate position
               Print("Existing position activated: Ticket=", ticket);   //--- Log activation
            } else {
               Print("Failed to activate existing position: Ticket=", ticket); //--- Log failure
            }
         }
      }
   }
   
   m_handleRsi = iRSI(m_tradeConfig.marketSymbol, PERIOD_CURRENT, 8, PRICE_CLOSE); //--- Initialize RSI
   if (m_handleRsi == INVALID_HANDLE) {                             //--- Check RSI
      Print("Failed to initialize RSI indicator");                  //--- Log failure
      return INIT_FAILED;                                           //--- Return failure
   }
   
   m_handleEnvUpper = iEnvelopes(m_tradeConfig.marketSymbol, PERIOD_CURRENT, 150, 0, MODE_SMA, PRICE_CLOSE, 0.1); //--- Initialize upper Envelopes
   if (m_handleEnvUpper == INVALID_HANDLE) {                        //--- Check upper Envelopes
      Print("Failed to initialize upper Envelopes indicator");      //--- Log failure
      return INIT_FAILED;                                           //--- Return failure
   }
   
   m_handleEnvLower = iEnvelopes(m_tradeConfig.marketSymbol, PERIOD_CURRENT, 95, 0, MODE_SMA, PRICE_CLOSE, 1.4); //--- Initialize lower Envelopes
   if (m_handleEnvLower == INVALID_HANDLE) {                        //--- Check lower Envelopes
      Print("Failed to initialize lower Envelopes indicator");      //--- Log failure
      return INIT_FAILED;                                           //--- Return failure
   }
   
   ArraySetAsSeries(m_rsiBuffer, true);                             //--- Set RSI buffer
   ArraySetAsSeries(m_envUpperBandBuffer, true);                    //--- Set upper Envelopes buffer
   ArraySetAsSeries(m_envLowerBandBuffer, true);                    //--- Set lower Envelopes buffer
   
   Print("EA initialized successfully");                            //--- Log success
   return INIT_SUCCEEDED;                                           //--- Return success
   //--- Initialization End
}

Hier beginnen wir mit der Erstellung einfacher Getter-Funktionen für den Zugriff auf wichtige Handelsdaten. Die Funktion „getCurrentState“ gibt „m_tradeConfig.currentState“ zurück, sodass wir überprüfen können, ob sich das System im Zustand „INACTIVE“, „RUNNING“ oder „TERMINATING“ befindet. Als Nächstes erstellen wir „getZoneTargetHigh“ und „getZoneTargetLow“, um „m_zoneBounds.zoneTargetHigh“ und „m_zoneBounds.zoneTargetLow“ abzurufen, die die Gewinnzielpreise für unsere Handelsgeschäfte liefern. Dann fügen wir „getZoneHigh“ und „getZoneLow“ hinzu, um „m_zoneBounds.zoneHigh“ und „m_zoneBounds.zoneLow“ abzurufen, wodurch wir die Grenzen der Erholungszone erhalten.

Weiter geht es mit der Funktion „Initialisieren“, mit der wir unseren Expert Advisor (EA) einrichten. Zunächst weisen wir „m_tradeConfig.tradeIdentifier“ dem „m_tradeExecutor“ zu und verwenden „SetExpertMagicNumber“, um unsere Handelsgeschäfte zu kennzeichnen. Dann prüfen wir mit „PositionsTotal“, ob es Positionen gibt, und gehen sie in einer Schleife durch, wobei wir jedes „Ticket“ mit „PositionGetTicket“ abrufen. Wenn PositionSelectByTicket erfolgreich ist und die Position mit „m_tradeConfig.marketSymbol“ und „m_tradeConfig.tradeIdentifier“ (über PositionGetString und „PositionGetInteger“) übereinstimmt, rufen wir „activateTrade“ auf, um sie zu verwalten, wobei Erfolg oder Misserfolg mit „Print“ protokolliert wird.

Als Nächstes richten wir unsere Indikatoren ein. Wir erstellen den RSI-Handle mit der Funktion iRSI für „m_tradeConfig.marketSymbol“ unter Verwendung einer 8-Perioden-Einstellung für den aktuellen Zeitrahmen und „PRICE_CLOSE“. Wenn „m_handleRsi“ INVALID_HANDLE ist, protokollieren wir den Fehler mit „Print“ und geben „INIT_FAILED“ zurück. Anschließend werden die Envelopes-Indikatoren initialisiert: „m_handleEnvUpper“ mit der Funktion „iEnvelopes“ unter Verwendung eines einfachen gleitenden Durchschnitts mit 150 Perioden und einer Abweichung von 0,1 sowie „PRICE_CLOSE“, und „m_handleEnvLower“ mit einer Periodenlänge von 95 und einer Abweichung von 1,4. Wenn eines der Handles „INVALID_HANDLE“ ist, wird der Fehler protokolliert und „INIT_FAILED“ zurückgegeben. Schließlich konfigurieren wir „m_rsiBuffer“, „m_envUpperBandBuffer“ und „m_envLowerBandBuffer“ als Zeitreihen-Arrays mit ArraySetAsSeries, protokollieren den Erfolg mit „Print“ und geben INIT_SUCCEEDED zurück. Wir können diese Funktion nun in OnInit aufrufen, aber zunächst benötigen wir eine Instanz der Klasse.

//--- Global Instance
MarketZoneTrader *trader = NULL;                                        //--- Declare trader instance

Hier richten wir die globale Instanz unseres Systems ein, indem wir einen Zeiger auf die Klasse „MarketZoneTrader“ deklarieren. Wir erstellen die Variable „trader“ als Zeiger auf „MarketZoneTrader“ und initialisieren sie mit „NULL“. Dieser Schritt stellt sicher, dass wir eine einzige, global zugängliche Instanz unseres Handelssystems haben, die wir im gesamten Expert Advisor (EA) verwenden können, um alle Handelsvorgänge zu verwalten, wie z. B. die Initialisierung von Handelsgeschäften, die Ausführung von Aufträgen und die Handhabung von Erholungszonen. Indem wir mit „NULL“ beginnen, bereiten wir den „Trader“ darauf vor, später ordnungsgemäß instanziiert zu werden, und verhindern so jeden vorzeitigen Zugriff, bevor der EA vollständig eingerichtet ist. Wir können nun die Funktion aufrufen.

int OnInit() {
   //--- EA Initialization Start
   trader = new MarketZoneTrader(lotOption, initialLotSize, riskPercentage, riskPoints, maxOrders, restrictMaxOrders, zoneTargetPoints, zoneSizePoints); //--- Create trader instance
   return trader.initialize();                                           //--- Initialize EA
   //--- EA Initialization End
}

In OnInit erstellen wir zunächst eine neue Instanz der Klasse „MarketZoneTrader“ und weisen ihr den globalen Zeiger „trader“ zu. Wir übergeben die nutzerdefinierten Eingabeparameter – „lotOption“, „initialLotSize“, „riskPercentage“, „riskPoints“, „maxOrders“, „restrictMaxOrders“, „zoneTargetPoints“ und „zoneSizePoints“ – an den Konstruktor, um das Handelssystem mit den gewünschten Einstellungen zu konfigurieren. Dann rufen wir die Funktion „initialize“ auf „trader“ auf, um den EA einzurichten, einschließlich der Handelskennzeichnung, der Überprüfung bestehender Positionen und der Initialisierung von Indikatoren, und geben das Ergebnis zurück, um zu signalisieren, ob die Einrichtung erfolgreich war. Diese Funktion stellt sicher, dass unser EA vollständig vorbereitet ist, um den Handel mit den angegebenen Konfigurationen zu starten. Nach dem Kompilieren erhalten wir die folgende Ausgabe.

INITIALISIERUNGSBILD

Aus dem Bild ist ersichtlich, dass das Programm erfolgreich initialisiert wurde. Es gibt jedoch ein Problem, wenn wir versuchen, das Programm zu entfernen. Siehe unten.

OBJEKTE SPEICHERLECK

Aus dem Bild ist ersichtlich, dass es ungelöschte Objekte gibt, die zu einem Speicherleck führen. Um dieses Problem zu lösen, müssen wir die Objektbereinigung durchführen. Um dies zu erreichen, verwenden wir die folgende Logik.

void OnDeinit(const int reason) {
   //--- EA Deinitialization Start
   if (trader != NULL) {                                                 //--- Check trader existence
      delete trader;                                                     //--- Delete trader
      trader = NULL;                                                     //--- Clear pointer
      Print("EA deinitialized");                                         //--- Log deinitialization
   }
   //--- EA Deinitialization End
}

Um die Bereinigung durchzuführen, wird in OnDeinit zunächst geprüft, ob der Zeiger „trader“ nicht „NULL“ ist, um sicherzustellen, dass die „MarketZoneTrader“-Instanz existiert. Wenn dies der Fall ist, verwenden wir den Operator delete, um den für „trader“ zugewiesenen Speicher freizugeben und so Speicherlecks zu vermeiden. Dann setzen wir „trader“ auf „NULL“, um einen versehentlichen Zugriff auf den freigegebenen Speicher zu vermeiden. Schließlich protokollieren wir eine Meldung mit der Funktion „Print“, um zu bestätigen, dass der EA deinitialisiert wurde. Diese Funktion stellt sicher, dass unser EA sauber beendet wird und die Ressourcen ordnungsgemäß freigibt. Wir können nun mit der Definition der Hauptlogik fortfahren, um Signalauswertungen und die Verwaltung der eröffneten Handelsgeschäfte zu handhaben. Dafür brauchen wir Hilfsfunktionen.

//--- Position Management
bool activateTrade(ulong ticket) {
   //--- Position Activation Start
   m_tradeConfig.currentState = INACTIVE;                           //--- Set state to inactive
   ArrayResize(m_tradeConfig.activeTickets, 0);                     //--- Clear tickets
   m_lossTracker.tradeLossTracker = 0.0;                            //--- Reset loss tracker
   if (!configureTrade(ticket)) {                                    //--- Configure trade
      return false;                                                 //--- Return failure
   }
   storeTradeTicket(ticket);                                        //--- Store ticket
   if (m_tradeConfig.direction == ORDER_TYPE_BUY) {                 //--- Handle buy position
      m_zoneBounds.zoneHigh = m_tradeConfig.openPrice;              //--- Set zone high
      m_zoneBounds.zoneLow = m_zoneBounds.zoneHigh - m_tradeConfig.zoneRecoverySpan; //--- Set zone low
      m_tradeConfig.accumulatedBuyVolume = m_tradeConfig.initialVolume; //--- Set buy volume
      m_tradeConfig.accumulatedSellVolume = 0.0;                    //--- Reset sell volume
   } else {                                                         //--- Handle sell position
      m_zoneBounds.zoneLow = m_tradeConfig.openPrice;               //--- Set zone low
      m_zoneBounds.zoneHigh = m_zoneBounds.zoneLow + m_tradeConfig.zoneRecoverySpan; //--- Set zone high
      m_tradeConfig.accumulatedSellVolume = m_tradeConfig.initialVolume; //--- Set sell volume
      m_tradeConfig.accumulatedBuyVolume = 0.0;                     //--- Reset buy volume
   }
   m_zoneBounds.zoneTargetHigh = m_zoneBounds.zoneHigh + m_tradeConfig.zoneProfitSpan; //--- Set target high
   m_zoneBounds.zoneTargetLow = m_zoneBounds.zoneLow - m_tradeConfig.zoneProfitSpan; //--- Set target low
   m_tradeConfig.currentState = RUNNING;                            //--- Set state to running
   return true;                                                     //--- Return success
   //--- Position Activation End
}

//--- Tick Processing
void processTick() {
   //--- Tick Processing Start
   double askPrice = NormalizeDouble(getMarketAsk(), Digits());     //--- Get ask price
   double bidPrice = NormalizeDouble(getMarketBid(), Digits());     //--- Get bid price
   
   if (!isNewBar()) return;                                         //--- Exit if not new bar
   
   if (!CopyBuffer(m_handleRsi, 0, 0, 3, m_rsiBuffer)) {            //--- Load RSI data
      Print("Error loading RSI data. Reverting.");                  //--- Log RSI failure
      return;                                                       //--- Exit
   }
   
   if (!CopyBuffer(m_handleEnvUpper, 0, 0, 3, m_envUpperBandBuffer)) { //--- Load upper Envelopes
      Print("Error loading upper envelopes data. Reverting.");         //--- Log failure
      return;                                                          //--- Exit
   }
   
   if (!CopyBuffer(m_handleEnvLower, 1, 0, 3, m_envLowerBandBuffer)) { //--- Load lower Envelopes
      Print("Error loading lower envelopes data. Reverting.");         //--- Log failure
      return;                                                          //--- Exit
   }
   
   int ticket = 0;                                                     //--- Initialize ticket
   
   const int rsiOverbought = 70;                                       //--- Set RSI overbought level
   const int rsiOversold = 30;                                         //--- Set RSI oversold level
   
   if (m_rsiBuffer[1] < rsiOversold && m_rsiBuffer[2] > rsiOversold && m_rsiBuffer[0] < rsiOversold) { //--- Check buy signal
      if (askPrice > m_envUpperBandBuffer[0]) {                        //--- Confirm price above upper Envelopes
         if (!m_restrictMaxOrders || PositionsTotal() < m_maxOrders) { //--- Check position limit
            ticket = openOrder(ORDER_TYPE_BUY, 0, 0);                  //--- Open buy order
         }
      }
   } else if (m_rsiBuffer[1] > rsiOverbought && m_rsiBuffer[2] < rsiOverbought && m_rsiBuffer[0] > rsiOverbought) { //--- Check sell signal
      if (bidPrice < m_envLowerBandBuffer[0]) {                        //--- Confirm price below lower Envelopes
         if (!m_restrictMaxOrders || PositionsTotal() < m_maxOrders) { //--- Check position limit
            ticket = openOrder(ORDER_TYPE_SELL, 0, 0);                 //--- Open sell order
         }
      }
   }
   
   if (ticket > 0) {                                                //--- Check if trade opened
      if (activateTrade(ticket)) {                                  //--- Activate position
         Print("New position activated: Ticket=", ticket);          //--- Log activation
      } else {
         Print("Failed to activate new position: Ticket=", ticket); //--- Log failure
      }
   }
   //--- Tick Processing End
}

Hier entwickeln wir unser Programm weiter, indem wir die Funktionen „activateTrade“ und „processTick“ innerhalb der Klasse „MarketZoneTrader“ implementieren, um Positionen zu verwalten und Marktticks zu verarbeiten. Wir beginnen mit der Funktion „activateTrade“, um einen Handel für ein bestimmtes „Ticket“ zu aktivieren. Zunächst setzen wir „m_tradeConfig.currentState“ auf „INACTIVE“ und löschen „m_tradeConfig.activeTickets“ mit der Funktion ArrayResize, um die Ticketliste zurückzusetzen. Wir setzen „m_lossTracker.tradeLossTracker“ auf 0,0 zurück und rufen dann „configureTrade“ mit „ticket“ auf. Wenn sie fehlschlägt, geben wir false zurück. Als Nächstes speichern wir das „Ticket“ mit „storeTradeTicket“. Bei einem Kaufgeschäft („m_tradeConfig.direction“ als ORDER_TYPE_BUY) setzen wir „m_zoneBounds.zoneHigh“ auf „m_tradeConfig.openPrice“, berechnen „m_zoneBounds.zoneLow“ durch Subtraktion von „m_tradeConfig.zoneRecoverySpan“, und aktualisieren Sie „m_tradeConfig.accumulatedBuyVolume“ auf „m_tradeConfig.initialVolume“, während Sie „m_tradeConfig.accumulatedSellVolume“ zurücksetzen.

Bei einem Verkaufsgeschäft setzen wir „m_zoneBounds.zoneLow“ auf „m_tradeConfig.openPrice“, fügen „m_tradeConfig.zoneRecoverySpan“ für „m_zoneBounds.zoneHigh“ hinzu und passen die Volumina entsprechend an. Dann setzen wir „m_zoneBounds.zoneTargetHigh“ und „m_zoneBounds.zoneTargetLow“ mit „m_tradeConfig.zoneProfitSpan“, ändern „m_tradeConfig.currentState“ auf „RUNNING“ und geben true zurück.

Als Nächstes erstellen wir die Funktion „processTick“, um Marktticks zu verarbeiten. Wir holen uns „askPrice“ und „bidPrice“ mit „getMarketAsk“ und „getMarketBid“, normalisiert mit NormalizeDouble und „Digits“. Wenn „isNewBar“ den Wert „false“ zurückgibt, wird das Programm beendet, um Ressourcen zu sparen. Wir laden Indikator-Daten mit CopyBuffer für „m_handleRsi“ in „m_rsiBuffer“, „m_handleEnvUpper“ in „m_envUpperBandBuffer“, und „m_handleEnvLower“ in „m_envLowerBandBuffer“, wobei Fehler mit „Print“ protokolliert und bei Fehlern beendet werden. Für Handelssignale setzen wir „rsiOverbought“ auf 70 und „rsiOversold“ auf 30.

Wenn „m_rsiBuffer“ eine überverkaufte Situation anzeigt und „askPrice“ „m_envUpperBandBuffer“ übersteigt, eröffnen wir eine Kauforder mit „openOrder“, wenn „m_restrictMaxOrders“ falsch ist oder PositionsTotal unter „m_maxOrders“ liegt. Bei einem überkauften Zustand, bei dem „bidPrice“ unter „m_envLowerBandBuffer“ liegt, eröffnen wir einen Verkaufsauftrag. Wenn ein gültiges „Ticket“ zurückgegeben wird, rufen wir „activateTrade“ auf und protokollieren das Ergebnis im Journal. Wir können nun die Funktion in OnTick ausführen, um die Signalauswertung und Positionsauslösung zu verarbeiten.

void OnTick() {
   //--- Tick Handling Start
   if (trader != NULL) {                                                 //--- Check trader existence
      trader.processTick();                                              //--- Process tick
   }
   //--- Tick Handling End
}

In „OnTick“ überprüfen wir zunächst, ob der Zeiger „trader“, unsere Instanz der „MarketZoneTrader“-Klasse, nicht „NULL“ ist, um sicherzustellen, dass das Handelssystem initialisiert ist. Falls vorhanden, rufen wir die Funktion „processTick“ auf „trader“ auf, um jeden Markttick zu verarbeiten, Positionen zu bewerten, Indikatorsignale zu überprüfen und bei Bedarf Handelsgeschäfte auszuführen. Nach der Kompilierung erhalten wir folgendes Ergebnis.

ANFANGSPOSITION

Anhand des Bildes können wir erkennen, dass wir ein Signal identifiziert, bewertet und eine Kaufposition initiiert haben. Jetzt geht es darum, die offenen Positionen zu verwalten. Wir werden das aus Gründen der Modularität in Funktionen behandeln.

//--- Market Tick Evaluation
void evaluateMarketTick() {
   //--- Tick Evaluation Start
   if (m_tradeConfig.currentState == INACTIVE) return;              //--- Exit if inactive
   if (m_tradeConfig.currentState == TERMINATING) {                 //--- Check terminating state
      finalizePosition();                                           //--- Finalize position
      return;                                                       //--- Exit
   }
}

Hier implementieren wir die Funktion „evaluateMarketTick“ innerhalb der Klasse „MarketZoneTrader“, um die Marktbedingungen für aktive Handelsgeschäfte zu bewerten. Wir beginnen mit der Überprüfung des „m_tradeConfig.currentState“, um festzustellen, ob er „INACTIVE“ ist. Wenn dies der Fall ist, wird die Transaktion sofort beendet, um unnötige Verarbeitung zu vermeiden, wenn keine Handelsgeschäfte aktiv sind. Als Nächstes wird geprüft, ob „m_tradeConfig.currentState“ „TERMINATING“ ist. Wenn dies der Fall ist, rufen wir die Funktion „finalizePosition“ auf, um alle offenen Positionen zu schließen und den Handelszyklus abzuschließen und dann zu beenden. Hier ist die Funktion zum Schließen von Handelsgeschäften.

//--- Position Finalization
bool finalizePosition() {
   //--- Position Finalization Start
   m_tradeConfig.currentState = TERMINATING;                        //--- Set terminating state
   TradeMetrics metrics = {true, 0.0, 0.0};                         //--- Initialize metrics
   closeActiveTrades(metrics);                                       //--- Close all trades
   if (metrics.operationSuccess) {                                  //--- Check success
      ArrayResize(m_tradeConfig.activeTickets, 0);                  //--- Clear tickets
      m_tradeConfig.currentState = INACTIVE;                        //--- Set inactive state
      Print("Position closed successfully");                        //--- Log success
   } else {
      Print("Failed to close position");                            //--- Log failure
   }
   return metrics.operationSuccess;                                 //--- Return status
   //--- Position Finalization End
}

Wir beginnen damit, „m_tradeConfig.currentState“ auf „TERMINATING“ zu setzen, um anzuzeigen, dass der Handelszyklus beendet ist. Dies trägt dazu bei, das Handelsmanagement vor einem Zirkel zu schützen, wenn wir dabei sind, die Handelsgeschäfte abzuschließen. Dann initialisieren wir eine Struktur „TradeMetrics“ mit dem Namen „metrics“, wobei „operationSuccess“ auf true, „totalVolume“ auf 0,0 und „netProfitLoss“ auf 0,0 gesetzt wird, um die Ergebnisse der Schließung zu verfolgen. Wir rufen „closeActiveTrades“ mit „metrics“ auf, um alle in „m_tradeConfig.activeTickets“ aufgeführten offenen Positionen zu schließen. Wenn „metrics.operationSuccess“ wahr bleibt, löschen wir „m_tradeConfig.activeTickets“ mit ArrayResize, um die Ticketliste zurückzusetzen, setzen „m_tradeConfig.currentState“ auf „INACTIVE“, um das System als inaktiv zu kennzeichnen, und protokollieren den Erfolg mit „Print“.

Wenn das Schließen fehlschlägt, protokollieren wir den Fehler mit „Print“. Schließlich geben wir „metrics.operationSuccess“ zurück, um anzuzeigen, ob der Prozess erfolgreich abgeschlossen wurde. Wenn wir die Handelsgeschäfte zu diesem Zeitpunkt nicht geschlossen haben, bedeutet dies, dass wir uns nicht im Prozess der Positionsschließung befinden, sodass wir mit der Bewertung fortfahren können, um zu sehen, ob der Preis die Erholungszonen oder Zielniveaus erreicht hat. Wir beginnen mit einem Kauf

double currentPrice;                                             //--- Initialize price
if (m_tradeConfig.direction == ORDER_TYPE_BUY) {                 //--- Handle buy position
   currentPrice = getMarketBid();                                //--- Get bid price
   if (currentPrice > m_zoneBounds.zoneTargetHigh) {             //--- Check profit target
      Print("Closing position: Bid=", currentPrice, " > TargetHigh=", m_zoneBounds.zoneTargetHigh); //--- Log closure
      finalizePosition();                                        //--- Close position
      return;                                                    //--- Exit
   } else if (currentPrice < m_zoneBounds.zoneLow) {             //--- Check recovery trigger
      Print("Triggering recovery trade: Bid=", currentPrice, " < ZoneLow=", m_zoneBounds.zoneLow); //--- Log recovery
      triggerRecoveryTrade(ORDER_TYPE_SELL, currentPrice);       //--- Open sell recovery
   }
}

Wir fahren fort mit der Implementierung von Logik in der Funktion „evaluateMarketTick“ der Klasse „MarketZoneTrader“, um Kaufpositionen zu behandeln. Wir beginnen mit der Deklaration von „currentPrice“, um den Marktpreis zu speichern. Wenn „m_tradeConfig.direction“ ORDER_TYPE_BUY ist, setzen wir „currentPrice“ mit Hilfe der Funktion „getMarketBid“, um den Geldkurs zu ermitteln, da dies der Kurs ist, zu dem wir eine Kaufposition schließen können. Als Nächstes wird geprüft, ob „currentPrice“ „m_zoneBounds.zoneTargetHigh“ übersteigt. Wenn dies der Fall ist, protokollieren wir die Schließung mit „Print“ und zeigen den Geldkurs und das Ziel an, rufen dann „finalizePosition“ auf, um den Handel zu schließen und mit „return“ zu beenden.

Wenn „currentPrice“ unter „m_zoneBounds.zoneLow“ fällt, protokollieren wir einen Recovery-Trigger mit „Print“ und rufen „triggerRecoveryTrade“ mit ORDER_TYPE_SELL und „currentPrice“ auf, um ein Verkaufsgeschäft zu eröffnen, um Verluste zu begrenzen. Diese Logik stellt sicher, dass wir gewinnbringende Kaufgeschäfte abschließen oder bei Verlusten die Wiederherstellung einleiten, damit unsere Strategie reaktionsfähig bleibt. Hier ist die Logik für die Funktion, die für die Eröffnung von Wiederherstellungsgeschäften verantwortlich ist.

//--- Recovery Trade Handling
void triggerRecoveryTrade(ENUM_ORDER_TYPE tradeDirection, double price) {
   //--- Recovery Trade Start
   TradeMetrics metrics = {true, 0.0, 0.0};                         //--- Initialize metrics
   closeActiveTrades(metrics);                                      //--- Close existing trades
   for (int i = 0; i < 10 && !metrics.operationSuccess; i++) {      //--- Retry closure
      Sleep(1000);                                                  //--- Wait 1 second
      metrics.operationSuccess = true;                              //--- Reset success flag
      closeActiveTrades(metrics);                                   //--- Retry closure
   }
   m_lossTracker.tradeLossTracker += metrics.netProfitLoss;         //--- Update loss tracker
   if (m_lossTracker.tradeLossTracker > 0 && metrics.operationSuccess) { //--- Check positive profit
      Print("Closing position due to positive profit: ", m_lossTracker.tradeLossTracker); //--- Log closure
      finalizePosition();                                           //--- Close position
      m_lossTracker.tradeLossTracker = 0.0;                         //--- Reset loss tracker
      return;                                                       //--- Exit
   }
   double tradeSize = determineRecoverySize(tradeDirection);        //--- Calculate trade size
   ulong ticket = openMarketTrade(tradeDirection, tradeSize, price); //--- Open recovery trade
   if (ticket > 0) {                                                //--- Check if trade opened
      storeTradeTicket(ticket);                                     //--- Store ticket
      m_tradeConfig.direction = tradeDirection;                     //--- Update direction
      if (tradeDirection == ORDER_TYPE_BUY) m_tradeConfig.accumulatedBuyVolume += tradeSize; //--- Update buy volume
      else m_tradeConfig.accumulatedSellVolume += tradeSize;        //--- Update sell volume
      Print("Recovery trade opened: Ticket=", ticket, ", Direction=", EnumToString(tradeDirection), ", Volume=", tradeSize); //--- Log recovery trade
   }
   //--- Recovery Trade End
}

//--- Recovery Size Calculation
double determineRecoverySize(ENUM_ORDER_TYPE tradeDirection) {
   //--- Recovery Size Calculation Start
   double tradeSize = -m_lossTracker.tradeLossTracker / m_tradeConfig.zoneProfitSpan; //--- Calculate lot size
   tradeSize = MathCeil(tradeSize / getMarketVolumeStep()) * getMarketVolumeStep(); //--- Round to volume step
   return tradeSize;                                                //--- Return trade size
   //--- Recovery Size Calculation End
}

Um Fälle zu behandeln, in denen der Markt Recovery-Instanzen auslösen muss, beginnen wir mit der Funktion „triggerRecoveryTrade“, um Recovery-Trades zu behandeln, wenn sich eine Position gegen uns bewegt. Zunächst initialisieren wir eine Struktur „TradeMetrics“ mit dem Namen „metrics“, wobei „operationSuccess“ auf true, „totalVolume“ auf 0,0 und „netProfitLoss“ auf 0,0 gesetzt wird. Wir rufen „closeActiveTrades“ mit „metrics“ auf, um bestehende Positionen zu schließen. Wenn „metrics.operationSuccess“ falsch ist, versuchen wir es bis zu 10 Mal erneut, warten eine Sekunde mit Sleep und setzen „operationSuccess“ vor jedem Versuch zurück.

Wir aktualisieren „m_lossTracker.tradeLossTracker“ durch Hinzufügen von „metrics.netProfitLoss“. Wenn „m_lossTracker.tradeLossTracker“ positiv ist und „metrics.operationSuccess“ wahr ist, protokollieren wir die Schließung mit „Print“, rufen „finalizePosition“ auf, setzen „m_lossTracker.tradeLossTracker“ auf 0,0 zurück und beenden mit „return“. Andernfalls berechnen wir die Erholungsgröße „tradeSize“ mit „determineRecoverySize“ und „tradeDirection“ und eröffnen dann einen neuen Handel mit „openMarketTrade“ unter Verwendung von „tradeDirection“, „tradeSize“ und „price“.

Wenn das zurückgegebene „Ticket“ gültig ist, speichern wir es mit „storeTradeTicket“, aktualisieren „m_tradeConfig.direction“, passen „m_tradeConfig.accumulatedBuyVolume“ oder „m_tradeConfig.accumulatedSellVolume“ basierend auf „tradeDirection“, und protokollieren den Handel mit „Print“ unter Verwendung von EnumToString. Als Nächstes erstellen wir die Funktion „determineRecoverySize“, um die Losgröße für Recovery Handelsgeschäfte zu berechnen. Wir berechnen „tradeSize“, indem wir den negativen Wert von „m_lossTracker.tradeLossTracker“ durch „m_tradeConfig.zoneProfitSpan“ dividieren, um die Größe des Handels zur Deckung der Verluste zu bestimmen. Anschließend wird „tradeSize“ mit MathCeil und „getMarketVolumeStep“ auf den Volumenschritt des Brokers gerundet, um die Konformität sicherzustellen, und das Ergebnis zurückgegeben. Jetzt werden die Wiederherstellungsinstanzen behandelt, und wir können mit der Logik für die Behandlung der Verkaufszonen fortfahren. Die Logik ist genau das Gegenteil von Kaufen, also werden wir nicht viel Zeit darauf verwenden. Die endgültige vollständige Funktion wird wie folgt aussehen.

//--- Market Tick Evaluation
void evaluateMarketTick() {
   //--- Tick Evaluation Start
   if (m_tradeConfig.currentState == INACTIVE) return;              //--- Exit if inactive
   if (m_tradeConfig.currentState == TERMINATING) {                 //--- Check terminating state
      finalizePosition();                                           //--- Finalize position
      return;                                                       //--- Exit
   }
   double currentPrice;                                             //--- Initialize price
   if (m_tradeConfig.direction == ORDER_TYPE_BUY) {                 //--- Handle buy position
      currentPrice = getMarketBid();                                //--- Get bid price
      if (currentPrice > m_zoneBounds.zoneTargetHigh) {             //--- Check profit target
         Print("Closing position: Bid=", currentPrice, " > TargetHigh=", m_zoneBounds.zoneTargetHigh); //--- Log closure
         finalizePosition();                                        //--- Close position
         return;                                                    //--- Exit
      } else if (currentPrice < m_zoneBounds.zoneLow) {             //--- Check recovery trigger
         Print("Triggering recovery trade: Bid=", currentPrice, " < ZoneLow=", m_zoneBounds.zoneLow); //--- Log recovery
         triggerRecoveryTrade(ORDER_TYPE_SELL, currentPrice);       //--- Open sell recovery
      }
   } else if (m_tradeConfig.direction == ORDER_TYPE_SELL) {         //--- Handle sell position
      currentPrice = getMarketAsk();                                //--- Get ask price
      if (currentPrice < m_zoneBounds.zoneTargetLow) {              //--- Check profit target
         Print("Closing position: Ask=", currentPrice, " < TargetLow=", m_zoneBounds.zoneTargetLow); //--- Log closure
         finalizePosition();                                        //--- Close position
         return;                                                    //--- Exit
      } else if (currentPrice > m_zoneBounds.zoneHigh) {            //--- Check recovery trigger
         Print("Triggering recovery trade: Ask=", currentPrice, " > ZoneHigh=", m_zoneBounds.zoneHigh); //--- Log recovery
         triggerRecoveryTrade(ORDER_TYPE_BUY, currentPrice);        //--- Open buy recovery
      }
   }
   //--- Tick Evaluation End
}

Die Funktion übernimmt nun alle Anweisungen für die Wiederherstellung. Nach der Kompilierung erhalten wir folgendes Ergebnis.

ENDGÜLTIGES ERGEBNIS

Aus dem Bild können wir ersehen, dass wir Positionen, die aufgrund von Abprallsignalen bei einem Trend ausgelöst werden, erfolgreich abwickeln. Bleiben nur noch die Backtests des Programms, und das wird im nächsten Abschnitt behandelt.


Backtests

Nach einem gründlichen Backtest erhalten wir folgende Ergebnisse.

Backtest-Grafik:

GRAPH

Bericht des Backtest:

BERICHT


Schlussfolgerung

Abschließend haben wir ein robustes MQL5-Programm entwickelt, das ein Zone Recovery System für den Trendhandel mit Envelopes implementiert, das den Relative Strength Index (RSI) und die Envelopes-Indikatoren kombiniert, um Handelsgelegenheiten zu identifizieren und Verluste durch strukturierte Erholungszonen zu verwalten, wobei ein objektorientierter Programmieransatz (OOP) verwendet wird. Mit Komponenten wie der Klasse „MarketZoneTrader“, Strukturen wie „TradeConfig“ und „ZoneBoundaries“ und Funktionen wie „processTick“ und „triggerRecoveryTrade“ haben wir ein flexibles System geschaffen, das Sie durch Anpassung von Parametern wie „zoneTargetPoints“ oder „riskPercentage“ an verschiedene Marktbedingungen anpassen können.

Haftungsausschluss: Dieser Artikel ist nur für Bildungszwecke gedacht. Der Handel ist mit erheblichen finanziellen Risiken verbunden, und die Volatilität der Märkte kann zu Verlusten führen. Gründliche Backtests und sorgfältiges Risikomanagement sind unerlässlich, bevor Sie dieses Programm auf den Live-Märkten einsetzen.

Mit den in diesem Artikel geschaffenen Grundlagen können Sie dieses System zur Zonenerholung verfeinern oder seine Logik anpassen, um neue Handelsstrategien zu entwickeln, die Ihren Fortschritt im algorithmischen Handel vorantreiben. Viel Spaß beim Handeln!

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

Letzte Kommentare | Zur Diskussion im Händlerforum (2)
Sabrina Hellal
Sabrina Hellal | 9 Juli 2025 in 13:44
Vielen Dank 🙏.
Allan Munene Mutiiria
Allan Munene Mutiiria | 9 Juli 2025 in 16:46
Sabrina Hellal #:
Vielen Dank 🙏.

Sehr willkommen. Danke

Vom Neuling zum Experten: Animierte Nachrichten-Schlagzeile mit MQL5 (IV) – Markteinsichten durch lokal verfügbare KI-Modelle Vom Neuling zum Experten: Animierte Nachrichten-Schlagzeile mit MQL5 (IV) – Markteinsichten durch lokal verfügbare KI-Modelle
In der heutigen Diskussion untersuchen wir, wie man Open-Source-KI-Modelle selbst hosten und zur Gewinnung von Markteinblicken nutzen kann. Dies ist Teil unserer laufenden Bemühungen, den News Headline EA zu erweitern, indem wir einen AI Info-Streifen einführen, die ihn in ein Multi-Integrations-Assistenz-Tool verwandelt. Der aktualisierte EA zielt darauf ab, Händler durch Kalenderereignisse, aktuelle Finanznachrichten, technische Indikatoren und jetzt auch durch KI-generierte Marktperspektiven auf dem Laufenden zu halten - und bietet so zeitnahe, vielfältige und intelligente Unterstützung für Handelsentscheidungen. Seien Sie dabei, wenn wir praktische Integrationsstrategien erforschen und untersuchen, wie MQL5 mit externen Ressourcen zusammenarbeiten kann, um ein leistungsstarkes und intelligentes Arbeitsterminal für den Handel aufzubauen.
Graphentheorie: Dijkstras Algorithmus angewandt im Handel Graphentheorie: Dijkstras Algorithmus angewandt im Handel
Dijkstras Algorithmus, eine klassische Lösung für den kürzesten Weg in der Graphentheorie, kann Handelsstrategien durch die Modellierung von Marktnetzwerken optimieren. Händler können damit die effizientesten Routen in den Kerzen-Chartdaten finden.
Selbstoptimierende Expert Advisors in MQL5 (Teil 8): Analyse mehrerer Strategien (3) – Gewichtetes Abstimmungsverhalten Selbstoptimierende Expert Advisors in MQL5 (Teil 8): Analyse mehrerer Strategien (3) – Gewichtetes Abstimmungsverhalten
In diesem Artikel wird untersucht, wie die Bestimmung der optimalen Anzahl von Strategien in einem Ensemble eine komplexe Aufgabe sein kann, die durch den Einsatz des genetischen Optimierers von MetaTrader 5 leichter zu lösen ist. Die MQL5 Cloud wird auch als Schlüsselressource zur Beschleunigung von Backtests und Optimierung eingesetzt. Alles in allem schafft unsere Diskussion hier die Grundlage für die Entwicklung statistischer Modelle zur Bewertung und Verbesserung von Handelsstrategien auf der Grundlage unserer ersten Ensemble-Ergebnisse.
MQL5-Assistenz-Techniken, die Sie kennen sollten (Teil 75): Verwendung des Awesome Oszillators und des Envelopes MQL5-Assistenz-Techniken, die Sie kennen sollten (Teil 75): Verwendung des Awesome Oszillators und des Envelopes
Der Awesome Oscillator von Bill Williams und der Envelopes-Kanal sind ein Paar, das komplementär in einem MQL5 Expert Advisor verwendet werden kann. Wir verwenden den Awesome Oscillator wegen seiner Fähigkeit, Trends zu erkennen, während der Envelope-Kanal zur Definition unserer Unterstützungs-/Widerstandsniveaus herangezogen wird. Bei der Erkundung dieser Indikatorpaarung verwenden wir den MQL5-Assistenten, um das Potenzial dieser beiden Indikatoren zu ermitteln und zu testen.