Skip to main content

nautilus_binance/spot/websocket/public_json/
messages.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2026 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//! Binance Spot public JSON WebSocket message types.
17
18use nautilus_network::websocket::WebSocketClient;
19use serde::{Deserialize, Serialize};
20use ustr::Ustr;
21
22use crate::{
23    common::enums::{BinanceKlineInterval, BinanceWsMethod},
24    spot::websocket::streams::messages::{
25        BinanceWsErrorMsg, BinanceWsErrorResponse, BinanceWsResponse,
26    },
27};
28
29/// Output message from the Spot public JSON WebSocket handler.
30#[derive(Debug, Clone)]
31pub enum BinanceSpotPublicWsMessage {
32    /// Trade stream event.
33    Trade(BinanceSpotTradeMsg),
34    /// Best bid/ask stream event.
35    BookTicker(BinanceSpotBookTickerMsg),
36    /// Partial depth snapshot stream event.
37    DepthSnapshot(BinanceSpotPartialDepthMsg),
38    /// Depth diff stream event.
39    DepthDiff(BinanceSpotDepthDiffMsg),
40    /// Kline/candlestick stream event.
41    Kline(BinanceSpotKlineMsg),
42    /// Rolling 24-hour ticker stream event.
43    Ticker(BinanceSpotTickerMsg),
44    /// Server shutdown notice.
45    ServerShutdown(BinanceSpotServerShutdownMsg),
46    /// Raw JSON message (unhandled or unknown event).
47    RawJson(serde_json::Value),
48    /// Error from the server.
49    Error(BinanceWsErrorMsg),
50    /// WebSocket reconnected.
51    Reconnected,
52}
53
54/// Commands sent from the outer client to the inner handler.
55#[allow(
56    missing_debug_implementations,
57    clippy::large_enum_variant,
58    reason = "Commands are ephemeral and immediately consumed"
59)]
60pub enum BinanceSpotPublicWsCommand {
61    /// Set the WebSocket client after connection.
62    SetClient(WebSocketClient),
63    /// Disconnect and clean up.
64    Disconnect,
65    /// Subscribe to streams.
66    Subscribe { streams: Vec<String> },
67    /// Unsubscribe from streams.
68    Unsubscribe { streams: Vec<String> },
69}
70
71/// Binance WebSocket subscription request.
72#[derive(Debug, Clone, Serialize)]
73pub struct BinanceWsSubscription {
74    /// Request method.
75    pub method: BinanceWsMethod,
76    /// Stream names to subscribe/unsubscribe.
77    pub params: Vec<String>,
78    /// Request ID for correlation.
79    pub id: u64,
80}
81
82impl BinanceWsSubscription {
83    /// Create a subscribe request.
84    #[must_use]
85    pub fn subscribe(streams: Vec<String>, id: u64) -> Self {
86        Self {
87            method: BinanceWsMethod::Subscribe,
88            params: streams,
89            id,
90        }
91    }
92
93    /// Create an unsubscribe request.
94    #[must_use]
95    pub fn unsubscribe(streams: Vec<String>, id: u64) -> Self {
96        Self {
97            method: BinanceWsMethod::Unsubscribe,
98            params: streams,
99            id,
100        }
101    }
102}
103
104/// Combined stream wrapper used by `/stream` endpoint.
105#[derive(Debug, Clone, Deserialize)]
106pub struct BinanceCombinedStreamEvent {
107    /// Stream name (e.g., `btcusdt@depth20`).
108    pub stream: String,
109    /// Payload data.
110    pub data: serde_json::Value,
111}
112
113/// Trade stream message.
114#[derive(Debug, Clone, Deserialize)]
115pub struct BinanceSpotTradeMsg {
116    /// Event type.
117    #[serde(rename = "e")]
118    pub event_type: String,
119    /// Event time in milliseconds.
120    #[serde(rename = "E")]
121    pub event_time: i64,
122    /// Symbol.
123    #[serde(rename = "s")]
124    pub symbol: Ustr,
125    /// Trade ID.
126    #[serde(rename = "t")]
127    pub trade_id: u64,
128    /// Price.
129    #[serde(rename = "p")]
130    pub price: String,
131    /// Quantity.
132    #[serde(rename = "q")]
133    pub quantity: String,
134    /// Trade time in milliseconds.
135    #[serde(rename = "T")]
136    pub trade_time: i64,
137    /// Is buyer the market maker.
138    #[serde(rename = "m")]
139    pub is_buyer_maker: bool,
140}
141
142/// Best bid/ask stream message.
143#[derive(Debug, Clone, Deserialize)]
144pub struct BinanceSpotBookTickerMsg {
145    /// Event type.
146    #[serde(rename = "e", default)]
147    pub event_type: Option<String>,
148    /// Event time in milliseconds.
149    #[serde(rename = "E", default)]
150    pub event_time: Option<i64>,
151    /// Symbol.
152    #[serde(rename = "s")]
153    pub symbol: Ustr,
154    /// Order book update id.
155    #[serde(rename = "u")]
156    pub book_update_id: u64,
157    /// Best bid price.
158    #[serde(rename = "b")]
159    pub best_bid_price: String,
160    /// Best bid quantity.
161    #[serde(rename = "B")]
162    pub best_bid_qty: String,
163    /// Best ask price.
164    #[serde(rename = "a")]
165    pub best_ask_price: String,
166    /// Best ask quantity.
167    #[serde(rename = "A")]
168    pub best_ask_qty: String,
169    /// Transaction time in milliseconds (if provided).
170    #[serde(rename = "T", default)]
171    pub transaction_time: Option<i64>,
172}
173
174/// Partial depth stream message with symbol inferred from stream name.
175#[derive(Debug, Clone)]
176pub struct BinanceSpotPartialDepthMsg {
177    /// Symbol.
178    pub symbol: Ustr,
179    /// Last update ID.
180    pub last_update_id: u64,
181    /// Bid levels `[price, qty]`.
182    pub bids: Vec<[String; 2]>,
183    /// Ask levels `[price, qty]`.
184    pub asks: Vec<[String; 2]>,
185}
186
187/// Raw partial depth payload from Spot JSON stream.
188#[derive(Debug, Clone, Deserialize)]
189pub struct BinanceSpotPartialDepthPayload {
190    /// Last update ID.
191    #[serde(rename = "lastUpdateId")]
192    pub last_update_id: u64,
193    /// Bid levels `[price, qty]`.
194    pub bids: Vec<[String; 2]>,
195    /// Ask levels `[price, qty]`.
196    pub asks: Vec<[String; 2]>,
197}
198
199/// Diff depth stream message.
200#[derive(Debug, Clone, Deserialize)]
201pub struct BinanceSpotDepthDiffMsg {
202    /// Event type.
203    #[serde(rename = "e")]
204    pub event_type: String,
205    /// Event time in milliseconds.
206    #[serde(rename = "E")]
207    pub event_time: i64,
208    /// Symbol.
209    #[serde(rename = "s")]
210    pub symbol: Ustr,
211    /// First update ID in event.
212    #[serde(rename = "U")]
213    pub first_update_id: u64,
214    /// Final update ID in event.
215    #[serde(rename = "u")]
216    pub final_update_id: u64,
217    /// Bid updates `[price, qty]`.
218    #[serde(rename = "b")]
219    pub bids: Vec<[String; 2]>,
220    /// Ask updates `[price, qty]`.
221    #[serde(rename = "a")]
222    pub asks: Vec<[String; 2]>,
223}
224
225/// Kline stream message.
226#[derive(Debug, Clone, Deserialize)]
227pub struct BinanceSpotKlineMsg {
228    /// Event type.
229    #[serde(rename = "e")]
230    pub event_type: String,
231    /// Event time in milliseconds.
232    #[serde(rename = "E")]
233    pub event_time: i64,
234    /// Symbol.
235    #[serde(rename = "s")]
236    pub symbol: Ustr,
237    /// Kline data.
238    #[serde(rename = "k")]
239    pub kline: BinanceSpotKlineData,
240}
241
242/// Kline data within kline message.
243#[derive(Debug, Clone, Deserialize)]
244pub struct BinanceSpotKlineData {
245    /// Kline start time.
246    #[serde(rename = "t")]
247    pub start_time: i64,
248    /// Kline close time.
249    #[serde(rename = "T")]
250    pub close_time: i64,
251    /// Symbol.
252    #[serde(rename = "s")]
253    pub symbol: Ustr,
254    /// Kline interval.
255    #[serde(rename = "i")]
256    pub interval: BinanceKlineInterval,
257    /// First trade ID.
258    #[serde(rename = "f")]
259    pub first_trade_id: i64,
260    /// Last trade ID.
261    #[serde(rename = "L")]
262    pub last_trade_id: i64,
263    /// Open price.
264    #[serde(rename = "o")]
265    pub open: String,
266    /// Close price.
267    #[serde(rename = "c")]
268    pub close: String,
269    /// High price.
270    #[serde(rename = "h")]
271    pub high: String,
272    /// Low price.
273    #[serde(rename = "l")]
274    pub low: String,
275    /// Base asset volume.
276    #[serde(rename = "v")]
277    pub volume: String,
278    /// Number of trades.
279    #[serde(rename = "n")]
280    pub num_trades: i64,
281    /// Is this kline closed.
282    #[serde(rename = "x")]
283    pub is_closed: bool,
284    /// Quote asset volume.
285    #[serde(rename = "q")]
286    pub quote_volume: String,
287    /// Taker buy base asset volume.
288    #[serde(rename = "V")]
289    pub taker_buy_base_volume: String,
290    /// Taker buy quote asset volume.
291    #[serde(rename = "Q")]
292    pub taker_buy_quote_volume: String,
293}
294
295/// Rolling 24-hour ticker stream message.
296#[derive(Debug, Clone, Deserialize)]
297pub struct BinanceSpotTickerMsg {
298    #[serde(rename = "E")]
299    pub event_time: i64,
300    #[serde(rename = "s")]
301    pub symbol: Ustr,
302    #[serde(rename = "p")]
303    pub price_change: String,
304    #[serde(rename = "P")]
305    pub price_change_percent: String,
306    #[serde(rename = "w")]
307    pub weighted_avg_price: String,
308    #[serde(rename = "x")]
309    pub prev_close_price: String,
310    #[serde(rename = "c")]
311    pub last_price: String,
312    #[serde(rename = "Q")]
313    pub last_qty: String,
314    #[serde(rename = "b")]
315    pub bid_price: String,
316    #[serde(rename = "B")]
317    pub bid_qty: String,
318    #[serde(rename = "a")]
319    pub ask_price: String,
320    #[serde(rename = "A")]
321    pub ask_qty: String,
322    #[serde(rename = "o")]
323    pub open_price: String,
324    #[serde(rename = "h")]
325    pub high_price: String,
326    #[serde(rename = "l")]
327    pub low_price: String,
328    #[serde(rename = "v")]
329    pub volume: String,
330    #[serde(rename = "q")]
331    pub quote_volume: String,
332    #[serde(rename = "O")]
333    pub open_time: i64,
334    #[serde(rename = "C")]
335    pub close_time: i64,
336    #[serde(rename = "F")]
337    pub first_trade_id: i64,
338    #[serde(rename = "L")]
339    pub last_trade_id: i64,
340    #[serde(rename = "n")]
341    pub num_trades: i64,
342}
343
344/// Server shutdown event sent before Binance disconnects clients.
345#[derive(Debug, Clone, Deserialize)]
346pub struct BinanceSpotServerShutdownMsg {
347    /// Event type (`"serverShutdown"`).
348    #[serde(rename = "e")]
349    pub event_type: String,
350    /// Event time in milliseconds.
351    #[serde(rename = "E")]
352    pub event_time: i64,
353}
354
355pub type BinanceSpotWsResponse = BinanceWsResponse;
356pub type BinanceSpotWsErrorResponse = BinanceWsErrorResponse;