//------------------------------------------------------------------
#property copyright "www.forex-tsd.com"
#property link      "www.forex-tsd.com"
//------------------------------------------------------------------
#property indicator_separate_window
#property indicator_buffers    7
#property indicator_color1     clrDodgerBlue
#property indicator_color2     clrSandyBrown
#property indicator_color3     clrSilver
#property indicator_color4     clrDarkSlateGray
#property indicator_color5     clrLimeGreen
#property indicator_color6     clrRed
#property indicator_color7     clrDimGray
#property indicator_width1     2
#property indicator_width2     2
#property indicator_width3     2
#property indicator_width4     2
#property indicator_style5     STYLE_DOT
#property indicator_style6     STYLE_DOT
#property indicator_style7     STYLE_DOT
#property strict

//
//
//
//
//

#import "dynamicZone.dll"
   double dzBuyP(double& sourceArray[],double probabiltyValue, int lookBack, int bars, int i, double precision);
   double dzSellP(double& sourceArray[],double probabiltyValue, int lookBack, int bars, int i, double precision);
#import

//
//
//
//
//

extern ENUM_TIMEFRAMES    TimeFrame              = PERIOD_CURRENT;// Time frame to use
extern int                CciPeriod              = 25;            // CCI period
extern ENUM_APPLIED_PRICE CciPrice               = PRICE_TYPICAL; // Price to use
extern int                CciPriceSmoothing      = 1;             // Cci price pre-smoothing
extern int                CciMaPeriod            = 7;             // Average period
extern ENUM_MA_METHOD     CciMaMethod            = MODE_LWMA;     // Average method 
extern int                DzLookBackBars         = 35;            // dynamic zones lookback
extern double             DzStartBuyProbability  = 0.90;          // dynamic upper zone
extern double             DzStartSellProbability = 0.90;          // dynamic lower zone
extern bool               alertsOn               = false;         // Turn alerts on?
extern bool               alertsOnCurrent        = false;         // Alerts on current (still opened) bar?
extern bool               alertsMessage          = false;         // Alerts should display pop-up message?
extern bool               alertsEmail            = false;         // Alerts should send an email?
extern bool               alertsSound            = false;         // Alerts should play an alert sound?
extern string             soundFile              = "alert2.wav";  // Sound file to use when playing alerts sounds
extern bool               ShowHistogram          = true;          // Display histogram?
extern bool               ShowCciMa              = true;          // Display CCI average?
extern bool               Interpolate            = true;          // Interpolate in multi time frame mode?

//
//
//
//
//

double cci[];
double cciU[];
double cciD[];
double cciMa[];
double buffer1[];
double buffer2[];
double buffer3[];
double prices[];
double trend[];
string indicatorFileName;
bool   returnBars;

//------------------------------------------------------------------
//
//------------------------------------------------------------------
//
//
//
//
//

int init()
{
   IndicatorBuffers(9);
   SetIndexBuffer(0,cciU);  
   SetIndexBuffer(1,cciD);  
   SetIndexBuffer(2,cci);  
   SetIndexBuffer(3,cciMa); 
   SetIndexBuffer(4,buffer1);
   SetIndexBuffer(5,buffer2);
   SetIndexBuffer(6,buffer3);
   SetIndexBuffer(7,prices);
   SetIndexBuffer(8,trend); 
      
      if (ShowHistogram)
            { SetIndexStyle(0,DRAW_HISTOGRAM); SetIndexStyle(1,DRAW_HISTOGRAM); }
      else  { SetIndexStyle(0,DRAW_NONE);      SetIndexStyle(1,DRAW_NONE);      }
      if (ShowCciMa)
               { SetIndexStyle(3,DRAW_LINE); }
         else  { SetIndexStyle(3,DRAW_NONE); }
   
            indicatorFileName = WindowExpertName();
            returnBars        = TimeFrame==-99;
            TimeFrame         = MathMax(TimeFrame,_Period);
   
   IndicatorShortName(timeFrameToString(TimeFrame)+" Cci Adaptive Smoother(" +(string)CciPeriod+")");
   return(0);
}

