Skip to main content

binance_sdk/margin_trading/rest_api/apis/
borrow_repay_api.rs

1/*
2 * Margin REST API
3 *
4 * Access account information, borrow and repay assets, and trade with Binance Margin.
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#[allow(non_camel_case_types)]
72#[derive(Debug, Clone, Serialize, Deserialize)]
73pub enum GetFutureHourlyInterestRateIsIsolatedEnum {
74    #[serde(rename = "TRUE")]
75    True,
76    #[serde(rename = "FALSE")]
77    False,
78}
79
80impl GetFutureHourlyInterestRateIsIsolatedEnum {
81    #[must_use]
82    pub fn as_str(&self) -> &'static str {
83        match self {
84            Self::True => "TRUE",
85            Self::False => "FALSE",
86        }
87    }
88}
89
90impl std::str::FromStr for GetFutureHourlyInterestRateIsIsolatedEnum {
91    type Err = Box<dyn std::error::Error + Send + Sync>;
92
93    fn from_str(s: &str) -> Result<Self, Self::Err> {
94        match s {
95            "TRUE" => Ok(Self::True),
96            "FALSE" => Ok(Self::False),
97            other => Err(format!(
98                "invalid GetFutureHourlyInterestRateIsIsolatedEnum: {}",
99                other
100            )
101            .into()),
102        }
103    }
104}
105
106#[allow(non_camel_case_types)]
107#[derive(Debug, Clone, Serialize, Deserialize)]
108pub enum MarginAccountBorrowRepayIsIsolatedEnum {
109    #[serde(rename = "TRUE")]
110    True,
111    #[serde(rename = "FALSE")]
112    False,
113}
114
115impl MarginAccountBorrowRepayIsIsolatedEnum {
116    #[must_use]
117    pub fn as_str(&self) -> &'static str {
118        match self {
119            Self::True => "TRUE",
120            Self::False => "FALSE",
121        }
122    }
123}
124
125impl std::str::FromStr for MarginAccountBorrowRepayIsIsolatedEnum {
126    type Err = Box<dyn std::error::Error + Send + Sync>;
127
128    fn from_str(s: &str) -> Result<Self, Self::Err> {
129        match s {
130            "TRUE" => Ok(Self::True),
131            "FALSE" => Ok(Self::False),
132            other => {
133                Err(format!("invalid MarginAccountBorrowRepayIsIsolatedEnum: {}", other).into())
134            }
135        }
136    }
137}
138
139#[allow(non_camel_case_types)]
140#[derive(Debug, Clone, Serialize, Deserialize)]
141pub enum MarginAccountBorrowRepayTypeEnum {
142    #[serde(rename = "BORROW")]
143    Borrow,
144    #[serde(rename = "REPAY")]
145    Repay,
146}
147
148impl MarginAccountBorrowRepayTypeEnum {
149    #[must_use]
150    pub fn as_str(&self) -> &'static str {
151        match self {
152            Self::Borrow => "BORROW",
153            Self::Repay => "REPAY",
154        }
155    }
156}
157
158impl std::str::FromStr for MarginAccountBorrowRepayTypeEnum {
159    type Err = Box<dyn std::error::Error + Send + Sync>;
160
161    fn from_str(s: &str) -> Result<Self, Self::Err> {
162        match s {
163            "BORROW" => Ok(Self::Borrow),
164            "REPAY" => Ok(Self::Repay),
165            other => Err(format!("invalid MarginAccountBorrowRepayTypeEnum: {}", other).into()),
166        }
167    }
168}
169
170#[allow(non_camel_case_types)]
171#[derive(Debug, Clone, Serialize, Deserialize)]
172pub enum QueryBorrowRepayRecordsInMarginAccountTypeEnum {
173    #[serde(rename = "BORROW")]
174    Borrow,
175    #[serde(rename = "REPAY")]
176    Repay,
177}
178
179impl QueryBorrowRepayRecordsInMarginAccountTypeEnum {
180    #[must_use]
181    pub fn as_str(&self) -> &'static str {
182        match self {
183            Self::Borrow => "BORROW",
184            Self::Repay => "REPAY",
185        }
186    }
187}
188
189impl std::str::FromStr for QueryBorrowRepayRecordsInMarginAccountTypeEnum {
190    type Err = Box<dyn std::error::Error + Send + Sync>;
191
192    fn from_str(s: &str) -> Result<Self, Self::Err> {
193        match s {
194            "BORROW" => Ok(Self::Borrow),
195            "REPAY" => Ok(Self::Repay),
196            other => Err(format!(
197                "invalid QueryBorrowRepayRecordsInMarginAccountTypeEnum: {}",
198                other
199            )
200            .into()),
201        }
202    }
203}
204
205/// Request parameters for the [`get_future_hourly_interest_rate`] operation.
206///
207/// This struct holds all of the inputs you can pass when calling
208/// [`get_future_hourly_interest_rate`](#method.get_future_hourly_interest_rate).
209#[derive(Clone, Debug, Builder, Deserialize)]
210#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
211pub struct GetFutureHourlyInterestRateParams {
212    ///
213    /// The `assets` parameter.
214    ///
215    /// This field is **required.
216    #[builder(setter(into))]
217    #[serde(rename = "assets")]
218    pub assets: String,
219    ///
220    /// The `is_isolated` parameter.
221    ///
222    /// This field is **required.
223    #[builder(setter(into))]
224    #[serde(rename = "isIsolated")]
225    pub is_isolated: GetFutureHourlyInterestRateIsIsolatedEnum,
226}
227
228impl GetFutureHourlyInterestRateParams {
229    /// Create a builder for [`get_future_hourly_interest_rate`].
230    ///
231    /// Required parameters:
232    ///
233    /// * `assets` — String
234    /// * `is_isolated` — String
235    ///
236    #[must_use]
237    pub fn builder(
238        assets: String,
239        is_isolated: GetFutureHourlyInterestRateIsIsolatedEnum,
240    ) -> GetFutureHourlyInterestRateParamsBuilder {
241        GetFutureHourlyInterestRateParamsBuilder::default()
242            .assets(assets)
243            .is_isolated(is_isolated)
244    }
245}
246/// Request parameters for the [`get_interest_history`] operation.
247///
248/// This struct holds all of the inputs you can pass when calling
249/// [`get_interest_history`](#method.get_interest_history).
250#[derive(Clone, Debug, Builder, Deserialize, Default)]
251#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
252pub struct GetInterestHistoryParams {
253    ///
254    /// The `asset` parameter.
255    ///
256    /// This field is **optional.
257    #[builder(setter(into), default)]
258    #[serde(rename = "asset", default)]
259    pub asset: Option<String>,
260    ///
261    /// The `isolated_symbol` parameter.
262    ///
263    /// This field is **optional.
264    #[builder(setter(into), default)]
265    #[serde(rename = "isolatedSymbol", default)]
266    pub isolated_symbol: Option<String>,
267    /// Only supports querying data from the past 90 days.
268    ///
269    /// This field is **optional.
270    #[builder(setter(into), default)]
271    #[serde(rename = "startTime", default)]
272    pub start_time: Option<i64>,
273    ///
274    /// The `end_time` parameter.
275    ///
276    /// This field is **optional.
277    #[builder(setter(into), default)]
278    #[serde(rename = "endTime", default)]
279    pub end_time: Option<i64>,
280    ///
281    /// The `current` parameter.
282    ///
283    /// This field is **optional.
284    #[builder(setter(into), default)]
285    #[serde(rename = "current", default)]
286    pub current: Option<i64>,
287    ///
288    /// The `size` parameter.
289    ///
290    /// This field is **optional.
291    #[builder(setter(into), default)]
292    #[serde(rename = "size", default)]
293    pub size: Option<i64>,
294    ///
295    /// The `recv_window` parameter.
296    ///
297    /// This field is **optional.
298    #[builder(setter(into), default)]
299    #[serde(rename = "recvWindow", default)]
300    pub recv_window: Option<i64>,
301}
302
303impl GetInterestHistoryParams {
304    /// Create a builder for [`get_interest_history`].
305    ///
306    #[must_use]
307    pub fn builder() -> GetInterestHistoryParamsBuilder {
308        GetInterestHistoryParamsBuilder::default()
309    }
310}
311/// Request parameters for the [`margin_account_borrow_repay`] operation.
312///
313/// This struct holds all of the inputs you can pass when calling
314/// [`margin_account_borrow_repay`](#method.margin_account_borrow_repay).
315#[derive(Clone, Debug, Builder, Deserialize)]
316#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
317pub struct MarginAccountBorrowRepayParams {
318    ///
319    /// The `asset` parameter.
320    ///
321    /// This field is **required.
322    #[builder(setter(into))]
323    #[serde(rename = "asset")]
324    pub asset: String,
325    /// `TRUE` for Isolated Margin, `FALSE` for Cross Margin
326    ///
327    /// This field is **required.
328    #[builder(setter(into))]
329    #[serde(rename = "isIsolated")]
330    pub is_isolated: MarginAccountBorrowRepayIsIsolatedEnum,
331    ///
332    /// The `amount` parameter.
333    ///
334    /// This field is **required.
335    #[builder(setter(into))]
336    #[serde(rename = "amount")]
337    pub amount: String,
338    ///
339    /// The `r#type` parameter.
340    ///
341    /// This field is **required.
342    #[builder(setter(into))]
343    #[serde(rename = "type")]
344    pub r#type: MarginAccountBorrowRepayTypeEnum,
345    /// Only for Isolated margin
346    ///
347    /// This field is **optional.
348    #[builder(setter(into), default)]
349    #[serde(rename = "symbol", default)]
350    pub symbol: Option<String>,
351    ///
352    /// The `recv_window` parameter.
353    ///
354    /// This field is **optional.
355    #[builder(setter(into), default)]
356    #[serde(rename = "recvWindow", default)]
357    pub recv_window: Option<i64>,
358}
359
360impl MarginAccountBorrowRepayParams {
361    /// Create a builder for [`margin_account_borrow_repay`].
362    ///
363    /// Required parameters:
364    ///
365    /// * `asset` — String
366    /// * `is_isolated` — `TRUE` for Isolated Margin, `FALSE` for Cross Margin
367    /// * `amount` — String
368    /// * `r#type` — String
369    ///
370    #[must_use]
371    pub fn builder(
372        asset: String,
373        is_isolated: MarginAccountBorrowRepayIsIsolatedEnum,
374        amount: String,
375        r#type: MarginAccountBorrowRepayTypeEnum,
376    ) -> MarginAccountBorrowRepayParamsBuilder {
377        MarginAccountBorrowRepayParamsBuilder::default()
378            .asset(asset)
379            .is_isolated(is_isolated)
380            .amount(amount)
381            .r#type(r#type)
382    }
383}
384/// Request parameters for the [`query_borrow_repay_records_in_margin_account`] operation.
385///
386/// This struct holds all of the inputs you can pass when calling
387/// [`query_borrow_repay_records_in_margin_account`](#method.query_borrow_repay_records_in_margin_account).
388#[derive(Clone, Debug, Builder, Deserialize)]
389#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
390pub struct QueryBorrowRepayRecordsInMarginAccountParams {
391    ///
392    /// The `r#type` parameter.
393    ///
394    /// This field is **required.
395    #[builder(setter(into))]
396    #[serde(rename = "type")]
397    pub r#type: QueryBorrowRepayRecordsInMarginAccountTypeEnum,
398    ///
399    /// The `asset` parameter.
400    ///
401    /// This field is **optional.
402    #[builder(setter(into), default)]
403    #[serde(rename = "asset", default)]
404    pub asset: Option<String>,
405    ///
406    /// The `isolated_symbol` parameter.
407    ///
408    /// This field is **optional.
409    #[builder(setter(into), default)]
410    #[serde(rename = "isolatedSymbol", default)]
411    pub isolated_symbol: Option<String>,
412    ///
413    /// The `tx_id` parameter.
414    ///
415    /// This field is **optional.
416    #[builder(setter(into), default)]
417    #[serde(rename = "txId", default)]
418    pub tx_id: Option<i64>,
419    ///
420    /// The `start_time` parameter.
421    ///
422    /// This field is **optional.
423    #[builder(setter(into), default)]
424    #[serde(rename = "startTime", default)]
425    pub start_time: Option<i64>,
426    ///
427    /// The `end_time` parameter.
428    ///
429    /// This field is **optional.
430    #[builder(setter(into), default)]
431    #[serde(rename = "endTime", default)]
432    pub end_time: Option<i64>,
433    ///
434    /// The `current` parameter.
435    ///
436    /// This field is **optional.
437    #[builder(setter(into), default)]
438    #[serde(rename = "current", default)]
439    pub current: Option<i64>,
440    ///
441    /// The `size` parameter.
442    ///
443    /// This field is **optional.
444    #[builder(setter(into), default)]
445    #[serde(rename = "size", default)]
446    pub size: Option<i64>,
447    ///
448    /// The `recv_window` parameter.
449    ///
450    /// This field is **optional.
451    #[builder(setter(into), default)]
452    #[serde(rename = "recvWindow", default)]
453    pub recv_window: Option<i64>,
454}
455
456impl QueryBorrowRepayRecordsInMarginAccountParams {
457    /// Create a builder for [`query_borrow_repay_records_in_margin_account`].
458    ///
459    /// Required parameters:
460    ///
461    /// * `r#type` — String
462    ///
463    #[must_use]
464    pub fn builder(
465        r#type: QueryBorrowRepayRecordsInMarginAccountTypeEnum,
466    ) -> QueryBorrowRepayRecordsInMarginAccountParamsBuilder {
467        QueryBorrowRepayRecordsInMarginAccountParamsBuilder::default().r#type(r#type)
468    }
469}
470/// Request parameters for the [`query_margin_interest_rate_history`] operation.
471///
472/// This struct holds all of the inputs you can pass when calling
473/// [`query_margin_interest_rate_history`](#method.query_margin_interest_rate_history).
474#[derive(Clone, Debug, Builder, Deserialize)]
475#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
476pub struct QueryMarginInterestRateHistoryParams {
477    ///
478    /// The `asset` parameter.
479    ///
480    /// This field is **required.
481    #[builder(setter(into))]
482    #[serde(rename = "asset")]
483    pub asset: String,
484    ///
485    /// The `vip_level` parameter.
486    ///
487    /// This field is **optional.
488    #[builder(setter(into), default)]
489    #[serde(rename = "vipLevel", default)]
490    pub vip_level: Option<i64>,
491    ///
492    /// The `start_time` parameter.
493    ///
494    /// This field is **optional.
495    #[builder(setter(into), default)]
496    #[serde(rename = "startTime", default)]
497    pub start_time: Option<i64>,
498    ///
499    /// The `end_time` parameter.
500    ///
501    /// This field is **optional.
502    #[builder(setter(into), default)]
503    #[serde(rename = "endTime", default)]
504    pub end_time: Option<i64>,
505    ///
506    /// The `recv_window` parameter.
507    ///
508    /// This field is **optional.
509    #[builder(setter(into), default)]
510    #[serde(rename = "recvWindow", default)]
511    pub recv_window: Option<i64>,
512}
513
514impl QueryMarginInterestRateHistoryParams {
515    /// Create a builder for [`query_margin_interest_rate_history`].
516    ///
517    /// Required parameters:
518    ///
519    /// * `asset` — String
520    ///
521    #[must_use]
522    pub fn builder(asset: String) -> QueryMarginInterestRateHistoryParamsBuilder {
523        QueryMarginInterestRateHistoryParamsBuilder::default().asset(asset)
524    }
525}
526/// Request parameters for the [`query_max_borrow`] operation.
527///
528/// This struct holds all of the inputs you can pass when calling
529/// [`query_max_borrow`](#method.query_max_borrow).
530#[derive(Clone, Debug, Builder, Deserialize)]
531#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
532pub struct QueryMaxBorrowParams {
533    ///
534    /// The `asset` parameter.
535    ///
536    /// This field is **required.
537    #[builder(setter(into))]
538    #[serde(rename = "asset")]
539    pub asset: String,
540    ///
541    /// The `isolated_symbol` parameter.
542    ///
543    /// This field is **optional.
544    #[builder(setter(into), default)]
545    #[serde(rename = "isolatedSymbol", default)]
546    pub isolated_symbol: Option<String>,
547    ///
548    /// The `recv_window` parameter.
549    ///
550    /// This field is **optional.
551    #[builder(setter(into), default)]
552    #[serde(rename = "recvWindow", default)]
553    pub recv_window: Option<i64>,
554}
555
556impl QueryMaxBorrowParams {
557    /// Create a builder for [`query_max_borrow`].
558    ///
559    /// Required parameters:
560    ///
561    /// * `asset` — String
562    ///
563    #[must_use]
564    pub fn builder(asset: String) -> QueryMaxBorrowParamsBuilder {
565        QueryMaxBorrowParamsBuilder::default().asset(asset)
566    }
567}
568
569#[async_trait]
570impl BorrowRepayApi for BorrowRepayApiClient {
571    async fn get_future_hourly_interest_rate(
572        &self,
573        params: GetFutureHourlyInterestRateParams,
574    ) -> anyhow::Result<RestApiResponse<Vec<models::GetFutureHourlyInterestRateResponseInner>>>
575    {
576        let GetFutureHourlyInterestRateParams {
577            assets,
578            is_isolated,
579        } = params;
580
581        let mut query_params = BTreeMap::new();
582        let body_params = BTreeMap::new();
583
584        query_params.insert("assets".to_string(), json!(assets));
585
586        query_params.insert("isIsolated".to_string(), json!(is_isolated));
587
588        send_request::<Vec<models::GetFutureHourlyInterestRateResponseInner>>(
589            &self.configuration,
590            "/sapi/v1/margin/next-hourly-interest-rate",
591            reqwest::Method::GET,
592            query_params,
593            body_params,
594            if HAS_TIME_UNIT {
595                self.configuration.time_unit
596            } else {
597                None
598            },
599            true,
600        )
601        .await
602    }
603
604    async fn get_interest_history(
605        &self,
606        params: GetInterestHistoryParams,
607    ) -> anyhow::Result<RestApiResponse<models::GetInterestHistoryResponse>> {
608        let GetInterestHistoryParams {
609            asset,
610            isolated_symbol,
611            start_time,
612            end_time,
613            current,
614            size,
615            recv_window,
616        } = params;
617
618        let mut query_params = BTreeMap::new();
619        let body_params = BTreeMap::new();
620
621        if let Some(rw) = asset {
622            query_params.insert("asset".to_string(), json!(rw));
623        }
624
625        if let Some(rw) = isolated_symbol {
626            query_params.insert("isolatedSymbol".to_string(), json!(rw));
627        }
628
629        if let Some(rw) = start_time {
630            query_params.insert("startTime".to_string(), json!(rw));
631        }
632
633        if let Some(rw) = end_time {
634            query_params.insert("endTime".to_string(), json!(rw));
635        }
636
637        if let Some(rw) = current {
638            query_params.insert("current".to_string(), json!(rw));
639        }
640
641        if let Some(rw) = size {
642            query_params.insert("size".to_string(), json!(rw));
643        }
644
645        if let Some(rw) = recv_window {
646            query_params.insert("recvWindow".to_string(), json!(rw));
647        }
648
649        send_request::<models::GetInterestHistoryResponse>(
650            &self.configuration,
651            "/sapi/v1/margin/interestHistory",
652            reqwest::Method::GET,
653            query_params,
654            body_params,
655            if HAS_TIME_UNIT {
656                self.configuration.time_unit
657            } else {
658                None
659            },
660            true,
661        )
662        .await
663    }
664
665    async fn margin_account_borrow_repay(
666        &self,
667        params: MarginAccountBorrowRepayParams,
668    ) -> anyhow::Result<RestApiResponse<models::MarginAccountBorrowRepayResponse>> {
669        let MarginAccountBorrowRepayParams {
670            asset,
671            is_isolated,
672            amount,
673            r#type,
674            symbol,
675            recv_window,
676        } = params;
677
678        let mut query_params = BTreeMap::new();
679        let body_params = BTreeMap::new();
680
681        query_params.insert("asset".to_string(), json!(asset));
682
683        query_params.insert("isIsolated".to_string(), json!(is_isolated));
684
685        if let Some(rw) = symbol {
686            query_params.insert("symbol".to_string(), json!(rw));
687        }
688
689        query_params.insert("amount".to_string(), json!(amount));
690
691        query_params.insert("type".to_string(), json!(r#type));
692
693        if let Some(rw) = recv_window {
694            query_params.insert("recvWindow".to_string(), json!(rw));
695        }
696
697        send_request::<models::MarginAccountBorrowRepayResponse>(
698            &self.configuration,
699            "/sapi/v1/margin/borrow-repay",
700            reqwest::Method::POST,
701            query_params,
702            body_params,
703            if HAS_TIME_UNIT {
704                self.configuration.time_unit
705            } else {
706                None
707            },
708            true,
709        )
710        .await
711    }
712
713    async fn query_borrow_repay_records_in_margin_account(
714        &self,
715        params: QueryBorrowRepayRecordsInMarginAccountParams,
716    ) -> anyhow::Result<RestApiResponse<models::QueryBorrowRepayRecordsInMarginAccountResponse>>
717    {
718        let QueryBorrowRepayRecordsInMarginAccountParams {
719            r#type,
720            asset,
721            isolated_symbol,
722            tx_id,
723            start_time,
724            end_time,
725            current,
726            size,
727            recv_window,
728        } = params;
729
730        let mut query_params = BTreeMap::new();
731        let body_params = BTreeMap::new();
732
733        if let Some(rw) = asset {
734            query_params.insert("asset".to_string(), json!(rw));
735        }
736
737        if let Some(rw) = isolated_symbol {
738            query_params.insert("isolatedSymbol".to_string(), json!(rw));
739        }
740
741        if let Some(rw) = tx_id {
742            query_params.insert("txId".to_string(), json!(rw));
743        }
744
745        if let Some(rw) = start_time {
746            query_params.insert("startTime".to_string(), json!(rw));
747        }
748
749        if let Some(rw) = end_time {
750            query_params.insert("endTime".to_string(), json!(rw));
751        }
752
753        if let Some(rw) = current {
754            query_params.insert("current".to_string(), json!(rw));
755        }
756
757        if let Some(rw) = size {
758            query_params.insert("size".to_string(), json!(rw));
759        }
760
761        query_params.insert("type".to_string(), json!(r#type));
762
763        if let Some(rw) = recv_window {
764            query_params.insert("recvWindow".to_string(), json!(rw));
765        }
766
767        send_request::<models::QueryBorrowRepayRecordsInMarginAccountResponse>(
768            &self.configuration,
769            "/sapi/v1/margin/borrow-repay",
770            reqwest::Method::GET,
771            query_params,
772            body_params,
773            if HAS_TIME_UNIT {
774                self.configuration.time_unit
775            } else {
776                None
777            },
778            true,
779        )
780        .await
781    }
782
783    async fn query_margin_interest_rate_history(
784        &self,
785        params: QueryMarginInterestRateHistoryParams,
786    ) -> anyhow::Result<RestApiResponse<Vec<models::QueryMarginInterestRateHistoryResponseInner>>>
787    {
788        let QueryMarginInterestRateHistoryParams {
789            asset,
790            vip_level,
791            start_time,
792            end_time,
793            recv_window,
794        } = params;
795
796        let mut query_params = BTreeMap::new();
797        let body_params = BTreeMap::new();
798
799        query_params.insert("asset".to_string(), json!(asset));
800
801        if let Some(rw) = vip_level {
802            query_params.insert("vipLevel".to_string(), json!(rw));
803        }
804
805        if let Some(rw) = start_time {
806            query_params.insert("startTime".to_string(), json!(rw));
807        }
808
809        if let Some(rw) = end_time {
810            query_params.insert("endTime".to_string(), json!(rw));
811        }
812
813        if let Some(rw) = recv_window {
814            query_params.insert("recvWindow".to_string(), json!(rw));
815        }
816
817        send_request::<Vec<models::QueryMarginInterestRateHistoryResponseInner>>(
818            &self.configuration,
819            "/sapi/v1/margin/interestRateHistory",
820            reqwest::Method::GET,
821            query_params,
822            body_params,
823            if HAS_TIME_UNIT {
824                self.configuration.time_unit
825            } else {
826                None
827            },
828            true,
829        )
830        .await
831    }
832
833    async fn query_max_borrow(
834        &self,
835        params: QueryMaxBorrowParams,
836    ) -> anyhow::Result<RestApiResponse<models::QueryMaxBorrowResponse>> {
837        let QueryMaxBorrowParams {
838            asset,
839            isolated_symbol,
840            recv_window,
841        } = params;
842
843        let mut query_params = BTreeMap::new();
844        let body_params = BTreeMap::new();
845
846        query_params.insert("asset".to_string(), json!(asset));
847
848        if let Some(rw) = isolated_symbol {
849            query_params.insert("isolatedSymbol".to_string(), json!(rw));
850        }
851
852        if let Some(rw) = recv_window {
853            query_params.insert("recvWindow".to_string(), json!(rw));
854        }
855
856        send_request::<models::QueryMaxBorrowResponse>(
857            &self.configuration,
858            "/sapi/v1/margin/maxBorrowable",
859            reqwest::Method::GET,
860            query_params,
861            body_params,
862            if HAS_TIME_UNIT {
863                self.configuration.time_unit
864            } else {
865                None
866            },
867            true,
868        )
869        .await
870    }
871}
872
873#[cfg(all(test, feature = "margin_trading"))]
874mod tests {
875    use super::*;
876    use crate::TOKIO_SHARED_RT;
877    use crate::{errors::ConnectorError, models::DataFuture, models::RestApiRateLimit};
878    use async_trait::async_trait;
879    use std::collections::HashMap;
880
881    struct DummyRestApiResponse<T> {
882        inner: Box<dyn FnOnce() -> DataFuture<Result<T, ConnectorError>> + Send + Sync>,
883        status: u16,
884        headers: HashMap<String, String>,
885        rate_limits: Option<Vec<RestApiRateLimit>>,
886    }
887
888    impl<T> From<DummyRestApiResponse<T>> for RestApiResponse<T> {
889        fn from(dummy: DummyRestApiResponse<T>) -> Self {
890            Self {
891                data_fn: dummy.inner,
892                status: dummy.status,
893                headers: dummy.headers,
894                rate_limits: dummy.rate_limits,
895            }
896        }
897    }
898
899    struct MockBorrowRepayApiClient {
900        force_error: bool,
901    }
902
903    #[async_trait]
904    impl BorrowRepayApi for MockBorrowRepayApiClient {
905        async fn get_future_hourly_interest_rate(
906            &self,
907            _params: GetFutureHourlyInterestRateParams,
908        ) -> anyhow::Result<RestApiResponse<Vec<models::GetFutureHourlyInterestRateResponseInner>>>
909        {
910            if self.force_error {
911                return Err(ConnectorError::ConnectorClientError {
912                    msg: "ResponseError".to_string(),
913                    code: None,
914                }
915                .into());
916            }
917
918            let resp_json: Value =
919                serde_json::from_str(r#"[{"asset":"BTC","nextHourlyInterestRate":"0.00000571"}]"#)
920                    .unwrap_or_else(|_| serde_json::json!({}));
921            let dummy_response: Vec<models::GetFutureHourlyInterestRateResponseInner> =
922                serde_json::from_value(resp_json.clone()).expect(
923                    "should parse into Vec<models::GetFutureHourlyInterestRateResponseInner>",
924                );
925
926            let dummy = DummyRestApiResponse {
927                inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
928                status: 200,
929                headers: HashMap::new(),
930                rate_limits: None,
931            };
932
933            Ok(dummy.into())
934        }
935
936        async fn get_interest_history(
937            &self,
938            _params: GetInterestHistoryParams,
939        ) -> anyhow::Result<RestApiResponse<models::GetInterestHistoryResponse>> {
940            if self.force_error {
941                return Err(ConnectorError::ConnectorClientError {
942                    msg: "ResponseError".to_string(),
943                    code: None,
944                }
945                .into());
946            }
947
948            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_or_else(|_| serde_json::json!({}));
949            let dummy_response: models::GetInterestHistoryResponse =
950                serde_json::from_value(resp_json.clone())
951                    .expect("should parse into models::GetInterestHistoryResponse");
952
953            let dummy = DummyRestApiResponse {
954                inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
955                status: 200,
956                headers: HashMap::new(),
957                rate_limits: None,
958            };
959
960            Ok(dummy.into())
961        }
962
963        async fn margin_account_borrow_repay(
964            &self,
965            _params: MarginAccountBorrowRepayParams,
966        ) -> anyhow::Result<RestApiResponse<models::MarginAccountBorrowRepayResponse>> {
967            if self.force_error {
968                return Err(ConnectorError::ConnectorClientError {
969                    msg: "ResponseError".to_string(),
970                    code: None,
971                }
972                .into());
973            }
974
975            let resp_json: Value = serde_json::from_str(r#"{"tranId":100000001}"#)
976                .unwrap_or_else(|_| serde_json::json!({}));
977            let dummy_response: models::MarginAccountBorrowRepayResponse =
978                serde_json::from_value(resp_json.clone())
979                    .expect("should parse into models::MarginAccountBorrowRepayResponse");
980
981            let dummy = DummyRestApiResponse {
982                inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
983                status: 200,
984                headers: HashMap::new(),
985                rate_limits: None,
986            };
987
988            Ok(dummy.into())
989        }
990
991        async fn query_borrow_repay_records_in_margin_account(
992            &self,
993            _params: QueryBorrowRepayRecordsInMarginAccountParams,
994        ) -> anyhow::Result<RestApiResponse<models::QueryBorrowRepayRecordsInMarginAccountResponse>>
995        {
996            if self.force_error {
997                return Err(ConnectorError::ConnectorClientError {
998                    msg: "ResponseError".to_string(),
999                    code: None,
1000                }
1001                .into());
1002            }
1003
1004            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_or_else(|_| serde_json::json!({}));
1005            let dummy_response: models::QueryBorrowRepayRecordsInMarginAccountResponse =
1006                serde_json::from_value(resp_json.clone()).expect(
1007                    "should parse into models::QueryBorrowRepayRecordsInMarginAccountResponse",
1008                );
1009
1010            let dummy = DummyRestApiResponse {
1011                inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
1012                status: 200,
1013                headers: HashMap::new(),
1014                rate_limits: None,
1015            };
1016
1017            Ok(dummy.into())
1018        }
1019
1020        async fn query_margin_interest_rate_history(
1021            &self,
1022            _params: QueryMarginInterestRateHistoryParams,
1023        ) -> anyhow::Result<RestApiResponse<Vec<models::QueryMarginInterestRateHistoryResponseInner>>>
1024        {
1025            if self.force_error {
1026                return Err(ConnectorError::ConnectorClientError {
1027                    msg: "ResponseError".to_string(),
1028                    code: None,
1029                }
1030                .into());
1031            }
1032
1033            let resp_json: Value = serde_json::from_str(r#"[{"asset":"BTC","dailyInterestRate":"0.00025000","timestamp":1611544731000,"vipLevel":1}]"#).unwrap_or_else(|_| serde_json::json!({}));
1034            let dummy_response: Vec<models::QueryMarginInterestRateHistoryResponseInner> =
1035                serde_json::from_value(resp_json.clone()).expect(
1036                    "should parse into Vec<models::QueryMarginInterestRateHistoryResponseInner>",
1037                );
1038
1039            let dummy = DummyRestApiResponse {
1040                inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
1041                status: 200,
1042                headers: HashMap::new(),
1043                rate_limits: None,
1044            };
1045
1046            Ok(dummy.into())
1047        }
1048
1049        async fn query_max_borrow(
1050            &self,
1051            _params: QueryMaxBorrowParams,
1052        ) -> anyhow::Result<RestApiResponse<models::QueryMaxBorrowResponse>> {
1053            if self.force_error {
1054                return Err(ConnectorError::ConnectorClientError {
1055                    msg: "ResponseError".to_string(),
1056                    code: None,
1057                }
1058                .into());
1059            }
1060
1061            let resp_json: Value =
1062                serde_json::from_str(r#"{"amount":"1.69248805","borrowLimit":"60"}"#)
1063                    .unwrap_or_else(|_| serde_json::json!({}));
1064            let dummy_response: models::QueryMaxBorrowResponse =
1065                serde_json::from_value(resp_json.clone())
1066                    .expect("should parse into models::QueryMaxBorrowResponse");
1067
1068            let dummy = DummyRestApiResponse {
1069                inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
1070                status: 200,
1071                headers: HashMap::new(),
1072                rate_limits: None,
1073            };
1074
1075            Ok(dummy.into())
1076        }
1077    }
1078
1079    #[test]
1080    fn get_future_hourly_interest_rate_required_params_success() {
1081        TOKIO_SHARED_RT.block_on(async {
1082            let client = MockBorrowRepayApiClient { force_error: false };
1083
1084            let params = GetFutureHourlyInterestRateParams::builder(
1085                "BTC,ETH".to_string(),
1086                GetFutureHourlyInterestRateIsIsolatedEnum::True,
1087            )
1088            .build()
1089            .unwrap();
1090
1091            let resp_json: Value =
1092                serde_json::from_str(r#"[{"asset":"BTC","nextHourlyInterestRate":"0.00000571"}]"#)
1093                    .unwrap_or_else(|_| serde_json::json!({}));
1094            let expected_response: Vec<models::GetFutureHourlyInterestRateResponseInner> =
1095                serde_json::from_value(resp_json.clone()).expect(
1096                    "should parse into Vec<models::GetFutureHourlyInterestRateResponseInner>",
1097                );
1098
1099            let resp = client
1100                .get_future_hourly_interest_rate(params)
1101                .await
1102                .expect("Expected a response");
1103            let data_future = resp.data();
1104            let actual_response = data_future.await.unwrap();
1105            assert_eq!(actual_response, expected_response);
1106        });
1107    }
1108
1109    #[test]
1110    fn get_future_hourly_interest_rate_optional_params_success() {
1111        TOKIO_SHARED_RT.block_on(async {
1112            let client = MockBorrowRepayApiClient { force_error: false };
1113
1114            let params = GetFutureHourlyInterestRateParams::builder(
1115                "BTC,ETH".to_string(),
1116                GetFutureHourlyInterestRateIsIsolatedEnum::True,
1117            )
1118            .build()
1119            .unwrap();
1120
1121            let resp_json: Value =
1122                serde_json::from_str(r#"[{"asset":"BTC","nextHourlyInterestRate":"0.00000571"}]"#)
1123                    .unwrap_or_else(|_| serde_json::json!({}));
1124            let expected_response: Vec<models::GetFutureHourlyInterestRateResponseInner> =
1125                serde_json::from_value(resp_json.clone()).expect(
1126                    "should parse into Vec<models::GetFutureHourlyInterestRateResponseInner>",
1127                );
1128
1129            let resp = client
1130                .get_future_hourly_interest_rate(params)
1131                .await
1132                .expect("Expected a response");
1133            let data_future = resp.data();
1134            let actual_response = data_future.await.unwrap();
1135            assert_eq!(actual_response, expected_response);
1136        });
1137    }
1138
1139    #[test]
1140    fn get_future_hourly_interest_rate_response_error() {
1141        TOKIO_SHARED_RT.block_on(async {
1142            let client = MockBorrowRepayApiClient { force_error: true };
1143
1144            let params = GetFutureHourlyInterestRateParams::builder(
1145                "BTC,ETH".to_string(),
1146                GetFutureHourlyInterestRateIsIsolatedEnum::True,
1147            )
1148            .build()
1149            .unwrap();
1150
1151            match client.get_future_hourly_interest_rate(params).await {
1152                Ok(_) => panic!("Expected an error"),
1153                Err(err) => {
1154                    assert_eq!(err.to_string(), "Connector client error: ResponseError");
1155                }
1156            }
1157        });
1158    }
1159
1160    #[test]
1161    fn get_interest_history_required_params_success() {
1162        TOKIO_SHARED_RT.block_on(async {
1163            let client = MockBorrowRepayApiClient { force_error: false };
1164
1165            let params = GetInterestHistoryParams::builder().build().unwrap();
1166
1167            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_or_else(|_| serde_json::json!({}));
1168            let expected_response : models::GetInterestHistoryResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::GetInterestHistoryResponse");
1169
1170            let resp = client.get_interest_history(params).await.expect("Expected a response");
1171            let data_future = resp.data();
1172            let actual_response = data_future.await.unwrap();
1173            assert_eq!(actual_response, expected_response);
1174        });
1175    }
1176
1177    #[test]
1178    fn get_interest_history_optional_params_success() {
1179        TOKIO_SHARED_RT.block_on(async {
1180            let client = MockBorrowRepayApiClient { force_error: false };
1181
1182            let params = GetInterestHistoryParams::builder().asset("USDT".to_string()).isolated_symbol("BNBUSDT".to_string()).start_time(1623319461670).end_time(1641782889000).current(1).size(10).recv_window(5000).build().unwrap();
1183
1184            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_or_else(|_| serde_json::json!({}));
1185            let expected_response : models::GetInterestHistoryResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::GetInterestHistoryResponse");
1186
1187            let resp = client.get_interest_history(params).await.expect("Expected a response");
1188            let data_future = resp.data();
1189            let actual_response = data_future.await.unwrap();
1190            assert_eq!(actual_response, expected_response);
1191        });
1192    }
1193
1194    #[test]
1195    fn get_interest_history_response_error() {
1196        TOKIO_SHARED_RT.block_on(async {
1197            let client = MockBorrowRepayApiClient { force_error: true };
1198
1199            let params = GetInterestHistoryParams::builder().build().unwrap();
1200
1201            match client.get_interest_history(params).await {
1202                Ok(_) => panic!("Expected an error"),
1203                Err(err) => {
1204                    assert_eq!(err.to_string(), "Connector client error: ResponseError");
1205                }
1206            }
1207        });
1208    }
1209
1210    #[test]
1211    fn margin_account_borrow_repay_required_params_success() {
1212        TOKIO_SHARED_RT.block_on(async {
1213            let client = MockBorrowRepayApiClient { force_error: false };
1214
1215            let params = MarginAccountBorrowRepayParams::builder(
1216                "USDT".to_string(),
1217                MarginAccountBorrowRepayIsIsolatedEnum::True,
1218                "1.0".to_string(),
1219                MarginAccountBorrowRepayTypeEnum::Borrow,
1220            )
1221            .build()
1222            .unwrap();
1223
1224            let resp_json: Value = serde_json::from_str(r#"{"tranId":100000001}"#)
1225                .unwrap_or_else(|_| serde_json::json!({}));
1226            let expected_response: models::MarginAccountBorrowRepayResponse =
1227                serde_json::from_value(resp_json.clone())
1228                    .expect("should parse into models::MarginAccountBorrowRepayResponse");
1229
1230            let resp = client
1231                .margin_account_borrow_repay(params)
1232                .await
1233                .expect("Expected a response");
1234            let data_future = resp.data();
1235            let actual_response = data_future.await.unwrap();
1236            assert_eq!(actual_response, expected_response);
1237        });
1238    }
1239
1240    #[test]
1241    fn margin_account_borrow_repay_optional_params_success() {
1242        TOKIO_SHARED_RT.block_on(async {
1243            let client = MockBorrowRepayApiClient { force_error: false };
1244
1245            let params = MarginAccountBorrowRepayParams::builder(
1246                "USDT".to_string(),
1247                MarginAccountBorrowRepayIsIsolatedEnum::True,
1248                "1.0".to_string(),
1249                MarginAccountBorrowRepayTypeEnum::Borrow,
1250            )
1251            .symbol("BTCUSDT".to_string())
1252            .recv_window(5000)
1253            .build()
1254            .unwrap();
1255
1256            let resp_json: Value = serde_json::from_str(r#"{"tranId":100000001}"#)
1257                .unwrap_or_else(|_| serde_json::json!({}));
1258            let expected_response: models::MarginAccountBorrowRepayResponse =
1259                serde_json::from_value(resp_json.clone())
1260                    .expect("should parse into models::MarginAccountBorrowRepayResponse");
1261
1262            let resp = client
1263                .margin_account_borrow_repay(params)
1264                .await
1265                .expect("Expected a response");
1266            let data_future = resp.data();
1267            let actual_response = data_future.await.unwrap();
1268            assert_eq!(actual_response, expected_response);
1269        });
1270    }
1271
1272    #[test]
1273    fn margin_account_borrow_repay_response_error() {
1274        TOKIO_SHARED_RT.block_on(async {
1275            let client = MockBorrowRepayApiClient { force_error: true };
1276
1277            let params = MarginAccountBorrowRepayParams::builder(
1278                "USDT".to_string(),
1279                MarginAccountBorrowRepayIsIsolatedEnum::True,
1280                "1.0".to_string(),
1281                MarginAccountBorrowRepayTypeEnum::Borrow,
1282            )
1283            .build()
1284            .unwrap();
1285
1286            match client.margin_account_borrow_repay(params).await {
1287                Ok(_) => panic!("Expected an error"),
1288                Err(err) => {
1289                    assert_eq!(err.to_string(), "Connector client error: ResponseError");
1290                }
1291            }
1292        });
1293    }
1294
1295    #[test]
1296    fn query_borrow_repay_records_in_margin_account_required_params_success() {
1297        TOKIO_SHARED_RT.block_on(async {
1298            let client = MockBorrowRepayApiClient { force_error: false };
1299
1300            let params = QueryBorrowRepayRecordsInMarginAccountParams::builder(QueryBorrowRepayRecordsInMarginAccountTypeEnum::Borrow,).build().unwrap();
1301
1302            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_or_else(|_| serde_json::json!({}));
1303            let expected_response : models::QueryBorrowRepayRecordsInMarginAccountResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::QueryBorrowRepayRecordsInMarginAccountResponse");
1304
1305            let resp = client.query_borrow_repay_records_in_margin_account(params).await.expect("Expected a response");
1306            let data_future = resp.data();
1307            let actual_response = data_future.await.unwrap();
1308            assert_eq!(actual_response, expected_response);
1309        });
1310    }
1311
1312    #[test]
1313    fn query_borrow_repay_records_in_margin_account_optional_params_success() {
1314        TOKIO_SHARED_RT.block_on(async {
1315            let client = MockBorrowRepayApiClient { force_error: false };
1316
1317            let params = QueryBorrowRepayRecordsInMarginAccountParams::builder(QueryBorrowRepayRecordsInMarginAccountTypeEnum::Borrow,).asset("BNB".to_string()).isolated_symbol("BNBUSDT".to_string()).tx_id(1).start_time(1623319461670).end_time(1641782889000).current(1).size(10).recv_window(5000).build().unwrap();
1318
1319            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_or_else(|_| serde_json::json!({}));
1320            let expected_response : models::QueryBorrowRepayRecordsInMarginAccountResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::QueryBorrowRepayRecordsInMarginAccountResponse");
1321
1322            let resp = client.query_borrow_repay_records_in_margin_account(params).await.expect("Expected a response");
1323            let data_future = resp.data();
1324            let actual_response = data_future.await.unwrap();
1325            assert_eq!(actual_response, expected_response);
1326        });
1327    }
1328
1329    #[test]
1330    fn query_borrow_repay_records_in_margin_account_response_error() {
1331        TOKIO_SHARED_RT.block_on(async {
1332            let client = MockBorrowRepayApiClient { force_error: true };
1333
1334            let params = QueryBorrowRepayRecordsInMarginAccountParams::builder(
1335                QueryBorrowRepayRecordsInMarginAccountTypeEnum::Borrow,
1336            )
1337            .build()
1338            .unwrap();
1339
1340            match client
1341                .query_borrow_repay_records_in_margin_account(params)
1342                .await
1343            {
1344                Ok(_) => panic!("Expected an error"),
1345                Err(err) => {
1346                    assert_eq!(err.to_string(), "Connector client error: ResponseError");
1347                }
1348            }
1349        });
1350    }
1351
1352    #[test]
1353    fn query_margin_interest_rate_history_required_params_success() {
1354        TOKIO_SHARED_RT.block_on(async {
1355            let client = MockBorrowRepayApiClient { force_error: false };
1356
1357            let params = QueryMarginInterestRateHistoryParams::builder("BTC".to_string(),).build().unwrap();
1358
1359            let resp_json: Value = serde_json::from_str(r#"[{"asset":"BTC","dailyInterestRate":"0.00025000","timestamp":1611544731000,"vipLevel":1}]"#).unwrap_or_else(|_| serde_json::json!({}));
1360            let expected_response : Vec<models::QueryMarginInterestRateHistoryResponseInner> = serde_json::from_value(resp_json.clone()).expect("should parse into Vec<models::QueryMarginInterestRateHistoryResponseInner>");
1361
1362            let resp = client.query_margin_interest_rate_history(params).await.expect("Expected a response");
1363            let data_future = resp.data();
1364            let actual_response = data_future.await.unwrap();
1365            assert_eq!(actual_response, expected_response);
1366        });
1367    }
1368
1369    #[test]
1370    fn query_margin_interest_rate_history_optional_params_success() {
1371        TOKIO_SHARED_RT.block_on(async {
1372            let client = MockBorrowRepayApiClient { force_error: false };
1373
1374            let params = QueryMarginInterestRateHistoryParams::builder("BTC".to_string(),).vip_level(1).start_time(1623319461670).end_time(1641782889000).recv_window(5000).build().unwrap();
1375
1376            let resp_json: Value = serde_json::from_str(r#"[{"asset":"BTC","dailyInterestRate":"0.00025000","timestamp":1611544731000,"vipLevel":1}]"#).unwrap_or_else(|_| serde_json::json!({}));
1377            let expected_response : Vec<models::QueryMarginInterestRateHistoryResponseInner> = serde_json::from_value(resp_json.clone()).expect("should parse into Vec<models::QueryMarginInterestRateHistoryResponseInner>");
1378
1379            let resp = client.query_margin_interest_rate_history(params).await.expect("Expected a response");
1380            let data_future = resp.data();
1381            let actual_response = data_future.await.unwrap();
1382            assert_eq!(actual_response, expected_response);
1383        });
1384    }
1385
1386    #[test]
1387    fn query_margin_interest_rate_history_response_error() {
1388        TOKIO_SHARED_RT.block_on(async {
1389            let client = MockBorrowRepayApiClient { force_error: true };
1390
1391            let params = QueryMarginInterestRateHistoryParams::builder("BTC".to_string())
1392                .build()
1393                .unwrap();
1394
1395            match client.query_margin_interest_rate_history(params).await {
1396                Ok(_) => panic!("Expected an error"),
1397                Err(err) => {
1398                    assert_eq!(err.to_string(), "Connector client error: ResponseError");
1399                }
1400            }
1401        });
1402    }
1403
1404    #[test]
1405    fn query_max_borrow_required_params_success() {
1406        TOKIO_SHARED_RT.block_on(async {
1407            let client = MockBorrowRepayApiClient { force_error: false };
1408
1409            let params = QueryMaxBorrowParams::builder("BTC".to_string())
1410                .build()
1411                .unwrap();
1412
1413            let resp_json: Value =
1414                serde_json::from_str(r#"{"amount":"1.69248805","borrowLimit":"60"}"#)
1415                    .unwrap_or_else(|_| serde_json::json!({}));
1416            let expected_response: models::QueryMaxBorrowResponse =
1417                serde_json::from_value(resp_json.clone())
1418                    .expect("should parse into models::QueryMaxBorrowResponse");
1419
1420            let resp = client
1421                .query_max_borrow(params)
1422                .await
1423                .expect("Expected a response");
1424            let data_future = resp.data();
1425            let actual_response = data_future.await.unwrap();
1426            assert_eq!(actual_response, expected_response);
1427        });
1428    }
1429
1430    #[test]
1431    fn query_max_borrow_optional_params_success() {
1432        TOKIO_SHARED_RT.block_on(async {
1433            let client = MockBorrowRepayApiClient { force_error: false };
1434
1435            let params = QueryMaxBorrowParams::builder("BTC".to_string())
1436                .isolated_symbol("BTCUSDT".to_string())
1437                .recv_window(5000)
1438                .build()
1439                .unwrap();
1440
1441            let resp_json: Value =
1442                serde_json::from_str(r#"{"amount":"1.69248805","borrowLimit":"60"}"#)
1443                    .unwrap_or_else(|_| serde_json::json!({}));
1444            let expected_response: models::QueryMaxBorrowResponse =
1445                serde_json::from_value(resp_json.clone())
1446                    .expect("should parse into models::QueryMaxBorrowResponse");
1447
1448            let resp = client
1449                .query_max_borrow(params)
1450                .await
1451                .expect("Expected a response");
1452            let data_future = resp.data();
1453            let actual_response = data_future.await.unwrap();
1454            assert_eq!(actual_response, expected_response);
1455        });
1456    }
1457
1458    #[test]
1459    fn query_max_borrow_response_error() {
1460        TOKIO_SHARED_RT.block_on(async {
1461            let client = MockBorrowRepayApiClient { force_error: true };
1462
1463            let params = QueryMaxBorrowParams::builder("BTC".to_string())
1464                .build()
1465                .unwrap();
1466
1467            match client.query_max_borrow(params).await {
1468                Ok(_) => panic!("Expected an error"),
1469                Err(err) => {
1470                    assert_eq!(err.to_string(), "Connector client error: ResponseError");
1471                }
1472            }
1473        });
1474    }
1475}