Entwicklung des Price Action Analysis Toolkit (Teil 50): Entwicklung der RVGI, CCI und SMA Confluence Engine in MQL5
Inhalt
Einführung
Zu erkennen, wann der Markt einen überkauften oder überverkauften Zustand erreicht hat, bleibt eine der schwierigsten Aufgaben beim Handel. Selbst erfahrenen Händlern fällt es oft schwer, den genauen Zeitpunkt zu bestimmen, an dem ein starker Trend seinen Höhepunkt erreicht hat und eine Umkehr bevorsteht. Man kann Stunden damit verbringen, Charts zu analysieren und zu erwarten, dass sich die vorherrschende Bewegung fortsetzt, nur um dann zu spät festzustellen, dass sich die Dynamik bereits verlagert hat. Für viele, vor allem für diejenigen, die sich noch mit Preisaktionen beschäftigen, führt diese Unsicherheit zu Verwirrung und emotionalen Entscheidungen, die eine disziplinierte Ausführung untergraben. In meiner jahrelangen Arbeit mit Charts habe ich dieses Muster immer wieder beobachtet. Es liegt nicht an mangelnden Fähigkeiten. Die Märkte bewegen sich einfach nicht in perfektem Gleichgewicht. Sie erzeugen falsche Signale, abrupte Ausschläge und irreführende Rücksetzer, die selbst den diszipliniertesten Marktteilnehmer in die Falle locken können. Diese Realität inspirierte die Price Action Analysis Toolkit-Serie. Das Projekt zielt darauf ab, die Marktinterpretation zu vereinfachen, die emotionale Beeinflussung zu reduzieren und mehr Ordnung in das Lesen von Charts zu bringen.
Von Anfang an war das Ziel streng und praktisch. Wir zerlegen das Preisverhalten in messbare Komponenten, die getestet, automatisiert und konsistent angewendet werden können. Jede Herausforderung – das Erkennen echter Umkehrungen, das Bestätigen von Ausbrüchen, das Herausfiltern schwacher Impulse – wird zu einer Gelegenheit, systematische Regeln zu entwickeln. Diese Regeln müssen im Code reproduzierbar und unter Live-Bedingungen robust sein. Die gemeinsamen Schwierigkeiten, mit denen die Händler konfrontiert sind, werden in den Entwicklungsprozess einbezogen, und diese gemeinsame Erfahrung führt zu einer kontinuierlichen Verbesserung. In der heutigen Umgebung bietet die Automatisierung einen klaren Vorteil. Mit MQL5 können wir sorgfältige Beobachtungen in deterministische Logik umsetzen. Indikator-Handles, Pufferwerte und Prüfungen auf geschlossene Balken laufen ermüdungsfrei. Ein gut konzipierter Expert Advisor führt eine objektive Analyse durch und sorgt für Disziplin, sodass sich der Händler auf die Ausführung und das Risikomanagement konzentrieren kann.
In dieser Folge stelle ich einen analytischen Ansatz vor, der Klarheit in unsichere Märkte bringen soll. Die Strategie umfasst drei Komponenten: RVGI, CCI(14) und SMA(30), denen jeweils eine andere Rolle zukommt. RVGI liefert ein geglättetes Momentum-Signal. Der CCI erkennt überkaufte und überverkaufte Extremwerte. SMA bietet einen strukturellen Kontext und eine Trendverzerrung. Wenn diese Elemente zusammenpassen, erzeugt das System ein einziges zusammenhängendes Signal, das die Richtung bestätigt, Umkehrungen bestätigt und frühes Ausbruchspotenzial identifiziert.

