Valuing European Option Using the Heston Model in QuantLib

Gouthaman Balaraman

Heston model can be used to value options by modeling the underlying asset such as the stock of a company. The one major feature of the Heston model is that it inocrporates a stochastic volatility term.

\begin{eqnarray} dS_t &=& \mu S_tdt + \sqrt{V_t} S_t dW_t^1 \\ dV_t &=& \kappa(\theta-V_t) + \sigma \sqrt{V_t} dW_t^2 \end{eqnarray}

Here :

  • $S_t$ is the asset's value at time $t$
  • $\mu$ is the expected growth rate of the log normal stock value
  • $V_t$ is the variance of the asset $S_t$
  • $W_t^1$ is the stochastic process governing the $S_t$ process
  • $\theta$ is the value of mean reversion for the variance $V_t$
  • $\kappa$ is the strengh of mean reversion
  • $\sigma$ is the volatility of volatility
  • $W_t^2$ is the stochastic process governing the $V_t$ process
  • The correlation between $W_t^1$ and $W_t^2$ is $\rho$

In contrast, the Black-Scholes-Merton process assumes that the volatility is constant.


In [1]:
# <!-- collapse=True -->
import QuantLib as ql
import matplotlib.pyplot as plt
import numpy as np
from scipy.integrate import simps, cumtrapz, romb
% matplotlib inline
import math

Let us consider a European call option for AAPL with a strike price of \$130 maturing on 15th Jan, 2016. Let the spot price be \$127.62. The volatility of the underlying stock is know to be 20%, and has a dividend yield of 1.63%. We assume a short term risk free rate of 0.1%. Lets value this option as of 8th May, 2015.


In [2]:
# <!-- collapse=True -->
# option parameters
strike_price = 110.0
payoff = ql.PlainVanillaPayoff(ql.Option.Call, strike_price)

# option data
maturity_date = ql.Date(15, 1, 2016)
spot_price = 127.62
strike_price = 130
volatility = 0.20 # the historical vols for a year
dividend_rate =  0.0163
option_type = ql.Option.Call

risk_free_rate = 0.001
day_count = ql.Actual365Fixed()
calendar = ql.UnitedStates()

calculation_date = ql.Date(8, 5, 2015)
ql.Settings.instance().evaluationDate = calculation_date

Using the above inputs, we construct the European option as shown below.


In [3]:
# construct the European Option
payoff = ql.PlainVanillaPayoff(option_type, strike_price)
exercise = ql.EuropeanExercise(maturity_date)
european_option = ql.VanillaOption(payoff, exercise)

In order to price the option using the Heston model, we first create the Heston process. In order to create the Heston process, we use the parameter values: mean reversion strength kappa = 0.1, the spot variance v0 = volatility*volatility = 0.04, the mean reversion variance theta=v0, volatility of volatility sigma = 0.1 and the correlation between the asset price and its variance is rho = -0.75.


In [4]:
# <!-- collapse=True -->
# construct the Heston process

v0 = volatility*volatility  # spot variance
kappa = 0.1
theta = v0
sigma = 0.1
rho = -0.75

spot_handle = ql.QuoteHandle(
    ql.SimpleQuote(spot_price)
)
flat_ts = ql.YieldTermStructureHandle(
    ql.FlatForward(calculation_date, risk_free_rate, day_count)
)
dividend_yield = ql.YieldTermStructureHandle(
    ql.FlatForward(calculation_date, dividend_rate, day_count)
)
heston_process = ql.HestonProcess(flat_ts,
                                  dividend_yield,
                                  spot_handle,
                                  v0,
                                  kappa,
                                  theta,
                                  sigma,
                                  rho)

On valuing the option using the Heston model, we get the net present value as:


In [5]:
engine = ql.AnalyticHestonEngine(ql.HestonModel(heston_process),0.01, 1000)
european_option.setPricingEngine(engine)
h_price = european_option.NPV()
print "The Heston model price is",h_price


The Heston model price is 6.5308651372

Performing the same calculation using the Black-Scholes-Merton process, we get:


In [6]:
# <!-- collapse=True -->
flat_vol_ts = ql.BlackVolTermStructureHandle(
    ql.BlackConstantVol(calculation_date, calendar, volatility, day_count)
)
bsm_process = ql.BlackScholesMertonProcess(spot_handle, 
                                           dividend_yield, 
                                           flat_ts, 
                                           flat_vol_ts)
european_option.setPricingEngine(ql.AnalyticEuropeanEngine(bsm_process))
bs_price = european_option.NPV()
print "The Black-Scholes-Merton model price is ", bs_price


The Black-Scholes-Merton model price is  6.74927181246

The difference in the price between the two models is: bs_price - h_price = 0.21840667525992163. This difference is due to the stochastic modeling of the volatility as a CIR-process.