Skip to content

Add shifted short-rate support to BDTTree (#20) - #406

Merged
domokane merged 1 commit into
domokane:masterfrom
huuan26:feat/shifted-bdt-tree-20
Oct 17, 2026
Merged

domokane merged 1 commit into
domokane:masterfrom
huuan26:feat/shifted-bdt-tree-20

Conversation

@huuan26

@huuan26 huuan26 commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Implements shifted short-rate support in BDTTree (financepy/models/bdt_tree.py) to allow calibration and derivative valuation under negative interest rates, resolving #20.

Mathematical Formulation & Changes

  • Shifted Lognormal Short-Rate Process:
    Extends the constant-volatility Black–Derman–Toy short-rate dynamics to:
$$d\ln(r_t + s) = \theta(t)\,dt + \sigma\,dW_t, \quad r_t + s > 0$$

where $s$ (shift: float = 0.0 by default) uses the same sign convention as BlackShifted and SABRShifted for full backward compatibility.

  • Tree Calibration (f, search_root, build_tree_fast, BDTTree):
    • Propagates the positive shifted short rate $x_{m,i} = x_0 \exp\big(2(i - m_0)\sigma\sqrt{\Delta t}\big)$ across nodes at step $m$ (with middle node $m_0 = \lfloor m/2 \rfloor$) and stores the actual short rate $r_{m,i} = x_{m,i} - s$ in rt[m, i], so Arrow–Debreu state price updates and backward-induction pricers (bond_option, bermudan_swaption, callable_puttable_bond_tree) discount at the true (possibly negative) short rate without modification.
    • Validates that discount factors are positive and that $r_t + s > 0$, i.e. $DF(t_{m+1}) < \left(\sum_{i=0}^m Q_{m,i}\right) e^{s \Delta t}$, at every time step, raising FinError when the shift is insufficient for a negative-rate curve.

Testing

  • Added unit tests in tests/unit/test_FinModelRatesBDT.py:
    • test_shifted_bdt_negative_rates_calibration_and_spacing: Verifies Arrow–Debreu state price calibration $\sum_{i=0}^m Q_{m,i} = DF(t_m)$, log-spacing of $r_{m,i} + s$, and European bond option put–call parity on a negative-rate discount curve with $r = -1.5%$ and $s = 4%$.
    • test_shifted_bdt_zero_vol_shift_invariance_and_pricing: Verifies that at $\sigma = 0$, calibrated short rates and option/swaption values under negative rates are invariant to the choice of valid shift.
    • test_shifted_bdt_rejects_rates_below_negative_shift: Verifies that FinError is raised when $r_t + s \le 0$ either initially or at later tree steps.

Fixes #20

Allow BDTTree and its Numba tree calibration helpers to accept an optional shift parameter (default 0.0) so that the shifted short rate (r + shift) follows a lognormal process and negative interest rates above -shift can be calibrated and priced.

Fixes domokane#20
@domokane
domokane merged commit 3600ac9 into domokane:master Oct 17, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement Shifted BDT Tree

2 participants