English Русский
preview
Vom Neuling zum Experten: Handel mit dem RSI unter Berücksichtigung der Struktur des Marktes

Vom Neuling zum Experten: Handel mit dem RSI unter Berücksichtigung der Struktur des Marktes

MetaTrader 5Beispiele |
14 1
Clemence Benjamin
Clemence Benjamin

Inhalt



Einführung

Der Weg zu einer konsistenten Handelsleistung wird oft durch die Abhängigkeit von fragmentierten, verzögerten Signalen behindert. Die herkömmliche Ausbildung legt den Schwerpunkt auf Instrumente wie Kanäle für Ausbruchsgeschäfte oder den Relative Strength Index für überkaufte/überverkaufte Umkehrphasen. Obwohl diese Methoden grundlegend sind, weisen sie gut dokumentierte Fallstricke auf: Ausbrüche scheitern oft, Retests finden möglicherweise nie statt, und der RSI kann weit länger in extremen Bereichen verbleiben, als das Kapital eines Händlers aushalten kann. Dieser reaktive Ansatz macht Händler anfällig für falsche Signale und verpasste Gelegenheiten, da sie immer erst dann einsteigen, wenn die optimale Dynamik bereits eingesetzt hat.

In diesem Artikel wird eine strukturierte Methode vorgestellt, die diese Beschränkungen überwinden soll, indem sie die Kenntnis der Preisentwicklung mit der frühzeitigen Bestätigung des Momentums verknüpft. Wir gehen darüber hinaus, die Marktstruktur – insbesondere die Trendkanäle – als bloße Grenzen für Ausbrüche zu betrachten. Stattdessen betrachten wir sie als den grundlegenden Kontext, der die Stärke und Gültigkeit von Impulssignalen bestimmt.

Wir konzentrieren uns auf eine präzise Technik: Wir verwenden den RSI nicht als eigenständiges Umkehr-Orakel, sondern als Bestätigungsmotor im Rahmen der etablierten Marktstruktur. Durch die algorithmische Erkennung von Preiskanälen und die Suche nach einer frühzeitigen Bestätigung durch den RSI auf wichtigen Strukturniveaus versuchen wir, Einstiege mit höherer Wahrscheinlichkeit zu identifizieren, die sowohl früher als auch sicherer sind als traditionelle Breakout-Retest-Modelle.

Darüber hinaus überbrücken wir die Lücke zwischen konzeptioneller Strategie und ausführbarem Rand, indem wir die Programmiersprache MQL5 nutzen. Die manuelle Identifizierung von Strukturen und Signalen ist nicht nur zeitaufwändig, sondern auch subjektiv. Deshalb werden wir die Entwicklung eines automatisierten Systems erläutern, das:

  1. Der Algorithmus identifiziert gültige Trendkanäle.
  2. Interpretiert das RSI-Verhalten im Zusammenhang mit diesen Kanälen auf intelligente Weise.
  3. Führt Handelsgeschäfte auf der Grundlage einer vordefinierten, regelbasierten Logik aus und verwaltet sie.

Wir beginnen mit einer gründlichen Analyse des üblichen manuellen Kanalhandels und seiner gängigen Muster, wobei wir die wichtigsten Preisaktionsprinzipien festlegen. Anschließend wird ein robuster Algorithmus zur Kanalerkennung entwickelt. Der Eckpfeiler unserer Diskussion ist die innovative Integration dieser Strukturanalyse mit der gefilterten RSI-Dynamik, um Signale mit hohem Vertrauen zu erzeugen. Schließlich kapseln wir diese gesamte Logik in einem Prototyp eines automatisierten Handelssystems in MQL5 ein und demonstrieren damit einen praktischen Weg von der Markttheorie zur algorithmischen Ausführung.

Verstehen des Konzepts und der gemeinsamen Strukturen

Muster für Bullen und Bären stehen für eine Konsolidierung der Dynamik innerhalb eines starken Trends. Obwohl sie traditionell als einfache Fortsetzungsmuster betrachtet werden, liegt ihr wahrer Wert in dem spezifischen Momentum-Verhalten, das innerhalb ihrer Kanalgrenzen zu beobachten ist. Der herkömmliche Ansatz des Ausbruchs und der Wiederholung ist zwar logisch, gibt aber systematisch einen wesentlichen Teil der nachfolgenden Bewegung auf.

Unsere Methodik verfeinert dies, indem sie genau den Moment innerhalb der Konsolidierung anvisiert, in dem sich die dem Trend zugrunde liegende Stärke wieder durchsetzt, und dabei ein starkes Zusammenspiel von Struktur und Dynamik nutzt.

Die Anatomie eines verlässlichen Musters

Bullen-Muster: Es bildet sich in ein starker Aufwärtstrend. Sie besteht aus einer scharfen, fast vertikalen Aufwärtsbewegung (der anfänglichen impulsiven Bewegung), gefolgt von einem abwärts gerichteten oder rechteckigen Konsolidierungskanal. Das Muster ist nur gültig, wenn die Konsolidierung nicht über den Beginn der Fahnenstange hinausgeht.

Bullen-Muster

Bullen-Muster

Bären-Muster: Das Spiegelbild innerhalb des Abwärtstrends. Auf eine steil abfallende Kursbewegung folgt eine aufwärts gerichteter oder rechteckiger Konsolidierungskanal.

Bären-Muster

Bären-Muster

Jenseits des Ausbruchs: Die Divergenzkante

Beim Standardausbruch wird abgewartet, bis der Kurs den Kanal verlässt und dann seine Begrenzung erneut testet. Unser Ansatz zielt auf einen früheren Einstieg mit höherer Wahrscheinlichkeit ab, indem wir den Verlust der Dynamik der Gegenbewegung erkennen, bevor der Ausbruch überhaupt erfolgt. Dies wird durch eine regelmäßige RSI-Divergenz am kritischen Niveau des Musters erreicht.

Für ein Bullen-Muster:

Der strukturelle Kontext: Der Preis bildet eine Reihe von unteren Tiefs (LL), während er innerhalb der abwärts gerichteten Handelsspanne sinkt.

Das Divergenzsignal (unser Einstiegsmoment): Wenn der Kurs sein drittes oder viertes Tief (LL) in der Nähe der unteren Unterstützungsgrenze des Kanals erreicht, bildet der RSI ein höheres Tief (HL). Diese Divergenz deutet darauf hin, dass sich das Abwärtsmomentum innerhalb der Konsolidierung genau im Bereich der logischen Unterstützung erschöpft. Der übergeordnete Aufwärtstrend steht kurz vor seiner Wiederaufnahme.

Für ein Bären-Muster:

Der strukturelle Kontext: Die Preise bilden eine Reihe höherer Hochs (HH), während sie innerhalb der aufwärts gerichteten Handelsspanne steigen.

Das Divergenzsignal (unser Einstiegsmoment): Wenn der Kurs ein neues Hoch (HH) nahe der oberen Widerstandsgrenze des Kanals erreicht, bildet der RSI ein tieferes Hoch (LH). Dies signalisiert, dass das Aufwärtsmomentum innerhalb des Rücksetzers genau am logischen Widerstand scheitert. Der größere Abwärtstrend ist bereit, sich fortzusetzen.

Anzeige der RSI-Divergenz und der strukturelle Beziehung

Zusammenkommen der Divergenzkanäle bei einem Bären-Muster

Warum dieses Zusammentreffen den Handel verändert:

Diese auf Divergenz basierende Logik behebt direkt den Kernfehler sowohl einfacher Ausbruchsregeln als auch grundlegender überkaufter/überverkaufter RSI-Signale:

  1. Es verhindert das „Fangen des fallenden Messers“: Ein überverkaufter RSI an der Kanalunterstützung ist üblich, aber kein zuverlässiges Kaufsignal. Eine steigende RSI-Divergenz an derselben Unterstützung deutet jedoch darauf hin, dass der Verkaufsdruck nachlässt und aus einem bloßen „Abprallniveau“ ein wahrscheinlicher „Umkehrpunkt“ wird.
  2. Es bietet überlegenes Risikomanagement: Ein Einstieg, der durch eine Divergenz an einer Kanalgrenze ausgelöst wird, ermöglicht einen logisch engen Stop-Loss (der knapp über dem letzten hohen/tiefen Umkehrpunkt der Divergenz platziert wird). Das Chance-Risiko-Verhältnis ist im Vergleich zu einem Breakout-Einstieg deutlich besser.
  3. Es sucht frühere Einträge mit hoher Bestätigungsrate: Anstatt darauf zu warten, dass die Preisbewegung den Ausbruch vollendet (und oft den besten Teil der Bewegung), handeln wir auf die Momentum-Warnung, die diesem Ausbruch vorausgeht und ihn vorhersagt.

Im Grunde genommen handeln wir nicht mehr nur ein geometrisches Muster. Wir handeln den Beweis der Momentum-Erschöpfung innerhalb dieses Musters, indem wir die Divergenz als endgültigen Beweis verwenden.

Dieses präzise, regelbasierte Konzept ist perfekt für die algorithmische Übersetzung geeignet. Im nächsten Abschnitt werden wir die genauen Berechnungsregeln definieren, um diese Muster zu erkennen und die kritische RSI-Divergenz an ihren Grenzen zu identifizieren, die die Kernlogik unseres MQL5-Expertenberaters bilden.

Begleiten Sie uns, wenn wir den Bauplan des Marktes dekonstruieren und einen systematischen Prozess aufbauen, um ihn mit größerer Klarheit, Timing und Disziplin zu handeln.



Umsetzung

Die erfolgreiche Umsetzung eines Handelskonzepts in ein robustes automatisiertes System erfordert einen methodischen, modularen Ansatz. Während die Erkennung von RSI-Divergenzen in Kombination mit einer kanalbasierten Strukturbestätigung theoretisch einfach klingt, ist die praktische Umsetzung technisch sehr komplex. Um diese Komplexität effektiv zu bewältigen, habe ich die Lösung als zwei unabhängige, aber komplementäre Module konzipiert:
  1. RSI-Divergenzindikator: Ein eigenständiger technischer Indikator, der Divergenzmuster auf Kurscharts identifiziert und visuell markiert.
  2. Equidistant Channel Expert Advisor: Ein automatischer Handelsassistent, der Kanalstrukturen in Echtzeit erkennt und zeichnet.

Diese modulare Architektur bietet mehrere strategische Vorteile. Jede Komponente kann unabhängig entwickelt, getestet und optimiert werden, um die Zuverlässigkeit vor der Integration zu gewährleisten. Händler können je nach ihrer Strategie beide Tools separat verwenden oder sie für eine bessere Überzeugung kombinieren. Letztlich können diese Module nahtlos in ein einheitliches Handelssystem integriert werden, das Strukturerkennung, Signalbestätigung und automatische Ausführung bietet.

Die Umsetzung erfolgt nach einem systematischen Schema. Zunächst werde ich den schrittweisen Aufbau des RSI-Divergenz-Indikators erläutern und die Logik der Pivot-Erkennung, der Divergenz-Validierung und der visuellen Signaldarstellung erklären. Als Nächstes werde ich den Algorithmus des Channel Placer EA zur Identifizierung und Zeichnung von äquidistanten Kanälen auf Preisstrukturen demonstrieren. Wenn diese Tools zusammenarbeiten, erhalten Händler rechtzeitig Divergenzwarnungen innerhalb bestätigter Channel-Kontexte und verwandeln die subjektive technische Analyse in eine mechanische, regelbasierte Handelsmethodik, die durch algorithmische Präzision unterstützt wird.

