Sunday, August 10, 2025

1. Introduction

The Commodity Channel Index Multi-Timeframe (CCI MTF) Indicator for MetaTrader 5 provides a comprehensive and compact way to read CCI momentum across all 21 standard timeframes (from M1 to MN1) on a single chart. Instead of switching charts and guessing whether shorter- or longer-term momentum supports your idea, this indicator calculates CCI on each timeframe independently and summarizes directional momentum with per-timeframe arrows plus a majority-based summary signal.

Each arrow corresponds to the directional assessment of the CCI for a particular timeframe:

  • 🟢 Green Arrow = Bullish momentum (CCI moving upward)
  • 🔴 Red Arrow = Bearish momentum (CCI moving downward)
  • Gold/Neutral Arrow = Neutral / weak/no dominant momentum

🚀 This tool is ideal for multi-timeframe traders, scalpers, and swing traders who want fast visual confirmation across the full timeframe (21 timeframes) spectrum before pulling the trigger.

2. About This Indicator

The Commodity Channel Index Multi-Timeframe Indicator for MT5 (CCI_MTF) utilizes a template and displays a panel on the chart.

Its function, operation, and usage are identical to the On Balance Volume Multi-Timeframe Indicator for MT5 (OBV_MTF) as detailed in the previous article. For a comprehensive guide on creating a multi-timeframe indicator, you can refer to the following resources: On Balance Volume Multi-Timeframe Indicator for MT5

🔁 Note: All multi-timeframe indicators developed by Forex Home Expert use this exact signal logic programs structure.

CCI_MTF

3. Key Features

✅ Full 21-timeframe CCI scanning (M1 → MN1) in one indicator.
✅ Per-timeframe arrow overlays for quick visual scanning.
✅ Majority-based summary signal that reduces noise (summary arrow appears when up > down + 1 or down > up + 1).
✅ Customizable parameters: CCI calculation period (BarCalc), arrow colors, fonts.
✅ On-screen alerts + optional email and push notifications.
✅ Lightweight and optimized for MT5 — uses indicator handles (iCCI) and CopyBuffer for efficient buffer reading.
✅ Non-repainting behavior — uses closed bar data via CopyBuffer() for stable historical signals.
✅ Graphical Interface Panel for Timeframe Switching (via `OnChartEvent`)

4. How It Works (Behind the Scenes)

Commodity Channel Index (CCI) in a Nutshell

CCI is a momentum oscillator that measures the difference between a typical price and its moving average, normalized by mean absolute deviation. Common interpretation:

  • CCI > +100 → strong bullish momentum / overbought region
  • CCI < -100 → strong bearish momentum / oversold region

5. Multi-Timeframe Signal Logic

The indicator operates in two main steps:

  1. Create a handle for the CCI on each timeframe using iCCI() and keep those handles in an array (one per timeframe).
  2. For each bar the indicator examines, read the most recent closed CCI value(s) using CopyBuffer() and determine whether the CCI is rising or falling on that timeframe. Count the timeframes that are rising (up) vs falling (down). When the count for one direction exceeds the other by more than one (up > down + 1 or down > up + 1) a summary arrow is shown.

6. Relevant Code Snippets

Technical Overview: How the Indicator Works


//--
enum YN
  {
   No,
   Yes
  };
//--
enum fonts
  {
   Verdana,
   Bodoni_MT_Black
  };
//--
//-- Input Parameter
//---
input group   "====  Indicator Period Calculation  ===="
input int                BarCalc = 14;                // Input Indicator Period
input group   "====  Indicator Color and Font   ===="
input color              ArrowUp = clrMediumSeaGreen; // Arrow Up Color
input color              ArrowDn = clrDeepPink;       // Arrow Down Color
input color              NTArrow = clrGold;           // Arrow No Signal
input fonts              f_model = Bodoni_MT_Black;   // Select Font Model
input group   "====  Input parameters for alerts  ===="
input YN                  alerts = Yes;               // Display Alerts Pop-up on Chart (Yes) or (No)
input YN           UseEmailAlert = No;                // Email Alert (Yes) or (No)
input YN           UseSendnotify = No;                // Send Notification (Yes) or (No)
//---