//------------------------------------------------------------------
//
//------------------------------------------------------------------
//
//
//
//

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);
            if (returnBars) { cciU[0] = MathMin(limit+1,Bars-1); return(0); }
            if (TimeFrame != Period())
            {
               for(int i=limit; i>=0; i--)
               {
                  int y = iBarShift(NULL,TimeFrame,Time[i]);
                  cci[i]     = iCustom(NULL,TimeFrame,indicatorFileName,PERIOD_CURRENT,CciPeriod,CciPrice,CciPriceSmoothing,CciMaPeriod,CciMaMethod,DzLookBackBars,DzStartBuyProbability,DzStartSellProbability,alertsOn,alertsOnCurrent,alertsMessage,alertsEmail,alertsSound,soundFile,2,y);
                  cciMa[i]   = iCustom(NULL,TimeFrame,indicatorFileName,PERIOD_CURRENT,CciPeriod,CciPrice,CciPriceSmoothing,CciMaPeriod,CciMaMethod,DzLookBackBars,DzStartBuyProbability,DzStartSellProbability,alertsOn,alertsOnCurrent,alertsMessage,alertsEmail,alertsSound,soundFile,3,y);
                  buffer1[i] = iCustom(NULL,TimeFrame,indicatorFileName,PERIOD_CURRENT,CciPeriod,CciPrice,CciPriceSmoothing,CciMaPeriod,CciMaMethod,DzLookBackBars,DzStartBuyProbability,DzStartSellProbability,alertsOn,alertsOnCurrent,alertsMessage,alertsEmail,alertsSound,soundFile,4,y);
                  buffer2[i] = iCustom(NULL,TimeFrame,indicatorFileName,PERIOD_CURRENT,CciPeriod,CciPrice,CciPriceSmoothing,CciMaPeriod,CciMaMethod,DzLookBackBars,DzStartBuyProbability,DzStartSellProbability,alertsOn,alertsOnCurrent,alertsMessage,alertsEmail,alertsSound,soundFile,5,y);
                  buffer3[i] = iCustom(NULL,TimeFrame,indicatorFileName,PERIOD_CURRENT,CciPeriod,CciPrice,CciPriceSmoothing,CciMaPeriod,CciMaMethod,DzLookBackBars,DzStartBuyProbability,DzStartSellProbability,alertsOn,alertsOnCurrent,alertsMessage,alertsEmail,alertsSound,soundFile,6,y);
                  trend[i]   = iCustom(NULL,TimeFrame,indicatorFileName,PERIOD_CURRENT,CciPeriod,CciPrice,CciPriceSmoothing,CciMaPeriod,CciMaMethod,DzLookBackBars,DzStartBuyProbability,DzStartSellProbability,alertsOn,alertsOnCurrent,alertsMessage,alertsEmail,alertsSound,soundFile,8,y);
                  cciU[i]    = EMPTY_VALUE;
                  cciD[i]    = EMPTY_VALUE;
                  
                  if (!Interpolate || (i>0 && y==iBarShift(NULL,TimeFrame,Time[i-1]))) continue;
                  
                  //
                  //
                  //
                  //
                  //
                  
                  int n,k; datetime time = iTime(NULL,TimeFrame,y);
                     for(n = 1; (i+n)<Bars && Time[i+n] >= time; n++) continue;	
                     for(k = 1; k<n && (i+n)<Bars && (i+k)<Bars; k++) 
                     {
                        cci[i+k]     = cci[i]     + (cci[i+n]     - cci[i])     * k/n;
                        cciMa[i+k]   = cciMa[i]   + (cciMa[i+n]   - cciMa[i])   * k/n;
                        buffer1[i+k] = buffer1[i] + (buffer1[i+n] - buffer1[i]) * k/n;
                        buffer2[i+k] = buffer2[i] + (buffer2[i+n] - buffer2[i]) * k/n;
                        buffer3[i+k] = buffer3[i] + (buffer3[i+n] - buffer3[i]) * k/n;
                     }                           
               }
               for(int i=limit; i>=0; i--)
               {
                  if (trend[i] == 1) cciU[i] = cci[i];  
                  if (trend[i] ==-1) cciD[i] = cci[i]; 
               }
               return(0);
            }

   //
   //
   //
   //
   //

   for(int i=limit; i>=0; i--)
   {
      prices[i] = iMA(NULL,0,CciPriceSmoothing,0,CciMaMethod,CciPrice,i);
         double noise = 0, vhf = 0;
         double max   = prices[i];
         double min   = prices[i];
         for (int k=0; k<CciPeriod && (i+k+1)<Bars; k++)
         {
            noise += MathAbs(prices[i+k]-prices[i+k+1]);
            max    = MathMax(prices[i+k],max);   
            min    = MathMin(prices[i+k],min);   
         }      
         if (noise>0) vhf = (max-min)/noise;
         double period = (int)MathMax(-MathLog(vhf)*CciPeriod,3); 
      
      //
      //
      //
      //
      //
      
      double avgs = 0; for(int k=0; k<CciPeriod && (i+k)<Bars; k++) avgs +=         prices[i+k];       avgs /= CciPeriod;
      double devs = 0; for(int k=0; k<CciPeriod && (i+k)<Bars; k++) devs += MathAbs(prices[i+k]-avgs); devs /= CciPeriod;
         if (devs!=0)
               cci[i] = iSmooth((prices[i]-avgs)/(0.015*devs),period,i,0);
         else  cci[i] = iSmooth(0,                            period,i,0); 
         if (DzStartBuyProbability >0) buffer1[i] = dzBuyP (cci, DzStartBuyProbability,  DzLookBackBars, Bars, i, 0.0001);
         if (DzStartSellProbability>0) buffer2[i] = dzSellP(cci, DzStartSellProbability, DzLookBackBars, Bars, i, 0.0001);
                                       buffer3[i] = dzSellP(cci, 0.5                   , DzLookBackBars, Bars, i, 0.0001);
               cciU[i] = EMPTY_VALUE;
               cciD[i] = EMPTY_VALUE;
               if (i<Bars-1)
               {
                  trend[i]= trend[i+1];
                     if (cci[i]>0) trend[i]=  1; 
                     if (cci[i]<0) trend[i]= -1;
                     if (trend[i] == 1) cciU[i] = cci[i];  
                     if (trend[i] ==-1) cciD[i] = cci[i]; 
               }                     
   }
   for (int i=limit; i>=0; i--) cciMa[i] = iMAOnArray(cci,0,CciMaPeriod,0,CciMaMethod,i);

   //
   //
   //
   //
   //

   if (alertsOn)
   {
      int whichBar = 1; if (alertsOnCurrent) whichBar = 0;
      if (trend[whichBar] != trend[whichBar+1])
      if (trend[whichBar] == 1)
            doAlert("uptrend");
      else  doAlert("downtrend");       
   }
   return(0);
}

