use chrono::NaiveDate;
use super::types::BondPrice;
use crate::calendar::DayCountConvention;
use crate::cashflows::CashflowPricer;
use crate::cashflows::CurveProvider;
use crate::cashflows::FloatingIndex;
use crate::cashflows::Leg;
use crate::cashflows::NotionalSchedule;
use crate::traits::FloatExt;
#[derive(Debug, Clone)]
pub struct FloatingRateBond<T: FloatExt> {
pub face_value: T,
pub index: FloatingIndex<T>,
pub spread: T,
pub coupon_day_count: DayCountConvention,
leg: Leg<T>,
}
impl<T: FloatExt> FloatingRateBond<T> {
pub fn new(
schedule: &crate::calendar::Schedule,
face_value: T,
index: FloatingIndex<T>,
spread: T,
coupon_day_count: DayCountConvention,
) -> Self {
assert!(
schedule.adjusted_dates.len() >= 2,
"bond schedule must contain at least two dates"
);
let maturity = *schedule.adjusted_dates.last().unwrap();
let leg = Leg::floating_rate(
schedule,
NotionalSchedule::bullet(schedule.adjusted_dates.len() - 1, face_value),
index.clone(),
spread,
coupon_day_count,
)
.with_redemption(maturity, face_value);
Self {
face_value,
index,
spread,
coupon_day_count,
leg,
}
}
pub fn leg(&self) -> &Leg<T> {
&self.leg
}
pub fn price_from_curve(
&self,
valuation_date: NaiveDate,
discount_day_count: DayCountConvention,
curves: &(impl CurveProvider<T> + ?Sized),
) -> BondPrice<T> {
let summary =
CashflowPricer::new(valuation_date, discount_day_count).summarize_leg(&self.leg, curves);
BondPrice {
dirty_price: summary.dirty_npv,
accrued_interest: summary.accrued_interest,
clean_price: summary.clean_npv,
}
}
pub fn accrued_interest(
&self,
valuation_date: NaiveDate,
curves: &(impl CurveProvider<T> + ?Sized),
) -> T {
CashflowPricer::new(valuation_date, self.coupon_day_count)
.leg_accrued_interest(&self.leg, curves)
}
}