English
preview
Automatisieren von Handelsstrategien in MQL5 (Teil 19): Envelopes Trend Bounce Scalping - Handelsausführung und Risikomanagement (Teil II)

Automatisieren von Handelsstrategien in MQL5 (Teil 19): Envelopes Trend Bounce Scalping - Handelsausführung und Risikomanagement (Teil II)

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

Einführung

In unserem letzten Artikel (Teil 18) haben wir die Grundlage für die Envelopes Trend Bounce Scalping Strategie in MetaQuotes Language 5 (MQL5) gelegt, indem wir die Kerninfrastruktur und die Logik der Signalerzeugung des Expert Advisors aufgebaut haben. In Teil 19 gehen wir nun weiter und implementieren die Handelsausführung und das Risikomanagement, um die Strategie vollständig zu automatisieren. Wir werden die folgenden Themen behandeln:

  1. Strategiefahrplan und Architektur
  2. Implementation in MQL5
  3. Backtest und Optimierung
  4. Schlussfolgerung

Am Ende werden Sie ein komplettes MQL5-Handelssystem für das Scalping von Trendausschlägen haben, das für die Leistung optimiert ist - legen wir los!


Strategiefahrplan und Architektur

In Teil 18 haben wir die Grundlage für die Envelopes Trend Bounce Scalping-Strategie geschaffen, indem wir das System so eingerichtet haben, dass es Handelssignale anhand von Preisinteraktionen mit dem Envelopes-Indikator erkennt, die durch Trendfilter wie gleitende Durchschnitte und RSI bestätigt wurden. Wir haben uns auf den Aufbau der Infrastruktur konzentriert, um die Marktbedingungen zu überwachen und potenzielle Handelsmöglichkeiten zu erkennen, aber wir haben das System nicht in die Lage versetzt, Handelsgeschäfte auszuführen oder Risiken zu verwalten. Unser Fahrplan für Teil 19 verlagert sich auf die Aktivierung der Handelsausführung und die Implementierung des Risikomanagements, um sicherzustellen, dass die Strategie effektiv und sicher auf Signale reagieren kann.

Unser architektonischer Plan legt den Schwerpunkt auf einen modularen Ansatz, der einen klaren Weg für die Umsetzung von Signalen in Handlungen schafft und gleichzeitig Sicherheitsvorkehrungen einschließt. Unser Ziel ist es, einen Mechanismus für die Platzierung von Kauf- und Verkaufsaufträgen auf der Grundlage von validierten Signalen zu entwickeln, zusammen mit einem Rahmen für das Risikomanagement, der Stop-Loss- und Take-Profit-Levels festlegt, die Positionsgrößen auf der Grundlage des Kontostands anpasst und die Gesamtverluste zum Schutz des Kapitals begrenzt. Mit diesem Entwurf wird ein kohärentes, automatisiertes Scalping-System geschaffen. Wir werden einige weitere Klassen definieren, um den Handel zu handhaben und die gesamte Logik in die Tick-Logik einzubinden, um alles zum Leben zu erwecken. Kurz gesagt, das ist es, was wir erreichen wollen.

PLANEN DER ARCHITEKTUR


Implementation in MQL5

Um das Programm in MQL5 zu erstellen, öffnen Sie MetaEditor, gehen zum Navigator, suchen Sie den Ordner Experts, klicken Sie auf die Registerkarte „Neu“ und folgen Sie den Anweisungen, um die Datei zu erstellen. Sobald dies geschehen ist, müssen wir in der Codierungsumgebung einige Schnittstellen und Klassen für eine verbesserte Signalerzeugung, Organisation und Handelsverwaltung deklarieren. Beginnen wir mit der grundlegenden Schnittstelle für Signalausdrücke.

//--- Define interface for strategy signal expressions
interface IAdvisorStrategyExpression {
   bool Evaluate();                                       //--- Evaluate signal
   bool GetFireOnlyWhenReset();                           //--- Retrieve fire-only-when-reset flag
   void SetFireOnlyWhenReset(bool value);                 //--- Set fire-only-when-reset flag
   void ResetSignalValue();                               //--- Reset signal value
};

//--- Define base class for advisor signals
class ASSignal : public IAdvisorStrategyExpression {
private:
   bool _fireOnlyWhenReset;                               //--- Store fire-only-when-reset flag
   int _previousSignalValue;                              //--- Store previous signal value
   int _signalValue;                                      //--- Store current signal value

protected:
   //--- Declare pure virtual method for signal evaluation
   bool virtual EvaluateSignal() = 0;                      //--- Require derived classes to implement

public:
   //--- Initialize signal
   void ASSignal() {
      _fireOnlyWhenReset = false;                         //--- Set fire-only-when-reset to false
      _previousSignalValue = -1;                          //--- Set previous value to -1
      _signalValue = -1;                                  //--- Set current value to -1
   }

   //--- Evaluate signal
   bool Evaluate() {
      if (_signalValue == -1) {                           //--- Check if signal uncomputed
         _signalValue = EvaluateSignal();                 //--- Compute signal
      }
      if (_fireOnlyWhenReset) {                           //--- Check fire-only-when-reset
         return (_previousSignalValue <= 0 && _signalValue == 1); //--- Return true if signal transitions to 1
      } else {
         return _signalValue == 1;                        //--- Return true if signal is 1
      }
   }

   //--- Retrieve fire-only-when-reset flag
   bool GetFireOnlyWhenReset() {
      return _fireOnlyWhenReset;                          //--- Return flag
   }

   //--- Set fire-only-when-reset flag
   void SetFireOnlyWhenReset(bool value) {
      _fireOnlyWhenReset = value;                         //--- Set flag
   }

   //--- Reset signal value
   void ResetSignalValue() {
      _previousSignalValue = _signalValue;                //--- Store current as previous
      _signalValue = -1;                                  //--- Reset current value
   }
};

Um einen schlanken Rahmen für den Umgang mit Handelssignalen zu schaffen, konzentrieren wir uns auf ein modulares und zuverlässiges Signalbewertungssystem. Wir beginnen mit der Definition der Schnittstelle „IAdvisorStrategyExpression“, die wir verwenden, um einen konsistenten Entwurf für Signaloperationen zu erstellen. Diese Schnittstelle umfasst vier Schlüsselfunktionen: die Funktion „Evaluate“, um festzustellen, ob ein Signal aktiv ist, die Funktion „GetFireOnlyWhenReset“, um ein Flag zu prüfen, das die Signalauslösung steuert, die Funktion „SetFireOnlyWhenReset“, um dieses Flag zu ändern, und die Funktion „ResetSignalValue“, um den Zustand des Signals für nachfolgende Auswertungen zu löschen.

Anschließend entwickeln wir die Klasse „ASSignal“, die wir so gestalten, dass sie die Schnittstelle „IAdvisorStrategyExpression“ implementiert und als Grundlage für bestimmte Signaltypen in unserer Strategie dient. Innerhalb der Klasse „ASSignal“ definieren wir drei private Variablen: „_fireOnlyWhenReset“, um zu steuern, ob Signale nur nach einem Reset aktiviert werden, „_previousSignalValue“, um den vorherigen Signalzustand zu verfolgen, und „_signalValue“, um den aktuellen Zustand zu speichern. Wir initialisieren diese in der Konstruktor „ASSignal“, indem wir „_fireOnlyWhenReset“ auf false und sowohl „_previousSignalValue“ als auch „_signalValue“ auf -1 setzen, was einen nicht berechneten Zustand anzeigt. Unsere Funktion „Evaluate“ prüft, ob „_signalValue“ -1 ist, ruft die rein virtuelle Funktion „EvaluateSignal“ auf (die in abgeleiteten Klassen zu definieren ist), um das Signal zu berechnen, und gibt true zurück, basierend auf „_fireOnlyWhenReset“ - entweder wenn „_signalValue“ 1 ist oder beim Übergang von einem nicht-positiven „_previousSignalValue“ auf 1.

Zur Verwaltung des Signalverhaltens implementieren wir die Funktion „GetFireOnlyWhenReset“, um den Wert des Flags „_fireOnlyWhenReset“ abzurufen, und die Funktion „SetFireOnlyWhenReset“, um ihn zu aktualisieren. Außerdem ist die Funktion „ResetSignalValue“ enthalten, mit der wir „_signalValue“ in „_previousSignalValue“ speichern und „_signalValue“ auf -1 zurücksetzen, um den nächsten Auswertungszyklus vorzubereiten. Indem wir „EvaluateSignal“ als rein virtuell markieren, verlangen wir von abgeleiteten Klassen, dass sie eine spezifische Signallogik bereitstellen, was Flexibilität gewährleistet. Wir können nun einige weitere Klassen für die Logik der Signalverwaltung definieren.

//--- Define class for managing trade signals
class TradeSignalCollection {
private:
   IAdvisorStrategyExpression* _tradeSignals[];           //--- Store array of signal pointers
   int _pointer;                                          //--- Track current iteration index
   int _size;                                             //--- Track number of signals

public:
   //--- Initialize empty signal collection
   void TradeSignalCollection() {
      _pointer = -1;                                      //--- Set initial pointer to -1
      _size = 0;                                          //--- Set initial size to 0
   }

   //--- Destructor to clean up signals
   void ~TradeSignalCollection() {
      for (int i = 0; i < ArraySize(_tradeSignals); i++) { //--- Iterate signals
         delete(_tradeSignals[i]);                        //--- Delete each signal
      }
   }

   //--- Add signal to collection
   void Add(IAdvisorStrategyExpression* item) {
      _size = _size + 1;                                  //--- Increment size
      ArrayResize(_tradeSignals, _size, 8);               //--- Resize array with reserve
      _tradeSignals[(_size - 1)] = item;                  //--- Store signal at last index
   }

   //--- Remove signal at index
   IAdvisorStrategyExpression* Remove(int index) {
      IAdvisorStrategyExpression* removed = NULL;         //--- Initialize removed signal as null
      if (index >= 0 && index < _size) {                  //--- Check valid index
         removed = _tradeSignals[index];                  //--- Store signal to remove
         for (int i = index; i < (_size - 1); i++) {      //--- Shift signals left
            _tradeSignals[i] = _tradeSignals[i + 1];      //--- Move signal
         }
         ArrayResize(_tradeSignals, ArraySize(_tradeSignals) - 1, 8); //--- Reduce array size
         _size = _size - 1;                               //--- Decrement size
      }
      return removed;                                     //--- Return removed signal or null
   }

   //--- Retrieve signal at index
   IAdvisorStrategyExpression* Get(int index) {
      if (index >= 0 && index < _size) {                  //--- Check valid index
         return _tradeSignals[index];                     //--- Return signal
      }
      return NULL;                                        //--- Return null for invalid index
   }

   //--- Retrieve number of signals
   int Count() {
      return _size;                                       //--- Return current size
   }

   //--- Reset iterator to start
   void Rewind() {
      _pointer = -1;                                      //--- Set pointer to -1
   }

   //--- Move to next signal
   IAdvisorStrategyExpression* Next() {
      _pointer++;                                         //--- Increment pointer
      if (_pointer == _size) {                            //--- Check if at end
         Rewind();                                        //--- Reset pointer
         return NULL;                                     //--- Return null
      }
      return Current();                                   //--- Return current signal
   }

   //--- Move to previous signal
   IAdvisorStrategyExpression* Prev() {
      _pointer--;                                         //--- Decrement pointer
      if (_pointer == -1) {                               //--- Check if before start
         return NULL;                                     //--- Return null
      }
      return Current();                                   //--- Return current signal
   }

   //--- Check if more signals exist
   bool HasNext() {
      return (_pointer < (_size - 1));                    //--- Return true if pointer is before end
   }

   //--- Retrieve current signal
   IAdvisorStrategyExpression* Current() {
      return _tradeSignals[_pointer];                     //--- Return signal at pointer
   }

   //--- Retrieve current iterator index
   int Key() {
      return _pointer;                                    //--- Return current pointer
   }
};

//--- Define class for managing trading signals
class AdvisorStrategy {
private:
   TradeSignalCollection* _openBuySignals;                //--- Store open Buy signals
   TradeSignalCollection* _openSellSignals;               //--- Store open Sell signals
   TradeSignalCollection* _closeBuySignals;               //--- Store close Buy signals
   TradeSignalCollection* _closeSellSignals;              //--- Store close Sell signals

   //--- Evaluate signal at specified level
   bool EvaluateASLevel(TradeSignalCollection* signals, int level) {
      if (level > 0 && level <= signals.Count()) {        //--- Check valid level
         return signals.Get(level - 1).Evaluate();        //--- Evaluate signal
      }
      return false;                                       //--- Return false for invalid level
   }

public:
   //--- Initialize strategy
   void AdvisorStrategy() {
      _openBuySignals = new TradeSignalCollection();      //--- Create open Buy signals collection
      _openSellSignals = new TradeSignalCollection();     //--- Create open Sell signals collection
      _closeBuySignals = new TradeSignalCollection();     //--- Create close Buy signals collection
      _closeSellSignals = new TradeSignalCollection();    //--- Create close Sell signals collection
   }

   //--- Destructor to clean up signals
   void ~AdvisorStrategy() {
      delete(_openBuySignals);                            //--- Delete open Buy signals
      delete(_openSellSignals);                           //--- Delete open Sell signals
      delete(_closeBuySignals);                           //--- Delete close Buy signals
      delete(_closeSellSignals);                          //--- Delete close Sell signals
   }

   //--- Retrieve trading advice
   bool GetAdvice(TradeAction tradeAction, int level) {
      if (tradeAction == OpenBuyAction) {                  //--- Check open Buy action
         return EvaluateASLevel(_openBuySignals, level);   //--- Evaluate Buy signal
      } else if (tradeAction == OpenSellAction) {          //--- Check open Sell action
         return EvaluateASLevel(_openSellSignals, level);  //--- Evaluate Sell signal
      } else if (tradeAction == CloseBuyAction) {          //--- Check close Buy action
         return EvaluateASLevel(_closeBuySignals, level);  //--- Evaluate close Buy signal
      } else if (tradeAction == CloseSellAction) {         //--- Check close Sell action
         return EvaluateASLevel(_closeSellSignals, level); //--- Evaluate close Sell signal
      } else {
         Alert("Unsupported TradeAction in Advisor Strategy. TradeAction: " + DoubleToStr(tradeAction)); //--- Log unsupported action
      }
      return false;                                       //--- Return false for invalid action
   }

   //--- Register open Buy signal
   void RegisterOpenBuy(IAdvisorStrategyExpression* openBuySignal, int level) {
      if (level <= _openBuySignals.Count()) {             //--- Check if level already set
         Alert("Register Open Buy failed: level already set."); //--- Log failure
         return;                                          //--- Exit
      }
      _openBuySignals.Add(openBuySignal);                 //--- Add signal
   }

