//+------------------------------------------------------------------+
//|                                                                  |
//| "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    3
#property indicator_color1     clrGold
#property indicator_color2     clrDimGray
#property indicator_color3     clrDimGray
#property indicator_style1     STYLE_DOT

//
//
//
//
//

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[],vbu[],vbd[],prices[],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(5);
   SetIndexBuffer(0,vbc); 
   SetIndexBuffer(1,vbu); 
   SetIndexBuffer(2,vbd); 
   SetIndexBuffer(3,prices); 
   SetIndexBuffer(4,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(4,0)*TimeFrame/Period()));
            for (i=limit;i>=0;i--)
            {
               int y = iBarShift(NULL,TimeFrame,Time[i]);
               vbc[i] = _mtfCall(0,y); 
               vbu[i] = _mtfCall(1,y); 
               vbd[i] = _mtfCall(2,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);
                   }                           
            }
   return(0);
   }
     
   //
   //
   //
   //
   //

   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;
    }
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(MathSqrt(sum/period));
}

//+-------------------------------------------------------------------
//|                                                                  
//+-------------------------------------------------------------------
//
//
//
//
//

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("");
}
