use crate::core::instrument::AssetClass;
use crate::utils::errors::{QSError, Result};
use crate::{currencies::currency::Currency, indices::marketindex::MarketIndex};
use std::collections::HashMap;
pub trait Discountable {
#[must_use]
fn asset_class(&self) -> AssetClass;
#[must_use]
fn discount_index(&self) -> Option<MarketIndex> {
None
}
#[must_use]
fn currency(&self) -> Currency;
}
pub trait DiscountPolicy: Send + Sync {
fn accept(&self, target: &dyn Discountable) -> Result<MarketIndex>;
fn discount_indices(&self) -> Vec<MarketIndex>;
}
#[derive(Default)]
pub struct FixedIncomeDiscountPolicy {
risk_free_by_currency: HashMap<Currency, MarketIndex>,
prefer_instrument_index: bool,
}
impl FixedIncomeDiscountPolicy {
#[must_use]
pub fn new(prefer_instrument_index: bool) -> Self {
Self {
risk_free_by_currency: HashMap::new(),
prefer_instrument_index,
}
}
#[must_use]
pub fn with_risk_free_index(mut self, currency: Currency, market_index: MarketIndex) -> Self {
self.risk_free_by_currency.insert(currency, market_index);
self
}
#[must_use]
fn risk_free_index(&self, currency: Currency) -> Option<MarketIndex> {
self.risk_free_by_currency.get(¤cy).cloned()
}
}
impl DiscountPolicy for FixedIncomeDiscountPolicy {
fn accept(&self, target: &dyn Discountable) -> Result<MarketIndex> {
match target.asset_class() {
AssetClass::FixedIncome => {
if self.prefer_instrument_index {
if let Some(indices) = target.discount_index() {
return Ok(indices);
}
}
self.risk_free_index(target.currency()).map_or_else(
|| {
Err(QSError::InvalidValueErr(format!(
"No risk-free index configured for currency {}",
target.currency()
)))
},
Ok,
)
}
_ => Err(QSError::InvalidValueErr(format!(
"Unsupported asset class for FixedIncomeDiscountPolicy: {:?}",
target.asset_class()
))),
}
}
fn discount_indices(&self) -> Vec<MarketIndex> {
self.risk_free_by_currency.values().cloned().collect()
}
}
pub struct SingleCurveCSADiscountPolicy {
discount_index: MarketIndex,
currency: Currency,
}
impl SingleCurveCSADiscountPolicy {
#[must_use]
pub const fn new(discount_index: MarketIndex, currency: Currency) -> Self {
Self {
discount_index,
currency,
}
}
}
impl DiscountPolicy for SingleCurveCSADiscountPolicy {
fn accept(&self, target: &dyn Discountable) -> Result<MarketIndex> {
if target.currency() == self.currency {
Ok(self.discount_index.clone())
} else {
Ok(MarketIndex::Collateral(target.currency(), self.currency))
}
}
fn discount_indices(&self) -> Vec<MarketIndex> {
vec![self.discount_index.clone()]
}
}