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