use crate::curves::Curve;
use crate::error::CurveError;
pub trait VolatilitySkewCurve {
fn volatility_skew(&self) -> Result<Curve, CurveError>;
}
#[cfg(test)]
mod tests_volatility_skew_traits {
use super::*;
use crate::curves::Point2D;
use rust_decimal::Decimal;
use rust_decimal_macros::dec;
use std::collections::BTreeSet;
struct TestSkew;
impl VolatilitySkewCurve for TestSkew {
fn volatility_skew(&self) -> Result<Curve, CurveError> {
let curve = create_sample_curve();
Ok(curve)
}
}
fn create_sample_curve() -> Curve {
let mut points = BTreeSet::new();
points.insert(Point2D::new(dec!(90.0), dec!(0.25)));
points.insert(Point2D::new(dec!(95.0), dec!(0.22)));
points.insert(Point2D::new(dec!(100.0), dec!(0.20)));
points.insert(Point2D::new(dec!(105.0), dec!(0.22)));
points.insert(Point2D::new(dec!(110.0), dec!(0.25)));
Curve {
points,
x_range: (dec!(90.0), dec!(110.0)),
}
}
#[test]
fn test_volatility_skew_implementation() {
let skew = TestSkew;
let curve = skew.volatility_skew().unwrap();
assert_eq!(curve.points.len(), 5);
assert_eq!(curve.x_range, (dec!(90.0), dec!(110.0)));
let points: Vec<&Point2D> = curve.points.iter().collect();
assert_eq!(points[0].x, dec!(90.0));
assert_eq!(points[0].y, dec!(0.25));
assert_eq!(points[2].x, dec!(100.0));
assert_eq!(points[2].y, dec!(0.20));
assert_eq!(points[4].x, dec!(110.0));
assert_eq!(points[4].y, dec!(0.25));
}
#[test]
fn test_volatility_skew() {
let skew = TestSkew;
let curve = skew.volatility_skew().unwrap();
assert_eq!(curve.points.len(), 5);
assert_eq!(curve.x_range, (dec!(90.0), dec!(110.0)));
let points: Vec<&Point2D> = curve.points.iter().collect();
assert_eq!(points[0].x, dec!(90.0));
assert_eq!(points[0].y, dec!(0.25));
assert_eq!(points[2].x, dec!(100.0));
assert_eq!(points[2].y, dec!(0.20));
assert_eq!(points[4].x, dec!(110.0));
assert_eq!(points[4].y, dec!(0.25));
}
#[test]
fn test_volatility_skew_with_empty_curve() {
struct EmptySkew;
impl VolatilitySkewCurve for EmptySkew {
fn volatility_skew(&self) -> Result<Curve, CurveError> {
let curve = Curve {
points: BTreeSet::new(),
x_range: (Decimal::ZERO, Decimal::ZERO),
};
Ok(curve)
}
}
let skew = EmptySkew;
let curve = skew.volatility_skew().unwrap();
assert!(curve.points.is_empty());
assert_eq!(curve.x_range, (Decimal::ZERO, Decimal::ZERO));
}
#[test]
fn test_volatility_skew_with_multiple_points() {
struct MultiPointSkew;
impl VolatilitySkewCurve for MultiPointSkew {
fn volatility_skew(&self) -> Result<Curve, CurveError> {
let mut points = BTreeSet::new();
points.insert(Point2D::new(dec!(-10.0), dec!(0.30))); points.insert(Point2D::new(dec!(-5.0), dec!(0.25)));
points.insert(Point2D::new(dec!(0.0), dec!(0.20))); points.insert(Point2D::new(dec!(5.0), dec!(0.22)));
points.insert(Point2D::new(dec!(10.0), dec!(0.25)));
let curve = Curve {
points,
x_range: (dec!(-10.0), dec!(10.0)),
};
Ok(curve)
}
}
let skew = MultiPointSkew;
let curve = skew.volatility_skew().unwrap();
assert_eq!(curve.points.len(), 5);
assert_eq!(curve.x_range, (dec!(-10.0), dec!(10.0)));
let points: Vec<&Point2D> = curve.points.iter().collect();
assert_eq!(points[2].x, dec!(0.0));
assert_eq!(points[2].y, dec!(0.20));
}
#[test]
fn test_volatility_skew_negative_moneyness() {
struct NegativeSkew;
impl VolatilitySkewCurve for NegativeSkew {
fn volatility_skew(&self) -> Result<Curve, CurveError> {
let mut points = BTreeSet::new();
points.insert(Point2D::new(dec!(-20.0), dec!(0.40)));
points.insert(Point2D::new(dec!(-15.0), dec!(0.35)));
points.insert(Point2D::new(dec!(-10.0), dec!(0.30)));
points.insert(Point2D::new(dec!(-5.0), dec!(0.25)));
let curve = Curve {
points,
x_range: (dec!(-20.0), dec!(-5.0)),
};
Ok(curve)
}
}
let skew = NegativeSkew;
let curve = skew.volatility_skew().unwrap();
assert_eq!(curve.points.len(), 4);
for point in curve.points.iter() {
assert!(point.x < Decimal::ZERO);
}
}
}