Vom Neuling zum Experten: Handel mit dem RSI unter Berücksichtigung der Struktur des Marktes
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:
- Der Algorithmus identifiziert gültige Trendkanäle.
- Interpretiert das RSI-Verhalten im Zusammenhang mit diesen Kanälen auf intelligente Weise.
- 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
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
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.

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:
- 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.
- 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.
- 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: - RSI-Divergenzindikator: Ein eigenständiger technischer Indikator, der Divergenzmuster auf Kurscharts identifiziert und visuell markiert.
- 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.

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 Lektionen | Beschreibung |
|---|---|
| 1. Modulare Systemarchitektur | Wir trennen komplexe Systeme in unabhängige, testbare Module (Indikator + EA), die später integriert werden können. |
| 2. Zustandsmanagement für die Kontrolle von Alarmen | Es wird eine zeitbasierte Drosselung der Warnmeldungen (lastAlertTime) implementiert, um ständige Warnmeldungen und Benachrichtigungsmüdigkeit zu vermeiden. |
| 3. Validation-First-Entwurfsmuster | Es erfordert mehrere Kursberührungen und Bestätigungsbrüche, bevor Muster signalisiert werden, der Wert liegt auf der Zuverlässigkeit. |
| 4. Smart Object Lifecycle Management | Es werden eindeutige Namenspräfixe und systematische Aufräumarbeiten (OnDeinit) verwendet, um eine Anhäufung von Chart-Objekten zu verhindern. |
| 5. Leistungsoptimierte Ereignisbehandlung | Es wird die Erkennung neuer Balken und die Drosselung der Verarbeitung Implementiert, um ein Gleichgewicht zwischen Reaktionsfähigkeit und Recheneffizienz herzustellen. |
Anlagen
| Quelle Dateiname | Version | Beschreibung |
|---|---|---|
| RSIDivergenceDetector.mq5 | 1.00 | Nutzerdefinierter 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.mq5 | 1.00 | Intelligenter 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
Warnung: Alle Rechte sind von MetaQuotes Ltd. vorbehalten. Kopieren oder Vervielfältigen untersagt.
Dieser Artikel wurde von einem Nutzer der Website verfasst und gibt dessen persönliche Meinung wieder. MetaQuotes Ltd übernimmt keine Verantwortung für die Richtigkeit der dargestellten Informationen oder für Folgen, die sich aus der Anwendung der beschriebenen Lösungen, Strategien oder Empfehlungen ergeben.
Tabellen- und Kopfzeilen-Klassen auf der Grundlage eines Tabellenmodells in MQL5: Anwendung des MVC-Konzepts
Implementierung eines Tabellenmodells in MQL5: Anwendung des MVC-Konzepts
Vom Neuling zum Experten: Automatisierung der Handelsdisziplin mit einem MQL5 Risk Enforcement EA
Vom Neuling zum Experten: Entwicklung eines geografischen Marktbewusstseins mit MQL5-Visualisierung
- Freie Handelsapplikationen
- Über 8.000 Signale zum Kopieren
- Wirtschaftsnachrichten für die Lage an den Finanzmärkte
Sie stimmen der Website-Richtlinie und den Nutzungsbedingungen zu.