Diese nächsten Schritte führen Sie durch die gesamte Implementierungsphase, von der Entwicklung einzelner Komponenten bis hin zur Bereitstellung eines integrierten Systems, das Ihnen ein professionelles Handels-Toolkit bietet, das auf soliden algorithmischen Grundlagen basiert.

 RSI-Divergenz-Detektor-Indikator

Abschnitt 1: Einrichtung und Konfiguration des Indikatorrahmens

Die Grundlage unseres RSI-Divergenz-Detektors beginnt mit der Einrichtung des richtigen MetaTrader 5-Indikatorrahmens. Wir beginnen mit den obligatorischen Indikatoreigenschaften, die festlegen, wie das Tool mit der Handelsplattform interagiert. Die Direktiven #property in den Zeilen 1-22 konfigurieren wichtige Metadaten: Copyright-Informationen, Versionskontrolle und vor allem die Einstellungen für die visuelle Darstellung.

Der indicator_separate_window Deklaration stellt sicher, dass unsere RSI-Berechnungen in einem eigenen Unterfenster unterhalb des Hauptpreisdiagramms angezeigt werden, um visuelle Unordnung zu vermeiden. Wir weisen 7 Datenpuffer für die Speicherung verschiedener Berechnungsergebnisse zu, während wir nur 3 tatsächliche Darstellungen für die Anzeige definieren – diese Trennung zwischen Berechnungspuffern und visuellen Ausgaben ermöglicht eine effiziente Speicherverwaltung bei gleichzeitiger sauberer Visualisierung.

Die Farb- und Stilkonfigurationen (Zeilen 11-17) schaffen eine intuitive visuelle Sprache: DodgerBlue für die RSI-Linie, Orange für hohe Pivots und DeepSkyBlue für niedrige Pivots, wodurch eine sofortige visuelle Unterscheidung zwischen verschiedenen Arten von Preisstrukturpunkten geschaffen wird.

//+------------------------------------------------------------------+
//|                                        RSIDivergenceDetector.mq5 |
//|                                    Copyright 2025, MetaQuotes Ltd|
//|                                            https://www.mql5.com  |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property description "RSI Divergence Detector with Main Chart Arrows"
#property description "Shows divergence arrows on main chart, stores pivot values"

#property indicator_separate_window
#property indicator_buffers 7
#property indicator_plots   3
#property indicator_color1  clrDodgerBlue      // RSI line
#property indicator_color2  clrOrange          // RSI High Pivots
#property indicator_color3  clrDeepSkyBlue     // RSI Low Pivots
#property indicator_width1  2
#property indicator_width2  3
#property indicator_width3  3
#property indicator_label1  "RSI"
#property indicator_label2  "RSI High Pivot"
#property indicator_label3  "RSI Low Pivot"

Abschnitt 2: Nutzerkonfiguration und Eingabeparameter

Professionelle Handelswerkzeuge müssen ein Gleichgewicht zwischen Automatisierung und Nutzerkontrolle herstellen, was wir durch das umfassende Eingabeparametersystem (Zeilen 25-42) erreichen. Mit dem Eingabeschlüsselwort werden vom Nutzer veränderbare Variablen erstellt, die im Einstellungsdialog des Indikators erscheinen und es Händlern ermöglichen, den Erkennungsalgorithmus an ihren spezifischen Handelsstil anzupassen.

Wir bieten Kontrolle über die RSI-Kernberechnung (Periode und Preisquelle), die Empfindlichkeit der Pivot-Erkennung durch InpPivotStrength und die Divergenz-Filterparameter. Die booleschen Flags InpShowRegular und InpShowHidden geben Händlern die Möglichkeit, je nach Handelsstrategie zwischen verschiedenen Divergenztypen umzuschalten.

Besonders wichtig ist der Parameter InpRequireRSIBreak (Zeile 40), der die von Ihnen geforderte Bestätigungslogik implementiert – er stellt sicher, dass der RSI das vorherige Pivot-Level tatsächlich durchbricht, bevor er eine Divergenz signalisiert. Dadurch wird eine zusätzliche Validierungsebene geschaffen, die verfrühte Signale verhindert und die Zuverlässigkeit erhöht.

//--- Input parameters
input int                InpRSIPeriod     = 14;            // RSI Period
input ENUM_APPLIED_PRICE InpRSIPrice      = PRICE_CLOSE;   // RSI Applied Price
input int                InpPivotStrength = 3;             // Pivot Strength (bars on each side)
input double             InpOverbought    = 70.0;          // Overbought Level
input double             InpOversold      = 30.0;          // Oversold Level
input bool               InpShowRegular   = true;          // Show Regular Divergences
input bool               InpShowHidden    = true;          // Show Hidden Divergences
input color              InpBullishColor  = clrLimeGreen;  // Bullish divergence arrow color
input color              InpBearishColor  = clrRed;        // Bearish divergence arrow color
input int                InpArrowSize     = 3;             // Arrow size on chart
input bool               InpAlertOnDivergence = true;      // Alert on divergence
input bool               InpSendNotification = false;      // Send notification
input bool               InpRequireRSIBreak = true;        // Require RSI to break pivot line
input double             InpMinDivergenceStrength = 2.0;   // Minimum RSI divergence strength
input int                InpMaxPivotDistance = 100;        // Max bars between pivots for divergence
input double             InpArrowOffsetPct = 0.3;          // Arrow offset percentage (0.3 = 30%)

Abschnitt 3: Datenstrukturentwurf und Speicherverwaltung

In den Zeilen 44-67 implementieren wir die Kerndatenarchitektur unter Verwendung eines nutzerdefinierten Struktur RSI_PIVOT. Diese Struktur stellt eine wichtige Design-Entscheidung dar: Anstatt sich auf einfache Arrays zu verlassen, die verschiedene Datentypen mischen, erstellen wir einen speziellen Datentyp, der alle relevanten Informationen über jeden Pivot-Punkt kapselt.

Jede Instanz von RSI_PIVOT speichert den Balkenindex, den RSI-Wert, das entsprechende Preisniveau, den Zeitstempel, den Pivot-Typ (Hoch/Tief), die Erkennungsstärke und den Bestätigungsstatus.

Der Konstruktor (Zeilen 59-62) sorgt für eine ordnungsgemäße Initialisierung und verhindert so häufige Programmierfehler mit nicht initialisierten Variablen. Wir führen ein dynamisches Array rsiPivots[], um diese Strukturen zu speichern, wobei pivotCount die aktuelle Anzahl der gültigen Einträge erfasst. Dieser Ansatz bietet sowohl Datenintegrität als auch eine effiziente Speichernutzung, da wir Pivot-Daten ohne komplexe Indexverwaltung einfach hinzufügen, entfernen oder durchsuchen können.

//--- Indicator buffers
double BufferRSI[];
double BufferRSIHighPivot[];
double BufferRSILowPivot[];
double BufferRSIHigh[];
double BufferRSILow[];
double BufferPivotHigh[];
double BufferPivotLow[];

//--- Global variables
int rsiHandle;
datetime lastAlertTime;
string indicatorPrefix = "RSI_DIV_";

//--- Structures for storing pivot data
struct RSI_PIVOT
{
   int barIndex;
   double value;
   double price;
   datetime time;
   bool isHigh;
   int strength;
   bool isConfirmed;
   
   // Constructor to initialize values
   RSI_PIVOT() : barIndex(-1), value(0.0), price(0.0), time(0), 
                 isHigh(false), strength(0), isConfirmed(false) {}
};

RSI_PIVOT rsiPivots[];
int pivotCount = 0;

Abschnitt 4: Initialisierung und Ressourceneinrichtung

Die Funktion OnInit() (Zeilen 70-125) behandelt die kritische Einrichtungsphase beim Laden des Indikators. Die Zeilen 74-80 erstellen die Verbindung zwischen unseren Berechnungspuffern und dem Anzeigesystem des Indikators mit SetIndexBuffer().

Jeder Puffer erhält eine bestimmte Rolle: BufferRSI speichert die berechneten RSI-Werte, während BufferRSIHighPivot und BufferRSILowPivot die visuellen Markierungen für Pivot-Punkte enthalten.

Die Zeilen 83-94 konfigurieren das Plotverhalten: Die Hauptlinie des RSI verwendet DRAW_LINE, während die Pivotpunkte DRAW_ARROW mit dem Zeichencode 159 (ein quadratisches Symbol). In Zeile 103 wird mit iRSI() ein Handle auf den eingebauten RSI-Indikator erstellt, der einen effizienten Zugriff auf vorberechnete RSI-Werte ermöglicht, ohne das Rad neu zu erfinden.

Die Zeilen 112-115 richten visuelle Referenzlinien für überkaufte/überverkaufte Niveaus ein, und Zeile 121 ruft CleanChartObjects() auf, um alle verbleibenden grafischen Elemente aus früheren Indikatorläufen zu entfernen und einen sauberen Ausgangszustand zu gewährleisten.

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
{
   SetIndexBuffer(0, BufferRSI, INDICATOR_DATA);
   SetIndexBuffer(1, BufferRSIHighPivot, INDICATOR_DATA);
   SetIndexBuffer(2, BufferRSILowPivot, INDICATOR_DATA);
   SetIndexBuffer(3, BufferRSIHigh, INDICATOR_DATA);
   SetIndexBuffer(4, BufferRSILow, INDICATOR_DATA);
   SetIndexBuffer(5, BufferPivotHigh, INDICATOR_DATA);
   SetIndexBuffer(6, BufferPivotLow, INDICATOR_DATA);
   
   //--- Set plot properties
   PlotIndexSetInteger(0, PLOT_DRAW_TYPE, DRAW_LINE);
   PlotIndexSetInteger(1, PLOT_DRAW_TYPE, DRAW_ARROW);
   PlotIndexSetInteger(2, PLOT_DRAW_TYPE, DRAW_ARROW);
   
   //--- Set arrow codes for RSI pivot points
   PlotIndexSetInteger(1, PLOT_ARROW, 159);  // Square dot for high pivots
   PlotIndexSetInteger(2, PLOT_ARROW, 159);  // Square dot for low pivots
   
   //--- Set empty values for arrow buffers
   PlotIndexSetDouble(1, PLOT_EMPTY_VALUE, EMPTY_VALUE);
   PlotIndexSetDouble(2, PLOT_EMPTY_VALUE, EMPTY_VALUE);
   
   //--- Set indicator labels
   IndicatorSetString(INDICATOR_SHORTNAME, "RSI Divergence (" + string(InpRSIPeriod) + ")");
   IndicatorSetInteger(INDICATOR_DIGITS, 2);
   
   //--- Create RSI handle
   rsiHandle = iRSI(_Symbol, _Period, InpRSIPeriod, InpRSIPrice);
   if(rsiHandle == INVALID_HANDLE)
   {
      Print("Failed to create RSI handle");
      return(INIT_FAILED);
   }
   
   //--- Set overbought/oversold levels
   IndicatorSetDouble(INDICATOR_LEVELVALUE, 0, InpOversold);
   IndicatorSetDouble(INDICATOR_LEVELVALUE, 1, InpOverbought);
   IndicatorSetInteger(INDICATOR_LEVELCOLOR, 0, clrSilver);
   IndicatorSetInteger(INDICATOR_LEVELCOLOR, 1, clrSilver);
   IndicatorSetInteger(INDICATOR_LEVELSTYLE, 0, STYLE_DOT);
   IndicatorSetInteger(INDICATOR_LEVELSTYLE, 1, STYLE_DOT);
   
   lastAlertTime = 0;
   
   //--- Clean any existing objects from previous runs
   CleanChartObjects();
   
   return(INIT_SUCCEEDED);
}

