financialkit.interest_rate_models#
Equilibrium short rates: Merton’s Gaussian rate, Vasicek and
Cox-Ingersoll-Ross with Feller’s condition, and Duffie-Kan affine models
priced through their Riccati ODEs; curve-fitted models: Ho-Lee,
Hull-White, the two-factor G2++ and the Black-Derman-Toy tree, all taking
a financialkit.fixed_income yield curve; Feynman-Kac bond prices by
Monte Carlo through financialkit.integrators; Jamshidian’s bond
options, coupon bond options and swaptions; caps, floors and swaptions by
Black’s formula (financialkit.options.black76_price()); the futures
convexity adjustment; and the Heath-Jarrow-Morton drift condition with
forward-curve simulation.
financialkit.interest_rate_models: short-rate models, curve-fitted models, HJM and rate options.
Equilibrium short rates: Merton’s arithmetic Brownian rate, Vasicek and
Cox-Ingersoll-Ross (with Feller’s condition and exact noncentral
chi-square sampling), and Duffie-Kan affine models of any dimension
priced through their Riccati ODEs (scipy.integrate.solve_ivp()).
Curve-fitted models take an initial
YieldCurve: Ho-Lee,
Hull-White, the two-factor G2++ and the Black-Derman-Toy lognormal tree
(calibrated by forward induction). Every closed-form bond price is
checked against Feynman-Kac Monte Carlo through
financialkit.integrators.
Options: zero-coupon bond options in any Gaussian model (Jamshidian 1989,
via the financialkit.options.black76_price() implementation),
coupon bond options and swaptions by Jamshidian’s decomposition, caps and
floors by Black’s formula on forward rates (the Brace-Gatarek-Musiela
market model) or as bond options, Black swaptions under the swap market
model, and the futures convexity adjustment. The Heath-Jarrow-Morton
framework: the no-arbitrage drift and one-factor forward-curve
simulation, with and without it.
- class financialkit.interest_rate_models.AffineModel(x0, K0, K1, sigma, alpha, beta, delta0, delta1)[source]#
Bases:
ShortRateModelA \(d\)-factor affine short-rate model, priced through its Riccati ODEs.
- Parameters:
x0 (array_like, shape (d,)) – Initial state.
K0 (array_like, shape (d,)) – Drift intercept.
sigma (array_like, shape (d,)) – Volatility scale of each factor.
alpha (array_like, shape (d,)) – Constant part of each factor’s variance.
beta (array_like, shape (d, d)) –
beta[k]is the loading of factork’s variance on the state.delta0 (
float) – Constant part of the short rate.delta1 (array_like, shape (d,)) – Loadings of the short rate on the state.
Examples
Vasicek written as an affine model gives Vasicek’s bond price:
>>> from financialkit.interest_rate_models.systems.short_rate import VasicekModel >>> m = AffineModel([0.03], [0.5 * 0.05], [[-0.5]], [0.01], [1.0], [[0.0]], 0.0, [1.0]) >>> bool(np.isclose(m.zero_coupon_bond(5.0), VasicekModel(0.03, 0.5, 0.05, 0.01).zero_coupon_bond(5.0))) True
- classmethod cir(r0, kappa, theta, sigma)[source]#
The CIR model in affine form (state = short rate,
alpha = 0).- Return type:
- Parameters:
- simulate(T, n_steps, n_paths, rng=None)[source]#
Risk-neutral short-rate paths on a uniform grid over
[0, T].
- simulate_factors(T, n_steps, n_paths, rng=None)[source]#
State paths by Euler-Maruyama, with the variances truncated at zero.
- classmethod vasicek(r0, kappa, theta, sigma)[source]#
Vasicek’s model in affine form (state = short rate,
beta = 0).- Return type:
- Parameters:
- class financialkit.interest_rate_models.BondMonteCarloResult(maturities, prices, std_errors, exact, n_paths=0)[source]#
Bases:
objectZero-coupon bond prices \(E[e^{-\int_0^T r\,dt}]\) estimated by simulation (Feynman-Kac).
- Parameters:
- class financialkit.interest_rate_models.CIRModel(r0, kappa, theta, sigma)[source]#
Bases:
ShortRateModelCox, Ingersoll and Ross’s (1985) square-root short rate.
\[dr = \kappa(\theta - r)\,dt + \sigma\sqrt{r}\,dW\]Volatility falls with the rate, so (under the Feller condition) rates stay positive. Bond prices are exponential-affine,
\[B = \frac{2(e^{\gamma\tau} - 1)}{(\gamma + \kappa)(e^{\gamma\tau} - 1) + 2\gamma}, \quad A = \left[\frac{2\gamma e^{(\kappa + \gamma)\tau/2}}{(\gamma + \kappa)(e^{\gamma\tau} - 1) + 2\gamma}\right]^{2\kappa\theta/\sigma^2},\]with \(\gamma = \sqrt{\kappa^2 + 2\sigma^2}\) and \(P = A e^{-Br}\). The transition is a scaled noncentral chi-square, and the stationary law a gamma distribution with shape \(2\kappa\theta/\sigma^2\) and scale \(\sigma^2/(2\kappa)\).
- Parameters:
r0 (
float) – Initial rate (non-negative), mean-reversion speed, long-run mean and volatility.kappa (
float) – Initial rate (non-negative), mean-reversion speed, long-run mean and volatility.theta (
float) – Initial rate (non-negative), mean-reversion speed, long-run mean and volatility.sigma (
float) – Initial rate (non-negative), mean-reversion speed, long-run mean and volatility.
Examples
>>> m = CIRModel(0.06, 0.1, 0.1, 0.11) >>> round(float(m.zero_coupon_bond(10.0)), 4) 0.5039 >>> round(m.feller_ratio, 3) 1.653
- property feller_ratio: float#
\(2\kappa\theta/\sigma^2\) (see
feller_condition()).- Type:
- simulate(T, n_steps, n_paths, rng=None, method='exact')[source]#
Short-rate paths;
method="exact"samples the noncentral chi-square transition,"euler"runs full-truncation Euler-Maruyama.
- class financialkit.interest_rate_models.CapFloorResult(price, caplets, forwards, reset_times, payment_times, strike, kind='cap')[source]#
Bases:
objectA cap or floor as a strip of caplets (floorlets), each priced by Black-76.
- Parameters:
- class financialkit.interest_rate_models.G2PlusPlusModel(curve, a, sigma, b, eta, rho)[source]#
Bases:
GaussianShortRateModelThe G2++ model, fitted to an initial curve.
Bond prices given the factors \((x, y)\) at time \(t\) are
\[P(t, T) = \frac{P(0, T)}{P(0, t)} \exp\left\{\tfrac12[V(t, T) - V(0, T) + V(0, t)] - B_a(\tau) x - B_b(\tau) y\right\},\]with \(B_k(\tau) = (1 - e^{-k\tau})/k\) and \(V(t, T)\) the variance of \(\int_t^T (x + y)\,du\) (Brigo-Mercurio eq. 4.10).
- Parameters:
curve (
YieldCurve|float) – Initial term structure.a (
float) – Mean reversion and volatility of the first factor.sigma (
float) – Mean reversion and volatility of the first factor.b (
float) – Mean reversion and volatility of the second factor.eta (
float) – Mean reversion and volatility of the second factor.rho (
float) – Correlation of the two Brownian drivers, in(-1, 1).
Examples
>>> m = G2PlusPlusModel(0.03, 0.5, 0.01, 0.05, 0.008, -0.7) >>> round(float(m.zero_coupon_bond(10.0)), 6) == round(float(np.exp(-0.3)), 6) True
- bond_option_volatility(T, S)[source]#
Total volatility \(\sigma_P\) of \(\ln P(T, S)\) seen from time 0.
- Parameters:
T (array_like) – Option expiry.
S (array_like) – Maturity of the underlying bond,
S > T.
- Returns:
float or ndarray – Standard deviation of \(\ln P(T, S)\) (not annualised).
- n_factors: int = 2#
Number of Brownian drivers; one-factor models have the short rate as their state.
- shift(t)[source]#
\(\varphi(t) = f(0, t) + \tfrac{\sigma^2}{2}B_a(t)^2 + \tfrac{\eta^2}{2}B_b(t)^2 + \rho\sigma\eta B_a(t) B_b(t)\).
- simulate(T, n_steps, n_paths, rng=None)[source]#
Risk-neutral short-rate paths on a uniform grid over
[0, T].
- simulate_factors(T, n_steps, n_paths, rng=None)[source]#
Factor paths by Euler-Maruyama on correlated increments.
- yield_correlation(T1, T2)[source]#
Instantaneous correlation of the zero yields of maturities
T1andT2.A yield \(R(t, t + \tau)\) moves by \([B_a(\tau)\sigma\,dW_1 + B_b(\tau)\eta\,dW_2]/\tau\), so two yields are perfectly correlated in any one-factor model but not here.
- Parameters:
T1 (array_like) – Times to maturity.
T2 (array_like) – Times to maturity.
- Returns:
float or ndarray
- class financialkit.interest_rate_models.GaussianShortRateModel[source]#
Bases:
ShortRateModelA short-rate model in which bond log-prices are Gaussian.
Then \(P(T, S) / P(0, T, S)\) is lognormal under the \(T\)-forward measure, and a European option on a zero-coupon bond is priced by Black-76 with total volatility \(\sigma_P\) given by
bond_option_volatility()(Jamshidian 1989).- abstractmethod bond_option_volatility(T, S)[source]#
Total volatility \(\sigma_P\) of \(\ln P(T, S)\) seen from time 0.
- Parameters:
T (array_like) – Option expiry.
S (array_like) – Maturity of the underlying bond,
S > T.
- Returns:
float or ndarray – Standard deviation of \(\ln P(T, S)\) (not annualised).
- class financialkit.interest_rate_models.HJMResult(times, forwards, initial_curve, drift_condition=True)[source]#
Bases:
objectSimulated forward curves \(f(t, T_j)\) under a one-factor HJM model.
- bond_prices()[source]#
Monte Carlo \(P(0, T_j) = E[e^{-\int_0^{T_j} r\,dt}]\) (trapezoidal rule) and its standard errors.
- class financialkit.interest_rate_models.HoLeeModel(curve, sigma)[source]#
Bases:
_CurveFittedGaussianHo and Lee’s (1986) arbitrage-free model, in its continuous-time limit.
\[dr = \theta(t)\,dt + \sigma\,dW, \qquad \theta(t) = f_T(0, t) + \sigma^2 t,\]where \(f(0, t)\) is today’s instantaneous forward curve. The drift is whatever it takes to reprice every zero-coupon bond, so the model fits any initial curve exactly. Bonds at later dates are
\[P(t, T) = \frac{P(0, T)}{P(0, t)} \exp\left(\tau f(0, t) - \tfrac12\sigma^2 t \tau^2 - \tau r(t)\right).\]Ho and Lee built it as a binomial tree of whole discount curves; it is Merton’s Gaussian rate with a curve-fitting drift.
- Parameters:
curve (
YieldCurve|float) – Initial term structure (a float is a flat rate).sigma (
float) – Absolute volatility of the short rate.
Examples
>>> from financialkit.fixed_income import NelsonSiegelCurve >>> curve = NelsonSiegelCurve(0.05, -0.02, 0.01, 2.0) >>> bool(np.isclose(HoLeeModel(curve, 0.01).zero_coupon_bond(7.0), curve.discount(7.0))) True
- class financialkit.interest_rate_models.HullWhiteModel(curve, a, sigma)[source]#
Bases:
_CurveFittedGaussianHull and White’s (1990) extended Vasicek model.
\[dr = (\theta(t) - a r)\,dt + \sigma\,dW, \qquad \theta(t) = f_T(0, t) + a f(0, t) + \frac{\sigma^2}{2a}(1 - e^{-2at}).\]Vasicek’s mean reversion with Ho-Lee’s curve-fitting drift: the initial curve is matched exactly and volatility of long rates decays like \(B(\tau)/\tau\). The short rate is \(r(t) = x(t) + \varphi(t)\) with \(x\) an Ornstein-Uhlenbeck process started at 0 and \(\varphi(t) = f(0, t) + \tfrac{\sigma^2}{2a^2}(1 - e^{-at})^2\).
- Parameters:
curve (
YieldCurve|float) – Initial term structure (a float is a flat rate).a (
float) – Mean-reversion speed;a = 0is Ho-Lee.sigma (
float) – Absolute volatility of the short rate.
Examples
>>> hw = HullWhiteModel(0.04, 0.1, 0.01) >>> round(float(hw.zero_coupon_bond(5.0)), 6) 0.818731
- class financialkit.interest_rate_models.JamshidianResult(price, critical_rate, payment_times, cash_flows, strikes, components)[source]#
Bases:
objectAn option on a coupon bond split into zero-coupon bond options (Jamshidian 1989).
- Parameters:
- cash_flows: ndarray#
Amounts paid at
payment_times.- Type:
ndarray, shape (n,)
- components: ndarray#
cash_flows[i]times the zero-bond option struck atstrikes[i].- Type:
ndarray, shape (n,)
- price: float#
Option value, the sum of
components.- Type:
- class financialkit.interest_rate_models.MertonShortRateModel(r0, mu, sigma)[source]#
Bases:
GaussianShortRateModelMerton’s (1973) Gaussian short rate, \(dr = \mu\,dt + \sigma\,dW\).
The first stochastic interest rate in continuous time, from Section 6 of Merton’s “Theory of Rational Option Pricing”. Bond prices are
\[P(t, T) = \exp\left(-r\tau - \tfrac12 \mu\tau^2 + \tfrac16 \sigma^2\tau^3\right), \quad \tau = T - t,\]so the cubic convexity term eventually makes long yields fall without bound: rates without mean reversion cannot carry a long curve.
- Parameters:
Examples
>>> m = MertonShortRateModel(0.05, 0.0, 0.0) >>> round(float(m.zero_coupon_bond(2.0)), 6) 0.904837
- bond_option_volatility(T, S)[source]#
Total volatility \(\sigma_P\) of \(\ln P(T, S)\) seen from time 0.
- Parameters:
T (array_like) – Option expiry.
S (array_like) – Maturity of the underlying bond,
S > T.
- Returns:
float or ndarray – Standard deviation of \(\ln P(T, S)\) (not annualised).
- simulate(T, n_steps, n_paths, rng=None)[source]#
Risk-neutral short-rate paths on a uniform grid over
[0, T].
- class financialkit.interest_rate_models.ModelCurve(model)[source]#
Bases:
YieldCurveA model’s time-0 zero curve, \(r(t) = -\ln P(0, t) / t\), with \(r(0) = r_0\).
- Parameters:
model (ShortRateModel)
- class financialkit.interest_rate_models.RateTree(times, rates, arrow_debreu, dt, sigma=<factory>)[source]#
Bases:
objectA recombining binomial short-rate lattice with probability 1/2 on each branch.
rates[i, j]is the rate over[t_i, t_{i+1}]at nodej(j = 0lowest,j <= i); entries withj > iarenan. Rates are continuously compounded over one step of lengthdt.- arrow_debreu: ndarray#
Price at time 0 of 1 paid at node
(i, j).- Type:
ndarray, shape (n + 1, n + 1)
- bond_option(expiry, maturity, K, option_type='call', american=False)[source]#
European or American option on a zero-coupon bond, by backward induction.
- Parameters:
- Return type:
- Returns:
float
- class financialkit.interest_rate_models.ShortRateModel[source]#
Bases:
ABCA risk-neutral model of the instantaneous short rate \(r(t)\).
Subclasses set
r0andn_factorsand implementzero_coupon_bond()andsimulate().- curve()[source]#
The model’s initial term structure as a
YieldCurve.- Return type:
- n_factors: int = 1#
Number of Brownian drivers; one-factor models have the short rate as their state.
- abstractmethod simulate(T, n_steps, n_paths, rng=None)[source]#
Risk-neutral short-rate paths on a uniform grid over
[0, T].
- yields(T, t=0.0, state=None)[source]#
Continuously compounded zero yields \(-\ln P(t, T) / (T - t)\).
- Parameters:
t (float)
- class financialkit.interest_rate_models.VasicekModel(r0, kappa, theta, sigma)[source]#
Bases:
GaussianShortRateModelVasicek’s (1977) mean-reverting short rate, \(dr = \kappa(\theta - r)\,dt + \sigma\,dW\).
The rate is an Ornstein-Uhlenbeck process, so bond prices are exponential-affine,
\[P(t, T) = A(\tau) e^{-B(\tau) r}, \quad B = \frac{1 - e^{-\kappa\tau}}{\kappa}, \quad \ln A = \left(\theta - \frac{\sigma^2}{2\kappa^2}\right)(B - \tau) - \frac{\sigma^2 B^2}{4\kappa},\]and long yields tend to \(\theta - \sigma^2/(2\kappa^2)\). Depending on \(r_0\) against that level, the curve is upward sloping, inverted or humped.
- Parameters:
r0 (
float) – Initial rate, mean-reversion speed, long-run mean and volatility.kappa (
float) – Initial rate, mean-reversion speed, long-run mean and volatility.theta (
float) – Initial rate, mean-reversion speed, long-run mean and volatility.sigma (
float) – Initial rate, mean-reversion speed, long-run mean and volatility.
Examples
>>> m = VasicekModel(0.06, 0.1, 0.1, 0.02) >>> round(float(m.zero_coupon_bond(10.0)), 4) 0.4899 >>> round(m.long_yield, 4) 0.08
- bond_option_volatility(T, S)[source]#
Total volatility \(\sigma_P\) of \(\ln P(T, S)\) seen from time 0.
- Parameters:
T (array_like) – Option expiry.
S (array_like) – Maturity of the underlying bond,
S > T.
- Returns:
float or ndarray – Standard deviation of \(\ln P(T, S)\) (not annualised).
- simulate(T, n_steps, n_paths, rng=None)[source]#
Risk-neutral short-rate paths on a uniform grid over
[0, T].
- financialkit.interest_rate_models.black_derman_toy_tree(curve, sigma, maturity, n_steps)[source]#
Calibrate a Black-Derman-Toy lognormal binomial short-rate tree to a zero curve.
At step \(i\) the rates are \(r_{i,j} = U_i e^{\sigma_i\sqrt{\Delta t}\,(2j - i)}\), \(j = 0, \dots, i\), with probability 1/2 on each branch, so the log short rate has volatility \(\sigma_i\) and rates stay positive. Each median level \(U_i\) is solved by
scipy.optimize.brentq()so that the tree reprices the bond maturing at \(t_{i+1}\),\[\sum_j Q_{i,j} e^{-r_{i,j}\Delta t} = P(0, t_{i+1}),\]where the Arrow-Debreu prices \(Q\) are carried forward one level at a time (Jamshidian’s 1991 forward induction). This is the short-rate-volatility form of the model; Black, Derman and Toy’s original also matched a term structure of yield volatilities.
- Parameters:
- Return type:
- Returns:
RateTree
Examples
>>> from financialkit.fixed_income import FlatCurve >>> tree = black_derman_toy_tree(FlatCurve(0.05), 0.2, 5.0, 50) >>> round(tree.zero_coupon_bond(5.0), 8) == round(float(np.exp(-0.25)), 8) True
- financialkit.interest_rate_models.black_swaption_price(curve, expiry, tenor, strike, vol, freq=2, payer=True, notional=1.0)[source]#
Black’s formula for a European swaption (Jamshidian’s 1997 swap market model).
Under the annuity measure the forward swap rate \(S\) is a martingale; taking it lognormal gives
\[V_{\text{payer}} = A\,[S N(d_1) - K N(d_2)], \quad V_{\text{receiver}} = A\,[K N(-d_2) - S N(-d_1)],\]Black-76 with zero rate times the annuity \(A\).
- Parameters:
curve (
YieldCurve|float)expiry (
float) – Option expiry and swap length.tenor (
float) – Option expiry and swap length.strike (array_like) – Fixed rate and Black volatility of the swap rate.
vol (array_like) – Fixed rate and Black volatility of the swap rate.
freq (
int)payer (
bool)notional (
float)
- Returns:
float or ndarray
Examples
Hull, 10th ed., Example 29.5: a 5 x 3 payer swaption struck at 6.2%, semiannual, flat 6% curve with continuous compounding, 20% volatility, principal 100.
>>> round(black_swaption_price(0.06, 5.0, 3.0, 0.062, 0.2, 2, notional=100.0), 2) 2.07
- financialkit.interest_rate_models.cap_floor_price(curve, strike, maturity, vol, freq=4, kind='cap', notional=1.0)[source]#
A cap or floor priced caplet by caplet with Black’s formula.
Caplet \(k\) pays \(\tau_k \max(L_k - K, 0)\) at \(t_{k+1}\) on the rate \(L_k\) fixed at \(t_k\). Under the \(t_{k+1}\)-forward measure the simple forward rate \(F_k = [P(0, t_k)/P(0, t_{k+1}) - 1]/\tau_k\) is a martingale, and taking it lognormal (Brace-Gatarek-Musiela 1997) gives
\[\text{caplet}_k = \tau_k P(0, t_{k+1})\,[F_k N(d_1) - K N(d_2)].\]The first period, whose rate is already fixed, is excluded.
- Parameters:
- Return type:
- Returns:
CapFloorResult
Examples
On a curve flat at 7% with quarterly compounding every forward is 7%; cap minus floor at the same strike is the forward-starting swap:
>>> curve = 4 * np.log(1.0175) >>> cap = cap_floor_price(curve, 0.07, 2.0, 0.20, freq=4) >>> floor = cap_floor_price(curve, 0.07, 2.0, 0.20, freq=4, kind="floor") >>> cap.caplets.size, round(float(cap.forwards[0]), 4), bool(np.isclose(cap.price, floor.price)) (7, 0.07, True)
- financialkit.interest_rate_models.coupon_bond_option(model, T, payment_times, cash_flows, K, option_type='call')[source]#
European option on a coupon bond by Jamshidian’s (1989) decomposition.
In a one-factor model the bond’s value at expiry, \(\sum_i c_i P(T, t_i; r)\), falls with \(r\). Let \(r^*\) solve \(\sum_i c_i P(T, t_i; r^*) = K\) and \(K_i = P(T, t_i; r^*)\). The bond is above the strike exactly when every zero is above its \(K_i\), so
\[\max\left(\sum_i c_i P(T, t_i) - K, 0\right) = \sum_i c_i \max(P(T, t_i) - K_i, 0),\]and the option is a portfolio of zero-coupon bond options.
- Parameters:
model (
GaussianShortRateModel) – A one-factor model (Merton, Vasicek, Ho-Lee or Hull-White).T (
float) – Option expiry.payment_times (array_like, shape (n,)) – Bond payments after
T.cash_flows (array_like, shape (n,)) – Bond payments after
T.K (
float) – Strike (on the bond’s dirty price).option_type (
str)
- Return type:
- Returns:
JamshidianResult
- financialkit.interest_rate_models.feller_condition(kappa, theta, sigma)[source]#
Feller’s ratio \(2\kappa\theta / \sigma^2\) for the square-root process.
Feller (1951) classified the boundary at zero of \(dr = \kappa(\theta - r)\,dt + \sigma\sqrt{r}\,dW\): the process never reaches zero when the ratio is at least 1, and touches it (and is reflected) when it is below 1. The ratio is also the shape parameter of the stationary gamma distribution, whose density diverges at zero when it is below 1.
- Parameters:
kappa (array_like) – Mean-reversion speed, long-run level and volatility.
theta (array_like) – Mean-reversion speed, long-run level and volatility.
sigma (array_like) – Mean-reversion speed, long-run level and volatility.
- Returns:
float or ndarray – \(2\kappa\theta/\sigma^2\); the condition holds where it is
>= 1.
Examples
>>> round(feller_condition(0.5, 0.05, 0.2), 10) 1.25
- financialkit.interest_rate_models.forward_swap_annuity(curve, expiry, tenor, freq=2)[source]#
Annuity \(A = \sum_i \tau_i P(0, t_i)\) of a forward-starting swap’s fixed leg.
- financialkit.interest_rate_models.forward_swap_rate(curve, expiry, tenor, freq=2)[source]#
Forward par swap rate \(S = [P(0, T_0) - P(0, T_n)] / A\).
Examples
>>> from financialkit.fixed_income import FlatCurve >>> round(forward_swap_rate(FlatCurve(0.04), 1.0, 5.0, 1), 6) == round(float(np.expm1(0.04)), 6) True
- financialkit.interest_rate_models.futures_convexity_adjustment(t1, t2, sigma, a=0.0)[source]#
Futures rate minus forward rate for a futures contract on the rate over
[t1, t2].Futures are settled daily, so their rate is the risk-neutral expectation of the future rate rather than the forward rate, and is higher by a convexity adjustment. Under Ho-Lee (
a = 0),\[\text{futures} - \text{forward} = \tfrac12 \sigma^2 t_1 t_2,\]and under Hull-White
\[\frac{B(t_1, t_2)}{t_2 - t_1}\left[B(t_1, t_2)(1 - e^{-2at_1}) + 2aB(0, t_1)^2\right]\frac{\sigma^2}{4a},\]with \(B(s, t) = (1 - e^{-a(t-s)})/a\). Both rates are continuously compounded.
- Parameters:
t1 (array_like) – Start and end of the underlying rate’s period,
t2 > t1 >= 0.t2 (array_like) – Start and end of the underlying rate’s period,
t2 > t1 >= 0.sigma (array_like) – Short-rate volatility.
a (
float) – Hull-White mean reversion; 0 gives the Ho-Lee formula.
- Returns:
float or ndarray
Examples
Hull, 10th ed., Example 6.4: sigma = 1.2%, an 8-year futures contract on 3-month rates.
>>> round(100 * futures_convexity_adjustment(8.0, 8.25, 0.012), 3) 0.475
- financialkit.interest_rate_models.hjm_drift(sigma, t, T)[source]#
HJM no-arbitrage drift \(\alpha(t, T) = \sigma(t, T)\int_t^T \sigma(t, u)\,du\).
- Parameters:
- Return type:
- Returns:
float or ndarray
Examples
Constant volatility (Ho-Lee) gives \(\sigma^2 (T - t)\):
>>> round(hjm_drift(lambda t, T: 0.01, 0.0, 5.0), 10) 0.0005
- financialkit.interest_rate_models.jamshidian_swaption(model, expiry, tenor, strike, freq=2, payer=True, notional=1.0)[source]#
European swaption in a one-factor model, by Jamshidian’s decomposition.
At expiry, receiving fixed at \(K\) on a swap is worth \(\sum_i c_i P(T_0, t_i) - 1\) with \(c_i = K\tau_i\) (plus 1 at the end), so a receiver swaption is a call, and a payer swaption a put, on that coupon bond struck at par.
- Parameters:
model (
GaussianShortRateModel) – One-factor model.expiry (
float) – Option expiry and length of the underlying swap.tenor (
float) – Option expiry and length of the underlying swap.strike (
float) – Fixed rate.freq (
int) – Fixed-leg payments per year.payer (
bool) – Payer (right to pay fixed) or receiver swaption.notional (
float)
- Return type:
- Returns:
JamshidianResult – Scaled by notional.
- financialkit.interest_rate_models.model_cap_floor_price(model, strike, maturity, freq=4, kind='cap', notional=1.0)[source]#
A cap or floor in a Gaussian short-rate model, as options on zero-coupon bonds.
A caplet paying at \(t_{k+1}\) is worth, at its fixing \(t_k\), \((1 + K\tau)\max(1/(1 + K\tau) - P(t_k, t_{k+1}), 0)\): a put on a zero-coupon bond (Hull, 10th ed., Sec. 32.4). A floorlet is the call.
- Parameters:
- Return type:
- Returns:
CapFloorResult
- financialkit.interest_rate_models.monte_carlo_bond_prices(model, maturities, n_steps=200, n_paths=10000, rng=None)[source]#
Zero-coupon bond prices by simulating the short rate (Feynman-Kac).
Kac (1949) showed that the solution of the bond-pricing PDE \(\partial_t P + \mu \partial_r P + \tfrac12 \sigma^2 \partial_{rr} P - rP = 0\) is the expectation
\[P(0, T) = E^Q\left[\exp\left(-\int_0^T r(s)\,ds\right)\right],\]so a bond is priced by averaging discount factors over simulated paths. The integral is the trapezoidal rule on the simulation grid, an \(O(\Delta t^2)\) bias well below the sampling error.
- Parameters:
- Return type:
- Returns:
BondMonteCarloResult – With the model’s closed-form prices in
exact.
Examples
>>> res = monte_carlo_bond_prices(VasicekModel(0.03, 0.5, 0.05, 0.01), [1.0, 5.0], n_paths=4000, rng=0) >>> bool(abs(res.z_scores).max() < 4) True
- financialkit.interest_rate_models.path_discount_factors(times, rates)[source]#
Pathwise discount factors \(e^{-\int_0^{t_i} r(s)\,ds}\) by the trapezoidal rule.
- Parameters:
times (array_like, shape (n + 1,)) – Simulation grid starting at 0.
rates (array_like, shape (n_paths, n + 1)) – Short-rate paths on that grid.
- Return type:
- Returns:
ndarray, shape (n_paths, n + 1) – Equal to 1 in the first column.
Examples
>>> import numpy as np >>> t = np.linspace(0.0, 2.0, 5) >>> float(path_discount_factors(t, np.full((1, 5), 0.05))[0, -1].round(6)) 0.904837
- financialkit.interest_rate_models.plot_bond_monte_carlo(result, ax=None, label='Monte Carlo')[source]#
Monte Carlo bond prices with 95% error bars against the closed form.
- Parameters:
result (
BondMonteCarloResult)ax (matplotlib.axes.Axes, optional)
label (
str)
- Returns:
matplotlib.axes.Axes
- financialkit.interest_rate_models.plot_caplets(result, ax=None, label='')[source]#
Caplet (floorlet) prices by fixing date, with the forward rates on a twin axis.
- Parameters:
result (
CapFloorResult)ax (matplotlib.axes.Axes, optional)
label (
str)
- Returns:
matplotlib.axes.Axes
- financialkit.interest_rate_models.plot_forward_curves(result, path=0, every=5, ax=None)[source]#
Snapshots of one simulated forward curve \(T \mapsto f(t, T)\) as time passes.
- financialkit.interest_rate_models.plot_jamshidian(result, ax=None)[source]#
Jamshidian’s decomposition: the zero-coupon option behind each payment.
- Parameters:
result (
JamshidianResult)ax (matplotlib.axes.Axes, optional)
- Returns:
matplotlib.axes.Axes
- financialkit.interest_rate_models.plot_model_yields(model, t_max=30.0, ax=None, label='')[source]#
A model’s time-0 zero curve, in percent.
- Parameters:
model (
ShortRateModel)t_max (
float) – Longest maturity.ax (matplotlib.axes.Axes, optional)
label (
str)
- Returns:
matplotlib.axes.Axes
- financialkit.interest_rate_models.plot_rate_tree(tree, ax=None, max_steps=12)[source]#
The nodes of a binomial short-rate tree, in percent, joined by their branches.
- financialkit.interest_rate_models.plot_short_rate_paths(times, rates, n_show=20, ax=None, label='', quantiles=True)[source]#
A sample of short-rate paths, in percent, with the 5-95% band and the mean.
- Parameters:
times (array_like, shape (n + 1,))
rates (array_like, shape (n_paths, n + 1))
n_show (
int) – Number of individual paths drawn.ax (matplotlib.axes.Axes, optional) – Axes to draw on; a new figure is created if omitted.
label (
str) – Legend label for the mean.quantiles (
bool) – Shade the 5-95% band across paths.
- Returns:
matplotlib.axes.Axes
- financialkit.interest_rate_models.simulate_hjm(curve, sigma, a=0.0, horizon=5.0, n_steps=50, n_paths=2000, drift_condition=True, rng=None)[source]#
Simulate one-factor HJM forward curves with \(\sigma(t, T) = \sigma e^{-a(T - t)}\).
The maturity grid is the simulation grid, so the short rate is \(r(t_i) = f(t_i, t_i)\). Every maturity shares one Brownian driver, and the drift is the HJM drift (or zero when
drift_condition=False).- Parameters:
curve (
YieldCurve|float) – Initial term structure; its instantaneous forwards start the simulation.sigma (
float) – Forward-rate volatility at zero time to maturity.a (
float) – Exponential decay of volatility with maturity; 0 is Ho-Lee.horizon (
float) – Last date and maturity.n_steps (
int)n_paths (
int)drift_condition (
bool) – Apply the no-arbitrage drift.
- Return type:
- Returns:
HJMResult
Examples
>>> res = simulate_hjm(0.03, 0.01, horizon=2.0, n_steps=20, n_paths=500, rng=0) >>> res.forwards.shape (500, 21, 21)
- financialkit.interest_rate_models.swaption_schedule(expiry, tenor, freq=2)[source]#
Fixed-leg payment dates of a swap starting at expiry and running for tenor years.
Examples
>>> swaption_schedule(1.0, 2.0, 2).tolist() [1.5, 2.0, 2.5, 3.0]
- financialkit.interest_rate_models.zero_coupon_bond_option(model, T, S, K, option_type='call')[source]#
European option, expiring at
T, on a unit zero-coupon bond maturing atS.\[c = P(0, S) N(h) - K P(0, T) N(h - \sigma_P), \quad h = \frac{1}{\sigma_P}\ln\frac{P(0, S)}{K P(0, T)} + \frac{\sigma_P}{2},\]which is Black-76 on the forward bond price \(P(0, S)/P(0, T)\) with volatility \(\sigma_P/\sqrt{T}\), discounted by \(P(0, T)\). \(\sigma_P\) is the model’s
bond_option_volatility()(Jamshidian 1989 for Vasicek; the same formula holds for Ho-Lee, Hull-White and G2++).- Parameters:
model (
GaussianShortRateModel)T (array_like) – Option expiry and bond maturity,
0 < T < S.S (array_like) – Option expiry and bond maturity,
0 < T < S.K (array_like) – Strike per unit face.
option_type (
str)
- Returns:
float or ndarray
Examples
A 1-year option on a 5-year bond under Vasicek; call minus put is the forward value \(P(0, 5) - K P(0, 1)\):
>>> from financialkit.interest_rate_models.systems.short_rate import VasicekModel >>> m = VasicekModel(0.1, 0.1, 0.1, 0.02) >>> c = zero_coupon_bond_option(m, 1.0, 5.0, 0.67, "call") >>> p = zero_coupon_bond_option(m, 1.0, 5.0, 0.67, "put") >>> bool(np.isclose(c - p, m.zero_coupon_bond(5.0) - 0.67 * m.zero_coupon_bond(1.0))) True