//+------------------------------------------------------------------+
//|                                                   CCI simple.mq4 |
//+------------------------------------------------------------------+
#property copyright "www,forex-station.com"
#property link      "www,forex-station.com"

#property indicator_separate_window
#property indicator_buffers 6
#property indicator_color1  LimeGreen
#property indicator_width1  1
#property indicator_color2  Red
#property indicator_width2  1
#property indicator_color3  DimGray
#property indicator_style3  STYLE_DASH
#property indicator_color4  DimGray
#property indicator_style4  STYLE_DOT
#property indicator_color5  DimGray
#property indicator_style5  STYLE_DASH
#property indicator_color6  LightCoral
#property indicator_width6  2
#property indicator_level1  200
#property indicator_level2  -200
#property indicator_level3  0
#property indicator_levelcolor MediumOrchid

//
//
//
//
//

extern string TimeFrame                = "Current time frame";
extern string separator1               = "T3 CCI Settings";
extern int    CCIPeriod                = 50;
extern int    CCIPrice                 = PRICE_TYPICAL;
extern int    T3Period                 = 5;
extern double T3Hot                    = 0.70;
extern bool   T3Original               = false;
extern int    VolatilityBandPeriod     = 34;
extern int    VolatilityBandMAMode     = MODE_LWMA;
extern double VolatilityBandMultiplier = 2.0;
extern double OverSold                 = -200;
extern double OverBought               =  200;

extern string separator2               = "Indicator Settings";
extern bool   drawDivergences          = true;
extern bool   drawIndicatorTrendLines  = true;
extern bool   drawPriceTrendLines      = true;
extern string drawLinesIdentificator   = "ccidiverge";
extern bool   displayAlert             = true;
extern string NameSound                = "alert1.wav";
extern bool   Interpolate              = true;






double bullishDivergence[];
double bearishDivergence[];
double bandUp[];
double bandMiddle[];
double bandDown[];
double CCI[];
double prices[];
double emas[][6];

double alpha;
double c1;
double c2;
double c3;
double c4;
double pipMultiplier = 1;

static datetime lastAlertTime;
string indicatorName;
string labelNames;

//
//
//
//
//

int    timeFrame;
string indicatorFileName;
bool   returnBars;
bool   calculateValue;

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
//
//
//
//
//

int init()
{
      IndicatorBuffers(7);
      SetIndexBuffer(0,bullishDivergence);SetIndexStyle(0, DRAW_ARROW);SetIndexArrow(0, 233);
      SetIndexBuffer(1,bearishDivergence);SetIndexStyle(1, DRAW_ARROW);SetIndexArrow(1, 234);
      SetIndexBuffer(2,bandUp);
      SetIndexBuffer(3,bandMiddle);
      SetIndexBuffer(4,bandDown);
      SetIndexBuffer(5,CCI);
      SetIndexBuffer(6,prices);
      
      SetLevelValue(0,OverBought);
      SetLevelValue(1,OverSold);
      
      IndicatorDigits(Digits + 2);
         
       
       double a = T3Hot;
             c1 = -a*a*a;
             c2 =  3*(a*a+a*a*a);
             c3 = -3*(2*a*a+a+a*a*a);
             c4 = 1+3*a+a*a*a+3*a*a;

      T3Period = MathMax(1,T3Period);
      if (T3Original)
           alpha = 2.0/(1.0 + T3Period);
      else alpha = 2.0/(2.0 + (T3Period-1.0)/2.0);
      
      //
      //
      //
      //
      //
      
      labelNames    = "T3 CCI_DivergenceLine "+drawLinesIdentificator+":";
      indicatorName = "T3 CCI Divergence";
      IndicatorShortName(indicatorName);
   
      //
      //
      //
      //
      //
         
      indicatorFileName = WindowExpertName();
      calculateValue    = (TimeFrame=="calculateValue"); if (calculateValue) return(0);
      returnBars        = (TimeFrame=="returnBars");     if (returnBars)     return(0);
      timeFrame         = stringToTimeFrame(TimeFrame);
   
   return(0);
}

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
//
//
//
//
//

