//+------------------------------------------------------------------+
//|                                         Volatility_Optimizer.mq4 |
//|                        Copyright 2022, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
// https://www.tradingview.com/script/ukSDV6uT-Volatility-Optimizer/
//+------------------------------------------------------------------+
//
//  //first
//  l1 = input(title="Fastet", defval=8, minval=1)
//  s1 = input(title="Smoothing", defval="EMA", options=)
//  atr1(source, l1) =>
//  if s1 == "EMA"
//  ema(source, l1)
//  else
//  if s1 == "SMA"
//  sma(source, l1)
//  if s1 == "RMA"
//  rma(source, l1)
//  else
//  wma(source, l1)
//  
//  //second
//  l2 = input(title="Middle", defval=32, minval=1)
//  s2 = input(title="Smoothing", defval="SMA", options=)
//  atr2(source, l2) =>
//  if s2 == "SMA"
//  sma(source, l2)
//  else
//  if s1 == "RMA"
//  rma(source, l2)
//  else
//  if s2 == "EMA"
//  ema(source, l2)
//  else
//  wma(source, l2)
//  
//  //third
//  l3 = input(title="Slowest", defval=115, minval=1)
//  s3 = input(title="Smoothing", defval="RMA", options=)
//  atr3(source, l3) =>
//  if s1 == "RMA"
//  rma(source, l3)
//  else
//  if s1 == "SMA"
//  sma(source, l3)
//  else
//  if s1 == "EMA"
//  ema(source, l3)
//  else
//  wma(source, l3)
//  
//  atr10=atr1(tr(true), l1)
//  atr20=atr2(tr(true), l2)
//  atr30=atr3(tr(true), l3)
//  
//  plot(atr10, title = "ATR short", color=#0066ff, style=plot.style_area, transp=30)
//  plot(atr20, title = "ATR middle", color=#ff3300, style=plot.style_area, transp=30)
//  plot(atr30, title = "ATR long", color=#996600, style=plot.style_area, transp=30)
//  
//  plot(atr10, title = "ATR short", color=#0066ff, transp=0, linewidth=2)
//  plot(atr20, title = "ATR middle", color=#ff3300, transp=0, linewidth=2)
//  plot(atr30, title = "ATR long", color=#996600, transp=0, linewidth=2)
//  
//  shape1 = atr10>atr20 and atr10>atr30
//  shape2 = atr20>atr10 and atr20>atr30
//  shape3 = atr30>atr10 and atr30>atr20
//  
//  plotshape(shape1, title = "ATR short", style=shape.circle, location=location.top, color=#0066ff, size=size.tiny)
//  plotshape(shape2, title = "ATR middle", style=shape.circle, location=location.top, color=#ff3300, size=size.tiny)
//  plotshape(shape3, title = "ATR long", style=shape.circle, location=location.top, color=#996600, size=size.tiny)
//+------------------------------------------------------------------+

#property copyright "www,forex-station.com"
#property link      "www,forex-station.com"
#property version   "1.00"
#property strict
#property indicator_separate_window
#property indicator_buffers 4
#property indicator_color1  clrCornflowerBlue
#property indicator_color2  clrRed
#property indicator_color3  clrYellow
#property indicator_width1  2
#property indicator_width2  2
#property indicator_width3  2
//
//
//
 
extern int                l1               = 8;                // 1 Length
extern int                Length1          = 5;                // 1 Jurik and filter period to use
extern double             Phase1           = 0.0;              // 1 Jurik phase 
extern bool               Double1          = false;            // 1 Jurik smooth double
extern int                l2               = 32;               // 2 Length
extern int                l3               = 115;              // 3 Length

double atr10[],atr20[],atr30[],voltop[];

