//+------------------------------------------------------------------+
//|                                           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  5
#property indicator_color1   Goldenrod
#property indicator_color2   Gold
#property indicator_color3   LimeGreen
#property indicator_color4   Red
#property indicator_color5   Red
#property indicator_width3   2
#property indicator_width4   2
#property indicator_width5   2
#property indicator_style1   STYLE_DOT
#property indicator_style2   STYLE_DOT
#property strict

//
//
//
//
//

extern ENUM_TIMEFRAMES TimeFrame              = PERIOD_CURRENT; // Time frame to use
extern int             PriceSmoothing         = 5;
input ENUM_MA_METHOD   PriceSmoothingMethod   = MODE_LWMA;
input int              MA1Period              = 9;
input ENUM_MA_METHOD   MA1Method              = MODE_SMA;
input int              MA2Period              = 200;
input ENUM_MA_METHOD   MA2Method              = MODE_SMA;
input double           FilterInPips           = 2.0;
input bool             alertsOn               = true;
input bool             alertsOnCurrent        = false;
input bool             alertsMessage          = false;
input bool             alertsSound            = false;
input bool             alertsEmail            = false;
input bool             alertsPushNotification = false;
input bool             arrowsVisible          = true;              // Arrows visible?
input bool             arrowsOnNewest         = true;              // Arrows drawn on newst bar of higher time frame bar?
input string           arrowsIdentifier       = "vq Arrows1";       // Unique ID for arrows
input double           arrowsUpperGap         = 1.0;                // Upper arrow gap
input double           arrowsLowerGap         = 1.0;                // Lower arrow gap
input color            arrowsUpColor          = clrWhite;       // Up arrow color
input color            arrowsDnColor          = clrWhite;          // Down arrow color
input int              arrowsUpCode           = 221;                // Up arrow code
input int              arrowsDnCode           = 222;                // Down arrow code
input int              arrowsSize             = 7;                  // Arrows size
input bool             Interpolate            = true;               // Interpolate in multi time frame mode?

double sumVqi[],sumVqida[],sumVqidb[],sumVqi1[],sumVqi2[],Vqi[],trend[],cross[],count[];
string indicatorFileName;
#define _mtfCall(_buff,_ind) iCustom(NULL,TimeFrame,indicatorFileName,0,PriceSmoothing,PriceSmoothingMethod,MA1Period,MA1Method,MA2Period,MA2Method,FilterInPips,alertsOn,alertsOnCurrent,alertsMessage,alertsSound,alertsEmail,alertsPushNotification,arrowsVisible,arrowsOnNewest,arrowsIdentifier,arrowsUpperGap,arrowsLowerGap,arrowsUpColor,arrowsDnColor,arrowsUpCode,arrowsDnCode,arrowsSize,_buff,_ind)

//------------------------------------------------------------------
//
//------------------------------------------------------------------
//
//
//
//
//

