Aufbau eines Smart Trade Managers in MQL5: Automatisieren Sie Break-Even, Trailing Stop und Teilweises Schließen
Einführung
Das Handelsmanagement ist einer der meist übersehenen Aspekte des Handels. Viele Händler konzentrieren sich darauf, gute Einstiegsmöglichkeiten zu finden, vergessen aber, dass Gewinne durch die Art und Weise, wie der Handel nach dem Einstieg gehandhabt wird, erzielt oder verloren werden können. Das Verschieben von Stop-Loss-Werten, das Mitnehmen von Teilgewinnen oder das Nachziehen eines Stop-Loss-Werts erfordert oft eine genaue Überwachung der Handelsgeschäfte und schnelle Reaktionen, die in einem lebhaften Marktumfeld nur schwer aufrechtzuerhalten sind.
Das manuelle Handelsmanagement wird schnell anstrengend, vor allem wenn mehrere Positionen über verschiedene Symbole hinweg überwacht werden. Ein Händler zögert vielleicht, einen Stop-Loss zu verschieben, passt ihn zu spät an oder vergisst ihn gänzlich. Diese kleinen Fehler können dazu führen, dass Gewinne in Verluste umgewandelt werden, was im Laufe der Zeit zu emotionalen Entscheidungen und inkonsistenten Ergebnissen führt.
Selbst disziplinierte Händler, die ihre Regeln befolgen, können die manuelle Ausführung als ermüdend empfinden. Das ständige Beobachten von Charts, Berechnen von Kursen und Reagieren auf Kursbewegungen führt zu Ermüdung und Ablenkung. Das mindert die Effizienz und nimmt die ruhige Konzentration, die ein erfolgreicher Handel erfordert.
In diesem Artikel wird eine automatisierte Lösung für diese Probleme vorgestellt. Es wird Sie durch die Erstellung eines voll funktionsfähigen Trade Manager Expert Advisors führen. Der EA übernimmt drei wesentliche Aspekte des Handelsmanagements: automatisches Verschieben des Stop-Loss auf Break-Even, um Verluste zu vermeiden, Nachziehen der Stop-Loss-Niveaus, wenn sich der Kurs zugunsten einer Position bewegt, und Schließen von Teilgewinnen, um Gewinne schrittweise zu sichern.
Wie der Smart Trade Manager EA aufgebaut ist
Der Smart Trade Manager, AutoProtect genannt, ist als Expert Advisor (EA) innerhalb der MQL5-Umgebung konzipiert. Dies ist wichtig zu erwähnen, da MQL5 verschiedene Programmtypen unterstützt, darunter Skripte, Indikatoren, Dienste und Expert Advisors. Da der Zweck von AutoProtect darin besteht, offene Positionen automatisch zu überwachen und zu verwalten, muss es kontinuierlich in Echtzeit arbeiten.
AutoProtect hat drei Hauptfunktionen, die zusammenarbeiten, um den Handel zu schützen und den Verwaltungsprozess des Händlers zu automatisieren. Dazu gehören die Break-Even-Funktion, die Trailing-Stop-Funktion und die Teilgewinn-Schlussfunktion. Jede dieser Funktionen kann vom Nutzer aktiviert oder deaktiviert werden, sodass der EA ganz flexibel nach den persönlichen Handelsvorlieben konfiguriert werden kann. Ein Händler, der zum Beispiel nur die Break-Even-Funktion nutzen möchte, kann die beiden anderen Funktionen einfach deaktivieren.
Die Break-Even-Funktion verschiebt den Stop-Loss automatisch auf den Einstiegskurs (plus einen optionalen Pufferabstand), wenn der Handel ein bestimmtes Gewinnniveau erreicht. Dies trägt dazu bei, Risiken zu eliminieren und die Position zu sichern, sobald sie sich günstig entwickelt. Die Trailing-Stop-Funktion übernimmt dann die Aufgabe, den Stop-Loss anzupassen, wenn sich der Markt weiter in die profitable Richtung bewegt. Sie gewährleistet, dass die Gewinne geschützt sind, während der Handel auf natürliche Weise wachsen kann. Die Funktion „Partial Close“ schließlich sichert die Gewinne schrittweise, indem sie einen Teil der Position schließt, wenn ein bestimmter Gewinnabstand (in Punkten) erreicht ist.
AutoProtect verwaltet nur die Handelsgeschäfte des Charts, an den es angehängt ist. Diese Entscheidung ist beabsichtigt. Jedes Finanzpapier hat einen eindeutigen Punktwert, sodass die universelle Verwaltung von Handelsgeschäften über mehrere Instrumente mit einer einzigen Konfiguration zu Inkonsistenzen führen könnte. Die Beschränkung des Handelsmanagements auf ein Symbol gewährleistet ein präzises und konsistentes Verhalten. Darüber hinaus können die Nutzer den EA so konfigurieren, dass er Handelsgeschäfte auf der Grundlage einer bestimmten magischen Zahl verwaltet. Dies ist nützlich, wenn ein Händler möchte, dass AutoProtect nur Handelsgeschäfte verwaltet, die von einem bestimmten Expert Advisor eröffnet wurden.
Es ist auch wichtig zu wissen, dass AutoProtect nur die Handelsgeschäfte überwacht und verwaltet, die nach dem Start im Chart geöffnet wurden. Er reagiert nicht auf ältere oder bereits offene Positionen zum Zeitpunkt der Initialisierung. Dieser Ansatz stellt sicher, dass nur Handelsgeschäfte betroffen sind, die unter seiner Aufsicht erstellt wurden, um unbeabsichtigtes Verhalten zu vermeiden.
Der EA verfügt über mehrere Eingabeparameter, die zur besseren Übersichtlichkeit und einfacheren Konfiguration in logische Gruppen eingeteilt sind:
//+------------------------------------------------------------------+ //| User Input Variables | //+------------------------------------------------------------------+ input group "Information" input bool manageTradesByMagicNumber = true; input ulong tradeSelectionMagicNumber = 254700680002; input group "Break-Even" input bool enableBreakEven = false; input int breakEvenTriggerPoints = 50; input int breakEvenLockPoints = 0; input group "Trailing Stop" input bool enableTrailingStop = false; input int trailingStartPoints = 50; input int trailingStepPoints = 20; input int trailingDistancePoints = 100; input group "Partial Close" input bool enablePartialClose = false; input int partialCloseTriggerPoints = 100; input double partialClosePercent = 50.0;
Jeder Parameter hat eine bestimmte Funktion. Die breakEvenTriggerPoints legen zum Beispiel fest, wann die Break-Even-Bewegung aktiviert wird, und die breakEvenLockPoints bestimmen, wie viele zusätzliche Punkte über den Einstieg hinaus als Gewinn zu sichern sind. Im Trailing-Stop-Abschnitt markieren trailingStartPoints das Profit-Level, bei dem das Trailing beginnt, trailingStepPoints legt fest, wie häufig der Stop verschoben wird, und trailingDistancePoints gibt an, wie weit der Stop hinter dem aktuellen Preis bleiben soll. Die partialCloseTriggerPoints und partialClosePercent legen fest, wie viel von einem Handel geschlossen werden soll, wenn sich der Preis um eine bestimmte Distanz (angegeben in Punkten) in den Gewinn bewegt.
Eine der Konstruktionsregeln von AutoProtect stellt sicher, dass, wenn sowohl die Break-Even- als auch die Trailing-Stop-Funktion aktiviert sind, der Trailing-Stop sofort nach Erreichen der Break-Even-Bedingung einsetzt. Dies wird in der Initialisierungslogik wie unten gezeigt erzwungen:
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit(){ ... //--- If both BreakEven and Trailing are enabled,trailing should only start after break-even has triggered. if(enableBreakEven && enableTrailingStop){ usefulTrailingStartPoints = breakEvenTriggerPoints; } ... return(INIT_SUCCEEDED); }
Intern folgt der EA einem modularen Programmieransatz mit sauberem, gut kommentiertem Code. Jede Funktion ist als ein in sich geschlossenes Modul geschrieben. Dieses Design verbessert die Lesbarkeit, vereinfacht die Fehlersuche und ermöglicht künftige Erweiterungen, ohne bestehende Funktionen zu beeinträchtigen.
Einrichten des Projekts
Um mit der Entwicklung des AutoProtect Expert Advisor zu beginnen, öffnen Sie MetaEditor, erstellen Sie ein neues Expert Advisor-Projekt und nennen Sie es AutoProtect.mq5. Sobald die Projektdatei erstellt ist, ersetzen Sie ihren Inhalt durch den folgenden Standardcode.
//+------------------------------------------------------------------+ //| AutoProtect.mq5 | //| Copyright 2025, MetaQuotes Ltd. Developer is Chacha Ian | //| https://www.mql5.com/en/users/chachaian | //+------------------------------------------------------------------+ #property copyright "Copyright 2025, MetaQuotes Ltd. Developer is Chacha Ian" #property link "https://www.mql5.com/en/users/chachaian" #property version "1.00" //+------------------------------------------------------------------+ //| Libraries | //+------------------------------------------------------------------+ #include <Trade\Trade.mqh> //+------------------------------------------------------------------+ //| User Input Variables | //+------------------------------------------------------------------+ input group "Information" input bool manageTradesByMagicNumber = true; input ulong tradeSelectionMagicNumber = 254700680002; input group "Break-Even" input bool enableBreakEven = false; input int breakEvenTriggerPoints = 50; input int breakEvenLockPoints = 0; input group "Trailing Stop" input bool enableTrailingStop = false; input int trailingStartPoints = 50; input int trailingStepPoints = 20; input int trailingDistancePoints = 100; input group "Partial Close" input bool enablePartialClose = false; input int partialCloseTriggerPoints = 100; input double partialClosePercent = 50.0; //+------------------------------------------------------------------+ //| Global Variables | //+------------------------------------------------------------------+ CTrade Trade; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit(){ // --- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason){ //--- } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick(){ //--- } //+------------------------------------------------------------------+ //| Trade Transaction Events handler | //+------------------------------------------------------------------+ void OnTradeTransaction( const MqlTradeTransaction &trans, const MqlTradeRequest &request, const MqlTradeResult &result){ // ---- } //+------------------------------------------------------------------+
Diese Struktur bietet alle wesentlichen Ereignisbehandlungen, die für einen Expert Advisor in MQL5 benötigt werden, einschließlich Initialisierung, Deinitialisierung, Tick-Handling und Verfolgung von Handelstransaktionen.
Um die Automatisierung von Handelsaktionen wie die Änderung von Stopps oder das Schließen von Positionen zu ermöglichen, binden wir zunächst die Standardbibliothek Trade.mqh ein. Diese Bibliothek ermöglicht den Zugriff auf die Klasse CTrade, die die Auftragsverwaltung durch ihre integrierten Methoden wie PositionModify und PositionClosePartial vereinfacht.
Als Nächstes erstellen wir eine globale Instanz der Klasse CTrade mit dem Namen Trade. Dieses Objekt wird im gesamten Expert Advisor verwendet, um alle Handelsoperationen auszuführen und sicherzustellen, dass wir effizient und konsistent mit der Handelsumgebung interagieren können.
Durch die globale Definition von CTrade Trade können alle Funktionen in unserem EA problemlos darauf zugreifen, sei es zur Anpassung von Stop-Loss-Levels, zur Aktivierung der Break-Even-Logik oder zum Trailing von Positionen.
Logik der Handelsauswahl
Der AutoProtect Expert Advisor verwaltet nur Handelsgeschäfte, die auf demselben Chart eröffnet werden, auf dem er gestartet wurde. Diese Design-Entscheidung gewährleistet ein konsistentes Verhalten, da verschiedene Symbole eindeutige Punktwerte haben, die zu Berechnungsfehlern führen könnten, wenn sie universell behandelt würden. Die Beschränkung des Handelsmanagements auf das Chart-Symbol garantiert Präzision und verhindert Konflikte zwischen verschiedenen Märkten.
Um mehr Kontrolle zu ermöglichen, erlaubt der EA auch das Filtern von Handelsgeschäften anhand einer bestimmten magischen Zahl. Wenn diese Option aktiviert ist, verwaltet der Expert Advisor nur Handelsgeschäfte, die der angegebenen Zahl entsprechen. Diese Funktion ist besonders hilfreich, wenn ein Händler mehrere EAs auf demselben Symbol betreibt, aber möchte, dass AutoProtect nur die Handelsgeschäfte einer bestimmten Strategie verarbeitet.
Ein weiterer wichtiger Aspekt des Designs ist, dass AutoProtect nur auf neu eröffnete Positionen reagiert, nachdem es gestartet wurde. Es wird nicht versucht, bereits bestehende Handelsgeschäfte zu verwalten, die aktiv waren, bevor der EA mit dem Chart verbunden wurde. Dieser Ansatz vermeidet Inkonsistenzen und stellt sicher, dass jede verwaltete Position ab dem Zeitpunkt ihrer Erstellung ordnungsgemäß registriert und nachverfolgt wird.
Um dies zu ermöglichen, wird eine nutzerdefinierte Struktur namens MqlTradeInfo definiert. Diese Struktur enthält wesentliche Handelsdetails wie Ticketnummer, Positionstyp, Einstiegskurs, Stop-Levels und Trailing-Parameter. Wir pflegen dann ein globales Array dieser Strukturen, um den Überblick über alle vom EA verwalteten Positionen zu behalten.
... //+------------------------------------------------------------------+ //| User Input Variables | //+------------------------------------------------------------------+ input group "Information" input bool manageTradesByMagicNumber = true; input ulong tradeSelectionMagicNumber = 254700680002; ... //+------------------------------------------------------------------+ //| Data Structures | //+------------------------------------------------------------------+ struct MqlTradeInfo{ ulong ticket; ENUM_POSITION_TYPE positionType; double openPrice; double originalStopLevel; double currentStopLevel; double nextStopLevel; double originalTargetLevel; double nextTrailTriggerPrice; bool isMovedToBreakEven; bool isPartialProfitsSecured; }; //+------------------------------------------------------------------+ //| Global Variables | //+------------------------------------------------------------------+ CTrade Trade; MqlTradeInfo tradeInfo[]; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit(){ //--- return(INIT_SUCCEEDED); } ...
Als Nächstes wird die Ereignisbehandlung von OnTradeTransaction verwendet, um zu erkennen, wenn neue Handelsgeschäfte eröffnet werden. Wann immer eine neue Position erstellt wird, bestätigt der EA sie durch die Handelshistorie und zeichnet dann ihre Details im Array tradeInfo auf. Nur diese verfolgten Handelsgeschäfte werden später von der Trailing-Stop-Funktion verarbeitet.
... //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick(){ //--- } //+------------------------------------------------------------------+ //| Trade Transaction Events handler | //+------------------------------------------------------------------+ void OnTradeTransaction( const MqlTradeTransaction &trans, const MqlTradeRequest &request, const MqlTradeResult &result){ // When a new position is opened if(trans.type != TRADE_TRANSACTION_DEAL_ADD){ return; } if(trans.symbol != _Symbol){ return; } if(trans.deal == 0){ return; } bool selected = false; HistorySelect(TimeCurrent() - 60, TimeCurrent()); for(int i = 0; i < 6; i++){ if(HistoryDealSelect(trans.deal)){ selected = true; break; } Sleep(5); HistorySelect(TimeCurrent() - 60, TimeCurrent()); } long entry = -1; if(selected){ entry = (long)HistoryDealGetInteger(trans.deal, DEAL_ENTRY); } if(selected && entry == DEAL_ENTRY_IN){ ulong positionTicket = trans.position; double openPrice = 0.000000; double originalStopLevel = 0.000000; double originalTargetLevel = 0.000000; ulong magicNumber = 0; if(PositionSelectByTicket(positionTicket)){ openPrice = PositionGetDouble(POSITION_PRICE_OPEN); originalStopLevel = PositionGetDouble(POSITION_SL); originalTargetLevel = PositionGetDouble(POSITION_TP); magicNumber = (ulong)PositionGetInteger(POSITION_MAGIC); } if(manageTradesByMagicNumber){ if(magicNumber != tradeSelectionMagicNumber){ return; } } if(trans.deal_type == DEAL_TYPE_BUY){ Print("NEW LONG opened (confirmed via history): deal=", trans.deal, " pos=", trans.position); ArrayResize(tradeInfo, ArraySize(tradeInfo) + 1); tradeInfo[ArraySize(tradeInfo) - 1].ticket = positionTicket; tradeInfo[ArraySize(tradeInfo) - 1].positionType = POSITION_TYPE_BUY; tradeInfo[ArraySize(tradeInfo) - 1].openPrice = openPrice; tradeInfo[ArraySize(tradeInfo) - 1].originalStopLevel = originalStopLevel; tradeInfo[ArraySize(tradeInfo) - 1].currentStopLevel = originalStopLevel; tradeInfo[ArraySize(tradeInfo) - 1].nextStopLevel = originalStopLevel + trailingStepPoints * pointValue; tradeInfo[ArraySize(tradeInfo) - 1].originalTargetLevel = originalTargetLevel; tradeInfo[ArraySize(tradeInfo) - 1].nextTrailTriggerPrice = openPrice + usefulTrailingStartPoints * pointValue; tradeInfo[ArraySize(tradeInfo) - 1].isMovedToBreakEven = false; tradeInfo[ArraySize(tradeInfo) - 1].isPartialProfitsSecured = false; } if(trans.deal_type == DEAL_TYPE_SELL){ Print("NEW SHORT opened (confirmed via history): deal=", trans.deal, " pos=", trans.position); tradeInfo[ArraySize(tradeInfo) - 1].ticket = positionTicket; tradeInfo[ArraySize(tradeInfo) - 1].positionType = POSITION_TYPE_SELL; tradeInfo[ArraySize(tradeInfo) - 1].openPrice = openPrice; tradeInfo[ArraySize(tradeInfo) - 1].originalStopLevel = originalStopLevel; tradeInfo[ArraySize(tradeInfo) - 1].currentStopLevel = originalStopLevel; tradeInfo[ArraySize(tradeInfo) - 1].nextStopLevel = originalStopLevel - trailingStepPoints * pointValue; tradeInfo[ArraySize(tradeInfo) - 1].originalTargetLevel = originalTargetLevel; tradeInfo[ArraySize(tradeInfo) - 1].nextTrailTriggerPrice = openPrice - usefulTrailingStartPoints * pointValue; tradeInfo[ArraySize(tradeInfo) - 1].isMovedToBreakEven = false; tradeInfo[ArraySize(tradeInfo) - 1].isPartialProfitsSecured = false; } } if(!selected){ if(trans.deal_type == DEAL_TYPE_BUY && trans.position != 0){ Print("Probable NEW LONG (fallback): deal=", trans.deal, " pos=", trans.position); } if(trans.deal_type == DEAL_TYPE_SELL && trans.position != 0){ Print("Probable NEW SHORT (fallback): deal=", trans.deal, " pos=", trans.position); } } } //+------------------------------------------------------------------+
Dieser Codeblock stellt sicher, dass AutoProtect automatisch jede neue Position registriert, die unter den Symbol- und (falls eingestellt) Magic Number-Filtern qualifiziert ist. Von nun an werden diese Handelsgeschäfte offiziell verfolgt und können durch das Trailing-Stop-Modul des EAs sicher modifiziert werden.
Wenn Sie versuchen, den EA in diesem Stadium zu kompilieren, werden Sie einige Kompilierfehler feststellen. Dies geschieht, weil das Programm auf einige globale Variablen verweist, die noch nicht deklariert wurden. Um diese Fehler zu beheben, müssen wir diese Variablen deklarieren, bevor wir sie verwenden.
Wir fügen nun einige weitere globale Variablen hinzu, die wichtige Handelsdaten speichern, die in verschiedenen Teilen des Expert Advisors verwendet werden. Dazu gehören freezeLevelPoints, askPrice, bidPrice und pointValue.
... //+------------------------------------------------------------------+ //| Global Variables | //+------------------------------------------------------------------+ CTrade Trade; MqlTradeInfo tradeInfo[]; double closePriceMinutesData []; int usefulTrailingStartPoints = trailingStartPoints; long freezeLevelPoints; double askPrice; double bidPrice; double pointValue; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit(){ // --- return(INIT_SUCCEEDED); } ...
Die Variable freezeLevelPoints enthält den Freeze-Level des Brokers in Punkten. Dieser Wert legt den Mindestabstand zum aktuellen Marktpreis fest, ab dem die Stop-Levels (Stop Loss und Take Profit) geändert werden können. Da sich dieser Wert während der Programmausführung nicht ändert, können wir ihn sicher in der OnInit-Funktion initialisieren.
... //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit(){ //--- Initialize global variables freezeLevelPoints = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_FREEZE_LEVEL); return(INIT_SUCCEEDED); } ...
Die Variablen askPrice und bidPrice stellen die aktuellen Marktpreise für Kauf und Verkauf dar. Da sich diese Werte mit jedem neuen Tick ändern, sollten wir sie innerhalb der OnTick-Funktion aktualisieren.
... //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick(){ //--- Scope variables askPrice = SymbolInfoDouble(_Symbol, SYMBOL_ASK); bidPrice = SymbolInfoDouble(_Symbol, SYMBOL_BID); } ...
pointValue stellt die kleinste Kursbewegung für das aktuelle Symbol dar. Sie bleibt konstant, sodass wir sie zusammen mit freezeLevelPoints in der OnInit-Funktion initialisieren können.
... //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ int OnInit(){ //--- Initialize global variables freezeLevelPoints = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_FREEZE_LEVEL); pointValue = SymbolInfoDouble(_Symbol, SYMBOL_POINT); return(INIT_SUCCEEDED); } ...
Bevor wir weitermachen, müssen wir zwei weitere globale Variablen definieren, die bei der Arbeit des EA eine wichtige Rolle spielen werden. Das sind:
double closePriceMinutesData[]; int usefulTrailingStartPoints = trailingStartPoints;
Das closePriceMinutesData-Array wird verwendet, um die letzten Schlusskurse des 1-Minuten-Zeitrahmens (M1) zu speichern. Diese Daten werden dem EA später helfen, Bedingungen wie Preisüberschneidungen und Crossunders zu erkennen, die für einige Handelsmanagementfunktionen wichtig sind, die in späteren Abschnitten behandelt werden.
Die Variable usefulTrailingStartPoints hilft bei der Koordinierung des Verhaltens zwischen der Break-Even- und der Trailing-Stop-Funktion. Normalerweise wird der Wert von trailingStartPoints verwendet, der angibt, um wie viele Punkte in den Gewinn hinein das Trailing beginnen soll. Wenn jedoch sowohl die Break-Even- als auch die Trailing-Stop-Funktion gleichzeitig aktiviert sind, ist es logisch, dass der Trailing-Stop erst nach dem Auslösen des Break-Even ausgelöst werden sollte. Damit dies automatisch geschieht, wird der folgende Code in die OnInit-Funktion eingefügt.
... //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit(){ ... //--- If both BreakEven and Trailing are enabled,trailing should only start after break-even has triggered. if(enableBreakEven && enableTrailingStop){ usefulTrailingStartPoints = breakEvenTriggerPoints; } // Set arrays as series ArraySetAsSeries(closePriceMinutesData, true); return(INIT_SUCCEEDED); } ...
Die Funktion ArraySetAsSeries stellt sicher, dass der jüngste Datenpunkt des Arrays closePriceMinutesData immer bei Index 0 des Arrays gespeichert wird, was den Zugriff auf die neuesten Preise vereinfacht.
Dann laden wir in der Funktion OnTick mit der Funktion CopyClose die letzten sieben Schlusskurse aus dem Zeitrahmen M1:
... //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick(){ ... bidPrice = SymbolInfoDouble(_Symbol, SYMBOL_BID); // Get some minutes data if(CopyClose(_Symbol, PERIOD_M1, 0, 7, closePriceMinutesData) == -1){ Print("Error while copying minutes datas ", GetLastError()); return; } } ...
Durch die Aktualisierung dieses Arrays bei jedem Tick hält der EA ein kleines rollendes Fenster mit den jüngsten Marktdaten vor, auf das immer dann Bezug genommen werden kann, wenn eine Crossover-basierte Logik benötigt wird.
Break-Even-Funktion
Bevor die Break-Even-Logik implementiert wird, ist es wichtig, einige Hilfsfunktionen zu definieren, die unseren Code sauberer und einfacher zu pflegen machen. Diese kleinen Hilfsfunktionen führen spezifische Aufgaben aus, auf die sich die Hauptlogik stützt. Wir platzieren sie in einem separaten Abschnitt, der den Utility-Funktionen gewidmet ist, direkt unterhalb unserer bestehenden Codestruktur.
... //--- UTILITY FUNCTIONS //+------------------------------------------------------------------+ //| Checks if position modification is allowed | //+------------------------------------------------------------------+ bool IsTradeModificationAllowed(long freezeLevelPts, ENUM_ORDER_TYPE action, double askPr, double bidPr, double stopLossLvl, double takeProfitLvl){ double freezeDistance = freezeLevelPts * pointValue; if(freezeLevelPts == 0){ return true; } if(action == ORDER_TYPE_BUY) { double distanceFromSpotPriceToTP = takeProfitLvl - bidPr; double distanceFromSpotPriceToSL = bidPr - stopLossLvl; if(distanceFromSpotPriceToTP > freezeDistance && distanceFromSpotPriceToSL > freezeDistance){ return true; } } if(action == ORDER_TYPE_SELL){ double distanceFromSpotPriceToTP = askPr - takeProfitLvl; double distanceFromSpotPriceToSL = stopLossLvl - askPr; if(distanceFromSpotPriceToTP > freezeDistance && distanceFromSpotPriceToSL > freezeDistance){ return true; } } return false; } //+------------------------------------------------------------------+ //| To detect a crossover | //+------------------------------------------------------------------+ bool IsCrossOver(const double price, const double &closePriceMinsData[]){ if(closePriceMinsData[1] <= price && closePriceMinsData[0] > price){ return true; } return false; } //+------------------------------------------------------------------+ //| To detect a crossunder | //+------------------------------------------------------------------+ bool IsCrossUnder(const double price, const double &closePriceMinsData[]){ if(closePriceMinsData[1] >= price && closePriceMinsData[0] < price){ return true; } return false; }
Die erste Funktion, IsTradeModificationAllowed, prüft, ob eine Position auf der Grundlage des Freeze-Levels des Brokers geändert werden kann. Ein Freeze-Level legt fest, wie nah ein Auftrag oder ein Stop-Level am aktuellen Marktpreis liegen darf. Liegt ein Auftrag zu nahe am Kurs innerhalb der Sperrzone, wird jede Änderung vom Makler abgelehnt. Diese Funktion berechnet den Abstand zwischen dem aktuellen Kurs und dem Stop-Loss- sowie dem Take-Profit-Niveau. Es stellt dann sicher, dass diese Abstände größer sind als der Einfrierabstand, bevor es eine Aktualisierung zulässt.
Als Nächstes definieren wir zwei einfache Hilfsfunktionen: IsCrossOver und IsCrossUnder. Diese erkennen, wenn der letzte einminütige Schlusskurs ein bestimmtes Kursniveau über- oder unterschreitet. Sie werden verwendet, um zu bestätigen, dass der Kurs unser Break-Even-Trigger-Level überschritten hat, bevor ein Stop-Loss geändert wird. Indem wir diese Funktionen getrennt halten, machen wir das Programm modular, lesbar und später leicht erweiterbar. Zum Beispiel können wir dieselbe Crossover-Logik für Trailing-Stops oder Teilschließungen verwenden.
Nachdem wir die Hilfsfunktionen definiert haben, erstellen wir die Funktion ManageBreakEven. Diese Funktion ist dafür verantwortlich, dass der Stop-Loss eines Handels automatisch auf den Break-Even-Punkt verschoben wird, sobald sich der Kurs um eine bestimmte Strecke im Gewinn bewegt.
... //--- UTILITY FUNCTIONS ... //+------------------------------------------------------------------+ //| To mange the trade break even functionality | //+------------------------------------------------------------------+ void ManageBreakEven (){ int totalPositions = PositionsTotal(); for(int i = totalPositions - 1; i >= 0; i--){ ulong ticket = PositionGetTicket(i); if(ticket != 0){ // Get some useful position properties ENUM_POSITION_TYPE positionType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); double openPrice = PositionGetDouble(POSITION_PRICE_OPEN); double currentPrice = PositionGetDouble(POSITION_PRICE_CURRENT); string symbol = PositionGetString(POSITION_SYMBOL); double stopLevel = PositionGetDouble(POSITION_SL); double takeProfitLevel = PositionGetDouble(POSITION_TP); ulong magicNumber = PositionGetInteger(POSITION_MAGIC); if(symbol == _Symbol){ if(!manageTradesByMagicNumber){ if(positionType == POSITION_TYPE_BUY ){ for(int j = ArraySize(tradeInfo) - 1; j >= 0; j--){ if(tradeInfo[j].ticket == ticket && tradeInfo[j].isMovedToBreakEven == false){ if(IsCrossOver(openPrice + breakEvenTriggerPoints * pointValue, closePriceMinutesData)){ // Move SL to breakeven + breakEvenLockPoints double newStopLevel = openPrice + (breakEvenLockPoints * pointValue); if(IsTradeModificationAllowed(freezeLevelPoints, ORDER_TYPE_BUY, askPrice, bidPrice, stopLevel, takeProfitLevel)){ if(!Trade.PositionModify(ticket, newStopLevel, takeProfitLevel)){ Print("Error while moving Stop Loss to breakeven! ", GetLastError()); Print(Trade.ResultComment()); } tradeInfo[j].isMovedToBreakEven = true; } } } } } if(positionType == POSITION_TYPE_SELL){ for(int j = ArraySize(tradeInfo) - 1; i >= 0; i--){ if(tradeInfo[j].ticket == ticket && tradeInfo[j].isMovedToBreakEven == false){ if(IsCrossUnder(openPrice - breakEvenTriggerPoints * pointValue, closePriceMinutesData)){ // Move SL to breakeven + breakEvenLockPoints double newStopLevel = openPrice - (breakEvenLockPoints * pointValue); if(IsTradeModificationAllowed(freezeLevelPoints, ORDER_TYPE_BUY, askPrice, bidPrice, stopLevel, takeProfitLevel)){ if(!Trade.PositionModify(ticket, newStopLevel, takeProfitLevel)){ Print("Error while moving Stop Loss to breakeven! ", GetLastError()); Print(Trade.ResultComment()); } tradeInfo[j].isMovedToBreakEven = true; } } } } } } if(manageTradesByMagicNumber){ if(magicNumber == tradeSelectionMagicNumber){ if(positionType == POSITION_TYPE_BUY ){ for(int j = ArraySize(tradeInfo) - 1; j >= 0; j--){ if(tradeInfo[j].ticket == ticket && tradeInfo[j].isMovedToBreakEven == false){ if(IsCrossOver(openPrice + breakEvenTriggerPoints * pointValue, closePriceMinutesData)){ // Move SL to breakeven + breakEvenLockPoints double newStopLevel = openPrice + (breakEvenLockPoints * pointValue); if(IsTradeModificationAllowed(freezeLevelPoints, ORDER_TYPE_BUY, askPrice, bidPrice, stopLevel, takeProfitLevel)){ if(!Trade.PositionModify(ticket, newStopLevel, takeProfitLevel)){ Print("Error while moving Stop Loss to breakeven! ", GetLastError()); Print(Trade.ResultComment()); } tradeInfo[j].isMovedToBreakEven = true; } } } } } if(positionType == POSITION_TYPE_SELL){ for(int j = ArraySize(tradeInfo) - 1; i >= 0; i--){ if(tradeInfo[j].ticket == ticket && tradeInfo[j].isMovedToBreakEven == false){ if(IsCrossUnder(openPrice - breakEvenTriggerPoints * pointValue, closePriceMinutesData)){ // Move SL to breakeven + breakEvenLockPoints double newStopLevel = openPrice - (breakEvenLockPoints * pointValue); if(IsTradeModificationAllowed(freezeLevelPoints, ORDER_TYPE_BUY, askPrice, bidPrice, stopLevel, takeProfitLevel)){ if(!Trade.PositionModify(ticket, newStopLevel, takeProfitLevel)){ Print("Error while moving Stop Loss to breakeven! ", GetLastError()); Print(Trade.ResultComment()); } tradeInfo[j].isMovedToBreakEven = true; } } } } } } } }else{ continue; } }else{ Print("Error while getting a position ticket!", GetLastError()); continue; } } }
Innerhalb der Funktion führt der EA eine Schleife durch alle offenen Positionen des Kontos. Bei jedem Handelsgeschäft wird zunächst geprüft, ob das Symbol mit dem Chart-Symbol übereinstimmt, auf dem der EA ausgeführt wird. Wenn der Nutzer die Filterung von Handelsgeschäften nach der magischen Zahl aktiviert hat, wird außerdem sichergestellt, dass nur Handelsgeschäfte, die der angegebenen magischen Zahl entsprechen, verwaltet werden.
Bei einer Kaufposition wartet der EA, bis der Marktpreis die Break-Even-Schwelle überschreitet. In diesem Fall berechnet die Funktion ein neues Stop-Loss-Niveau zum Eröffnungskurs zuzüglich der nutzerdefinierten Lock-Points, die einen kleinen Puffer über den Break-Even hinaus darstellen. Anschließend wird die Funktion IsTradeModificationAllowed aufgerufen, um sicherzustellen, dass der Broker Änderungen zulässt, bevor der Stop Loss aktualisiert wird.
Die gleiche Logik gilt für eine Verkaufsposition, allerdings in umgekehrter Richtung. Sobald der Kurs unter das Break-Even-Trigger-Level fällt, verschiebt der EA den Stop-Loss auf den Eröffnungskurs abzüglich der Pufferpunkte.
Dieses Design stellt sicher, dass die Break-Even-Funktion nur auf aktive Handelsgeschäfte reagiert, die nach dem Start des EAs eröffnet werden. Es wird vermieden, dass historische oder nicht verwandte Handelsgeschäfte berührt werden. Wenn die Änderung fehlschlägt, gibt der EA eindeutige Fehlermeldungen aus, um die Fehlersuche zu erleichtern.
Kurz gesagt, das Ziel dieser Funktion ist es, das Kapital des Händlers automatisch zu schützen, indem sichergestellt wird, dass ein Handel, sobald er sich um den festgelegten Abstand in Punkten positiv entwickelt, nicht mehr in einen Verlust umschlagen kann. Diese kleine, aber entscheidende Funktion ist einer der Eckpfeiler des AutoProtect Smart Trade Manager EA.
Trailing-Stop-Funktionalität
Die Trailing-Stop-Funktion ermöglicht es dem Expert Advisor, das Stop-Loss-Niveau automatisch zu verschieben und so schrittweise Gewinne zu sichern, wenn sich der Kurs weiter in Richtung Gewinn bewegt. Auf diese Weise können Gewinne gesichert werden, ohne dass der Händler die Positionen ständig überwachen muss. Der Trailing-Prozess erfolgt schrittweise und stellt sicher, dass gewinnbringende Handelsgeschäfte nicht vorzeitig geschlossen werden, während gleichzeitig Gewinne geschützt werden, falls der Markt sich umkehrt. Wir werden die Funktion ManageTrailingStop direkt unter der Funktion ManageBreakEvenFunction definieren.
... //+------------------------------------------------------------------+ //| To mange the trade break even functionality | //+------------------------------------------------------------------+ void ManageBreakEven (){ ... } //+------------------------------------------------------------------+ //| To manage the trade trailing stop functionality | //+------------------------------------------------------------------+ void ManageTrailingStop(){ int totalPositions = PositionsTotal(); // Loop through all open positions for(int i = totalPositions - 1; i >= 0; i--){ ulong ticket = PositionGetTicket(i); if(ticket != 0){ // Get some useful position properties ENUM_POSITION_TYPE positionType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); double openPrice = PositionGetDouble (POSITION_PRICE_OPEN); double currentPrice = PositionGetDouble (POSITION_PRICE_CURRENT); string symbol = PositionGetString (POSITION_SYMBOL); double stopLevel = PositionGetDouble (POSITION_SL); double takeProfitLevel = PositionGetDouble (POSITION_TP); ulong magicNumber = PositionGetInteger(POSITION_MAGIC); // Skip positions not matching this financial security if(symbol == _Symbol){ if(!manageTradesByMagicNumber){ if(positionType == POSITION_TYPE_BUY ){ // 07-10-2025 for(int i = ArraySize(tradeInfo) - 1; i >= 0; i--){ if(tradeInfo[i].ticket == ticket){ if(IsCrossOver(tradeInfo[i].nextTrailTriggerPrice, closePriceMinutesData)){ // Physically trail SL only when next SL level is above current SL Level if(tradeInfo[i].nextStopLevel > tradeInfo[i].currentStopLevel){ if(IsTradeModificationAllowed(freezeLevelPoints, ORDER_TYPE_BUY, askPrice, bidPrice, stopLevel, takeProfitLevel)){ if(!Trade.PositionModify(ticket, tradeInfo[i].nextStopLevel, takeProfitLevel)){ Print("Error while trailing Stop Loss! ", GetLastError()); Print(Trade.ResultComment()); Print(Trade.ResultRetcode()); } } } tradeInfo[i].currentStopLevel = tradeInfo[i].currentStopLevel + trailingStepPoints * pointValue; tradeInfo[i].nextStopLevel = tradeInfo[i].nextStopLevel + trailingStepPoints * pointValue; } } } } if(positionType == POSITION_TYPE_SELL){ // 07-10-2025 for(int i = ArraySize(tradeInfo) - 1; i >= 0; i--){ if(tradeInfo[i].ticket == ticket){ if(IsCrossUnder(tradeInfo[i].nextTrailTriggerPrice, closePriceMinutesData)){ // Physically trail SL only when next SL level is above current SL Level if(tradeInfo[i].nextStopLevel < tradeInfo[i].currentStopLevel){ if(IsTradeModificationAllowed(freezeLevelPoints, ORDER_TYPE_SELL, askPrice, bidPrice, stopLevel, takeProfitLevel)){ if(!Trade.PositionModify(ticket, tradeInfo[i].nextStopLevel, takeProfitLevel)){ Print("Error while trailing Stop Loss! ", GetLastError()); Print(Trade.ResultComment()); Print(Trade.ResultRetcode()); } } } tradeInfo[i].currentStopLevel = tradeInfo[i].currentStopLevel - trailingStepPoints * pointValue; tradeInfo[i].nextStopLevel = tradeInfo[i].nextStopLevel - trailingStepPoints * pointValue; } } } } } if( manageTradesByMagicNumber){ if(magicNumber == tradeSelectionMagicNumber){ if(positionType == POSITION_TYPE_BUY ){ // 07-10-2025 for(int i = ArraySize(tradeInfo) - 1; i >= 0; i--){ if(tradeInfo[i].ticket == ticket){ if(IsCrossOver(tradeInfo[i].nextTrailTriggerPrice, closePriceMinutesData)){ // Physically trail SL only when next SL level is above current SL Level if(tradeInfo[i].nextStopLevel > tradeInfo[i].currentStopLevel){ if(IsTradeModificationAllowed(freezeLevelPoints, ORDER_TYPE_BUY, askPrice, bidPrice, stopLevel, takeProfitLevel)){ if(!Trade.PositionModify(ticket, tradeInfo[i].nextStopLevel, takeProfitLevel)){ Print("Error while trailing Stop Loss! ", GetLastError()); Print(Trade.ResultComment()); Print(Trade.ResultRetcode()); } } } tradeInfo[i].currentStopLevel = tradeInfo[i].currentStopLevel + trailingStepPoints * pointValue; tradeInfo[i].nextStopLevel = tradeInfo[i].nextStopLevel + trailingStepPoints * pointValue; } } } } if(positionType == POSITION_TYPE_SELL){ // 07-10-2025 for(int i = ArraySize(tradeInfo) - 1; i >= 0; i--){ if(tradeInfo[i].ticket == ticket){ if(IsCrossUnder(tradeInfo[i].nextTrailTriggerPrice, closePriceMinutesData)){ // Physically trail SL only when next SL level is above current SL Level if(tradeInfo[i].nextStopLevel < tradeInfo[i].currentStopLevel){ if(IsTradeModificationAllowed(freezeLevelPoints, ORDER_TYPE_SELL, askPrice, bidPrice, stopLevel, takeProfitLevel)){ if(!Trade.PositionModify(ticket, tradeInfo[i].nextStopLevel, takeProfitLevel)){ Print("Error while trailing Stop Loss! ", GetLastError()); Print(Trade.ResultComment()); Print(Trade.ResultRetcode()); } } } tradeInfo[i].currentStopLevel = tradeInfo[i].currentStopLevel - trailingStepPoints * pointValue; tradeInfo[i].nextStopLevel = tradeInfo[i].nextStopLevel - trailingStepPoints * pointValue; } } } } }else{ continue; } } }else{ continue; } }else{ Print("Error while getting a position ticket! ", GetLastError()); continue; } } }
Die Funktion ManageTrailingStop geht alle offenen Positionen durch und prüft, ob eine von ihnen die Bedingungen für Trailing erfüllt. Für jede Position wird zunächst festgestellt, ob sie zum gleichen Chart-Symbol gehört und ob sie je nach Nutzereinstellungen mit einer bestimmten magischen Zahl verwaltet werden soll. Auf diese Weise wird sichergestellt, dass nur relevante Gewerke geändert werden.
Innerhalb der Funktion vergleicht das Programm die jüngsten Marktpreise mit Hilfe der Hilfsfunktionen IsCrossOver und IsCrossUnder. Mit diesen Kontrollen wird festgestellt, wann der Kurs bestimmte Niveaus überschritten hat, die eine Stop-Loss-Anpassung auslösen. Bei einem Kreuzen über oder unter die Niveaus berechnet der EA ein neues Stop-Loss-Niveau und verschiebt es um eine vordefinierte Schrittgröße (trailingStepPoints) in die Richtung der Position.
Bevor das Handelsgeschäft geändert wird, prüft die Funktion mit der Funktion IsTradeModificationAllowed, ob es sicher ist, dies zu tun. Dadurch werden Änderungen verhindert, wenn der Preis zu nahe an der „Freeze-Level“ liegt, was zu Maklerfehlern führen könnte. Nach der Überprüfung ändert der EA den Stop-Loss mit der Methode Trade.PositionModify und protokolliert alle Fehler, die während des Prozesses auftreten können.
Jedes Mal, wenn die Trailing-Bedingung erfüllt ist, aktualisiert der EA die in der Struktur „tradeInfo“ gespeicherten Daten des Handels. Er erhöht sowohl den currentStopLevel als auch den nextStopLevel, damit das Trailing in Zukunft reibungslos verläuft. Die Funktion funktioniert sowohl für Kauf- als auch für Verkaufspositionen und wendet dieselbe Logik an, jedoch in entgegengesetzter Richtung.
Mit dieser Funktion müssen Händler nicht mehr manuell Stop-Losses nachziehen. Der EA passt diese automatisch an und lässt profitable Handelsgeschäfte laufen, während er das Risiko minimiert.
Funktionsweise zum Schließen von Teilgewinnen
Unmittelbar unter der Funktion ManageTrailingStop fügen wir die Funktion ManagePartialClose hinzu.
... //+------------------------------------------------------------------+ //| To manage the trade trailing stop functionality | //+------------------------------------------------------------------+ void ManageTrailingStop(){ ... } //+------------------------------------------------------------------+ //| To manage the trade partial close functionality | //+------------------------------------------------------------------+ void ManagePartialClose(){ int totalPositions = PositionsTotal(); for(int i = totalPositions - 1; i >= 0; i--){ ulong ticket = PositionGetTicket(i); if(ticket != 0){ // Get some useful position properties ENUM_POSITION_TYPE positionType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); double openPrice = PositionGetDouble (POSITION_PRICE_OPEN); double currentPrice = PositionGetDouble (POSITION_PRICE_CURRENT); string symbol = PositionGetString (POSITION_SYMBOL); ulong magicNumber = PositionGetInteger(POSITION_MAGIC); double positionVolume = PositionGetDouble (POSITION_VOLUME); if(symbol == _Symbol){ double volumeToDecrease = NormalizeDouble((partialClosePercent / 100.0) * positionVolume, 2); if(!manageTradesByMagicNumber){ if(positionType == POSITION_TYPE_BUY ){ for(int j = ArraySize(tradeInfo) - 1; j >= 0; j--){ if(tradeInfo[j].ticket == ticket && tradeInfo[j].isPartialProfitsSecured == false){ if(IsCrossOver(openPrice + partialCloseTriggerPoints * pointValue, closePriceMinutesData)){ if(!Trade.PositionClosePartial(ticket, volumeToDecrease)){ Print("Error closing partial volume! ", GetLastError()); Print(Trade.ResultComment()); } tradeInfo[j].isPartialProfitsSecured = true; } } } } if(positionType == POSITION_TYPE_SELL){ for(int j = ArraySize(tradeInfo) - 1; j >= 0; j--){ if(tradeInfo[j].ticket == ticket && tradeInfo[j].isPartialProfitsSecured == false){ if(IsCrossOver(openPrice - partialCloseTriggerPoints * pointValue, closePriceMinutesData)){ if(!Trade.PositionClosePartial(ticket, volumeToDecrease)){ Print("Error closing partial volume! ", GetLastError()); Print(Trade.ResultComment()); } tradeInfo[j].isPartialProfitsSecured = true; } } } } } if( manageTradesByMagicNumber){ if(magicNumber == tradeSelectionMagicNumber){ if(positionType == POSITION_TYPE_BUY ){ for(int j = ArraySize(tradeInfo) - 1; j >= 0; j--){ if(tradeInfo[j].ticket == ticket && tradeInfo[j].isPartialProfitsSecured == false){ if(IsCrossOver(openPrice + partialCloseTriggerPoints * pointValue, closePriceMinutesData)){ if(!Trade.PositionClosePartial(ticket, volumeToDecrease)){ Print("Error closing partial volume! ", GetLastError()); Print(Trade.ResultComment()); } tradeInfo[j].isPartialProfitsSecured = true; } } } } if(positionType == POSITION_TYPE_SELL){ for(int j = ArraySize(tradeInfo) - 1; j >= 0; j--){ if(tradeInfo[j].ticket == ticket && tradeInfo[j].isPartialProfitsSecured == false){ if(IsCrossOver(openPrice - partialCloseTriggerPoints * pointValue, closePriceMinutesData)){ if(!Trade.PositionClosePartial(ticket, volumeToDecrease)){ Print("Error closing partial volume! ", GetLastError()); Print(Trade.ResultComment()); } tradeInfo[j].isPartialProfitsSecured = true; } } } } } } } }else{ Print("Error while getting a position ticket!", GetLastError()); continue; } } }
Diese Funktion regelt die teilweise Schließung von offenen Handelsgeschäften, sobald diese ein bestimmtes Gewinnniveau erreicht haben. So kann der Expert Advisor einen Teil des Gewinns sichern, während er die verbleibende Position offen hält, um weitere Gewinne zu erzielen.
Die Funktion beginnt mit einer Schleife durch alle offenen Positionen. Für jede Position werden nützliche Informationen wie die Art der Position (Kauf oder Verkauf), der offene Preis, der aktuelle Preis, das Symbol, die magische Zahl und das Handelsvolumen abgerufen.
Anschließend wird geprüft, ob die Position zum aktuellen Chart-Symbol gehört. Wenn die Handelsverwaltung nach der magischen Zahl gefiltert wird, wird auch überprüft, ob die Position mit der angegebenen magischen Zahl übereinstimmt, bevor sie fortgesetzt wird.
Die Funktion berechnet das Volumen bis zum Abschluss, indem sie den Prozentsatz des teilweisen Abschlusses auf das aktuelle Handelsvolumen anwendet. Sobald sich der Preis um die angegebene Triggerdistanz (in Punkten) in den Gewinn bewegt, schließt der EA den berechneten Teil des Handels mit der Methode CTrade.PositionClosePartial.
Bei Kaufpositionen wird der Teilschluss ausgelöst, wenn der aktuelle Kurs um den festgelegten Schwellenwert über den offenen Kurs steigt. Bei Verkaufspositionen wird er ausgelöst, wenn der aktuelle Kurs um die gleiche Distanz unter den offenen Kurs fällt.
Tritt während des partiellen Schließvorgangs ein Fehler auf, werden eine Fehlermeldung und der Grund für das Scheitern zur Fehlersuche in das Journal gedruckt.
Tests und Validierung
Nachdem die Entwicklung unseres Trade Manager Expert Advisors abgeschlossen ist, besteht der nächste Schritt darin, seine Funktionen zu testen. Dieser Prozess hilft zu bestätigen, dass sich jede Funktion unter realen Handelsbedingungen wie erwartet verhält. Jede Funktion, wie z. B. Break-Even, Trailing Stop und Partial Close, sollte separat getestet werden, um eine ordnungsgemäße Funktionsweise sicherzustellen.
Um dies zu erreichen, werden wir einen einfachen Test-EA verwenden, der automatisch Handelsgeschäfte eröffnet, sobald er gestartet wird. Wir werden dann eine Funktion nach der anderen aktivieren und ihre Auswirkungen auf den Handel beobachten, wobei wir Screenshots und Notizen als Referenz aufzeichnen. Dieser Ansatz bietet eine klare und zuverlässige Möglichkeit, um zu überprüfen, ob alle Funktionen in unserem EA korrekt funktionieren.
Um unsere Schutzfunktionen effektiv zu testen, werden wir eine neue Expert Advisor-Datei mit dem Namen AutoProtect.mq5 erstellen. Diese Datei öffnet automatisch einen Handel, wenn sie gestartet wird, und ermöglicht es uns zu beobachten, wie sich jedes Merkmal in einer kontrollierten Umgebung verhält. Nachstehend finden Sie eine Erläuterung der zusätzlichen Funktionen und des Codes, die in diesem Test-EA verwendet werden.
1. Prüfen auf aktive PositionenWir beginnen mit der Definition der folgenden zwei Funktionen direkt unter unseren anderen bestehenden Nutzenfunktionen: IsThereAnActiveBuyPosition und IsThereAnActiveSellPosition.
... //+------------------------------------------------------------------+ //| To manage the trade partial close functionality | //+------------------------------------------------------------------+ void ManagePartialClose(){ ... } //+------------------------------------------------------------------+ //| To check if there is an active buy position opened by this EA | //+------------------------------------------------------------------+ bool IsThereAnActiveBuyPosition(ulong magicNm){ for(int i = PositionsTotal() - 1; i >= 0; i--){ ulong ticket = PositionGetTicket(i); if(ticket == 0){ Print("Error while fetching position ticket ", _LastError); continue; }else{ if(PositionGetInteger(POSITION_MAGIC) == magicNm && PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY){ return true; } } } return false; } //+------------------------------------------------------------------+ //| To check if there is an active sell position opened by this EA | //+------------------------------------------------------------------+ bool IsThereAnActiveSellPosition(ulong mgcNumber){ for(int i = PositionsTotal() - 1; i >= 0; i--){ ulong ticket = PositionGetTicket(i); if(ticket == 0){ Print("Error while fetching position ticket ", _LastError); continue; }else{ if(PositionGetInteger(POSITION_MAGIC) == mgcNumber && PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL){ return true; } } } return false; } ...
Diese Funktionen werden verwendet, um zu prüfen, ob es eine aktive Kauf- oder Verkaufsposition gibt, die von unserem EA eröffnet wurde. Jede Funktion durchläuft alle offenen Positionen des Kontos mit PositionsTotal und PositionGetTicket. Er vergleicht dann die magische Zahl und den Positionstyp, um festzustellen, ob die Position zu unserem EA gehört. Wird eine übereinstimmende Position gefunden, gibt die Funktion true zurück; andernfalls gibt sie false zurück. Dadurch wird sichergestellt, dass der EA nicht unnötigerweise mehrere Handelsgeschäfte desselben Typs eröffnet.
2. Einmalige Ausführung von Handelsgeschäften zulassen
Als Nächstes führen wir eine neue globale Variable ein.
bool isNewTradeAllowed; Diese Variable wird als Flag verwendet, um zu kontrollieren, ob ein neuer Handel eröffnet werden kann. Innerhalb der OnInit-Funktion wird sie wie folgt initialisiert:
isNewTradeAllowed = true; Das bedeutet, dass der EA beim Start nur einen Handel eröffnen darf. Nach der Eröffnung des ersten Handelsgeschäfts wird das Flag auf „false“ gesetzt, sodass weitere Handelsgeschäfte nicht mehr automatisch eröffnet werden können.
3. Automatisches Eröffnen eines Testgeschäfts
In der Funktion OnTick fügen wir eine Logik hinzu, um sofort nach dem Start eine Kaufposition zu eröffnen.
... //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick(){ ... //--- Get some minutes data if(CopyClose(_Symbol, PERIOD_M1, 0, 7, closePriceMinutesData) == -1){ Print("Error while copying minutes datas ", GetLastError()); return; } //--- Open a long position immediately after launch if(isNewTradeAllowed){ if(!IsThereAnActiveBuyPosition(tradeSelectionMagicNumber) && !IsThereAnActiveSellPosition(tradeSelectionMagicNumber)){ Trade.Buy(1.0, _Symbol, askPrice, askPrice - 400 * pointValue, askPrice + 800 * pointValue); isNewTradeAllowed = false; } } } ...
Hier prüft der EA, ob es für die angegebene magische Zahl keine bestehenden Handelsgeschäfte gibt. Wenn keine offenen Positionen gefunden werden, wird ein Kaufgeschäft mit vordefinierten Stop-Loss- und Take-Profit-Levels eröffnet. Sobald der Handel eröffnet ist, wird isNewTradeAllowed auf false gesetzt, um weitere Handelsgeschäfte zu verhindern.
4. Aufrufen der Schutzfunktionen
Sowohl AutoProtect.mq5 als auch AutoProtectTest.mq5 rufen die wichtigsten Schutzfunktionen wie unten dargestellt auf:
... //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick(){ ... //--- Open a long position immediately after launch if(isNewTradeAllowed){ ... } if(enableBreakEven){ ManageBreakEven(); } if(enableTrailingStop){ ManageTrailingStop(); } if(enablePartialClose){ ManagePartialClose(); } } ...
Jede Bedingung prüft, ob eine bestimmte Funktion aktiviert ist. Ist dies der Fall, wird die entsprechende Funktion ausgeführt. Auf diese Weise können wir jeweils ein Merkmal testen, indem wir einfach den entsprechenden Eingabeparameter in den EA-Einstellungen umschalten.
5. Konfigurieren des Charterscheinungsbildes
Um unsere Testumgebung übersichtlicher zu gestalten, definieren wir eine Hilfsfunktion namens ConfigureChartAppearance.
... //+------------------------------------------------------------------+ //| To check if there is an active sell position opened by this EA | //+------------------------------------------------------------------+ bool IsThereAnActiveSellPosition(ulong mgcNumber){ ... } //+------------------------------------------------------------------+ //| This function configures the chart's appearance | //+------------------------------------------------------------------+ bool ConfigureChartAppearance(){ if(!ChartSetInteger(0, CHART_COLOR_BACKGROUND, clrWhiteSmoke)){ Print("Error while setting chart background, ", GetLastError()); return false; } if(!ChartSetInteger(0, CHART_SHOW_GRID, false)){ Print("Error while setting chart grid, ", GetLastError()); return false; } if(!ChartSetInteger(0, CHART_MODE, CHART_CANDLES)){ Print("Error while setting chart mode, ", GetLastError()); return false; } if(!ChartSetInteger(0, CHART_COLOR_CANDLE_BULL, clrGreen)){ Print("Error while setting bullish candles color, ", GetLastError()); return false; } if(!ChartSetInteger(0, CHART_COLOR_CANDLE_BEAR, clrDarkRed)){ Print("Error while setting bearish candles color, ", GetLastError()); return false; } if(!ChartSetInteger(0, CHART_COLOR_CHART_UP, clrGreen)){ Print("Error while setting bearish candles color, ", GetLastError()); return false; } if(!ChartSetInteger(0, CHART_COLOR_CHART_DOWN, clrDarkRed)){ Print("Error while setting bearish candles color, ", GetLastError()); return false; } if(!ChartSetInteger(0, CHART_COLOR_BID, clrDarkRed)){ Print("Error while setting chart bid line, ", GetLastError()); return false; } if(!ChartSetInteger(0, CHART_COLOR_ASK, clrGreen)){ Print("Error while setting chart ask line, ", GetLastError()); return false; } if(!ChartSetInteger(0, CHART_SHOW_ONE_CLICK, true)){ Print("Error while setting one click buttons, ", GetLastError()); return false; } if(!ChartSetInteger(0, CHART_COLOR_STOP_LEVEL, clrDarkBlue)){ Print("Error while setting stop levels, ", GetLastError()); return false; } if(!ChartSetInteger(0, CHART_COLOR_FOREGROUND, clrBlack)){ Print("Error while setting chart foreground, ", GetLastError()); return false; } return true; } //+------------------------------------------------------------------+
Mit dieser Funktion können Sie das Aussehen des Charts zur besseren Sichtbarkeit während der Prüfung anpassen. Er passt Elemente wie den Charthintergrund, die Kerzenfarben, die Gitterlinien und die Stop-Level-Linien an.
Zum Beispiel:
- Die Hintergrundfarbe wird auf clrWhiteSmoke gesetzt.
- Abwärtskerze sind grün gefärbt, während Abwärtskerze dunkelrot sind.
- Die Bid- und Ask-Linien sind zur leichteren Identifizierung auch farbig.
Jede Einstellung wird mit der MQL5-Funktion ChartSetInteger vorgenommen. Wenn ein Fehler auftritt, gibt die Funktion eine Fehlermeldung aus und gibt false zurück. Wir rufen diese Funktion dann innerhalb von OnInit auf.
... //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit(){ if(!ConfigureChartAppearance()){ Print("Error while configuring the chart's appearance, ", GetLastError()); return(INIT_FAILED); } ... } ...
Dadurch wird sichergestellt, dass das Chart korrekt konfiguriert ist, bevor der EA gestartet wird. Wenn die Konfiguration fehlschlägt, hält der EA die Initialisierung an, um visuelle Verwirrung zu vermeiden.
Dieses Setup bietet eine saubere und automatisierte Umgebung für das Testen jeder Funktion. Es stellt sicher, dass unsere Tests wiederholbar, kontrolliert und einfach zu beobachten sind, insbesondere bei der Überprüfung des Verhaltens der Break-Even-, Trailing-Stop- und Partial-Close-Funktionalitäten.
Wir beginnen diesen Prozess, indem wir die Break-Even-Funktionalität testen, um zu bestätigen, dass sie den Stop-Loss auf ein risikofreies Niveau anpasst, sobald sich der Handel im Gewinn bewegt.
Wir werden die Funktion sowohl des Trailing-Stop als auch des teilweisen Schließens deaktivieren, sodass nur die Break-Even-Funktion bleibt aktiviert. Als Nächstes setzen wir die Eingabevariable breakEvenTriggerPoints auf 200 und starten den EA auf dem EURUSD-Chart, um sein Verhalten zu beobachten.

Unmittelbar nach dem Start des EA werden Sie feststellen, dass er eine neue Kaufposition mit einem Stop-Loss, der 400 Punkte unter und einem Take-Profit, der 800 Punkte über dem Einstiegskurs liegt, eröffnet.

Sobald sich die Position um 200 Punkte in den Gewinn bewegt, werden Sie feststellen, dass der Stop-Loss automatisch auf das Break-Even-Niveau angepasst wurde.

Als Nächstes werden wir die Trailing-Stop-Funktion testen. Wir werden alle anderen Funktionen deaktivieren und nur den Trailing-Stop in den Eingabeparametern aktivieren. Danach werden wir den EA auf dem Chart starten und beobachten, wie der Stop-Loss angepasst wird, wenn der Kurs weiter in den Gewinn läuft.

Wir setzen die trailingStartPoints auf 50 und die trailingStepPoints auf 20. Als Nächstes werden wir den EA auf dem EURUSD-Chart starten und sein Verhalten beobachten. Unmittelbar nach dem Start werden Sie feststellen, dass der EA eine Kaufposition mit einem Stop-Loss, der 400 Punkte unter dem Einstiegskurs liegt, und einem Take-Profit, der 800 Punkte darüber liegt, eröffnet.

In diesem Fall wurde der Trailing-Stop von 1,05945 auf 1,05985 gesetzt.

Anschließend wurde die Stop-Level von 1,05985 auf 1,06025 angepasst.

Auch hier wurde der Trailing-Stop von 1,0625 auf 1,06165 angepasst.

Dies zeigt deutlich, dass die Trailing-Stop-Funktion wie erwartet funktioniert. Abschließend wollen wir die Funktion des teilweisen Schließens testen. Wir werden alle anderen Funktionen deaktivieren und nur die Funktion „Teilweise schließen“ aktivieren. Setzen Sie PartialTriggerPoints auf 200 und PartialClosePercent auf 50,0. Das bedeutet, dass 50 % des Positionsvolumens geschlossen werden, sobald sich der Preis um 200 Punkte in den Gewinn bewegt.

Starten wir nun den EA erneut auf dem EURUSD-Chart und beobachten wir sein Verhalten. Auch hier werden Sie feststellen, dass der EA sofort nach dem Start eine Kaufposition eröffnet. Diese Position hat einen Stop-Loss, der 400 Punkte unter dem Einstiegskurs liegt, und einen Take-Profit, der 800 Punkte über dem Einstiegskurs liegt. Beachten Sie auch, dass die Position mit einem Volumen von 1 Standardlot eröffnet wurde.

Nachdem sich die Position um 200 Punkte in den Gewinn bewegt hat, werden Sie feststellen, dass 50 % des anfänglichen Handelsvolumens geschlossen worden sind. Dies bestätigt, dass unsere Teilabschlussfunktionalität wie erwartet funktioniert, und markiert den erfolgreichen Abschluss unserer Test- und Validierungsphase.
Schlussfolgerung
Die meisten Händler beginnen mit der manuellen Verwaltung von Handelsgeschäften – sie beobachten Charts, passen Stop-Losses an und nehmen Gewinne intuitiv mit. Dies führt häufig zu emotionalen Fehlern und verpassten Chancen.In diesem Artikel haben wir einen Smart Trade Manager EA entwickelt, der diese Aufgaben automatisiert: Verschieben von Stop-Loss auf Break-Even, Trailing-Profits und Schließen von Teilpositionen. Jede Funktion wurde getestet und hat sich unter Echtzeitbedingungen als zuverlässig erwiesen.
Die Automatisierung bringt nicht nur Bequemlichkeit, sondern auch Disziplin und Beständigkeit. Mit AutoProtect EA können Händler ihr Kapital schützen, Gewinne sichern und Positionen effizient verwalten – und so den Handel von einer emotionalen Reaktion zu einem strukturierten, regelbasierten Prozess machen.
Übersetzt aus dem Englischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/en/articles/19911
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.
Entwicklung des Price Action Analysis Toolkit (Teil 48): Multi-Timeframe Harmony Index mit gewichtetem Bias Dashboard
Automatisieren von Handelsstrategien in MQL5 (Teil 37): Regelmäßige RSI-Divergenz-Konvergenz mit visuellen Indikatoren
Automatisieren von Handelsstrategien in MQL5 (Teil 38): Versteckter RSI-Divergenzhandel mit Steigungswinkel-Filtern
Von der Grundstufe bis zur Mittelstufe: Struct (VI)
- 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.