int factor;

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
{
   SetIndexBuffer(0,atr10,INDICATOR_DATA); SetIndexStyle(0,DRAW_LINE);  SetIndexLabel(0,"Short");
   SetIndexBuffer(1,atr20,INDICATOR_DATA); SetIndexStyle(1,DRAW_LINE);  SetIndexLabel(1,"Mid");
   SetIndexBuffer(2,atr30,INDICATOR_DATA); SetIndexStyle(2,DRAW_LINE);  SetIndexLabel(2,"Long");
   
   // 1 = Blue on top, 2 = Red on top & 3 = Yellow on top
   SetIndexBuffer(3,voltop,INDICATOR_DATA); SetIndexStyle(3,DRAW_NONE);  SetIndexLabel(3,"VOL_TOP");
   
   atr1.OnInit(l1);
   atr2.OnInit(l2);
   atr3.OnInit(l3);
   
   factor=GetPipFactor(Symbol());
    
   IndicatorSetString(INDICATOR_SHORTNAME," Volatility Optimizer ("+(string)l1+","+(string)l2+","+(string)l3+")");
   return(INIT_SUCCEEDED);
}
void OnDeinit(const int reason) {   }  
 
//+------------------------------------------------------------------+
//| 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 i,r,limit=fmin(rates_total-prev_calculated+1,rates_total-1);
   
   //
   //
   //
   
   for(i=limit, r=rates_total-limit-1; i>=0; i--,r++)
   {
	   double tr = (r>0) ? fmax((high[i] - low[i]), fmax(MathAbs(high[i] - close[i+1]), MathAbs(low[i] - close[i+1]))): (high[i] - low[i]);
	   atr10[i]  = iDSmooth((factor*atr1.OnCalculate(tr,r,rates_total)),Length1,Phase1,Double1,i,0);
	   atr20[i]  = factor*atr2.OnCalculate(tr,r,rates_total);
	   atr30[i]  = factor*atr3.OnCalculate(tr,r,rates_total);
	   voltop[i] = 0;
	   if (atr10[i] > atr20[i] && atr10[i] > atr30[i])
	   {
	      voltop[i] = 1;
	   } else
	   {
	      if(atr20[i] > atr10[i] && atr20[i] > atr30[i])
	      {
	         voltop[i] = 2;
	      } else
	      {
	         if(atr30[i] > atr10[i] && atr30[i] > atr20[i])
	         {
	            voltop[i] = 3;
	         } else
	         {
	            voltop[i] = 0;
	         }
	      }
	   }
	   
	   
   }
return(rates_total);
}

//------------------------------------------------------------------
//
//------------------------------------------------------------------

class CEma
{
   private :
         double m_alpha;
         double m_period;
         double m_array[];
         int    m_arraySize;
   public :
      CEma() : m_alpha(1), m_arraySize(-1), m_period(1) {}
     ~CEma()                                            {}
     
      //
      //
      //
     
      void OnInit(double period)
         {
            m_period = (period>1) ? period : 1;
            m_alpha  = 2.0/(1.0+m_period);
         }
      double OnCalculate(double value, int i, int bars)
         {
            if (m_arraySize<bars) m_arraySize=ArrayResize(m_array,bars+500);
            
            //
            //
            //
            
            if (i>0)
                    m_array[i] = m_array[i-1]+m_alpha*(value-m_array[i-1]); 
            else    m_array[i] = value;
            return (m_array[i]);
         }   
};
CEma atr1;

/*
class CSma
{
   private :
         double m_period;
         double m_array[];
         int    m_arraySize;
   public :
      CSma() : m_arraySize(-1), m_period(1) {}
     ~CSma()                                {}
     
      //
      //
      //
     
      void OnInit(double period)
         {
            m_period = (period>1) ? period : 1;
         }
      double OnCalculate(double value, int i, int bars)
         {
            if (m_arraySize<bars) m_arraySize=ArrayResize(m_array,bars+500);
            
            //
            //
            //
            
            if (i>0)
                    {m_array[i] = m_array[i-1]+(value-m_array[(int)(i-m_period)])/m_period;} 
            else    m_array[i] = value;
            return (m_array[i]);
         }   
};
CSma atr2;
*/

