use crate::curves::Curve;
use crate::error::CurveError;
pub trait RiskReversalCurve {
fn risk_reversal_curve(&self) -> Result<Curve, CurveError>;
}
#[cfg(test)]
mod tests_risk_reversal_traits {
use super::*;
use crate::curves::Point2D;
use rust_decimal_macros::dec;
use std::collections::BTreeSet;
struct TestRiskReversal;
impl RiskReversalCurve for TestRiskReversal {
fn risk_reversal_curve(&self) -> Result<Curve, CurveError> {
let mut points = BTreeSet::new();
points.insert(Point2D::new(dec!(90.0), dec!(-0.05)));
points.insert(Point2D::new(dec!(95.0), dec!(-0.03)));
points.insert(Point2D::new(dec!(100.0), dec!(-0.01)));
points.insert(Point2D::new(dec!(105.0), dec!(0.00)));
points.insert(Point2D::new(dec!(110.0), dec!(0.01)));
Ok(Curve::new(points))
}
}
struct TestPositiveRiskReversal;
impl RiskReversalCurve for TestPositiveRiskReversal {
fn risk_reversal_curve(&self) -> Result<Curve, CurveError> {
let mut points = BTreeSet::new();
points.insert(Point2D::new(dec!(90.0), dec!(0.01)));
points.insert(Point2D::new(dec!(95.0), dec!(0.02)));
points.insert(Point2D::new(dec!(100.0), dec!(0.03)));
points.insert(Point2D::new(dec!(105.0), dec!(0.04)));
points.insert(Point2D::new(dec!(110.0), dec!(0.05)));
Ok(Curve::new(points))
}
}
struct TestEmptyRiskReversal;
impl RiskReversalCurve for TestEmptyRiskReversal {
fn risk_reversal_curve(&self) -> Result<Curve, CurveError> {
Err(CurveError::ConstructionError(
"No options with both call and put IV available".to_string(),
))
}
}
#[test]
fn test_risk_reversal_implementation() {
let rr = TestRiskReversal;
let curve = rr.risk_reversal_curve().unwrap();
assert_eq!(curve.points.len(), 5);
let points: Vec<&Point2D> = curve.points.iter().collect();
assert_eq!(points[0].x, dec!(90.0));
assert_eq!(points[0].y, dec!(-0.05));
}
#[test]
fn test_risk_reversal_negative_skew() {
let rr = TestRiskReversal;
let curve = rr.risk_reversal_curve().unwrap();
let points: Vec<&Point2D> = curve.points.iter().collect();
assert!(points[0].y < points[4].y);
}
#[test]
fn test_risk_reversal_positive_skew() {
let rr = TestPositiveRiskReversal;
let curve = rr.risk_reversal_curve().unwrap();
let points: Vec<&Point2D> = curve.points.iter().collect();
for point in points.iter() {
assert!(point.y > rust_decimal::Decimal::ZERO);
}
}
#[test]
fn test_risk_reversal_empty_error() {
let rr = TestEmptyRiskReversal;
let result = rr.risk_reversal_curve();
assert!(result.is_err());
match result {
Err(CurveError::ConstructionError(msg)) => {
assert!(msg.contains("No options"));
}
_ => panic!("Expected ConstructionError"),
}
}
#[test]
fn test_risk_reversal_atm_near_zero() {
let rr = TestRiskReversal;
let curve = rr.risk_reversal_curve().unwrap();
let points: Vec<&Point2D> = curve.points.iter().collect();
let atm_point = points.iter().find(|p| p.x == dec!(100.0)).unwrap();
assert!(atm_point.y.abs() < dec!(0.02));
}
#[test]
fn test_risk_reversal_monotonic_increase() {
let rr = TestRiskReversal;
let curve = rr.risk_reversal_curve().unwrap();
let points: Vec<&Point2D> = curve.points.iter().collect();
for i in 1..points.len() {
assert!(points[i].y >= points[i - 1].y);
}
}
}