binance/spot/
incoming_message.rs

1use rust_decimal::Decimal;
2use serde::Deserialize;
3
4use crate::spot::{KlineInterval, StreamName, Timestamp};
5
6#[derive(PartialEq, Deserialize, Debug)]
7#[serde(untagged)]
8pub enum IncomingMessage {
9    Response {
10        result: Option<serde_json::Value>,
11        id: String,
12    },
13    Error {
14        code: i64,
15        msg: String,
16        id: Option<String>,
17    },
18    StreamEvent(CombinedStreamEvent<StreamEvent>),
19}
20
21#[derive(Debug, Deserialize, PartialEq)]
22pub struct CombinedStreamEvent<T> {
23    pub stream: StreamName,
24    pub data: T,
25}
26
27#[derive(PartialEq, Deserialize, Debug)]
28#[serde(tag = "e")]
29pub enum StreamEvent {
30    #[serde(rename = "aggTrade")]
31    AggTrade(EventAggTrade),
32    #[serde(rename = "trade")]
33    Trade(EventTrade),
34    #[serde(rename = "kline")]
35    Kline(EventKline),
36    #[serde(rename = "24hrMiniTicker")]
37    MiniTicker24(EventMiniTicker24),
38}
39
40/// The Aggregate Trade Streams push trade information that is aggregated for a single taker order.
41#[derive(PartialEq, Deserialize, Debug)]
42pub struct EventAggTrade {
43    /// Event time
44    #[serde(rename = "E")]
45    pub event_time: Timestamp,
46    /// Symbol
47    #[serde(rename = "s")]
48    pub symbol: String,
49    /// Aggregate trade ID
50    #[serde(rename = "a")]
51    pub trade_id: i64,
52    /// Price
53    #[serde(rename = "p")]
54    pub price: Decimal,
55    /// Quantity
56    #[serde(rename = "q")]
57    pub qty: Decimal,
58    /// First trade ID
59    #[serde(rename = "f")]
60    pub first_trade_id: i64,
61    /// Last trade ID
62    #[serde(rename = "l")]
63    pub last_trade_id: i64,
64    /// Trade time
65    #[serde(rename = "T")]
66    pub trade_time: Timestamp,
67    /// Is the buyer the market maker?
68    #[serde(rename = "m")]
69    pub is_buyer: bool,
70}
71
72/// The Trade Streams push raw trade information; each trade has a unique buyer and seller.
73#[derive(PartialEq, Deserialize, Debug)]
74pub struct EventTrade {
75    /// Event time
76    #[serde(rename = "E")]
77    pub event_time: Timestamp,
78    /// Symbol
79    #[serde(rename = "s")]
80    pub symbol: String,
81    /// Aggregate trade ID
82    #[serde(rename = "t")]
83    pub trade_id: i64,
84    /// Price
85    #[serde(rename = "p")]
86    pub price: Decimal,
87    /// Quantity
88    #[serde(rename = "q")]
89    pub qty: Decimal,
90    /// Trade time
91    #[serde(rename = "T")]
92    pub trade_time: Timestamp,
93    /// Is the buyer the market maker?
94    #[serde(rename = "m")]
95    pub is_buyer: bool,
96}
97
98/// The Kline/Candlestick Stream push updates to the current klines/candlestick every second in UTC+0 timezone
99#[derive(PartialEq, Deserialize, Debug)]
100pub struct EventKline {
101    /// Event time
102    #[serde(rename = "E")]
103    pub event_time: Timestamp,
104    /// Symbol
105    #[serde(rename = "s")]
106    pub symbol: String,
107    #[serde(rename = "k")]
108    pub kline: KlineMsg,
109}
110#[derive(PartialEq, Deserialize, Debug)]
111pub struct KlineMsg {
112    /// Kline start time
113    #[serde(rename = "t")]
114    pub start_time: Timestamp,
115    /// Kline close time
116    #[serde(rename = "T")]
117    pub close_time: Timestamp,
118    /// Symbol
119    #[serde(rename = "s")]
120    pub symbol: String,
121    /// Interval
122    #[serde(rename = "i")]
123    pub interval: KlineInterval,
124    /// First trade ID
125    #[serde(rename = "f")]
126    pub first_trade_id: i64,
127    /// Last trade ID
128    #[serde(rename = "L")]
129    pub last_trade_id: i64,
130    /// Open price
131    #[serde(rename = "o")]
132    pub open_price: Decimal,
133    /// Close price
134    #[serde(rename = "c")]
135    pub close_price: Decimal,
136    /// High price
137    #[serde(rename = "h")]
138    pub high_price: Decimal,
139    /// Low price
140    #[serde(rename = "l")]
141    pub low_price: Decimal,
142    /// Base asset volume
143    #[serde(rename = "v")]
144    pub base_asset_volume: Decimal,
145    /// Number of trades
146    #[serde(rename = "n")]
147    pub trade_number: i64,
148    /// Is this kline closed?
149    #[serde(rename = "x")]
150    pub is_closed: bool,
151    /// Quote asset volume
152    #[serde(rename = "q")]
153    pub quote_asset_volume: Decimal,
154    /// Taker buy base asset volume
155    #[serde(rename = "V")]
156    pub taker_buy_base_asset_volume: Decimal,
157    /// Taker buy quote asset volume
158    #[serde(rename = "Q")]
159    pub taker_buy_quote_asset_volume: Decimal,
160}
161
162/// 24hr rolling window mini-ticker statistics. These are NOT the statistics of the UTC day, but a 24hr rolling window for the previous 24hrs.
163#[derive(PartialEq, Deserialize, Debug)]
164pub struct EventMiniTicker24 {
165    /// Event time
166    #[serde(rename = "E")]
167    pub event_time: Timestamp,
168    /// Symbol
169    #[serde(rename = "s")]
170    pub symbol: String,
171    /// Open price
172    #[serde(rename = "o")]
173    pub open_price: Decimal,
174    /// Close price
175    #[serde(rename = "c")]
176    pub close_price: Decimal,
177    /// High price
178    #[serde(rename = "h")]
179    pub high_price: Decimal,
180    /// Low price
181    #[serde(rename = "l")]
182    pub low_price: Decimal,
183    /// Total traded base asset volume
184    #[serde(rename = "v")]
185    pub total_base_asset_volume: Decimal,
186    /// Total traded quote asset volume
187    #[serde(rename = "q")]
188    pub total_quote_asset_volume: Decimal,
189}
190
191#[cfg(test)]
192mod tests {
193    use rust_decimal::dec;
194
195    use super::*;
196
197    #[test]
198    fn test_deserialize_combined_stream_event() {
199        let json = r#"{
200            "stream": "bnbbtc@trade",
201            "data": "DATA"
202        }"#;
203        let expected = CombinedStreamEvent {
204            stream: StreamName::Trade {
205                symbol: String::from("BNBBTC").to_lowercase(),
206            },
207            data: String::from("DATA"),
208        };
209
210        let current = serde_json::from_str(json).unwrap();
211
212        assert_eq!(expected, current);
213    }
214
215    #[test]
216    fn test_deserialize_stream_event_agg_trade() {
217        let json = r#"{
218            "e": "aggTrade",
219            "E": 1672515782136,
220            "s": "BNBBTC",
221            "a": 12345,
222            "p": "0.001",
223            "q": "100",
224            "f": 100,
225            "l": 105,
226            "T": 1672515782136,
227            "m": true,
228            "M": true
229        }"#;
230        let expected = EventAggTrade {
231            event_time: 1672515782136,
232            symbol: String::from("BNBBTC"),
233            trade_id: 12345,
234            price: dec!(0.001),
235            qty: dec!(100),
236            first_trade_id: 100,
237            last_trade_id: 105,
238            trade_time: 1672515782136,
239            is_buyer: true,
240        };
241
242        let current = serde_json::from_str(json).unwrap();
243
244        assert_eq!(expected, current);
245    }
246
247    #[test]
248    fn test_deserialize_stream_event_trade() {
249        let json = r#"{
250            "e": "trade",
251            "E": 1672515782136,
252            "s": "BNBBTC",
253            "t": 12345,
254            "p": "0.001",
255            "q": "100",
256            "T": 1672515782136,
257            "m": true,
258            "M": true
259        }"#;
260        let expected = EventTrade {
261            event_time: 1672515782136,
262            symbol: String::from("BNBBTC"),
263            trade_id: 12345,
264            price: dec!(0.001),
265            qty: dec!(100),
266            trade_time: 1672515782136,
267            is_buyer: true,
268        };
269
270        let current = serde_json::from_str(json).unwrap();
271
272        assert_eq!(expected, current);
273    }
274
275    #[test]
276    fn test_deserialize_stream_event_kline() {
277        let json = r#"{
278            "e": "kline",
279            "E": 1672515782136,
280            "s": "BNBBTC",
281            "k": {
282                "t": 1672515780000,
283                "T": 1672515839999,
284                "s": "BNBBTC",
285                "i": "1m",
286                "f": 100,
287                "L": 200,
288                "o": "0.0010",
289                "c": "0.0020",
290                "h": "0.0025",
291                "l": "0.0015",
292                "v": "1000",
293                "n": 100,
294                "x": false,
295                "q": "1.0000",
296                "V": "500",
297                "Q": "0.500",
298                "B": "123456"
299            }
300        }"#;
301        let symbol = String::from("BNBBTC");
302        let expected = EventKline {
303            event_time: 1672515782136,
304            symbol: symbol.clone(),
305            kline: KlineMsg {
306                start_time: 1672515780000,
307                close_time: 1672515839999,
308                symbol,
309                interval: KlineInterval::Minute1,
310                first_trade_id: 100,
311                last_trade_id: 200,
312                open_price: dec!(0.0010),
313                close_price: dec!(0.0020),
314                high_price: dec!(0.0025),
315                low_price: dec!(0.0015),
316                base_asset_volume: dec!(1000),
317                trade_number: 100,
318                is_closed: false,
319                quote_asset_volume: dec!(1.0000),
320                taker_buy_base_asset_volume: dec!(500),
321                taker_buy_quote_asset_volume: dec!(0.500),
322            },
323        };
324
325        let current = serde_json::from_str(json).unwrap();
326
327        assert_eq!(expected, current);
328    }
329
330    #[test]
331    fn test_deserialize_stream_event_mini_ticker24() {
332        let json = r#"{
333            "e": "24hrMiniTicker",
334            "E": 1672515782136,
335            "s": "BNBBTC",
336            "c": "0.0025",
337            "o": "0.0010",
338            "h": "0.0025",
339            "l": "0.0010",
340            "v": "10000",
341            "q": "18"
342        }"#;
343        let expected = EventMiniTicker24 {
344            event_time: 1672515782136,
345            symbol: String::from("BNBBTC"),
346            open_price: dec!(0.0010),
347            close_price: dec!(0.0025),
348            high_price: dec!(0.0025),
349            low_price: dec!(0.0010),
350            total_base_asset_volume: dec!(10000),
351            total_quote_asset_volume: dec!(18),
352        };
353
354        let current = serde_json::from_str(json).unwrap();
355
356        assert_eq!(expected, current);
357    }
358}