# Heston volatility surface in Python QuantLib

Does anyone have experience with the Python QuantLib function HestonBlackVolSurface? I'm trying to produce a 3D plot of the volatility surface as done in the example

http://gouthamanbalaraman.com/blog/volatility-smile-heston-model-calibration-quantlib-python.html

Here is my attempt, based on the data of the example

import QuantLib as quant
heston_vol_surface = quant.HestonBlackVolSurface(
quant.HestonModelHandle(model),
quant.AnalyticHestonEngine.Gatheral)

strikes_grid = np.arange(strikes[0], strikes[-1],10)
expiry = 1.0
implied_vols = [heston_vol_surface.blackVol(expiry, s)
for s in strikes_grid]


I understand that I need to pass the the volatility term structure, but my knowledge of QuantLib is too limited right now. Thanks for you help.

Here is something I did, maybe it helps:

• You should include the content from your link. It would be helpful for future viewers should the link break. – amdopt May 20 '20 at 13:21
• @Ege Yilmaz thanks, that really helped a lot! – FunnyBuzer May 21 '20 at 11:05

Here is a snip that will create and plot a Heston vol surface

import numpy as np
import QuantLib as ql
from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

# Utility function to plot vol surfaces (can pass in ql.BlackVarianceSurface objects too)
def plot_vol_surface(vol_surface, plot_years=np.arange(0.1, 2, 0.1), plot_strikes=np.arange(80, 120, 1)):
fig = plt.figure()
ax = fig.gca(projection='3d')

X, Y = np.meshgrid(plot_strikes, plot_years)
Z = np.array([vol_surface.blackVol(float(y), float(x))
for xr, yr in zip(X, Y)
for x, y in zip(xr,yr) ]
).reshape(len(X), len(X[0]))

surf = ax.plot_surface(X,Y,Z, rstride=1, cstride=1, linewidth=0.1)

fig.colorbar(surf, shrink=0.5, aspect=5)

# World State setup
spot = 100
rate = 0.0
today = ql.Date(1, 7, 2020)

# Set up the flat risk-free curves
riskFreeCurve = ql.FlatForward(today, rate, ql.Actual365Fixed())
flat_ts = ql.YieldTermStructureHandle(riskFreeCurve)
dividend_ts = ql.YieldTermStructureHandle(riskFreeCurve)

# Setting up a Heston model with dummy parameters (roughly 10% constant BS vol)
v0 = 0.01; kappa = 0.01; theta = 0.01; rho = 0.0; sigma = 0.01

process = ql.HestonProcess(flat_ts, dividend_ts, ql.QuoteHandle(ql.SimpleQuote(spot)), v0, kappa, theta, sigma, rho)

# Boilerplate to get to the Vol Surface object
heston_model = ql.HestonModel(process)
heston_handle = ql.HestonModelHandle(heston_model)
heston_vol_surface = ql.HestonBlackVolSurface(heston_handle)

# Plot the vol surface ...
plot_vol_surface(heston_vol_surface)


Produces:

• thanks for sharing your code! I noticed that the Feller condition is not guaranteed to hold when performing the calibration. Are you aware of any way to impose it when calibrating the parameters? – FunnyBuzer Oct 6 '20 at 13:06
• Good question. It depends how you calibrate. The most common calibration method for Heston is least-squares via the Levenberg-Marquardt method. Many other techniques are possible, several are discussed in this excellent blog post: gouthamanbalaraman.com/blog/…. In particular, you could very easily include a penalty term for Feller in any of the cost functions, which would encourage your fitter to keep it positive (at the cost of a less-good fit to observed vanilla prices) – StackG Oct 6 '20 at 14:00