Abschnitt 5: Kernberechnungsmaschine und Datenverarbeitung

Die Funktion OnCalculate() (Zeilen 128-169) dient als Hauptverarbeitungsschleife, die bei jeder Tick- und Balkenaktualisierung aufgerufen wird. Wir beginnen mit der Validierung in Zeile 130, um sicherzustellen, dass genügend historische Daten für sinnvolle Berechnungen vorhanden sind.

Die Zeilen 133-142 implementieren eine intelligente Neuberechnungslogik: Wenn dies der erste Durchlauf ist (prev_calculated == 0), initialisieren wir unser Pivot-Array und setzen die Zähler zurück; andernfalls machen wir dort weiter, wo wir aufgehört haben, um die Leistung durch Vermeidung redundanter Berechnungen zu optimieren. In Zeile 145 rufen wir die RSI-Werte mithilfe von CopyBuffer() von unserem RSI-Handle ab – dies demonstriert die ordnungsgemäße Verwendung der API für technische Indikatoren MetaTrader 5 für den Datenzugriff.

Die Funktion orchestriert dann die drei Hauptverarbeitungsschritte: Auffinden von RSI-Pivots, Erkennen von Divergenzen und Bereinigung alter Daten. Dieser modulare Ansatz trennt die einzelnen Bereiche und gewährleistet gleichzeitig einen effizienten Datenfluss.

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
{
   if(rates_total < InpRSIPeriod + 10) return(0);
   
   int start;
   if(prev_calculated == 0)
   {
      start = InpRSIPeriod + InpPivotStrength;
      ArrayResize(rsiPivots, rates_total);
      pivotCount = 0;
      CleanChartObjects();
   }
   else
   {
      start = prev_calculated - 1;
   }
   
   //--- Get RSI values
   if(CopyBuffer(rsiHandle, 0, 0, rates_total, BufferRSI) <= 0)
      return(0);
   
   //--- Find RSI pivots
   FindRSIPivots(rates_total, start, high, low, time);
   
   //--- Detect divergences and draw arrows on main chart
   DetectDivergences(rates_total, start, high, low, close, time);
   
   //--- Clean old pivot data
   CleanOldPivots(rates_total);
   
   return(rates_total);
}

Abschnitt 6: Implementierung des Algorithmus zur Pivot-Erkennung

Die Funktion FindRSIPivots() (Zeilen 172-218) implementiert die zentrale Logik zur Identifizierung von Schwungpunkten. In den Zeilen 175-176 werden frühere Pivot-Markierungen aus den Anzeigepuffern gelöscht, sodass nur die aktuellen, relevanten Pivots angezeigt werden.

Die Schleife in den Zeilen 178-216 durchläuft die Balken und wendet dabei die Kriterien für die Pivot-Erkennung an. Die Hilfsfunktionen IsHighPivot() und IsLowPivot() (Zeilen 221-272) enthalten die eigentliche Erkennungslogik: Damit ein Balken als High Pivot gilt, muss sein RSI-Wert größer sein als eine bestimmte Anzahl von Balken auf beiden Seiten (gesteuert durch InpPivotStrength).

Wenn ein gültiger Pivot gefunden wird (Zeilen 183-215), wird eine neue Struktur RSI_PIVOT mit allen relevanten Daten gefüllt: dem Balkenindex als Referenz, dem RSI-Wert zum Vergleich, dem Preisniveau für die Divergenzanalyse, dem Zeitstempel für die Nachverfolgung und der Typklassifizierung. Das Flag isConfirmed wird zunächst auf false gesetzt, damit unsere Divergenzerkennungslogik die Pivots markieren kann, sobald sie zu einem gültigen Signal beigetragen haben.

//+------------------------------------------------------------------+
//| Find RSI pivots function                                         |
//+------------------------------------------------------------------+
void FindRSIPivots(int rates_total, int start, const double &high[], const double &low[], const datetime &time[])
{
   // Clear pivot buffers
   ArrayFill(BufferRSIHighPivot, start, rates_total - start, EMPTY_VALUE);
   ArrayFill(BufferRSILowPivot, start, rates_total - start, EMPTY_VALUE);
   
   for(int i = start; i < rates_total - InpPivotStrength; i++)
   {
      //--- Check for RSI high pivot
      if(IsHighPivot(i, BufferRSI, InpPivotStrength))
      {
         if(pivotCount < ArraySize(rsiPivots))
         {
            rsiPivots[pivotCount].barIndex = i;
            rsiPivots[pivotCount].value = BufferRSI[i];
            rsiPivots[pivotCount].price = high[i];
            rsiPivots[pivotCount].time = time[i];
            rsiPivots[pivotCount].isHigh = true;
            rsiPivots[pivotCount].strength = InpPivotStrength;
            rsiPivots[pivotCount].isConfirmed = false;
            pivotCount++;
            
            BufferRSIHighPivot[i] = BufferRSI[i];
         }
      }
      
      //--- Check for RSI low pivot
      if(IsLowPivot(i, BufferRSI, InpPivotStrength))
      {
         if(pivotCount < ArraySize(rsiPivots))
         {
            rsiPivots[pivotCount].barIndex = i;
            rsiPivots[pivotCount].value = BufferRSI[i];
            rsiPivots[pivotCount].price = low[i];
            rsiPivots[pivotCount].time = time[i];
            rsiPivots[pivotCount].isHigh = false;
            rsiPivots[pivotCount].strength = InpPivotStrength;
            rsiPivots[pivotCount].isConfirmed = false;
            pivotCount++;
            
            BufferRSILowPivot[i] = BufferRSI[i];
         }
      }
   }
}

Abschnitt 7: Hilfsfunktionen für die Pivot-Erkennung

Die Funktionen IsHighPivot() und IsLowPivot() Funktionen enthalten die genaue Logik für die Identifizierung von Schwankungspunkten in RSI-Werten. Diese Funktionen implementieren das, was Händler gemeinhin als „Pivot-Stärke“-Erkennung bezeichnen.

Damit ein Balken als hoher Pivot (Zeilen 221-246) gilt, muss sein RSI-Wert größer sein als eine bestimmte Anzahl vorhergehender Balken (nach links) UND größer als eine bestimmte Anzahl nachfolgender Balken (nach rechts).

Diese bilaterale Überprüfung stellt sicher, dass wir echte lokale Maxima/Minima und keine vorübergehenden Schwankungen feststellen. Der Parameter Stärke steuert die Empfindlichkeit: höhere Werte erfordern mehr Bestätigungsbalken, was zu weniger, aber zuverlässigeren Pivots führt. Diese Funktionen liefern einfache boolesche Ergebnisse, die in unsere Hauptschleife zur Pivot-Erkennung einfließen und eine saubere Trennung der Erkennungslogik von der Datenverwaltung demonstrieren.

//+------------------------------------------------------------------+
//| Check if bar is a high pivot                                     |
//+------------------------------------------------------------------+
bool IsHighPivot(int index, double &buffer[], int strength)
{
   if(index < strength || index >= ArraySize(buffer) - strength)
      return false;
   
   double pivotValue = buffer[index];
   
   // Check left side
   for(int i = 1; i <= strength; i++)
   {
      if(buffer[index - i] > pivotValue)
         return false;
   }
   
   // Check right side
   for(int i = 1; i <= strength; i++)
   {
      if(buffer[index + i] > pivotValue)
         return false;
   }
   
   return true;
}

//+------------------------------------------------------------------+
//| Check if bar is a low pivot                                      |
//+------------------------------------------------------------------+
bool IsLowPivot(int index, double &buffer[], int strength)
{
   if(index < strength || index >= ArraySize(buffer) - strength)
      return false;
   
   double pivotValue = buffer[index];
   
   // Check left side
   for(int i = 1; i <= strength; i++)
   {
      if(buffer[index - i] < pivotValue)
         return false;
   }
   
   // Check right side
   for(int i = 1; i <= strength; i++)
   {
      if(buffer[index + i] < pivotValue)
         return false;
   }
   
   return true;
}

Abschnitt 8: Logik der Divergenzerfassung und Signalerzeugung

Die Funktion DetectDivergences() (Zeilen 275-373) ist der intelligente Kern unseres Indikators. Nachdem wir in Zeile 277 überprüft haben, dass genügend Pivot-Daten vorhanden sind, implementieren wir eine verschachtelte Schleifenstruktur (Zeilen 280-372), die alle möglichen Pivot-Paare vergleicht.

Zeile 283 wendet einen praktischen Filter an: Zu weit auseinander liegende Pivots (die InpMaxPivotDistance überschreiten) werden ignoriert, da weit auseinander liegende Korrelationen oft unterschiedliche Marktphasen darstellen. Die Logik teilt sich dann in zwei Hauptzweige: Vergleiche mit hohen Pivot-Werten für bärische Divergenzen (Zeilen 287-322) und Vergleiche mit niedrigen Pivot-Werten für bullische Divergenzen (Zeilen 323-372).

Für jeden Divergenztyp prüfen wir sowohl die regulären als auch die versteckten Varianten, indem wir die spezifischen Check...Divergence()-Funktionen anwenden, die die genauen Kriterien für die Preis/RSI-Beziehung kapseln. Wenn eine gültige Divergenz gefunden wird, berechnen die Zeilen 294-298 (für fallende Kurse) und 330-334 (für steigende Kurse) eine geeignete Pfeilplatzierung unter Verwendung eines prozentualen Versatzes von der aktuellen Preisspanne, um sicherzustellen, dass die Pfeile deutlich sichtbar sind, ohne das Kursgeschehen zu verdecken.