int OnInit()
{
   IndicatorBuffers(9);
      SetIndexBuffer(0,sumVqi1); 
      SetIndexBuffer(1,sumVqi2); 
      SetIndexBuffer(2,sumVqi); 
      SetIndexBuffer(3,sumVqida); 
      SetIndexBuffer(4,sumVqidb); 
      SetIndexBuffer(5,Vqi); 
      SetIndexBuffer(6,trend);
      SetIndexBuffer(7,cross);
      SetIndexBuffer(8,count);
         indicatorFileName = WindowExpertName();
         TimeFrame         = MathMax(TimeFrame,_Period);
         PriceSmoothing    = MathMax(PriceSmoothing,1);
         IndicatorSetString(INDICATOR_SHORTNAME,timeFrameToString(TimeFrame)+" VQ Stridsman");
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 start()
{
   int counted_bars=IndicatorCounted();
      if(counted_bars<0) return(-1);
      if(counted_bars>0) counted_bars--;
         int limit = MathMin(Bars-counted_bars,Bars-1); count[0] = limit;
            if (TimeFrame != Period())
            {
               limit = (int)MathMax(limit,MathMin(Bars-1,_mtfCall(8,0)*TimeFrame/_Period));
               if (trend[limit] == -1) CleanPoint(limit,sumVqida,sumVqidb);
               for(int i=limit; i>=0 && !_StopFlag; i--)
               {
                  int y = iBarShift(NULL,TimeFrame,Time[i]);
                  sumVqi1[i]  = _mtfCall(0,y);
                  sumVqi2[i]  = _mtfCall(1,y);
                  sumVqi[i]   = _mtfCall(2,y);
                  trend[i]    = _mtfCall(6,y);
                  sumVqida[i] = sumVqidb[i] = EMPTY_VALUE;
                  
                  //
                  //
                  //
                  //
                  //
                  
                  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 itime = iTime(NULL,TimeFrame,y);
                        for(n = 1; (i+n)<Bars && Time[i+n] >= itime; n++) continue;	
                        for(k = 1; k<n && (i+n)<Bars && (i+k)<Bars; k++)
                        {
                          _interpolate(sumVqi1);                      
                          _interpolate(sumVqi2);                      
                          _interpolate(sumVqi);                      
                        }                          
               }
               for(int i=limit; i>=0; i--) if (trend[i] == -1) PlotPoint(i,sumVqida,sumVqidb,sumVqi);
               return(0);
            }            
         
   //
   //
   //
   //
   //
            
   double pipMultiplier = MathPow(10,_Digits%2);
   if (trend[limit] == -1) CleanPoint(limit,sumVqida,sumVqidb);
   for(int 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;
         double       vqi = (range != 0 && trueRange!=0) ? ((cClose-pClose)/trueRange + (cClose-cOpen)/range)*0.5 : (i<Bars-1) ? Vqi[i+1] : 0;

      //
      //
      //
      //
      //
         
         Vqi[i]      = MathAbs(vqi)*(cClose-pClose+cClose-cOpen)*0.5;
         sumVqi[i]   = (i<Bars-1) ? sumVqi[i+1]+Vqi[i] : Vqi[i];
         sumVqida[i] = sumVqidb[i] = EMPTY_VALUE;
            if (FilterInPips > 0 && i<Bars-1) if (MathAbs(sumVqi[i]-sumVqi[i+1]) < FilterInPips*pipMultiplier*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;
         if (trend[i] == -1) PlotPoint(i,sumVqida,sumVqidb,sumVqi);
   }
   for(int i=limit; i>=0; i--)
   {
      if (MA1Period > 1) sumVqi1[i] = iMAOnArray(sumVqi,0,MA1Period,0,MA1Method,i);
      if (MA2Period > 1) sumVqi2[i] = iMAOnArray(sumVqi,0,MA2Period,0,MA2Method,i);
      cross[i] = (i<Bars-1) ? (sumVqi[i]>sumVqi2[i]) ? 1 : (sumVqi[i]<sumVqi2[i]) ? -1 : cross[i+1] : 0;
      
       //
       //
       //
       //
       //
                
       if (arrowsVisible)
       {
           string lookFor = arrowsIdentifier+":"+(string)Time[i]; ObjectDelete(lookFor);            
           if (i<(Bars-1) && cross[i] != cross[i+1])
           {
              if (cross[i] == 1) drawArrow(i,arrowsUpColor,arrowsUpCode,false);
              if (cross[i] ==-1) drawArrow(i,arrowsDnColor,arrowsDnCode, true);
           }
       }
      
   }      
   manageAlerts();
   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; }
}

//+-------------------------------------------------------------------
//|                                                                  
//+-------------------------------------------------------------------
//
//
//
//
//

void manageAlerts()
{
   if (alertsOn)
   {
      int whichBar = (alertsOnCurrent) ? 0 : 1;
      if (trend[whichBar] != trend[whichBar+1])
      {
         if (trend[whichBar] == 1) doAlert(whichBar,"up");
         if (trend[whichBar] ==-1) doAlert(whichBar,"down");
      }         
   }
}   

//
//
//
//
//

void doAlert(int forBar, string doWhat)
{
   static string   previousAlert="nothing";
   static datetime previousTime;
   string message;
   
   if (previousAlert != doWhat || previousTime != Time[forBar]) {
       previousAlert  = doWhat;
       previousTime   = Time[forBar];

       //
       //
       //
       //
       //

       message = timeFrameToString(_Period)+" "+_Symbol+" at "+TimeToStr(TimeLocal(),TIME_SECONDS)+" - volatility quality Stridsman trend changed to "+doWhat;
          if (alertsMessage)          Alert(message);
          if (alertsEmail)            SendMail(_Symbol+" volatility quality Stridsman ",message);
          if (alertsPushNotification) SendNotification(message);
          if (alertsSound)            PlaySound("alert2.wav");
   }
}

//-------------------------------------------------------------------
//                                                                  
//-------------------------------------------------------------------
//
//
//
//
//

void drawArrow(int i,color theColor,int theCode,bool tup)
{
   string name = arrowsIdentifier+":"+(string)Time[i];
   double gap  = iATR(NULL,0,20,i);   
   
      //
      //
      //
      //
      //

      datetime time = Time[i]; if (arrowsOnNewest) time += _Period*60-1;      
      ObjectCreate(name,OBJ_ARROW,0,Time[i],0);
         ObjectSet(name,OBJPROP_ARROWCODE,theCode);
         ObjectSet(name,OBJPROP_WIDTH,arrowsSize);
         ObjectSet(name,OBJPROP_COLOR,theColor);
         if (tup)
               ObjectSet(name,OBJPROP_PRICE1,High[i] + arrowsUpperGap * gap);
         else  ObjectSet(name,OBJPROP_PRICE1,Low[i]  - arrowsLowerGap * gap);
}

//
//
//
//
//

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("");
}