int deinit()
{
   int length=StringLen(labelNames);
   for(int i=ObjectsTotal()-1; i>=0; i--)
   {
      string name = ObjectName(i);
      if(StringSubstr(name,0,length) == labelNames)  ObjectDelete(name);   
   }
   return(0);
}

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
//
//
//
//
//

int start()
{
   int counted_bars=IndicatorCounted();
   int i,n,k,limit;

   if(counted_bars<0) return(-1);
   if(counted_bars>0) counted_bars--;
         limit = MathMin(Bars-counted_bars,Bars-1);
         if (returnBars) { bullishDivergence[0] = limit+1; return(0); }
         if (Digits==3 || Digits==5) 
               pipMultiplier = 10;
         else  pipMultiplier = 1; 
   
   //
   //
   //
   //
   //

   if (calculateValue || timeFrame==Period())
   {
      if (ArrayRange(emas,0) != Bars) ArrayResize(emas,Bars);
      for(i=limit; i>=0; i--)
      {
         prices[i]  = iMA(NULL,0,1,0,MODE_SMA,CCIPrice,i);
         double avg = 0; for(k=0; k<CCIPeriod; k++) avg +=         prices[i+k];      avg /= CCIPeriod;
         double dev = 0; for(k=0; k<CCIPeriod; k++) dev += MathAbs(prices[i+k]-avg); dev /= CCIPeriod;
            if (dev!=0)
                  CCI[i] = iT3((prices[i]-avg)/(0.015*dev),i);
            else  CCI[i] = iT3(0,                          i); 
      }
      for(i=limit; i>=0; i--)
      {
         
         double deviation     = iStdDevOnArray(CCI,0,VolatilityBandPeriod,0,VolatilityBandMAMode,i);
         double average       = iMAOnArray(CCI,0,VolatilityBandPeriod,0,VolatilityBandMAMode,i);
                bandUp[i]     = average+VolatilityBandMultiplier*deviation;
                bandDown[i]   = average-VolatilityBandMultiplier*deviation;
                bandMiddle[i] = average; 
         
         if (drawDivergences)
         {
            CatchBullishDivergence(i);
            CatchBearishDivergence(i);
         }           
      }
      return(0);
   }

   //
   //
   //
   //
   //

   limit = MathMax(limit,MathMin(Bars-1,iCustom(NULL,timeFrame,indicatorFileName,"returnBars",0,0)*timeFrame/Period()));
   for(i=limit; i>=0; i--)
   {
      int y = iBarShift(NULL,timeFrame,Time[i]);
         bandUp[i]            = iCustom(NULL,timeFrame,indicatorFileName,"calculateValue","",CCIPeriod,CCIPrice,T3Period,T3Hot,T3Original,VolatilityBandPeriod,VolatilityBandMAMode,VolatilityBandMultiplier,OverBought,OverSold,"",drawDivergences,drawIndicatorTrendLines,drawPriceTrendLines,drawLinesIdentificator,displayAlert,NameSound,2,y);
         bandMiddle[i]        = iCustom(NULL,timeFrame,indicatorFileName,"calculateValue","",CCIPeriod,CCIPrice,T3Period,T3Hot,T3Original,VolatilityBandPeriod,VolatilityBandMAMode,VolatilityBandMultiplier,OverBought,OverSold,"",drawDivergences,drawIndicatorTrendLines,drawPriceTrendLines,drawLinesIdentificator,displayAlert,NameSound,3,y);
         bandDown[i]          = iCustom(NULL,timeFrame,indicatorFileName,"calculateValue","",CCIPeriod,CCIPrice,T3Period,T3Hot,T3Original,VolatilityBandPeriod,VolatilityBandMAMode,VolatilityBandMultiplier,OverBought,OverSold,"",drawDivergences,drawIndicatorTrendLines,drawPriceTrendLines,drawLinesIdentificator,displayAlert,NameSound,4,y);
         CCI[i]               = iCustom(NULL,timeFrame,indicatorFileName,"calculateValue","",CCIPeriod,CCIPrice,T3Period,T3Hot,T3Original,VolatilityBandPeriod,VolatilityBandMAMode,VolatilityBandMultiplier,OverBought,OverSold,"",drawDivergences,drawIndicatorTrendLines,drawPriceTrendLines,drawLinesIdentificator,displayAlert,NameSound,5,y);
         bullishDivergence[i] = EMPTY_VALUE;
         bearishDivergence[i] = EMPTY_VALUE;

         int firstBar = iBarShift(NULL,0,iTime(NULL,timeFrame,y));
            if (i==firstBar)
            {
               bullishDivergence[i] = iCustom(NULL,timeFrame,indicatorFileName,"calculateValue","",CCIPeriod,CCIPrice,T3Period,T3Hot,T3Original,VolatilityBandPeriod,VolatilityBandMAMode,VolatilityBandMultiplier,OverBought,OverSold,"",drawDivergences,drawIndicatorTrendLines,drawPriceTrendLines,drawLinesIdentificator,displayAlert,NameSound,0,y);
               bearishDivergence[i] = iCustom(NULL,timeFrame,indicatorFileName,"calculateValue","",CCIPeriod,CCIPrice,T3Period,T3Hot,T3Original,VolatilityBandPeriod,VolatilityBandMAMode,VolatilityBandMultiplier,OverBought,OverSold,"",drawDivergences,drawIndicatorTrendLines,drawPriceTrendLines,drawLinesIdentificator,displayAlert,NameSound,1,y);
            }

         //
         //
         //
         //
         //
      
         if (!Interpolate || y==iBarShift(NULL,timeFrame,Time[i-1])) continue;

         //
         //
         //
         //
         //

         datetime time = iTime(NULL,timeFrame,y);
            for(n = 1; i+n < Bars && Time[i+n] >= time; n++) continue;	
            for(k = 1; k < n; k++)
            {
               bandUp[i+k]     = bandUp[i]     + (bandUp[i+n]    - bandUp[i]    )*k/n;
               bandMiddle[i+k] = bandMiddle[i] + (bandMiddle[i+n]- bandMiddle[i])*k/n;
               bandDown[i+k]   = bandDown[i]   + (bandDown[i+n]  - bandDown[i]  )*k/n;
               CCI[i+k]        = CCI[i]        + (CCI[i+n]       - CCI[i]       )*k/n;
            }
   }

   //
   //
   //
   //
   //
   
   return(0);
}

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
//
//
//
//
//

