1use crate::client::Client;
10use crate::error::Result;
11use crate::models::margin::{
12 BnbBurnStatus, InterestHistoryRecord, InterestRateRecord, IsolatedAccountLimit,
13 IsolatedMarginAccountDetails, IsolatedMarginTransferType, LoanRecord, MarginAccountDetails,
14 MarginAssetInfo, MarginOrderCancellation, MarginOrderResult, MarginOrderState,
15 MarginPairDetails, MarginPriceIndex, MarginTrade, MarginTransferType, MaxBorrowableAmount,
16 MaxTransferableAmount, RecordsQueryResult, RepayRecord, SideEffectType, TransactionId,
17};
18use crate::types::{OrderSide, OrderType, TimeInForce};
19
20const SAPI_V1_MARGIN_TRANSFER: &str = "/sapi/v1/margin/transfer";
22const SAPI_V1_MARGIN_ISOLATED_TRANSFER: &str = "/sapi/v1/margin/isolated/transfer";
23const SAPI_V1_MARGIN_LOAN: &str = "/sapi/v1/margin/loan";
24const SAPI_V1_MARGIN_REPAY: &str = "/sapi/v1/margin/repay";
25const SAPI_V1_MARGIN_ACCOUNT: &str = "/sapi/v1/margin/account";
26const SAPI_V1_MARGIN_ISOLATED_ACCOUNT: &str = "/sapi/v1/margin/isolated/account";
27const SAPI_V1_MARGIN_ORDER: &str = "/sapi/v1/margin/order";
28const SAPI_V1_MARGIN_OPEN_ORDERS: &str = "/sapi/v1/margin/openOrders";
29const SAPI_V1_MARGIN_ALL_ORDERS: &str = "/sapi/v1/margin/allOrders";
30const SAPI_V1_MARGIN_MY_TRADES: &str = "/sapi/v1/margin/myTrades";
31const SAPI_V1_MARGIN_MAX_BORROWABLE: &str = "/sapi/v1/margin/maxBorrowable";
32const SAPI_V1_MARGIN_MAX_TRANSFERABLE: &str = "/sapi/v1/margin/maxTransferable";
33const SAPI_V1_MARGIN_INTEREST_HISTORY: &str = "/sapi/v1/margin/interestHistory";
34const SAPI_V1_MARGIN_INTEREST_RATE_HISTORY: &str = "/sapi/v1/margin/interestRateHistory";
35const SAPI_V1_MARGIN_PAIR: &str = "/sapi/v1/margin/pair";
36const SAPI_V1_MARGIN_ALL_PAIRS: &str = "/sapi/v1/margin/allPairs";
37const SAPI_V1_MARGIN_ASSET: &str = "/sapi/v1/margin/asset";
38const SAPI_V1_MARGIN_ALL_ASSETS: &str = "/sapi/v1/margin/allAssets";
39const SAPI_V1_MARGIN_PRICE_INDEX: &str = "/sapi/v1/margin/priceIndex";
40const SAPI_V1_MARGIN_ISOLATED_ACCOUNT_LIMIT: &str = "/sapi/v1/margin/isolated/accountLimit";
41const SAPI_V1_BNB_BURN: &str = "/sapi/v1/bnbBurn";
42
43#[derive(Clone)]
62pub struct Margin {
63 pub(crate) client: Client,
64}
65
66impl Margin {
67 pub(crate) fn new(client: Client) -> Self {
69 Self { client }
70 }
71
72 pub async fn account(&self) -> Result<MarginAccountDetails> {
92 self.client.get_signed(SAPI_V1_MARGIN_ACCOUNT, &[]).await
93 }
94
95 pub async fn isolated_account(
110 &self,
111 symbols: Option<&str>,
112 ) -> Result<IsolatedMarginAccountDetails> {
113 let mut params: Vec<(&str, String)> = vec![];
114
115 if let Some(s) = symbols {
116 params.push(("symbols", s.to_string()));
117 }
118
119 let params_ref: Vec<(&str, &str)> = params.iter().map(|(k, v)| (*k, v.as_str())).collect();
120 self.client
121 .get_signed(SAPI_V1_MARGIN_ISOLATED_ACCOUNT, ¶ms_ref)
122 .await
123 }
124
125 pub async fn max_borrowable(
139 &self,
140 asset: &str,
141 isolated_symbol: Option<&str>,
142 ) -> Result<MaxBorrowableAmount> {
143 let mut params: Vec<(&str, String)> = vec![("asset", asset.to_string())];
144
145 if let Some(s) = isolated_symbol {
146 params.push(("isolatedSymbol", s.to_string()));
147 }
148
149 let params_ref: Vec<(&str, &str)> = params.iter().map(|(k, v)| (*k, v.as_str())).collect();
150 self.client
151 .get_signed(SAPI_V1_MARGIN_MAX_BORROWABLE, ¶ms_ref)
152 .await
153 }
154
155 pub async fn max_transferable(
169 &self,
170 asset: &str,
171 isolated_symbol: Option<&str>,
172 ) -> Result<MaxTransferableAmount> {
173 let mut params: Vec<(&str, String)> = vec![("asset", asset.to_string())];
174
175 if let Some(s) = isolated_symbol {
176 params.push(("isolatedSymbol", s.to_string()));
177 }
178
179 let params_ref: Vec<(&str, &str)> = params.iter().map(|(k, v)| (*k, v.as_str())).collect();
180 self.client
181 .get_signed(SAPI_V1_MARGIN_MAX_TRANSFERABLE, ¶ms_ref)
182 .await
183 }
184
185 pub async fn isolated_account_limit(&self) -> Result<IsolatedAccountLimit> {
194 self.client
195 .get_signed(SAPI_V1_MARGIN_ISOLATED_ACCOUNT_LIMIT, &[])
196 .await
197 }
198
199 pub async fn transfer(
221 &self,
222 asset: &str,
223 amount: &str,
224 transfer_type: MarginTransferType,
225 ) -> Result<TransactionId> {
226 let type_val = match transfer_type {
227 MarginTransferType::MainToMargin => "1",
228 MarginTransferType::MarginToMain => "2",
229 };
230
231 let params: Vec<(&str, String)> = vec![
232 ("asset", asset.to_string()),
233 ("amount", amount.to_string()),
234 ("type", type_val.to_string()),
235 ];
236
237 let params_ref: Vec<(&str, &str)> = params.iter().map(|(k, v)| (*k, v.as_str())).collect();
238 self.client
239 .post_signed(SAPI_V1_MARGIN_TRANSFER, ¶ms_ref)
240 .await
241 }
242
243 pub async fn isolated_transfer(
270 &self,
271 asset: &str,
272 symbol: &str,
273 amount: &str,
274 trans_from: IsolatedMarginTransferType,
275 trans_to: IsolatedMarginTransferType,
276 ) -> Result<TransactionId> {
277 let from_str = match trans_from {
278 IsolatedMarginTransferType::Spot => "SPOT",
279 IsolatedMarginTransferType::IsolatedMargin => "ISOLATED_MARGIN",
280 };
281 let to_str = match trans_to {
282 IsolatedMarginTransferType::Spot => "SPOT",
283 IsolatedMarginTransferType::IsolatedMargin => "ISOLATED_MARGIN",
284 };
285
286 let params: Vec<(&str, String)> = vec![
287 ("asset", asset.to_string()),
288 ("symbol", symbol.to_string()),
289 ("amount", amount.to_string()),
290 ("transFrom", from_str.to_string()),
291 ("transTo", to_str.to_string()),
292 ];
293
294 let params_ref: Vec<(&str, &str)> = params.iter().map(|(k, v)| (*k, v.as_str())).collect();
295 self.client
296 .post_signed(SAPI_V1_MARGIN_ISOLATED_TRANSFER, ¶ms_ref)
297 .await
298 }
299
300 pub async fn loan(
319 &self,
320 asset: &str,
321 amount: &str,
322 is_isolated: bool,
323 symbol: Option<&str>,
324 ) -> Result<TransactionId> {
325 let mut params: Vec<(&str, String)> =
326 vec![("asset", asset.to_string()), ("amount", amount.to_string())];
327
328 if is_isolated {
329 params.push(("isIsolated", "TRUE".to_string()));
330 if let Some(s) = symbol {
331 params.push(("symbol", s.to_string()));
332 }
333 }
334
335 let params_ref: Vec<(&str, &str)> = params.iter().map(|(k, v)| (*k, v.as_str())).collect();
336 self.client
337 .post_signed(SAPI_V1_MARGIN_LOAN, ¶ms_ref)
338 .await
339 }
340
341 pub async fn repay(
358 &self,
359 asset: &str,
360 amount: &str,
361 is_isolated: bool,
362 symbol: Option<&str>,
363 ) -> Result<TransactionId> {
364 let mut params: Vec<(&str, String)> =
365 vec![("asset", asset.to_string()), ("amount", amount.to_string())];
366
367 if is_isolated {
368 params.push(("isIsolated", "TRUE".to_string()));
369 if let Some(s) = symbol {
370 params.push(("symbol", s.to_string()));
371 }
372 }
373
374 let params_ref: Vec<(&str, &str)> = params.iter().map(|(k, v)| (*k, v.as_str())).collect();
375 self.client
376 .post_signed(SAPI_V1_MARGIN_REPAY, ¶ms_ref)
377 .await
378 }
379
380 pub async fn loan_records(
402 &self,
403 asset: &str,
404 isolated_symbol: Option<&str>,
405 start_time: Option<u64>,
406 end_time: Option<u64>,
407 current: Option<u32>,
408 size: Option<u32>,
409 ) -> Result<RecordsQueryResult<LoanRecord>> {
410 let mut params: Vec<(&str, String)> = vec![("asset", asset.to_string())];
411
412 if let Some(s) = isolated_symbol {
413 params.push(("isolatedSymbol", s.to_string()));
414 }
415 if let Some(st) = start_time {
416 params.push(("startTime", st.to_string()));
417 }
418 if let Some(et) = end_time {
419 params.push(("endTime", et.to_string()));
420 }
421 if let Some(c) = current {
422 params.push(("current", c.to_string()));
423 }
424 if let Some(s) = size {
425 params.push(("size", s.to_string()));
426 }
427
428 let params_ref: Vec<(&str, &str)> = params.iter().map(|(k, v)| (*k, v.as_str())).collect();
429 self.client
430 .get_signed(SAPI_V1_MARGIN_LOAN, ¶ms_ref)
431 .await
432 }
433
434 pub async fn repay_records(
445 &self,
446 asset: &str,
447 isolated_symbol: Option<&str>,
448 start_time: Option<u64>,
449 end_time: Option<u64>,
450 current: Option<u32>,
451 size: Option<u32>,
452 ) -> Result<RecordsQueryResult<RepayRecord>> {
453 let mut params: Vec<(&str, String)> = vec![("asset", asset.to_string())];
454
455 if let Some(s) = isolated_symbol {
456 params.push(("isolatedSymbol", s.to_string()));
457 }
458 if let Some(st) = start_time {
459 params.push(("startTime", st.to_string()));
460 }
461 if let Some(et) = end_time {
462 params.push(("endTime", et.to_string()));
463 }
464 if let Some(c) = current {
465 params.push(("current", c.to_string()));
466 }
467 if let Some(s) = size {
468 params.push(("size", s.to_string()));
469 }
470
471 let params_ref: Vec<(&str, &str)> = params.iter().map(|(k, v)| (*k, v.as_str())).collect();
472 self.client
473 .get_signed(SAPI_V1_MARGIN_REPAY, ¶ms_ref)
474 .await
475 }
476
477 #[allow(clippy::too_many_arguments)]
515 pub async fn create_order(
516 &self,
517 symbol: &str,
518 side: OrderSide,
519 order_type: OrderType,
520 quantity: Option<&str>,
521 quote_order_qty: Option<&str>,
522 price: Option<&str>,
523 stop_price: Option<&str>,
524 time_in_force: Option<TimeInForce>,
525 new_client_order_id: Option<&str>,
526 side_effect_type: Option<SideEffectType>,
527 is_isolated: Option<bool>,
528 ) -> Result<MarginOrderResult> {
529 let mut params: Vec<(&str, String)> = vec![
530 ("symbol", symbol.to_string()),
531 ("side", format!("{:?}", side).to_uppercase()),
532 ("type", format!("{:?}", order_type).to_uppercase()),
533 ];
534
535 if let Some(qty) = quantity {
536 params.push(("quantity", qty.to_string()));
537 }
538 if let Some(qty) = quote_order_qty {
539 params.push(("quoteOrderQty", qty.to_string()));
540 }
541 if let Some(p) = price {
542 params.push(("price", p.to_string()));
543 }
544 if let Some(sp) = stop_price {
545 params.push(("stopPrice", sp.to_string()));
546 }
547 if let Some(tif) = time_in_force {
548 params.push(("timeInForce", format!("{:?}", tif).to_uppercase()));
549 }
550 if let Some(id) = new_client_order_id {
551 params.push(("newClientOrderId", id.to_string()));
552 }
553 if let Some(se) = side_effect_type {
554 params.push((
555 "sideEffectType",
556 match se {
557 SideEffectType::NoSideEffect => "NO_SIDE_EFFECT",
558 SideEffectType::MarginBuy => "MARGIN_BUY",
559 SideEffectType::AutoRepay => "AUTO_REPAY",
560 }
561 .to_string(),
562 ));
563 }
564 if let Some(isolated) = is_isolated {
565 params.push((
566 "isIsolated",
567 if isolated { "TRUE" } else { "FALSE" }.to_string(),
568 ));
569 }
570
571 let params_ref: Vec<(&str, &str)> = params.iter().map(|(k, v)| (*k, v.as_str())).collect();
572 self.client
573 .post_signed(SAPI_V1_MARGIN_ORDER, ¶ms_ref)
574 .await
575 }
576
577 pub async fn cancel_order(
594 &self,
595 symbol: &str,
596 order_id: Option<u64>,
597 orig_client_order_id: Option<&str>,
598 is_isolated: Option<bool>,
599 ) -> Result<MarginOrderCancellation> {
600 let mut params: Vec<(&str, String)> = vec![("symbol", symbol.to_string())];
601
602 if let Some(id) = order_id {
603 params.push(("orderId", id.to_string()));
604 }
605 if let Some(cid) = orig_client_order_id {
606 params.push(("origClientOrderId", cid.to_string()));
607 }
608 if let Some(isolated) = is_isolated {
609 params.push((
610 "isIsolated",
611 if isolated { "TRUE" } else { "FALSE" }.to_string(),
612 ));
613 }
614
615 let params_ref: Vec<(&str, &str)> = params.iter().map(|(k, v)| (*k, v.as_str())).collect();
616 self.client
617 .delete_signed(SAPI_V1_MARGIN_ORDER, ¶ms_ref)
618 .await
619 }
620
621 pub async fn cancel_all_orders(
636 &self,
637 symbol: &str,
638 is_isolated: Option<bool>,
639 ) -> Result<Vec<MarginOrderCancellation>> {
640 let mut params: Vec<(&str, String)> = vec![("symbol", symbol.to_string())];
641
642 if let Some(isolated) = is_isolated {
643 params.push((
644 "isIsolated",
645 if isolated { "TRUE" } else { "FALSE" }.to_string(),
646 ));
647 }
648
649 let params_ref: Vec<(&str, &str)> = params.iter().map(|(k, v)| (*k, v.as_str())).collect();
650 self.client
651 .delete_signed(SAPI_V1_MARGIN_OPEN_ORDERS, ¶ms_ref)
652 .await
653 }
654
655 pub async fn get_order(
664 &self,
665 symbol: &str,
666 order_id: Option<u64>,
667 orig_client_order_id: Option<&str>,
668 is_isolated: Option<bool>,
669 ) -> Result<MarginOrderState> {
670 let mut params: Vec<(&str, String)> = vec![("symbol", symbol.to_string())];
671
672 if let Some(id) = order_id {
673 params.push(("orderId", id.to_string()));
674 }
675 if let Some(cid) = orig_client_order_id {
676 params.push(("origClientOrderId", cid.to_string()));
677 }
678 if let Some(isolated) = is_isolated {
679 params.push((
680 "isIsolated",
681 if isolated { "TRUE" } else { "FALSE" }.to_string(),
682 ));
683 }
684
685 let params_ref: Vec<(&str, &str)> = params.iter().map(|(k, v)| (*k, v.as_str())).collect();
686 self.client
687 .get_signed(SAPI_V1_MARGIN_ORDER, ¶ms_ref)
688 .await
689 }
690
691 pub async fn open_orders(
698 &self,
699 symbol: Option<&str>,
700 is_isolated: Option<bool>,
701 ) -> Result<Vec<MarginOrderState>> {
702 let mut params: Vec<(&str, String)> = vec![];
703
704 if let Some(s) = symbol {
705 params.push(("symbol", s.to_string()));
706 }
707 if let Some(isolated) = is_isolated {
708 params.push((
709 "isIsolated",
710 if isolated { "TRUE" } else { "FALSE" }.to_string(),
711 ));
712 }
713
714 let params_ref: Vec<(&str, &str)> = params.iter().map(|(k, v)| (*k, v.as_str())).collect();
715 self.client
716 .get_signed(SAPI_V1_MARGIN_OPEN_ORDERS, ¶ms_ref)
717 .await
718 }
719
720 pub async fn all_orders(
731 &self,
732 symbol: &str,
733 order_id: Option<u64>,
734 start_time: Option<u64>,
735 end_time: Option<u64>,
736 limit: Option<u32>,
737 is_isolated: Option<bool>,
738 ) -> Result<Vec<MarginOrderState>> {
739 let mut params: Vec<(&str, String)> = vec![("symbol", symbol.to_string())];
740
741 if let Some(id) = order_id {
742 params.push(("orderId", id.to_string()));
743 }
744 if let Some(st) = start_time {
745 params.push(("startTime", st.to_string()));
746 }
747 if let Some(et) = end_time {
748 params.push(("endTime", et.to_string()));
749 }
750 if let Some(l) = limit {
751 params.push(("limit", l.to_string()));
752 }
753 if let Some(isolated) = is_isolated {
754 params.push((
755 "isIsolated",
756 if isolated { "TRUE" } else { "FALSE" }.to_string(),
757 ));
758 }
759
760 let params_ref: Vec<(&str, &str)> = params.iter().map(|(k, v)| (*k, v.as_str())).collect();
761 self.client
762 .get_signed(SAPI_V1_MARGIN_ALL_ORDERS, ¶ms_ref)
763 .await
764 }
765
766 #[allow(clippy::too_many_arguments)]
778 pub async fn my_trades(
779 &self,
780 symbol: &str,
781 order_id: Option<u64>,
782 start_time: Option<u64>,
783 end_time: Option<u64>,
784 from_id: Option<u64>,
785 limit: Option<u32>,
786 is_isolated: Option<bool>,
787 ) -> Result<Vec<MarginTrade>> {
788 let mut params: Vec<(&str, String)> = vec![("symbol", symbol.to_string())];
789
790 if let Some(id) = order_id {
791 params.push(("orderId", id.to_string()));
792 }
793 if let Some(st) = start_time {
794 params.push(("startTime", st.to_string()));
795 }
796 if let Some(et) = end_time {
797 params.push(("endTime", et.to_string()));
798 }
799 if let Some(id) = from_id {
800 params.push(("fromId", id.to_string()));
801 }
802 if let Some(l) = limit {
803 params.push(("limit", l.to_string()));
804 }
805 if let Some(isolated) = is_isolated {
806 params.push((
807 "isIsolated",
808 if isolated { "TRUE" } else { "FALSE" }.to_string(),
809 ));
810 }
811
812 let params_ref: Vec<(&str, &str)> = params.iter().map(|(k, v)| (*k, v.as_str())).collect();
813 self.client
814 .get_signed(SAPI_V1_MARGIN_MY_TRADES, ¶ms_ref)
815 .await
816 }
817
818 pub async fn interest_history(
831 &self,
832 asset: Option<&str>,
833 isolated_symbol: Option<&str>,
834 start_time: Option<u64>,
835 end_time: Option<u64>,
836 current: Option<u32>,
837 size: Option<u32>,
838 ) -> Result<RecordsQueryResult<InterestHistoryRecord>> {
839 let mut params: Vec<(&str, String)> = vec![];
840
841 if let Some(a) = asset {
842 params.push(("asset", a.to_string()));
843 }
844 if let Some(s) = isolated_symbol {
845 params.push(("isolatedSymbol", s.to_string()));
846 }
847 if let Some(st) = start_time {
848 params.push(("startTime", st.to_string()));
849 }
850 if let Some(et) = end_time {
851 params.push(("endTime", et.to_string()));
852 }
853 if let Some(c) = current {
854 params.push(("current", c.to_string()));
855 }
856 if let Some(s) = size {
857 params.push(("size", s.to_string()));
858 }
859
860 let params_ref: Vec<(&str, &str)> = params.iter().map(|(k, v)| (*k, v.as_str())).collect();
861 self.client
862 .get_signed(SAPI_V1_MARGIN_INTEREST_HISTORY, ¶ms_ref)
863 .await
864 }
865
866 pub async fn interest_rate_history(
876 &self,
877 asset: &str,
878 vip_level: Option<u32>,
879 start_time: Option<u64>,
880 end_time: Option<u64>,
881 limit: Option<u32>,
882 ) -> Result<Vec<InterestRateRecord>> {
883 let mut params: Vec<(&str, String)> = vec![("asset", asset.to_string())];
884
885 if let Some(vip) = vip_level {
886 params.push(("vipLevel", vip.to_string()));
887 }
888 if let Some(st) = start_time {
889 params.push(("startTime", st.to_string()));
890 }
891 if let Some(et) = end_time {
892 params.push(("endTime", et.to_string()));
893 }
894 if let Some(l) = limit {
895 params.push(("limit", l.to_string()));
896 }
897
898 let params_ref: Vec<(&str, &str)> = params.iter().map(|(k, v)| (*k, v.as_str())).collect();
899 self.client
900 .get_signed(SAPI_V1_MARGIN_INTEREST_RATE_HISTORY, ¶ms_ref)
901 .await
902 }
903
904 pub async fn pair(&self, symbol: &str) -> Result<MarginPairDetails> {
912 let params: Vec<(&str, String)> = vec![("symbol", symbol.to_string())];
913 let params_ref: Vec<(&str, &str)> = params.iter().map(|(k, v)| (*k, v.as_str())).collect();
914 self.client
915 .get_signed(SAPI_V1_MARGIN_PAIR, ¶ms_ref)
916 .await
917 }
918
919 pub async fn all_pairs(&self) -> Result<Vec<MarginPairDetails>> {
921 self.client.get_signed(SAPI_V1_MARGIN_ALL_PAIRS, &[]).await
922 }
923
924 pub async fn asset(&self, asset: &str) -> Result<MarginAssetInfo> {
930 let params: Vec<(&str, String)> = vec![("asset", asset.to_string())];
931 let params_ref: Vec<(&str, &str)> = params.iter().map(|(k, v)| (*k, v.as_str())).collect();
932 self.client
933 .get_signed(SAPI_V1_MARGIN_ASSET, ¶ms_ref)
934 .await
935 }
936
937 pub async fn all_assets(&self) -> Result<Vec<MarginAssetInfo>> {
939 self.client.get_signed(SAPI_V1_MARGIN_ALL_ASSETS, &[]).await
940 }
941
942 pub async fn price_index(&self, symbol: &str) -> Result<MarginPriceIndex> {
948 let params: Vec<(&str, String)> = vec![("symbol", symbol.to_string())];
949 let params_ref: Vec<(&str, &str)> = params.iter().map(|(k, v)| (*k, v.as_str())).collect();
950 self.client
951 .get_signed(SAPI_V1_MARGIN_PRICE_INDEX, ¶ms_ref)
952 .await
953 }
954
955 pub async fn bnb_burn_status(&self) -> Result<BnbBurnStatus> {
959 self.client.get_signed(SAPI_V1_BNB_BURN, &[]).await
960 }
961
962 pub async fn toggle_bnb_burn(
969 &self,
970 spot_bnb_burn: Option<bool>,
971 interest_bnb_burn: Option<bool>,
972 ) -> Result<BnbBurnStatus> {
973 let mut params: Vec<(&str, String)> = vec![];
974
975 if let Some(spot) = spot_bnb_burn {
976 params.push(("spotBNBBurn", spot.to_string()));
977 }
978 if let Some(interest) = interest_bnb_burn {
979 params.push(("interestBNBBurn", interest.to_string()));
980 }
981
982 let params_ref: Vec<(&str, &str)> = params.iter().map(|(k, v)| (*k, v.as_str())).collect();
983 self.client.post_signed(SAPI_V1_BNB_BURN, ¶ms_ref).await
984 }
985}