//+------------------------------------------------------------------+
//|                                                          QBV.mq4 |
//|                                                           extraw |
//|                               http://www.forexfactory.com/extraw |
//+------------------------------------------------------------------+
#property copyright "extraw"
#property link      "http://www.forexfactory.com/extraw"
#property version   "1.14" //option for filtering out overlap zone
#property strict
#property indicator_chart_window
#property indicator_buffers 2
enum Style
{
   Style_Solid       = 0,    //Solid
   Style_Dash        = 1,    //Dash
   Style_Dot         = 2,    //Dot
   Style_DashDot     = 3,    //DashDot
   Style_DashDotDot  = 4     //DashDotDot
};
enum Width
{
   Width_1 = 1,    //1
   Width_2 = 2,    //2
   Width_3 = 3,    //3
   Width_4 = 4,    //4
   Width_5 = 5    //5
};
//--- input parameters
input int      PercentThreshold=50;
input int      N=50; // number of bars to calc average
input color    UpperBandColor=clrSkyBlue;
input color    LowerBandColor=clrDarkOrange;
input Style    BandStyle            = 0;
input Width    BandWidth            = 1;
input bool     Filter_Overlap_Zone  = true;
input bool     NewZoneCreated_Alert   = true;

//--- variables
double   UpperBand[], LowerBand[];
string         lineUpper, lineLower;

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
      SetIndexBuffer(0,UpperBand);
      SetIndexStyle(0,DRAW_LINE, BandStyle, BandWidth, UpperBandColor);
      
      SetIndexBuffer(1,LowerBand);
      SetIndexStyle(1,DRAW_LINE, BandStyle, BandWidth, LowerBandColor);
      
      IndicatorShortName("QBV(" + IntegerToString(PercentThreshold) + "%" + ") - " + IntegerToString(N) + " bars");
      
      lineUpper = "[QBV]LineUpper_" + IntegerToString(PercentThreshold) + "_" + IntegerToString(N);
      ObjectCreate(0, lineUpper, OBJ_TREND, 0, 0, 0, 0, 0);
      ObjectSetInteger(0,lineUpper,OBJPROP_COLOR,UpperBandColor);
      ObjectSetInteger(0,lineUpper,OBJPROP_STYLE,BandStyle);
      ObjectSetInteger(0,lineUpper,OBJPROP_WIDTH,BandWidth);
      ObjectSetInteger(0,lineUpper,OBJPROP_BACK,false);
      ObjectSetInteger(0,lineUpper,OBJPROP_SELECTABLE,false);
      
      lineLower = "[QBV]LineLower_" + IntegerToString(PercentThreshold) + "_" + IntegerToString(N);;
      ObjectCreate(0, lineLower, OBJ_TREND, 0, 0, 0, 0, 0);
      ObjectSetInteger(0,lineLower,OBJPROP_COLOR,LowerBandColor);
      ObjectSetInteger(0,lineLower,OBJPROP_STYLE,BandStyle);
      ObjectSetInteger(0,lineLower,OBJPROP_WIDTH,BandWidth);
      ObjectSetInteger(0,lineLower,OBJPROP_BACK,false);
      ObjectSetInteger(0,lineLower,OBJPROP_SELECTABLE,false);
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//---
      int counted_bars, limit;
      double volume_sum = 0, volume_avg = 0;
      double upper_value = 0, lower_value = 0;
      double upper_new = 0, lower_new = 0;
      
      counted_bars = IndicatorCounted();
      if(counted_bars < 0)  return(-1);
      //last counted bar will be recounted
      if(counted_bars > 0) counted_bars--;
      limit = Bars - counted_bars;
      if (limit < N) return(-2);
      
      // calc sum volume and avg volume of first 50 bars
      for(int i = limit - 1; i >= limit - 1 - N; i--)
      {
         volume_sum = volume_sum + Volume[i];
      }
      volume_avg = volume_sum / N;
      
      //Begin the loop of calculations for the range of chart bars.
      for(int i = limit - 1 - N; i >= 0; i--)
      {
         if (Volume[i] > volume_avg + PercentThreshold / 100.0 * volume_avg)
         {
            upper_new = iHigh(NULL, 0, i);
            lower_new = iLow(NULL, 0, i);
            
            if (Filter_Overlap_Zone == true)
            {
               if (lower_new > upper_value || upper_new < lower_value) 
               {
                  upper_value = upper_new;
                  lower_value = lower_new;
                  
                  // alert if this is last bar
                  if (i == 0 && NewZoneCreated_Alert == true) Alert(Symbol() + " QBV(" + IntegerToString(PercentThreshold) + "%, " + IntegerToString(N) + "): New zone created");
               }
            }
            else
            {
               upper_value = upper_new;
               lower_value = lower_new;
               
               // alert if this is last bar
               if (i == 0 && NewZoneCreated_Alert == true) Alert(Symbol() + " QBV(" + IntegerToString(PercentThreshold) + "%, " + IntegerToString(N) + "): New zone created");
            }
         }
         
         UpperBand[i] = upper_value;
         LowerBand[i] = lower_value;
         
         // calc new sum and new avg
         volume_sum = volume_sum - Volume[i + N] + Volume[i];
         volume_avg = volume_sum / N;
      }
      
      // draw lines beyond last bar 
      ObjectMove(0, lineUpper, 0, Time[0], upper_value);
      ObjectMove(0, lineUpper, 1, Time[0] + PeriodSeconds(), upper_value);
      
      ObjectMove(0, lineLower, 0, Time[0], lower_value);
      ObjectMove(0, lineLower, 1, Time[0] + PeriodSeconds(), lower_value);
   
//--- return value of prev_calculated for next call
   return(rates_total);
  }

//+------------------------------------------------------------------+
//| Custor indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---- 
      string name;
      
      for (int i= ObjectsTotal(); i>=0; i--) 
      {
         name = ObjectName(i);
       
         if (StringSubstr(name,0,5) == "[QBV]") 
            ObjectDelete(name);
      }
//----
   return;
  }  
//+------------------------------------------------------------------+