class CSma
{
   private :
      struct scSmaArrayStruct
      {
         double value;
         double sum;
      };
      scSmaArrayStruct m_array[];
      int              m_arraySize;
      int              m_period;
   public :
      CSma() : m_period(1), m_arraySize(-1) {                     return; }
     ~CSma()                                { ArrayFree(m_array); return; }
     
     //
     //
     //
      
     void OnInit(int period) 
     { 
         m_period = (period>1) ? period : 1; 
     }
     double OnCalculate(double value, int i, int bars)
     {
        if (m_arraySize<bars)
          { m_arraySize=ArrayResize(m_array,bars+500); if (m_arraySize<bars) return(0); }

         //
         //
         //

         m_array[i].value=value;
            if (i>m_period)
                   m_array[i].sum = m_array[i-1].sum + value - m_array[i-m_period].value;
            else { m_array[i].sum = 0; for(int k=0; k<m_period && i>=k; k++) m_array[i].sum += m_array[i-k].value; }
            return(m_array[i].sum / (double)m_period);
      }
};
CSma atr2;


class CRma
{
   private :
         double m_alpha;
         double m_period;
         double m_array[];
         int    m_arraySize;
   public :
      CRma() : m_alpha(1), m_arraySize(-1), m_period(1) {}
     ~CRma()                                            {}
     
      //
      //
      //
     
      void OnInit(double period)
         {
            m_period = (period>1) ? period : 1;
            m_alpha  = 1.0/(m_period);
         }
      double OnCalculate(double value, int i, int bars)
         {
            if (m_arraySize<bars) m_arraySize=ArrayResize(m_array,bars+500);
            
            //
            //
            //
            
            if (i>0)
                    m_array[i] = m_array[i-1]+m_alpha*(value-m_array[i-1]); 
            else    m_array[i] = value;
            return (m_array[i]);
         }   
};
CRma atr3;



//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int GetPipFactor(string Xsymbol)
{
   //Code from Tommaso's APTM. Thanks Tommaso.
   
   static const string factor1000[]        = {"SEK","TRY","ZAR","MXN"};
   static const string factor100[]         = {"JPY","XAG","SILVER","BRENT","WTI"};
   static const string factor10[]          = {"XAU","GOLD","SP500","US500Cash","US500","Bund"};
   static const string factor1[]           = {"UK100","WS30","DAX30","NAS100","CAC40","FRA40","GER30","ITA40","EUSTX50","JPN225","US30Cash","US30"};

   int j = 0;
   
   int xFactor=10000;       // correct xFactor for most pairs
   if(MarketInfo(Xsymbol,MODE_DIGITS)<=1) xFactor=1;
   else if(MarketInfo(Xsymbol,MODE_DIGITS)==2) xFactor=10;
   else if(MarketInfo(Xsymbol,MODE_DIGITS)==3) xFactor=100;
   else if(MarketInfo(Xsymbol,MODE_DIGITS)==4) xFactor=1000;
   else if(MarketInfo(Xsymbol,MODE_DIGITS)==5) xFactor=10000;
   else if(MarketInfo(Xsymbol,MODE_DIGITS)==6) xFactor=100000;
   else if(MarketInfo(Xsymbol,MODE_DIGITS)==7) xFactor=1000000;
   for(j=0; j<ArraySize(factor1000); j++)
   {
      if(StringFind(Xsymbol,factor1000[j])!=-1) xFactor=1000;
   }
   for(j=0; j<ArraySize(factor100); j++)
   {
      if(StringFind(Xsymbol,factor100[j])!=-1) xFactor=100;
   }
   for(j=0; j<ArraySize(factor10); j++)
   {
      if(StringFind(Xsymbol,factor10[j])!=-1) xFactor=10;
   }
   for(j=0; j<ArraySize(factor1); j++)
   {
      if(StringFind(Xsymbol,factor1[j])!=-1) xFactor=1;
   }

   return (xFactor);
}//End int GetPipFactor(string Xsymbol)




