ThinkSript's Relative Volatility Index Indicator in MQL5

 

Trying to translate ThinkOrSwim's Relative Volatility Index indicator to MQL5, and I'm close (I think), but it's not plotting the same as in ToS. I found this indicator already written in MQL5, however whenever I utilize it in optimization of a trading strategy, it runs extremely slow, so I want to trim it down and make it myself to run as fast as it should.

The applicable code in ThinkScript looks like this:

input stDevLength = 10;
input averageLength = 14;
input averageType = AverageType.EXPONENTIAL;

def stDevHi = stDev(high, stDevLength);
def stDevLo = stDev(low, stDevLength);

def avgStDevHiUp = MovingAverage(averageType, if high > high[1] then stDevHi else 0, averageLength);
def avgStDevHiDown = MovingAverage(averageType, if high < high[1] then stDevHi else 0, averageLength);

def avgStDevLoUp = MovingAverage(averageType, if low > low[1] then stDevLo else 0, averageLength);
def avgStDevLoDown = MovingAverage(averageType, if low < low[1] then stDevLo else 0, averageLength);

def rviHi = if avgStDevHiUp + avgStDevHiDown == 0 then 50 else 100 * avgStDevHiUp / (avgStDevHiUp + avgStDevHiDown);
def rviLo = if avgStDevLoUp + avgStDevLoDown == 0 then 50 else 100 * avgStDevLoUp / (avgStDevLoUp + avgStDevLoDown);

plot RVI = (rviHi + rviLo) / 2;
plot OverBought = 70;
plot OverSold = 30;

We start by finding the trailing 10 periods of STDDEV of the high and low. We then find their respective moving average values of either 0 or stDevHi/stDevLo based on the shown price conditions. We then check if their respective sums are == 0, if so the rviHi/rviLo would be 50 for that bar, otherwise you see the formula for the value it should be. Finally, you calculate the current bar's average between rviHi and rviLo. This RVI is the line I'd like to plot.

The code I have and some screenshots of the current output are below. The code is well commented, I'm just not very skilled in MQL5 yet to figure out why this doesn't work just yet. Hoping someone can shed some light on it.

//+------------------------------------------------------------------+
//|                                   _Relative_Volatility_Index.mq5 |
//|                                                      windowshopr |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "windowshopr"
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_separate_window
#property indicator_minimum 0
#property indicator_maximum 100
#property indicator_buffers 3 // 3 buffers, only 1 plot
#property indicator_plots   1

//--- plot rviPlot
#property indicator_label1  "rviPlot"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrDarkTurquoise
#property indicator_style1  STYLE_SOLID
#property indicator_width1  3

//--- Bring in the iMA_On_Array function
#import "iMA_On_Array"
   double iMAOnArray(double& array[], int period, int ma_shift, ENUM_MA_METHOD ma_method, int shift);
#import

//--- input parameters
input int      stDevLength=10;
input int      avgLength=14;
input ENUM_MA_METHOD MA_Method=MODE_EMA;

//--- buffers
double         rviPlotBuffer[], // This is the one I want to plot
               stDevHiBuffer[], // These two just hold values for calculation later
               stDevLoBuffer[];

//--- indicator handles
double stDevHi, stDevLo;

//--- Misc
int MaxPeriod;



//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
{
//--- indicator buffers mapping
   SetIndexBuffer(0,stDevHiBuffer,INDICATOR_DATA);
   SetIndexBuffer(1,stDevLoBuffer,INDICATOR_DATA);
   SetIndexBuffer(2,rviPlotBuffer,INDICATOR_DATA);
   
   MaxPeriod = (int)MathMax(avgLength, stDevLength);
   
   // ToS equivalent of:
   // def stDevHi = stDev(high, stDevLength);
   // def stDevLo = stDev(low, stDevLength);
   stDevHi = iStdDev(_Symbol,_Period,stDevLength,0,MODE_SMA,PRICE_HIGH);
   stDevLo = iStdDev(_Symbol,_Period,stDevLength,0,MODE_SMA,PRICE_LOW);
   
   PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,MaxPeriod);
   IndicatorSetString(INDICATOR_SHORTNAME,"Relative Volatility Index ("+(string)stDevLength+ ", " + (string)avgLength +")");

//---
   return(INIT_SUCCEEDED);
}



void OnDeinit(const int reason)
{
//---
   if(stDevHi!=INVALID_HANDLE) IndicatorRelease(stDevHi);
   if(stDevLo!=INVALID_HANDLE) IndicatorRelease(stDevLo);
//---

}

