1mod account;
2mod client;
3mod common;
4mod market;
5mod orders;
6mod positions;
7mod user;
8
9pub use account::*;
10pub use client::*;
11pub use common::*;
12pub use market::*;
13pub use orders::*;
14pub use positions::*;
15pub use user::*;
16
17#[cfg(test)]
18mod tests {
19 use std::collections::HashMap;
20
21 use rust_decimal::dec;
22
23 use crate::{
24 AccountType, AdlRankIndicator, CancelType, CreateType, MarginMode, OrderStatus, OrderType,
25 PositionIdx, PositionStatus, RejectReason, Side, SmpType, SpotHedgingStatus, TimeInForce,
26 TpslMode, TriggerBy, TriggerDirection, UnifiedMarginStatus,
27 enums::{Category, StopOrderType},
28 serde::{Unique, deserialize_json},
29 };
30
31 use super::*;
32
33 #[test]
34 fn deserialize_response_kline_inverse() {
35 let json = r#"{
36 "retCode": 0,
37 "retMsg": "OK",
38 "result": {
39 "symbol": "BTCUSD",
40 "category": "inverse",
41 "list": [
42 [
43 "1670608800000",
44 "17071",
45 "17073",
46 "17027",
47 "17055.5",
48 "268611",
49 "15.74462667"
50 ],
51 [
52 "1670605200000",
53 "17071.5",
54 "17071.5",
55 "17061",
56 "17071",
57 "4177",
58 "0.24469757"
59 ],
60 [
61 "1670601600000",
62 "17086.5",
63 "17088",
64 "16978",
65 "17071.5",
66 "6356",
67 "0.37288112"
68 ]
69 ]
70 },
71 "retExtInfo": {},
72 "time": 1672025956592
73 }"#;
74 let expected = Resp {
75 ret_code: 0,
76 ret_msg: String::from("OK"),
77 result: KLine::Inverse {
78 symbol: String::from("BTCUSD"),
79 list: vec![
80 KLineRow {
81 start_time: 1670608800000,
82 open_price: dec!(17071),
83 high_price: dec!(17073),
84 low_price: dec!(17027),
85 close_price: dec!(17055.5),
86 volume: dec!(268611),
87 turnover: dec!(15.74462667),
88 },
89 KLineRow {
90 start_time: 1670605200000,
91 open_price: dec!(17071.5),
92 high_price: dec!(17071.5),
93 low_price: dec!(17061),
94 close_price: dec!(17071),
95 volume: dec!(4177),
96 turnover: dec!(0.24469757),
97 },
98 KLineRow {
99 start_time: 1670601600000,
100 open_price: dec!(17086.5),
101 high_price: dec!(17088),
102 low_price: dec!(16978),
103 close_price: dec!(17071.5),
104 volume: dec!(6356),
105 turnover: dec!(0.37288112),
106 },
107 ],
108 },
109 time: Some(1672025956592),
110 ret_ext_info: Some(RetExtInfo::default()),
111 };
112
113 let message = deserialize_json(json).unwrap();
114
115 assert_eq!(expected, message);
116 }
117
118 #[test]
119 fn deserialize_response_ticker_inverse() {
120 let json = r#"{
121 "retCode": 0,
122 "retMsg": "OK",
123 "result": {
124 "category": "inverse",
125 "list": [
126 {
127 "symbol": "BTCUSD",
128 "lastPrice": "16597.00",
129 "indexPrice": "16598.54",
130 "markPrice": "16596.00",
131 "prevPrice24h": "16464.50",
132 "price24hPcnt": "0.008047",
133 "highPrice24h": "30912.50",
134 "lowPrice24h": "15700.00",
135 "prevPrice1h": "16595.50",
136 "openInterest": "373504107",
137 "openInterestValue": "22505.67",
138 "turnover24h": "2352.94950046",
139 "volume24h": "49337318",
140 "fundingRate": "-0.001034",
141 "nextFundingTime": "1672387200000",
142 "predictedDeliveryPrice": "",
143 "basisRate": "",
144 "deliveryFeeRate": "",
145 "deliveryTime": "0",
146 "ask1Size": "1",
147 "bid1Price": "16596.00",
148 "ask1Price": "16597.50",
149 "bid1Size": "1",
150 "basis": ""
151 }
152 ]
153 },
154 "retExtInfo": {},
155 "time": 1672376496682
156 }"#;
157 let expected = Resp {
158 ret_code: 0,
159 ret_msg: String::from("OK"),
160 result: Ticker::Inverse {
161 list: vec![LinearInverseTicker {
162 symbol: String::from("BTCUSD"),
163 last_price: dec!(16597.00),
164 mark_price: dec!(16596.00),
165 index_price: dec!(16598.54),
166 prev_price24h: dec!(16464.50),
167 price24h_pcnt: dec!(0.008047),
168 high_price24h: dec!(30912.50),
169 low_price24h: dec!(15700.00),
170 prev_price1h: dec!(16595.50),
171 open_interest: dec!(373504107),
172 open_interest_value: dec!(22505.67),
173 turnover24h: dec!(2352.94950046),
174 volume24h: dec!(49337318),
175 funding_rate: Some(dec!(-0.001034)),
176 next_funding_time: 1672387200000,
177 predicted_delivery_price: None,
178 basis_rate: None,
179 basis: None,
180 delivery_fee_rate: None,
181 delivery_time: Some(0),
182 bid1_price: dec!(16596.00),
183 bid1_size: dec!(1),
184 ask1_price: dec!(16597.50),
185 ask1_size: dec!(1),
186 pre_open_price: None,
187 pre_qty: None,
188 cur_pre_listing_phase: None,
189 }],
190 },
191 time: Some(1672376496682),
192 ret_ext_info: Some(RetExtInfo::default()),
193 };
194
195 let message = deserialize_json(json).unwrap();
196
197 assert_eq!(expected, message);
198 }
199
200 #[test]
201 fn deserialize_response_trad_spot() {
202 let json = r#"{
203 "retCode": 0,
204 "retMsg": "OK",
205 "result": {
206 "category": "spot",
207 "list": [
208 {
209 "execId": "2100000000007764263",
210 "symbol": "BTCUSDT",
211 "price": "16618.49",
212 "size": "0.00012",
213 "side": "Buy",
214 "time": "1672052955758",
215 "isBlockTrade": false,
216 "isRPITrade": true
217 }
218 ]
219 },
220 "retExtInfo": {},
221 "time": 1672053054358
222 }"#;
223 let expected = Resp {
224 ret_code: 0,
225 ret_msg: String::from("OK"),
226 result: Trade::Spot {
227 list: vec![InverseLinearSpotTrade {
228 exec_id: String::from("2100000000007764263"),
229 symbol: String::from("BTCUSDT"),
230 price: dec!(16618.49),
231 size: dec!(0.00012),
232 side: Side::Buy,
233 time: 1672052955758,
234 is_block_trade: false,
235 is_rpi_trade: true,
236 }],
237 },
238 time: Some(1672053054358),
239 ret_ext_info: Some(RetExtInfo::default()),
240 };
241
242 let message = deserialize_json(json).unwrap();
243
244 assert_eq!(expected, message);
245 }
246
247 #[test]
248 fn deserialize_response_get_open_closed_orders_linear() {
249 let json = r#"{
250 "retCode": 0,
251 "retMsg": "OK",
252 "result": {
253 "list": [
254 {
255 "orderId": "fd4300ae-7847-404e-b947-b46980a4d140",
256 "orderLinkId": "test-000005",
257 "blockTradeId": "",
258 "symbol": "ETHUSDT",
259 "price": "1600.00",
260 "qty": "0.10",
261 "side": "Buy",
262 "isLeverage": "",
263 "positionIdx": 1,
264 "orderStatus": "New",
265 "cancelType": "UNKNOWN",
266 "rejectReason": "EC_NoError",
267 "avgPrice": "0",
268 "leavesQty": "0.10",
269 "leavesValue": "160",
270 "cumExecQty": "0.00",
271 "cumExecValue": "0",
272 "cumExecFee": "0",
273 "timeInForce": "GTC",
274 "orderType": "Limit",
275 "stopOrderType": "UNKNOWN",
276 "orderIv": "",
277 "triggerPrice": "0.00",
278 "takeProfit": "2500.00",
279 "stopLoss": "1500.00",
280 "tpTriggerBy": "LastPrice",
281 "slTriggerBy": "LastPrice",
282 "triggerDirection": 0,
283 "triggerBy": "UNKNOWN",
284 "lastPriceOnCreated": "",
285 "reduceOnly": false,
286 "closeOnTrigger": false,
287 "smpType": "None",
288 "smpGroup": 0,
289 "smpOrderId": "",
290 "tpslMode": "Full",
291 "tpLimitPrice": "",
292 "slLimitPrice": "",
293 "placeType": "",
294 "createdTime": "1684738540559",
295 "updatedTime": "1684738540561"
296 }
297 ],
298 "nextPageCursor": "page_args%3Dfd4300ae-7847-404e-b947-b46980a4d140%26symbol%3D6%26",
299 "category": "linear"
300 },
301 "retExtInfo": {},
302 "time": 1684765770483
303 }"#;
304 let expected = Resp {
305 ret_code: 0,
306 ret_msg: String::from("OK"),
307 result: CursorPagination {
308 category: Some(Category::Linear),
309 next_page_cursor: Some(String::from(
310 "page_args%3Dfd4300ae-7847-404e-b947-b46980a4d140%26symbol%3D6%26",
311 )),
312 list: vec![Order {
313 order_id: String::from("fd4300ae-7847-404e-b947-b46980a4d140"),
314 order_link_id: Some(String::from("test-000005")),
315 block_trade_id: None,
316 symbol: String::from("ETHUSDT"),
317 price: dec!(1600.00),
318 qty: dec!(0.10),
319 side: Side::Buy,
320 is_leverage: None,
321 position_idx: PositionIdx::Buy,
322 order_status: OrderStatus::New,
323 create_type: None,
324 cancel_type: CancelType::UNKNOWN,
325 reject_reason: RejectReason::EcNoError,
326 avg_price: Some(dec!(0.0)),
327 leaves_qty: dec!(0.10),
328 leaves_value: dec!(160),
329 cum_exec_qty: dec!(0.00),
330 cum_exec_value: dec!(0),
331 cum_exec_fee: dec!(0),
332 time_in_force: TimeInForce::GTC,
333 order_type: OrderType::Limit,
334 stop_order_type: Some(StopOrderType::UNKNOWN),
335 order_iv: None,
336 market_unit: None,
337 trigger_price: Some(dec!(0.00)),
338 take_profit: Some(dec!(2500.00)),
339 stop_loss: Some(dec!(1500.00)),
340 tpsl_mode: Some(TpslMode::Full),
341 oco_trigger_by: None,
342 tp_limit_price: None,
343 sl_limit_price: None,
344 tp_trigger_by: Some(TriggerBy::LastPrice),
345 sl_trigger_by: Some(TriggerBy::LastPrice),
346 trigger_direction: TriggerDirection::UNKNOWN,
347 trigger_by: Some(TriggerBy::UNKNOWN),
348 last_price_on_created: None,
349 base_price: None,
350 reduce_only: false,
351 close_on_trigger: false,
352 place_type: None,
353 smp_type: SmpType::None,
354 smp_group: 0,
355 smp_order_id: None,
356 created_time: 1684738540559,
357 updated_time: 1684738540561,
358 }],
359 },
360 time: Some(1684765770483),
361 ret_ext_info: Some(RetExtInfo::default()),
362 };
363
364 let message = deserialize_json(json).unwrap();
365
366 assert_eq!(expected, message);
367 }
368
369 #[test]
370 fn deserialize_response_get_open_closed_orders_linear2() {
371 let json = r#"{
372 "retCode":0,
373 "retMsg":"OK",
374 "result":{
375 "nextPageCursor":"aed77e97-492f-45be-8ada-4ff350ec07a5%3A1762701687113%2Caed77e97-492f-45be-8ada-4ff350ec07a5%3A1762701687113",
376 "category":"linear",
377 "list":[
378 {
379 "symbol":"BTCUSDT",
380 "orderType":"Limit",
381 "orderLinkId":"",
382 "slLimitPrice":"0",
383 "orderId":"aed77e97-492f-45be-8ada-4ff350ec07a5",
384 "cancelType":"UNKNOWN",
385 "avgPrice":"",
386 "stopOrderType":"",
387 "lastPriceOnCreated":"103550",
388 "orderStatus":"New",
389 "createType":"CreateByUser",
390 "takeProfit":"",
391 "cumExecValue":"0",
392 "tpslMode":"",
393 "smpType":"None",
394 "triggerDirection":0,
395 "blockTradeId":"",
396 "isLeverage":"",
397 "rejectReason":"EC_NoError",
398 "price":"103000",
399 "orderIv":"",
400 "createdTime":"1762701687113",
401 "tpTriggerBy":"",
402 "positionIdx":1,
403 "timeInForce":"GTC",
404 "leavesValue":"1030",
405 "updatedTime":"1762701687113",
406 "side":"Buy",
407 "smpGroup":0,
408 "triggerPrice":"",
409 "tpLimitPrice":"0",
410 "cumExecFee":"0",
411 "leavesQty":"0.01",
412 "slTriggerBy":"",
413 "closeOnTrigger":false,
414 "placeType":"",
415 "cumExecQty":"0",
416 "reduceOnly":false,
417 "qty":"0.01",
418 "stopLoss":"",
419 "marketUnit":"",
420 "smpOrderId":"",
421 "triggerBy":""
422 }
423 ]
424 },
425 "retExtInfo":{},
426 "time":1762711342768
427 }"#;
428 let expected = Resp {
429 ret_code: 0,
430 ret_msg: String::from("OK"),
431 result: CursorPagination {
432 category: Some(Category::Linear),
433 next_page_cursor: Some(String::from(
434 "aed77e97-492f-45be-8ada-4ff350ec07a5%3A1762701687113%2Caed77e97-492f-45be-8ada-4ff350ec07a5%3A1762701687113",
435 )),
436 list: vec![Order {
437 order_id: String::from("aed77e97-492f-45be-8ada-4ff350ec07a5"),
438 order_link_id: None,
439 block_trade_id: None,
440 symbol: String::from("BTCUSDT"),
441 price: dec!(103000),
442 qty: dec!(0.01),
443 side: Side::Buy,
444 is_leverage: None,
445 position_idx: PositionIdx::Buy,
446 order_status: OrderStatus::New,
447 create_type: Some(CreateType::CreateByUser),
448 cancel_type: CancelType::UNKNOWN,
449 reject_reason: RejectReason::EcNoError,
450 avg_price: None,
451 leaves_qty: dec!(0.01),
452 leaves_value: dec!(1030),
453 cum_exec_qty: dec!(0),
454 cum_exec_value: dec!(0),
455 cum_exec_fee: dec!(0),
456 time_in_force: TimeInForce::GTC,
457 order_type: OrderType::Limit,
458 stop_order_type: None,
459 order_iv: None,
460 market_unit: None,
461 trigger_price: None,
462 take_profit: None,
463 stop_loss: None,
464 tpsl_mode: None,
465 oco_trigger_by: None,
466 tp_limit_price: Some(dec!(0)),
467 sl_limit_price: Some(dec!(0)),
468 tp_trigger_by: None,
469 sl_trigger_by: None,
470 trigger_direction: TriggerDirection::UNKNOWN,
471 trigger_by: None,
472 last_price_on_created: Some(dec!(103550)),
473 base_price: None,
474 reduce_only: false,
475 close_on_trigger: false,
476 place_type: None,
477 smp_type: SmpType::None,
478 smp_group: 0,
479 smp_order_id: None,
480 created_time: 1762701687113,
481 updated_time: 1762701687113,
482 }],
483 },
484 time: Some(1762711342768),
485 ret_ext_info: Some(RetExtInfo::default()),
486 };
487
488 let message = deserialize_json(json).unwrap();
489
490 assert_eq!(expected, message);
491 }
492
493 #[test]
494 fn deserialize_response_get_position_info_inverse() {
495 let json = r#"{
496 "retCode": 0,
497 "retMsg": "OK",
498 "result": {
499 "list": [
500 {
501 "positionIdx": 0,
502 "riskId": 1,
503 "riskLimitValue": "150",
504 "symbol": "BTCUSD",
505 "side": "Sell",
506 "size": "300",
507 "avgPrice": "27464.50441675",
508 "positionValue": "0.01092319",
509 "tradeMode": 0,
510 "positionStatus": "Normal",
511 "autoAddMargin": 1,
512 "adlRankIndicator": 2,
513 "leverage": "10",
514 "positionBalance": "0.00139186",
515 "markPrice": "28224.50",
516 "liqPrice": "",
517 "bustPrice": "999999.00",
518 "positionMM": "0.0000015",
519 "positionMMByMp": "0.0000015",
520 "positionIM": "0.00010923",
521 "positionIMByMp": "0.00010923",
522 "tpslMode": "Full",
523 "takeProfit": "0.00",
524 "stopLoss": "0.00",
525 "trailingStop": "0.00",
526 "unrealisedPnl": "-0.00029413",
527 "curRealisedPnl": "0.00013123",
528 "cumRealisedPnl": "-0.00096902",
529 "seq": 5723621632,
530 "isReduceOnly": false,
531 "mmrSysUpdatedTime": "",
532 "leverageSysUpdatedTime": "",
533 "sessionAvgPrice": "",
534 "createdTime": "1676538056258",
535 "updatedTime": "1697673600012"
536 }
537 ],
538 "nextPageCursor": "",
539 "category": "inverse"
540 },
541 "retExtInfo": {},
542 "time": 1697684980172
543 }"#;
544 let expected = Resp {
545 ret_code: 0,
546 ret_msg: String::from("OK"),
547 result: CursorPagination {
548 category: Some(Category::Inverse),
549 next_page_cursor: None,
550 list: vec![Position {
551 position_idx: PositionIdx::OneWay,
552 risk_id: 1,
553 risk_limit_value: Some(dec!(150)),
554 symbol: String::from("BTCUSD"),
555 side: Some(Side::Sell),
556 size: dec!(300),
557 avg_price: dec!(27464.50441675),
558 position_value: Some(dec!(0.01092319)),
559 auto_add_margin: true,
560 position_status: PositionStatus::Normal,
561 leverage: dec!(10),
562 mark_price: dec!(28224.50),
563 liq_price: None,
564 position_im: Some(dec!(0.00010923)),
565 position_im_by_mp: Some(dec!(0.00010923)),
566 position_mm: Some(dec!(0.0000015)),
567 position_mm_by_mp: Some(dec!(0.0000015)),
568 take_profit: Some(dec!(0.00)),
569 stop_loss: Some(dec!(0.00)),
570 trailing_stop: Some(dec!(0.00)),
571 session_avg_price: None,
572 delta: None,
573 gamma: None,
574 vega: None,
575 theta: None,
576 unrealised_pnl: Some(dec!(-0.00029413)),
577 cur_realised_pnl: dec!(0.00013123),
578 cum_realised_pnl: dec!(-0.00096902),
579 adl_rank_indicator: AdlRankIndicator::Two,
580 created_time: 1676538056258,
581 updated_time: 1697673600012,
582 seq: 5723621632,
583 is_reduce_only: false,
584 mmr_sys_updated_time: None,
585 leverage_sys_updated_time: None,
586 }],
587 },
588 time: Some(1697684980172),
589 ret_ext_info: Some(RetExtInfo::default()),
590 };
591
592 let message: Resp<CursorPagination<Position>> = deserialize_json(json).unwrap();
593
594 assert_eq!(message, expected);
595 }
596
597 #[test]
598 fn deserialize_response_get_wallet_balance() {
599 let json = r#"{
600 "retCode": 0,
601 "retMsg": "OK",
602 "result": {
603 "list": [
604 {
605 "totalEquity": "3.31216591",
606 "accountIMRate": "0",
607 "accountIMRateByMp": "0",
608 "totalMarginBalance": "3.00326056",
609 "totalInitialMargin": "0",
610 "totalInitialMarginByMp": "0",
611 "accountType": "UNIFIED",
612 "totalAvailableBalance": "3.00326056",
613 "accountMMRate": "0",
614 "accountMMRateByMp": "0",
615 "totalPerpUPL": "0",
616 "totalWalletBalance": "3.00326056",
617 "accountLTV": "0",
618 "totalMaintenanceMargin": "0",
619 "totalMaintenanceMarginByMp": "0",
620 "coin": [
621 {
622 "availableToBorrow": "3",
623 "bonus": "0",
624 "accruedInterest": "0",
625 "availableToWithdraw": "0",
626 "totalOrderIM": "0",
627 "equity": "0",
628 "totalPositionMM": "0",
629 "usdValue": "0",
630 "spotHedgingQty": "0.01592413",
631 "unrealisedPnl": "0",
632 "collateralSwitch": true,
633 "borrowAmount": "0.0",
634 "totalPositionIM": "0",
635 "walletBalance": "0",
636 "cumRealisedPnl": "0",
637 "locked": "0",
638 "marginCollateral": true,
639 "coin": "BTC",
640 "spotBorrow": "0"
641 }
642 ]
643 }
644 ]
645 },
646 "retExtInfo": {},
647 "time": 1690872862481
648 }"#;
649 let coin = WalletCoin {
650 coin: String::from("BTC"),
651 equity: dec!(0),
652 usd_value: dec!(0),
653 wallet_balance: dec!(0),
654 locked: dec!(0),
655 spot_hedging_qty: dec!(0.01592413),
656 borrow_amount: dec!(0.0),
657 accrued_interest: dec!(0),
658 total_order_im: Some(dec!(0)),
659 total_position_im: Some(dec!(0)),
660 total_position_mm: Some(dec!(0)),
661 unrealised_pnl: dec!(0),
662 cum_realised_pnl: dec!(0),
663 bonus: dec!(0),
664 margin_collateral: true,
665 collateral_switch: true,
666 spot_borrow: Some(dec!(0)),
667 };
668 let coin = HashMap::from([(Unique::unique_key(&coin), coin)]);
669 let expected = Resp {
670 ret_code: 0,
671 ret_msg: String::from("OK"),
672 result: List {
673 list: vec![WalletBalance {
674 account_type: AccountType::UNIFIED,
675 account_im_rate: dec!(0),
676 account_im_rate_by_mp: dec!(0),
677 account_mm_rate: dec!(0),
678 account_mm_rate_by_mp: dec!(0),
679 total_equity: dec!(3.31216591),
680 total_wallet_balance: dec!(3.00326056),
681 total_margin_balance: dec!(3.00326056),
682 total_available_balance: dec!(3.00326056),
683 total_perp_upl: dec!(0),
684 total_initial_margin: dec!(0),
685 total_initial_margin_by_mp: dec!(0),
686 total_maintenance_margin: dec!(0),
687 total_maintenance_margin_by_mp: dec!(0),
688 coin,
689 }],
690 },
691 time: Some(1690872862481),
692 ret_ext_info: Some(RetExtInfo::default()),
693 };
694
695 let message = deserialize_json(json).unwrap();
696
697 assert_eq!(expected, message);
698 }
699
700 #[test]
701 fn deserialize_response_get_wallet_balance_2() {
702 let json = r#"{
703 "retCode":0,
704 "retMsg":"OK",
705 "result":{
706 "list":[
707 {
708 "totalEquity":"36.42053792",
709 "accountIMRate":"0",
710 "accountIMRateByMp":"0",
711 "totalMarginBalance":"36.42053792",
712 "totalInitialMargin":"0",
713 "totalInitialMarginByMp":"0",
714 "accountType":"UNIFIED",
715 "totalAvailableBalance":"36.42053792",
716 "accountMMRate":"0",
717 "accountMMRateByMp":"0",
718 "totalPerpUPL":"0",
719 "totalWalletBalance":"36.42053792",
720 "accountLTV":"0",
721 "totalMaintenanceMargin":"0",
722 "totalMaintenanceMarginByMp":"0",
723 "coin":[
724 {
725 "availableToBorrow":"",
726 "bonus":"0",
727 "accruedInterest":"0",
728 "availableToWithdraw":"",
729 "totalOrderIM":"0",
730 "equity":"36.4061211",
731 "totalPositionMM":"0",
732 "usdValue":"36.42053792",
733 "unrealisedPnl":"0",
734 "collateralSwitch":true,
735 "spotHedgingQty":"0",
736 "borrowAmount":"0.000000000000000000",
737 "totalPositionIM":"0",
738 "walletBalance":"36.4061211",
739 "cumRealisedPnl":"-2084.9938789",
740 "locked":"0",
741 "marginCollateral":true,
742 "coin":"USDT",
743 "spotBorrow": "0"
744 }
745 ]
746 }
747 ]
748 },
749 "retExtInfo":{},
750 "time":1751570498412
751 }"#;
752 let coin = WalletCoin {
753 coin: String::from("USDT"),
754 equity: dec!(36.4061211),
755 usd_value: dec!(36.42053792),
756 wallet_balance: dec!(36.4061211),
757 locked: dec!(0),
758 spot_hedging_qty: dec!(0),
759 borrow_amount: dec!(0.000000000000000000),
760 accrued_interest: dec!(0),
761 total_order_im: Some(dec!(0)),
762 total_position_im: Some(dec!(0)),
763 total_position_mm: Some(dec!(0)),
764 unrealised_pnl: dec!(0),
765 cum_realised_pnl: dec!(-2084.9938789),
766 bonus: dec!(0),
767 margin_collateral: true,
768 collateral_switch: true,
769 spot_borrow: Some(dec!(0)),
770 };
771 let coin = HashMap::from([(Unique::unique_key(&coin), coin)]);
772 let expected = Resp {
773 ret_code: 0,
774 ret_msg: String::from("OK"),
775 result: List {
776 list: vec![WalletBalance {
777 account_type: AccountType::UNIFIED,
778 account_im_rate: dec!(0),
779 account_im_rate_by_mp: dec!(0),
780 account_mm_rate: dec!(0),
781 account_mm_rate_by_mp: dec!(0),
782 total_equity: dec!(36.42053792),
783 total_wallet_balance: dec!(36.42053792),
784 total_margin_balance: dec!(36.42053792),
785 total_available_balance: dec!(36.42053792),
786 total_perp_upl: dec!(0),
787 total_initial_margin: dec!(0),
788 total_initial_margin_by_mp: dec!(0),
789 total_maintenance_margin: dec!(0),
790 total_maintenance_margin_by_mp: dec!(0),
791 coin,
792 }],
793 },
794 time: Some(1751570498412),
795 ret_ext_info: Some(RetExtInfo::default()),
796 };
797
798 let message = deserialize_json(json).unwrap();
799
800 assert_eq!(expected, message);
801 }
802
803 #[test]
804 fn deserialize_response_get_account_info() {
805 let json = r#"{
806 "retCode": 0,
807 "retMsg": "OK",
808 "result": {
809 "marginMode": "REGULAR_MARGIN",
810 "updatedTime": "1697078946000",
811 "unifiedMarginStatus": 4,
812 "dcpStatus": "OFF",
813 "timeWindow": 10,
814 "smpGroup": 0,
815 "isMasterTrader": false,
816 "spotHedgingStatus": "OFF"
817 }
818 }"#;
819 let expected = Resp {
820 ret_code: 0,
821 ret_msg: String::from("OK"),
822 result: AccountInfo {
823 unified_margin_status: UnifiedMarginStatus::UnifiedTradingAccount1Pro,
824 margin_mode: MarginMode::RegularMargin,
825 is_master_trader: false,
826 spot_hedging_status: SpotHedgingStatus::Off,
827 updated_time: 1697078946000,
828 },
829 time: None,
830 ret_ext_info: None,
831 };
832
833 let message = deserialize_json(json).unwrap();
834
835 assert_eq!(expected, message);
836 }
837}