Timeframe Management

The indicator prepares and manages handles for all standard MT5 timeframes:


   virtual void      CCI_MTF_Config(void)
     {
      //--
      ENUM_TIMEFRAMES TFIx[]= {PERIOD_M1,PERIOD_M2,PERIOD_M3,PERIOD_M4,PERIOD_M5,PERIOD_M6,PERIOD_M10,PERIOD_M12,PERIOD_M15,
                               PERIOD_M20,PERIOD_M30,PERIOD_H1,PERIOD_H2,PERIOD_H3,PERIOD_H4,PERIOD_H6,PERIOD_H8,PERIOD_H12,
                               PERIOD_D1,PERIOD_W1,PERIOD_MN1};
      tfxar=ArraySize(TFIx);
      ArrayResize(TFId,tfxar,tfxar);
      ArrayResize(Arwcolor,tfxar,tfxar);
      ArrayCopy(TFId,TFIx,0,0,WHOLE_ARRAY);
      //--
      string TFxc[]= {"M1","M2","M3","M4","M5","M6","M10","M12","M15","M20","M30","H1",                   "H2","H3","H4","H6","H8","H12","D1","W1","MN1"}; // 21 Timeframes
      //--
      ArrayResize(hCCI,tfxar,tfxar);
      ArrayResize(TFSc,tfxar,tfxar);
      ArrayCopy(TFSc,TFxc,0,0,WHOLE_ARRAY);
      //--
      if(BarCalc<1)
        {
         CCI_period=14;
         PrintFormat("Incorrect value for input variable BarCalc = %d. Indicator will use value %d for calculations.",
                  BarCalc,CCI_period);
        }
      else
         CCI_period=BarCalc;
      //--
      ttlbars=BarCalc*3+3;
      //--
      for(int x=0; x<tfxar; x++)
        hCCI[x]=iCCI(Symbol(),TFId[x],CCI_period,PRICE_TYPICAL); // Handle of iCCI Indicator on each timeframe
      //--
      DeletedCCIObject();
      CCIMovementCalculation(25);
      PositionCore();
      //--
      if(display)
         DrawCCIObject();
      //---
     }
   //---

Signal Extraction Logic

Each timeframe is scanned using:


   //---
   //---
   int               CCIDirectionScan(const ENUM_TIMEFRAMES stf,int shift) // Scan OBV Direction
     {
      //--
      int ret=0;
      int rise=1,
          down=-1;
      //--
      int br=shift+2;
      double res=0.0;
      UpdatePrice(stf);
      //--
      double CCI[];
      ArrayResize(CCI,br,br);
      ArraySetAsSeries(CCI,true);
      //--
      int xx=TFIndexArray(stf);
      CopyBuffer(hCCI[xx],0,0,br,CCI);
      //--
      if(CCI[shift]>CCI[shift+1]) ret=rise;
      if(CCI[shift]<CCI[shift+1]) ret=down;
      //--
      return(ret);
      //---
     } //-end CCIDirectionScan()
   //---

Consensus Calculation Logic.


   //---
   void              CCIMovementCalculation(int barCnt) // Scan the direction of iCCI on each timeframe
     {
      //--
      ArrayResize(PowerMove,barCnt,barCnt);
      ArraySetAsSeries(PowerMove,true);
      //--
      for(int i=barCnt-1; i>=0; i--)
        {
         up=0;
         dw=0;
         //--
         for(int x=0; x<tfxar; x++)
           {
            Arwcolor[x]=NTArrow;
            PowerMove[i]=0.0;
            int PPM=CCIDirectionScan(TFId[x],0);
            if(PPM>0)
              {
               up++;
               Arwcolor[x]=ArrowUp;
              }
            if(PPM<0)
              {
               dw++;
               Arwcolor[x]=ArrowDn;
              }
            if(x==tfxar-1)
              {
               if(up>dw+1)
                 {
                  PowerMove[i]=1.0;
                  TColor=ArrowUp;
                  curAlert=1;
                 }
               if(dw>up+1)
                 {
                  PowerMove[i]=-1.0;
                  TColor=ArrowDn;
                  curAlert=-1;
                 }
              }
           }
        }
      //--
      return;
      //---
     } //-end CCIMovementCalculation()
   //---
