Vasicek with jumps
rl.SwitchingVasicekJumps(chain, r0, a, b, sigma, jumpIntensity, jumpMean): the mean level, the volatility and
the jump intensity may switch. QuantLib has no jump short-rate model; the frozen limit is checked against the affine
closed form.
The model
where, while \(y_t = i\), \(J\) jumps at rate \(\ell_i\) by exponentially distributed amounts with mean \(m\).
Reduction
The pricing equation is the Vasicek one with the extra term \(\ell_i\,\mathbb{E}[u_i(t, r + J) - u_i(t, r)]\). With \(u_i = e^{-B(t)r}a_i(t)\) a jump of size \(J\) multiplies \(e^{-Br}\) by \(e^{-BJ}\), and for exponential jumps \(\mathbb{E}[e^{-BJ}] = 1/(1 + mB)\). So the jumps change only the forcing:
Closed form
Two integrals beyond the \(I_n\) of the Vasicek page are needed,
with \(E = e^{-aT}\). Then, with \(\bar\ell, \tilde\ell = \tfrac12(\ell_1 \pm \ell_2)\),
Python
r0, a, b, sigma, ell, m, lam, T = 0.0, 2.0, [0.05, 0.02], [0.02, 0.01], [3.0, 0.2], 0.03, 10.0, 3.0
E, B, I1, I2, I3, I4 = powers_of_B(a, T)
J1 = a / (a + m) * (T + np.log(1 + m / a * (1 - E)) / a)
J2 = a / (a + m) * (J1 - (1 / (1 + m / a * (1 - E)) - 1) / a)
bbar, bt = half(b)
sbar, st = half([s * s for s in sigma])
lbar, lt = half(ell)
closed = np.exp(-B * r0) * second_order(
int_gbar=-a * bbar * I1 + sbar * I2 / 2 + lbar * (J1 - T),
int_gt2=(a * a * bt * bt * I2 - a * bt * st * I3 + st * st * I4 / 4 + lt * lt * (J2 - 2 * J1 + T)
- 2 * a * bt * lt * ((T - J1) / m - I1) + st * lt * ((I1 - (T - J1) / m) / m - I2)),
gt_T=-a * bt * B + st * B * B / 2 + lt * (1 / (1 + m * B) - 1),
gt_0=0.0,
dgt_T=(-a * bt + st * B - lt * m / (1 + m * B) ** 2) * E,
lam=lam)
model = rl.SwitchingVasicekJumps(rl.RegimeChain.twoState(lam, lam), r0, a, b, sigma, ell, m)
print(f"closed form, second order {closed:.8f}")
print(f"library, numerical {library_bond(model, T):.8f}")
closed form, second order 0.86213677
library, numerical 0.86213670
regimelib.symbolic.VasicekJumpsBondFirstOrder gives the first-order formula for any finite chain as a sympy
expression (Symbolic).