nautilus_binance/common/
error.rs1use std::fmt::Display;
19
20use crate::common::consts::{BINANCE_STATUS_UNKNOWN_CODE, BINANCE_UNEXPECTED_RESPONSE_CODE};
21
22#[derive(Debug)]
24pub enum BinanceWsError {
25 ClientError(String),
27 AuthenticationError(String),
29 ParseError(String),
31 NetworkError(String),
33 Timeout(String),
35}
36
37impl Display for BinanceWsError {
38 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
39 match self {
40 Self::ClientError(msg) => write!(f, "Client error: {msg}"),
41 Self::AuthenticationError(msg) => write!(f, "Authentication error: {msg}"),
42 Self::ParseError(msg) => write!(f, "Parse error: {msg}"),
43 Self::NetworkError(msg) => write!(f, "Network error: {msg}"),
44 Self::Timeout(msg) => write!(f, "Timeout: {msg}"),
45 }
46 }
47}
48
49impl std::error::Error for BinanceWsError {}
50
51pub type BinanceWsResult<T> = Result<T, BinanceWsError>;
53
54#[derive(Debug, thiserror::Error)]
56pub enum BinanceError {
57 #[error("Spot HTTP error: {0}")]
59 SpotHttp(#[from] crate::spot::http::error::BinanceSpotHttpError),
60
61 #[error("Futures HTTP error: {0}")]
63 FuturesHttp(#[from] crate::futures::http::error::BinanceFuturesHttpError),
64
65 #[error("WebSocket error: {0}")]
67 WebSocket(#[from] BinanceWsError),
68
69 #[error("Spot WS API error: {0}")]
71 SpotWsApi(#[from] crate::spot::websocket::trading::error::BinanceWsApiError),
72
73 #[error("Futures WS API error: {0}")]
75 FuturesWsApi(#[from] crate::futures::websocket::trading::error::BinanceFuturesWsApiError),
76
77 #[error("Config error: {0}")]
79 Config(String),
80}
81
82const BINANCE_RATE_LIMIT_ERROR_CODES: [i64; 2] = [
84 -1003, -1015, ];
87
88pub(crate) fn is_retryable_venue_code(code: i64) -> bool {
90 BINANCE_RATE_LIMIT_ERROR_CODES.contains(&code)
91}
92
93pub(crate) fn is_retryable_http_status(status: u16) -> bool {
96 status == 429 || status == 418 || status >= 500
97}
98
99pub(crate) fn is_ambiguous_venue_code(code: i64) -> bool {
104 code == BINANCE_UNEXPECTED_RESPONSE_CODE || code == BINANCE_STATUS_UNKNOWN_CODE
105}
106
107#[cfg(test)]
108mod tests {
109 use rstest::rstest;
110
111 use super::*;
112
113 #[rstest]
114 #[case::too_many_requests(-1003, true)]
115 #[case::too_many_new_orders(-1015, true)]
116 #[case::illegal_characters(-1100, false)]
117 #[case::invalid_api_key(-2015, false)]
118 #[case::invalid_signature(-1022, false)]
119 #[case::unexpected_response(-1006, false)]
120 fn test_is_retryable_venue_code(#[case] code: i64, #[case] expected: bool) {
121 assert_eq!(is_retryable_venue_code(code), expected);
122 }
123
124 #[rstest]
125 #[case::rate_limited(429, true)]
126 #[case::banned(418, true)]
127 #[case::server_error(500, true)]
128 #[case::bad_gateway(502, true)]
129 #[case::bad_request(400, false)]
130 #[case::unauthorized(401, false)]
131 #[case::forbidden(403, false)]
132 #[case::success(200, false)]
133 fn test_is_retryable_http_status(#[case] status: u16, #[case] expected: bool) {
134 assert_eq!(is_retryable_http_status(status), expected);
135 }
136
137 #[rstest]
138 #[case::unexpected_response(-1006, true)]
139 #[case::status_unknown(-1007, true)]
140 #[case::rate_limit(-1003, false)]
141 #[case::no_such_order(-2013, false)]
142 fn test_is_ambiguous_venue_code(#[case] code: i64, #[case] expected: bool) {
143 assert_eq!(is_ambiguous_venue_code(code), expected);
144 }
145}