/*
This logic ensures that only dominant directional consensus (not slight differences) triggers a clear summary signal.
*/

Indicator signal calculation starts from OnCalculate(...)


//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
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[])
  {
//---
   ResetLastError();
   //--
   int limit;
   limit=rates_total-prev_calculated;
   if(prev_calculated>0)
      limit++;
   if(limit>mi.ttlbars)
      limit=mi.ttlbars;
   if(limit-3<=1)
      limit=3;
   //--
   mi.CCIMovementCalculation(limit);
   //--
   if(alerts==Yes||UseEmailAlert==Yes||UseSendnotify==Yes)
     {
       mi.curmin=mi.ThisTime(mi.min);
       if(mi.curmin!=mi.prvmin && mi.curAlert==1 && mi.curAlert!=mi.prvAlert)
         {
           string AlertTxt="The strength of the CCI movement appears to be Rise.";
           mi.Do_Alerts(AlertTxt);
           mi.prvAlert=mi.curAlert;
           mi.prvmin=mi.curmin;
         }
       if(mi.curmin!=mi.prvmin && mi.curAlert==-1 && mi.curAlert!=mi.prvAlert)
         {
           string AlertTxt="The strength of the CCI movement appears to be Down.";
           mi.Do_Alerts(AlertTxt);
           mi.prvAlert=mi.curAlert;
           mi.prvmin=mi.curmin;
         }
     }
   //--
   if(mi.display)
     mi.DrawCCIObject();
   //---
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//---------//

Function DrawCCIObject() – Drawing multi-timeframe CCI results.


   //---
   void              DrawCCIObject(void)
     {
      //--
CreateButtonTemplate(CI,ObjName+"Template",397,66,STYLE_SOLID,9,BORDER_RAISED,clrMistyRose,clrLavenderBlush,clrWhite,bcor,corx,cory,true);
      for(int x=0; x<tfhalf; x++)
        {
         CreateArrowLabel(CI,ObjName+"_win_arrow_"+string(x),CharToString((uchar)windchar),"Wingdings",windsize,Arwcolor[x],bcor,
                             txttf+horizL1+(x*scaleX)+offsetX+x,vertiL1,true,"Arrow_"+TFSc[x]);
         CreateButtonClick(CI,TFSc[x],27,15,font_mode,fontSize,BORDER_FLAT,TFSc[x],clrBurlyWood,clrSilver,clrBlue,
                              bcor,txttf+horizL2+(x*scaleA)+offsetX,vertiL2,true,
                              "Change Timeframe to : "+TFSc[x]);
        }
      for(int x=tfhalf, x2=0; x<tfxar; x++, x2++)
        {
         CreateArrowLabel(CI,ObjName+"_win_arrow_"+string(x),CharToString((uchar)windchar),"Wingdings",windsize,Arwcolor[x],bcor,
                             txttf+horizL3+(x2*scaleX)+offsetX+x2,vertiL3,true,"Arrow_"+TFSc[x]);
         CreateButtonClick(CI,TFSc[x],27,15,font_mode,fontSize,BORDER_FLAT,TFSc[x],clrBurlyWood,clrSilver,clrBlue,
                              bcor,txttf+horizL4+(x2*scaleA)+offsetX,vertiL4,true,
                              "Change Timeframe to : "+TFSc[x]);
         //--
         if(x==20)
           {
            int arrowChar=TColor==ArrowUp ? 200 : TColor==ArrowDn ? 202 : windchar;
            CreateArrowLabel(CI,ObjName+"_tfx_arrow_"+string(x+1),"Move",font_mode,fontSize,clrBlue,bcor,
                                519+horizL5,vertiL5,true,"Move");
            CreateArrowLabel(CI,ObjName+"_win_arrow_"+string(x+1),CharToString((uchar)arrowChar),"Wingdings",15,TColor,bcor,
                                522+horizL6,vertiL6,true,"Arrow Indicator Movement");
           }
        }
      DisplayButtonClick("X");
      CreateButtonTemplate(CI,ObjName+"TemplateName1",17,15,STYLE_SOLID,1,BORDER_FLAT,clrMistyRose,clrLavenderBlush,clrWhite,bcor,TNamex,TNamey1,true);
      CreateButtonTemplate(CI,ObjName+"TemplateName2",17,15,STYLE_SOLID,1,BORDER_FLAT,clrMistyRose,clrLavenderBlush,clrWhite,bcor,TNamex,TNamey2,true);
      CreateButtonTemplate(CI,ObjName+"TemplateName3",17,15,STYLE_SOLID,1,BORDER_FLAT,clrMistyRose,clrLavenderBlush,clrWhite,bcor,TNamex,TNamey3,true);
      CreateArrowLabel(CI,ObjName+"_name1",hName1,font_mode,fontSize+1,clrBlue,bcor,TNamexn,TNamey1n,true,hName1);
      CreateArrowLabel(CI,ObjName+"_name2",hName2,font_mode,fontSize+1,clrBlue,bcor,TNamexn,TNamey2n,true,hName2);
      CreateArrowLabel(CI,ObjName+"_name3",hName3,font_mode,fontSize+1,clrBlue,bcor,TNamexn,TNamey3n,true,hName3);
      //--
      if(corpos==postop)
        {
          DisplayButtonClick("cstar");
          DisplayButtonClick("arbot");
        }
      if(corpos==posbot)
        {
          DisplayButtonClick("cstar");
          DisplayButtonClick("artop");
        }
      //--
      return;
      //---
     } //-end DrawCCIObject()
   //---
 //- This function is responsible for creating labels on the chart for each timeframe, with colors according to Overbought (green), Oversold (red), or No Signal (gold) Neutral conditions.

Dynamic Timeframe Panel OnChartEvent() Fuction

The indicator creates a clickable panel on the chart containing labels for all 21 timeframes. When a label (e.g., "H4") is clicked, the internal reference timeframe for analysis is updated, and the timeframe is changed to H4.

This makes it easy for the user to interact with the indicator dynamically.


//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//---
//--- handling CHARTEVENT_CLICK event ("Clicking the chart")
   ResetLastError();
//--
   if(id==CHARTEVENT_OBJECT_CLICK)
     {
      //--- if "X" button is click
      if(sparam=="X")
        {
         mi.DeletedCCIObject();
         //--- unpress the button
         ObjectSetInteger(mi.CI,"X",OBJPROP_STATE,false);
         ObjectSetInteger(mi.CI,"X",OBJPROP_ZORDER,0);
         //--
         mi.display=false;
         ObjectDelete(mi.CI,"X");
         mi.DisplayPanelButton();
        }
      //--- if "cstar" button is click
      if(sparam==mi.cstar)
        {
         mi.DeletedCCIObject();
         mi.DisplayPanelButton();
         //--- unpress the button
         ObjectSetInteger(mi.CI,mi.cstar,OBJPROP_STATE,false);
         ObjectSetInteger(mi.CI,mi.cstar,OBJPROP_ZORDER,0);
         if(!mi.display)
            mi.display=true;
         if(mi.corpos==mi.posbot) ObjectDelete(mi.CI,mi.arbot);
         if(mi.corpos==mi.postop) ObjectDelete(mi.CI,mi.artop);
         mi.DrawCCIObject();
         //--
         ChartRedraw(mi.CI);
        }
      //--- if "artop" button is click
      if(sparam==mi.artop)
        {
         mi.DeletedCCIObject();
         mi.DisplayPanelButton();
         //--- unpress the button
         ObjectSetInteger(mi.CI,mi.artop,OBJPROP_STATE,false);
         ObjectSetInteger(mi.CI,mi.artop,OBJPROP_ZORDER,0);
         if(!mi.display)
            mi.display=true;
         ObjectDelete(mi.CI,mi.artop);
         mi.PanelPosChange(mi.postop);
         //--
         ObjectDelete(mi.CI,"X");
         mi.DisplayButtonClick("arbot");
         mi.DrawCCIObject();
         //--
         ChartRedraw(mi.CI);
        }
      //--- if "arbot" button is click
      if(sparam==mi.arbot)
        {
         mi.DeletedCCIObject();
         mi.DisplayPanelButton();
         //--- unpress the button
         ObjectSetInteger(mi.CI,mi.arbot,OBJPROP_STATE,false);
         ObjectSetInteger(mi.CI,mi.arbot,OBJPROP_ZORDER,0);
         if(!mi.display)
            mi.display=true;
         ObjectDelete(mi.CI,mi.arbot);
         mi.PanelPosChange(mi.posbot);
         //--
         ObjectDelete(mi.CI,"X");
         mi.DisplayButtonClick("artop");
         mi.DrawCCIObject();
         //--
         ChartRedraw(mi.CI);
        }
      //--- if TF button is click
      //--
      if(sparam==mi.TFSc[0])
        {
         mi.ChangeChartSymbol(mi.TFSc[0],mi.TFId[0]);
        }
      //--
      if(sparam==mi.TFSc[1])
        {
         mi.ChangeChartSymbol(mi.TFSc[1],mi.TFId[1]);
        }
      //--
      if(sparam==mi.TFSc[2])
        {
         mi.ChangeChartSymbol(mi.TFSc[2],mi.TFId[2]);
        }
      //--
      if(sparam==mi.TFSc[3])
        {
         mi.ChangeChartSymbol(mi.TFSc[3],mi.TFId[3]);
        }
      //--
      if(sparam==mi.TFSc[4])
        {
         mi.ChangeChartSymbol(mi.TFSc[4],mi.TFId[4]);
        }
      //--
      if(sparam==mi.TFSc[5])
        {
         mi.ChangeChartSymbol(mi.TFSc[5],mi.TFId[5]);
        }
      //--
      if(sparam==mi.TFSc[6])
        {
         mi.ChangeChartSymbol(mi.TFSc[6],mi.TFId[6]);
        }
      //--
      if(sparam==mi.TFSc[7])
        {
         mi.ChangeChartSymbol(mi.TFSc[7],mi.TFId[7]);
        }
      //--
      if(sparam==mi.TFSc[8])
        {
         mi.ChangeChartSymbol(mi.TFSc[8],mi.TFId[8]);
        }
      //--
      if(sparam==mi.TFSc[9])
        {
         mi.ChangeChartSymbol(mi.TFSc[9],mi.TFId[9]);
        }
      //--
      if(sparam==mi.TFSc[10])
        {
         mi.ChangeChartSymbol(mi.TFSc[10],mi.TFId[10]);
        }
      //--
      if(sparam==mi.TFSc[11])
        {
         mi.ChangeChartSymbol(mi.TFSc[11],mi.TFId[11]);
        }
      //--
      if(sparam==mi.TFSc[12])
        {
         mi.ChangeChartSymbol(mi.TFSc[12],mi.TFId[12]);
        }
      //--
      if(sparam==mi.TFSc[13])
        {
         mi.ChangeChartSymbol(mi.TFSc[13],mi.TFId[13]);
        }
      //--
      if(sparam==mi.TFSc[14])
        {
         mi.ChangeChartSymbol(mi.TFSc[14],mi.TFId[14]);
        }
      //--
      if(sparam==mi.TFSc[15])
        {
         mi.ChangeChartSymbol(mi.TFSc[15],mi.TFId[15]);
        }
      //--
      if(sparam==mi.TFSc[16])
        {
         mi.ChangeChartSymbol(mi.TFSc[16],mi.TFId[16]);
        }
      //--
      if(sparam==mi.TFSc[17])
        {
         mi.ChangeChartSymbol(mi.TFSc[17],mi.TFId[17]);
        }
      //--
      if(sparam==mi.TFSc[18])
        {
         mi.ChangeChartSymbol(mi.TFSc[18],mi.TFId[18]);
        }
      //--
      if(sparam==mi.TFSc[19])
        {
         mi.ChangeChartSymbol(mi.TFSc[19],mi.TFId[19]);
        }
      //--
      if(sparam==mi.TFSc[20])
        {
         mi.ChangeChartSymbol(mi.TFSc[20],mi.TFId[20]);
        }
      //--
     }
//---
  } //-end OnChartEvent()