//+------------------------------------------------------------------+
//| Detect divergences between price and RSI                         |
//+------------------------------------------------------------------+
void DetectDivergences(int rates_total, int start, const double &high[], const double &low[], 
                      const double &close[], const datetime &time[])
{
   if(pivotCount < 4) return;
   
   // Check all pivot pairs for divergence
   for(int i = pivotCount - 1; i >= 0; i--)
   {
      for(int j = i - 1; j >= 0; j--)
      {
         // Skip if pivots are too far apart
         if(rsiPivots[i].barIndex - rsiPivots[j].barIndex > InpMaxPivotDistance)
            continue;
         
         // Check if both are high pivots
         if(rsiPivots[i].isHigh && rsiPivots[j].isHigh)
         {
            // Check for regular bearish divergence
            if(InpShowRegular && CheckBearishDivergence(rsiPivots[i], rsiPivots[j], rates_total))
            {
               // Check RSI break confirmation if required
               if(!InpRequireRSIBreak || CheckRSIBreak(rsiPivots[j], rsiPivots[i], rates_total, true))
               {
                  double arrowPrice = high[rsiPivots[i].barIndex];
                  double range = high[rsiPivots[i].barIndex] - low[rsiPivots[i].barIndex];
                  double offset = range * InpArrowOffsetPct;
                  
                  DrawChartArrow(rsiPivots[i].barIndex, time[rsiPivots[i].barIndex], 
                                arrowPrice + offset, false, "Bearish");
                  TriggerAlert("Regular Bearish Divergence detected!", time[rsiPivots[i].barIndex]);
                  // Mark as confirmed to avoid duplicate signals
                  rsiPivots[i].isConfirmed = true;
                  rsiPivots[j].isConfirmed = true;
               }
            }
            // Check for hidden bearish divergence
            else if(InpShowHidden && CheckHiddenBearishDivergence(rsiPivots[i], rsiPivots[j]))
            {
               if(!InpRequireRSIBreak || CheckRSIBreak(rsiPivots[j], rsiPivots[i], rates_total, true))
               {
                  double arrowPrice = high[rsiPivots[i].barIndex];
                  double range = high[rsiPivots[i].barIndex] - low[rsiPivots[i].barIndex];
                  double offset = range * InpArrowOffsetPct;
                  
                  DrawChartArrow(rsiPivots[i].barIndex, time[rsiPivots[i].barIndex], 
                                arrowPrice + offset, false, "Bearish(H)");
                  TriggerAlert("Hidden Bearish Divergence detected!", time[rsiPivots[i].barIndex]);
                  rsiPivots[i].isConfirmed = true;
                  rsiPivots[j].isConfirmed = true;
               }
            }
         }
         // Check if both are low pivots
         else if(!rsiPivots[i].isHigh && !rsiPivots[j].isHigh)
         {
            // Check for regular bullish divergence
            if(InpShowRegular && CheckBullishDivergence(rsiPivots[i], rsiPivots[j], rates_total))
            {
               // Check RSI break confirmation if required
               if(!InpRequireRSIBreak || CheckRSIBreak(rsiPivots[j], rsiPivots[i], rates_total, false))
               {
                  double arrowPrice = low[rsiPivots[i].barIndex];
                  double range = high[rsiPivots[i].barIndex] - low[rsiPivots[i].barIndex];
                  double offset = range * InpArrowOffsetPct;
                  
                  DrawChartArrow(rsiPivots[i].barIndex, time[rsiPivots[i].barIndex], 
                                arrowPrice - offset, true, "Bullish");
                  TriggerAlert("Regular Bullish Divergence detected!", time[rsiPivots[i].barIndex]);
                  rsiPivots[i].isConfirmed = true;
                  rsiPivots[j].isConfirmed = true;
               }
            }
            // Check for hidden bullish divergence
            else if(InpShowHidden && CheckHiddenBullishDivergence(rsiPivots[i], rsiPivots[j]))
            {
               if(!InpRequireRSIBreak || CheckRSIBreak(rsiPivots[j], rsiPivots[i], rates_total, false))
               {
                  double arrowPrice = low[rsiPivots[i].barIndex];
                  double range = high[rsiPivots[i].barIndex] - low[rsiPivots[i].barIndex];
                  double offset = range * InpArrowOffsetPct;
                  
                  DrawChartArrow(rsiPivots[i].barIndex, time[rsiPivots[i].barIndex], 
                                arrowPrice - offset, true, "Bullish(H)");
                  TriggerAlert("Hidden Bullish Divergence detected!", time[rsiPivots[i].barIndex]);
                  rsiPivots[i].isConfirmed = true;
                  rsiPivots[j].isConfirmed = true;
               }
            }
         }
      }
   }
}

Abschnitt 9: Funktionen zur Validierung und Bestätigung von Divergenzen

Die Validierungsfunktionen CheckBearishDivergence(), CheckBullishDivergence(), CheckHiddenBearishDivergence() und CheckHiddenBullishDivergence() (Zeilen 376-445) implementieren die genauen mathematischen Definitionen der einzelnen Divergenztypen.

Jede Funktion folgt einem einheitlichen Muster: Validierung der Parameter, Überprüfung der Preisbeziehung, Überprüfung der RSI-Beziehung, Überprüfung der Stärke und Überprüfung des Bestätigungsstatus.

Zum Beispiel, CheckBearishDivergence() in den Zeilen 376-395 verlangt: 1) der neuere Pivot hat einen höheren Preis als der ältere Pivot, 2) der neuere Pivot hat einen niedrigeren RSI-Wert (um mindestens InpMinDivergenceStrength), 3) die absolute Differenz erfüllt die Mindestanforderungen an die Stärke und 4) keiner der beiden Pivots wurde zuvor in einer anderen Divergenz bestätigt. Diese strengen Kriterien verhindern Fehlsignale und stellen sicher, dass nur aussagekräftige Divergenzen Alarme auslösen.

//+------------------------------------------------------------------+
//| Check for regular bearish divergence                             |
//+------------------------------------------------------------------+
bool CheckBearishDivergence(RSI_PIVOT &pivot1, RSI_PIVOT &pivot2, int rates_total)
{
   // pivot1 is newer, pivot2 is older
   if(pivot1.barIndex <= pivot2.barIndex) return false;
   if(pivot1.barIndex >= rates_total - 1 || pivot2.barIndex >= rates_total - 1) return false;
   
   // Price makes higher high
   bool priceHigher = pivot1.price > pivot2.price;
   
   // RSI makes lower high
   bool rsiLower = pivot1.value < pivot2.value - InpMinDivergenceStrength;
   
   // Ensure there's enough divergence strength
   bool enoughStrength = MathAbs(pivot2.value - pivot1.value) >= InpMinDivergenceStrength;
   
   // Not already confirmed
   bool notConfirmed = !pivot1.isConfirmed && !pivot2.isConfirmed;
   
   return priceHigher && rsiLower && enoughStrength && notConfirmed;
}

//+------------------------------------------------------------------+
//| Check for regular bullish divergence                             |
//+------------------------------------------------------------------+
bool CheckBullishDivergence(RSI_PIVOT &pivot1, RSI_PIVOT &pivot2, int rates_total)
{
   // pivot1 is newer, pivot2 is older
   if(pivot1.barIndex <= pivot2.barIndex) return false;
   if(pivot1.barIndex >= rates_total - 1 || pivot2.barIndex >= rates_total - 1) return false;
   
   // Price makes lower low
   bool priceLower = pivot1.price < pivot2.price;
   
   // RSI makes higher low
   bool rsiHigher = pivot1.value > pivot2.value + InpMinDivergenceStrength;
   
   // Ensure there's enough divergence strength
   bool enoughStrength = MathAbs(pivot1.value - pivot2.value) >= InpMinDivergenceStrength;
   
   // Not already confirmed
   bool notConfirmed = !pivot1.isConfirmed && !pivot2.isConfirmed;
   
   return priceLower && rsiHigher && enoughStrength && notConfirmed;
}

//+------------------------------------------------------------------+
//| Check for hidden bearish divergence                              |
//+------------------------------------------------------------------+
bool CheckHiddenBearishDivergence(RSI_PIVOT &pivot1, RSI_PIVOT &pivot2)
{
   // Price makes lower high
   bool priceLower = pivot1.price < pivot2.price;
   
   // RSI makes higher high
   bool rsiHigher = pivot1.value > pivot2.value + InpMinDivergenceStrength;
   
   // Not already confirmed
   bool notConfirmed = !pivot1.isConfirmed && !pivot2.isConfirmed;
   
   return priceLower && rsiHigher && notConfirmed;
}

//+------------------------------------------------------------------+
//| Check for hidden bullish divergence                              |
//+------------------------------------------------------------------+
bool CheckHiddenBullishDivergence(RSI_PIVOT &pivot1, RSI_PIVOT &pivot2)
{
   // Price makes higher low
   bool priceHigher = pivot1.price > pivot2.price;
   
   // RSI makes lower low
   bool rsiLower = pivot1.value < pivot2.value - InpMinDivergenceStrength;
   
   // Not already confirmed
   bool notConfirmed = !pivot1.isConfirmed && !pivot2.isConfirmed;
   
   return priceHigher && rsiLower && notConfirmed;
}

Abschnitt 10: RSI-Break-Bestätigung und Signalvalidierung

Die Funktion CheckRSIBreak() (Zeilen 448-478) implementiert die von Ihnen angegebene zusätzliche Validierungsebene – sie stellt sicher, dass der RSI das vorherige Pivot-Level tatsächlich durchbricht, bevor eine Divergenz als bestätigt gilt.

Diese Funktion befasst sich mit dem häufigen Problem der verfrühten Divergenzsignale, die auftreten, bevor sich das Momentum tatsächlich verschiebt. In den Zeilen 456-458 wird ein Rückblicksfenster (10 Bars) nach dem neueren Pivot eingerichtet, in dem wir auf die Break-Bedingung achten.

Bei fallenden Divergenzen (Zeilen 464-467) prüfen wir, ob der RSI unter den älteren Pivot-Wert fällt; bei steigenden Divergenzen (Zeilen 469-472) prüfen wir, ob der RSI über den älteren Pivot-Wert steigt. Dieser Bestätigungsschritt verwandelt den Indikator von einem einfachen Musterdetektor in einen Momentum-Shift-Validator, was die Zuverlässigkeit der Signale deutlich erhöht.

//+------------------------------------------------------------------+
//| Check if RSI has broken the previous pivot level                 |
//+------------------------------------------------------------------+
bool CheckRSIBreak(RSI_PIVOT &olderPivot, RSI_PIVOT &newerPivot, int rates_total, bool isBearish)
{
   // For bearish: Check if RSI has broken below the older pivot's value
   // For bullish: Check if RSI has broken above the older pivot's value
   
   if(newerPivot.barIndex >= rates_total - 1) return false;
   
   // Look for break in recent bars after newer pivot
   int lookbackBars = 10;
   int startBar = newerPivot.barIndex + 1;
   int endBar = MathMin(rates_total - 1, newerPivot.barIndex + lookbackBars);
   
   for(int i = startBar; i <= endBar; i++)
   {
      if(isBearish)
      {
         // Bearish: RSI should break below older pivot value
         if(BufferRSI[i] < olderPivot.value)
            return true;
      }
      else
      {
         // Bullish: RSI should break above older pivot value
         if(BufferRSI[i] > olderPivot.value)
            return true;
      }
   }
   
   return false;
}

Abschnitt 11: Visuelle Signaldarstellung und Kartenverwaltung

Die Funktion DrawChartArrow() (Zeilen 481-529) dient der visuellen Darstellung von Divergenzsignalen im Hauptpreisdiagramm. In den Zeilen 483-484 werden anhand des Zeitstempels eindeutige Objektnamen generiert, sodass die einzelnen Paare von Pfeil-Kennzeichnungen unabhängig voneinander verwaltet werden können. Die Zeilen 487-488 bereinigen alle vorhandenen Objekte mit denselben Namen und verhindern so eine Duplizierung, wenn der Indikator neu berechnet wird.

Die Funktion verzweigt dann je nach Signalrichtung: Bei Aufwärtssignalen werden die Objekte OBJ_ARROW_BUY verwendet (Zeilen 491-505), bei Abwärtssignalen OBJ_ARROW_SELL (Zeilen 507-522). Jeder Pfeil erhält ein konfigurierbares Styling (Farbe aus InpBullishColor/InpBearishColor, Größe aus InpArrowSize) und enthält eine Textbeschriftung, die den Divergenztyp angibt.

Die Funktion CleanChartObjects() (Zeilen 532-542) sorgt für eine systematische Bereinigung aller von den Indikatoren erstellten Objekte, um die Sauberkeit des Charts zu gewährleisten.

