Automatisieren von Handelsstrategien in MQL5 (Teil 37): Regelmäßige RSI-Divergenz-Konvergenz mit visuellen Indikatoren
Einführung
In unserem letzten Artikel (Teil 36) haben wir ein Handelssystem nach Angebot und Nachfrage in MetaQuotes Language 5 (MQL5) entwickelt, das Zonen von Angebot und Nachfrage durch Konsolidierungsbereiche identifiziert, sie mit impulsiven Bewegungen validiert und Retests mit Trendbestätigung und anpassbaren Risikoparametern gehandelt hat. In Teil 37 entwickeln wir ein Regular RSI Divergence Convergence System mit visuellen Indikatoren. Dieses System erkennt regelmäßige Divergenzen zwischen Kursschwankungen und den Werten des Relative Strength Index (RSI) nach oben und unten, führt Handelsgeschäfte auf Signale mit optionaler Risikokontrolle aus und bietet Visualisierungen auf dem Chart für eine verbesserte Analyse. Wir werden die folgenden Themen behandeln:
- Verstehen der regulären RSI-Divergenz-Konvergenz-Strategie
- Implementation in MQL5
- Backtests
- Schlussfolgerung
Am Ende haben wir eine funktionierende MQL5-Strategie für den Handel mit regelmäßigen RSI-Divergenzen, die Sie anpassen können – legen wir los!
Verstehen der regulären RSI-Divergenz-Konvergenz-Strategie
Die reguläre RSI-Divergenz-Konvergenz-Strategie konzentriert sich auf die Erkennung potenzieller Trendumkehrungen durch die Identifizierung von Diskrepanzen zwischen dem Kursgeschehen und dem Relative Strength Index (RSI)-Oszillator, der das Momentum misst. Bei einer Aufwärtsdivergenzen bildet der Kurs ein tieferes Tief, während der RSI ein höheres Tief bildet, was auf ein nachlassendes Abwärtsmomentum und eine wahrscheinliche Aufwärtstrendwende hindeutet. Bei einer Aufwärtsdivergenz erreicht der Kurs ein höheres Hoch, während der RSI ein niedrigeres Hoch anzeigt, was auf ein nachlassendes Aufwärtsmomentum und eine potenzielle Abwärtsbewegung hinweist.
Wir verwenden bestätigte Umkehrpunkte über eine bestimmte Anzahl von Balken, um diese Muster zu erkennen. Um eine saubere Divergenz zu gewährleisten, wenden wir eine Toleranz an, die Zwischenkreuzungen vermeidet. Der Einstieg wird ausgelöst, wenn eine Aufwärtsdivergenz festgestellt wird – wir gehen bei Schließen des Bestätigungsbalkens eine Kaufposition ein – oder wenn eine Abwärtsdivergenz festgestellt wird – gehen bei Schließen des Bestätigungsbalkens eine Verkaufsposition ein. Positionen werden mit Risikokontrollen verwaltet, einschließlich vordefinierter Stopp- und Gewinnziele sowie dynamischer Trailing-Stops. Indem wir diese Elemente einbeziehen, können wir aus Umkehrsituationen Kapital schlagen. Sehen Sie sich unten die verschiedenen Möglichkeiten an, die wir haben.
Setup der Aufwärtsdivergenz:

Setup der Abwärtsdivergenz:

