The Volatility of Volatility for Stocks and Crypto, Part 3

Partly controllable volatility persistence

NTTP
12 min readMay 17, 2024
Part three. Photo by Elena G on Unsplash

Partly? Why not wholly? Because this is still a stochastic model, that’s why… where random occurrences can change your situation entirely. As in real life!

Editor’s note May 20 2024: While working on Part 4 of this series, we realized that we might have been playing fast and loose with the vocab in some places here. We sometimes refer to “volatility” when we should say “return variance” (= volatility²). Volatility = SQRT(variance) as per usual statistics terminology.

We show in the following image (reproduced from our last article) where we left off our discussion. As a reminder, we are working on the 2nd equation of the Heston stochvol model, with some items crossed out:

We will only be considering Heston eqn 2 in this article. In the next article, we will put the results of this work back into eqn 1.

We are going to gradually add these crossed off items in Heston Equation 2 back into our Excel-based variable volatility formulation.

The Excel file for this article is here:

https://github.com/diffent/excel/blob/main/HE2.xlsx

For numerical simulation (e.g. Excel), [Quantstart] [QS here for short] suggests to use a discrete recurrence relation for what we call Heston equation two (HE2), and it is kind of a “doozy”:

Figure Q (and words) from [Quantstart]: Note that this shows the next volatility at (i+1) is a function of the prior volatility (i subscripts)… except for the pesky i+1 subscript on the ΔW value at the end. Hmm. What to think about that last (i+1)?

Here we write ξ, the constant coefficient of the final term, as “xi,” its letter name, with xi representing the volatility of volatility which we reviewed in the prior articles in this series.

A simpler recurrence relation, ignoring that last i+1 on ΔW is:

v(i+1) = f(v(i)) [Equation 1]

This recurrence interpretation is what made the concept “pop” in our mind when we studied it while prepping to write the code for Heston-inspired models in our drift-diffusion price range modeling apps.

The recurrence relation merely means that the current value of something is functionally related to the immediate prior value of that same something. In our case, the something will be volatility (standard deviation of returns). The current volatility will not only be related to the prior value of itself… additional randomness will be injected along the way.

We are a little bit worried about that ΔW at i+1 being also on the right side of this relation; but as we will see later, and as the description above notes, ΔW is in essence a random generator, so… is i+1 so different from i when we are generating random numbers? Hmm. We may need to dig into the Quantstart article more to clarify this for our readers if we can. There seems to be a bit of hand-waving in the above description saying that ΔW(i+1) can be replaced by a standard normal distribution, but let’s move on for now and see if these interpretations make sense.

Plotting the recurrence relation by itself over time

To show what is going on here in this recurrence relation before we start injecting randomness, let’s just look at the first two terms of the right side of the = sign where there is no randomness involved. The Δt we will set to 1 (it doesn’t appear here):

And let’s even not worry about the v+ business yet (which is a max(v, 0) function if you look into that Quantstart article). We then get:

v(i+1) = v(i) + kappa*(theta - v(i))

Kappa is just a constant, so we can write it as k, with theta (also a constant) written as T:

v(i+1) = v(i) + k*(T - v(i)) [Equation 2]

Pretty simple, right? Machine learning, this is not. Or: Is it a simple form of ML? We will let you mull that rhetorical distinction. Nothing is being learned yet; we are just setting up the formulation for later learning.

The max( ) function is just to clip or truncate the volatility so it doesn’t go below zero; that would be non-physical. We can’t have an actual standard deviation that is less than zero in our ordinary price-and-return domain. We will add this max function back in before we start modeling returns for real.

[Equation 2] is just a simple machine or filter of an “attractor” sort. It tries to push the output value back towards a long-term constant value over time. Theta is the long term constant target in this case. How fast the value gets pushed back to the long term stable/target value depends upon the particular value of k that we use. We will show that if we set k close to zero and if the volatility gets shocked or bumped far from its long term mean value, it may stay there for a while… which is sometimes what we see in real life in these types of assets.

We set up a new Excel sheet to demonstrate this idea (link above).

To make things simple to start, we set the long term value that we want to center around (theta or T) to 1, and we set an initial condition v(0) of 1.5.

Since this is a recurrence relation, we need to define what v(0) is, since v(1) is computed from v(0). If we don’t have v(0), we are out of luck before we even start. Once we have v(1), we can compute all other v(.) values forward in time.

If we set this concept up in Excel and plot it, we can see that the initial “outlier” value of 1.5 gets pulled smoothly toward the long term target when k is in the range 0 < k < 1, since we have not disrupted the signal with any other inputs.

Kappa (k) = 0.6. Long term target constant theta = 1.

The closer to 1 we set k , the quicker this formulation will try to push the signal back towards the long term constant.

kappa = 0.9 converges to the long term constant quicker than prior example

