1use crate::{
2 Error, Timestamp,
3 crypto::{SensitiveString, Signer},
4 enums::Category,
5 http::{
6 APIErrorResponse, APIKeyInformation, AccountCoinBalance, AccountInfo,
7 AmendOrderBatchRequest, AmendOrderBatchResult, AmendOrderRequest, AmendOrderResponse,
8 AssetInfoResult, BorrowHistoryEntry, CancelAllOrdersRequest, CancelAllOrdersResponse,
9 CancelOrderBatchRequest, CancelOrderBatchResult, CancelOrderRequest, CancelOrderResponse,
10 CancelWithdrawalRequest, CancelWithdrawalResult, ClosedPnl, CoinGreeks, CoinInfoResult,
11 CollateralInfoEntry, CursorPagination, DeliveryPrice, DeliveryRecord, DepositAddress,
12 DepositAllowedCoinInfoResult, DepositRecords, EmptyResult, ExchangeOrderRecords,
13 ExecutionEntry, FeeRateEntry, FundingRateHistory, GetAccountCoinBalanceParams,
14 GetAssetInfoParams, GetBorrowHistoryParams, GetClosedPnlParams, GetCoinGreeksParams,
15 GetCoinInfoParams, GetCollateralInfoParams, GetDeliveryPriceParams,
16 GetDeliveryRecordParams, GetDepositAddressParams, GetDepositAllowedCoinInfoParams,
17 GetDepositRecordsParams, GetExchangeOrderRecordParams, GetExecutionListParams,
18 GetFeeRateParams, GetFundingRateHistoryParams, GetHistoricalVolatilityParams,
19 GetInstrumentsInfoParams, GetInsuranceParams, GetInternalTransferRecordsParams,
20 GetKLinesParams, GetLeverageTokenInfoParams, GetLeverageTokenMarketParams,
21 GetLeverageTokenOrderRecordsParams, GetOpenClosedOrdersParams, GetOpenInterestParams,
22 GetOrderHistoryParams, GetOrderbookParams, GetPositionInfoParams, GetRiskLimitParams,
23 GetSettlementRecordParams, GetSpotBorrowCheckParams, GetSubDepositAddressParams,
24 GetSubDepositRecordsParams, GetTickersParams, GetTradesParams, GetTransactionLogParams,
25 GetTransferableCoinsParams, GetUniversalTransferRecordsParams, GetWalletBalanceParams,
26 GetWithdrawableAmountParams, GetWithdrawalRecordsParams, Headers,
27 HistoricalVolatilityEntry, InstrumentsInfo, Insurance, InternalTransferEntry,
28 InternalTransferRequest, KLine, LeverageTokenInfo, LeverageTokenMarket,
29 LeverageTokenOrderRecord, List, OpenInterest, Order, Orderbook, PlaceOrderBatchRequest,
30 PlaceOrderBatchResult, PlaceOrderRequest, PlaceOrderResponse, Position,
31 PurchaseLeverageTokenRequest, PurchaseLeverageTokenResult, RedeemLeverageTokenRequest,
32 RedeemLeverageTokenResult, Resp, Response, RiskLimit, SaveTransferSubMemberRequest,
33 ServerTime, SetAutoAddMarginRequest, SetLeverageRequest, SetMarginModeRequest,
34 SetMarginModeResponse, SetRiskLimitRequest, SetRiskLimitResponse,
35 SetSpotMarginLeverageRequest, SetTradingStopRequest, SettlementRecord, SpotBorrowCheck,
36 SwitchCrossIsolatedMarginRequest, SwitchPositionModeRequest, SwitchSpotMarginModeRequest,
37 SwitchSpotMarginModeResult, Ticker, Trade, TransactionLog, TransferResult,
38 TransferableSubMembers, UniversalTransferEntry, UniversalTransferRequest,
39 UpgradeToUtaResult, WalletBalance, WithdrawRecords, WithdrawRequest, WithdrawResult,
40 WithdrawableAmountResult,
41 },
42 serde::{deserialize_json, serialize_json, serialize_query},
43 url::*,
44};
45use reqwest::{self, Method, RequestBuilder, header::HeaderMap};
46
47use super::rate_limiter::{RateLimitKey, RateLimiter, RateLimiterConfig};
48
49pub struct Config {
50 pub base_url: String,
51 pub api_key: Option<SensitiveString>,
52 pub api_secret: Option<SensitiveString>,
53 pub recv_window: Timestamp,
55 pub referer: Option<String>,
57 pub rate_limiter: Option<RateLimiterConfig>,
61}
62
63#[derive(Debug)]
65pub struct Client {
66 base_url: String,
67 headers: HeaderMap,
68 client: reqwest::Client,
69 signer: Option<Signer>,
70 rate_limiter: Option<RateLimiter>,
71}
72
73impl Client {
74 pub fn new(cfg: Config) -> Result<Self, Error> {
75 let mut headers = HeaderMap::new();
76
77 if let Some(api_key) = cfg.api_key.as_ref() {
78 let api_key = api_key.expose().parse()?;
79 headers.append(HEADER_X_BAPI_API_KEY, api_key);
80 }
81
82 let recv_window = cfg.recv_window.to_string().parse()?;
83 headers.append(HEADER_X_BAPI_RECV_WINDOW, recv_window);
84
85 if let Some(referer) = cfg.referer {
86 let referer = referer.parse()?;
87 headers.append(HEADER_X_REFERER, referer);
88 }
89
90 let signer = cfg
91 .api_secret
92 .map(|api_secret| -> Result<Signer, Error> {
93 let api_key = cfg
94 .api_key
95 .ok_or_else(|| Error::from("api_key is required when api_secret is set"))?;
96 Ok(Signer::new(api_key, api_secret, cfg.recv_window, None))
97 })
98 .transpose()?;
99
100 let rate_limiter = cfg.rate_limiter.map(RateLimiter::new);
101
102 Ok(Self {
103 base_url: cfg.base_url,
104 headers,
105 client: reqwest::Client::builder().build()?,
106 signer,
107 rate_limiter,
108 })
109 }
110
111 fn get_signed_headers(&self, s: &str) -> HeaderMap {
112 let mut headers = self.headers.clone();
113
114 let (signature, timestamp) = self.signer.as_ref().unwrap().sign(s);
115 let signature = signature.parse().unwrap();
116 headers.append(HEADER_X_BAPI_SIGN, signature);
117 let timestamp = timestamp.parse().unwrap();
118 headers.append(HEADER_X_BAPI_TIMESTAMP, timestamp);
119
120 headers
121 }
122}
123
124impl Client {
126 #[tracing::instrument(skip(self), err)]
127 pub async fn get_server_time(&self) -> Result<Response<ServerTime>, Error> {
128 let url = format!("{}{}", self.base_url, Path::MarketServerTime);
129
130 let request = self.client.request(Method::GET, url);
131
132 let response = self.send(Path::MarketServerTime, request).await?;
133 Ok(response)
134 }
135
136 #[tracing::instrument(skip(self), err)]
137 pub async fn get_kline(&self, params: &GetKLinesParams) -> Result<Response<KLine>, Error> {
138 let url = format!("{}{}", self.base_url, Path::MarketKline);
139
140 let request = self.client.request(Method::GET, url).query(params);
141
142 let response = self.send(Path::MarketKline, request).await?;
143 Ok(response)
144 }
145
146 #[tracing::instrument(skip(self), err)]
151 pub async fn get_mark_price_kline(
152 &self,
153 params: &GetKLinesParams,
154 ) -> Result<Response<KLine>, Error> {
155 let url = format!("{}{}", self.base_url, Path::MarketMarkPriceKline);
156
157 let request = self.client.request(Method::GET, url).query(params);
158
159 let response = self.send(Path::MarketMarkPriceKline, request).await?;
160 Ok(response)
161 }
162
163 #[tracing::instrument(skip(self), err)]
168 pub async fn get_index_price_kline(
169 &self,
170 params: &GetKLinesParams,
171 ) -> Result<Response<KLine>, Error> {
172 let url = format!("{}{}", self.base_url, Path::MarketIndexPriceKline);
173
174 let request = self.client.request(Method::GET, url).query(params);
175
176 let response = self.send(Path::MarketIndexPriceKline, request).await?;
177 Ok(response)
178 }
179
180 #[tracing::instrument(skip(self), err)]
185 pub async fn get_premium_index_price_kline(
186 &self,
187 params: &GetKLinesParams,
188 ) -> Result<Response<KLine>, Error> {
189 let url = format!("{}{}", self.base_url, Path::MarketPremiumIndexPriceKline);
190
191 let request = self.client.request(Method::GET, url).query(params);
192
193 let response = self
194 .send(Path::MarketPremiumIndexPriceKline, request)
195 .await?;
196 Ok(response)
197 }
198
199 #[tracing::instrument(skip(self), err)]
203 pub async fn get_tickers(&self, params: &GetTickersParams) -> Result<Response<Ticker>, Error> {
204 let url = format!("{}{}", self.base_url, Path::MarketTickers);
205
206 let request = self.client.request(Method::GET, url).query(params);
207
208 let response = self.send(Path::MarketTickers, request).await?;
209 Ok(response)
210 }
211
212 #[tracing::instrument(skip(self), err)]
217 pub async fn get_orderbook(
218 &self,
219 params: &GetOrderbookParams,
220 ) -> Result<Response<Orderbook>, Error> {
221 let url = format!("{}{}", self.base_url, Path::MarketOrderbook);
222
223 let request = self.client.request(Method::GET, url).query(params);
224
225 let response = self.send(Path::MarketOrderbook, request).await?;
226 Ok(response)
227 }
228
229 #[tracing::instrument(skip(self), err)]
230 pub async fn get_instruments_info(
231 &self,
232 params: &GetInstrumentsInfoParams,
233 ) -> Result<Response<InstrumentsInfo>, Error> {
234 let url = format!("{}{}", self.base_url, Path::MarketInstrumentsInfo);
235
236 let request = self.client.request(Method::GET, url).query(params);
237
238 let response = self.send(Path::MarketInstrumentsInfo, request).await?;
239 Ok(response)
240 }
241
242 #[tracing::instrument(skip(self), err)]
243 pub async fn get_public_recent_trading_history(
244 &self,
245 params: &GetTradesParams,
246 ) -> Result<Response<Trade>, Error> {
247 let url = format!("{}{}", self.base_url, Path::MarketRecentTrade);
248
249 let request = self.client.request(Method::GET, url).query(params);
250
251 let response = self.send(Path::MarketRecentTrade, request).await?;
252 Ok(response)
253 }
254
255 #[tracing::instrument(skip(self), err)]
260 pub async fn get_funding_rate_history(
261 &self,
262 params: &GetFundingRateHistoryParams,
263 ) -> Result<Response<FundingRateHistory>, Error> {
264 let url = format!("{}{}", self.base_url, Path::MarketFundingHistory);
265
266 let request = self.client.request(Method::GET, url).query(params);
267
268 let response = self.send(Path::MarketFundingHistory, request).await?;
269 Ok(response)
270 }
271
272 #[tracing::instrument(skip(self), err)]
277 pub async fn get_open_interest(
278 &self,
279 params: &GetOpenInterestParams,
280 ) -> Result<Response<OpenInterest>, Error> {
281 let url = format!("{}{}", self.base_url, Path::MarketOpenInterest);
282
283 let request = self.client.request(Method::GET, url).query(params);
284
285 let response = self.send(Path::MarketOpenInterest, request).await?;
286 Ok(response)
287 }
288
289 #[tracing::instrument(skip(self), err)]
297 pub async fn get_historical_volatility(
298 &self,
299 params: &GetHistoricalVolatilityParams,
300 ) -> Result<Response<Vec<HistoricalVolatilityEntry>>, Error> {
301 let url = format!("{}{}", self.base_url, Path::MarketHistoricalVolatility);
302
303 let request = self.client.request(Method::GET, url).query(params);
304
305 let response = self.send(Path::MarketHistoricalVolatility, request).await?;
306 Ok(response)
307 }
308
309 #[tracing::instrument(skip(self), err)]
312 pub async fn get_insurance(
313 &self,
314 params: &GetInsuranceParams,
315 ) -> Result<Response<Insurance>, Error> {
316 let url = format!("{}{}", self.base_url, Path::MarketInsurance);
317
318 let request = self.client.request(Method::GET, url).query(params);
319
320 let response = self.send(Path::MarketInsurance, request).await?;
321 Ok(response)
322 }
323
324 #[tracing::instrument(skip(self), err)]
329 pub async fn get_risk_limit(
330 &self,
331 params: &GetRiskLimitParams,
332 ) -> Result<Response<RiskLimit>, Error> {
333 let url = format!("{}{}", self.base_url, Path::MarketRiskLimit);
334
335 let request = self.client.request(Method::GET, url).query(params);
336
337 let response = self.send(Path::MarketRiskLimit, request).await?;
338 Ok(response)
339 }
340
341 #[tracing::instrument(skip(self), err)]
346 pub async fn get_delivery_price(
347 &self,
348 params: &GetDeliveryPriceParams,
349 ) -> Result<Response<DeliveryPrice>, Error> {
350 let url = format!("{}{}", self.base_url, Path::MarketDeliveryPrice);
351
352 let request = self.client.request(Method::GET, url).query(params);
353
354 let response = self.send(Path::MarketDeliveryPrice, request).await?;
355 Ok(response)
356 }
357}
358
359impl Client {
361 #[tracing::instrument(skip(self), err)]
408 pub async fn place_order(
409 &self,
410 request: &PlaceOrderRequest,
411 ) -> Result<Response<PlaceOrderResponse>, Error> {
412 let category = request.category;
413 let url = format!("{}{}", self.base_url, Path::TradeOrderCreate);
414 let json = serialize_json(request)?;
415 let headers = self.get_signed_headers(&json);
416
417 let request = self
418 .client
419 .request(Method::POST, url)
420 .headers(headers)
421 .body(json);
422
423 let response = self
424 .send_weighted(Path::TradeOrderCreate, Some(category), 1, request)
425 .await?;
426 Ok(response)
427 }
428
429 #[tracing::instrument(skip(self), err)]
433 pub async fn amend_order(
434 &self,
435 request: &AmendOrderRequest,
436 ) -> Result<Response<AmendOrderResponse>, Error> {
437 let category = request.category;
438 let url = format!("{}{}", self.base_url, Path::TradeOrderAmend);
439 let json = serialize_json(request)?;
440 let headers = self.get_signed_headers(&json);
441
442 let request = self
443 .client
444 .request(Method::POST, url)
445 .headers(headers)
446 .body(json);
447
448 let response = self
449 .send_weighted(Path::TradeOrderAmend, Some(category), 1, request)
450 .await?;
451 Ok(response)
452 }
453
454 #[tracing::instrument(skip(self), err)]
460 pub async fn cancel_order(
461 &self,
462 request: &CancelOrderRequest,
463 ) -> Result<Response<CancelOrderResponse>, Error> {
464 let category = request.category;
465 let url = format!("{}{}", self.base_url, Path::TradeOrderCancel);
466 let json = serialize_json(request)?;
467 let headers = self.get_signed_headers(&json);
468
469 let request = self
470 .client
471 .request(Method::POST, url)
472 .headers(headers)
473 .body(json);
474
475 let response = self
476 .send_weighted(Path::TradeOrderCancel, Some(category), 1, request)
477 .await?;
478 Ok(response)
479 }
480
481 #[tracing::instrument(skip(self), err)]
495 pub async fn get_open_closed_orders(
496 &self,
497 params: &GetOpenClosedOrdersParams,
498 ) -> Result<Response<CursorPagination<Order>>, Error> {
499 let query = serialize_query(params)?;
500 let url = format!("{}{}?{query}", self.base_url, Path::TradeOrderRealtime);
501 let headers = self.get_signed_headers(&query);
502
503 let request = self.client.request(Method::GET, url).headers(headers);
504
505 let response = self.send(Path::TradeOrderRealtime, request).await?;
506 Ok(response)
507 }
508
509 #[tracing::instrument(skip(self), err)]
513 pub async fn get_open_closed_orders_all(
514 &self,
515 params: &GetOpenClosedOrdersParams,
516 ) -> Result<Vec<Order>, Error> {
517 let mut all = Vec::new();
518 let mut p = params.clone();
519 loop {
520 let page = self.get_open_closed_orders(&p).await?;
521 all.extend(page.result.list);
522 match page.result.next_page_cursor {
523 Some(cursor) => p = p.with_cursor(cursor),
524 None => break,
525 }
526 }
527 Ok(all)
528 }
529
530 #[tracing::instrument(skip(self), err)]
533 pub async fn cancel_all_orders(
534 &self,
535 request: &CancelAllOrdersRequest,
536 ) -> Result<Response<CancelAllOrdersResponse>, Error> {
537 let category = request.category;
538 let url = format!("{}{}", self.base_url, Path::TradeOrderCancelAll);
539 let json = serialize_json(request)?;
540 let headers = self.get_signed_headers(&json);
541
542 let request = self
543 .client
544 .request(Method::POST, url)
545 .headers(headers)
546 .body(json);
547
548 let response = self
549 .send_weighted(Path::TradeOrderCancelAll, Some(category), 1, request)
550 .await?;
551 Ok(response)
552 }
553
554 #[tracing::instrument(skip(self), err)]
558 pub async fn get_order_history(
559 &self,
560 params: &GetOrderHistoryParams,
561 ) -> Result<Response<CursorPagination<Order>>, Error> {
562 let query = serialize_query(params)?;
563 let url = format!("{}{}?{query}", self.base_url, Path::TradeOrderHistory);
564 let headers = self.get_signed_headers(&query);
565
566 let request = self.client.request(Method::GET, url).headers(headers);
567
568 let response = self.send(Path::TradeOrderHistory, request).await?;
569 Ok(response)
570 }
571
572 #[tracing::instrument(skip(self), err)]
574 pub async fn get_order_history_all(
575 &self,
576 params: &GetOrderHistoryParams,
577 ) -> Result<Vec<Order>, Error> {
578 let mut all = Vec::new();
579 let mut p = params.clone();
580 loop {
581 let page = self.get_order_history(&p).await?;
582 all.extend(page.result.list);
583 match page.result.next_page_cursor {
584 Some(cursor) => p = p.with_cursor(cursor),
585 None => break,
586 }
587 }
588 Ok(all)
589 }
590
591 #[tracing::instrument(skip(self), err)]
595 pub async fn place_orders_batch(
596 &self,
597 request: &PlaceOrderBatchRequest,
598 ) -> Result<Response<List<PlaceOrderBatchResult>>, Error> {
599 let category = request.category;
600 let cost = request.request.len() as u32;
601 let url = format!("{}{}", self.base_url, Path::TradeOrderCreateBatch);
602 let json = serialize_json(request)?;
603 let headers = self.get_signed_headers(&json);
604
605 let request = self
606 .client
607 .request(Method::POST, url)
608 .headers(headers)
609 .body(json);
610
611 let response = self
612 .send_weighted(Path::TradeOrderCreateBatch, Some(category), cost, request)
613 .await?;
614 Ok(response)
615 }
616
617 #[tracing::instrument(skip(self), err)]
621 pub async fn amend_orders_batch(
622 &self,
623 request: &AmendOrderBatchRequest,
624 ) -> Result<Response<List<AmendOrderBatchResult>>, Error> {
625 let category = request.category;
626 let cost = request.request.len() as u32;
627 let url = format!("{}{}", self.base_url, Path::TradeOrderAmendBatch);
628 let json = serialize_json(request)?;
629 let headers = self.get_signed_headers(&json);
630
631 let request = self
632 .client
633 .request(Method::POST, url)
634 .headers(headers)
635 .body(json);
636
637 let response = self
638 .send_weighted(Path::TradeOrderAmendBatch, Some(category), cost, request)
639 .await?;
640 Ok(response)
641 }
642
643 #[tracing::instrument(skip(self), err)]
647 pub async fn cancel_orders_batch(
648 &self,
649 request: &CancelOrderBatchRequest,
650 ) -> Result<Response<List<CancelOrderBatchResult>>, Error> {
651 let category = request.category;
652 let cost = request.request.len() as u32;
653 let url = format!("{}{}", self.base_url, Path::TradeOrderCancelBatch);
654 let json = serialize_json(request)?;
655 let headers = self.get_signed_headers(&json);
656
657 let request = self
658 .client
659 .request(Method::POST, url)
660 .headers(headers)
661 .body(json);
662
663 let response = self
664 .send_weighted(Path::TradeOrderCancelBatch, Some(category), cost, request)
665 .await?;
666 Ok(response)
667 }
668
669 #[tracing::instrument(skip(self), err)]
674 pub async fn get_spot_borrow_check(
675 &self,
676 params: &GetSpotBorrowCheckParams,
677 ) -> Result<Response<SpotBorrowCheck>, Error> {
678 let url = format!("{}{}", self.base_url, Path::TradeOrderSpotBorrowCheck);
679 let query = serialize_query(params)?;
680 let headers = self.get_signed_headers(&query);
681
682 let request = self
683 .client
684 .request(Method::GET, url)
685 .headers(headers)
686 .query(params);
687
688 let response = self.send(Path::TradeOrderSpotBorrowCheck, request).await?;
689 Ok(response)
690 }
691}
692
693impl Client {
695 #[tracing::instrument(skip(self), err)]
706 pub async fn get_position_info(
707 &self,
708 params: &GetPositionInfoParams,
709 ) -> Result<Response<CursorPagination<Position>>, Error> {
710 let query = serialize_query(params)?;
711 let url = format!("{}{}?{query}", self.base_url, Path::PositionList);
712 let headers = self.get_signed_headers(&query);
713
714 let request = self.client.request(Method::GET, url).headers(headers);
715
716 let response = self.send(Path::PositionList, request).await?;
717 Ok(response)
718 }
719
720 #[tracing::instrument(skip(self), err)]
724 pub async fn get_position_info_all(
725 &self,
726 params: &GetPositionInfoParams,
727 ) -> Result<Vec<Position>, Error> {
728 let mut all = Vec::new();
729 let mut p = params.clone();
730 loop {
731 let page = self.get_position_info(&p).await?;
732 all.extend(page.result.list);
733 match page.result.next_page_cursor {
734 Some(cursor) => p = p.with_cursor(cursor),
735 None => break,
736 }
737 }
738 Ok(all)
739 }
740
741 #[tracing::instrument(skip(self), err)]
744 pub async fn set_leverage(
745 &self,
746 request: &SetLeverageRequest,
747 ) -> Result<Response<EmptyResult>, Error> {
748 let url = format!("{}{}", self.base_url, Path::PositionSetLeverage);
749 let json = serialize_json(request)?;
750 let headers = self.get_signed_headers(&json);
751
752 let request = self
753 .client
754 .request(Method::POST, url)
755 .headers(headers)
756 .body(json);
757
758 let response = self.send(Path::PositionSetLeverage, request).await?;
759 Ok(response)
760 }
761
762 #[tracing::instrument(skip(self), err)]
765 pub async fn set_trading_stop(
766 &self,
767 request: &SetTradingStopRequest,
768 ) -> Result<Response<EmptyResult>, Error> {
769 let url = format!("{}{}", self.base_url, Path::PositionTradingStop);
770 let json = serialize_json(request)?;
771 let headers = self.get_signed_headers(&json);
772
773 let request = self
774 .client
775 .request(Method::POST, url)
776 .headers(headers)
777 .body(json);
778
779 let response = self.send(Path::PositionTradingStop, request).await?;
780 Ok(response)
781 }
782
783 #[tracing::instrument(skip(self), err)]
786 pub async fn switch_cross_isolated_margin(
787 &self,
788 request: &SwitchCrossIsolatedMarginRequest,
789 ) -> Result<Response<EmptyResult>, Error> {
790 let url = format!("{}{}", self.base_url, Path::PositionSwitchIsolated);
791 let json = serialize_json(request)?;
792 let headers = self.get_signed_headers(&json);
793
794 let request = self
795 .client
796 .request(Method::POST, url)
797 .headers(headers)
798 .body(json);
799
800 let response = self.send(Path::PositionSwitchIsolated, request).await?;
801 Ok(response)
802 }
803
804 #[tracing::instrument(skip(self), err)]
807 pub async fn switch_position_mode(
808 &self,
809 request: &SwitchPositionModeRequest,
810 ) -> Result<Response<EmptyResult>, Error> {
811 let url = format!("{}{}", self.base_url, Path::PositionSwitchMode);
812 let json = serialize_json(request)?;
813 let headers = self.get_signed_headers(&json);
814
815 let request = self
816 .client
817 .request(Method::POST, url)
818 .headers(headers)
819 .body(json);
820
821 let response = self.send(Path::PositionSwitchMode, request).await?;
822 Ok(response)
823 }
824
825 #[tracing::instrument(skip(self), err)]
828 pub async fn set_auto_add_margin(
829 &self,
830 request: &SetAutoAddMarginRequest,
831 ) -> Result<Response<EmptyResult>, Error> {
832 let url = format!("{}{}", self.base_url, Path::PositionSetAutoAddMargin);
833 let json = serialize_json(request)?;
834 let headers = self.get_signed_headers(&json);
835
836 let request = self
837 .client
838 .request(Method::POST, url)
839 .headers(headers)
840 .body(json);
841
842 let response = self.send(Path::PositionSetAutoAddMargin, request).await?;
843 Ok(response)
844 }
845
846 #[tracing::instrument(skip(self), err)]
849 pub async fn set_risk_limit(
850 &self,
851 request: &SetRiskLimitRequest,
852 ) -> Result<Response<SetRiskLimitResponse>, Error> {
853 let url = format!("{}{}", self.base_url, Path::PositionSetRiskLimit);
854 let json = serialize_json(request)?;
855 let headers = self.get_signed_headers(&json);
856
857 let request = self
858 .client
859 .request(Method::POST, url)
860 .headers(headers)
861 .body(json);
862
863 let response = self.send(Path::PositionSetRiskLimit, request).await?;
864 Ok(response)
865 }
866
867 #[tracing::instrument(skip(self), err)]
870 pub async fn get_closed_pnl(
871 &self,
872 params: &GetClosedPnlParams,
873 ) -> Result<Response<CursorPagination<ClosedPnl>>, Error> {
874 let query = serialize_query(params)?;
875 let url = format!("{}{}?{query}", self.base_url, Path::PositionClosedPnl);
876 let headers = self.get_signed_headers(&query);
877
878 let request = self.client.request(Method::GET, url).headers(headers);
879
880 let response = self.send(Path::PositionClosedPnl, request).await?;
881 Ok(response)
882 }
883
884 #[tracing::instrument(skip(self), err)]
886 pub async fn get_closed_pnl_all(
887 &self,
888 params: &GetClosedPnlParams,
889 ) -> Result<Vec<ClosedPnl>, Error> {
890 let mut all = Vec::new();
891 let mut p = params.clone();
892 loop {
893 let page = self.get_closed_pnl(&p).await?;
894 all.extend(page.result.list);
895 match page.result.next_page_cursor {
896 Some(cursor) => p = p.with_cursor(cursor),
897 None => break,
898 }
899 }
900 Ok(all)
901 }
902
903 #[tracing::instrument(skip(self), err)]
906 pub async fn get_execution_list(
907 &self,
908 params: &GetExecutionListParams,
909 ) -> Result<Response<CursorPagination<ExecutionEntry>>, Error> {
910 let query = serialize_query(params)?;
911 let url = format!("{}{}?{query}", self.base_url, Path::ExecutionList);
912 let headers = self.get_signed_headers(&query);
913
914 let request = self.client.request(Method::GET, url).headers(headers);
915
916 let response = self.send(Path::ExecutionList, request).await?;
917 Ok(response)
918 }
919
920 #[tracing::instrument(skip(self), err)]
922 pub async fn get_execution_list_all(
923 &self,
924 params: &GetExecutionListParams,
925 ) -> Result<Vec<ExecutionEntry>, Error> {
926 let mut all = Vec::new();
927 let mut p = params.clone();
928 loop {
929 let page = self.get_execution_list(&p).await?;
930 all.extend(page.result.list);
931 match page.result.next_page_cursor {
932 Some(cursor) => p = p.with_cursor(cursor),
933 None => break,
934 }
935 }
936 Ok(all)
937 }
938}
939
940impl Client {
942 #[tracing::instrument(skip(self), err)]
944 pub async fn get_wallet_balance(
945 &self,
946 params: &GetWalletBalanceParams,
947 ) -> Result<Response<List<WalletBalance>>, Error> {
948 let query = serialize_query(params)?;
949 let url = format!("{}{}?{query}", self.base_url, Path::AccountWalletBalance);
950 let headers = self.get_signed_headers(&query);
951
952 let request = self.client.request(Method::GET, url).headers(headers);
953
954 let response = self.send(Path::AccountWalletBalance, request).await?;
955 Ok(response)
956 }
957
958 #[tracing::instrument(skip(self), err)]
961 pub async fn get_transaction_log(
962 &self,
963 params: &GetTransactionLogParams,
964 ) -> Result<Response<CursorPagination<TransactionLog>>, Error> {
965 let query = serialize_query(params)?;
966 let url = format!("{}{}?{query}", self.base_url, Path::AccountTransactionLog);
967 let headers = self.get_signed_headers(&query);
968
969 let request = self.client.request(Method::GET, url).headers(headers);
970
971 let response = self.send(Path::AccountTransactionLog, request).await?;
972 Ok(response)
973 }
974
975 #[tracing::instrument(skip(self), err)]
979 pub async fn get_transaction_log_all(
980 &self,
981 params: &GetTransactionLogParams,
982 ) -> Result<Vec<TransactionLog>, Error> {
983 let mut all = Vec::new();
984 let mut p = params.clone();
985 loop {
986 let page = self.get_transaction_log(&p).await?;
987 all.extend(page.result.list);
988 match page.result.next_page_cursor {
989 Some(cursor) => p = p.with_cursor(cursor),
990 None => break,
991 }
992 }
993 Ok(all)
994 }
995
996 #[tracing::instrument(skip(self), err)]
998 pub async fn get_account_info(&self) -> Result<Response<AccountInfo>, Error> {
999 let url = format!("{}{}", self.base_url, Path::AccountInfo);
1000 let query = "";
1001 let headers = self.get_signed_headers(query);
1002
1003 let request = self.client.request(Method::GET, url).headers(headers);
1004
1005 let response = self.send(Path::AccountInfo, request).await?;
1006 Ok(response)
1007 }
1008
1009 #[tracing::instrument(skip(self), err)]
1014 pub async fn get_fee_rate(
1015 &self,
1016 params: &GetFeeRateParams,
1017 ) -> Result<Response<List<FeeRateEntry>>, Error> {
1018 let url = format!("{}{}", self.base_url, Path::AccountFeeRate);
1019 let query = serialize_query(params)?;
1020 let headers = self.get_signed_headers(&query);
1021
1022 let request = self
1023 .client
1024 .request(Method::GET, url)
1025 .headers(headers)
1026 .query(params);
1027
1028 let response = self.send(Path::AccountFeeRate, request).await?;
1029 Ok(response)
1030 }
1031
1032 #[tracing::instrument(skip(self), err)]
1037 pub async fn set_margin_mode(
1038 &self,
1039 request_body: &SetMarginModeRequest,
1040 ) -> Result<Response<SetMarginModeResponse>, Error> {
1041 let url = format!("{}{}", self.base_url, Path::AccountSetMarginMode);
1042 let body = serialize_json(request_body)?;
1043 let headers = self.get_signed_headers(&body);
1044
1045 let request = self
1046 .client
1047 .request(Method::POST, url)
1048 .headers(headers)
1049 .body(body);
1050
1051 let response = self.send(Path::AccountSetMarginMode, request).await?;
1052 Ok(response)
1053 }
1054
1055 #[tracing::instrument(skip(self), err)]
1061 pub async fn upgrade_to_uta(&self) -> Result<Response<UpgradeToUtaResult>, Error> {
1062 let url = format!("{}{}", self.base_url, Path::AccountUpgradeToUta);
1063 let body = "{}".to_owned();
1064 let headers = self.get_signed_headers(&body);
1065
1066 let request = self
1067 .client
1068 .request(Method::POST, url)
1069 .headers(headers)
1070 .body(body);
1071
1072 let response = self.send(Path::AccountUpgradeToUta, request).await?;
1073 Ok(response)
1074 }
1075
1076 #[tracing::instrument(skip(self), err)]
1081 pub async fn get_borrow_history(
1082 &self,
1083 params: &GetBorrowHistoryParams,
1084 ) -> Result<Response<CursorPagination<BorrowHistoryEntry>>, Error> {
1085 let query = serialize_query(params)?;
1086 let url = format!("{}{}?{query}", self.base_url, Path::AccountBorrowHistory);
1087 let headers = self.get_signed_headers(&query);
1088
1089 let request = self.client.request(Method::GET, url).headers(headers);
1090
1091 let response = self.send(Path::AccountBorrowHistory, request).await?;
1092 Ok(response)
1093 }
1094
1095 #[tracing::instrument(skip(self), err)]
1099 pub async fn get_borrow_history_all(
1100 &self,
1101 params: &GetBorrowHistoryParams,
1102 ) -> Result<Vec<BorrowHistoryEntry>, Error> {
1103 let mut all = Vec::new();
1104 let mut p = params.clone();
1105 loop {
1106 let page = self.get_borrow_history(&p).await?;
1107 all.extend(page.result.list);
1108 match page.result.next_page_cursor {
1109 Some(cursor) => p = p.with_cursor(cursor),
1110 None => break,
1111 }
1112 }
1113 Ok(all)
1114 }
1115
1116 #[tracing::instrument(skip(self), err)]
1122 pub async fn get_collateral_info(
1123 &self,
1124 params: &GetCollateralInfoParams,
1125 ) -> Result<Response<List<CollateralInfoEntry>>, Error> {
1126 let url = format!("{}{}", self.base_url, Path::AccountCollateralInfo);
1127 let query = serialize_query(params)?;
1128 let headers = self.get_signed_headers(&query);
1129
1130 let request = self
1131 .client
1132 .request(Method::GET, url)
1133 .headers(headers)
1134 .query(params);
1135
1136 let response = self.send(Path::AccountCollateralInfo, request).await?;
1137 Ok(response)
1138 }
1139}
1140
1141impl Client {
1143 #[tracing::instrument(skip(self), err)]
1146 pub async fn get_api_key_information(&self) -> Result<Response<APIKeyInformation>, Error> {
1147 let url = format!("{}{}", self.base_url, Path::UserQueryApi);
1148 let query = "";
1149 let headers = self.get_signed_headers(query);
1150
1151 let request = self.client.request(Method::GET, url).headers(headers);
1152
1153 let response = self.send(Path::UserQueryApi, request).await?;
1154 Ok(response)
1155 }
1156}
1157
1158impl Client {
1160 #[tracing::instrument(skip(self), err)]
1165 pub async fn internal_transfer(
1166 &self,
1167 request: &InternalTransferRequest,
1168 ) -> Result<Response<TransferResult>, Error> {
1169 let url = format!("{}{}", self.base_url, Path::AssetTransferInterTransfer);
1170 let body = serialize_json(request)?;
1171 let headers = self.get_signed_headers(&body);
1172
1173 let request = self
1174 .client
1175 .request(Method::POST, url)
1176 .headers(headers)
1177 .body(body);
1178
1179 let response = self.send(Path::AssetTransferInterTransfer, request).await?;
1180 Ok(response)
1181 }
1182
1183 #[tracing::instrument(skip(self), err)]
1188 pub async fn get_internal_transfer_records(
1189 &self,
1190 params: &GetInternalTransferRecordsParams,
1191 ) -> Result<Response<CursorPagination<InternalTransferEntry>>, Error> {
1192 let query = serialize_query(params)?;
1193 let url = format!(
1194 "{}{}?{query}",
1195 self.base_url,
1196 Path::AssetTransferQueryInterTransferList
1197 );
1198 let headers = self.get_signed_headers(&query);
1199
1200 let request = self.client.request(Method::GET, url).headers(headers);
1201
1202 let response = self
1203 .send(Path::AssetTransferQueryInterTransferList, request)
1204 .await?;
1205 Ok(response)
1206 }
1207
1208 #[tracing::instrument(skip(self), err)]
1214 pub async fn universal_transfer(
1215 &self,
1216 request: &UniversalTransferRequest,
1217 ) -> Result<Response<TransferResult>, Error> {
1218 let url = format!("{}{}", self.base_url, Path::AssetTransferUniversalTransfer);
1219 let body = serialize_json(request)?;
1220 let headers = self.get_signed_headers(&body);
1221
1222 let request = self
1223 .client
1224 .request(Method::POST, url)
1225 .headers(headers)
1226 .body(body);
1227
1228 let response = self
1229 .send(Path::AssetTransferUniversalTransfer, request)
1230 .await?;
1231 Ok(response)
1232 }
1233
1234 #[tracing::instrument(skip(self), err)]
1239 pub async fn get_universal_transfer_records(
1240 &self,
1241 params: &GetUniversalTransferRecordsParams,
1242 ) -> Result<Response<CursorPagination<UniversalTransferEntry>>, Error> {
1243 let query = serialize_query(params)?;
1244 let url = format!(
1245 "{}{}?{query}",
1246 self.base_url,
1247 Path::AssetTransferQueryUniversalTransferList
1248 );
1249 let headers = self.get_signed_headers(&query);
1250
1251 let request = self.client.request(Method::GET, url).headers(headers);
1252
1253 let response = self
1254 .send(Path::AssetTransferQueryUniversalTransferList, request)
1255 .await?;
1256 Ok(response)
1257 }
1258
1259 #[tracing::instrument(skip(self), err)]
1264 pub async fn get_transferable_coins(
1265 &self,
1266 params: &GetTransferableCoinsParams,
1267 ) -> Result<Response<List<String>>, Error> {
1268 let query = serialize_query(params)?;
1269 let url = format!(
1270 "{}{}?{query}",
1271 self.base_url,
1272 Path::AssetTransferQueryTransferCoinList
1273 );
1274 let headers = self.get_signed_headers(&query);
1275
1276 let request = self.client.request(Method::GET, url).headers(headers);
1277
1278 let response = self
1279 .send(Path::AssetTransferQueryTransferCoinList, request)
1280 .await?;
1281 Ok(response)
1282 }
1283
1284 #[tracing::instrument(skip(self), err)]
1290 pub async fn save_transfer_sub_member(
1291 &self,
1292 request: &SaveTransferSubMemberRequest,
1293 ) -> Result<Response<EmptyResult>, Error> {
1294 let url = format!(
1295 "{}{}",
1296 self.base_url,
1297 Path::AssetTransferSaveTransferSubMember
1298 );
1299 let body = serialize_json(request)?;
1300 let headers = self.get_signed_headers(&body);
1301
1302 let request = self
1303 .client
1304 .request(Method::POST, url)
1305 .headers(headers)
1306 .body(body);
1307
1308 let response = self
1309 .send(Path::AssetTransferSaveTransferSubMember, request)
1310 .await?;
1311 Ok(response)
1312 }
1313
1314 #[tracing::instrument(skip(self), err)]
1320 pub async fn get_transferable_sub_members(
1321 &self,
1322 ) -> Result<Response<TransferableSubMembers>, Error> {
1323 let url = format!("{}{}", self.base_url, Path::AssetTransferQuerySubMemberList);
1324 let query = "";
1325 let headers = self.get_signed_headers(query);
1326
1327 let request = self.client.request(Method::GET, url).headers(headers);
1328
1329 let response = self
1330 .send(Path::AssetTransferQuerySubMemberList, request)
1331 .await?;
1332 Ok(response)
1333 }
1334
1335 #[tracing::instrument(skip(self), err)]
1341 pub async fn get_account_coin_balance(
1342 &self,
1343 params: &GetAccountCoinBalanceParams,
1344 ) -> Result<Response<AccountCoinBalance>, Error> {
1345 let query = serialize_query(params)?;
1346 let url = format!(
1347 "{}{}?{query}",
1348 self.base_url,
1349 Path::AssetTransferQueryAccountCoinBalance
1350 );
1351 let headers = self.get_signed_headers(&query);
1352
1353 let request = self.client.request(Method::GET, url).headers(headers);
1354
1355 let response = self
1356 .send(Path::AssetTransferQueryAccountCoinBalance, request)
1357 .await?;
1358 Ok(response)
1359 }
1360
1361 #[tracing::instrument(skip(self), err)]
1366 pub async fn get_asset_info(
1367 &self,
1368 params: &GetAssetInfoParams,
1369 ) -> Result<Response<AssetInfoResult>, Error> {
1370 let query = serialize_query(params)?;
1371 let url = format!(
1372 "{}{}?{query}",
1373 self.base_url,
1374 Path::AssetTransferQueryAssetInfo
1375 );
1376 let headers = self.get_signed_headers(&query);
1377
1378 let request = self.client.request(Method::GET, url).headers(headers);
1379
1380 let response = self
1381 .send(Path::AssetTransferQueryAssetInfo, request)
1382 .await?;
1383 Ok(response)
1384 }
1385
1386 #[tracing::instrument(skip(self), err)]
1390 pub async fn get_deposit_allowed_coin_info(
1391 &self,
1392 params: &GetDepositAllowedCoinInfoParams,
1393 ) -> Result<Response<DepositAllowedCoinInfoResult>, Error> {
1394 let query = serialize_query(params)?;
1395 let url = format!(
1396 "{}{}?{query}",
1397 self.base_url,
1398 Path::AssetDepositQueryAllowedList
1399 );
1400 let headers = self.get_signed_headers(&query);
1401
1402 let request = self.client.request(Method::GET, url).headers(headers);
1403
1404 let response = self
1405 .send(Path::AssetDepositQueryAllowedList, request)
1406 .await?;
1407 Ok(response)
1408 }
1409
1410 #[tracing::instrument(skip(self), err)]
1414 pub async fn get_deposit_records(
1415 &self,
1416 params: &GetDepositRecordsParams,
1417 ) -> Result<Response<DepositRecords>, Error> {
1418 let query = serialize_query(params)?;
1419 let url = format!("{}{}?{query}", self.base_url, Path::AssetDepositQueryRecord);
1420 let headers = self.get_signed_headers(&query);
1421
1422 let request = self.client.request(Method::GET, url).headers(headers);
1423
1424 let response = self.send(Path::AssetDepositQueryRecord, request).await?;
1425 Ok(response)
1426 }
1427
1428 #[tracing::instrument(skip(self), err)]
1432 pub async fn get_sub_deposit_records(
1433 &self,
1434 params: &GetSubDepositRecordsParams,
1435 ) -> Result<Response<DepositRecords>, Error> {
1436 let query = serialize_query(params)?;
1437 let url = format!(
1438 "{}{}?{query}",
1439 self.base_url,
1440 Path::AssetDepositQuerySubMemberRecord
1441 );
1442 let headers = self.get_signed_headers(&query);
1443
1444 let request = self.client.request(Method::GET, url).headers(headers);
1445
1446 let response = self
1447 .send(Path::AssetDepositQuerySubMemberRecord, request)
1448 .await?;
1449 Ok(response)
1450 }
1451
1452 #[tracing::instrument(skip(self), err)]
1456 pub async fn get_deposit_address(
1457 &self,
1458 params: &GetDepositAddressParams,
1459 ) -> Result<Response<DepositAddress>, Error> {
1460 let query = serialize_query(params)?;
1461 let url = format!(
1462 "{}{}?{query}",
1463 self.base_url,
1464 Path::AssetDepositQueryAddress
1465 );
1466 let headers = self.get_signed_headers(&query);
1467
1468 let request = self.client.request(Method::GET, url).headers(headers);
1469
1470 let response = self.send(Path::AssetDepositQueryAddress, request).await?;
1471 Ok(response)
1472 }
1473
1474 #[tracing::instrument(skip(self), err)]
1478 pub async fn get_sub_deposit_address(
1479 &self,
1480 params: &GetSubDepositAddressParams,
1481 ) -> Result<Response<DepositAddress>, Error> {
1482 let query = serialize_query(params)?;
1483 let url = format!(
1484 "{}{}?{query}",
1485 self.base_url,
1486 Path::AssetDepositQuerySubMemberAddress
1487 );
1488 let headers = self.get_signed_headers(&query);
1489
1490 let request = self.client.request(Method::GET, url).headers(headers);
1491
1492 let response = self
1493 .send(Path::AssetDepositQuerySubMemberAddress, request)
1494 .await?;
1495 Ok(response)
1496 }
1497
1498 #[tracing::instrument(skip(self), err)]
1502 pub async fn get_withdrawal_records(
1503 &self,
1504 params: &GetWithdrawalRecordsParams,
1505 ) -> Result<Response<WithdrawRecords>, Error> {
1506 let query = serialize_query(params)?;
1507 let url = format!(
1508 "{}{}?{query}",
1509 self.base_url,
1510 Path::AssetWithdrawQueryRecord
1511 );
1512 let headers = self.get_signed_headers(&query);
1513
1514 let request = self.client.request(Method::GET, url).headers(headers);
1515
1516 let response = self.send(Path::AssetWithdrawQueryRecord, request).await?;
1517 Ok(response)
1518 }
1519
1520 #[tracing::instrument(skip(self), err)]
1526 pub async fn get_withdrawable_amount(
1527 &self,
1528 params: &GetWithdrawableAmountParams,
1529 ) -> Result<Response<WithdrawableAmountResult>, Error> {
1530 let query = serialize_query(params)?;
1531 let url = format!(
1532 "{}{}?{query}",
1533 self.base_url,
1534 Path::AssetWithdrawWithdrawableAmount
1535 );
1536 let headers = self.get_signed_headers(&query);
1537
1538 let request = self.client.request(Method::GET, url).headers(headers);
1539
1540 let response = self
1541 .send(Path::AssetWithdrawWithdrawableAmount, request)
1542 .await?;
1543 Ok(response)
1544 }
1545
1546 #[tracing::instrument(skip(self), err)]
1551 pub async fn withdraw(
1552 &self,
1553 request: &WithdrawRequest,
1554 ) -> Result<Response<WithdrawResult>, Error> {
1555 let url = format!("{}{}", self.base_url, Path::AssetWithdrawCreate);
1556 let body = serialize_json(request)?;
1557 let headers = self.get_signed_headers(&body);
1558
1559 let request = self
1560 .client
1561 .request(Method::POST, url)
1562 .headers(headers)
1563 .body(body);
1564
1565 let response = self.send(Path::AssetWithdrawCreate, request).await?;
1566 Ok(response)
1567 }
1568
1569 #[tracing::instrument(skip(self), err)]
1574 pub async fn cancel_withdrawal(
1575 &self,
1576 request: &CancelWithdrawalRequest,
1577 ) -> Result<Response<CancelWithdrawalResult>, Error> {
1578 let url = format!("{}{}", self.base_url, Path::AssetWithdrawCancel);
1579 let body = serialize_json(request)?;
1580 let headers = self.get_signed_headers(&body);
1581
1582 let request = self
1583 .client
1584 .request(Method::POST, url)
1585 .headers(headers)
1586 .body(body);
1587
1588 let response = self.send(Path::AssetWithdrawCancel, request).await?;
1589 Ok(response)
1590 }
1591
1592 #[tracing::instrument(skip(self), err)]
1597 pub async fn get_coin_info(
1598 &self,
1599 params: &GetCoinInfoParams,
1600 ) -> Result<Response<CoinInfoResult>, Error> {
1601 let url = format!("{}{}", self.base_url, Path::AssetCoinQueryInfo);
1602 let query = serialize_query(params)?;
1603 let headers = self.get_signed_headers(&query);
1604
1605 let request = self
1606 .client
1607 .request(Method::GET, url)
1608 .headers(headers)
1609 .query(params);
1610
1611 let response = self.send(Path::AssetCoinQueryInfo, request).await?;
1612 Ok(response)
1613 }
1614
1615 #[tracing::instrument(skip(self), err)]
1619 pub async fn get_exchange_order_record(
1620 &self,
1621 params: &GetExchangeOrderRecordParams,
1622 ) -> Result<Response<ExchangeOrderRecords>, Error> {
1623 let query = serialize_query(params)?;
1624 let url = format!(
1625 "{}{}?{query}",
1626 self.base_url,
1627 Path::AssetExchangeOrderRecord
1628 );
1629 let headers = self.get_signed_headers(&query);
1630
1631 let request = self.client.request(Method::GET, url).headers(headers);
1632
1633 let response = self.send(Path::AssetExchangeOrderRecord, request).await?;
1634 Ok(response)
1635 }
1636
1637 #[tracing::instrument(skip(self), err)]
1642 pub async fn get_delivery_record(
1643 &self,
1644 params: &GetDeliveryRecordParams,
1645 ) -> Result<Response<CursorPagination<DeliveryRecord>>, Error> {
1646 let query = serialize_query(params)?;
1647 let url = format!("{}{}?{query}", self.base_url, Path::AssetDeliveryRecord);
1648 let headers = self.get_signed_headers(&query);
1649
1650 let request = self.client.request(Method::GET, url).headers(headers);
1651
1652 let response = self.send(Path::AssetDeliveryRecord, request).await?;
1653 Ok(response)
1654 }
1655
1656 #[tracing::instrument(skip(self), err)]
1661 pub async fn get_settlement_record(
1662 &self,
1663 params: &GetSettlementRecordParams,
1664 ) -> Result<Response<CursorPagination<SettlementRecord>>, Error> {
1665 let query = serialize_query(params)?;
1666 let url = format!("{}{}?{query}", self.base_url, Path::AssetSettlementRecord);
1667 let headers = self.get_signed_headers(&query);
1668
1669 let request = self.client.request(Method::GET, url).headers(headers);
1670
1671 let response = self.send(Path::AssetSettlementRecord, request).await?;
1672 Ok(response)
1673 }
1674
1675 #[tracing::instrument(skip(self), err)]
1680 pub async fn get_coin_greeks(
1681 &self,
1682 params: &GetCoinGreeksParams,
1683 ) -> Result<Response<List<CoinGreeks>>, Error> {
1684 let query = serialize_query(params)?;
1685 let url = format!("{}{}?{query}", self.base_url, Path::AssetCoinGreeks);
1686 let headers = self.get_signed_headers(&query);
1687
1688 let request = self.client.request(Method::GET, url).headers(headers);
1689
1690 let response = self.send(Path::AssetCoinGreeks, request).await?;
1691 Ok(response)
1692 }
1693}
1694
1695impl Client {
1697 #[tracing::instrument(skip(self), err)]
1702 pub async fn get_leverage_token_info(
1703 &self,
1704 params: &GetLeverageTokenInfoParams,
1705 ) -> Result<Response<List<LeverageTokenInfo>>, Error> {
1706 let url = format!("{}{}", self.base_url, Path::SpotLeverTokenInfo);
1707
1708 let request = self.client.request(Method::GET, url).query(params);
1709
1710 let response = self.send(Path::SpotLeverTokenInfo, request).await?;
1711 Ok(response)
1712 }
1713
1714 #[tracing::instrument(skip(self), err)]
1719 pub async fn get_leverage_token_market(
1720 &self,
1721 params: &GetLeverageTokenMarketParams,
1722 ) -> Result<Response<LeverageTokenMarket>, Error> {
1723 let url = format!("{}{}", self.base_url, Path::SpotLeverTokenReference);
1724
1725 let request = self.client.request(Method::GET, url).query(params);
1726
1727 let response = self.send(Path::SpotLeverTokenReference, request).await?;
1728 Ok(response)
1729 }
1730
1731 #[tracing::instrument(skip(self), err)]
1736 pub async fn purchase_leverage_token(
1737 &self,
1738 request: &PurchaseLeverageTokenRequest,
1739 ) -> Result<Response<PurchaseLeverageTokenResult>, Error> {
1740 let url = format!("{}{}", self.base_url, Path::SpotLeverTokenPurchase);
1741 let body = serialize_json(request)?;
1742 let headers = self.get_signed_headers(&body);
1743
1744 let request = self
1745 .client
1746 .request(Method::POST, url)
1747 .headers(headers)
1748 .body(body);
1749
1750 let response = self.send(Path::SpotLeverTokenPurchase, request).await?;
1751 Ok(response)
1752 }
1753
1754 #[tracing::instrument(skip(self), err)]
1759 pub async fn redeem_leverage_token(
1760 &self,
1761 request: &RedeemLeverageTokenRequest,
1762 ) -> Result<Response<RedeemLeverageTokenResult>, Error> {
1763 let url = format!("{}{}", self.base_url, Path::SpotLeverTokenRedeem);
1764 let body = serialize_json(request)?;
1765 let headers = self.get_signed_headers(&body);
1766
1767 let request = self
1768 .client
1769 .request(Method::POST, url)
1770 .headers(headers)
1771 .body(body);
1772
1773 let response = self.send(Path::SpotLeverTokenRedeem, request).await?;
1774 Ok(response)
1775 }
1776
1777 #[tracing::instrument(skip(self), err)]
1781 pub async fn get_leverage_token_order_records(
1782 &self,
1783 params: &GetLeverageTokenOrderRecordsParams,
1784 ) -> Result<Response<List<LeverageTokenOrderRecord>>, Error> {
1785 let query = serialize_query(params)?;
1786 let url = format!(
1787 "{}{}?{query}",
1788 self.base_url,
1789 Path::SpotLeverTokenOrderRecord
1790 );
1791 let headers = self.get_signed_headers(&query);
1792
1793 let request = self.client.request(Method::GET, url).headers(headers);
1794
1795 let response = self.send(Path::SpotLeverTokenOrderRecord, request).await?;
1796 Ok(response)
1797 }
1798}
1799
1800impl Client {
1802 #[tracing::instrument(skip(self), err)]
1808 pub async fn switch_spot_margin_mode(
1809 &self,
1810 request: &SwitchSpotMarginModeRequest,
1811 ) -> Result<Response<SwitchSpotMarginModeResult>, Error> {
1812 let url = format!("{}{}", self.base_url, Path::SpotMarginTradeSwitchMode);
1813 let body = serialize_json(request)?;
1814 let headers = self.get_signed_headers(&body);
1815
1816 let request = self
1817 .client
1818 .request(Method::POST, url)
1819 .headers(headers)
1820 .body(body);
1821
1822 let response = self.send(Path::SpotMarginTradeSwitchMode, request).await?;
1823 Ok(response)
1824 }
1825
1826 #[tracing::instrument(skip(self), err)]
1832 pub async fn set_spot_margin_leverage(
1833 &self,
1834 request: &SetSpotMarginLeverageRequest,
1835 ) -> Result<Response<EmptyResult>, Error> {
1836 let url = format!("{}{}", self.base_url, Path::SpotMarginTradeSetLeverage);
1837 let body = serialize_json(request)?;
1838 let headers = self.get_signed_headers(&body);
1839
1840 let request = self
1841 .client
1842 .request(Method::POST, url)
1843 .headers(headers)
1844 .body(body);
1845
1846 let response = self.send(Path::SpotMarginTradeSetLeverage, request).await?;
1847 Ok(response)
1848 }
1849}
1850
1851impl Client {
1852 async fn send<T>(&self, path: Path, request: RequestBuilder) -> Result<Response<T>, Error>
1854 where
1855 T: serde::de::DeserializeOwned,
1856 {
1857 self.send_weighted(path, None, 1, request).await
1858 }
1859
1860 async fn send_weighted<T>(
1864 &self,
1865 path: Path,
1866 category: Option<Category>,
1867 cost: u32,
1868 request: RequestBuilder,
1869 ) -> Result<Response<T>, Error>
1870 where
1871 T: serde::de::DeserializeOwned,
1872 {
1873 let key = RateLimitKey { path, category };
1874
1875 if let Some(rl) = &self.rate_limiter {
1876 rl.check(key, cost)
1877 .map_err(|retry_after_ms| Error::RateLimited { retry_after_ms })?;
1878 }
1879
1880 let start = std::time::Instant::now();
1881 let response = request.send().await?;
1882 let elapsed_ms = start.elapsed().as_millis();
1883 let headers = parse_headers(response.headers());
1884
1885 if let Some(rl) = &self.rate_limiter {
1886 rl.update(
1887 key,
1888 headers.api_limit_status,
1889 headers.api_limit_reset_timestamp,
1890 );
1891 }
1892
1893 let json = response.text().await?;
1894 if !headers.is_ret_code_ok() {
1895 let msg: APIErrorResponse = deserialize_json(&json)?;
1896 tracing::debug!(elapsed_ms, ret_code = msg.ret_code, "api error response");
1897 return Err(msg.into());
1898 }
1899
1900 tracing::debug!(
1901 elapsed_ms,
1902 api_limit = headers.api_limit,
1903 api_limit_status = headers.api_limit_status,
1904 "api call completed"
1905 );
1906 let response: Resp<_> = deserialize_json(&json)?;
1907 let response = Response {
1908 result: response.result,
1909 time: response.time,
1910 headers,
1911 ret_ext_info: response.ret_ext_info,
1912 };
1913 Ok(response)
1914 }
1915}
1916
1917fn parse_headers(headers: &HeaderMap) -> Headers {
1919 let ret_code = headers
1920 .get(HEADER_RET_CODE)
1921 .and_then(|h| h.to_str().unwrap_or_default().parse().ok());
1922 let trace_id = headers
1923 .get(HEADER_TRACE_ID)
1924 .and_then(|h| h.to_str().map(|str| str.into()).ok());
1925 let time_now = headers
1926 .get(HEADER_TIME_NOW)
1927 .and_then(|h| h.to_str().unwrap_or_default().parse().ok());
1928
1929 let api_limit = headers
1930 .get(HEADER_X_BAPI_LIMIT)
1931 .and_then(|h| h.to_str().unwrap_or_default().parse().ok());
1932 let api_limit_status = headers
1933 .get(HEADER_X_BAPI_LIMIT_STATUS)
1934 .and_then(|h| h.to_str().unwrap_or_default().parse().ok());
1935 let api_limit_reset_timestamp = headers
1936 .get(HEADER_X_BAPI_LIMIT_RESET_TIMESTAMP)
1937 .and_then(|h| h.to_str().unwrap_or_default().parse().ok());
1938
1939 Headers {
1940 ret_code,
1941 trace_id,
1942 time_now,
1943 api_limit,
1944 api_limit_status,
1945 api_limit_reset_timestamp,
1946 }
1947}