Unser Plan ist es, mit Hilfe einer Stärkebestätigung nach hohen und tiefen Umkehrpunkten zu suchen, Divergenzen innerhalb festgelegter Balkenbereiche und Toleranzschwellen zu validieren, automatisierte Handelsgeschäfte mit anpassbarer Losgröße und Risikoparametern auszuführen und visuelle Hilfsmittel wie farbige Linien und Etiketten zur einfachen Überwachung hinzuzufügen, um ein System für den auf Divergenzen basierenden Handel aufzubauen. Kurz gesagt, wollen wir Folgendes erreichen:

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 Regular 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 "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 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 die integrierten Funktionen für die Verwaltung von Positionen und Aufträgen zu aktivieren. Als Nächstes definieren wir verschiedene Eingabeparameter, die nach Kategorien gruppiert sind und vom Benutzer 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 rechnen zu lassen. Unter „Swing Settings“ wird „Swing_Strength“ auf 5 gesetzt, um die für die Bestätigung von hohen und tiefen Umkehrpunkten benötigten Balken zu bestimmen, während „Min_Bar_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.
Für „Trade Settings“ haben wir „Lot_Size“ auf 0.01 für feste Positionsgrößen, „Magic_Number“ als 123456789, um unsere Handelsgeschäfte zu identifizieren, und „SL_Pips“ und „TP_Pips“ beide auf 300.0, um optional Stop-Loss- und Take-Profit-Abstände zu setzen (deaktiviert, wenn Null). In der Gruppe „Trailing-Stop-Einstellungen“ ist „Enable_Trailing_Stop“ auf „true“ gesetzt, um dynamische Stops zu aktivieren, mit „Trailing_Stop_Pips“ auf 30,0 für den Trailing-Abstand und „Min_Profit_To_Trail_Pips“ auf 50,0 als Gewinnschwelle für den Beginn des Trailing. Im Bereich der Visualisierung legen wir Farben wie „Bull_Color“ als clrGreen für Aufwärtsdivergenzen, „Bear_Color“ als clrRed für Abwärtsdivergenzen, „Swing_High_Color“ als clrRed, und „Swing_Low_Color“ als clrGreen für Beschriftungen, zusammen mit „Line_Width“ auf 2, „Line_Style“ als STYLE_SOLID für Divergenzlinien und „Font_Size“ auf 8 für Textbeschriftungen.
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 Umkehrpunkte 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 hohen Umkehrpunktes sowie „Prev_High_Price“ und „Prev_High_Time“ für den davor. In ähnlicher Weise gibt es für tiefe 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 Tiefstwerte 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 auf ihm zeichnen können.
//+------------------------------------------------------------------+ //| 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 Divergence EA initialized"); //--- Log initialization success return(INIT_SUCCEEDED); //--- Return initialization success }
In der Ereignisbehandlung von OnInit erstellen wir zunächst das RSI-Indikator-Handle mit 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 unseren eindeutigen Bezeichner 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. Wenn wir das Programm initialisieren, 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. Es prüft zunächst, ob der Balken auf der Grundlage von „Swing_Strength“ und unter Verwendung von ArraySize außerhalb der Grenzen liegt, um Indexfehler zu vermeiden, und gibt in diesem Fall false zurück. Anschließend wird das aktuelle Hoch 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. Wir können auch eine Funktion zur Erkennung einer sauberen RSI-Divergenz definieren.
//+------------------------------------------------------------------+ //| 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 }
Hier implementieren wir 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 „bearish“ 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 einer Abwärtsbewegung prüfen wir, ob „rsi_data[b]“ den Wert „interp_rsi + Tolerance“ überschreitet und geben bei Überschreitung „false“ zurück; im Falle einer Aufwärtsbewegung stellen wir sicher, dass „rsi_data[b]“ nicht unter „interp_rsi – Tolerance“ 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. Wir haben alle Funktionen, die uns bei der Identifizierung der Abweichungen helfen, und können diese in OnTick implementieren. Wir beginnen mit der Abwärtsdivergenz.
//+------------------------------------------------------------------+ //| 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 // 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 higher_high = Last_High_Price > Prev_High_Price; //--- Check for higher high bool lower_rsi_high = Last_High_RSI < Prev_High_RSI; //--- Check for lower RSI high int bars_diff = prev_high_bar - last_high_bar; //--- Calculate bars between highs bool bear_div = false; //--- Initialize bearish divergence flag if (Prev_High_Price > 0.0 && higher_high && lower_rsi_high && bars_diff >= Min_Bars_Between && bars_diff <= Max_Bars_Between) { //--- Check bearish divergence conditions if (CleanDivergence(Prev_High_RSI, Last_High_RSI, prev_high_bar, last_high_bar, rsi_data, true)) { //--- Check clean divergence bear_div = true; //--- Set bearish divergence flag // Draw divergence lines string line_name = "DivLine_Bear_" + 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 int rsi_window = ChartWindowFind(chart_id, "RSI(" + IntegerToString(RSI_Period) + ")"); //--- Find RSI subwindow if (rsi_window != -1) { //--- Check if RSI subwindow found string rsi_line = "DivLine_RSI_Bear_" + 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 string swing_name = "SwingHigh_" + TimeToString(Last_High_Time); //--- Set swing high label name if (ObjectFind(chart_id, swing_name) < 0) { //--- Check if label exists 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_type + (bear_div ? " Bear Div" : "")); //--- 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 } ChartRedraw(chart_id); //--- Redraw chart } }
In OnTick, das bei jedem neuen Tick ausgeführt wird, verwenden wir die statische Variable „last_time“, um den Zeitstempel des zuletzt verarbeiteten Balkens zu verfolgen, indem wir die aktuelle Balkenzeit mit iTime abrufen und vorzeitig beenden, wenn sie unverändert ist, um eine redundante Verarbeitung desselben Balkens zu vermeiden, und dann „last_time“ aktualisieren. Wir setzen eine „data_size“ von 200 Balken für die Analyse, was ein willkürlicher Wert ist, den Sie nach Belieben erhöhen oder verringern können, und deklarieren Arrays für „high_data“, „low_data“, „rsi_data“ und „time_data“ und füllten sie über CopyHigh, „CopyLow“, „CopyTime“ und CopyBuffer, um die letzten Höchst- und Tiefstkurse, Zeitstempel und RSI-Werte aus dem Handle zu holen. Wir konfigurieren diese Arrays als Zeitreihen mit ArraySetAsSeries für die Indizierung der Zeitreihen und erhalten die „chart_id“ mit „ChartID“ für die spätere Objektzeichnung.
Um die letzten hohen Umkehrpunkt zu ermitteln, initialisieren wir „last_high_bar“ und „prev_high_bar“ auf -1, führen dann eine Rückwärtsschleife ab dem letzten Balken aus (1 bis „data_size – Swing_Strength“), rufen „CheckSwingHigh“ auf, um die beiden letzten gültigen hohen Umkehrpunkt zu ermitteln, weisen sie entsprechend zu und brechen nach dem zweiten ab. Wenn ein neuer hoher Umkehrpunkt durch den Vergleich von „time_data[last_high_bar]“ mit „Last_High_Time“ bestätigt wird, aktualisieren wir die vorherigen Hoch-Variablen mit den aktuellen letzten Werten, setzen den neuen „Last_High_Price“Last_High_Price“, „Last_High_Time“, „Last_High_RSI“ aus den Arrays und bestimmen den „high_type“ als „H“ Standard oder „HH“ für ein höheres Hoch oder „LH“ für ein niedrigeres Hoch, wenn ein vorheriges existiert.
Wir bewerten dann die Bedingungen für eine Abwärtsdivergenz: Wenn „Prev_High_Price“ existiert, der Preis ein höheres Hoch zeigt, der RSI ein niedrigeres Hoch, und die Balkendifferenz („prev_high_bar – last_high_bar“) innerhalb von „Min_Bars_Between“ liegt und „Max_Bars_Between“ liegt, rufen wir „CleanDivergence“ mit den vorherigen und letzten RSI-Hochs, ihren Verschiebungen, dem Array „rsi_data“ und „true“ für den Abwärtsmodus auf. Wenn es sauber ist, setzen wir das Flag „bear_div“ auf true und zeichnen visuelle Elemente: Wir erstellen ein Trendlinien-Objekt mit dem Namen „DivLine_Bear_“ plus Zeitstempel im Hauptchart mit ObjectCreate, das den vorherigen und den letzten Hochpunkt verbindet, und setzen Eigenschaften wie „OBJPROP_COLOR“ auf „Bear_Color“, „OBJPROP_WIDTH“ auf „Line_Width“, „OBJPROP_STYLE“ auf „Line_Style“, Deaktivieren des Strahls und Einstellen des Vordergrunds über die Funktion ObjectSetInteger.
Wir suchen das RSI-Unterfenster mit „ChartWindowFind“ unter Verwendung des periodenbasierten Namens; wenn es gefunden wird, erstellen wir eine ähnliche RSI-Divergenzlinie mit dem Namen „DivLine_RSI_Bear_“ in diesem Unterfenster, die RSI-Werte mit passenden Eigenschaften verbindet. Für das Umkehrpunktkennzeichnung schließlich prüfen wir mit „ObjectFind“, ob ein Textobjekt mit dem Namen „SwingHigh_“ plus Zeitstempel existiert; falls nicht, erstellen wir es mit „ObjectCreate“ am Hochpunkt, setzen den Text mit „ObjectSetString“, um „high_type“ und ggf. „ Bear Div“, wenden „Swing_High_Color“, Anker „ANCHOR_LEFT_LOWER“ und „Font_Size“ mit „ObjectSetInteger“ an, und aktualisieren dann das Chart mit der Funktion ChartRedraw. Nach dem Kompilieren erhalten wir folgendes Ergebnis:

