# Volatility analysis : bridging the gap from volatility forecasting to price forecasting

[Versiunea romaneasca] [MQLmagazine.com in romana] [English edition]

Volatility. A word that generates panic for investors as well as faster heartbeats for traders. How many times haven’t you tried before to trade only trending markets and catch the momentum ?

According to InvestorWords.com, volatility is ** the relative rate at which the price of a security moves up and down. Volatility is found by calculating the annualized standard deviation of daily change in price. If the price of a stock moves up and down rapidly over short time periods, it has high volatility. If the price almost never changes, it has low volatility.** So volatility is measured by standard deviation (noted with greek lowercase sigma letter). And what’s the most popular indicator based on standard deviation ? You guessed, the famous Bollinger Bands. Bollinger Bands (BB) is one of the first indicators that you have learned to use in trading.

**You have been told that, there is a signal to buy or sell whenever the market touches one of the bands, followed by a possible walk along the band, and afterwards when price turns away, it will go like that until it touches the moving average. This rule actually doesn’t tell too much. There is nothing saying how much it will walk along the band until it turns back. Which you may find very frustrating on your own … equity. If you graph the basis of this indicator, the standard deviation, you will see a sinusoidal chart. Volatility goes up and down in**

*quite a continuous pattern, without swift moves.*Since it’s not making swift moves, it means the volatility itself is more predictible than the price. Since it’s forecastable, why not using BBs over volatility ? But wait. Standard deviation is relative. It measures the dispersion over the analyzed period. If we add the BBs over the standard deviation, it means we will use the standard deviation of the standard deviation to catch moves in volatility! This time there is no fear of false positives, because the analysed data set, standard deviation of the price, is a continuous function. Second, even if the signal is wrong and the volatility turns back and trends down towards its moving average, there’s not too much damage done, because the volatility is already down when signal is taken, and a move down in volatility will mean less movement in price. The bluish line is the standard deviation of last 20 bars opening price. Signals we are interested in happen when volatility crosses its own upper BB. Of course, we are not interested in crosses with lower BB, because they mean volatility is going down faster than normal. During a trend, a powerful countermovement will appear like the volatility would be going down. Best signals are when crosses with the upper BB happen at a very reduced historical volatility – below 20% of the on-screen range. If you compare with the other signals on chart, BB over price intersections with the price, you can see that this one may cross even when the volatility is within its own BB bands, so this is a way to filter lots of error-prone BB signals that are given by price BB intersections. A good entry-exit criteria would be that the distance between the upper volatility BB band and the volatility to be below 2% of volatility range. Below is the

**MQL4 transcript of the**

