bybit_rust_api/rest/crypto_loan/
crypto_loan_client.rs

1use crate::rest::client::{RestClient, SecType, ServerResponse};
2use crate::rest::BybitResult as Result;
3use serde_json::json;
4
5#[derive(Clone)]
6pub struct CryptoLoanClient {
7    client: RestClient,
8}
9
10impl CryptoLoanClient {
11    pub fn new(client: RestClient) -> Self {
12        CryptoLoanClient { client }
13    }
14
15    /// Get loan product info
16    /// https://bybit-exchange.github.io/docs/v5/crypto-loan/collateral-data
17    pub async fn get_collateral_data(
18        &self,
19        ltv_type: Option<&str>,
20    ) -> Result<ServerResponse<serde_json::Value>> {
21        let endpoint = "v5/crypto-loan/collateral-data";
22        let mut params = json!({});
23
24        if let Some(ltv_type) = ltv_type {
25            params["ltvType"] = json!(ltv_type);
26        }
27
28        let response = self.client.get(endpoint, params, SecType::Signed).await?;
29        Ok(response)
30    }
31
32    /// Borrow crypto loan
33    /// https://bybit-exchange.github.io/docs/v5/crypto-loan/borrow
34    pub async fn borrow(
35        &self,
36        ltv_type: &str,
37        loan_currency: &str,
38        loan_amount: &str,
39        collateral_currency: &str,
40        max_rate: Option<&str>,
41    ) -> Result<ServerResponse<serde_json::Value>> {
42        let endpoint = "v5/crypto-loan/borrow";
43        let mut body = json!({
44            "ltvType": ltv_type,
45            "loanCurrency": loan_currency,
46            "loanAmount": loan_amount,
47            "collateralCurrency": collateral_currency,
48        });
49
50        if let Some(max_rate) = max_rate {
51            body["maxRate"] = json!(max_rate);
52        }
53
54        let response = self.client.post(endpoint, body, SecType::Signed).await?;
55        Ok(response)
56    }
57
58    /// Repay crypto loan
59    /// https://bybit-exchange.github.io/docs/v5/crypto-loan/repay
60    pub async fn repay(
61        &self,
62        order_id: &str,
63        repay_amount: Option<&str>,
64    ) -> Result<ServerResponse<serde_json::Value>> {
65        let endpoint = "v5/crypto-loan/repay";
66        let mut body = json!({
67            "orderId": order_id,
68        });
69
70        if let Some(repay_amount) = repay_amount {
71            body["repayAmount"] = json!(repay_amount);
72        }
73
74        let response = self.client.post(endpoint, body, SecType::Signed).await?;
75        Ok(response)
76    }
77
78    /// Get ongoing orders
79    /// https://bybit-exchange.github.io/docs/v5/crypto-loan/ongoing-orders
80    pub async fn get_ongoing_orders(
81        &self,
82        order_id: Option<&str>,
83        loan_currency: Option<&str>,
84        collateral_currency: Option<&str>,
85        limit: Option<i32>,
86        cursor: Option<&str>,
87    ) -> Result<ServerResponse<serde_json::Value>> {
88        let endpoint = "v5/crypto-loan/ongoing-orders";
89        let mut params = json!({});
90
91        if let Some(order_id) = order_id {
92            params["orderId"] = json!(order_id);
93        }
94        if let Some(loan_currency) = loan_currency {
95            params["loanCurrency"] = json!(loan_currency);
96        }
97        if let Some(collateral_currency) = collateral_currency {
98            params["collateralCurrency"] = json!(collateral_currency);
99        }
100        if let Some(limit) = limit {
101            params["limit"] = json!(limit);
102        }
103        if let Some(cursor) = cursor {
104            params["cursor"] = json!(cursor);
105        }
106
107        let response = self.client.get(endpoint, params, SecType::Signed).await?;
108        Ok(response)
109    }
110
111    /// Get borrow history
112    /// https://bybit-exchange.github.io/docs/v5/crypto-loan/borrow-history
113    pub async fn get_borrow_history(
114        &self,
115        order_id: Option<&str>,
116        loan_currency: Option<&str>,
117        collateral_currency: Option<&str>,
118        start_time: Option<i64>,
119        end_time: Option<i64>,
120        limit: Option<i32>,
121        cursor: Option<&str>,
122    ) -> Result<ServerResponse<serde_json::Value>> {
123        let endpoint = "v5/crypto-loan/borrow-history";
124        let mut params = json!({});
125
126        if let Some(order_id) = order_id {
127            params["orderId"] = json!(order_id);
128        }
129        if let Some(loan_currency) = loan_currency {
130            params["loanCurrency"] = json!(loan_currency);
131        }
132        if let Some(collateral_currency) = collateral_currency {
133            params["collateralCurrency"] = json!(collateral_currency);
134        }
135        if let Some(start_time) = start_time {
136            params["startTime"] = json!(start_time);
137        }
138        if let Some(end_time) = end_time {
139            params["endTime"] = json!(end_time);
140        }
141        if let Some(limit) = limit {
142            params["limit"] = json!(limit);
143        }
144        if let Some(cursor) = cursor {
145            params["cursor"] = json!(cursor);
146        }
147
148        let response = self.client.get(endpoint, params, SecType::Signed).await?;
149        Ok(response)
150    }
151
152    /// Get max collateral amount
153    /// https://bybit-exchange.github.io/docs/v5/crypto-loan/max-collateral-amount
154    pub async fn get_max_collateral_amount(
155        &self,
156        ltv_type: &str,
157        loan_currency: &str,
158        loan_amount: &str,
159        collateral_currency: &str,
160    ) -> Result<ServerResponse<serde_json::Value>> {
161        let endpoint = "v5/crypto-loan/max-collateral-amount";
162        let params = json!({
163            "ltvType": ltv_type,
164            "loanCurrency": loan_currency,
165            "loanAmount": loan_amount,
166            "collateralCurrency": collateral_currency,
167        });
168
169        let response = self.client.get(endpoint, params, SecType::Signed).await?;
170        Ok(response)
171    }
172
173    /// Adjust LTV
174    /// https://bybit-exchange.github.io/docs/v5/crypto-loan/adjust-ltv
175    pub async fn adjust_ltv(
176        &self,
177        order_id: &str,
178        amount: &str,
179        direction: i32, // 1: add collateral, 2: remove collateral
180    ) -> Result<ServerResponse<serde_json::Value>> {
181        let endpoint = "v5/crypto-loan/adjust-ltv";
182        let body = json!({
183            "orderId": order_id,
184            "amount": amount,
185            "direction": direction,
186        });
187
188        let response = self.client.post(endpoint, body, SecType::Signed).await?;
189        Ok(response)
190    }
191
192    /// Get adjustment history
193    /// https://bybit-exchange.github.io/docs/v5/crypto-loan/adjustment-history
194    pub async fn get_adjustment_history(
195        &self,
196        order_id: Option<&str>,
197        adjustment_id: Option<&str>,
198        start_time: Option<i64>,
199        end_time: Option<i64>,
200        limit: Option<i32>,
201        cursor: Option<&str>,
202    ) -> Result<ServerResponse<serde_json::Value>> {
203        let endpoint = "v5/crypto-loan/adjustment-history";
204        let mut params = json!({});
205
206        if let Some(order_id) = order_id {
207            params["orderId"] = json!(order_id);
208        }
209        if let Some(adjustment_id) = adjustment_id {
210            params["adjustmentId"] = json!(adjustment_id);
211        }
212        if let Some(start_time) = start_time {
213            params["startTime"] = json!(start_time);
214        }
215        if let Some(end_time) = end_time {
216            params["endTime"] = json!(end_time);
217        }
218        if let Some(limit) = limit {
219            params["limit"] = json!(limit);
220        }
221        if let Some(cursor) = cursor {
222            params["cursor"] = json!(cursor);
223        }
224
225        let response = self.client.get(endpoint, params, SecType::Signed).await?;
226        Ok(response)
227    }
228}
229
230#[cfg(test)]
231mod tests {
232    use super::*;
233    use crate::rest::ApiKeyPair;
234
235    fn create_test_client() -> CryptoLoanClient {
236        let api_key_pair = ApiKeyPair::new(
237            "test_key".to_string(),
238            "test_secret".to_string(),
239            "".to_string(),
240        );
241        let rest_client =
242            RestClient::new(api_key_pair, "https://api-testnet.bybit.com".to_string());
243        CryptoLoanClient::new(rest_client)
244    }
245
246    #[test]
247    fn test_client_creation() {
248        let client = create_test_client();
249        // Test that client was created successfully
250        assert_eq!(
251            std::mem::size_of_val(&client),
252            std::mem::size_of::<CryptoLoanClient>()
253        );
254    }
255
256    #[tokio::test]
257    async fn test_borrow_required_params() {
258        let client = create_test_client();
259        let result = client
260            .borrow("1", "USDT", "1000.0", "BTC", Some("0.05"))
261            .await;
262        // Should not panic with valid required parameters
263        assert!(result.is_err() || result.is_ok());
264    }
265
266    #[tokio::test]
267    async fn test_repay_required_params() {
268        let client = create_test_client();
269        let result = client.repay("order_123", Some("500.0")).await;
270        // Should not panic with valid required order_id parameter
271        assert!(result.is_err() || result.is_ok());
272    }
273
274    #[tokio::test]
275    async fn test_adjust_ltv_required_params() {
276        let client = create_test_client();
277        let result = client
278            .adjust_ltv(
279                "order_123",
280                "100.0",
281                1, // add collateral
282            )
283            .await;
284        // Should not panic with valid required parameters
285        assert!(result.is_err() || result.is_ok());
286    }
287}