//------------------------------------------------------------------
//
//------------------------------------------------------------------
//
//
//
//
//

double workSmooth[][5];
double iSmooth(double price, double length, int r, int instanceNo=0)
{
   if (ArrayRange(workSmooth,0)!=Bars) ArrayResize(workSmooth,Bars); instanceNo *= 5; r = Bars-r-1;
 	if(r<=2) { workSmooth[r][instanceNo] = price; workSmooth[r][instanceNo+2] = price; workSmooth[r][instanceNo+4] = price; return(price); }
   
   //
   //
   //
   //
   //
   
	double alpha = 0.45*(length-1.0)/(0.45*(length-1.0)+2.0);
   	  workSmooth[r][instanceNo+0] =  price+alpha*(workSmooth[r-1][instanceNo]-price);
	     workSmooth[r][instanceNo+1] = (price - workSmooth[r][instanceNo])*(1-alpha)+alpha*workSmooth[r-1][instanceNo+1];
	     workSmooth[r][instanceNo+2] =  workSmooth[r][instanceNo+0] + workSmooth[r][instanceNo+1];
	     workSmooth[r][instanceNo+3] = (workSmooth[r][instanceNo+2] - workSmooth[r-1][instanceNo+4])*MathPow(1.0-alpha,2) + MathPow(alpha,2)*workSmooth[r-1][instanceNo+3];
	     workSmooth[r][instanceNo+4] =  workSmooth[r][instanceNo+3] + workSmooth[r-1][instanceNo+4]; 
   return(workSmooth[r][instanceNo+4]);
}

//-------------------------------------------------------------------
//
//-------------------------------------------------------------------
//
//
//
//
//

double workSma[][2];
double iSma(double price, int period, int r, int instanceNo=0)
{
   if (ArrayRange(workSma,0)!= Bars) ArrayResize(workSma,Bars); instanceNo *= 2; r = Bars-r-1; int k=1;

   //
   //
   //
   //
   //
      
   workSma[r][instanceNo] = price;
   if (r>=period)
          workSma[r][instanceNo+1] = workSma[r-1][instanceNo+1]+(workSma[r][instanceNo]-workSma[r-period][instanceNo])/period;
   else { workSma[r][instanceNo+1] = 0; for(k=0; k<period && (r-k)>=0; k++) workSma[r][instanceNo+1] += workSma[r-k][instanceNo];  
          workSma[r][instanceNo+1] /= k; }
   return(workSma[r][instanceNo+1]);
}

//-------------------------------------------------------------------
//
//-------------------------------------------------------------------
//
//
//
//
//

void doAlert(string doWhat)
{
   static string   previousAlert="nothing";
   static datetime previousTime;
   string message;
   
      if (previousAlert != doWhat || previousTime != Time[0]) {
          previousAlert  = doWhat;
          previousTime   = Time[0];

          //
          //
          //
          //
          //

          message =  StringConcatenate(Symbol()," at ",TimeToStr(TimeLocal(),TIME_SECONDS)," Cci Adaptive Smoother ",doWhat);
             if (alertsMessage) Alert(message);
             if (alertsEmail)   SendMail(StringConcatenate(Symbol()," Cci Adaptive Smoother "),message);
             if (alertsSound)   PlaySound(soundFile);
      }
}
 
//-------------------------------------------------------------------
//
//-------------------------------------------------------------------
//
//
//
//
//

string sTfTable[] = {"M1","M5","M10","M15","M30","H1","H4","D1","W1","MN"};
int    iTfTable[] = {1,5,10,15,30,60,240,1440,10080,43200};

string timeFrameToString(int tf)
{
   for (int i=ArraySize(iTfTable)-1; i>=0; i--) 
         if (tf==iTfTable[i]) return(sTfTable[i]);
                              return("");
}