//+------------------------------------------------------------------+
//|                                           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         = 3;
extern ENUM_MA_METHOD  PriceSmoothingMethod   = MODE_LWMA;
extern int             MA1Period              = 20;
extern int             MA2Period              = 50;
extern int             Filter                 = 1;
input int              inpHistoWidth          = 8;                 // Histogram bars width
input color            inpUpHistoColor        = C'45,170,242';     // Bullish color
input color            inpDnHistoColor        = C'245,95,125';           // Bearish color
input bool             alertsOn               = false;              // Alerts true/false?
input bool             alertsOnCurrent        = false;             // Alerts open bar true/false?
input bool             alertsMessage          = false;              // Alerts pop-up message true/false?
input bool             alertsSound            = false;             // Alerts sound true/false?
input bool             alertsNotify           = false;             // Alerts push notification true/false?
input bool             alertsEmail            = false;             // Alerts email true/false?
input string           soundFile              = "alert2.wav";      // Sound file
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          = clrNONE;           // Up arrow color
input color            arrowsDnColor          = clrNONE;        // 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,alertsOn,alertsOnCurrent,alertsMessage,alertsSound,alertsNotify,alertsEmail,soundFile,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.1);
      IndicatorSetDouble(INDICATOR_MAXIMUM,1);
      
      PriceSmoothing    = fmax(PriceSmoothing,1);
      indicatorFileName = WindowExpertName();
      TimeFrame         = fmax(TimeFrame,_Period);
   
   IndicatorSetString(INDICATOR_SHORTNAME," "); 
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);
               }
             }     
   }
   if (alertsOn)
   {
         int whichBar = 1; if (alertsOnCurrent) whichBar = 0; 
         if (trend[whichBar] != trend[whichBar+1])
         {
            if (trend[whichBar] == 1) doAlert(" up");
            if (trend[whichBar] ==-1) doAlert(" down");       
         }         
   }     
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);
}

//------------------------------------------------------------------
//
//------------------------------------------------------------------
//
//
//
//
//

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)+" VQ "+doWhat;
             if (alertsMessage) Alert(message);
             if (alertsNotify)  SendNotification(message);
             if (alertsEmail)   SendMail(_Symbol+" VQ ",message);
             if (alertsSound)   PlaySound(soundFile);
      }
}

