Skip to main content

binance_api_client/models/
websocket.rs

1//! WebSocket event models.
2//!
3//! These models represent events received from Binance WebSocket streams.
4
5use serde::{Deserialize, Serialize};
6
7use crate::types::{ExecutionType, KlineInterval, OrderSide, OrderStatus, OrderType, TimeInForce};
8
9use super::market::string_or_float;
10
11/// WebSocket event wrapper.
12///
13/// All WebSocket events have an "e" field indicating the event type.
14#[derive(Debug, Clone, Serialize, Deserialize)]
15#[serde(tag = "e")]
16pub enum WebSocketEvent {
17    /// Aggregate trade event.
18    #[serde(rename = "aggTrade")]
19    AggTrade(AggTradeEvent),
20    /// Trade event.
21    #[serde(rename = "trade")]
22    Trade(TradeEvent),
23    /// Kline/candlestick event.
24    #[serde(rename = "kline")]
25    Kline(KlineEvent),
26    /// 24hr mini ticker event.
27    #[serde(rename = "24hrMiniTicker")]
28    MiniTicker(MiniTickerEvent),
29    /// 24hr ticker event.
30    #[serde(rename = "24hrTicker")]
31    Ticker(TickerEvent),
32    /// Book ticker event.
33    #[serde(rename = "bookTicker")]
34    BookTicker(BookTickerEvent),
35    /// Depth update event.
36    #[serde(rename = "depthUpdate")]
37    Depth(DepthEvent),
38    /// Account position update (user data stream).
39    #[serde(rename = "outboundAccountPosition")]
40    AccountPosition(AccountPositionEvent),
41    /// Balance update (user data stream).
42    #[serde(rename = "balanceUpdate")]
43    BalanceUpdate(BalanceUpdateEvent),
44    /// Order update (user data stream).
45    #[serde(rename = "executionReport")]
46    ExecutionReport(ExecutionReportEvent),
47    /// OCO order update (user data stream).
48    #[serde(rename = "listStatus")]
49    ListStatus(ListStatusEvent),
50}
51
52/// Aggregate trade event.
53#[derive(Debug, Clone, Serialize, Deserialize)]
54pub struct AggTradeEvent {
55    /// Event time.
56    #[serde(rename = "E")]
57    pub event_time: u64,
58    /// Symbol.
59    #[serde(rename = "s")]
60    pub symbol: String,
61    /// Aggregate trade ID.
62    #[serde(rename = "a")]
63    pub agg_trade_id: u64,
64    /// Price.
65    #[serde(rename = "p", with = "string_or_float")]
66    pub price: f64,
67    /// Quantity.
68    #[serde(rename = "q", with = "string_or_float")]
69    pub quantity: f64,
70    /// First trade ID.
71    #[serde(rename = "f")]
72    pub first_trade_id: u64,
73    /// Last trade ID.
74    #[serde(rename = "l")]
75    pub last_trade_id: u64,
76    /// Trade time.
77    #[serde(rename = "T")]
78    pub trade_time: u64,
79    /// Is buyer the maker.
80    #[serde(rename = "m")]
81    pub is_buyer_maker: bool,
82    /// Ignore.
83    #[serde(rename = "M")]
84    pub is_best_match: bool,
85}
86
87/// Trade event.
88#[derive(Debug, Clone, Serialize, Deserialize)]
89pub struct TradeEvent {
90    /// Event time.
91    #[serde(rename = "E")]
92    pub event_time: u64,
93    /// Symbol.
94    #[serde(rename = "s")]
95    pub symbol: String,
96    /// Trade ID.
97    #[serde(rename = "t")]
98    pub trade_id: u64,
99    /// Price.
100    #[serde(rename = "p", with = "string_or_float")]
101    pub price: f64,
102    /// Quantity.
103    #[serde(rename = "q", with = "string_or_float")]
104    pub quantity: f64,
105    /// Buyer order ID.
106    #[serde(rename = "b")]
107    pub buyer_order_id: u64,
108    /// Seller order ID.
109    #[serde(rename = "a")]
110    pub seller_order_id: u64,
111    /// Trade time.
112    #[serde(rename = "T")]
113    pub trade_time: u64,
114    /// Is buyer the maker.
115    #[serde(rename = "m")]
116    pub is_buyer_maker: bool,
117    /// Ignore.
118    #[serde(rename = "M")]
119    pub is_best_match: bool,
120}
121
122/// Kline/candlestick event.
123#[derive(Debug, Clone, Serialize, Deserialize)]
124pub struct KlineEvent {
125    /// Event time.
126    #[serde(rename = "E")]
127    pub event_time: u64,
128    /// Symbol.
129    #[serde(rename = "s")]
130    pub symbol: String,
131    /// Kline data.
132    #[serde(rename = "k")]
133    pub kline: KlineData,
134}
135
136/// Kline data within a kline event.
137#[derive(Debug, Clone, Serialize, Deserialize)]
138pub struct KlineData {
139    /// Kline start time.
140    #[serde(rename = "t")]
141    pub start_time: i64,
142    /// Kline close time.
143    #[serde(rename = "T")]
144    pub close_time: i64,
145    /// Symbol.
146    #[serde(rename = "s")]
147    pub symbol: String,
148    /// Interval.
149    #[serde(rename = "i")]
150    pub interval: KlineInterval,
151    /// First trade ID.
152    #[serde(rename = "f")]
153    pub first_trade_id: i64,
154    /// Last trade ID.
155    #[serde(rename = "L")]
156    pub last_trade_id: i64,
157    /// Open price.
158    #[serde(rename = "o", with = "string_or_float")]
159    pub open: f64,
160    /// Close price.
161    #[serde(rename = "c", with = "string_or_float")]
162    pub close: f64,
163    /// High price.
164    #[serde(rename = "h", with = "string_or_float")]
165    pub high: f64,
166    /// Low price.
167    #[serde(rename = "l", with = "string_or_float")]
168    pub low: f64,
169    /// Volume.
170    #[serde(rename = "v", with = "string_or_float")]
171    pub volume: f64,
172    /// Number of trades.
173    #[serde(rename = "n")]
174    pub number_of_trades: i64,
175    /// Is this kline closed.
176    #[serde(rename = "x")]
177    pub is_closed: bool,
178    /// Quote asset volume.
179    #[serde(rename = "q", with = "string_or_float")]
180    pub quote_asset_volume: f64,
181    /// Taker buy base asset volume.
182    #[serde(rename = "V", with = "string_or_float")]
183    pub taker_buy_base_volume: f64,
184    /// Taker buy quote asset volume.
185    #[serde(rename = "Q", with = "string_or_float")]
186    pub taker_buy_quote_volume: f64,
187}
188
189/// 24hr mini ticker event.
190#[derive(Debug, Clone, Serialize, Deserialize)]
191pub struct MiniTickerEvent {
192    /// Event time.
193    #[serde(rename = "E")]
194    pub event_time: u64,
195    /// Symbol.
196    #[serde(rename = "s")]
197    pub symbol: String,
198    /// Close price.
199    #[serde(rename = "c", with = "string_or_float")]
200    pub close: f64,
201    /// Open price.
202    #[serde(rename = "o", with = "string_or_float")]
203    pub open: f64,
204    /// High price.
205    #[serde(rename = "h", with = "string_or_float")]
206    pub high: f64,
207    /// Low price.
208    #[serde(rename = "l", with = "string_or_float")]
209    pub low: f64,
210    /// Total traded base asset volume.
211    #[serde(rename = "v", with = "string_or_float")]
212    pub volume: f64,
213    /// Total traded quote asset volume.
214    #[serde(rename = "q", with = "string_or_float")]
215    pub quote_volume: f64,
216}
217
218/// 24hr ticker event.
219#[derive(Debug, Clone, Serialize, Deserialize)]
220pub struct TickerEvent {
221    /// Event time.
222    #[serde(rename = "E")]
223    pub event_time: u64,
224    /// Symbol.
225    #[serde(rename = "s")]
226    pub symbol: String,
227    /// Price change.
228    #[serde(rename = "p", with = "string_or_float")]
229    pub price_change: f64,
230    /// Price change percent.
231    #[serde(rename = "P", with = "string_or_float")]
232    pub price_change_percent: f64,
233    /// Weighted average price.
234    #[serde(rename = "w", with = "string_or_float")]
235    pub weighted_avg_price: f64,
236    /// Previous day's close price.
237    #[serde(rename = "x", with = "string_or_float")]
238    pub prev_close_price: f64,
239    /// Current day's close price.
240    #[serde(rename = "c", with = "string_or_float")]
241    pub close_price: f64,
242    /// Close trade quantity.
243    #[serde(rename = "Q", with = "string_or_float")]
244    pub close_quantity: f64,
245    /// Best bid price.
246    #[serde(rename = "b", with = "string_or_float")]
247    pub bid_price: f64,
248    /// Best bid quantity.
249    #[serde(rename = "B", with = "string_or_float")]
250    pub bid_quantity: f64,
251    /// Best ask price.
252    #[serde(rename = "a", with = "string_or_float")]
253    pub ask_price: f64,
254    /// Best ask quantity.
255    #[serde(rename = "A", with = "string_or_float")]
256    pub ask_quantity: f64,
257    /// Open price.
258    #[serde(rename = "o", with = "string_or_float")]
259    pub open_price: f64,
260    /// High price.
261    #[serde(rename = "h", with = "string_or_float")]
262    pub high_price: f64,
263    /// Low price.
264    #[serde(rename = "l", with = "string_or_float")]
265    pub low_price: f64,
266    /// Total traded base asset volume.
267    #[serde(rename = "v", with = "string_or_float")]
268    pub volume: f64,
269    /// Total traded quote asset volume.
270    #[serde(rename = "q", with = "string_or_float")]
271    pub quote_volume: f64,
272    /// Statistics open time.
273    #[serde(rename = "O")]
274    pub open_time: u64,
275    /// Statistics close time.
276    #[serde(rename = "C")]
277    pub close_time: u64,
278    /// First trade ID.
279    #[serde(rename = "F")]
280    pub first_trade_id: i64,
281    /// Last trade ID.
282    #[serde(rename = "L")]
283    pub last_trade_id: i64,
284    /// Total number of trades.
285    #[serde(rename = "n")]
286    pub number_of_trades: u64,
287}
288
289/// Book ticker event (best bid/ask).
290#[derive(Debug, Clone, Serialize, Deserialize)]
291pub struct BookTickerEvent {
292    /// Update ID.
293    #[serde(rename = "u")]
294    pub update_id: u64,
295    /// Symbol.
296    #[serde(rename = "s")]
297    pub symbol: String,
298    /// Best bid price.
299    #[serde(rename = "b", with = "string_or_float")]
300    pub bid_price: f64,
301    /// Best bid quantity.
302    #[serde(rename = "B", with = "string_or_float")]
303    pub bid_quantity: f64,
304    /// Best ask price.
305    #[serde(rename = "a", with = "string_or_float")]
306    pub ask_price: f64,
307    /// Best ask quantity.
308    #[serde(rename = "A", with = "string_or_float")]
309    pub ask_quantity: f64,
310}
311
312/// Depth update event.
313#[derive(Debug, Clone, Serialize, Deserialize)]
314pub struct DepthEvent {
315    /// Event time.
316    #[serde(rename = "E")]
317    pub event_time: u64,
318    /// Symbol.
319    #[serde(rename = "s")]
320    pub symbol: String,
321    /// First update ID in event.
322    #[serde(rename = "U")]
323    pub first_update_id: u64,
324    /// Final update ID in event.
325    #[serde(rename = "u")]
326    pub final_update_id: u64,
327    /// Bids to be updated.
328    #[serde(rename = "b")]
329    pub bids: Vec<DepthLevel>,
330    /// Asks to be updated.
331    #[serde(rename = "a")]
332    pub asks: Vec<DepthLevel>,
333}
334
335/// Depth level (price/quantity pair).
336#[derive(Debug, Clone, Serialize, Deserialize)]
337pub struct DepthLevel {
338    /// Price level.
339    #[serde(with = "string_or_float")]
340    pub price: f64,
341    /// Quantity.
342    #[serde(with = "string_or_float")]
343    pub quantity: f64,
344}
345
346/// Account position update event (user data stream).
347#[derive(Debug, Clone, Serialize, Deserialize)]
348pub struct AccountPositionEvent {
349    /// Event time.
350    #[serde(rename = "E")]
351    pub event_time: u64,
352    /// Time of last account update.
353    #[serde(rename = "u")]
354    pub last_update_time: u64,
355    /// Balances.
356    #[serde(rename = "B")]
357    pub balances: Vec<AccountBalance>,
358}
359
360/// Account balance in position event.
361#[derive(Debug, Clone, Serialize, Deserialize)]
362pub struct AccountBalance {
363    /// Asset.
364    #[serde(rename = "a")]
365    pub asset: String,
366    /// Free balance.
367    #[serde(rename = "f", with = "string_or_float")]
368    pub free: f64,
369    /// Locked balance.
370    #[serde(rename = "l", with = "string_or_float")]
371    pub locked: f64,
372}
373
374/// Balance update event (user data stream).
375#[derive(Debug, Clone, Serialize, Deserialize)]
376pub struct BalanceUpdateEvent {
377    /// Event time.
378    #[serde(rename = "E")]
379    pub event_time: u64,
380    /// Asset.
381    #[serde(rename = "a")]
382    pub asset: String,
383    /// Balance delta.
384    #[serde(rename = "d", with = "string_or_float")]
385    pub balance_delta: f64,
386    /// Clear time.
387    #[serde(rename = "T")]
388    pub clear_time: u64,
389}
390
391/// Order execution report event (user data stream).
392#[derive(Debug, Clone, Serialize, Deserialize)]
393pub struct ExecutionReportEvent {
394    /// Event time.
395    #[serde(rename = "E")]
396    pub event_time: u64,
397    /// Symbol.
398    #[serde(rename = "s")]
399    pub symbol: String,
400    /// Client order ID.
401    #[serde(rename = "c")]
402    pub client_order_id: String,
403    /// Side.
404    #[serde(rename = "S")]
405    pub side: OrderSide,
406    /// Order type.
407    #[serde(rename = "o")]
408    pub order_type: OrderType,
409    /// Time in force.
410    #[serde(rename = "f")]
411    pub time_in_force: TimeInForce,
412    /// Order quantity.
413    #[serde(rename = "q", with = "string_or_float")]
414    pub quantity: f64,
415    /// Order price.
416    #[serde(rename = "p", with = "string_or_float")]
417    pub price: f64,
418    /// Stop price.
419    #[serde(rename = "P", with = "string_or_float")]
420    pub stop_price: f64,
421    /// Iceberg quantity.
422    #[serde(rename = "F", with = "string_or_float")]
423    pub iceberg_quantity: f64,
424    /// Order list ID.
425    #[serde(rename = "g")]
426    pub order_list_id: i64,
427    /// Original client order ID (for cancel/replace).
428    #[serde(rename = "C")]
429    pub orig_client_order_id: String,
430    /// Current execution type.
431    #[serde(rename = "x")]
432    pub execution_type: ExecutionType,
433    /// Current order status.
434    #[serde(rename = "X")]
435    pub order_status: OrderStatus,
436    /// Order reject reason.
437    #[serde(rename = "r")]
438    pub reject_reason: String,
439    /// Order ID.
440    #[serde(rename = "i")]
441    pub order_id: u64,
442    /// Last executed quantity.
443    #[serde(rename = "l", with = "string_or_float")]
444    pub last_executed_quantity: f64,
445    /// Cumulative filled quantity.
446    #[serde(rename = "z", with = "string_or_float")]
447    pub cumulative_filled_quantity: f64,
448    /// Last executed price.
449    #[serde(rename = "L", with = "string_or_float")]
450    pub last_executed_price: f64,
451    /// Commission amount.
452    #[serde(rename = "n", with = "string_or_float")]
453    pub commission: f64,
454    /// Commission asset.
455    #[serde(rename = "N")]
456    pub commission_asset: Option<String>,
457    /// Transaction time.
458    #[serde(rename = "T")]
459    pub transaction_time: u64,
460    /// Trade ID.
461    #[serde(rename = "t")]
462    pub trade_id: i64,
463    /// Ignore.
464    #[serde(rename = "I")]
465    pub ignore_a: u64,
466    /// Is the order on the book.
467    #[serde(rename = "w")]
468    pub is_on_book: bool,
469    /// Is this trade the maker side.
470    #[serde(rename = "m")]
471    pub is_maker: bool,
472    /// Ignore.
473    #[serde(rename = "M")]
474    pub ignore_b: bool,
475    /// Order creation time.
476    #[serde(rename = "O")]
477    pub order_creation_time: u64,
478    /// Cumulative quote asset transacted quantity.
479    #[serde(rename = "Z", with = "string_or_float")]
480    pub cumulative_quote_quantity: f64,
481    /// Last quote asset transacted quantity.
482    #[serde(rename = "Y", with = "string_or_float")]
483    pub last_quote_quantity: f64,
484    /// Quote order quantity.
485    #[serde(rename = "Q", with = "string_or_float")]
486    pub quote_order_quantity: f64,
487}
488
489/// OCO list status event (user data stream).
490#[derive(Debug, Clone, Serialize, Deserialize)]
491pub struct ListStatusEvent {
492    /// Event time.
493    #[serde(rename = "E")]
494    pub event_time: u64,
495    /// Symbol.
496    #[serde(rename = "s")]
497    pub symbol: String,
498    /// Order list ID.
499    #[serde(rename = "g")]
500    pub order_list_id: u64,
501    /// Contingency type.
502    #[serde(rename = "c")]
503    pub contingency_type: String,
504    /// List status type.
505    #[serde(rename = "l")]
506    pub list_status_type: String,
507    /// List order status.
508    #[serde(rename = "L")]
509    pub list_order_status: String,
510    /// List reject reason.
511    #[serde(rename = "r")]
512    pub list_reject_reason: String,
513    /// List client order ID.
514    #[serde(rename = "C")]
515    pub list_client_order_id: String,
516    /// Transaction time.
517    #[serde(rename = "T")]
518    pub transaction_time: u64,
519    /// Orders.
520    #[serde(rename = "O")]
521    pub orders: Vec<ListStatusOrder>,
522}
523
524/// Order in list status event.
525#[derive(Debug, Clone, Serialize, Deserialize)]
526pub struct ListStatusOrder {
527    /// Symbol.
528    #[serde(rename = "s")]
529    pub symbol: String,
530    /// Order ID.
531    #[serde(rename = "i")]
532    pub order_id: u64,
533    /// Client order ID.
534    #[serde(rename = "c")]
535    pub client_order_id: String,
536}
537
538#[cfg(test)]
539mod tests {
540    use super::*;
541
542    #[test]
543    fn test_agg_trade_event_deserialize() {
544        let json = r#"{
545            "e": "aggTrade",
546            "E": 1234567890123,
547            "s": "BTCUSDT",
548            "a": 12345,
549            "p": "50000.00",
550            "q": "1.5",
551            "f": 100,
552            "l": 105,
553            "T": 1234567890123,
554            "m": true,
555            "M": true
556        }"#;
557
558        let event: WebSocketEvent = serde_json::from_str(json).unwrap();
559        match event {
560            WebSocketEvent::AggTrade(e) => {
561                assert_eq!(e.symbol, "BTCUSDT");
562                assert_eq!(e.agg_trade_id, 12345);
563                assert_eq!(e.price, 50000.0);
564                assert_eq!(e.quantity, 1.5);
565            }
566            _ => panic!("Expected AggTrade event"),
567        }
568    }
569
570    #[test]
571    fn test_book_ticker_event_deserialize() {
572        let json = r#"{
573            "e": "bookTicker",
574            "u": 400900217,
575            "s": "BTCUSDT",
576            "b": "50000.00",
577            "B": "1.5",
578            "a": "50001.00",
579            "A": "2.0"
580        }"#;
581
582        let event: WebSocketEvent = serde_json::from_str(json).unwrap();
583        match event {
584            WebSocketEvent::BookTicker(e) => {
585                assert_eq!(e.symbol, "BTCUSDT");
586                assert_eq!(e.bid_price, 50000.0);
587                assert_eq!(e.ask_price, 50001.0);
588            }
589            _ => panic!("Expected BookTicker event"),
590        }
591    }
592
593    #[test]
594    fn test_mini_ticker_event_deserialize() {
595        let json = r#"{
596            "e": "24hrMiniTicker",
597            "E": 1234567890123,
598            "s": "BTCUSDT",
599            "c": "50000.00",
600            "o": "49000.00",
601            "h": "51000.00",
602            "l": "48000.00",
603            "v": "1000.00",
604            "q": "50000000.00"
605        }"#;
606
607        let event: WebSocketEvent = serde_json::from_str(json).unwrap();
608        match event {
609            WebSocketEvent::MiniTicker(e) => {
610                assert_eq!(e.symbol, "BTCUSDT");
611                assert_eq!(e.close, 50000.0);
612            }
613            _ => panic!("Expected MiniTicker event"),
614        }
615    }
616
617    #[test]
618    fn test_depth_level_deserialize() {
619        let json = r#"["50000.00", "1.5"]"#;
620        let level: DepthLevel = serde_json::from_str(json).unwrap();
621        assert_eq!(level.price, 50000.0);
622        assert_eq!(level.quantity, 1.5);
623    }
624
625    #[test]
626    fn test_account_balance_deserialize() {
627        let json = r#"{"a": "BTC", "f": "1.5", "l": "0.5"}"#;
628        let balance: AccountBalance = serde_json::from_str(json).unwrap();
629        assert_eq!(balance.asset, "BTC");
630        assert_eq!(balance.free, 1.5);
631        assert_eq!(balance.locked, 0.5);
632    }
633}