This relation models a simple control system which moves a wandering signal towards its target constant value over time. Here is another example plot generated from when k values are in this “nice” range (0,1):

kappa = 0.1 (even slower convergence to 1)

For another trial, let’s set k to be greater than 1:

kappa 1.5 results in overshoot of target 1 and decaying oscillation around 1

This will cause the function value to overshoot the target and then oscillate back and overshoot the other way, and so on alternately as the value approaches closer to the target over time: A decaying oscillator. This is not necessarily a bad model of some phenomena. We are not sure if this oscillating-towards-a-constant volatility will be useful for modeling asset volatility, but we shall see.

Keep the behavior of this simple non-random relation in mind as we start to add the remaining parts of this “Heston equation 2” to the spreadsheet formulas.

Time dependency

This deterministic portion of HE2 adds time dependency to our volatility model. Instead of each day’s volatility being independent of all prior days’ volatility (like each roll of a pair of dice is independent of all prior rolls), each day’s volatility will now be influenced, somewhat, by the prior day’s volatility. Since this happens recursively, random volatility shocks from one or more days ago will still have an effect on “today’s” volatility, as we demonstrate in our above Excel examples. How much effect these prior day events have on results is controlled by k (kappa in the real HE2).

Instability possibility

Note: some values of kappa (C1 in our Excel) will cause the time series to “blow up” or increase without bounds as time goes on. Let’s try kappa 2.5:

Figure E: kappa = 2.5, no convergence, unstable control system

Instead of approaching the target value, the time series diverges farther and farther from it. Hence, it is suspicious to us when Quantstart is arriving at a kappa of over 6. Hmm… See below, the black screenshots.

Values of kappa that cause this divergence will not likely be useful in our model. These are what are sometimes called unstable systems, where error or noise is magnified instead of damped or reduced. The function value gets farther from the target over time instead of closer to it, though it may swing wildly past the target on occasion.

More model details, and some puzzling unit issues

Now that we have the first two terms demonstrated to be fairly straightforward to understand, we can proceed to the remaining pieces.

Another key to implementing this method is in the Quantstart description involving Δt (the time step) [see above Figure Q].

From our interpretation of this, it seems that if we assume Δt (time step) of 1 day (1 in our formula), it suggests that we can use a standard normal generator directly in place of the ΔW term, since then sqrt(Δt) = 1 in the last line of Figure Q.

N(0,1) in our Excel formulation is NORMINV(RAND( ), 0, 1). If Δt is 1 (day) then the coefficient of this N function would be 1 since SQRT(1) is 1 (not to be all Cap’n Obvious about it)

This may require some more thought versus the Quantstart code, because it looks like they are using a total time period measured in years (1 year in their example) and then they are dividing by the number of time steps to get a fraction less than 1.0 for Δt:

Notable constants in the Quantstart implementation. Theta, the long term volatility, seems to be a daily volatility, right? For a relatively high volatility asset (almost 2% daily vol).
More notable constants. The risk free rate r seems to be a yearly rate, right? About 3% / year. Initial volatility seems to be a daily number. We won’t need the risk free rate yet, as that is for computing options prices which the QS example does, and which we still have a few steps to go before we can do that.
Details of how the volatility path is computed in the QS code. We annotate to keep you from getting lost in the C++. Since dt is just a constant within the path generation loop, it seems that dt and sqrt(dt) just scale what kappa and xi need to be to get the same answer as we do. So this seems equivalent in concept to our Excel version. Verification TBD.

Of course they are also using a huge kappa value of > 6, which in our formulation would result in unstable “blowing up” of the function values (see Figure E above). This is speculation here, but the large kappa may be accounting for the unit discrepancies that we think we see in the QS code versus our code: To be determined. For example if their Δt (timestep) is << 1 (which it is), this then reduces the effective kappa multiplier versus our formulation with Δt = 1:

Since we assume Δt = 1, and QS code sets Δt << 1, this could account for their kappa >> 1

A Matlab ref suggests kappa values closer to 0 (0.003 in this example) implying very sticky volatility:

https://www.mathworks.com/help/fininst/finmodel.heston.html#d126e439657

But other references we saw online also suggest values of kappa > 1. So we will make sure that we take this piece of the model “under advisement,” as the saying goes.

However, in this Excel here, we are always measuring time in days and using daily returns… so… let’s leave this Δt business as an open issue for now, as we are trying to get a coarse model that mimics how some generic asset might perform over time. We are not yet tuning the model to real data. When you are working with analytic equations, you can perform a certain amount of hand waving about units [“implementation detail”?], but when you are writing code and actually implementing — even if on a simple spreadsheet — you need to keep units consistent.

Other unit notes

Our long term theta in this example of 1 implies 1%. In our app MCarloRisk3D [ref], we display this 1% as 0.01. We just used 1 for 1% here to make the Excel formulas simpler to understand at the start. Similarly for volatility of volatility and other units in this intro example.