//+------------------------------------------------------------------+
//| Draw arrow on main chart                                         |
//+------------------------------------------------------------------+
void DrawChartArrow(int barIndex, datetime time, double price, bool isBullish, string labelText)
{
   string arrowName = indicatorPrefix + "Arrow_" + IntegerToString(time);
   string labelName = indicatorPrefix + "Label_" + IntegerToString(time);
   
   // Remove existing objects with same name
   ObjectDelete(0, arrowName);
   ObjectDelete(0, labelName);
   
   if(isBullish)
   {
      // Draw bullish arrow (up arrow)
      if(!ObjectCreate(0, arrowName, OBJ_ARROW_BUY, 0, time, price))
      {
         Print("Failed to create arrow object: ", GetLastError());
         return;
      }
      
      ObjectSetInteger(0, arrowName, OBJPROP_COLOR, InpBullishColor);
      ObjectSetInteger(0, arrowName, OBJPROP_WIDTH, InpArrowSize);
      ObjectSetInteger(0, arrowName, OBJPROP_BACK, false);
      
      // Draw label
      if(!ObjectCreate(0, labelName, OBJ_TEXT, 0, time, price))
         return;
      
      ObjectSetString(0, labelName, OBJPROP_TEXT, labelText);
      ObjectSetInteger(0, labelName, OBJPROP_COLOR, InpBullishColor);
      ObjectSetInteger(0, labelName, OBJPROP_FONTSIZE, 8);
   }
   else
   {
      // Draw bearish arrow (down arrow)
      if(!ObjectCreate(0, arrowName, OBJ_ARROW_SELL, 0, time, price))
      {
         Print("Failed to create arrow object: ", GetLastError());
         return;
      }
      
      ObjectSetInteger(0, arrowName, OBJPROP_COLOR, InpBearishColor);
      ObjectSetInteger(0, arrowName, OBJPROP_WIDTH, InpArrowSize);
      ObjectSetInteger(0, arrowName, OBJPROP_BACK, false);
      
      // Draw label
      if(!ObjectCreate(0, labelName, OBJ_TEXT, 0, time, price))
         return;
      
      ObjectSetString(0, labelName, OBJPROP_TEXT, labelText);
      ObjectSetInteger(0, labelName, OBJPROP_COLOR, InpBearishColor);
      ObjectSetInteger(0, labelName, OBJPROP_FONTSIZE, 8);
   }
   
   ObjectSetInteger(0, labelName, OBJPROP_ANCHOR, ANCHOR_CENTER);
   ObjectSetInteger(0, labelName, OBJPROP_BACK, false);
}

Abschnitt 12: Bereinigungsfunktionen und Alarmsystem

Die Funktionen CleanChartObjects() (Zeilen 532-542) und OnDeinit() (Zeilen 572-582) übernehmen die Verwaltung wichtiger Ressourcen. Die Pivot-Bereinigungslogik behält ein rollierendes Fenster mit den letzten 500 Pivots bei und verwirft ältere, um eine Speicheraufblähung während einer längeren Chartanalyse zu vermeiden. Wenn der Indikator entfernt wird, gibt OnDeinit() den RSI-Indikator-Handle mit IndicatorRelease() frei und bereinigt alle Chart-Objekte.

Die Funktion TriggerAlert() Funktion implementiert ein ausgeklügeltes Benachrichtigungssystem, das ein Gleichgewicht zwischen Signalbewusstsein und Benachrichtigungsmüdigkeit herstellt. Zeile 554 berücksichtigt die Einstellung des Nutzers für Warnmeldungen.

In Zeile 557 wird eine zeitbasierte Filterung mit lastAlertTime implementiert, um wiederholte Warnungen für denselben Balken zu verhindern – ein häufiges Ärgernis bei Handelsindikatoren. Wenn die Bedingungen erfüllt sind, löst Zeile 562 einen Standard-MetaTrader 5-Alarm mit Ton aus, und die Zeilen 565-568 senden optional Plattform-Benachrichtigungen, wenn sie aktiviert sind.

//+------------------------------------------------------------------+
//| Clean chart objects                                              |
//+------------------------------------------------------------------+
void CleanChartObjects()
{
   int total = ObjectsTotal(0, 0, -1);
   for(int i = total - 1; i >= 0; i--)
   {
      string name = ObjectName(0, i, 0, -1);
      if(StringFind(name, indicatorPrefix, 0) != -1)
      {
         ObjectDelete(0, name);
      }
   }
}

//+------------------------------------------------------------------+
//| Clean old pivot data                                             |
//+------------------------------------------------------------------+
void CleanOldPivots(int rates_total)
{
   if(pivotCount > 500)  // Keep last 500 pivots maximum
   {
      int newCount = 250;
      for(int i = 0; i < newCount; i++)
      {
         rsiPivots[i] = rsiPivots[pivotCount - newCount + i];
      }
      pivotCount = newCount;
   }
}

//+------------------------------------------------------------------+
//| Trigger alert function                                           |
//+------------------------------------------------------------------+
void TriggerAlert(string message, datetime time)
{
   if(!InpAlertOnDivergence) return;
   
   // Avoid repeated alerts for same bar
   if(time <= lastAlertTime) return;
   
   lastAlertTime = time;
   
   // Play sound
   Alert(message + " at ", TimeToString(time, TIME_DATE|TIME_MINUTES));
   
   // Send notification if enabled
   if(InpSendNotification)
      SendNotification("RSI Divergence: " + Symbol() + " " + 
                       StringSubstr(EnumToString(_Period), 7) + 
                       " - " + message + " at " + TimeToString(time));
}

//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   // Delete RSI handle
   if(rsiHandle != INVALID_HANDLE)
      IndicatorRelease(rsiHandle);
   
   // Clean up chart objects
   CleanChartObjects();
}

Nachdem wir die Architektur und Implementierung des RSI-Divergenz-Detektors gründlich untersucht haben, finden Sie den vollständigen Quellcode im Anhang dieses Artikels. Nachdem wir die Grundlage für unsere technischen Indikatoren geschaffen haben, gehen wir nun zur zweiten Komponente unseres Handelssystems über: der Entwicklung des Equidistant Channel Auto-Placement Expert Advisor. Dieses automatisierte Tool wird unsere Divergenzerfassung durch die Identifizierung struktureller Preismuster ergänzen und letztendlich eine integrierte algorithmische Handelslösung schaffen. Nach der Implementierung werden wir umfassende Testergebnisse für beide Projekte austauschen und einen strategischen Fahrplan für die weitere Entwicklung und Systemintegration aufstellen.

Äquidistanter Kanal Auto-Placement Expert Advisor

Der Equidistant Channel Auto-Placement EA schafft eine solide Grundlage durch ein sorgfältig entwickeltes Aufzählungssystem und umfassende, vom Nutzer konfigurierbare Parameter. Die ENUM_CHANNEL_TYPE bietet eine klare semantische Unterscheidung zwischen steigenden und fallenden Kanälen und ersetzt die traditionelle Terminologie von „Bullen und Bären“ durch beschreibendere Begriffe, die auf Handels-Setups abgestimmt sind: steigende Kanäle (höhere Tiefststände) weisen auf Verkaufsgelegenheiten hin, während fallende Kanäle (niedrigere Höchststände) Kaufgelegenheiten bieten. Diese Umstellung der Nomenklatur stellt eine wichtige Designentscheidung dar, die die Channel-Terminologie an das tatsächliche Handelsverhalten und nicht an abstrakte technische Konzepte anpasst.

Abschnitt 1: Architektonische Grundlage und Nutzer-Konfigurationsrahmen

Das Eingabeparametersystem implementiert eine hochentwickelte Konfigurationsschnittstelle, die ein Gleichgewicht zwischen Automatisierung und Nutzerkontrolle herstellt. Jeder Parameter dient einem bestimmten Zweck im Kanalerkennungsalgorithmus: LookbackBars definiert den Umfang der historischen Daten, SwingStrength steuert die Empfindlichkeit der Erkennung von Umkehrpunkten, während MinTouchesPerLine die neuartige Validierungsanforderung von mehreren Kursberührungen pro Kanallinie einführt – eine Funktion, die falsche Signale erheblich reduziert. Der Parameter TouchTolerancePips zeigt, dass die realen Handelsbedingungen berücksichtigt werden, indem geringfügige Kursabweichungen berücksichtigt werden, die andernfalls echte Kanalstrukturen ungültig machen könnten.

enum ENUM_CHANNEL_TYPE
{
   CHANNEL_NONE,
   CHANNEL_RISING,   // Higher lows - Sell setups (formerly bullish)
   CHANNEL_FALLING   // Lower highs - Buy setups (formerly bearish)
};

input bool   EnableRisingChannels   = true;
input bool   EnableFallingChannels  = true;
input int    LookbackBars           = 150;
input int    SwingStrength          = 2;
input bool   ShowChannel            = true;
input color  RisingChannelColor     = clrRed;
input color  FallingChannelColor    = clrLimeGreen;
input int    ChannelWidth           = 1;
input int    MinChannelLengthBars   = 15;
input double MinChannelHeightPct    = 0.5;
input bool   AlertOnNewChannel      = true;
input int    MinTouchesPerLine      = 2;
input double TouchTolerancePips     = 5.0;
input int    MaxExtensionBars       = 50;
input bool   ExtendLeft             = true;
input bool   ExtendRight            = false;

Abschnitt 2: Zustandsverwaltung und Initialisierungsarchitektur

Der EA implementiert ein ausgeklügeltes Zustandsverwaltungssystem mit Hilfe globaler Variablen, die das Fortbestehen von Kanälen und das Timing von Alarmen verfolgen, und behebt damit kritische Probleme früherer Implementierungen. Die Variable lastAlertTime führt eine intelligente Alarmunterdrückung ein, die das bei den Tests festgestellte Problem der ständigen Alarmierung verhindert. Die Variablen channelStartBar und channelEndBar schaffen ein Speichersystem, das es dem EA ermöglicht, zwischen neuen Kanalformationen und dem Fortbestehen bestehender Kanäle zu unterscheiden – ein entscheidendes Merkmal für die Beibehaltung des Ein-Kanal-Anzeigeverhaltens.

Die Initialisierungsfunktion demonstriert professionelles Ressourcenmanagement, indem sie vorhandene Chart-Objekte während des Starts bereinigt. Dieser proaktive Ansatz verhindert die Ansammlung von Objekten, die die Chart-Oberfläche unübersichtlich machen könnten. Die Deinitialisierungsfunktion folgt dem Prinzip des minimalen Eingriffs, indem sie gezeichnete Kanäle für die Nutzeranalyse intakt lässt und gleichzeitig kommentierte Optionen für die automatische Bereinigung bietet – ein Design, das ein Gleichgewicht zwischen Automatisierung und Nutzerkontrolle schafft.

datetime lastBarTime = 0;
string currentChannelName = "";
string channelPrefix = "SmartCh_";
bool channelFound = false;
ENUM_CHANNEL_TYPE currentChannelType = CHANNEL_NONE;
datetime lastAlertTime = 0;
int channelStartBar = -1;
int channelEndBar = -1;

int OnInit()
{
   DeleteAllChannels();
   Print("Smart Single Channel EA initialized");
   return(INIT_SUCCEEDED);
}

void OnDeinit(const int reason)
{
   // Optional: Delete channel on exit
   // DeleteAllChannels();
}

