
Automatisieren von Handelsstrategien in MQL5 (Teil 19): Envelopes Trend Bounce Scalping - Handelsausführung und Risikomanagement (Teil II)
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:
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.
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.
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.
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.
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.
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:
Bericht des Backtest:
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
Warnung: Alle Rechte sind von MetaQuotes Ltd. vorbehalten. Kopieren oder Vervielfältigen untersagt.
Dieser Artikel wurde von einem Nutzer der Website verfasst und gibt dessen persönliche Meinung wieder. MetaQuotes Ltd übernimmt keine Verantwortung für die Richtigkeit der dargestellten Informationen oder für Folgen, die sich aus der Anwendung der beschriebenen Lösungen, Strategien oder Empfehlungen ergeben.





- Freie Handelsapplikationen
- Über 8.000 Signale zum Kopieren
- Wirtschaftsnachrichten für die Lage an den Finanzmärkte
Sie stimmen der Website-Richtlinie und den Nutzungsbedingungen zu.