libitofin 0.47.0

A ground-up Rust port of QuantLib: quantitative-finance primitives for pricing, risk, and numerical methods.
Documentation
use crate::math::array::Array;
use crate::math::optimization::constraint::Constraint;
use crate::processes::GjrGarchParameters;
use crate::types::Real;

pub(super) struct ScalarDomain {
    index: usize,
}

impl ScalarDomain {
    pub(super) fn new(index: usize) -> Self {
        Self { index }
    }

    fn accepts(&self, value: Real) -> bool {
        let (lower, upper) = self.bounds();
        value.is_finite()
            && value >= lower
            && value <= upper
            && (!matches!(self.index, 0 | 5) || value > 0.0)
    }

    fn bounds(&self) -> (Real, Real) {
        match self.index {
            0 | 5 => (0.0, Real::MAX),
            1 | 2 => (0.0, 1.0),
            3 => (-1.0, 1.0),
            _ => (-Real::MAX, Real::MAX),
        }
    }
}

impl Constraint for ScalarDomain {
    fn test(&self, params: &Array) -> bool {
        params.iter().all(|&value| self.accepts(value))
    }

    fn lower_bound(&self, params: &Array) -> Array {
        Array::filled(params.size(), self.bounds().0)
    }

    fn upper_bound(&self, params: &Array) -> Array {
        Array::filled(params.size(), self.bounds().1)
    }
}

pub(super) struct GjrGarchConstraint {
    days_per_year: Real,
}

impl GjrGarchConstraint {
    pub(super) fn new(days_per_year: Real) -> Self {
        Self { days_per_year }
    }
}

impl Constraint for GjrGarchConstraint {
    fn test(&self, params: &Array) -> bool {
        params.size() == 6
            && params
                .iter()
                .enumerate()
                .all(|(index, &value)| ScalarDomain::new(index).accepts(value))
            && params[2] + params[3] >= 0.0
            && GjrGarchParameters {
                omega: params[0],
                alpha: params[1],
                beta: params[2],
                gamma: params[3],
                lambda: params[4],
                v0: params[5],
                days_per_year: self.days_per_year,
            }
            .validate()
            .is_ok()
    }

    fn lower_bound(&self, params: &Array) -> Array {
        (0..params.size())
            .map(|index| ScalarDomain::new(index).bounds().0)
            .collect()
    }

    fn upper_bound(&self, params: &Array) -> Array {
        (0..params.size())
            .map(|index| ScalarDomain::new(index).bounds().1)
            .collect()
    }
}