Some kind of hand waving. Photo by Aziz Acharki on Unsplash

But we see another deviation from what we have described in our earlier articles in this series: Instead of controlling the standard deviation directly in our NORMINV(RAND( ), mean, stdev) formulation, QS is suggesting to use a stdnorm distribution externally scaled by the xi factor:

xi*NORMINV(RAND( ), 0, 1) [this]

NORMINV(RAND( ), 0, xi) [versus this from our prior articles]

This is not to say that putting xi as the standard deviation is necessarily a bad idea. It just isn’t what the Heston model is doing. It is an alternate model.

Along with this change, we will also include (finally, if you have read our prior articles on this) the square root of the prior volatility v(i), along with this constant xi factor as a multiplier of the standard normal “buzz” generator. This prior volatility will be truncated to be >= 0 in case it wanders into the negative zone. So the final term of the above recurrence relation becomes, in Excel pseudocode, not yet referencing sheet cells:

xi * SQRT(MAX(v(i), 0)) * NORMINV(RAND( ), 0, 1)

The formula

Okay: We have enough now to try setting up the whole HE2 (the QS recurrence relation version of it) in column B of our sheet.

For B1 (initial condition) we will just set it to theta (the long term constant that the volatility level will “buzz” around). Theta = 1 in our example. So B1 is just a reference to D1 (our constant theta). This initial condition may require more thought as we tune the model to real data. But then again, it may not matter so much, since, as we will show, the volatility rapidly wanders around due to the random generator.

The remainder of the equation is complicated enough that we will diagram the mapping to Excel directly:

We didn’t draw a line for it, but we are using the final NORMINV function in place of the final ΔW. Eagle eyed readers will recall that in our past articles, we centered the vol-of-vol buzz around mean 1 instead of 0. But the first part of this equation (the theta) does this centering for us here. This change should become more clear when we integrate this formulation with the return formulation in our prior articles.

Results of all this will be in column B.

Now we can make some plots of this column B, this recurrence relation that computes volatility, at the same and at different values of kappa (the stickiness factor) and xi (the volatility of volatility). Theta is just the long term constant, so leaving it set to 1.0 is good enough for now.

Figure V: Lower right plot is the final volatility path from column B. Vert axis interpreted as percent e.g. 1 = 1% daily volatility. This is for kappa probably unrealistically close to 0, and so we see periods of high volatility and periods of low almost zero volatility. Maybe some less than zero volatility (cough). “Implementation detail.”
Now with kappa = 0.1, we see some more oscillation of the volatility level (bottom right plot). But you can see that sometimes the vol is relative high, sometimes relatively low.
With kappa = 0.5 the volatility tends to stay around it’s target long term mean of 1, but does wander up and down.
Keeping kappa at 0.5 and reducing xi (the vol of vol) to 0.1 results in even less movement from the long term mean value of 1. You can experiment with various kappa and xi values in this sheet to get an intuitive feel for what is going on in this part of the model.

Since this is a random generating process, every time we refresh the sheet — even if we don’t change any parameter settings — we will get a different plot:

kappa = 0.05 and xi = 0.2, refresh 1
kappa = 0.05 and xi = 0.2, refresh 2 … different volatility path, lower right

Observe that sometimes our volatility goes below zero, mainly when the volatility of volatility (xi) is large with respect to the the long term volatility target (theta), and we set kappa close to zero. [See Figure V above]. Not good. We will fix this when we integrate this piece into the main model of returns which we started developing in our previous articles.

Discussion

With values of kappa closer to 0, we get time periods where high volatility sticks in place for a while (and with similarly stickiness for mid or low volatility periods). As we increase kappa closer to 1, the periods of time that volatility stays high or low (or at some middle-ground fairly constant level) are reduced in time width, or become non-existent or difficult to determine.

Once again we pause…

With that, we let you experiment with this sheet until we get a chance to work on our next article, which will tie this Heston Equation 2 back into the main return generator equation (HE1) and hopefully resolve some of these formulation discrepancies that we are noticing versus Quantstart, because:

Regarding what Δt should be… we have questions. This may become more clear when we integrate the HE2 formula with our prior work.

However, you can see from the above Excel plots that our volatility level charts seem to be behaving in reasonable ways as we adjust the parameters of the model (mainly kappa and xi in these examples, since theta just controls the long term constant that the volatility wants to settle to — the long term mean — but never does). You can change theta also in your experimentation with this sheet.

Further reading

Part 4 of this series is now online:

https://medium.com/@nttp/the-volatility-of-volatility-part-4-fb168534c89c

Reference

[Quantstart] Heston Stochastic Volatility Model with Euler Discretisation in C++, https://www.quantstart.com/articles/Heston-Stochastic-Volatility-Model-with-Euler-Discretisation-in-C/

--

--