//+------------------------------------------------------------------+
//|                                           Volatility quality.mq4 |
//|                                                                  |
//|                                                                  |
//| Volatility quality index originaly developed by                  |
//| Thomas Stridsman (August 2002 Active Trader Magazine)            |
//|                                                                  |
//| Price pre-smoothing and filter added by raff1410                 |
//+------------------------------------------------------------------+
#property copyright "mladen"
#property link      "mladenfx@gmail.com"

#property indicator_separate_window
#property indicator_buffers 2

//
//
//
//
//

extern ENUM_TIMEFRAMES TimeFrame              = PERIOD_CURRENT;    // Time frame to use
extern int             PriceSmoothing         = 5;
extern ENUM_MA_METHOD  PriceSmoothingMethod   = MODE_LWMA;
extern int             MA1Period              = 9;
extern int             MA2Period              = 200;
extern int             Filter                 = 5;
input int              inpHistoWidth          = 2;                 // Histogram bars width
input color            inpUpHistoColor        = clrDodgerBlue;     // Bullish color
input color            inpDnHistoColor        = clrRed;            // Bearish color
input bool             arrowsVisible          = false;             // Arrows visible true/false?
input bool             arrowsOnNewest         = false;             // Arrows drawn on newest bar of higher time frame bar true/false?
input string           arrowsIdentifier       = "vq Arrows1";      // Unique ID for arrows
input double           arrowsUpperGap         = 0.5;               // Upper arrow gap
input double           arrowsLowerGap         = 0.5;               // Lower arrow gap
input color            arrowsUpColor          = clrBlue;           // Up arrow color
input color            arrowsDnColor          = clrCrimson;        // Down arrow color
input int              arrowsUpCode           = 221;               // Up arrow code
input int              arrowsDnCode           = 222;               // Down arrow code
input int              arrowsUpSize           = 2;                 // Up arrow size
input int              arrowsDnSize           = 2;                 // Down arrow size
input bool             Interpolate            = true;              // Interpolate in mtf mode?

//
//
//
//
//

double sumVqi[];
double upH[];
double dnH[];
double sumVqi1[];
double sumVqi2[];
double Vqi[];
double trend[];
double atrend[],count[];
string indicatorFileName;
#define _mtfCall(_buff,_ind) iCustom(NULL,TimeFrame,indicatorFileName,PERIOD_CURRENT,PriceSmoothing,PriceSmoothingMethod,MA1Period,MA2Period,Filter,inpHistoWidth,inpUpHistoColor,inpDnHistoColor,arrowsVisible,arrowsOnNewest,arrowsIdentifier,arrowsUpperGap,arrowsLowerGap,arrowsUpColor,arrowsDnColor,arrowsUpCode,arrowsDnCode,arrowsUpSize,arrowsDnSize,_buff,_ind)

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
//
//
//
//
//

int OnInit()
{
   IndicatorBuffers(6);
      SetIndexBuffer(0,upH,INDICATOR_DATA); SetIndexStyle(0,DRAW_HISTOGRAM,EMPTY,inpHistoWidth,inpUpHistoColor);
      SetIndexBuffer(1,dnH,INDICATOR_DATA); SetIndexStyle(1,DRAW_HISTOGRAM,EMPTY,inpHistoWidth,inpDnHistoColor);
      SetIndexBuffer(2,sumVqi); 
      SetIndexBuffer(3,Vqi); 
      SetIndexBuffer(4,trend);
      SetIndexBuffer(5,count);
      
      IndicatorSetDouble(INDICATOR_MINIMUM,0);
      IndicatorSetDouble(INDICATOR_MAXIMUM,1);
      
      PriceSmoothing    = fmax(PriceSmoothing,1);
      indicatorFileName = WindowExpertName();
      TimeFrame         = fmax(TimeFrame,_Period);
   
   IndicatorSetString(INDICATOR_SHORTNAME,timeFrameToString(TimeFrame)+" VQ ("+(string)PriceSmoothing+","+(string)MA1Period+")"); 
return(INIT_SUCCEEDED);
}
void OnDeinit(const int reason)
{ 
    string lookFor       = arrowsIdentifier+":";
    int    lookForLength = StringLen(lookFor);
    for (int i=ObjectsTotal()-1; i>=0; i--)
    {
       string objectName = ObjectName(i);
       if (StringSubstr(objectName,0,lookForLength) == lookFor) ObjectDelete(objectName);
    }
}

