ccxt_exchanges/binance/parser/
balance.rs1use super::{parse_decimal, value_to_hashmap};
2use ccxt_core::{
3 Result,
4 error::{Error, ParseError},
5 types::{Balance, BalanceEntry},
6};
7use rust_decimal::Decimal;
8use rust_decimal::prelude::FromPrimitive;
9use serde_json::Value;
10use std::collections::HashMap;
11
12pub fn parse_balance(data: &Value) -> Result<Balance> {
14 let mut balances = HashMap::new();
15
16 if let Some(balances_array) = data["balances"].as_array() {
17 for balance in balances_array {
18 let currency = balance["asset"]
19 .as_str()
20 .ok_or_else(|| Error::from(ParseError::missing_field("asset")))?
21 .to_string();
22
23 let free = parse_decimal(balance, "free").unwrap_or(Decimal::ZERO);
24 let locked = parse_decimal(balance, "locked").unwrap_or(Decimal::ZERO);
25 let total = free + locked;
26
27 balances.insert(
28 currency,
29 BalanceEntry {
30 free,
31 used: locked,
32 total,
33 },
34 );
35 }
36 }
37
38 Ok(Balance {
39 balances,
40 info: value_to_hashmap(data),
41 })
42}
43
44pub fn parse_balance_with_type(data: &Value, account_type: &str) -> Result<Balance> {
46 let mut balances = HashMap::new();
47
48 match account_type {
49 "spot" => {
50 if let Some(balances_array) = data["balances"].as_array() {
51 for item in balances_array {
52 if let Some(asset) = item["asset"].as_str() {
53 let free = if let Some(free_str) = item["free"].as_str() {
54 free_str.parse::<f64>().unwrap_or(0.0)
55 } else {
56 item["free"].as_f64().unwrap_or(0.0)
57 };
58
59 let locked = if let Some(locked_str) = item["locked"].as_str() {
60 locked_str.parse::<f64>().unwrap_or(0.0)
61 } else {
62 item["locked"].as_f64().unwrap_or(0.0)
63 };
64
65 balances.insert(
66 asset.to_string(),
67 BalanceEntry {
68 free: Decimal::from_f64(free).unwrap_or(Decimal::ZERO),
69 used: Decimal::from_f64(locked).unwrap_or(Decimal::ZERO),
70 total: Decimal::from_f64(free + locked).unwrap_or(Decimal::ZERO),
71 },
72 );
73 }
74 }
75 }
76 }
77 "margin" | "cross" => {
78 if let Some(user_assets) = data["userAssets"].as_array() {
79 for item in user_assets {
80 if let Some(asset) = item["asset"].as_str() {
81 let free = if let Some(free_str) = item["free"].as_str() {
82 free_str.parse::<f64>().unwrap_or(0.0)
83 } else {
84 item["free"].as_f64().unwrap_or(0.0)
85 };
86
87 let locked = if let Some(locked_str) = item["locked"].as_str() {
88 locked_str.parse::<f64>().unwrap_or(0.0)
89 } else {
90 item["locked"].as_f64().unwrap_or(0.0)
91 };
92
93 balances.insert(
94 asset.to_string(),
95 BalanceEntry {
96 free: Decimal::from_f64(free).unwrap_or(Decimal::ZERO),
97 used: Decimal::from_f64(locked).unwrap_or(Decimal::ZERO),
98 total: Decimal::from_f64(free + locked).unwrap_or(Decimal::ZERO),
99 },
100 );
101 }
102 }
103 }
104 }
105 "isolated" => {
106 if let Some(assets) = data["assets"].as_array() {
107 for item in assets {
108 if let Some(base_asset) = item["baseAsset"].as_object() {
109 if let Some(asset) = base_asset["asset"].as_str() {
110 let free = if let Some(free_str) = base_asset["free"].as_str() {
111 free_str.parse::<f64>().unwrap_or(0.0)
112 } else {
113 base_asset["free"].as_f64().unwrap_or(0.0)
114 };
115
116 let locked = if let Some(locked_str) = base_asset["locked"].as_str() {
117 locked_str.parse::<f64>().unwrap_or(0.0)
118 } else {
119 base_asset["locked"].as_f64().unwrap_or(0.0)
120 };
121
122 balances.insert(
123 asset.to_string(),
124 BalanceEntry {
125 free: Decimal::from_f64(free).unwrap_or(Decimal::ZERO),
126 used: Decimal::from_f64(locked).unwrap_or(Decimal::ZERO),
127 total: Decimal::from_f64(free + locked)
128 .unwrap_or(Decimal::ZERO),
129 },
130 );
131 }
132 }
133
134 if let Some(quote_asset) = item["quoteAsset"].as_object() {
135 if let Some(asset) = quote_asset["asset"].as_str() {
136 let free = if let Some(free_str) = quote_asset["free"].as_str() {
137 free_str.parse::<f64>().unwrap_or(0.0)
138 } else {
139 quote_asset["free"].as_f64().unwrap_or(0.0)
140 };
141
142 let locked = if let Some(locked_str) = quote_asset["locked"].as_str() {
143 locked_str.parse::<f64>().unwrap_or(0.0)
144 } else {
145 quote_asset["locked"].as_f64().unwrap_or(0.0)
146 };
147
148 balances.insert(
149 asset.to_string(),
150 BalanceEntry {
151 free: Decimal::from_f64(free).unwrap_or(Decimal::ZERO),
152 used: Decimal::from_f64(locked).unwrap_or(Decimal::ZERO),
153 total: Decimal::from_f64(free + locked)
154 .unwrap_or(Decimal::ZERO),
155 },
156 );
157 }
158 }
159 }
160 }
161 }
162 "linear" | "future" | "inverse" | "delivery" => {
163 let assets = if let Some(arr) = data.as_array() {
164 arr.clone()
165 } else if let Some(arr) = data["assets"].as_array() {
166 arr.clone()
167 } else {
168 vec![]
169 };
170
171 for item in &assets {
172 if let Some(asset) = item["asset"].as_str() {
173 let available_balance =
174 if let Some(balance_str) = item["availableBalance"].as_str() {
175 balance_str.parse::<f64>().unwrap_or(0.0)
176 } else {
177 item["availableBalance"].as_f64().unwrap_or(0.0)
178 };
179
180 let wallet_balance = if let Some(balance_str) = item["walletBalance"].as_str() {
181 balance_str.parse::<f64>().unwrap_or(0.0)
182 } else {
183 item["walletBalance"].as_f64().unwrap_or(0.0)
184 };
185
186 let wallet_balance = if wallet_balance == 0.0 {
187 if let Some(balance_str) = item["balance"].as_str() {
188 balance_str.parse::<f64>().unwrap_or(0.0)
189 } else {
190 item["balance"].as_f64().unwrap_or(wallet_balance)
191 }
192 } else {
193 wallet_balance
194 };
195
196 let used = wallet_balance - available_balance;
197
198 balances.insert(
199 asset.to_string(),
200 BalanceEntry {
201 free: Decimal::from_f64(available_balance).unwrap_or(Decimal::ZERO),
202 used: Decimal::from_f64(used).unwrap_or(Decimal::ZERO),
203 total: Decimal::from_f64(wallet_balance).unwrap_or(Decimal::ZERO),
204 },
205 );
206 }
207 }
208 }
209 "funding" => {
210 if let Some(assets) = data.as_array() {
211 for item in assets {
212 if let Some(asset) = item["asset"].as_str() {
213 let free = if let Some(free_str) = item["free"].as_str() {
214 free_str.parse::<f64>().unwrap_or(0.0)
215 } else {
216 item["free"].as_f64().unwrap_or(0.0)
217 };
218
219 let locked = if let Some(locked_str) = item["locked"].as_str() {
220 locked_str.parse::<f64>().unwrap_or(0.0)
221 } else {
222 item["locked"].as_f64().unwrap_or(0.0)
223 };
224
225 let total = free + locked;
226
227 balances.insert(
228 asset.to_string(),
229 BalanceEntry {
230 free: Decimal::from_f64(free).unwrap_or(Decimal::ZERO),
231 used: Decimal::from_f64(locked).unwrap_or(Decimal::ZERO),
232 total: Decimal::from_f64(total).unwrap_or(Decimal::ZERO),
233 },
234 );
235 }
236 }
237 }
238 }
239 "option" => {
240 if let Some(asset) = data["asset"].as_str() {
241 let equity = if let Some(equity_str) = data["equity"].as_str() {
242 equity_str.parse::<f64>().unwrap_or(0.0)
243 } else {
244 data["equity"].as_f64().unwrap_or(0.0)
245 };
246
247 let available = if let Some(available_str) = data["available"].as_str() {
248 available_str.parse::<f64>().unwrap_or(0.0)
249 } else {
250 data["available"].as_f64().unwrap_or(0.0)
251 };
252
253 let used = equity - available;
254
255 balances.insert(
256 asset.to_string(),
257 BalanceEntry {
258 free: Decimal::from_f64(available).unwrap_or(Decimal::ZERO),
259 used: Decimal::from_f64(used).unwrap_or(Decimal::ZERO),
260 total: Decimal::from_f64(equity).unwrap_or(Decimal::ZERO),
261 },
262 );
263 }
264 }
265 "portfolio" => {
266 let assets = if let Some(arr) = data.as_array() {
267 arr.clone()
268 } else if data.is_object() {
269 vec![data.clone()]
270 } else {
271 vec![]
272 };
273
274 for item in &assets {
275 if let Some(asset) = item["asset"].as_str() {
276 let total_wallet_balance =
277 if let Some(balance_str) = item["totalWalletBalance"].as_str() {
278 balance_str.parse::<f64>().unwrap_or(0.0)
279 } else {
280 item["totalWalletBalance"].as_f64().unwrap_or(0.0)
281 };
282
283 let available_balance =
284 if let Some(balance_str) = item["availableBalance"].as_str() {
285 balance_str.parse::<f64>().unwrap_or(0.0)
286 } else {
287 item["availableBalance"].as_f64().unwrap_or(0.0)
288 };
289
290 let free = if available_balance > 0.0 {
291 available_balance
292 } else if let Some(cross_str) = item["crossWalletBalance"].as_str() {
293 cross_str.parse::<f64>().unwrap_or(0.0)
294 } else {
295 item["crossWalletBalance"].as_f64().unwrap_or(0.0)
296 };
297
298 let used = total_wallet_balance - free;
299
300 balances.insert(
301 asset.to_string(),
302 BalanceEntry {
303 free: Decimal::from_f64(free).unwrap_or(Decimal::ZERO),
304 used: Decimal::from_f64(used).unwrap_or(Decimal::ZERO),
305 total: Decimal::from_f64(total_wallet_balance).unwrap_or(Decimal::ZERO),
306 },
307 );
308 }
309 }
310 }
311 _ => {
312 return Err(Error::from(ParseError::invalid_value(
313 "account_type",
314 format!("Unsupported account type: {}", account_type),
315 )));
316 }
317 }
318
319 let mut info_map = HashMap::new();
320 if let Some(obj) = data.as_object() {
321 for (k, v) in obj {
322 info_map.insert(k.clone(), v.clone());
323 }
324 }
325
326 Ok(Balance {
327 balances,
328 info: info_map,
329 })
330}