**Bollinger Bands over Standard Deviation**indicator.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 | //+------------------------------------------------------------------+ //| BBoverSTD.mq4 | //| Bogdan Caramalac | //| mailto:fxeconomist@yahoo.com | //+------------------------------------------------------------------+ #property copyright "Bogdan Caramalac" #property link "mailto:fxeconomist@yahoo.com" #property indicator_separate_window #property indicator_buffers 4 #property indicator_color1 LightSeaGreen //STDDEV #property indicator_color2 Blue //MA #property indicator_color3 Yellow //Upper BB #property indicator_color4 Yellow //Lower BB //---- input parameters extern int STDLEN=20; extern int BBLEN=10; extern double BBMULT=2.0; extern string MATYPE="SMA"; extern int MALEN=0; extern int Logarithm=0; double ExtMapBuffer1[]; double ExtMapBuffer2[]; double ExtMapBuffer3[]; double ExtMapBuffer4[]; //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int init() { //---- indicators SetIndexStyle(0,DRAW_LINE, 0, 1, indicator_color1); SetIndexBuffer(0, ExtMapBuffer1); SetIndexStyle(1,DRAW_LINE, 0, 1, indicator_color2); SetIndexBuffer(1, ExtMapBuffer2); SetIndexStyle(2,DRAW_LINE, 0, 1, indicator_color3); SetIndexBuffer(2, ExtMapBuffer3); SetIndexStyle(3,DRAW_LINE, 0, 1, indicator_color4); SetIndexBuffer(3, ExtMapBuffer4); //---- SetIndexDrawBegin(0,STDLEN); SetIndexDrawBegin(1,STDLEN+BBLEN-1); SetIndexDrawBegin(2,STDLEN+BBLEN-1); SetIndexDrawBegin(3,STDLEN+BBLEN-1); return(0); } //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int start() { if (Bars<=MathMax(STDLEN+BBLEN,MALEN)) return(0); int pos=Bars-1; ArraySetAsSeries(ExtMapBuffer1,true); while (pos>=0) { if (MATYPE=="SMA") { ExtMapBuffer1[pos]=iStdDev(Symbol(),Period(),STDLEN,0,MODE_SMA,MODE_OPEN,pos); if (Logarithm==1&&ExtMapBuffer1[pos]!=0.00) ExtMapBuffer1[pos]=MathLog(ExtMapBuffer1[pos]); } if (MATYPE=="EMA") { ExtMapBuffer1[pos]=iStdDev(Symbol(),Period(),STDLEN,0,MODE_EMA,MODE_OPEN,pos); if (Logarithm==1&&ExtMapBuffer1[pos]!=0.00) ExtMapBuffer1[pos]=MathLog(ExtMapBuffer1[pos]); } if (MATYPE=="SMMA") { ExtMapBuffer1[pos]=iStdDev(Symbol(),Period(),STDLEN,0,MODE_SMMA,MODE_OPEN,pos); if (Logarithm==1&&ExtMapBuffer1[pos]!=0.00) ExtMapBuffer1[pos]=MathLog(ExtMapBuffer1[pos]); } if (MATYPE=="LWMA") { ExtMapBuffer1[pos]=iStdDev(Symbol(),Period(),STDLEN,0,MODE_LWMA,MODE_OPEN,pos); if (Logarithm==1&&ExtMapBuffer1[pos]!=0.00) ExtMapBuffer1[pos]=MathLog(ExtMapBuffer1[pos]); } pos--; } pos=Bars-1; while(pos>=0) { if (MATYPE=="SMA") { if (MALEN!=0) ExtMapBuffer2[pos]=iMAOnArray(ExtMapBuffer1,0,MALEN,0,MODE_SMA,pos); else ExtMapBuffer2[pos]=EMPTY_VALUE; } if (MATYPE=="EMA") { if (MALEN!=0) ExtMapBuffer2[pos]=iMAOnArray(ExtMapBuffer1,0,MALEN,0,MODE_EMA,pos); else ExtMapBuffer2[pos]=EMPTY_VALUE; } if (MATYPE=="SMMA") { if (MALEN!=0) ExtMapBuffer2[pos]=iMAOnArray(ExtMapBuffer1,0,MALEN,0,MODE_SMMA,pos); else ExtMapBuffer2[pos]=EMPTY_VALUE; } if (MATYPE=="LWMA") { if (MALEN!=0) ExtMapBuffer2[pos]=iMAOnArray(ExtMapBuffer1,0,MALEN,0,MODE_LWMA,pos); else ExtMapBuffer2[pos]=EMPTY_VALUE; } pos--; } pos=Bars-1; while(pos>=0) { ExtMapBuffer3[pos]=EMPTY_VALUE; ExtMapBuffer4[pos]=EMPTY_VALUE; ExtMapBuffer3[pos]=iBandsOnArray(ExtMapBuffer1,0,BBLEN,BBMULT,0,MODE_HIGH,pos); ExtMapBuffer4[pos]=iBandsOnArray(ExtMapBuffer1,0,BBLEN,BBMULT,0,MODE_LOW,pos); pos--; } return(0); } |

Parameters:

STDLEN – length of the period whose standard deviation is calculated;

BBLEN – length of the period whose BB bands are calculated for;

BBMULT – standard deviation multiplicator for BB bands;

MATYPE – a helper Moving Average over the volatility is created, of this type ; same type is applied to the construction of the standard deviation in first place;

MALEN – period of the helper Moving Average (turned off with 0)

Logarithm – used for logarithming the scale ; for 0, disabled, for 1, natural logarithm;

Credits to Irtron and Ruptor from the mql4.com forum for sorting out this indicator.

The iMAOnArray function is buggy; an MQL4 indicator file can contain more than one loop used for calculation.

*Let’s take a peek at the anatomy of this MQL4 indicator.*

Lines 9 to 15 are the intro of the indicator ; it is specified that the indicator is in a **separate window** , number of buffers and coloring for each;