double iT3(double price,int shift)
{
   int i = Bars-shift-1;
   if (i < 1)
      {
         emas[i][0] = price;
         emas[i][1] = price;
         emas[i][2] = price;
         emas[i][3] = price;
         emas[i][4] = price;
         emas[i][5] = price;
      }
   else
      {
         emas[i][0] = emas[i-1][0]+alpha*(price     -emas[i-1][0]);
         emas[i][1] = emas[i-1][1]+alpha*(emas[i][0]-emas[i-1][1]);
         emas[i][2] = emas[i-1][2]+alpha*(emas[i][1]-emas[i-1][2]);
         emas[i][3] = emas[i-1][3]+alpha*(emas[i][2]-emas[i-1][3]);
         emas[i][4] = emas[i-1][4]+alpha*(emas[i][3]-emas[i-1][4]);
         emas[i][5] = emas[i-1][5]+alpha*(emas[i][4]-emas[i-1][5]);
      }
   return(c1*emas[i][5] + c2*emas[i][4] + c3*emas[i][3] + c4*emas[i][2]);
}

//
//
//
//
//

void CatchBullishDivergence(int shift)
{
   shift++;
         bullishDivergence[shift] = EMPTY_VALUE;
            ObjectDelete(labelNames+"l"+DoubleToStr(Time[shift],0));
            ObjectDelete(labelNames+"l"+"os" + DoubleToStr(Time[shift],0));            
   if(!IsIndicatorLow(shift)) return;  

   //
   //
   //
   //
   //
      
   int currentLow = shift;
   int lastLow = GetIndicatorLastLow(shift+1);

   //
   //
   //
   //
   //
    
   if(CCI[currentLow] > CCI[lastLow] && Low[currentLow] < Low[lastLow])
   {
      bullishDivergence[currentLow] = CCI[currentLow] - Point*pipMultiplier;
      if(drawPriceTrendLines == true)
                 DrawPriceTrendLine("l",Time[currentLow],Time[lastLow],Low[currentLow],Low[lastLow],Green,STYLE_SOLID);
      if(drawIndicatorTrendLines == true)
                 DrawIndicatorTrendLine("l",Time[currentLow],Time[lastLow],CCI[currentLow],CCI[lastLow],Green,STYLE_SOLID);
      if(displayAlert == true)
                 DisplayAlert("Classical bullish divergence on: ",currentLow);  
                        
   }
     
   //
   //
   //
   //
   //
        
   if(CCI[currentLow] < CCI[lastLow] && Low[currentLow] > Low[lastLow])
   {
      bullishDivergence[currentLow] = CCI[currentLow] - Point*pipMultiplier;
      if(drawPriceTrendLines == true)
                 DrawPriceTrendLine("l",Time[currentLow],Time[lastLow],Low[currentLow],Low[lastLow], Green, STYLE_DOT);
      if(drawIndicatorTrendLines == true)                            
                 DrawIndicatorTrendLine("l",Time[currentLow],Time[lastLow],CCI[currentLow], CCI[lastLow], Green, STYLE_DOT);
      if(displayAlert == true)
                 DisplayAlert("Reverse bullish divergence on: ",currentLow); 
   }
}


