libitofin 0.48.0

A ground-up Rust port of QuantLib: quantitative-finance primitives for pricing, risk, and numerical methods.
Documentation
use std::cell::RefCell;

use crate::errors::{QlError, QlResult};
use crate::math::integrals::Integrator;
use crate::math::integrals::segment::SegmentIntegral;
use crate::methods::montecarlo::{Path, PathPricer};
use crate::processes::GeneralizedBlackScholesProcess;
use crate::shared::Shared;
use crate::stochasticprocess::StochasticProcess1D;
use crate::{fail, require};

pub(super) struct VariancePathPricer {
    pub process: Shared<GeneralizedBlackScholesProcess>,
    pub error: RefCell<Option<QlError>>,
}

impl VariancePathPricer {
    fn variance(&self, path: &Path) -> QlResult<f64> {
        require!(path.length() >= 2, "variance MC path is empty");
        require!(
            path.values().iter().all(|x| x.is_finite() && *x > 0.0),
            "variance MC path states must be finite and positive"
        );
        let grid = path.time_grid();
        let Some(end) = grid.back() else {
            fail!("variance MC path end missing");
        };
        let dt = grid.dt(0);
        require!(
            end.is_finite() && end > 0.0 && dt.is_finite() && dt > 0.0,
            "variance MC grid must have positive finite spacing"
        );
        let count = end / dt;
        require!(
            count.is_finite() && count >= 1.0 && count <= path.length() as f64,
            "variance MC integration count invalid"
        );
        let intervals = count as usize;
        let integrator = SegmentIntegral::new(intervals)?;
        let mut failure = None;
        let integral = integrator.integrate(
            |time| {
                let result = (|| {
                    let index = time / dt;
                    require!(
                        index.is_finite() && index >= 0.0 && index < path.length() as f64,
                        "variance MC integration index invalid"
                    );
                    let Some(state) = path.at(index as usize) else {
                        fail!("variance MC integration state missing");
                    };
                    let sigma = self.process.diffusion(time, state)?;
                    let variance = sigma * sigma;
                    require!(
                        sigma.is_finite() && sigma >= 0.0 && variance.is_finite(),
                        "variance MC diffusion must be finite and nonnegative"
                    );
                    Ok(variance)
                })();
                match result {
                    Ok(value) => value,
                    Err(error) => {
                        if failure.is_none() {
                            failure = Some(error);
                        }
                        f64::NAN
                    }
                }
            },
            0.0,
            end,
        )?;
        if let Some(error) = failure {
            return Err(error);
        }
        let variance = integral / end;
        require!(
            integral.is_finite() && variance.is_finite() && variance >= 0.0,
            "variance MC integrated variance must be finite and nonnegative"
        );
        Ok(variance)
    }
}

impl PathPricer<Path> for VariancePathPricer {
    fn price(&self, path: &Path) -> f64 {
        match self.variance(path) {
            Ok(value) => value,
            Err(error) => {
                let mut failure = self.error.borrow_mut();
                if failure.is_none() {
                    *failure = Some(error);
                }
                f64::NAN
            }
        }
    }
}