Skip to main content

binance_sdk/margin_trading/rest_api/apis/
borrow_repay_api.rs

1/*
2 * Binance Margin Trading REST API
3 *
4 * OpenAPI Specification for the Binance Margin Trading REST API
5 *
6 * The version of the OpenAPI document: 1.0.0
7 *
8 *
9 * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
10 * https://openapi-generator.tech
11 * Do not edit the class manually.
12 */
13
14#![allow(unused_imports)]
15use async_trait::async_trait;
16use derive_builder::Builder;
17use reqwest;
18use rust_decimal::prelude::*;
19use serde::{Deserialize, Serialize};
20use serde_json::{Value, json};
21use std::collections::BTreeMap;
22
23use crate::common::{
24    config::ConfigurationRestApi,
25    models::{ParamBuildError, RestApiResponse},
26    utils::send_request,
27};
28use crate::margin_trading::rest_api::models;
29
30const HAS_TIME_UNIT: bool = false;
31
32#[async_trait]
33pub trait BorrowRepayApi: Send + Sync {
34    async fn get_future_hourly_interest_rate(
35        &self,
36        params: GetFutureHourlyInterestRateParams,
37    ) -> anyhow::Result<RestApiResponse<Vec<models::GetFutureHourlyInterestRateResponseInner>>>;
38    async fn get_interest_history(
39        &self,
40        params: GetInterestHistoryParams,
41    ) -> anyhow::Result<RestApiResponse<models::GetInterestHistoryResponse>>;
42    async fn margin_account_borrow_repay(
43        &self,
44        params: MarginAccountBorrowRepayParams,
45    ) -> anyhow::Result<RestApiResponse<models::MarginAccountBorrowRepayResponse>>;
46    async fn query_borrow_repay_records_in_margin_account(
47        &self,
48        params: QueryBorrowRepayRecordsInMarginAccountParams,
49    ) -> anyhow::Result<RestApiResponse<models::QueryBorrowRepayRecordsInMarginAccountResponse>>;
50    async fn query_margin_interest_rate_history(
51        &self,
52        params: QueryMarginInterestRateHistoryParams,
53    ) -> anyhow::Result<RestApiResponse<Vec<models::QueryMarginInterestRateHistoryResponseInner>>>;
54    async fn query_max_borrow(
55        &self,
56        params: QueryMaxBorrowParams,
57    ) -> anyhow::Result<RestApiResponse<models::QueryMaxBorrowResponse>>;
58}
59
60#[derive(Debug, Clone)]
61pub struct BorrowRepayApiClient {
62    configuration: ConfigurationRestApi,
63}
64
65impl BorrowRepayApiClient {
66    pub fn new(configuration: ConfigurationRestApi) -> Self {
67        Self { configuration }
68    }
69}
70
71/// Request parameters for the [`get_future_hourly_interest_rate`] operation.
72///
73/// This struct holds all of the inputs you can pass when calling
74/// [`get_future_hourly_interest_rate`](#method.get_future_hourly_interest_rate).
75#[derive(Clone, Debug, Builder)]
76#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
77pub struct GetFutureHourlyInterestRateParams {
78    /// List of assets, separated by commas, up to 20
79    ///
80    /// This field is **required.
81    #[builder(setter(into))]
82    pub assets: String,
83    /// for isolated margin or not, "TRUE", "FALSE"
84    ///
85    /// This field is **required.
86    #[builder(setter(into))]
87    pub is_isolated: bool,
88}
89
90impl GetFutureHourlyInterestRateParams {
91    /// Create a builder for [`get_future_hourly_interest_rate`].
92    ///
93    /// Required parameters:
94    ///
95    /// * `assets` — List of assets, separated by commas, up to 20
96    /// * `is_isolated` — for isolated margin or not, \"TRUE\", \"FALSE\"
97    ///
98    #[must_use]
99    pub fn builder(assets: String, is_isolated: bool) -> GetFutureHourlyInterestRateParamsBuilder {
100        GetFutureHourlyInterestRateParamsBuilder::default()
101            .assets(assets)
102            .is_isolated(is_isolated)
103    }
104}
105/// Request parameters for the [`get_interest_history`] operation.
106///
107/// This struct holds all of the inputs you can pass when calling
108/// [`get_interest_history`](#method.get_interest_history).
109#[derive(Clone, Debug, Builder, Default)]
110#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
111pub struct GetInterestHistoryParams {
112    ///
113    /// The `asset` parameter.
114    ///
115    /// This field is **optional.
116    #[builder(setter(into), default)]
117    pub asset: Option<String>,
118    /// isolated symbol
119    ///
120    /// This field is **optional.
121    #[builder(setter(into), default)]
122    pub isolated_symbol: Option<String>,
123    /// Only supports querying data from the past 90 days.
124    ///
125    /// This field is **optional.
126    #[builder(setter(into), default)]
127    pub start_time: Option<i64>,
128    ///
129    /// The `end_time` parameter.
130    ///
131    /// This field is **optional.
132    #[builder(setter(into), default)]
133    pub end_time: Option<i64>,
134    /// Currently querying page. Start from 1. Default:1
135    ///
136    /// This field is **optional.
137    #[builder(setter(into), default)]
138    pub current: Option<i64>,
139    /// Default:10 Max:100
140    ///
141    /// This field is **optional.
142    #[builder(setter(into), default)]
143    pub size: Option<i64>,
144    /// No more than 60000
145    ///
146    /// This field is **optional.
147    #[builder(setter(into), default)]
148    pub recv_window: Option<i64>,
149}
150
151impl GetInterestHistoryParams {
152    /// Create a builder for [`get_interest_history`].
153    ///
154    #[must_use]
155    pub fn builder() -> GetInterestHistoryParamsBuilder {
156        GetInterestHistoryParamsBuilder::default()
157    }
158}
159/// Request parameters for the [`margin_account_borrow_repay`] operation.
160///
161/// This struct holds all of the inputs you can pass when calling
162/// [`margin_account_borrow_repay`](#method.margin_account_borrow_repay).
163#[derive(Clone, Debug, Builder)]
164#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
165pub struct MarginAccountBorrowRepayParams {
166    ///
167    /// The `asset` parameter.
168    ///
169    /// This field is **required.
170    #[builder(setter(into))]
171    pub asset: String,
172    /// `TRUE` for Isolated Margin, `FALSE` for Cross Margin, Default `FALSE`
173    ///
174    /// This field is **required.
175    #[builder(setter(into))]
176    pub is_isolated: String,
177    ///
178    /// The `symbol` parameter.
179    ///
180    /// This field is **required.
181    #[builder(setter(into))]
182    pub symbol: String,
183    ///
184    /// The `amount` parameter.
185    ///
186    /// This field is **required.
187    #[builder(setter(into))]
188    pub amount: String,
189    /// `MARGIN`,`ISOLATED`
190    ///
191    /// This field is **required.
192    #[builder(setter(into))]
193    pub r#type: String,
194    /// No more than 60000
195    ///
196    /// This field is **optional.
197    #[builder(setter(into), default)]
198    pub recv_window: Option<i64>,
199}
200
201impl MarginAccountBorrowRepayParams {
202    /// Create a builder for [`margin_account_borrow_repay`].
203    ///
204    /// Required parameters:
205    ///
206    /// * `asset` — String
207    /// * `is_isolated` — `TRUE` for Isolated Margin, `FALSE` for Cross Margin, Default `FALSE`
208    /// * `symbol` — String
209    /// * `amount` — String
210    /// * `r#type` — `MARGIN`,`ISOLATED`
211    ///
212    #[must_use]
213    pub fn builder(
214        asset: String,
215        is_isolated: String,
216        symbol: String,
217        amount: String,
218        r#type: String,
219    ) -> MarginAccountBorrowRepayParamsBuilder {
220        MarginAccountBorrowRepayParamsBuilder::default()
221            .asset(asset)
222            .is_isolated(is_isolated)
223            .symbol(symbol)
224            .amount(amount)
225            .r#type(r#type)
226    }
227}
228/// Request parameters for the [`query_borrow_repay_records_in_margin_account`] operation.
229///
230/// This struct holds all of the inputs you can pass when calling
231/// [`query_borrow_repay_records_in_margin_account`](#method.query_borrow_repay_records_in_margin_account).
232#[derive(Clone, Debug, Builder)]
233#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
234pub struct QueryBorrowRepayRecordsInMarginAccountParams {
235    /// `MARGIN`,`ISOLATED`
236    ///
237    /// This field is **required.
238    #[builder(setter(into))]
239    pub r#type: String,
240    ///
241    /// The `asset` parameter.
242    ///
243    /// This field is **optional.
244    #[builder(setter(into), default)]
245    pub asset: Option<String>,
246    /// isolated symbol
247    ///
248    /// This field is **optional.
249    #[builder(setter(into), default)]
250    pub isolated_symbol: Option<String>,
251    /// `tranId` in `POST /sapi/v1/margin/loan`
252    ///
253    /// This field is **optional.
254    #[builder(setter(into), default)]
255    pub tx_id: Option<i64>,
256    /// Only supports querying data from the past 90 days.
257    ///
258    /// This field is **optional.
259    #[builder(setter(into), default)]
260    pub start_time: Option<i64>,
261    ///
262    /// The `end_time` parameter.
263    ///
264    /// This field is **optional.
265    #[builder(setter(into), default)]
266    pub end_time: Option<i64>,
267    /// Currently querying page. Start from 1. Default:1
268    ///
269    /// This field is **optional.
270    #[builder(setter(into), default)]
271    pub current: Option<i64>,
272    /// Default:10 Max:100
273    ///
274    /// This field is **optional.
275    #[builder(setter(into), default)]
276    pub size: Option<i64>,
277    /// No more than 60000
278    ///
279    /// This field is **optional.
280    #[builder(setter(into), default)]
281    pub recv_window: Option<i64>,
282}
283
284impl QueryBorrowRepayRecordsInMarginAccountParams {
285    /// Create a builder for [`query_borrow_repay_records_in_margin_account`].
286    ///
287    /// Required parameters:
288    ///
289    /// * `r#type` — `MARGIN`,`ISOLATED`
290    ///
291    #[must_use]
292    pub fn builder(r#type: String) -> QueryBorrowRepayRecordsInMarginAccountParamsBuilder {
293        QueryBorrowRepayRecordsInMarginAccountParamsBuilder::default().r#type(r#type)
294    }
295}
296/// Request parameters for the [`query_margin_interest_rate_history`] operation.
297///
298/// This struct holds all of the inputs you can pass when calling
299/// [`query_margin_interest_rate_history`](#method.query_margin_interest_rate_history).
300#[derive(Clone, Debug, Builder)]
301#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
302pub struct QueryMarginInterestRateHistoryParams {
303    ///
304    /// The `asset` parameter.
305    ///
306    /// This field is **required.
307    #[builder(setter(into))]
308    pub asset: String,
309    /// User's current specific margin data will be returned if vipLevel is omitted
310    ///
311    /// This field is **optional.
312    #[builder(setter(into), default)]
313    pub vip_level: Option<i64>,
314    /// Only supports querying data from the past 90 days.
315    ///
316    /// This field is **optional.
317    #[builder(setter(into), default)]
318    pub start_time: Option<i64>,
319    ///
320    /// The `end_time` parameter.
321    ///
322    /// This field is **optional.
323    #[builder(setter(into), default)]
324    pub end_time: Option<i64>,
325    /// No more than 60000
326    ///
327    /// This field is **optional.
328    #[builder(setter(into), default)]
329    pub recv_window: Option<i64>,
330}
331
332impl QueryMarginInterestRateHistoryParams {
333    /// Create a builder for [`query_margin_interest_rate_history`].
334    ///
335    /// Required parameters:
336    ///
337    /// * `asset` — String
338    ///
339    #[must_use]
340    pub fn builder(asset: String) -> QueryMarginInterestRateHistoryParamsBuilder {
341        QueryMarginInterestRateHistoryParamsBuilder::default().asset(asset)
342    }
343}
344/// Request parameters for the [`query_max_borrow`] operation.
345///
346/// This struct holds all of the inputs you can pass when calling
347/// [`query_max_borrow`](#method.query_max_borrow).
348#[derive(Clone, Debug, Builder)]
349#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
350pub struct QueryMaxBorrowParams {
351    ///
352    /// The `asset` parameter.
353    ///
354    /// This field is **required.
355    #[builder(setter(into))]
356    pub asset: String,
357    /// isolated symbol
358    ///
359    /// This field is **optional.
360    #[builder(setter(into), default)]
361    pub isolated_symbol: Option<String>,
362    /// No more than 60000
363    ///
364    /// This field is **optional.
365    #[builder(setter(into), default)]
366    pub recv_window: Option<i64>,
367}
368
369impl QueryMaxBorrowParams {
370    /// Create a builder for [`query_max_borrow`].
371    ///
372    /// Required parameters:
373    ///
374    /// * `asset` — String
375    ///
376    #[must_use]
377    pub fn builder(asset: String) -> QueryMaxBorrowParamsBuilder {
378        QueryMaxBorrowParamsBuilder::default().asset(asset)
379    }
380}
381
382#[async_trait]
383impl BorrowRepayApi for BorrowRepayApiClient {
384    async fn get_future_hourly_interest_rate(
385        &self,
386        params: GetFutureHourlyInterestRateParams,
387    ) -> anyhow::Result<RestApiResponse<Vec<models::GetFutureHourlyInterestRateResponseInner>>>
388    {
389        let GetFutureHourlyInterestRateParams {
390            assets,
391            is_isolated,
392        } = params;
393
394        let mut query_params = BTreeMap::new();
395        let body_params = BTreeMap::new();
396
397        query_params.insert("assets".to_string(), json!(assets));
398
399        query_params.insert("isIsolated".to_string(), json!(is_isolated));
400
401        send_request::<Vec<models::GetFutureHourlyInterestRateResponseInner>>(
402            &self.configuration,
403            "/sapi/v1/margin/next-hourly-interest-rate",
404            reqwest::Method::GET,
405            query_params,
406            body_params,
407            if HAS_TIME_UNIT {
408                self.configuration.time_unit
409            } else {
410                None
411            },
412            true,
413        )
414        .await
415    }
416
417    async fn get_interest_history(
418        &self,
419        params: GetInterestHistoryParams,
420    ) -> anyhow::Result<RestApiResponse<models::GetInterestHistoryResponse>> {
421        let GetInterestHistoryParams {
422            asset,
423            isolated_symbol,
424            start_time,
425            end_time,
426            current,
427            size,
428            recv_window,
429        } = params;
430
431        let mut query_params = BTreeMap::new();
432        let body_params = BTreeMap::new();
433
434        if let Some(rw) = asset {
435            query_params.insert("asset".to_string(), json!(rw));
436        }
437
438        if let Some(rw) = isolated_symbol {
439            query_params.insert("isolatedSymbol".to_string(), json!(rw));
440        }
441
442        if let Some(rw) = start_time {
443            query_params.insert("startTime".to_string(), json!(rw));
444        }
445
446        if let Some(rw) = end_time {
447            query_params.insert("endTime".to_string(), json!(rw));
448        }
449
450        if let Some(rw) = current {
451            query_params.insert("current".to_string(), json!(rw));
452        }
453
454        if let Some(rw) = size {
455            query_params.insert("size".to_string(), json!(rw));
456        }
457
458        if let Some(rw) = recv_window {
459            query_params.insert("recvWindow".to_string(), json!(rw));
460        }
461
462        send_request::<models::GetInterestHistoryResponse>(
463            &self.configuration,
464            "/sapi/v1/margin/interestHistory",
465            reqwest::Method::GET,
466            query_params,
467            body_params,
468            if HAS_TIME_UNIT {
469                self.configuration.time_unit
470            } else {
471                None
472            },
473            true,
474        )
475        .await
476    }
477
478    async fn margin_account_borrow_repay(
479        &self,
480        params: MarginAccountBorrowRepayParams,
481    ) -> anyhow::Result<RestApiResponse<models::MarginAccountBorrowRepayResponse>> {
482        let MarginAccountBorrowRepayParams {
483            asset,
484            is_isolated,
485            symbol,
486            amount,
487            r#type,
488            recv_window,
489        } = params;
490
491        let mut query_params = BTreeMap::new();
492        let body_params = BTreeMap::new();
493
494        query_params.insert("asset".to_string(), json!(asset));
495
496        query_params.insert("isIsolated".to_string(), json!(is_isolated));
497
498        query_params.insert("symbol".to_string(), json!(symbol));
499
500        query_params.insert("amount".to_string(), json!(amount));
501
502        query_params.insert("type".to_string(), json!(r#type));
503
504        if let Some(rw) = recv_window {
505            query_params.insert("recvWindow".to_string(), json!(rw));
506        }
507
508        send_request::<models::MarginAccountBorrowRepayResponse>(
509            &self.configuration,
510            "/sapi/v1/margin/borrow-repay",
511            reqwest::Method::POST,
512            query_params,
513            body_params,
514            if HAS_TIME_UNIT {
515                self.configuration.time_unit
516            } else {
517                None
518            },
519            true,
520        )
521        .await
522    }
523
524    async fn query_borrow_repay_records_in_margin_account(
525        &self,
526        params: QueryBorrowRepayRecordsInMarginAccountParams,
527    ) -> anyhow::Result<RestApiResponse<models::QueryBorrowRepayRecordsInMarginAccountResponse>>
528    {
529        let QueryBorrowRepayRecordsInMarginAccountParams {
530            r#type,
531            asset,
532            isolated_symbol,
533            tx_id,
534            start_time,
535            end_time,
536            current,
537            size,
538            recv_window,
539        } = params;
540
541        let mut query_params = BTreeMap::new();
542        let body_params = BTreeMap::new();
543
544        if let Some(rw) = asset {
545            query_params.insert("asset".to_string(), json!(rw));
546        }
547
548        if let Some(rw) = isolated_symbol {
549            query_params.insert("isolatedSymbol".to_string(), json!(rw));
550        }
551
552        if let Some(rw) = tx_id {
553            query_params.insert("txId".to_string(), json!(rw));
554        }
555
556        if let Some(rw) = start_time {
557            query_params.insert("startTime".to_string(), json!(rw));
558        }
559
560        if let Some(rw) = end_time {
561            query_params.insert("endTime".to_string(), json!(rw));
562        }
563
564        if let Some(rw) = current {
565            query_params.insert("current".to_string(), json!(rw));
566        }
567
568        if let Some(rw) = size {
569            query_params.insert("size".to_string(), json!(rw));
570        }
571
572        query_params.insert("type".to_string(), json!(r#type));
573
574        if let Some(rw) = recv_window {
575            query_params.insert("recvWindow".to_string(), json!(rw));
576        }
577
578        send_request::<models::QueryBorrowRepayRecordsInMarginAccountResponse>(
579            &self.configuration,
580            "/sapi/v1/margin/borrow-repay",
581            reqwest::Method::GET,
582            query_params,
583            body_params,
584            if HAS_TIME_UNIT {
585                self.configuration.time_unit
586            } else {
587                None
588            },
589            true,
590        )
591        .await
592    }
593
594    async fn query_margin_interest_rate_history(
595        &self,
596        params: QueryMarginInterestRateHistoryParams,
597    ) -> anyhow::Result<RestApiResponse<Vec<models::QueryMarginInterestRateHistoryResponseInner>>>
598    {
599        let QueryMarginInterestRateHistoryParams {
600            asset,
601            vip_level,
602            start_time,
603            end_time,
604            recv_window,
605        } = params;
606
607        let mut query_params = BTreeMap::new();
608        let body_params = BTreeMap::new();
609
610        query_params.insert("asset".to_string(), json!(asset));
611
612        if let Some(rw) = vip_level {
613            query_params.insert("vipLevel".to_string(), json!(rw));
614        }
615
616        if let Some(rw) = start_time {
617            query_params.insert("startTime".to_string(), json!(rw));
618        }
619
620        if let Some(rw) = end_time {
621            query_params.insert("endTime".to_string(), json!(rw));
622        }
623
624        if let Some(rw) = recv_window {
625            query_params.insert("recvWindow".to_string(), json!(rw));
626        }
627
628        send_request::<Vec<models::QueryMarginInterestRateHistoryResponseInner>>(
629            &self.configuration,
630            "/sapi/v1/margin/interestRateHistory",
631            reqwest::Method::GET,
632            query_params,
633            body_params,
634            if HAS_TIME_UNIT {
635                self.configuration.time_unit
636            } else {
637                None
638            },
639            true,
640        )
641        .await
642    }
643
644    async fn query_max_borrow(
645        &self,
646        params: QueryMaxBorrowParams,
647    ) -> anyhow::Result<RestApiResponse<models::QueryMaxBorrowResponse>> {
648        let QueryMaxBorrowParams {
649            asset,
650            isolated_symbol,
651            recv_window,
652        } = params;
653
654        let mut query_params = BTreeMap::new();
655        let body_params = BTreeMap::new();
656
657        query_params.insert("asset".to_string(), json!(asset));
658
659        if let Some(rw) = isolated_symbol {
660            query_params.insert("isolatedSymbol".to_string(), json!(rw));
661        }
662
663        if let Some(rw) = recv_window {
664            query_params.insert("recvWindow".to_string(), json!(rw));
665        }
666
667        send_request::<models::QueryMaxBorrowResponse>(
668            &self.configuration,
669            "/sapi/v1/margin/maxBorrowable",
670            reqwest::Method::GET,
671            query_params,
672            body_params,
673            if HAS_TIME_UNIT {
674                self.configuration.time_unit
675            } else {
676                None
677            },
678            true,
679        )
680        .await
681    }
682}
683
684#[cfg(all(test, feature = "margin_trading"))]
685mod tests {
686    use super::*;
687    use crate::TOKIO_SHARED_RT;
688    use crate::{errors::ConnectorError, models::DataFuture, models::RestApiRateLimit};
689    use async_trait::async_trait;
690    use std::collections::HashMap;
691
692    struct DummyRestApiResponse<T> {
693        inner: Box<dyn FnOnce() -> DataFuture<Result<T, ConnectorError>> + Send + Sync>,
694        status: u16,
695        headers: HashMap<String, String>,
696        rate_limits: Option<Vec<RestApiRateLimit>>,
697    }
698
699    impl<T> From<DummyRestApiResponse<T>> for RestApiResponse<T> {
700        fn from(dummy: DummyRestApiResponse<T>) -> Self {
701            Self {
702                data_fn: dummy.inner,
703                status: dummy.status,
704                headers: dummy.headers,
705                rate_limits: dummy.rate_limits,
706            }
707        }
708    }
709
710    struct MockBorrowRepayApiClient {
711        force_error: bool,
712    }
713
714    #[async_trait]
715    impl BorrowRepayApi for MockBorrowRepayApiClient {
716        async fn get_future_hourly_interest_rate(
717            &self,
718            _params: GetFutureHourlyInterestRateParams,
719        ) -> anyhow::Result<RestApiResponse<Vec<models::GetFutureHourlyInterestRateResponseInner>>>
720        {
721            if self.force_error {
722                return Err(ConnectorError::ConnectorClientError {
723                    msg: "ResponseError".to_string(),
724                    code: None,
725                }
726                .into());
727            }
728
729            let resp_json: Value = serde_json::from_str(r#"[{"asset":"BTC","nextHourlyInterestRate":"0.00000571"},{"asset":"ETH","nextHourlyInterestRate":"0.00000578"}]"#).unwrap();
730            let dummy_response: Vec<models::GetFutureHourlyInterestRateResponseInner> =
731                serde_json::from_value(resp_json.clone()).expect(
732                    "should parse into Vec<models::GetFutureHourlyInterestRateResponseInner>",
733                );
734
735            let dummy = DummyRestApiResponse {
736                inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
737                status: 200,
738                headers: HashMap::new(),
739                rate_limits: None,
740            };
741
742            Ok(dummy.into())
743        }
744
745        async fn get_interest_history(
746            &self,
747            _params: GetInterestHistoryParams,
748        ) -> anyhow::Result<RestApiResponse<models::GetInterestHistoryResponse>> {
749            if self.force_error {
750                return Err(ConnectorError::ConnectorClientError {
751                    msg: "ResponseError".to_string(),
752                    code: None,
753                }
754                .into());
755            }
756
757            let resp_json: Value = serde_json::from_str(r#"{"rows":[{"txId":1352286576452864800,"interestAccuredTime":1672160400000,"asset":"USDT","rawAsset":"USDT","principal":"45.3313","interest":"0.00024995","interestRate":"0.00013233","type":"ON_BORROW","isolatedSymbol":"BNBUSDT"}],"total":1}"#).unwrap();
758            let dummy_response: models::GetInterestHistoryResponse =
759                serde_json::from_value(resp_json.clone())
760                    .expect("should parse into models::GetInterestHistoryResponse");
761
762            let dummy = DummyRestApiResponse {
763                inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
764                status: 200,
765                headers: HashMap::new(),
766                rate_limits: None,
767            };
768
769            Ok(dummy.into())
770        }
771
772        async fn margin_account_borrow_repay(
773            &self,
774            _params: MarginAccountBorrowRepayParams,
775        ) -> anyhow::Result<RestApiResponse<models::MarginAccountBorrowRepayResponse>> {
776            if self.force_error {
777                return Err(ConnectorError::ConnectorClientError {
778                    msg: "ResponseError".to_string(),
779                    code: None,
780                }
781                .into());
782            }
783
784            let resp_json: Value = serde_json::from_str(r#"{"tranId":100000001}"#).unwrap();
785            let dummy_response: models::MarginAccountBorrowRepayResponse =
786                serde_json::from_value(resp_json.clone())
787                    .expect("should parse into models::MarginAccountBorrowRepayResponse");
788
789            let dummy = DummyRestApiResponse {
790                inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
791                status: 200,
792                headers: HashMap::new(),
793                rate_limits: None,
794            };
795
796            Ok(dummy.into())
797        }
798
799        async fn query_borrow_repay_records_in_margin_account(
800            &self,
801            _params: QueryBorrowRepayRecordsInMarginAccountParams,
802        ) -> anyhow::Result<RestApiResponse<models::QueryBorrowRepayRecordsInMarginAccountResponse>>
803        {
804            if self.force_error {
805                return Err(ConnectorError::ConnectorClientError {
806                    msg: "ResponseError".to_string(),
807                    code: None,
808                }
809                .into());
810            }
811
812            let resp_json: Value = serde_json::from_str(r#"{"rows":[{"type":"AUTO","isolatedSymbol":"BNBUSDT","amount":"14.00000000","asset":"BNB","interest":"0.01866667","principal":"13.98133333","status":"CONFIRMED","timestamp":1563438204000,"txId":2970933056}],"total":1}"#).unwrap();
813            let dummy_response: models::QueryBorrowRepayRecordsInMarginAccountResponse =
814                serde_json::from_value(resp_json.clone()).expect(
815                    "should parse into models::QueryBorrowRepayRecordsInMarginAccountResponse",
816                );
817
818            let dummy = DummyRestApiResponse {
819                inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
820                status: 200,
821                headers: HashMap::new(),
822                rate_limits: None,
823            };
824
825            Ok(dummy.into())
826        }
827
828        async fn query_margin_interest_rate_history(
829            &self,
830            _params: QueryMarginInterestRateHistoryParams,
831        ) -> anyhow::Result<RestApiResponse<Vec<models::QueryMarginInterestRateHistoryResponseInner>>>
832        {
833            if self.force_error {
834                return Err(ConnectorError::ConnectorClientError {
835                    msg: "ResponseError".to_string(),
836                    code: None,
837                }
838                .into());
839            }
840
841            let resp_json: Value = serde_json::from_str(r#"[{"asset":"BTC","dailyInterestRate":"0.00025000","timestamp":1611544731000,"vipLevel":1},{"asset":"BTC","dailyInterestRate":"0.00035000","timestamp":1610248118000,"vipLevel":1}]"#).unwrap();
842            let dummy_response: Vec<models::QueryMarginInterestRateHistoryResponseInner> =
843                serde_json::from_value(resp_json.clone()).expect(
844                    "should parse into Vec<models::QueryMarginInterestRateHistoryResponseInner>",
845                );
846
847            let dummy = DummyRestApiResponse {
848                inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
849                status: 200,
850                headers: HashMap::new(),
851                rate_limits: None,
852            };
853
854            Ok(dummy.into())
855        }
856
857        async fn query_max_borrow(
858            &self,
859            _params: QueryMaxBorrowParams,
860        ) -> anyhow::Result<RestApiResponse<models::QueryMaxBorrowResponse>> {
861            if self.force_error {
862                return Err(ConnectorError::ConnectorClientError {
863                    msg: "ResponseError".to_string(),
864                    code: None,
865                }
866                .into());
867            }
868
869            let resp_json: Value =
870                serde_json::from_str(r#"{"amount":"1.69248805","borrowLimit":"60"}"#).unwrap();
871            let dummy_response: models::QueryMaxBorrowResponse =
872                serde_json::from_value(resp_json.clone())
873                    .expect("should parse into models::QueryMaxBorrowResponse");
874
875            let dummy = DummyRestApiResponse {
876                inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
877                status: 200,
878                headers: HashMap::new(),
879                rate_limits: None,
880            };
881
882            Ok(dummy.into())
883        }
884    }
885
886    #[test]
887    fn get_future_hourly_interest_rate_required_params_success() {
888        TOKIO_SHARED_RT.block_on(async {
889            let client = MockBorrowRepayApiClient { force_error: false };
890
891            let params = GetFutureHourlyInterestRateParams::builder("assets_example".to_string(),false).build().unwrap();
892
893            let resp_json: Value = serde_json::from_str(r#"[{"asset":"BTC","nextHourlyInterestRate":"0.00000571"},{"asset":"ETH","nextHourlyInterestRate":"0.00000578"}]"#).unwrap();
894            let expected_response : Vec<models::GetFutureHourlyInterestRateResponseInner> = serde_json::from_value(resp_json.clone()).expect("should parse into Vec<models::GetFutureHourlyInterestRateResponseInner>");
895
896            let resp = client.get_future_hourly_interest_rate(params).await.expect("Expected a response");
897            let data_future = resp.data();
898            let actual_response = data_future.await.unwrap();
899            assert_eq!(actual_response, expected_response);
900        });
901    }
902
903    #[test]
904    fn get_future_hourly_interest_rate_optional_params_success() {
905        TOKIO_SHARED_RT.block_on(async {
906            let client = MockBorrowRepayApiClient { force_error: false };
907
908            let params = GetFutureHourlyInterestRateParams::builder("assets_example".to_string(),false).build().unwrap();
909
910            let resp_json: Value = serde_json::from_str(r#"[{"asset":"BTC","nextHourlyInterestRate":"0.00000571"},{"asset":"ETH","nextHourlyInterestRate":"0.00000578"}]"#).unwrap();
911            let expected_response : Vec<models::GetFutureHourlyInterestRateResponseInner> = serde_json::from_value(resp_json.clone()).expect("should parse into Vec<models::GetFutureHourlyInterestRateResponseInner>");
912
913            let resp = client.get_future_hourly_interest_rate(params).await.expect("Expected a response");
914            let data_future = resp.data();
915            let actual_response = data_future.await.unwrap();
916            assert_eq!(actual_response, expected_response);
917        });
918    }
919
920    #[test]
921    fn get_future_hourly_interest_rate_response_error() {
922        TOKIO_SHARED_RT.block_on(async {
923            let client = MockBorrowRepayApiClient { force_error: true };
924
925            let params =
926                GetFutureHourlyInterestRateParams::builder("assets_example".to_string(), false)
927                    .build()
928                    .unwrap();
929
930            match client.get_future_hourly_interest_rate(params).await {
931                Ok(_) => panic!("Expected an error"),
932                Err(err) => {
933                    assert_eq!(err.to_string(), "Connector client error: ResponseError");
934                }
935            }
936        });
937    }
938
939    #[test]
940    fn get_interest_history_required_params_success() {
941        TOKIO_SHARED_RT.block_on(async {
942            let client = MockBorrowRepayApiClient { force_error: false };
943
944            let params = GetInterestHistoryParams::builder().build().unwrap();
945
946            let resp_json: Value = serde_json::from_str(r#"{"rows":[{"txId":1352286576452864800,"interestAccuredTime":1672160400000,"asset":"USDT","rawAsset":"USDT","principal":"45.3313","interest":"0.00024995","interestRate":"0.00013233","type":"ON_BORROW","isolatedSymbol":"BNBUSDT"}],"total":1}"#).unwrap();
947            let expected_response : models::GetInterestHistoryResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::GetInterestHistoryResponse");
948
949            let resp = client.get_interest_history(params).await.expect("Expected a response");
950            let data_future = resp.data();
951            let actual_response = data_future.await.unwrap();
952            assert_eq!(actual_response, expected_response);
953        });
954    }
955
956    #[test]
957    fn get_interest_history_optional_params_success() {
958        TOKIO_SHARED_RT.block_on(async {
959            let client = MockBorrowRepayApiClient { force_error: false };
960
961            let params = GetInterestHistoryParams::builder().asset("asset_example".to_string()).isolated_symbol("isolated_symbol_example".to_string()).start_time(1623319461670).end_time(1641782889000).current(1).size(10).recv_window(5000).build().unwrap();
962
963            let resp_json: Value = serde_json::from_str(r#"{"rows":[{"txId":1352286576452864800,"interestAccuredTime":1672160400000,"asset":"USDT","rawAsset":"USDT","principal":"45.3313","interest":"0.00024995","interestRate":"0.00013233","type":"ON_BORROW","isolatedSymbol":"BNBUSDT"}],"total":1}"#).unwrap();
964            let expected_response : models::GetInterestHistoryResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::GetInterestHistoryResponse");
965
966            let resp = client.get_interest_history(params).await.expect("Expected a response");
967            let data_future = resp.data();
968            let actual_response = data_future.await.unwrap();
969            assert_eq!(actual_response, expected_response);
970        });
971    }
972
973    #[test]
974    fn get_interest_history_response_error() {
975        TOKIO_SHARED_RT.block_on(async {
976            let client = MockBorrowRepayApiClient { force_error: true };
977
978            let params = GetInterestHistoryParams::builder().build().unwrap();
979
980            match client.get_interest_history(params).await {
981                Ok(_) => panic!("Expected an error"),
982                Err(err) => {
983                    assert_eq!(err.to_string(), "Connector client error: ResponseError");
984                }
985            }
986        });
987    }
988
989    #[test]
990    fn margin_account_borrow_repay_required_params_success() {
991        TOKIO_SHARED_RT.block_on(async {
992            let client = MockBorrowRepayApiClient { force_error: false };
993
994            let params = MarginAccountBorrowRepayParams::builder(
995                "asset_example".to_string(),
996                "FALSE".to_string(),
997                "symbol_example".to_string(),
998                "amount_example".to_string(),
999                "r#type_example".to_string(),
1000            )
1001            .build()
1002            .unwrap();
1003
1004            let resp_json: Value = serde_json::from_str(r#"{"tranId":100000001}"#).unwrap();
1005            let expected_response: models::MarginAccountBorrowRepayResponse =
1006                serde_json::from_value(resp_json.clone())
1007                    .expect("should parse into models::MarginAccountBorrowRepayResponse");
1008
1009            let resp = client
1010                .margin_account_borrow_repay(params)
1011                .await
1012                .expect("Expected a response");
1013            let data_future = resp.data();
1014            let actual_response = data_future.await.unwrap();
1015            assert_eq!(actual_response, expected_response);
1016        });
1017    }
1018
1019    #[test]
1020    fn margin_account_borrow_repay_optional_params_success() {
1021        TOKIO_SHARED_RT.block_on(async {
1022            let client = MockBorrowRepayApiClient { force_error: false };
1023
1024            let params = MarginAccountBorrowRepayParams::builder(
1025                "asset_example".to_string(),
1026                "FALSE".to_string(),
1027                "symbol_example".to_string(),
1028                "amount_example".to_string(),
1029                "r#type_example".to_string(),
1030            )
1031            .recv_window(5000)
1032            .build()
1033            .unwrap();
1034
1035            let resp_json: Value = serde_json::from_str(r#"{"tranId":100000001}"#).unwrap();
1036            let expected_response: models::MarginAccountBorrowRepayResponse =
1037                serde_json::from_value(resp_json.clone())
1038                    .expect("should parse into models::MarginAccountBorrowRepayResponse");
1039
1040            let resp = client
1041                .margin_account_borrow_repay(params)
1042                .await
1043                .expect("Expected a response");
1044            let data_future = resp.data();
1045            let actual_response = data_future.await.unwrap();
1046            assert_eq!(actual_response, expected_response);
1047        });
1048    }
1049
1050    #[test]
1051    fn margin_account_borrow_repay_response_error() {
1052        TOKIO_SHARED_RT.block_on(async {
1053            let client = MockBorrowRepayApiClient { force_error: true };
1054
1055            let params = MarginAccountBorrowRepayParams::builder(
1056                "asset_example".to_string(),
1057                "FALSE".to_string(),
1058                "symbol_example".to_string(),
1059                "amount_example".to_string(),
1060                "r#type_example".to_string(),
1061            )
1062            .build()
1063            .unwrap();
1064
1065            match client.margin_account_borrow_repay(params).await {
1066                Ok(_) => panic!("Expected an error"),
1067                Err(err) => {
1068                    assert_eq!(err.to_string(), "Connector client error: ResponseError");
1069                }
1070            }
1071        });
1072    }
1073
1074    #[test]
1075    fn query_borrow_repay_records_in_margin_account_required_params_success() {
1076        TOKIO_SHARED_RT.block_on(async {
1077            let client = MockBorrowRepayApiClient { force_error: false };
1078
1079            let params = QueryBorrowRepayRecordsInMarginAccountParams::builder("r#type_example".to_string(),).build().unwrap();
1080
1081            let resp_json: Value = serde_json::from_str(r#"{"rows":[{"type":"AUTO","isolatedSymbol":"BNBUSDT","amount":"14.00000000","asset":"BNB","interest":"0.01866667","principal":"13.98133333","status":"CONFIRMED","timestamp":1563438204000,"txId":2970933056}],"total":1}"#).unwrap();
1082            let expected_response : models::QueryBorrowRepayRecordsInMarginAccountResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::QueryBorrowRepayRecordsInMarginAccountResponse");
1083
1084            let resp = client.query_borrow_repay_records_in_margin_account(params).await.expect("Expected a response");
1085            let data_future = resp.data();
1086            let actual_response = data_future.await.unwrap();
1087            assert_eq!(actual_response, expected_response);
1088        });
1089    }
1090
1091    #[test]
1092    fn query_borrow_repay_records_in_margin_account_optional_params_success() {
1093        TOKIO_SHARED_RT.block_on(async {
1094            let client = MockBorrowRepayApiClient { force_error: false };
1095
1096            let params = QueryBorrowRepayRecordsInMarginAccountParams::builder("r#type_example".to_string(),).asset("asset_example".to_string()).isolated_symbol("isolated_symbol_example".to_string()).tx_id(1).start_time(1623319461670).end_time(1641782889000).current(1).size(10).recv_window(5000).build().unwrap();
1097
1098            let resp_json: Value = serde_json::from_str(r#"{"rows":[{"type":"AUTO","isolatedSymbol":"BNBUSDT","amount":"14.00000000","asset":"BNB","interest":"0.01866667","principal":"13.98133333","status":"CONFIRMED","timestamp":1563438204000,"txId":2970933056}],"total":1}"#).unwrap();
1099            let expected_response : models::QueryBorrowRepayRecordsInMarginAccountResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::QueryBorrowRepayRecordsInMarginAccountResponse");
1100
1101            let resp = client.query_borrow_repay_records_in_margin_account(params).await.expect("Expected a response");
1102            let data_future = resp.data();
1103            let actual_response = data_future.await.unwrap();
1104            assert_eq!(actual_response, expected_response);
1105        });
1106    }
1107
1108    #[test]
1109    fn query_borrow_repay_records_in_margin_account_response_error() {
1110        TOKIO_SHARED_RT.block_on(async {
1111            let client = MockBorrowRepayApiClient { force_error: true };
1112
1113            let params =
1114                QueryBorrowRepayRecordsInMarginAccountParams::builder("r#type_example".to_string())
1115                    .build()
1116                    .unwrap();
1117
1118            match client
1119                .query_borrow_repay_records_in_margin_account(params)
1120                .await
1121            {
1122                Ok(_) => panic!("Expected an error"),
1123                Err(err) => {
1124                    assert_eq!(err.to_string(), "Connector client error: ResponseError");
1125                }
1126            }
1127        });
1128    }
1129
1130    #[test]
1131    fn query_margin_interest_rate_history_required_params_success() {
1132        TOKIO_SHARED_RT.block_on(async {
1133            let client = MockBorrowRepayApiClient { force_error: false };
1134
1135            let params = QueryMarginInterestRateHistoryParams::builder("asset_example".to_string(),).build().unwrap();
1136
1137            let resp_json: Value = serde_json::from_str(r#"[{"asset":"BTC","dailyInterestRate":"0.00025000","timestamp":1611544731000,"vipLevel":1},{"asset":"BTC","dailyInterestRate":"0.00035000","timestamp":1610248118000,"vipLevel":1}]"#).unwrap();
1138            let expected_response : Vec<models::QueryMarginInterestRateHistoryResponseInner> = serde_json::from_value(resp_json.clone()).expect("should parse into Vec<models::QueryMarginInterestRateHistoryResponseInner>");
1139
1140            let resp = client.query_margin_interest_rate_history(params).await.expect("Expected a response");
1141            let data_future = resp.data();
1142            let actual_response = data_future.await.unwrap();
1143            assert_eq!(actual_response, expected_response);
1144        });
1145    }
1146
1147    #[test]
1148    fn query_margin_interest_rate_history_optional_params_success() {
1149        TOKIO_SHARED_RT.block_on(async {
1150            let client = MockBorrowRepayApiClient { force_error: false };
1151
1152            let params = QueryMarginInterestRateHistoryParams::builder("asset_example".to_string(),).vip_level(1).start_time(1623319461670).end_time(1641782889000).recv_window(5000).build().unwrap();
1153
1154            let resp_json: Value = serde_json::from_str(r#"[{"asset":"BTC","dailyInterestRate":"0.00025000","timestamp":1611544731000,"vipLevel":1},{"asset":"BTC","dailyInterestRate":"0.00035000","timestamp":1610248118000,"vipLevel":1}]"#).unwrap();
1155            let expected_response : Vec<models::QueryMarginInterestRateHistoryResponseInner> = serde_json::from_value(resp_json.clone()).expect("should parse into Vec<models::QueryMarginInterestRateHistoryResponseInner>");
1156
1157            let resp = client.query_margin_interest_rate_history(params).await.expect("Expected a response");
1158            let data_future = resp.data();
1159            let actual_response = data_future.await.unwrap();
1160            assert_eq!(actual_response, expected_response);
1161        });
1162    }
1163
1164    #[test]
1165    fn query_margin_interest_rate_history_response_error() {
1166        TOKIO_SHARED_RT.block_on(async {
1167            let client = MockBorrowRepayApiClient { force_error: true };
1168
1169            let params = QueryMarginInterestRateHistoryParams::builder("asset_example".to_string())
1170                .build()
1171                .unwrap();
1172
1173            match client.query_margin_interest_rate_history(params).await {
1174                Ok(_) => panic!("Expected an error"),
1175                Err(err) => {
1176                    assert_eq!(err.to_string(), "Connector client error: ResponseError");
1177                }
1178            }
1179        });
1180    }
1181
1182    #[test]
1183    fn query_max_borrow_required_params_success() {
1184        TOKIO_SHARED_RT.block_on(async {
1185            let client = MockBorrowRepayApiClient { force_error: false };
1186
1187            let params = QueryMaxBorrowParams::builder("asset_example".to_string())
1188                .build()
1189                .unwrap();
1190
1191            let resp_json: Value =
1192                serde_json::from_str(r#"{"amount":"1.69248805","borrowLimit":"60"}"#).unwrap();
1193            let expected_response: models::QueryMaxBorrowResponse =
1194                serde_json::from_value(resp_json.clone())
1195                    .expect("should parse into models::QueryMaxBorrowResponse");
1196
1197            let resp = client
1198                .query_max_borrow(params)
1199                .await
1200                .expect("Expected a response");
1201            let data_future = resp.data();
1202            let actual_response = data_future.await.unwrap();
1203            assert_eq!(actual_response, expected_response);
1204        });
1205    }
1206
1207    #[test]
1208    fn query_max_borrow_optional_params_success() {
1209        TOKIO_SHARED_RT.block_on(async {
1210            let client = MockBorrowRepayApiClient { force_error: false };
1211
1212            let params = QueryMaxBorrowParams::builder("asset_example".to_string())
1213                .isolated_symbol("isolated_symbol_example".to_string())
1214                .recv_window(5000)
1215                .build()
1216                .unwrap();
1217
1218            let resp_json: Value =
1219                serde_json::from_str(r#"{"amount":"1.69248805","borrowLimit":"60"}"#).unwrap();
1220            let expected_response: models::QueryMaxBorrowResponse =
1221                serde_json::from_value(resp_json.clone())
1222                    .expect("should parse into models::QueryMaxBorrowResponse");
1223
1224            let resp = client
1225                .query_max_borrow(params)
1226                .await
1227                .expect("Expected a response");
1228            let data_future = resp.data();
1229            let actual_response = data_future.await.unwrap();
1230            assert_eq!(actual_response, expected_response);
1231        });
1232    }
1233
1234    #[test]
1235    fn query_max_borrow_response_error() {
1236        TOKIO_SHARED_RT.block_on(async {
1237            let client = MockBorrowRepayApiClient { force_error: true };
1238
1239            let params = QueryMaxBorrowParams::builder("asset_example".to_string())
1240                .build()
1241                .unwrap();
1242
1243            match client.query_max_borrow(params).await {
1244                Ok(_) => panic!("Expected an error"),
1245                Err(err) => {
1246                    assert_eq!(err.to_string(), "Connector client error: ResponseError");
1247                }
1248            }
1249        });
1250    }
1251}