use chrono::NaiveDate;
use super::coupon::Cashflow;
use super::coupon::CmsCoupon;
use super::coupon::FixedRateCoupon;
use super::coupon::FloatingRateCoupon;
use super::coupon::SimpleCashflow;
use super::types::AccrualPeriod;
use super::types::CmsIndex;
use super::types::FloatingIndex;
use super::types::NotionalSchedule;
use crate::calendar::DayCountConvention;
use crate::calendar::Schedule;
use crate::traits::FloatExt;
#[derive(Debug, Clone)]
pub struct Leg<T: FloatExt> {
cashflows: Vec<Cashflow<T>>,
}
impl<T: FloatExt> Leg<T> {
pub fn from_cashflows(mut cashflows: Vec<Cashflow<T>>) -> Self {
cashflows.sort_by_key(Cashflow::payment_date);
Self { cashflows }
}
pub fn fixed_rate(
schedule: &Schedule,
notionals: NotionalSchedule<T>,
fixed_rate: T,
day_count: DayCountConvention,
) -> Self {
let periods = schedule.adjusted_dates.len().saturating_sub(1);
notionals.validate(periods);
let cashflows = schedule
.adjusted_dates
.windows(2)
.zip(notionals.notionals().iter())
.map(|(window, ¬ional)| {
Cashflow::Fixed(FixedRateCoupon {
period: AccrualPeriod::new(window[0], window[1], window[1], day_count),
notional,
fixed_rate,
})
})
.collect();
Self::from_cashflows(cashflows)
}
pub fn floating_rate(
schedule: &Schedule,
notionals: NotionalSchedule<T>,
index: FloatingIndex<T>,
spread: T,
day_count: DayCountConvention,
) -> Self {
let periods = schedule.adjusted_dates.len().saturating_sub(1);
notionals.validate(periods);
let cashflows = schedule
.adjusted_dates
.windows(2)
.zip(notionals.notionals().iter())
.map(|(window, ¬ional)| {
Cashflow::Floating(FloatingRateCoupon {
period: AccrualPeriod::new(window[0], window[1], window[1], day_count),
notional,
index: index.clone(),
spread,
observed_rate: None,
})
})
.collect();
Self::from_cashflows(cashflows)
}
pub fn cms(
schedule: &Schedule,
notionals: NotionalSchedule<T>,
index: CmsIndex<T>,
spread: T,
day_count: DayCountConvention,
) -> Self {
let periods = schedule.adjusted_dates.len().saturating_sub(1);
notionals.validate(periods);
let cashflows = schedule
.adjusted_dates
.windows(2)
.zip(notionals.notionals().iter())
.map(|(window, ¬ional)| {
Cashflow::Cms(CmsCoupon {
period: AccrualPeriod::new(window[0], window[1], window[1], day_count),
notional,
index: index.clone(),
spread,
observed_rate: None,
})
})
.collect();
Self::from_cashflows(cashflows)
}
pub fn with_redemption(mut self, payment_date: NaiveDate, amount: T) -> Self {
self.cashflows.push(Cashflow::Simple(SimpleCashflow {
payment_date,
amount,
}));
self.cashflows.sort_by_key(Cashflow::payment_date);
self
}
pub fn push(&mut self, cashflow: Cashflow<T>) {
self.cashflows.push(cashflow);
self.cashflows.sort_by_key(Cashflow::payment_date);
}
pub fn cashflows(&self) -> &[Cashflow<T>] {
&self.cashflows
}
pub fn is_empty(&self) -> bool {
self.cashflows.is_empty()
}
pub fn len(&self) -> usize {
self.cashflows.len()
}
}
#[derive(Debug, Clone, Default)]
pub struct LegBuilder<T: FloatExt> {
cashflows: Vec<Cashflow<T>>,
}
impl<T: FloatExt> LegBuilder<T> {
pub fn new() -> Self {
Self { cashflows: vec![] }
}
pub fn push(mut self, cashflow: Cashflow<T>) -> Self {
self.cashflows.push(cashflow);
self
}
pub fn build(self) -> Leg<T> {
Leg::from_cashflows(self.cashflows)
}
}