use crate::{
ad::adreal::{ADReal, IsReal},
indices::marketindex::MarketIndex,
instruments::cashflows::coupons::{NonLinearCoupon, PayoffOps},
time::date::Date,
utils::errors::{QSError, Result},
};
pub struct OptionEmbeddedCoupon<T: IsReal> {
notional: f64,
fixing: Option<T>,
spread: ADReal,
index: MarketIndex,
accrual_start_date: Date,
accrual_end_date: Date,
payment_date: Date,
payoff: PayoffOps,
}
impl<T: IsReal> OptionEmbeddedCoupon<T> {
#[must_use]
pub const fn new(
notional: f64,
index: MarketIndex,
spread: ADReal,
accrual_start_date: Date,
accrual_end_date: Date,
payment_date: Date,
payoff: PayoffOps,
) -> Self {
Self {
notional,
fixing: None,
spread,
index,
accrual_start_date,
accrual_end_date,
payment_date,
payoff,
}
}
#[must_use]
pub const fn with_fixing(mut self, fixing: T) -> Self {
self.fixing = Some(fixing);
self
}
pub const fn spread(&self) -> ADReal {
self.spread
}
pub const fn fixing(&self) -> Option<T> {
self.fixing
}
pub const fn market_index(&self) -> &MarketIndex {
&self.index
}
pub const fn payoff_ops(&self) -> &PayoffOps {
&self.payoff
}
}
impl OptionEmbeddedCoupon<ADReal> {
pub fn amount(&self) -> Result<ADReal> {
self.accrued_amount(self.accrual_start_date, self.accrual_end_date)
}
}
impl NonLinearCoupon<ADReal> for OptionEmbeddedCoupon<ADReal> {
fn accrual_end_date(&self) -> Date {
self.accrual_end_date
}
fn accrual_start_date(&self) -> Date {
self.accrual_start_date
}
fn accrued_amount(&self, start_date: Date, end_date: Date) -> Result<ADReal> {
let fixing = self
.fixing
.ok_or_else(|| QSError::NotFoundErr("Fixing not set".into()))?;
let year_fraction = self
.index
.rate_index_details()?
.rate_definition()
.day_counter()
.year_fraction(start_date, end_date);
let resuling_rate = self.payoff.evaluate(fixing)?;
Ok(((self.spread + resuling_rate) * year_fraction * self.notional).into())
}
fn notional(&self) -> f64 {
self.notional
}
fn payoff_description(&self) -> PayoffOps {
self.payoff.clone()
}
fn payment_date(&self) -> Date {
self.payment_date
}
}