nautilus_binance/spot/websocket/
streams.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2025 Nautech Systems Pty Ltd. All rights reserved.
3//  https://nautechsystems.io
4//
5//  Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
6//  You may not use this file except in compliance with the License.
7//  You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
8//
9//  Unless required by applicable law or agreed to in writing, software
10//  distributed under the License is distributed on an "AS IS" BASIS,
11//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//  See the License for the specific language governing permissions and
13//  limitations under the License.
14// -------------------------------------------------------------------------------------------------
15
16//! Stream subscription management for Binance Spot WebSocket.
17//!
18//! ## Stream Names
19//!
20//! - `<symbol>@trade` - Trade stream
21//! - `<symbol>@bestBidAsk` - Best bid/ask stream (with auto-culling)
22//! - `<symbol>@depth` - Diff depth stream (50ms updates)
23//! - `<symbol>@depth20` - Partial book depth (top 20 levels, 50ms updates)
24//!
25//! ## Connection URL Patterns
26//!
27//! Single stream: `/ws/<streamName>`
28//! Multiple streams: `/stream?streams=<stream1>/<stream2>/...`
29
30/// Maximum number of streams per connection.
31pub const MAX_STREAMS_PER_CONNECTION: usize = 1024;
32
33/// Stream type for subscription management.
34#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
35pub enum StreamType {
36    /// Trade stream (`<symbol>@trade`).
37    Trade,
38    /// Best bid/ask stream (`<symbol>@bestBidAsk`).
39    BestBidAsk,
40    /// Diff depth stream (`<symbol>@depth`).
41    DepthDiff,
42    /// Partial book depth stream (`<symbol>@depth<N>`).
43    DepthSnapshot { levels: u8 },
44}
45
46impl StreamType {
47    /// Build stream name for a symbol.
48    #[must_use]
49    pub fn stream_name(&self, symbol: &str) -> String {
50        let symbol_lower = symbol.to_lowercase();
51        match self {
52            Self::Trade => format!("{symbol_lower}@trade"),
53            Self::BestBidAsk => format!("{symbol_lower}@bestBidAsk"),
54            Self::DepthDiff => format!("{symbol_lower}@depth"),
55            Self::DepthSnapshot { levels } => format!("{symbol_lower}@depth{levels}"),
56        }
57    }
58}
59
60#[cfg(test)]
61mod tests {
62    use rstest::rstest;
63
64    use super::*;
65
66    #[rstest]
67    fn test_stream_names() {
68        assert_eq!(StreamType::Trade.stream_name("BTCUSDT"), "btcusdt@trade");
69        assert_eq!(
70            StreamType::BestBidAsk.stream_name("ETHUSDT"),
71            "ethusdt@bestBidAsk"
72        );
73        assert_eq!(
74            StreamType::DepthDiff.stream_name("BTCUSDT"),
75            "btcusdt@depth"
76        );
77        assert_eq!(
78            StreamType::DepthSnapshot { levels: 20 }.stream_name("BTCUSDT"),
79            "btcusdt@depth20"
80        );
81    }
82}