1use serde::{Deserialize, Serialize};
6
7use crate::types::{ExecutionType, KlineInterval, OrderSide, OrderStatus, OrderType, TimeInForce};
8
9use super::market::string_or_float;
10
11#[derive(Debug, Clone, Serialize, Deserialize)]
15#[serde(tag = "e")]
16pub enum WebSocketEvent {
17 #[serde(rename = "aggTrade")]
19 AggTrade(AggTradeEvent),
20 #[serde(rename = "trade")]
22 Trade(TradeEvent),
23 #[serde(rename = "kline")]
25 Kline(KlineEvent),
26 #[serde(rename = "24hrMiniTicker")]
28 MiniTicker(MiniTickerEvent),
29 #[serde(rename = "24hrTicker")]
31 Ticker(TickerEvent),
32 #[serde(rename = "bookTicker")]
34 BookTicker(BookTickerEvent),
35 #[serde(rename = "depthUpdate")]
37 Depth(DepthEvent),
38 #[serde(rename = "outboundAccountPosition")]
40 AccountPosition(AccountPositionEvent),
41 #[serde(rename = "balanceUpdate")]
43 BalanceUpdate(BalanceUpdateEvent),
44 #[serde(rename = "executionReport")]
46 ExecutionReport(ExecutionReportEvent),
47 #[serde(rename = "listStatus")]
49 ListStatus(ListStatusEvent),
50}
51
52#[derive(Debug, Clone, Serialize, Deserialize)]
54pub struct AggTradeEvent {
55 #[serde(rename = "E")]
57 pub event_time: u64,
58 #[serde(rename = "s")]
60 pub symbol: String,
61 #[serde(rename = "a")]
63 pub agg_trade_id: u64,
64 #[serde(rename = "p", with = "string_or_float")]
66 pub price: f64,
67 #[serde(rename = "q", with = "string_or_float")]
69 pub quantity: f64,
70 #[serde(rename = "f")]
72 pub first_trade_id: u64,
73 #[serde(rename = "l")]
75 pub last_trade_id: u64,
76 #[serde(rename = "T")]
78 pub trade_time: u64,
79 #[serde(rename = "m")]
81 pub is_buyer_maker: bool,
82 #[serde(rename = "M")]
84 pub is_best_match: bool,
85}
86
87#[derive(Debug, Clone, Serialize, Deserialize)]
89pub struct TradeEvent {
90 #[serde(rename = "E")]
92 pub event_time: u64,
93 #[serde(rename = "s")]
95 pub symbol: String,
96 #[serde(rename = "t")]
98 pub trade_id: u64,
99 #[serde(rename = "p", with = "string_or_float")]
101 pub price: f64,
102 #[serde(rename = "q", with = "string_or_float")]
104 pub quantity: f64,
105 #[serde(rename = "b")]
107 pub buyer_order_id: u64,
108 #[serde(rename = "a")]
110 pub seller_order_id: u64,
111 #[serde(rename = "T")]
113 pub trade_time: u64,
114 #[serde(rename = "m")]
116 pub is_buyer_maker: bool,
117 #[serde(rename = "M")]
119 pub is_best_match: bool,
120}
121
122#[derive(Debug, Clone, Serialize, Deserialize)]
124pub struct KlineEvent {
125 #[serde(rename = "E")]
127 pub event_time: u64,
128 #[serde(rename = "s")]
130 pub symbol: String,
131 #[serde(rename = "k")]
133 pub kline: KlineData,
134}
135
136#[derive(Debug, Clone, Serialize, Deserialize)]
138pub struct KlineData {
139 #[serde(rename = "t")]
141 pub start_time: i64,
142 #[serde(rename = "T")]
144 pub close_time: i64,
145 #[serde(rename = "s")]
147 pub symbol: String,
148 #[serde(rename = "i")]
150 pub interval: KlineInterval,
151 #[serde(rename = "f")]
153 pub first_trade_id: i64,
154 #[serde(rename = "L")]
156 pub last_trade_id: i64,
157 #[serde(rename = "o", with = "string_or_float")]
159 pub open: f64,
160 #[serde(rename = "c", with = "string_or_float")]
162 pub close: f64,
163 #[serde(rename = "h", with = "string_or_float")]
165 pub high: f64,
166 #[serde(rename = "l", with = "string_or_float")]
168 pub low: f64,
169 #[serde(rename = "v", with = "string_or_float")]
171 pub volume: f64,
172 #[serde(rename = "n")]
174 pub number_of_trades: i64,
175 #[serde(rename = "x")]
177 pub is_closed: bool,
178 #[serde(rename = "q", with = "string_or_float")]
180 pub quote_asset_volume: f64,
181 #[serde(rename = "V", with = "string_or_float")]
183 pub taker_buy_base_volume: f64,
184 #[serde(rename = "Q", with = "string_or_float")]
186 pub taker_buy_quote_volume: f64,
187}
188
189#[derive(Debug, Clone, Serialize, Deserialize)]
191pub struct MiniTickerEvent {
192 #[serde(rename = "E")]
194 pub event_time: u64,
195 #[serde(rename = "s")]
197 pub symbol: String,
198 #[serde(rename = "c", with = "string_or_float")]
200 pub close: f64,
201 #[serde(rename = "o", with = "string_or_float")]
203 pub open: f64,
204 #[serde(rename = "h", with = "string_or_float")]
206 pub high: f64,
207 #[serde(rename = "l", with = "string_or_float")]
209 pub low: f64,
210 #[serde(rename = "v", with = "string_or_float")]
212 pub volume: f64,
213 #[serde(rename = "q", with = "string_or_float")]
215 pub quote_volume: f64,
216}
217
218#[derive(Debug, Clone, Serialize, Deserialize)]
220pub struct TickerEvent {
221 #[serde(rename = "E")]
223 pub event_time: u64,
224 #[serde(rename = "s")]
226 pub symbol: String,
227 #[serde(rename = "p", with = "string_or_float")]
229 pub price_change: f64,
230 #[serde(rename = "P", with = "string_or_float")]
232 pub price_change_percent: f64,
233 #[serde(rename = "w", with = "string_or_float")]
235 pub weighted_avg_price: f64,
236 #[serde(rename = "x", with = "string_or_float")]
238 pub prev_close_price: f64,
239 #[serde(rename = "c", with = "string_or_float")]
241 pub close_price: f64,
242 #[serde(rename = "Q", with = "string_or_float")]
244 pub close_quantity: f64,
245 #[serde(rename = "b", with = "string_or_float")]
247 pub bid_price: f64,
248 #[serde(rename = "B", with = "string_or_float")]
250 pub bid_quantity: f64,
251 #[serde(rename = "a", with = "string_or_float")]
253 pub ask_price: f64,
254 #[serde(rename = "A", with = "string_or_float")]
256 pub ask_quantity: f64,
257 #[serde(rename = "o", with = "string_or_float")]
259 pub open_price: f64,
260 #[serde(rename = "h", with = "string_or_float")]
262 pub high_price: f64,
263 #[serde(rename = "l", with = "string_or_float")]
265 pub low_price: f64,
266 #[serde(rename = "v", with = "string_or_float")]
268 pub volume: f64,
269 #[serde(rename = "q", with = "string_or_float")]
271 pub quote_volume: f64,
272 #[serde(rename = "O")]
274 pub open_time: u64,
275 #[serde(rename = "C")]
277 pub close_time: u64,
278 #[serde(rename = "F")]
280 pub first_trade_id: i64,
281 #[serde(rename = "L")]
283 pub last_trade_id: i64,
284 #[serde(rename = "n")]
286 pub number_of_trades: u64,
287}
288
289#[derive(Debug, Clone, Serialize, Deserialize)]
291pub struct BookTickerEvent {
292 #[serde(rename = "u")]
294 pub update_id: u64,
295 #[serde(rename = "s")]
297 pub symbol: String,
298 #[serde(rename = "b", with = "string_or_float")]
300 pub bid_price: f64,
301 #[serde(rename = "B", with = "string_or_float")]
303 pub bid_quantity: f64,
304 #[serde(rename = "a", with = "string_or_float")]
306 pub ask_price: f64,
307 #[serde(rename = "A", with = "string_or_float")]
309 pub ask_quantity: f64,
310}
311
312#[derive(Debug, Clone, Serialize, Deserialize)]
314pub struct DepthEvent {
315 #[serde(rename = "E")]
317 pub event_time: u64,
318 #[serde(rename = "s")]
320 pub symbol: String,
321 #[serde(rename = "U")]
323 pub first_update_id: u64,
324 #[serde(rename = "u")]
326 pub final_update_id: u64,
327 #[serde(rename = "b")]
329 pub bids: Vec<DepthLevel>,
330 #[serde(rename = "a")]
332 pub asks: Vec<DepthLevel>,
333}
334
335#[derive(Debug, Clone, Serialize, Deserialize)]
337pub struct DepthLevel {
338 #[serde(with = "string_or_float")]
340 pub price: f64,
341 #[serde(with = "string_or_float")]
343 pub quantity: f64,
344}
345
346#[derive(Debug, Clone, Serialize, Deserialize)]
348pub struct AccountPositionEvent {
349 #[serde(rename = "E")]
351 pub event_time: u64,
352 #[serde(rename = "u")]
354 pub last_update_time: u64,
355 #[serde(rename = "B")]
357 pub balances: Vec<AccountBalance>,
358}
359
360#[derive(Debug, Clone, Serialize, Deserialize)]
362pub struct AccountBalance {
363 #[serde(rename = "a")]
365 pub asset: String,
366 #[serde(rename = "f", with = "string_or_float")]
368 pub free: f64,
369 #[serde(rename = "l", with = "string_or_float")]
371 pub locked: f64,
372}
373
374#[derive(Debug, Clone, Serialize, Deserialize)]
376pub struct BalanceUpdateEvent {
377 #[serde(rename = "E")]
379 pub event_time: u64,
380 #[serde(rename = "a")]
382 pub asset: String,
383 #[serde(rename = "d", with = "string_or_float")]
385 pub balance_delta: f64,
386 #[serde(rename = "T")]
388 pub clear_time: u64,
389}
390
391#[derive(Debug, Clone, Serialize, Deserialize)]
393pub struct ExecutionReportEvent {
394 #[serde(rename = "E")]
396 pub event_time: u64,
397 #[serde(rename = "s")]
399 pub symbol: String,
400 #[serde(rename = "c")]
402 pub client_order_id: String,
403 #[serde(rename = "S")]
405 pub side: OrderSide,
406 #[serde(rename = "o")]
408 pub order_type: OrderType,
409 #[serde(rename = "f")]
411 pub time_in_force: TimeInForce,
412 #[serde(rename = "q", with = "string_or_float")]
414 pub quantity: f64,
415 #[serde(rename = "p", with = "string_or_float")]
417 pub price: f64,
418 #[serde(rename = "P", with = "string_or_float")]
420 pub stop_price: f64,
421 #[serde(rename = "F", with = "string_or_float")]
423 pub iceberg_quantity: f64,
424 #[serde(rename = "g")]
426 pub order_list_id: i64,
427 #[serde(rename = "C")]
429 pub orig_client_order_id: String,
430 #[serde(rename = "x")]
432 pub execution_type: ExecutionType,
433 #[serde(rename = "X")]
435 pub order_status: OrderStatus,
436 #[serde(rename = "r")]
438 pub reject_reason: String,
439 #[serde(rename = "i")]
441 pub order_id: u64,
442 #[serde(rename = "l", with = "string_or_float")]
444 pub last_executed_quantity: f64,
445 #[serde(rename = "z", with = "string_or_float")]
447 pub cumulative_filled_quantity: f64,
448 #[serde(rename = "L", with = "string_or_float")]
450 pub last_executed_price: f64,
451 #[serde(rename = "n", with = "string_or_float")]
453 pub commission: f64,
454 #[serde(rename = "N")]
456 pub commission_asset: Option<String>,
457 #[serde(rename = "T")]
459 pub transaction_time: u64,
460 #[serde(rename = "t")]
462 pub trade_id: i64,
463 #[serde(rename = "I")]
465 pub ignore_a: u64,
466 #[serde(rename = "w")]
468 pub is_on_book: bool,
469 #[serde(rename = "m")]
471 pub is_maker: bool,
472 #[serde(rename = "M")]
474 pub ignore_b: bool,
475 #[serde(rename = "O")]
477 pub order_creation_time: u64,
478 #[serde(rename = "Z", with = "string_or_float")]
480 pub cumulative_quote_quantity: f64,
481 #[serde(rename = "Y", with = "string_or_float")]
483 pub last_quote_quantity: f64,
484 #[serde(rename = "Q", with = "string_or_float")]
486 pub quote_order_quantity: f64,
487}
488
489#[derive(Debug, Clone, Serialize, Deserialize)]
491pub struct ListStatusEvent {
492 #[serde(rename = "E")]
494 pub event_time: u64,
495 #[serde(rename = "s")]
497 pub symbol: String,
498 #[serde(rename = "g")]
500 pub order_list_id: u64,
501 #[serde(rename = "c")]
503 pub contingency_type: String,
504 #[serde(rename = "l")]
506 pub list_status_type: String,
507 #[serde(rename = "L")]
509 pub list_order_status: String,
510 #[serde(rename = "r")]
512 pub list_reject_reason: String,
513 #[serde(rename = "C")]
515 pub list_client_order_id: String,
516 #[serde(rename = "T")]
518 pub transaction_time: u64,
519 #[serde(rename = "O")]
521 pub orders: Vec<ListStatusOrder>,
522}
523
524#[derive(Debug, Clone, Serialize, Deserialize)]
526pub struct ListStatusOrder {
527 #[serde(rename = "s")]
529 pub symbol: String,
530 #[serde(rename = "i")]
532 pub order_id: u64,
533 #[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}