Skip to main content

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