Struct PiecewiseDiscount
pub struct PiecewiseDiscount {
reference_date: NaiveDate,
reference_ordinal: i32,
dates: Vec<NaiveDate>,
taus: Vec<f64>,
log_dfs: Vec<f64>,
interp: Interpolation,
mixed: Option<(Interpolation, usize)>,
long_taus: Vec<f64>,
rate_day_count: DayCountFraction,
}Expand description
A discount curve stored as log discount factors at dated pillars, linear in log-space between them and at the last segment’s forward rate beyond the last.
The parameters are f64. Lifting them into a mode is Self::view.
Fields§
§reference_date: NaiveDate§reference_ordinal: i32reference_date as a proleptic-Gregorian day number, so a query by
ordinal measures time with one integer subtraction. Bit-identical to
the date subtraction: both are differences in the same day numbering.
dates: Vec<NaiveDate>Free pillar dates, strictly increasing and all after reference_date.
taus: Vec<f64>Year fractions from the reference date, index-aligned with dates.
log_dfs: Vec<f64>Natural log of the discount factor at each pillar. The anchor
(tau = 0, log_df = 0) is implicit rather than stored: it is not a
free parameter, and storing it would put a permanently-zero row in every
risk ladder.
interp: InterpolationHow the curve reads between its pillars. A stored property, not an evaluation argument: two curves over the same pillars under different schemes are two curves.
mixed: Option<(Interpolation, usize)>A second scheme and the pillar it takes over at, when the curve is read under two.
Some((long, k)) means: interp from the anchor to dates[k], and
long from dates[k] onward. The changeover is a pillar of this
curve rather than a free date — a knot between pillars would move the
curve while owning no row in any risk ladder, which is the discipline
the implicit anchor already follows.
The changeover pillar is the right end knot of the short side and the left end knot of the long one, so both reproduce it and the curve is continuous there. It is not the anchor’s twin: the anchor is pinned at zero and owns no adjoint, while this is a free parameter that owns a risk row and that both sides’ coefficients depend on.
long_taus: Vec<f64>The long side’s year fractions, measured from the changeover so it can be read in the same frame every scheme assumes. Passive and identical every lift, so it is stored here rather than rebuilt per query — a query must allocate nothing.
rate_day_count: DayCountFractionThe day count a rate read off this curve is formed under.
Deliberately not the measure taus above are built on. Those
parametrise the pillars a caller stated as dated discount factors, and
rescaling them would move a factor the caller gave; this is read only
where a rate is formed. A curve that never reports a rate reads the
same whatever this says, which is the property
rate_measure_moves_no_factor holds it to.
Implementations§
§impl PiecewiseDiscount
impl PiecewiseDiscount
pub fn from_discount_factors(
reference_date: NaiveDate,
pillars: &[(NaiveDate, f64)],
) -> Result<Self, CurveError>
pub fn from_discount_factors( reference_date: NaiveDate, pillars: &[(NaiveDate, f64)], ) -> Result<Self, CurveError>
Builds a curve from pillar dates and their discount factors.
§Errors
CurveError::Malformed if there are no pillars, if the dates are not
strictly increasing, if any date is not after reference_date, or if any
discount factor is not strictly positive.
CurveError::NonFinite if any discount factor is NaN or infinite.
pub fn with_rate_day_count(self, day_count: DayCountFraction) -> Self
pub fn with_rate_day_count(self, day_count: DayCountFraction) -> Self
This curve reporting its rates under day_count.
Moves no discount factor — see the field’s own note — so a caller who only ever discounts can ignore it, and a caller quoting a market that measures otherwise states it once here rather than restating every rate it reads back.
pub fn rate_day_count(&self) -> DayCountFraction
pub fn rate_day_count(&self) -> DayCountFraction
The day count this curve forms a rate under.
pub fn with_interpolation(
self,
interp: Interpolation,
) -> Result<Self, CurveError>
pub fn with_interpolation( self, interp: Interpolation, ) -> Result<Self, CurveError>
This curve read under interp between its pillars.
§Errors
CurveError::Malformed if the scheme’s own parameters are unusable —
a non-positive tension, a non-finite clamped end slope.
pub fn with_long_interpolation(
self,
long: Interpolation,
changeover: NaiveDate,
) -> Result<Self, CurveError>
pub fn with_long_interpolation( self, long: Interpolation, changeover: NaiveDate, ) -> Result<Self, CurveError>
This curve read under long from changeover onward, and under its
existing scheme before it.
changeover must be one of this curve’s own pillar dates. It becomes
the shared end knot of both sides, so both reproduce it and the curve
is continuous there — but only C⁰: the instantaneous forward jumps
across it, as it already does at every interior pillar of a
BackwardFlat curve. Matching the derivative would make each side’s
solve depend on the other’s ordinates, which is the coupling that
reading a curve in two halves exists to avoid.
§Errors
CurveError::Malformed if long’s own parameters are unusable, if
changeover is not a pillar of this curve, or if it is the last one.
pub fn adjoint_refusal(&self) -> Option<&'static str>
pub fn adjoint_refusal(&self) -> Option<&'static str>
Why this curve cannot report correct pillar sensitivities, if it cannot — the worse of its two sides.
A curve is no more differentiable than the least differentiable scheme reachable in it, which is the rule a composed family already follows, applied here across the changeover. Mixing a recording scheme with a refusing one refuses: half the adjoints would be frozen, and a ladder with a frozen half is not a ladder a caller can act on.
pub fn long_interpolation(&self) -> Option<(Interpolation, NaiveDate)>
pub fn long_interpolation(&self) -> Option<(Interpolation, NaiveDate)>
The second scheme and its changeover pillar, when this curve reads under two.
pub fn interpolation(&self) -> Interpolation
pub fn interpolation(&self) -> Interpolation
How this curve reads between its pillars.
pub fn pillar_dates(&self) -> &[NaiveDate]
pub fn pillar_dates(&self) -> &[NaiveDate]
The free pillar dates, in order.
pub fn parameters(&self) -> &[f64]
pub fn parameters(&self) -> &[f64]
The stored parameters — log discount factors at the free pillars.
pub fn view<R: Scalar>(&self) -> PiecewiseView<'_, R>
pub fn view<R: Scalar>(&self) -> PiecewiseView<'_, R>
This curve lifted into mode R.
The view borrows this storage; for the passive mode it borrows the parameters too, so a value-only valuation allocates nothing per curve.
pub fn with_parameters(&self, params: &[f64]) -> Result<Self, CurveError>
pub fn with_parameters(&self, params: &[f64]) -> Result<Self, CurveError>
This curve with params substituted, keeping every date and year
fraction. The shape is what calibration and a bumped reprice vary.
§Errors
CurveError::Malformed if params is not the same length as the
curve’s own parameter vector.
pub fn view_seeded<R: Scalar + Seedable>(
&self,
index: usize,
) -> Result<PiecewiseView<'_, R>, CurveError>
pub fn view_seeded<R: Scalar + Seedable>( &self, index: usize, ) -> Result<PiecewiseView<'_, R>, CurveError>
This curve lifted into mode R with pillar index seeded — a unit
tangent there and zero elsewhere. The single-input, single-output shape
the sequential calibration solve needs.
§Errors
CurveError::NoSuchParameter if index is not a pillar of this curve.
pub(crate) fn view_with_slice<'a, R: Scalar>(
&'a self,
rest: &mut &'a [R],
) -> PiecewiseView<'a, R>
pub(crate) fn view_with_slice<'a, R: Scalar>( &'a self, rest: &mut &'a [R], ) -> PiecewiseView<'a, R>
This curve lifted into mode R over parameters supplied by the caller,
borrowed rather than copied. See
take_params.
pub(crate) fn view_seeding<R: Scalar + Seedable>(
&self,
seed: &mut Option<usize>,
) -> PiecewiseView<'_, R>
pub(crate) fn view_seeding<R: Scalar + Seedable>( &self, seed: &mut Option<usize>, ) -> PiecewiseView<'_, R>
One step of the seeded walk. See take_seed.
fn short_side<'a, R: Scalar>(&'a self, params: &'a [R]) -> (&'a [f64], &'a [R])
fn short_side<'a, R: Scalar>(&'a self, params: &'a [R]) -> (&'a [f64], &'a [R])
The short side’s knots: the pillars up to and including the changeover, or all of them on a single-scheme curve. The implicit anchor is its left end, exactly as for an unmixed curve.
fn long_ordinates<R: Scalar>(params: &[R], k: usize) -> Vec<R>
fn long_ordinates<R: Scalar>(params: &[R], k: usize) -> Vec<R>
The long side’s knots, shifted so the changeover pillar sits where
the anchor sits on the short side — at (0, 0).
The shift is what lets one solver serve both halves untouched: every
scheme takes its leftmost knot to be the implicit (0, 0), so
subtracting the changeover’s year fraction and its ordinate makes the
changeover play that role. A scheme reading only differences between
knots — every spline, and the log-linear and forward-flat readings — is
unaffected by this, because the system is linear and shifting every
ordinate by a constant shifts the interpolant by the same constant.
The two rate-based schemes are the exception and it is a real one.
BackwardFlat and LinearZero read −y/τ, a rate measured from
their leftmost knot. On the long side that knot is the changeover, so
the rate they hold flat or interpolate is a forward zero rate from
the changeover rather than a spot zero rate from the reference date.
That is a different quantity, and it is the one a long-end scheme
arguably wants — but it is not what an unmixed curve means by those
schemes, so it is stated rather than left to be discovered.
fn tau(&self, date: NaiveDate) -> Result<f64, CurveError>
fn tau(&self, date: NaiveDate) -> Result<f64, CurveError>
Year fraction from this curve’s reference date.
fn tau_at_ordinal(&self, ordinal: i32) -> Result<f64, CurveError>
fn tau_at_ordinal(&self, ordinal: i32) -> Result<f64, CurveError>
Year fraction from this curve’s reference date, by day ordinal.
fn measured_tau(&self, date: NaiveDate) -> Result<f64, CurveError>
fn measured_tau(&self, date: NaiveDate) -> Result<f64, CurveError>
Year fraction from this curve’s reference date under the measure it
reports rates in — the companion to tau, which is the
abscissa. Equal to it, bit for bit, on the ACT/365 default.
Trait Implementations§
§impl Clone for PiecewiseDiscount
impl Clone for PiecewiseDiscount
§fn clone(&self) -> PiecewiseDiscount
fn clone(&self) -> PiecewiseDiscount
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read more§impl Debug for PiecewiseDiscount
impl Debug for PiecewiseDiscount
§impl PartialEq for PiecewiseDiscount
impl PartialEq for PiecewiseDiscount
§fn eq(&self, other: &PiecewiseDiscount) -> bool
fn eq(&self, other: &PiecewiseDiscount) -> bool
self and other values to be equal, and is used by ==.