ccxt_exchanges/binance/parser/
margin.rs1#![allow(dead_code, clippy::unnecessary_wraps)]
2
3use ccxt_core::{
4 Result,
5 error::{Error, ParseError},
6 types::{
7 BorrowInterest, BorrowRate, BorrowRateHistory, MarginAdjustment, MarginLoan, MaxBorrowable,
8 MaxTransferable,
9 },
10};
11use serde_json::Value;
12
13pub fn parse_borrow_rate(data: &Value, currency: &str, symbol: Option<&str>) -> Result<BorrowRate> {
15 let rate = if let Some(rate_str) = data["dailyInterestRate"].as_str() {
16 rate_str.parse::<f64>().unwrap_or(0.0)
17 } else {
18 data["dailyInterestRate"].as_f64().unwrap_or(0.0)
19 };
20
21 let timestamp = data["timestamp"]
22 .as_i64()
23 .or_else(|| {
24 data["vipLevel"]
25 .as_i64()
26 .map(|_| chrono::Utc::now().timestamp_millis())
27 })
28 .unwrap_or_else(|| chrono::Utc::now().timestamp_millis());
29
30 if let Some(sym) = symbol {
31 Ok(BorrowRate::new_isolated(
32 currency.to_string(),
33 sym.to_string(),
34 rate,
35 timestamp,
36 data.clone(),
37 ))
38 } else {
39 Ok(BorrowRate::new_cross(
40 currency.to_string(),
41 rate,
42 timestamp,
43 data.clone(),
44 ))
45 }
46}
47
48pub fn parse_margin_loan(data: &Value) -> Result<MarginLoan> {
50 let id = data["tranId"]
51 .as_i64()
52 .or_else(|| data["txId"].as_i64())
53 .map(|id| id.to_string())
54 .unwrap_or_default();
55
56 let currency = data["asset"]
57 .as_str()
58 .ok_or_else(|| Error::from(ParseError::missing_field("asset")))?
59 .to_string();
60
61 let symbol = data["symbol"].as_str().map(ToString::to_string);
62
63 let amount = if let Some(amount_str) = data["amount"].as_str() {
64 amount_str.parse::<f64>().unwrap_or(0.0)
65 } else {
66 data["amount"].as_f64().unwrap_or(0.0)
67 };
68
69 let timestamp = data["timestamp"]
70 .as_i64()
71 .unwrap_or_else(|| chrono::Utc::now().timestamp_millis());
72
73 let status = data["status"].as_str().unwrap_or("CONFIRMED").to_string();
74
75 Ok(MarginLoan::new(
76 id,
77 currency,
78 symbol,
79 amount,
80 timestamp,
81 status,
82 data.clone(),
83 ))
84}
85
86pub fn parse_margin_adjustment(data: &Value) -> Result<MarginAdjustment> {
88 let id = data["tranId"]
89 .as_i64()
90 .or_else(|| data["txId"].as_i64())
91 .map(|id| id.to_string())
92 .unwrap_or_default();
93
94 let symbol = data["symbol"].as_str().map(ToString::to_string);
95
96 let currency = data["asset"]
97 .as_str()
98 .ok_or_else(|| Error::from(ParseError::missing_field("asset")))?
99 .to_string();
100
101 let amount = if let Some(amount_str) = data["amount"].as_str() {
102 amount_str.parse::<f64>().unwrap_or(0.0)
103 } else {
104 data["amount"].as_f64().unwrap_or(0.0)
105 };
106
107 let transfer_type = data["type"]
108 .as_str()
109 .or_else(|| data["transFrom"].as_str())
110 .map_or("IN", |t| {
111 if t.contains("MAIN") || t.eq("1") || t.eq("ROLL_IN") {
112 "IN"
113 } else {
114 "OUT"
115 }
116 })
117 .to_string();
118
119 let timestamp = data["timestamp"]
120 .as_i64()
121 .unwrap_or_else(|| chrono::Utc::now().timestamp_millis());
122
123 let status = data["status"].as_str().unwrap_or("SUCCESS").to_string();
124
125 Ok(MarginAdjustment::new(
126 id,
127 symbol,
128 currency,
129 amount,
130 transfer_type,
131 timestamp,
132 status,
133 data.clone(),
134 ))
135}
136
137pub fn parse_borrow_interest(data: &Value) -> Result<BorrowInterest> {
139 let id = data["txId"]
140 .as_i64()
141 .or_else(|| {
142 data["isolatedSymbol"]
143 .as_str()
144 .map(|_| chrono::Utc::now().timestamp_millis())
145 })
146 .map(|id| id.to_string())
147 .unwrap_or_default();
148
149 let currency = data["asset"]
150 .as_str()
151 .ok_or_else(|| Error::from(ParseError::missing_field("asset")))?
152 .to_string();
153
154 let symbol = data["isolatedSymbol"].as_str().map(ToString::to_string);
155
156 let interest = if let Some(interest_str) = data["interest"].as_str() {
157 interest_str.parse::<f64>().unwrap_or(0.0)
158 } else {
159 data["interest"].as_f64().unwrap_or(0.0)
160 };
161
162 let interest_rate = if let Some(rate_str) = data["interestRate"].as_str() {
163 rate_str.parse::<f64>().unwrap_or(0.0)
164 } else {
165 data["interestRate"].as_f64().unwrap_or(0.0)
166 };
167
168 let principal = if let Some(principal_str) = data["principal"].as_str() {
169 principal_str.parse::<f64>().unwrap_or(0.0)
170 } else {
171 data["principal"].as_f64().unwrap_or(0.0)
172 };
173
174 let timestamp = data["interestAccuredTime"]
175 .as_i64()
176 .or_else(|| data["timestamp"].as_i64())
177 .unwrap_or_else(|| chrono::Utc::now().timestamp_millis());
178
179 Ok(BorrowInterest::new(
180 id,
181 currency,
182 symbol,
183 interest,
184 interest_rate,
185 principal,
186 timestamp,
187 data.clone(),
188 ))
189}
190
191pub fn parse_borrow_interests(data: &Value) -> Result<Vec<BorrowInterest>> {
193 let mut interests = Vec::new();
194
195 if let Some(array) = data.as_array() {
196 for item in array {
197 match parse_borrow_interest(item) {
198 Ok(interest) => interests.push(interest),
199 Err(e) => {
200 eprintln!("Failed to parse borrow interest: {}", e);
201 }
202 }
203 }
204 }
205
206 Ok(interests)
207}
208
209pub fn parse_borrow_rate_history(data: &Value, currency: &str) -> Result<BorrowRateHistory> {
211 let timestamp = data["timestamp"]
212 .as_i64()
213 .or_else(|| data["time"].as_i64())
214 .unwrap_or(0);
215
216 let rate = data["hourlyInterestRate"]
217 .as_str()
218 .or_else(|| data["dailyInterestRate"].as_str())
219 .or_else(|| data["rate"].as_str())
220 .and_then(|s| s.parse::<f64>().ok())
221 .or_else(|| data["hourlyInterestRate"].as_f64())
222 .or_else(|| data["dailyInterestRate"].as_f64())
223 .or_else(|| data["rate"].as_f64())
224 .unwrap_or(0.0);
225
226 let datetime = chrono::DateTime::from_timestamp_millis(timestamp)
227 .map(|dt| dt.to_rfc3339())
228 .unwrap_or_default();
229
230 let symbol = data["symbol"].as_str().map(ToString::to_string);
231 let vip_level = data["vipLevel"].as_i64().map(|v| v as i32);
232
233 Ok(BorrowRateHistory {
234 currency: currency.to_string(),
235 symbol,
236 rate,
237 timestamp,
238 datetime,
239 vip_level,
240 info: data.clone(),
241 })
242}
243
244pub fn parse_isolated_borrow_rates(
246 data: &Value,
247) -> Result<std::collections::HashMap<String, ccxt_core::types::IsolatedBorrowRate>> {
248 use ccxt_core::types::IsolatedBorrowRate;
249 use std::collections::HashMap;
250
251 let mut rates_map = HashMap::new();
252
253 if let Some(array) = data.as_array() {
254 for item in array {
255 let symbol = item["symbol"].as_str().unwrap_or("");
256 let base = item["base"].as_str().unwrap_or("");
257 let quote = item["quote"].as_str().unwrap_or("");
258
259 let base_rate = item["dailyInterestRate"]
260 .as_str()
261 .and_then(|s| s.parse::<f64>().ok())
262 .unwrap_or(0.0);
263
264 let quote_rate = item["quoteDailyInterestRate"]
265 .as_str()
266 .and_then(|s| s.parse::<f64>().ok())
267 .or_else(|| {
268 item["dailyInterestRate"]
269 .as_str()
270 .and_then(|s| s.parse::<f64>().ok())
271 })
272 .unwrap_or(0.0);
273
274 let timestamp = item["timestamp"].as_i64().or_else(|| item["time"].as_i64());
275
276 let datetime = timestamp.and_then(|ts| {
277 chrono::DateTime::from_timestamp_millis(ts).map(|dt| dt.to_rfc3339())
278 });
279
280 let isolated_rate = IsolatedBorrowRate {
281 symbol: symbol.to_string(),
282 base: base.to_string(),
283 base_rate,
284 quote: quote.to_string(),
285 quote_rate,
286 period: 86400000,
287 timestamp,
288 datetime,
289 info: item.clone(),
290 };
291
292 rates_map.insert(symbol.to_string(), isolated_rate);
293 }
294 }
295
296 Ok(rates_map)
297}
298
299pub fn parse_max_borrowable(
301 data: &Value,
302 currency: &str,
303 symbol: Option<String>,
304) -> Result<MaxBorrowable> {
305 let amount = if let Some(amount_str) = data["amount"].as_str() {
306 amount_str.parse::<f64>().unwrap_or(0.0)
307 } else {
308 data["amount"].as_f64().unwrap_or(0.0)
309 };
310
311 let borrow_limit = if let Some(limit_str) = data["borrowLimit"].as_str() {
312 Some(limit_str.parse::<f64>().unwrap_or(0.0))
313 } else {
314 data["borrowLimit"].as_f64()
315 };
316
317 let timestamp = chrono::Utc::now().timestamp_millis();
318 let datetime = chrono::DateTime::from_timestamp_millis(timestamp)
319 .map(|dt| dt.to_rfc3339())
320 .unwrap_or_default();
321
322 Ok(MaxBorrowable {
323 currency: currency.to_string(),
324 amount,
325 borrow_limit,
326 symbol,
327 timestamp,
328 datetime,
329 info: data.clone(),
330 })
331}
332
333pub fn parse_max_transferable(
335 data: &Value,
336 currency: &str,
337 symbol: Option<String>,
338) -> Result<MaxTransferable> {
339 let amount = if let Some(amount_str) = data["amount"].as_str() {
340 amount_str.parse::<f64>().unwrap_or(0.0)
341 } else {
342 data["amount"].as_f64().unwrap_or(0.0)
343 };
344
345 let timestamp = chrono::Utc::now().timestamp_millis();
346 let datetime = chrono::DateTime::from_timestamp_millis(timestamp)
347 .map(|dt| dt.to_rfc3339())
348 .unwrap_or_default();
349
350 Ok(MaxTransferable {
351 currency: currency.to_string(),
352 amount,
353 symbol,
354 timestamp,
355 datetime,
356 info: data.clone(),
357 })
358}