Skip to main content

bybit_rust_api/ws/public/
trade.rs

1//! Public trade stream — real-time executed trades.
2//!
3//! # Topic format
4//! `publicTrade.{symbol}` — e.g. `publicTrade.BTCUSDT`
5
6use serde::Deserialize;
7
8/// A single public trade execution.
9#[derive(Debug, Clone, Deserialize)]
10pub struct PublicTrade {
11    /// Trade ID
12    #[serde(rename = "i")]
13    #[serde(default)]
14    pub trade_id: Option<String>,
15    /// Trade time in milliseconds
16    #[serde(rename = "T")]
17    #[serde(default)]
18    pub timestamp: Option<i64>,
19    /// Symbol
20    #[serde(rename = "s")]
21    #[serde(default)]
22    pub symbol: Option<String>,
23    /// Side: "Buy" or "Sell"
24    #[serde(rename = "S")]
25    #[serde(default)]
26    pub side: Option<String>,
27    /// Trade price
28    #[serde(rename = "p")]
29    #[serde(default)]
30    pub price: Option<String>,
31    /// Trade size/quantity
32    #[serde(rename = "v")]
33    #[serde(default)]
34    pub size: Option<String>,
35    /// Direction (for linear/inverse futures)
36    #[serde(rename = "L")]
37    #[serde(default)]
38    pub direction: Option<String>,
39    /// Is this a block trade?
40    #[serde(rename = "BT")]
41    #[serde(default)]
42    pub block_trade: Option<bool>,
43}
44
45/// Typed wrapper for public trade stream data.
46///
47/// Bybit sends an array of trades in each message.
48pub struct TradeStream;
49
50impl TradeStream {
51    /// Parse raw WS data into a vector of trades.
52    pub fn parse(data: &serde_json::Value) -> serde_json::Result<Vec<PublicTrade>> {
53        serde_json::from_value(data.clone())
54    }
55
56    /// Check if the given topic matches a public trade channel.
57    pub fn matches_topic(topic: &str) -> bool {
58        topic.starts_with("publicTrade.")
59    }
60}
61
62#[cfg(test)]
63mod tests {
64    use super::*;
65
66    #[test]
67    fn test_parse_trades() {
68        let json = serde_json::json!([
69            {
70                "i": "123456",
71                "T": 1672828800000_i64,
72                "s": "BTCUSDT",
73                "S": "Buy",
74                "p": "50000.00",
75                "v": "0.01",
76                "L": "PlusTick",
77                "BT": false
78            }
79        ]);
80
81        let trades = TradeStream::parse(&json).unwrap();
82        assert_eq!(trades.len(), 1);
83        assert_eq!(trades[0].side.as_deref(), Some("Buy"));
84        assert_eq!(trades[0].price.as_deref(), Some("50000.00"));
85    }
86
87    #[test]
88    fn test_matches_topic() {
89        assert!(TradeStream::matches_topic("publicTrade.BTCUSDT"));
90        assert!(!TradeStream::matches_topic("orderbook.1.BTCUSDT"));
91    }
92}