use crate::errors::QlResult;
use crate::handle::Handle;
use crate::patterns::observable::{AsObservable, Observable, Observer};
use crate::shared::SharedMut;
use crate::termstructures::{TermStructure, TermStructureBase};
use crate::time::businessdayconvention::BusinessDayConvention;
use crate::time::date::Date;
use crate::time::daycounter::DayCounter;
use crate::types::{Rate, Real, Time, Volatility};
use super::{BlackVolTermStructure, VolatilityTermStructure};
pub struct ImpliedVolTermStructure {
base: TermStructureBase,
original: Handle<dyn BlackVolTermStructure>,
}
impl ImpliedVolTermStructure {
pub fn new(original: Handle<dyn BlackVolTermStructure>, reference_date: Date) -> Self {
let base = TermStructureBase::with_reference_date(reference_date, None, None);
original.register_observer(&base.updater());
Self { base, original }
}
}
impl AsObservable for ImpliedVolTermStructure {
fn observable(&self) -> &Observable {
self.base.observable()
}
}
impl TermStructure for ImpliedVolTermStructure {
fn base(&self) -> &TermStructureBase {
&self.base
}
fn register_upstream(&self, observer: &SharedMut<dyn Observer>) {
self.original.register_observer(observer);
}
fn max_date(&self) -> Date {
self.original
.current_link()
.map(|curve| curve.max_date())
.unwrap_or_else(|_| Date::null())
}
fn day_counter(&self) -> Option<DayCounter> {
self.original
.current_link()
.ok()
.and_then(|curve| curve.day_counter())
}
}
impl VolatilityTermStructure for ImpliedVolTermStructure {
fn business_day_convention(&self) -> BusinessDayConvention {
BusinessDayConvention::Following
}
fn min_strike(&self) -> Rate {
self.original
.current_link()
.map(|curve| curve.min_strike())
.unwrap_or(Rate::MIN)
}
fn max_strike(&self) -> Rate {
self.original
.current_link()
.map(|curve| curve.max_strike())
.unwrap_or(Rate::MAX)
}
}
impl BlackVolTermStructure for ImpliedVolTermStructure {
fn black_vol_impl(&self, t: Time, strike: Real) -> QlResult<Volatility> {
let maturity = if t == 0.0 { 1.0e-5 } else { t };
let variance = self.black_variance_impl(maturity, strike)?;
Ok((variance / maturity).sqrt())
}
fn black_variance_impl(&self, t: Time, strike: Real) -> QlResult<Real> {
let original = self.original.current_link()?;
let day_counter = original.require_day_counter()?;
let shift = day_counter.year_fraction(original.reference_date()?, self.reference_date()?);
original.black_forward_variance(shift, shift + t, strike, true)
}
}