Nachdem wir nun die Abwärtsdivergenz identifiziert und visualisiert haben, 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 behalten. 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 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 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 } } }
Da wir sowohl Kauf- als auch Verkaufspositionen eröffnen müssen, erstellen wir alle dafür erforderlichen Hilfsfunktionen. Zunächst definieren wir die Funktion „OpenBuy“, um die Eröffnung von Kaufpositionen bei Erkennung einer Aufwärtsdivergenz zu steuern. Wir haben noch keine Logik zur Identifizierung von Kaufsignalen definiert; wir machen einfach einen Sprung nach vorn, da wir sie so oder so brauchen werden. Zunächst wird der aktuelle Briefkurs mit der Funktion SymbolInfoDouble abgefragt. Wenn „SL_Pips“ größer als Null ist, berechnen wir den Stop Loss als „ask_price – SL_Pips * _Point“, normalisiert auf die Ziffern des Symbols mit NormalizeDouble; andernfalls setzen wir ihn auf Null. Ähnlich verhält es sich mit 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“ mit den Parametern Symbol, ORDER_TYPE_BUY, „Lot_Size“, keine Slippage und den berechneten Sl und Tp zu eröffnen; bei Erfolg wird eine Meldung mit „Print“ protokolliert, andernfalls wird die Fehlerbeschreibung aus „obj_Trade.ResultRetcodeDescription“ gedruckt.
Als Nächstes erstellen wir die Funktion „OpenSell“ für Verkaufspositionen bei Abwärtssignalen und spiegeln dabei die Struktur wider: Wir holen den Geldkurs mit „SymbolInfoDouble(_Symbol, SYMBOL_BID)“, setzen sl als „bid_price + SL_Pips * _Point“, falls aktiviert, tp als „bid_price – TP_Pips * _Point“, falls gesetzt, und verwenden „obj_Trade.PositionOpen“ mit „ORDER_TYPE_SELL“. Protokollieren Sie 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 „PositionsTotal() – 1“ bis Null durchführen. Für jede Position, wenn „PositionGetTicket(p)“ gültig ist und mit unserem „_Symbol“ über „PositionGetString(POSITION_SYMBOL)“ und „Magic_Number“ mit „PositionGetInteger(POSITION_MAGIC)“ übereinstimmt, schließen wir sie mit „obj_Trade.PositionClose“ auf dem Ticket. Wir müssen nun diese Funktionen hinzufügen, wenn wir ein Signal haben, indem wir zuerst die Positionen schließen und dann die entsprechenden Signalpositionen öffnen. Für das Abwärtsdivergenzsignal fügen wir die folgenden Funktionen hinzu.
// Bearish divergence detected - Sell signal CloseAll(); //--- Close all open positions OpenSell(); //--- Open sell position
Nach der Kompilierung ergibt sich folgendes Ergebnis.

