Skip to main content

binance_api_client/api/
margin.rs

1//! Margin Trading API endpoints (SAPI).
2//!
3//! This module provides access to Binance Margin SAPI endpoints for:
4//! - Cross-margin and isolated margin account management
5//! - Margin transfers, loans, and repayments
6//! - Margin trading (orders)
7//! - Interest and loan history
8
9use 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
20// SAPI endpoints.
21const 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/// Margin Trading API client.
44///
45/// Provides access to Binance Margin SAPI endpoints for margin trading,
46/// loans, transfers, and account management.
47///
48/// # Example
49///
50/// ```rust,ignore
51/// let client = Binance::new("api_key", "secret_key")?;
52///
53/// // Get cross-margin account details
54/// let account = client.margin().account().await?;
55/// println!("Margin level: {}", account.margin_level);
56///
57/// // Check max borrowable
58/// let max = client.margin().max_borrowable("BTC", None).await?;
59/// println!("Max borrowable BTC: {}", max.amount);
60/// ```
61#[derive(Clone)]
62pub struct Margin {
63    pub(crate) client: Client,
64}
65
66impl Margin {
67    /// Create a new Margin API client.
68    pub(crate) fn new(client: Client) -> Self {
69        Self { client }
70    }
71
72    // Account Management.
73
74    /// Get cross-margin account details.
75    ///
76    /// Returns margin account information including balances, margin level,
77    /// and trading status.
78    ///
79    /// # Example
80    ///
81    /// ```rust,ignore
82    /// let account = client.margin().account().await?;
83    /// println!("Margin level: {}", account.margin_level);
84    /// println!("Total assets (BTC): {}", account.total_asset_of_btc);
85    /// for asset in account.user_assets {
86    ///     if asset.net_asset > 0.0 {
87    ///         println!("{}: free={}, borrowed={}", asset.asset, asset.free, asset.borrowed);
88    ///     }
89    /// }
90    /// ```
91    pub async fn account(&self) -> Result<MarginAccountDetails> {
92        self.client.get_signed(SAPI_V1_MARGIN_ACCOUNT, &[]).await
93    }
94
95    /// Get isolated margin account details.
96    ///
97    /// # Arguments
98    ///
99    /// * `symbols` - Optional list of symbols to filter (comma-separated if multiple)
100    ///
101    /// # Example
102    ///
103    /// ```rust,ignore
104    /// let account = client.margin().isolated_account(Some("BTCUSDT")).await?;
105    /// for asset in account.assets {
106    ///     println!("{}: margin_level={}", asset.symbol, asset.margin_level);
107    /// }
108    /// ```
109    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, &params_ref)
122            .await
123    }
124
125    /// Get max borrowable amount for an asset.
126    ///
127    /// # Arguments
128    ///
129    /// * `asset` - Asset to query
130    /// * `isolated_symbol` - Isolated margin symbol (for isolated margin)
131    ///
132    /// # Example
133    ///
134    /// ```rust,ignore
135    /// let max = client.margin().max_borrowable("BTC", None).await?;
136    /// println!("Max borrowable: {} BTC", max.amount);
137    /// ```
138    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, &params_ref)
152            .await
153    }
154
155    /// Get max transferable amount for an asset.
156    ///
157    /// # Arguments
158    ///
159    /// * `asset` - Asset to query
160    /// * `isolated_symbol` - Isolated margin symbol (for isolated margin)
161    ///
162    /// # Example
163    ///
164    /// ```rust,ignore
165    /// let max = client.margin().max_transferable("USDT", None).await?;
166    /// println!("Max transferable: {} USDT", max.amount);
167    /// ```
168    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, &params_ref)
182            .await
183    }
184
185    /// Get isolated margin account limit.
186    ///
187    /// # Example
188    ///
189    /// ```rust,ignore
190    /// let limit = client.margin().isolated_account_limit().await?;
191    /// println!("Enabled: {}, Max: {}", limit.enabled_account, limit.max_account);
192    /// ```
193    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    // Transfer.
200
201    /// Execute a cross-margin transfer between spot and margin accounts.
202    ///
203    /// # Arguments
204    ///
205    /// * `asset` - Asset to transfer
206    /// * `amount` - Amount to transfer
207    /// * `transfer_type` - Direction of transfer
208    ///
209    /// # Example
210    ///
211    /// ```rust,ignore
212    /// use binance_api_client::MarginTransferType;
213    ///
214    /// // Transfer 100 USDT from spot to margin
215    /// let result = client.margin()
216    ///     .transfer("USDT", "100.0", MarginTransferType::MainToMargin)
217    ///     .await?;
218    /// println!("Transfer ID: {}", result.tran_id);
219    /// ```
220    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, &params_ref)
240            .await
241    }
242
243    /// Execute an isolated margin transfer.
244    ///
245    /// # Arguments
246    ///
247    /// * `asset` - Asset to transfer
248    /// * `symbol` - Isolated margin symbol
249    /// * `amount` - Amount to transfer
250    /// * `trans_from` - Source account type
251    /// * `trans_to` - Destination account type
252    ///
253    /// # Example
254    ///
255    /// ```rust,ignore
256    /// use binance_api_client::IsolatedMarginTransferType;
257    ///
258    /// // Transfer 100 USDT from spot to BTCUSDT isolated margin
259    /// let result = client.margin()
260    ///     .isolated_transfer(
261    ///         "USDT",
262    ///         "BTCUSDT",
263    ///         "100.0",
264    ///         IsolatedMarginTransferType::Spot,
265    ///         IsolatedMarginTransferType::IsolatedMargin,
266    ///     )
267    ///     .await?;
268    /// ```
269    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, &params_ref)
297            .await
298    }
299
300    // Borrow/Repay.
301
302    /// Apply for a margin loan.
303    ///
304    /// # Arguments
305    ///
306    /// * `asset` - Asset to borrow
307    /// * `amount` - Amount to borrow
308    /// * `is_isolated` - Whether this is isolated margin
309    /// * `symbol` - Symbol for isolated margin (required if is_isolated is true)
310    ///
311    /// # Example
312    ///
313    /// ```rust,ignore
314    /// // Borrow 0.1 BTC on cross margin
315    /// let result = client.margin().loan("BTC", "0.1", false, None).await?;
316    /// println!("Loan transaction ID: {}", result.tran_id);
317    /// ```
318    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, &params_ref)
338            .await
339    }
340
341    /// Repay a margin loan.
342    ///
343    /// # Arguments
344    ///
345    /// * `asset` - Asset to repay
346    /// * `amount` - Amount to repay
347    /// * `is_isolated` - Whether this is isolated margin
348    /// * `symbol` - Symbol for isolated margin (required if is_isolated is true)
349    ///
350    /// # Example
351    ///
352    /// ```rust,ignore
353    /// // Repay 0.1 BTC on cross margin
354    /// let result = client.margin().repay("BTC", "0.1", false, None).await?;
355    /// println!("Repay transaction ID: {}", result.tran_id);
356    /// ```
357    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, &params_ref)
377            .await
378    }
379
380    /// Get loan records.
381    ///
382    /// # Arguments
383    ///
384    /// * `asset` - Asset to query
385    /// * `isolated_symbol` - Isolated margin symbol (optional)
386    /// * `start_time` - Start timestamp (optional)
387    /// * `end_time` - End timestamp (optional)
388    /// * `current` - Page number (default 1)
389    /// * `size` - Page size (default 10, max 100)
390    ///
391    /// # Example
392    ///
393    /// ```rust,ignore
394    /// let records = client.margin()
395    ///     .loan_records("BTC", None, None, None, None, Some(20))
396    ///     .await?;
397    /// for record in records.rows {
398    ///     println!("Loan: {} {} at {}", record.principal, record.asset, record.timestamp);
399    /// }
400    /// ```
401    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, &params_ref)
431            .await
432    }
433
434    /// Get repay records.
435    ///
436    /// # Arguments
437    ///
438    /// * `asset` - Asset to query
439    /// * `isolated_symbol` - Isolated margin symbol (optional)
440    /// * `start_time` - Start timestamp (optional)
441    /// * `end_time` - End timestamp (optional)
442    /// * `current` - Page number (default 1)
443    /// * `size` - Page size (default 10, max 100)
444    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, &params_ref)
474            .await
475    }
476
477    // Trading.
478
479    /// Create a new margin order.
480    ///
481    /// # Arguments
482    ///
483    /// * `symbol` - Trading pair symbol
484    /// * `side` - Order side (Buy/Sell)
485    /// * `order_type` - Order type
486    /// * `quantity` - Order quantity (optional for some order types)
487    /// * `quote_order_qty` - Quote order quantity (optional)
488    /// * `price` - Price (required for limit orders)
489    /// * `stop_price` - Stop price (for stop orders)
490    /// * `time_in_force` - Time in force (optional)
491    /// * `new_client_order_id` - Client order ID (optional)
492    /// * `side_effect_type` - Side effect type (optional)
493    /// * `is_isolated` - Whether isolated margin (optional)
494    ///
495    /// # Example
496    ///
497    /// ```rust,ignore
498    /// use binance_api_client::{OrderSide, OrderType, TimeInForce, SideEffectType};
499    ///
500    /// let order = client.margin().create_order(
501    ///     "BTCUSDT",
502    ///     OrderSide::Buy,
503    ///     OrderType::Limit,
504    ///     Some("0.001"),
505    ///     None,
506    ///     Some("50000.00"),
507    ///     None,
508    ///     Some(TimeInForce::GTC),
509    ///     None,
510    ///     Some(SideEffectType::MarginBuy),
511    ///     None,
512    /// ).await?;
513    /// ```
514    #[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, &params_ref)
574            .await
575    }
576
577    /// Cancel a margin order.
578    ///
579    /// # Arguments
580    ///
581    /// * `symbol` - Trading pair symbol
582    /// * `order_id` - Order ID (optional if orig_client_order_id provided)
583    /// * `orig_client_order_id` - Original client order ID (optional if order_id provided)
584    /// * `is_isolated` - Whether isolated margin
585    ///
586    /// # Example
587    ///
588    /// ```rust,ignore
589    /// let result = client.margin()
590    ///     .cancel_order("BTCUSDT", Some(12345), None, None)
591    ///     .await?;
592    /// ```
593    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, &params_ref)
618            .await
619    }
620
621    /// Cancel all open orders on a symbol.
622    ///
623    /// # Arguments
624    ///
625    /// * `symbol` - Trading pair symbol
626    /// * `is_isolated` - Whether isolated margin
627    ///
628    /// # Example
629    ///
630    /// ```rust,ignore
631    /// let results = client.margin()
632    ///     .cancel_all_orders("BTCUSDT", None)
633    ///     .await?;
634    /// ```
635    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, &params_ref)
652            .await
653    }
654
655    /// Query a margin order.
656    ///
657    /// # Arguments
658    ///
659    /// * `symbol` - Trading pair symbol
660    /// * `order_id` - Order ID (optional)
661    /// * `orig_client_order_id` - Original client order ID (optional)
662    /// * `is_isolated` - Whether isolated margin
663    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, &params_ref)
688            .await
689    }
690
691    /// Get all open margin orders.
692    ///
693    /// # Arguments
694    ///
695    /// * `symbol` - Trading pair symbol (optional)
696    /// * `is_isolated` - Whether isolated margin
697    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, &params_ref)
717            .await
718    }
719
720    /// Get all margin orders (active, cancelled, or filled).
721    ///
722    /// # Arguments
723    ///
724    /// * `symbol` - Trading pair symbol
725    /// * `order_id` - Order ID to start from (optional)
726    /// * `start_time` - Start timestamp (optional)
727    /// * `end_time` - End timestamp (optional)
728    /// * `limit` - Number of records (default 500, max 500)
729    /// * `is_isolated` - Whether isolated margin
730    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, &params_ref)
763            .await
764    }
765
766    /// Get margin trades.
767    ///
768    /// # Arguments
769    ///
770    /// * `symbol` - Trading pair symbol
771    /// * `order_id` - Filter by order ID (optional)
772    /// * `start_time` - Start timestamp (optional)
773    /// * `end_time` - End timestamp (optional)
774    /// * `from_id` - Trade ID to start from (optional)
775    /// * `limit` - Number of records (default 500, max 1000)
776    /// * `is_isolated` - Whether isolated margin
777    #[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, &params_ref)
815            .await
816    }
817
818    // Interest.
819
820    /// Get interest history.
821    ///
822    /// # Arguments
823    ///
824    /// * `asset` - Asset to query (optional)
825    /// * `isolated_symbol` - Isolated margin symbol (optional)
826    /// * `start_time` - Start timestamp (optional)
827    /// * `end_time` - End timestamp (optional)
828    /// * `current` - Page number (default 1)
829    /// * `size` - Page size (default 10, max 100)
830    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, &params_ref)
863            .await
864    }
865
866    /// Get interest rate history.
867    ///
868    /// # Arguments
869    ///
870    /// * `asset` - Asset to query
871    /// * `vip_level` - VIP level (optional, default uses user's vip level)
872    /// * `start_time` - Start timestamp (optional)
873    /// * `end_time` - End timestamp (optional)
874    /// * `limit` - Number of records (default 20, max 100)
875    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, &params_ref)
901            .await
902    }
903
904    // Market Data.
905
906    /// Get cross margin pair details.
907    ///
908    /// # Arguments
909    ///
910    /// * `symbol` - Trading pair symbol
911    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, &params_ref)
916            .await
917    }
918
919    /// Get all cross margin pairs.
920    pub async fn all_pairs(&self) -> Result<Vec<MarginPairDetails>> {
921        self.client.get_signed(SAPI_V1_MARGIN_ALL_PAIRS, &[]).await
922    }
923
924    /// Get margin asset info.
925    ///
926    /// # Arguments
927    ///
928    /// * `asset` - Asset symbol
929    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, &params_ref)
934            .await
935    }
936
937    /// Get all margin assets info.
938    pub async fn all_assets(&self) -> Result<Vec<MarginAssetInfo>> {
939        self.client.get_signed(SAPI_V1_MARGIN_ALL_ASSETS, &[]).await
940    }
941
942    /// Get margin price index for a symbol.
943    ///
944    /// # Arguments
945    ///
946    /// * `symbol` - Trading pair symbol
947    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, &params_ref)
952            .await
953    }
954
955    // BNB Burn.
956
957    /// Get BNB burn status for spot trading and margin interest.
958    pub async fn bnb_burn_status(&self) -> Result<BnbBurnStatus> {
959        self.client.get_signed(SAPI_V1_BNB_BURN, &[]).await
960    }
961
962    /// Toggle BNB burn on spot trade and margin interest.
963    ///
964    /// # Arguments
965    ///
966    /// * `spot_bnb_burn` - Enable BNB for spot trading fees (optional)
967    /// * `interest_bnb_burn` - Enable BNB for margin interest (optional)
968    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, &params_ref).await
984    }
985}