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