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.