Abschnitt 3: Intelligente ereignisgesteuerte Verarbeitung 

Die Funktion OnTick() implementiert eine optimierte ereignisgesteuerte Architektur, die ein Gleichgewicht zwischen Reaktionsfähigkeit und Berechnungseffizienz herstellt. Die Funktion richtet einen Mechanismus zur Erkennung eines neuen Kursbalkens mit Hilfe von iTime() ein, um sicherzustellen, dass die Verarbeitung nur dann erfolgt, wenn aussagekräftige Kursaktualisierungen verfügbar sind, und verhindert so unnötige Berechnungen während der Kurskonsolidierung. Die innovative Drosselungslogik führt eine kontrollierte Verarbeitungshäufigkeit ein, indem sie die Kanäle nur jeden dritten Takt überprüft – eine strategische Designentscheidung, die die Häufigkeit von Alarmen drastisch reduziert und gleichzeitig eine rechtzeitige Kanalerkennung gewährleistet.

Dieser Ansatz der Unterdrückung geht direkt auf das Problem der kontinuierlichen Alarmierung ein, das während der Tests festgestellt wurde, und verwandelt den EA von einem verrauschten, überreaktiven System in ein professionelles Handelsinstrument, das nur dann Signale liefert, wenn bedeutende Kanalentwicklungen auftreten. Die modulare Architektur, die die Kernverarbeitung an FindAndDrawSingleChannel() delegiert, demonstriert die Einhaltung des Prinzips der einzigen Verantwortung, das die Ereignisbehandlung von der Implementierung der Geschäftslogik trennt.

void OnTick()
{
   datetime currentBarTime = iTime(_Symbol, _Period, 0);
   if(currentBarTime <= lastBarTime) return;
   lastBarTime = currentBarTime;
   
   int barShift = iBarShift(_Symbol, _Period, currentBarTime);
   if(barShift % 3 != 0) return;
   
   FindAndDrawSingleChannel();
}

Abschnitt 4: Logik zur Erkennung und Validierung von Kernkanälen

Die Funktion FindAndDrawSingleChannel() implementiert die hochentwickelte Entscheidungsfindungsmaschine des EA, die mehrere Erkennungsalgorithmen koordiniert und fortschrittliche Validierungskriterien anwendet. Die Funktion demonstriert einen parallelen Verarbeitungsansatz, der gleichzeitig die Möglichkeiten eines ansteigenden und eines abfallenden Kanals auswertet, wobei die Ausführung von den Nutzerpräferenzen abhängt. Diese Architektur ermöglicht eine umfassende Marktanalyse bei gleichzeitiger Aufrechterhaltung der Recheneffizienz durch frühzeitige Beendigung deaktivierter Erkennungspfade.

Die Kanalauswahllogik implementiert einen multikriteriellen Entscheidungsalgorithmus, der sowohl die Aktualität als auch die Validierungsstärke berücksichtigt. Das Scoring-System priorisiert die Kanäle anhand von drei Faktoren: Nähe zum aktuellen Preis, Robustheit der Validierung und Mindestanforderungen an die Berührung. Durch diese mehrdimensionale Bewertung wird sichergestellt, dass nur hochwertige, gut bewertete Kanäle vorrangig angezeigt werden.

Die neue Kanalerkennungslogik führt einen ausgeklügelten Zustandsvergleich ein, der redundante Alarme durch vier Validierungskriterien verhindert: Vorhandensein von Kanälen, Typänderungen, Strukturverschiebungen und zeitliche Abkühlung. Durch diesen umfassenden Ansatz wird das Problem der ständigen Alarmierung beseitigt und gleichzeitig die Sensibilität für echte Veränderungen der Marktstruktur erhalten.

void FindAndDrawSingleChannel()
{
   bool risingFound = false;
   int risePoint1 = -1, risePoint2 = -1;
   double riseSlope = 0;
   int riseTouchCount = 0;
   
   if(EnableRisingChannels)
      risingFound = FindRisingChannel(risePoint1, risePoint2, riseSlope, riseTouchCount);
   
   bool fallingFound = false;
   int fallPoint1 = -1, fallPoint2 = -1;
   double fallSlope = 0;
   int fallTouchCount = 0;
   
   if(EnableFallingChannels)
      fallingFound = FindFallingChannel(fallPoint1, fallPoint2, fallSlope, fallTouchCount);
   
   bool drawChannel = false;
   int point1 = -1, point2 = -1;
   double slope = 0;
   ENUM_CHANNEL_TYPE newType = CHANNEL_NONE;
   int touchCount = 0;
   
   if(risingFound && fallingFound)
   {
      if(risePoint1 > fallPoint1 || riseTouchCount > fallTouchCount + 1)
      {
         drawChannel = true;
         point1 = risePoint1;
         point2 = risePoint2;
         slope = riseSlope;
         newType = CHANNEL_RISING;
         touchCount = riseTouchCount;
      }
      else
      {
         drawChannel = true;
         point1 = fallPoint1;
         point2 = fallPoint2;
         slope = fallSlope;
         newType = CHANNEL_FALLING;
         touchCount = fallTouchCount;
      }
   }
   else if(risingFound && riseTouchCount >= MinTouchesPerLine * 2)
   {
      drawChannel = true;
      point1 = risePoint1;
      point2 = risePoint2;
      slope = riseSlope;
      newType = CHANNEL_RISING;
      touchCount = riseTouchCount;
   }
   else if(fallingFound && fallTouchCount >= MinTouchesPerLine * 2)
   {
      drawChannel = true;
      point1 = fallPoint1;
      point2 = fallPoint2;
      slope = fallSlope;
      newType = CHANNEL_FALLING;
      touchCount = fallTouchCount;
   }
   
   if(drawChannel && touchCount >= MinTouchesPerLine * 2)
   {
      bool isNewChannel = (!channelFound) || 
                         (currentChannelType != newType) ||
                         (MathAbs(point1 - channelStartBar) > 10) ||
                         (TimeCurrent() - lastAlertTime > 3600);
      
      if(isNewChannel)
      {
         DeleteAllChannels();
         
         if(DrawChannel(point1, point2, slope, newType))
         {
            channelFound = true;
            currentChannelType = newType;
            channelStartBar = point1;
            channelEndBar = point2;
            
            if(AlertOnNewChannel && (TimeCurrent() - lastAlertTime > 3600))
            {
               string typeStr = (newType == CHANNEL_RISING) ? "Rising (Sell Setup)" : "Falling (Buy Setup)";
               string message = StringFormat("%s channel detected on %s %s. %d touches confirmed.", 
                           typeStr, Symbol(), PeriodToString(_Period), touchCount);
               Alert(message);
               lastAlertTime = TimeCurrent();
            }
         }
      }
   }
   else
   {
      if(channelFound && !IsChannelStillValid())
      {
         DeleteAllChannels();
         channelFound = false;
         currentChannelType = CHANNEL_NONE;
      }
   }
}

Abschnitt 5: Erweiterte Kanalerkennung mit Touch Validation

Die Funktionen FindRisingChannel() und FindFallingChannel() implementieren ausgefeilte Algorithmen zur Mustererkennung, die über die einfache Erkennung von Schwungpunkten hinausgehen. Diese Funktionen verwenden eine Doppelschleifenarchitektur, die alle möglichen Kombinationen von Umkehrpunkten innerhalb des Rückblickzeitraums auswertet und mehrere Validierungsfilter anwendet, um echte Kanalstrukturen zu erkennen.

Die Validierungskriterien zeugen von professioneller Aufmerksamkeit für die Mikrostruktur des Marktes, indem sie die korrekte Neigungsrichtung des Kanals sicherstellen und die strukturelle Mindestintegrität durch Anforderungen an die Kanallänge erzwingen. Die Validierung der Steigung schützt vor nahezu horizontalen Strukturen, die keine aussagekräftigen Trendinformationen enthalten.

Das innovative System zur Validierung von Berührungen, das durch CountChannelTouches() implementiert wird, stellt den bedeutendsten Fortschritt des EA gegenüber herkömmlichen Kanaldetektoren dar. Da dieses System mehrere Preisinteraktionen mit beiden Kanalgrenzen erfordert, wird sichergestellt, dass die entdeckten Kanäle durch das Marktgeschehen getestet und validiert wurden und nicht nur mathematische Artefakte sind. Der Bewertungsalgorithmus kombiniert Häufigkeit, Berührungshäufigkeit und Kanalhöhe zu einer zusammengesetzten Bewertung, die objektiv die wichtigste Kanalstruktur identifiziert.

bool FindRisingChannel(int &point1, int &point2, double &slope, int &touchCount)
{
   int swingLows[];
   FindSwingLows(swingLows, SwingStrength, LookbackBars);
   
   if(ArraySize(swingLows) < 2) return false;
   
   int bestPoint1 = -1, bestPoint2 = -1;
   double bestScore = -1;
   int bestTouches = 0;
   
   for(int i = 0; i < ArraySize(swingLows) - 1; i++)
   {
      for(int j = i + 1; j < ArraySize(swingLows); j++)
      {
         int low1 = swingLows[i];
         int low2 = swingLows[j];
         
         if(iLow(NULL, 0, low1) <= iLow(NULL, 0, low2)) continue;
         if(MathAbs(low1 - low2) < MinChannelLengthBars) continue;
         
         double low1Price = iLow(NULL, 0, low1);
         double low2Price = iLow(NULL, 0, low2);
         
         double barDiff = MathAbs(low1 - low2);
         slope = (low1Price - low2Price) / barDiff;
         
         if(slope < 0.00005) continue;
         
         double channelHeight = CalculateChannelHeight(low1, low2, true);
         int touches = CountChannelTouches(low1, low2, slope, low2Price, channelHeight, true);
         
         double recencyScore = 100.0 - (low1 * 100.0 / LookbackBars);
         double touchScore = touches * 25.0;
         double heightScore = (channelHeight / SymbolInfoDouble(_Symbol, SYMBOL_POINT)) / 100.0;
         
         double totalScore = recencyScore + touchScore + heightScore;
         
         if(totalScore > bestScore && touches >= MinTouchesPerLine * 2)
         {
            bestScore = totalScore;
            bestPoint1 = low1;
            bestPoint2 = low2;
            bestTouches = touches;
         }
      }
   }
   
   if(bestScore > 0)
   {
      point1 = bestPoint1;
      point2 = bestPoint2;
      slope = (iLow(NULL, 0, bestPoint1) - iLow(NULL, 0, bestPoint2)) / MathAbs(bestPoint1 - bestPoint2);
      touchCount = bestTouches;
      return true;
   }
   
   return false;
}

Abschnitt 6: Mathematische Grundlage für die Kanalgeometrie

Die Funktion CalculateChannelHeight() und CountChannelTouches() implementieren den mathematischen Kern, der Preisdaten in validierte Kanalstrukturen umwandelt. Diese Funktionen demonstrieren ausgeklügeltes algorithmisches Denken, indem sie die geometrischen Herausforderungen der Kanalerkennung in Finanzzeitreihen angehen.

CalculateChannelHeight() implementiert einen Algorithmus mit doppeltem Zweck, der die empirische Höhenberechnung durch Messung der maximalen Preisabweichung von der berechneten Basislinie durchführt und gleichzeitig strukturelle Mindestanforderungen durch prozentuale Schwellenwerte erzwingt. Dieser Ansatz stellt ein Gleichgewicht zwischen empirischen Beobachtungen und theoretischen Anforderungen her und gewährleistet, dass die Kanäle sinnvolle Handelsdimensionen aufweisen.