   //--- Register open Sell signal
   void RegisterOpenSell(IAdvisorStrategyExpression* openSellSignal, int level) {
      if (level <= _openSellSignals.Count()) {            //--- Check if level already set
         Alert("Register Open Sell failed: level already set."); //--- Log failure
         return;                                          //--- Exit
      }
      _openSellSignals.Add(openSellSignal);               //--- Add signal
   }

   //--- Register close Buy signal
   void RegisterCloseBuy(IAdvisorStrategyExpression* closeBuySignal, int level) {
      if (level <= _closeBuySignals.Count()) {            //--- Check if level already set
         Alert("Register Close Buy failed: level already set."); //--- Log failure
         return;                                          //--- Exit
      }
      _closeBuySignals.Add(closeBuySignal);               //--- Add signal
   }

   //--- Register close Sell signal
   void RegisterCloseSell(IAdvisorStrategyExpression* closeSellSignal, int level) {
      if (level <= _closeSellSignals.Count()) {           //--- Check if level already set
         Alert("Register Close Sell failed: level already set."); //--- Log failure
         return;                                          //--- Exit
      }
      _closeSellSignals.Add(closeSellSignal);             //--- Add signal
   }

   //--- Retrieve number of signals for action
   int GetNumberOfExpressions(TradeAction tradeAction) {
      if (tradeAction == OpenBuyAction) {                 //--- Check open Buy action
         return _openBuySignals.Count();                  //--- Return Buy signal count
      } else if (tradeAction == OpenSellAction) {         //--- Check open Sell action
         return _openSellSignals.Count();                 //--- Return Sell signal count
      } else if (tradeAction == CloseBuyAction) {         //--- Check close Buy action
         return _closeBuySignals.Count();                 //--- Return close Buy signal count
      } else if (tradeAction == CloseSellAction) {        //--- Check close Sell action
         return _closeSellSignals.Count();                //--- Return close Sell signal count
      }
      return 0;                                           //--- Return 0 for invalid action
   }

   //--- Set fire-only-when-reset for all signals
   void SetFireOnlyWhenReset(bool value) {
      _openBuySignals.Rewind();                           //--- Reset Buy signals iterator
      while (_openBuySignals.Next() != NULL) {            //--- Iterate Buy signals
         _openBuySignals.Current().SetFireOnlyWhenReset(value); //--- Set flag
      }
      _openSellSignals.Rewind();                          //--- Reset Sell signals iterator
      while (_openSellSignals.Next() != NULL) {           //--- Iterate Sell signals
         _openSellSignals.Current().SetFireOnlyWhenReset(value); //--- Set flag
      }
      _closeBuySignals.Rewind();                          //--- Reset close Buy signals iterator
      while (_closeBuySignals.Next() != NULL) {           //--- Iterate close Buy signals
         _closeBuySignals.Current().SetFireOnlyWhenReset(value); //--- Set flag
      }
      _closeSellSignals.Rewind();                         //--- Reset close Sell signals iterator
      while (_closeSellSignals.Next() != NULL) {          //--- Iterate close Sell signals
         _closeSellSignals.Current().SetFireOnlyWhenReset(value); //--- Set flag
      }
   }

   //--- Handle tick event for signals
   void HandleTick() {
      _openBuySignals.Rewind();                           //--- Reset Buy signals iterator
      while (_openBuySignals.Next() != NULL) {            //--- Iterate Buy signals
         _openBuySignals.Current().ResetSignalValue();    //--- Reset signal value
         if (_openBuySignals.Current().GetFireOnlyWhenReset()) { //--- Check fire-only-when-reset
            _openBuySignals.Current().Evaluate();         //--- Evaluate signal
         }
      }
      _openSellSignals.Rewind();                          //--- Reset Sell signals iterator
      while (_openSellSignals.Next() != NULL) {           //--- Iterate Sell signals
         _openSellSignals.Current().ResetSignalValue();   //--- Reset signal value
         if (_openSellSignals.Current().GetFireOnlyWhenReset()) { //--- Check fire-only-when-reset
            _openSellSignals.Current().Evaluate();        //--- Evaluate signal
         }
      }
      _closeBuySignals.Rewind();                          //--- Reset close Buy signals iterator
      while (_closeBuySignals.Next() != NULL) {           //--- Iterate close Buy signals
         _closeBuySignals.Current().ResetSignalValue();   //--- Reset signal value
         if (_closeBuySignals.Current().GetFireOnlyWhenReset()) { //--- Check fire-only-when-reset
            _closeBuySignals.Current().Evaluate();        //--- Evaluate signal
         }
      }
      _closeSellSignals.Rewind();                         //--- Reset close Sell signals iterator
      while (_closeSellSignals.Next() != NULL) {          //--- Iterate close Sell signals
         _closeSellSignals.Current().ResetSignalValue();  //--- Reset signal value
         if (_closeSellSignals.Current().GetFireOnlyWhenReset()) { //--- Check fire-only-when-reset
            _closeSellSignals.Current().Evaluate();       //--- Evaluate signal
         }
      }
   }
};

Hier implementieren wir weitere Klassen zur Verwaltung der Handelssignale. Da wir in den vorangegangenen Teilen bereits einige Erklärungen zu den Klassen gegeben haben, hoffen wir, dass Sie die Syntax der Klassen bereits kennen, sodass wir nur die wichtigsten Teile zeigen. Wir erstellen die Klasse „TradeSignalCollection“ und verwenden „_tradeSignals“ zum Speichern der Zeiger von „IAdvisorStrategyExpression“, „_pointer“ für die Iteration und „_size“ für die Signalanzahl, die im Konstruktor von „TradeSignalCollection“ initialisiert werden. Wir fügen Signale mit der Funktion „Add“ hinzu, entfernen sie mit „Remove“ und navigieren mit den Funktionen „Next“, „Prev“, „Current“, „Rewind“ und „HasNext“, wobei die Bereinigung durch den Destruktor erfolgt.

In der Klasse „AdvisorStrategy“ definieren wir „_openBuySignals“, „_openSellSignals“, „_closeBuySignals“ und „_closeSellSignals“ als „TradeSignalCollection“-Objekte, die im „AdvisorStrategy“-Konstruktor eingerichtet werden. Wir werten Signale mit den Funktionen „GetAdvice“ und „EvaluateASLevel“ aus, registrieren Signale über „RegisterOpenBuy“, „RegisterOpenSell“, „RegisterCloseBuy“ und „RegisterCloseSell“ und verwalten die Signalanzahl mit „GetNumberOfExpressions“. Die Funktion „SetFireOnlyWhenReset“ wendet Reset-Flags an, und „HandleTick“ setzt Signale zurück und wertet sie mit „ResetSignalValue“ und „Evaluate“ aus, um eine effiziente Signalverarbeitung für unsere Strategie zu gewährleisten. Wir können nun Klassen für Verkaufs- und Kaufsignale auf bestimmten Niveaus definieren. Beginnen wir mit der Verkaufsklasse.

//--- Define class for open Sell signal at level 1
class ASOpenSellLevel1 : public ASSignal {
protected:
   //--- Evaluate Sell signal
   bool EvaluateSignal() {
      Order* openOrder = _ea.GetWallet().GetMostRecentOpenOrder(); //--- Retrieve recent open order
      if (openOrder != NULL && openOrder.Type == ORDER_TYPE_BUY) { //--- Check if Buy order
         openOrder = NULL;                                //--- Clear if Buy to avoid conflict
      }
      if (((((openOrder != NULL ? TimeCurrent() - openOrder.OpenTime : EMPTY_VALUE) == EMPTY_VALUE) //--- Check no recent order
            && ((BidFunc.GetValue(0) < fn_iMA_SMA_4(Symbol(), 0)) //--- Check Bid below 4-period SMA
                && ((BidFunc.GetValue(0) > fn_iMA_SMA8(Symbol(), 0)) //--- Check Bid above 8-period SMA
                    && ((BidFunc.GetValue(0) < fn_iEnvelopes_ENV_UPPER(Symbol(), 0, 0)) //--- Check Bid below upper Envelope
                        && ((fn_iRSI_RSI(Symbol(), 1) < OpenSell_Const_0) //--- Check previous RSI below threshold
                            && (fn_iRSI_RSI(Symbol(), 0) >= OpenSell_Const_0) //--- Check current RSI above threshold
                           )
                       )
                   )
               )
           )
            || (((openOrder != NULL ? TimeCurrent() - openOrder.OpenTime : EMPTY_VALUE) != EMPTY_VALUE) //--- Check existing order
                && (BidFunc.GetValue(0) > ((openOrder != NULL ? openOrder.OpenPrice : EMPTY_VALUE) + (PipPoint * OpenSell_Const_1))) //--- Check Bid above open price plus offset
               )
          )) {
         return true;                                     //--- Return true for Sell signal
      }
      return false;                                       //--- Return false if no signal
   }

public:
   //--- Initialize Sell signal
   void ASOpenSellLevel1() {}                             //--- Empty constructor
};

Wir entwickeln eine Verkaufssignallogik unter Verwendung der Klasse „ASOpenSellLevel1“, die von der Klasse „ASSignal“ abgeleitet ist. In der geschützten Funktion „EvaluateSignal“ prüfen wir die letzte Order über die Funktion „GetMostRecentOpenOrder“ aus der „Wallet“ von „_ea“ und löschen sie, wenn es sich um eine Kauforder handelt. Wir lösen ein Verkaufssignal aus, wenn keine aktuelle Order vorhanden ist und der Geldkurs, aus der „GetValue“ Funktion von „BidFunc“, unter dem 4-Perioden-SMA („fn_iMA_SMA_4“), über dem 8-Perioden-SMA („fn_iMA_SMA8“), unter dem oberen Envelopes-Band („fn_iEnvelopes_ENV_UPPER“), wenn der vorherige RSI („fn_iRSI_RSI“) unter „OpenSell_Const_0“ und der aktuelle RSI auf oder darüber liegt, oder wenn ein Auftrag vorliegt und das Gebot den offenen Preis plus „PipPoint“ mal „OpenSell_Const_1“ überschreitet.

Bei einem gültigen Signal wird true zurückgegeben, sonst false. Die Konstruktorfunktion „ASOpenSellLevel1“ ist leer und nutzt die Initialisierung von „ASSignal“. Wir verwenden dieselbe Logik für ein Kaufsignal.

//--- Define class for open Buy signal at level 1
class ASOpenBuyLevel1 : public ASSignal {
protected:
   //--- Evaluate Buy signal
   bool EvaluateSignal() {
      Order* openOrder = _ea.GetWallet().GetMostRecentOpenOrder();  //--- Retrieve most recent open order
      if (openOrder != NULL && openOrder.Type == ORDER_TYPE_SELL) { //--- Check if Sell order
         openOrder = NULL;                                          //--- Clear if Sell to avoid conflict
      }
      if (((((openOrder != NULL ? TimeCurrent() - openOrder.OpenTime : EMPTY_VALUE) == EMPTY_VALUE) //--- Check no recent order
            && ((AskFunc.GetValue(0) > fn_iMA_SMA_4(Symbol(), 0))                                   //--- Check Ask above 4-period SMA
                && ((AskFunc.GetValue(0) < fn_iMA_SMA8(Symbol(), 0))                                //--- Check Ask below 8-period SMA
                    && ((AskFunc.GetValue(0) > fn_iEnvelopes_ENV_LOW(Symbol(), 1, 0))               //--- Check Ask above lower Envelope
                        && ((fn_iRSI_RSI(Symbol(), 1) > OpenBuy_Const_0)                            //--- Check previous RSI above threshold
                            && (fn_iRSI_RSI(Symbol(), 0) <= OpenBuy_Const_0)                        //--- Check current RSI below threshold
                           )
                       )
                   )
               )
           )
            || (((openOrder != NULL ? TimeCurrent() - openOrder.OpenTime : EMPTY_VALUE) != EMPTY_VALUE) //--- Check existing order
                && (AskFunc.GetValue(0) < ((openOrder != NULL ? openOrder.OpenPrice : EMPTY_VALUE) - (PipPoint * OpenBuy_Const_1))) //--- Check Ask below open price minus offset
               )
          )) {
         return true;                                     //--- Return true for Buy signal
      }
      return false;                                       //--- Return false if no signal
   }

public:
   //--- Initialize Buy signal
   void ASOpenBuyLevel1() {}                              //--- Empty constructor
};

Um die Kauflogik zu definieren, verwenden wir einfach die gleiche Logik wie bei der Verkaufslogik. Dann können wir einige Eingänge hinzufügen, um die Signalhandelslogik zu steuern.

//--- Define input group for trade and risk module settings
input string trademodule = "------TRADE/RISK MODULE------";//--- Label trade/risk module inputs
//--- Define input group for open Buy constants
input double LotSizePercentage = 1;                        //--- Set lot size as percentage of account balance (default: 1%)
input double OpenBuy_Const_0 = 11;                         //--- Set RSI threshold for Buy signal (default: 11)
input double OpenBuy_Const_1 = 10;                         //--- Set pip offset for additional Buy orders (default: 10 pips)
input double OpenSell_Const_0 = 89;                        //--- Set RSI threshold for Sell signal (default: 89)
input double OpenSell_Const_1 = 10;                        //--- Set pip offset for additional Sell orders (default: 10 pips)

Nachdem wir die Eingangsvariablen für die Signalerzeugung definiert haben, können wir mit der Definition der Logik für das Risikomanagement und das Handelsmanagement nach der Signalausführung fortfahren. Um dies zu erreichen, benötigen wir eine weitere Schnittstelle und Klasse.

//--- Define interface for money management
interface IMoneyManager {
   double GetLotSize();                                   //--- Retrieve lot size
   int GetNextLevel(Wallet* wallet);                      //--- Retrieve next trading level
};

//--- Define class for money management
class MoneyManager : public IMoneyManager {
public:
   //--- Initialize money manager
   void MoneyManager(Wallet* wallet) {
      _minLot = MarketInfo_LibFunc(Symbol(), MODE_MINLOT);   //--- Set minimum lot size
      _maxLot = MarketInfo_LibFunc(Symbol(), MODE_MAXLOT);   //--- Set maximum lot size
      _lotStep = MarketInfo_LibFunc(Symbol(), MODE_LOTSTEP); //--- Set lot step
   }

   //--- Retrieve calculated lot size
   double GetLotSize() {
      double lotSize = NormalizeLots(NormalizeDouble(AccountInfoDouble(ACCOUNT_BALANCE) * 0.0001 * LotSizePercentage / 100.0, 2)); //--- Calculate lot size
      return lotSize;                                     //--- Return normalized lot size
   }

   //--- Retrieve next trading level
   int GetNextLevel(Wallet* wallet) {
      return wallet.GetOpenOrders().Count() + 1;          //--- Return next level based on open orders
   }

private:
   double _minLot;                                        //--- Store minimum lot size
   double _maxLot;                                        //--- Store maximum lot size
   double _lotStep;                                       //--- Store lot step

