use chrono::NaiveDate;
use super::shared::fixed_leg_bpv_annuity;
use super::types::SwapDirection;
use super::types::SwapValuation;
use crate::calendar::DayCountConvention;
use crate::calendar::Schedule;
use crate::cashflows::CashflowPricer;
use crate::cashflows::CurveProvider;
use crate::cashflows::FloatingIndex;
use crate::cashflows::Leg;
use crate::cashflows::NotionalSchedule;
use crate::cashflows::OvernightIndex;
use crate::traits::FloatExt;
#[derive(Debug, Clone)]
pub struct VanillaInterestRateSwap<T: FloatExt> {
pub direction: SwapDirection,
pub notional: T,
pub fixed_rate: T,
pub fixed_leg: Leg<T>,
pub floating_leg: Leg<T>,
}
impl<T: FloatExt> VanillaInterestRateSwap<T> {
pub fn new(
direction: SwapDirection,
fixed_schedule: &Schedule,
float_schedule: &Schedule,
notional: T,
fixed_rate: T,
fixed_day_count: DayCountConvention,
float_index: FloatingIndex<T>,
float_spread: T,
float_day_count: DayCountConvention,
) -> Self {
assert!(
fixed_schedule.adjusted_dates.len() >= 2,
"fixed schedule must contain at least two dates"
);
assert!(
float_schedule.adjusted_dates.len() >= 2,
"floating schedule must contain at least two dates"
);
let fixed_leg = Leg::fixed_rate(
fixed_schedule,
NotionalSchedule::bullet(fixed_schedule.adjusted_dates.len() - 1, notional),
fixed_rate,
fixed_day_count,
);
let floating_leg = Leg::floating_rate(
float_schedule,
NotionalSchedule::bullet(float_schedule.adjusted_dates.len() - 1, notional),
float_index,
float_spread,
float_day_count,
);
Self {
direction,
notional,
fixed_rate,
fixed_leg,
floating_leg,
}
}
pub fn overnight_indexed(
direction: SwapDirection,
fixed_schedule: &Schedule,
float_schedule: &Schedule,
notional: T,
fixed_rate: T,
fixed_day_count: DayCountConvention,
overnight_index: OvernightIndex<T>,
overnight_spread: T,
float_day_count: DayCountConvention,
) -> Self {
Self::new(
direction,
fixed_schedule,
float_schedule,
notional,
fixed_rate,
fixed_day_count,
FloatingIndex::Overnight(overnight_index),
overnight_spread,
float_day_count,
)
}
pub fn valuation(
&self,
valuation_date: NaiveDate,
discount_day_count: DayCountConvention,
curves: &(impl CurveProvider<T> + ?Sized),
) -> SwapValuation<T> {
let pricer = CashflowPricer::new(valuation_date, discount_day_count);
let fixed_leg_npv = pricer.leg_npv(&self.fixed_leg, curves);
let floating_leg_npv = pricer.leg_npv(&self.floating_leg, curves);
let annuity =
fixed_leg_bpv_annuity(&self.fixed_leg, valuation_date, discount_day_count, curves);
let fair_rate = if annuity > T::zero() {
floating_leg_npv / annuity
} else {
T::zero()
};
let bpv = annuity * T::from_f64_fast(1e-4);
let dv01 = match self.direction {
SwapDirection::Payer => -bpv,
SwapDirection::Receiver => bpv,
};
let net_npv = match self.direction {
SwapDirection::Payer => floating_leg_npv - fixed_leg_npv,
SwapDirection::Receiver => fixed_leg_npv - floating_leg_npv,
};
SwapValuation {
fixed_leg_npv,
floating_leg_npv,
net_npv,
fair_rate,
annuity,
bpv,
dv01,
}
}
pub fn npv(
&self,
valuation_date: NaiveDate,
discount_day_count: DayCountConvention,
curves: &(impl CurveProvider<T> + ?Sized),
) -> T {
self
.valuation(valuation_date, discount_day_count, curves)
.net_npv
}
pub fn fair_rate(
&self,
valuation_date: NaiveDate,
discount_day_count: DayCountConvention,
curves: &(impl CurveProvider<T> + ?Sized),
) -> T {
self
.valuation(valuation_date, discount_day_count, curves)
.fair_rate
}
pub fn bpv(
&self,
valuation_date: NaiveDate,
discount_day_count: DayCountConvention,
curves: &(impl CurveProvider<T> + ?Sized),
) -> T {
self
.valuation(valuation_date, discount_day_count, curves)
.bpv
}
pub fn dv01(
&self,
valuation_date: NaiveDate,
discount_day_count: DayCountConvention,
curves: &(impl CurveProvider<T> + ?Sized),
) -> T {
self
.valuation(valuation_date, discount_day_count, curves)
.dv01
}
}