Skip to main content

ccxt_exchanges/binance/parser/
orderbook.rs

1#![allow(dead_code)]
2
3use super::value_to_hashmap;
4use ccxt_core::{
5    Result,
6    error::{Error, ParseError},
7    types::{
8        OrderBook, OrderBookDelta, OrderBookEntry,
9        financial::{Amount, Price},
10    },
11};
12use rust_decimal::Decimal;
13use serde_json::Value;
14
15/// Parse orderbook data from Binance depth response.
16pub fn parse_orderbook(data: &Value, symbol: String) -> Result<OrderBook> {
17    let timestamp = data["T"]
18        .as_i64()
19        .or_else(|| data["E"].as_i64())
20        .unwrap_or_else(|| chrono::Utc::now().timestamp_millis());
21
22    let bids = parse_orderbook_side(&data["bids"])?;
23    let asks = parse_orderbook_side(&data["asks"])?;
24
25    Ok(OrderBook {
26        symbol,
27        timestamp,
28        datetime: Some({
29            chrono::DateTime::from_timestamp_millis(timestamp)
30                .map(|dt| dt.to_rfc3339())
31                .unwrap_or_default()
32        }),
33        nonce: data["lastUpdateId"].as_i64(),
34        bids,
35        asks,
36        buffered_deltas: std::collections::VecDeque::new(),
37        bids_map: std::collections::BTreeMap::new(),
38        asks_map: std::collections::BTreeMap::new(),
39        is_synced: false,
40        needs_resync: false,
41        last_resync_time: 0,
42        info: value_to_hashmap(data),
43    })
44}
45
46/// Parse WebSocket orderbook data from Binance depth stream.
47pub fn parse_ws_orderbook(data: &Value, symbol: String) -> Result<OrderBook> {
48    let timestamp = data["E"]
49        .as_i64()
50        .or_else(|| data["T"].as_i64())
51        .unwrap_or_else(|| chrono::Utc::now().timestamp_millis());
52
53    let bids = parse_orderbook_side(&data["b"])?;
54    let asks = parse_orderbook_side(&data["a"])?;
55
56    Ok(OrderBook {
57        symbol,
58        timestamp,
59        datetime: Some(
60            chrono::DateTime::from_timestamp_millis(timestamp)
61                .map(|dt| dt.to_rfc3339())
62                .unwrap_or_default(),
63        ),
64        nonce: data["u"].as_i64(),
65        bids,
66        asks,
67        buffered_deltas: std::collections::VecDeque::new(),
68        bids_map: std::collections::BTreeMap::new(),
69        asks_map: std::collections::BTreeMap::new(),
70        is_synced: false,
71        needs_resync: false,
72        last_resync_time: 0,
73        info: value_to_hashmap(data),
74    })
75}
76
77/// Parse WebSocket orderbook delta data from Binance diff depth stream.
78pub fn parse_ws_orderbook_delta(data: &Value, symbol: String) -> Result<OrderBookDelta> {
79    let first_update_id = data["U"]
80        .as_i64()
81        .ok_or_else(|| Error::from(ParseError::missing_field("U (first_update_id)")))?;
82
83    let final_update_id = data["u"]
84        .as_i64()
85        .ok_or_else(|| Error::from(ParseError::missing_field("u (final_update_id)")))?;
86
87    let prev_final_update_id = data["pu"].as_i64();
88
89    let timestamp = data["E"]
90        .as_i64()
91        .unwrap_or_else(|| chrono::Utc::now().timestamp_millis());
92
93    let bids = parse_orderbook_side_ws(&data["b"])?;
94    let asks = parse_orderbook_side_ws(&data["a"])?;
95
96    Ok(OrderBookDelta {
97        symbol,
98        first_update_id,
99        final_update_id,
100        prev_final_update_id,
101        timestamp,
102        bids,
103        asks,
104    })
105}
106
107/// Parse one side (bids or asks) of orderbook data.
108pub fn parse_orderbook_side(data: &Value) -> Result<Vec<OrderBookEntry>> {
109    let array = data
110        .as_array()
111        .ok_or_else(|| Error::from(ParseError::invalid_value("data", "orderbook side")))?;
112
113    let mut result = Vec::new();
114
115    for item in array {
116        if let Some(arr) = item.as_array() {
117            if arr.len() >= 2 {
118                let price = arr[0]
119                    .as_str()
120                    .and_then(|s| s.parse::<f64>().ok())
121                    .and_then(Decimal::from_f64_retain)
122                    .ok_or_else(|| Error::from(ParseError::invalid_value("data", "price")))?;
123                let amount = arr[1]
124                    .as_str()
125                    .and_then(|s| s.parse::<f64>().ok())
126                    .and_then(Decimal::from_f64_retain)
127                    .ok_or_else(|| Error::from(ParseError::invalid_value("data", "amount")))?;
128                result.push(OrderBookEntry {
129                    price: Price::new(price),
130                    amount: Amount::new(amount),
131                });
132            }
133        }
134    }
135
136    Ok(result)
137}
138
139/// Parse one side (bids or asks) of WebSocket orderbook delta data.
140pub fn parse_orderbook_side_ws(data: &Value) -> Result<Vec<OrderBookEntry>> {
141    let Some(array) = data.as_array() else {
142        return Ok(Vec::new());
143    };
144
145    let mut result = Vec::with_capacity(array.len());
146
147    for item in array {
148        if let Some(arr) = item.as_array() {
149            if arr.len() >= 2 {
150                let price = arr[0]
151                    .as_str()
152                    .and_then(|s| s.parse::<f64>().ok())
153                    .and_then(Decimal::from_f64_retain)
154                    .ok_or_else(|| Error::from(ParseError::invalid_value("data", "price")))?;
155                let amount = arr[1]
156                    .as_str()
157                    .and_then(|s| s.parse::<f64>().ok())
158                    .and_then(Decimal::from_f64_retain)
159                    .ok_or_else(|| Error::from(ParseError::invalid_value("data", "amount")))?;
160                result.push(OrderBookEntry {
161                    price: Price::new(price),
162                    amount: Amount::new(amount),
163                });
164            }
165        }
166    }
167
168    Ok(result)
169}