English 日本語
preview
Automatisieren von Handelsstrategien in MQL5 (Teil 37): Regelmäßige RSI-Divergenz-Konvergenz mit visuellen Indikatoren

Automatisieren von Handelsstrategien in MQL5 (Teil 37): Regelmäßige RSI-Divergenz-Konvergenz mit visuellen Indikatoren

MetaTrader 5Handel |
141 0
Allan Munene Mutiiria
Allan Munene Mutiiria

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:

  1. Verstehen der regulären RSI-Divergenz-Konvergenz-Strategie
  2. Implementation in MQL5
  3. Backtests
  4. 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:

REGULÄRE AUFWÄRTSDIVERGENZ

Setup der Abwärtsdivergenz:

REGULÄRE 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:

STRATEGIEPLAN


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.

INITIALISIERUNG

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:

ABWÄRTSDIVERGENZ

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.

VRERKAUFSPOSITION

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.

POSITION DER AUFWÄRTSDIVERGENZ

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.

TRAILING STOP

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:

GRAPH

Bericht des Backtest:

BERICHT


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

Aufbau eines Smart Trade Managers in MQL5: Automatisieren Sie Break-Even, Trailing Stop und Teilweises Schließen Aufbau eines Smart Trade Managers in MQL5: Automatisieren Sie Break-Even, Trailing Stop und Teilweises Schließen
Lernen Sie, wie man einen Smart Trade Manager Expert Advisor in MQL5 erstellt, der das Handelsmanagement mit Break-Even-, Trailing-Stop- und Partial-Close-Funktionen automatisiert. Ein praktischer, schrittweiser Leitfaden für Händler, die durch Automatisierung Zeit sparen und die Konsistenz verbessern wollen.
Von der Grundstufe bis zur Mittelstufe: Struct (VI) Von der Grundstufe bis zur Mittelstufe: Struct (VI)
In diesem Artikel werden wir untersuchen, wie man die Implementierung einer gemeinsamen strukturellen Codebasis angehen kann. Ziel ist es, den Programmieraufwand zu verringern und das volle Potenzial der Programmiersprache selbst – in diesem Fall MQL5 – zu nutzen.
Entwicklung des Price Action Analysis Toolkit (Teil 48): Multi-Timeframe Harmony Index mit gewichtetem Bias Dashboard Entwicklung des Price Action Analysis Toolkit (Teil 48): Multi-Timeframe Harmony Index mit gewichtetem Bias Dashboard
In diesem Artikel wird der „Multi-Timeframe Harmony Index“ vorgestellt – ein fortschrittlicher Expert Advisor für MetaTrader 5, der einen gewichteten Bias aus mehreren Timeframes berechnet, die Messwerte mithilfe des EMA glättet und die Ergebnisse in einem übersichtlichen Dashboard anzeigt. Es umfasst anpassbare Warnungen und automatische Kauf-/Verkaufssignale bei Überschreiten von Schwellenwerten für starke Verzerrungen. Geeignet für Händler, die Multi-Timeframe-Analysen nutzen, um ihre Einstiege an der allgemeinen Marktstruktur auszurichten.
Einführung in MQL5 (Teil 29): Beherrschung der API- und WebRequest-Funktion in MQL5 (III) Einführung in MQL5 (Teil 29): Beherrschung der API- und WebRequest-Funktion in MQL5 (III)
In diesem Artikel setzen wir die Beherrschung von API und WebRequest in MQL5 fort, indem wir Kerzendaten aus einer externen Quelle abrufen. Wir konzentrieren uns auf die Aufteilung der Serverantwort, die Bereinigung der Daten und die Extraktion wesentlicher Elemente wie Eröffnungszeit und OHLC-Werte für mehrere Tageskerzen, um die Daten für die weitere Analyse vorzubereiten.