use crate::no_arb::butterfly::{g, g_with_scale};
use crate::numerics::index_to_f64;
use crate::smile::raw::RawSvi;
use core::fmt;
const TWO_PI: f64 = std::f64::consts::TAU;
#[must_use]
#[inline]
pub fn d_minus(svi: &RawSvi, k: f64) -> f64 {
let w = svi.total_variance(k);
let sqrt_w = w.sqrt();
-k / sqrt_w - sqrt_w / 2.0
}
#[must_use]
#[inline]
pub fn d_plus(svi: &RawSvi, k: f64) -> f64 {
let w = svi.total_variance(k);
let sqrt_w = w.sqrt();
-k / sqrt_w + sqrt_w / 2.0
}
pub fn risk_neutral_density(svi: &RawSvi, k: f64) -> Result<f64, DensityError> {
if !k.is_finite() {
return Err(DensityError::InvalidDomain);
}
let w = svi.total_variance(k);
if !w.is_finite() {
return Err(DensityError::NonFiniteEvaluation { k });
}
if w <= 0.0 {
return Err(DensityError::NonPositiveVariance { k, w });
}
let variance_scale = 1.0 + svi.a().abs() + svi.b().abs() * svi.sigma().abs();
let near_minimum = (k - svi.k_min()).abs() <= 16.0 * f64::EPSILON * (1.0 + k.abs());
let uncertainty = 16.0 * f64::EPSILON * variance_scale;
if svi.b() > 0.0 && near_minimum && w <= uncertainty {
return Err(DensityError::IllConditionedVariance { k, w, uncertainty });
}
let dm = d_minus(svi, k);
let value = g(svi, k) / (TWO_PI * w).sqrt() * (-0.5 * dm * dm).exp();
if value.is_finite() {
Ok(value)
} else {
Err(DensityError::NonFiniteEvaluation { k })
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum DensityError {
InvalidDomain,
NonPositiveVariance {
k: f64,
w: f64,
},
IllConditionedVariance {
k: f64,
w: f64,
uncertainty: f64,
},
NonFiniteEvaluation {
k: f64,
},
}
impl fmt::Display for DensityError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::InvalidDomain => {
write!(f, "density domain must be finite, ordered, and non-empty")
}
Self::NonPositiveVariance { k, w } => {
write!(f, "density is singular at k={k}: total variance is {w}")
}
Self::IllConditionedVariance { k, w, uncertainty } => write!(
f,
"density is ill-conditioned at k={k}: total variance {w} is within uncertainty {uncertainty}"
),
Self::NonFiniteEvaluation { k } => {
write!(f, "density evaluation is non-finite at k={k}")
}
}
}
}
impl std::error::Error for DensityError {}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct DensityReport {
lower: f64,
upper: f64,
panels: usize,
integral: f64,
integration_error: f64,
min_density: f64,
violation_observed: bool,
}
impl DensityReport {
#[must_use]
pub const fn domain(self) -> (f64, f64) {
(self.lower, self.upper)
}
#[must_use]
pub const fn panels(self) -> usize {
self.panels
}
#[must_use]
pub const fn integral(self) -> f64 {
self.integral
}
#[must_use]
pub const fn integration_error(self) -> f64 {
self.integration_error
}
#[must_use]
pub const fn min_density(self) -> f64 {
self.min_density
}
#[must_use]
pub const fn violation_observed(self) -> bool {
self.violation_observed
}
}
pub fn integral(svi: &RawSvi, k_lo: f64, k_hi: f64, n: usize) -> Result<f64, DensityError> {
if !k_lo.is_finite() || !k_hi.is_finite() || k_lo >= k_hi || n == 0 || n > usize::MAX / 2 {
return Err(DensityError::InvalidDomain);
}
let panels = 2 * n;
let h = (k_hi - k_lo) / index_to_f64(panels);
let mut sum = risk_neutral_density(svi, k_lo)? + risk_neutral_density(svi, k_hi)?;
if !sum.is_finite() {
return Err(DensityError::NonFiniteEvaluation { k: k_lo });
}
for i in 1..panels {
let k = h.mul_add(index_to_f64(i), k_lo);
let weight = if i % 2 == 1 { 4.0 } else { 2.0 };
sum += weight * risk_neutral_density(svi, k)?;
if !sum.is_finite() {
return Err(DensityError::NonFiniteEvaluation { k });
}
}
let value = sum * h / 3.0;
if value.is_finite() {
Ok(value)
} else {
Err(DensityError::NonFiniteEvaluation { k: k_hi })
}
}
pub fn density_report(
svi: &RawSvi,
k_lo: f64,
k_hi: f64,
n: usize,
) -> Result<DensityReport, DensityError> {
if !k_lo.is_finite() || !k_hi.is_finite() || k_lo >= k_hi || n == 0 || n > usize::MAX / 4 {
return Err(DensityError::InvalidDomain);
}
let panels = 4 * n;
let h = (k_hi - k_lo) / index_to_f64(panels);
let mut min_density = f64::INFINITY;
let mut violation_observed = false;
for i in 0..=panels {
let k = h.mul_add(index_to_f64(i), k_lo);
let p = risk_neutral_density(svi, k)?;
if p < min_density {
min_density = p;
}
let (density_factor, evaluation_scale) = g_with_scale(svi, k);
violation_observed |= density_factor < -128.0 * f64::EPSILON * evaluation_scale;
}
let coarse_integral = integral(svi, k_lo, k_hi, n)?;
let fine_integral = integral(svi, k_lo, k_hi, 2 * n)?;
let integration_error = (fine_integral - coarse_integral).abs() / 15.0;
if !integration_error.is_finite() {
return Err(DensityError::NonFiniteEvaluation { k: k_hi });
}
Ok(DensityReport {
lower: k_lo,
upper: k_hi,
panels,
integral: fine_integral,
integration_error,
min_density,
violation_observed,
})
}
#[cfg(test)]
#[allow(clippy::expect_used)] mod tests {
use super::*;
#[test]
fn d_plus_d_minus_differ_by_sqrt_w() {
let svi =
RawSvi::new(0.04, 0.2, -0.3, 0.05, 0.12).expect("valid test or documentation fixture");
for &k in &[-0.5, 0.0, 0.3] {
let w = svi.total_variance(k);
assert!((d_plus(&svi, k) - d_minus(&svi, k) - w.sqrt()).abs() < 1e-12);
}
}
#[test]
fn density_positive_for_benign_slice() {
let svi =
RawSvi::new(0.04, 0.1, -0.2, 0.0, 0.3).expect("valid test or documentation fixture");
for &k in &[-1.0, -0.3, 0.0, 0.3, 1.0] {
assert!(
risk_neutral_density(&svi, k).expect("valid test or documentation fixture") > 0.0,
"p({k})"
);
}
}
#[test]
fn density_integrates_to_one() {
let svi =
RawSvi::new(0.04, 0.05, -0.1, 0.0, 0.4).expect("valid test or documentation fixture");
let mass = integral(&svi, -8.0, 8.0, 4000).expect("valid test or documentation fixture");
assert!((mass - 1.0).abs() < 1e-3, "mass = {mass}");
}
#[test]
fn density_integrates_to_one_low_vol() {
let svi =
RawSvi::new(0.02, 0.04, -0.15, 0.0, 0.3).expect("valid test or documentation fixture");
let mass = integral(&svi, -6.0, 6.0, 4000).expect("valid test or documentation fixture");
assert!((mass - 1.0).abs() < 1e-3, "mass = {mass}");
}
#[test]
fn density_report_benign_slice() {
let svi =
RawSvi::new(0.04, 0.05, -0.1, 0.0, 0.4).expect("valid test or documentation fixture");
let report =
density_report(&svi, -8.0, 8.0, 4000).expect("valid test or documentation fixture");
assert!(!report.violation_observed());
assert!((report.integral() - 1.0).abs() < 1e-3);
assert!(report.integration_error().is_finite());
assert!(report.integration_error() >= 0.0);
let (lower, upper) = report.domain();
assert!((lower + 8.0).abs() < f64::EPSILON);
assert!((upper - 8.0).abs() < f64::EPSILON);
assert_eq!(report.panels(), 16_000);
assert!(report.min_density() >= 0.0);
}
#[test]
fn density_report_flags_vogt_slice() {
let vogt = RawSvi::new(-0.0410, 0.1331, 0.3060, 0.3586, 0.4153)
.expect("valid test or documentation fixture");
let report =
density_report(&vogt, -2.0, 2.0, 2000).expect("valid test or documentation fixture");
assert!(report.violation_observed());
assert!(report.min_density() < 0.0);
}
#[test]
fn density_handles_zero_variance_gracefully() {
let svi = RawSvi::new(-0.125, 0.5, 0.0, 0.0, 0.25).expect("valid exact-zero fixture");
assert!(matches!(
risk_neutral_density(&svi, svi.k_min()),
Err(DensityError::NonPositiveVariance { .. })
));
}
#[test]
fn tiny_positive_variance_is_not_classified_as_non_positive() {
let slice =
RawSvi::new(-0.02 + 1e-16, 0.1, 0.0, 0.0, 0.2).expect("valid tiny-positive fixture");
assert!(slice.w_min() > 0.0);
assert!(matches!(
risk_neutral_density(&slice, slice.k_min()),
Err(DensityError::IllConditionedVariance { w, .. }) if w > 0.0
));
}
#[test]
fn non_finite_variance_is_not_classified_as_non_positive() {
let slice = RawSvi::new_unchecked(0.0, f64::MAX, 0.0, -f64::MAX, f64::MIN_POSITIVE);
assert!(matches!(
risk_neutral_density(&slice, 0.0),
Err(DensityError::NonFiniteEvaluation { k: 0.0 })
));
}
}