Struct Leg
pub struct Leg {
coupons: Vec<AnyCoupon>,
direction: Direction,
accumulation: Accumulation,
non_deliverable: Option<NonDeliverable>,
}Expand description
A compiled leg: coupons in payment order, the side they are on, how they combine, and — for a non-deliverable leg — how they settle.
Fields§
§coupons: Vec<AnyCoupon>§direction: Direction§accumulation: Accumulation§non_deliverable: Option<NonDeliverable>Implementations§
§impl Leg
impl Leg
pub fn new(coupons: Vec<AnyCoupon>, direction: Direction) -> Self
pub fn new(coupons: Vec<AnyCoupon>, direction: Direction) -> Self
A leg from already-resolved coupons, each paying on its own date.
pub fn accumulating(
coupons: Vec<AnyCoupon>,
direction: Direction,
accumulation: Accumulation,
) -> Self
pub fn accumulating( coupons: Vec<AnyCoupon>, direction: Direction, accumulation: Accumulation, ) -> Self
A leg whose coupons accumulate into one payment rather than paying separately — a zero-coupon swap’s fixed or floating side.
pub fn payment_dates(&self) -> Vec<NaiveDate>
pub fn payment_dates(&self) -> Vec<NaiveDate>
The dates this leg pays on, one per payment, in leg order: each coupon’s when it pays per coupon; when it accumulates, its single payment date in place of its first accruing coupon. A principal exchange is always a payment of its own, so an accumulating leg’s exchanges stand beside its one accumulated payment.
pub fn with_exchanges(
self,
initial: Option<(NaiveDate, f64)>,
last: Option<(NaiveDate, f64)>,
) -> Result<Self, NonDeliverableError>
pub fn with_exchanges( self, initial: Option<(NaiveDate, f64)>, last: Option<(NaiveDate, f64)>, ) -> Result<Self, NonDeliverableError>
This leg with principal exchanged: initial paid first and last
paid last, each a (date, amount) in the leg’s currency signed as the
leg is oriented — a receive-oriented cross-currency leg pays out its
notional at the start (negative) and takes it back at the end. Kept
as Redemption flows in the
leg, so a valuation, its records and any conversion read one list.
§Errors
NonDeliverableError::UnconvertedExchange when the leg already
settles non-deliverably and an exchange is added: its conversions
number the payments it had, and a new payment has none. Exchange
principal first, then settle the leg.
pub(crate) fn exchanging(
self,
initial: Option<(NaiveDate, f64)>,
last: Option<(NaiveDate, f64)>,
) -> Self
pub(crate) fn exchanging( self, initial: Option<(NaiveDate, f64)>, last: Option<(NaiveDate, f64)>, ) -> Self
with_exchanges on a leg the caller knows is
deliverable.
pub fn without_exchanges(self) -> Self
pub fn without_exchanges(self) -> Self
This leg without its principal exchanges: its rate-bearing coupons, a non-deliverable leg keeping the conversions they pay at.
pub fn without_initial_exchange(self) -> Self
pub fn without_initial_exchange(self) -> Self
This leg without its initial exchange — every exchange paid before its first rate-bearing coupon — and that exchange’s conversion on a non-deliverable leg. Read by position, not by sign, so a leg on a negative notional loses the same exchange.
fn dropping_exchanges(self, drop: impl Fn(usize) -> bool) -> Self
fn dropping_exchanges(self, drop: impl Fn(usize) -> bool) -> Self
This leg without the exchanges at the coupon positions drop picks,
a non-deliverable leg’s conversions for them taken out with them, so
the conversions still number its payments.
pub fn exchanges(&self) -> Vec<(NaiveDate, f64)>
pub fn exchanges(&self) -> Vec<(NaiveDate, f64)>
This leg’s principal exchanges, (date, signed amount) in leg order,
the leg’s direction not applied.
pub fn with_non_deliverable(
self,
nd: NonDeliverable,
) -> Result<Self, NonDeliverableError>
pub fn with_non_deliverable( self, nd: NonDeliverable, ) -> Result<Self, NonDeliverableError>
This leg settling non-deliverably: each payment converted into
nd.settlement at the FX fixing on its date.
§Errors
NonDeliverableError when the settlement currency is the leg’s own,
the FX series is not their pair, the fixing dates do not number the
payments, or a payment fixes after it pays — each naming what failed.
pub fn non_deliverable(&self) -> Option<&NonDeliverable>
pub fn non_deliverable(&self) -> Option<&NonDeliverable>
How this leg settles non-deliverably, or None for a leg paid in the
currency it accrues in.
pub fn non_deliverable_payments<R: Scalar, C: CurveByIndex<R> + ?Sized>(
&self,
curves: &C,
fixings: &FixingsView<'_, R>,
as_of: NaiveDate,
) -> Result<Option<Vec<(R, NaiveDate, Conversion)>>, CashflowError>
pub fn non_deliverable_payments<R: Scalar, C: CurveByIndex<R> + ?Sized>( &self, curves: &C, fixings: &FixingsView<'_, R>, as_of: NaiveDate, ) -> Result<Option<Vec<(R, NaiveDate, Conversion)>>, CashflowError>
The payments still to come as of as_of — each its projected amount
in the leg’s own currency, unsigned and undiscounted, with its payment
date and its conversion — for a non-deliverable leg; None for a
deliverable one. A leg that accumulates makes its one payment, beside
any principal exchanges.
§Errors
As cashflows.
pub fn accumulation(&self) -> Accumulation
pub fn accumulation(&self) -> Accumulation
How this leg’s coupons combine.
fn accruing(&self) -> impl Iterator<Item = &AnyCoupon>
fn accruing(&self) -> impl Iterator<Item = &AnyCoupon>
The coupons that accrue — every one but a principal exchange.
pub(crate) fn with_fixed_rate(self, rate: f64) -> Self
pub(crate) fn with_fixed_rate(self, rate: f64) -> Self
This leg with every fixed coupon’s rate set to rate, its dates,
accruals, side and accumulation kept — the leg a par-quoted swap or
CDS rung is at another quote.
pub(crate) fn with_floating_spread(self, spread: f64) -> Self
pub(crate) fn with_floating_spread(self, spread: f64) -> Self
This leg with every IBOR and overnight coupon’s spread set to
spread, everything else kept — the leg a spread-quoted basis rung is
at another quote.
pub fn flipped(self) -> Self
pub fn flipped(self) -> Self
This leg on the other side, its coupons and accumulation untouched.
A product that can be booked either way flips its legs with itself, so
what legs() hands out is the trade rather than one canonical
orientation with the real one held beside it.
pub fn with_payoff(self, payoff: RatePayoff) -> Self
pub fn with_payoff(self, payoff: RatePayoff) -> Self
This leg with payoff on the rate of every IBOR, overnight, CMS and
year-on-year inflation coupon, and on every CPI notional — whose
bounds are annualised zero-coupon rates from its base date (see
CpiRedemption). Other
coupons are unchanged.
pub fn with_collar(self, collar: Collar) -> Self
pub fn with_collar(self, collar: Collar) -> Self
This leg with collar on the rate of every IBOR, overnight and CMS
coupon.
pub fn with_digital(self, digital: Digital) -> Self
pub fn with_digital(self, digital: Digital) -> Self
This leg with digital on the rate of every IBOR, overnight and CMS
coupon.
pub fn option_currency(&self) -> Option<Currency>
pub fn option_currency(&self) -> Option<Currency>
The currency of the first coupon on this leg carrying an option on its
rate — the cap vols a product holding it must declare — or None.
pub fn yoy_vol_index(&self) -> Option<&str>
pub fn yoy_vol_index(&self) -> Option<&str>
The price index of the first year-on-year coupon on this leg carrying
an option on its rate — the YoY vols a product holding it must
declare — or None.
pub fn cpi_vol_index(&self) -> Option<&str>
pub fn cpi_vol_index(&self) -> Option<&str>
The price index of the first CPI notional on this leg carrying an
option on its growth — the zero-coupon CPI vols a product holding it
must declare — or None.
pub fn swaption_currency(&self) -> Option<Currency>
pub fn swaption_currency(&self) -> Option<Currency>
The currency of the first CMS coupon on this leg — the swaption vols a
product holding it must declare — or None.
pub fn cms_spread_correlation(&self) -> Option<(&str, &str)>
pub fn cms_spread_correlation(&self) -> Option<(&str, &str)>
The two swap-index codes of the first CMS spread coupon on this leg
that reads its correlation from the market — the pair a product holding
it must declare — or None.
pub fn price_index(&self) -> Option<&str>
pub fn price_index(&self) -> Option<&str>
The price index the leg’s CPI coupons are scaled by, or None for a
leg without one — the inflation key the leg declares.
pub fn currency(&self) -> Option<Currency>
pub fn currency(&self) -> Option<Currency>
The currency the leg pays in, or None for an empty leg.
A leg whose coupons disagree is not a leg, so this reports the first and leaves the check to construction.
pub fn cashflows<R: Scalar, C: CurveByIndex<R> + ?Sized>(
&self,
curves: &C,
fixings: &FixingsView<'_, R>,
as_of: NaiveDate,
) -> Result<Vec<Cashflow<R>>, CashflowError>
pub fn cashflows<R: Scalar, C: CurveByIndex<R> + ?Sized>( &self, curves: &C, fixings: &FixingsView<'_, R>, as_of: NaiveDate, ) -> Result<Vec<Cashflow<R>>, CashflowError>
This leg’s cashflows in mode R, each coupon projected off the curve
curves holds for its index, as of as_of.
§Errors
Whatever a coupon reports — a missing historical fixing, a missing forward curve for an unfixed coupon, or a curve query before its own reference date.
pub fn npv<R: Scalar, C: CurveByIndex<R> + ?Sized>(
&self,
disc: &dyn YieldCurve<R>,
curves: &C,
fixings: &FixingsView<'_, R>,
) -> Result<R, CashflowError>
pub fn npv<R: Scalar, C: CurveByIndex<R> + ?Sized>( &self, disc: &dyn YieldCurve<R>, curves: &C, fixings: &FixingsView<'_, R>, ) -> Result<R, CashflowError>
The leg’s present value: each coupon’s amount projected off the curve
curves holds for its index, discounted on disc, valued as of the
discount curve’s own reference date.
The direction’s sign is applied once here rather than per coupon, so an active mode records one negation for the leg.
§Errors
As cashflows.
pub fn coupons_npv<R: Scalar, C: CurveByIndex<R> + ?Sized>(
&self,
disc: &dyn YieldCurve<R>,
curves: &C,
fixings: &FixingsView<'_, R>,
) -> Result<R, CashflowError>
pub fn coupons_npv<R: Scalar, C: CurveByIndex<R> + ?Sized>( &self, disc: &dyn YieldCurve<R>, curves: &C, fixings: &FixingsView<'_, R>, ) -> Result<R, CashflowError>
pub fn accumulated_amount<R: Scalar, C: CurveByIndex<R> + ?Sized>(
&self,
curves: &C,
fixings: &FixingsView<'_, R>,
as_of: NaiveDate,
) -> Result<R, CashflowError>
pub fn accumulated_amount<R: Scalar, C: CurveByIndex<R> + ?Sized>( &self, curves: &C, fixings: &FixingsView<'_, R>, as_of: NaiveDate, ) -> Result<R, CashflowError>
What a non-summing leg pays, before discounting and before the
direction’s sign: N · (growth − 1) under the leg’s accumulation.
Unsigned and undiscounted so the one caller that wants the bare amount — a zero-coupon swap quoting its own sides — reads the same arithmetic the valuation runs, rather than a second copy of it.
Every coupon counts, including one whose own accrual has passed: a zero-coupon leg has not paid until its single payment date, so a reset already behind the valuation is realised growth this leg still owes.
§Errors
As cashflows, and
CashflowError::MissingContractRate where an accrued-rate leg holds
a coupon carrying no contract rate to accrue.
pub fn accrued_amount(&self) -> Result<f64, CashflowError>
pub fn accrued_amount(&self) -> Result<f64, CashflowError>
What an accrued-rate leg pays, needing no market at all.
The rate is written into the trade and the period is fixed, so this is
contract arithmetic: N · (growth − 1), where the growth is K·τ
simple, (1 + K)^τ compounded and exp(K·τ) continuous. Computing it
passively is also what keeps it bit-identical to the closed form each
zero-coupon pricer used to carry.
accumulated_amount is this, lifted — one
body — for the leg walk that does have a market in hand.
§Errors
CashflowError::MissingContractRate where a coupon carries no
contract rate to accrue.
pub fn npv_and_bpv<R: Scalar, C: CurveByIndex<R> + ?Sized>(
&self,
disc: &dyn YieldCurve<R>,
curves: &C,
fixings: &FixingsView<'_, R>,
) -> Result<(R, R), CashflowError>
pub fn npv_and_bpv<R: Scalar, C: CurveByIndex<R> + ?Sized>( &self, disc: &dyn YieldCurve<R>, curves: &C, fixings: &FixingsView<'_, R>, ) -> Result<(R, R), CashflowError>
The leg’s present value and its analytic annuity from one walk:
(npv, bpv), both signed by the direction.
A par spread needs the floating leg’s value and its annuity over the same coupons and the same discount factors; reading them off one pass evaluates each payment’s discount factor once rather than twice.
§Errors
As npv.
fn walk<R: Scalar, C: CurveByIndex<R> + ?Sized>(
&self,
disc: &dyn YieldCurve<R>,
curves: &C,
fixings: &FixingsView<'_, R>,
with_weights: bool,
skip_exchanges: bool,
) -> Result<(Vec<R>, Vec<R>, Vec<f64>), CashflowError>
fn walk<R: Scalar, C: CurveByIndex<R> + ?Sized>( &self, disc: &dyn YieldCurve<R>, curves: &C, fixings: &FixingsView<'_, R>, with_weights: bool, skip_exchanges: bool, ) -> Result<(Vec<R>, Vec<R>, Vec<f64>), CashflowError>
One pass over the future coupons: each one’s projected amount, the discount factor at its payment, and — when asked — its basis-point weight. Everything a leg-level measure fuses over, in two vectors sized once and no intermediate cashflow.
The valuation date is the discount curve’s anchor: a coupon paid on or before it has been realised, and a reset on or before it has happened. One date decides both, and already-paid coupons are dropped before projection so no historical fixing the result cannot depend on is demanded.
The boundary carry. Adjacent projected IBOR coupons share a curve
date — one’s accrual end is the next’s accrual start — so the discount
factor read at the end of one period is carried into the start of the
next rather than read again. And when the projection curve is the
discounting curve and a coupon pays on its accrual end, the same factor
discounts the payment. A leg of n contiguous coupons then reads
n + 1 factors off its curve, not 2n or 3n. The carried value is
the identical number the curve would have produced from the identical
date, so nothing about the value changes — only how often it is
computed. A gap in the schedule simply misses the carry.
pub fn bpv<R: Scalar>(
&self,
disc: &dyn YieldCurve<R>,
) -> Result<R, CashflowError>
pub fn bpv<R: Scalar>( &self, disc: &dyn YieldCurve<R>, ) -> Result<R, CashflowError>
The leg’s analytic annuity — the value of one basis point on every
coupon’s rate, discounted on disc.
A weighted sum with passive weights (notional · tau · 1bp), so an
active mode records one n-ary statement rather than a chain.
§Errors
Whatever the discount curve reports.
§impl Leg
What a bare leg reads, and its value against a market. Written here, beside
the declaration it builds, so the edge points from the market layer down to
the cashflow layer and not back.
impl Leg
What a bare leg reads, and its value against a market. Written here, beside the declaration it builds, so the edge points from the market layer down to the cashflow layer and not back.
pub fn projections(&self) -> Vec<String>
pub fn projections(&self) -> Vec<String>
The distinct indices this leg’s coupons name, in first-seen order — an interpolated stub’s two tenors among them.
pub fn projections_of(coupons: &[AnyCoupon]) -> Vec<String>
pub fn projections_of(coupons: &[AnyCoupon]) -> Vec<String>
The distinct indices coupons name, in first-seen order: what
projections reads off a leg’s own coupons.
pub fn risk_factors<'a>(
&'a self,
discount: &'a CollateralSpec,
projections: &'a [String],
) -> RiskFactors<'a>
pub fn risk_factors<'a>( &'a self, discount: &'a CollateralSpec, projections: &'a [String], ) -> RiskFactors<'a>
What this leg declares it is priced against, discounted on discount:
the first of projections (this leg’s projections)
as the projection, the second as the second projection, the rest as the
projection basket — each coupon is priced off the curve its own index
names — and the cap vols, swaption vols, CMS spread correlation, price
index and inflation vols its coupons read.
pub fn npv_on(
&self,
market: &MarketContext,
discount: &CollateralSpec,
) -> Result<f64, PricingError>
pub fn npv_on( &self, market: &MarketContext, discount: &CollateralSpec, ) -> Result<f64, PricingError>
This leg’s present value against market, discounted on discount:
its own declaration resolved through
MarketContext::priced.
§Errors
Whatever the resolution refuses, and whatever a coupon reports.
pub fn cashflow_records(
&self,
market: &MarketContext,
discount: &CollateralSpec,
) -> Result<Records, PricingError>
pub fn cashflow_records( &self, market: &MarketContext, discount: &CollateralSpec, ) -> Result<Records, PricingError>
This leg’s cashflow schedule against market, discounted on
discount: the table, with its columns in their order, that
AnyInstrument::cashflow_records
produces for an instrument holding it, as of the discount curve’s date.
§Errors
Whatever the resolution refuses, and whatever a coupon reports.
pub fn bps(
&self,
market: &MarketContext,
discount: &CollateralSpec,
) -> Result<f64, PricingError>
pub fn bps( &self, market: &MarketContext, discount: &CollateralSpec, ) -> Result<f64, PricingError>
This leg’s basis-point sensitivity against market: one basis point of
every accruing coupon’s rate, discounted — Σ notional · year_fraction · discount_factor · 1e-4 over the rows cashflow_records gives.
A redemption accrues nothing; a paid leg’s
BPS is negative. The sum of bps_by_flow.
§Errors
Whatever cashflow_records refuses.
pub fn bps_by_flow(
&self,
market: &MarketContext,
discount: &CollateralSpec,
) -> Result<Vec<f64>, PricingError>
pub fn bps_by_flow( &self, market: &MarketContext, discount: &CollateralSpec, ) -> Result<Vec<f64>, PricingError>
bps flow by flow: one figure per row of
cashflow_records, in its order, each flow
alone. A row that accrues
nothing, a redemption, gives zero.
§Errors
Whatever cashflow_records refuses.