Automatisieren von Handelsstrategien in MQL5 (Teil 40): Fibonacci-Retracement-Handel mit nutzerdefinierten Levels
Einführung
In unserem vorherigen Artikel (Teil 39) haben wir ein statistisches System der Rückkehr zum Mittelwert in MetaQuotes Language 5 (MQL5) entwickelt, das Preisdaten auf Momente wie Mittelwert, Varianz, Schiefe, Kurtosis und den Jarque-Bera Test analysiert, Reversionssignale auf der Basis von Konfidenzintervallen mit adaptiven Schwellenwerten und höherer Zeitrahmenbestätigung generiert, Handelsgeschäfte mit aktienbasiertem Sizing, Trailing Stops, Partial Closes und zeitbasierten Exits verwaltet und gleichzeitig ein On-Chart-Dashboard zur Echtzeitüberwachung bereitstellt. In Teil 40 entwickeln wir ein Fibonacci Retracement-Handelssystem mit nutzerdefinierten Levels.
Dieses System berechnet Retracement-Levels entweder anhand von Tageskerzenbereichen oder Lookback-Arrays, identifiziert Auf- oder Abwärts-Setups auf der Basis des Schlusskurses im Vergleich zum Eröffnungskurs, löst Einstiege beim Überschreiten bestimmter Levels wie 50% oder 61,8% mit maximalen Handelslimits aus, beinhaltet optionale Schließungen bei neuen Fib-Berechnungen, punktbasierte Trailing-Stops nach einem Gewinnschwellenwert und SL/TP-Puffer als Range-Prozentsätze. Wir werden die folgenden Themen behandeln:
Am Ende haben wir eine funktionierende MQL5-Strategie für den Handel mit Fibonacci-Retracements, die Sie anpassen können – los geht's!
Die Fibonacci-Retracement-Strategie verstehen
Die Fibonacci Rretracement-Strategy identifiziert potenzielle Unterstützungs- und Widerstandsniveaus, indem sie die aus der Fibonacci Sequenz abgeleiteten Schlüsselkennzahlen auf einen früheren Kursschwung anwendet, um Händlern zu helfen, Rücksetzer in Trendmärkten zu antizipieren, bei denen es wahrscheinlich ist, dass der Kurs sich umkehrt oder fortsetzt, nachdem er einen Teil der Bewegung zurückgegangen ist.
Bei einem Aufwärts-Setup, nach einem Aufwärtsschwung von einem Tief zu einem Hoch, fungieren Retracement-Levels wie 50% oder 61,8% als potenzielle Kaufzonen während Pullbacks, in Erwartung einer Rückkehr nach oben; bei einem Abwärts-Setup, nach einem Abwärtsschwung von einem Hoch zu einem Tief, dienen diese Levels als Verkaufszonen während Aufwärtskorrekturen, in Erwartung einer Wiederaufnahme des Abwärtstrends.
Wir verbessern die Einstiegsmöglichkeiten, indem wir die Überkreuzungen auf diesen Niveaus bestätigen, Puffer auf die Handelsniveaus anwenden, die auf der Swing-Range basieren, um das Risiko anzupassen, den Handel pro Niveau begrenzen, um ein übermäßiges Engagement zu vermeiden, und Trailing-Stops einbauen, um die Gewinne zu schützen, wenn sich der Preis günstig entwickelt, während wir optional die Positionen bei neuen Fib-Berechnungen für neue Setups schließen. Durch die Kombination dieser Elemente können wir Umkehrpunkte innerhalb von Trends anvisieren. Schauen Sie sich unten ein Beispiel für ein Abwärts-Retracement-Setup an, das wir haben könnten.

