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;
14use std::str::FromStr;
15
16pub 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
47pub 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
78pub 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
108pub 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
138pub 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}