Bates
rl.SwitchingBatesModel(chain, S0, r, q, v0, kappa, theta, sigma, rho, jumpIntensity, logJumpMean, logJumpVol) is
QuantLib’s BatesModel with the long-run variance and the jump intensity allowed to switch.
The model
Heston’s variance with Merton’s jumps: with \(S_T = Fe^{X_T}\),
where \(N\) jumps at rate \(\ell_i\) in regime \(i\) by normal amounts \(J\) of mean \(\mu_J\) and standard deviation \(\delta\), and \(\bar k = e^{\mu_J + \delta^2/2} - 1\).
Reduction
A jump in the log price multiplies \(e^{iuX}\) by \(e^{iuJ}\) and leaves the variance alone, so the jump term of the pricing equation is a multiple of \(\phi_i\). The Heston form \(\phi_i = e^{D(t)v}a_i(t)\) works with the same \(D\) as on the Heston page, which involves neither \(\theta\) nor \(\ell\):
The first term vanishes at \(t = 0\) and the second does not.
Closed form
With \(I_1 = \int_0^TD\) and \(I_2 = \int_0^TD^2\) from the Heston page,
The middle term of \(\int\tilde g^{\,2}\) is the covariance of the variance level and the jump count, present because the turbulent regime raises both. It needs no new integral.
Python
S0, r, q, v0, kappa, theta, xi, rho, lam, T = 100.0, 0.0, 0.0, 0.04, 2.0, [0.09, 0.02], 0.4, -0.6, 10.0, 1.0
ell, muJ, delta = [2.0, 0.2], -0.08, 0.10
kbar = np.exp(muJ + delta ** 2 / 2) - 1
def heston_D(u):
b = kappa - rho * xi * 1j * u
d = np.sqrt(b * b + xi * xi * (u * u + 1j * u))
gam, E = (b - d) / (b + d), np.exp(-d * T)
D = (b - d) / xi ** 2 * (1 - E) / (1 - gam * E)
dD = -0.5 * (u * u + 1j * u) - b * D + 0.5 * xi * xi * D * D
L = np.log((1 - gam * E) / (1 - gam))
al = (gam * gam - 1) / gam
be = 2 * gam - 2 - al
I1 = ((b - d) * T - 2 * L) / xi ** 2
I2 = ((b - d) / xi ** 2) ** 2 * (T + al * L / (gam * d) - be / (gam * d) * (1 / (1 - gam * E) - 1 / (1 - gam)))
return D, dD, I1, I2
thbar, tht = half(theta)
lbar, lt = half(ell)
def phi(u):
D, dD, I1, I2 = heston_D(u)
cJ = np.exp(1j * u * muJ - 0.5 * u * u * delta ** 2) - 1 - 1j * u * kbar
return np.exp(D * v0) * second_order(
int_gbar=kappa * thbar * I1 + lbar * cJ * T,
int_gt2=kappa ** 2 * tht ** 2 * I2 + 2 * kappa * tht * lt * cJ * I1 + lt ** 2 * cJ ** 2 * T,
gt_T=kappa * tht * D + lt * cJ,
gt_0=lt * cJ,
dgt_T=kappa * tht * dD,
lam=lam)
model = rl.SwitchingBatesModel(rl.RegimeChain.twoState(lam, lam), S0, r, q, v0, kappa, theta, xi, rho,
ell, muJ, delta)
for K in (80.0, 100.0, 120.0):
print(f"K = {K:5.0f} closed form, second order {lewis_call(phi, S0, K, r, q, T, U=40.0):.5f}"
f" library {library_call(model, K, T):.5f}")
K = 80 closed form, second order 22.93733 library 22.93715
K = 100 closed form, second order 9.92245 library 9.92241
K = 120 closed form, second order 2.99202 library 2.99189