//+------------------------------------------------------------------+
//|                                                          CCI.mq4 |
//|                      Copyright © 2004, MetaQuotes Software Corp. |
//|                                       http://www.metaquotes.net/ |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2004, MetaQuotes Software Corp."
#property link      "http://www.metaquotes.net/"
//----
#property indicator_separate_window
#property indicator_buffers 2
#property indicator_color1 Red
#property indicator_color2 DeepSkyBlue

//---- input parameters
//---- indicator parameters
extern string  m = "--Moving Average Types--";
extern string  m0 = " 0 = SMA";
extern string  m1 = " 1 = EMA";
extern string  m2 = " 2 = SMMA";
extern string  m3 = " 3 = LWMA";
extern string  m4 = " 4 = LSMA";
extern int     MA_Type=0;
extern string  p = "--Applied Price Types--";
extern string  p0 = " 0 = close";
extern string  p1 = " 1 = open";
extern string  p2 = " 2 = high";
extern string  p3 = " 3 = low";
extern string  p4 = " 4 = median(high+low)/2";
extern string  p5 = " 5 = typical(high+low+close)/3";
extern string  p6 = " 6 = weighted(high+low+close+close)/4";
extern int     MA_AppliedPrice = 0;
extern string  c = "--CCI Inputs--";
extern int     CCIPeriod = 14;
extern int     SmoothingMA_Type = 2;
extern int     SmoothingMA_Period = 3;
extern bool   alertsOn         = false;            // Turn alert on or off
extern bool   alertsOnCurrent  = true;             // Alerts on current (still opened) bar
extern bool   alertsMessage    = true;             // Alerts should show pop-up message
extern bool   alertsPushNotif  = false;            // Alerts should send push notification
extern bool   alertsSound      = false;            // Alerts should play a sound
extern bool   alertsEmail      = false;            // Alerts should send email
extern bool   arrowsVisible      = false;
extern string arrowsIdentifier   = "CCI_ma Arrows1";
extern int    arrowsSize         = 1;
extern double arrowsDisplacement = 1.0;
extern color  arrowsUpColor      = LimeGreen;
extern color  arrowsDnColor      = Orange;
extern int    arrowsUpCode       = 241;
extern int    arrowsDownCode     = 242;

//---- buffers
double CCIBuffer[];
double CCIBufferSmoothed[];
double MovBuffer[];
double cross[];

int    MA_Mode;
string strMAType;
int SmoothingMode;

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int init()
{
   string short_name;
   IndicatorBuffers(4);
   SetIndexStyle(0, DRAW_LINE);
   SetIndexBuffer(0, CCIBuffer);
   SetIndexStyle(1, DRAW_LINE);
   SetIndexBuffer(1, CCIBufferSmoothed);
   SetIndexBuffer(2, MovBuffer);
   SetIndexBuffer(3, cross);

   if(CCIPeriod <= 0)
       CCIPeriod = 14;
       
   SmoothingMode = SmoothingMA_Type;
   if (SmoothingMA_Type > 3) SmoothingMode = 2;

   SetIndexDrawBegin(0, CCIPeriod);
  
//---- name for DataWindow and indicator subwindow label
   switch (MA_Type)
   {
      case 1: strMAType="EMA"; MA_Mode=MODE_EMA; break;
      case 2: strMAType="SMMA"; MA_Mode=MODE_SMMA; break;
      case 3: strMAType="LWMA"; MA_Mode=MODE_LWMA; break;
      case 4: strMAType="LSMA"; break;
      default: strMAType="SMA"; MA_Mode=MODE_SMA; break;
   }
   short_name = "CCI-" +strMAType+ " (" +CCIPeriod + ") ";
   IndicatorShortName(short_name);
   SetIndexLabel(0, short_name);
//----
   return(0);
  }

int deinit()
{
   string lookFor       = arrowsIdentifier+":";
   int    lookForLength = StringLen(lookFor);
   for (int i=ObjectsTotal()-1; i>=0; i--)
   {
      string objectName = ObjectName(i);
         if (StringSubstr(objectName,0,lookForLength) == lookFor) ObjectDelete(objectName);
   }
   return(0); 
}
  
//+------------------------------------------------------------------------+
//| LSMA - Least Squares Moving Average function calculation               |
//| LSMA_In_Color Indicator plots the end of the linear regression line    |
//+------------------------------------------------------------------------+

//+------------------------------------------------------------------+
//| LSMA with PriceMode                                              |
//| PrMode  0=close, 1=open, 2=high, 3=low, 4=median(high+low)/2,    |
//| 5=typical(high+low+close)/3, 6=weighted(high+low+close+close)/4  |
//+------------------------------------------------------------------+

