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