1use 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(|v| v.as_i64())
113 .unwrap_or(0);
114
115 let msg = response
116 .get("retMsg")
117 .and_then(|v| v.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 => Error::exchange(code.to_string(), msg),
137 BybitErrorCode::Unknown(_) => Error::exchange(code.to_string(), msg),
138 }
139}
140
141pub fn is_error_response(response: &Value) -> bool {
153 response
154 .get("retCode")
155 .and_then(|v| v.as_i64())
156 .map(|code| code != 0)
157 .unwrap_or(true)
158}
159
160pub fn extract_error_code(response: &Value) -> i64 {
170 response
171 .get("retCode")
172 .and_then(|v| v.as_i64())
173 .unwrap_or(0)
174}
175
176pub fn extract_error_message(response: &Value) -> &str {
186 response
187 .get("retMsg")
188 .and_then(|v| v.as_str())
189 .unwrap_or("Unknown error")
190}
191
192#[cfg(test)]
193mod tests {
194 use super::*;
195 use serde_json::json;
196
197 #[test]
198 fn test_parse_authentication_error_invalid_api_key() {
199 let response = json!({
200 "retCode": 10003,
201 "retMsg": "Invalid API key"
202 });
203
204 let error = parse_error(&response);
205 assert!(error.as_authentication().is_some());
206 assert!(error.to_string().contains("Invalid API key"));
207 }
208
209 #[test]
210 fn test_parse_authentication_error_invalid_signature() {
211 let response = json!({
212 "retCode": 10004,
213 "retMsg": "Invalid signature"
214 });
215
216 let error = parse_error(&response);
217 assert!(error.as_authentication().is_some());
218 assert!(error.to_string().contains("Invalid signature"));
219 }
220
221 #[test]
222 fn test_parse_authentication_error_permission_denied() {
223 let response = json!({
224 "retCode": 10005,
225 "retMsg": "Permission denied"
226 });
227
228 let error = parse_error(&response);
229 assert!(error.as_authentication().is_some());
230 assert!(error.to_string().contains("Permission denied"));
231 }
232
233 #[test]
234 fn test_parse_authentication_error_ip_not_allowed() {
235 let response = json!({
236 "retCode": 10010,
237 "retMsg": "IP not allowed"
238 });
239
240 let error = parse_error(&response);
241 assert!(error.as_authentication().is_some());
242 assert!(error.to_string().contains("IP not allowed"));
243 }
244
245 #[test]
246 fn test_parse_rate_limit_error() {
247 let response = json!({
248 "retCode": 10006,
249 "retMsg": "Rate limit exceeded"
250 });
251
252 let error = parse_error(&response);
253 assert!(error.as_rate_limit().is_some());
254 let (msg, retry_after) = error.as_rate_limit().unwrap();
255 assert!(msg.contains("Rate limit"));
256 assert!(retry_after.is_some());
257 }
258
259 #[test]
260 fn test_parse_invalid_request_error() {
261 let response = json!({
262 "retCode": 10001,
263 "retMsg": "Invalid request parameters"
264 });
265
266 let error = parse_error(&response);
267 let display = error.to_string();
268 assert!(display.contains("Invalid request"));
269 }
270
271 #[test]
272 fn test_parse_insufficient_funds_error() {
273 let response = json!({
274 "retCode": 110001,
275 "retMsg": "Insufficient balance"
276 });
277
278 let error = parse_error(&response);
279 let display = error.to_string();
280 assert!(display.contains("Insufficient balance"));
281 }
282
283 #[test]
284 fn test_parse_insufficient_available_balance_error() {
285 let response = json!({
286 "retCode": 110007,
287 "retMsg": "Insufficient available balance"
288 });
289
290 let error = parse_error(&response);
291 let display = error.to_string();
292 assert!(display.contains("Insufficient"));
293 }
294
295 #[test]
296 fn test_parse_bad_symbol_error() {
297 let response = json!({
298 "retCode": 10016,
299 "retMsg": "Invalid symbol"
300 });
301
302 let error = parse_error(&response);
303 let display = error.to_string();
304 assert!(display.contains("Bad symbol"));
305 }
306
307 #[test]
308 fn test_parse_order_not_found_error() {
309 let response = json!({
310 "retCode": 110008,
311 "retMsg": "Order not found"
312 });
313
314 let error = parse_error(&response);
315 let display = error.to_string();
316 assert!(display.contains("Order not found"));
317 }
318
319 #[test]
320 fn test_parse_unknown_error() {
321 let response = json!({
322 "retCode": 99999,
323 "retMsg": "Some unknown error"
324 });
325
326 let error = parse_error(&response);
327 let display = error.to_string();
328 assert!(display.contains("Some unknown error"));
329 }
330
331 #[test]
332 fn test_parse_error_missing_code() {
333 let response = json!({
334 "retMsg": "Error without code"
335 });
336
337 let error = parse_error(&response);
338 let display = error.to_string();
339 assert!(display.contains("Error without code"));
340 }
341
342 #[test]
343 fn test_parse_error_missing_message() {
344 let response = json!({
345 "retCode": 10003
346 });
347
348 let error = parse_error(&response);
349 let display = error.to_string();
350 assert!(display.contains("Unknown error"));
351 }
352
353 #[test]
354 fn test_is_error_response_success() {
355 let response = json!({
356 "retCode": 0,
357 "retMsg": "OK",
358 "result": {}
359 });
360
361 assert!(!is_error_response(&response));
362 }
363
364 #[test]
365 fn test_is_error_response_error() {
366 let response = json!({
367 "retCode": 10003,
368 "retMsg": "Invalid API key"
369 });
370
371 assert!(is_error_response(&response));
372 }
373
374 #[test]
375 fn test_is_error_response_missing_code() {
376 let response = json!({
377 "retMsg": "No code field"
378 });
379
380 assert!(is_error_response(&response));
382 }
383
384 #[test]
385 fn test_extract_error_code() {
386 let response = json!({
387 "retCode": 10003,
388 "retMsg": "Invalid API key"
389 });
390
391 assert_eq!(extract_error_code(&response), 10003);
392 }
393
394 #[test]
395 fn test_extract_error_code_missing() {
396 let response = json!({
397 "retMsg": "No code"
398 });
399
400 assert_eq!(extract_error_code(&response), 0);
401 }
402
403 #[test]
404 fn test_extract_error_message() {
405 let response = json!({
406 "retCode": 10003,
407 "retMsg": "Invalid API key"
408 });
409
410 assert_eq!(extract_error_message(&response), "Invalid API key");
411 }
412
413 #[test]
414 fn test_extract_error_message_missing() {
415 let response = json!({
416 "retCode": 10003
417 });
418
419 assert_eq!(extract_error_message(&response), "Unknown error");
420 }
421
422 #[test]
423 fn test_bybit_error_code_from_code() {
424 assert_eq!(
425 BybitErrorCode::from_code(10001),
426 BybitErrorCode::InvalidRequest
427 );
428 assert_eq!(
429 BybitErrorCode::from_code(10003),
430 BybitErrorCode::InvalidApiKey
431 );
432 assert_eq!(
433 BybitErrorCode::from_code(10004),
434 BybitErrorCode::InvalidSignature
435 );
436 assert_eq!(
437 BybitErrorCode::from_code(10005),
438 BybitErrorCode::PermissionDenied
439 );
440 assert_eq!(
441 BybitErrorCode::from_code(10006),
442 BybitErrorCode::RateLimitExceeded
443 );
444 assert_eq!(
445 BybitErrorCode::from_code(10010),
446 BybitErrorCode::IpNotAllowed
447 );
448 assert_eq!(BybitErrorCode::from_code(10016), BybitErrorCode::BadSymbol);
449 assert_eq!(
450 BybitErrorCode::from_code(110001),
451 BybitErrorCode::InsufficientFunds
452 );
453 assert_eq!(
454 BybitErrorCode::from_code(110007),
455 BybitErrorCode::InsufficientAvailableBalance
456 );
457 assert_eq!(
458 BybitErrorCode::from_code(110008),
459 BybitErrorCode::OrderNotFound
460 );
461 assert_eq!(
462 BybitErrorCode::from_code(99999),
463 BybitErrorCode::Unknown(99999)
464 );
465 }
466
467 #[test]
468 fn test_bybit_error_code_code() {
469 assert_eq!(BybitErrorCode::InvalidRequest.code(), 10001);
470 assert_eq!(BybitErrorCode::InvalidApiKey.code(), 10003);
471 assert_eq!(BybitErrorCode::InvalidSignature.code(), 10004);
472 assert_eq!(BybitErrorCode::PermissionDenied.code(), 10005);
473 assert_eq!(BybitErrorCode::RateLimitExceeded.code(), 10006);
474 assert_eq!(BybitErrorCode::IpNotAllowed.code(), 10010);
475 assert_eq!(BybitErrorCode::BadSymbol.code(), 10016);
476 assert_eq!(BybitErrorCode::InsufficientFunds.code(), 110001);
477 assert_eq!(BybitErrorCode::InsufficientAvailableBalance.code(), 110007);
478 assert_eq!(BybitErrorCode::OrderNotFound.code(), 110008);
479 assert_eq!(BybitErrorCode::Unknown(12345).code(), 12345);
480 }
481}