use crate::Positive;
use crate::chains::chain::OptionChain;
use crate::curves::Curve;
use std::error::Error;
pub trait VolatilitySmile {
fn smile(&self) -> Curve;
}
pub trait AtmIvProvider {
fn atm_iv(&self) -> Result<&Positive, Box<dyn Error>>;
}
impl AtmIvProvider for Positive {
fn atm_iv(&self) -> Result<&Positive, Box<dyn Error>> {
Ok(self)
}
}
impl AtmIvProvider for OptionChain {
fn atm_iv(&self) -> Result<&Positive, Box<dyn Error>> {
match self.get_atm_implied_volatility() {
Ok(iv) => Ok(iv),
Err(e) => Err(Box::new(std::io::Error::new(
std::io::ErrorKind::NotFound,
format!("ATM IV not available: {e}"),
))),
}
}
}
#[cfg(test)]
mod tests_volatility_traits {
use super::*;
use crate::curves::{Curve, Point2D};
use crate::pos;
use rust_decimal::Decimal;
use rust_decimal_macros::dec;
use std::collections::BTreeSet;
use std::io::ErrorKind;
struct TestSmile;
impl VolatilitySmile for TestSmile {
fn smile(&self) -> Curve {
create_sample_curve()
}
}
struct TestIvProvider {
iv: Positive,
}
impl AtmIvProvider for TestIvProvider {
fn atm_iv(&self) -> Result<&Positive, Box<dyn Error>> {
Ok(&self.iv)
}
}
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_smile_implementation() {
let smile = TestSmile;
let curve = smile.smile();
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_smile() {
let smile = TestSmile;
let curve = smile.smile();
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_smile_with_empty_curve() {
struct EmptySmile;
impl VolatilitySmile for EmptySmile {
fn smile(&self) -> Curve {
Curve {
points: BTreeSet::new(),
x_range: (Decimal::ZERO, Decimal::ZERO),
}
}
}
let smile = EmptySmile;
let curve = smile.smile();
assert!(curve.points.is_empty());
assert_eq!(curve.x_range, (Decimal::ZERO, Decimal::ZERO));
}
#[test]
fn test_atm_iv_provider_for_positive() {
let value = pos!(0.2);
let result = value.atm_iv();
assert!(result.is_ok());
}
#[test]
fn test_atm_iv_provider_some() {
let provider = TestIvProvider { iv: pos!(0.25) };
let result = provider.atm_iv();
assert!(result.is_ok());
}
#[test]
fn test_atm_iv_provider_error() {
struct ErrorIvProvider;
impl AtmIvProvider for ErrorIvProvider {
fn atm_iv(&self) -> Result<&Positive, Box<dyn Error>> {
Err(Box::new(std::io::Error::new(
ErrorKind::NotFound,
"ATM IV not available: test error",
)))
}
}
let provider = ErrorIvProvider;
let result = provider.atm_iv();
assert!(result.is_err());
let error = result.unwrap_err();
let io_error = error.downcast_ref::<std::io::Error>().unwrap();
assert_eq!(io_error.kind(), ErrorKind::NotFound);
assert!(io_error.to_string().contains("ATM IV not available"));
}
#[test]
fn test_combined_traits_usage() {
struct CombinedProvider {
iv_value: Positive,
}
impl VolatilitySmile for CombinedProvider {
fn smile(&self) -> Curve {
create_sample_curve()
}
}
impl AtmIvProvider for CombinedProvider {
fn atm_iv(&self) -> Result<&Positive, Box<dyn Error>> {
Ok(&self.iv_value)
}
}
let provider_with_iv = CombinedProvider {
iv_value: pos!(0.2),
};
let curve = provider_with_iv.smile();
assert_eq!(curve.points.len(), 5);
let iv_result = provider_with_iv.atm_iv();
assert!(iv_result.is_ok());
}
}