nautilus_interactive_brokers/
error.rs1use thiserror::Error;
19
20#[derive(Error, Debug)]
22pub enum InteractiveBrokersError {
23 #[error("Connection error: {0}")]
25 Connection(String),
26
27 #[error("Authentication error: {0}")]
29 Authentication(String),
30
31 #[error("Invalid configuration: {0}")]
33 Configuration(String),
34
35 #[error("API request error: {0}")]
37 Request(String),
38
39 #[error("Response parsing error: {0}")]
41 Parse(String),
42
43 #[error("Instrument error: {0}")]
45 Instrument(String),
46
47 #[error("Order error: {0}")]
49 Order(String),
50
51 #[error("Market data error: {0}")]
53 MarketData(String),
54
55 #[error("IB API error: {0}")]
57 IbApi(String),
58
59 #[error("Internal error: {0}")]
61 Internal(String),
62}
63
64impl InteractiveBrokersError {
65 #[must_use]
67 pub const fn kind(&self) -> InteractiveBrokersErrorKind {
68 match self {
69 Self::Connection(_) => InteractiveBrokersErrorKind::Connection,
70 Self::Authentication(_) => InteractiveBrokersErrorKind::Authentication,
71 Self::Configuration(_) => InteractiveBrokersErrorKind::Configuration,
72 Self::Request(_) => InteractiveBrokersErrorKind::Request,
73 Self::Parse(_) => InteractiveBrokersErrorKind::Parse,
74 Self::Instrument(_) => InteractiveBrokersErrorKind::Instrument,
75 Self::Order(_) => InteractiveBrokersErrorKind::Order,
76 Self::MarketData(_) => InteractiveBrokersErrorKind::MarketData,
77 Self::IbApi(_) => InteractiveBrokersErrorKind::IbApi,
78 Self::Internal(_) => InteractiveBrokersErrorKind::Internal,
79 }
80 }
81}
82
83#[derive(Debug, Clone, Copy, PartialEq, Eq)]
85#[cfg_attr(
86 feature = "python",
87 pyo3::pyclass(
88 module = "nautilus_trader.core.nautilus_pyo3.interactive_brokers",
89 from_py_object
90 )
91)]
92pub enum InteractiveBrokersErrorKind {
93 Connection,
95 Authentication,
97 Configuration,
99 Request,
101 Parse,
103 Instrument,
105 Order,
107 MarketData,
109 IbApi,
111 Internal,
113}
114
115pub type InteractiveBrokersResult<T> = Result<T, InteractiveBrokersError>;
117
118#[derive(Debug, Clone, Copy, PartialEq, Eq)]
120#[cfg_attr(
121 feature = "python",
122 pyo3::pyclass(
123 module = "nautilus_trader.core.nautilus_pyo3.interactive_brokers",
124 from_py_object
125 )
126)]
127pub enum ErrorCategory {
128 ClientError,
130 ConnectivityError,
132 SubscriptionError,
134 OrderError,
136 MarketDataError,
138 Unknown,
140}
141
142pub fn classify_error_code(error_code: i32) -> ErrorCategory {
144 match error_code {
145 200..=299 => ErrorCategory::ClientError,
147
148 326 | 502 | 503 | 504 | 1100 | 1101 | 1102 | 1300 | 1301 | 1302 => {
150 ErrorCategory::ConnectivityError
151 }
152
153 10189 | 366 | 102 | 10182 => ErrorCategory::SubscriptionError,
155
156 100..=199 if error_code != 10182 => ErrorCategory::MarketDataError,
158
159 _ => ErrorCategory::Unknown,
164 }
165}
166
167pub fn is_recoverable_error(error_code: i32) -> bool {
169 matches!(
170 classify_error_code(error_code),
171 ErrorCategory::ConnectivityError | ErrorCategory::SubscriptionError
172 )
173}
174
175pub fn requires_resubscription(error_code: i32) -> bool {
177 matches!(error_code, 10189 | 366 | 102 | 10182)
178}
179
180pub fn format_error_message(error_code: i32, error_string: &str) -> String {
182 let category = classify_error_code(error_code);
183 let category_str = match category {
184 ErrorCategory::ClientError => "Client Error",
185 ErrorCategory::ConnectivityError => "Connectivity Error",
186 ErrorCategory::SubscriptionError => "Subscription Error",
187 ErrorCategory::OrderError => "Order Error",
188 ErrorCategory::MarketDataError => "Market Data Error",
189 ErrorCategory::Unknown => "Unknown Error",
190 };
191
192 format!(
193 "[{}] {} (Code: {}): {}",
194 category_str, error_string, error_code, error_string
195 )
196}
197
198#[cfg(test)]
199mod tests {
200 use rstest::rstest;
201
202 use super::{ErrorCategory, classify_error_code, is_recoverable_error};
203
204 #[rstest]
205 #[case(326, ErrorCategory::ConnectivityError)]
206 #[case(502, ErrorCategory::ConnectivityError)]
207 #[case(10182, ErrorCategory::SubscriptionError)]
208 #[case(200, ErrorCategory::ClientError)]
209 fn test_classify_error_code(#[case] error_code: i32, #[case] expected: ErrorCategory) {
210 assert_eq!(classify_error_code(error_code), expected);
211 }
212
213 #[rstest]
214 #[case(326, true)]
215 #[case(10182, true)]
216 #[case(200, false)]
217 fn test_is_recoverable_error(#[case] error_code: i32, #[case] expected: bool) {
218 assert_eq!(is_recoverable_error(error_code), expected);
219 }
220}