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}