double LSMA(int TimeFrame, int Rperiod, int prMode, int shift)
{
   int i, myShift;
   double sum, pr;
   int length;
   double lengthvar;
   double tmp;
   double wt;

   length = Rperiod;
 
   sum = 0;
   for(i = length; i >= 1  ; i--)
   {
     lengthvar = length + 1;
     lengthvar /= 3;
     tmp = 0;
     myShift = length - i + shift;
     switch (prMode)
     {
     case 0: pr = iClose(NULL,TimeFrame,myShift);break;
     case 1: pr = iOpen(NULL,TimeFrame,myShift);break;
     case 2: pr = iHigh(NULL,TimeFrame,myShift);break;
     case 3: pr = iLow(NULL,TimeFrame,myShift);break;
     case 4: pr = (iHigh(NULL,TimeFrame,myShift) + iLow(NULL,TimeFrame,myShift))/2;break;
     case 5: pr = (iHigh(NULL,TimeFrame,myShift) + iLow(NULL,TimeFrame,myShift) + iClose(NULL,TimeFrame,myShift))/3;break;
     case 6: pr = (iHigh(NULL,TimeFrame,myShift) + iLow(NULL,TimeFrame,myShift) + iClose(NULL,TimeFrame,myShift) + iClose(NULL,TimeFrame,myShift))/4;break;
     }
     tmp = ( i - lengthvar)*pr;
     sum+=tmp;
    }
    wt = sum*6/(length*(length+1));
    wt = MathFloor(wt/Point)*Point;
    
    return(wt);
}

//+------------------------------------------------------------------+
//| Commodity Channel Index                                          |
//+------------------------------------------------------------------+
int start()
{
   int counted_bars=IndicatorCounted();
      if(counted_bars < 0) return(-1);
      if(counted_bars > 0) counted_bars--;
           int limit = MathMin(Bars-counted_bars,Bars-1);

   //
   //
   //
   //
   //
   
   double mul = 0.015 / CCIPeriod;
   for(int i = limit; i >=0; i--)
   {
     if (MA_Type == 4)
            MovBuffer[i]=LSMA(0,CCIPeriod, MA_AppliedPrice,i);
     else   MovBuffer[i]=iMA(NULL,0,CCIPeriod,0,MA_Mode,MA_AppliedPrice,i);
     double sum = 0.0;
     for(int k = 0; k<CCIPeriod; k++)
         sum         += MathAbs(((High[i+k]+Low[i+k]+Close[i+k])/3) - MovBuffer[i]);
         double dev   = sum*mul;
         double rel   = ((High[i]+Low[i]+ Close[i])/3.0) - MovBuffer[i];
         if(dev == 0.0) 
                  CCIBuffer[i] = 0.0;
          else    CCIBuffer[i] = rel / dev;
   }       
   for(i = limit; i >=0; i--)
   {
      CCIBufferSmoothed[i] = iMAOnArray(CCIBuffer, 0, SmoothingMA_Period, 0, SmoothingMode, i);
      cross[i] = cross[i+1];
         if (CCIBuffer[i]>CCIBufferSmoothed[i]) cross[i] =  1;
         if (CCIBuffer[i]<CCIBufferSmoothed[i]) cross[i] = -1;
         manageArrow(i);
   }
   manageAlerts();         
   return(0);
}

//-------------------------------------------------------------------
//                                                                  
//-------------------------------------------------------------------
//
//
//
//
//

void manageAlerts()
{
   if (alertsOn)
   {
      int whichBar = 1; if (alertsOnCurrent) whichBar = 0;
      if (cross[whichBar] != cross[whichBar+1])
      {
         if (cross[whichBar+1]==-1) doAlert(whichBar,"CCI crossed ma up");
         if (cross[whichBar+1]== 1) doAlert(whichBar,"CCI crossed ma down");
      }
   }
}

//
//
//
//
//

void doAlert(int forBar, string doWhat)
{
   static string   previousAlert="nothing";
   static datetime previousTime;
   string message;
   
   if (previousAlert != doWhat || previousTime != Time[forBar]) {
       previousAlert  = doWhat;
       previousTime   = Time[forBar];

       //
       //
       //
       //
       //

       message = Symbol()+" at "+TimeToStr(TimeLocal(),TIME_SECONDS)+" "+doWhat;
          if (alertsMessage)   Alert(message);
          if (alertsEmail)     SendMail(StringConcatenate(Symbol()," CCI ma smoothed "),message);
          if (alertsPushNotif) SendNotification(StringConcatenate(Symbol()," CCI ma smoothed "+message));
          if (alertsSound)     PlaySound("alert2.wav");
   }
}

//------------------------------------------------------------------
//
//------------------------------------------------------------------
//
//
//
//
//

void manageArrow(int i)
{
   if (arrowsVisible)
   {
      ObjectDelete(arrowsIdentifier+":"+Time[i]);
      if (cross[i]!=cross[i+1])
      {
         if (cross[i] == 1) drawArrow(i,arrowsUpColor,arrowsUpCode  ,false);
         if (cross[i] ==-1) drawArrow(i,arrowsDnColor,arrowsDownCode,true);
      }
   }
}               
void drawArrow(int i,color theColor,int theCode,bool up)
{
   string name = arrowsIdentifier+":"+Time[i];
   double gap  = iATR(NULL,0,20,i);   
   
      ObjectCreate(name,OBJ_ARROW,0,Time[i],0);
         ObjectSet(name,OBJPROP_ARROWCODE,theCode);
         ObjectSet(name,OBJPROP_COLOR,theColor);
         ObjectSet(name,OBJPROP_WIDTH,arrowsSize);
         if (up)
               ObjectSet(name,OBJPROP_PRICE1,High[i] + arrowsDisplacement * gap);
         else  ObjectSet(name,OBJPROP_PRICE1,Low[i]  - arrowsDisplacement * gap);
}