Lines 17 to 22 are the definition of parameters; what makes them parameters, as opposed to what is known as global variables in other programming languages , (for instance the Public declarations in Visual Basic) is the keyword **extern **in the front of the type ; when the indicator is installed (dragged over the chart) , these parameters can be modified . Also these parameters can be modified any time by the user ; but no expert advisor or script can touch the parameters of an indicator running in a window.

Lines 24 to 27 are the buffers ; since we specified that are 4 of them, 4 are defined. Indicator buffers are numbers that denominate arrays inside indicators that contain data. For instance, if an indicator would copy OHLC bar data to indicator buffers, 0 would be for open prices, 1 for high , 2 for low, 3 for close. In our case, as you will see later, 0 is for standard deviation, 1 for helper moving average, 2 for higher BB band and 3 for lower BB band.

Lines 31 to 49 make up the **init() **function. This function is called every time the chart parameters are changed (symbol , periodicity) or connection coming back up. The function defines the drawing style for each of the buffers and assigns the arrays to indicator buffers. Also specifies where drawing point begins for each buffer. Drawing point should be carefully calculated, so that data that is the base for buffer to be there previously. For instance, if you calculate a 10 element moving average, drawing point should not be larger than 10 elements below the count of bars.

Lines 54 to 131 make up the **start() **function. **start()** is triggered at every incoming quote. It starts with a condition checking that there are enough **Bars** to draw the indicator. Then, sets the position to start drawing. Position is a bar index. Indicator is calculated from a positive position to zero (current bar). The **ArraySetAsSeries() **function alters the working mode of an array, setting it work like time series. This does not affect the array per se. Rather, it tells the special functions that work with arrays to treat arrays reversed. The functions that calculate indicators over arrays (like **iMAOnArray()** , **iStdDevOnArray**() and so on) pass the array from left to right, from lower indexes to higher indexes. The **ArraySetAsSeries**() applied to an array will make these functions pass the array from right to left, from higher indexes to lower indexes, exactly how a custom indicator loop does. Then what follows are the loops. In a simple indicator quite everything can be done in a single loop, but here three loops do the trick. The first loop will calculate the standard deviation ; the second loop will calculate the helper moving average, and finally the third loop will calculate the upper and lower BBs.

