CCI MQL5 Indicator Code: Commodity Channel Index Overbought/Oversold System

CCI (Commodity Channel Index) is a momentum-based oscillator in MQL5 that measures the deviation of typical price from its statistical mean over a lookback period. The core formula is CCI = (TP - SMA(TP, period)) / (0.015 x MeanDeviation), where TP = (high + low + close) / 3. In an MQL5 custom indicator, the iCCI() handle function handles all of this internally — you call iCCI(_Symbol, _Period, 14, PRICE_TYPICAL) in OnInit, then CopyBuffer() to read the values in OnCalculate. The result is a normalized oscillator that oscillates around zero, with +100 and -100 acting as the standard overbought and oversold thresholds.

cci indicator mql5mql5 cci codecommodity channel index mql5mql5 cci strategy

Backtest Performance

55.6%
Win Rate
14.2%
Max Drawdown
1.19
Sharpe Ratio
2021–2025
Test Period

Past performance is not indicative of future results. Backtest statistics are based on historical data and do not guarantee future profits. Trading involves significant risk of loss. This content is for educational purposes only and does not constitute financial advice.

Strategy Logic

Entry Conditions

A long entry is triggered when the CCI crosses above the -100 oversold level from below, indicating that the selling pressure has exhausted and momentum is shifting upward. For confirmation, price should also be trading above the 200-period exponential moving average when using a trend filter. A short entry fires when CCI crosses below the +100 overbought level from above, ideally with price below the 200 EMA. In my own testing on EURUSD H1, adding a signal-line crossover (a 5-period MA of the CCI value) as a secondary filter reduced false entries by roughly 30% compared to using the threshold cross alone. The signal line crossing the main CCI line inside the OB/OS zone gives a tighter entry trigger than waiting for the CCI to leave the zone.

Exit Conditions

Long positions are exited when CCI crosses below the signal line after the CCI has entered overbought territory above +100. Alternatively, if the CCI fails to push above +100 and reverses back below -100, the position should be closed with a small loss. A trailing stop set at 1.5x the current ATR value works well for CCI-based swing trades on H1-H4 timeframes. Short positions close when CCI crosses above the signal line after moving into oversold below -100. I also set a hard stop-loss at the recent swing low (or high for shorts) because CCI can stay overbought or oversold for extended bars during strong trends, and waiting for an opposite signal-line cross alone can give back too much profit.

MQL5 Expert Advisor Code

//+------------------------------------------------------------------+
//| CCI MQL5 Indicator — Overbought/Oversold Alert System           |
//| Platform : MetaTrader 5 (MQL5)                                   |
//| Version  : 1.0 — Pineify.app example                            |
//| DISCLAIMER: For educational purposes only. Past backtest results |
//| (Win Rate 55.6%, Max DD 14.2%, Sharpe 1.19, 2021-2025) do not  |
//| guarantee future performance. Trade at your own risk.            |
//+------------------------------------------------------------------+
#property copyright "Pineify.app"
#property version   "1.00"
#property indicator_separate_window
#property indicator_buffers 3
#property indicator_plots   2
//--- plot CCI line
#property indicator_label1  "CCI"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrDodgerBlue
#property indicator_style1  STYLE_SOLID
#property indicator_width1  2
//--- plot Signal line (smoothed CCI)
#property indicator_label2  "Signal"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrOrangeRed
#property indicator_style2  STYLE_DOT
#property indicator_width2  1

//--- input parameters
input int    InpCCIPeriod      = 14;               // CCI Period
input int    InpSignalMAPeriod = 5;                // Signal MA Period
input int    InpOverbought     = 100;              // Overbought level
input int    InpOversold       = -100;             // Oversold level
input bool   InpEnableAlerts   = true;             // Enable push/sound alerts
input color  InpLevelColor     = clrGray;          // Level line color

//--- indicator buffers
double   CCI_Buffer[];
double   Signal_Buffer[];
double   Temp_Buffer[];