Unser Plan ist es, Fib-Level mit Tageskerzen oder Lookback Arrays zu berechnen, die wir mit jeder Ihrer designierten Strategie wechseln können, bestimmen Auf- bzw. Abwärts-Richtung von schließen vs. öffnen, Trigger Einträge auf Kreuzungen von nutzerdefinierten Verhältnisse wie 50% oder 61.8%, oder jedes andere, das Sie wollen, dies sind die willkürlichen Niveaus, die wir für die bedeutendsten und häufigsten hielten; mit maximalen Handelslimits, SL/TP mit optionalen Range-basierten Puffern einstellen, Punkte-Trailing nach einer Gewinnschwelle aktivieren, auf neuen Fibs schließen, wenn gewählt, und mit farbigen Objekten und Info-Labels visualisieren, ein flexibles System für den Retracement-Handel aufbauen.
Implementation in MQL5
Um das Programm in MQL5 zu erstellen, öffnen wir den MetaEditor, gehen zum Navigator, suchen den Ordner Experts, klicken auf die Registerkarte „Neu“ und folgen den Anweisungen, um die Datei zu erstellen. Sobald das erstellt ist, müssen wir in der Programmierumgebung einige Eingabeparameter und globale Variablen deklarieren, die wir im gesamten Programm verwenden werden.
//+------------------------------------------------------------------+ //| Fibonacci Retracement Ratios.mq5 | //| Copyright 2025, Allan Munene Mutiiria. | //| https://t.me/Forex_Algo_Trader | //+------------------------------------------------------------------+ #property copyright "Copyright 2025, Allan Munene Mutiiria." #property link "https://t.me/Forex_Algo_Trader" #property version "1.00" #property strict #include <Trade\Trade.mqh> // For trade execution //+------------------------------------------------------------------+ //| Enums | //+------------------------------------------------------------------+ enum CloseOnNewEnum { // Define enum for closing on new Fibonacci CloseOnNew_No = 0, // No CloseOnNew_Yes = 1 // Yes }; enum TrailingTypeEnum { // Define enum for trailing stop types Trailing_None = 0, // None Trailing_Points = 2 // By Points }; //+------------------------------------------------------------------+ //| Input Parameters | //+------------------------------------------------------------------+ input bool UseDailyApproach = true; // Use daily candle (true) or array (false) input string fibLevelsStr = "50,61.8"; // Comma-separated Fib levels for entry (e.g., 50,61.8) input int maxTradesPerLevel = 1; // Max trades per level per Fib period (0=unlimited) input CloseOnNewEnum CloseOnNewFib = CloseOnNew_No; // Close trades on new Fib calc input TrailingTypeEnum TrailingType = Trailing_None; // Trailing Stop Type input double Trailing_Stop_Pips = 30.0; // Trailing Stop in Pips (for Points type) input double Min_Profit_To_Trail_Pips = 50.0; // Min Profit to Start Trailing in Pips input int LookbackSize = 100; // Number of candles for array approach input double LotSize = 0.1; // Trade lot size input int MagicNumber = 12345; // Magic number for trades input bool IncludeCurrentBar = false; // Include current bar in array calcs for updates input double SlBufferPercent = 0.0; // SL buffer percent of range (0=no buffer) input double TpBufferPercent = 0.0; // TP buffer percent of range (0=no buffer)
Wir beginnen mit der Einbindung der Bibliothek „Trade“ mit „#include <Trade\Trade.mqh>“, um die Funktionen zur Auftragsausführung und Positionsverwaltung zu aktivieren. Wir definieren zwei Enumerationen für Nutzeroptionen: „CloseOnNewEnum“ mit den Werten „CloseOnNew_No“, um Handelsgeschäfte bei neuen Fib-Berechnungen nicht zu schließen, und „CloseOnNew_Yes“, um dies zu tun, und „TrailingTypeEnum“ mit „Trailing_None“, um das Trailing zu deaktivieren, oder „Trailing_Points“ für eine punktbasierte Anpassung.
Als Nächstes richten wir die Eingabeparameter für die Anpassung ein. „UseDailyApproach“ ist standardmäßig true für Bereiche der Tageskerzen oder false für array-basierte Lookbacks, „fibLevelsStr“ als „50,61.8“ erlaubt kommagetrennte Retracement-Levels für Eingaben, „maxTradesPerLevel“ bei 1 begrenzt Handelsgeschäfte pro Level pro Periode (0 für unbegrenzt), „CloseOnNewFib“ verwendet das Enum, um Schließungen bei Recalcs zu entscheiden, und „TrailingType“ wählt den Trailing-Modus.
Für Trailing-Details legt „Trailing_Stop_Pips“ mit 30,0 den Abstand fest, „Min_Profit_To_Trail_Pips“ mit 50,0 die Gewinnschwelle für den Start. „LookbackSize“ mit 100 definiert Kerzen für den Array-Modus, „LotSize“ mit 0.1 für die Positionsgröße, „MagicNumber“ als 12345, um Handelsgeschäfte zu identifizieren, „IncludeCurrentBar“ als false, um optional den sich bildenden Balken in die Berechnungen einzubeziehen, und „SlBufferPercent“ sowie „TpBufferPercent“ beide auf 0,0 für bereichsbasierte Anpassungen an Stops und Gewinne (höhere Werte fügen Puffer hinzu). Wenn wir kompilieren, erhalten wir diesen Satz von Eingaben.