**Same indicator, MQL5 version**

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 | //+------------------------------------------------------------------+ //| BBoverSTD.mq5 | //| Copyright Bogdan Caramalac | //| mqlmagazine.com | //+------------------------------------------------------------------+ #property copyright "Bogdan Caramalac" #property link "mqlmagazine.com" #property version "1.02" #property indicator_separate_window #property indicator_buffers 4 #property indicator_applied_price PRICE_OPEN #property indicator_plots 4 #property indicator_type1 DRAW_LINE #property indicator_label1 "Stddev" #property indicator_color1 LightSeaGreen //STDDEV #property indicator_type2 DRAW_LINE #property indicator_label2 "Helper MA" #property indicator_color2 Blue //MA #property indicator_type3 DRAW_LINE #property indicator_label3 "Upper BB" #property indicator_color3 Yellow //Upper BB #property indicator_type4 DRAW_LINE #property indicator_label4 "Lower BB" #property indicator_color4 Yellow //Lower BB //---- input parameters input int STDLEN=20; input int BBLEN=10; input double BBMULT=2.0; input string MATYPE="SMA"; input int MALEN=2; input int Logarithm=0; double ExtMapBuffer1[]; double ExtMapBuffer2[]; double ExtMapBuffer3[]; double ExtMapBuffer4[]; #include <MovingAverages.mqh> int StdDevHandle; //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- indicator buffers mapping SetIndexBuffer(0, ExtMapBuffer1); SetIndexBuffer(1, ExtMapBuffer2); SetIndexBuffer(2, ExtMapBuffer3); SetIndexBuffer(3, ExtMapBuffer4); //---- PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,STDLEN); PlotIndexSetInteger(1,PLOT_DRAW_BEGIN,STDLEN+BBLEN-1); PlotIndexSetInteger(2,PLOT_DRAW_BEGIN,STDLEN+BBLEN-1); PlotIndexSetInteger(3,PLOT_DRAW_BEGIN,STDLEN+BBLEN-1); //--- //we create iStdDev handler for use in main cycle; if (MATYPE=="SMA") StdDevHandle=iStdDev(Symbol(),Period(),STDLEN,0,MODE_SMA,PRICE_OPEN); if (MATYPE=="EMA") StdDevHandle=iStdDev(Symbol(),Period(),STDLEN,0,MODE_EMA,PRICE_OPEN); if (MATYPE=="LWMA") StdDevHandle=iStdDev(Symbol(),Period(),STDLEN,0,MODE_LWMA,PRICE_OPEN); if (MATYPE=="SMMA") StdDevHandle=iStdDev(Symbol(),Period(),STDLEN,0,MODE_SMMA,PRICE_OPEN); return(0); } //+------------------------------------------------------------------+ //| 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[]) { double res[]; //array to hold current iStdDev value; double buffer_helper[500]; double stddevonstddev=0.0; //current stddev on stddev double squaredsum=0.0; //current squared sum double bbma=0.0; //current ma for bollinger bands calculus double bbsum=0.0; //current sum for calculus of bbma double prevma=0.0; //previous helper moving average value, for use with SMMA or LWMA int buff_stddevs=0; //used length of stddevs buffer int buff_helper_len=0; //used length of helper buffer int startpos=MathRound(MathMin(prev_calculated,rates_total-1)); //this is the start position ; it goes UPWARD, we are on an array, not on series if (rates_total<STDLEN) return(rates_total); if (startpos<STDLEN) startpos=STDLEN; for (int pos=startpos;pos<rates_total;pos++) //main cycle, going from startpos to rates_total { //Reads 1 element from position rates_total-pos, buffer 0 of StdDevIndicator calculus designated by StdDevHandle //rates_total-pos to convert position ; buffers are time series (curent value has index 0) CopyBuffer(StdDevHandle,0,rates_total-pos,1,res); ExtMapBuffer1[pos]=res[0];//copy value from temporary res[0] array index to final ExtMapBuffer1 if (Logarithm==1) ExtMapBuffer1[pos]=MathLog(ExtMapBuffer1[pos]); if (Logarithm==2) ExtMapBuffer1[pos]=MathLog10(ExtMapBuffer1[pos]); if (pos>STDLEN+BBLEN) { //enough values to calculate Bollies over volatility squaredsum=0.0; bbsum=0.0; //calculating the moving average for bollinger bands for (int j=pos-BBLEN+1;j<=pos;j++) bbsum=bbsum+ExtMapBuffer1[j]; bbma=bbsum/BBLEN; //calculating the squared sum for the stddev over stddev needed for bollinger bands for (int j=pos-BBLEN+1;j<=pos;j++) squaredsum=squaredsum+(ExtMapBuffer1[j]-bbma)*(ExtMapBuffer1[j]-bbma); //stddev on stddev and bollinger bands stddevonstddev=MathSqrt(squaredsum/BBLEN); ExtMapBuffer3[pos]=bbma+BBMULT*stddevonstddev; ExtMapBuffer4[pos]=bbma-BBMULT*stddevonstddev; if (pos>MALEN+BBLEN+STDLEN) { if (MALEN!=0) //goes in only if helper moving average is indeed enabled { //making buffer for helper MA for (int j=1;j<=MALEN;j++) buffer_helper[MALEN-j+1]=ExtMapBuffer1[pos-j+1]; //calculating different kinds of moving averages, by calling moving average methods from MovingAverages.mqh if (MATYPE=="SMA") ExtMapBuffer2[pos]=SimpleMA(MALEN,MALEN,buffer_helper); if (MATYPE=="EMA") { ExtMapBuffer2[pos]=ExponentialMA(MALEN,MALEN,prevma,buffer_helper); prevma=ExtMapBuffer2[pos]; } if (MATYPE=="SMMA") { ExtMapBuffer2[pos]=SmoothedMA(MALEN,MALEN,prevma,buffer_helper); prevma=ExtMapBuffer2[pos]; } if (MATYPE=="LWMA") ExtMapBuffer2[pos]=LinearWeightedMA(MALEN,MALEN,buffer_helper); }//if (MALEN!=0) }//if (pos>MALEN+STDLEN) }//else if if (buff_stddevs<BBLEN) }//for (pos=startpos;pos<rates_total;pos++) return(rates_total); } |

