use crate::error::common::OperationErrorKind;
use crate::error::{OptionsError, PositionError};
use std::error::Error;
use std::fmt;
impl Error for StrategyError {}
impl Error for PriceErrorKind {}
impl Error for BreakEvenErrorKind {}
impl Error for ProfitLossErrorKind {}
#[derive(Debug)]
pub enum StrategyError {
PriceError(PriceErrorKind),
BreakEvenError(BreakEvenErrorKind),
ProfitLossError(ProfitLossErrorKind),
OperationError(OperationErrorKind),
StdError {
reason: String,
},
NotImplemented,
}
#[derive(Debug)]
pub enum PriceErrorKind {
InvalidUnderlyingPrice {
reason: String,
},
InvalidPriceRange {
start: f64,
end: f64,
reason: String,
},
}
#[derive(Debug)]
pub enum BreakEvenErrorKind {
CalculationError {
reason: String,
},
NoBreakEvenPoints,
}
#[derive(Debug)]
pub enum ProfitLossErrorKind {
MaxProfitError {
reason: String,
},
MaxLossError {
reason: String,
},
ProfitRangeError {
reason: String,
},
}
impl fmt::Display for StrategyError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
StrategyError::PriceError(err) => write!(f, "Price error: {err}"),
StrategyError::BreakEvenError(err) => write!(f, "Break-even error: {err}"),
StrategyError::ProfitLossError(err) => write!(f, "Profit/Loss error: {err}"),
StrategyError::OperationError(err) => write!(f, "Operation error: {err}"),
StrategyError::StdError { reason } => write!(f, "Error: {reason}"),
StrategyError::NotImplemented => write!(f, "Operation not implemented"),
}
}
}
impl fmt::Display for PriceErrorKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
PriceErrorKind::InvalidUnderlyingPrice { reason } => {
write!(f, "Invalid underlying price: {reason}")
}
PriceErrorKind::InvalidPriceRange { start, end, reason } => {
write!(f, "Invalid price range [{start}, {end}]: {reason}")
}
}
}
}
impl fmt::Display for BreakEvenErrorKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
BreakEvenErrorKind::CalculationError { reason } => {
write!(f, "Break-even calculation error: {reason}")
}
BreakEvenErrorKind::NoBreakEvenPoints => {
write!(f, "No break-even points found")
}
}
}
}
impl fmt::Display for ProfitLossErrorKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ProfitLossErrorKind::MaxProfitError { reason } => {
write!(f, "Maximum profit calculation error: {reason}")
}
ProfitLossErrorKind::MaxLossError { reason } => {
write!(f, "Maximum loss calculation error: {reason}")
}
ProfitLossErrorKind::ProfitRangeError { reason } => {
write!(f, "Profit range calculation error: {reason}")
}
}
}
}
pub type StrategyResult<T> = Result<T, StrategyError>;
impl StrategyError {
pub fn operation_not_supported(operation: &str, strategy_type: &str) -> Self {
StrategyError::OperationError(OperationErrorKind::NotSupported {
operation: operation.to_string(),
reason: strategy_type.to_string(),
})
}
pub fn invalid_parameters(operation: &str, reason: &str) -> Self {
StrategyError::OperationError(OperationErrorKind::InvalidParameters {
operation: operation.to_string(),
reason: reason.to_string(),
})
}
}
impl From<PositionError> for StrategyError {
fn from(err: PositionError) -> Self {
StrategyError::OperationError(OperationErrorKind::InvalidParameters {
operation: "Position".to_string(),
reason: err.to_string(),
})
}
}
impl From<OptionsError> for StrategyError {
fn from(err: OptionsError) -> Self {
StrategyError::OperationError(OperationErrorKind::InvalidParameters {
operation: "Options".to_string(),
reason: err.to_string(),
})
}
}
impl From<Box<dyn Error>> for StrategyError {
fn from(err: Box<dyn Error>) -> Self {
StrategyError::StdError {
reason: err.to_string(),
}
}
}
#[cfg(test)]
mod tests_from_str {
use super::*;
use crate::error::ProbabilityError;
#[test]
fn test_strategy_to_probability_error_conversion() {
let strategy_error = StrategyError::operation_not_supported("max_profit", "TestStrategy");
let probability_error = ProbabilityError::from(strategy_error);
assert!(probability_error.to_string().contains("max_profit"));
assert!(probability_error.to_string().contains("TestStrategy"));
}
#[test]
fn test_profit_loss_error_conversion() {
let strategy_error = StrategyError::ProfitLossError(ProfitLossErrorKind::MaxProfitError {
reason: "Test error".to_string(),
});
let probability_error = ProbabilityError::from(strategy_error);
assert!(probability_error.to_string().contains("Test error"));
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_strategy_error_creation() {
let error = StrategyError::operation_not_supported("max_profit", "TestStrategy");
assert!(matches!(
error,
StrategyError::OperationError(OperationErrorKind::NotSupported { .. })
));
}
#[test]
fn test_error_messages() {
let error = StrategyError::operation_not_supported("max_profit", "TestStrategy");
let error_string = error.to_string();
assert!(error_string.contains("max_profit"));
assert!(error_string.contains("TestStrategy"));
}
}
#[cfg(test)]
mod tests_display {
use super::*;
#[test]
fn test_price_error_display() {
let error = StrategyError::PriceError(PriceErrorKind::InvalidUnderlyingPrice {
reason: "Price cannot be negative".to_string(),
});
assert!(error.to_string().contains("Price cannot be negative"));
}
#[test]
fn test_break_even_error_display() {
let error = StrategyError::BreakEvenError(BreakEvenErrorKind::CalculationError {
reason: "Invalid input parameters".to_string(),
});
assert!(error.to_string().contains("Invalid input parameters"));
}
#[test]
fn test_profit_loss_error_display() {
let error = StrategyError::ProfitLossError(ProfitLossErrorKind::MaxProfitError {
reason: "Cannot calculate maximum profit".to_string(),
});
assert!(
error
.to_string()
.contains("Cannot calculate maximum profit")
);
}
#[test]
fn test_operation_error_display() {
let error = StrategyError::operation_not_supported("max_profit", "TestStrategy");
assert!(error.to_string().contains("max_profit"));
assert!(error.to_string().contains("TestStrategy"));
}
}
#[cfg(test)]
mod tests_extended {
use super::*;
#[test]
fn test_strategy_error_std_error() {
let error = StrategyError::StdError {
reason: "General failure".to_string(),
};
assert_eq!(format!("{error}"), "Error: General failure");
}
#[test]
fn test_price_error_invalid_price_range() {
let error = PriceErrorKind::InvalidPriceRange {
start: 10.0,
end: 50.0,
reason: "Start price must be less than end price".to_string(),
};
assert_eq!(
format!("{error}"),
"Invalid price range [10, 50]: Start price must be less than end price"
);
}
#[test]
fn test_break_even_error_no_points() {
let error = BreakEvenErrorKind::NoBreakEvenPoints;
assert_eq!(format!("{error}"), "No break-even points found");
}
#[test]
fn test_profit_loss_error_max_loss_error() {
let error = ProfitLossErrorKind::MaxLossError {
reason: "Loss exceeds margin requirements".to_string(),
};
assert_eq!(
format!("{error}"),
"Maximum loss calculation error: Loss exceeds margin requirements"
);
}
#[test]
fn test_profit_loss_error_profit_range_error() {
let error = ProfitLossErrorKind::ProfitRangeError {
reason: "Profit calculation failed".to_string(),
};
assert_eq!(
format!("{error}"),
"Profit range calculation error: Profit calculation failed"
);
}
#[test]
fn test_strategy_error_invalid_parameters_constructor() {
let error = StrategyError::invalid_parameters("Open position", "Margin insufficient");
assert_eq!(
format!("{error}"),
"Operation error: Invalid parameters for operation 'Open position': Margin insufficient"
);
}
#[test]
fn test_strategy_error_from_boxed_error() {
let boxed_error: Box<dyn Error> = Box::new(std::io::Error::other("Underlying failure"));
let error: StrategyError = boxed_error.into();
assert_eq!(format!("{error}"), "Error: Underlying failure");
}
}