Skip to main content

ledger_models/
fintekkers.requests.valuation.rs

1/// A single observation that contributes one point to the curve.
2///
3/// Two equivalent shapes are supported:
4///
5///    1. Bond + price (the typical case): provide `security` (with `issue_date`
6///       and `maturity_date` populated) and either `price` (already a yield) or
7///       `clean_price` (server runs YTM internally to derive the yield). The
8///       server computes the tenor from `(maturity_date - asof_datetime)`.
9///
10///    2. Synthetic CMT-style point: provide `tenor` and `price` directly. Use
11///       this for inputs that have no underlying bond (e.g. CMT par yields
12///       published by Treasury). When `tenor` is set it overrides any tenor
13///       that would otherwise be computed from `security`.
14///
15/// `price` and `clean_price` are mutually exclusive; if both are set the
16/// server returns InvalidArgument.
17#[allow(clippy::derive_partial_eq_without_eq)]
18#[derive(Clone, PartialEq, ::prost::Message)]
19pub struct CurveInputProto {
20    /// The bond security at this curve point. Required unless `tenor` is set.
21    /// Must carry `issue_date` and `maturity_date` for tenor computation when
22    /// `tenor` is not explicitly provided.
23    #[prost(message, optional, tag = "1")]
24    pub security: ::core::option::Option<super::super::models::security::SecurityProto>,
25    /// The observed yield for this security, expressed as a yield (e.g. 4.25
26    /// for 4.25%). Mutually exclusive with `clean_price`.
27    #[prost(message, optional, tag = "2")]
28    pub price: ::core::option::Option<super::super::models::price::PriceProto>,
29    /// Optional tenor override, in decimal years (e.g. 0.5 for 6M, 10.0 for 10Y).
30    /// When set, this is used as the curve point's x-coordinate directly, bypassing
31    /// any date-based computation from `security`. Intended for synthetic CMT-style
32    /// inputs that have no bond.
33    #[prost(message, optional, tag = "4")]
34    pub tenor: ::core::option::Option<super::super::models::util::DecimalValueProto>,
35    /// Optional clean price (quoted as % of par, e.g. 99.50). Alternative to
36    /// `price`: when set, the server runs a YTM solver against the bond's cash
37    /// flows to derive the yield used for curve fitting. Mutually exclusive
38    /// with `price`.
39    #[prost(message, optional, tag = "5")]
40    pub clean_price: ::core::option::Option<
41        super::super::models::util::DecimalValueProto,
42    >,
43}
44/// Request to construct a yield curve from a set of bond prices.
45///
46/// The caller provides:
47///    - A set of securities with their market prices (the curve inputs)
48///    - The type of curve to compute (par, spot, or forward)
49///    - The tenor points at which to interpolate the curve
50///
51/// The valuation service bootstraps the curve from the inputs and returns
52/// yields at the requested tenor points.
53#[allow(clippy::derive_partial_eq_without_eq)]
54#[derive(Clone, PartialEq, ::prost::Message)]
55pub struct CurveRequestProto {
56    #[prost(string, tag = "1")]
57    pub object_class: ::prost::alloc::string::String,
58    #[prost(string, tag = "2")]
59    pub version: ::prost::alloc::string::String,
60    /// The as-of datetime for the curve construction. **Required** — the server
61    /// returns InvalidArgument if missing. This drives the effective tenor of
62    /// every input bond (`maturity_date - asof_datetime`) when `CurveInputProto.tenor`
63    /// is not explicitly set, and any YTM solve performed against `clean_price`.
64    #[prost(message, optional, tag = "10")]
65    pub asof_datetime: ::core::option::Option<
66        super::super::models::util::LocalTimestampProto,
67    >,
68    /// The curve type to compute: PAR_YIELD, SPOT_YIELD, or FORWARD_YIELD.
69    /// Multiple types can be requested in a single call — the response will
70    /// contain one CurveResultProto per requested type.
71    #[prost(
72        enumeration = "super::super::models::position::MeasureProto",
73        repeated,
74        tag = "20"
75    )]
76    pub curve_types: ::prost::alloc::vec::Vec<i32>,
77    /// The input securities and their observed prices. These are the bonds
78    /// from which the curve is constructed (e.g. on-the-run US Treasuries).
79    /// Should be sorted by maturity for clarity, though the service will
80    /// sort internally if needed.
81    #[prost(message, repeated, tag = "30")]
82    pub curve_inputs: ::prost::alloc::vec::Vec<CurveInputProto>,
83    /// The tenor points (in years as decimal) at which to interpolate the curve.
84    /// Examples: 0.25 (3M), 0.5 (6M), 1.0 (1Y), 2.0, 3.0, 5.0, 7.0, 10.0, 20.0, 30.0.
85    /// If empty, the service returns yields only at the observed maturity points.
86    #[prost(message, repeated, tag = "40")]
87    pub tenor_points: ::prost::alloc::vec::Vec<
88        super::super::models::util::DecimalValueProto,
89    >,
90    /// Term length, in whole years, for FORWARD_YIELD curve construction.
91    ///
92    /// When set, FORWARD_YIELD curve result returns f(t, t+T) at annual t for
93    /// t in [0, T_max - T]. When unset, existing behavior preserved.
94    ///
95    /// Annual granularity only (uint32). Added per #264. Only meaningful when
96    /// FORWARD_YIELD is among `curve_types`; ignored otherwise.
97    #[prost(uint32, tag = "50")]
98    pub forward_term_years: u32,
99}
100/// A single point on the yield curve: a tenor and its corresponding yield.
101#[allow(clippy::derive_partial_eq_without_eq)]
102#[derive(Clone, PartialEq, ::prost::Message)]
103pub struct CurvePointProto {
104    /// The tenor in years (e.g. 0.25 = 3 months, 2.0 = 2 years, 10.0 = 10 years).
105    #[prost(message, optional, tag = "1")]
106    pub tenor: ::core::option::Option<super::super::models::util::DecimalValueProto>,
107    /// The yield at this tenor point (decimal, 0-1 scale; e.g. 0.045 = 4.50%).
108    #[prost(message, optional, tag = "2")]
109    pub r#yield: ::core::option::Option<super::super::models::util::DecimalValueProto>,
110}
111/// The computed yield curve for a single curve type (par, spot, or forward).
112#[allow(clippy::derive_partial_eq_without_eq)]
113#[derive(Clone, PartialEq, ::prost::Message)]
114pub struct CurveResultProto {
115    /// Which curve type this result represents (PAR_YIELD, SPOT_YIELD, or FORWARD_YIELD).
116    #[prost(enumeration = "super::super::models::position::MeasureProto", tag = "1")]
117    pub curve_type: i32,
118    /// The curve points, ordered by ascending tenor.
119    #[prost(message, repeated, tag = "2")]
120    pub points: ::prost::alloc::vec::Vec<CurvePointProto>,
121}
122/// Response from a curve construction request.
123#[allow(clippy::derive_partial_eq_without_eq)]
124#[derive(Clone, PartialEq, ::prost::Message)]
125pub struct CurveResponseProto {
126    #[prost(string, tag = "1")]
127    pub object_class: ::prost::alloc::string::String,
128    #[prost(string, tag = "2")]
129    pub version: ::prost::alloc::string::String,
130    /// The original request, echoed back for correlation.
131    #[prost(message, optional, tag = "10")]
132    pub curve_request: ::core::option::Option<CurveRequestProto>,
133    /// One result per requested curve_type. For example, if the request asked for
134    /// both PAR_YIELD and SPOT_YIELD, this will contain two CurveResultProto entries.
135    #[prost(message, repeated, tag = "20")]
136    pub curve_results: ::prost::alloc::vec::Vec<CurveResultProto>,
137    /// Errors and warnings. Examples:
138    ///    Warning: "Tenor gap between 7Y and 20Y — interpolation may be unreliable"
139    ///    Error: "Insufficient inputs: need at least 2 bonds to bootstrap a curve"
140    #[prost(message, optional, tag = "30")]
141    pub summary: ::core::option::Option<super::util::errors::SummaryProto>,
142}
143/// ═══════════════════════════════════════════════════════════════════════════
144/// ProductInput — the dispatch field on ValuationRequestProto.
145///
146/// Exactly one variant should be set per request. The service routes to the
147/// appropriate calculation engine based on which oneof field is populated.
148///
149/// Field numbers are reserved per the platform product roadmap:
150///    1–7  fixed-income (bond, callable, tips, muni, amortizing, ...)
151///    8    frn
152///    9–12 rates/credit (swap, xccy, repo, loan)
153///    13+  other (mbs, money market, futures, scenario, krd, ...)
154/// ═══════════════════════════════════════════════════════════════════════════
155#[allow(clippy::derive_partial_eq_without_eq)]
156#[derive(Clone, PartialEq, ::prost::Message)]
157pub struct ProductInput {
158    #[prost(oneof = "product_input::Input", tags = "1, 2, 8")]
159    pub input: ::core::option::Option<product_input::Input>,
160}
161/// Nested message and enum types in `ProductInput`.
162pub mod product_input {
163    #[allow(clippy::derive_partial_eq_without_eq)]
164    #[derive(Clone, PartialEq, ::prost::Oneof)]
165    pub enum Input {
166        #[prost(message, tag = "1")]
167        Bond(super::BondInput),
168        #[prost(message, tag = "2")]
169        Tips(super::TipsInput),
170        #[prost(message, tag = "8")]
171        Frn(super::FrnInput),
172    }
173}
174/// ═══════════════════════════════════════════════════════════════════════════
175/// BondInput — valuation request for a fixed-rate bond.
176///
177/// Static security details (coupon_rate, coupon_frequency, face_value,
178/// dated_date, maturity_date) are read from the SecurityProto.
179///
180/// Settlement date is read from ValuationRequestProto.asof_datetime.
181/// ═══════════════════════════════════════════════════════════════════════════
182#[allow(clippy::derive_partial_eq_without_eq)]
183#[derive(Clone, PartialEq, ::prost::Message)]
184pub struct BondInput {
185    /// The bond security. Must be ProductTypeProto.TREASURY_NOTE with
186    /// coupon_type FIXED and all standard fixed-income fields populated.
187    #[prost(message, optional, tag = "1")]
188    pub security: ::core::option::Option<super::super::models::security::SecurityProto>,
189    /// Market clean price as a percentage of face value (e.g. 99.75 = 99.75% of par).
190    #[prost(message, optional, tag = "2")]
191    pub clean_price: ::core::option::Option<
192        super::super::models::util::DecimalValueProto,
193    >,
194    /// Optional benchmark curve for Z-spread computation, expressed as a set of
195    /// benchmark securities and their market clean prices. The engine bootstraps
196    /// a zero-coupon spot curve internally. When omitted, Z-spread is not computed.
197    #[prost(message, optional, tag = "10")]
198    pub benchmark_curve: ::core::option::Option<SecurityBasedCurveInput>,
199}
200/// ═══════════════════════════════════════════════════════════════════════════
201/// TipsInput — valuation request for a Treasury Inflation-Protected Security.
202///
203/// Static security details: shared bond fields (real coupon_rate,
204/// coupon_frequency, face_value, maturity_date) are read from
205/// security.bond_details. TIPS-specific extras (base_cpi, index_date,
206/// inflation_index_type) are read from security.tips_extension.
207///
208/// Settlement date is read from ValuationRequestProto.asof_datetime.
209/// ═══════════════════════════════════════════════════════════════════════════
210#[allow(clippy::derive_partial_eq_without_eq)]
211#[derive(Clone, PartialEq, ::prost::Message)]
212pub struct TipsInput {
213    /// The TIPS security. Must be ProductTypeProto.TIPS with bond_details
214    /// populated (coupon_type FIXED, etc.) and tips_extension populated with
215    /// base_cpi set to the reference CPI index value at issuance.
216    #[prost(message, optional, tag = "1")]
217    pub security: ::core::option::Option<super::super::models::security::SecurityProto>,
218    /// Market clean price as a percentage of face value (e.g. 99.75 = 99.75% of par).
219    #[prost(message, optional, tag = "2")]
220    pub clean_price: ::core::option::Option<
221        super::super::models::util::DecimalValueProto,
222    >,
223    /// Current CPI index value (e.g. 310.326). Used to compute:
224    ///    index_ratio = current_cpi / base_cpi
225    ///    adjusted_principal = face_value * index_ratio
226    /// The base CPI is read from security.tips_extension.base_cpi.
227    #[prost(message, optional, tag = "3")]
228    pub current_cpi: ::core::option::Option<
229        super::super::models::util::DecimalValueProto,
230    >,
231}
232/// ═══════════════════════════════════════════════════════════════════════════
233/// FrnInput — valuation request for a Floating Rate Note.
234///
235/// Static security details: shared bond fields (coupon_frequency, face_value,
236/// dated_date, maturity_date) are read from security.bond_details; FRN-specific
237/// extras (spread, reference_rate_index, reset_frequency) are read from
238/// security.frn_extension. None are repeated on this message.
239///
240/// Settlement date is read from ValuationRequestProto.asof_datetime.
241/// ═══════════════════════════════════════════════════════════════════════════
242#[allow(clippy::derive_partial_eq_without_eq)]
243#[derive(Clone, PartialEq, ::prost::Message)]
244pub struct FrnInput {
245    /// The FRN security. Must have bond_details and frn_extension populated.
246    #[prost(message, optional, tag = "1")]
247    pub security: ::core::option::Option<super::super::models::security::SecurityProto>,
248    /// Market clean price as a percentage of face value (e.g. 99.75 = 99.75% of par).
249    #[prost(message, optional, tag = "2")]
250    pub clean_price: ::core::option::Option<
251        super::super::models::util::DecimalValueProto,
252    >,
253    /// Yield curve for projecting floating coupons and discounting cashflows.
254    /// curve.index must match security.frn_extension.reference_rate_index.
255    /// A single curve is used for both projection and discounting (single-curve framework).
256    /// This is appropriate for RFR-based FRNs (SOFR, SONIA, ESTR, TONA).
257    #[prost(message, optional, tag = "10")]
258    pub curve: ::core::option::Option<YieldCurveInput>,
259}
260/// ═══════════════════════════════════════════════════════════════════════════
261/// YieldCurveInput — a term structure of continuously compounded zero rates.
262///
263/// Used to project forward rates for floating coupons and to compute
264/// discount factors for present-valuing cashflows.
265/// ═══════════════════════════════════════════════════════════════════════════
266#[allow(clippy::derive_partial_eq_without_eq)]
267#[derive(Clone, PartialEq, ::prost::Message)]
268pub struct YieldCurveInput {
269    /// The benchmark this curve represents (e.g. SOFR, SONIA).
270    /// Must match the FRN security's reference_rate_index — validated by the service.
271    #[prost(
272        enumeration = "super::super::models::security::index::IndexTypeProto",
273        tag = "1"
274    )]
275    pub index: i32,
276    /// As-of date for the curve, typically today or the most recent business day.
277    #[prost(message, optional, tag = "2")]
278    pub reference_date: ::core::option::Option<
279        super::super::models::util::LocalDateProto,
280    >,
281    /// Term structure points, must be sorted by tenor strictly ascending.
282    #[prost(message, repeated, tag = "3")]
283    pub points: ::prost::alloc::vec::Vec<CurvePoint>,
284}
285/// A single point on a yield curve.
286#[allow(clippy::derive_partial_eq_without_eq)]
287#[derive(Clone, PartialEq, ::prost::Message)]
288pub struct CurvePoint {
289    /// Time from reference_date in years (e.g. 0.25, 0.5, 1.0, 2.0, 5.0, 10.0).
290    #[prost(message, optional, tag = "1")]
291    pub tenor: ::core::option::Option<super::super::models::util::DecimalValueProto>,
292    /// Continuously compounded zero rate as a decimal (e.g. 0.0425 = 4.25%).
293    /// Not a percentage — 4.25 would be interpreted as 425%.
294    #[prost(message, optional, tag = "2")]
295    pub rate: ::core::option::Option<super::super::models::util::DecimalValueProto>,
296}
297/// ═══════════════════════════════════════════════════════════════════════════
298/// SecurityBasedCurveInput — a benchmark curve specified as a set of benchmark
299/// securities and their observed market clean prices.
300///
301/// The valuation engine bootstraps a zero-coupon spot rate curve from these
302/// inputs using a closed-form iterative bootstrap (shortest maturity first).
303/// Callers do not supply rates directly; the engine derives them internally.
304/// ═══════════════════════════════════════════════════════════════════════════
305#[allow(clippy::derive_partial_eq_without_eq)]
306#[derive(Clone, PartialEq, ::prost::Message)]
307pub struct SecurityBasedCurveInput {
308    /// Which government benchmark this curve represents.
309    /// Must be one of the government curve identifiers: US_TREASURY, UK_GILT,
310    /// DE_BUND, JP_JGB.
311    #[prost(
312        enumeration = "super::super::models::security::index::IndexTypeProto",
313        tag = "1"
314    )]
315    pub index: i32,
316    /// As-of date for the curve, typically today or the most recent business day.
317    #[prost(message, optional, tag = "2")]
318    pub reference_date: ::core::option::Option<
319        super::super::models::util::LocalDateProto,
320    >,
321    /// Benchmark securities sorted by maturity ascending. Each entry pairs a
322    /// security with its observed clean price. The engine processes them in order,
323    /// bootstrapping the zero rate at each maturity from the previously solved
324    /// shorter-dated zeros.
325    #[prost(message, repeated, tag = "3")]
326    pub points: ::prost::alloc::vec::Vec<SecurityCurvePoint>,
327}
328/// A benchmark security and its market clean price, used as one input to the
329/// bootstrap algorithm in SecurityBasedCurveInput.
330#[allow(clippy::derive_partial_eq_without_eq)]
331#[derive(Clone, PartialEq, ::prost::Message)]
332pub struct SecurityCurvePoint {
333    /// The benchmark bond (must have fixed coupon, maturity_date, coupon_frequency,
334    /// face_value, and coupon_rate populated).
335    #[prost(message, optional, tag = "1")]
336    pub security: ::core::option::Option<super::super::models::security::SecurityProto>,
337    /// Market clean price as a percentage of face value (e.g. 99.50 = 99.50% of par).
338    #[prost(message, optional, tag = "2")]
339    pub clean_price: ::core::option::Option<
340        super::super::models::util::DecimalValueProto,
341    >,
342}
343/// Developer notes. This will need some re-organization once we start thinking through
344/// varied valuations (e.g. value over a time range, value multiple securities in the same
345/// request/etc. For now, the caller will need to make individual requests.
346#[allow(clippy::derive_partial_eq_without_eq)]
347#[derive(Clone, PartialEq, ::prost::Message)]
348pub struct ValuationRequestProto {
349    #[prost(string, tag = "1")]
350    pub object_class: ::prost::alloc::string::String,
351    #[prost(string, tag = "2")]
352    pub version: ::prost::alloc::string::String,
353    /// Only supports GET, since there is no backing store, so CREATE isn't relevant. SEARCH isn't relevant either.
354    /// VALIDATE could be implemented later, e.g. if the caller wants to check their inputs are correct.
355    #[prost(enumeration = "super::util::RequestOperationTypeProto", tag = "10")]
356    pub operation_type: i32,
357    /// The list of measures to be generated by this request, e.g. MARKET_VALUE, CURRENT_YIELD, etc.
358    #[prost(
359        enumeration = "super::super::models::position::MeasureProto",
360        repeated,
361        tag = "30"
362    )]
363    pub measures: ::prost::alloc::vec::Vec<i32>,
364    /// The full security object for which we are going to run the valuation
365    #[prost(message, optional, tag = "20")]
366    pub security_input: ::core::option::Option<
367        super::super::models::security::SecurityProto,
368    >,
369    /// The positions we are going to value.
370    #[prost(message, optional, tag = "21")]
371    pub position_input: ::core::option::Option<
372        super::super::models::position::PositionProto,
373    >,
374    /// The price we are going to use for the valuation.
375    #[prost(message, optional, tag = "22")]
376    pub price_input: ::core::option::Option<super::super::models::price::PriceProto>,
377    /// The asof datetime for the valuation.
378    #[prost(message, optional, tag = "23")]
379    pub asof_datetime: ::core::option::Option<
380        super::super::models::util::LocalTimestampProto,
381    >,
382    /// The CPI index observation used for inflation-linked bond valuation (e.g. TIPS).
383    /// Modeled as a PriceProto on a SecurityProto representing the CPI index.
384    #[prost(message, optional, tag = "24")]
385    pub cpi_price_input: ::core::option::Option<super::super::models::price::PriceProto>,
386    /// The current reference rate observation for floating rate note (FRN) valuation.
387    /// Modeled as a PriceProto on an INDEX_SECURITY representing the benchmark (e.g. SOFR).
388    /// Deprecated in favour of FrnInput.curve — retained for backward compatibility with flat-rate FRN pricing.
389    #[prost(message, optional, tag = "25")]
390    pub reference_rate_input: ::core::option::Option<
391        super::super::models::price::PriceProto,
392    >,
393    /// Product-specific input — determines the calculation path in the service.
394    /// When set, the service routes to the new engine dispatch; existing flat fields
395    /// (security_input, price_input, etc.) are ignored for the purposes of the
396    /// product calculation but may still be read for logging and audit.
397    #[prost(message, optional, tag = "70")]
398    pub product_input: ::core::option::Option<ProductInput>,
399}
400#[allow(clippy::derive_partial_eq_without_eq)]
401#[derive(Clone, PartialEq, ::prost::Message)]
402pub struct ValuationResponseProto {
403    #[prost(string, tag = "1")]
404    pub object_class: ::prost::alloc::string::String,
405    #[prost(string, tag = "2")]
406    pub version: ::prost::alloc::string::String,
407    #[prost(message, optional, tag = "20")]
408    pub valuation_request: ::core::option::Option<ValuationRequestProto>,
409    #[prost(message, repeated, tag = "30")]
410    pub measure_results: ::prost::alloc::vec::Vec<
411        super::super::models::position::MeasureMapEntry,
412    >,
413    #[prost(message, optional, tag = "40")]
414    pub summary: ::core::option::Option<super::util::errors::SummaryProto>,
415    /// The full schedule of cashflows, populated when PRESENT_VALUE_CASHFLOWS is requested.
416    /// Each entry represents a single coupon or principal payment with PV and FV amounts.
417    #[prost(message, repeated, tag = "50")]
418    pub cashflows: ::prost::alloc::vec::Vec<
419        super::super::models::valuation::CashflowProto,
420    >,
421}