Struct HullWhiteLattice
pub struct HullWhiteLattice<R: Scalar> {
params: HullWhiteParameters<R>,
grid: TimeGrid,
dx: Vec<R>,
hw: Vec<usize>,
alpha: Vec<R>,
branch: Vec<[(R, i64); 3]>,
branch_off: Vec<usize>,
qpow: Vec<R>,
}Expand description
The Hull-White trinomial lattice at one mode. Geometry, displacements and
rollback values are all R; only node indices are integers.
Fields§
§params: HullWhiteParameters<R>§grid: TimeGrid§dx: Vec<R>Per-slice node spacing Δx_i (x_{i,j} = j·Δx_i); Δx_0 unused (root).
hw: Vec<usize>Per-slice half-width (max |j|).
alpha: Vec<R>Per-slice displacement α_i (r_{i,j} = α_i + j·Δx_i). Indices
0..n_steps−1 are fitted to reprice the curve; the terminal one is the
analytic HW displacement, used only to value exercise at the last slice.
branch: Vec<[(R, i64); 3]>The precomputed branch table — one [(prob, child_j); 3] per node,
step-major. Geometry only (a, σ, Δt, Δx): curve-, spread- and
asset-independent, built once and shared by the fit and every rollback.
branch_off: Vec<usize>Per-step offsets into branch; branch_off[n] is the table length.
qpow: Vec<R>The per-node discount ladder q_i^j = e^{−jΔx_iΔt_i}, node-major on
the same offsets as branch. Geometry only, like the branch table:
computed once with the same power the fit and every rollback used to
recompute per node per pass — so the values are the same and the
power is gone from the passes.
Implementations§
§impl<R: Scalar> HullWhiteLattice<R>
impl<R: Scalar> HullWhiteLattice<R>
pub fn build<C: YieldCurve<R>>(
params: HullWhiteParameters<R>,
grid: TimeGrid,
curve: &C,
) -> Result<HullWhiteLattice<R>, CurveError>
pub fn build<C: YieldCurve<R>>( params: HullWhiteParameters<R>, grid: TimeGrid, curve: &C, ) -> Result<HullWhiteLattice<R>, CurveError>
Builds and curve-fits a lattice over grid against curve, with the
mean reversion and the volatility term in the pricing mode — market
inputs, so their sensitivities flow with everything else’s.
§Errors
CurveError from the curve queries the fit makes.
pub fn geometry(
params: HullWhiteParameters<R>,
grid: TimeGrid,
) -> HullWhiteLattice<R>
pub fn geometry( params: HullWhiteParameters<R>, grid: TimeGrid, ) -> HullWhiteLattice<R>
The lattice’s geometry alone — grid, node spacing, half-widths, branch table and the per-node discount ladder — which depends on the mean reversion, the volatility term and the grid, and on no curve.
Separable from the fit on purpose: a scenario ladder over curves, or
an OAS solve re-rolling one asset, builds this once and calls
fit per curve. Until it is fitted the lattice has no
displacements and cannot roll anything back.
fn half_width(&self, i: usize) -> usize
fn half_width(&self, i: usize) -> usize
The half-width (max |j|) of the node range at step i.
fn j_at(&self, i: usize, jo: usize) -> i64
fn j_at(&self, i: usize, jo: usize) -> i64
The level j at offset jo of step i (offset 0 ⇒ j = −half_width).
pub fn node_rate(&self, i: usize, j: i64) -> R
pub fn node_rate(&self, i: usize, j: i64) -> R
The short rate at node (i, j): α_i + j·Δx_i, in the pricing mode.
pub(crate) fn implied_forward(&self, i: usize) -> R
pub(crate) fn implied_forward(&self, i: usize) -> R
The instantaneous forward f(0, t_i) the lattice’s fitted
displacement at step i implies: α_i − C(t_i) — α_i − (σ²/2a²)(1 − e^{−a·t_i})² for a flat volatility, α_i − ½σ²t_i² as
a → 0 — the inverse of the analytic relation the terminal slice is
built from. An affine bond price read at a node of
this lattice must use this forward, not the curve’s: the node rates
are this lattice’s, fitted over its own steps, and a bond formula keyed
to a different forward misprices every node by B·Δf.
fn branches(&self, i: usize, j: i64) -> [(R, i64); 3]
fn branches(&self, i: usize, j: i64) -> [(R, i64); 3]
The three (probability, child_j) branches from node (i, j).
pub fn fit<C: YieldCurve<R>>(&mut self, curve: &C) -> Result<(), CurveError>
pub fn fit<C: YieldCurve<R>>(&mut self, curve: &C) -> Result<(), CurveError>
Fits α to the discount curve via forward induction of the Arrow-Debreu
state prices Q: α_i is chosen so the tree reprices P(0, t_{i+1}).
Callable again on the same geometry against another curve — that is what makes a scenario ladder a sequence of fits over one build.
§Errors
CurveError from the curve queries the fit makes.
fn inst_fwd<C: YieldCurve<R>>(
curve: &C,
date: NaiveDate,
) -> Result<R, CurveError>
fn inst_fwd<C: YieldCurve<R>>( curve: &C, date: NaiveDate, ) -> Result<R, CurveError>
Instantaneous forward f^M(0, date) from the curve — a forward
difference of −ln P(0,·) over the one day from date, in the pricing
mode: the rate of the segment the terminal slice enters, exactly, on a
curve read log-linearly between day-dated pillars. The stencil
instantaneous_forward
uses, so the exercise values and this displacement agree.
pub fn rollback<A: DiscretizedAsset<R> + ?Sized>(&self, asset: &A, oas: R) -> R
pub fn rollback<A: DiscretizedAsset<R> + ?Sized>(&self, asset: &A, oas: R) -> R
Rolls asset backward from its last step to the root, returning the
root present value. At each step the node value is the spread-discounted
probability-weighted branch sum, then asset.adjust_values is applied.
oas is a continuously compounded zero spread added to every node’s
short rate — the option-adjusted-spread slot. Zero is the identity.
pub fn rollback_risky<A: DiscretizedAsset<R> + ?Sized>(
&self,
asset: &A,
oas: R,
credit: Option<&StepCredit<R>>,
) -> R
pub fn rollback_risky<A: DiscretizedAsset<R> + ?Sized>( &self, asset: &A, oas: R, credit: Option<&StepCredit<R>>, ) -> R
rollback on an issuer’s credit: each step’s
continuation is weighted by the survival over the step, and the
recovery a default in it pays is added at the step’s end, before
DiscretizedAsset::adjust_values — so exercise compares against the
risky continuation. None is the riskless rollback, the same
arithmetic bit for bit.