   //--- Normalize lot size to broker specifications
   double NormalizeLots(double lots) {
      lots = MathRound(lots / _lotStep) * _lotStep;       //--- Round to lot step
      if (lots < _minLot) lots = _minLot;                 //--- Enforce minimum lot
      else if (lots > _maxLot) lots = _maxLot;            //--- Enforce maximum lot
      return lots;                                        //--- Return normalized lot size
   }
};

Um die Geldverwaltung für die Strategie einzurichten, definieren wir die Schnittstelle „IMoneyManager“ mit den Funktionen „GetLotSize“ und „GetNextLevel“, um die Handelsgröße und die Level-Progression zu standardisieren. In der Klasse „MoneyManager“, die „IMoneyManager“ implementiert, initialisieren wir die Variablen „_minLot“, „_maxLot“ und „_lotStep“ im „MoneyManager“-Konstruktor mit der Funktion „MarketInfo_LibFunc“. Unsere Funktion „GetLotSize“ berechnet die Losgröße aus dem Kontostand über AccountInfoDouble, angepasst durch „LotSizePercentage“, und normalisiert mit „NormalizeLots“, um den Regeln des Brokers zu entsprechen.

Die Funktion „GetNextLevel“ gibt die Anzahl der offenen Aufträge aus „GetOpenOrders“ plus eins zurück. Die Funktion „NormalizeLots“ gewährleistet gültige Losgrößen, indem sie auf „_lotStep“ rundet und „_minLot“ und „_maxLot“ beachtet. Dies wird dazu beitragen, ein übersichtliches System zur Verwaltung des Handelsvolumens und der Handelsstufen zu schaffen. Wir können nun zur Verwaltung der Handelsgeschäfte übergehen, wofür wir eine weitere Schnittstelle benötigen.

//--- Define enumeration for trading module demands
enum TradingModuleDemand {
   NoneDemand = 0,                                        //--- Represent no demand
   NoBuyDemand = 1,                                       //--- Prevent Buy orders
   NoSellDemand = 2,                                      //--- Prevent Sell orders
   NoOpenDemand = 4,                                      //--- Prevent all open orders
   OpenBuySellDemand = 8,                                 //--- Demand both Buy and Sell opens
   OpenBuyDemand = 16,                                    //--- Demand Buy open
   OpenSellDemand = 32,                                   //--- Demand Sell open
   CloseBuyDemand = 64,                                   //--- Demand Buy close
   CloseSellDemand = 128,                                 //--- Demand Sell close
   CloseBuySellDemand = 256                               //--- Demand both Buy and Sell closes
};

//--- Define interface for trading module signals
interface ITradingModuleSignal {
   string GetName();                                      //--- Retrieve signal name
   bool Evaluate(Order* openOrder = NULL);                //--- Evaluate signal
};

//--- Define interface for trading module values
interface ITradingModuleValue {
   string GetName();                                      //--- Retrieve value name
   double Evaluate(Order* openOrder = NULL);              //--- Evaluate value
};

//--- Define interface for trade strategy modules
interface ITradeStrategyModule {
   TradingModuleDemand Evaluate(Wallet* wallet, TradingModuleDemand demand, int level = 1); //--- Evaluate module
   void RegisterTradeSignal(ITradingModuleSignal* tradeSignal); //--- Register signal
};

//--- Define interface for open trade strategy modules
interface ITradeStrategyOpenModule : public ITradeStrategyModule {
   TradingModuleDemand EvaluateOpenSignals(Wallet* wallet, TradingModuleDemand demand, int requestedEvaluationLevel = 0); //--- Evaluate open signals
   TradingModuleDemand EvaluateCloseSignals(Wallet* wallet, TradingModuleDemand demand); //--- Evaluate close signals
};

//--- Define interface for close trade strategy modules
interface ITradeStrategyCloseModule : public ITradeStrategyModule {
   ORDER_GROUP_TYPE GetOrderGroupingType();                  //--- Retrieve grouping type
   void RegisterTradeValue(ITradingModuleValue* tradeValue); //--- Register value
};

Hier legen wir den Grundstein für die Verwaltung von Handelsentscheidungen, indem wir einen strukturierten Satz von Enumeration und Schnittstellen definieren. Wir beginnen mit der Erstellung der Enumerationen „TradingModuleDemand“, die wir verwenden, um Handelsaktionen und -beschränkungen zu kategorisieren, wie „NoneDemand“ für keine Aktion, „NoBuyDemand“ und „NoSellDemand“, um bestimmte Ordertypen zu blockieren, „OpenBuyDemand“ und „OpenSellDemand“, um Handelsgeschäfte zu initiieren, und „CloseBuyDemand“ und „CloseSellDemand“, um Positionen zu schließen, um nur einige zu nennen. Diese Enumeration bietet eine klare Möglichkeit, die Absicht der Strategie für die Handelsausführung zu signalisieren.

Anschließend definieren wir die Schnittstelle „ITradingModuleSignal“, die wir zur Standardisierung von Handelssignaloperationen entwickeln, einschließlich der Funktion „GetName“, um die Kennung eines Signals abzurufen, und der Funktion „Evaluate“, um zu beurteilen, ob ein Signal aktiv ist, wobei optional ein offener Auftrag berücksichtigt wird. In ähnlicher Weise führen wir die Schnittstelle „ITradingModuleValue“ mit den Funktionen „GetName“ und „Evaluate“ ein, um numerische Werte im Zusammenhang mit Handelsentscheidungen, wie z. B. Gewinnziele, zu verwalten. Für Strategiemodule erstellen wir die Schnittstelle „ITradeStrategyModule“, die die Funktion „Evaluate“ zur Verarbeitung von Handelsanforderungen auf der Grundlage eines „Wallet“-Objekts und die Funktion „RegisterTradeSignal“ zur Aufnahme von Signalen enthält.

Wir erweitern diese mit der Schnittstelle „ITradeStrategyOpenModule“, indem wir die Funktionen „EvaluateOpenSignals“ und „EvaluateCloseSignals“ hinzufügen, um Signale für die Eröffnung und Schließung von Handelsgeschäften zu behandeln, und die Schnittstelle „ITradeStrategyCloseModule“-Schnittstelle, die die Funktion „GetOrderGroupingType“ für die Auftragsgruppierung und die Funktion „RegisterTradeValue“ für die Wertregistrierung enthält. Zusammen bilden diese Komponenten einen flexiblen Rahmen für die Koordinierung von Handelsmaßnahmen im Rahmen unserer Strategie. Wir können nun auf der Grundlage dieser Module Verwaltungsklassen definieren.

//--- Define class for managing trade strategy
class TradeStrategy {
public:
   ITradeStrategyCloseModule* CloseModules[];             //--- Store close modules

private:
   ITradeStrategyModule* _preventOpenModules[];           //--- Store prevent-open modules
   ITradeStrategyOpenModule* _openModule;                 //--- Store open module

   //--- Evaluate prevent-open modules
   TradingModuleDemand EvaluatePreventOpenModules(Wallet* wallet, TradingModuleDemand preventOpenDemand, int evaluationLevel = 1) {
      TradingModuleDemand preventOpenDemands[];                           //--- Declare prevent-open demands array
      ArrayResize(preventOpenDemands, ArraySize(_preventOpenModules), 8); //--- Resize array
      for (int i = 0; i < ArraySize(_preventOpenModules); i++) {          //--- Iterate modules
         preventOpenDemands[i] = _preventOpenModules[i].Evaluate(wallet, NoneDemand, evaluationLevel); //--- Evaluate module
      }
      return PreventOpenModuleBase::GetCombinedPreventOpenDemand(preventOpenDemands); //--- Return combined demand
   }

   //--- Evaluate close modules
   TradingModuleDemand EvaluateCloseModules(Wallet* wallet, TradingModuleDemand closeDemand, int evaluationLevel = 1) {
      TradingModuleDemand closeDemands[];                    //--- Declare close demands array
      ArrayResize(closeDemands, ArraySize(CloseModules), 8); //--- Resize array
      for (int i = 0; i < ArraySize(CloseModules); i++) {    //--- Iterate modules
         closeDemands[i] = CloseModules[i].Evaluate(wallet, NoneDemand, evaluationLevel); //--- Evaluate module
      }
      return CloseModuleBase::GetCombinedCloseDemand(closeDemands); //--- Return combined demand
   }

   //--- Evaluate close conditions for TP/SL
   void EvaluateCloseConditions(Wallet* wallet, TradingModuleDemand signalDemand) {
      OrderCollection* openOrders = wallet.GetOpenOrders(); //--- Retrieve open orders
      if (openOrders.Count() == 0) {                        //--- Check if no open orders
         return;                                            //--- Exit
      }
      double bid = Bid_LibFunc();                           //--- Retrieve Bid price
      double ask = Ask_LibFunc();                           //--- Retrieve Ask price
      for (int i = openOrders.Count() - 1; i >= 0; i--) {   //--- Iterate open orders
         Order* order = openOrders.Get(i);                  //--- Get order
         bool closeSignal = (order.Type == OP_BUY && signalDemand == CloseBuyDemand) ||   //--- Check Buy close signal
                            (order.Type == OP_SELL && signalDemand == CloseSellDemand) || //--- Check Sell close signal
                            signalDemand == CloseBuySellDemand;                           //--- Check Buy/Sell close signal
         bool closeManualSLTP = AllowManualTPSLChanges && ((order.StopLossManual != 0 && order.Type == OP_BUY && bid <= order.StopLossManual) ||    //--- Check manual Buy SL
                                                          (order.StopLossManual != 0 && order.Type == OP_SELL && ask >= order.StopLossManual) ||    //--- Check manual Sell SL
                                                          (order.TakeProfitManual != 0 && order.Type == OP_BUY && bid >= order.TakeProfitManual) || //--- Check manual Buy TP
                                                          (order.TakeProfitManual != 0 && order.Type == OP_SELL && ask <= order.TakeProfitManual)); //--- Check manual Sell TP
         bool fullOrderClose = closeSignal || closeManualSLTP; //--- Determine full close
         OrderCloseInfo* activePartialCloseCloseInfo = NULL;   //--- Initialize partial close info
         if (!fullOrderClose) {                                //--- Check if not full close
            if (!AllowManualTPSLChanges || order.StopLossManual == 0) { //--- Check manual SL
               for (int cli = 0; cli < ArraySize(order.CloseInfosSL); cli++) { //--- Iterate SL info
                  if (order.CloseInfosSL[cli].IsOld) continue; //--- Skip old info
                  if (order.CloseInfosSL[cli].IsClosePriceSLHit(order.Type, ask, bid)) { //--- Check SL hit
                     if (activePartialCloseCloseInfo == NULL || order.CloseInfosSL[cli].Percentage > activePartialCloseCloseInfo.Percentage) { //--- Check higher percentage
                        activePartialCloseCloseInfo = order.CloseInfosSL[cli]; //--- Set active info
                     }
                  }
               }
            }
            if (!AllowManualTPSLChanges || order.TakeProfitManual == 0) {      //--- Check manual TP
               for (int cli = 0; cli < ArraySize(order.CloseInfosTP); cli++) { //--- Iterate TP info
                  if (order.CloseInfosTP[cli].IsOld) continue;                 //--- Skip old info
                  if (order.CloseInfosTP[cli].IsClosePriceTPHit(order.Type, ask, bid)) { //--- Check TP hit
                     if (activePartialCloseCloseInfo == NULL || order.CloseInfosTP[cli].Percentage > activePartialCloseCloseInfo.Percentage) { //--- Check higher percentage
                        activePartialCloseCloseInfo = order.CloseInfosTP[cli]; //--- Set active info
                     }
                  }
               }
            }
            fullOrderClose = activePartialCloseCloseInfo != NULL && activePartialCloseCloseInfo.Percentage == 100; //--- Check if full close
         }
         if (fullOrderClose) {                            //--- Handle full close
            TradingModuleDemand finalPreventOpenAdvice = EvaluatePreventOpenModules(wallet, NoneDemand, 0);      //--- Evaluate prevent-open
            TradingModuleDemand openDemand = _openModule.EvaluateOpenSignals(wallet, finalPreventOpenAdvice, 1); //--- Evaluate open signals
            int orderTypeOfOpeningOrder = wallet.GetOpenOrders().Get(0).Type;                                    //--- Get first order type
            if ((orderTypeOfOpeningOrder == ORDER_TYPE_BUY && openDemand == OpenBuyDemand) ||                    //--- Check Buy re-entry
                (orderTypeOfOpeningOrder == ORDER_TYPE_SELL && openDemand == OpenSellDemand) ||                  //--- Check Sell re-entry
                (openDemand == OpenBuySellDemand)) {      //--- Check Buy/Sell re-entry
               return;                                    //--- Block close to prevent re-entry
            }
            wallet.SetOpenOrderToPendingClose(order);      //--- Move order to pending close
         } else if (activePartialCloseCloseInfo != NULL) { //--- Handle partial close
            Order* partialCloseOrder = order.SplitOrder(activePartialCloseCloseInfo.Percentage); //--- Split order
            if (partialCloseOrder.Lots < 1e-13) {          //--- Check if last piece
               delete(partialCloseOrder);                  //--- Delete split order
               wallet.SetOpenOrderToPendingClose(order);   //--- Move to pending close
            } else {
               partialCloseOrder.ParentOrder = order;      //--- Link to parent order
               if (wallet.AddPendingCloseOrder(partialCloseOrder)) { //--- Add to pending close
                  activePartialCloseCloseInfo.IsOld = true; //--- Mark info as old
               }
            }
         }
      }
   }

public:
   //--- Initialize trade strategy
   void TradeStrategy(ITradeStrategyOpenModule* openModule) {
      _openModule = openModule;                           //--- Set open module
   }

   //--- Destructor to clean up strategy
   void ~TradeStrategy() {
      for (int i = ArraySize(_preventOpenModules) - 1; i >= 0; i--) { //--- Iterate prevent-open modules
         delete(_preventOpenModules[i]);                  //--- Delete module
      }
      delete(_openModule);                                //--- Delete open module
      for (int i = ArraySize(CloseModules) - 1; i >= 0; i--) { //--- Iterate close modules
         delete(CloseModules[i]);                         //--- Delete module
      }
   }

