libitofin 0.52.1

A ground-up Rust port of QuantLib: quantitative-finance primitives for pricing, risk, and numerical methods.
Documentation
//! Optionlet (caplet/floorlet) volatility structures.
//!
//! Port of `ql/termstructures/volatility/optionlet/`.
//! [`OptionletVolatilityStructure`] adds the caplet volatility and Black
//! variance queries on top of [`VolatilityTermStructure`], in tenor, date and
//! time form, range- and strike-checked exactly as the C++ base performs them
//! before dispatching to the volatility hook. The volatility type and
//! displacement select the pricing model the surface feeds the coupon pricer.
//!
mod constantoptionletvol;
mod cubicsmile;
mod optionletstripper;
mod optionletstripper1;
mod optionletstripper2;
pub use optionletstripper2::OptionletStripper2;
mod strippedoptionletadapter;
mod strippedoptionletbase;

pub use constantoptionletvol::ConstantOptionletVolatility;
pub use optionletstripper::{OptionletStripper, OptionletStripperCaches};
pub use optionletstripper1::{OptionletStripper1, OptionletStripperOptions};
pub use strippedoptionletadapter::StrippedOptionletAdapter;
pub use strippedoptionletbase::StrippedOptionletBase;

use crate::errors::QlResult;
use crate::termstructures::volatility::{VolatilityTermStructure, VolatilityType};
use crate::time::date::Date;
use crate::time::period::Period;
use crate::types::{Rate, Real, Time, Volatility};

/// Optionlet (caplet/floorlet) volatility structure.
///
/// Mirrors QuantLib's `OptionletVolatilityStructure`: concrete surfaces
/// implement [`volatility_impl`](Self::volatility_impl); the provided queries
/// run the range and strike checks and dispatch to it, deriving the Black
/// variance as `volatility^2 * time`. Volatilities are expressed on an annual
/// basis.
pub trait OptionletVolatilityStructure: VolatilityTermStructure {
    /// Smile implementation for a checked option time.
    fn smile_section_impl(
        &self,
        _time: Time,
    ) -> QlResult<crate::shared::Shared<dyn super::SmileSection>> {
        crate::fail!("smile sections are not implemented for this optionlet surface")
    }

    /// Smile at an option time, with the same range checks as volatility queries.
    fn smile_section(
        &self,
        time: Time,
        extrapolate: bool,
    ) -> QlResult<crate::shared::Shared<dyn super::SmileSection>> {
        self.check_range_time(time, extrapolate)?;
        self.smile_section_impl(time)
    }

    /// Smile at an option date.
    fn smile_section_date(
        &self,
        date: Date,
        extrapolate: bool,
    ) -> QlResult<crate::shared::Shared<dyn super::SmileSection>> {
        self.check_range_date(date, extrapolate)?;
        self.smile_section_impl(self.time_from_reference(date)?)
    }

    /// Smile at an option tenor.
    fn smile_section_tenor(
        &self,
        tenor: Period,
        extrapolate: bool,
    ) -> QlResult<crate::shared::Shared<dyn super::SmileSection>> {
        self.smile_section_date(self.option_date_from_tenor(tenor)?, extrapolate)
    }

    /// Volatility calculation hook; range and strike checks have already run.
    fn volatility_impl(&self, option_time: Time, strike: Rate) -> QlResult<Volatility>;

    /// The pricing model the quoted volatilities are expressed in.
    fn volatility_type(&self) -> VolatilityType {
        VolatilityType::ShiftedLognormal
    }

    /// The lognormal shift applied to forward and strike; `0.0` for the
    /// unshifted lognormal and the normal model.
    fn displacement(&self) -> Real {
        0.0
    }

    /// Volatility for a given option date and strike rate.
    fn volatility_date(
        &self,
        option_date: Date,
        strike: Rate,
        extrapolate: bool,
    ) -> QlResult<Volatility> {
        self.check_range_date(option_date, extrapolate)?;
        self.check_strike(strike, extrapolate)?;
        let t = self.time_from_reference(option_date)?;
        self.volatility_impl(t, strike)
    }

    /// Volatility for a given option time and strike rate.
    fn volatility(
        &self,
        option_time: Time,
        strike: Rate,
        extrapolate: bool,
    ) -> QlResult<Volatility> {
        self.check_range_time(option_time, extrapolate)?;
        self.check_strike(strike, extrapolate)?;
        self.volatility_impl(option_time, strike)
    }

    /// Volatility for a given option tenor and strike rate.
    fn volatility_tenor(
        &self,
        option_tenor: Period,
        strike: Rate,
        extrapolate: bool,
    ) -> QlResult<Volatility> {
        let option_date = self.option_date_from_tenor(option_tenor)?;
        self.volatility_date(option_date, strike, extrapolate)
    }

    /// Black variance for a given option date and strike rate.
    fn black_variance_date(
        &self,
        option_date: Date,
        strike: Rate,
        extrapolate: bool,
    ) -> QlResult<Real> {
        let v = self.volatility_date(option_date, strike, extrapolate)?;
        let t = self.time_from_reference(option_date)?;
        Ok(v * v * t)
    }

    /// Black variance for a given option time and strike rate.
    fn black_variance(&self, option_time: Time, strike: Rate, extrapolate: bool) -> QlResult<Real> {
        let v = self.volatility(option_time, strike, extrapolate)?;
        Ok(v * v * option_time)
    }

    /// Black variance for a given option tenor and strike rate.
    fn black_variance_tenor(
        &self,
        option_tenor: Period,
        strike: Rate,
        extrapolate: bool,
    ) -> QlResult<Real> {
        let option_date = self.option_date_from_tenor(option_tenor)?;
        self.black_variance_date(option_date, strike, extrapolate)
    }
}