use std::fmt::Display;
use chrono::NaiveDate;
use super::CurveProvider;
use super::RateIndex;
use super::types::AccrualPeriod;
use super::types::CmsIndex;
use super::types::FloatingIndex;
use crate::traits::FloatExt;
#[derive(Debug, Clone)]
pub struct FixedRateCoupon<T: FloatExt> {
pub period: AccrualPeriod<T>,
pub notional: T,
pub fixed_rate: T,
}
impl<T: FloatExt> FixedRateCoupon<T> {
pub fn amount(&self) -> T {
self.notional * self.fixed_rate * self.period.accrual_factor
}
pub fn accrued_interest(&self, as_of: NaiveDate) -> T {
self.notional * self.fixed_rate * self.period.accrued_factor(as_of)
}
}
#[derive(Debug, Clone)]
pub struct FloatingRateCoupon<T: FloatExt> {
pub period: AccrualPeriod<T>,
pub notional: T,
pub index: FloatingIndex<T>,
pub spread: T,
pub observed_rate: Option<T>,
}
impl<T: FloatExt> FloatingRateCoupon<T> {
pub fn rate(&self, curves: &(impl CurveProvider<T> + ?Sized), valuation_date: NaiveDate) -> T {
self.observed_rate.unwrap_or_else(|| {
self
.index
.forward_rate(curves, valuation_date, &self.period)
}) + self.spread
}
pub fn amount(&self, curves: &(impl CurveProvider<T> + ?Sized), valuation_date: NaiveDate) -> T {
self.notional * self.period.accrual_factor * self.rate(curves, valuation_date)
}
pub fn accrued_interest(
&self,
curves: &(impl CurveProvider<T> + ?Sized),
valuation_date: NaiveDate,
as_of: NaiveDate,
) -> T {
self.notional * self.period.accrued_factor(as_of) * self.rate(curves, valuation_date)
}
}
#[derive(Debug, Clone)]
pub struct CmsCoupon<T: FloatExt> {
pub period: AccrualPeriod<T>,
pub notional: T,
pub index: CmsIndex<T>,
pub spread: T,
pub observed_rate: Option<T>,
}
impl<T: FloatExt> CmsCoupon<T> {
pub fn rate(&self, curves: &(impl CurveProvider<T> + ?Sized), valuation_date: NaiveDate) -> T {
self.observed_rate.unwrap_or_else(|| {
self
.index
.forward_rate(curves, valuation_date, &self.period)
}) + self.spread
}
pub fn amount(&self, curves: &(impl CurveProvider<T> + ?Sized), valuation_date: NaiveDate) -> T {
self.notional * self.period.accrual_factor * self.rate(curves, valuation_date)
}
pub fn accrued_interest(
&self,
curves: &(impl CurveProvider<T> + ?Sized),
valuation_date: NaiveDate,
as_of: NaiveDate,
) -> T {
self.notional * self.period.accrued_factor(as_of) * self.rate(curves, valuation_date)
}
}
#[derive(Debug, Clone)]
pub struct SimpleCashflow<T: FloatExt> {
pub payment_date: NaiveDate,
pub amount: T,
}
#[derive(Debug, Clone)]
pub enum Cashflow<T: FloatExt> {
Fixed(FixedRateCoupon<T>),
Floating(FloatingRateCoupon<T>),
Cms(CmsCoupon<T>),
Simple(SimpleCashflow<T>),
}
impl<T: FloatExt> Cashflow<T> {
pub fn payment_date(&self) -> NaiveDate {
match self {
Self::Fixed(coupon) => coupon.period.payment_date,
Self::Floating(coupon) => coupon.period.payment_date,
Self::Cms(coupon) => coupon.period.payment_date,
Self::Simple(cashflow) => cashflow.payment_date,
}
}
pub fn amount(&self, curves: &(impl CurveProvider<T> + ?Sized), valuation_date: NaiveDate) -> T {
match self {
Self::Fixed(coupon) => coupon.amount(),
Self::Floating(coupon) => coupon.amount(curves, valuation_date),
Self::Cms(coupon) => coupon.amount(curves, valuation_date),
Self::Simple(cashflow) => cashflow.amount,
}
}
pub fn accrued_interest(
&self,
curves: &(impl CurveProvider<T> + ?Sized),
valuation_date: NaiveDate,
as_of: NaiveDate,
) -> T {
match self {
Self::Fixed(coupon) => coupon.accrued_interest(as_of),
Self::Floating(coupon) => coupon.accrued_interest(curves, valuation_date, as_of),
Self::Cms(coupon) => coupon.accrued_interest(curves, valuation_date, as_of),
Self::Simple(_) => T::zero(),
}
}
}
impl<T: FloatExt> Display for Cashflow<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Fixed(_) => write!(f, "Fixed coupon"),
Self::Floating(_) => write!(f, "Floating coupon"),
Self::Cms(_) => write!(f, "CMS coupon"),
Self::Simple(_) => write!(f, "Simple cashflow"),
}
}
}