Automatisieren von Handelsstrategien in MQL5 (Teil 38): Versteckter RSI-Divergenzhandel mit Steigungswinkel-Filtern
Einführung
In unserem vorangegangenen Artikel (Teil 37) haben wir ein Regular RSI Divergence Convergence System in MetaQuotes Language 5 (MQL5) entwickelt, das regelmäßige Auf- und Abwärts-Divergenzen zwischen Kursschwankungen und Relative Strength Index (RSI)-Werten erkennt, Handelsgeschäfte auf Signale mit optionalen Risikokontrollen ausführt und Visualisierungen auf dem Chart für eine verbesserte Analyse bietet. In Teil 38 entwickeln wir ein Handelssystem der versteckten RSI-Divergenzen mit Filtern der Steigungswinkel.
Dieses System identifiziert versteckte Aufwärts- und Abwärtsdivergenzen mit Hilfe von Umkehrpunkte, wendet saubere Kontrollen mit Balkenbereiche und Toleranzen an, filtert Signale über anpassbare Steigungswinkel auf Preis- und RSI-Linien, führt Handelsgeschäfte mit Risikomanagement aus und beinhaltet visuelle Marker mit Winkelanzeigen auf Charts. Wir werden die folgenden Themen behandeln:
Am Ende haben Sie eine funktionierende MQL5-Strategie für den Handel mit versteckten RSI-Divergenzen, die Sie anpassen können – legen wir los!
Verstehen der versteckte RSI-Divergenzstrategie
Die Strategie versteckter RSI-Divergenzen konzentriert sich auf die Identifizierung von Gelegenheiten zur Trendfortsetzung, indem sie spezifische Diskrepanzen zwischen Kursschwankungen und dem Relative Strength Index (RSI) Oszillator aufspürt, der die zugrunde liegende Momentum-Stärke in laufenden Trends hervorhebt. Bei einer versteckten Aufwärts-Divergenz bildet der Kurs ein höheres Tief, während der RSI ein niedrigeres Tief bildet, was darauf hindeutet, dass die Rücksetzer nach unten schwächer werden und der Aufwärtstrend wieder aufgenommen werden kann. Bei einer versteckten Abwärts-Divergenz bildet der Kurs ein niedrigeres Hoch, der RSI zeigt jedoch ein höheres Hoch an, was darauf hindeutet, dass die Aufwärtskorrekturen nachlassen und der Abwärtstrend anhalten könnte.
Wir beabsichtigen, die Zuverlässigkeit zu erhöhen, indem wir Divergenzen mit Steigungswinkeln sowohl auf den Preis- als auch auf den RSI-Linien filtern, um eine ausreichende Steilheit oder Flachheit zu bestätigen, Toleranzschwellen für saubere Muster ohne Durchbrüche anzuwenden und dementsprechend Handelsgeschäfte einzugehen – Käufe bei versteckten Aufwärts-Signalen oder Verkäufe bei versteckten Abwärts-Signalen – mit definierten Risikoparametern wie Stopps, Gewinne und Trailing-Mechanismen. Wenn wir uns diese Elemente zunutze machen, können wir in etablierten Trends mit hoher Wahrscheinlichkeit Fortsetzungsgeschäfte tätigen. Sehen Sie sich unten die verschiedenen Möglichkeiten an, die wir haben.
Versteckte Aufwärts-Divergenzeinstellung:

Versteckte Abwärts-Divergenzeinstellung:

