Merton jump diffusion

rl.SwitchingMerton76Process(chain, S0, r, q, sigma, jumpIntensity, logJumpMean, logJumpVol) is QuantLib’s Merton76Process with the volatility and the jump intensity allowed to switch.

The model

\[\frac{dS_t}{S_{t-}} = \big(r - q - \ell_{y_t}\bar k\big)\,dt + \sigma_{y_t}\,dW_t + \big(e^{J} - 1\big)\,dN_t,\]

where, while \(y_t = i\), \(N\) jumps at rate \(\ell_i\), each jump multiplies the price by \(e^J\) with \(J\) normal of mean \(\mu_J\) and standard deviation \(\delta\), and \(\bar k = e^{\mu_J + \delta^2/2} - 1\) is the mean relative jump.

Reduction

Over \(dt\) in regime \(i\) the diffusion contributes the Black–Scholes exponent, and with probability \(\ell_i\,dt\) a jump multiplies \(e^{iuX}\) by \(e^{iuJ}\), whose mean is \(\phi_J(u) = \exp(iu\mu_J - \tfrac12u^2\delta^2)\). So, for the martingale log return,

\[\phi_i(u) = a_i(T), \qquad g_i = -\tfrac12\sigma_i^2\,(u^2 + iu) + \ell_i\,c_J, \qquad c_J = \phi_J(u) - 1 - iu\bar k .\]

The forcing is the Lévy exponent of regime \(i\). A jump law that differs by regime would only replace \(c_J\) by a per-regime \(c_{J,i}\); the class switches the intensity.

Closed form

The forcing is constant, so the two-regime characteristic function is exact, as for Black–Scholes, with

\[\bar g = -\tfrac12\bar s\,(u^2 + iu) + \bar\ell\,c_J, \qquad \tilde g = -\tfrac12\tilde s\,(u^2 + iu) + \tilde\ell\,c_J, \qquad \bar\ell, \tilde\ell = \tfrac12(\ell_1 \pm \ell_2).\]

The Green–Kubo exponent \(\tfrac12\varepsilon T\tilde g^2\) has three parts: the volatility’s, the jump count’s, and the covariance of the two, present because the same regime raises both.

Python

S0, r, q, sigma, ell, muJ, delta, lam, T = 100.0, 0.03, 0.0, [0.25, 0.12], [2.0, 0.2], -0.08, 0.10, 25.0, 1.0
kbar = np.exp(muJ + delta ** 2 / 2) - 1

def phi(u):
    cJ = np.exp(1j * u * muJ - 0.5 * u * u * delta ** 2) - 1 - 1j * u * kbar
    g = [-0.5 * s * s * (u * u + 1j * u) + l * cJ for s, l in zip(sigma, ell)]
    return exact_two_state(g[0], g[1], lam, T)

model = rl.SwitchingMerton76Process(rl.RegimeChain.twoState(lam, lam), S0, r, q, sigma, ell, muJ, delta)
for K in (90.0, 110.0):
    print(f"K = {K:5.0f}   closed form, exact  {lewis_call(phi, S0, K, r, q, T):.6f}"
          f"   library  {library_call(model, K, T):.6f}")
K =    90   closed form, exact  16.626633   library  16.626633
K =   110   closed form, exact  6.533332   library  6.533332