rusty_feeder/exchange/binance/spot/data/
subscription.rs

1//! Subscription message types for Binance Spot WebSocket API
2//!
3//! This module provides functions for creating subscription messages
4//! for various Binance Spot WebSocket streams.
5
6use serde::{Deserialize, Serialize};
7use smallvec::SmallVec;
8use smartstring::alias::String;
9
10/// Creates a trade stream subscription for the given symbols
11#[inline]
12pub fn create_trade_subscription(
13    id: &str,
14    symbols: SmallVec<[String; 8]>,
15    request_id: Option<u64>,
16    api_key: Option<&str>,
17    test: bool,
18) -> WebSocketSubscribeRequest {
19    let streams = symbols
20        .iter()
21        .map(|s| format!("{}@trade", s.to_lowercase()).into())
22        .collect();
23
24    WebSocketSubscribeRequest {
25        id: id.into(),
26        method: "SUBSCRIBE".into(),
27        params: streams,
28        request_id,
29        api_key: api_key.map(|s| s.into()),
30        test,
31    }
32}
33
34/// Creates an order book stream subscription for the given symbols
35#[inline]
36pub fn create_orderbook_subscription(
37    id: &str,
38    symbols: SmallVec<[String; 8]>,
39    depth: Option<usize>,
40    update_speed_ms: Option<u64>,
41    request_id: Option<u64>,
42    api_key: Option<&str>,
43    test: bool,
44) -> WebSocketSubscribeRequest {
45    // Depth can be 5, 10, or 20
46    let depth_str = match depth {
47        Some(5) => "5",
48        Some(10) => "10",
49        Some(20) => "20",
50        _ => "10", // Default to 10
51    };
52
53    // Update speed can be 100ms or 1000ms (default)
54    let speed_suffix = match update_speed_ms {
55        Some(100) => "@100ms",
56        _ => "",
57    };
58
59    let streams = symbols
60        .iter()
61        .map(|s| format!("{}@depth{}{}", s.to_lowercase(), depth_str, speed_suffix).into())
62        .collect();
63
64    WebSocketSubscribeRequest {
65        id: id.into(),
66        method: "SUBSCRIBE".into(),
67        params: streams,
68        request_id,
69        api_key: api_key.map(|s| s.into()),
70        test,
71    }
72}
73
74/// Creates a ticker stream subscription for the given symbols
75#[inline]
76pub fn create_ticker_subscription(
77    id: &str,
78    symbols: SmallVec<[String; 8]>,
79    request_id: Option<u64>,
80    api_key: Option<&str>,
81    test: bool,
82) -> WebSocketSubscribeRequest {
83    let streams = symbols
84        .iter()
85        .map(|s| format!("{}@ticker", s.to_lowercase()).into())
86        .collect();
87
88    WebSocketSubscribeRequest {
89        id: id.into(),
90        method: "SUBSCRIBE".into(),
91        params: streams,
92        request_id,
93        api_key: api_key.map(|s| s.into()),
94        test,
95    }
96}
97
98/// Creates a diff depth stream subscription for the given symbols
99#[inline]
100pub fn create_diff_depth_subscription(
101    id: &str,
102    symbols: SmallVec<[String; 8]>,
103    request_id: Option<u64>,
104    api_key: Option<&str>,
105    test: bool,
106) -> WebSocketSubscribeRequest {
107    let streams = symbols
108        .iter()
109        .map(|s| format!("{}@depth@100ms", s.to_lowercase()).into())
110        .collect();
111
112    WebSocketSubscribeRequest {
113        id: id.into(),
114        method: "SUBSCRIBE".into(),
115        params: streams,
116        request_id,
117        api_key: api_key.map(|s| s.into()),
118        test,
119    }
120}
121
122/// Creates a kline/candlestick stream subscription for the given symbols
123#[inline]
124pub fn create_kline_subscription(
125    id: &str,
126    symbols: SmallVec<[String; 8]>,
127    interval: &str,
128    request_id: Option<u64>,
129    api_key: Option<&str>,
130    test: bool,
131) -> WebSocketSubscribeRequest {
132    let streams = symbols
133        .iter()
134        .map(|s| format!("{}@kline_{}", s.to_lowercase(), interval).into())
135        .collect();
136
137    WebSocketSubscribeRequest {
138        id: id.into(),
139        method: "SUBSCRIBE".into(),
140        params: streams,
141        request_id,
142        api_key: api_key.map(|s| s.into()),
143        test,
144    }
145}
146
147/// WebSocket subscription request
148#[derive(Debug, Clone, Serialize, Deserialize)]
149pub struct WebSocketSubscribeRequest {
150    /// Request ID (String identifier)
151    pub id: String,
152
153    /// Method ("SUBSCRIBE" or "UNSUBSCRIBE")
154    pub method: String,
155
156    /// Stream names to subscribe to
157    pub params: SmallVec<[String; 8]>,
158
159    /// Optional numeric request ID for tracking responses
160    #[serde(skip_serializing_if = "Option::is_none")]
161    pub request_id: Option<u64>,
162
163    /// Optional API key for authenticated requests
164    #[serde(skip_serializing_if = "Option::is_none")]
165    pub api_key: Option<String>,
166
167    /// Whether this is a test request
168    #[serde(skip_serializing_if = "std::ops::Not::not")]
169    pub test: bool,
170}
171
172/// WebSocket subscription response
173#[derive(Debug, Clone, Serialize, Deserialize)]
174pub struct WebSocketSubscribeResponse {
175    /// Request ID
176    pub id: u64,
177
178    /// Response code (200 for success)
179    pub result: Option<simd_json::OwnedValue>,
180
181    /// Error code
182    #[serde(default)]
183    pub code: i32,
184
185    /// Error message
186    #[serde(default)]
187    pub msg: String,
188}