   //--- Evaluate trading strategy
   void Evaluate(Wallet* wallet) {
      int orderCount = wallet.GetOpenOrders().Count();    //--- Retrieve open order count
      TradingModuleDemand finalPreventOpenAdvice = EvaluatePreventOpenModules(wallet, NoneDemand, orderCount + 1); //--- Evaluate prevent-open
      if (orderCount > 0) {                               //--- Check if orders exist
         EvaluateCloseModules(wallet, NoneDemand);        //--- Evaluate close modules
         TradingModuleDemand signalDemand = _openModule.EvaluateCloseSignals(wallet, finalPreventOpenAdvice); //--- Evaluate close signals
         EvaluateCloseConditions(wallet, signalDemand);   //--- Evaluate close conditions
      }
      _openModule.Evaluate(wallet, finalPreventOpenAdvice, 0); //--- Evaluate open module
   }

   //--- Register prevent-open module
   void RegisterPreventOpenModule(ITradeStrategyModule* preventOpenModule) {
      int size = ArraySize(_preventOpenModules);          //--- Get current array size
      ArrayResize(_preventOpenModules, size + 1, 8);      //--- Resize array
      _preventOpenModules[size] = preventOpenModule;      //--- Add module
   }

   //--- Register close module
   void RegisterCloseModule(ITradeStrategyCloseModule* closeModule) {
      int size = ArraySize(CloseModules);                 //--- Get current array size
      ArrayResize(CloseModules, size + 1, 8);             //--- Resize array
      CloseModules[size] = closeModule;                   //--- Add module
   }
};

//--- Define base class for module calculations
class ModuleCalculationsBase {
public:
   //--- Calculate profit for order collection
   static double CalculateOrderCollectionProfit(OrderCollection &orders, ORDER_PROFIT_CALCULATION_TYPE calculationType) {
      double collectionProfit = 0;                        //--- Initialize profit
      for (int i = 0; i < orders.Count(); i++) {          //--- Iterate orders
         Order* order = orders.Get(i);                    //--- Get order
         collectionProfit += CalculateOrderProfit(order, calculationType); //--- Add order profit
      }
      return collectionProfit;                            //--- Return total profit
   }

   //--- Calculate profit for single order
   static double CalculateOrderProfit(Order* order, ORDER_PROFIT_CALCULATION_TYPE calculationType) {
      if (calculationType == Pips) {                      //--- Check pips calculation
         return order.CalculateProfitPips();              //--- Return profit in pips
      } else if (calculationType == Money) {              //--- Check money calculation
         return order.CalculateProfitCurrency();          //--- Return profit in currency
      } else if (calculationType == EquityPercentage) {   //--- Check equity percentage
         return order.CalculateProfitEquityPercentage();  //--- Return profit as percentage
      } else {
         Alert("Can't execute CalculateOrderCollectionProfit. Unknown calculationType: " + IntegerToString(calculationType)); //--- Log error
         return 0;                                        //--- Return 0 for invalid type
      }
   }
};

//--- Define base class for open trade modules
class OpenModuleBase : public ITradeStrategyOpenModule {
protected:
   AdvisorStrategy* _advisorStrategy;                     //--- Store advisor strategy
   IMoneyManager* _moneyManager;                          //--- Store money manager

   //--- Create new order
   Order* OpenOrder(ENUM_ORDER_TYPE orderType, bool mustBeVisibleOnChart) {
      Order* order = new Order(mustBeVisibleOnChart);     //--- Create new order
      order.SymbolCode = Symbol();                        //--- Set symbol
      order.Type = orderType;                             //--- Set order type
      order.MagicNumber = MagicNumber;                    //--- Set magic number
      order.Lots = _moneyManager.GetLotSize();            //--- Set lot size
      if (order.Type == ORDER_TYPE_BUY) {                 //--- Check Buy order
         order.OpenPrice = Ask_LibFunc();                 //--- Set open price to Ask
         order.StopLoss = -DBL_MAX;                       //--- Set initial SL to minimum
         order.TakeProfit = DBL_MAX;                      //--- Set initial TP to maximum
      } else if (order.Type == ORDER_TYPE_SELL) {         //--- Check Sell order
         order.OpenPrice = Bid_LibFunc();                 //--- Set open price to Bid
         order.StopLoss = DBL_MAX;                        //--- Set initial SL to maximum
         order.TakeProfit = -DBL_MAX;                     //--- Set initial TP to minimum
      }
      order.LowestProfitPips = DBL_MAX;                   //--- Set initial lowest profit
      order.HighestProfitPips = -DBL_MAX;                 //--- Set initial highest profit
      order.Comment = OrderComment;                       //--- Set order comment
      OrderRepository::CalculateAndSetCommision(order);   //--- Calculate and set commission
      return order;                                       //--- Return order
   }

public:
   //--- Initialize open module
   void OpenModuleBase(AdvisorStrategy* advisorStrategy, IMoneyManager* moneyManager) {
      _advisorStrategy = advisorStrategy;                 //--- Set advisor strategy
      _moneyManager = moneyManager;                       //--- Set money manager
   }

   //--- Retrieve trade actions
   void GetTradeActions(Wallet* wallet, TradingModuleDemand preventOpenDemand, TradeAction& result[]) {
      TradeAction tempresult[];                             //--- Declare temporary actions array
      if (wallet.GetOpenOrders().Count() > 0) {             //--- Check if open orders exist
         Order* firstOrder = wallet.GetOpenOrders().Get(0); //--- Get first open order
         if (firstOrder.Type == ORDER_TYPE_BUY) {           //--- Check if Buy order
            ArrayResize(tempresult, ArraySize(tempresult) + 1, 8); //--- Resize array
            tempresult[0] = OpenBuyAction;                  //--- Add open Buy action
            ArrayResize(tempresult, ArraySize(tempresult) + 1, 8); //--- Resize array
            tempresult[1] = CloseBuyAction;                 //--- Add close Buy action
         } else if (firstOrder.Type == ORDER_TYPE_SELL) {   //--- Check if Sell order
            ArrayResize(tempresult, ArraySize(tempresult) + 1, 8); //--- Resize array
            tempresult[0] = OpenSellAction;                 //--- Add open Sell action
            ArrayResize(tempresult, ArraySize(tempresult) + 1, 8); //--- Resize array
            tempresult[1] = CloseSellAction;                //--- Add close Sell action
         } else {
            Alert("Unsupported ordertype. Ordertype: " + DoubleToStr(firstOrder.Type)); //--- Log error
         }
      } else {
         ArrayResize(tempresult, ArraySize(tempresult) + 1, 8); //--- Resize array
         tempresult[0] = OpenBuyAction;                   //--- Add open Buy action
         ArrayResize(tempresult, ArraySize(tempresult) + 1, 8); //--- Resize array
         tempresult[1] = OpenSellAction;                  //--- Add open Sell action
      }
      for (int i = 0; i < ArraySize(tempresult); i++) {   //--- Iterate actions
         if ((preventOpenDemand == NoOpenDemand && (tempresult[i] == OpenBuyAction || tempresult[i] == OpenSellAction)) || //--- Check no open demand
             (preventOpenDemand == NoBuyDemand && tempresult[i] == OpenBuyAction) || //--- Check no Buy demand
             (preventOpenDemand == NoSellDemand && tempresult[i] == OpenSellAction)) { //--- Check no Sell demand
            continue;                                     //--- Skip action
         }
         ArrayResize(result, ArraySize(result) + 1, 8);   //--- Resize result array
         result[ArraySize(result) - 1] = tempresult[i];   //--- Add action
      }
   }

   //--- Register trade signal (empty implementation)
   virtual void RegisterTradeSignal(ITradingModuleSignal* tradeSignal) {} //--- Do nothing

   //--- Combine open demands
   static TradingModuleDemand GetCombinedOpenDemand(TradingModuleDemand &openDemands[]) {
      TradingModuleDemand result = NoneDemand;            //--- Initialize result
      for (int i = 0; i < ArraySize(openDemands); i++) {  //--- Iterate demands
         if (result == OpenBuySellDemand) {               //--- Check if Buy/Sell demand
            return OpenBuySellDemand;                     //--- Return Buy/Sell demand
         }
         if (openDemands[i] == OpenBuySellDemand) {       //--- Check if demand is Buy/Sell
            result = OpenBuySellDemand;                   //--- Set Buy/Sell demand
         } else if (result == NoneDemand && openDemands[i] == OpenBuyDemand) { //--- Check Buy demand
            result = OpenBuyDemand;                       //--- Set Buy demand
         } else if (result == NoneDemand && openDemands[i] == OpenSellDemand) { //--- Check Sell demand
            result = OpenSellDemand;                      //--- Set Sell demand
         } else if (result == OpenBuyDemand && openDemands[i] == OpenSellDemand) { //--- Check mixed demands
            result = OpenBuySellDemand;                   //--- Set Buy/Sell demand
         } else if (result == OpenSellDemand && openDemands[i] == OpenBuyDemand) { //--- Check mixed demands
            result = OpenBuySellDemand;                   //--- Set Buy/Sell demand
         }
      }
      return result;                                      //--- Return combined demand
   }

   //--- Combine close demands
   static TradingModuleDemand GetCombinedCloseDemand(TradingModuleDemand &closeDemands[]) {
      TradingModuleDemand result = NoneDemand;            //--- Initialize result
      for (int i = 0; i < ArraySize(closeDemands); i++) { //--- Iterate demands
         if (result == CloseBuySellDemand) {              //--- Check if Buy/Sell demand
            return CloseBuySellDemand;                    //--- Return Buy/Sell demand
         }
         if (closeDemands[i] == CloseBuySellDemand) {     //--- Check if demand is Buy/Sell
            result = CloseBuySellDemand;                  //--- Set Buy/Sell demand
         } else if (result == NoneDemand && closeDemands[i] == CloseBuyDemand) { //--- Check Buy demand
            result = CloseBuyDemand;                      //--- Set Buy demand
         } else if (result == NoneDemand && closeDemands[i] == CloseSellDemand) { //--- Check Sell demand
            result = CloseSellDemand;                     //--- Set Sell demand
         } else if (result == CloseBuyDemand && closeDemands[i] == CloseSellDemand) { //--- Check mixed demands
            result = CloseBuySellDemand;                  //--- Set Buy/Sell demand
         } else if (result == CloseSellDemand && closeDemands[i] == CloseBuyDemand) { //--- Check mixed demands
            result = CloseBuySellDemand;                  //--- Set Buy/Sell demand
         }
      }
      return result;                                      //--- Return combined demand
   }

   //--- Retrieve number of open orders
   int GetNumberOfOpenOrders(Wallet* wallet) {
      return wallet.GetOpenOrders().Count();              //--- Return open order count
   }
};

Wir bauen den Handelsstrategie-Rahmen mit der Klasse „TradeStrategy“ auf und speichern „ITradeStrategyCloseModule“-Objekte in „CloseModules“, „ITradeStrategyModule“-Objekte in „_preventOpenModules“ und ein „ITradeStrategyOpenModule“ in „_openModule“. Wir initialisieren „_openModule“ im Konstruktor von „TradeStrategy“ und bereinigen die Module im Destruktor. Unsere Funktion „Evaluate“ bewertet offene Aufträge über „GetOpenOrders“, wertet „_preventOpenModules“ mit „EvaluatePreventOpenModules“ und „GetCombinedPreventOpenDemand“, und verarbeitet „CloseModules“ mit „EvaluateCloseModules“ und „GetCombinedCloseDemand“.

Die Funktion „EvaluateCloseConditions“ prüft Ordertypen anhand von „CloseBuyDemand“ oder „CloseSellDemand“, verifiziert manuelle Stop-Loss/Take-Profit mit „Ask_LibFunc“ und „Bid_LibFunc“ und behandelt Schließungen mit „SetOpenOrderToPendingClose“ oder „SplitOrder“. Wir registrieren Module mit „RegisterPreventOpenModule“ und „RegisterCloseModule“. Die Klasse „ModuleCalculationsBase“ berechnet Gewinne über „CalculateOrderCollectionProfit“ und „CalculateOrderProfit“, während „OpenModuleBase“ Aufträge mit „OpenOrder“ anlegt und mit „GetTradeActions“ Aktionen bestimmt und mit „GetCombinedOpenDemand“ und „GetCombinedCloseDemand“ Forderungen für ein zusammenhängendes System zusammenführt.

Um sicherzustellen, dass wir nicht zu viel handeln, können wir eine externe Variable verwenden, um die maximale Anzahl von Handelsgeschäften pro Instanz zu steuern.

//--- Define input for maximum open orders
input int MaxNumberOfOpenOrders1 = 1;                     //--- Set maximum number of open orders (default: 1)

Nachdem wir die Variable zur Beschränkung der Anzahl der Handelsgeschäfte festgelegt haben, können wir nun eine Klasse definieren, die mehrere Module öffnet.

//--- Define class for multiple open module
class MultipleOpenModule_1 : public OpenModuleBase {
protected:
   TradingModuleDemand previousSignalDemand;              //--- Store previous signal demand

public:
   //--- Initialize multiple open module
   void MultipleOpenModule_1(AdvisorStrategy* advisorStrategy, MoneyManager* moneyManager)
      : OpenModuleBase(advisorStrategy, moneyManager) {
      _advisorStrategy.SetFireOnlyWhenReset(true);         //--- Configure signals to fire only when reset
   }

   //--- Evaluate and act on signals
   TradingModuleDemand Evaluate(Wallet* wallet, TradingModuleDemand preventOpenDemand, int level) {
      TradingModuleDemand newSignalsDemand = EvaluateSignals(wallet, preventOpenDemand, level); //--- Evaluate signals
      if (newSignalsDemand != NoneDemand) {                //--- Check if demand exists
         EvaluateOpenConditions(wallet, newSignalsDemand); //--- Evaluate open conditions
      }
      return newSignalsDemand;                             //--- Return new signal demand
   }