//---------//

7. How to Use It

  1. Attach the Indicator — Add CCI_MTF to any chart (e.g., EURUSD H1).
  2. Configure Settings — Open the indicator inputs to set BarCalc, choose arrow colors, font, and turn alerts on/off.
  3. Indicator wiil Interpret Signals — Look for alignment across timeframes and the summary arrow.
  4. Confirm with Price Action — Validate with swing structure, support/resistance, or another momentum/confirmation indicator (moving averages, RSI, etc.).
  5. Trend-following entry: — enter in direction of majority summary arrow on a pullback in your execution timeframe.
  6. Counter-trend: — trade only with defined risk and confluence (e.g., bullish divergence on CCI + summary turning bullish).

8. FAQ

Q1: What does the indicator measure?
A1: It measures the direction of the CCI on each timeframe — whether the recent closed CCI values are moving upward or downward — then aggregates those direction votes into a majority-based summary.

Q2: How is the summary arrow calculated?
A2: The indicator counts how many timeframes show “up” vs “down.” If up > down + 1, a summary up arrow is shown. If down > up + 1, a summary down arrow is shown. This reduces false signals from single-timeframe noise.

Q3: Does this indicator repaint?
A3: No. It uses CopyBuffer() to read closed-bar values from iCCI handles, which prevents repainting and ensures stable historical signals.

