ccxt_exchanges/binance/parser/
orderbook.rs1#![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
15pub 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
46pub 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
77pub 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
107pub 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
139pub 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}