   //--- Evaluate open signals without acting
   TradingModuleDemand EvaluateOpenSignals(Wallet* wallet, TradingModuleDemand preventOpenDemand, int requestedEvaluationLevel) {
      TradingModuleDemand openDemands[];                   //--- Declare open demands array
      TradeAction tradeActionsToEvaluate[];                //--- Declare actions to evaluate
      GetTradeActions(wallet, preventOpenDemand, tradeActionsToEvaluate); //--- Retrieve actions
      AddPreviousDemandTradeActionIfMissing(tradeActionsToEvaluate); //--- Add previous demand actions
      int level;                                           //--- Declare level
      if (requestedEvaluationLevel == 0) {                 //--- Check if level unspecified
         level = _moneyManager.GetNextLevel(wallet);       //--- Set level based on orders
      } else {
         level = requestedEvaluationLevel;                 //--- Use specified level
      }
      for (int i = 0; i < ArraySize(tradeActionsToEvaluate); i++) { //--- Iterate actions
         if (tradeActionsToEvaluate[i] == CloseBuyAction || tradeActionsToEvaluate[i] == CloseSellAction) { //--- Skip close actions
            continue;                                      //--- Move to next
         }
         if (requestedEvaluationLevel == 0) {              //--- Check if level unspecified
            level = GetTopLevel(tradeActionsToEvaluate[i], level); //--- Cap level
            if (wallet.GetOpenOrders().Count() >= MaxNumberOfOpenOrders1) { //--- Check order limit
               level += 1;                                 //--- Increment level
            }
         }
         if (_advisorStrategy.GetAdvice(tradeActionsToEvaluate[i], level)) { //--- Check if action advised
            if (tradeActionsToEvaluate[i] == OpenBuyAction) { //--- Check Buy action
               int size = ArraySize(openDemands);             //--- Get current size
               int newSize = size + 1;                        //--- Calculate new size
               ArrayResize(openDemands, newSize, 8);          //--- Resize array
               openDemands[newSize - 1] = OpenBuyDemand;      //--- Add Buy demand
            } else if (tradeActionsToEvaluate[i] == OpenSellAction) { //--- Check Sell action
               int size = ArraySize(openDemands);             //--- Get current size
               int newSize = size + 1;                        //--- Calculate new size
               ArrayResize(openDemands, newSize, 8);          //--- Resize array
               openDemands[newSize - 1] = OpenSellDemand;     //--- Add Sell demand
            }
         }
      }
      TradingModuleDemand combinedOpenSignalDemand = OpenModuleBase::GetCombinedOpenDemand(openDemands); //--- Combine open demands
      TradingModuleDemand multiOrderOpenSignal = GetOpenDemandBasedOnPreviousOpenDemand(combinedOpenSignalDemand, level - 1); //--- Adjust for previous demand
      multiOrderOpenSignal = FilterPreventOpenDemand(multiOrderOpenSignal, preventOpenDemand); //--- Filter prevent-open demands
      return multiOrderOpenSignal;                        //--- Return final open signal
   }

   //--- Retrieve trade actions (custom for multiple orders)
   void GetTradeActions(Wallet* wallet, TradingModuleDemand preventOpenDemand, TradeAction& result[]) {
      if (wallet.GetOpenOrders().Count() > 0) {             //--- Check if open orders exist
         Order* firstOrder = wallet.GetOpenOrders().Get(0); //--- Get first open order
         if (firstOrder.Type == ORDER_TYPE_BUY) {           //--- Check if Buy order
            ArrayResize(result, ArraySize(result) + 1, 8);  //--- Resize array
            result[0] = OpenBuyAction;                      //--- Add open Buy action
            ArrayResize(result, ArraySize(result) + 1, 8);  //--- Resize array
            result[1] = CloseBuyAction;                     //--- Add close Buy action
         } else if (firstOrder.Type == ORDER_TYPE_SELL) {   //--- Check if Sell order
            ArrayResize(result, ArraySize(result) + 1, 8);  //--- Resize array
            result[0] = OpenSellAction;                     //--- Add open Sell action
            ArrayResize(result, ArraySize(result) + 1, 8);  //--- Resize array
            result[1] = CloseSellAction;                    //--- Add close Sell action
         } else {
            Alert("Unsupported ordertype");                 //--- Log error
         }
      } else {
         ArrayResize(result, ArraySize(result) + 1, 8);     //--- Resize array
         result[0] = OpenBuyAction;                         //--- Add open Buy action
         ArrayResize(result, ArraySize(result) + 1, 8);     //--- Resize array
         result[1] = OpenSellAction;                        //--- Add open Sell action
      }
   }

   //--- Evaluate close signals
   TradingModuleDemand EvaluateCloseSignals(Wallet* wallet, TradingModuleDemand preventOpenDemand) {
      TradingModuleDemand closeDemands[];                 //--- Declare close demands array
      TradeAction tradeActionsToEvaluate[];               //--- Declare actions to evaluate
      GetTradeActions(wallet, preventOpenDemand, tradeActionsToEvaluate); //--- Retrieve actions
      for (int i = 0; i < ArraySize(tradeActionsToEvaluate); i++) { //--- Iterate actions
         if (tradeActionsToEvaluate[i] != CloseBuyAction && tradeActionsToEvaluate[i] != CloseSellAction) { //--- Skip non-close actions
            continue;                                     //--- Move to next
         }
         if (_advisorStrategy.GetAdvice(tradeActionsToEvaluate[i], 1)) { //--- Check if action advised (level 1)
            if (tradeActionsToEvaluate[i] == CloseBuyAction) { //--- Check Buy close
               int size = ArraySize(closeDemands);          //--- Get current size
               int newSize = size + 1;                      //--- Calculate new size
               ArrayResize(closeDemands, newSize, 8);       //--- Resize array
               closeDemands[newSize - 1] = CloseBuyDemand;  //--- Add Buy close demand
            } else if (tradeActionsToEvaluate[i] == CloseSellAction) { //--- Check Sell close
               int size = ArraySize(closeDemands);          //--- Get current size
               int newSize = size + 1;                      //--- Calculate new size
               ArrayResize(closeDemands, newSize, 8);       //--- Resize array
               closeDemands[newSize - 1] = CloseSellDemand; //--- Add Sell close demand
            }
         }
      }
      TradingModuleDemand combinedCloseSignalDemand = OpenModuleBase::GetCombinedCloseDemand(closeDemands); //--- Combine close demands
      return combinedCloseSignalDemand;                   //--- Return combined demand
   }

private:
   //--- Evaluate open and close signals
   TradingModuleDemand EvaluateSignals(Wallet* wallet, TradingModuleDemand preventOpenDemand, int requestedEvaluationLevel) {
      TradingModuleDemand openDemands[];                  //--- Declare open demands array
      TradingModuleDemand closeDemands[];                 //--- Declare close demands array
      TradeAction tradeActionsToEvaluate[];               //--- Declare actions to evaluate
      GetTradeActions(wallet, preventOpenDemand, tradeActionsToEvaluate); //--- Retrieve actions
      AddPreviousDemandTradeActionIfMissing(tradeActionsToEvaluate); //--- Add previous demand actions
      int moneyManagementLevel;                           //--- Declare level
      if (requestedEvaluationLevel == 0) {                //--- Check if level unspecified
         moneyManagementLevel = _moneyManager.GetNextLevel(wallet); //--- Set level based on orders
      } else {
         moneyManagementLevel = requestedEvaluationLevel; //--- Use specified level
      }
      for (int i = 0; i < ArraySize(tradeActionsToEvaluate); i++) { //--- Iterate actions
         int tradeActionEvaluationLevel = GetTopLevel(tradeActionsToEvaluate[i], moneyManagementLevel); //--- Cap level
         if (wallet.GetOpenOrders().Count() >= MaxNumberOfOpenOrders1) { //--- Check order limit
            tradeActionEvaluationLevel += 1;              //--- Increment level
         }
         if (_advisorStrategy.GetAdvice(tradeActionsToEvaluate[i], tradeActionEvaluationLevel)) { //--- Check if action advised
            if (tradeActionsToEvaluate[i] == OpenBuyAction) { //--- Check Buy open
               int size = ArraySize(openDemands);         //--- Get current size
               int newSize = size + 1;                    //--- Calculate new size
               ArrayResize(openDemands, newSize, 8);      //--- Resize array
               openDemands[newSize - 1] = OpenBuyDemand;  //--- Add Buy demand
            } else if (tradeActionsToEvaluate[i] == OpenSellAction) { //--- Check Sell open
               int size = ArraySize(openDemands);         //--- Get current size
               int newSize = size + 1;                    //--- Calculate new size
               ArrayResize(openDemands, newSize, 8);      //--- Resize array
               openDemands[newSize - 1] = OpenSellDemand; //--- Add Sell demand
            } else if (tradeActionsToEvaluate[i] == CloseBuyAction) { //--- Check Buy close
               int size = ArraySize(closeDemands);        //--- Get current size
               int newSize = size + 1;                    //--- Calculate new size
               ArrayResize(closeDemands, newSize, 8);     //--- Resize array
               closeDemands[newSize - 1] = CloseBuyDemand; //--- Add Buy close demand
            } else if (tradeActionsToEvaluate[i] == CloseSellAction) { //--- Check Sell close
               int size = ArraySize(closeDemands);        //--- Get current size
               int newSize = size + 1;                    //--- Calculate new size
               ArrayResize(closeDemands, newSize, 8);     //--- Resize array
               closeDemands[newSize - 1] = CloseSellDemand; //--- Add Sell close demand
            }
         }
      }
      TradingModuleDemand combinedCloseSignalDemand = OpenModuleBase::GetCombinedCloseDemand(closeDemands); //--- Combine close demands
      if (combinedCloseSignalDemand != NoneDemand) {       //--- Check if close demand exists
         return combinedCloseSignalDemand;                //--- Return close demand
      }
      TradingModuleDemand combinedOpenSignalDemand = OpenModuleBase::GetCombinedOpenDemand(openDemands); //--- Combine open demands
      TradingModuleDemand multiOrderOpenSignal = GetOpenDemandBasedOnPreviousOpenDemand(combinedOpenSignalDemand, GetNumberOfOpenOrders(wallet)); //--- Adjust for previous demand
      previousSignalDemand = combinedOpenSignalDemand;    //--- Update previous demand
      multiOrderOpenSignal = FilterPreventOpenDemand(multiOrderOpenSignal, preventOpenDemand); //--- Filter prevent-open demands
      return multiOrderOpenSignal;                        //--- Return final signal
   }

   //--- Evaluate open conditions and add orders
   void EvaluateOpenConditions(Wallet* wallet, TradingModuleDemand signalDemand) {
      if (signalDemand == OpenBuySellDemand) {            //--- Check Buy/Sell demand
         return;                                          //--- Exit (hedging not supported)
      } else {
         double currentFreeMargin = AccountFreeMargin_LibFunc(); //--- Retrieve free margin
         double requiredMargin;                           //--- Declare required margin
         if (signalDemand == OpenBuyDemand) {             //--- Check Buy demand
            if (!MarginRequired(ORDER_TYPE_BUY, _moneyManager.GetLotSize(), requiredMargin)) { //--- Check margin
               return;                                    //--- Exit if margin check fails
            }
            if (currentFreeMargin < requiredMargin) {     //--- Check sufficient margin
               HandleErrors("Not enough free margin to open buy order with requested volume."); //--- Log error
               return;                                    //--- Exit
            }
            wallet.GetPendingOpenOrders().Add(OpenOrder(ORDER_TYPE_BUY, false)); //--- Add Buy order
         } else if (signalDemand == OpenSellDemand) {     //--- Check Sell demand
            if (!MarginRequired(ORDER_TYPE_SELL, _moneyManager.GetLotSize(), requiredMargin)) { //--- Check margin
               return;                                    //--- Exit if margin check fails
            }
            if (currentFreeMargin < requiredMargin) {     //--- Check sufficient margin
               HandleErrors("Not enough free margin to open sell order with requested volume."); //--- Log error
               return;                                    //--- Exit
            }
            wallet.GetPendingOpenOrders().Add(OpenOrder(ORDER_TYPE_SELL, false)); //--- Add Sell order
         }
      }
   }

   //--- Adjust open demand based on previous demand
   TradingModuleDemand GetOpenDemandBasedOnPreviousOpenDemand(TradingModuleDemand openDemand, int numberOfOpenOrders) {
      if (numberOfOpenOrders == 0 || previousSignalDemand == NoneDemand) { //--- Check no orders or no previous demand
         return openDemand;                               //--- Return current demand
      }
      if (previousSignalDemand == OpenBuyDemand && openDemand == OpenSellDemand) { //--- Check Buy to Sell switch
         return openDemand;                               //--- Allow Sell demand
      } else if (previousSignalDemand == OpenSellDemand && openDemand == OpenBuyDemand) { //--- Check Sell to Buy switch
         return openDemand;                               //--- Allow Buy demand
      }
      return NoneDemand;                                  //--- Block same-direction or mixed demands
   }

private:
   //--- Cap evaluation level
   int GetTopLevel(TradeAction tradeAction, int level) {
      int numberOfExpressions = _advisorStrategy.GetNumberOfExpressions(tradeAction); //--- Retrieve expression count
      if (level > numberOfExpressions) {                  //--- Check if level exceeds expressions
         level = numberOfExpressions;                     //--- Cap level
      }
      return level;                                       //--- Return capped level
   }

   //--- Add previous demand action if missing
   void AddPreviousDemandTradeActionIfMissing(TradeAction& result[]) {
      if (previousSignalDemand == NoneDemand) {           //--- Check if no previous demand
         return;                                          //--- Exit
      }
      bool foundPreviousDemand = false;                   //--- Initialize found flag
      if (previousSignalDemand == OpenBuyDemand) {        //--- Check Buy demand
         AddPreviousDemandTradeAction(result, OpenBuyDemand, OpenBuyAction); //--- Add Buy action
      } else if (previousSignalDemand == OpenSellDemand) { //--- Check Sell demand
         AddPreviousDemandTradeAction(result, OpenSellDemand, OpenSellAction); //--- Add Sell action
      } else if (previousSignalDemand == OpenBuySellDemand) { //--- Check Buy/Sell demand
         AddPreviousDemandTradeAction(result, OpenBuyDemand, OpenBuyAction); //--- Add Buy action
         AddPreviousDemandTradeAction(result, OpenSellDemand, OpenSellAction); //--- Add Sell action
      }
   }

   //--- Add specific previous demand action
   void AddPreviousDemandTradeAction(TradeAction& result[], TradingModuleDemand demand, TradeAction action) {
      bool foundPreviousDemand = false;                   //--- Initialize found flag
      if (previousSignalDemand == demand) {               //--- Check matching demand
         for (int i = 0; i < ArraySize(result); i++) {    //--- Iterate actions
            if (action == result[i]) {                    //--- Check if action exists
               foundPreviousDemand = true;                //--- Set found flag
            }
         }
         if (!foundPreviousDemand) {                      //--- Check if action missing
            ArrayResize(result, ArraySize(result) + 1, 8); //--- Resize array
            result[ArraySize(result) - 1] = action;       //--- Add action
         }
      }
   }

   //--- Filter prevent-open demands
   TradingModuleDemand FilterPreventOpenDemand(TradingModuleDemand multiOrderOpendDemand, TradingModuleDemand preventOpenDemand) {
      if (multiOrderOpendDemand == NoneDemand) {          //--- Check no demand
         return multiOrderOpendDemand;                    //--- Return no demand
      } else if (multiOrderOpendDemand == OpenBuyDemand && (preventOpenDemand == NoBuyDemand || preventOpenDemand == NoOpenDemand)) { //--- Check blocked Buy
         return NoneDemand;                               //--- Block Buy demand
      } else if (multiOrderOpendDemand == OpenSellDemand && (preventOpenDemand == NoSellDemand || preventOpenDemand == NoOpenDemand)) { //--- Check blocked Sell
         return NoneDemand;                               //--- Block Sell demand
      } else if (multiOrderOpendDemand == OpenBuySellDemand) { //--- Check Buy/Sell demand
         if (preventOpenDemand == NoBuyDemand) {          //--- Check no Buy
            return OpenSellDemand;                        //--- Allow Sell demand
         } else if (preventOpenDemand == NoSellDemand) {  //--- Check no Sell
            return OpenBuyDemand;                         //--- Allow Buy demand
         } else if (preventOpenDemand == NoOpenDemand) {  //--- Check no open
            return NoneDemand;                            //--- Block all demands
         }
      }
      return multiOrderOpendDemand;                       //--- Return unfiltered demand
   }
};

