Skip to main content

PiecewiseDiscount

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: i32

reference_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: Interpolation

How 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: DayCountFraction

The 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

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

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

The day count this curve forms a rate under.

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>

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>

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)>

The second scheme and its changeover pillar, when this curve reads under two.

pub fn interpolation(&self) -> Interpolation

How this curve reads between its pillars.

pub fn pillar_dates(&self) -> &[NaiveDate]

The free pillar dates, in order.

pub fn parameters(&self) -> &[f64]

The stored parameters — log discount factors at the free pillars.

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>

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>

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>

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>

One step of the seeded walk. See take_seed.

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>

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>

Year fraction from this curve’s reference date.

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>

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

§

fn clone(&self) -> PiecewiseDiscount

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
§

impl Debug for PiecewiseDiscount

§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
§

impl PartialEq for PiecewiseDiscount

§

fn eq(&self, other: &PiecewiseDiscount) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
§

impl StructuralPartialEq for PiecewiseDiscount

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.