Wir sehen, dass wir mit dem Signal der rückläufigen Divergenz handeln können. Eine ähnliche Logik müssen wir nun auch für die Aufwärtsdivergenz 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 lower_low = Last_Low_Price < Prev_Low_Price; //--- Check for lower low bool higher_rsi_low = Last_Low_RSI > Prev_Low_RSI; //--- Check for higher RSI low int bars_diff = prev_low_bar - last_low_bar; //--- Calculate bars between lows bool bull_div = false; //--- Initialize bullish divergence flag if (Prev_Low_Price > 0.0 && lower_low && higher_rsi_low && bars_diff >= Min_Bars_Between && bars_diff <= Max_Bars_Between) { //--- Check bullish divergence conditions if (CleanDivergence(Prev_Low_RSI, Last_Low_RSI, prev_low_bar, last_low_bar, rsi_data, false)) { //--- Check clean divergence bull_div = true; //--- Set bullish divergence flag // Bullish divergence detected - Buy signal CloseAll(); //--- Close all open positions OpenBuy(); //--- Open buy position // Draw divergence lines string line_name = "DivLine_Bull_" + 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 int rsi_window = ChartWindowFind(chart_id, "RSI(" + IntegerToString(RSI_Period) + ")"); //--- Find RSI subwindow if (rsi_window != -1) { //--- Check if RSI subwindow found string rsi_line = "DivLine_RSI_Bull_" + 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 string swing_name = "SwingLow_" + TimeToString(Last_Low_Time); //--- Set swing low label name if (ObjectFind(chart_id, swing_name) < 0) { //--- Check if label exists 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_type + (bull_div ? " Bull Div" : "")); //--- 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 } ChartRedraw(chart_id); //--- Redraw chart }
In diesem Fall haben wir die gleiche Logik wie bei dem Abwärtsdivergenzsignal 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ärtsdivergenzen 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 } } } } } }
Hier implementieren wir die Funktion „ApplyTrailingStop“, um die Stop-Loss für offene Positionen dynamisch anzupassen, sobald sie einen profitablen Schwellenwert erreichen. Wir beginnen damit, den Punktwert des Symbols „point“ zuzuweisen, indem wir „_Point“ für Pip-Berechnungen verwenden. Dann wird in einer Schleife rückwärts durch alle Positionen vom Ergebnis von „PositionsTotal“ minus 1 bis Null gegangen, um Abschlüsse oder Änderungen ohne Indexverschiebungen sicher zu behandeln.
Für jede Position, wenn die Funktion PositionGetTicket mit dem Parameter 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 unsere Kennung ü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 von PositionGetInteger mit „POSITION_TYPE“ gleich POSITION_TYPE_BUY geprüft wurde, berechnen wir ein „newSL“ als das aktuelle Gebot, das von „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 Bid-Preis 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-Preis 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-Event-Handler aufrufen, um die schwere Arbeit zu erledigen.
if (Enable_Trailing_Stop && PositionsTotal() > 0) { //--- Check if trailing stop enabled ApplyTrailingStop(); //--- Apply trailing stop to positions }
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) IndicatorRelease(RSI_Handle); //--- Release RSI handle if valid ObjectsDeleteAll(0, "DivLine_"); //--- Delete all divergence line objects ObjectsDeleteAll(0, "SwingHigh_"); //--- Delete all swing high objects ObjectsDeleteAll(0, "SwingLow_"); //--- Delete all swing low objects Print("RSI Divergence EA deinitialized"); //--- Log deinitialization }
Wenn „RSI_Handle“ nicht gleich INVALID_HANDLE ist, geben wir in OnDeinit den Speicher des Indikators frei, indem wir IndicatorRelease mit „RSI_Handle“ als Argument aufrufen. Als Nächstes löschen wir alle relevanten Chart-Objekte: mit ObjectsDeleteAll mit dem Unterfenster 0 und dem Präfix „DivLine_“, um Divergenzlinien zu entfernen, dann mit „SwingHigh_“ zur Kennzeichnung von hohen Umkehrpunkten und „SwingLow_“ zur Kennzeichnung von tiefen Umkehrpunkten. Zum Schluss geben wir eine Bestätigungsmeldung über Print aus, um zu protokollieren, dass der RSI Divergence EA erfolgreich deinitialisiert wurde. Da wir unsere Ziele erreicht haben, bleibt nur noch der Backtest 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
Zusammenfassend haben wir ein reguläres RSI-Divergenz-Konvergenzsystem in MQL5 entwickelt, das Auf- und Abwärtsdivergenzen durch hohe und tiefe Umkehrpunkte mit Stärkebestätigung erkennt, sie mit sauberen Checks innerhalb von Balken-Ranges und Toleranz validiert und Handelsgeschäfte mit festen Lots, optionalem Stop-Loss und Take-Profit in Pips sowie Trailing-Stops für dynamisches Risikomanagement ausführt.
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 regelmäßigen RSI-Divergenzstrategie sind Sie für den effektiven Handel mit Divergenzsignalen 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/20031
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.
Aufbau eines Smart Trade Managers in MQL5: Automatisieren Sie Break-Even, Trailing Stop und Teilweises Schließen
Von der Grundstufe bis zur Mittelstufe: Struct (VI)
Entwicklung des Price Action Analysis Toolkit (Teil 48): Multi-Timeframe Harmony Index mit gewichtetem Bias Dashboard
Einführung in MQL5 (Teil 29): Beherrschung der API- und WebRequest-Funktion in MQL5 (III)
- 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.