//
//
//
//
//

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 i,limit=fmin(rates_total-prev_calculated+1,rates_total-1); count[0]=limit;
      if (TimeFrame!=_Period)
      {
         limit = (int)fmax(limit,fmin(rates_total-1,_mtfCall(5,0)*TimeFrame/_Period));
         for (i=limit;i>=0 && !_StopFlag; i--)
         {
            int y = iBarShift(NULL,TimeFrame,time[i]);
               upH[i] = _mtfCall(0,y);
               dnH[i] = _mtfCall(1,y);
               
        }   
	return(rates_total);
	}      
         
   //
   //
   //
            
   for(i=limit; i>=0; i--)
   {
      if (i==(Bars-1))
      {
         Vqi[i]    = 0;
         sumVqi[i] = 0;
         continue;
      }
      
      //
      //
      //
      //
      //
      
         double cHigh  = iMA(NULL,0,PriceSmoothing,0,PriceSmoothingMethod,PRICE_HIGH ,i);
         double cLow   = iMA(NULL,0,PriceSmoothing,0,PriceSmoothingMethod,PRICE_LOW  ,i);
         double cOpen  = iMA(NULL,0,PriceSmoothing,0,PriceSmoothingMethod,PRICE_OPEN ,i);
         double cClose = iMA(NULL,0,PriceSmoothing,0,PriceSmoothingMethod,PRICE_CLOSE,i);
         double pClose = iMA(NULL,0,PriceSmoothing,0,PriceSmoothingMethod,PRICE_CLOSE,i+1);
         
         double trueRange = MathMax(cHigh,pClose)-MathMin(cLow,pClose);
         double     range = cHigh-cLow;
      
            if (range != 0 && trueRange!=0)
               double vqi = ((cClose-pClose)/trueRange + (cClose-cOpen)/range)*0.5;
            else      vqi = Vqi[i+1];

      //
      //
      //
      //
      //
         
         Vqi[i]      = MathAbs(vqi)*(cClose-pClose+cClose-cOpen)*0.5;
         sumVqi[i]   = sumVqi[i+1]+Vqi[i];
            if (Filter > 0) if (MathAbs(sumVqi[i]-sumVqi[i+1]) < Filter*_Point) sumVqi[i] = sumVqi[i+1];
            trend[i] = (i<Bars-1) ? (sumVqi[i] > sumVqi[i+1]) ? 1 : (sumVqi[i] < sumVqi[i+1]) ? -1 : trend[i+1] : 0;
            upH[i] = (trend[i] == 1) ? 1 : EMPTY_VALUE;
            dnH[i] = (trend[i] ==-1) ? 1 : EMPTY_VALUE;   
            
            //
            //
            //

            if (arrowsVisible)
            {
               string lookFor = arrowsIdentifier+":"+(string)time[i]; ObjectDelete(lookFor);            
               if (i<(rates_total-1) && trend[i] != trend[i+1])
               {
                  if (trend[i] == 1) drawArrow(i,arrowsUpColor,arrowsUpCode,arrowsUpSize,false);
                  if (trend[i] ==-1) drawArrow(i,arrowsDnColor,arrowsDnCode,arrowsDnSize, true);
               }
             }     
   }
return(0);
}

//-------------------------------------------------------------------
//                                                                  
//-------------------------------------------------------------------

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("");
}


//-------------------------------------------------------------------
//                                                                  
//-------------------------------------------------------------------

void drawArrow(int i,color theColor,int theCode, int theSize, bool up)
{
   string name = arrowsIdentifier+":"+(string)Time[i];
   double gap  = iATR(NULL,0,20,i);   
   
      //
      //
      //
   
      datetime atime = Time[i]; if (arrowsOnNewest) atime += _Period*60-1;      
      ObjectCreate(name,OBJ_ARROW,0,atime,0);
         ObjectSet(name,OBJPROP_ARROWCODE,theCode);
         ObjectSet(name,OBJPROP_COLOR,theColor);
         ObjectSet(name,OBJPROP_WIDTH,theSize);
         if (up)
               ObjectSet(name,OBJPROP_PRICE1,High[i] + arrowsUpperGap * gap);
         else  ObjectSet(name,OBJPROP_PRICE1,Low[i]  - arrowsLowerGap * gap);
}
