use crate::{
indices::marketindex::MarketIndex,
quotes::fixingstore::FixingStore,
time::{date::Date, daycounter::DayCounter},
xva::{claimevaluationstrategy::ClaimEvaluationStrategy, contigentclaim::ContingentClaim},
};
use super::claimpreprocessor::ClaimPreprocessor;
pub struct FixingPreprocessor {
ref_date: Date,
day_counter: DayCounter,
fixing_store: FixingStore,
}
impl FixingPreprocessor {
#[must_use]
pub const fn new(ref_date: Date, day_counter: DayCounter, fixing_store: FixingStore) -> Self {
Self {
ref_date,
day_counter,
fixing_store,
}
}
fn compound_daily_fixings(&self, index: &MarketIndex, start: Date, end: Date) -> Option<f64> {
let fixings = self.fixing_store.fixings(index).ok()?;
let mut factor = 1.0;
let dates: Vec<Date> = fixings.range(start..end).map(|(d, _)| *d).collect();
for (i, &d) in dates.iter().enumerate() {
let rate = fixings[&d];
let next = if i + 1 < dates.len() {
dates[i + 1]
} else {
end
};
let dt = self.day_counter.year_fraction(d, next);
factor *= rate.mul_add(dt, 1.0);
}
Some(factor)
}
}
impl ClaimPreprocessor for FixingPreprocessor {
fn process(&self, claim: &mut ContingentClaim) {
let is_rate_claim = matches!(
claim.evaluation_strategy(),
ClaimEvaluationStrategy::LinearRate { .. }
| ClaimEvaluationStrategy::NonLinearRate { .. }
);
if !is_rate_claim {
return;
}
let Some(fixing_date) = claim.fixing_date() else { return };
if fixing_date >= self.ref_date {
return;
}
let market_index = match claim.index() {
Some(idx) => idx.clone(),
None => return,
};
let in_arrears = market_index
.rate_index_details()
.map_or(true, |d| d.is_in_arrears());
let accrual_start = claim.accrual_start().unwrap_or(fixing_date);
let accrual_end = claim.accrual_end().unwrap_or_else(|| claim.payment_date());
if !in_arrears {
if let Ok(rate) = self.fixing_store.fixing(&market_index, fixing_date) {
claim.set_realized_fixing(rate);
}
} else if accrual_end <= self.ref_date {
if let Some(factor) =
self.compound_daily_fixings(&market_index, accrual_start, accrual_end)
{
let tau = self.day_counter.year_fraction(accrual_start, accrual_end);
if tau.abs() > 1e-14 {
let rate = (factor - 1.0) / tau;
claim.set_realized_fixing(rate);
}
}
} else if accrual_start < self.ref_date {
if let Some(realized_factor) =
self.compound_daily_fixings(&market_index, accrual_start, self.ref_date)
{
claim.set_partial_fixing(realized_factor, accrual_start, self.ref_date);
}
}
}
}