Der Rest des Artikels behandelt den Entscheidungsablauf und die MQL5-Implementierung. Erwarten Sie eine klare Umsetzung des Konzepts in Code, einschließlich der Indikator-Handles, der Signallogik, der Visualisierung und des Expert Advisor-Lebenszyklus, wie OnInit und OnTick. Das Ziel ist kein weiterer Indikator. Unser Ziel ist eine strukturierte Entscheidungsmaschine, die Preise intelligent liest, Rauschen reduziert und das Vertrauen in den Handel stärkt. Dieses Toolkit destilliert jahrelange Beobachtungen in ein adaptives System, das die Preisaktionsanalyse unter realen Handelsbedingungen vereinfachen und verbessern soll.
Strategielogik und Gestaltungsrahmen
Jedes Analyseinstrument beginnt mit einem klaren Ziel. Bei diesem System besteht das Hauptziel darin, die Momente der Erschöpfung und des Übergangs innerhalb eines vorherrschenden Trends zu identifizieren – die kritischen Punkte, an denen die Dynamik nachlässt und eine potenzielle Umkehrung Gestalt annimmt. Um dies zu erreichen, bedarf es eines Rahmens, der in der Lage ist, die subtilen Veränderungen der Marktenergie, der Richtung und des Timings zu erfassen. Um dies zu erreichen, integriert die Strategie drei unterschiedliche, aber miteinander verbundene Komponenten: Momentum-Erkennung (RVGI), Marktzustandsmessung (CCI) und Trendfilterung (SMA). In Kombination bilden diese Elemente einen synchronisierten Mechanismus, der den Marktrhythmus liest und interpretiert und einen umfassenden Überblick über Energie, Übertreibungen und Richtungsvorgaben bietet.
1. Erkennen von Impulsen – Die Rolle von RVGI
Der Relative Vigor Index (RVGI) misst die anhaltende Kraft hinter den Kursbewegungen, indem er vergleicht, wo sich die Balken relativ zu ihrer Spanne schließen, und diese Beziehung dann über die Zeit glättet. In einem gesunden Aufwärtstrend häufen sich die Schlusskurse in der Nähe der Höchststände, und der RVGI steigt; in einem Abwärtstrend häufen sich die Schlusskurse in der Nähe der Tiefststände, und der RVGI fällt. Bei dieser Strategie ist das RVGI die erste Bestätigungsebene, der Herzschlag des Momentums, der uns sagt, ob die Energie des Marktes eine Wende unterstützt. Ein sauberes Kreuzen der RVGI-Hauptlinien seiner Signallinie signalisiert eine Verschiebung der Impulsrichtung, und wir verwenden das Kreuzen von geschlossenen Balken, um nicht auf das Rauschen innerhalb der Balken zu reagieren. Eine Divergenz – z. B. ein fallender RVGI, während der Kurs neue Höchststände erreicht – deutet darauf hin, dass die aktuelle Bewegung an Stärke verliert, und bereitet das Setup für die Bestätigung durch CCI und SMA vor. Auf diese Weise löst der RVGI nicht nur Handelsgeschäfte aus, sondern bestätigt auch, dass sich das Momentum ändert, sodass jede vom CCI angezeigte und vom SMA begrenzte Umkehr eine höhere Überzeugungskraft hat.
2. Messung der Marktlage – Die Funktion des CCI
Der Commodity Channel Index, der auf 14 Perioden eingestellt ist, misst, wie weit sich der Preis von seinem statistischen Mittelwert entfernt hat. Dies geschieht durch den Vergleich des typischen Kurses mit seinem gleitenden Durchschnitt, skaliert mit der mittleren Abweichung. In der Praxis hebt der CCI hervor, wenn sich der Kurs aus seiner normalen Spanne herausbewegt, was ihn zu einem klaren Instrument für die Erkennung von überkauften und überverkauften Bedingungen macht. Im Extremfall ist CCI eine Warnleuchte. Werte über +100 deuten im Allgemeinen auf eine überkaufte Spannung hin, während Werte unter -100 auf einen überverkauften Druck hindeuten. Diese extremen Werte allein sind keine Garantie für eine sofortige Umkehr, aber sie markieren Zonen, in denen eine Umkehr wahrscheinlicher wird, wenn die Dynamik die Richtung ändert.
Beim Timing und der Bestätigung funktioniert der CCI am besten als Realitätscheck und nicht als eigenständiger Auslöser. Stellen wir uns damit eine einfache Frage: Hat sich der Markt weit genug bewegt, um eine Gegenbewegung zu rechtfertigen? In Kombination mit einem Momentum-Tool wie RVGI fügt der CCI einen Kontext hinzu. Ein positiver CCI nahe +100, während das RVGI-Momentum nachlässt, erhöht die Wahrscheinlichkeit eines Tops. Ein tief negativer CCI mit nachlassendem Abwärtsmomentum deutet auf ein Rebound-Setup hin. In operativer Hinsicht behandelt das System CCI als das Erschöpfungsgatter. Eine gültige Aufwärts-Trendwende setzt voraus, dass der CCI -100 erreicht oder überschritten hat und dann wieder über -100 steigt. Für eine gültige Abwärts-Trendwende muss der CCI +100 erreicht oder überschritten haben und dann wieder unter +100 fallen. Diese „Rückkehr vom Extremwert“-Prüfung verhindert den Einstieg, während der Preis noch im Extremwert gefangen ist, und legt die Messlatte für die Signalqualität höher.
In unserer Strategie wird der CCI niemals isoliert eingesetzt. Sie muss mit der RVGI-Dynamik und dem von SMA definierten Kontext übereinstimmen. Zusammen bilden sie eine dreischichtige Bestätigung: Der SMA definiert die strukturelle Verzerrung, der RVGI bestätigt die Momentumverschiebung, und der CCI bestätigt, dass der Preis sein Extrem ausgeschöpft hat. Diese doppelte Bestätigung – Drehung des Momentums und Rückkehr des CCI von einem Extremwert – reduziert Fehlsignale erheblich und verbessert die Zuverlässigkeit jeder aufgezeichneten Umkehrung.
bool CheckReversalSignal(int shift, bool &isBuy) { // defensive if(iBars(_Symbol,_Period) <= shift + InpCCI_Period + InpRVI_Smooth + 4) return(false); // CCI read double cciNow = cci_at(shift, InpCCI_Period); double cciPrev = cci_at(shift + 1, InpCCI_Period); // RVI read double rviMainNow = rvi_main_at(shift); double rviMainPrev = rvi_main_at(shift + 1); double rviSigNow = rvi_signal_at(shift); double rviSigPrev = rvi_signal_at(shift + 1); // SMA & price double smaVal = /* read or compute SMA */; double price = EA_Close(shift); bool priceAboveSMA = (price > smaVal); bool priceBelowSMA = (price < smaVal); // flags bool cciBuy = (cciPrev <= CCI_LOWER) && (cciNow > CCI_LOWER); bool cciSell = (cciPrev >= CCI_UPPER) && (cciNow < CCI_UPPER); bool rviUp = (rviMainPrev <= rviSigPrev) && (rviMainNow > rviSigNow); bool rviDown = (rviMainPrev >= rviSigPrev) && (rviMainNow < rviSigNow); // final combination (mean-reversion style) if(priceBelowSMA && cciBuy && rviUp) { isBuy = true; return(true); } if(priceAboveSMA && cciSell && rviDown){ isBuy = false; return(true); } return(false); }
3. Trendfilterung – Der Leitfaden von SMA(30)
Der einfache gleitende Durchschnitt mit einer 30-Perioden-Einstellung dient als strukturelles Rückgrat dieses Analyserahmens. Er glättet das Rauschen im Markt und zeigt die zugrunde liegende Richtung der Preisbewegung auf. Auf breiterer Ebene fungiert der SMA als dynamische Gleichgewichtslinie – wenn der Kurs durchgängig darüber liegt, wird der Markt als steigend eingestuft, während ein anhaltender Handel unterhalb des SMA ein fallendes Umfeld widerspiegelt. Dies macht den SMA zu einem einfachen, aber leistungsstarken Referenzpunkt für den Trendkontext.
Neben der Trenddefinition spielt der SMA(30) auch eine wichtige Rolle für das Timing und die Bestätigung. Unter Trendbedingungen hebt er potenzielle Erschöpfungszonen hervor, wenn sich der Preis zu weit von seinem Mittelwert entfernt, was darauf hindeutet, dass die Dynamik überstrapaziert sein könnte. Wenn der Kurs in Richtung des SMA zurückgeht, markiert dies häufig den Beginn einer Korrektur oder einer potenziellen Umkehr. Diese natürliche Ebbe und Flut um den SMA herum wird zur Struktur, auf der RVGI und CCI eine tiefer gehende Analyse durchführen können.
Aus strategischer Sicht ermöglicht uns der SMA, das Marktverhalten in zwei handlungsfähige Zustände zu kategorisieren – Expansion und Umkehr. In Expansionsphasen, wenn der Kurs gut auf den SMA ausgerichtet ist und sich mit Schwung bewegt, vermeiden wir gegenläufige Handelsgeschäfte und warten auf die Bestätigung der Erschöpfung. In Umkehrphasen, wenn der Kurs zu weit über oder unter den SMA steigt und die Dynamik nachlässt, bereiten wir uns auf Umkehrsignale vor. In unserem derzeitigen System definiert der SMA(30) diese wichtigen Grenzen. Wenn der Kurs über dem SMA liegt, achten wir auf eine mögliche Abwärtsumkehr, die bestätigt wird, wenn der CCI über +100 kreuzt und der RVGI einen Momentum-Kreuz nach unten zeigt. Umgekehrt, wenn der Preis unter dem SMA liegt, erwarten wir eine Aufwärts-Umkehr, die bestätigt wird, wenn der CCI auf -100 oder darunter fällt und der RVGI nach oben kreuzt.
Durch diese Angleichung entsteht ein dreistufiges Bestätigungsmodell:
- SMA(30) definiert den strukturellen Kontext und die Preisverzerrung.
- Der CCI(14) identifiziert überzogene Marktextreme.
- RVGI validiert Schwungverschiebungen durch Crossover.
Gemeinsam sorgen diese Komponenten dafür, dass jedes Signal aus einer ausgewogenen Kombination von Struktur, Momentum und Erschöpfung entsteht. Der SMA fungiert nicht nur als gleitende Durchschnittslinie – er wird zur Achse, um die sich das gesamte System dreht, und hilft, Timing, Richtung und Überzeugung zu synchronisieren. Durch die Kombination mit RVGI und CCI verwandeln wir einen normalerweise statischen Trendfilter in einen dynamischen Entscheidungsrahmen, der sich dem Marktrhythmus anpasst und die Genauigkeit der Umkehrerkennung verbessert.
Einheitliche Signalformulierung
Die wahre Stärke dieses Systems liegt in der Synchronisation dieser drei Komponenten. Ein Signal ist nur dann gültig, wenn alle drei Ebenen – Momentum (RVGI), Marktzustand (CCI) und Trend (SMA) – zusammenpassen.
- Aufwärts-Setup: Tritt auf, wenn der RVGI nach oben kreuzt, der CCI aus dem überverkauften Bereich ansteigt und der Preis unter dem SMA(30) liegt.

- Abwärts-Setup: Tritt auf, wenn der RVGI nach unten kreuzt, der CCI aus dem überkauften Bereich fällt und der Preis über dem SMA(30) liegt.

Diese dreifache Bestätigungslogik filtert die meisten falschen Signale heraus und konzentriert sich nur auf die Punkte, an denen echte Marktenergie zusammenläuft. Der Schwerpunkt liegt auf Qualität statt Quantität, d. h. es werden eher konsistente Handelsgeschäfte mit hoher Wahrscheinlichkeit als häufige, unzuverlässige Einträge angestrebt. Dieser strukturierte Ansatz bietet Händlern eine disziplinierte Methode, um die Marktdynamik genau zu interpretieren, emotionale Reaktionen zu reduzieren und eine systematische Entscheidungsfindung zu fördern.
MQL5-Implementierung
Einrichten des Grundgerüsts
Zunächst legen wir das Fundament für unseren Expert Advisor. Wir fügen eine Kopfzeile mit Kommentaren ein, in der wir unser Copyright, die Versionsnummer und Links zu relevanten Ressourcen angeben. Dies hilft uns, unseren Code übersichtlich und professionell zu halten. Dann verwenden wir #property-Direktiven wie #property copyright, #property link und #property version, um Metadaten zu definieren, die MetaTrader verwendet, um unser Skript richtig zu identifizieren. Durch die Einrichtung dieser Struktur wird sichergestellt, dass unser Code gut dokumentiert und leicht zu pflegen ist, vor allem, wenn wir ihn später weitergeben oder überarbeiten wollen.
//+------------------------------------------------------------------+ //| RVGI_CCI_SMA_Panel_EA.mq5 | //| Copyright 2025, Christian Benjamin. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2025, MetaQuotes Ltd." #property link "https://www.mql5.com" #property version "1.00"
Definieren von Nutzereingaben
Als Nächstes fügen wir Nutzereingabeparameter hinzu, um unser System flexibel und anpassungsfähig zu machen. Diese Eingaben sind von entscheidender Bedeutung, denn sie ermöglichen es uns, das System zu optimieren, ohne den Code neu zu schreiben. Wir definieren beispielsweise InpSMA_Period, um festzulegen, wie viele Balken der einfache gleitende Durchschnitt berücksichtigen soll – dies beeinflusst die Trenderkennung. Ähnlich steuert InpCCI_Period das Rückblickfenster für den Commodity Channel Index, der überkaufte und überverkaufte Bedingungen erkennt. InpRVI_Smooth legt fest, aus wie vielen Balken der Durchschnitt für den RVGI gebildet wird, um das Marktrauschen zu glätten und das Momentum besser widerzuspiegeln.
Es gibt auch Parameter für den ATR, wie z.B. InpATR_Period, der festlegt, wie viele Balken bei der ATR-Berechnung berücksichtigt werden, und InpATR_Multiplier, der der ATR für die Festlegung von Stop-Loss- und Take-Profit-Levels skaliert. Darüber hinaus können wir mit Kippschaltern wie InpUseATRStop wählen, ob die Stopps auf ATR oder Swing-Lows/Highs basieren sollen. Diese Eingaben geben uns die Kontrolle über die Sensibilität und das Risikomanagement des Systems und ermöglichen es uns, es für verschiedene Instrumente und Zeiträume zu optimieren.
// User inputs input int InpSMA_Period = 30; // SMA period input int InpCCI_Period = 14; // CCI period input int InpRVI_Smooth = 4; // RVI smoothing input int InpATR_Period = 14; // ATR period input double InpATR_Multiplier = 1.5; // ATR multiplier for stops input bool InpUseATRStop = true; // Use ATR for stops input int InpSL_SwingBars = 10; // Swing lookback input double InpTarget1_ATR = 1.0; // Target 1 ATR multiple input double InpTarget2_ATR = 2.0; // Target 2 ATR multiple input int InpSignalLookback = 1; // Bars to look back input int InpCheckIntervalMs = 500; // Check interval in ms
Initialisierung von Indikator-Handles und der Variablen
Sobald unsere Eingaben festgelegt sind, deklarieren wir interne Variablen, um die Indikator-Handles – hSMA für den gleitenden Durchschnitt und hATR für das Volatilitätsmaß – zu speichern. Wir initialisieren diese mit INVALID_HANDLE, um anzuzeigen, dass sie noch nicht erstellt wurden. Während der Funktion OnInit() erstellen wir die eigentlichen Indikator-Handles mit Funktionen wie iMA() für den SMA und iATR() für den ATR.
// Declare handles int hSMA = INVALID_HANDLE; int hATR = INVALID_HANDLE; // Inside OnInit() hSMA = iMA(_Symbol, _Period, InpSMA_Period, 0, MODE_SMA, PRICE_CLOSE); hATR = iATR(_Symbol, _Period, InpATR_Period);
Auf diese Weise können wir bei jedem Tick effizient auf Indikatorpuffer zugreifen, anstatt die Indikatoren jedes Mal manuell neu zu berechnen, was die Leistung verbessert. Wir initialisieren auch Variablen wie lastSignalledShift (um den letzten Balken zu verfolgen, an dem wir ein Signal erzeugt haben), lastCheckMs (um zu verwalten, wie oft wir die Marktbedingungen bewerten), lastSignalText und lastSignalTime. Diese Variablen helfen uns, doppelte Signale zu vermeiden und die Häufigkeit der Überprüfungen zu kontrollieren, um sicherzustellen, dass unser System reaktionsfähig bleibt, ohne die CPU zu überlasten.
// Internal variables int lastSignalledShift = -1; uint lastCheckMs = 0; string lastSignalText = "none"; datetime lastSignalTime = 0;
Erstellen von Hilfsfunktionen für mehr Klarheit
Um unseren Code sauber, überschaubar und wiederverwendbar zu halten, entwickeln wir Hilfsfunktionen. Zum Beispiel konstruiert make_object_name() eindeutige Namen für Chartobjekte durch Verkettung eines Präfixes mit einem Zeitstempel, was wichtig ist, wenn mehrere grafische Objekte erstellt werden, damit sie sich nicht gegenseitig überschreiben. In ähnlicher Weise konvertiert format_time_dt() datetime-Werte in sprechende Zeichenketten, wodurch Beschriftungen und Kommentare verständlicher werden. Wir erstellen auch Wrapper-Funktionen wie EA_Close(), EA_Open(), EA_High(), EA_Low() und EA_Time(), die intern die Standardfunktionen iClose(), iOpen() usw. aufrufen, jedoch mit vereinfachten Parametern. Diese Wrapper machen den Zugriff auf die Preisdaten einfacher und konsistent im gesamten Code, wodurch Fehler reduziert und die Lesbarkeit verbessert werden.
// Generate unique object names string make_object_name(const string prefix, datetime t) { return prefix + IntegerToString((int)t); } // Format time for display string format_time_dt(const datetime t) { return(TimeToString(t, TIME_DATE | TIME_SECONDS)); } // Wrappers for data retrieval double EA_Close(int shift) { return(iClose(_Symbol, _Period, shift)); } double EA_Open(int shift) { return(iOpen(_Symbol, _Period, shift)); } double EA_High(int shift) { return(iHigh(_Symbol, _Period, shift)); } double EA_Low(int shift) { return(iLow(_Symbol, _Period, shift)); } datetime EA_Time(int shift) { return(iTime(_Symbol, _Period, shift)); }
Berechnung des RVGI-Indikators
Konzentrieren wir uns nun auf die Implementierung des zentralen Momentum-Indikators RVGI. Wir beginnen mit rvi_raw_at_shift(), das eine gewichtete Summe der Schluss- und Eröffnungskurse über vier Balken berechnet. Diese Funktion hebt die jüngste Marktaktivität hervor, indem sie den mittleren Balken mehr Gewicht verleiht, was dazu beiträgt, die aktuelle Dynamik zu erfassen. Um den Rohwert zu normalisieren, wird er durch den Hoch-Tief-Bereich für jeden Balken geteilt, um sicherzustellen, dass der Indikator skaleninvariant ist. Wichtig ist, dass wir eine Prüfung hinzufügen, um eine Division durch Null zu verhindern, indem wir Null zurückgeben, wenn der Nenner zu klein ist, wodurch die Stabilität erhalten bleibt.
// Raw RVGI calculation double rvi_raw_at_shift(int shift) { if(iBars(_Symbol, _Period) <= shift + 3) return 0.0; double num0 = EA_Close(shift) - EA_Open(shift); double num1 = EA_Close(shift + 1) - EA_Open(shift + 1); double num2 = EA_Close(shift + 2) - EA_Open(shift + 2); double num3 = EA_Close(shift + 3) - EA_Open(shift + 3); double num = num0 + 2.0 * num1 + 2.0 * num2 + num3; double den0 = EA_High(shift) - EA_Low(shift); double den1 = EA_High(shift + 1) - EA_Low(shift + 1); double den2 = EA_High(shift + 2) - EA_Low(shift + 2); double den3 = EA_High(shift + 3) - EA_Low(shift + 3); double den = den0 + 2.0 * den1 + 2.0 * den2 + den3; if(MathAbs(den) < DBL_EPSILON) return 0.0; return num / den; }
Als Nächstes erstellen wir rvi_main_at(), das den rohen RVGI über die angegebene Anzahl von Balken – oft vier – glättet, um das Marktrauschen zu reduzieren und eine zuverlässigere Momentum-Linie zu erzeugen.
// Smoothed RVGI (main line) double rvi_main_at(int shift) { int n = MathMax(1, InpRVI_Smooth); double sum = 0.0; for(int i=0; i<n; i++) { sum += rvi_raw_at_shift(shift + i); } return sum / n; }
Dies geschieht durch Mittelung der Rohwerte über das Glättungsfenster. Danach führt rvi_signal_at() diesen Prozess weiter und glättet die RVGI-Hauptlinie erneut, um eine Signallinie zu erzeugen, die für die Erkennung von Überkreuzungen, die auf Verschiebungen im Marktmomentum hinweisen, unerlässlich ist. Diese kombinierten Funktionen geben uns einen robusten und reaktionsschnellen Momentum-Indikator, der uns hilft, potenzielle Umschwünge frühzeitig zu erkennen.
// Signal line (smoothed RVGI) double rvi_signal_at(int shift) { int n = MathMax(1, InpRVI_Smooth); double sum = 0.0; for(int i=0; i<n; i++) { sum += rvi_main_at(shift + i); } return sum / n; }
Implementierung der CCI-Berechnung
Anschließend implementieren wir die Funktion cci_at(), um den Commodity Channel Index manuell zu berechnen. Wir beginnen mit der Berechnung des typischen Preises für jeden Balken innerhalb des Rückblicksfensters: (Hoch + Tief + Schluss) geteilt durch 3. Wir summieren diese typischen Preise über den Zeitraum, um ihren Durchschnitt zu ermitteln, und berechnen dann die mittlere Abweichung, d. h. wie weit jeder typische Preis von diesem Mittelwert abweicht. Die aktuelle Abweichung des typischen Kurses vom Mittelwert, entsprechend skaliert, ergibt den CCI-Wert. Diese manuelle Berechnung stellt sicher, dass unser Indikator präzise und konsistent ist, und ermöglicht uns eine einfache Anpassung der Parameter. Der CCI hilft uns zu erkennen, wann der Markt in eine der beiden Richtungen übergewichtet ist, was für das Timing von Ein- und Ausstiegen entscheidend ist.
double cci_at(int shift, int period) { int totalBars = iBars(_Symbol, _Period); if(totalBars <= shift + period - 1) return 0.0; // Not enough bars double sumTP = 0.0; for(int k=0; k<period; k++) { int idx = shift + k; double tp = (EA_High(idx) + EA_Low(idx) + EA_Close(idx)) / 3.0; sumTP += tp; } double smaTP = sumTP / period; double meanDev = 0.0; for(int k=0; k<period; k++) { int idx = shift + k; double tp = (EA_High(idx) + EA_Low(idx) + EA_Close(idx)) / 3.0; meanDev += MathAbs(tp - smaTP); } meanDev /= period; if(meanDev < DBL_EPSILON) return 0.0; double tp_current = (EA_High(shift) + EA_Low(shift) + EA_Close(shift)) / 3.0; double cci = (tp_current - smaTP) / (0.015 * meanDev); return cci; }
Zugriff auf ATR-Daten
Als Nächstes implementieren wir get_atr_current(), das den letzten ATR-Wert unter Verwendung des Indikator-Handles hATR abruft. Wir verwenden CopyBuffer(), um die letzte ATR-Lesung zu erhalten; wenn der Handle ungültig oder die Daten nicht verfügbar sind, geben wir Null zurück. Dieser Wert ist von entscheidender Bedeutung, da er uns ermöglicht, adaptive Stop-Loss- und Take-Profit-Niveaus festzulegen, die die aktuelle Marktvolatilität widerspiegeln. Die Verwendung von ATR stellt sicher, dass unsere Niveaus weder zu eng noch zu locker sind und ein gutes Risiko-Ertrags-Verhältnis beibehalten wird.
double get_atr_current()
{
if(hATR == INVALID_HANDLE)
return 0.0;
double tmp[];
if(CopyBuffer(hATR, 0, 0, 1, tmp) <= 0)
return 0.0;
return tmp[0];
}Bilden der Handelsstufen
Wenn alle Indikatordaten vorhanden sind, definieren wir eine Levels-Struktur, die unseren Einstiegskurs, den Stop-Loss und zwei Take-Profit-Ziele sowie zusätzliche Informationen wie die Handelsrichtung und den Zeitstempel enthält. Die Funktion build_levels() berechnet diese Ebenen dynamisch. Zu Beginn wird der Einstieg auf den aktuellen Schlusskurs gesetzt. Wenn ATR-basierte Stops aktiviert sind, werden Stop-Loss- und TP-Levels durch Multiplikation der ATR mit nutzerdefinierten Faktoren berechnet. Wenn ATR nicht verfügbar oder deaktiviert ist, sucht es nach tiefen und hohen Umkehrpunkten innerhalb eines Rückblick-Fensters, um die Stopp-Levels festzulegen, wobei zur Sicherheit Puffer hinzugefügt werden. Dieser Ansatz stellt sicher, dass sich unsere Niveaus an das aktuelle Marktumfeld anpassen und ein Gleichgewicht zwischen Risiko und potenzieller Rendite herstellen.
struct Levels { double entry; double stop; double tp1; double tp2; datetime time; bool isBuy; }; Levels build_levels(bool isBuy, int shift) { Levels L; L.entry = EA_Close(shift); L.time = EA_Time(shift); L.isBuy = isBuy; double atr = get_atr_current(); if(atr <= 0.0) atr = SymbolInfoDouble(_Symbol, SYMBOL_POINT) * 10; if(InpUseATRStop) { L.stop = isBuy ? L.entry - atr * InpATR_Multiplier : L.entry + atr * InpATR_Multiplier; } else { // Swing low/high search double extremePrice = isBuy ? EA_Low(shift) : EA_High(shift); for(int i=1; i<=InpSL_SwingBars; i++) { double v = isBuy ? EA_Low(shift + i) : EA_High(shift + i); if(v == WRONG_VALUE) continue; if(isBuy && v < extremePrice) extremePrice = v; if(!isBuy && v > extremePrice) extremePrice = v; } // Add buffer double buffer = SymbolInfoDouble(_Symbol, SYMBOL_POINT) * 5; L.stop = isBuy ? extremePrice - buffer : extremePrice + buffer; } // Targets L.tp1 = isBuy ? L.entry + atr * InpTarget1_ATR : L.entry - atr * InpTarget1_ATR; L.tp2 = isBuy ? L.entry + atr * InpTarget2_ATR : L.entry - atr * InpTarget2_ATR; return L; }
Visualisierung von Signalen und Levels
Um unsere Signale klar zu kommunizieren, implementieren wir draw_signal_objects(). Diese Funktion erstellt grafische Elemente auf dem Chart: Pfeile, die den Einstiegspunkt anzeigen, horizontale Linien für Stop-Loss- und TP-Niveaus und Etiketten, die die genauen Preise anzeigen. Jedes Objekt erhält einen eindeutigen Namen, indem ein Präfix mit dem Zeitstempel kombiniert wird, um Konflikte zu vermeiden, wenn mehrere Signale erzeugt werden. Wir passen Farben, Linienstärken und Pfeilformen an, um die Übersichtlichkeit zu erhöhen. Nachdem wir diese Objekte erstellt haben, rufen wir ChartRedraw() auf, um das Chart sofort zu aktualisieren. Dieses visuelle Feedback hilft uns, Signale und Füllstände auf einen Blick zu überprüfen, was die manuelle Verwaltung erleichtert.
void draw_signal_objects(const Levels &L) { string baseName = make_object_name("RVGI_SIG_", L.time); // Draw arrow string arrowName = baseName + (L.isBuy ? "_BUY_ARR" : "_SELL_ARR"); if(ObjectFind(0, arrowName) >= 0) ObjectDelete(0, arrowName); ObjectCreate(0, arrowName, OBJ_ARROW, 0, L.time, L.entry); ObjectSetInteger(0, arrowName, OBJPROP_ARROWCODE, L.isBuy ? 233 : 234); ObjectSetInteger(0, arrowName, OBJPROP_COLOR, L.isBuy ? InpArrowUpColor : InpArrowDownColor); ObjectSetInteger(0, arrowName, OBJPROP_WIDTH, 2); ObjectSetInteger(0, arrowName, OBJPROP_BACK, true); // Stop line string slName = baseName + "_SL"; if(ObjectFind(0, slName) >= 0) ObjectDelete(0, slName); ObjectCreate(0, slName, OBJ_HLINE, 0, 0, L.stop); ObjectSetDouble(0, slName, OBJPROP_PRICE, L.stop); ObjectSetInteger(0, slName, OBJPROP_COLOR, InpSL_Color); ObjectSetString(0, slName, OBJPROP_TEXT, "SL: " + DoubleToString(L.stop, _Digits)); // TP1 line string tp1Name = baseName + "_TP1"; if(ObjectFind(0, tp1Name) >= 0) ObjectDelete(0, tp1Name); ObjectCreate(0, tp1Name, OBJ_HLINE, 0, 0, L.tp1); ObjectSetDouble(0, tp1Name, OBJPROP_PRICE, L.tp1); ObjectSetInteger(0, tp1Name, OBJPROP_COLOR, InpTP_Color); ObjectSetString(0, tp1Name, OBJPROP_TEXT, "TP1: " + DoubleToString(L.tp1, _Digits)); // TP2 line string tp2Name = baseName + "_TP2"; if(ObjectFind(0, tp2Name) >= 0) ObjectDelete(0, tp2Name); ObjectCreate(0, tp2Name, OBJ_HLINE, 0, 0, L.tp2); ObjectSetDouble(0, tp2Name, OBJPROP_PRICE, L.tp2); ObjectSetInteger(0, tp2Name, OBJPROP_COLOR, InpTP_Color); ObjectSetString(0, tp2Name, OBJPROP_TEXT, "TP2: " + DoubleToString(L.tp2, _Digits)); ChartRedraw(); }
Erstellen eines Echtzeit-Datenpanels
Wir fügen auch draw_panel() hinzu, um einen schnellen Überblick direkt auf dem Chart zu geben. Es holt die aktuellen Indikatorwerte – wie SMA, RVGI, CCI und ATR – entweder aus Puffern oder durch manuelle Berechnung, wenn die Indikator-Handles ungültig sind. Wir formatieren diese Daten in eine klare, lesbare Zeichenkette und zeigen sie mit Comment() an. Dieses Panel gibt uns einen sofortigen Einblick in die Marktbedingungen und den Status des Systems und hilft uns, fundierte Entscheidungen zu treffen und die laufende Leistung zu überwachen, ohne zu anderen Bildschirmen wechseln zu müssen.
void draw_panel() { string txt = "RVGI + CCI + SMA Panel\n"; // SMA double smaValue = 0; if(hSMA != INVALID_HANDLE) { double tmp[]; if(CopyBuffer(hSMA, 0, 0, 1, tmp) > 0) smaValue = tmp[0]; else smaValue = 0; // fallback } else { // manual calculation fallback double sum=0; int cnt=0; for(int i=0; i<InpSMA_Period; i++) { if(iBars(_Symbol, _Period) <= i) break; sum += EA_Close(i); cnt++; } if(cnt>0) smaValue = sum / cnt; } txt += "SMA(" + IntegerToString(InpSMA_Period) + "): " + DoubleToString(smaValue, _Digits) + "\n"; // RVGI & CCI double rviM = rvi_main_at(1); double rviS = rvi_signal_at(1); double cciV = cci_at(1, InpCCI_Period); txt += "RVI: " + DoubleToString(rviM,5) + " sig: " + DoubleToString(rviS,5) + "\n"; txt += "CCI: " + DoubleToString(cciV,2) + "\n"; // ATR double atr = get_atr_current(); txt += "ATR: " + DoubleToString(atr, _Digits) + "\n"; // Last signal info txt += "Last signal: " + lastSignalText; if(lastSignalTime != 0) txt += " " + TimeToString(lastSignalTime, TIME_DATE | TIME_SECONDS); Comment(txt); }
Bewertung der Marktbedingungen und Generierung von Signalen
Der Kern der Entscheidungsfindung befindet sich in check_for_signals(). Wir implementieren eine Drosselung mit GetTickCount(), um eine übermäßige CPU-Belastung zu vermeiden, indem wir die Häufigkeit der Auswertungen kontrollieren. Wir bestimmen die Verschiebung auf der Grundlage unserer Rückblick-Einstellung und überprüfen, ob genügend Balken für die Analyse geladen sind. Anschließend rufen wir die Indikatordaten aus Puffern ab oder berechnen sie neu, wenn die Griffe ungültig sind, um sicherzustellen, dass unsere Berechnungen immer aktuell sind.
void check_for_signals() { uint now = (uint)GetTickCount(); if(lastCheckMs != 0 && (now - lastCheckMs) < (uint)InpCheckIntervalMs) return; // Throttle lastCheckMs = now; int shift = InpSignalLookback; // Verify enough bars loaded if(iBars(_Symbol, _Period) <= shift + 6) return; // Retrieve SMA double smaVal = 0; if(hSMA != INVALID_HANDLE) { double tmp[]; if(CopyBuffer(hSMA, 0, shift, 1, tmp) > 0) smaVal = tmp[0]; } else { // fallback manual SMA double sum=0; int cnt=0; for(int i=shift; i<shift + InpSMA_Period; i++) { if(iBars(_Symbol, _Period) <= i) break; sum += EA_Close(i); cnt++; } if(cnt > 0) smaVal = sum / cnt; else return; // Not enough data } // Get indicator values double rviMainNow = rvi_main_at(shift); double rviMainPrev = rvi_main_at(shift + 1); double rviSigNow = rvi_signal_at(shift); double rviSigPrev = rvi_signal_at(shift + 1); double cciNow = cci_at(shift, InpCCI_Period); double cciPrev = cci_at(shift + 1, InpCCI_Period); double price = EA_Close(shift); // Trend and momentum analysis bool priceAboveSMA = (price > smaVal); bool priceBelowSMA = (price < smaVal); bool rviCrossUp = (rviMainPrev <= rviSigPrev) && (rviMainNow > rviSigNow); bool rviCrossDown = (rviMainPrev >= rviSigPrev) && (rviMainNow < rviSigNow); bool cciOverbought = (cciNow >= 100); bool cciOversold = (cciNow <= -100); // Generate signals based on conditions if(priceAboveSMA && cciOversold && rviCrossUp) { // Buy signal if(shift != lastSignalledShift) { Levels L = build_levels(true, shift); draw_signal_objects(L); lastSignalledShift = shift; lastSignalText = "BUY @ " + DoubleToString(price, _Digits); lastSignalTime = EA_Time(shift); // Send alerts if enabled... } } else if(priceBelowSMA && cciOverbought && rviCrossDown) { // Sell signal if(shift != lastSignalledShift) { Levels L = build_levels(false, shift); draw_signal_objects(L); lastSignalledShift = shift; lastSignalText = "SELL @ " + DoubleToString(price, _Digits); lastSignalTime = EA_Time(shift); // Send alerts if enabled... } } // Update panel info draw_panel(); }
Als Nächstes analysieren wir die Signale der Indikatoren: Wir prüfen, ob der RVGI seine Signallinie über- oder unterschritten hat – was auf Schwungverschiebungen hindeutet – und bewerten, ob sich der CCI in überkauften oder überverkauften Zonen befindet. Wir vergleichen auch den aktuellen Kurs mit dem SMA, um den Trend zu bestimmen. Wenn alle diese Bedingungen zusammentreffen – z. B. wenn der RVGI nach oben kreuzt, der CCI überverkauft ist und der Preis unter dem SMA liegt – generieren wir ein Kaufsignal. Die umgekehrten Bedingungen lösen ein Verkaufssignal aus. Wir stellen sicher, dass wir keine doppelten Signale erzeugen, indem wir die zuletzt gemeldete Schicht verfolgen. Wenn ein neues Signal bestätigt wird, generieren wir Handelsstufen, visualisieren sie auf dem Chart und aktualisieren interne Variablen. Wir senden auch Warnungen oder Benachrichtigungen, wenn sie aktiviert sind, um uns auf dem Laufenden zu halten.
Verwaltung des EA-Lebenszyklus und Ressourcenbereinigung
Schließlich erstellen wir in OnInit() die Indikator-Handles für SMA und ATR, prüfen auf Fehler und initialisieren die Variablen. Wir zeichnen auch das Informationsfeld für schnelle Statusaktualisierungen.
// In OnInit() int OnInit() { hSMA = iMA(_Symbol, _Period, InpSMA_Period, 0, MODE_SMA, PRICE_CLOSE); hATR = iATR(_Symbol, _Period, InpATR_Period); // Initialize variables lastSignalledShift = -1; lastCheckMs = 0; lastSignalText = "none"; lastSignalTime = 0; draw_panel(); Print("EA initialized"); return(INIT_SUCCEEDED); }
Während OnDeinit() geben wir die Indikator-Handles mit IndicatorRelease() frei, damit die Ressourcen ordnungsgemäß freigegeben werden. In OnTick() rufen wir einfach check_for_signals() auf, um unser System kontinuierlich laufen zu lassen. Diese Struktur gewährleistet, dass unser Sachverständiger während seiner gesamten Tätigkeit effizient, reaktionsschnell und gut verwaltet bleibt.
// In OnDeinit() void OnDeinit(const int reason) { if(hSMA != INVALID_HANDLE) IndicatorRelease(hSMA); if(hATR != INVALID_HANDLE) IndicatorRelease(hATR); } // In OnTick() void OnTick() { check_for_signals(); }
Tests und Ergebnisse
Nach der Implementierung des Systems führten wir gründliche Tests durch, um seine Wirksamkeit unter realen Marktbedingungen zu bewerten. Die Ergebnisse zeigen deutlich, dass die integrierten Indikatoren und Niveaus verlässliche Einstiegssignale liefern, die auf dem Chart visuell bestätigt werden und so für Transparenz und einfache Validierung sorgen.
Visuelle Bestätigung und Signalgenauigkeit
Das erste beobachtete Ergebnis war die genaue visuelle Signalisierung auf der Karte. Das System generierte Kauf- und Verkaufspfeile genau dann, wenn die RVGI-, CCI- und SMA-Bedingungen gemäß unserer Logik übereinstimmten. So erschien beispielsweise ein grüner Aufwärtspfeil, als der RVGI nach oben kreuzte, der CCI in den überverkauften Bereich eintrat und der Kurs unter dem SMA lag, was ein starkes Kaufsignal darstellte. Diese Signale wurden von klar markierten Einstiegs-, Stop-Loss- und Take-Profit-Levels begleitet, die eine sofortige visuelle Bestätigung der Systemanalyse lieferten.
Kaufsignal: GBPUSD M1

Verkaufssignal: Volatilität 75 (1s) Index M1

Validierung von Indikatordaten
Das zweite wichtige Ergebnis war die Angleichung der Indikatorwerte zum Zeitpunkt der Signale. Aus den detaillierten Daten ging hervor, dass sich der RVGI nach oben bewegte, was eine zunehmende Dynamik bestätigte. Der CCI befand sich im überverkauften Bereich (unter -100), was darauf hindeutet, dass der Markt nach unten hin überreizt und bereit für eine Umkehr ist. Gleichzeitig lag der Kurs unter dem SMA, was auf ein Aufwärts-Setup hindeutet. Diese mehrschichtige Bestätigung gab uns die Gewissheit, dass die Signale auf einer soliden technischen Grundlage beruhten, was die Zahl der Fehlalarme reduzierte.

Ausführung des Handels und Rentabilität
Das dritte und wichtigste Ergebnis war die erfolgreiche Ausführung von Handelsgeschäften auf den vorgesehenen Ebenen. Die Orders wurden genau an den visuellen Einstiegspunkten ausgelöst, wobei Stop-Loss- und Take-Profit-Levels auf der Grundlage von ATR oder hohen undd tiefen Umkehrpunkten, wie konfiguriert, genau festgelegt wurden. Die Handelsgeschäfte verliefen wie erwartet, wobei das System bei günstigen Marktentwicklungen effizient Gewinne mitnahm. Das in den Ergebnissen gezeigte Beispiel bestätigte die Fähigkeit des Systems, Eingänge mit hoher Wahrscheinlichkeit zu erkennen und Handelsgeschäfte effektiv zu verwalten, was zu gewinnbringenden Geschäften während des Testzeitraums führte.

Gesamteffektivität
- Die kombinierten Indikatorensignale (RVGI, CCI, SMA) lieferten konsistente und verlässliche Einstiegspunkte, die durch visuelle Anhaltspunkte bestätigt wurden.
- Die für Stopps und Ziele generierten Niveaus waren gut platziert und ermöglichten ein optimales Risiko-Ertrags-Management.
- Die visuellen und datenbezogenen Ausgaben des Systems ermöglichten eine einfache manuelle Validierung und Anpassung und sorgten für Transparenz.
Auf der Grundlage dieser Ergebnisse empfehlen wir eine weitere Feinabstimmung von Parametern wie Indikatorperioden, ATR-Multiplikatoren und Signalbedingungen, um die Leistung für bestimmte Märkte oder Zeitrahmen zu optimieren. Kontinuierliche Tests und Validierungen tragen dazu bei, die Genauigkeit zu erhöhen, falsche Signale zu reduzieren und die Gesamtrentabilität zu verbessern.
Schlussfolgerung
Dieses System bringt Klarheit in eines der schwierigsten Probleme im Handel: das zuverlässige Erkennen von Umkehrbewegungen. Durch das Erfordernis von drei unabhängigen Bestätigungen – Trend, Momentum und Erschöpfung – isoliert die Engine die Momente, in denen Preisstruktur, direktionale Energie und Marktmüdigkeit konvergieren. Das Ergebnis ist eine geringere Anzahl von Signalen, von denen jedes eine klare technische Begründung und einen messbaren Wert auf dem Chart hat. Betrachten Sie den Expert Advisor als eine Analyse-Engine und nicht als eine Plug-and-Play-Lösung. Verwenden Sie es zur Visualisierung von Einstellungen, zur Aufzeichnung von Signalen und zum systematischen Testen von Ideen. Führen Sie reproduzierbare Backtests auf der Logik geschlossener Balken durch, protokollieren Sie jedes Signal mit der zugrunde liegenden Struktur und überprüfen Sie die Out-of-Sample-Performance, bevor Sie Parameter ändern. Diese Disziplin verhindert eine Kurvenanpassung und bewahrt die Integrität Ihrer Ergebnisse.
Bewusster Umgang mit Risiken. Verwenden Sie die vom EA vorgeschlagenen Stopps – Swing-basiert oder ATR-skaliert – als Basisregeln. Wählen Sie das Volumen der Positionen, um Ihr Kapital zu schützen, und bevorzugen Sie Teilausstiege oder Trailing-Stops, die Ihrem Zeithorizont und Ihrer Risikotoleranz entsprechen. Wenn Sie später ein Ausführungsmodul erstellen, sollten Sie die ursprünglichen Signalregeln beibehalten und eine Auftragslogik entwerfen, die dieselben Bestätigungen und Risikoleitplanken beachtet.
Betrachten Sie dieses Toolkit als eine Brücke von der Beobachtung zum Handeln. Es wandelt das Preisverhalten in wiederholbare Regeln um, reduziert emotionale Störungen und bietet einen klaren Rahmen für disziplinierte Entscheidungen. Nutzen Sie es, um Ihr Urteilsvermögen zu schärfen, zu dokumentieren, was funktioniert, und das System von einer Forschungsmaschine zu einem zuverlässigen Teil Ihres Handelsablaufs zu entwickeln.
Bitte beachten: Die vollständige Quellcodedatei ist am Ende dieses Artikels beigefügt. Klicken Sie auf die Datei, um sie herunterzuladen, öffnen Sie sie dann und kompilieren Sie sie in MetaEditor. Testen Sie es gründlich und passen Sie die Parameter an Ihre Vorlieben und Handelsbedingungen an.
Übersetzt aus dem Englischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/en/articles/20262
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.
Entwicklung einer Handelsstrategie: Die Triple-Sinus-Mittelwertumkehrmethode
Automatisieren von Handelsstrategien in MQL5 (Teil 40): Fibonacci-Retracement-Handel mit nutzerdefinierten Levels
Eine alternative Log-datei mit der Verwendung der HTML und CSS
Klassische Strategien neu interpretieren (Teil 13): Unsere Kreuz-Strategie in neue Dimensionen führen (Teil 2)
- 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.