Complete .mq5 source code

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.

How the indicator signal works

Calculation and signal conditions

The indicator plots iCCI values and a simple moving average of CCI, then alerts when the previous completed bar crosses upward through the configured lower threshold. This is an oscillator condition, not proof that selling is exhausted. The example does not apply a 200 EMA trend filter, open positions or validate an entry strategy.

Interpretation and limitations

A second alert reports a completed-bar crossing downward through the upper threshold. The plotted signal line is for inspection; it does not drive the threshold alerts or manage exits. CCI is unbounded and can remain beyond a threshold. Define and test separate position, stop and exit rules before using alerts in an execution workflow.

MQL5 Custom Indicator Code

//+------------------------------------------------------------------+
//| CCI MQL5 Indicator — Overbought/Oversold Alert System           |
//| Platform : MetaTrader 5 (MQL5)                                   |
//| Version  : 1.0 — Pineify.app example                            |
//| Educational indicator; no verified performance results.          |
//+------------------------------------------------------------------+
#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);

   PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,InpCCIPeriod);
   PlotIndexSetInteger(1,PLOT_DRAW_BEGIN,InpCCIPeriod+InpSignalMAPeriod);
   IndicatorSetInteger(INDICATOR_LEVELCOLOR,InpLevelColor);

   //--- 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)
     {
      ArrayInitialize(Signal_Buffer,EMPTY_VALUE);
      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)
   static datetime lastAlertBar=0;
   datetime completedBar=iTime(_Symbol,_Period,1);
   if(InpEnableAlerts && rates_total >= 3 && completedBar!=lastAlertBar)
     {
      lastAlertBar=completedBar;
      //--- 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, save it in the MQL5/Indicators folder, and compile with F7.

Change the settings or add your own signal filter

Describe the inputs, buffers, alerts, visual style, or confirmation filter you want. Pineify generates editable MQL5 source code that you can inspect and compile in MetaEditor.

Frequently Asked Questions

Related MQL5 pages

Technical references

Risk and testing note

These educational examples require independent compilation and testing. A compile pass or an illustrative setting does not establish execution correctness or profitability. Any reported performance must be supported by reproducible data and test settings; past results do not predict future returns. Trading involves significant risk of loss. This content is not financial advice.