Examples
A smile from a regime
Two volatility regimes make a smile without any other ingredient; calibration recovers them from the quotes.
import regimelib as rl
chain = rl.RegimeChain.twoState(4.0, 2.0)
truth = rl.SwitchingBlackScholesProcess(chain, 100.0, 0.02, 0.0, [0.40, 0.15])
engine = rl.NumericalSwitchingEngine(truth, regime=1)
quotes = []
for T in (0.25, 1.0):
for K in (80.0, 90.0, 100.0, 110.0, 120.0):
o = rl.VanillaOption(("call", K), maturity=T); o.setPricingEngine(engine)
quotes.append(rl.VolatilityHelper(T, K, o.impliedVolatility()))
model = rl.SwitchingBlackScholesProcess(rl.RegimeChain.twoState(3.0, 3.0), 100.0, 0.02, 0.0, [0.30, 0.20])
rl.calibrate(model, quotes, ["sigma", "chain"], engine=lambda m: rl.NumericalSwitchingEngine(m, regime=1))
print(model.sigma, model.chain.generator) # [0.40, 0.15], rates 4 and 2
Which order to trust
The expansion at successive orders against the numerical solution, and the warning when the chain is too slow.
chain = rl.RegimeChain.twoState(12.0, 8.0)
model = rl.SwitchingVasicek(chain, 0.03, 0.5, [0.06, 0.02], [0.015, 0.008])
swaption = rl.Swaption("payer", 2.0, [3.0, 4.0, 5.0, 6.0, 7.0], 0.035, notional=100.0)
swaption.setPricingEngine(rl.NumericalSwitchingEngine(model)); reference = swaption.NPV()
for order in (0, 1, 2, 3, 4):
swaption.setPricingEngine(rl.FastSwitchingEngine(model, order=order))
print(order, swaption.NPV() - reference)
slow = rl.SwitchingBlackScholesProcess(rl.RegimeChain.twoState(4.0, 2.0), 100.0, 0.02, 0.0, [0.40, 0.15])
option = rl.VanillaOption(("call", 100.0), maturity=1.0)
option.setPricingEngine(rl.FastSwitchingEngine(slow, order=None))
option.NPV() # raises ExpansionWarning: use NumericalSwitchingEngine
Default correlation from a common regime
Two names whose intensities rise in the same regime default together more often than independent names would.
chain = rl.RegimeChain.twoState(1.0, 1.0)
name = lambda levels: rl.SwitchingVasicek(chain, 0.02, 0.5, levels, 0.003)
same = rl.SwitchingIntensityBasket([name([0.01, 0.06]), name([0.01, 0.06])])
opposite = rl.SwitchingIntensityBasket([name([0.01, 0.06]), name([0.06, 0.01])])
same.defaultCorrelation(5.0), opposite.defaultCorrelation(5.0) # positive, negative
ftd = rl.FirstToDefaultSwap("buyer", 0.01, [0.5 * i for i in range(1, 11)], 0.4, discount=0.03)
ftd.setPricingEngine(rl.NumericalSwitchingEngine(same, regime=0))
ftd.fairSpread()
A greek as a formula
from regimelib.symbolic import VasicekTwoStateBond
f = VasicekTwoStateBond(regime=0)
f.terms["green_kubo"] # the first-order term of log P as a sympy expression
f.greek("lam") # sensitivity of the price to the switching rate
f.logGreekTerms("theta1") # d log P / d theta1, term by term