Skip to main content

nautilus_binance/futures/websocket/streams/
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 Futures WebSocket message types.
17//!
18//! Futures streams use standard JSON encoding (not SBE like Spot).
19//!
20//! The handler emits venue-specific types via [`BinanceFuturesWsStreamsMessage`].
21//! Data and execution client layers convert these to Nautilus domain types.
22
23use nautilus_core::serialization::{
24    deserialize_decimal_from_str, deserialize_optional_decimal_from_str,
25};
26use nautilus_model::identifiers::{
27    ClientOrderId, InstrumentId, StrategyId, TraderId, VenueOrderId,
28};
29use nautilus_network::websocket::WebSocketClient;
30use rust_decimal::Decimal;
31use serde::{Deserialize, Serialize};
32use ustr::Ustr;
33
34use crate::{
35    common::enums::{
36        BinanceAlgoStatus, BinanceAlgoType, BinanceFuturesOrderType, BinanceKlineInterval,
37        BinanceMarginType, BinanceOrderStatus, BinancePositionSide, BinancePriceMatch,
38        BinanceSelfTradePreventionMode, BinanceSide, BinanceTimeInForce, BinanceWorkingType,
39        BinanceWsMethod,
40    },
41    futures::http::BinanceFuturesInstrument,
42};
43
44/// Output message from the Futures WebSocket streams handler.
45///
46/// Contains venue-specific types for both market data and user data stream
47/// events. The data and execution client layers convert these to Nautilus
48/// domain types using parse functions with instrument context.
49#[derive(Debug, Clone)]
50pub enum BinanceFuturesWsStreamsMessage {
51    /// Aggregate trade stream.
52    AggTrade(BinanceFuturesAggTradeMsg),
53    /// Trade stream.
54    Trade(BinanceFuturesTradeMsg),
55    /// Best bid/ask (book ticker) stream.
56    BookTicker(BinanceFuturesBookTickerMsg),
57    /// Order book depth update stream.
58    DepthUpdate(BinanceFuturesDepthUpdateMsg),
59    /// Mark price stream.
60    MarkPrice(BinanceFuturesMarkPriceMsg),
61    /// Kline/candlestick stream.
62    Kline(BinanceFuturesKlineMsg),
63    /// Force liquidation order stream.
64    ForceOrder(BinanceFuturesLiquidationMsg),
65    /// 24hr ticker stream.
66    Ticker(BinanceFuturesTickerMsg),
67    /// Account update (balance/position changes).
68    AccountUpdate(BinanceFuturesAccountUpdateMsg),
69    /// Order/trade update.
70    OrderUpdate(Box<BinanceFuturesOrderUpdateMsg>),
71    /// Trade Lite fill notification (low-latency subset of `OrderUpdate`).
72    TradeLite(Box<BinanceFuturesTradeLiteMsg>),
73    /// Algo order update (conditional orders via Algo Service).
74    AlgoUpdate(Box<BinanceFuturesAlgoUpdateMsg>),
75    /// Margin call warning.
76    MarginCall(BinanceFuturesMarginCallMsg),
77    /// Account configuration change (leverage, etc.).
78    AccountConfigUpdate(BinanceFuturesAccountConfigMsg),
79    /// Listen key expired.
80    ListenKeyExpired,
81    /// Error from the server.
82    Error(BinanceFuturesWsErrorMsg),
83    /// WebSocket reconnected; carries the correlation IDs of the unsubscribe
84    /// requests the old connection abandoned.
85    Reconnected(Vec<u64>),
86    /// Venue confirmation of an unsubscribe request, carrying the confirmed stream
87    /// names and the request's correlation ID when one was supplied.
88    Unsubscribed {
89        /// Confirmed stream names.
90        streams: Vec<String>,
91        /// Correlates the confirmation with the caller's unsubscribe lifecycle.
92        correlation: Option<u64>,
93    },
94}
95
96/// Error message from Binance Futures WebSocket.
97#[derive(Debug, Clone)]
98pub struct BinanceFuturesWsErrorMsg {
99    /// Error code from Binance.
100    pub code: i64,
101    /// Error message.
102    pub msg: String,
103}
104
105/// Handler command for data client-handler communication.
106#[derive(Debug)]
107pub enum BinanceFuturesWsStreamsCommand {
108    /// Set the WebSocket client reference.
109    SetClient(WebSocketClient),
110    /// Disconnect from the WebSocket.
111    Disconnect,
112    /// Subscribe to streams.
113    Subscribe { streams: Vec<String> },
114    /// Unsubscribe from streams.
115    Unsubscribe {
116        /// Streams to unsubscribe from.
117        streams: Vec<String>,
118        /// Correlates the venue confirmation with the caller's unsubscribe lifecycle.
119        correlation: Option<u64>,
120    },
121}
122
123/// Handler command for execution client-handler communication.
124#[derive(Debug)]
125#[expect(
126    clippy::large_enum_variant,
127    reason = "Commands are ephemeral and immediately consumed"
128)]
129pub enum ExecHandlerCommand {
130    /// Set the WebSocket client reference.
131    SetClient(WebSocketClient),
132    /// Disconnect from the WebSocket.
133    Disconnect,
134    /// Initialize instruments in the handler cache.
135    InitializeInstruments(Vec<BinanceFuturesInstrument>),
136    /// Update a single instrument in the handler cache.
137    UpdateInstrument(BinanceFuturesInstrument),
138    /// Subscribe to user data stream.
139    Subscribe { streams: Vec<String> },
140    /// Register an order for context tracking.
141    RegisterOrder {
142        client_order_id: ClientOrderId,
143        trader_id: TraderId,
144        strategy_id: StrategyId,
145        instrument_id: InstrumentId,
146    },
147    /// Register a cancel request for context tracking.
148    RegisterCancel {
149        client_order_id: ClientOrderId,
150        trader_id: TraderId,
151        strategy_id: StrategyId,
152        instrument_id: InstrumentId,
153        venue_order_id: Option<VenueOrderId>,
154    },
155    /// Register a modify request for context tracking.
156    RegisterModify {
157        client_order_id: ClientOrderId,
158        trader_id: TraderId,
159        strategy_id: StrategyId,
160        instrument_id: InstrumentId,
161        venue_order_id: Option<VenueOrderId>,
162    },
163}
164
165/// Aggregate trade stream message.
166#[derive(Debug, Clone, Deserialize)]
167pub struct BinanceFuturesAggTradeMsg {
168    /// Event type.
169    #[serde(rename = "e")]
170    pub event_type: String,
171    /// Event time in milliseconds.
172    #[serde(rename = "E")]
173    pub event_time: i64,
174    /// Symbol.
175    #[serde(rename = "s")]
176    pub symbol: Ustr,
177    /// Aggregate trade ID.
178    #[serde(rename = "a")]
179    pub agg_trade_id: u64,
180    /// Price.
181    #[serde(rename = "p")]
182    pub price: String,
183    /// Quantity.
184    #[serde(rename = "q")]
185    pub quantity: String,
186    /// First trade ID.
187    #[serde(rename = "f")]
188    pub first_trade_id: u64,
189    /// Last trade ID.
190    #[serde(rename = "l")]
191    pub last_trade_id: u64,
192    /// Trade time in milliseconds.
193    #[serde(rename = "T")]
194    pub trade_time: i64,
195    /// Is buyer the market maker.
196    #[serde(rename = "m")]
197    pub is_buyer_maker: bool,
198}
199
200/// Trade stream message.
201#[derive(Debug, Clone, Deserialize)]
202pub struct BinanceFuturesTradeMsg {
203    /// Event type.
204    #[serde(rename = "e")]
205    pub event_type: String,
206    /// Event time in milliseconds.
207    #[serde(rename = "E")]
208    pub event_time: i64,
209    /// Symbol.
210    #[serde(rename = "s")]
211    pub symbol: Ustr,
212    /// Trade ID.
213    #[serde(rename = "t")]
214    pub trade_id: u64,
215    /// Price.
216    #[serde(rename = "p")]
217    pub price: String,
218    /// Quantity.
219    #[serde(rename = "q")]
220    pub quantity: String,
221    /// Trade time in milliseconds.
222    #[serde(rename = "T")]
223    pub trade_time: i64,
224    /// Is buyer the market maker.
225    #[serde(rename = "m")]
226    pub is_buyer_maker: bool,
227}
228
229/// Order book depth update stream message.
230#[derive(Debug, Clone, Deserialize)]
231pub struct BinanceFuturesDepthUpdateMsg {
232    /// Event type.
233    #[serde(rename = "e")]
234    pub event_type: String,
235    /// Event time in milliseconds.
236    #[serde(rename = "E")]
237    pub event_time: i64,
238    /// Transaction time in milliseconds.
239    #[serde(rename = "T")]
240    pub transaction_time: i64,
241    /// Symbol.
242    #[serde(rename = "s")]
243    pub symbol: Ustr,
244    /// First update ID.
245    #[serde(rename = "U")]
246    pub first_update_id: u64,
247    /// Final update ID.
248    #[serde(rename = "u")]
249    pub final_update_id: u64,
250    /// Previous final update ID.
251    #[serde(rename = "pu")]
252    pub prev_final_update_id: u64,
253    /// Bids [price, quantity].
254    #[serde(rename = "b")]
255    pub bids: Vec<[String; 2]>,
256    /// Asks [price, quantity].
257    #[serde(rename = "a")]
258    pub asks: Vec<[String; 2]>,
259}
260
261/// Mark price stream message.
262#[derive(Debug, Clone, Deserialize)]
263pub struct BinanceFuturesMarkPriceMsg {
264    /// Event type.
265    #[serde(rename = "e")]
266    pub event_type: String,
267    /// Event time in milliseconds.
268    #[serde(rename = "E")]
269    pub event_time: i64,
270    /// Symbol.
271    #[serde(rename = "s")]
272    pub symbol: Ustr,
273    /// Mark price.
274    #[serde(rename = "p")]
275    pub mark_price: String,
276    /// Mark price moving average (added by venue, may be absent on older payloads).
277    #[serde(rename = "ap", default)]
278    pub mark_price_moving_avg: Option<String>,
279    /// Index price.
280    #[serde(rename = "i")]
281    pub index_price: String,
282    /// Estimated settle price.
283    #[serde(rename = "P")]
284    pub estimated_settle_price: String,
285    /// Funding rate.
286    #[serde(rename = "r")]
287    pub funding_rate: String,
288    /// Next funding time in milliseconds.
289    #[serde(rename = "T")]
290    pub next_funding_time: i64,
291}
292
293/// Book ticker stream message.
294#[derive(Debug, Clone, Deserialize)]
295pub struct BinanceFuturesBookTickerMsg {
296    /// Event type.
297    #[serde(rename = "e")]
298    pub event_type: String,
299    /// Update ID.
300    #[serde(rename = "u")]
301    pub update_id: u64,
302    /// Event time in milliseconds.
303    #[serde(rename = "E")]
304    pub event_time: i64,
305    /// Transaction time in milliseconds.
306    #[serde(rename = "T")]
307    pub transaction_time: i64,
308    /// Symbol.
309    #[serde(rename = "s")]
310    pub symbol: Ustr,
311    /// Best bid price.
312    #[serde(rename = "b")]
313    pub best_bid_price: String,
314    /// Best bid quantity.
315    #[serde(rename = "B")]
316    pub best_bid_qty: String,
317    /// Best ask price.
318    #[serde(rename = "a")]
319    pub best_ask_price: String,
320    /// Best ask quantity.
321    #[serde(rename = "A")]
322    pub best_ask_qty: String,
323}
324
325/// Kline/candlestick stream message.
326#[derive(Debug, Clone, Deserialize)]
327pub struct BinanceFuturesKlineMsg {
328    /// Event type.
329    #[serde(rename = "e")]
330    pub event_type: String,
331    /// Event time in milliseconds.
332    #[serde(rename = "E")]
333    pub event_time: i64,
334    /// Symbol.
335    #[serde(rename = "s")]
336    pub symbol: Ustr,
337    /// Kline data.
338    #[serde(rename = "k")]
339    pub kline: BinanceFuturesKlineData,
340}
341
342/// Kline data within kline message.
343#[derive(Debug, Clone, Deserialize)]
344pub struct BinanceFuturesKlineData {
345    /// Kline start time.
346    #[serde(rename = "t")]
347    pub start_time: i64,
348    /// Kline close time.
349    #[serde(rename = "T")]
350    pub close_time: i64,
351    /// Symbol.
352    #[serde(rename = "s")]
353    pub symbol: Ustr,
354    /// Kline interval.
355    #[serde(rename = "i")]
356    pub interval: BinanceKlineInterval,
357    /// First trade ID.
358    #[serde(rename = "f")]
359    pub first_trade_id: i64,
360    /// Last trade ID.
361    #[serde(rename = "L")]
362    pub last_trade_id: i64,
363    /// Open price.
364    #[serde(rename = "o")]
365    pub open: String,
366    /// Close price.
367    #[serde(rename = "c")]
368    pub close: String,
369    /// High price.
370    #[serde(rename = "h")]
371    pub high: String,
372    /// Low price.
373    #[serde(rename = "l")]
374    pub low: String,
375    /// Base asset volume.
376    #[serde(rename = "v")]
377    pub volume: String,
378    /// Number of trades.
379    #[serde(rename = "n")]
380    pub num_trades: i64,
381    /// Is this kline closed.
382    #[serde(rename = "x")]
383    pub is_closed: bool,
384    /// Quote asset volume.
385    #[serde(rename = "q")]
386    pub quote_volume: String,
387    /// Taker buy base asset volume.
388    #[serde(rename = "V")]
389    pub taker_buy_volume: String,
390    /// Taker buy quote asset volume.
391    #[serde(rename = "Q")]
392    pub taker_buy_quote_volume: String,
393}
394
395/// Liquidation order stream message.
396#[derive(Debug, Clone, Deserialize)]
397pub struct BinanceFuturesLiquidationMsg {
398    /// Event type.
399    #[serde(rename = "e")]
400    pub event_type: String,
401    /// Event time in milliseconds.
402    #[serde(rename = "E")]
403    pub event_time: i64,
404    /// Order data.
405    #[serde(rename = "o")]
406    pub order: BinanceFuturesLiquidationOrder,
407}
408
409/// Liquidation order details.
410#[derive(Debug, Clone, Deserialize)]
411pub struct BinanceFuturesLiquidationOrder {
412    /// Symbol.
413    #[serde(rename = "s")]
414    pub symbol: Ustr,
415    /// Order side.
416    #[serde(rename = "S")]
417    pub side: BinanceSide,
418    /// Order type.
419    #[serde(rename = "o")]
420    pub order_type: BinanceFuturesOrderType,
421    /// Time in force.
422    #[serde(rename = "f")]
423    pub time_in_force: BinanceTimeInForce,
424    /// Original quantity.
425    #[serde(rename = "q")]
426    pub original_qty: String,
427    /// Price.
428    #[serde(rename = "p")]
429    pub price: String,
430    /// Average price.
431    #[serde(rename = "ap")]
432    pub average_price: String,
433    /// Order status.
434    #[serde(rename = "X")]
435    pub status: BinanceOrderStatus,
436    /// Last filled quantity.
437    #[serde(rename = "l")]
438    pub last_filled_qty: String,
439    /// Accumulated filled quantity.
440    #[serde(rename = "z")]
441    pub accumulated_qty: String,
442    /// Trade time in milliseconds.
443    #[serde(rename = "T")]
444    pub trade_time: i64,
445}
446
447/// 24hr ticker stream message.
448#[derive(Debug, Clone, Deserialize)]
449pub struct BinanceFuturesTickerMsg {
450    /// Event type.
451    #[serde(rename = "e")]
452    pub event_type: String,
453    /// Event time in milliseconds.
454    #[serde(rename = "E")]
455    pub event_time: i64,
456    /// Symbol.
457    #[serde(rename = "s")]
458    pub symbol: Ustr,
459    /// Price change.
460    #[serde(rename = "p")]
461    pub price_change: String,
462    /// Price change percent.
463    #[serde(rename = "P")]
464    pub price_change_percent: String,
465    /// Weighted average price.
466    #[serde(rename = "w")]
467    pub weighted_avg_price: String,
468    /// Last price.
469    #[serde(rename = "c")]
470    pub last_price: String,
471    /// Last quantity.
472    #[serde(rename = "Q")]
473    pub last_qty: String,
474    /// Open price.
475    #[serde(rename = "o")]
476    pub open_price: String,
477    /// High price.
478    #[serde(rename = "h")]
479    pub high_price: String,
480    /// Low price.
481    #[serde(rename = "l")]
482    pub low_price: String,
483    /// Total traded base asset volume.
484    #[serde(rename = "v")]
485    pub volume: String,
486    /// Total traded quote asset volume.
487    #[serde(rename = "q")]
488    pub quote_volume: String,
489    /// Statistics open time in milliseconds.
490    #[serde(rename = "O")]
491    pub open_time: i64,
492    /// Statistics close time in milliseconds.
493    #[serde(rename = "C")]
494    pub close_time: i64,
495    /// First trade ID.
496    #[serde(rename = "F")]
497    pub first_trade_id: i64,
498    /// Last trade ID.
499    #[serde(rename = "L")]
500    pub last_trade_id: i64,
501    /// Total number of trades.
502    #[serde(rename = "n")]
503    pub num_trades: i64,
504}
505
506/// WebSocket subscription request.
507#[derive(Debug, Clone, Serialize)]
508pub struct BinanceFuturesWsSubscribeRequest {
509    /// Request method.
510    pub method: BinanceWsMethod,
511    /// Stream names to subscribe.
512    pub params: Vec<String>,
513    /// Request ID.
514    pub id: u64,
515}
516
517/// WebSocket subscription response.
518#[derive(Debug, Clone, Deserialize)]
519pub struct BinanceFuturesWsSubscribeResponse {
520    /// Response result (null on success).
521    pub result: Option<serde_json::Value>,
522    /// Request ID echoed back.
523    pub id: u64,
524}
525
526/// WebSocket error response.
527#[derive(Debug, Clone, Deserialize)]
528pub struct BinanceFuturesWsErrorResponse {
529    /// Error code.
530    pub code: i64,
531    /// Error message.
532    pub msg: String,
533    /// Request ID if available.
534    pub id: Option<u64>,
535}
536
537/// Account update event from user data stream.
538#[derive(Debug, Clone, Deserialize)]
539pub struct BinanceFuturesAccountUpdateMsg {
540    /// Event type.
541    #[serde(rename = "e")]
542    pub event_type: String,
543    /// Event time in milliseconds.
544    #[serde(rename = "E")]
545    pub event_time: i64,
546    /// Transaction time in milliseconds.
547    #[serde(rename = "T")]
548    pub transaction_time: i64,
549    /// Account update data.
550    #[serde(rename = "a")]
551    pub account: AccountUpdateData,
552}
553
554/// Account update data payload.
555#[derive(Debug, Clone, Deserialize)]
556pub struct AccountUpdateData {
557    /// Reason for account update.
558    #[serde(rename = "m")]
559    pub reason: AccountUpdateReason,
560    /// Balance updates.
561    #[serde(rename = "B", default)]
562    pub balances: Vec<BalanceUpdate>,
563    /// Position updates.
564    #[serde(rename = "P", default)]
565    pub positions: Vec<PositionUpdate>,
566}
567
568/// Account update reason type.
569#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
570#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
571pub enum AccountUpdateReason {
572    Deposit,
573    Withdraw,
574    Order,
575    FundingFee,
576    WithdrawReject,
577    Adjustment,
578    InsuranceClear,
579    AdminDeposit,
580    AdminWithdraw,
581    MarginTransfer,
582    MarginTypeChange,
583    AssetTransfer,
584    OptionsPremiumFee,
585    OptionsSettleProfit,
586    AutoExchange,
587    Adl,
588    CoinSwapDeposit,
589    CoinSwapWithdraw,
590    #[serde(other)]
591    Unknown,
592}
593
594/// Balance update within account update.
595#[derive(Debug, Clone, Deserialize)]
596pub struct BalanceUpdate {
597    /// Asset name.
598    #[serde(rename = "a")]
599    pub asset: Ustr,
600    /// Wallet balance.
601    #[serde(rename = "wb", deserialize_with = "deserialize_decimal_from_str")]
602    pub wallet_balance: Decimal,
603    /// Cross wallet balance.
604    #[serde(rename = "cw", deserialize_with = "deserialize_decimal_from_str")]
605    pub cross_wallet_balance: Decimal,
606    /// Balance change (except for PnL and commission).
607    #[serde(
608        rename = "bc",
609        default,
610        deserialize_with = "deserialize_optional_decimal_from_str"
611    )]
612    pub balance_change: Option<Decimal>,
613}
614
615/// Position update within account update.
616#[derive(Debug, Clone, Deserialize)]
617pub struct PositionUpdate {
618    /// Symbol.
619    #[serde(rename = "s")]
620    pub symbol: Ustr,
621    /// Position amount.
622    #[serde(rename = "pa")]
623    pub position_amount: String,
624    /// Entry price.
625    #[serde(rename = "ep")]
626    pub entry_price: String,
627    /// Break-even price.
628    #[serde(rename = "bep", default)]
629    pub break_even_price: Option<String>,
630    /// Accumulated realized (pre-fee).
631    #[serde(rename = "cr")]
632    pub accumulated_realized: String,
633    /// Unrealized PnL.
634    #[serde(rename = "up")]
635    pub unrealized_pnl: String,
636    /// Margin type.
637    #[serde(rename = "mt")]
638    pub margin_type: BinanceMarginType,
639    /// Isolated wallet (if isolated position).
640    #[serde(rename = "iw")]
641    pub isolated_wallet: String,
642    /// Position side.
643    #[serde(rename = "ps")]
644    pub position_side: BinancePositionSide,
645}
646
647/// Order/trade update event from user data stream.
648#[derive(Debug, Clone, Deserialize)]
649pub struct BinanceFuturesOrderUpdateMsg {
650    /// Event type.
651    #[serde(rename = "e")]
652    pub event_type: String,
653    /// Event time in milliseconds.
654    #[serde(rename = "E")]
655    pub event_time: i64,
656    /// Transaction time in milliseconds.
657    #[serde(rename = "T")]
658    pub transaction_time: i64,
659    /// Order data.
660    #[serde(rename = "o")]
661    pub order: OrderUpdateData,
662}
663
664/// Order update data payload.
665#[derive(Debug, Clone, Deserialize)]
666pub struct OrderUpdateData {
667    /// Symbol.
668    #[serde(rename = "s")]
669    pub symbol: Ustr,
670    /// Client order ID.
671    #[serde(rename = "c")]
672    pub client_order_id: String,
673    /// Order side.
674    #[serde(rename = "S")]
675    pub side: BinanceSide,
676    /// Order type.
677    #[serde(rename = "o")]
678    pub order_type: BinanceFuturesOrderType,
679    /// Time in force.
680    #[serde(rename = "f")]
681    pub time_in_force: BinanceTimeInForce,
682    /// Original quantity.
683    #[serde(rename = "q")]
684    pub original_qty: String,
685    /// Original price.
686    #[serde(rename = "p")]
687    pub original_price: String,
688    /// Average price.
689    #[serde(rename = "ap")]
690    pub average_price: String,
691    /// Stop price.
692    #[serde(rename = "sp")]
693    pub stop_price: String,
694    /// Execution type.
695    #[serde(rename = "x")]
696    pub execution_type: BinanceExecutionType,
697    /// Order status.
698    #[serde(rename = "X")]
699    pub order_status: BinanceOrderStatus,
700    /// Order ID.
701    #[serde(rename = "i")]
702    pub order_id: i64,
703    /// Last executed quantity.
704    #[serde(rename = "l")]
705    pub last_filled_qty: String,
706    /// Cumulative filled quantity.
707    #[serde(rename = "z")]
708    pub cumulative_filled_qty: String,
709    /// Last executed price.
710    #[serde(rename = "L")]
711    pub last_filled_price: String,
712    /// Commission asset.
713    #[serde(rename = "N", default)]
714    pub commission_asset: Option<Ustr>,
715    /// Commission amount.
716    #[serde(rename = "n", default)]
717    pub commission: Option<String>,
718    /// Order trade time.
719    #[serde(rename = "T")]
720    pub trade_time: i64,
721    /// Trade ID.
722    #[serde(rename = "t")]
723    pub trade_id: i64,
724    /// Bids notional.
725    #[serde(rename = "b", default)]
726    pub bids_notional: Option<String>,
727    /// Asks notional.
728    #[serde(rename = "a", default)]
729    pub asks_notional: Option<String>,
730    /// Is maker.
731    #[serde(rename = "m")]
732    pub is_maker: bool,
733    /// Is reduce only.
734    #[serde(rename = "R")]
735    pub is_reduce_only: bool,
736    /// Working type.
737    #[serde(rename = "wt")]
738    pub working_type: BinanceWorkingType,
739    /// Original order type.
740    #[serde(rename = "ot")]
741    pub original_order_type: BinanceFuturesOrderType,
742    /// Position side.
743    #[serde(rename = "ps")]
744    pub position_side: BinancePositionSide,
745    /// Close all (for stop orders).
746    #[serde(rename = "cp", default)]
747    pub close_position: Option<bool>,
748    /// Activation price (for trailing stop).
749    #[serde(rename = "AP", default)]
750    pub activation_price: Option<String>,
751    /// Callback rate (for trailing stop).
752    #[serde(rename = "cr", default)]
753    pub callback_rate: Option<String>,
754    /// Price protection.
755    #[serde(rename = "pP", default)]
756    pub price_protect: Option<bool>,
757    /// Realized profit.
758    #[serde(rename = "rp")]
759    pub realized_profit: String,
760    /// Self-trade prevention mode.
761    #[serde(rename = "V", default)]
762    pub stp_mode: Option<BinanceSelfTradePreventionMode>,
763    /// Price match mode.
764    #[serde(rename = "pm", default)]
765    pub price_match: Option<BinancePriceMatch>,
766    /// Good till date for GTD orders.
767    #[serde(rename = "gtd", default)]
768    pub good_till_date: Option<i64>,
769}
770
771impl OrderUpdateData {
772    /// Returns true if this is a liquidation order.
773    #[must_use]
774    pub fn is_liquidation(&self) -> bool {
775        self.client_order_id.starts_with("autoclose-")
776    }
777
778    /// Returns true if this is an ADL (Auto-Deleveraging) order.
779    #[must_use]
780    pub fn is_adl(&self) -> bool {
781        self.client_order_id.starts_with("adl_autoclose")
782    }
783
784    /// Returns true if this is a settlement order.
785    ///
786    /// USDT-margined futures use `settlement_autoclose-` for funding/margin
787    /// settlement; coin-margined delivery futures use `delivery_autoclose-`
788    /// when an expiring contract auto-closes.
789    #[must_use]
790    pub fn is_settlement(&self) -> bool {
791        self.client_order_id.starts_with("settlement_autoclose-")
792            || self.client_order_id.starts_with("delivery_autoclose-")
793    }
794
795    /// Returns true if this is an exchange-generated order.
796    #[must_use]
797    pub fn is_exchange_generated(&self) -> bool {
798        self.is_liquidation() || self.is_adl() || self.is_settlement()
799    }
800}
801
802/// Trade Lite event from user data stream.
803///
804/// Binance pushes `TRADE_LITE` alongside `ORDER_TRADE_UPDATE` as a lower-latency
805/// subset containing only the fields needed to recognize a fill. Clients that
806/// prioritize latency can opt to act on `TRADE_LITE` and dedup the matching
807/// fill portion of the full `ORDER_TRADE_UPDATE` event.
808#[derive(Debug, Clone, Deserialize)]
809pub struct BinanceFuturesTradeLiteMsg {
810    /// Event type.
811    #[serde(rename = "e")]
812    pub event_type: String,
813    /// Event time in milliseconds.
814    #[serde(rename = "E")]
815    pub event_time: i64,
816    /// Transaction time in milliseconds.
817    #[serde(rename = "T")]
818    pub transaction_time: i64,
819    /// Symbol.
820    #[serde(rename = "s")]
821    pub symbol: Ustr,
822    /// Client order ID.
823    #[serde(rename = "c")]
824    pub client_order_id: String,
825    /// Order side.
826    #[serde(rename = "S")]
827    pub side: BinanceSide,
828    /// Original quantity.
829    #[serde(rename = "q")]
830    pub original_qty: String,
831    /// Original price.
832    #[serde(rename = "p")]
833    pub original_price: String,
834    /// Order ID.
835    #[serde(rename = "i")]
836    pub order_id: i64,
837    /// Last executed quantity.
838    #[serde(rename = "l")]
839    pub last_filled_qty: String,
840    /// Last executed price.
841    #[serde(rename = "L")]
842    pub last_filled_price: String,
843    /// Trade ID.
844    #[serde(rename = "t")]
845    pub trade_id: i64,
846    /// Is maker.
847    #[serde(rename = "m")]
848    pub is_maker: bool,
849}
850
851/// Execution type for order updates.
852#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Eq)]
853#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
854pub enum BinanceExecutionType {
855    /// New order accepted.
856    New,
857    /// Order canceled.
858    Canceled,
859    /// Calculated (liquidation, ADL).
860    Calculated,
861    /// Order expired.
862    Expired,
863    /// Trade (partial or full fill).
864    Trade,
865    /// Amendment (order modified).
866    Amendment,
867    /// Unknown or undocumented execution type.
868    #[serde(other)]
869    Unknown,
870}
871
872/// Margin call event from user data stream.
873#[derive(Debug, Clone, Deserialize)]
874pub struct BinanceFuturesMarginCallMsg {
875    /// Event type.
876    #[serde(rename = "e")]
877    pub event_type: String,
878    /// Event time in milliseconds.
879    #[serde(rename = "E")]
880    pub event_time: i64,
881    /// Cross wallet balance.
882    #[serde(rename = "cw")]
883    pub cross_wallet_balance: String,
884    /// Positions at risk.
885    #[serde(rename = "p")]
886    pub positions: Vec<MarginCallPosition>,
887}
888
889/// Position at risk in margin call.
890#[derive(Debug, Clone, Deserialize)]
891pub struct MarginCallPosition {
892    /// Symbol.
893    #[serde(rename = "s")]
894    pub symbol: Ustr,
895    /// Position side.
896    #[serde(rename = "ps")]
897    pub position_side: BinancePositionSide,
898    /// Position amount.
899    #[serde(rename = "pa")]
900    pub position_amount: String,
901    /// Margin type.
902    #[serde(rename = "mt")]
903    pub margin_type: BinanceMarginType,
904    /// Isolated wallet (if any).
905    #[serde(rename = "iw")]
906    pub isolated_wallet: String,
907    /// Mark price.
908    #[serde(rename = "mp")]
909    pub mark_price: String,
910    /// Unrealized PnL.
911    #[serde(rename = "up")]
912    pub unrealized_pnl: String,
913    /// Maintenance margin required.
914    #[serde(rename = "mm")]
915    pub maintenance_margin: String,
916}
917
918/// Account configuration update event.
919#[derive(Debug, Clone, Deserialize)]
920pub struct BinanceFuturesAccountConfigMsg {
921    /// Event type.
922    #[serde(rename = "e")]
923    pub event_type: String,
924    /// Event time in milliseconds.
925    #[serde(rename = "E")]
926    pub event_time: i64,
927    /// Transaction time in milliseconds.
928    #[serde(rename = "T")]
929    pub transaction_time: i64,
930    /// Leverage configuration data.
931    #[serde(rename = "ac", default)]
932    pub leverage_config: Option<LeverageConfig>,
933    /// Asset index price data (for multi-assets mode).
934    #[serde(rename = "ai", default)]
935    pub asset_index: Option<AssetIndexConfig>,
936}
937
938/// Leverage configuration change.
939#[derive(Debug, Clone, Deserialize)]
940pub struct LeverageConfig {
941    /// Symbol.
942    #[serde(rename = "s")]
943    pub symbol: Ustr,
944    /// New leverage value.
945    #[serde(rename = "l")]
946    pub leverage: u32,
947}
948
949/// Asset index configuration (multi-assets mode).
950#[derive(Debug, Clone, Deserialize)]
951pub struct AssetIndexConfig {
952    /// Symbol.
953    #[serde(rename = "s")]
954    pub symbol: Ustr,
955}
956
957/// Algo order update event from user data stream (Binance Futures Algo Service).
958///
959/// This event is triggered for conditional orders (STOP_MARKET, STOP_LIMIT,
960/// TAKE_PROFIT, TAKE_PROFIT_MARKET, TRAILING_STOP_MARKET) managed by the
961/// Algo Service.
962///
963/// # References
964///
965/// - <https://developers.binance.com/docs/derivatives/usds-margined-futures/user-data-streams/Event-Algo-Order-Update>
966#[derive(Debug, Clone, Deserialize)]
967pub struct BinanceFuturesAlgoUpdateMsg {
968    /// Event type ("ALGO_UPDATE").
969    #[serde(rename = "e")]
970    pub event_type: String,
971    /// Event time in milliseconds.
972    #[serde(rename = "E")]
973    pub event_time: i64,
974    /// Transaction time in milliseconds.
975    #[serde(rename = "T")]
976    pub transaction_time: i64,
977    /// Algo order data.
978    #[serde(rename = "o", alias = "ao")]
979    pub algo_order: AlgoOrderUpdateData,
980}
981
982/// Algo order update data payload.
983#[derive(Debug, Clone, Deserialize)]
984pub struct AlgoOrderUpdateData {
985    /// Client algo order ID.
986    #[serde(rename = "caid")]
987    pub client_algo_id: String,
988    /// Algo order ID.
989    #[serde(rename = "aid")]
990    pub algo_id: i64,
991    /// Algo type (currently only `Conditional`).
992    #[serde(rename = "at")]
993    pub algo_type: BinanceAlgoType,
994    /// Order type (STOP_MARKET, STOP, TAKE_PROFIT, TAKE_PROFIT_MARKET, TRAILING_STOP_MARKET).
995    #[serde(rename = "o")]
996    pub order_type: BinanceFuturesOrderType,
997    /// Symbol.
998    #[serde(rename = "s")]
999    pub symbol: Ustr,
1000    /// Order side.
1001    #[serde(rename = "S")]
1002    pub side: BinanceSide,
1003    /// Position side.
1004    #[serde(rename = "ps")]
1005    pub position_side: BinancePositionSide,
1006    /// Time in force.
1007    #[serde(rename = "f")]
1008    pub time_in_force: BinanceTimeInForce,
1009    /// Order quantity.
1010    #[serde(rename = "q")]
1011    pub quantity: String,
1012    /// Algo order status (NEW, TRIGGERING, TRIGGERED, FINISHED, CANCELED, EXPIRED, REJECTED).
1013    #[serde(rename = "X")]
1014    pub algo_status: BinanceAlgoStatus,
1015    /// Trigger price.
1016    #[serde(rename = "tp")]
1017    pub trigger_price: String,
1018    /// Limit price.
1019    #[serde(rename = "p")]
1020    pub price: String,
1021    /// Working type for trigger price calculation.
1022    #[serde(rename = "wt")]
1023    pub working_type: BinanceWorkingType,
1024    /// Price match mode.
1025    #[serde(rename = "pm", default)]
1026    pub price_match: Option<BinancePriceMatch>,
1027    /// Close position flag.
1028    #[serde(rename = "cp", default)]
1029    pub close_position: Option<bool>,
1030    /// Price protection flag.
1031    #[serde(rename = "pP", default)]
1032    pub price_protect: Option<bool>,
1033    /// Reduce-only flag.
1034    #[serde(rename = "R", default)]
1035    pub reduce_only: Option<bool>,
1036    /// Trigger time in milliseconds.
1037    #[serde(rename = "tt", default)]
1038    pub trigger_time: Option<i64>,
1039    /// Good till date in milliseconds.
1040    #[serde(rename = "gtd", default)]
1041    pub good_till_date: Option<i64>,
1042    /// Order ID in matching engine (populated when triggered).
1043    #[serde(rename = "ai", default)]
1044    pub actual_order_id: Option<String>,
1045    /// Average fill price in matching engine (populated when triggered).
1046    #[serde(rename = "ap", default)]
1047    pub avg_price: Option<String>,
1048    /// Executed quantity in matching engine (populated when triggered).
1049    #[serde(rename = "aq", default)]
1050    pub executed_qty: Option<String>,
1051    /// Actual order type in matching engine (populated when triggered).
1052    #[serde(rename = "act", default)]
1053    pub actual_order_type: Option<String>,
1054    /// Callback rate for trailing stop (0.1 to 10, where 1 = 1%).
1055    #[serde(rename = "cr", default)]
1056    pub callback_rate: Option<String>,
1057    /// Self-trade prevention mode.
1058    #[serde(rename = "V", default)]
1059    pub stp_mode: Option<BinanceSelfTradePreventionMode>,
1060}
1061
1062/// Listen key expired event.
1063#[derive(Debug, Clone, Deserialize)]
1064pub struct BinanceFuturesListenKeyExpiredMsg {
1065    /// Event type.
1066    #[serde(rename = "e")]
1067    pub event_type: String,
1068    /// Event time in milliseconds.
1069    #[serde(rename = "E")]
1070    pub event_time: i64,
1071}
1072
1073#[cfg(test)]
1074mod tests {
1075    use rstest::rstest;
1076
1077    use super::*;
1078
1079    #[rstest]
1080    fn test_account_update_reason_adl_deserializes() {
1081        let value: AccountUpdateReason = serde_json::from_str("\"ADL\"").unwrap();
1082        assert_eq!(value, AccountUpdateReason::Adl);
1083    }
1084
1085    #[rstest]
1086    fn test_account_update_reason_unknown_fallback() {
1087        let value: AccountUpdateReason = serde_json::from_str("\"SOMETHING_NEW\"").unwrap();
1088        assert_eq!(value, AccountUpdateReason::Unknown);
1089    }
1090}