ccxt_exchanges/binance/parser/
mod.rs

1//! Binance data parser module.
2//!
3//! Converts Binance API response data into standardized CCXT format structures.
4
5use rust_decimal::Decimal;
6use rust_decimal::prelude::{FromPrimitive, FromStr};
7use serde_json::Value;
8use std::collections::HashMap;
9
10// Re-export all parser functions
11pub use balance::{parse_balance, parse_balance_with_type};
12pub use funding::{parse_funding_rate, parse_funding_rate_history};
13pub use margin::{parse_margin_adjustment, parse_margin_loan};
14pub use market::{parse_currencies, parse_market};
15pub use misc::{parse_mark_prices, parse_stats_24hr, parse_trading_fee, parse_trading_limits};
16pub use ohlcv::{parse_ohlcvs, parse_ws_ohlcv};
17pub use order::parse_order;
18pub use orderbook::{parse_orderbook, parse_ws_orderbook};
19pub use position::{parse_leverage, parse_leverage_tier, parse_position};
20pub use ticker::{
21    parse_bids_asks, parse_last_prices, parse_ticker, parse_ws_bid_ask, parse_ws_ticker,
22};
23pub use trade::{parse_agg_trade, parse_trade, parse_ws_trade};
24pub use transaction::{parse_deposit_address, parse_deposit_withdraw_fees, parse_transaction};
25
26mod balance;
27mod funding;
28mod margin;
29mod market;
30mod misc;
31mod ohlcv;
32mod order;
33mod orderbook;
34mod position;
35mod ticker;
36mod trade;
37mod transaction;
38
39// ============================================================================
40// Helper Functions - Type Conversion
41// ============================================================================
42
43/// Parse an f64 value from JSON (supports both string and number formats).
44pub(crate) fn parse_f64(data: &Value, key: &str) -> Option<f64> {
45    data.get(key).and_then(|v| {
46        v.as_f64()
47            .or_else(|| v.as_str().and_then(|s| s.parse::<f64>().ok()))
48    })
49}
50
51/// Parse a `Decimal` value from JSON (supports both string and number formats).
52/// Prioritizes string parsing for maximum precision, falls back to f64 only when necessary.
53pub(crate) fn parse_decimal(data: &Value, key: &str) -> Option<Decimal> {
54    data.get(key).and_then(|v| {
55        // Prioritize string parsing for maximum precision
56        if let Some(s) = v.as_str() {
57            Decimal::from_str(s).ok()
58        } else if let Some(num) = v.as_f64() {
59            // Fallback to f64 only when value is a JSON number
60            Decimal::from_f64(num)
61        } else {
62            None
63        }
64    })
65}
66
67/// Parse a `Decimal` value from JSON, trying multiple keys in order.
68/// Useful when different API responses use different field names for the same value.
69pub(crate) fn parse_decimal_multi(data: &Value, keys: &[&str]) -> Option<Decimal> {
70    for key in keys {
71        if let Some(decimal) = parse_decimal(data, key) {
72            return Some(decimal);
73        }
74    }
75    None
76}
77
78/// Convert a JSON `Value` into a `HashMap<String, Value>`.
79pub(crate) fn value_to_hashmap(data: &Value) -> HashMap<String, Value> {
80    data.as_object()
81        .map(|obj| obj.iter().map(|(k, v)| (k.clone(), v.clone())).collect())
82        .unwrap_or_default()
83}
84
85/// Parse an array of orderbook entries from JSON.
86#[allow(dead_code)]
87pub(crate) fn parse_order_book_entries(data: &Value) -> Vec<ccxt_core::types::OrderBookEntry> {
88    use ccxt_core::types::{
89        OrderBookEntry,
90        financial::{Amount, Price},
91    };
92
93    data.as_array()
94        .map(|arr| {
95            arr.iter()
96                .filter_map(|item| {
97                    let price = if let Some(arr) = item.as_array() {
98                        arr.first()
99                            .and_then(serde_json::Value::as_str)
100                            .and_then(|s| Decimal::from_str(s).ok())
101                    } else {
102                        None
103                    }?;
104
105                    let amount = if let Some(arr) = item.as_array() {
106                        arr.get(1)
107                            .and_then(serde_json::Value::as_str)
108                            .and_then(|s| Decimal::from_str(s).ok())
109                    } else {
110                        None
111                    }?;
112
113                    Some(OrderBookEntry {
114                        price: Price::new(price),
115                        amount: Amount::new(amount),
116                    })
117                })
118                .collect()
119        })
120        .unwrap_or_default()
121}