trader_bot/exchange/
error.rs1use thiserror::Error;
4
5pub type ExchangeResult<T> = Result<T, ExchangeError>;
7
8#[derive(Debug, Error)]
10pub enum ExchangeError {
11 #[error("authentication failed: {0}")]
13 AuthenticationFailed(String),
14
15 #[error("rate limit exceeded, retry after {retry_after_ms}ms")]
17 RateLimitExceeded {
18 retry_after_ms: u64,
20 },
21
22 #[error("network error: {0}")]
24 NetworkError(String),
25
26 #[error("order rejected: {0}")]
28 OrderRejected(String),
29
30 #[error("insufficient funds: required {required}, available {available}")]
32 InsufficientFunds {
33 required: String,
35 available: String,
37 },
38
39 #[error("instrument not found: {0}")]
41 InstrumentNotFound(String),
42
43 #[error("market closed for {instrument}")]
45 MarketClosed {
46 instrument: String,
48 },
49
50 #[error("invalid order: {0}")]
52 InvalidOrder(String),
53
54 #[error("order not found: {0}")]
56 OrderNotFound(String),
57
58 #[error("unexpected response: {0}")]
60 UnexpectedResponse(String),
61
62 #[error("configuration error: {0}")]
64 ConfigurationError(String),
65
66 #[error("exchange error [{code}]: {message}")]
68 ExchangeSpecific {
69 code: String,
71 message: String,
73 },
74
75 #[error("operation timed out after {0}ms")]
77 Timeout(u64),
78
79 #[error("internal error: {0}")]
81 Internal(String),
82}
83
84impl ExchangeError {
85 #[must_use]
87 pub const fn is_retryable(&self) -> bool {
88 matches!(
89 self,
90 Self::RateLimitExceeded { .. } | Self::NetworkError(_) | Self::Timeout(_)
91 )
92 }
93
94 #[must_use]
96 pub const fn retry_delay_ms(&self) -> Option<u64> {
97 match self {
98 Self::RateLimitExceeded { retry_after_ms } => Some(*retry_after_ms),
99 Self::NetworkError(_) | Self::Timeout(_) => Some(1000), _ => None,
101 }
102 }
103}
104
105#[cfg(test)]
106mod tests {
107 use super::*;
108
109 #[test]
110 fn test_retryable_errors() {
111 let rate_limit = ExchangeError::RateLimitExceeded {
112 retry_after_ms: 5000,
113 };
114 assert!(rate_limit.is_retryable());
115 assert_eq!(rate_limit.retry_delay_ms(), Some(5000));
116
117 let network = ExchangeError::NetworkError("connection reset".into());
118 assert!(network.is_retryable());
119 assert_eq!(network.retry_delay_ms(), Some(1000));
120
121 let rejected = ExchangeError::OrderRejected("invalid price".into());
122 assert!(!rejected.is_retryable());
123 assert_eq!(rejected.retry_delay_ms(), None);
124 }
125}