1use rust_decimal::Decimal;
7
8#[derive(Debug, thiserror::Error)]
10pub enum FinError {
11 #[error("Symbol '{0}' is invalid (empty or contains whitespace)")]
13 InvalidSymbol(String),
14
15 #[error("Price must be positive, got {0}")]
17 InvalidPrice(Decimal),
18
19 #[error("Quantity must be non-negative, got {0}")]
21 InvalidQuantity(Decimal),
22
23 #[error("Order book sequence mismatch: expected {expected}, got {got}")]
25 SequenceMismatch {
26 expected: u64,
28 got: u64,
30 },
31
32 #[error("No liquidity available for requested quantity {0}")]
34 InsufficientLiquidity(Decimal),
35
36 #[error("OHLCV bar invariant violated: {0}")]
38 BarInvariant(String),
39
40 #[error("Signal '{name}' not ready (requires {required} periods, have {have})")]
42 SignalNotReady {
43 name: String,
45 required: usize,
47 have: usize,
49 },
50
51 #[error("Position not found for symbol '{0}'")]
53 PositionNotFound(String),
54
55 #[error("Insufficient funds: need {need}, have {have}")]
57 InsufficientFunds {
58 need: Decimal,
60 have: Decimal,
62 },
63
64 #[error("Timeframe duration must be positive")]
66 InvalidTimeframe,
67
68 #[error("Arithmetic overflow in financial calculation")]
70 ArithmeticOverflow,
71
72 #[error("Inverted spread: best_bid {best_bid} >= best_ask {best_ask}")]
74 InvertedSpread {
75 best_bid: Decimal,
77 best_ask: Decimal,
79 },
80
81 #[error("Period must be at least 1, got {0}")]
83 InvalidPeriod(usize),
84
85 #[error("Invalid input: {0}")]
87 InvalidInput(String),
88}
89
90impl FinError {
91 pub fn is_period_error(&self) -> bool {
93 matches!(self, FinError::InvalidPeriod(_))
94 }
95}
96
97#[cfg(test)]
98mod tests {
99 use super::*;
100
101 #[test]
102 fn test_is_period_error_true_for_invalid_period() {
103 let e = FinError::InvalidPeriod(0);
104 assert!(e.is_period_error());
105 }
106
107 #[test]
108 fn test_is_period_error_false_for_other_errors() {
109 let e = FinError::InvalidSymbol("".to_owned());
110 assert!(!e.is_period_error());
111 }
112
113 #[test]
114 fn test_invalid_input_error_message() {
115 let e = FinError::InvalidInput("bad value".to_owned());
116 assert!(e.to_string().contains("bad value"));
117 }
118}