//! Error types for the risk management system // #![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic)] // COMMENTED: Crate-level allows applied use thiserror::Error; use common::error::CommonError; use common::types::Price; use crate::risk_types::RiskSeverity; /// Comprehensive error types for the risk management system /// /// This enum covers all possible error conditions that can occur during risk /// management operations, from configuration issues to critical safety violations. /// Each error variant includes context-specific information to aid in debugging /// and incident response. #[derive(Debug, Error)] #[error(transparent)] pub enum RiskError { /// Configuration error occurred during system initialization or validation #[error("Configuration error: {0}")] Config(String), /// Database operation failed (connection, query, transaction, etc.) #[error("Database error: {0}")] Database(String), /// Position limit exceeded for a specific instrument #[error("Position limit exceeded: {instrument} has {current} but limit is {limit}")] PositionLimitExceeded { /// The financial instrument that exceeded its limit instrument: String, /// Current position size current: Price, /// Maximum allowed position size limit: Price, }, /// Value at Risk limit exceeded, indicating portfolio risk is too high #[error("VaR limit exceeded: {var} exceeds limit of {limit}")] VarLimitExceeded { /// Current `VaR` value var: Price, /// Maximum allowed `VaR` limit: Price, }, /// Drawdown limit exceeded, indicating excessive portfolio losses #[error("Drawdown limit exceeded: {drawdown}% exceeds limit of {limit}%")] DrawdownLimitExceeded { /// Current drawdown percentage drawdown: Price, /// Maximum allowed drawdown percentage limit: Price, }, /// Daily loss limit exceeded, triggering risk controls #[error("Daily loss limit exceeded: {loss} exceeds limit of {limit}")] DailyLossLimitExceeded { /// Current daily loss amount loss: Price, /// Maximum allowed daily loss limit: Price, }, /// Circuit breaker is active, preventing trading on an instrument #[error("Circuit breaker active for {instrument}: {reason}")] CircuitBreakerActive { /// The instrument with an active circuit breaker instrument: String, /// Reason why the circuit breaker was triggered reason: String, }, /// Kill switch is active, immediately halting operations #[error("Kill switch active for {scope:?}: {message}")] KillSwitchActive { /// Scope of the kill switch (global, strategy, symbol) scope: crate::risk_types::KillSwitchScope, /// Detailed message about why the kill switch was activated message: String, }, /// Market data is unavailable for required calculations #[error("Market data unavailable for {instrument}")] MarketDataUnavailable { /// The instrument for which market data is missing instrument: String, }, /// Insufficient historical data for reliable risk calculations #[error("Insufficient historical data: need {required} but have {available}")] InsufficientHistoricalData { /// Number of data points required required: usize, /// Number of data points available available: usize, }, /// Correlation calculation failed between instruments #[error("Correlation calculation failed: {reason}")] CorrelationCalculationFailed { /// Detailed reason for the correlation calculation failure reason: String, }, /// Stress test scenario failed, indicating portfolio vulnerability #[error("Stress test failed: {scenario}")] StressTestFailed { /// The stress test scenario that failed scenario: String, }, /// Performance metric violated its threshold #[error("Performance violation: {metric} = {value}, threshold = {threshold}")] PerformanceViolation { /// The performance metric that was violated metric: String, /// Current value of the metric value: Price, /// Threshold value that was exceeded threshold: Price, }, /// Regulatory compliance rule was violated #[error("Compliance violation: {rule}")] ComplianceViolation { /// The compliance rule that was violated rule: String, }, /// User authorization failed for the requested operation #[error("Authorization failed: {reason}")] AuthorizationFailed { /// Reason why authorization failed reason: String, }, /// Order validation failed #[error("Invalid order: {reason}")] InvalidOrder { /// Reason why the order is invalid reason: String, }, /// Required service is unavailable #[error("Service unavailable: {service}")] ServiceUnavailable { /// Name of the unavailable service service: String, }, /// Operation timed out after specified duration #[error("Operation timeout: {timeout_ms}ms")] Timeout { /// Timeout duration in milliseconds timeout_ms: u64, }, /// Data serialization/deserialization failed #[error("Serialization error: {0}")] Serialization(String), /// Network communication error occurred #[error("Network error: {0}")] Network(String), /// Internal system error - should not occur in normal operation #[error("Internal error: {0}")] Internal(String), /// Data validation failed for a specific field #[error("Validation error: {field} - {message}")] Validation { /// The field that failed validation field: String, /// Detailed validation error message message: String, }, /// System resource was exhausted (memory, connections, etc.) #[error("Resource exhausted: {resource}")] ResourceExhausted { /// The resource that was exhausted resource: String, }, /// Mathematical calculation failed #[error("Calculation error: {operation} failed - {reason}")] Calculation { /// The calculation operation that failed operation: String, /// Reason for the calculation failure reason: String, }, /// Rate limit exceeded, operation must wait #[error("Rate limited: {remaining_ms}ms until reset")] RateLimited { /// Milliseconds remaining until rate limit resets remaining_ms: u64, }, /// Order side (buy/sell) is invalid #[error("Invalid order side: {side}")] InvalidOrderSide { /// The invalid order side value side: String, }, /// Order type is invalid or not supported #[error("Invalid order type: {order_type}")] InvalidOrderType { /// The invalid order type value order_type: String, }, /// Order quantity is invalid (negative, zero, too large, etc.) #[error("Invalid quantity: {quantity}")] InvalidQuantity { /// The invalid quantity value quantity: String, }, /// Order price is invalid (negative, zero, outside valid range, etc.) #[error("Invalid price: {price}")] InvalidPrice { /// The invalid price value price: String, }, /// Generic connection error occurred #[error("Connection error: {message}")] ConnectionError { /// Detailed connection error message message: String, }, /// Configuration-related error occurred #[error("Configuration error: {message}")] Configuration { /// Detailed configuration error message message: String, }, /// Generic validation error occurred #[error("Validation error: {message}")] ValidationError { /// Detailed validation error message message: String, }, /// Serialization/deserialization error occurred #[error("Serialization error: {message}")] SerializationError { /// Detailed serialization error message message: String, }, // NEW: Enhanced error types for hardened risk management /// Type conversion failed between incompatible types #[error("Type conversion error: {from_type} to {to_type} failed - {reason}")] TypeConversion { /// Source type being converted from from_type: String, /// Target type being converted to to_type: String, /// Reason for the conversion failure reason: String, }, /// Calculation error with simplified message #[error("Calculation error: {0}")] CalculationError(String), /// Required configuration parameter is missing #[error("Missing configuration: {config_key}")] MissingConfiguration { /// The configuration key that is missing config_key: String, }, /// Environment validation failed - missing requirements #[error("Environment validation failed: {environment} requires {requirement}")] EnvironmentValidation { /// The environment being validated environment: String, /// The requirement that was not met requirement: String, }, /// Data integrity check failed #[error("Data integrity error: {data_type} validation failed - {details}")] DataIntegrity { /// Type of data that failed integrity check data_type: String, /// Detailed information about the integrity failure details: String, }, /// Resource is temporarily unavailable #[error("Resource unavailable: {resource} temporarily unavailable - {reason}")] ResourceUnavailable { /// The unavailable resource resource: String, /// Reason why the resource is unavailable reason: String, }, /// Safety limit exceeded - immediate intervention required #[error("Safety limit exceeded: {limit_type} current={current} max={maximum}")] SafetyLimitExceeded { /// Type of safety limit that was exceeded limit_type: String, /// Current value that exceeded the limit current: String, /// Maximum allowed value maximum: String, }, /// Emergency stop triggered - all operations must halt immediately #[error("Emergency stop triggered: {trigger} - immediate halt required")] EmergencyStop { /// The trigger that caused the emergency stop trigger: String, }, /// Production safety violation detected #[error("Production safety violation: {violation} in {environment}")] ProductionSafety { /// Description of the safety violation violation: String, /// Environment where the violation occurred environment: String, }, /// Broker system error occurred #[error("Broker connection error: {0}")] BrokerError(String), /// Broker connection failed #[error("Broker connection error: {message}")] BrokerConnection { /// Detailed broker connection error message message: String, }, /// Connection to specific endpoint failed #[error("Connection failed to {endpoint}: {reason}")] Connection { /// The endpoint that failed to connect endpoint: String, /// Reason for the connection failure reason: String, }, /// Market data system error occurred #[error("Market data error: {0}")] MarketDataError(String), /// Required data is unavailable for calculations #[error("Data unavailable: {resource} - {reason}")] DataUnavailable { /// The data resource that is unavailable resource: String, /// Reason why the data is unavailable reason: String, }, /// Arithmetic overflow detected during calculation #[error("Arithmetic overflow: {operation} - {context}")] ArithmeticOverflow { /// The operation that overflowed operation: String, /// Context about the overflow context: String, }, } /// Result type for risk management operations pub type RiskResult = Result; /// Safe conversion helpers to eliminate `unwrap()` patterns use num::ToPrimitive; use rust_decimal::Decimal; use std::fmt::Display; /// Safely convert f64 to Price with context pub fn f64_to_price_safe(value: f64, context: &str) -> RiskResult { Price::from_f64(value).map_err(|_| RiskError::TypeConversion { from_type: "f64".to_owned(), to_type: "Price".to_owned(), reason: format!("{context}: invalid f64 value {value}"), }) } /// Safely convert f64 to Decimal with context pub fn f64_to_decimal_safe(value: f64, context: &str) -> RiskResult { Decimal::try_from(value).map_err(|_| RiskError::TypeConversion { from_type: "f64".to_owned(), to_type: "Decimal".to_owned(), reason: format!("{context}: invalid f64 value {value}"), }) } /// Safely convert Price to Decimal with context pub fn price_to_decimal_safe(price: Price, context: &str) -> RiskResult { price.to_decimal().map_err(|_| RiskError::TypeConversion { from_type: "Price".to_owned(), to_type: "Decimal".to_owned(), reason: format!("{context}: price conversion failed"), }) } /// Safely convert Decimal to f64 with context pub fn decimal_to_f64_safe(decimal: Decimal, context: &str) -> RiskResult { decimal.to_f64().ok_or_else(|| RiskError::TypeConversion { from_type: "Decimal".to_owned(), to_type: "f64".to_owned(), reason: format!("{context}: decimal too large for f64"), }) } /// Safely parse environment variable with context pub fn parse_env_var(var_name: &str, context: &str) -> RiskResult where T::Err: Display, { std::env::var(var_name) .map_err(|_| RiskError::MissingConfiguration { config_key: var_name.to_owned(), })? .parse() .map_err(|e| RiskError::EnvironmentValidation { environment: var_name.to_owned(), requirement: format!("{context}: {e}"), }) } /// Safe division with zero check pub fn safe_divide(numerator: Decimal, denominator: Decimal, context: &str) -> RiskResult { if denominator.is_zero() { return Err(RiskError::Calculation { operation: "division".to_owned(), reason: format!("{context}: division by zero"), }); } Ok(numerator / denominator) } // ELIMINATED: Re-exports removed to force explicit imports // Also support FoxhuntResult for consistency with error-handling framework // Removed core dependency - use core instead impl RiskError { /// Get the severity level of this error #[must_use] pub const fn severity(&self) -> RiskSeverity { match self { RiskError::KillSwitchActive { .. } | RiskError::DailyLossLimitExceeded { .. } | RiskError::DrawdownLimitExceeded { .. } | RiskError::EmergencyStop { .. } | RiskError::ProductionSafety { .. } => RiskSeverity::Critical, RiskError::PositionLimitExceeded { .. } | RiskError::VarLimitExceeded { .. } | RiskError::CircuitBreakerActive { .. } | RiskError::ComplianceViolation { .. } => RiskSeverity::High, RiskError::PerformanceViolation { .. } | RiskError::MarketDataUnavailable { .. } | RiskError::AuthorizationFailed { .. } => RiskSeverity::Medium, _ => RiskSeverity::Low, } } /// Check if the error should trigger a kill switch #[must_use] pub const fn should_trigger_kill_switch(&self) -> bool { matches!( self, RiskError::DailyLossLimitExceeded { .. } | RiskError::DrawdownLimitExceeded { .. } ) } /// Check if the error should trigger a circuit breaker #[must_use] pub const fn should_trigger_circuit_breaker(&self) -> bool { matches!( self, RiskError::PositionLimitExceeded { .. } | RiskError::VarLimitExceeded { .. } | RiskError::PerformanceViolation { .. } ) } /// Get error code for logging and monitoring #[must_use] pub const fn error_code(&self) -> &'static str { match self { RiskError::Config(_) => "CONFIG_ERROR", RiskError::Database(_) => "DATABASE_ERROR", RiskError::PositionLimitExceeded { .. } => "POSITION_LIMIT_EXCEEDED", RiskError::VarLimitExceeded { .. } => "VAR_LIMIT_EXCEEDED", RiskError::DrawdownLimitExceeded { .. } => "DRAWDOWN_LIMIT_EXCEEDED", RiskError::DailyLossLimitExceeded { .. } => "DAILY_LOSS_LIMIT_EXCEEDED", RiskError::CircuitBreakerActive { .. } => "CIRCUIT_BREAKER_ACTIVE", RiskError::KillSwitchActive { .. } => "KILL_SWITCH_ACTIVE", RiskError::MarketDataUnavailable { .. } => "MARKET_DATA_UNAVAILABLE", RiskError::InsufficientHistoricalData { .. } => "INSUFFICIENT_HISTORICAL_DATA", RiskError::CorrelationCalculationFailed { .. } => "CORRELATION_CALCULATION_FAILED", RiskError::StressTestFailed { .. } => "STRESS_TEST_FAILED", RiskError::PerformanceViolation { .. } => "PERFORMANCE_VIOLATION", RiskError::ComplianceViolation { .. } => "COMPLIANCE_VIOLATION", RiskError::AuthorizationFailed { .. } => "AUTHORIZATION_FAILED", RiskError::InvalidOrder { .. } => "INVALID_ORDER", RiskError::ServiceUnavailable { .. } => "SERVICE_UNAVAILABLE", RiskError::Timeout { .. } => "TIMEOUT", RiskError::Serialization(_) => "SERIALIZATION_ERROR", RiskError::Network(_) => "NETWORK_ERROR", RiskError::Internal(_) => "INTERNAL_ERROR", RiskError::Validation { .. } => "VALIDATION_ERROR", RiskError::ResourceExhausted { .. } => "RESOURCE_EXHAUSTED", RiskError::Calculation { .. } => "CALCULATION_ERROR", RiskError::RateLimited { .. } => "RATE_LIMITED", RiskError::InvalidOrderSide { .. } => "INVALID_ORDER_SIDE", RiskError::InvalidOrderType { .. } => "INVALID_ORDER_TYPE", RiskError::InvalidQuantity { .. } => "INVALID_QUANTITY", RiskError::InvalidPrice { .. } => "INVALID_PRICE", RiskError::ConnectionError { .. } => "CONNECTION_ERROR", RiskError::Configuration { .. } => "CONFIGURATION_ERROR", RiskError::ValidationError { .. } => "VALIDATION_ERROR", RiskError::SerializationError { .. } => "SERIALIZATION_ERROR", // NEW: Enhanced error codes RiskError::TypeConversion { .. } => "TYPE_CONVERSION_ERROR", RiskError::CalculationError(_) => "CALCULATION_ERROR", RiskError::MissingConfiguration { .. } => "MISSING_CONFIGURATION", RiskError::EnvironmentValidation { .. } => "ENVIRONMENT_VALIDATION_ERROR", RiskError::DataIntegrity { .. } => "DATA_INTEGRITY_ERROR", RiskError::ResourceUnavailable { .. } => "RESOURCE_UNAVAILABLE", RiskError::SafetyLimitExceeded { .. } => "SAFETY_LIMIT_EXCEEDED", RiskError::EmergencyStop { .. } => "EMERGENCY_STOP", RiskError::ProductionSafety { .. } => "PRODUCTION_SAFETY_VIOLATION", RiskError::BrokerError(_) => "BROKER_ERROR", RiskError::BrokerConnection { .. } => "BROKER_CONNECTION_ERROR", RiskError::Connection { .. } => "CONNECTION_ERROR", RiskError::MarketDataError(_) => "MARKET_DATA_ERROR", RiskError::DataUnavailable { .. } => "DATA_UNAVAILABLE", RiskError::ArithmeticOverflow { .. } => "ARITHMETIC_OVERFLOW", } } } /// Convert from tokio timeout error impl From for RiskError { fn from(_: tokio::time::error::Elapsed) -> Self { RiskError::Timeout { timeout_ms: 0 } } } /// Convert from `anyhow::Error` impl From for RiskError { fn from(err: anyhow::Error) -> Self { RiskError::Internal(err.to_string()) } } /// Convert from `CommonError` to `RiskError` impl From for RiskError { fn from(err: CommonError) -> Self { RiskError::Internal(format!("CommonError: {err}")) } } /// Convert from `CommonTypeError` impl From for RiskError { fn from(err: common::types::CommonTypeError) -> Self { RiskError::Internal(format!("CommonTypeError: {err}")) } } /// Convert from `serde_json::Error` impl From for RiskError { fn from(err: serde_json::Error) -> Self { RiskError::Serialization(err.to_string()) } } /// Convert from `std::num::ParseFloatError` impl From for RiskError { fn from(err: std::num::ParseFloatError) -> Self { RiskError::TypeConversion { from_type: "string".to_owned(), to_type: "f64".to_owned(), reason: err.to_string(), } } } /// Convert from `std::num::ParseIntError` impl From for RiskError { fn from(err: std::num::ParseIntError) -> Self { RiskError::TypeConversion { from_type: "string".to_owned(), to_type: "integer".to_owned(), reason: err.to_string(), } } } /// Convert from `std::env::VarError` impl From for RiskError { fn from(err: std::env::VarError) -> Self { match err { std::env::VarError::NotPresent => RiskError::MissingConfiguration { config_key: "unknown".to_owned(), }, std::env::VarError::NotUnicode(_) => RiskError::EnvironmentValidation { environment: "unknown".to_owned(), requirement: "valid unicode".to_owned(), }, } } } /// Convert from `std::io::Error` impl From for RiskError { fn from(err: std::io::Error) -> Self { RiskError::Network(format!("IO error: {err}")) } }