English
preview
Entwicklung des Price Action Analysis Toolkit (Teil 30): Commodity Channel Index (CCI), Zero Line EA

Entwicklung des Price Action Analysis Toolkit (Teil 30): Commodity Channel Index (CCI), Zero Line EA

MetaTrader 5Handelssysteme |
16 0
Christian Benjamin
Christian Benjamin

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.


Überblick über die Indikatoren

Commodity Channel Index (CCI)

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,015

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





   
Chart Projector
Analytical Comment
Analytics Master
Analytics Forecaster 
Volatility Navigator
Der Mean Reversion Signal Reaper
Signal Pulse 
Metrics Board 
External Flow
VWAP
Heikin Ashi   FibVWAP  
RSI DIVERGENCE
Parabolic Stop and Reverse (PSAR) 
Quarters Drawer Script
Intrusion Detector
TrendLoom Tool  Quarters Board 
ZigZag Analyzer  Correlation Pathfinder  Market Structure Flip Detector Tool
Correlation Dashboard   Währungsstärkemesser 
PAQ-Analyse-Tool 
Dual EMA Fractal Breaker
Pin-Bar, Engulfing und RSI-Divergenz
Liquidity Sweep Werkzeug zum Ausbrechen aus dem Eröffnungsbereich Ausleger und Crash Interceptor CCI Zero-Line EA

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

Beigefügte Dateien |
CCI.mq5 (18.28 KB)
Die Übertragung der Trading-Signale in einem universalen Expert Advisor. Die Übertragung der Trading-Signale in einem universalen Expert Advisor.
In diesem Artikel wurden die verschiedenen Möglichkeiten beschrieben, um die Trading-Signale von einem Signalmodul des universalen EAs zum Steuermodul der Positionen und Orders zu übertragen. Es wurden die seriellen und parallelen Interfaces betrachtet.
MQL5-Assistenz-Techniken, die Sie kennen sollten (Teil 69): Verwendung der Muster von SAR und RVI MQL5-Assistenz-Techniken, die Sie kennen sollten (Teil 69): Verwendung der Muster von SAR und RVI
Der Parabolic-SAR (SAR) und der Relative Vigour Index (RVI) sind ein weiteres Paar von Indikatoren, die in Verbindung mit einem MQL5 Expert Advisor verwendet werden können. Auch dieses Indikatorpaar ist, wie die anderen, die wir in der Vergangenheit behandelt haben, komplementär, da der SAR den Trend definiert, während der RVI das Momentum überprüft. Wie üblich verwenden wir den MQL5-Assistenten, um das Potenzial dieser Indikatorenkombination zu ermitteln und zu testen.
Eine alternative Log-datei mit der Verwendung der HTML und CSS Eine alternative Log-datei mit der Verwendung der HTML und CSS
In diesem Artikel werden wir eine sehr einfache, aber leistungsfähige Bibliothek zur Erstellung der HTML-Dateien schreiben, dabei lernen wir auch, wie man eine ihre Darstellung einstellen kann (nach seinem Geschmack) und sehen wir, wie man es leicht in seinem Expert Advisor oder Skript hinzufügen oder verwenden kann.
Automatisieren von Handelsstrategien in MQL5 (Teil 20): Multi-Symbol-Strategie mit CCI und AO Automatisieren von Handelsstrategien in MQL5 (Teil 20): Multi-Symbol-Strategie mit CCI und AO
In diesem Artikel erstellen wir eine Multi-Symbol-Handelsstrategie, die CCI- und AO-Indikatoren verwendet, um Trendumkehrungen zu erkennen. Wir behandeln seinen Entwurf, die MQL5-Implementierung und den Backtest-Prozess. Der Artikel schließt mit Tipps zur Leistungssteigerung.