//+------------------------------------------------------------------+
//| 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[])
{
//---

   // Simple error checking
   if (IsStopped()) return(0);
   if(rates_total<MaxPeriod) return(0);
   if (BarsCalculated(stDevHi)<rates_total) return(0);
   if (BarsCalculated(stDevLo)<rates_total) return(0);

   // Get number of bars to calculate for
   int copyBars = 0;
   if ((prev_calculated > rates_total) || prev_calculated <= 0)
   {
      copyBars = rates_total;
   } else {
      copyBars = rates_total - prev_calculated;
      if (prev_calculated > 0) copyBars++;
   }

   // Copy the stddev indicators into their respective buffers
   if (CopyBuffer(stDevHi, 0, 0, copyBars, stDevHiBuffer) <= 0) return(0);
   if (CopyBuffer(stDevLo, 0, 0, copyBars, stDevLoBuffer) <= 0) return(0);
   //if (CopyBuffer(stDevHi, 0, 0, copyBars, rviPlotBuffer) <= 0) return(0);

   // Define some empty arrays, used to get their respective moving average later
   double Array_avgStDevHiUpTemp[];
   double Array_avgStDevHiDownTemp[];
   double Array_avgStDevLoUpTemp[];
   double Array_avgStDevLoDownTemp[];
   
   ArrayResize(Array_avgStDevHiUpTemp,avgLength,0);
   ArrayResize(Array_avgStDevHiDownTemp,avgLength,0);
   ArrayResize(Array_avgStDevLoUpTemp,avgLength,0);
   ArrayResize(Array_avgStDevLoDownTemp,avgLength,0);
   
   ArrayInitialize(Array_avgStDevHiUpTemp,EMPTY_VALUE);
   ArrayInitialize(Array_avgStDevHiDownTemp,EMPTY_VALUE);
   ArrayInitialize(Array_avgStDevLoUpTemp,EMPTY_VALUE);
   ArrayInitialize(Array_avgStDevLoDownTemp,EMPTY_VALUE);

   // Convert arrays to series, NOT in reverse order for ease of calculating MA's
   ArraySetAsSeries(rviPlotBuffer, false);
   ArraySetAsSeries(stDevHiBuffer, false);
   ArraySetAsSeries(stDevLoBuffer, false);
   ArraySetAsSeries(Array_avgStDevHiUpTemp, false);
   ArraySetAsSeries(Array_avgStDevHiDownTemp, false);
   ArraySetAsSeries(Array_avgStDevLoUpTemp, false);
   ArraySetAsSeries(Array_avgStDevLoDownTemp, false);

   // For each bar we need to calculate for, starting from far left (Not 0 shift)...
   for (int i=copyBars-1; i>-1; i--)
   {
      // ...and for each bar in avgLength
      for (int j=avgLength-1; j>-1; j--)
      {
         // ToS equivalent of
         // if high > high[1] then stDevHi else 0
         if (iHigh(_Symbol,_Period,i) > iHigh(_Symbol,_Period,i+1))
         {
            Array_avgStDevHiUpTemp[j] = stDevHiBuffer[i];
         } else {
            Array_avgStDevHiUpTemp[j] = 0;
         }
         // ToS equivalent of
         // if high < high[1] then stDevHi else 0
         if (iHigh(_Symbol,_Period,i) < iHigh(_Symbol,_Period,i+1))
         {
            Array_avgStDevHiDownTemp[j] = stDevHiBuffer[i];
         } else {
            Array_avgStDevHiDownTemp[j] = 0;
         }
         // ToS equivalent of
         // if low > low[1] then stDevLo else 0
         if (iLow(_Symbol,_Period,i) > iLow(_Symbol,_Period,i+1))
         {
            Array_avgStDevLoUpTemp[j] = stDevLoBuffer[i];
         } else {
            Array_avgStDevLoUpTemp[j] = 0;
         }
         // ToS equivalent of
         // if low < low[1] then stDevLo else 0
         if (iLow(_Symbol,_Period,i) < iLow(_Symbol,_Period,i+1))
         {
            Array_avgStDevLoDownTemp[j] = stDevLoBuffer[i];
         } else {
            Array_avgStDevLoDownTemp[j] = 0;
         }
      }
      
      // Now get the array's respective moving average values, ToS equivalent of
      // def avgStDevHiUp = MovingAverage(averageType, if high > high[1] then stDevHi else 0, averageLength);
      double avgStDevHiUp = iMAOnArray(Array_avgStDevHiUpTemp,avgLength,0,MA_Method,0);
      double avgStDevHiDown = iMAOnArray(Array_avgStDevHiDownTemp,avgLength,0,MA_Method,0);
      double avgStDevLoUp = iMAOnArray(Array_avgStDevLoUpTemp,avgLength,0,MA_Method,0);
      double avgStDevLoDown = iMAOnArray(Array_avgStDevLoDownTemp,avgLength,0,MA_Method,0);
      
      // Get the rviHi/rviLo values, ToS equivalent of
      // def rviHi = if avgStDevHiUp + avgStDevHiDown == 0 then 50 else 100 * avgStDevHiUp / (avgStDevHiUp + avgStDevHiDown);
      double rviHi, rviLo;
      if (avgStDevHiUp + avgStDevHiDown==0)
      {
         rviHi = 50;
      } else {
         rviHi = 100 * avgStDevHiUp / (avgStDevHiUp + avgStDevHiDown);
      }
      
      if (avgStDevLoUp+avgStDevLoDown==0)
      {
         rviLo = 50;
      } else {
         rviLo = 100 * avgStDevLoUp / (avgStDevLoUp + avgStDevLoDown);
      }
      
      // Assign the RVI value at index i, ToS equivalent of
      // plot RVI = (rviHi + rviLo) / 2;
      rviPlotBuffer[i] = (rviHi + rviLo) / 2;
   }

   
//--- return value of prev_calculated for next call
   return(rates_total);
}
//+------------------------------------------------------------------+

This is what it SHOULD look like from ToS chart:


And this is the current output of the code I've shown:


Indicators: Relative Volatility Index
Indicators: Relative Volatility Index
  • 2016.10.28
  • www.mql5.com
Relative Volatility Index: Author: Mladen Rakic...
 

I should mention the iMA_On_Array function I got from here:


https://www.mql5.com/en/forum/43562#comment_20143403

iMAOnArray
iMAOnArray
  • 2015.04.24
  • www.mql5.com
Could you suggest function or technique similar to iMAOnArray in MQL5...