use crate::error::{GreeksError, OptionsError};
use positive::Positive;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum VolatilityError {
#[error("Invalid price {price}: {reason}")]
InvalidPrice {
price: Positive,
reason: String,
},
#[error("Invalid time {time}: {reason}")]
InvalidTime {
time: Positive,
reason: String,
},
#[error("Vega is zero, cannot calculate implied volatility")]
ZeroVega,
#[error(transparent)]
Greeks(#[from] GreeksError),
#[error(transparent)]
Options(#[from] OptionsError),
#[error("Error calculating vega: {reason}")]
VegaError {
reason: String,
},
#[error("Option error: {reason}")]
OptionError {
reason: String,
},
#[error("No convergence after {iterations} iterations. Last volatility: {last_volatility}")]
NoConvergence {
iterations: u32,
last_volatility: Positive,
},
#[error(transparent)]
PositiveError(#[from] positive::PositiveError),
}
impl From<&str> for VolatilityError {
fn from(s: &str) -> Self {
VolatilityError::OptionError {
reason: s.to_string(),
}
}
}
impl From<String> for VolatilityError {
fn from(s: String) -> Self {
VolatilityError::OptionError { reason: s }
}
}
#[cfg(test)]
mod tests_volatility_errors {
use super::*;
use crate::error::greeks::InputErrorKind;
use crate::error::{GreeksError, OptionsError};
use positive::pos_or_panic;
#[test]
fn test_invalid_price_error() {
let error = VolatilityError::InvalidPrice {
price: Positive::ZERO,
reason: "Price cannot be zero".to_string(),
};
assert_eq!(error.to_string(), "Invalid price 0: Price cannot be zero");
}
#[test]
fn test_invalid_time_error() {
let error = VolatilityError::InvalidTime {
time: Positive::ZERO,
reason: "Time cannot be zero".to_string(),
};
assert_eq!(error.to_string(), "Invalid time 0: Time cannot be zero");
}
#[test]
fn test_zero_vega_error() {
let error = VolatilityError::ZeroVega;
assert_eq!(
error.to_string(),
"Vega is zero, cannot calculate implied volatility"
);
}
#[test]
fn test_vega_error() {
let error = VolatilityError::VegaError {
reason: "Failed to calculate vega".to_string(),
};
assert_eq!(
error.to_string(),
"Error calculating vega: Failed to calculate vega"
);
}
#[test]
fn test_option_error() {
let error = VolatilityError::OptionError {
reason: "Invalid option parameters".to_string(),
};
assert_eq!(error.to_string(), "Option error: Invalid option parameters");
}
#[test]
fn test_no_convergence_error() {
let error = VolatilityError::NoConvergence {
iterations: 100,
last_volatility: pos_or_panic!(0.5),
};
assert_eq!(
error.to_string(),
"No convergence after 100 iterations. Last volatility: 0.5"
);
}
#[test]
fn test_from_greeks_error() {
let greeks_error = GreeksError::InputError(InputErrorKind::InvalidVolatility {
value: 0.0,
reason: "Volatility cannot be zero".to_string(),
});
let implied_vol_error: VolatilityError = greeks_error.into();
match implied_vol_error {
VolatilityError::Greeks(_) => {
}
_ => panic!("Wrong error variant"),
}
}
#[test]
fn test_from_options_error() {
let greeks_error = OptionsError::OtherError {
reason: "Invalid option parameters".to_string(),
};
let implied_vol_error: VolatilityError = greeks_error.into();
match implied_vol_error {
VolatilityError::Options(_) => {
}
_ => panic!("Wrong error variant"),
}
}
#[test]
fn test_error_is_send() {
fn assert_send<T: Send>() {}
assert_send::<VolatilityError>();
}
#[test]
fn test_error_is_sync() {
fn assert_sync<T: Sync>() {}
assert_sync::<VolatilityError>();
}
}