Struct BondFuturePosition
pub struct BondFuturePosition {
pub delivery: NaiveDate,
pub deliverables: Vec<Deliverable>,
pub(crate) repo_keys: Vec<String>,
pub currency: Currency,
pub collateral: CollateralSpec,
pub contracts: f64,
pub contract_size: f64,
pub traded_price: f64,
pub side: f64,
}Expand description
A bookable bond future position: a basket, a size, and a traded price.
The contract BondFuture is reference data — a delivery date and a list of
ISINs. This is a position in it, and the difference is what makes it
valuable on a book: it carries its deliverables already resolved to
bonds, so pricing consults no security master, and it carries one repo key
per deliverable, so the seam can hand it one carry curve per row.
The value is undiscounted. A bond future is margined daily, so the position
settles variation margin rather than a discounted claim, exactly as
RateFuture does.
Fields§
§delivery: NaiveDateThe delivery date the basket forwards to.
deliverables: Vec<Deliverable>The deliverables, resolved once at construction.
repo_keys: Vec<String>The carry keys, BondFuturePosition::CARRY_TIERS per deliverable, in
deliverable order then tier order.
The passive analytics run a cascade against the market — the row’s own key, then its repo group, then its currency, then the discount curve — and the position declares the whole cascade rather than a preferred key from it. Declaring one key was the first shape and it was wrong: against a market holding a security-tier repo curve the analytics carried the row on it and the position did not, and the two named different prices for a basket both could price — 0.61 of a price point on the fixture, with no principled account of the difference.
The seam carries a flat key list and resolves each key independently, so the cascade crosses it as a fixed-stride run and the pricer takes the first key that resolved. A deliverable with no repo group repeats its own key in that slot: the tiers are a preference order, not a set, and a key resolved twice costs one lookup and preserves the order exactly.
Which keys are asked is fixed at construction; whether one resolves
stays a pricing-time question, which is why the resolved slot is an
Option and a row no key answers carries on the discount curve.
Not public, and read through carry_keys. The list
is a pure function of deliverables —
carry_cascade is that function — and it exists
as a field only because risk_factors hands out keys borrowed from
the position, so something here must own the strings.
Two mechanisms guard the correspondence between this list and the basket,
because they cover different populations. Narrowing the field to the crate
means no caller outside it can set the two apart — a struct literal that
desynchronised them no longer compiles. Inside the crate a literal still
can, which is why
cascade_is_well_formed is checked where
the declaration is made. Neither is redundant: without the check a short
list reads past the end of the resolved slots into exactly the
discount-curve fall back a market gap produces, and prices silently.
currency: CurrencyThe currency the position settles in.
collateral: CollateralSpecThe collateral its discounting is read under.
contracts: f64The number of contracts held.
contract_size: f64The face each contract delivers — 100_000 for the Treasury complex.
A price point is worth contract_size / 100.
traded_price: f64The price the position was struck at, per 100.
side: f64+1 long, −1 short.
Implementations§
§impl BondFuturePosition
impl BondFuturePosition
pub const CARRY_TIERS: usize = 3
pub const CARRY_TIERS: usize = 3
How many carry keys a deliverable declares: the security, group and
currency tiers of RepoTier, in that order.
pub fn carry_cascade(deliverable: &Deliverable) -> [String; 3]
pub fn carry_cascade(deliverable: &Deliverable) -> [String; 3]
One deliverable’s carry cascade, in preference order: its own key, its repo group, its currency.
The single definition of a row’s run. A deliverable with no repo group
repeats its own key rather than shortening the run — the tiers are a
preference order, not a set, so a key resolved twice costs one lookup and
keeps the stride exactly CARRY_TIERS wide. Stating
the width and the content in one place is what keeps them from drifting.
pub fn carry_keys(&self) -> &[String]
pub fn carry_keys(&self) -> &[String]
The declared carry keys: one CARRY_TIERS-wide run
per deliverable, in deliverable order then tier order.
pub fn cascade_is_well_formed(&self) -> bool
pub fn cascade_is_well_formed(&self) -> bool
Whether the declared key list holds exactly one full run per deliverable.
The correspondence the seam cannot express: it carries the cascade as a flat list with no arity, so this is the only thing standing between a desynchronised list and a silent mispricing.
pub fn new(
future: &BondFuture,
securities: &Securities,
contracts: f64,
contract_size: f64,
traded_price: f64,
) -> Result<Self, BondFutureError>
pub fn new( future: &BondFuture, securities: &Securities, contracts: f64, contract_size: f64, traded_price: f64, ) -> Result<Self, BondFutureError>
A position in future, resolving every basket ISIN through the security
master once, here, so that pricing never needs one.
Each row declares its whole carry cascade — its ISIN, its repo group, its currency — so the position and the passive analytics carry every row the same way on any market both can price.
§Errors
BondFutureError::UnknownDeliverable for an ISIN the master does not
hold, and BondFutureError::NotFixedCoupon for a deliverable that is
not a fixed-coupon bond.
pub fn from_deliverables(
delivery: NaiveDate,
deliverables: Vec<Deliverable>,
contracts: f64,
contract_size: f64,
traded_price: f64,
) -> Self
pub fn from_deliverables( delivery: NaiveDate, deliverables: Vec<Deliverable>, contracts: f64, contract_size: f64, traded_price: f64, ) -> Self
A position over deliverables the caller has already resolved.
§Panics
If deliverables is empty: the position takes its currency from the
first deliverable, and a basket with nothing in it has no currency to
take. A future over no deliverable is not a position.
pub fn short(self) -> Self
pub fn short(self) -> Self
The short side.
pub fn price_point_value(&self) -> f64
pub fn price_point_value(&self) -> f64
What one price point is worth to this position.
§impl BondFuturePosition
impl BondFuturePosition
pub fn model_price<R: Scalar>(
&self,
view: &PricingView<'_, R>,
) -> Result<R, PricingError>
pub fn model_price<R: Scalar>( &self, view: &PricingView<'_, R>, ) -> Result<R, PricingError>
The theoretical futures price: the cheapest-to-deliver’s forward clean price over its conversion factor.
Mode-generic, which is the whole point — the passive analytics on
BondFuture answer the same question without derivatives, and this
answers it in whichever mode the caller lifted, so a booked position
reports risk.
Each row carries on its own basket curve, falling back to the discount
curve where the market holds none, exactly as analyze_resolved does.
§Errors
PricingError for a curve that will not resolve or answer, and for an
empty basket or a delivery on or before the valuation date.
fn repo_basket_curve<'v, R: Scalar>(
view: &'v PricingView<'_, R>,
i: usize,
) -> Option<&'v ResolvedCurve<'v, R>>
fn repo_basket_curve<'v, R: Scalar>( view: &'v PricingView<'_, R>, i: usize, ) -> Option<&'v ResolvedCurve<'v, R>>
The carry curve for deliverable i: the first of its declared cascade
that resolved, or None for the discount fall back — a row no key
answered, or a basket the view did not resolve because no repo basket
was declared.
The keys cross the seam as a flat list with a fixed stride, so row i’s
tiers are the run at i · CARRY_TIERS, in preference order. Taking the
first Some of that run is the cascade analyze_resolved walks.
Trait Implementations§
§impl Clone for BondFuturePosition
impl Clone for BondFuturePosition
§fn clone(&self) -> BondFuturePosition
fn clone(&self) -> BondFuturePosition
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 BondFuturePosition
impl Debug for BondFuturePosition
§impl PartialEq for BondFuturePosition
impl PartialEq for BondFuturePosition
§fn eq(&self, other: &BondFuturePosition) -> bool
fn eq(&self, other: &BondFuturePosition) -> bool
self and other values to be equal, and is used by ==.§impl<R: Scalar> Priceable<R> for BondFuturePosition
impl<R: Scalar> Priceable<R> for BondFuturePosition
§fn npv(&self, view: &PricingView<'_, R>) -> Result<R, PricingError>
fn npv(&self, view: &PricingView<'_, R>) -> Result<R, PricingError>
side · contracts · price-point value · (model − traded), in the
position’s currency and undiscounted: a daily-margined future
settles variation margin, so there is no discounted claim to value.
§fn par_rate(&self, view: &PricingView<'_, R>) -> Result<R, PricingError>
fn par_rate(&self, view: &PricingView<'_, R>) -> Result<R, PricingError>
The futures price at which the position is worth nothing — its model price.
§fn par_spread(&self, _view: &PricingView<'_, R>) -> Result<R, PricingError>
fn par_spread(&self, _view: &PricingView<'_, R>) -> Result<R, PricingError>
§impl PricingRiskFactors for BondFuturePosition
impl PricingRiskFactors for BondFuturePosition
§fn risk_factors(&self) -> Result<RiskFactors<'_>, PricingError>
fn risk_factors(&self) -> Result<RiskFactors<'_>, PricingError>
Discounts on the declared collateral and carries every deliverable on its own key — the variadic repo slot, mirroring the credit basket an nth-to-default swap declares.
The cascade crosses the seam as a flat list read at a fixed stride, and
the seam carries no arity: nothing downstream can tell a run short of its
basket from a market holding no curve, because both surface as a None
slot and both are read as the discount-curve fall back. So the
correspondence is checked here, where the list is committed to, rather
than at the read that cannot distinguish them.
§Errors
PricingError::MalformedDeclaration when the declared key list does
not hold exactly one CARRY_TIERS-wide
run per deliverable.
§impl Product for BondFuturePosition
impl Product for BondFuturePosition
§fn currency(&self) -> Currency
fn currency(&self) -> Currency
§fn contract_dates(&self) -> Vec<(&'static str, NaiveDate)>
fn contract_dates(&self) -> Vec<(&'static str, NaiveDate)>
(name, date) pairs —
exactly the dates its stated constructor takes, so a product built
through a derived form can be read back and rebuilt through the stated
one. Empty for a product with no stated form. No default: every
product states which it is — see AnyInstrument::contract_dates.§fn legs(&self) -> Option<Vec<&Leg>>
fn legs(&self) -> Option<Vec<&Leg>>
None
for a product that holds none — see AnyInstrument::legs. No
default: every product states which it is.§fn final_payment_date(&self) -> Option<NaiveDate>
fn final_payment_date(&self) -> Option<NaiveDate>
None for a product no ladder is built from.§fn restruck_at_rate(&self, _rate: f64) -> Option<AnyInstrument>
fn restruck_at_rate(&self, _rate: f64) -> Option<AnyInstrument>
rate — the scalar a par
calibration rung on it is quoted in — or None for a product that
carries no such scalar. What a quote scenario moves a rung to.§fn restruck_at_spread(&self, _spread: f64) -> Option<AnyInstrument>
fn restruck_at_spread(&self, _spread: f64) -> Option<AnyInstrument>
spread on its spread
leg, or None for a product that quotes none.