- Published on
Local Volatility in PyQL
- Authors
- Name
- Kevin Givens
Following on my previous post, I wanted to review the important concept of Local Volatility. Many books and articles 1, 2 are dedicated to discussing this topic. I won't go into great detail here, I just want to give a basic overview along with implementation details in PyQL.
Essentially, the idea of Local Volatility is to identify a level-dependent diffusion that exactly reproduces market implied volatilities.
The contribution of Dupire was to prove that this diffusion is unique while also providing a convenient technique for deriving it from market quotes.
The full derivation of the local volatility function is given in section 2.2 and the appendix of 2. Here, I'll just give a sketch of the approach.
To begin, consider a generic, level-dependent, 1-D Brownian motion
Note that, the volatility for this process is not itself stochastic. We can use this process to model the diffusion on a equity spot price in the presence of a short rate and a dividend yield . A European call option for this process then satisfies a modified version of the Black-Scholes equation:
We can simplify this equation by writing as a function of the forward price , (i.e. use the forward measure)
In these units, Black-Scholes equation simplifies to
or solving for the volatility gives Dupire's equation
Market quotes are usually given in terms of Black-Scholes implied volatilities. We can express the local volatility in terms of these quantities by equating the price equations for the two models
The strategy is then to solve for the local volatility in terms of the Black-Scholes impliedvolatility since we have a closed form expression for the . The full derivation is given in section 2.2 of 2 . Here we just reproduce the results.
Namely, using more convenient units, the Black-Scholes total variance
and log-moneyness
We can show that the local variance
satisfies the following expression:
Quantlib implements this equation in ql.termstructure.volatility.equityfx.localvolsurface
In the code excerpt below we, show the LocalVolSurface is used in PyQL. It follows a similiar interface and BlackVarianceSurface given in the previous post.
calc_date = Date(6, 11, 2015)
Settings().evaluation_date = calc_date
risk_free_rate = 0.01
dividend_rate = 0.0
day_count = Actual365Fixed()
calendar = UnitedStates()
flat_ts = FlatForward(calc_date, risk_free_rate, day_count)
dividend_ts = FlatForward(calc_date, dividend_rate, day_count)
expiration_dates = [
Date(6,12,2015),
Date(6,1,2016),
Date(6,2,2016),
Date(6,3,2016),
Date(6,4,2016)
]
strikes = [527.50, 560.46, 593.43, 626.40]
data = np.array(
[
[0.37819,0.34450,0.37419,0.37498,0.35941],
[0.34177,0.31769,0.35372,0.35847,0.34516],
[0.30394,0.29330,0.33729,0.34475,0.33296],
[0.27832,0.27614,0.32492,0.33399,0.32275]
]
)
vols = Matrix.from_ndarray(data)
black_var_surf = BlackVarianceSurface(
calc_date,
calendar,
expiration_dates,
strikes,
vols,
day_count
)
spot = 659.37
strike = 600.0
expiry = 0.2 # years
local_vol_surf = LocalVolSurface(
black_var_surf,
flat_ts,
dividend_ts,
spot
)
print("local vol: ", local_vol_surf.localVol(expiry, strike))
In the plots below, we can see what the LocalVolSurface looks like:
Take a look at the PyQL bindings on my github to see an example of the LocalVolSurface
. See you next time.