Unser Plan ist es, hohe und tiefe Umkehrpunkte mit Bestätigungsstärke zu erkennen, versteckte Divergenzen durch saubere Checks innerhalb festgelegter Balkenbereiche und Toleranzen zu validieren, optionale Steigungswinkel-Filter auf Preis und RSI für die Signalqualität anzuwenden, automatisierte Handelsgeschäfte mit anpassbarer Losgröße und Risikokontrolle auszuführen und visuelle Hilfen wie farbige Linien und Etiketten mit Winkelanzeigen auf beiden Charts bereitzustellen, um ein effektives System für den Handel mit versteckten Divergenzen aufzubauen. Kurz gesagt, hier ist eine visuelle Darstellung unserer Ziele.

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.
//+------------------------------------------------------------------+ //| RSI Hidden Divergence Convergence EA.mq5 | //| Copyright 2025, Allan Munene Mutiiria. | //| https://t.me/Forex_Algo_Trader | //+------------------------------------------------------------------+ #property copyright "Copyright 2025, Allan Munene Mutiiria." #property link "https://t.me/Forex_Algo_Trader" #property version "1.00" #property strict #include <Trade\Trade.mqh> //+------------------------------------------------------------------+ //| Input Parameters | //+------------------------------------------------------------------+ input group "RSI Settings" input int RSI_Period = 14; // RSI Period input ENUM_APPLIED_PRICE RSI_Applied = PRICE_CLOSE; // RSI Applied Price input group "Swing Settings" input int Swing_Strength = 5; // Bars to confirm swing high/low input int Min_Bars_Between = 5; // Min bars between swings for divergence input int Max_Bars_Between = 50; // Max bars between swings for divergence input double Tolerance = 0.1; // Tolerance for clean divergence check input group "Price Divergence Filter" input bool Use_Price_Slope_Filter = false; // Use Slope Angle Filter for Price input double Price_Min_Slope_Degrees = 10.0; // Minimum Slope Angle in Degrees for Price (0 to disable min) input double Price_Max_Slope_Degrees = 80.0; // Maximum Slope Angle in Degrees for Price (90 to disable max) input group "RSI Divergence Filter" input bool Use_RSI_Slope_Filter = true; // Use Slope Angle Filter for RSI input double RSI_Min_Slope_Degrees = 1.0; // Minimum Slope Angle in Degrees for RSI (0 to disable min) input double RSI_Max_Slope_Degrees = 89.0; // Maximum Slope Angle in Degrees for RSI (90 to disable max) input group "Trade Settings" input double Lot_Size = 0.01; // Fixed Lot Size input int Magic_Number = 123456789; // Magic Number input double SL_Pips = 300.0; // Stop Loss in Pips (0 to disable) input double TP_Pips = 300.0; // Take Profit in Pips (0 to disable) input group "Trailing Stop Settings" input bool Enable_Trailing_Stop = true; // Enable Trailing Stop input double Trailing_Stop_Pips = 30.0; // Trailing Stop in Pips input double Min_Profit_To_Trail_Pips = 50.0; // Minimum Profit to Start Trailing in Pips input group "Visualization" input bool Mark_Swings_On_Price = true; // Mark Swing Points on Price Chart input bool Mark_Swings_On_RSI = true; // Mark Swing Points on RSI input color Bull_Color = clrGreen; // Bullish Divergence Color input color Bear_Color = clrRed; // Bearish Divergence Color input color Swing_High_Color = clrRed; // Color for Swing High Labels input color Swing_Low_Color = clrGreen; // Color for Swing Low Labels input int Line_Width = 2; // Divergence Line Width input ENUM_LINE_STYLE Line_Style = STYLE_SOLID; // Divergence Line Style input int Font_Size = 8; // Swing Point Font Size //+------------------------------------------------------------------+ //| Indicator Handles and Trade Object | //+------------------------------------------------------------------+ int RSI_Handle = INVALID_HANDLE; //--- RSI indicator handle CTrade obj_Trade; //--- Trade object for position management
Wir beginnen mit der Einbindung der Bibliothek „Trade“ mit „#include <Trade\Trade.mqh>“, um integrierte Funktionen für die Verwaltung von Positionen und Aufträgen zu ermöglichen. Als Nächstes definieren wir verschiedene Eingabeparameter, die nach Kategorien gruppiert sind und vom Nutzer angepasst werden können. Unter „RSI-Einstellungen“ setzen wir „RSI_Period“ auf 14 für die RSI-Berechnungsdauer und „RSI_Applied“ auf PRICE_CLOSE, um ihn mit Schlusskursen zu rechnen. Dies sind nur die Standardeinstellungen. Sie können sie nach Belieben anpassen. Unter „Swing Settings“ wird „Swing_Strength“ auf 5 gesetzt, um die für die Bestätigung von hohe und tiefe Umkehrpunkte benötigten Balken zu bestimmen, während „Min_Bars_Between“ und „Max_Bars_Between“ die Divergenzerkennung auf 5 bis 50 Balken begrenzen und „Tolerance“ mit 0,1 einen kleinen Puffer für saubere Divergenzprüfungen ermöglicht, genau wie bei der regulären Version.
Für die Gruppe „Price Divergence Filter“ ist „Use_Price_Slope_Filter“ standardmäßig auf false eingestellt, um optional eine winkelbasierte Filterung zu aktivieren, wobei „Price_Min_Slope_Degrees“ auf 10,0 und „Price_Max_Slope_Degrees“ auf 80,0 eingestellt ist, um akzeptable Steigungsbereiche in Grad zu definieren (min deaktiviert bei 0, max bei 90). In ähnlicher Weise hat der „RSI Divergence Filter“ „Use_RSI_Slope_Filter“ auf true, mit „RSI_Min_Slope_Degrees“ auf 1.0 und „RSI_Max_Slope_Degrees“ auf 89.0 für die Steigungen der RSI-Linien. Der Rest der Parameter ist identisch mit der vorherigen regulären Version, mit der Ausnahme, dass wir die Option der Gradfilterung hinzugefügt haben.
Schließlich deklarieren wir globale Variablen: „RSI_Handle“, initialisiert auf INVALID_HANDLE, um die RSI-Indikatorreferenz zu speichern, und „obj_Trade“ als CTrade-Instanz für die Abwicklung von Handelsoperationen. Nun müssen wir die globalen Variablen für die Schwingungspunkte definieren und sie initialisieren.
//+------------------------------------------------------------------+ //| Swing Variables | //+------------------------------------------------------------------+ double Last_High_Price = 0.0; //--- Last swing high price datetime Last_High_Time = 0; //--- Last swing high time double Prev_High_Price = 0.0; //--- Previous swing high price datetime Prev_High_Time = 0; //--- Previous swing high time double Last_Low_Price = 0.0; //--- Last swing low price datetime Last_Low_Time = 0; //--- Last swing low time double Prev_Low_Price = 0.0; //--- Previous swing low price datetime Prev_Low_Time = 0; //--- Previous swing low time double Last_High_RSI = 0.0; //--- Last swing high RSI value double Prev_High_RSI = 0.0; //--- Previous swing high RSI value double Last_Low_RSI = 0.0; //--- Last swing low RSI value double Prev_Low_RSI = 0.0; //--- Previous swing low RSI value
Wir fahren fort, indem wir eine Reihe von globalen Variablen im Abschnitt „Swing Variables“ deklarieren, um Details über die letzten und vorherigen Umkehrpunkte für Hochs und Tiefs zu speichern. Dazu gehören „Last_High_Price“ und „Last_High_Time“ für den Preis und den Zeitstempel des jüngsten hohe Umkehrpunkte sowie „Prev_High_Price“ und „Prev_High_Time“ für das davor. Ähnlich verhält es sich bei den tiefen Umkehrpunkte: „Last_Low_Price“, „Last_Low_Time“, „Prev_Low_Price“, und „Prev_Low_Time“. Um diese mit den Indikatordaten zu verknüpfen, fügen wir „Last_High_RSI“ und „Prev_High_RSI“ für die RSI-Werte an diesen Hochpunkten sowie „Last_Low_RSI“ und „Prev_Low_RSI“ für die Tiefststände hinzu. Alle werden auf Null initialisiert, damit wir sie während der Laufzeit zur Erkennung von Divergenzen dynamisch aktualisieren und vergleichen können. Damit sind wir gut gerüstet. Wir müssen nur das Programm initialisieren, insbesondere den RSI-Indikator, und sicherstellen, dass wir auf sein Fenster verweisen können, damit wir später darauf zeichnen können, aber diesmal mit einer Graddarstellung.
//+------------------------------------------------------------------+ //| Expert Initialization Function | //+------------------------------------------------------------------+ int OnInit() { RSI_Handle = iRSI(_Symbol, _Period, RSI_Period, RSI_Applied); //--- Create RSI indicator handle if (RSI_Handle == INVALID_HANDLE) { //--- Check if RSI creation failed Print("Failed to create RSI indicator"); //--- Log error return(INIT_FAILED); //--- Return initialization failure } long chart_id = ChartID(); //--- Get current chart ID string rsi_name = "RSI(" + IntegerToString(RSI_Period) + ")"; //--- Generate RSI indicator name int rsi_subwin = ChartWindowFind(chart_id, rsi_name); //--- Find RSI subwindow if (rsi_subwin == -1) { //--- Check if RSI subwindow not found if (!ChartIndicatorAdd(chart_id, 1, RSI_Handle)) { //--- Add RSI to chart subwindow Print("Failed to add RSI indicator to chart"); //--- Log error } } obj_Trade.SetExpertMagicNumber(Magic_Number); //--- Set magic number for trade object Print("RSI Hidden Divergence EA initialized"); //--- Log initialization success return(INIT_SUCCEEDED); //--- Return initialization success }
In OnInit erstellen wir zunächst das RSI-Indikator-Handle mit der Funktion iRSI und übergeben das aktuelle Symbol, den Zeitrahmen, die RSI-Periode und den angewandten Preistyp, um den Oszillator einzurichten. Anschließend wird geprüft, ob „RSI_Handle“ INVALID_HANDLE ist; ist dies der Fall, wird mit Print eine Fehlermeldung protokolliert und „INIT_FAILED“ zurückgegeben, um die Initialisierung zu beenden. Als Nächstes rufen wir die aktuelle Chart-ID mit ChartID ab und konstruieren den RSI-Indikatornamen als String, der „RSI(“ mit der über die Funktion IntegerToString konvertierten Periode kombiniert.
Wir versuchen, das RSI-Subfenster mithilfe von ChartWindowFind mit der Chart-ID und dem Namen zu finden; wenn es nicht gefunden wird (rsi_subwin == -1), fügen wir den Indikator über ChartIndicatorAdd zum Subfenster 1 hinzu und protokollieren einen Fehler, wenn dies nicht gelingt. Danach konfigurieren wir das Handelsobjekt, indem wir „obj_Trade.SetExpertMagicNumber(Magic_Number)“ aufrufen, um unser eindeutiges Kennzeichen mit den Handelsgeschäften zu verknüpfen. Schließlich geben wir eine Erfolgsmeldung aus und geben INIT_SUCCEEDED zurück, um die ordnungsgemäße Einrichtung zu bestätigen. Nach der Initialisierung erhalten wir das folgende Ergebnis.

Nun, da wir das Programm initialisieren und den Indikator zu seinem Unterfenster hinzufügen können, auf das wir Bezug nehmen können, müssen wir die Umkehrpunkte auf dem Chart überprüfen und einzeichnen, damit wir sie zur Identifizierung der Divergenzen oder Konvergenzen verwenden können. Lassen Sie uns dafür einige Hilfsfunktionen definieren.
//+------------------------------------------------------------------+ //| Check for Swing High | //+------------------------------------------------------------------+ bool CheckSwingHigh(int bar, double& highs[]) { if (bar < Swing_Strength || bar + Swing_Strength >= ArraySize(highs)) return false; //--- Return false if bar index out of range for swing strength double current = highs[bar]; //--- Get current high price for (int i = 1; i <= Swing_Strength; i++) { //--- Iterate through adjacent bars if (highs[bar - i] >= current || highs[bar + i] >= current) return false; //--- Return false if not a swing high } return true; //--- Return true if swing high } //+------------------------------------------------------------------+ //| Check for Swing Low | //+------------------------------------------------------------------+ bool CheckSwingLow(int bar, double& lows[]) { if (bar < Swing_Strength || bar + Swing_Strength >= ArraySize(lows)) return false; //--- Return false if bar index out of range for swing strength double current = lows[bar]; //--- Get current low price for (int i = 1; i <= Swing_Strength; i++) { //--- Iterate through adjacent bars if (lows[bar - i] <= current || lows[bar + i] <= current) return false; //--- Return false if not a swing low } return true; //--- Return true if swing low }
Wir definieren die Funktion „CheckSwingHigh“, die einen ganzzahligen Balkenindex und einen Verweis auf ein Array von Höchstkursen annimmt, um festzustellen, ob ein hoher Umkehrpunkt an diesem Balken existiert. Sie prüft zunächst, ob der Balken auf der Grundlage von „Swing_Strength“ außerhalb der Grenzen liegt, indem sie die Funktion ArraySize verwendet, um Indexfehler zu vermeiden, und gibt in diesem Fall false zurück. Anschließend wird der aktuelle Höchstkurs abgerufen und eine Schleife von 1 bis „Swing_Strength“ durchlaufen, wobei überprüft wird, ob keine links oder rechts angrenzenden Balken Höchstkurse aufweisen, die größer oder gleich dem aktuellen sind; ist dies der Fall, wird false zurückgegeben, andernfalls true, um ein hoher Umkehrpunkt zu bestätigen.
In ähnlicher Weise erstellen wir die Funktion „CheckSwingLow“ mit der gleichen Struktur, aber für niedrige Preise, wobei wir sicherstellen, dass der Balken im Bereich liegt, den aktuellen Tiefstwert ermitteln und in der Schleife prüfen, ob keine benachbarten Balken Tiefstwerte aufweisen, die kleiner oder gleich dem aktuellen sind, und nur dann true zurückgeben, wenn es sich um einen gültigen Tiefstwert handelt.
//+------------------------------------------------------------------+ //| Check for Clean Divergence | //+------------------------------------------------------------------+ bool CleanDivergence(double rsi1, double rsi2, int shift1, int shift2, double& rsi_data[], bool bearish) { if (shift1 <= shift2) return false; //--- Return false if shifts invalid for (int b = shift2 + 1; b < shift1; b++) { //--- Iterate between shifts double interp_factor = (double)(b - shift2) / (shift1 - shift2); //--- Calculate interpolation factor double interp_rsi = rsi2 + interp_factor * (rsi1 - rsi2); //--- Calculate interpolated RSI if (bearish) { //--- Check for bearish divergence if (rsi_data[b] > interp_rsi + Tolerance) return false; //--- Return false if RSI exceeds line plus tolerance } else { //--- Check for bullish divergence if (rsi_data[b] < interp_rsi - Tolerance) return false; //--- Return false if RSI below line minus tolerance } } return true; //--- Return true if divergence is clean } //+------------------------------------------------------------------+ //| Calculate Visual Angle | //+------------------------------------------------------------------+ double CalculateVisualAngle(long chart_id, int sub_window, datetime time1, double val1, datetime time2, double val2) { int x1 = 0, y1 = 0, x2 = 0, y2 = 0; //--- Initialize pixel coordinates bool ok1 = ChartTimePriceToXY(chart_id, sub_window, time1, val1, x1, y1); //--- Convert first point to XY bool ok2 = ChartTimePriceToXY(chart_id, sub_window, time2, val2, x2, y2); //--- Convert second point to XY if (!ok1 || !ok2 || x1 == x2) return 0.0; //--- Return zero if conversion failed or same x double dx = (double)(x2 - x1); //--- Calculate delta x double dy = (double)(y2 - y1); //--- Calculate delta y if (dx == 0.0) return (dy > 0.0 ? -90.0 : 90.0); //--- Handle vertical line case double angle = MathArctan(-dy / dx) * 180.0 / M_PI; //--- Calculate angle in degrees return MathAbs(angle); //--- Return absolute angle }
Wir implementieren die Funktion „CleanDivergence“, um zu überprüfen, ob die Divergenzlinie zwischen zwei RSI-Punkten nicht von RSI-Zwischenwerten überschritten wird, um ein „sauberes“ Muster ohne Verletzungen zu gewährleisten. Sie akzeptiert Parameter wie „rsi1“ und „rsi2“ für die RSI-Werte bei den Swings, „shift1“ und „shift2“ für ihre Balkenverschiebungen (wobei „shift1“ erwartungsgemäß größer ist), einen Verweis auf das Array „rsi_data“ und einen booleschen Wert zur Unterscheidung der Divergenzart. Zunächst werden die Verschiebungen validiert, wobei false zurückgegeben wird, wenn sie ungültig sind. Dann wird eine Schleife durch die Balken zwischen „shift2 + 1“ und „shift1 – 1“ gezogen, wobei ein „interp_factor“ als normalisierte Position und ein „interp_rsi“ als linear interpolierter Wert zwischen „rsi1“ und „rsi2“ berechnet wird. Im Falle eines Abwärtstrends prüfen wir, ob „rsi_data[b]“ den Wert „interp_rsi + Toleranz“ überschreitet und geben bei Überschreitung „false“ zurück; im Falle eines Aufwärtstrends stellen wir sicher, dass „rsi_data[b]“ nicht unter „interp_rsi – Toleranz“ fällt. Wenn alle Prüfungen erfolgreich sind, geben wir „true“ zurück, um zu bestätigen, dass die Divergenz sauber und zuverlässig für die Signalisierung ist.
Als Nächstes definieren wir die Funktion „CalculateVisualAngle“, um den visuellen Steigungswinkel in Grad zwischen zwei Punkten auf dem Chart zu berechnen, was bei der Divergenzfilterung hilfreich ist. Er benötigt „chart_id“ für die Kennung des Charts, „sub_window“ zur Angabe des Haupt- oder Unterfensters sowie „time1“, „val1“, „time2“ und „val2“ für die Koordinaten. Wir initialisieren die Pixelvariablen „x1“, „y1“, „x2“, „y2“ auf Null, konvertieren dann den Zeit und Preis des Charts in XY-Pixel mit ChartTimePriceToXY für beide Punkte und speichern den Erfolg in „ok1“ und „ok2“. Wenn die Umwandlung fehlschlägt oder die x-Koordinaten übereinstimmen, wird 0,0 zurückgegeben; andernfalls wird „dx“ als „x2-x1“ und „dy“ als „y2-y1“ berechnet. Bei vertikalen Linien, bei denen „dx“ gleich Null ist, wird -90,0 oder 90,0 zurückgegeben, je nach Vorzeichen von „dy“; andernfalls wird der „Winkel“ mit MathArctan auf „-dy / dx“ berechnet, durch Multiplikation mit 180,0 durch M_PI in Grad umgewandelt und sein absoluter Wert über MathAbs für ein positives Steigungsmaß zurückgegeben. Wir haben alle Funktionen, die uns bei der Identifizierung der Abweichungen helfen, und können diese in OnTick implementieren. Wir beginnen mit der versteckten Abwärts-Divergenz.
//+------------------------------------------------------------------+ //| Expert Tick Function | //+------------------------------------------------------------------+ void OnTick() { static datetime last_time = 0; //--- Store last processed time datetime current_time = iTime(_Symbol, _Period, 0); //--- Get current bar time if (current_time == last_time) return; //--- Exit if bar not new last_time = current_time; //--- Update last time int data_size = 200; //--- Set data size for analysis double high_data[], low_data[], rsi_data[]; //--- Declare arrays for high, low, RSI data datetime time_data[]; //--- Declare array for time data CopyHigh(_Symbol, _Period, 0, data_size, high_data); //--- Copy high prices CopyLow(_Symbol, _Period, 0, data_size, low_data); //--- Copy low prices CopyTime(_Symbol, _Period, 0, data_size, time_data); //--- Copy time values CopyBuffer(RSI_Handle, 0, 0, data_size, rsi_data); //--- Copy RSI values ArraySetAsSeries(high_data, true); //--- Set high data as series ArraySetAsSeries(low_data, true); //--- Set low data as series ArraySetAsSeries(time_data, true); //--- Set time data as series ArraySetAsSeries(rsi_data, true); //--- Set RSI data as series long chart_id = ChartID(); //--- Get current chart ID int rsi_window = ChartWindowFind(chart_id, "RSI(" + IntegerToString(RSI_Period) + ")"); //--- Find RSI subwindow // Find latest swing high int last_high_bar = -1, prev_high_bar = -1; //--- Initialize swing high bars for (int b = 1; b < data_size - Swing_Strength; b++) { //--- Iterate through bars if (CheckSwingHigh(b, high_data)) { //--- Check for swing high if (last_high_bar == -1) { //--- Check if first swing high last_high_bar = b; //--- Set last high bar } else { //--- Second swing high found prev_high_bar = b; //--- Set previous high bar break; //--- Exit loop } } } if (last_high_bar > 0 && time_data[last_high_bar] > Last_High_Time) { //--- Check new swing high Prev_High_Price = Last_High_Price; //--- Update previous high price Prev_High_Time = Last_High_Time; //--- Update previous high time Last_High_Price = high_data[last_high_bar]; //--- Set last high price Last_High_Time = time_data[last_high_bar]; //--- Set last high time Prev_High_RSI = Last_High_RSI; //--- Update previous high RSI Last_High_RSI = rsi_data[last_high_bar]; //--- Set last high RSI string high_type = "H"; //--- Set default high type if (Prev_High_Price > 0.0) { //--- Check if previous high exists high_type = (Last_High_Price > Prev_High_Price) ? "HH" : "LH"; //--- Set high type } bool lower_high = Last_High_Price < Prev_High_Price; //--- Check for lower high bool higher_rsi_high = Last_High_RSI > Prev_High_RSI; //--- Check for higher RSI high int bars_diff = prev_high_bar - last_high_bar; //--- Calculate bars between highs bool hidden_bear_div = false; //--- Initialize hidden bearish divergence flag double price_angle = 0.0; //--- Initialize price angle double rsi_angle = 0.0; //--- Initialize RSI angle if (Prev_High_Price > 0.0 && lower_high && higher_rsi_high && bars_diff >= Min_Bars_Between && bars_diff <= Max_Bars_Between) { //--- Check hidden bearish divergence conditions if (CleanDivergence(Prev_High_RSI, Last_High_RSI, prev_high_bar, last_high_bar, rsi_data, true)) { //--- Check clean divergence price_angle = CalculateVisualAngle(chart_id, 0, Prev_High_Time, Prev_High_Price, Last_High_Time, Last_High_Price); //--- Calculate price angle rsi_angle = CalculateVisualAngle(chart_id, rsi_window, Prev_High_Time, Prev_High_RSI, Last_High_Time, Last_High_RSI); //--- Calculate RSI angle if ((!Use_Price_Slope_Filter || (price_angle >= Price_Min_Slope_Degrees && price_angle <= Price_Max_Slope_Degrees)) && (!Use_RSI_Slope_Filter || (rsi_angle >= RSI_Min_Slope_Degrees && rsi_angle <= RSI_Max_Slope_Degrees))) { //--- Check slope filters hidden_bear_div = true; //--- Set hidden bearish divergence flag // Draw divergence lines string line_name = "DivLine_HiddenBear_" + TimeToString(Last_High_Time); //--- Set divergence line name ObjectCreate(chart_id, line_name, OBJ_TREND, 0, Prev_High_Time, Prev_High_Price, Last_High_Time, Last_High_Price); //--- Create trend line for price divergence ObjectSetInteger(chart_id, line_name, OBJPROP_COLOR, Bear_Color); //--- Set line color ObjectSetInteger(chart_id, line_name, OBJPROP_WIDTH, Line_Width); //--- Set line width ObjectSetInteger(chart_id, line_name, OBJPROP_STYLE, Line_Style); //--- Set line style ObjectSetInteger(chart_id, line_name, OBJPROP_RAY, false); //--- Disable ray ObjectSetInteger(chart_id, line_name, OBJPROP_BACK, false); //--- Set to foreground if (rsi_window != -1) { //--- Check if RSI subwindow found string rsi_line = "DivLine_RSI_HiddenBear_" + TimeToString(Last_High_Time); //--- Set RSI divergence line name ObjectCreate(chart_id, rsi_line, OBJ_TREND, rsi_window, Prev_High_Time, Prev_High_RSI, Last_High_Time, Last_High_RSI); //--- Create trend line for RSI divergence ObjectSetInteger(chart_id, rsi_line, OBJPROP_COLOR, Bear_Color); //--- Set line color ObjectSetInteger(chart_id, rsi_line, OBJPROP_WIDTH, Line_Width); //--- Set line width ObjectSetInteger(chart_id, rsi_line, OBJPROP_STYLE, Line_Style); //--- Set line style ObjectSetInteger(chart_id, rsi_line, OBJPROP_RAY, false); //--- Disable ray ObjectSetInteger(chart_id, rsi_line, OBJPROP_BACK, false); //--- Set to foreground } } } } // Draw swing label on price if enabled if (Mark_Swings_On_Price) { //--- Check if marking swings on price enabled string swing_name = "SwingHigh_" + TimeToString(Last_High_Time); //--- Set swing high label name if (ObjectFind(chart_id, swing_name) < 0) { //--- Check if label exists string high_label_text = " " + high_type + (hidden_bear_div ? " Hidden Bear Div " + DoubleToString(price_angle, 1) + "°" : ""); //--- Set label text with angle if divergence ObjectCreate(chart_id, swing_name, OBJ_TEXT, 0, Last_High_Time, Last_High_Price); //--- Create swing high label ObjectSetString(chart_id, swing_name, OBJPROP_TEXT, high_label_text); //--- Set label text ObjectSetInteger(chart_id, swing_name, OBJPROP_COLOR, Swing_High_Color); //--- Set label color ObjectSetInteger(chart_id, swing_name, OBJPROP_ANCHOR, ANCHOR_LEFT_LOWER); //--- Set label anchor ObjectSetInteger(chart_id, swing_name, OBJPROP_FONTSIZE, Font_Size); //--- Set label font size } } // Draw corresponding swing label on RSI if enabled if (Mark_Swings_On_RSI && rsi_window != -1) { //--- Check if marking swings on RSI enabled and subwindow found string rsi_swing_name = "RSI_SwingHigh_" + TimeToString(Last_High_Time); //--- Set RSI swing high label name if (ObjectFind(chart_id, rsi_swing_name) < 0) { //--- Check if label exists string high_label_text_rsi = " " + high_type + (hidden_bear_div ? " Hidden Bear Div " + DoubleToString(rsi_angle, 1) + "°" : ""); //--- Set label text with RSI angle if divergence ObjectCreate(chart_id, rsi_swing_name, OBJ_TEXT, rsi_window, Last_High_Time, Last_High_RSI); //--- Create RSI swing high label ObjectSetString(chart_id, rsi_swing_name, OBJPROP_TEXT, high_label_text_rsi); //--- Set label text ObjectSetInteger(chart_id, rsi_swing_name, OBJPROP_COLOR, Swing_High_Color); //--- Set label color ObjectSetInteger(chart_id, rsi_swing_name, OBJPROP_ANCHOR, ANCHOR_LEFT_LOWER); //--- Set label anchor ObjectSetInteger(chart_id, rsi_swing_name, OBJPROP_FONTSIZE, Font_Size); //--- Set label font size } } ChartRedraw(chart_id); //--- Redraw chart } }
In der Funktion OnTick, die bei jedem neuen Tick ausgeführt wird, verwenden wir zunächst eine statische Variable „last_time“, um den Zeitstempel des zuletzt verarbeiteten Balkens zu verfolgen, indem wir die aktuelle Balkenzeit mit iTime über _Symbol, _Period und 0 abrufen, bei unveränderten Werten vorzeitig beenden, um redundante Berechnungen zu vermeiden, und dann „last_time“ aktualisieren. Wir definieren eine „data_size“ von 200 Bars für die Analyse und deklarieren Arrays für „high_data“, „low_data“, „rsi_data“, und „time_data“und füllen sie mit CopyHigh, CopyLow, CopyTime und CopyBuffer, um die jüngsten Höchst- und Tiefstkurse, Zeitstempel und RSI-Werte von „RSI_Handle“ zu erhalten.
Wir setzen diese Arrays als Serien mit „ArraySetAsSeries“ für die umgekehrte Indizierung (jüngste zuerst) und erhalten die „chart_id“ über „ChartID“, zusammen mit dem Auffinden des RSI-Subwindow-Index mit ChartWindowFind basierend auf dem Perioden-formatierten Namen. Um die letzten hohen Umkehrpunkte zu identifizieren, initialisieren wir „last_high_bar“ und „prev_high_bar“ auf -1, führen eine Schleife von 1 bis „data_size – Swing_Strength“ durch und rufen „CheckSwingHigh“ auf, um die beiden letzten gültigen hohen Umkehrpunkte zu finden, setzen sie und brechen nach dem zweiten.
Wenn durch den Vergleich von „time_data[last_high_bar]“ mit „Last_High_Time“ ein neuer hoher Umkehrpunkt festgestellt wird, verschieben wir die Variablen für vorherige Hochs, um die aktuellen letzten Werte zu halten, aktualisieren „Last_High_Price“, „Last_High_Time“ und „Last_High_RSI“ aus den Arrays und weisen „high_type“ standardmäßig den Wert „H“ zu, oder „HH“ für ein höheres Hoch oder „LH“ für ein niedrigeres Hoch, wenn ein vorheriges existiert. Dann bewerten wir die versteckte Abwärts-Divergenz: Wenn „Prev_High_Price“ gesetzt ist, der Preis ein niedrigeres Hoch, der RSI ein höheres Hoch zeigt und die Balkendifferenz (“prev_high_bar – last_high_bar“) innerhalb von „Min_Bars_Between“ und „Max_Bars_Between“ liegt, rufen wir „CleanDivergence“ auf mit den vorherigen und letzten RSI-Hochs, ihren Verschiebungen, „rsi_data“, und true für fallend.
Wenn sauber, berechnen wir „price_angle“ mit „CalculateVisualAngle“ auf dem Hauptchart (Unterfenster 0) mit hohen Zeiten und Preisen und „rsi_angle“ auf dem RSI-Unterfenster mit hohen Zeiten und RSI-Werten. Wir überprüfen die Steigungsfilter: wenn kein Preisfilter verwendet wird oder „price_angle“ zwischen „Price_Min_Slope_Degrees“ und „Price_Max_Slope_Degrees“ liegt, und ähnlich für RSI mit „Use_RSI_Slope_Filter“, „rsi_angle“, „RSI_Min_Slope_Degrees“ und „RSI_Max_Slope_Degrees“, setzen wir „hidden_bear_div“ auf true, zeichnen Divergenzlinien und erstellen eine Trendlinie namens „DivLine_HiddenBear_“ plus Zeitstempel im Hauptchart mit ObjectCreate, das die Hochs verbindet, und konfigurieren Eigenschaften wie „OBJPROP_COLOR“ auf „Bear_Color“, „OBJPROP_WIDTH“ auf „Line_Width“, „OBJPROP_STYLE“ auf „Line_Style“, deaktivieren die Option Strahl und setzen den Vordergrunds über die Funktion ObjectSetInteger. Wenn das „rsi_window“ gültig ist, zeichnen wir ebenfalls eine RSI-Linie mit dem Namen „DivLine_RSI_HiddenBear_“ in das Unterfenster, die RSI-Hochs mit passenden Einstellungen verbindet.
Für die Kennzeichnung von Umkehrpunkten, wenn „Mark_Swings_On_Price“ wahr ist, prüfen wir, ob ein Textobjekt mit dem Namen „SwingHigh_“ plus Zeitstempel mit „ObjectFind“ existiert; falls nicht vorhanden, erstellen wir es mit „ObjectCreate“ am Hochpunkt, setzen den Text mit „ObjectSetString“, einschließlich „high_type“ und „Hidden Bear Div“ plus formatiertem „price_angle“, falls abweichend, setzen „Swing_High_Color“, den Anker mit „ANCHOR_LEFT_LOWER“ und „Font_Size“ mit „ObjectSetInteger“. Wenn „Mark_Swings_On_RSI“ wahr ist und ein Unterfenster gefunden wird, machen wir dasselbe für ein RSI-Label mit dem Namen „RSI_SwingHigh_“ am RSI-Hoch, mit Text einschließlich des „rsi_angle“, falls divergent, derselben Farbe, dem Anker und der Größe. Abschließend wird das Chart mit der Funktion ChartRedraw neu gezeichnet. Nach dem Kompilieren erhalten wir folgendes Ergebnis:

Jetzt, da wir die versteckte Abwärtsdivergenz identifizieren und visualisieren können, müssen wir sie handeln, indem wir ein Verkaufsgeschäft eröffnen. Bevor wir jedoch ein Verkaufsgeschäft eröffnen, wollen wir alle offenen Positionen schließen, um ein Überhandeln zu vermeiden. Wenn Sie möchten, können Sie sie aber weiter halten. Hierfür benötigen wir einige Hilfsfunktionen.
//+------------------------------------------------------------------+ //| Open Buy Position | //+------------------------------------------------------------------+ void OpenBuy() { double ask_price = SymbolInfoDouble(_Symbol, SYMBOL_ASK); //--- Get current ask price double sl = (SL_Pips > 0) ? NormalizeDouble(ask_price - SL_Pips * _Point, _Digits) : 0; //--- Calculate stop loss if enabled double tp = (TP_Pips > 0) ? NormalizeDouble(ask_price + TP_Pips * _Point, _Digits) : 0; //--- Calculate take profit if enabled if (obj_Trade.PositionOpen(_Symbol, ORDER_TYPE_BUY, Lot_Size, 0, sl, tp)) { //--- Attempt to open buy position Print("Buy trade opened on hidden bullish divergence"); //--- Log buy trade open } else { //--- Handle open failure Print("Failed to open Buy: ", obj_Trade.ResultRetcodeDescription()); //--- Log error } } //+------------------------------------------------------------------+ //| Open Sell Position | //+------------------------------------------------------------------+ void OpenSell() { double bid_price = SymbolInfoDouble(_Symbol, SYMBOL_BID); //--- Get current bid price double sl = (SL_Pips > 0) ? NormalizeDouble(bid_price + SL_Pips * _Point, _Digits) : 0; //--- Calculate stop loss if enabled double tp = (TP_Pips > 0) ? NormalizeDouble(bid_price - TP_Pips * _Point, _Digits) : 0; //--- Calculate take profit if enabled if (obj_Trade.PositionOpen(_Symbol, ORDER_TYPE_SELL, Lot_Size, 0, sl, tp)) { //--- Attempt to open sell position Print("Sell trade opened on hidden bearish divergence"); //--- Log sell trade open } else { //--- Handle open failure Print("Failed to open Sell: ", obj_Trade.ResultRetcodeDescription()); //--- Log error } } //+------------------------------------------------------------------+ //| Close All Positions | //+------------------------------------------------------------------+ void CloseAll() { for (int p = PositionsTotal() - 1; p >= 0; p--) { //--- Iterate through positions in reverse if (PositionGetTicket(p) > 0 && PositionGetString(POSITION_SYMBOL) == _Symbol && PositionGetInteger(POSITION_MAGIC) == Magic_Number) { //--- Check position details obj_Trade.PositionClose(PositionGetTicket(p)); //--- Close position } } }
Wir definieren die Funktion „OpenBuy“, um die Eröffnung von Kaufpositionen bei Erkennung einer versteckten Aufwärtsdivergenz zu steuern. Zunächst wird der aktuelle Briefkurs (ask) mit SymbolInfoDouble abgefragt, wobei _Symbol und „SYMBOL_ASK“ übergeben werden. Wenn „SL_Pips“ größer als Null ist, berechnen wir den Stop Loss als „ask_price – SL_Pips * _Point“, normalisiert auf „_Digits“ mit NormalizeDouble; andernfalls setzen wir ihn auf Null. Ähnlich verhält es sich mit dem Take Profit: Wenn „TP_Pips“ positiv ist, berechnen wir „ask_price + TP_Pips * _Point“ normalisiert, oder Null, wenn deaktiviert. Wir versuchen, die Kaufposition über „obj_Trade.PositionOpen“ zu eröffnen, wobei wir das Symbol, ORDER_TYPE_BUY, „Lot_Size“, Null Slippage und die berechneten Sl und Tp übergeben; bei Erfolg protokollieren wir eine Meldung mit „Print“, andernfalls drucken wir die Fehlerbeschreibung aus „obj_Trade.ResultRetcodeDescription“.
Als Nächstes erstellen wir die Funktion „OpenSell“ für Verkaufspositionen bei versteckten Abwärts-Signalen, wobei wir die Struktur widerspiegeln: Wir holen uns den Geldkurs mit „SymbolInfoDouble“ und übergeben „_Symbol“ und „SYMBOL_BID“, setzen sl als „bid_price + SL_Pips * _Point“, wenn aktiviert, tp als „bid_price – TP_Pips * _Point“, wenn gesetzt, und verwenden „obj_Trade.PositionOpen“ mit „ORDER_TYPE_SELL“. Wir protokollieren Erfolg oder Misserfolg auf ähnliche Weise.
Schließlich implementieren wir die Funktion „CloseAll“, um bestehende Handelsgeschäfte vor neuen zu schließen, wobei wir zur Sicherheit eine Rückwärtsschleife von der Funktion PositionsTotal minus 1 bis Null durchführen. Für jede Position, wenn PositionGetTicket mit p ein gültiges Ticket größer als Null liefert und mit unserem „_Symbol“ über „PositionGetString“ mit „POSITION_SYMBOL“ und „Magic_Number“ mit „PositionGetInteger“ mit „POSITION_MAGIC“ übereinstimmt, schließen wir es mit „obj_Trade.PositionClose“ auf dem Ticket. Jetzt müssen wir nur noch die Funktionen hinzufügen, um alle Positionen zu schließen und die entsprechenden Positionen zu öffnen. Wir nennen sie folgendermaßen.
// Hidden bearish divergence detected - Sell signal CloseAll(); //--- Close all open positions OpenSell(); //--- Open sell position
Nach der Kompilierung erhalten wir das folgende Ergebnis.

Wir sehen, dass wir mit dem Signal der Abwärts-Divergenz handeln können. Eine ähnliche Logik müssen wir nun auch für die Aufwärts-Divergenz anwenden. Hier ist die Logik, die wir angepasst haben, um das zu erreichen.
// Find latest swing low int last_low_bar = -1, prev_low_bar = -1; //--- Initialize swing low bars for (int b = 1; b < data_size - Swing_Strength; b++) { //--- Iterate through bars if (CheckSwingLow(b, low_data)) { //--- Check for swing low if (last_low_bar == -1) { //--- Check if first swing low last_low_bar = b; //--- Set last low bar } else { //--- Second swing low found prev_low_bar = b; //--- Set previous low bar break; //--- Exit loop } } } if (last_low_bar > 0 && time_data[last_low_bar] > Last_Low_Time) { //--- Check new swing low Prev_Low_Price = Last_Low_Price; //--- Update previous low price Prev_Low_Time = Last_Low_Time; //--- Update previous low time Last_Low_Price = low_data[last_low_bar]; //--- Set last low price Last_Low_Time = time_data[last_low_bar]; //--- Set last low time Prev_Low_RSI = Last_Low_RSI; //--- Update previous low RSI Last_Low_RSI = rsi_data[last_low_bar]; //--- Set last low RSI string low_type = "L"; //--- Set default low type if (Prev_Low_Price > 0.0) { //--- Check if previous low exists low_type = (Last_Low_Price < Prev_Low_Price) ? "LL" : "HL"; //--- Set low type } bool higher_low = Last_Low_Price > Prev_Low_Price; //--- Check for higher low bool lower_rsi_low = Last_Low_RSI < Prev_Low_RSI; //--- Check for lower RSI low int bars_diff = prev_low_bar - last_low_bar; //--- Calculate bars between lows bool hidden_bull_div = false; //--- Initialize hidden bullish divergence flag double price_angle = 0.0; //--- Initialize price angle double rsi_angle = 0.0; //--- Initialize RSI angle if (Prev_Low_Price > 0.0 && higher_low && lower_rsi_low && bars_diff >= Min_Bars_Between && bars_diff <= Max_Bars_Between) { //--- Check hidden bullish divergence conditions if (CleanDivergence(Prev_Low_RSI, Last_Low_RSI, prev_low_bar, last_low_bar, rsi_data, false)) { //--- Check clean divergence price_angle = CalculateVisualAngle(chart_id, 0, Prev_Low_Time, Prev_Low_Price, Last_Low_Time, Last_Low_Price); //--- Calculate price angle rsi_angle = CalculateVisualAngle(chart_id, rsi_window, Prev_Low_Time, Prev_Low_RSI, Last_Low_Time, Last_Low_RSI); //--- Calculate RSI angle if ((!Use_Price_Slope_Filter || (price_angle >= Price_Min_Slope_Degrees && price_angle <= Price_Max_Slope_Degrees)) && (!Use_RSI_Slope_Filter || (rsi_angle >= RSI_Min_Slope_Degrees && rsi_angle <= RSI_Max_Slope_Degrees))) { //--- Check slope filters hidden_bull_div = true; //--- Set hidden bullish divergence flag // Hidden bullish divergence detected - Buy signal CloseAll(); //--- Close all open positions OpenBuy(); //--- Open buy position // Draw divergence lines string line_name = "DivLine_HiddenBull_" + TimeToString(Last_Low_Time); //--- Set divergence line name ObjectCreate(chart_id, line_name, OBJ_TREND, 0, Prev_Low_Time, Prev_Low_Price, Last_Low_Time, Last_Low_Price); //--- Create trend line for price divergence ObjectSetInteger(chart_id, line_name, OBJPROP_COLOR, Bull_Color); //--- Set line color ObjectSetInteger(chart_id, line_name, OBJPROP_WIDTH, Line_Width); //--- Set line width ObjectSetInteger(chart_id, line_name, OBJPROP_STYLE, Line_Style); //--- Set line style ObjectSetInteger(chart_id, line_name, OBJPROP_RAY, false); //--- Disable ray ObjectSetInteger(chart_id, line_name, OBJPROP_BACK, false); //--- Set to foreground if (rsi_window != -1) { //--- Check if RSI subwindow found string rsi_line = "DivLine_RSI_HiddenBull_" + TimeToString(Last_Low_Time); //--- Set RSI divergence line name ObjectCreate(chart_id, rsi_line, OBJ_TREND, rsi_window, Prev_Low_Time, Prev_Low_RSI, Last_Low_Time, Last_Low_RSI); //--- Create trend line for RSI divergence ObjectSetInteger(chart_id, rsi_line, OBJPROP_COLOR, Bull_Color); //--- Set line color ObjectSetInteger(chart_id, rsi_line, OBJPROP_WIDTH, Line_Width); //--- Set line width ObjectSetInteger(chart_id, rsi_line, OBJPROP_STYLE, Line_Style); //--- Set line style ObjectSetInteger(chart_id, rsi_line, OBJPROP_RAY, false); //--- Disable ray ObjectSetInteger(chart_id, rsi_line, OBJPROP_BACK, false); //--- Set to foreground } } } } // Draw swing label on price if enabled if (Mark_Swings_On_Price) { //--- Check if marking swings on price enabled string swing_name = "SwingLow_" + TimeToString(Last_Low_Time); //--- Set swing low label name if (ObjectFind(chart_id, swing_name) < 0) { //--- Check if label exists string low_label_text = " " + low_type + (hidden_bull_div ? " Hidden Bull Div " + DoubleToString(price_angle, 1) + "°" : ""); //--- Set label text with angle if divergence ObjectCreate(chart_id, swing_name, OBJ_TEXT, 0, Last_Low_Time, Last_Low_Price); //--- Create swing low label ObjectSetString(chart_id, swing_name, OBJPROP_TEXT, low_label_text); //--- Set label text ObjectSetInteger(chart_id, swing_name, OBJPROP_COLOR, Swing_Low_Color); //--- Set label color ObjectSetInteger(chart_id, swing_name, OBJPROP_ANCHOR, ANCHOR_LEFT_UPPER); //--- Set label anchor ObjectSetInteger(chart_id, swing_name, OBJPROP_FONTSIZE, Font_Size); //--- Set label font size } } // Draw corresponding swing label on RSI if enabled if (Mark_Swings_On_RSI && rsi_window != -1) { //--- Check if marking swings on RSI enabled and subwindow found string rsi_swing_name = "RSI_SwingLow_" + TimeToString(Last_Low_Time); //--- Set RSI swing low label name if (ObjectFind(chart_id, rsi_swing_name) < 0) { //--- Check if label exists string low_label_text_rsi = " " + low_type + (hidden_bull_div ? " Hidden Bull Div " + DoubleToString(rsi_angle, 1) + "°" : ""); //--- Set label text with RSI angle if divergence ObjectCreate(chart_id, rsi_swing_name, OBJ_TEXT, rsi_window, Last_Low_Time, Last_Low_RSI); //--- Create RSI swing low label ObjectSetString(chart_id, rsi_swing_name, OBJPROP_TEXT, low_label_text_rsi); //--- Set label text ObjectSetInteger(chart_id, rsi_swing_name, OBJPROP_COLOR, Swing_Low_Color); //--- Set label color ObjectSetInteger(chart_id, rsi_swing_name, OBJPROP_ANCHOR, ANCHOR_LEFT_UPPER); //--- Set label anchor ObjectSetInteger(chart_id, rsi_swing_name, OBJPROP_FONTSIZE, Font_Size); //--- Set label font size } } ChartRedraw(chart_id); //--- Redraw chart }
Hier haben wir die gleiche Logik wie beim Signal der Abwärts-Divergenz angewandt, nur unter umgekehrten Bedingungen. Wir haben Kommentare hinzugefügt, damit es selbsterklärend ist. Nach dem Kompilieren erhalten wir folgendes Ergebnis.

Aus dem Bild können wir ersehen, dass wir auch mit den Aufwärts-Divergenzen handeln können. Jetzt müssen wir die Gewinne optimieren, indem wir einen Trailing-Stop hinzufügen. Auch dafür werden wir eine Funktion definieren. Das hilft, den Code modular zu halten.
//+------------------------------------------------------------------+ //| Apply Trailing Stop to Positions | //+------------------------------------------------------------------+ void ApplyTrailingStop() { double point = _Point; //--- Get symbol point value for (int i = PositionsTotal() - 1; i >= 0; i--) { //--- Iterate through positions in reverse if (PositionGetTicket(i) > 0) { //--- Check valid ticket if (PositionGetString(POSITION_SYMBOL) == _Symbol && PositionGetInteger(POSITION_MAGIC) == Magic_Number) { //--- Check symbol and magic double sl = PositionGetDouble(POSITION_SL); //--- Get current stop loss double tp = PositionGetDouble(POSITION_TP); //--- Get current take profit double openPrice = PositionGetDouble(POSITION_PRICE_OPEN); //--- Get open price ulong ticket = PositionGetInteger(POSITION_TICKET); //--- Get position ticket if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) { //--- Check buy position double newSL = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_BID) - Trailing_Stop_Pips * point, _Digits); //--- Calculate new stop loss if (newSL > sl && SymbolInfoDouble(_Symbol, SYMBOL_BID) - openPrice > Min_Profit_To_Trail_Pips * point) { //--- Check trailing conditions obj_Trade.PositionModify(ticket, newSL, tp); //--- Modify position with new stop loss } } else if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) { //--- Check sell position double newSL = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_ASK) + Trailing_Stop_Pips * point, _Digits); //--- Calculate new stop loss if (newSL < sl && openPrice - SymbolInfoDouble(_Symbol, SYMBOL_ASK) > Min_Profit_To_Trail_Pips * point) { //--- Check trailing conditions obj_Trade.PositionModify(ticket, newSL, tp); //--- Modify position with new stop loss } } } } } }
Wir implementieren die Funktion „ApplyTrailingStop“, um Stop-Losses für offene Positionen dynamisch anzupassen, sobald sie einen profitablen Schwellenwert erreichen, und so Gewinne zu sichern, wenn sich der Markt günstig entwickelt. Wir beginnen damit, den Punktwert des Symbols „point“ zuzuweisen, indem wir _Point für Pip-Berechnungen verwenden. Dann gehen wir in einer Schleife rückwärts durch alle Positionen vom Ergebnis der Funktion PositionsTotal minus 1 bis Null, um Änderungen ohne Indexverschiebungen sicher zu behandeln.
Für jede Position, wenn PositionGetTicket für i ein gültiges Ticket größer als Null zurückgibt, überprüfen wir, ob es zu unserem Symbol gehört, indem wir PositionGetString mit „POSITION_SYMBOL“ aufrufen und prüfen, ob es gleich _Symbol ist, und unseren Kennzeichnung über „PositionGetInteger“ mit „POSITION_MAGIC“ mit „Magic_Number“ abgleichen. Wir holen uns den aktuellen „sl“ mit PositionGetDouble über „POSITION_SL“, „tp“ über „PositionGetDouble“ mit „POSITION_TP“, „openPrice“ von „PositionGetDouble“ mit „POSITION_PRICE_OPEN“, und das „ticket“ über „PositionGetInteger“ mit „POSITION_TICKET“.
Wenn es sich um eine Kaufposition handelt, die durch „PositionGetInteger“ mit „POSITION_TYPE“ gleich „POSITION_TYPE_BUY“ geprüft wurde, berechnen wir „newSL“ als das aktuelle Gebot, das aus „SymbolInfoDouble“ über „_Symbol“ und „SYMBOL_BID“, minus „Trailing_Stop_Pips * Punkt“, normalisiert auf „_Digits“ mit „NormalizeDouble“. Wir testen dann, ob dieser „newSL“ größer als der bestehende „sl“ ist und der Gewinn, berechnet als das Gebot aus „SymbolInfoDouble“ minus „openPrice“, „Min_Profit_To_Trail_Pips * point“ übersteigt; wenn ja, aktualisieren wir die Position mit „obj_Trade.PositionModify“ mit dem Ticket, dem neuen SL und dem unveränderten TP.
Für Verkaufspositionen, bei denen „POSITION_TYPE“ gleich POSITION_TYPE_SELL aus „PositionGetInteger“ ist, berechnen wir „newSL“ als den Ask aus „SymbolInfoDouble“ über „_Symbol“ und „SYMBOL_ASK“, plus „Trailing_Stop_Pips * point“, ebenfalls normalisiert. Wir prüfen, ob „newSL“ kleiner als der aktuelle „sl“ ist und der Gewinn, abgeleitet aus „openPrice“ minus Ask über „SymbolInfoDouble“, die Mindestschwelle übersteigt, und modifizieren dann entsprechend mit „obj_Trade.PositionModify“. Dadurch wird sichergestellt, dass das Trailing nur bei qualifizierten Handelsgeschäften aktiviert wird, ohne andere zu beeinträchtigen. Jetzt können wir die Funktion in unserem Tick- Ereignisbehandlung aufrufen, um die schwere Arbeit zu erledigen.
//+------------------------------------------------------------------+ //| Expert Tick Function | //+------------------------------------------------------------------+ void OnTick() { if (Enable_Trailing_Stop && PositionsTotal() > 0) { //--- Check if trailing stop enabled ApplyTrailingStop(); //--- Apply trailing stop to positions } static datetime last_time = 0; //--- Store last processed time datetime current_time = iTime(_Symbol, _Period, 0); //--- Get current bar time //--- THE REST OF THE LOGIC }
Wir rufen einfach die obige Trailing-Stop-Funktion bei jedem Tick auf, wenn wir Positionen offen haben, und erhalten das folgende Ergebnis.

Nach der Anwendung des Trailing-Stopps ist das alles. Wir sind fertig. Wir müssen nun sicherstellen, dass wir unsere Chartobjekte löschen, wenn wir den Experten aus dem Chart entfernen, um Unordnung zu vermeiden.
//+------------------------------------------------------------------+ //| Expert Deinitialization Function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { if (RSI_Handle != INVALID_HANDLE) { //--- Check if RSI handle is valid long chart_id = ChartID(); //--- Get current chart ID string rsi_name = "RSI(" + IntegerToString(RSI_Period) + ")"; //--- Generate RSI indicator name int rsi_subwin = ChartWindowFind(chart_id, rsi_name); //--- Find RSI subwindow if (rsi_subwin != -1) { //--- Check if RSI subwindow found ChartIndicatorDelete(chart_id, rsi_subwin, rsi_name); //--- Delete RSI indicator from chart Print("RSI indicator removed from chart"); //--- Log removal } IndicatorRelease(RSI_Handle); //--- Release RSI handle } ObjectsDeleteAll(0, "DivLine_"); //--- Delete all divergence line objects ObjectsDeleteAll(0, "SwingHigh_"); //--- Delete all swing high objects ObjectsDeleteAll(0, "SwingLow_"); //--- Delete all swing low objects ObjectsDeleteAll(0, "RSI_SwingHigh_"); //--- Delete all RSI swing high objects ObjectsDeleteAll(0, "RSI_SwingLow_"); //--- Delete all RSI swing low objects Print("RSI Hidden Divergence EA deinitialized"); //--- Log deinitialization }
Wir definieren die Ereignishandler von OnDeinit, das einen konstanten Integer-Parameter „reason“ akzeptiert, der die Ursache der Deinitialisierung angibt, um Aufräumarbeiten durchzuführen, wenn der Expert Advisor aus dem Chart entladen wird. Wenn „RSI_Handle“ nicht gleich INVALID_HANDLE ist, wird die aktuelle Chart-ID mit „ChartID“ abgerufen und der Name des RSI-Indikators durch Kombination von „RSI(“ mit der über die Funktion IntegerToString umgewandelten Periode konstruiert. Dann suchen wir das RSI-Subfenster mit ChartWindowFind unter Angabe der Chart-ID und des Namens; wenn es gefunden wird (rsi_subwin != -1), entfernen wir den Indikator aus dem Chart mit ChartIndicatorDelete unter Angabe der Chart-ID, des Subfenster-Index und des Namens und protokollieren die Entfernung. Danach geben wir die Ressourcen des Indikators frei, indem wir IndicatorRelease mit dem RSI-Handle aufrufen.
Als Nächstes löschen wir alle Chartobjekte, indem wir die Funktion ObjectsDeleteAll mehrmals aufrufen: zuerst mit dem Teilfenster 0 und dem Präfix „DivLine_“, um die Divergenzlinien zu löschen, dann „SwingHigh_“ für die Kennzeichnung von hohen Umkehrpunkten, „SwingLow_“ für die Kennzeichnung von tiefen Umkehrpunkten, „RSI_SwingHigh_“ für die Kennzeichnung RSI-Hochs und „RSI_SwingLow_“ für die RSI-Tiefs. Schließlich protokollieren wir den Abschluss der Deinitialisierung mit „Drucken“, um zu bestätigen, dass der RSI Hidden Divergence EA ordnungsgemäß beendet wurde. Da wir unsere Ziele erreicht haben, bleibt nur noch das Backtesting des Programms, das im nächsten Abschnitt behandelt wird.
Backtests
Nach einem gründlichen Backtest erhalten wir folgende Ergebnisse.
Backtest-Grafik:

Bericht des Backtest:

Schlussfolgerung
Abschließend haben wir das Handelssystem einer versteckten RSI-Divergenz in MQL5 entwickelt, das versteckte Aus- und Abwärts-Divergenzen durch hohe und tiefe Umkehrpunkte mit Stärkebestätigung identifiziert, sie mit sauberen Checks innerhalb von Balkenbereich und Toleranz validiert und Signale mit anpassbaren Steigungswinkeln auf Preis- und RSI-Linien für eine verbesserte Genauigkeit filtert. Das System führt Handelsgeschäfte mit einer festen Losgröße, optionalem Stop-Loss und Take-Profit in Pips sowie Trailing-Stops für dynamisches Risikomanagement aus und bietet visuelles Feedback über farbige Trendlinien und beschriftete Umkehrpunkte mit Winkelanzeigen auf Preis- und RSI-Charts.
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 versteckten RSI-Divergenz-Strategie sind Sie für den effektiven Handel mit Fortsetzungssignalen gerüstet und bereit für weitere Optimierungen auf Ihrem Handelsweg. Viel Spaß beim Handeln!
Übersetzt aus dem Englischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/en/articles/20157
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.
Risk-Based Trade Placement EA mit On-Chart UI (Part 1): Gestaltung der Nutzeroberfläche
Entwicklung des Price Action Analysis Toolkit (Teil 48): Multi-Timeframe Harmony Index mit gewichtetem Bias Dashboard
Entwicklung des Price Action Analysis Toolkit (Teil 49): Integration von Trend-, Momentum- und Volatilitätsindikatoren in ein MQL5-System
Aufbau eines Smart Trade Managers in MQL5: Automatisieren Sie Break-Even, Trailing Stop und Teilweises Schließen
- 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.