Hier entwickeln wir ein Modul für die Verwaltung mehrerer Eröffnungen von Handelsgeschäften mit Hilfe der Klasse „MultipleOpenModule_1“, die von „OpenModuleBase“ abgeleitet ist. Wir initialisieren es mit dem Konstruktor von „MultipleOpenModule_1“, stellen „_advisorStrategy“ so ein, dass Signale nur nach dem Zurücksetzen über „SetFireOnlyWhenReset“ ausgelöst werden, und speichern vorherige Anforderungen in „previousSignalDemand“. Unsere Funktion „Evaluate“ bewertet Signale mit „EvaluateSignals“ und handelt auf gültige Forderungen mit „EvaluateOpenConditions“ und gibt die Forderung zurück.

In „EvaluateOpenSignals“ rufen wir Aktionen mit „GetTradeActions“ ab, fügen frühere Forderungen über „AddPreviousDemandTradeActionIfMissing“ hinzu und setzen das Niveau mit „_moneyManager“'s „GetNextLevel“ oder einem angegebenen Wert. Wir iterieren die Aktionen, überspringen „CloseBuyAction“ und „CloseSellAction“, begrenzen die Levels mit „GetTopLevel“ und prüfen die Orderlimits mit „GetOpenOrders“ und „MaxNumberOfOpenOrders1“. Gültige Signale von „_advisorStrategy“'s „GetAdvice“ fügen „OpenBuyDemand“ oder „OpenSellDemand“ zu einem Array hinzu, kombiniert mit „GetCombinedOpenDemand“, angepasst durch „GetOpenDemandBasedOnPreviousOpenDemand“ und gefiltert durch „FilterPreventOpenDemand“.

Die Funktion „EvaluateCloseSignals“ wertet auf ähnliche Weise „CloseBuyAction“ und „CloseSellAction“ aus, fügt „CloseBuyDemand“ oder „CloseSellDemand“ hinzu und kombiniert sie mit „GetCombinedCloseDemand“.

In „EvaluateOpenConditions“ überprüfen wir die Margin mit „AccountFreeMargin_LibFunc“ und „MarginRequired“, fügen Kauf- oder Verkaufsaufträge über „OpenOrder“ hinzu, wenn sie ausreichend sind, oder protokollieren Fehler mit „HandleErrors“. Wir verwenden eine ähnliche Logik, um die offenen Aufträge zu schließen, Take-Profit und Stop-Loss zu setzen und die entsprechenden Eingaben zu machen. Nach der Ausführung haben wir nun die folgenden Eingaben.

KONTROLL-EINGÄNGE

Wir können nun eine endgültige Klasse erstellen, die alle bereits implementierten Ausführungs- und Verwaltungsaufgaben übernimmt. Um dies zu erreichen, verwenden wir die folgende Logik.

//--- Define interface for trader
interface ITrader {
   void HandleTick();                                     //--- Handle tick event
   void Init();                                           //--- Initialize trader
   Wallet* GetWallet();                                   //--- Retrieve wallet
};

//--- Declare global trader pointer
ITrader *_ea;                                             //--- Store EA instance

//--- Define main Expert Advisor class
class EA : public ITrader {
private:
   bool _firstTick;                                       //--- Track first tick
   TradeStrategy* _tradeStrategy;                         //--- Store trade strategy
   AdvisorStrategy* _advisorStrategy;                     //--- Store advisor strategy
   IMoneyManager* _moneyManager;                          //--- Store money manager
   Wallet* _wallet;                                       //--- Store wallet

public:
   //--- Initialize EA
   void EA() {
      _firstTick = true;                                  //--- Set first tick flag
      _wallet = new Wallet();                             //--- Create wallet
      _wallet.SetLastClosedOrdersByTimeframe(DisplayOrderDuringTimeframe); //--- Set closed orders timeframe
      _advisorStrategy = new AdvisorStrategy();           //--- Create advisor strategy
      _advisorStrategy.RegisterOpenBuy(new ASOpenBuyLevel1(), 1); //--- Register Buy signal
      _advisorStrategy.RegisterOpenSell(new ASOpenSellLevel1(), 1); //--- Register Sell signal
      _moneyManager = new MoneyManager(_wallet);          //--- Create money manager
      _tradeStrategy = new TradeStrategy(new MultipleOpenModule_1(_advisorStrategy, _moneyManager)); //--- Create trade strategy
      _tradeStrategy.RegisterCloseModule(new TakeProfitCloseModule_1()); //--- Register TP module
      _tradeStrategy.RegisterCloseModule(new StopLossCloseModule_1()); //--- Register SL module
   }

   //--- Destructor to clean up EA
   void ~EA() {
      delete(_tradeStrategy);                             //--- Delete trade strategy
      delete(_moneyManager);                              //--- Delete money manager
      delete(_advisorStrategy);                           //--- Delete advisor strategy
      delete(_wallet);                                    //--- Delete wallet
   }

   //--- Initialize EA components
   void Init() {
      IsDemoLiveOrVisualMode = !MQLInfoInteger(MQL_TESTER) || MQLInfoInteger(MQL_VISUAL_MODE); //--- Set mode flag
      UnitsOneLot = MarketInfo_LibFunc(Symbol(), MODE_LOTSIZE); //--- Set lot size
      SetOrderGrouping();                                 //--- Configure order grouping
      _wallet.LoadOrdersFromBroker();                     //--- Load orders from broker
   }

   //--- Handle tick event
   void HandleTick() {
      if (MQLInfoInteger(MQL_TESTER) == 0) {              //--- Check if not in tester
         SyncOrders();                                    //--- Synchronize orders
      }
      if (AllowManualTPSLChanges) {                       //--- Check if manual TP/SL allowed
         SyncManualTPSLChanges();                         //--- Synchronize manual TP/SL
      }
      AskFunc.Evaluate();                                 //--- Update Ask price
      BidFunc.Evaluate();                                 //--- Update Bid price
      UpdateOrders();                                     //--- Update order profits
      if (!StopEA) {                                      //--- Check if EA not stopped
         _wallet.HandleTick();                            //--- Handle wallet tick
         _advisorStrategy.HandleTick();                   //--- Handle strategy tick
         if (_wallet.GetPendingOpenOrders().Count() == 0 && _wallet.GetPendingCloseOrders().Count() == 0) { //--- Check no pending orders
            _tradeStrategy.Evaluate(_wallet);             //--- Evaluate strategy
         }
         if (ExecutePendingCloseOrders()) {               //--- Execute close orders
            if (!ExecutePendingOpenOrders()) {            //--- Execute open orders
               HandleErrors(StringFormat("Open (all) order(s) failed. Please check EA %d and look at the Journal and Expert tab.", MagicNumber)); //--- Log error
            }
         } else {
            HandleErrors(StringFormat("Close (all) order(s) failed! Please check EA %d and look at the Journal and Expert tab.", MagicNumber)); //--- Log error
         }
      } else {
         if (ExecutePendingCloseOrders()) {               //--- Execute close orders
            _wallet.SetAllOpenOrdersToPendingClose();     //--- Move open orders to pending close
         } else {
            HandleErrors(StringFormat("Close (all) order(s) failed! Please check EA %d and look at the Journal and Expert tab.", MagicNumber)); //--- Log error
         }
      }
      if (_firstTick) {                                   //--- Check if first tick
         _firstTick = false;                              //--- Clear first tick flag
      }
   }

   //--- Retrieve wallet
   Wallet* GetWallet() {
      return _wallet;                                     //--- Return wallet
   }

private:
   //--- Configure order grouping
   void SetOrderGrouping() {
      int size = ArraySize(_tradeStrategy.CloseModules);  //--- Get close modules size
      ORDER_GROUP_TYPE groups[];                          //--- Declare groups array
      ArrayResize(groups, size);                          //--- Resize array
      for (int i = 0; i < ArraySize(_tradeStrategy.CloseModules); i++) { //--- Iterate modules
         groups[i] = _tradeStrategy.CloseModules[i].GetOrderGroupingType(); //--- Set grouping type
      }
      _wallet.ActivateOrderGroups(groups);                //--- Activate groups
   }

   //--- Synchronize orders with broker
   void SyncOrders() {
      OrderCollection* currentOpenOrders = OrderRepository::GetOpenOrders(MagicNumber, NULL, Symbol()); //--- Retrieve open orders
      if (currentOpenOrders.Count() != (_wallet.GetOpenOrders().Count() + _wallet.GetPendingCloseOrders().Count())) { //--- Check order mismatch
         Print("(Manual) orderchanges detected" + " (found in MT: " + IntegerToString(currentOpenOrders.Count()) + " and in wallet: " + IntegerToString(_wallet.GetOpenOrders().Count()) + "), resetting EA, loading open orders."); //--- Log mismatch
         _wallet.ResetOpenOrders();                       //--- Reset open orders
         _wallet.ResetPendingOrders();                    //--- Reset pending orders
         _wallet.LoadOrdersFromBroker();                  //--- Reload orders
      }
      delete(currentOpenOrders);                          //--- Delete orders collection
   }

   //--- Synchronize manual TP/SL changes
   void SyncManualTPSLChanges() {
      _wallet.GetOpenOrders().Rewind();                   //--- Reset orders iterator
      while (_wallet.GetOpenOrders().HasNext()) {         //--- Iterate orders
         Order* order = _wallet.GetOpenOrders().Next();   //--- Get order
         uint lineFindResult = ObjectFind(ChartID(), IntegerToString(order.Ticket) + "_SL"); //--- Find SL line
         if (lineFindResult != UINT_MAX) {                //--- Check if SL line exists
            double currentPosition = ObjectGetDouble(ChartID(), IntegerToString(order.Ticket) + "_SL", OBJPROP_PRICE); //--- Get SL position
            if ((order.StopLossManual == 0 && currentPosition != order.GetClosestSL()) || //--- Check manual SL change
                (order.StopLossManual != 0 && currentPosition != order.StopLossManual)) { //--- Check manual SL mismatch
               order.StopLossManual = currentPosition;       //--- Update manual SL
            }
         }
         lineFindResult = ObjectFind(ChartID(), IntegerToString(order.Ticket) + "_TP"); //--- Find TP line
         if (lineFindResult != UINT_MAX) {                //--- Check if TP line exists
            double currentPosition = ObjectGetDouble(ChartID(), IntegerToString(order.Ticket) + "_TP", OBJPROP_PRICE); //--- Get TP position
            if ((order.TakeProfitManual == 0 && currentPosition != order.GetClosestTP()) || //--- Check manual TP change
                (order.TakeProfitManual != 0 && currentPosition != order.TakeProfitManual)) { //--- Check manual TP mismatch
               order.TakeProfitManual = currentPosition;     //--- Update manual TP
            }
         }
      }
   }

   //--- Update order profits
   void UpdateOrders() {
      _wallet.GetOpenOrders().Rewind();                   //--- Reset orders iterator
      while (_wallet.GetOpenOrders().HasNext()) {         //--- Iterate orders
         Order* order = _wallet.GetOpenOrders().Next();   //--- Get order
         double pipsProfit = order.CalculateProfitPips(); //--- Calculate profit
         order.CurrentProfitPips = pipsProfit;            //--- Update current profit
         if (pipsProfit < order.LowestProfitPips) {       //--- Check if lowest profit
            order.LowestProfitPips = pipsProfit;          //--- Update lowest profit
         } else if (pipsProfit > order.HighestProfitPips) { //--- Check if highest profit
            order.HighestProfitPips = pipsProfit;         //--- Update highest profit
         }
      }
   }

   //--- Execute pending close orders
   bool ExecutePendingCloseOrders() {
      OrderCollection* pendingCloseOrders = _wallet.GetPendingCloseOrders(); //--- Retrieve pending close orders
      int ordersToCloseCount = pendingCloseOrders.Count(); //--- Get count
      if (ordersToCloseCount == 0) {                      //--- Check if no orders
         return true;                                     //--- Return true
      }
      if (_wallet.AreOrdersBeingOpened()) {               //--- Check if orders being opened
         return true;                                     //--- Return true
      }
      int ordersCloseSuccessCount = 0;                    //--- Initialize success count
      for (int i = ordersToCloseCount - 1; i >= 0; i--) { //--- Iterate orders
         Order* pendingCloseOrder = pendingCloseOrders.Get(i); //--- Get order
         if (pendingCloseOrder.IsAwaitingDealExecution) { //--- Check if awaiting execution
            ordersCloseSuccessCount++;                    //--- Increment success count
            continue;                                     //--- Move to next
         }
         bool success;                                    //--- Declare success flag
         if (AccountMarginMode == ACCOUNT_MARGIN_MODE_RETAIL_NETTING) { //--- Check netting mode
            Order* reversedOrder = new Order(pendingCloseOrder, false); //--- Create reversed order
            reversedOrder.Type = pendingCloseOrder.Type == ORDER_TYPE_BUY ? ORDER_TYPE_SELL : ORDER_TYPE_BUY; //--- Set opposite type
            success = OrderRepository::OpenOrder(reversedOrder); //--- Open reversed order
            if (success) {                                //--- Check if successful
               pendingCloseOrder.Ticket = reversedOrder.Ticket; //--- Update ticket
            }
            delete(reversedOrder);                        //--- Delete reversed order
         } else {
            success = OrderRepository::ClosePosition(pendingCloseOrder); //--- Close position
         }
         if (success) {                                   //--- Check if successful
            ordersCloseSuccessCount++;                    //--- Increment success count
         }
      }
      return ordersCloseSuccessCount == ordersToCloseCount; //--- Return true if all successful
   }