Well, programming in MQL5 is signifiantly harder. MQL5 has a whole different way of approaching indicators, so different that comparisons to MQL4 are practically pointless. The indicator begins in a similar manner, with a larger intro, lines 10-26 . The intro has a much wider number of settings compared to MQL4. Then follow the parameters, lines 29-34, and finally the buffers, lines 36-39. Note in lines 29-34 the replacement of **extern** by **input** , which takes its place in MQL5. **extern** still remains valid in MQL5, but it has a different role.

Until this moment it doesn’t seem too complicated, because code is similar to MQL4. But now it takes a complete different turn…

First, an include of the MovingAverages.mqh on line 41. This will be needed below, because the different moving averages calculations will be done by calling moving average routines residing in this lib. Then, on line 43, public declaration of **Standard Deviation indicator handle (only one, because we pick by moving average type)**. In MQL5, calls to indicators don’t return indicator values, as they were doing in MQL4. Rather, they return a handle. The handle is needed later to get indicator values, in a calls to **CopyBuffer()**. The **OnInit()**, spanning on lines 48-71, is similar again to MQL4 **init()** , at least in the 50-59 lines , where there are buffer assignations and draw begin setups. The **OnInit()** ends with calls to **iStdDev()** ; the indicator will be used inside the main loop, but the handle is generated once! Four calls to **iStdDev**, generating the unique handle for the chosen type of moving average required by MATYPE parameter.

The **OnCalculate()** event is practically the new master loop. This function has a kind of a mysterious prototype. Its implementation details are not fully known, so the function allows a pretty wide range of prototypes. The one I used in the indicator is the default one that you get when you create a new indicator. As opposed to MQL4, when index bars are descendant from *Bars* to 0, current bar, in MQL5, index bars are ascendant from *prev_calculated*-1 to *rates_total*-1.

Following the declarations of the variables, the loop begins in line 100. First thing to do, we get the current value of the standard deviation. Since we have the handler, we call the **CopyBuffer()** to retrive the value for the current position. The current position is the cycle variable *pos*. However, **CopyBuffer()** understands position as a position in a series, not as a position in an array. Therefore, we will copy 1 value from *rates-total-pos*, from the first series, 0. If the indicator would have returned more than one value, say the indicator would have returned more value, like a Heiken Ashi, for instance, that returns bar informations, we would have had more than one series, adressing series 0, 1, 2 and 3 to get values of Heiken Ashi bar prices. The values got with **CopyBuffer()** are put in an array. But we need one number, so the array where we put the data , *res* will have only one value, res[0], which is copied to our first buffer, ExtIndexBuffer1, and then logarithming is applied, according to Logarithm parameter, lines 106-109 (now the decimal logarithm is included too, besides the natural).

What follows is pretty easy to follow. We count if we have enough values to start calculating the Bollinger Bands over the standard deviation. When there is enough data , (“if” branch line 110), we go back BBLEN values and calculate the average, then we do again same loop and calculate the standard deviation over the standard deviation, then finally Bollinger Bands , lines 124-125.

Same as before, but with the condition of having at least one standard deviation over standard deviation calculated, we check to see if we have MALEN values of these, to compute the first helper moving average. But unlike previous time, we copy values of initial standard deviation in a buffer. Buffer will span from MALEN down to 1, with larger indexes for older values. The reason to do it is that we will pass this buffer as a parameter to moving average methods taken from MovingAverages.mqh, and their inner implementation is passing this way thru the arrays. These are simple functions. Unlike indicators, their results are answers of inner calculations, not handlers. And their results are written in the buffer for the helper moving average, ExtMapBuffer2. With the calculation of the helper moving average, all calculations are done and the loop closes.

**Volatility forecasting**

This is why I included in the indicator a helper moving average: to help forecast volatility. I would stress, however, that some key requirements to be met before attempting to do so:

**1. The BB band channel should be below 2% from historical standard deviation range – **this will insure that the signal does not happen accidentally when BB bands are already at distance.

**2. Current standard deviation should be below 5% from historical standard deviation high – **this will insure that there is a high chance of an explosive volatility growth after the signal.

