//-------------------------------------------------------------------
#property copyright "copyleft www.forex-tsd.com"
#property link      "www.forex-tsd.com"
//-------------------------------------------------------------------
#property indicator_separate_window
#property indicator_buffers    6
#property indicator_color1     clrGold
#property indicator_color2     clrLimeGreen
#property indicator_color3     clrRed
#property indicator_color4     clrDarkSlateGray
#property indicator_color5     clrDodgerBlue
#property indicator_color6     clrPaleVioletRed
#property indicator_width1     2
#property indicator_width2     2
#property indicator_width3     2
#property indicator_width4     2
#property indicator_style5     STYLE_DASH
#property indicator_style6     STYLE_DASH
#property indicator_levelcolor clrDarkOrchid
#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
extern int                CciPeriod         = 12;              // CCI period
extern ENUM_APPLIED_PRICE CciPrice          = PRICE_TYPICAL;   // Price
extern int                AdaptPeriod       = 18;              // Adapting period
extern int                CciMaPeriod       = 7;               // CCI average period
extern ENUM_MA_METHOD     CciMaMethod       = MODE_LWMA;       // CCI average method
extern int                DzLookBackBars    = 35;              // Dynamic zones lookback period
extern double             DzBuyProbability  = 0.25;            // Dynamic zones buy probability
extern double             DzSellProbability = 0.25;            // Dynamic zones sell probability
extern bool               ShowHistogram     = true;            // Show histogram?
extern bool               ShowCciMa         = true;            // Show cci average?
extern double             OverSold          = -150;            // Oversold level
extern double             OverBought        = 150;             // Overbought level
extern bool               alertsOn          = false;           // Turn alerts on?
extern bool               alertsOnCurrent   = false;           // Alerts on current bar?
extern bool               alertsMessage     = false;           // Alerts should display a message?
extern bool               alertsSound       = false;           // Alerts should play a sound?
extern bool               alertsEmail       = false;           // Alerts should send an email?
extern string             soundFile         = "alert2.wav";    // Sound file to use for sounds :
extern bool              Interpolate        = true;           // Interpolate in multi time frame mode?

double cci[],cciU[],cciD[],cciMa[],bli[],sli[],prices[],trend[];
string indicatorFileName;
bool   returnBars;

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

int init()
{
      IndicatorBuffers(8);
      SetIndexBuffer(0,cci);  
      SetIndexBuffer(1,cciU);  
      SetIndexBuffer(2,cciD);  
      SetIndexBuffer(3,cciMa); 
      SetIndexBuffer(4,bli);
      SetIndexBuffer(5,sli);
      SetIndexBuffer(6,prices);
      SetIndexBuffer(7,trend); 
         if (ShowHistogram)
               { SetIndexStyle(1,DRAW_HISTOGRAM);  SetIndexStyle(2,DRAW_HISTOGRAM);  }
         else  { SetIndexStyle(1,DRAW_NONE);       SetIndexStyle(2,DRAW_NONE);       }
         if (ShowCciMa)
               SetIndexStyle(3,DRAW_LINE);
         else  SetIndexStyle(3,DRAW_NONE); 
         SetLevelValue(0,OverBought);
         SetLevelValue(1,OverSold); 
         SetLevelValue(2,0); 

         //
         //
         //
         //
         //
         
         indicatorFileName = WindowExpertName();
         returnBars        = (TimeFrame==-99);
         TimeFrame         = MathMax(TimeFrame,_Period);  
   IndicatorShortName(timeFrameToString(TimeFrame)+" Dz Cci Adaptive Smoother(" +(string)CciPeriod+ ", " +(string)AdaptPeriod+")");
   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) { cci[0] = limit+1; return(0); }
         if (TimeFrame!=_Period)
         {
            #define _mtfCall(_buff,_y) iCustom(NULL,TimeFrame,indicatorFileName,PERIOD_CURRENT,CciPeriod,CciPrice,AdaptPeriod,CciMaPeriod,CciMaMethod,DzLookBackBars,DzBuyProbability,DzSellProbability,ShowHistogram,ShowCciMa,OverSold,OverBought,alertsOn,alertsOnCurrent,alertsMessage,alertsSound,alertsEmail,soundFile,_buff,_y)
            limit = (int)MathMax(limit,MathMin(Bars-1,iCustom(NULL,TimeFrame,indicatorFileName,-99,0,0)*TimeFrame/Period()));
            for (int i=limit;i>=0; i--)
            {
               int y = iBarShift(NULL,TimeFrame,Time[i]);
                  cci[i]   = _mtfCall(0,y);
                  cciU[i]  = _mtfCall(1,y);
                  cciD[i]  = _mtfCall(2,y);
                  cciMa[i] = _mtfCall(3,y);
                  bli[i]   = _mtfCall(4,y);
                  sli[i]   = _mtfCall(5,y);
                  trend[i] = _mtfCall(7,y);

                  if (!Interpolate || (i>0 && y==iBarShift(NULL,TimeFrame,Time[i-1]))) continue;
                  
                  //
                  //
                  //
                  //
                  //
                  
                  #define _interpolate(buff,i,k,n) buff[i+k] = buff[i]+(buff[i+n]-buff[i])*k/n
                     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++) 
                        {
                           _interpolate(cci  ,i,k,n);
                           _interpolate(cciMa,i,k,n);
                           _interpolate(bli  ,i,k,n);
                           _interpolate(sli  ,i,k,n);
                        }                           
            }
            for (int i=limit;i>=0; i--)
            {
               cciU[i] = EMPTY_VALUE;
               cciD[i] = EMPTY_VALUE;
                  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--)
   {
      double dev    = iStdDev(NULL,0,AdaptPeriod,0,MODE_SMA,PRICE_CLOSE,i);
      double avg    = iSma(dev,AdaptPeriod,i,0);
      double period = (dev!=0) ? CciPeriod*avg/dev : CciPeriod;
         if (period < 3) period = 3;
      
      //
      //
      //
      //
      //
      
      prices[i]   = iMA(NULL,0,1,0,MODE_SMA,CciPrice,i);
      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;
      double wcci = (devs!=0) ? (prices[i]-avgs)/(0.015*devs) : 0;
             cci[i] = iSmooth(wcci,period,i,0);
               if (DzBuyProbability >0) bli[i] = dzBuyP (cci,DzBuyProbability, DzLookBackBars,Bars,i,0.0001);
               if (DzSellProbability>0) sli[i] = dzSellP(cci,DzSellProbability,DzLookBackBars,Bars,i,0.0001);
         
         
           cciU[i] = EMPTY_VALUE;
           cciD[i] = EMPTY_VALUE;
           trend[i]= (cci[i]>0) ? 1 : (cci[i]<0) ? -1 : (i<Bars-1) ? trend[i+1] : 0;
               if (trend[i] == 1) cciU[i] = cci[i];  
               if (trend[i] ==-1) cciD[i] = cci[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");       
   }
   for (int i=limit; i>=0; i--) cciMa[i] = iMAOnArray(cci,0,CciMaPeriod,0,CciMaMethod,i);
   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=0;

   //
   //
   //
   //
   //
      
   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 = timeFrameToString(_Period)+" "+_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","M15","M30","H1","H4","D1","W1","MN"};
int    iTfTable[] = {1,5,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("");
}