Struct Book
pub struct Book {
trades: Vec<Trade>,
reporting: Option<Currency>,
scales: Scales,
market: Option<MarketContext>,
fetched: FetchReport,
moved_from: Option<MovedFrom>,
}Expand description
A collection of trades valued together.
No scalar type parameter — see the module note. The mode is chosen when a value is asked for, not when the book is built.
Fields§
§trades: Vec<Trade>§reporting: Option<Currency>The currency the book reports in. None means the trades’ own single
currency — a mixed-currency book must declare one, because amounts in
different currencies do not add.
scales: ScalesThe scale each quantity kind reports in where a measure does not state its own.
market: Option<MarketContext>The market the book’s verbs value against, at the as_of each is
asked for. Not part of equality: two books of the same trades are the
same book whatever they are marked against.
fetched: FetchReportWhat the book’s last fetch asked for and got. Not part of equality.
moved_from: Option<MovedFrom>The market demand_for moved away from, kept
until install settles the fetch: put back on a
refusal, forgotten on success. Not part of equality.
Implementations§
§impl Book
impl Book
§impl Book
impl Book
pub fn new() -> Self
pub fn new() -> Self
An empty book.
pub fn reporting_in(self, currency: Currency) -> Self
pub fn reporting_in(self, currency: Currency) -> Self
This book reporting in currency: every aggregate — value, scenario
value, risk — converts per-trade values into it through the market’s
FX spots. Required for a mixed-currency book; harmless on a
single-currency one.
pub fn scaled(self, scales: Scales) -> Self
pub fn scaled(self, scales: Scales) -> Self
This book reporting each quantity kind in scales wherever a measure
does not state its own scale — ahead of a preset’s default, behind the
measure’s. See Request::resolve.
pub fn available_measures_on(
&self,
market: Option<&MarketContext>,
) -> Result<Vec<&'static Preset>, PricingError>
pub fn available_measures_on( &self, market: Option<&MarketContext>, ) -> Result<Vec<&'static Preset>, PricingError>
The catalogued measures this book’s trades can answer, in catalogue order.
With no market, a preset is listed when some trade declares its axis —
read off the same RiskFactors a trade declares to be priced — and a
bumped one when some trade reads what it bumps. With a market, a par
preset is dropped when a curve a trade reads on its axis is held
without the ladder that calibrated it, and a vol preset when a surface
a trade reads is not held — the refusals a report would otherwise meet
after paying for the pass.
§Errors
PricingError from a trade that cannot state its risk factors.
pub(crate) fn reporting_currency(
&self,
) -> Result<Option<Currency>, PricingError>
pub(crate) fn reporting_currency( &self, ) -> Result<Option<Currency>, PricingError>
The currency aggregates report in: the declared one, or the single
currency the trades share. None for an empty book.
§Errors
PricingError::MixedCurrencyBook if the trades span currencies and
none was declared.
pub fn add(
self,
id: impl Into<String>,
instrument: AnyInstrument,
quantity: f64,
) -> Self
pub fn add( self, id: impl Into<String>, instrument: AnyInstrument, quantity: f64, ) -> Self
Books quantity of instrument under id — with on
the trade’s three terms, the line a desk writes.
pub fn extend(self, trades: impl IntoIterator<Item = Trade>) -> Self
pub fn extend(self, trades: impl IntoIterator<Item = Trade>) -> Self
This book with every trade of trades booked, in order — with
over a list, the form for a book of many trades.
pub fn npv_on(&self, market: &MarketContext) -> Result<f64, PricingError>
pub fn npv_on(&self, market: &MarketContext) -> Result<f64, PricingError>
pub fn npv_by_trade_on(
&self,
market: &MarketContext,
) -> Result<Records, PricingError>
pub fn npv_by_trade_on( &self, market: &MarketContext, ) -> Result<Records, PricingError>
Each trade’s value against market, in booking order: trade, its
npv_ccy and npv_local in it, quantity applied, and the P&L
currency pnl_ccy — the reporting one — with
npv_pnl converted into it at the market’s spot. npv_pnl sums to
npv_on.
§Errors
PricingError::MixedCurrencyBook for a mixed book with no reporting
currency, as npv_on; otherwise whatever resolution or
a pricer reports, named by trade.
pub fn npv_with<R: Scalar>(
&self,
view: &MarketView<'_, R>,
) -> Result<R, PricingError>
pub fn npv_with<R: Scalar>( &self, view: &MarketView<'_, R>, ) -> Result<R, PricingError>
The book’s value in mode R, against a market view the caller holds.
A generic method on a non-generic type: this is where the mode
enters, and it enters here only. The _with form of npv:
it borrows the caller’s view rather than lifting the market itself.
§Errors
Whatever resolution or a pricer reports.
pub fn risk_with(
&self,
market: &MarketContext,
tape: &mut Tape<f64>,
) -> Result<(f64, RiskVector), RiskError>
pub fn risk_with( &self, market: &MarketContext, tape: &mut Tape<f64>, ) -> Result<(f64, RiskVector), RiskError>
pub fn risk_on(
&self,
market: &MarketContext,
) -> Result<(f64, RiskVector), RiskError>
pub fn risk_on( &self, market: &MarketContext, ) -> Result<(f64, RiskVector), RiskError>
The book’s value and its risk vector, with no tape to supply.
The reverse mode is selected internally and the tape is created and dropped here, so a caller never names one. That is what lets a binding layer offer risk without depending on the AD backend at all — the Python crate cannot mention a tape because it cannot reach one.
The tape is thread-local and kept warm: the first risk pass on a
thread grows it, and every later pass on that thread reuses the
buffers — Tape::record clears the recording and keeps the capacity
— so a steady-state caller allocates no tape memory. The buffers stay
at the largest book ever risked on the thread; that is the cost of
never paying for growth twice. A caller that wants to own the tape
uses risk_with.
§Errors
RiskError from the valuation or the read-back.
§Panics
If another recording is already open on this thread, including a
re-entrant risk from inside a valuation.
pub fn report_on<R: Into<Request> + Copy>(
&self,
market: &MarketContext,
measures: &[R],
) -> Result<Vec<Row>, RiskError>
pub fn report_on<R: Into<Request> + Copy>( &self, market: &MarketContext, measures: &[R], ) -> Result<Vec<Row>, RiskError>
pub fn implied_vol_on(
&self,
market: &MarketContext,
premium: Option<f64>,
) -> Result<f64, PricingError>
pub fn implied_vol_on( &self, market: &MarketContext, premium: Option<f64>, ) -> Result<f64, PricingError>
The single flat volatility under which every option in the book, priced
at it by its own formula, sums to premium — or, with no premium, to
the book’s own value under market: the quote a strategy trades at.
The book is one strategy or it is nothing: every trade an FxOption on
one currency pair (Black-76 on the parity forward), or every trade a
surface-engine EuropeanSwaption in one currency (Black-76 or Bachelier
by the surface’s convention). A straddle at one strike therefore
returns the surface’s vol at that strike exactly; a strangle returns the
one vol between its wings that reprices the pair.
Solved by Brent on the relative residual Σ qᵢ·npvᵢ(σ) / premium − 1,
so the tolerance is scale-free, over [1e-6, 5] for a lognormal quote
and [1e-8, 1] in rate units for a normal one.
§Errors
UnsupportedForProduct for an empty book, a mixed one, a Hull-White
swaption or a zero premium; UnbracketedSolve when no vol in the
bracket reaches the premium; SolverUnconverged if Brent gives up;
otherwise whatever resolution or a pricer reports.
fn strategy_options(&self) -> Vec<(&AnyInstrument, f64)>
fn strategy_options(&self) -> Vec<(&AnyInstrument, f64)>
Every option the book holds with its quantity, a straddle read as its two options.
pub fn attach_market(self, market: MarketContext) -> Self
pub fn attach_market(self, market: MarketContext) -> Self
This book carrying market: the market its verbs value against, at
the as_of each is asked for. Attaching does no IO — a snapshot the
market lacks is fetched by fetch_market_data,
and no other Rust verb calls a source.
pub fn market_context(&self) -> Option<&MarketContext>
pub fn market_context(&self) -> Option<&MarketContext>
The market this book carries, if any.
pub fn fetch_report(&self) -> &FetchReport
pub fn fetch_report(&self) -> &FetchReport
What the book’s last fetch asked for and got.
pub fn fetch_market_data(
&mut self,
as_of: NaiveDate,
) -> Result<(), SnapshotError>
pub fn fetch_market_data( &mut self, as_of: NaiveDate, ) -> Result<(), SnapshotError>
Fetches what this book needs at as_of from its market’s data source
and keeps it: the market (an undated one where the book carries none)
is moved to as_of, and recipes, the demand, one fetch and the
build follow. Where the market already holds everything the book reads,
no source is called. On a refusal the book’s market is left as it was,
and fetch_report still says what was asked.
The one Rust verb that does IO, and so the one taking &mut self.
The market keeps the snapshot of every date it was fetched at, so a
book valued daily for a year holds a year of them; drop the dates no
longer needed with MarketContext::without.
§Errors
SnapshotError — a refused
recipe, the source’s own failure, every gap at once, or a curve that
did not solve.
fn fetch_moving(
&mut self,
as_of: NaiveDate,
demand: &Demand,
) -> Result<(), SnapshotError>
fn fetch_moving( &mut self, as_of: NaiveDate, demand: &Demand, ) -> Result<(), SnapshotError>
The fetch past fetch_market_data’s fast
path: recipes, the demand, one fetch and the build, the market moved to
as_of on the way — the caller restores it on a refusal.
fn fetch_base(&self, as_of: NaiveDate) -> MarketContext
fn fetch_base(&self, as_of: NaiveDate) -> MarketContext
The book’s market at as_of as a fetch starts from: the market it
carries (an undated one where it carries none), moved to as_of.
pub fn recipe_ask(
&self,
as_of: NaiveDate,
) -> Result<Option<RecipeAsk>, SnapshotError>
pub fn recipe_ask( &self, as_of: NaiveDate, ) -> Result<Option<RecipeAsk>, SnapshotError>
The first half of a fetch: what a recipe source must be asked before
the demand at as_of can be stated — or None where nothing is to be
asked: the date asks the source for nothing, or every key the book
reads has the caller’s own recipe or was already asked at that date.
§Errors
SnapshotError::Pricing
where a trade cannot state what it reads.
pub fn demand_for(
&mut self,
as_of: NaiveDate,
recipes: &[RecipeSpec],
) -> Result<Demand, SnapshotError>
pub fn demand_for( &mut self, as_of: NaiveDate, recipes: &[RecipeSpec], ) -> Result<Demand, SnapshotError>
The second half’s start: recipes — a source’s answer to
recipe_ask — kept as the source’s recipes for
as_of, the book’s market moved to as_of, and that date’s demand.
Recipes given where nothing was asked are kept too, and no key is
recorded as asked.
§Errors
SnapshotError::Recipe
naming a recipe the builder refuses; Pricing where a trade cannot
state what it reads.
fn remember_the_move(&mut self)
fn remember_the_move(&mut self)
Keeps the market a fetch is about to move away from — the first one, where a fetch is already in flight.
fn settle(&mut self, succeeded: bool)
fn settle(&mut self, succeeded: bool)
Ends a fetch begun by demand_for: on success the
market it moved away from is forgotten, on a refusal it is put back.
pub fn install(
&mut self,
demand: &Demand,
data: &MarketData,
) -> Result<(), SnapshotError>
pub fn install( &mut self, demand: &Demand, data: &MarketData, ) -> Result<(), SnapshotError>
The last half: data — a source’s answer to demand — built into the
book’s market at demand.as_of as
MarketContext::with_market_data builds it, and kept;
fetch_report written from the two, on a
refusal as well. A demand that asks nothing and names no gap changes
nothing and leaves the last report standing.
§Errors
SnapshotError — every gap at
once, a stamp from the future, a curve that did not solve — leaving the
market as it was; Malformed where demand was stated for another
state of the book’s market than the one it would be built into.
fn install_on_the_move(
&mut self,
demand: &Demand,
data: &MarketData,
) -> Result<(), SnapshotError>
fn install_on_the_move( &mut self, demand: &Demand, data: &MarketData, ) -> Result<(), SnapshotError>
install’s body, the market already moved to the
demand’s date; install settles the move.
fn date_or_market(
&self,
as_of: Option<NaiveDate>,
) -> Result<NaiveDate, PricingError>
fn date_or_market( &self, as_of: Option<NaiveDate>, ) -> Result<NaiveDate, PricingError>
The book’s market at as_of, borrowed where it is already there.
as_of, else the date of the market the book carries.
§Errors
PricingError::Undated with no date given and none on the market.
fn market_at( &self, as_of: NaiveDate, ) -> Result<Cow<'_, MarketContext>, PricingError>
pub fn npv(
&self,
as_of: impl Into<Option<NaiveDate>>,
) -> Result<f64, PricingError>
pub fn npv( &self, as_of: impl Into<Option<NaiveDate>>, ) -> Result<f64, PricingError>
The book’s present value at as_of, against the market it carries.
§Errors
PricingError::NoSnapshot where the book’s market holds no snapshot
at as_of — fetch one with fetch_market_data
— and whatever resolution or a pricer reports.
pub fn npv_by_trade(
&self,
as_of: impl Into<Option<NaiveDate>>,
) -> Result<Records, PricingError>
pub fn npv_by_trade( &self, as_of: impl Into<Option<NaiveDate>>, ) -> Result<Records, PricingError>
Each trade’s value at as_of, in its own currency and in the P&L
currency: npv_by_trade_on against the market
the book carries.
§Errors
As npv and npv_by_trade_on.
pub fn report<R: Into<Request> + Copy>(
&self,
measures: &[R],
as_of: impl Into<Option<NaiveDate>>,
) -> Result<Vec<Row>, RiskError>
pub fn report<R: Into<Request> + Copy>( &self, measures: &[R], as_of: impl Into<Option<NaiveDate>>, ) -> Result<Vec<Row>, RiskError>
pub fn implied_vol(
&self,
premium: Option<f64>,
as_of: NaiveDate,
) -> Result<f64, PricingError>
pub fn implied_vol( &self, premium: Option<f64>, as_of: NaiveDate, ) -> Result<f64, PricingError>
The one flat volatility the book’s options reprice to premium at,
at as_of — see implied_vol_on.
§Errors
As implied_vol_on, and a missing snapshot.
pub fn available_measures(
&self,
as_of: NaiveDate,
) -> Result<Vec<&'static Preset>, PricingError>
pub fn available_measures( &self, as_of: NaiveDate, ) -> Result<Vec<&'static Preset>, PricingError>
The catalogued measures this book can answer, limited to those its
market at as_of can supply where it holds a snapshot there. Never
fetches.
§Errors
PricingError from a trade that cannot state its risk factors.
pub fn credit_bumps(
&self,
kind: Sensitivity,
bump: f64,
as_of: NaiveDate,
) -> Result<Vec<(String, f64)>, CreditBumpError>
pub fn credit_bumps( &self, kind: Sensitivity, bump: f64, as_of: NaiveDate, ) -> Result<Vec<(String, f64)>, CreditBumpError>
The bumped sensitivity kind at as_of: one (key, value) row per
credit index or entity, as the module function it names computes it.
§Errors
As the module function, and a missing snapshot.
pub fn under(
&self,
scenario: &Scenario,
as_of: NaiveDate,
) -> Result<Book, ScenarioError>
pub fn under( &self, scenario: &Scenario, as_of: NaiveDate, ) -> Result<Book, ScenarioError>
This book on its market at as_of under scenario: a new book whose
market is the shocked one, at as_of.
§Errors
ScenarioError from the shock, and a missing snapshot.
pub fn explain_pnl(
&mut self,
as_of1: NaiveDate,
as_of2: NaiveDate,
order: &[RiskAxis],
) -> Result<ExplainReport, BookPnlError>
pub fn explain_pnl( &mut self, as_of1: NaiveDate, as_of2: NaiveDate, order: &[RiskAxis], ) -> Result<ExplainReport, BookPnlError>
The step-revaluation explain of this book from its snapshot at
as_of1 to as_of2 — see explain.
Within one day (as_of1 == as_of2) the snapshot held is the start,
it is fetched again from the market’s source (which answers the latest
it holds), and the new snapshot — kept as the book’s — is the end.
Across days the snapshot held at as_of1 is the start and the book’s
fetch at as_of2 the end, and two legs come first: the cash the book
was paid after as_of1 and up to as_of2, and the carry — the book on
as_of1’s quotes rebuilt at as_of2, each print it reads in between
at the forecast as_of1’s curves projected, less its value at
as_of1. The rest is the same-day ladder from that rolled market.
What no source serves — vols, credit curves, model marks — is not
rolled: the rolled market takes it as as_of2 holds it, so its
day’s move lands in the carry.
§Errors
fn explained_across_days(
&mut self,
as_of1: NaiveDate,
as_of2: NaiveDate,
order: &[RiskAxis],
) -> Result<ExplainReport, BookPnlError>
fn explained_across_days( &mut self, as_of1: NaiveDate, as_of2: NaiveDate, order: &[RiskAxis], ) -> Result<ExplainReport, BookPnlError>
explain_pnl across days. On any failure the
book keeps the market it had.
fn rolled(
&self,
before: &MarketContext,
after: &MarketContext,
) -> Result<MarketContext, BookPnlError>
fn rolled( &self, before: &MarketContext, after: &MarketContext, ) -> Result<MarketContext, BookPnlError>
before’s quotes rebuilt at after’s date: after’s recipes, the
strips and spots before was fetched from, the prints before held,
and each print learnt since at the forecast before projected it at.
What no source serves is carried from after.
fn cash_paid(
&self,
before: &MarketContext,
to: NaiveDate,
) -> Result<f64, BookPnlError>
fn cash_paid( &self, before: &MarketContext, to: NaiveDate, ) -> Result<f64, BookPnlError>
What the book is paid after before’s date and up to to, in its
reporting currency at before’s spots: each trade’s cashflow rows
paying in that window, as before projects them, signed by side and
scaled by quantity.
pub fn predicted_pnl(
&mut self,
as_of1: NaiveDate,
as_of2: NaiveDate,
) -> Result<PredictedPnl, BookPnlError>
pub fn predicted_pnl( &mut self, as_of1: NaiveDate, as_of2: NaiveDate, ) -> Result<PredictedPnl, BookPnlError>
pub fn reset_pnl(
&mut self,
as_of1: NaiveDate,
as_of2: NaiveDate,
) -> Result<ResetPnl, BookPnlError>
pub fn reset_pnl( &mut self, as_of1: NaiveDate, as_of2: NaiveDate, ) -> Result<ResetPnl, BookPnlError>
fn refetched<T>(
&mut self,
as_of1: NaiveDate,
as_of2: NaiveDate,
attribute: impl FnOnce(&Book, &MarketContext, &MarketContext) -> Result<T, PnlError>,
) -> Result<T, BookPnlError>
fn refetched<T>( &mut self, as_of1: NaiveDate, as_of2: NaiveDate, attribute: impl FnOnce(&Book, &MarketContext, &MarketContext) -> Result<T, PnlError>, ) -> Result<T, BookPnlError>
attribute run over the held snapshot at as_of1 and a fresh fetch
of the same date, which becomes the book’s. The fetch asks again for
every print the day’s earlier fetch learnt, so a fixing restated
during the day reaches the reset leg rather than being attributed to
the curves. What the source does not serve — vols, credit curves,
model marks — is carried from the held snapshot, so the fetch leaves
it where it was. On any failure the book keeps the market it had.