Q4: Which CCI period does it use?
A4: The default BarCalc (period) is configurable. Many traders use 14 as a starting point, but you can change it to suit your strategy.

Q5: Can I get alerts when the summary changes?
A5: Yes. The indicator includes on-screen alerts and optional email and push notifications (controlled by alerts, UseEmailAlert, and UseSendnotify inputs).

Q6: Is it resource heavy?
A6: No. The indicator uses MT5 indicator handles (iCCI) and CopyBuffer() calls efficiently. It is designed to be lightweight even while scanning 21 timeframes.

Q7: Can I change which timeframes are scanned?
A7: The indicator creates a clickable panel on the chart containing labels for all 21 timeframes (M1 → MN1). When a label (e.g., "H8") is clicked, the internal reference timeframe for analysis is updated, and the timeframe is changed to H8.

Q8: How to combine it with other indicators?
A8: Good compliment indicators are moving averages (for trend direction), RSI (for momentum confirmation), or price action signals for entry timing.

Q9: Suitable for which instruments?
A9: Works on forex, indices, commodities, stocks, and crypto — any instrument supported by MT5.

9. Conclusion

The Commodity Channel Index Multi-Timeframe (CCI_MTF) Indicator for MT5 gives traders an organized, low-noise way to read momentum across the entire range of MT5 timeframes. By using a majority-vote method and reliable closed-bar buffer reads, it delivers practical signals for both scalpers and swing traders. Combine it with price-action validation or a complementary indicator to refine entries and exits.

We hope that this article and the CCI_MTF or Commodity Channel Index Multi-Timeframe Indicator for MT5 program will be useful for traders in learning and generating new ideas for trading, who can ultimately make money from home by trading forex.

Thanks for reading this article.

Please download the CCI_MTF indicator: Commodity Channel Index Multi-Timeframe Indicator for MT5

If you are subscribed to my YouTube Channel, and would like to receive the source program of this article, please send a request via the Contact Us form page, and I will send it to your email, source code: Commodity Channel Index Multi-Timeframe Indicator for MT5

Don't forget to stop by and subscribe to Forex Home Experts YouTube Channel:

YouTube Channel: @ForexHomeExperts

YouTube Playlist: @ForexHomeExperts YouTube Playlist

Tagged: , , , , , , ,

0 comments:

Post a Comment

Featured Post

How to create a simple Multi-Currency Expert Advisor using MQL5 with Zigzag and RSI Indicators Signal

Introduction The Expert Advisor discussed in this article is a multi-currency trading robot that uses the Zigzag and RSI indicators. It fol...