The normal signal should be the cross of the upper BB band. The exit signal should be determining a weakening in volatility speed. And that happens when volatility tops while BB upper band still going up. To determine this quick, use a quick helper moving average – with a period of 2 . Once that crosses the volatility, you can already consider the exit signal. But it can be determined even quicker, when there is a too large distance between the higher BB band and the standard deviation. One of the best interpretations of this chart would be by converting data – BB bands, standard deviation, moving average etc. to their graphics counterparts – point coordinates on chart. This might tell you for instance when the standard deviation *touches* the upper BB band.

Provided that you calculate the upper and lower BB for a fixed number of bars maybe **WindowBarsPerChart() **that gives the number of bars of the chart where your expert advisor or script runs, or a given number of bars – and you extract the highest upper BB and lowest lower BB, let’s name them *bbmax *and *bbmin* – you can calculate a virtual graphics position of any value (be it a BB, standard deviation or helper moving average), using a given screen *height *(say 300 virtual “pixels”). Let’s call this one *testvalue*. So the *position *of the testvalue would be:

double position=MathRound( height*(testvalue-bbmin)/bbmax );

This way, a composite condition would be, after conditions 1. and 2. are fulfilled, that there is either a cross *or *a touch. Exit criteria would be : standard deviation is below upper BB and there is no touch anymore. However, at the first sign of weaking volatility, the exit signal will be triggered, and that might happen too early. It would be good to backcheck the touch status using a lower resolution.

There are two possibly situations that contribute to standard deviation growth**: either prices are going either up or down in an accelerated fashion, **or** prices change direction swiftly from up to down and viceversa with a growing amplitude. **** **

If the volatility kept going up for the last bars , we might presume first condition : that prices are trending. In this case, we can just check the last 2-3 opening prices to see the direction and trade it, if it’s established. **But, no matter if direction is established or not, **the volatility can still go up. Because volatility is at historically low when we take the signal, and tiny increases mean just the aggitation is growing up, not that the direction is established. Price fluctuations may follow an up-down pattern with **growing amplitude**, until direction becomes established. However, volatility is still low. Being low, it means that **prices don’t move too much – we can afford to lose**. No matter which direction we trade, we check at every new bar if we trade it in the good direction. If not, cut loss and trade again. Because if the signal is correct and volatility is going to traverse 30% to 70% of its historical range, we’re in for big bucks!

Volatility forecast is pretty straightforward, and is more manual than automated**. **Since we expect volatility to be going thru the roof, we can do a linear or parabolic forecast. The **linear model** is simple. If we have the current standard deviation on bar 0 (*s0*) and the expected standard deviation for bar m (*sm*)*, *we have forecasted standard deviation per bar f(bar)=so+(sm-so)*bar/m.

The **parabolic model** is harder, because it needs an extra point to define the curvature. This is solvable with an equation system. Given three points (b1,stddev1) , (b2,stddev2) , (b3,stddev3) where b1, b2, b3 are bar indexes and stddev1,stddev2,stddev3 are estimated standard deviations :

