//+------------------------------------------------------------------+
//|                                                                  |
//| "Fixing the Bollinger bands"                                     |
//| Futures magazine 05.2010, David Rooke                            }
//+------------------------------------------------------------------+
#property copyright "mladen"
#property link      "mladenfx@gmail.com"

#property indicator_chart_window
#property indicator_buffers    5
#property indicator_color1     clrLimeGreen
#property indicator_color2     clrSandyBrown
#property indicator_color3     clrSandyBrown
#property indicator_color4     clrDimGray
#property indicator_color5     clrDimGray
#property indicator_width1     3   
#property indicator_width2     3   
#property indicator_width3     3   

//
//
//
//
//

extern ENUM_TIMEFRAMES    TimeFrame              = PERIOD_CURRENT;   // Time frame
extern int                Length                 = 20;
extern ENUM_APPLIED_PRICE Price                  = PRICE_CLOSE;
extern double             Deviations             = 2.0;
extern bool               UseClassicalDeviations = false;
 extern bool              Interpolate            = true;             // Interpolate in mtf mode

double vbc[],vbcDa[],vbcDb[],vbu[],vbd[],prices[],trend[],count[];
string indicatorFileName;
#define _mtfCall(_buff,_ind) iCustom(NULL,TimeFrame,indicatorFileName,PERIOD_CURRENT,Length,Price,Deviations,UseClassicalDeviations,_buff,_ind)

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
//
//
//
//
//

int init()
{
   for (int i=0; i<indicator_buffers; i++) SetIndexStyle(i,DRAW_LINE);
   IndicatorBuffers(8);
   SetIndexBuffer(0,vbc); 
   SetIndexBuffer(1,vbcDa); 
   SetIndexBuffer(2,vbcDb); 
   SetIndexBuffer(3,vbu); 
   SetIndexBuffer(4,vbd); 
   SetIndexBuffer(5,prices); 
   SetIndexBuffer(6,trend);
   SetIndexBuffer(7,count);
   
   indicatorFileName = WindowExpertName();
   TimeFrame         = fmax(TimeFrame,_Period);
  
   IndicatorShortName (timeFrameToString(TimeFrame)+" Volatility bands");
return(0);
}
int deinit() {   return(0); }

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
//
//
//
//
//

int start()
{
   int i,counted_bars=IndicatorCounted();
      if(counted_bars<0) return(-1);
      if(counted_bars>0) counted_bars--;
         int limit=fmin(Bars-counted_bars,Bars-1); count[0] = limit;
         if (TimeFrame!=_Period)
         {
            limit = (int)fmax(limit,fmin(Bars-1,_mtfCall(7,0)*TimeFrame/Period()));
            if (trend[limit]==-1) CleanPoint(limit,vbcDa,vbcDb);
            for (i=limit;i>=0;i--)
            {
               int y = iBarShift(NULL,TimeFrame,Time[i]);
               vbc[i]   = _mtfCall(0,y); 
               vbcDa[i] = EMPTY_VALUE;
               vbcDb[i] = EMPTY_VALUE;
               vbu[i]   = _mtfCall(3,y); 
               vbd[i]   = _mtfCall(4,y); 
               trend[i] = _mtfCall(6,y); 
               
               //
               //
               //
               //
               //
      
               if (!Interpolate || (i>0 && y==iBarShift(NULL,TimeFrame,Time[i-1]))) continue;
               #define _interpolate(buff) 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(vbc);
                     _interpolate(vbu);
                     _interpolate(vbd);
                   }                           
            }
            for (i=limit; i >= 0; i--)  if (trend[i]==-1) PlotPoint(i,vbcDa,vbcDb,vbc); 
   return(0);
   }
     
   //
   //
   //
   //
   //
   
   if (trend[limit]==-1) CleanPoint(limit,vbcDa,vbcDb);
   for(i=limit; i>=0; i--)
   {
      prices[i] = iMA(NULL,0,1,0,MODE_SMA,Price,i);
         double tema1 = iTema(prices[i],Length,i,0);
         double tema2 = iTema(tema1    ,Length,i,3);
         vbc[i] = 2.0*tema1-tema2;
         double deviation = iDeviationPlus(prices,vbc,Length,i);

         vbu[i] = vbc[i]+deviation*Deviations;      
         vbd[i] = vbc[i]-deviation*Deviations;
         vbcDa[i] = EMPTY_VALUE;
         vbcDb[i] = EMPTY_VALUE;
         trend[i] = (i<Bars-1) ? (Close[i]>vbc[i]) ? 1 : (Close[i]<vbc[i]) ? -1 : trend[i+1] : 0;
         if (trend[i]==-1) PlotPoint(i,vbcDa,vbcDb,vbc);  
    }
return(0);
}

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
//
//
//
//
//

double wrk[][6];

double iTema(double price, int length, int i, int s=0)
{
   if (ArrayRange(wrk,0)!=Bars) ArrayResize(wrk,Bars);
   
   //
   //
   //
   //
   //
   
   double alpha = 2.0 /(1.0 + length);
   int    r = Bars-i-1;
   
   if (r < 1)
      {
         wrk[r][0+s] = price;
         wrk[r][1+s] = price;
         wrk[r][2+s] = price;
      }
   else
      {
         wrk[r][0+s] = wrk[r-1][0+s]+alpha*(price      -wrk[r-1][0+s]);
         wrk[r][1+s] = wrk[r-1][1+s]+alpha*(wrk[r][0+s]-wrk[r-1][1+s]);
         wrk[r][2+s] = wrk[r-1][2+s]+alpha*(wrk[r][1+s]-wrk[r-1][2+s]);
      }
   return(3*wrk[r][0+s] - 3*wrk[r][1+s] + wrk[r][2+s]);
}

//
//
//
//    In the original article the second method is how deviation is
//    calculated . It is a questionableble method , but if we apply
//    standard calculation the bands tend to be "spiky".That is the
//    reason to have the "UseClassicalDeviations" option
//
//
//

double iDeviationPlus(double& array[], double& ma[], int period, int i)
{
   double sum = 0;
      if (UseClassicalDeviations)
            for(int k=0; k<period; k++) sum += (array[i+k]-ma[i])  *(array[i+k]-ma[i]);
      else  for(    k=0; k<period; k++) sum += (array[i+k]-ma[i+k])*(array[i+k]-ma[i+k]);       
   return(sqrt(sum/period));
}

//-------------------------------------------------------------------
//                                                                  
//-------------------------------------------------------------------
//
//
//
//
//

void CleanPoint(int i,double& first[],double& second[])
{
   if (i>=Bars-3) return;
   if ((second[i]  != EMPTY_VALUE) && (second[i+1] != EMPTY_VALUE))
        second[i+1] = EMPTY_VALUE;
   else
      if ((first[i] != EMPTY_VALUE) && (first[i+1] != EMPTY_VALUE) && (first[i+2] == EMPTY_VALUE))
          first[i+1] = EMPTY_VALUE;
}

void PlotPoint(int i,double& first[],double& second[],double& from[])
{
   if (i>=Bars-2) return;
   if (first[i+1] == EMPTY_VALUE)
      if (first[i+2] == EMPTY_VALUE) 
            { first[i]  = from[i];  first[i+1]  = from[i+1]; second[i] = EMPTY_VALUE; }
      else  { second[i] =  from[i]; second[i+1] = from[i+1]; first[i]  = EMPTY_VALUE; }
   else     { first[i]  = from[i];                           second[i] = EMPTY_VALUE; }
}

//+-------------------------------------------------------------------
//|                                                                  
//+-------------------------------------------------------------------
//
//
//
//
//

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("");
}
