rusty_feeder/exchange/binance/futures/data/
orderbook.rs

1//! Order book message types for Binance Futures WebSocket API
2//!
3//! This module provides optimized data structures for processing
4//! order book messages from Binance Futures WebSocket streams.
5
6use rust_decimal::Decimal;
7use rust_decimal_macros::dec;
8use serde::{Deserialize, Serialize};
9use smallvec::SmallVec;
10use smartstring::alias::String;
11
12/// Depth update message from Binance Futures WebSocket (partial or diff)
13#[derive(Debug, Clone, Serialize, Deserialize)]
14#[repr(align(16))] // Align for better memory access
15pub struct OrderbookMessage {
16    /// Event type
17    #[serde(rename = "e")]
18    pub event_type: String,
19
20    /// Event time
21    #[serde(rename = "E")]
22    pub event_time: u64,
23
24    /// Transaction time
25    #[serde(rename = "T")]
26    pub transaction_time: u64,
27
28    /// Symbol
29    #[serde(rename = "s")]
30    pub symbol: String,
31
32    /// First update ID in event
33    #[serde(rename = "U")]
34    pub first_update_id: u64,
35
36    /// Final update ID in event
37    #[serde(rename = "u")]
38    pub final_update_id: u64,
39
40    /// Bids to update
41    #[serde(rename = "b")]
42    pub bids: SmallVec<[SmallVec<[String; 2]>; 32]>,
43
44    /// Asks to update
45    #[serde(rename = "a")]
46    pub asks: SmallVec<[SmallVec<[String; 2]>; 32]>,
47}
48
49/// Parsed orderbook data with Decimal values
50#[derive(Debug, Clone)]
51#[repr(align(16))] // Align for better memory access
52pub struct ParsedOrderbookData {
53    /// Symbol
54    pub symbol: String,
55
56    /// First update ID
57    pub first_update_id: u64,
58
59    /// Final update ID
60    pub final_update_id: u64,
61
62    /// Event timestamp
63    pub event_time: u64,
64
65    /// Transaction timestamp
66    pub transaction_time: u64,
67
68    /// Bid updates [price, quantity]
69    pub bids: SmallVec<[(Decimal, Decimal); 32]>,
70
71    /// Ask updates [price, quantity]
72    pub asks: SmallVec<[(Decimal, Decimal); 32]>,
73}
74
75impl From<OrderbookMessage> for ParsedOrderbookData {
76    fn from(msg: OrderbookMessage) -> Self {
77        let mut bids = SmallVec::with_capacity(msg.bids.len());
78        let mut asks = SmallVec::with_capacity(msg.asks.len());
79
80        // Parse bids and insert in sorted order (descending)
81        for bid in msg.bids {
82            if bid.len() >= 2 {
83                let price = bid[0].parse().unwrap_or(dec!(0));
84                let quantity = bid[1].parse().unwrap_or(dec!(0));
85
86                // Find the correct insertion position using binary search
87                // For bids, we want descending order (higher prices first)
88                let insert_pos = match bids.binary_search_by(|existing: &(Decimal, Decimal)| {
89                    existing.0.cmp(&price).reverse()
90                }) {
91                    Ok(pos) => pos,  // Exact match (shouldn't happen with unique prices)
92                    Err(pos) => pos, // This is where we should insert
93                };
94
95                bids.insert(insert_pos, (price, quantity));
96            }
97        }
98
99        // Parse asks and insert in sorted order (ascending)
100        for ask in msg.asks {
101            if ask.len() >= 2 {
102                let price = ask[0].parse().unwrap_or(dec!(0));
103                let quantity = ask[1].parse().unwrap_or(dec!(0));
104
105                // Find the correct insertion position using binary search
106                // For asks, we want ascending order (lower prices first)
107                let insert_pos = match asks
108                    .binary_search_by(|existing: &(Decimal, Decimal)| existing.0.cmp(&price))
109                {
110                    Ok(pos) => pos,  // Exact match (shouldn't happen with unique prices)
111                    Err(pos) => pos, // This is where we should insert
112                };
113
114                asks.insert(insert_pos, (price, quantity));
115            }
116        }
117
118        ParsedOrderbookData {
119            symbol: msg.symbol,
120            first_update_id: msg.first_update_id,
121            final_update_id: msg.final_update_id,
122            event_time: msg.event_time,
123            transaction_time: msg.transaction_time,
124            bids,
125            asks,
126        }
127    }
128}
129
130/// Orderbook snapshot message from Binance Futures REST API
131#[derive(Debug, Clone, Serialize, Deserialize)]
132pub struct OrderbookSnapshot {
133    /// Last update ID
134    #[serde(rename = "lastUpdateId")]
135    pub last_update_id: u64,
136
137    /// Message output time (UNIX timestamp in ms)
138    #[serde(rename = "E")]
139    pub event_time: u64,
140
141    /// Transaction time (UNIX timestamp in ms)
142    #[serde(rename = "T")]
143    pub transaction_time: u64,
144
145    /// Bid price and quantity pairs
146    pub bids: SmallVec<[SmallVec<[String; 2]>; 32]>,
147
148    /// Ask price and quantity pairs
149    pub asks: SmallVec<[SmallVec<[String; 2]>; 32]>,
150}
151
152/// Parsed orderbook snapshot with Decimal values
153#[derive(Debug, Clone)]
154#[repr(align(16))] // Align for better memory access
155pub struct ParsedOrderbookSnapshot {
156    /// Last update ID
157    pub last_update_id: u64,
158
159    /// Event timestamp
160    pub event_time: u64,
161
162    /// Transaction timestamp
163    pub transaction_time: u64,
164
165    /// Bid updates [price, quantity]
166    pub bids: SmallVec<[(Decimal, Decimal); 32]>,
167
168    /// Ask updates [price, quantity]
169    pub asks: SmallVec<[(Decimal, Decimal); 32]>,
170}
171
172impl From<OrderbookSnapshot> for ParsedOrderbookSnapshot {
173    fn from(snapshot: OrderbookSnapshot) -> Self {
174        let mut bids = SmallVec::with_capacity(snapshot.bids.len());
175        let mut asks = SmallVec::with_capacity(snapshot.asks.len());
176
177        // Parse bids and insert in sorted order (descending)
178        for bid in snapshot.bids {
179            if bid.len() >= 2 {
180                let price = bid[0].parse().unwrap_or(dec!(0));
181                let quantity = bid[1].parse().unwrap_or(dec!(0));
182
183                // Find the correct insertion position using binary search
184                // For bids, we want descending order (higher prices first)
185                let insert_pos = match bids.binary_search_by(|existing: &(Decimal, Decimal)| {
186                    existing.0.cmp(&price).reverse()
187                }) {
188                    Ok(pos) => pos,  // Exact match (shouldn't happen with unique prices)
189                    Err(pos) => pos, // This is where we should insert
190                };
191
192                bids.insert(insert_pos, (price, quantity));
193            }
194        }
195
196        // Parse asks and insert in sorted order (ascending)
197        for ask in snapshot.asks {
198            if ask.len() >= 2 {
199                let price = ask[0].parse().unwrap_or(dec!(0));
200                let quantity = ask[1].parse().unwrap_or(dec!(0));
201
202                // Find the correct insertion position using binary search
203                // For asks, we want ascending order (lower prices first)
204                let insert_pos = match asks
205                    .binary_search_by(|existing: &(Decimal, Decimal)| existing.0.cmp(&price))
206                {
207                    Ok(pos) => pos,  // Exact match (shouldn't happen with unique prices)
208                    Err(pos) => pos, // This is where we should insert
209                };
210
211                asks.insert(insert_pos, (price, quantity));
212            }
213        }
214
215        ParsedOrderbookSnapshot {
216            last_update_id: snapshot.last_update_id,
217            event_time: snapshot.event_time,
218            transaction_time: snapshot.transaction_time,
219            bids,
220            asks,
221        }
222    }
223}
224
225/// Mark price and funding rate from Binance Futures WebSocket
226#[derive(Debug, Clone, Serialize, Deserialize)]
227#[repr(align(16))] // Align for better memory access
228pub struct MarkPriceMessage {
229    /// Event type
230    #[serde(rename = "e")]
231    pub event_type: String,
232
233    /// Event time
234    #[serde(rename = "E")]
235    pub event_time: u64,
236
237    /// Symbol
238    #[serde(rename = "s")]
239    pub symbol: String,
240
241    /// Mark price
242    #[serde(rename = "p")]
243    pub mark_price: String,
244
245    /// Index price
246    #[serde(rename = "i")]
247    pub index_price: String,
248
249    /// Funding rate
250    #[serde(rename = "r")]
251    pub funding_rate: String,
252
253    /// Next funding time
254    #[serde(rename = "T")]
255    pub next_funding_time: u64,
256}
257
258/// Parsed mark price with Decimal values
259#[derive(Debug, Clone)]
260#[repr(align(16))] // Align for better memory access
261pub struct ParsedMarkPrice {
262    /// Symbol
263    pub symbol: String,
264
265    /// Mark price
266    pub mark_price: Decimal,
267
268    /// Index price
269    pub index_price: Decimal,
270
271    /// Funding rate
272    pub funding_rate: Decimal,
273
274    /// Next funding time
275    pub next_funding_time: u64,
276
277    /// Event timestamp
278    pub event_time: u64,
279}
280
281impl From<MarkPriceMessage> for ParsedMarkPrice {
282    fn from(msg: MarkPriceMessage) -> Self {
283        ParsedMarkPrice {
284            symbol: msg.symbol,
285            mark_price: msg.mark_price.parse().unwrap_or(dec!(0)),
286            index_price: msg.index_price.parse().unwrap_or(dec!(0)),
287            funding_rate: msg.funding_rate.parse().unwrap_or(dec!(0)),
288            next_funding_time: msg.next_funding_time,
289            event_time: msg.event_time,
290        }
291    }
292}