CountChannelTouches() führt eine toleranzbasierte Validierung ein, die das reale Kursverhalten berücksichtigt, bei dem exakte Linienberührungen selten sind. Bei der Toleranzberechnung wird die gerätespezifische Skalierung professionell berücksichtigt, sodass ein einheitliches Verhalten bei verschiedenen Symbolen und Pip-Werten gewährleistet ist. Die Struktur von zwei Schleifen validiert Berührungen an beiden Kanalgrenzen separat und liefert detaillierte Diagnoseinformationen über die Kanalintegrität.

double CalculateChannelHeight(int point1, int point2, bool isRising)
{
   double maxHeight = 0;
   int startBar = MathMin(point1, point2);
   int endBar = MathMax(point1, point2);
   
   double price1 = (isRising) ? iLow(NULL, 0, point1) : iHigh(NULL, 0, point1);
   double price2 = (isRising) ? iLow(NULL, 0, point2) : iHigh(NULL, 0, point2);
   
   double slope = (price1 - price2) / (point1 - point2);
   double intercept = price1 - slope * point1;
   
   for(int bar = startBar; bar <= endBar; bar++)
   {
      double currentPrice = (isRising) ? iHigh(NULL, 0, bar) : iLow(NULL, 0, bar);
      double baseLinePrice = slope * bar + intercept;
      double deviation = MathAbs(currentPrice - baseLinePrice);
      
      if(deviation > maxHeight) maxHeight = deviation;
   }
   
   double minHeight = (isRising) ? iLow(NULL, 0, startBar) * MinChannelHeightPct / 100.0 : 
                                   iHigh(NULL, 0, startBar) * MinChannelHeightPct / 100.0;
   
   return MathMax(maxHeight, minHeight);
}

int CountChannelTouches(int point1, int point2, double slope, double basePrice, double height, bool isRising)
{
   int touches = 0;
   int startBar = MathMin(point1, point2);
   int endBar = MathMax(point1, point2);
   
   double tolerance = TouchTolerancePips * SymbolInfoDouble(_Symbol, SYMBOL_POINT) * 10;
   
   for(int bar = startBar; bar <= endBar; bar++)
   {
      double currentPrice = (isRising) ? iLow(NULL, 0, bar) : iHigh(NULL, 0, bar);
      double baseLinePrice = slope * (bar - point2) + basePrice;
      
      if(MathAbs(currentPrice - baseLinePrice) <= tolerance) touches++;
   }
   
   for(int bar = startBar; bar <= endBar; bar++)
   {
      double currentPrice = (isRising) ? iHigh(NULL, 0, bar) : iLow(NULL, 0, bar);
      double parallelLinePrice = slope * (bar - point2) + basePrice + (isRising ? height : -height);
      
      if(MathAbs(currentPrice - parallelLinePrice) <= tolerance) touches++;
   }
   
   return touches;
}

Abschnitt 7: Swing Point Detection Engine

Die Funktionen FindSwingLows() und FindSwingHighs() implementieren eine robuste Erkennung von Umkehrpunkten unter Verwendung eines symmetrischen Validierungsansatzes, der sowohl die linke als auch die rechte Kursbewegung untersucht. Diese Funktionen bilden die Grundlage für die Kanalerkennung und müssen sorgfältig implementiert werden, um eine zuverlässige Strukturanalyse zu gewährleisten.

Beide Funktionen verwenden identische algorithmische Muster mit umgekehrten Vergleichsoperatoren, was die Wiederverwendung von Code unter Beibehaltung der logischen Klarheit demonstriert. Die Validierungsschleifen implementieren einen strengen Algorithmus zur Erkennung von Spitzen- und Tiefstständen, der voraussetzt, dass ein Umkehrpunkt höher (oder niedriger) ist als eine bestimmte Anzahl von Balken auf beiden Seiten, die durch den Parameter SwingStrength gesteuert wird. Durch diese bilaterale Überprüfung wird sichergestellt, dass die ermittelten Schwankungspunkte echte lokale Extrema und keine vorübergehenden Schwankungen darstellen.

Das Array-Management-Muster demonstriert den professionellen Umgang mit Speicher in MQL5, indem es die Größe von Arrays dynamisch anpasst, wenn gültige Schwungpunkte identifiziert werden. Die abschließenden Operationen von ArraySort() gewährleisten eine chronologische Reihenfolge vom jüngsten bis zum ältesten Datensatz, was eine effiziente Weiterverarbeitung in Kanalerkennungsalgorithmen ermöglicht.

void FindSwingLows(int &swingPoints[], int strength, int lookback)
{
   ArrayResize(swingPoints, 0);
   
   for(int i = strength; i < MathMin(lookback, Bars(NULL, 0) - strength); i++)
   {
      bool isSwingLow = true;
      double currentLow = iLow(NULL, 0, i);
      
      for(int left = 1; left <= strength && isSwingLow; left++)
         if(iLow(NULL, 0, i - left) < currentLow) isSwingLow = false;
      
      if(isSwingLow)
         for(int right = 1; right <= strength && isSwingLow; right++)
            if(iLow(NULL, 0, i + right) < currentLow) isSwingLow = false;
      
      if(isSwingLow)
      {
         int size = ArraySize(swingPoints);
         ArrayResize(swingPoints, size + 1);
         swingPoints[size] = i;
      }
   }
   
   ArraySort(swingPoints);
}

Abschnitt 8: Professionelle Kanalvisualisierung mit kontrollierter Erweiterung

Die Funktion DrawChannel() implementiert eine ausgefeilte grafische Darstellung mit intelligenter Erweiterungsverwaltung, die direkt auf die bei Tests festgestellten Probleme beim Zeichnen von Kanälen eingeht. Die Funktion verwendet einen strukturierten Ansatz für das Rendern von Kanälen, der ein Gleichgewicht zwischen visueller Klarheit und der Verwaltung des Kartenraums schafft.

Die Funktion zeigt eine professionelle Koordinatenberechnung, die mathematische Kanalparameter in visuelle Elemente umsetzt. Die Erweiterungslogik implementiert ein kontrolliertes Boundary Management: ExtendLeft bietet historischen Kontext, während ExtendRight absichtlich deaktiviert ist, um zu verhindern, dass Kanäle übermäßig in den leeren Chart hineinragen – eine direkte Reaktion auf das Test-Feedback über Kanäle, die „zu weit vom aktuellen Preis entfernt“ gezeichnet werden.

Die Implementierung der Kanalzeichnung verwendet separate Trendlinienobjekte für Basis- und Parallellinien, anstatt das in MT5 integrierte Kanalobjekt zu verwenden. Diese architektonische Entscheidung bietet eine bessere Kontrolle über die visuellen Eigenschaften und das Verhalten der Erweiterungen. Die Logik der Etikettenplatzierung zeigt, dass auf die visuelle Hierarchie geachtet wird, indem der beschreibende Text auf geeigneten Preisebenen im Verhältnis zu den Kanalgrenzen platziert wird.

bool DrawChannel(int point1, int point2, double slope, ENUM_CHANNEL_TYPE type)
{
   if(!ShowChannel) return false;
   
   DeleteAllChannels();
   
   datetime time1 = iTime(NULL, 0, point1);
   datetime time2 = iTime(NULL, 0, point2);
   
   double price1, price2;
   color channelColor;
   string channelLabel;
   bool isRising = (type == CHANNEL_RISING);
   
   if(isRising)
   {
      price1 = iLow(NULL, 0, point1);
      price2 = iLow(NULL, 0, point2);
      channelColor = RisingChannelColor;
      channelLabel = "Rising Channel (Sell)";
   }
   else
   {
      price1 = iHigh(NULL, 0, point1);
      price2 = iHigh(NULL, 0, point2);
      channelColor = FallingChannelColor;
      channelLabel = "Falling Channel (Buy)";
   }
   
   double channelHeight = CalculateChannelHeight(point1, point2, isRising);
   
   int extensionBars = MathMin(MaxExtensionBars, MathAbs(point1 - point2) / 2);
   datetime extendedTime1 = time1;
   datetime extendedTime2 = time2;
   
   if(ExtendLeft)
   {
      int extendBack = MathMin(extensionBars, point2);
      extendedTime2 = iTime(NULL, 0, point2 - extendBack);
   }
   
   if(ExtendRight)
   {
      int extendForward = MathMin(extensionBars, Bars(NULL, 0) - point1 - 1);
      extendedTime1 = iTime(NULL, 0, point1 + extendForward);
   }
   
   currentChannelName = channelPrefix + "Base";
   ObjectCreate(0, currentChannelName, OBJ_TREND, 0, extendedTime2, price2, time1, price1);
   ObjectSetInteger(0, currentChannelName, OBJPROP_COLOR, channelColor);
   ObjectSetInteger(0, currentChannelName, OBJPROP_WIDTH, ChannelWidth);
   ObjectSetInteger(0, currentChannelName, OBJPROP_RAY_RIGHT, false);
   ObjectSetInteger(0, currentChannelName, OBJPROP_RAY_LEFT, ExtendLeft);
   ObjectSetInteger(0, currentChannelName, OBJPROP_BACK, true);
   
   string parallelName = channelPrefix + "Parallel";
   double parallelPrice1 = price1 + (isRising ? channelHeight : -channelHeight);
   double parallelPrice2 = price2 + (isRising ? channelHeight : -channelHeight);
   
   ObjectCreate(0, parallelName, OBJ_TREND, 0, extendedTime2, parallelPrice2, time1, parallelPrice1);
   ObjectSetInteger(0, parallelName, OBJPROP_COLOR, channelColor);
   ObjectSetInteger(0, parallelName, OBJPROP_WIDTH, ChannelWidth);
   ObjectSetInteger(0, parallelName, OBJPROP_STYLE, STYLE_DASH);
   ObjectSetInteger(0, parallelName, OBJPROP_RAY_RIGHT, false);
   ObjectSetInteger(0, parallelName, OBJPROP_RAY_LEFT, ExtendLeft);
   ObjectSetInteger(0, parallelName, OBJPROP_BACK, true);
   
   string labelName = channelPrefix + "Label";
   ObjectCreate(0, labelName, OBJ_TEXT, 0, time1, isRising ? price1 + channelHeight * 1.1 : price1 - channelHeight * 1.1);
   ObjectSetString(0, labelName, OBJPROP_TEXT, channelLabel);
   ObjectSetInteger(0, labelName, OBJPROP_COLOR, channelColor);
   ObjectSetInteger(0, labelName, OBJPROP_FONTSIZE, 8);
   ObjectSetInteger(0, labelName, OBJPROP_BACK, true);
   
   return true;
}

Abschnitt 9: Validierung der Kanalpersistenz und Ressourcenverwaltung

Die Funktion IsChannelStillValid() und DeleteAllChannels() implementieren eine kritische Systemwartungs- und Validierungslogik, die sicherstellt, dass der EA auch während längerer Marktsitzungen zuverlässig funktioniert. Diese Funktionen befassen sich mit zwei wichtigen betrieblichen Belangen: der zeitlichen Relevanz der Kanäle und dem Ressourcenmanagement.