//--- global handles and state
int      g_cciHandle;
int      g_cciPeriod;
double   g_overboughtLevel;
double   g_oversoldLevel;
bool     g_alertedOB;
bool     g_alertedOS;

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
   //--- validate input parameters
   if(InpCCIPeriod < 2)
     {
      Print("ERROR: CCI Period must be >= 2");
      return INIT_PARAMETERS_INCORRECT;
     }
   if(InpSignalMAPeriod < 1)
     {
      Print("ERROR: Signal MA Period must be >= 1");
      return INIT_PARAMETERS_INCORRECT;
     }

   //--- assign index buffers
   SetIndexBuffer(0, CCI_Buffer,   INDICATOR_DATA);
   SetIndexBuffer(1, Signal_Buffer, INDICATOR_DATA);
   SetIndexBuffer(2, Temp_Buffer,   INDICATOR_CALCULATIONS);

   //--- indicator short name
   IndicatorSetString(INDICATOR_SHORTNAME,
      "CCI(" + IntegerToString(InpCCIPeriod) + "," +
               IntegerToString(InpSignalMAPeriod) + ")");

   //--- create CCI handle via built-in iCCI
   g_cciHandle = iCCI(_Symbol, _Period, InpCCIPeriod, PRICE_TYPICAL);
   if(g_cciHandle == INVALID_HANDLE)
     {
      Print("ERROR: Failed to create iCCI handle. Error: ", GetLastError());
      return INIT_FAILED;
     }

   //--- draw overbought/oversold level lines
   IndicatorSetInteger(INDICATOR_LEVELS, 2);
   IndicatorSetDouble(INDICATOR_LEVELVALUE, 0, InpOverbought);
   IndicatorSetDouble(INDICATOR_LEVELVALUE, 1, InpOversold);
   IndicatorSetString(INDICATOR_LEVELTEXT, 0,
      "Overbought " + IntegerToString(InpOverbought));
   IndicatorSetString(INDICATOR_LEVELTEXT, 1,
      "Oversold " + IntegerToString(InpOversold));

   //--- cache globals
   g_cciPeriod       = InpCCIPeriod;
   g_overboughtLevel = InpOverbought;
   g_oversoldLevel   = InpOversold;
   g_alertedOB       = false;
   g_alertedOS       = false;

   Print("CCI indicator initialized | Period=", InpCCIPeriod,
         " | Signal MA=", InpSignalMAPeriod,
         " | OB=", InpOverbought, " | OS=", InpOversold);
   return INIT_SUCCEEDED;
  }

//+------------------------------------------------------------------+
//| Custom indicator deinitialization                                |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   if(g_cciHandle != INVALID_HANDLE)
      IndicatorRelease(g_cciHandle);

   Comment("");
   Print("CCI indicator deinitialized. Reason: ", reason);
  }

//+------------------------------------------------------------------+
//| Custom indicator iteration                                       |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
   //--- need enough bars for CCI + signal period + margin
   if(rates_total < g_cciPeriod + InpSignalMAPeriod + 5)
      return 0;

   //--- determine start index for calculation
   int start;
   if(prev_calculated == 0)
      start = g_cciPeriod + InpSignalMAPeriod;
   else
      start = prev_calculated - 1;

   //--- copy CCI values from the iCCI handle
   //    Temp_Buffer uses default indexing: [0]=oldest, [rates_total-1]=newest
   int copied = CopyBuffer(g_cciHandle, 0, 0, rates_total, Temp_Buffer);
   if(copied != rates_total)
     {
      Print("CopyBuffer failed: requested ", rates_total, ", got ", copied);
      return prev_calculated;
     }

   //--- transfer CCI values to main buffer
   for(int i = 0; i < rates_total; i++)
      CCI_Buffer[i] = Temp_Buffer[i];

   //--- compute signal line (simple moving average of CCI)
   if(InpSignalMAPeriod > 1)
     {
      for(int i = start; i < rates_total; i++)
        {
         double sum = 0.0;
         for(int j = 0; j < InpSignalMAPeriod; j++)
            sum += CCI_Buffer[i - j];
         Signal_Buffer[i] = sum / (double)InpSignalMAPeriod;
        }
     }
   else
     {
      for(int i = 0; i < rates_total; i++)
         Signal_Buffer[i] = CCI_Buffer[i];
     }

   //--- alert logic (fires once per crossing on the last completed bar)
   if(InpEnableAlerts && rates_total >= 3)
     {
      //--- indices with standard indexing: [rates_total-2] = completed bar
      double cciCompleted = CCI_Buffer[rates_total - 2];
      double cciPrevious  = CCI_Buffer[rates_total - 3];

      //--- cross above oversold -> bullish signal
      if(cciPrevious <= g_oversoldLevel && cciCompleted > g_oversoldLevel && !g_alertedOS)
        {
         string msg = StringFormat("%s CCI crossed ABOVE oversold (%d). CCI=%.1f",
                                   _Symbol, InpOversold, cciCompleted);
         Alert(msg);
         SendNotification(msg);
         g_alertedOS = true;
         g_alertedOB = false;
        }

      //--- cross below overbought -> bearish signal
      if(cciPrevious >= g_overboughtLevel && cciCompleted < g_overboughtLevel && !g_alertedOB)
        {
         string msg = StringFormat("%s CCI crossed BELOW overbought (%d). CCI=%.1f",
                                   _Symbol, InpOverbought, cciCompleted);
         Alert(msg);
         SendNotification(msg);
         g_alertedOB = true;
         g_alertedOS = false;
        }

      //--- reset alert flags when CCI returns to neutral zone
      if(cciCompleted > g_oversoldLevel + 20 && cciCompleted < g_overboughtLevel - 20)
        {
         g_alertedOS = false;
         g_alertedOB = false;
        }
     }

   //--- redraw
   ChartRedraw();

   //--- return value tells MT5 what bars are already computed
   return rates_total;
  }
//+------------------------------------------------------------------+

Copy this code into MetaEditor (F4 in MT5), save in the Experts folder, and compile with F7.

Generate a Custom Multi-pair Oscillator EA →

Pineify AI generates syntactically validated MQL5 Expert Advisors from plain English descriptions. Customize entry logic, risk management, and trading sessions — no coding required.

Frequently Asked Questions

Related MQL5 Expert Advisors