Once we have the parameters of the quadratic function that describes the standard deviation according to estimated points, we can generate the standard deviation per each bar, see MQL4 script:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 | double Determinant(double &origa[][],int l1,int c1,int l2,int c2,int jumpline,int jumpcol) { double a[50,50]; int degree,i,j,ip,jp,dets; double result=0; double reshere=0; double r; string image; int ipm,jpm; for (i=0;i<50;i++) { for (j=0;j<50;j++) a[i,j]=0.0; } if (l1==0) l1=1; if (c1==0) c1=1; degree=c2-c1+1; if (jumpcol!=0) degree=degree-1; ip=1; for (i=l1;i<=l2;i++) { ipm=ip-1; jp=1; if (i!=jumpline) { for (j=c1;j<=c2;j++) { jpm=jp-1; if (j!=jumpcol) { r=origa[i-1,j-1]*1.0; a[ipm,jpm]=r; jp=jp+1; } }//for (j=c1;j<=c2;j++c2) ip=ip+1; }//if (i!=jumpline) }//for (i=l1;i<=l2;i++) result=0; if (degree==1) result=0; if (degree==2) result=a[1-1,1-1]*a[2-1,2-1]-a[2-1,1-1]*a[1-1,2-1]; if (degree==3) result=a[1-1,1-1] * a[2-1,2-1] * a[3-1,3-1] + a[1-1,2-1] * a[2-1,3-1] * a[3-1,1-1] + a[1-1,3-1] * a[2-1,1-1] * a[3-1,2-1] - a[1-1,3-1] * a[2-1,2-1] * a[3-1,1-1] - a[1-1,2-1] * a[2-1,1-1] * a[3-1,3-1] - a[1-1,1-1] * a[2-1,3-1] * a[3-1,2-1] ; if (degree>3) { for (dets=1;dets<=degree;dets++) { reshere=Determinant(origa,1,1,degree,degree,1,dets); result=result + MathPow(-1,1+dets)*origa[1-1,dets-1]*reshere; } } return(result); } void EstablishStddevFunction(int b1,double stddev1,int b2,double stddev2,int b3,double stddev3,double &a, double &b, double &c) { double d,da,db,dc; double barray[3]; double stddev[3]; double det_d[3][3]; double det_da[3][3]; double det_db[3][3]; double det_dc[3][3]; barray[0]=b1*1.0; barray[1]=b2*1.0; barray[2]=b3*1.0; stddev[0]=stddev1; stddev[1]=stddev2; stddev[2]=stddev3; det_d[0][0]=barray[0]*barray[0]; det_d[0][1]=barray[0]; det_d[0][2]=1; det_d[1][0]=barray[1]*barray[1]; det_d[1][1]=barray[1]; det_d[1][2]=1; det_d[2][0]=barray[2]*barray[2]; det_d[2][1]=barray[2]; det_d[2][2]=1; det_da[0][0]=stddev[0]; det_da[0][1]=barray[0]; det_da[0][2]=1; det_da[1][0]=stddev[1]; det_da[1][1]=barray[1]; det_da[1][2]=1; det_da[2][0]=stddev[2]; det_da[2][1]=barray[2]; det_da[2][2]=1; det_db[0][0]=barray[0]*barray[0]; det_db[0][1]=stddev[0]; det_db[0][2]=1; det_db[1][0]=barray[1]*barray[1]; det_db[1][1]=stddev[1]; det_db[1][2]=1; det_db[2][0]=barray[2]*barray[2]; det_db[2][1]=stddev[2]; det_db[2][2]=1; det_dc[0][0]=barray[0]*barray[0]; det_dc[0][1]=barray[0]; det_dc[0][2]=stddev[0]; det_dc[1][0]=barray[1]*barray[1]; det_dc[1][1]=barray[1]; det_dc[1][2]=stddev[1]; det_dc[2][0]=barray[2]*barray[2]; det_dc[2][1]=barray[2]; det_dc[2][2]=stddev[2]; d=Determinant(det_d,1,1,3,3,0,0); da=Determinant(det_da,1,1,3,3,0,0); db=Determinant(det_db,1,1,3,3,0,0); dc=Determinant(det_dc,1,1,3,3,0,0); a=da/d; b=db/d; c=dc/d; } //+------------------------------------------------------------------+ //| script program start function | //+------------------------------------------------------------------+ int start() { double aa,bb,cc; Print("Standard Deviation parabolic forecasting example"); EstablishStddevFunction(0,0.003,3,0.006,9,0.1,aa,bb,cc); Print("Forecasted standard deviations"); for (int bar=0;bar<10;bar++) { Print("bar ",DoubleToStr(bar,0)," stddev=",DoubleToStr(aa*bar*bar+bb*bar+cc,4)); } return(0); } //+------------------------------------------------------------------+ |

Note that the Determinant function has a recursive application, up to the degree 3, where Sarrus rule is applied. Thus you can use this function to calculate n-degree determinants – but be careful on time and memory allocation requirements. An MQL5 prototype of this function will look like this:

double Determinant(double &origa[][50],ushort l1,ushort c1,ushort l2,ushort c2,ushort jumpline,ushort jumpcol)

Note that the second dimension is fixed at 50. MQL5 does not support unspecified dimensions larger than one.

All of these might sound good, but don’t forget that *we don’t trade volatility.* We don’t have a price direction signal.

But the **price direction can be extracted from volatility evolution.**

**Price forecasting from volatility forecast**

We have to consider that at every new bar, a price goes out of analysis and another price comes in. If we have *oldest *for oldest price and *newest *for the incoming price, we have:

New Average = Old Average – Oldest member/N + Newest member/N ;

Now, given that we plotted already the following standard deviations, the question is, which is the *newest *price that will give the new standard deviation in the queue?

So we take the estimated new standard deviation, according to the model, and we square it : thus we have the variance. Multiplying the variance by N (number of prices in the analysis), we start shaping up the equation for the newest member. Taking into account *n *elements, from *P1 (*the element thrown out*) *to *Pn* (the last element), *Pn+1 *(the new price that has to be determined) and the current average, we form the standard deviation equation, by squaring the requested standard deviation and multiplying it by n, which yields the sum of squares *Pi* – new average* , *which is computed as above, cutting out the *P1* influence and adding *Pn+1* influence. We use as helper a variable called *avgc* , which is the average less the influence of P1: (*Needless to say, equation is written for the case standard deviation is calculated using a regular simple moving average)*

So what you can see is what it was expected. Every standard deviation appears as a result of a new price, which can be either up, or down, which are, of course, roots a quadratic equation. I grouped the elements in order to see the coefficients for free Pn+1, squared Pn+1 and free member. Given *m* forecasted standard deviations, we have 2^m forecasted prices final after *m* bars. Of course, these are not to be generated straight ; they form a tree ; so first calculation will yield two prices ; every one of them will enter the price based for a new calculation and so on. So we will have 2, 4, 8, 16…2^m prices until the end. Most likely, the price will not have an amplified ranging movement for all the m bars. Rather, we can expect a few bars to be like that, after that the direction will be established. It is good to keep the high and low prices per each calculated bar. We can be whipsawed at the beginning, during a few swings – and here the estimations will be useful to calculate possible losses – but after these swings price movements will be way larger than in the swing period. My advice is that this method to be used on longer timeframes – larger than four hours. It can work on shorter timeframes, but big volatility catches on longer timeframes can yield spectacular results.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | void CalculateStats(double &data[],int lastindex,double &avg, double &stddev) { double sum=0; int i; for (i=0;i<=lastindex;i++) { sum=sum+data[i]; } avg=sum/(lastindex+1); sum=0; for (i=0;i<=lastindex;i++) { sum=sum+(data[i]-avg)*(data[i]-avg); } stddev=MathSqrt(sum/(lastindex+1)); } void CalculateNextPrices(double &p[],int lastindex,double newstddev,double &newprice1,double &newprice2) { double avg,avgc,stddev,suma2pi,sumapi2,sumapimavgc,sumapimavgc2,delta,a,b,c; int n,i; CalculateStats(p,lastindex,avg,stddev); n=lastindex+1; avgc=avg-p[0]/n; for (i=1;i<=lastindex;i++) { suma2pi=suma2pi+(2*p[i]); sumapi2=sumapi2+(p[i]*p[i]); sumapimavgc=sumapimavgc+(p[i]-avgc); sumapimavgc2=sumapimavgc2+(p[i]-avgc)*(p[i]-avgc); } a=1-(1.0/n); b=(2.0*avgc/n)-(2.0/n)*sumapimavgc-2.0*avgc; c=sumapimavgc2+avgc*avgc-newstddev*newstddev*n; delta=b*b-4.0*a*c; newprice1=(-b-MathSqrt(delta))/(2.0*a); newprice2=(-b+MathSqrt(delta))/(2.0*a); } |

The first function calculates the statistical parameters : average and standard deviation. The second function calculates the new prices that match up the new standard deviation. Both arrays are zero-based. The *lastindex *parameter is the last subscript that contains data. Note the use of integer constants in formulas that calculate double variables. These constants must be written in a double fashion : 1.0 instead of 1, 2.0 instead of 2, and so on , otherwise results are error-prone.

Be careful when using parabollic standard deviation calculus. If the first chosen standard deviation is before parabola peak, the standard deviation got from the function may get to zero or negative, where real roots of the price quadratic function will be one or none – complex roots don’t have any meaning here!

Corrected an error affecting the MQL4 version of the indicator, on helper moving average calculus: replaced BBLEN with MALEN on lines 94,101,108 and 115.

Nice analysis Bogdan! Really good work

Corrected MQL5 version of indicator to make it work under Strategy Tester.

Modified MQL4 version of indicator, switched BBMULT type to double.