ccxt_exchanges/bybit/
error.rs1use ccxt_core::error::Error;
7use serde_json::Value;
8use std::time::Duration;
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub enum BybitErrorCode {
15 InvalidRequest,
17 InvalidApiKey,
19 InvalidSignature,
21 PermissionDenied,
23 RateLimitExceeded,
25 IpNotAllowed,
27 BadSymbol,
29 InsufficientFunds,
31 InsufficientAvailableBalance,
33 OrderNotFound,
35 Unknown(i64),
37}
38
39impl BybitErrorCode {
40 pub fn from_code(code: i64) -> Self {
42 match code {
43 10001 => BybitErrorCode::InvalidRequest,
44 10003 => BybitErrorCode::InvalidApiKey,
45 10004 => BybitErrorCode::InvalidSignature,
46 10005 => BybitErrorCode::PermissionDenied,
47 10006 => BybitErrorCode::RateLimitExceeded,
48 10010 => BybitErrorCode::IpNotAllowed,
49 10016 => BybitErrorCode::BadSymbol,
50 110001 => BybitErrorCode::InsufficientFunds,
51 110007 => BybitErrorCode::InsufficientAvailableBalance,
52 110008 => BybitErrorCode::OrderNotFound,
53 n => BybitErrorCode::Unknown(n),
54 }
55 }
56
57 pub fn code(&self) -> i64 {
59 match self {
60 BybitErrorCode::InvalidRequest => 10001,
61 BybitErrorCode::InvalidApiKey => 10003,
62 BybitErrorCode::InvalidSignature => 10004,
63 BybitErrorCode::PermissionDenied => 10005,
64 BybitErrorCode::RateLimitExceeded => 10006,
65 BybitErrorCode::IpNotAllowed => 10010,
66 BybitErrorCode::BadSymbol => 10016,
67 BybitErrorCode::InsufficientFunds => 110001,
68 BybitErrorCode::InsufficientAvailableBalance => 110007,
69 BybitErrorCode::OrderNotFound => 110008,
70 BybitErrorCode::Unknown(n) => *n,
71 }
72 }
73}
74
75pub fn parse_error(response: &Value) -> Error {
110 let code = response
111 .get("retCode")
112 .and_then(serde_json::Value::as_i64)
113 .unwrap_or(0);
114
115 let msg = response
116 .get("retMsg")
117 .and_then(serde_json::Value::as_str)
118 .unwrap_or("Unknown error");
119
120 let error_code = BybitErrorCode::from_code(code);
121
122 match error_code {
123 BybitErrorCode::InvalidApiKey
124 | BybitErrorCode::InvalidSignature
125 | BybitErrorCode::PermissionDenied
126 | BybitErrorCode::IpNotAllowed => Error::authentication(msg.to_string()),
127 BybitErrorCode::RateLimitExceeded => {
128 Error::rate_limit(msg.to_string(), Some(Duration::from_secs(1)))
130 }
131 BybitErrorCode::InvalidRequest => Error::invalid_request(msg.to_string()),
132 BybitErrorCode::InsufficientFunds | BybitErrorCode::InsufficientAvailableBalance => {
133 Error::insufficient_balance(msg.to_string())
134 }
135 BybitErrorCode::BadSymbol => Error::bad_symbol(msg.to_string()),
136 BybitErrorCode::OrderNotFound | BybitErrorCode::Unknown(_) => {
137 Error::exchange(code.to_string(), msg)
138 }
139 }
140}
141
142pub fn is_error_response(response: &Value) -> bool {
154 response.get("retCode").and_then(serde_json::Value::as_i64) != Some(0)
155}
156
157pub fn extract_error_code(response: &Value) -> i64 {
167 response
168 .get("retCode")
169 .and_then(serde_json::Value::as_i64)
170 .unwrap_or(0)
171}
172
173pub fn extract_error_message(response: &Value) -> &str {
183 response
184 .get("retMsg")
185 .and_then(serde_json::Value::as_str)
186 .unwrap_or("Unknown error")
187}
188
189#[cfg(test)]
190mod tests {
191 use super::*;
192 use serde_json::json;
193
194 #[test]
195 fn test_parse_authentication_error_invalid_api_key() {
196 let response = json!({
197 "retCode": 10003,
198 "retMsg": "Invalid API key"
199 });
200
201 let error = parse_error(&response);
202 assert!(error.as_authentication().is_some());
203 assert!(error.to_string().contains("Invalid API key"));
204 }
205
206 #[test]
207 fn test_parse_authentication_error_invalid_signature() {
208 let response = json!({
209 "retCode": 10004,
210 "retMsg": "Invalid signature"
211 });
212
213 let error = parse_error(&response);
214 assert!(error.as_authentication().is_some());
215 assert!(error.to_string().contains("Invalid signature"));
216 }
217
218 #[test]
219 fn test_parse_authentication_error_permission_denied() {
220 let response = json!({
221 "retCode": 10005,
222 "retMsg": "Permission denied"
223 });
224
225 let error = parse_error(&response);
226 assert!(error.as_authentication().is_some());
227 assert!(error.to_string().contains("Permission denied"));
228 }
229
230 #[test]
231 fn test_parse_authentication_error_ip_not_allowed() {
232 let response = json!({
233 "retCode": 10010,
234 "retMsg": "IP not allowed"
235 });
236
237 let error = parse_error(&response);
238 assert!(error.as_authentication().is_some());
239 assert!(error.to_string().contains("IP not allowed"));
240 }
241
242 #[test]
243 fn test_parse_rate_limit_error() {
244 let response = json!({
245 "retCode": 10006,
246 "retMsg": "Rate limit exceeded"
247 });
248
249 let error = parse_error(&response);
250 assert!(error.as_rate_limit().is_some());
251 let (msg, retry_after) = error.as_rate_limit().unwrap();
252 assert!(msg.contains("Rate limit"));
253 assert!(retry_after.is_some());
254 }
255
256 #[test]
257 fn test_parse_invalid_request_error() {
258 let response = json!({
259 "retCode": 10001,
260 "retMsg": "Invalid request parameters"
261 });
262
263 let error = parse_error(&response);
264 let display = error.to_string();
265 assert!(display.contains("Invalid request"));
266 }
267
268 #[test]
269 fn test_parse_insufficient_funds_error() {
270 let response = json!({
271 "retCode": 110001,
272 "retMsg": "Insufficient balance"
273 });
274
275 let error = parse_error(&response);
276 let display = error.to_string();
277 assert!(display.contains("Insufficient balance"));
278 }
279
280 #[test]
281 fn test_parse_insufficient_available_balance_error() {
282 let response = json!({
283 "retCode": 110007,
284 "retMsg": "Insufficient available balance"
285 });
286
287 let error = parse_error(&response);
288 let display = error.to_string();
289 assert!(display.contains("Insufficient"));
290 }
291
292 #[test]
293 fn test_parse_bad_symbol_error() {
294 let response = json!({
295 "retCode": 10016,
296 "retMsg": "Invalid symbol"
297 });
298
299 let error = parse_error(&response);
300 let display = error.to_string();
301 assert!(display.contains("Bad symbol"));
302 }
303
304 #[test]
305 fn test_parse_order_not_found_error() {
306 let response = json!({
307 "retCode": 110008,
308 "retMsg": "Order not found"
309 });
310
311 let error = parse_error(&response);
312 let display = error.to_string();
313 assert!(display.contains("Order not found"));
314 }
315
316 #[test]
317 fn test_parse_unknown_error() {
318 let response = json!({
319 "retCode": 99999,
320 "retMsg": "Some unknown error"
321 });
322
323 let error = parse_error(&response);
324 let display = error.to_string();
325 assert!(display.contains("Some unknown error"));
326 }
327
328 #[test]
329 fn test_parse_error_missing_code() {
330 let response = json!({
331 "retMsg": "Error without code"
332 });
333
334 let error = parse_error(&response);
335 let display = error.to_string();
336 assert!(display.contains("Error without code"));
337 }
338
339 #[test]
340 fn test_parse_error_missing_message() {
341 let response = json!({
342 "retCode": 10003
343 });
344
345 let error = parse_error(&response);
346 let display = error.to_string();
347 assert!(display.contains("Unknown error"));
348 }
349
350 #[test]
351 fn test_is_error_response_success() {
352 let response = json!({
353 "retCode": 0,
354 "retMsg": "OK",
355 "result": {}
356 });
357
358 assert!(!is_error_response(&response));
359 }
360
361 #[test]
362 fn test_is_error_response_error() {
363 let response = json!({
364 "retCode": 10003,
365 "retMsg": "Invalid API key"
366 });
367
368 assert!(is_error_response(&response));
369 }
370
371 #[test]
372 fn test_is_error_response_missing_code() {
373 let response = json!({
374 "retMsg": "No code field"
375 });
376
377 assert!(is_error_response(&response));
379 }
380
381 #[test]
382 fn test_extract_error_code() {
383 let response = json!({
384 "retCode": 10003,
385 "retMsg": "Invalid API key"
386 });
387
388 assert_eq!(extract_error_code(&response), 10003);
389 }
390
391 #[test]
392 fn test_extract_error_code_missing() {
393 let response = json!({
394 "retMsg": "No code"
395 });
396
397 assert_eq!(extract_error_code(&response), 0);
398 }
399
400 #[test]
401 fn test_extract_error_message() {
402 let response = json!({
403 "retCode": 10003,
404 "retMsg": "Invalid API key"
405 });
406
407 assert_eq!(extract_error_message(&response), "Invalid API key");
408 }
409
410 #[test]
411 fn test_extract_error_message_missing() {
412 let response = json!({
413 "retCode": 10003
414 });
415
416 assert_eq!(extract_error_message(&response), "Unknown error");
417 }
418
419 #[test]
420 fn test_bybit_error_code_from_code() {
421 assert_eq!(
422 BybitErrorCode::from_code(10001),
423 BybitErrorCode::InvalidRequest
424 );
425 assert_eq!(
426 BybitErrorCode::from_code(10003),
427 BybitErrorCode::InvalidApiKey
428 );
429 assert_eq!(
430 BybitErrorCode::from_code(10004),
431 BybitErrorCode::InvalidSignature
432 );
433 assert_eq!(
434 BybitErrorCode::from_code(10005),
435 BybitErrorCode::PermissionDenied
436 );
437 assert_eq!(
438 BybitErrorCode::from_code(10006),
439 BybitErrorCode::RateLimitExceeded
440 );
441 assert_eq!(
442 BybitErrorCode::from_code(10010),
443 BybitErrorCode::IpNotAllowed
444 );
445 assert_eq!(BybitErrorCode::from_code(10016), BybitErrorCode::BadSymbol);
446 assert_eq!(
447 BybitErrorCode::from_code(110001),
448 BybitErrorCode::InsufficientFunds
449 );
450 assert_eq!(
451 BybitErrorCode::from_code(110007),
452 BybitErrorCode::InsufficientAvailableBalance
453 );
454 assert_eq!(
455 BybitErrorCode::from_code(110008),
456 BybitErrorCode::OrderNotFound
457 );
458 assert_eq!(
459 BybitErrorCode::from_code(99999),
460 BybitErrorCode::Unknown(99999)
461 );
462 }
463
464 #[test]
465 fn test_bybit_error_code_code() {
466 assert_eq!(BybitErrorCode::InvalidRequest.code(), 10001);
467 assert_eq!(BybitErrorCode::InvalidApiKey.code(), 10003);
468 assert_eq!(BybitErrorCode::InvalidSignature.code(), 10004);
469 assert_eq!(BybitErrorCode::PermissionDenied.code(), 10005);
470 assert_eq!(BybitErrorCode::RateLimitExceeded.code(), 10006);
471 assert_eq!(BybitErrorCode::IpNotAllowed.code(), 10010);
472 assert_eq!(BybitErrorCode::BadSymbol.code(), 10016);
473 assert_eq!(BybitErrorCode::InsufficientFunds.code(), 110001);
474 assert_eq!(BybitErrorCode::InsufficientAvailableBalance.code(), 110007);
475 assert_eq!(BybitErrorCode::OrderNotFound.code(), 110008);
476 assert_eq!(BybitErrorCode::Unknown(12345).code(), 12345);
477 }
478}