
Entwicklung des Price Action Analysis Toolkit (Teil 30): Commodity Channel Index (CCI), Zero Line EA
Einführung
In diesem Artikel bauen wir ein MQL5-gestütztes Tool, das den Handel von Preis-Aktionen vereinfacht. Das System automatisiert eine Strategie, die auf vier Elementen basiert: einem dualen Commodity Channel Index (CCI), einem Exponential Moving Average (EMA) mit einer Periodenlänge von 34, dem Average True Range (ATR) und der reinen Preisaktion. Nachdem wir die einzelnen Indikatoren besprochen und die gesamte Handelsstrategie umrissen haben, werden wir die schrittweise Entwicklung des Expert Advisors durchgehen, ihn testen, die Ergebnisse analysieren und Schlussfolgerungen ziehen. Die Tabelle auf der letzten Seite des Artikels gibt einen Überblick über die anderen von uns entwickelten Instrumente.
Bitte lesen Sie das nachstehende Inhaltsverzeichnis.
- Einführung
- Überblick über die Indikatoren
- Strategische Logik
- Gestaltung des Expert Advisors
- Tests
- Schlussfolgerung
Überblick über die Indikatoren
Der Commodity Channel Index (CCI) ist ein Momentum-Oszillator, der den aktuellen Kurs mit seinem jüngsten Durchschnitt vergleicht, um potenzielle überkaufte, überverkaufte oder trendumkehrende Bedingungen anzuzeigen. Der Commodity Channel Index (CCI) lässt sich wie unten dargestellt ableiten:
CI = (Typical Price − SMA) / (0.015 × Mean Deviation)CCI – Commodity Channel Index
Typical Price (TP) – Er wird ermittelt, indem die Höchst-, Tiefst- und Schlusskurse des Zeitraums addiert und dann durch drei geteilt werden:
h + l + c
TP = ---------
3
SMA – Einfacher gleitender Durchschnitt
Bei einem einfachen gleitenden Durchschnitt (SMA) werden die typischen Preise eines bestimmten Zeitfensters, z. B. der letzten 20 Sitzungen, addiert und durch die Anzahl der Perioden dividiert. Dieser Prozess glättet die täglichen Schwankungen und macht den zugrunde liegenden Trend leichter erkennbar.
Mittelwert Abweichung
Er ist der Mittelwert der absoluten Abweichungen der typischen Preise von ihrem einfachen gleitenden Durchschnitt.
Diese Statistik drückt aus, wie weit jeder typische Kurs im Durchschnitt vom SMA abweicht und bietet damit einen praktischen Maßstab für die Kursschwankungen (Volatilität) während des ausgewählten Zeitraums.
Konstanter Faktor 0,015Durch die Multiplikation mit diesem festen Koeffizienten wird die CCI-Ausgabe so komprimiert, dass die überwiegende Mehrheit der Werte ungefähr zwischen -100 und +100 liegt. Dadurch wird der Indikator besser lesbar und es können starke Abweichungen, die ausgeprägte überkaufte oder überverkaufte Bedingungen signalisieren, hervorgehoben werden.
Der von Donald Lambert entwickelte und 1980 erstmals veröffentlichte Indikator diente der Analyse zyklischer Bewegungen bei Rohstoffen und wird heute in großem Umfang auf Aktien, Währungen und andere Anlageklassen angewendet. Bei dieser Strategie setzen wir zwei CCIs gleichzeitig ein: einen schnellen CCI (25), um das kurzfristige Momentum zu erfassen, und einen langsamen CCI (50), um die allgemeine Marktstärke zu messen. Die meisten CCI-Werte liegen zwischen -100 und +100, und Ausschläge über diese Bandbreite hinaus zeigen oft eine außergewöhnliche Stärke oder Schwäche. Während diese äußeren Niveaus als Volatilitätsfilter dienen können, bleibt die Nulllinie selbst der primäre Auslöser in unserer Strategie. Ein Wechsel vom negativen in den positiven Bereich signalisiert eine mögliche Verschiebung hin zu einem Aufwärtsmomentum; eine Bewegung in die entgegengesetzte Richtung deutet auf einen aufkommenden Abwärtsdruck hin.
Da die Nulllinien früher gekreuzt werden als die Durchbrüche von ±100, ermöglichen sie unserem Expert Advisor einen schnelleren Einstieg, wenn auch mit der Möglichkeit von gelegentlichem Hin und Her durch die Kurse. Um dieses Rauschen abzuschwächen, sucht der EA optional nach zusätzlicher Bestätigung: Ein nachhaltiger Anstieg über +100 verstärkt Kaufsignale, und ein Rückgang unter -100 verstärkt Verkaufssignale. Kurz gesagt, das Kreuzen der Nulllinie erzeugt die Hauptalarme, während die ±100-Pegel eher als sekundärer Filter denn als Ersatz dienen.
Abb. 2: CCI-Indikator
Im Folgenden wird erklärt, wie man zwei CCI-Handles in MQL5 erstellt, sie korrekt verwaltet und ihre Werte abruft.
- Erstellen der CCI-Handles in OnInit() mit
handleCCI_Long = iCCI(_Symbol, _Period, CCI_LongPeriod, PRICE_TYPICAL); handleCCI_Short = iCCI(_Symbol, _Period, CCI_ShortPeriod, PRICE_TYPICAL);
- Kopieren von Pufferwerten mit CopyBuffer() in OnTick()
CopyBuffer(handleCCI_Long, 0, 0, 2, cciL); CopyBuffer(handleCCI_Short, 0, 0, 1, cciS);
Exponentieller gleitender Durchschnitt (EMA) mit der Periodenlänge 34
Ein Exponential Moving Average (EMA) ist ein Trendfolgeindikator, der schnell auf neue Kursinformationen reagiert, indem er den jüngsten Kerzen ein größeres Gewicht zuweist. Durch diese exponentielle Gewichtung werden kurzfristige Störungen herausgefiltert und der vorherrschende Trend wird besser erkennbar. Auf einem Ein-Stunden-Chart wird häufig ein 34-Perioden-EMA verwendet, da er sich über etwa eine Handelswoche erstreckt. Handeln Sie nur in Richtung des 34-Perioden-EMA: Kaufen Sie, wenn der Kurs über dem EMA liegt, und verkaufen Sie, wenn der Kurs darunter liegt. Diese Regel fungiert als kontinuierlicher Trendfilter, der die Strategie von gegenläufigen Handelsgeschäfte fernhält.
Abb. 3: 34-Perioden-EMA
Nachfolgend sehen Sie, wie Sie einen 34-Perioden-EMA-Handle in MQL5 erstellen
- Deklaration:
input int EMAPeriod = 34;
- Initialisierung:
handleEMA = iMA(_Symbol, _Period, EMAPeriod, 0, MODE_EMA, PRICE_CLOSE);
- Kopieren des Puffers (gleiche Logik):
double ema34[1]; if(CopyBuffer(handleEMA, 0, 0, 1, ema34) < 1) return; double ema_now = ema34[0];
Average True Range (ATR)
Der Average True Range (ATR) ist ein Volatilitätsmaß, das von J. Welles Wilder Jr. entwickelt und 1978 in seinem Buch „New Concepts in Technical Trading Systems“ vorgestellt wurde. Er bildet den Durchschnitt des True Range über einen gewählten Rückblick – Standardperiodenlänge 14 – und spiegelt damit die „rohen“ Kursschwankungen des Marktes wider.
In diesem Artikel verwenden wir den Average True Range (ATR), um unsere Stop-Loss- (SL) und Take-Profit-Niveaus (TP) zu bestimmen und festzulegen. Nachfolgend wird beschrieben, wie man es in MQL5 implementiert:
- Deklaration
int handleATR = INVALID_HANDLE; input int ATR_Period = 14;
- Initialisierung
handleATR = iATR(_Symbol, _Period, ATR_Period); if(handleATR == INVALID_HANDLE) return INIT_FAILED;
- Kopierpuffer
double atrBuf[1]; if(CopyBuffer(handleATR, 0, 0, 1, atrBuf) > 0) { double atrValue = atrBuf[0]; // use atrValue here… }
- Aufräumen
if(handleATR != INVALID_HANDLE) IndicatorRelease(handleATR);
Strategische Logik
In diesem Abschnitt erläutern wir, wie unser Expert Advisor „CCI Zero Line“ durch die Kombination von zwei Commodity Channel Index (CCI)-Indikatoren und einem 34-periodischen Exponential Moving Average (EMA) Einstiegssignale erzeugt. Wenn der Wert die Nulllinie des Commodity Channel Index (CCI) überschreitet, deutet dies darauf hin, dass sich das Marktmomentum nach oben verlagert, was ein potenziell Aufwärtssignal oder eine Zunahme des Kaufdrucks darstellt. Umgekehrt signalisiert ein Wert, der unter die Nulllinie des CCI fällt, dass sich die Marktdynamik nach unten wendet, was auf einen möglichen Abwärtstrend oder erhöhten Verkaufsdruck hindeutet.
Bewegt sich der Kurs oberhalb des 34-Perioden-EMA, deutet dies im Allgemeinen auf eine Aufwärtsbewegung der Marktdynamik hin, was auf einen möglichen Aufwärtstrend oder eine Kaufgelegenheit hindeutet. Fällt der Kurs hingegen unter den 34 EMA, deutet dies auf eine nachlassende Dynamik und den möglichen Beginn eines Abwärtstrends hin, was ein mögliches Verkaufssignal darstellt.
Verwendete Indikatoren
- CCI (25) – schneller Commodity Channel Index
- CCI (50) – langsamer Commodity Channel Index
- EMA (34) – Exponentieller gleitender Durchschnitt über 34 Perioden auf Schlusskursen
Kriterien für ein Kaufsignal
- CCI (25) über Null: Der CCI (25) muss über der Nulllinie liegen, was ein kurzfristiges Aufwärtsmomentum anzeigt.
- CCI (50) Kreuzen der Null: Der CCI (50) muss die Nulllinie nach oben durchkreuzen, um den längerfristigen Trendwechsel zu bestätigen.
- Preisbestätigung: Die aktuelle Kerze muss oberhalb des EMA (34) schließen, um den Aufwärtstrend zu bestätigen.
Abb. 4: Bedingungen für Kaufsignale
Wenn alle drei Bedingungen bei einem neuen Balken eintreten, registriert der EA ein Kaufsignal.
Bedingungen für Verkaufssignale
- CCI (25) unter Null: Der CCI (25) muss unter der Nulllinie liegen, was ein kurzfristiges Abwärts-Momentum anzeigt.
- CCI (50) Kreuzen der Null: Der CCI (50) muss die Nulllinie nach unten durchkreuzen, um die längerfristige Abwärtsbewegung zu bestätigen.
- Preisbestätigung: Die aktuelle Kerze muss unter dem EMA (34) schließen, um den Abwärtstrend zu bestätigen.
Abb. 5: Bedingungen für Verkaufssignale
Wenn alle drei Bedingungen auf einem neuen Balken eintreten, registriert der EA ein Verkaufssignal.
Gestaltung des Expert Advisors
Wenn der EA startet, richtet er zunächst die erforderlichen Indikator-Handles ein, die es ihm ermöglichen, Echtzeitdaten aus dem Chart abzurufen. Dieser Vorgang findet in der Funktion OnInit() statt. Die Handles erfassen zwei CCIs, einen mit einer langen Periode (50), um den breiteren Trend zu identifizieren, und einen mit einer kürzeren Periode (25), der als Bestätigungsindikator dient, sowie einen EMA mit einer Periode von 34, der als Trendfilter fungiert. Der Code erstellt diese Handles mit Funktionen wie iCCI() und iMA(), die die Indikatoren von der internen MetaTrader-Engine anfordern.
Die ordnungsgemäße Erstellung dieser Handles ist von entscheidender Bedeutung. Wenn ein Handle nicht initialisiert werden kann, was durch die Rückgabe von INVALID_HANDLE angezeigt wird, hält der EA an und verhindert so falsche Signale oder Fehler während des Betriebs. Zusätzlich gibt es ein optionales ATR-Handle, das nur erstellt wird, wenn die ATR-basierte Stop-Loss- und Take-Profit-Verwaltung aktiviert ist (Parameter UseATR).
handleCCI_Long = iCCI(_Symbol, _Period, CCI_LongPeriod, PRICE_TYPICAL); handleCCI_Short = iCCI(_Symbol, _Period, CCI_ShortPeriod, PRICE_TYPICAL); handleEMA = iMA(_Symbol, _Period, EMAPeriod, 0, MODE_EMA, PRICE_CLOSE); if(UseATR) handleATR = iATR(_Symbol, _Period, ATR_Period);
- Dieser Ansatz stellt sicher, dass dem EA alle notwendigen Datenströme zur Verfügung stehen, um bei jedem Tick fundierte Handelsentscheidungen zu treffen.
Der EA enthält eine Funktion zur Beschränkung der Handelsaktivität auf bestimmte Stunden, insbesondere die Tokio-Sitzung für USDJPY, um den Handel in Zeiten mit geringem Handelsvolumen zu vermeiden, die unvorhersehbare Kursbewegungen verursachen können. Dies wird durch die Überprüfung der aktuellen Serverzeit in der Funktion OnTick() realisiert. Wenn der Sitzungsfilter aktiviert ist, konvertiert der Code die aktuelle Zeit in ein strukturiertes Format (MqlDateTime) und vergleicht die Stundenkomponente mit den nutzerdefinierten Anfangs- und Endstunden. Wenn die aktuelle Stunde außerhalb dieses Fensters liegt, wird der EA einfach beendet und überspringt die weitere Analyse oder den Handel.
if(UseTokyoSessionFilter && _Symbol == "USDJPY") { datetime now = TimeTradeServer(); MqlDateTime tm; TimeToStruct(now, tm); int hr = tm.hour; if(hr < TokyoStartHour || hr >= TokyoEndHour) return; }
- Wenn Sie nur zu bestimmten Zeiten handeln, verringern Sie das Risiko von Ausrutschern und falschen Signalen, die auf dünnen Märkten außerhalb der Spitzenzeiten häufig auftreten.
Um Mehrfachauswertungen innerhalb derselben Kerze zu verhindern, verwendet der EA eine einfache, aber effektive Logik: Er zeichnet den Zeitstempel des zuletzt verarbeiteten Balkens auf und fährt nur fort, wenn sich ein neuer Balken gebildet hat. Dies wird durch eine statische Variable lastTime erreicht, die ihren Wert über Funktionsaufrufe hinweg beibehält. Wenn ein neuer Tick eintrifft, vergleicht der Code die aktuelle Balkenzeit (iTime()) mit diesem gespeicherten Wert. Wenn sie übereinstimmen, kehrt die Funktion frühzeitig zurück, sodass alle Berechnungen und Entscheidungen nur einmal pro Takt getroffen werden.
static datetime lastTime=0; datetime t = iTime(_Symbol, _Period, 0); if(t == lastTime) return; // same bar, skip lastTime = t;
- Durch die Optimierung der Leistung, die Vermeidung doppelter Signale und die Anpassung der Ausführung an die typische zeitbasierte Analyse des Händlers verbessert dieser Ansatz die Gesamteffizienz und Genauigkeit der Handelsstrategie.
Bei jedem neuen Balken ruft der EA CopyBuffer() auf, um die neuesten CCI- (25), CCI- (50) und EMA- (34) Werte (einschließlich des vorherigen Balkens für dass Erkennen des Kreuzens) zu ziehen. Der langsame CCI (50) definiert den übergeordneten Trend, während der schnelle CCI (25) das Einstiegsmomentum bestätigt. Der EMA filtert Störungen heraus, um sicherzustellen, dass sich der Kurs mit dem Trend bewegt. Wenn Sie ATR-basierte Stopps aktiviert haben, ruft der EA auch die aktuelle ATR (14) ab.
double cciL[2], cciS[1], emaVal[1]; if(CopyBuffer(handleCCI_Long, 0, 0, 2, cciL) < 2) return; if(CopyBuffer(handleCCI_Short, 0, 0, 1, cciS) < 1) return; if(CopyBuffer(handleEMA, 0, 0, 1, emaVal) < 1) return; double atrValue = 0.0; if(UseATR) { double atrBuf[1]; if(CopyBuffer(handleATR, 0, 0, 1, atrBuf) < 1) return; atrValue = atrBuf[0]; }
- Diese Werte sind die Grundlage für alle Handelsentscheidungen: Die CCIs erkennen Umkehrungen oder Fortsetzungen, der EMA bestätigt die Trendausrichtung, und die ATR bestimmt dynamisch Ihr Risiko.
Der Kern der Handelslogik besteht darin, zu erkennen, wann der langfristige CCI seine Nulllinie durchbricht und damit eine potenzielle Trendwende signalisiert. Ein Durchgang nach oben (von negativ zu positiv) deutet auf eine Aufwärtsbewegung hin, während ein Durchgang nach unten auf ein Abwärtsmomentum hinweist. Der Code erfasst diese Momente, indem er die vorherigen und aktuellen Werte des langen CCI-Puffers vergleicht. Sobald ein Kreuzen erkannt wird, wendet der EA zusätzliche Filter an: Der kurze CCI muss übereinstimmen (positiv für steigend, negativ für fallend), und der aktuelle Preis muss über bzw. unter dem EMA liegen.
double price = SymbolInfoDouble(_Symbol, SYMBOL_BID); if(crossUp && cciS[0] > 0 && price > emaVal[0]) RegisterSignal(true, price, atrValue); if(crossDown && cciS[0] < 0 && price < emaVal[0]) RegisterSignal(false, price, atrValue);
- Diese mehrstufige Bestätigung hilft dabei, falsche Signale herauszufiltern und stellt sicher, dass nur dann gehandelt wird, wenn mehrere Indikatoren übereinstimmen und der Trendfilter die Bewegung unterstützt.
Sobald ein gültiges Signal identifiziert wird, berechnet der EA geeignete Levels für Stop-Loss (SL) und Take-Profit (TP). Wenn die ATR-basierte Verwaltung aktiviert ist, werden diese Werte dynamisch abgeleitet, indem der aktuelle ATR-Wert mit dem nutzerdefinierten Multiplikator (z. B. 1,5) multipliziert wird. Der SL wird in einem gewissen Abstand zum Einstiegskurs platziert, entweder unterhalb für einen Kauf oder oberhalb für einen Verkauf. Der TP wird auf der Grundlage des Chance-Risiko-Verhältnisses proportional weiter weg gesetzt, in der Regel das 1,5-fache des SL-Abstands.
double dist = UseATR ? atrValue * ATR_Multiplier : SLBufferPoints * _Point; double slPrice = isBuy ? price - dist : price + dist; double tpPrice = isBuy ? price + dist * RiskRewardRatio : price - dist * RiskRewardRatio;
- Der EA erstellt dann visuelle Indikatoren auf dem Chart: Pfeile knapp außerhalb der aktuellen Kerze, um Einstiegspunkte zu markieren, und horizontale Linien, die SL- und TP-Levels anzeigen.
ObjectCreate(0, name, OBJ_ARROW, 0, barTime, arrowPrice); ObjectSetInteger(0, name, OBJPROP_ARROWCODE, arrowCode); ObjectSetInteger(0, name, OBJPROP_COLOR, isBuy ? BuyArrowColor : SellArrowColor); ObjectCreate(0, "SL_Line", OBJ_HLINE, 0, 0, slPrice); ObjectCreate(0, "TP_Line", OBJ_HLINE, 0, 0, tpPrice);
- Diese visuellen Hinweise helfen dabei, Signale visuell zu überprüfen und sicherzustellen, dass die Logik des EAs mit den tatsächlichen Marktbedingungen übereinstimmt.
Nach dem Plotten der visuellen Marker gibt der EA eine Warnmeldung mit detaillierten Informationen über den Handel aus: Einstiegskurs, SL- und TP-Level.
Alert("CCI ZeroLine EMA + R:R 1:1.5 " + (isBuy ? "BUY" : "SELL") + StringFormat(" @%.5f | SL: %.5f | TP: %.5f", price, slPrice, tpPrice));
Außerdem werden die internen Tracking-Arrays aktualisiert, in denen die SL- und TP-Levels der einzelnen Handelsgeschäfte zusammen mit einem booleschen Flag gespeichert werden, das angibt, ob das Handelsergebnis aufgelöst wurde. Diese Arrays sind für die Überwachung laufender Handelsgeschäfte unerlässlich, insbesondere während der Backtests oder des Live-Handels. Sie ermöglichen es dem EA, mehrere offene Positionen gleichzeitig zu verwalten und ihre Leistung zu bewerten.
signalSL[totalSignals] = slPrice;
signalTP[totalSignals] = tpPrice;
resolved[totalSignals] = false;
Die Funktion ResolveSignals() ist für die Verfolgung aktiver Handelsgeschäfte zuständig. Sie durchläuft alle offenen Signale und prüft die Höchst- und Tiefstkurse der letzten Balken, um zu sehen, ob die SL- oder TP-Niveaus erreicht wurden. Wenn das Hoch den TP übersteigt, wird der Handel als Gewinn markiert; wenn das Tief den SL durchbricht, wird er als Verlust markiert. Dieser Prozess aktualisiert die Zähler für die Gesamtzahl der Signale und Gewinne und liefert so statistisches Feedback über die Wirksamkeit der Strategie. Diese Überwachung ist von entscheidender Bedeutung für Backtests, bei denen die genaue Aufzeichnung der Ergebnisse die Gesamtperformance-Kennzahlen wie Gewinnrate, Gewinnfaktor und Drawdown beeinflusst.
double high = iHigh(_Symbol, _Period, idx); double low = iLow(_Symbol, _Period, idx); if(high >= signalTP[i]) { winSignals++; resolved[i]=true; } if(low <= signalSL[i]) { resolved[i]=true; }
Wenn der EA entfernt oder das Chart geschlossen wird, sorgt die Funktion OnDeinit() dafür, dass alle Indikator-Handles mit IndicatorRelease() freigegeben werden. Außerdem werden alle während des Vorgangs erstellten grafischen Objekte, wie Pfeile und horizontale Linien, gelöscht. Eine ordnungsgemäße Ressourcenverwaltung verhindert Speicherlecks und sorgt dafür, dass das Chart übersichtlich bleibt, sodass nachfolgende Läufe oder andere EAs korrekt und ohne Restartefakte funktionieren.
IndicatorRelease(handleCCI_Long); IndicatorRelease(handleCCI_Short); ObjectDelete(0, "SL_Line"); ObjectDelete(0, "TP_Line");
Vollständiger Code
//+------------------------------------------------------------------+ //| CCI Zero-Line EA| //| Copyright 2025, MetaQuotes Ltd.| //| https://www.mql5.com/en/users/lynnchris| //+------------------------------------------------------------------+ #property copyright "Copyright 2025, MetaQuotes Ltd." #property link "https://www.mql5.com/en/users/lynnchris" #property version "1.0" #property strict #include <Trade\Trade.mqh> #include <Tools\Datetime.mqh> CTrade trade; //--- Session filter for USDJPY (Tokyo session) input bool UseTokyoSessionFilter = false; // enable Tokyo session filter input int TokyoStartHour = 0; // session start hour (server time) input int TokyoEndHour = 9; // session end hour (server time, exclusive) //--- SL/TP settings input bool UseATR = true; // true = ATR-based SL/TP, false = fixed points input int ATR_Period = 14; // ATR look-back input double ATR_Multiplier = 1.5; // SL/TP distance = ATR × this input double SLBufferPoints = 10.0; // fallback SL offset in pips if UseATR=false input double RiskRewardRatio = 1.5; // TP = SL × 1.5 (1:1.5 RR) //--- Indicator periods input int CCI_LongPeriod = 50; // CCI long period (zero-line cross) input int CCI_ShortPeriod = 25; // CCI short period (confirmation) input int EMAPeriod = 34; // EMA period for trend filter //--- Arrow colors input color BuyArrowColor = clrLime; input color SellArrowColor = clrRed; //--- Indicator handles int handleCCI_Long = INVALID_HANDLE; int handleCCI_Short= INVALID_HANDLE; int handleEMA = INVALID_HANDLE; int handleATR = INVALID_HANDLE; //--- Signal-tracking arrays int totalSignals = 0; int winSignals = 0; int signalBar[]; double signalSL[]; double signalTP[]; bool resolved[]; //+------------------------------------------------------------------+ //| Expert initialization | //+------------------------------------------------------------------+ int OnInit() { handleCCI_Long = iCCI(_Symbol, _Period, CCI_LongPeriod, PRICE_TYPICAL); handleCCI_Short = iCCI(_Symbol, _Period, CCI_ShortPeriod, PRICE_TYPICAL); handleEMA = iMA(_Symbol, _Period, EMAPeriod, 0, MODE_EMA, PRICE_CLOSE); if(handleCCI_Long == INVALID_HANDLE || handleCCI_Short== INVALID_HANDLE || handleEMA == INVALID_HANDLE) return(INIT_FAILED); if(UseATR) { handleATR = iATR(_Symbol, _Period, ATR_Period); if(handleATR == INVALID_HANDLE) return(INIT_FAILED); } if(RiskRewardRatio <= 0 || ATR_Multiplier <= 0 || (SLBufferPoints <= 0 && !UseATR)) return(INIT_FAILED); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { if(handleCCI_Long != INVALID_HANDLE) IndicatorRelease(handleCCI_Long); if(handleCCI_Short != INVALID_HANDLE) IndicatorRelease(handleCCI_Short); if(handleEMA != INVALID_HANDLE) IndicatorRelease(handleEMA); if(UseATR && handleATR != INVALID_HANDLE) IndicatorRelease(handleATR); ObjectDelete(0, "SL_Line"); ObjectDelete(0, "TP_Line"); } //+------------------------------------------------------------------+ //| Strategy Tester summary | //+------------------------------------------------------------------+ double OnTester() { double winRate = totalSignals > 0 ? 100.0 * winSignals / totalSignals : 0.0; PrintFormat("=== Backtest Win-Rate ===\nSignals: %d Wins: %d Win-Rate: %.2f%%", totalSignals, winSignals, winRate); return(winRate); } //+------------------------------------------------------------------+ //| Tick handler | //+------------------------------------------------------------------+ void OnTick() { // Tokyo session filter for USDJPY if(UseTokyoSessionFilter && _Symbol == "USDJPY") { datetime now = TimeTradeServer(); MqlDateTime tm; TimeToStruct(now, tm); int hr = tm.hour; if(hr < TokyoStartHour || hr >= TokyoEndHour) return; } // Only act on new bar static datetime lastTime = 0; datetime t = iTime(_Symbol, _Period, 0); if(t == lastTime) return; lastTime = t; // Copy indicator buffers double cciL[2], cciS[1], emaVal[1]; if(CopyBuffer(handleCCI_Long, 0, 0, 2, cciL) < 2) return; if(CopyBuffer(handleCCI_Short, 0, 0, 1, cciS) < 1) return; if(CopyBuffer(handleEMA, 0, 0, 1, emaVal) < 1) return; // ATR if needed double atrValue = 0.0; if(UseATR) { double atrBuf[1]; if(CopyBuffer(handleATR, 0, 0, 1, atrBuf) < 1) return; atrValue = atrBuf[0]; } double price = SymbolInfoDouble(_Symbol, SYMBOL_BID); // Detect zero-line cross bool crossUp = (cciL[1] < 0 && cciL[0] > 0); bool crossDown = (cciL[1] > 0 && cciL[0] < 0); // Confirm with short CCI & EMA trend filter if(crossUp && cciS[0] > 0 && price > emaVal[0]) RegisterSignal(true, price, atrValue); if(crossDown && cciS[0] < 0 && price < emaVal[0]) RegisterSignal(false, price, atrValue); // Resolve pending signals for SL/TP hits ResolveSignals(); } //+------------------------------------------------------------------+ //| Register new buy/sell signal | //+------------------------------------------------------------------+ void RegisterSignal(bool isBuy, double price, double atrVal) { double dist = UseATR ? atrVal * ATR_Multiplier : SLBufferPoints * _Point; double slPrice = isBuy ? price - dist : price + dist; double tpPrice = isBuy ? price + dist * RiskRewardRatio : price - dist * RiskRewardRatio; // Arrow placement just outside the candle datetime barTime = iTime(_Symbol, _Period, 0); double barHigh = iHigh(_Symbol, _Period, 0); double barLow = iLow(_Symbol, _Period, 0); double offset = 5 * _Point; // e.g. 5-pip offset double arrowPrice = isBuy ? barLow - offset : barHigh + offset; // Use numeric arrow codes directly int arrowCode = isBuy ? 233 : 234; string name = (isBuy ? "BUY_" : "SELL_") + TimeToString(TimeTradeServer(), TIME_SECONDS); ObjectCreate(0, name, OBJ_ARROW, 0, barTime, arrowPrice); ObjectSetInteger(0, name, OBJPROP_ARROWCODE, arrowCode); ObjectSetInteger(0, name, OBJPROP_COLOR, isBuy ? BuyArrowColor : SellArrowColor); ObjectSetInteger(0, name, OBJPROP_WIDTH, 2); // Draw SL/TP lines DrawLine("SL_Line", slPrice, clrRed); DrawLine("TP_Line", tpPrice, clrLime); // Alert Alert("CCI ZeroLine EMA + R:R 1:1.5 " + (isBuy ? "BUY" : "SELL") + StringFormat(" @%.5f | SL: %.5f | TP: %.5f", price, slPrice, tpPrice)); // Track for backtest metrics totalSignals++; ArrayResize(signalBar, totalSignals); ArrayResize(signalSL, totalSignals); ArrayResize(signalTP, totalSignals); ArrayResize(resolved, totalSignals); signalBar[totalSignals-1] = 0; signalSL[totalSignals-1] = slPrice; signalTP[totalSignals-1] = tpPrice; resolved[totalSignals-1] = false; } //+------------------------------------------------------------------+ //| Resolve pending signals (SL/TP checks) | //+------------------------------------------------------------------+ void ResolveSignals() { int bars = Bars(_Symbol, _Period); for(int i = 0; i < totalSignals; i++) { if(resolved[i]) continue; signalBar[i]++; int idx = signalBar[i]; if(idx >= bars) continue; double high = iHigh(_Symbol, _Period, idx); double low = iLow(_Symbol, _Period, idx); if(high >= signalTP[i]) { winSignals++; resolved[i] = true; } else if(low <= signalSL[i]) { resolved[i] = true; } } } //+------------------------------------------------------------------+ //| Draw a horizontal SL/TP line | //+------------------------------------------------------------------+ void DrawLine(string name, double price, color clr) { if(ObjectFind(0, name) >= 0) ObjectDelete(0, name); ObjectCreate(0, name, OBJ_HLINE, 0, 0, price); ObjectSetInteger(0, name, OBJPROP_COLOR, clr); ObjectSetInteger(0, name, OBJPROP_WIDTH, 2); ObjectSetString(0, name, OBJPROP_TEXT, name); } //+------------------------------------------------------------------+
Tests
Nachfolgend finden Sie die Ergebnisse der Backtests für unseren EA.
Ich habe den EA auf USDJPY getestet, und das Folgende sind die Protokolle des Journals.
- Eingaben
2025.06.30 09:34:54.140 UseTokyoSessionFilter=false 2025.06.30 09:34:54.140 TokyoStartHour=0 2025.06.30 09:34:54.140 TokyoEndHour=9 2025.06.30 09:34:54.140 UseATR=true 2025.06.30 09:34:54.140 ATR_Period=14 2025.06.30 09:34:54.140 ATR_Multiplier=1.5 2025.06.30 09:34:54.140 SLBufferPoints=10.0 2025.06.30 09:34:54.140 RiskRewardRatio=1.0 2025.06.30 09:34:54.140 CCI_LongPeriod=50 2025.06.30 09:34:54.140 CCI_ShortPeriod=25 2025.06.30 09:34:54.140 EMAPeriod=34 2025.06.30 09:34:54.140 BuyArrowColor=65280 2025.06.30 09:34:54.140 SellArrowColor=255
Die Konfiguration zeigt an, dass der Tokio-Sitzungsfilter deaktiviert ist, sodass der Handel außerhalb bestimmter Tokio-Stunden stattfinden kann; wäre er jedoch aktiviert, würde der Handel zwischen 00:00 und 09:00 Uhr Tokio-Zeit eingeschränkt. Der Expert Advisor verwendet den ATR-Indikator mit einer Periode von 14 und wendet einen Multiplikator von 1,5 an, um Stop-Loss- und Take-Profit-Levels auf der Grundlage der aktuellen Marktvolatilität anzupassen. Zum Stop-Loss wird ein Puffer von 10 Punkten hinzugefügt, um einen vorzeitigen Ausstieg zu verhindern, während das Chance-Risiko-Verhältnis auf 1:1 gesetzt wird, um potenzielle Gewinne und Verluste auszugleichen.
Der Commodity Channel Index (CCI) verwendet eine Periode von 50 für Kaufsignale und 25 für Verkaufssignale, und ein Exponential Moving Average (EMA) mit einer Periodenlänge von 34 ist in der Strategie enthalten. Visuelle Signale werden durch Pfeile dargestellt, wobei grün für Kaufsignale und blau für Verkaufssignale steht, die den jeweiligen Farbcodes entsprechen.
Die Eingaben können nach Ihren Wünschen angepasst werden. Die angegebenen Einstellungen sind die, die ich während der Tests verwendet habe und die meiner Meinung nach zu guten Ergebnissen geführt haben.
- Signale
2025.06.30 09:34:54.447 2025.01.02 15:00:00 Alert: CCI ZeroLine EMA + R:R 1:1.5 SELL @157.01800 | SL: 157.45075 | TP: 156.58525 2025.06.30 09:34:56.655 2025.01.09 06:00:00 Alert: CCI ZeroLine EMA + R:R 1:1.5 SELL @158.04800 | SL: 158.30343 | TP: 157.79257 2025.06.30 09:40:35.572 2025.01.29 07:00:00 Alert: CCI ZeroLine EMA + R:R 1:1.5 SELL @155.21100 | SL: 155.47704 | TP: 154.94496 2025.06.30 09:40:36.759 2025.01.31 07:00:00 Alert: CCI ZeroLine EMA + R:R 1:1.5 BUY @154.68200 | SL: 154.25439 | TP: 155.10961 2025.06.30 09:40:45.870 2025.02.26 06:00:00 Alert: CCI ZeroLine EMA + R:R 1:1.5 BUY @149.42600 | SL: 149.05400 | TP: 149.79800 2025.06.30 09:40:46.737 2025.02.28 03:00:00 Alert: CCI ZeroLine EMA + R:R 1:1.5 SELL @149.51900 | SL: 150.03650 | TP: 149.00150 2025.06.30 09:40:49.726 2025.03.06 01:00:00 Alert: CCI ZeroLine EMA + R:R 1:1.5 SELL @149.14200 | SL: 149.68200 | TP: 148.60200 2025.06.30 09:40:58.522 2025.03.21 16:00:00 Alert: CCI ZeroLine EMA + R:R 1:1.5 SELL @148.97200 | SL: 149.35418 | TP: 148.58982 2025.06.30 09:41:01.363 2025.04.01 00:00:00 Alert: CCI ZeroLine EMA + R:R 1:1.5 BUY @149.93500 | SL: 149.57425 | TP: 150.29575 2025.06.30 09:41:16.557 2025.04.28 13:00:00 Alert: CCI ZeroLine EMA + R:R 1:1.5 SELL @143.33500 | SL: 143.69232 | TP: 142.97768 2025.06.30 09:41:36.021 2025.06.18 14:00:00 Alert: CCI ZeroLine EMA + R:R 1:1.5 SELL @144.85500 | SL: 145.08911 | TP: 144.62089 2025.06.30 09:41:37.454 2025.06.23 19:00:00 Alert: CCI ZeroLine EMA + R:R 1:1.5 SELL @146.15000 | SL: 146.65711 | TP: 145.64289 2025.06.30 09:41:38.360 2025.06.25 23:00:00 Alert: CCI ZeroLine EMA + R:R 1:1.5 SELL @145.21700 | SL: 145.47286 | TP: 144.96114
- Gewinnrate
2025.06.30 09:41:39.206 2025.06.29 23:59:59 Signals: 13 Wins: 10 Win-Rate: 76.92%
Unten sehen Sie ein GIF, das die Testergebnisse visualisiert: Rote Pfeile nach unten zeigen Verkaufssignale an, grüne Pfeile nach oben Kaufsignale, rote Linien markieren Stop-Loss-Niveaus und grüne Linien Take-Profit-Ziele.
Schlussfolgerung
Zusammenfassend lässt sich sagen, dass dieser CCI Zero-Line EA alles, was Sie für einen klaren, regelbasierten Einstieg benötigen, direkt auf Ihrem Chart darstellt:
- Wir verwenden den CCI(50), um den langfristigen Trend zu erkennen, und den CCI(25), um zu bestätigen, dass das Momentum bereit ist, also handeln wir nur, wenn beide Linien steigen.
- Der 34-Perioden-EMA verhindert, dass wir zufälligen Ausschlägen hinterherlaufen. Wenn der Kurs auf der rechten Seite dieser Linie schließt, wissen wir, dass der Trend echt ist.
- Mit ATR-basierten Stopps passt sich unser Stopp-Loss-Abstand der Volatilität an: eng in ruhigen Märkten, weiter, wenn es losgeht. Unser Gewinnziel bleibt im Verhältnis und sorgt für ein stabiles Renditeverhältnis.
Diese Kombination bedeutet, dass die Pfeile nur dann erscheinen, wenn Preis, Dynamik und Volatilität in dieselbe Richtung weisen. Sie erhalten weniger Fehlsignale, ein reaktionsschnelles Risikomanagement und vollständige Transparenz darüber, warum die einzelnen Ein- und Ausstiegslevels gewählt wurden. Passen Sie die CCI-Perioden, die EMA-Länge oder den ATR-Multiplikator an jedes Symbol oder jeden Zeitrahmen an und handeln Sie mit dem Vertrauen in ein Tool, das auf einer soliden, vielschichtigen Analyse basiert. Dieser EA ist nur für Bildungszwecke gedacht. Setzen Sie es nicht auf dem Live-Markt ein, ohne gründliche Backtests mit historischen Daten oder Tests auf Demokonten durchzuführen. Vergewissern Sie sich, dass Sie mit den Ergebnissen zufrieden sind, bevor Sie einen Live-Handel in Betracht ziehen.
Übersetzt aus dem Englischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/en/articles/18551
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.





- 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.