Skip to main content

ledger_models/fintekkers/wrappers/models/
floating_rate_note.rs

1//! Typed wrapper around an FRN-shaped `SecurityProto`.
2//!
3//! FRNs carry shared bond fields on `bond_details` plus FRN-specific extras
4//! (spread over the reference rate, reference rate index, reset frequency)
5//! on `frn_extension`.
6
7use chrono::{Datelike, NaiveDate};
8use rust_decimal::Decimal;
9
10use crate::fintekkers::models::security::index::IndexTypeProto;
11use crate::fintekkers::models::security::{
12    BondDetailsProto, CouponFrequencyProto, CouponTypeProto, FrnExtensionProto,
13    ProductTypeProto, SecurityProto,
14};
15use crate::fintekkers::models::util::{DecimalValueProto, LocalDateProto};
16use crate::fintekkers::wrappers::models::utils::errors::Error;
17use crate::fintekkers::wrappers::models::utils::serialization::ProtoSerializationUtil;
18
19pub struct FloatingRateNote {
20    pub proto: SecurityProto,
21}
22
23impl FloatingRateNote {
24    pub fn from_proto(proto: SecurityProto) -> Result<Self, Error> {
25        let pt = ProductTypeProto::from_i32(proto.product_type)
26            .unwrap_or(ProductTypeProto::ProductTypeUnknown);
27        if pt == ProductTypeProto::TreasuryFrn {
28            Ok(FloatingRateNote { proto })
29        } else {
30            Err(Error::NotABondSecurity)
31        }
32    }
33
34    pub fn spread(&self) -> Option<Decimal> {
35        self.proto
36            .frn_extension
37            .as_ref()
38            .and_then(|f| f.spread.as_ref())
39            .and_then(|d| ProtoSerializationUtil::deserialize_decimal(d).ok())
40    }
41
42    pub fn reference_rate_index(&self) -> IndexTypeProto {
43        self.proto
44            .frn_extension
45            .as_ref()
46            .and_then(|f| IndexTypeProto::from_i32(f.reference_rate_index))
47            .unwrap_or(IndexTypeProto::UnknownIndexType)
48    }
49
50    pub fn reset_frequency(&self) -> CouponFrequencyProto {
51        self.proto
52            .frn_extension
53            .as_ref()
54            .and_then(|f| CouponFrequencyProto::from_i32(f.reset_frequency))
55            .unwrap_or(CouponFrequencyProto::UnknownCouponFrequency)
56    }
57
58    pub fn from_pricer_inputs(
59        face_value: Decimal,
60        coupon_rate: Decimal,
61        coupon_type: CouponTypeProto,
62        coupon_frequency: CouponFrequencyProto,
63        issue_date: NaiveDate,
64        maturity_date: NaiveDate,
65        spread: Decimal,
66        reference_rate_index: IndexTypeProto,
67        reset_frequency: CouponFrequencyProto,
68    ) -> SecurityProto {
69        SecurityProto {
70            product_type: ProductTypeProto::TreasuryFrn as i32,
71            bond_details: Some(BondDetailsProto {
72                coupon_rate: Some(decimal_proto(coupon_rate)),
73                coupon_type: coupon_type as i32,
74                coupon_frequency: coupon_frequency as i32,
75                face_value: Some(decimal_proto(face_value)),
76                issue_date: Some(date_proto(issue_date)),
77                dated_date: None,
78                maturity_date: Some(date_proto(maturity_date)),
79                issuance_info: vec![],
80            }),
81            frn_extension: Some(FrnExtensionProto {
82                spread: Some(decimal_proto(spread)),
83                reference_rate_index: reference_rate_index as i32,
84                reset_frequency: reset_frequency as i32,
85            }),
86            ..Default::default()
87        }
88    }
89}
90
91fn decimal_proto(d: Decimal) -> DecimalValueProto {
92    DecimalValueProto {
93        arbitrary_precision_value: d.to_string(),
94    }
95}
96
97fn date_proto(d: NaiveDate) -> LocalDateProto {
98    LocalDateProto {
99        year: d.year() as u32,
100        month: d.month(),
101        day: d.day(),
102    }
103}