//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
//
//

double wrk[][20];

#define bsmax  5
#define bsmin  6
#define volty  7
#define vsum   8
#define avolty 9

double iDSmooth(double price, double length, double phase, bool isDouble, int i, int s=0) 
{
   if (length==0)
         return (price);		
   else if (isDouble)
         return (iSmooth(iSmooth(price,MathSqrt(length),phase,i,s),MathSqrt(length),phase,i,s+10));
   else  return (iSmooth(price,length,phase,i,s));
}

//
//
//
//
//

double iSmooth(double price, double length, double phase, int i, int s=0)
{
   if (length <=1) return(price);
   if (ArrayRange(wrk,0) != Bars) ArrayResize(wrk,Bars);
   
   int r = Bars-i-1; 
      if (r==0) { int k; for(k=0; k<7; k++) wrk[r][k+s]=price; for(; k<10; k++) wrk[r][k+s]=0; return(price); }

   //
   //
   //
   //
   //
   
      double len1   = MathMax(MathLog(MathSqrt(0.5*(length-1)))/MathLog(2.0)+2.0,0);
      double pow1   = MathMax(len1-2.0,0.5);
      double del1   = price - wrk[r-1][bsmax+s];
      double del2   = price - wrk[r-1][bsmin+s];
      double div    = 1.0/(10.0+10.0*(MathMin(MathMax(length-10,0),100))/100);
      int    forBar = MathMin(r,10);
	
         wrk[r][volty+s] = 0;
               if(MathAbs(del1) > MathAbs(del2)) wrk[r][volty+s] = MathAbs(del1); 
               if(MathAbs(del1) < MathAbs(del2)) wrk[r][volty+s] = MathAbs(del2); 
         wrk[r][vsum+s] =	wrk[r-1][vsum+s] + (wrk[r][volty+s]-wrk[r-forBar][volty+s])*div;
         
         //
         //
         //
         //
         //
   
         wrk[r][avolty+s] = wrk[r-1][avolty+s]+(2.0/(MathMax(4.0*length,30)+1.0))*(wrk[r][vsum+s]-wrk[r-1][avolty+s]);
            double dVolty = 0;
            if (wrk[r][avolty+s] > 0)
                  dVolty = wrk[r][volty+s]/wrk[r][avolty+s];   
	               if (dVolty > MathPow(len1,1.0/pow1)) dVolty = MathPow(len1,1.0/pow1);
                  if (dVolty < 1)                      dVolty = 1.0;

      //
      //
      //
      //
      //
	        
   	double pow2 = MathPow(dVolty, pow1);
      double len2 = MathSqrt(0.5*(length-1))*len1;
      double Kv   = MathPow(len2/(len2+1), MathSqrt(pow2));

         if (del1 > 0) wrk[r][bsmax+s] = price; else wrk[r][bsmax+s] = price - Kv*del1;
         if (del2 < 0) wrk[r][bsmin+s] = price; else wrk[r][bsmin+s] = price - Kv*del2;
	
   //
   //
   //
   //
   //
      
      double R     = MathMax(MathMin(phase,100),-100)/100.0 + 1.5;
      double beta  = 0.45*(length-1)/(0.45*(length-1)+2);
      double alpha = MathPow(beta,pow2);

         wrk[r][0+s] = price + alpha*(wrk[r-1][0+s]-price);
         wrk[r][1+s] = (price - wrk[r][0+s])*(1-beta) + beta*wrk[r-1][1+s];
         wrk[r][2+s] = (wrk[r][0+s] + R*wrk[r][1+s]);
         wrk[r][3+s] = (wrk[r][2+s] - wrk[r-1][4+s])*MathPow((1-alpha),2) + MathPow(alpha,2)*wrk[r-1][3+s];
         wrk[r][4+s] = (wrk[r-1][4+s] + wrk[r][3+s]); 

   //
   //
   //
   //
   //

   return(wrk[r][4+s]);
}