Mit den Eingaben können wir nun einige globale Variablen erstellen, die wir im gesamten Programm verwenden werden.
//+------------------------------------------------------------------+ //| Global Variables | //+------------------------------------------------------------------+ CTrade obj_Trade; //--- Trade object int barsTotal; //--- For daily approach #define FIB_OBJ "Fibonacci Retracement" //--- Define Fibonacci object name // Persistent variables for both approaches static double storedEntryLvls[]; //--- Array of entry levels static int storedTradesCount[]; //--- Trades count per level static double storedSl = 0.0; //--- Stored stop loss static double storedTp = 0.0; //--- Stored take profit static string storedInfo = ""; //--- Stored information string static bool storedIsBullish = false; //--- Stored bullish flag static double fibLevels[]; //--- Parsed Fibonacci levels (original order) static string lastShownInfo = ""; //--- To detect changes and avoid unnecessary updates // For array approach static bool fibCalculated = false; //--- Fibonacci calculated flag static double currentHigh = 0.0; //--- Current high static double currentLow = 0.0; //--- Current low static string fibName = "Fib_Array"; //--- Fibonacci name for array
Als Nächstes deklarieren wir globale Variablen, beginnend mit „obj_Trade“ als Instanz von CTrade für die Bearbeitung von Aufträgen, „barsTotal“, um die täglichen Balken im täglichen Ansatz zu verfolgen, und definieren „FIB_OBJ“ als „Fibonacci Retracement“ für den Namen des Haupt-Fib-Objekts. Für die Persistenz über tägliche und Array-Methoden hinweg verwenden wir statische Arrays wie „storedEntryLvls[]“, um berechnete Einstiegspreise zu speichern, „storedTradesCount[]“ für die Anzahl der Handelsgeschäfte pro Ebene, die auf 0 initialisierten Doppelwerte „storedSl“ und „storedTp“.0 für Stop-Loss und Take-Profit, „storedInfo“ als leere Zeichenkette für den Anzeigetext, „storedIsBullish“ als false, um die Richtung zu kennzeichnen, „fibLevels[]“ für geparste Ratios und „lastShownInfo“ als leer, um Infoänderungen zu erkennen und Redraws zu minimieren.
Speziell für den Array-Ansatz beginnt der statische Wert „fibCalculated“ mit „false“, um anzuzeigen, ob Levels gesetzt sind, „currentHigh“ und „currentLow“ mit 0,0, um die Extremwerte für Überschreitungsprüfungen zu speichern, und „fibName“ als „Fib_Array“ für den Objektidentifikator. Damit sind wir bereit, mit der Implementierungslogik zu beginnen. Wir beginnen mit OnInit, um die Logik zu initialisieren.
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { obj_Trade.SetExpertMagicNumber(MagicNumber); //--- Set magic number for trade object // Force initial calculation for daily approach barsTotal = 0; //--- Ensure first tick updates // Parse fibLevelsStr into fibLevels array (from MQL5 docs: StringSplit) string tempLevels[]; //--- Temporary levels array ushort commaSep = StringGetCharacter(",", 0); //--- Get comma separator int numLevels = StringSplit(fibLevelsStr, commaSep, tempLevels); //--- Split string into levels ArrayResize(fibLevels, numLevels); //--- Resize fibLevels array for (int i = 0; i < numLevels; i++) { //--- Iterate through levels fibLevels[i] = StringToDouble(tempLevels[i]); //--- Convert to double } ArrayResize(storedEntryLvls, numLevels); //--- Resize storedEntryLvls ArrayResize(storedTradesCount, numLevels); //--- Resize storedTradesCount // Clean up old labels ObjectsDeleteAll(0, "InfoLabel_", -1, OBJ_LABEL); //--- Delete all info labels lastShownInfo = ""; //--- Reset last shown info // Clean up old Fib object for array ObjectDelete(0, fibName); //--- Delete Fibonacci object fibCalculated = false; //--- Reset calculated flag return(INIT_SUCCEEDED); //--- Return success }
In OnInit konfigurieren wir das Handelsobjekt, indem wir „obj_Trade.SetExpertMagicNumber“ mit „MagicNumber“ aufrufen, um unsere Aufträge zu identifizieren. Für den täglichen Ansatz setzen wir „barsTotal“ auf 0, um eine erste Aktualisierung beim ersten Tick zu gewährleisten. Wir parsen „fibLevelsStr“ in das Array „fibLevels“, indem wir die Zeichenkette mit StringSplit und StringGetCharacter als Trennzeichen an Kommas aufteilen, temporäre Zeichenketten in „tempLevels“ speichern, die Größe von „fibLevels“ an die Anzahl anpassen und jede Zeichenkette in einer Schleife mit StringToDouble in ein Double konvertieren. Anschließend wird die Größe von „storedEntryLvls“ und „storedTradesCount“ an die Anzahl der zu verfolgenden Ebenen angepasst.
Um aufzuräumen, löschen wir alle Info-Labels mit ObjectsDeleteAll unter Angabe des Präfixes „InfoLabel_“ und des Typs OBJ_LABEL, setzen „lastShownInfo“ auf leer, entfernen alle alten Fib-Objekte namens „fibName“ mit ObjectDelete und setzen „fibCalculated“ auf false. Schließlich geben wir INIT_SUCCEEDED zurück, um die erfolgreiche Einrichtung anzuzeigen. Wir können nun zur Ereignisbehandlung der Ticks übergehen und unsere erste Logik definieren, um alles auf den Weg zu bringen. Wir werden den Ansatz der täglichen Signale verwenden.
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { if (UseDailyApproach) { //--- Check daily approach // Daily approach logic remains the same int bars = iBars(_Symbol, PERIOD_D1); //--- Get daily bars if (barsTotal != bars && TimeCurrent() > StringToTime("00:05")) { //--- Check new bar barsTotal = bars; //--- Update bars total ObjectDelete(0, FIB_OBJ); //--- Delete Fib object double openPrice = iOpen(_Symbol, PERIOD_D1, 1); //--- Get open price double closePrice = iClose(_Symbol, PERIOD_D1, 1); //--- Get close price double high = iHigh(_Symbol, PERIOD_D1, 1); //--- Get high double low = iLow(_Symbol, PERIOD_D1, 1); //--- Get low datetime startingTime = iTime(_Symbol, PERIOD_D1, 1); //--- Get start time datetime endingTime = iTime(_Symbol, PERIOD_D1, 0) - 1; //--- Get end time double range = high - low; //--- Calc range storedIsBullish = (closePrice > openPrice); //--- Set bullish flag string levelsList = ""; //--- Init levels list for (int i = 0; i < ArraySize(fibLevels); i++) { //--- Iterate levels storedTradesCount[i] = 0; //--- Reset count if (storedIsBullish) { //--- Check bullish storedEntryLvls[i] = NormalizeDouble(high - range * fibLevels[i] / 100, _Digits); //--- Calc entry } else { //--- Handle bearish storedEntryLvls[i] = NormalizeDouble(low + range * fibLevels[i] / 100, _Digits); //--- Calc entry } levelsList += DoubleToString(fibLevels[i], 1) + ": " + DoubleToString(storedEntryLvls[i], _Digits) + "\n"; //--- Add to list } if (storedIsBullish) { //--- Check bullish // Bullish: Fibo from low to high for correct 0% at high, 100% at low, green ObjectCreate(0, FIB_OBJ, OBJ_FIBO, 0, startingTime, low, endingTime, high); //--- Create Fib ObjectSetInteger(0, FIB_OBJ, OBJPROP_COLOR, clrGreen); //--- Set color for (int i = 0; i < ObjectGetInteger(0, FIB_OBJ, OBJPROP_LEVELS); i++) { //--- Iterate levels ObjectSetInteger(0, FIB_OBJ, OBJPROP_LEVELCOLOR, i, clrGreen); //--- Set level color } storedSl = NormalizeDouble(low - range * (SlBufferPercent / 100), _Digits); //--- Calc SL storedTp = NormalizeDouble(high + range * (TpBufferPercent / 100), _Digits); //--- Calc TP storedInfo = "Daily Approach - Bullish\n" + //--- Set info "Open: " + DoubleToString(openPrice, _Digits) + "\n" + "Close: " + DoubleToString(closePrice, _Digits) + "\n" + "Buy Entries:\n" + levelsList + "SL: " + DoubleToString(storedSl, _Digits) + "\n" + "TP: " + DoubleToString(storedTp, _Digits); Print("New daily bar: Bullish Fibonacci levels calculated. Entries: ", levelsList); //--- Log } else { //--- Handle bearish // Bearish: Fibo from high to low for correct 0% at low, 100% at high, red ObjectCreate(0, FIB_OBJ, OBJ_FIBO, 0, startingTime, high, endingTime, low); //--- Create Fib ObjectSetInteger(0, FIB_OBJ, OBJPROP_COLOR, clrRed); //--- Set color for (int i = 0; i < ObjectGetInteger(0, FIB_OBJ, OBJPROP_LEVELS); i++) { //--- Iterate levels ObjectSetInteger(0, FIB_OBJ, OBJPROP_LEVELCOLOR, i, clrRed); //--- Set level color } storedSl = NormalizeDouble(high + range * (SlBufferPercent / 100), _Digits); //--- Calc SL storedTp = NormalizeDouble(low - range * (TpBufferPercent / 100), _Digits); //--- Calc TP storedInfo = "Daily Approach - Bearish\n" + //--- Set info "Open: " + DoubleToString(openPrice, _Digits) + "\n" + "Close: " + DoubleToString(closePrice, _Digits) + "\n" + "Sell Entries:\n" + levelsList + "SL: " + DoubleToString(storedSl, _Digits) + "\n" + "TP: " + DoubleToString(storedTp, _Digits); Print("New daily bar: Bearish Fibonacci levels calculated. Entries: ", levelsList); //--- Log } } } // Redraw chart objects ChartRedraw(); //--- Redraw chart }
In OnTick wird, wenn „UseDailyApproach“ aktiviert ist, die Anzahl der Tagesbalken mithilfe von iBars mit _Symbol und dem Makro PERIOD_D1 abgerufen. Wenn sich ein neuer Tagesbalken bildet und die aktuelle Zeit über 00:05 über TimeCurrent und StringToTime, aktualisieren wir „barsTotal“, entfernen ein eventuell vorhandenes Fib-Objekt mit „ObjectDelete“ auf „FIB_OBJ“ und holen den vorherigen Tagesbalken aus iOpen, Close über „iClose“, High mit iHigh, Low mit „iLow“, zusammen mit den Start- und Endzeiten, die durch Subtraktion von 1 Sekunde für eine korrekte Darstellung angepasst werden. Wir berechnen den Bereich als Hoch minus Tief, setzen „storedIsBullish“ auf true, wenn der Schlusskurs größer ist als der Eröffnungskurs, führen eine Schleife durch die „fibLevels“-Größe von ArraySize, um „storedTradesCount[i]“ auf Null zurückzusetzen, berechnen normalisierte Einstiegsniveaus mit „NormalizeDouble“ (subtrahieren den Bereich mal den Prozentsatz des Niveaus vom Hoch für aufwärts, addieren zum Tiefkurs für abwärts) und erstellen eine „levelsList“-String für die Anzeige.
Für Aufwärtsfälle erstellen wir ein Fib-Objekt mit dem Namen „FIB_OBJ“ als OBJ_FIBO, das vom Tiefpunkt zu Beginn bis zum Hochpunkt am Ende verankert ist, setzen seine Farbe mit ObjectSetInteger auf OBJPROP_COLOR auf grün und führen eine Schleife über die Anzahl der Levels aus „ObjectGetInteger“ mit „OBJPROP_LEVELS“ durch über „OBJPROP_LEVELCOLOR“ grün einfärben; „storedSl“ unterhalb von „low“ um den Pufferprozentsatz des Bereichs berechnen, „storedTp“ oberhalb von „high“ in ähnlicher Weise, „storedInfo“ mit Ansatztyp, Open/Close, Liste der Einträge und Handelsstufen formatieren und die Berechnung protokollieren. Für abwärts, spiegeln wir den Prozess: Anker Fib von hoch zu niedrig, verwenden rote Farben, setzen SL über hoch und TP unter niedrig, aktualisieren Informationen entsprechend, und loggen das. Abschließend aktualisieren wir das Bildmaterial mit der Funktion ChartRedraw. Es ist immer eine gute Programmierpraxis, den Code bei jedem Meilenstein zu kompilieren und zu testen, um sicherzustellen, dass alles in Ordnung ist. Beim Kompilieren erhalten wir folgendes Ergebnis.

Aus dem Bild können wir ersehen, dass wir den Bereich berechnen, die Richtung bestimmen und das entsprechende Fibonacci-Objekt zeichnen. Wir müssen nun die entsprechenden Positionen verfolgen und eröffnen, sobald wir auf die angegebenen Niveaus zurückgehen. Hier ist die Logik, mit der wir das erreicht haben.
//+------------------------------------------------------------------+ //| Display info using labels without flicker | //+------------------------------------------------------------------+ void ShowLabels(string info) { if (info == lastShownInfo) return; //--- Skip if no change lastShownInfo = info; //--- Update last info // Split info into lines string lines[]; //--- Lines array ushort nlSep = StringGetCharacter("\n", 0); //--- Get newline sep int numLines = StringSplit(info, nlSep, lines); //--- Split into lines int y = 10; //--- Starting Y for (int i = 0; i < numLines; i++) { //--- Iterate lines string name = "InfoLabel_" + IntegerToString(i); //--- Label name if (ObjectFind(0, name) < 0) { //--- Check exists ObjectCreate(0, name, OBJ_LABEL, 0, 0, 0); //--- Create label ObjectSetInteger(0, name, OBJPROP_CORNER, CORNER_LEFT_UPPER); //--- Set corner ObjectSetInteger(0, name, OBJPROP_XDISTANCE, 10); //--- Set X distance ObjectSetInteger(0, name, OBJPROP_FONTSIZE, 8); //--- Set font size } ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y); //--- Set Y distance ObjectSetString(0, name, OBJPROP_TEXT, lines[i]); //--- Set text y += 15; //--- Increment Y } // Delete extra labels if numLines decreased for (int i = numLines; ; i++) { //--- Iterate extras string name = "InfoLabel_" + IntegerToString(i); //--- Label name if (ObjectFind(0, name) < 0) break; //--- Break if none ObjectDelete(0, name); //--- Delete label } } // Display info every tick using labels (but only update if changed) ShowLabels(storedInfo); //--- Show labels // Entry logic: Checked every tick using stored levels (no existing position) if (PositionsTotal() == 0) { //--- Check no positions double close1 = iClose(_Symbol, _Period, 1); //--- Get close 1 double close2 = iClose(_Symbol, _Period, 2); //--- Get close 2 for (int i = 0; i < ArraySize(storedEntryLvls); i++) { //--- Iterate levels // Only enter on levels 0 < fib <=100 (retracements), ignore 0/100/extensions for entry if (fibLevels[i] <= 0 || fibLevels[i] > 100.0) continue; //--- Skip invalid if ((maxTradesPerLevel == 0 || storedTradesCount[i] < maxTradesPerLevel) && //--- Check count ((storedIsBullish && close1 > storedEntryLvls[i] && close2 <= storedEntryLvls[i]) || //--- Buy cross (!storedIsBullish && close1 < storedEntryLvls[i] && close2 >= storedEntryLvls[i]))) { //--- Sell cross string levelStr = DoubleToString(fibLevels[i], 1); //--- Level string ulong ticket = 0; //--- Init ticket if (storedIsBullish) { //--- Check buy Print("Buy signal triggered at ", close1, " crossing level ", levelStr, " (", storedEntryLvls[i], ")"); //--- Log obj_Trade.Buy(LotSize, _Symbol, 0, storedSl, storedTp, "Fibo Buy at " + levelStr); //--- Open buy ticket = obj_Trade.ResultDeal(); //--- Get deal } else { //--- Handle sell Print("Sell signal triggered at ", close1, " crossing level ", levelStr, " (", storedEntryLvls[i], ")"); //--- Log obj_Trade.Sell(LotSize, _Symbol, 0, storedSl, storedTp, "Fibo Sell at " + levelStr); //--- Open sell ticket = obj_Trade.ResultDeal(); //--- Get deal } storedTradesCount[i]++; //--- Increment count break; //--- Break loop } } }
Zunächst definieren wir die Funktion „ShowLabels“, um Strategie-Informationen auf dem Chart mit Hilfe von Etiketten ohne unnötige Redraws anzuzeigen. Dabei wird eine Zeichenkette „info“ genommen und frühzeitig zurückgegeben, wenn sie mit „lastShownInfo“ übereinstimmt, um ein Flackern zu vermeiden, andernfalls wird „lastShownInfo“ aktualisiert. Wir teilen „info“ in das Array „lines“ über StringSplit on newline von StringGetCharacter auf, dann durchlaufen wir eine Schleife durch „numLines“, um Etiketten mit dem Namen „InfoLabel_“ plus Index mit ObjectFind zu erstellen oder zu aktualisieren, um das Vorhandensein zu prüfen; wenn neu, verwenden wir ObjectCreate als OBJ_LABEL mit linker oberer Ecke, X-Abstand 10 und Schriftgröße 8. Wir stellen den Y-Abstand schrittweise ein, beginnend mit 10, indem wir jedes Mal 15 hinzufügen, und den Text zu „lines[i]“ mit „ObjectSetString“ auf „OBJPROP_TEXT“. Um Extras zu bereinigen, wenn die Anzahl der Zeilen abnimmt, wird eine Schleife von „numLines“ aufwärts gebildet, in der alle verbleibenden Beschriftungen mit ObjectDelete gelöscht werden, bis mit der Funktion „ObjectFind“ keine mehr gefunden werden.
Dann rufen wir in der Tick-Funktion direkt unter der zuvor definierten Logik die Funktion mit „storedInfo“ bei jedem Tick auf, um die Anzeige nur bei Änderungen zu aktualisieren. Für die Einstiegslogik, wenn keine Positionen aus PositionsTotal gleich Null vorhanden sind, holen wir vorherige Schließungen mit iClose bei den Verschiebungen 1 und 2, dann Schleife über „storedEntryLvls“ Größe von „ArraySize“, Überspringen Ebenen außerhalb 0 bis 100 für echte Retracements. Wenn Handelsgeschäfte erlaubt sind (unbegrenzt oder unter „maxTradesPerLevel“) und eine Kreuzung auftritt – Close1 über Level und Close2 bei oder unter für Käufe, oder Close1 unter und Close2 bei oder über für Verkäufe – formatieren wir „levelStr“ mit DoubleToString auf eine Dezimalstelle gerundet, initialisieren ein Ticket, protokollieren das Signal, eröffnen einen Kauf oder Verkauf über „obj_Trade.Buy“ oder „Sell“ mit „LotSize“, Symbol, Marktpreis 0, „storedSl“, „storedTp“ und Kommentar einschließlich Level, erfassen das Geschäftsergebnis, erhöhen „storedTradesCount[i]“ und brechen ab, um mehrere Einträge pro Tick zu vermeiden. Nach dem Kompilieren erhalten wir folgendes Ergebnis:

Nachdem wir nun den Einstieg in die Logik des täglichen Ansatzes bestätigen können, wollen wir uns dem anderen Ansatz zuwenden. Wir haben uns für dieses Projekt für zwei Logiken entschieden, um Ihnen zu zeigen, wie Sie diese umstellen oder an die von Ihnen gewünschten Handelsansätze anpassen können. Da dieser Array-Ansatz dynamisch ist, um mehr Signale zu geben, wollen wir die Analyse nur einmal durchführen, und so lange der Preis zwischen dem vorherigen Setup liegt, warten wir. Wir analysieren nur, wenn das vorherige Setup durchbrochen wurde, d. h. wenn der Kurs aus dem Bereich zwischen 0 und 100 herauskommt. Beachten Sie, dass es über 100 weitere Stufen gibt. Wir brauchen also eine Funktion, die den Bruch signalisiert.
//+------------------------------------------------------------------+ //| Check if price breaches the current Fib extremes | //+------------------------------------------------------------------+ bool IsBreach() { if (!fibCalculated) return false; //--- Return false if not calculated double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID); //--- Get bid price if (storedIsBullish) { //--- Check bullish // For bullish, 0% is high, 100% is low return (bid > currentHigh || bid < currentLow); //--- Check breach } else { //--- Handle bearish // For bearish, 0% is low, 100% is high return (bid > currentLow || bid < currentHigh); //--- Check breach } }
Hier implementieren wir die Funktion „IsBreach“, um zu erkennen, ob der aktuelle Geldkurs aus SymbolInfoDouble mit SYMBOL_BID über die gespeicherten Fib-Extreme hinausgebrochen ist, und geben frühzeitig false zurück, wenn „fibCalculated“ nicht true ist. Bei Aufwärts-Setups, bei denen 0% auf dem Hoch und 100% auf dem Tief liegen, wird geprüft, ob das Gebot über „currentHigh“ oder unter „currentLow“ liegt; bei Abwärts-Setups mit umgekehrten Ankern wird geprüft, ob das Gebot über „currentLow“ oder unter „currentHigh“ liegt, wobei bei „true“ Wiederholungen im Array-Modus ausgelöst werden. Gut. Wir können nun unseren Array-Ansatz einbeziehen, der mit dem täglichen Ansatz identisch ist.
else { //--- Array approach // Array approach: Calculate only when not calculated or breached if (!fibCalculated || IsBreach()) { //--- Check recalc if (fibCalculated) { //--- Check calculated // Invalidate and forget previous ObjectDelete(0, fibName); //--- Delete Fib fibCalculated = false; //--- Reset flag } int startShift = IncludeCurrentBar ? 0 : 1; //--- Set start shift int copyCount = IncludeCurrentBar ? LookbackSize : LookbackSize; //--- Set copy count double high[], low[]; //--- High and low arrays ArraySetAsSeries(high, true); //--- Set as series ArraySetAsSeries(low, true); //--- Set as series if (CopyHigh(_Symbol, _Period, startShift, copyCount, high) <= 0) return; //--- Copy high if (CopyLow(_Symbol, _Period, startShift, copyCount, low) <= 0) return; //--- Copy low int highestCandle = ArrayMaximum(high, 0, copyCount); //--- Get highest int lowestCandle = ArrayMinimum(low, 0, copyCount); //--- Get lowest MqlRates pArray[]; //--- Rates array ArraySetAsSeries(pArray, true); //--- Set as series int pData = CopyRates(_Symbol, _Period, startShift, copyCount, pArray); //--- Copy rates if (pData <= 0) return; //--- Check data double highVal = pArray[highestCandle].high; //--- Get high val double lowVal = pArray[lowestCandle].low; //--- Get low val double range = highVal - lowVal; //--- Calc range int oldestShift = IncludeCurrentBar ? (LookbackSize - 1) : LookbackSize; //--- Oldest shift double openCandle = iOpen(_Symbol, _Period, oldestShift); //--- Get open double closeCandle = iClose(_Symbol, _Period, IncludeCurrentBar ? 0 : 1); //--- Get close storedIsBullish = (closeCandle > openCandle); //--- Set bullish string levelsList = ""; //--- Init list for (int i = 0; i < ArraySize(fibLevels); i++) { //--- Iterate levels storedTradesCount[i] = 0; //--- Reset count if (storedIsBullish) { //--- Check bullish storedEntryLvls[i] = NormalizeDouble(highVal - range * fibLevels[i] / 100, _Digits); //--- Calc entry } else { //--- Handle bearish storedEntryLvls[i] = NormalizeDouble(lowVal + range * fibLevels[i] / 100, _Digits); //--- Calc entry } levelsList += DoubleToString(fibLevels[i], 1) + ": " + DoubleToString(storedEntryLvls[i], _Digits) + "\n"; //--- Add to list } if (storedIsBullish) { //--- Check bullish // Bullish: Anchor from low to high datetime time1 = pArray[lowestCandle].time; //--- Time1 double price1 = lowVal; //--- Price1 datetime time2 = pArray[highestCandle].time; //--- Time2 double price2 = highVal; //--- Price2 ObjectCreate(0, fibName, OBJ_FIBO, 0, time1, price1, time2, price2); //--- Create Fib ObjectSetInteger(0, fibName, OBJPROP_COLOR, clrGreen); //--- Set color for (int i = 0; i < ObjectGetInteger(0, fibName, OBJPROP_LEVELS); i++) { //--- Iterate levels ObjectSetInteger(0, fibName, OBJPROP_LEVELCOLOR, i, clrGreen); //--- Set level color } storedSl = NormalizeDouble(lowVal - range * (SlBufferPercent / 100), _Digits); //--- Calc SL storedTp = NormalizeDouble(highVal + range * (TpBufferPercent / 100), _Digits); //--- Calc TP storedInfo = "Array Approach - Bullish\n" + //--- Set info "Array Open: " + DoubleToString(openCandle, _Digits) + "\n" + "Array Close: " + DoubleToString(closeCandle, _Digits) + "\n" + "Buy Entries:\n" + levelsList + "SL: " + DoubleToString(storedSl, _Digits) + "\n" + "TP: " + DoubleToString(storedTp, _Digits); } else { //--- Handle bearish // Bearish: Anchor from high to low datetime time1 = pArray[highestCandle].time; //--- Time1 double price1 = highVal; //--- Price1 datetime time2 = pArray[lowestCandle].time; //--- Time2 double price2 = lowVal; //--- Price2 ObjectCreate(0, fibName, OBJ_FIBO, 0, time1, price1, time2, price2); //--- Create Fib ObjectSetInteger(0, fibName, OBJPROP_COLOR, clrRed); //--- Set color for (int i = 0; i < ObjectGetInteger(0, fibName, OBJPROP_LEVELS); i++) { //--- Iterate levels ObjectSetInteger(0, fibName, OBJPROP_LEVELCOLOR, i, clrRed); //--- Set level color } storedSl = NormalizeDouble(highVal + range * (SlBufferPercent / 100), _Digits); //--- Calc SL storedTp = NormalizeDouble(lowVal - range * (TpBufferPercent / 100), _Digits); //--- Calc TP storedInfo = "Array Approach - Bearish\n" + //--- Set info "Array Open: " + DoubleToString(openCandle, _Digits) + "\n" + "Array Close: " + DoubleToString(closeCandle, _Digits) + "\n" + "Sell Entries:\n" + levelsList + "SL: " + DoubleToString(storedSl, _Digits) + "\n" + "TP: " + DoubleToString(storedTp, _Digits); } currentHigh = storedIsBullish ? highVal : lowVal; //--- Set current high currentLow = storedIsBullish ? lowVal : highVal; //--- Set current low fibCalculated = true; //--- Set calculated } // Display info using labels (but only update if changed) ShowLabels(storedInfo); //--- Show labels // Entry logic: Checked every tick using stored levels (no existing position) if (PositionsTotal() == 0) { //--- Check no positions double close1 = iClose(_Symbol, _Period, 1); //--- Get close 1 double close2 = iClose(_Symbol, _Period, 2); //--- Get close 2 for (int i = 0; i < ArraySize(storedEntryLvls); i++) { //--- Iterate levels if (fibLevels[i] <= 0 || fibLevels[i] > 100.0) continue; //--- Skip invalid if ((maxTradesPerLevel == 0 || storedTradesCount[i] < maxTradesPerLevel) && //--- Check count ((storedIsBullish && close1 > storedEntryLvls[i] && close2 <= storedEntryLvls[i]) || //--- Buy cross (!storedIsBullish && close1 < storedEntryLvls[i] && close2 >= storedEntryLvls[i]))) { //--- Sell cross string levelStr = DoubleToString(fibLevels[i], 1); //--- Level string ulong ticket = 0; //--- Init ticket if (storedIsBullish) { //--- Check buy Print("Buy signal triggered (Array) at ", close1, " crossing level ", levelStr, " (", storedEntryLvls[i], ")"); //--- Log obj_Trade.Buy(LotSize, _Symbol, 0, storedSl, storedTp, "Fibo Buy Array at " + levelStr); //--- Open buy ticket = obj_Trade.ResultDeal(); //--- Get deal } else { //--- Handle sell Print("Sell signal triggered (Array) at ", close1, " crossing level ", levelStr, " (", storedEntryLvls[i], ")"); //--- Log obj_Trade.Sell(LotSize, _Symbol, 0, storedSl, storedTp, "Fibo Sell Array at " + levelStr); //--- Open sell ticket = obj_Trade.ResultDeal(); //--- Get deal } storedTradesCount[i]++; //--- Increment count break; //--- Break loop } } } }
Wir verwenden den Array-Ansatz für den nicht-täglichen Modus. Die Fib-Levels werden nur dann neu berechnet, wenn dies noch nicht geschehen ist oder wenn „IsBreach“ einen Preisdurchbruch feststellt. Falls bereits gesetzt, löschen wir das alte Fib-Objekt mit ObjectDelete auf „fibName“ und setzen „fibCalculated“ auf false zurück. Die Startverschiebung und die Anzahl der Kopien hängen von „IncludeCurrentBar“ ab: bei „true“ wird 0 und ein vollständiger Rückblick verwendet, bei „false“ wird 1 und nur abgeschlossene Takte verwendet. High und Low Arrays werden mit ArraySetAsSeries als Serie gesetzt und mit den Funktionen CopyHigh und CopyLow gefüllt. Wenn dies nicht gelingt, kehren wir vorzeitig zurück. Dann werden die höchste und die niedrigste Kerze mit ArrayMaximum und ArrayMinimum von Index 0 bis Count ermittelt.
Um genaue Werte und Zeiten zu erhalten, kopieren wir die Raten in „pArray“ mit CopyRates, und wenn die Daten nicht ausreichen, extrahieren wir „highVal“ aus dem Hoch der Kerze und „lowVal“ aus dem Tief der Kerze und berechnen die Spanne als deren Differenz. Für die Richtung wird mit iOpen die älteste Schicht geöffnet und mit iClose die jüngste Schicht geschlossen, wobei „storedIsBullish“ gesetzt wird, wenn close größer als open ist. Schleife durch die „fibLevels“-Größe von ArraySize, um die Zählungen zurückzusetzen, normalisierte Einträge zu berechnen (subtrahiere Bereichsprozent von „highVal“ für aufwärts, addiere zu „lowVal“ für abwärts), und stelle „levelsList“ für Info zusammen. Für die Zeichnung und den Handel verwenden wir einen ähnlichen Ansatz wie für den täglichen Modus. Nach dem Kompilieren erhalten wir folgendes Ergebnis:

Wir können sehen, dass wir den Array-Ansatz verwenden und Positionen einleiten. Jetzt geht es darum, die Positionen zu verwalten, indem wir sie schließen, wenn wir neue Signale erhalten, und die Positionen, die sich zu unseren Gunsten entwickeln, nachziehen.
//+------------------------------------------------------------------+ //| Close all positions with matching magic and symbol | //+------------------------------------------------------------------+ void CloseAllPositions() { for (int i = PositionsTotal() - 1; i >= 0; i--) { //--- Iterate positions reverse if (PositionGetTicket(i) > 0 && PositionGetInteger(POSITION_MAGIC) == MagicNumber && PositionGetString(POSITION_SYMBOL) == _Symbol) { //--- Check position obj_Trade.PositionClose(PositionGetTicket(i)); //--- Close position } } } //+------------------------------------------------------------------+ //| Apply Points Trailing Stop (from reference) | //+------------------------------------------------------------------+ void ApplyPointsTrailing() { double point = _Point; //--- Get point value for (int i = PositionsTotal() - 1; i >= 0; i--) { //--- Iterate positions reverse if (PositionGetTicket(i) > 0) { //--- Check valid ticket if (PositionGetString(POSITION_SYMBOL) == _Symbol && PositionGetInteger(POSITION_MAGIC) == MagicNumber) { //--- Check symbol and magic double sl = PositionGetDouble(POSITION_SL); //--- Get SL double tp = PositionGetDouble(POSITION_TP); //--- Get TP double openPrice = PositionGetDouble(POSITION_PRICE_OPEN); //--- Get open price ulong ticket = PositionGetInteger(POSITION_TICKET); //--- Get ticket if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) { //--- Check buy double newSL = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_BID) - Trailing_Stop_Pips * point, _Digits); //--- Calc new SL if (newSL > sl && SymbolInfoDouble(_Symbol, SYMBOL_BID) - openPrice > Min_Profit_To_Trail_Pips * point) { //--- Check conditions obj_Trade.PositionModify(ticket, newSL, tp); //--- Modify position } } else if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) { //--- Check sell double newSL = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_ASK) + Trailing_Stop_Pips * point, _Digits); //--- Calc new SL if (newSL < sl && openPrice - SymbolInfoDouble(_Symbol, SYMBOL_ASK) > Min_Profit_To_Trail_Pips * point) { //--- Check conditions obj_Trade.PositionModify(ticket, newSL, tp); //--- Modify position } } } } } }
Dies sind die Funktionen, die wir benötigen, um die Verwaltungslogik zu erreichen. Wir müssen sie nur bei Bedarf entsprechend aufrufen.
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { // Points trailing can run anytime if (TrailingType == Trailing_Points && PositionsTotal() > 0) { //--- Check trailing ApplyPointsTrailing(); //--- Apply trailing } //--- call where necessary if (CloseOnNewFib == CloseOnNew_Yes) { //--- Check close on new CloseAllPositions(); //--- Close positions } }
Wir rufen die Funktionen in der Tick-Funktion auf, wo es nötig ist. Nun müssen die Objekte, die wir beim Beenden des Charts erstellt haben, wie folgt gelöscht werden.
//+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { ObjectsDeleteAll(0, "InfoLabel_", -1, OBJ_LABEL); //--- Delete all info labels ObjectDelete(0, FIB_OBJ); //--- Delete daily Fibonacci ObjectDelete(0, fibName); //--- Delete array Fibonacci }
In der Funktion OnDeinit werden alle Info-Labels mit der Funktion ObjectsDeleteAll unter Angabe des Präfixes „InfoLabel_“ und des Typs OBJ_LABEL aus allen Unterfenstern entfernt. Anschließend wird das tägliche Fib-Objekt über „ObjectDelete“ auf „FIB_OBJ“ und das Array auf „fibName“ gelöscht, um das Bildmaterial zu löschen. Nach dem Kompilieren erhalten wir folgendes Ergebnis:

Wir können sehen, dass wir die Positionen standardmäßig verwalten, indem wir bei Bedarf Trailing-Stops anwenden und so unsere Ziele erreichen. Bleiben nur noch die Backtests des Programms, und das wird im nächsten Abschnitt behandelt.
Backtests
Nach einem gründlichen Backtest erhalten wir folgende Ergebnisse.
Backtest-Grafik:

Bericht des Backtest:

Schlussfolgerung
Zusammenfassend haben wir ein Retracement-Handelssystem in MQL5 entwickelt, das Niveaus anhand von Tageskerzen oder Lookback-Arrays berechnet, Auf- oder Abwärts-Setups anhand des Schlusskurses im Vergleich zum Eröffnungskurs identifiziert, Käufe oder Verkäufe bei der Überschreitung von nutzerdefinierten Quoten mit Handelslimits pro Niveau ausführt, optionale Schließungen bei Recalcs, punktbasierte Trailing-Stops nach einer Gewinnschwelle und Einstiegsniveaus mit Range-Puffern anwendet, ergänzt durch visuelle Darstellungen auf dem Chart und Informationsetiketten.
Haftungsausschluss: Dieser Artikel ist nur für Bildungszwecke gedacht. Der Handel ist mit erheblichen finanziellen Risiken verbunden, und die Volatilität der Märkte kann zu Verlusten führen. Gründliche Backtests und sorgfältiges Risikomanagement sind entscheidend, bevor Sie dieses Programm auf den Live-Märkten einsetzen.
Mit dieser Fibonacci-Retracement-Strategie sind Sie für den effektiven Handel mit Pullback-Gelegenheiten gerüstet und bereit für die weitere Optimierung Ihrer Handelsreise. Viel Spaß beim Handeln!
Übersetzt aus dem Englischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/en/articles/20221
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 50): Entwicklung der RVGI, CCI und SMA Confluence Engine in MQL5
Klassische Strategien neu interpretieren (Teil 13): Unsere Kreuz-Strategie in neue Dimensionen führen (Teil 2)
Entwicklung einer Handelsstrategie: Die Triple-Sinus-Mittelwertumkehrmethode
Markets Positioning Codex in MQL5 (Teil 2): Bitweises Lernen, mit Multi-Patterns für Nvidia
- 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.