//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
//
//
//
//
//

void CatchBearishDivergence(int shift)
{
   shift++; 
         bearishDivergence[shift] = EMPTY_VALUE;
            ObjectDelete(labelNames+"h"+DoubleToStr(Time[shift],0));
            ObjectDelete(labelNames+"h"+"os" + DoubleToStr(Time[shift],0));            
   if(IsIndicatorPeak(shift) == false) return;
   int currentPeak = shift;
   int lastPeak = GetIndicatorLastPeak(shift+1);

   //
   //
   //
   //
   //
      
   if(CCI[currentPeak] < CCI[lastPeak] && High[currentPeak]>High[lastPeak])
   {
       bearishDivergence[currentPeak] = CCI[currentPeak] + Point*pipMultiplier;
       if(drawPriceTrendLines == true)
               DrawPriceTrendLine("h",Time[currentPeak],Time[lastPeak],High[currentPeak],High[lastPeak],Red,STYLE_SOLID);
       if(drawIndicatorTrendLines == true)
               DrawIndicatorTrendLine("h",Time[currentPeak],Time[lastPeak],CCI[currentPeak],CCI[lastPeak],Red,STYLE_SOLID);
       if(displayAlert == true)
               DisplayAlert("Classical bearish divergence on: ",currentPeak); 
                        
   }
   
   //
   //
   //
   //
   //

   if(CCI[currentPeak] > CCI[lastPeak] && High[currentPeak] < High[lastPeak])
   {
      bearishDivergence[currentPeak] = CCI[currentPeak] + Point*pipMultiplier;
      if (drawPriceTrendLines == true)
               DrawPriceTrendLine("h",Time[currentPeak],Time[lastPeak],High[currentPeak],High[lastPeak], Red, STYLE_DOT);
      if (drawIndicatorTrendLines == true)
               DrawIndicatorTrendLine("h",Time[currentPeak],Time[lastPeak],CCI[currentPeak],CCI[lastPeak], Red, STYLE_DOT);
      if(displayAlert == true)
               DisplayAlert("Reverse bearish divergence on: ",currentPeak);
                         
   }   
}
//+------------------------------------------------------------------+
bool IsIndicatorPeak(int shift)
  {
   if(CCI[shift] >= CCI[shift+1] && CCI[shift] > CCI[shift+2] && 
      CCI[shift] > CCI[shift-1])
       return(true);
   else 
       return(false);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool IsIndicatorLow(int shift)
  {
   if(CCI[shift] <= CCI[shift+1] && CCI[shift] < CCI[shift+2] && 
      CCI[shift] < CCI[shift-1])
       return(true);
   else 
       return(false);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int GetIndicatorLastPeak(int shift)
  {
           for(int i = shift+5; i < Bars; i++)
             {
               if(CCI[i] >= CCI[i+1] && CCI[i] > CCI[i+2] &&
                  CCI[i] >= CCI[i-1] && CCI[i] > CCI[i-2])
                   return(i);
             }
   return(-1);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int GetIndicatorLastLow(int shift)
  {
            for(int i = shift+5; i < Bars; i++)
             {
                if(CCI[i] <= CCI[i+1] && CCI[i] < CCI[i+2] &&
                   CCI[i] <= CCI[i-1] && CCI[i] < CCI[i-2])
                    return(i);
              }
     
    return(-1);
  }

void DisplayAlert(string message, int shift)
  {
   if(shift <= 2 && Time[shift] != lastAlertTime)
     {
       lastAlertTime = Time[shift];
       PlaySound(NameSound);
       Alert(message, Symbol(), " , ", Period(), " minutes chart");
       
       
     }
  }


void DrawPriceTrendLine(string first,datetime t1, datetime t2, double p1, double p2, color lineColor, double style)
{
   string label = labelNames+first+"os"+DoubleToStr(t1,0);
   ObjectDelete(label);
      ObjectCreate(label, OBJ_TREND, 0, t1, p1, t2, p2, 0, 0);
         ObjectSet(label, OBJPROP_RAY, 0);
         ObjectSet(label, OBJPROP_COLOR, lineColor);
         ObjectSet(label, OBJPROP_STYLE, style);
}
void DrawIndicatorTrendLine(string first,datetime t1, datetime t2, double p1, double p2, color lineColor, double style)
{
   int indicatorWindow = WindowFind(indicatorName);
   if (indicatorWindow < 0) return;
   
   string label = labelNames+first+DoubleToStr(t1,0);
   ObjectDelete(label);
      ObjectCreate(label, OBJ_TREND, indicatorWindow, t1, p1, t2, p2, 0, 0);
         ObjectSet(label, OBJPROP_RAY, 0);
         ObjectSet(label, OBJPROP_COLOR, lineColor);
         ObjectSet(label, OBJPROP_STYLE, style);
}

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
//
//
//
//
//

string sTfTable[] = {"M1","M5","M15","M30","H1","H4","D1","W1","MN"};
int    iTfTable[] = {1,5,15,30,60,240,1440,10080,43200};

//
//
//
//
//

int stringToTimeFrame(string tfs)
{
   tfs = StringUpperCase(tfs);
   for (int i=ArraySize(iTfTable)-1; i>=0; i--)
         if (tfs==sTfTable[i] || tfs==""+iTfTable[i]) return(MathMax(iTfTable[i],Period()));
                                                      return(Period());
}

//
//
//
//
//

string StringUpperCase(string str)
{
   string   s = str;
   for (int length=StringLen(str)-1; length>=0; length--)
   {
      int tchar = StringGetChar(s, length);
         if((tchar > 96 && tchar < 123) || (tchar > 223 && tchar < 256))
                     s = StringSetChar(s, length, tchar - 32);
         else if(tchar > -33 && tchar < 0)
                     s = StringSetChar(s, length, tchar + 224);
   }
   return(s);
}