   //--- Execute pending open orders
   bool ExecutePendingOpenOrders() {
      OrderCollection* pendingOpenOrders = _wallet.GetPendingOpenOrders(); //--- Retrieve pending open orders
      int ordersToOpenCount = pendingOpenOrders.Count();  //--- Get count
      if (ordersToOpenCount == 0) {                       //--- Check if no orders
         return true;                                     //--- Return true
      }
      int ordersOpenSuccessCount = 0;                     //--- Initialize success count
      for (int i = ordersToOpenCount - 1; i >= 0; i--) {  //--- Iterate orders
         Order* order = pendingOpenOrders.Get(i);         //--- Get order
         if (order.IsAwaitingDealExecution) {             //--- Check if awaiting execution
            ordersOpenSuccessCount++;                     //--- Increment success count
            continue;                                     //--- Move to next
         }
         bool isTradeContextFree = false;                 //--- Initialize trade context flag
         double StartWaitingTime = GetTickCount();        //--- Start timer
         while (true) {                                   //--- Wait for trade context
            if (MQL5InfoInteger(MQL5_TRADE_ALLOWED)) {    //--- Check if trade allowed
               isTradeContextFree = true;                 //--- Set trade context free
               break;                                     //--- Exit loop
            }
            int MaxWaiting_sec = 10;                      //--- Set max wait time
            if (IsStopped()) {                            //--- Check if EA stopped
               HandleErrors("The expert was stopped by a user action."); //--- Log error
               break;                                     //--- Exit loop
            }
            if (GetTickCount() - StartWaitingTime > MaxWaiting_sec * 1000) { //--- Check if timeout
               HandleErrors(StringFormat("The (%d seconds) waiting time exceeded. Trade not allowed: EA disabled, market closed or trade context still not free.", MaxWaiting_sec)); //--- Log error
               break;                                     //--- Exit loop
            }
            Sleep(100);                                   //--- Wait briefly
         }
         if (!isTradeContextFree) {                       //--- Check if trade context not free
            if (!_wallet.CancelPendingOpenOrder(order)) { //--- Attempt to cancel order
               HandleErrors("Failed to cancel an order (because it couldn't open). Please see the Journal and Expert tab in Metatrader for more information."); //--- Log error
            }
            continue;                                     //--- Move to next
         }
         bool success = OrderRepository::OpenOrder(order); //--- Open order
         if (success) {                                    //--- Check if successful
            ordersOpenSuccessCount++;                      //--- Increment success count
         } else {
            if (!_wallet.CancelPendingOpenOrder(order)) {  //--- Attempt to cancel order
               HandleErrors("Failed to cancel an order (because it couldn't open). Please see the Journal and Expert tab in Metatrader for more information."); //--- Log error
            }
         }
      }
      return ordersOpenSuccessCount == ordersToOpenCount;  //--- Return true if all successful
   }
};

Schließlich wird die Kernlogik über die Klasse „EA“ gesteuert, die die Schnittstelle „ITrader“ mit den Funktionen „HandleTick“, „Init“ und „GetWallet“ implementiert. Diese Funktionen sollten Ihnen nun bekannt sein, denn wir haben sie bereits ausgeführt. Wir definieren private Variablen „_firstTick“, „_tradeStrategy“, „_advisorStrategy“, „_moneyManager“ und „_wallet“ zur Verwaltung von Status und Komponenten. Im Konstruktor von „EA“ setzen wir „_firstTick“ auf true, initialisieren „_wallet“ mit einem neuen „Wallet“-Objekt, konfigurieren seinen Zeitrahmen über „SetLastClosedOrdersByTimeframe“ und erstellen „_advisorStrategy“ mit einem neuen „AdvisorStrategy“-Objekt und registrieren die Signale „ASOpenBuyLevel1“ und „ASOpenSellLevel1“ mit „RegisterOpenBuy“ und „RegisterOpenSell“.

Wir instanziieren auch „_moneyManager“ mit einem „MoneyManager“-Objekt und „_tradeStrategy“ mit einem „TradeStrategy“-Objekt, das ein „MultipleOpenModule_1“ und registrieren „TakeProfitCloseModule_1“ und „StopLossCloseModule_1“ über „RegisterCloseModule“. Der Destruktor räumt alle Komponenten auf.

Unsere Funktion „Init“ setzt Modus-Flags mit MQLInfoInteger, konfiguriert die Losgröße über „MarketInfo_LibFunc“, ruft „SetOrderGrouping“ auf, um Auftragsgruppen mit „GetOrderGroupingType“ zu aktivieren, und lädt Aufträge mit „LoadOrdersFromBroker“. Die Funktion „HandleTick“ verwaltet Ticks, synchronisiert Aufträge mit „SyncOrders“, wenn sie sich nicht im Testermodus befinden, aktualisiert manuelle TP/SL mit „SyncManualTPSLChanges“, wenn dies erlaubt ist, und aktualisiert die Preise über die „Evaluate“-Funktionen von „AskFunc“ und „BidFunc“.

Es aktualisiert Gewinne mit „UpdateOrders“, verarbeitet Wallet- und Strategie-Ticks mit „HandleTick“, bewertet „_tradeStrategy“ mit „Evaluate“ wenn keine ausstehenden Aufträge vorliegen, und führt ausstehende Schließungen und Öffnungen mit „ExecutePendingCloseOrders“ und „ExecutePendingOpenOrders“ aus, wobei Fehler mit „HandleErrors“ protokolliert werden, wenn Fehler auftreten. Wenn der EA gestoppt wird, schließt er die Aufträge mit „SetAllOpenOrdersToPendingClose“. Wir können dies nun in OnTick aufrufen, um die Trades zu verwalten.

//--- Handle tick event
datetime LastActionTime = 0;                          //--- Track last action time
void OnTick() {
   string AccountServer = AccountInfoString(ACCOUNT_SERVER);                //--- Retrieve account server
   string AccountCurrency = AccountInfoString(ACCOUNT_CURRENCY);            //--- Retrieve account currency
   string AccountName = AccountInfoString(ACCOUNT_NAME);                    //--- Retrieve account name
   long AccountTradeMode = AccountInfoInteger(ACCOUNT_TRADE_MODE);          //--- Retrieve trade mode
   string ReadableAccountTrademode = "";                                    //--- Initialize readable trade mode
   if (AccountTradeMode == 0) ReadableAccountTrademode = "DEMO ACCOUNT";    //--- Set demo mode
   if (AccountTradeMode == 1) ReadableAccountTrademode = "CONTEST ACCOUNT"; //--- Set contest mode
   if (AccountTradeMode == 2) ReadableAccountTrademode = "REAL ACCOUNT";    //--- Set real mode
   long AccountLogin = AccountInfoInteger(ACCOUNT_LOGIN);                   //--- Retrieve account login
   string AccountCompany = AccountInfoString(ACCOUNT_COMPANY);              //--- Retrieve account company
   long AccountLeverage = AccountInfoInteger(ACCOUNT_LEVERAGE);             //--- Retrieve account leverage
   long AccountLimitOrders = AccountInfoInteger(ACCOUNT_LIMIT_ORDERS);      //--- Retrieve order limit
   double AccountMarginFree = AccountInfoDouble(ACCOUNT_MARGIN_FREE);       //--- Retrieve free margin
   bool AccountTradeAllowed = AccountInfoInteger(ACCOUNT_TRADE_ALLOWED);    //--- Retrieve trade allowed
   bool AccountTradeExpert = AccountInfoInteger(ACCOUNT_TRADE_EXPERT);      //--- Retrieve expert allowed
   string ReadableAccountMarginMode = "";                                   //--- Initialize readable margin mode
   if (AccountMarginMode == 0) ReadableAccountMarginMode = "NETTING MODE";  //--- Set netting mode
   if (AccountMarginMode == 1) ReadableAccountMarginMode = "EXCHANGE MODE"; //--- Set exchange mode
   if (AccountMarginMode == 2) ReadableAccountMarginMode = "HEDGING MODE";  //--- Set hedging mode
   if (OneQuotePerBar) {                                 //--- Check one quote per bar
      datetime currentTime = iTime(_Symbol, _Period, 0); //--- Get current bar time
      if (LastActionTime == currentTime) {               //--- Check if same bar
         return;                                         //--- Exit
      } else {
         LastActionTime = currentTime;                   //--- Update last action time
      }
   }
   Error = NULL;                                         //--- Clear current error
   _ea.HandleTick();                                     //--- Handle tick
   if (IsDemoLiveOrVisualMode) {                         //--- Check visual mode
      MqlDateTime mql_datetime;                          //--- Declare datetime
      TimeCurrent(mql_datetime);                         //--- Get current time
      string comment = "\n" + (string)mql_datetime.year + "." + (string)mql_datetime.mon + "." + (string)mql_datetime.day + "   " + TimeToString(TimeCurrent(), TIME_SECONDS) + OrderInfoComment; //--- Build comment
      if (DisplayOnChartError) {                         //--- Check if error display enabled
         if (Error != NULL) comment += "\n      :: Current error : " + Error; //--- Add current error
         if (ErrorPreviousQuote != NULL) comment += "\n      :: Last error : " + ErrorPreviousQuote; //--- Add previous error
      }
      comment += "";                                     //--- Append empty line
      Comment("ACCOUNT SERVER:  ", AccountServer, "\n",  //--- Display account info
              "ACCOUNT CURRENCY:  ", AccountCurrency, "\n",
              "ACCOUNT NAME:  ", AccountName, "\n",
              "ACCOUNT TRADEMODE:  ", ReadableAccountTrademode, "\n",
              "ACCOUNT LOGIN:  ", AccountLogin, "\n",
              "ACCOUNT COMPANY:  ", AccountCompany, "\n",
              "ACCOUNT LEVERAGE:  ", AccountLeverage, "\n",
              "ACCOUNT LIMIT ORDERS:  ", AccountLimitOrders, "\n",
              "ACCOUNT MARGIN FREE:  ", AccountMarginFree, "\n",
              "ACCOUNT TRADING ALLOWED:  ", AccountTradeAllowed, "\n",
              "ACCOUNT EXPERT ALLOWED:  ", AccountTradeExpert, "\n",
              "ACCOUNT MARGIN ALLOWED:  ", ReadableAccountMarginMode);
   }
}

Hier verwalten wir Echtzeit-Marktaktualisierungen für das Programm über OnTick, der jeden neuen Preis-Tick verarbeitet. Wir beginnen mit der Erfassung von Kontodetails mit Hilfe von Funktionen wie „AccountInfoString“ für den Server, die Währung, den Namen und das Unternehmen, AccountInfoInteger für den Handelsmodus, das Login, den Leverage und die Orderlimits sowie AccountInfoDouble für die freie Marge. Wir konvertieren den Handelsmodus in eine lesbare Zeichenkette („DEMO ACCOUNT“, „CONTEST ACCOUNT“ oder „REAL ACCOUNT“) und den Margin-Modus, der in „AccountMarginMode“ gespeichert ist, der Übersichtlichkeit halber in „NETTING MODE“, „EXCHANGE MODE“ oder „HEDGING MODE“.

Um die Tick-Verarbeitung zu steuern, überprüfen wir das Flag „OneQuotePerBar“, und wenn es aktiviert ist, verwenden wir die Funktion iTime, um die Startzeit des aktuellen Balkens zu ermitteln und sie mit der „LastActionTime“ zu vergleichen. Wenn sie übereinstimmen, wird der Vorgang abgebrochen, um eine doppelte Verarbeitung zu vermeiden; andernfalls wird „LastActionTime“ aktualisiert. Wir löschen alle vorhandenen Fehler, indem wir „Error“ auf null setzen und die Funktion „HandleTick“ für „_ea“ aufrufen, um die Logik der Strategie auszuführen.

Für die Anzeige prüfen wir „IsDemoLiveOrVisualMode“, verwenden dann TimeCurrent und MqlDateTime, um den Zeitstempel zu formatieren, und erstellen einen Kommentarstring mit „OrderInfoComment“. Wenn „DisplayOnChartError“ aktiviert ist, werden Fehler aus „Error“ und „ErrorPreviousQuote“ angehängt. Schließlich verwenden wir die Kommentarfunktion, um Kontodetails auf dem Chart anzuzeigen, was eine Echtzeitüberwachung und ein Feedback für das Handelssystem gewährleistet. Wenn wir das Programm ausführen, erhalten wir die folgende Ausgabe.

SIGNALERZEUGUNG

Aus dem Bild können wir ersehen, dass wir einen Handel generieren und ausführen können, in diesem Fall einen Kauf. Um das zu erreichen, müssen wir die Transaktionen nachverfolgen. Daher wird die Ereignisbehandlung von OnTradeTransaction auf erfolgreich geöffnete Transaktionen reagieren und sie wie folgt verwalten.

