5 A flexible derivative pricing model
The model we consider in this section is the Bakshi-Cao- (BCC) model from “Empirical Performance of Alternative Option Pricing Models” in The Journal of Finance, Vol. 52, No. 5 (Dec., 1997), pp. 2003-2049.
The BCC model is characterized by a jump-diffusion model of asset price, with a square-root diffusion process for stochastic volatility, and a Cox-Ingersoll-Ross (CIR) for the stochastic short-term interest rate. Formally, the model may be expressed as follows: \[\begin{align*} dS_t &= (r_t - r_J)S_tdt + \sqrt{v_t}S_tdZ_t^1 + J_tS_tdN_t\\ dv_t &= \kappa_v(\theta_v - v_t)dt + \sigma_v\sqrt{v_t}dZ_t^2\\ dr_t &= \kappa_r(\theta_r - r_t)dt + \sigma_r\sqrt{r_t}dZ_t^3. \end{align*}\]
The parameters in the above model have the following meanings: This model is inspired by several real-world observations, including the following:5.1 Present-value with stochastic short-term rate
Under continuous compounding the present value at time \(t\) of a dollar paid at time \(T\) is \[B_0(T) = E_t^Q\left(\exp\left\{-\int_t^T r_udu\right\}\right).\] An advantage of the CIR model is that the above discounting has a closed-form: \[\begin{align*} B_t(T) &= b_1(T)e^{-b_2(T)r_0}\\ b_1(T) &= \left(\frac{2\gamma \exp((\kappa_r+\gamma)T/2)}{2\gamma + (\kappa_r + \gamma)(e^{\gamma T}-1)}\right)^{\frac{2\kappa_r\theta_r}{\sigma_r^2}}\\ b_2(T) &= \frac{2(e^{\gamma T}-1)}{2\gamma + (\kappa_r + \gamma)(e^{\gamma T}-1)}\\ \gamma = \sqrt{\kappa_r^2 + 2\sigma_r^2} \end{align*}\]
The following python codes implement discounting under the CIR model.
import math
import numpy as np
= 0.3, 0.04, 0.1, 0.04, 1.0
kappa_r, theta_r, sigma_r, r0, T
def gamma(kappa_r, sigma_r):
''' Help Function. '''
return math.sqrt(kappa_r ** 2 + 2 * sigma_r ** 2)
def b1(alpha):
''' Help Function. '''
= alpha
kappa_r, theta_r, sigma_r, r0, T = gamma(kappa_r, sigma_r)
g return (((2 * g * math.exp((kappa_r + g) * T / 2)) /(2 * g + (kappa_r + g) * (math.exp(g * T) - 1)))** (2 * kappa_r * theta_r / sigma_r ** 2))
def b2(alpha):
''' Help Function. '''
= alpha
kappa_r, theta_r, sigma_r, r0, T = gamma(kappa_r, sigma_r)
g return ((2 * (math.exp(g * T) - 1)) /(2 * g + (kappa_r + g) * (math.exp(g * T) - 1)))
def B(alpha):
''' Function to value unit zero-coupon bonds in Cox-Ingersoll-Ross (1985)
model.
Parameters
==========
r0: float
initial short rate
kappa_r: float
mean-reversion factor
theta_r: float
long-run mean of short rate
sigma_r: float
volatility of short rate
T: float
time horizon/interval
Returns
=======
zcb_value: float
zero-coupon bond present value
'''
= b1(alpha)
b_1 = b2(alpha)
b_2 = alpha
kappa_r, theta_r, sigma_r, r0, T return b_1 * math.exp(-b_2 * r0)
if __name__ == '__main__':
#
# Example Valuation
#
= B([kappa_r, theta_r, sigma_r, r0, T])
B0T # discount factor, ZCB value
print("ZCB Value %10.4f" % B0T)
## ZCB Value 0.9608
5.2 Evaluating a European option via Fourier transform
import numpy as np
from scipy.integrate import quad
import warnings
'ignore')
warnings.simplefilter(
#
# Example Parameters B96 Model
#
## H93 Parameters
= 1.5
kappa_v = 0.02
theta_v = 0.15
sigma_v = 0.1
rho = 0.01
v0 ## M76 Parameters
= 0.25
lamb = -0.2
mu = 0.1
delta = np.sqrt(v0)
sigma ## General Parameters
= 100.0
S0 = 100.0
K = 1.0
T = 0.05
r
def BCC_call_value(S0, K, T, r, kappa_v, theta_v, sigma_v, rho, v0, lamb, mu, delta):
''' Valuation of European call option in B96 Model via Lewis (2001)
Fourier-based approach.
Parameters
==========
S0: float
initial stock/index level
K: float
strike price
T: float
time-to-maturity (for t=0)
r: float
constant risk-free short rate
kappa_v: float
mean-reversion factor
theta_v: float
long-run mean of variance
sigma_v: float
volatility of variance
rho: float
correlation between variance and stock/index level
v0: float
initial level of variance
lamb: float
jump intensity
mu: float
expected jump size
delta: float
standard deviation of jump
Returns
=======
call_value: float
present value of European call option
'''
= quad(lambda u: BCC_int_func(u, S0, K, T, r, kappa_v, theta_v,sigma_v, rho, v0, lamb, mu, delta), 0, np.inf,limit=250)[0]
int_value = max(0, S0 - np.exp(-r * T) * np.sqrt(S0 * K)/ np.pi * int_value)
call_value return call_value
def H93_call_value(S0, K, T, r, kappa_v, theta_v, sigma_v, rho, v0):
''' Valuation of European call option in H93 model via Lewis (2001)
Fourier-based approach.
Parameters
==========
S0: float
initial stock/index level
K: float
strike price
T: float
time-to-maturity (for t=0)
r: float
constant risk-free short rate
kappa_v: float
mean-reversion factor
theta_v: float
long-run mean of variance
sigma_v: float
volatility of variance
rho: float
correlation between variance and stock/index level
v0: float
initial level of variance
Returns
=======
call_value: float
present value of European call option
'''
= quad(lambda u: H93_int_func(u, S0, K, T, r, kappa_v, theta_v, sigma_v, rho, v0), 0, np.inf, limit=250)[0]
int_value = max(0, S0 - np.exp(-r * T) * np.sqrt(S0 * K)/ np.pi * int_value)
call_value return call_value
def M76_call_value(S0, K, T, r, v0, lamb, mu, delta):
''' Valuation of European call option in M76 model via Lewis (2001)
Fourier-based approach.
Parameters
==========
S0: float
initial stock/index level
K: float
strike price
T: float
time-to-maturity (for t=0)
r: float
constant risk-free short rate
lamb: float
jump intensity
mu: float
expected jump size
delta: float
standard deviation of jump
Returns
=======
call_value: float
present value of European call option
'''
= np.sqrt(v0)
sigma = quad(lambda u: M76_int_func_sa(u, S0, K, T, r,
int_value 0, np.inf, limit=250)[0]
sigma, lamb, mu, delta), = max(0, S0 - np.exp(-r * T) * np.sqrt(S0 * K)/ np.pi * int_value)
call_value return call_value
def BCC_int_func(u, S0, K, T, r, kappa_v, theta_v, sigma_v, rho, v0,lamb, mu, delta):
''' Valuation of European call option in BCC97 model via Lewis (2001)
Fourier-based approach: integration function.
Parameter definitions see function BCC_call_value.'''
= BCC_char_func(u - 1j * 0.5, T, r, kappa_v, theta_v, sigma_v, rho, v0, lamb, mu, delta)
char_func_value = 1 / (u ** 2 + 0.25) * (np.exp(1j * u * np.log(S0 / K)) * char_func_value).real
int_func_value return int_func_value
def H93_int_func(u, S0, K, T, r, kappa_v, theta_v, sigma_v, rho, v0):
''' Valuation of European call option in H93 model via Lewis (2001)
Fourier-based approach: integration function.
Parameter definitions see function H93_call_value.'''
= H93_char_func(u - 1j * 0.5, T, r, kappa_v, theta_v, sigma_v, rho, v0)
char_func_value = 1 / (u ** 2 + 0.25) * (np.exp(1j * u * np.log(S0 / K)) * char_func_value).real
int_func_value return int_func_value
def M76_int_func_sa(u, S0, K, T, r, sigma, lamb, mu, delta):
''' Valuation of European call option in M76 model via Lewis (2001)
Fourier-based approach: integration function.
Parameter definitions see function M76_call_value.'''
= M76_char_func_sa(u - 0.5 * 1j, T, r, sigma, lamb, mu, delta)
char_func_value = 1 / (u ** 2 + 0.25) * (np.exp(1j * u * np.log(S0 / K)) * char_func_value).real
int_func_value return int_func_value
def BCC_char_func(u, T, r, kappa_v, theta_v, sigma_v, rho, v0,lamb, mu, delta):
''' Valuation of European call option in BCC97 model via Lewis (2001)
Fourier-based approach: characteristic function.
Parameter definitions see function BCC_call_value.'''
= H93_char_func(u, T, r, kappa_v, theta_v, sigma_v, rho, v0)
BCC1 = M76_char_func(u, T, lamb, mu, delta)
BCC2 return BCC1 * BCC2
def H93_char_func(u, T, r, kappa_v, theta_v, sigma_v, rho, v0):
''' Valuation of European call option in H93 model via Lewis (2001)
Fourier-based approach: characteristic function.
Parameter definitions see function BCC_call_value.'''
= kappa_v * theta_v
c1 = -np.sqrt((rho * sigma_v * u * 1j - kappa_v)** 2 - sigma_v ** 2 * (-u * 1j - u ** 2))
c2 = (kappa_v - rho * sigma_v * u * 1j + c2) / (kappa_v - rho * sigma_v * u * 1j - c2)
c3 = (r * u * 1j * T + (c1 / sigma_v ** 2)* ((kappa_v - rho * sigma_v * u * 1j + c2) * T- 2 * np.log((1 - c3 * np.exp(c2 * T)) / (1 - c3))))
H1 = ((kappa_v - rho * sigma_v * u * 1j + c2) / sigma_v ** 2* ((1 - np.exp(c2 * T)) / (1 - c3 * np.exp(c2 * T))))
H2 = np.exp(H1 + H2 * v0)
char_func_value return char_func_value
def M76_char_func(u, T, lamb, mu, delta):
''' Valuation of European call option in M76 model via Lewis (2001)
Fourier-based approach: characteristic function.
Parameter definitions see function M76_call_value.'''
= -lamb * (np.exp(mu + 0.5 * delta ** 2) - 1)
omega = np.exp((1j * u * omega + lamb * (np.exp(1j * u * mu - u ** 2 * delta ** 2 * 0.5) - 1)) * T)
char_func_value return char_func_value
def M76_char_func_sa(u, T, r, sigma, lamb, mu, delta):
''' Valuation of European call option in M76 model via Lewis (2001)
Fourier-based approach: characteristic function "jump component".
Parameter definitions see function M76_call_value.'''
= r - 0.5 * sigma ** 2 - lamb * (np.exp(mu + 0.5 * delta ** 2) - 1)
omega = np.exp((1j * u * omega - 0.5 * u ** 2 * sigma ** 2+ lamb * (np.exp(1j * u * mu - u ** 2 * delta ** 2 * 0.5)- 1)) * T)
char_func_value return char_func_value
if __name__ == '__main__':
#
# Example Parameters CIR85 Model
#
= 0.3, 0.04, 0.1, 0.04, T
kappa_r, theta_r, sigma_r, r0, T = B([kappa_r, theta_r, sigma_r, r0, T]) # discount factor
B0T = -np.log(B0T) / T
r #
# Example Values
#
print("M76 Value %10.4f" % M76_call_value(S0, K, T, r, v0, lamb, mu, delta))
print("H93 Value %10.4f" % H93_call_value(S0, K, T, r, kappa_v, theta_v, sigma_v, rho, v0))
print("BCC97 Value %10.4f" % BCC_call_value(S0, K, T, r, kappa_v, theta_v,sigma_v, rho, v0, lamb, mu, delta))
## M76 Value 7.7611
## H93 Value 6.8672
## BCC97 Value 8.2942