IsChannelStillValid() implementiert einen vereinfachten, aber effektiven Algorithmus zur Erkennung von Kanalbrüchen, der die jüngsten Kursbewegungen auf signifikante Abweichungen von den etablierten Kanalgrenzen überwacht. Die Funktion verwendet prozentuale Schwellenwerte, um potenzielle Kanalunterbrechungen zu erkennen, und bietet so ein pragmatisches Gleichgewicht zwischen Empfindlichkeit und Robustheit. Durch den konservativen Ansatz der Funktion wird die Zahl der Fehlbewertungen bei normalen Kursschwankungen reduziert, während die Reaktionsfähigkeit auf echte Strukturbrüche erhalten bleibt.

DeleteAllChannels() demonstriert professionelles Objektmanagement durch systematisches Traversieren und Löschen von Chart-Objekten mit dem Namenspräfix des EA. Das Muster der umgekehrten Iteration gewährleistet das sichere Löschen von Objekten während der Iteration, ein wichtiges Detail bei der Änderung von Sammlungen während der Durchquerung. Die präfixbasierte Filterung verhindert Interferenzen mit anderen Chart-Objekten, was die Berücksichtigung von Multi-Tool-Handelsumgebungen zeigt.

bool IsChannelStillValid()
{
   if(!channelFound) return false;
   
   int recentBars = 10;
   bool isRising = (currentChannelType == CHANNEL_RISING);
   
   for(int i = 0; i < recentBars; i++)
   {
      double high = iHigh(NULL, 0, i);
      double low = iLow(NULL, 0, i);
      
      if(isRising)
      {
         if(low > iLow(NULL, 0, channelStartBar) * 1.01) return false;
      }
      else
      {
         if(high < iHigh(NULL, 0, channelStartBar) * 0.99) return false;
      }
   }
   
   return true;
}

void DeleteAllChannels()
{
   int total = ObjectsTotal(0);
   for(int i = total - 1; i >= 0; i--)
   {
      string name = ObjectName(0, i);
      if(StringFind(name, channelPrefix) == 0)
         ObjectDelete(0, name);
   }
   currentChannelName = "";
}


Tests

Unsere Testmethodik umfasste den direkten Einsatz und die Beobachtung der Leistung des Indikators auf Live-Charts. Die Systemarchitektur trennt den RSI-Oszillator erfolgreich in ein eigenes Fenster, während die klare Signalvisualisierung sowohl im Hauptpreischart als auch in der Indikatoroberfläche beibehalten wird.

Nach Erhalt von Divergenzwarnungen können Händler sofort eine Strukturanalyse durchführen, um unterstützende Channel-Formationen zu identifizieren und einen multifaktoriellen Entscheidungsrahmen für die manuelle Handelsausführung zu schaffen. Dieser Ansatz kombiniert eine automatische Mustererkennung mit einer diskretionären strukturellen Validierung.

Das umfassende System von Eingabeparametern ermöglicht eine weitreichende Anpassung, sodass die Nutzer die Erkennungsalgorithmen optimieren, die Alarmschwellen anpassen und die visuellen Anzeigen so verändern können, dass sie mit ihren individuellen Handelsmethoden und Marktbedingungen übereinstimmen.

Einsatz des RSIDivergenceDetector

Der Bildschirmausschnitt unten zeigt den Equidistant Channel Auto-Placement EA, der im Strategy Tester getestet wird und dessen Effektivität bei der genauen Identifizierung und Zeichnung von Kanalstrukturen demonstriert wird. In Verbindung mit dem RSI-Divergenz-Detektor können Handelssignale anhand dieser strukturellen Formationen validiert werden, wodurch ein Rahmen mit mehreren Bestätigungen für Ausführungsentscheidungen mit höherer Konfidenz entsteht.

Prüfung des ECAP

Testen des EquidistantChannel Auto-Placement EA im Strategy Tester


Schlussfolgerung

Das Zusammentreffen von RSI-Divergenzsignalen mit etablierten Marktstrukturen, insbesondere Kanalgrenzen, schafft einen leistungsstarken Rahmen für die Identifizierung von Handels-Setups mit höherer Wahrscheinlichkeit. In dieser umfassenden Diskussion haben wir erfolgreich zwei sich ergänzende Komponenten der technischen Analyse automatisiert: Erkennung von RSI-Divergenzmustern und intelligente äquidistante Kanalplatzierung.

Diese Tools arbeiten synergetisch und konfliktfrei auf demselben Chart – der RSI-Divergenz-Detektor fungiert als nutzerdefinierter Indikator, während das Equidistant Channel Auto-Placement als Expert Advisor arbeitet. Unser modularer Entwicklungsansatz ermöglichte eine zielgerichtete Implementierung und Prüfung jeder einzelnen Komponente, wobei eine klare Trennung der einzelnen Bereiche beibehalten wurde.

Während die derzeitigen unabhängigen Module einen unmittelbaren Nutzen bieten, besteht die natürliche Weiterentwicklung darin, ein einheitliches Handelssystem zu entwickeln, das diese Funktionen mit einer automatisierten Ausführungslogik integriert – eine spannende Perspektive für die zukünftige Entwicklung.

Die detaillierten Code-Erklärungen und Implementierungsbeispiele bieten praktische Einblicke in die professionelle MQL5-Programmierung, die Architektur von Handelssystemen und algorithmische Validierungstechniken. Die vollständigen Quelldateien für beide Projekte sind in den Anhängen für weitere Studien und Anpassungen verfügbar.

Wir freuen uns über weitere Diskussionen, Fragen und konstruktives Feedback in den unten stehenden Kommentaren.



Wichtige Lektionen

Wichtige LektionenBeschreibung
1. Modulare SystemarchitekturWir trennen komplexe Systeme in unabhängige, testbare Module (Indikator + EA), die später integriert werden können.
2. Zustandsmanagement für die Kontrolle von AlarmenEs wird eine zeitbasierte Drosselung der Warnmeldungen (lastAlertTime) implementiert, um ständige Warnmeldungen und Benachrichtigungsmüdigkeit zu vermeiden.
3. Validation-First-EntwurfsmusterEs erfordert mehrere Kursberührungen und Bestätigungsbrüche, bevor Muster signalisiert werden, der Wert liegt auf der Zuverlässigkeit.
4. Smart Object Lifecycle ManagementEs werden eindeutige Namenspräfixe und systematische Aufräumarbeiten (OnDeinit) verwendet, um eine Anhäufung von Chart-Objekten zu verhindern.
5. Leistungsoptimierte EreignisbehandlungEs wird die Erkennung neuer Balken und die Drosselung der Verarbeitung Implementiert, um ein Gleichgewicht zwischen Reaktionsfähigkeit und Recheneffizienz herzustellen.

Anlagen

Quelle DateinameVersionBeschreibung
RSIDivergenceDetector.mq51.00Nutzerdefinierter RSI-Divergenzindikator, der regelmäßige/versteckte Divergenzen erkennt, Pivot-Werte speichert und klare Kauf-/Verkaufspfeile auf dem Hauptpreischart mit konfigurierbaren Warnungen und RSI-Break-Bestätigung anzeigt.
EquidistantChannelAuto-Placement.mq51.00Intelligenter Expert Advisor, der automatisch einen einzigen gültigen äquidistanten Kanal mit Berührungsvalidierung, kontrollierten Erweiterungen und intelligenter Alarmdrosselung für steigende (Verkauf) und fallende (Kauf) Setups erkennt und zeichnet.

Übersetzt aus dem Englischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/en/articles/20554

Letzte Kommentare | Zur Diskussion im Händlerforum (1)
Sasha Voitko
Sasha Voitko | 21 Jan. 2026 in 06:23
Das ist eine großartige Idee. In Echtzeit konnte das volle Potenzial der Entwicklungen nicht ausgeschöpft werden. Die RSI-Signale erscheinen nur, wenn man auf die Schaltfläche "Aktualisieren" drückt oder von einem Zeitrahmen zum anderen wechselt. Der Kanal wird gezeichnet und verschwindet nach einiger Zeit oder erscheint sogar überhaupt nicht. Ich möchte ihn nutzen, aber ich kann es noch nicht.
Tabellen- und Kopfzeilen-Klassen auf der Grundlage eines Tabellenmodells in MQL5: Anwendung des MVC-Konzepts Tabellen- und Kopfzeilen-Klassen auf der Grundlage eines Tabellenmodells in MQL5: Anwendung des MVC-Konzepts
Dies ist der zweite Teil des Artikels, der sich mit der Implementierung des Tabellenmodells in MQL5 unter Verwendung des MVC (Model-View-Controller) Architekturparadigmas beschäftigt. Der Artikel behandelt die Entwicklung von Tabellenklassen und des Tabellenkopfes auf der Grundlage eines zuvor erstellten Tabellenmodells. Die entwickelten Klassen bilden die Grundlage für die weitere Implementierung von View- und Controller-Komponenten, die in den folgenden Artikeln behandelt werden.
Implementierung eines Tabellenmodells in MQL5: Anwendung des MVC-Konzepts Implementierung eines Tabellenmodells in MQL5: Anwendung des MVC-Konzepts
In diesem Artikel betrachten wir den Prozess der Entwicklung eines Tabellenmodells in MQL5 unter Verwendung des MVC-Architekturmusters (Model-View-Controller) zur Trennung der Logik, Darstellung und Steuerung der Daten, was strukturierten, flexiblen und skalierbaren Code ermöglicht. Wir betrachten die Implementierung von Klassen zum Aufbau eines Tabellenmodells, einschließlich der Verwendung von verknüpften Listen zur Speicherung von Daten.
Vom Neuling zum Experten: Automatisierung der Handelsdisziplin mit einem MQL5 Risk Enforcement EA Vom Neuling zum Experten: Automatisierung der Handelsdisziplin mit einem MQL5 Risk Enforcement EA
Für viele Händler ist die Lücke zwischen der Kenntnis einer Risikoregel und deren konsequenter Befolgung der Punkt, an dem die Konten sterben. Emotionale Übertreibungen, Kompensationshandel und einfaches Versehen können selbst die beste Strategie zunichte machen. Heute werden wir die MetaTrader 5-Plattform in einen unnachgiebigen Vollstrecker Ihrer Handelsregeln verwandeln, indem wir einen Risk Enforcement Expert Advisor entwickeln. Nehmen Sie an dieser Diskussion teil und erfahren Sie mehr.
Vom Neuling zum Experten: Entwicklung eines geografischen Marktbewusstseins mit MQL5-Visualisierung Vom Neuling zum Experten: Entwicklung eines geografischen Marktbewusstseins mit MQL5-Visualisierung
Handeln ohne Sitzungsbewusstsein ist wie Navigieren ohne Kompass – man bewegt sich, aber nicht zielgerichtet. Heute revolutionieren wir die Art und Weise, wie Händler das Markt-Timing wahrnehmen, indem wir gewöhnliche Charts in dynamische geografische Darstellungen verwandeln. Mithilfe der leistungsstarken Visualisierungsfunktionen von MQL5 erstellen wir eine Live-Weltkarte, die aktive Handelssitzungen in Echtzeit beleuchtet und abstrakte Marktzeiten in intuitive visuelle Intelligenz verwandelt. Diese Reise schärft Ihre Handelspsychologie und offenbart professionelle Programmiertechniken, die die Lücke zwischen komplexen Marktstrukturen und praktischen, umsetzbaren Erkenntnissen schließen.