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}