//--- Handle trade transactions
void OnTradeTransaction(const MqlTradeTransaction& trans, const MqlTradeRequest& request, const MqlTradeResult& result) {
   switch (trans.type) {                               //--- Handle transaction type
   case TRADE_TRANSACTION_DEAL_ADD: {                  //--- Handle deal addition
      datetime end = TimeCurrent();                    //--- Get current server time
      datetime start = end - PeriodSeconds(PERIOD_D1); //--- Set start time (1 day ago)
      HistorySelect(start, end + PeriodSeconds(PERIOD_D1)); //--- Select history
      int dealsTotal = HistoryDealsTotal();            //--- Get total deals
      if (dealsTotal == 0) {                           //--- Check if no deals
         Print("No deals found");                      //--- Log message
         return;                                       //--- Exit
      }
      ulong orderTicketId = HistoryDealGetInteger(trans.deal, DEAL_ORDER); //--- Get order ticket
      CDealInfo dealInfo;                              //--- Declare deal info
      dealInfo.Ticket(trans.deal);                     //--- Set deal ticket
      ENUM_DEAL_ENTRY deal_entry = dealInfo.Entry();   //--- Get deal entry type
      bool found = false;                              //--- Initialize found flag
      if (deal_entry == DEAL_ENTRY_IN) {               //--- Handle deal entry
         OrderCollection* pendingOpenOrders = _ea.GetWallet().GetPendingOpenOrders(); //--- Retrieve pending open orders
         for (int i = 0; i < pendingOpenOrders.Count(); i++) { //--- Iterate orders
            Order* order = pendingOpenOrders.Get(i);   //--- Get order
            if (order.Ticket == orderTicketId) {       //--- Check matching ticket
               found = true;                           //--- Set found flag
               order.OpenTime = dealInfo.Time();       //--- Set open time
               order.OpenPrice = trans.price;          //--- Set open price
               order.TradePrice = order.OpenPrice;     //--- Set trade price
               if (OrderFillingType == ORDER_FILLING_FOK) {                 //--- Check FOK filling
                  order.OrderFilledLots += trans.volume;                    //--- Add volume
                  if (MathAbs(order.Lots - order.OrderFilledLots) < 1e-5) { //--- Check if fully filled
                     order.IsAwaitingDealExecution = false;                 //--- Clear execution flag
                     order.Lots = order.OrderFilledLots;                    //--- Update lots
                     order.TradeVolume = order.Lots;                        //--- Update trade volume
                     _ea.GetWallet().SetPendingOpenOrderToOpen(order);      //--- Move to open
                     Print(StringFormat("Execution done for order (%d) by EA (%d)", orderTicketId, MagicNumber)); //--- Log success
                  }
               } else {
                  order.IsAwaitingDealExecution = false; //--- Clear execution flag
                  bool actualVolumeDiffers = MathAbs(order.Lots - trans.volume) > 1e-5; //--- Check volume difference
                  order.OrderFilledLots += trans.volume; //--- Add volume
                  order.Lots = order.OrderFilledLots;    //--- Update lots
                  order.TradeVolume = order.Lots;        //--- Update trade volume
                  if (actualVolumeDiffers) {             //--- Check if volume differs
                     Print("Broker executed volume differs from requested volume. Executed volume: " + DoubleToStr(trans.volume)); //--- Log difference
                     OrderRepository::CalculateAndSetCommision(order); //--- Recalculate commission
                  }
                  _ea.GetWallet().SetPendingOpenOrderToOpen(order); //--- Move to open
                  Print(StringFormat("Execution done for order (%d) by EA (%d)", orderTicketId, MagicNumber)); //--- Log success
               }
            }
         }
      } else if (deal_entry == DEAL_ENTRY_OUT) {      //--- Handle deal exit
         OrderCollection* pendingCloseOrders = _ea.GetWallet().GetPendingCloseOrders(); //--- Retrieve pending close orders
         for (int i = 0; i < pendingCloseOrders.Count(); i++) { //--- Iterate orders
            Order* order = pendingCloseOrders.Get(i); //--- Get order
            if (order.Ticket == orderTicketId) {      //--- Check matching ticket
               found = true;                          //--- Set found flag
               if (OrderFillingType == ORDER_FILLING_FOK) {   //--- Check FOK filling
                  order.OrderFilledLots += trans.volume;      //--- Add volume
                  if (MathAbs(order.Lots - order.OrderFilledLots) < 1e-5) { //--- Check if fully filled
                     order.IsAwaitingDealExecution = false;   //--- Clear execution flag
                     order.CloseTime = dealInfo.Time();       //--- Set close time
                     order.ClosePrice = trans.price;          //--- Set close price
                     if (order.MagicNumber == MagicNumber) {  //--- Check EA order
                        TotalCommission += order.Commission;  //--- Add commission
                     }
                     if (IsDemoLiveOrVisualMode) {      //--- Check visual mode
                        AnyChartObjectDelete(ChartID(), IntegerToString(order.Ticket) + "_TP"); //--- Delete TP line
                        AnyChartObjectDelete(ChartID(), IntegerToString(order.Ticket) + "_SL"); //--- Delete SL line
                     }
                     if (order.ParentOrder != NULL) {   //--- Check if parent order
                        order.ParentOrder.Paint();      //--- Redraw parent
                     }
                     _ea.GetWallet().SetPendingCloseOrderToClosed(order); //--- Move to closed
                     Print(StringFormat("Execution done for order (%d) by EA (%d)", orderTicketId, MagicNumber)); //--- Log success
                  }
               } else {
                  bool actualVolumeDiffers = MathAbs(order.Lots - trans.volume) > 1e-5; //--- Check volume difference
                  if (actualVolumeDiffers) {                           //--- Handle partial close
                     Print("Broker executed volume differs from requested volume.Requested volume: " + DoubleToStr(order.Lots) + ".Executed volume: " + DoubleToStr(trans.volume)); //--- Log difference
                     Order* remainderOrder = new Order(order, false);  //--- Create remainder order
                     remainderOrder.Ticket = 0;                        //--- Clear ticket
                     remainderOrder.Lots = order.Lots - trans.volume;  //--- Set remaining volume
                     remainderOrder.TradeVolume = remainderOrder.Lots; //--- Update trade volume
                     OrderRepository::CalculateAndSetCommision(remainderOrder); //--- Recalculate commission
                     _ea.GetWallet().GetPendingCloseOrders().Add(remainderOrder); //--- Add remainder
                     order.Lots = trans.volume;                        //--- Update original volume
                     order.TradeVolume = order.Lots;                   //--- Update trade volume
                     OrderRepository::CalculateAndSetCommision(order); //--- Recalculate commission
                  } else {
                     Print("Broker executed volume: " + DoubleToStr(trans.volume)); //--- Log volume
                  }
                  order.IsAwaitingDealExecution = false;   //--- Clear execution flag
                  order.CloseTime = dealInfo.Time();       //--- Set close time
                  order.ClosePrice = trans.price;          //--- Set close price
                  if (order.MagicNumber == MagicNumber) {  //--- Check EA order
                     TotalCommission += order.Commission;  //--- Add commission
                  }
                  if (IsDemoLiveOrVisualMode) {            //--- Check visual mode
                     AnyChartObjectDelete(ChartID(), IntegerToString(order.Ticket) + "_TP"); //--- Delete TP line
                     AnyChartObjectDelete(ChartID(), IntegerToString(order.Ticket) + "_SL"); //--- Delete SL line
                  }
                  _ea.GetWallet().SetPendingCloseOrderToClosed(order); //--- Move to closed
                  Print(StringFormat("Execution done for order (%d) by EA (%d)", orderTicketId, MagicNumber)); //--- Log success
               }
            }
         }
      }
      if (found) {                                         //--- Check if deal found
         Print("Updated order with deal info.");           //--- Log update
      } else if (trans.symbol == Symbol() && dealInfo.Magic() == MagicNumber) { //--- Check EA deal
         Print("Couldn't find deal info for place/done order"); //--- Log missing deal
      }
      break;
   }
   }
}

In OnTradeTransaction, wo wir die Ereignisse von MqlTradeTransaction verarbeiten, konzentrieren wir uns auf die Transaktionen TRADE_TRANSACTION_DEAL_ADD, bei denen wir über HistorySelect ein eintägiges Historienfenster mit TimeCurrent und „PeriodSeconds“ abrufen. Wenn keine Angebote mit „HistoryDealsTotal“ gefunden werden, wird eine Meldung mit „Print“ protokolliert und die Suche beendet. Für jedes Handelsgeschäft extrahieren wir das Auftragsticket mit „HistoryDealGetInteger“ und erstellen ein „CDealInfo“-Objekt mit „Ticket“, um die Art der Eingabe über „Entry“ zu prüfen.

Für DEAL_ENTRY_IN (neue Handelsgeschäfte), iterieren wir „GetPendingOpenOrders“ von „_ea“'s „Wallet“, passende Tickets zur Aktualisierung der Aufträge mit „Time“ und „price“ von „trans“. Wenn „OrderFillingType“ gleich „ORDER_FILLING_FOK“ ist, wird „OrderFilledLots“ aktualisiert, und wenn der Auftrag erfüllt ist, wird „IsAwaitingDealExecution“ gelöscht und der Auftrag mit „SetPendingOpenOrderToOpen“ in die offene Position verschoben. Andernfalls behandeln wir Teilfüllungen, protokollieren Volumendifferenzen mit „Print“ und aktualisieren über „CalculateAndSetCommision“.

Für „DEAL_ENTRY_OUT“ (Abschlüsse) verarbeiten wir „GetPendingCloseOrders“, aktualisieren übereinstimmende Aufträge auf ähnliche Weise, entfernen Chart-Linien mit „AnyChartObjectDelete“ wenn „IsDemoLiveOrVisualMode“, Hinzufügen von Provisionen zu „TotalCommission“ und Verschieben von Aufträgen zum Abschluss mit „SetPendingCloseOrderToClosed“. Bei Teilabschlüssen wird ein neuer Auftrag mit „CalculateAndSetCommission“ für das verbleibende Volumen erstellt. Wir protokollieren Aktualisierungen oder Fehler mit „Drucken“, um eine genaue Verfolgung des Handels zu gewährleisten. Nach der Kompilierung erhalten wir die folgende Ausgabe.

GESCHÄFTSABWICKLUNGEN

Auf dem Bild sehen wir, dass wir die Handelsausführungen verwalten und die Metadaten im Fenster anzeigen. Bleibt nur noch die Backtests des Programms, um sicherzustellen, dass es korrekt funktioniert, und das wird im nächsten Abschnitt behandelt.


Backtest und Optimierung

Beim Testen des Programms ist uns ein Versehen aufgefallen, das wir übersehen hatten, und zwar die Freigabe des Programms und seiner Komponenten, als wir das Programm aus der Tabelle entfernten. Das Problem ist wie folgt.

BACKTEST-PROBLEME

Auf dem Bild sehen wir die Illustration der Probleme. Um diese Probleme zu beheben, haben wir die folgende Logik in den OnDeinit implementiert.

//--- Deinitialize Expert Advisor
void OnDeinit(const int reason) {
   Comment("");
   delete(_ea);                                        //--- Delete EA instance
   delete(AskFunc);                                    //--- Delete Ask function
   delete(BidFunc);                                    //--- Delete Bid function
}

Wir haben den Bereinigungsprozess für das Programm mithilfe von OnDeinit verwaltet, der ausgelöst wird, wenn der Expert Advisor entfernt oder das Terminal heruntergefahren wird. Wir begannen damit, alle Chart-Kommentare mit der Kommentarfunktion zu löschen, um eine saubere Darstellung des Charts zu gewährleisten. Als Nächstes haben wir den Speicher freigegeben, indem wir das Objekt „_ea“, das die Hauptinstanz „EA“ darstellt, mit dem Operator delete gelöscht haben. Wir haben auch die Objekte „AskFunc“ und „BidFunc“ gelöscht, die für den Abruf von Preisdaten zuständig sind, um Ressourcen freizugeben.

Dieser übersichtliche Prozess gewährleistet eine ordnungsgemäße Deinitialisierung, verhindert Speicherlecks und erhält die Effizienz des Systems, wenn das Programm beendet wird. Damit war alles gelöst, und nach einer gründlichen Optimierung und einem Backtest, bei dem die Standardeinstellungen verwendet und die Einstellungen für das Handels- bzw. Risikomodul so geändert wurden, dass 1 %, 5, 30, 10 bzw. 60 Eingaben verwendet wurden, erhielten wir die folgenden Ergebnisse.

Grafik des Backtests:

GRAFIK

Bericht des Backtest:

BERICHT


Schlussfolgerung

Zusammenfassend haben wir eine MQL5-Implementierung der Strategie Envelopes Trend Bounce Scalping entwickelt, die eine automatisierte Handelsausführung ermöglicht, die durch präzise Signale von Envelopes, gleitenden Durchschnitten und dem Relative Strength Index gesteuert wird, mit integriertem Risikomanagement für kontrollierte Positionsgröße und Verlustschutz. Unser modulares Framework mit dynamischer Signalauswertung, Handelsausführung und Auftragsüberwachung in Echtzeit bietet eine skalierbare Grundlage für Scalping. Sie können dieses Programm weiter verfeinern, indem Sie die Signalschwellen anpassen, die Risikoparameter optimieren oder zusätzliche Indikatoren einbeziehen, um es an Ihre Handelsziele anzupassen.

Haftungsausschluss: Diese Umsetzung ist nur für Bildungszwecke gedacht. Der Handel ist mit erheblichen finanziellen Risiken verbunden, und die Volatilität der Märkte kann zu erheblichen Verlusten führen. Gründliches Backtests und umsichtiges Risikomanagement sind unerlässlich, bevor Sie dieses Programm im Live-Handel einsetzen.

Wenn wir diese Techniken beherrschen, können wir die Anpassungsfähigkeit und Robustheit des Programms verbessern oder seine Struktur nutzen, um andere Handelsstrategien zu entwickeln und Handelsalgorithmus weiterzuentwickeln.


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

MQL5-Assistenten-Techniken, die Sie kennen sollten (Teil 68):  Verwendung von TRIX-Mustern und des Williams Percent Range mit einem Cosinus-Kernel-Netzwerk MQL5-Assistenten-Techniken, die Sie kennen sollten (Teil 68): Verwendung von TRIX-Mustern und des Williams Percent Range mit einem Cosinus-Kernel-Netzwerk
Wir knüpfen an unseren letzten Artikel an, in dem wir das Indikatorpaar TRIX und Williams Percent Range vorstellten, und überlegen, wie dieses Indikatorpaar durch maschinelles Lernen erweitert werden kann. TRIX und Williams Percent sind ein Trend- und Unterstützungs-/Widerstandspaar, das sich gegenseitig ergänzt. Unser Ansatz des maschinellen Lernens verwendet ein neuronales Faltungsnetzwerk, das bei der Feinabstimmung der Prognosen dieses Indikatorpaares den Kosinus-Kernel in seine Architektur einbezieht. Wie immer wird dies in einer nutzerdefinierten Signalklassendatei durchgeführt, die mit dem MQL5-Assistenten arbeitet, um einen Expert Advisor zusammenzustellen.
MQL5-Assistenten-Techniken, die Sie kennen sollten (Teil 67): Verwendung von TRIX-Mustern und der Williams Percent Range MQL5-Assistenten-Techniken, die Sie kennen sollten (Teil 67): Verwendung von TRIX-Mustern und der Williams Percent Range
Der Triple Exponential Moving Average Oscillator (TRIX) und der Williams Percentage Range Oscillator sind ein weiteres Paar von Indikatoren, die in Verbindung mit einem MQL5 Expert Advisor verwendet werden können. Dieses Indikatorpaar ist, wie die anderen, die wir kürzlich behandelt haben, ebenfalls komplementär, da der TRIX den Trend definiert, während die Williams Percent Range die Unterstützungs- und Widerstandsniveaus bestätigt. Wie immer verwenden wir den MQL5-Assistenten, um das Potenzial dieser beiden zu testen.
Entwicklung des Price Action Analysis Toolkit (Teil 24): Analyse-Tool zur Quantifizierung von Preisaktionen Entwicklung des Price Action Analysis Toolkit (Teil 24): Analyse-Tool zur Quantifizierung von Preisaktionen
Kerzenmuster bieten wertvolle Einblicke in potenzielle Marktbewegungen. Einige einzelne Kerzen signalisieren die Fortsetzung des aktuellen Trends, während andere, je nach ihrer Position innerhalb der Kursbewegung, Umkehrungen vorhersagen. In diesem Artikel wird ein EA vorgestellt, der automatisch vier wichtige Kerzen-Formationen identifiziert. In den folgenden Abschnitten erfahren Sie, wie dieses Tool Ihre Preis-Aktions-Analyse verbessern kann.
Vom Neuling zum Experten: Autogeometrisches Analysesystem Vom Neuling zum Experten: Autogeometrisches Analysesystem
Geometrische Muster bieten Händlern eine prägnante Methode zur Interpretation von Kursbewegungen. Viele Analysten zeichnen Trendlinien, Rechtecke und andere Formen mit der Hand und treffen ihre Handelsentscheidungen dann auf der Grundlage der von ihnen gesehenen Formationen. In diesem Artikel untersuchen wir eine automatisierte Alternative: die Nutzung von MQL5 zur Erkennung und Analyse der gängigsten geometrischen Muster. Wir schlüsseln die Methodik auf, erörtern Details der Implementierung und zeigen auf, wie die automatische Mustererkennung die Markteinblicke eines Händlers schärfen kann.