The Volatility of Volatility for Stocks and Crypto, Part 3
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 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”:
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.
The closer to 1 we set k , the quicker this formulation will try to push the signal back towards the long term constant.
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):
For another trial, let’s set k to be greater than 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:
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.
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:
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:
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.
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:
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.
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:
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/