bybit_rust_api/rest/crypto_loan/
crypto_loan_client.rs

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