nautilus_bitmex/websocket/
messages.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2025 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//! BitMEX WebSocket message structures and helper types.
17
18use std::collections::HashMap;
19
20use chrono::{DateTime, Utc};
21use nautilus_model::{
22    data::{Data, funding::FundingRateUpdate},
23    events::{AccountState, OrderUpdated},
24    reports::{FillReport, OrderStatusReport, PositionStatusReport},
25};
26use serde::{Deserialize, Deserializer, Serialize, de};
27use serde_json::Value;
28use strum::Display;
29use ustr::Ustr;
30use uuid::Uuid;
31
32use super::enums::{
33    BitmexAction, BitmexSide, BitmexTickDirection, BitmexWsAuthAction, BitmexWsOperation,
34};
35use crate::common::enums::{
36    BitmexContingencyType, BitmexExecInstruction, BitmexExecType, BitmexLiquidityIndicator,
37    BitmexOrderStatus, BitmexOrderType, BitmexPegPriceType, BitmexTimeInForce,
38};
39
40/// Custom deserializer for comma-separated `ExecInstruction` values.
41fn deserialize_exec_instructions<'de, D>(
42    deserializer: D,
43) -> Result<Option<Vec<BitmexExecInstruction>>, D::Error>
44where
45    D: serde::Deserializer<'de>,
46{
47    let s: Option<String> = Option::deserialize(deserializer)?;
48    match s {
49        None => Ok(None),
50        Some(ref s) if s.is_empty() => Ok(None),
51        Some(s) => {
52            let instructions: Result<Vec<BitmexExecInstruction>, _> = s
53                .split(',')
54                .map(|inst| {
55                    let trimmed = inst.trim();
56                    match trimmed {
57                        "ParticipateDoNotInitiate" => {
58                            Ok(BitmexExecInstruction::ParticipateDoNotInitiate)
59                        }
60                        "AllOrNone" => Ok(BitmexExecInstruction::AllOrNone),
61                        "MarkPrice" => Ok(BitmexExecInstruction::MarkPrice),
62                        "IndexPrice" => Ok(BitmexExecInstruction::IndexPrice),
63                        "LastPrice" => Ok(BitmexExecInstruction::LastPrice),
64                        "Close" => Ok(BitmexExecInstruction::Close),
65                        "ReduceOnly" => Ok(BitmexExecInstruction::ReduceOnly),
66                        "Fixed" => Ok(BitmexExecInstruction::Fixed),
67                        "" => Ok(BitmexExecInstruction::Unknown),
68                        _ => Err(format!("Unknown exec instruction: {trimmed}")),
69                    }
70                })
71                .collect();
72            instructions.map(Some).map_err(de::Error::custom)
73        }
74    }
75}
76
77/// BitMEX WebSocket authentication message.
78///
79/// The args array contains [api_key, expires/nonce, signature].
80/// The second element must be a number (not a string) for proper authentication.
81#[derive(Debug, Clone, Serialize, Deserialize)]
82pub struct BitmexAuthentication {
83    pub op: BitmexWsAuthAction,
84    pub args: (String, i64, String),
85}
86
87/// BitMEX WebSocket subscription message.
88#[derive(Debug, Clone, Serialize, Deserialize)]
89pub struct BitmexSubscription {
90    pub op: BitmexWsOperation,
91    pub args: Vec<Ustr>,
92}
93
94/// Unified WebSocket message type for BitMEX.
95#[derive(Clone, Debug)]
96pub enum NautilusWsMessage {
97    Data(Vec<Data>),
98    OrderStatusReports(Vec<OrderStatusReport>),
99    OrderUpdated(OrderUpdated),
100    FillReports(Vec<FillReport>),
101    PositionStatusReport(PositionStatusReport),
102    FundingRateUpdates(Vec<FundingRateUpdate>),
103    AccountState(AccountState),
104    Reconnected,
105}
106
107/// Represents all possible message types from the BitMEX WebSocket API.
108#[derive(Debug, Display, Deserialize)]
109#[serde(untagged)]
110pub enum BitmexWsMessage {
111    /// Table websocket message.
112    Table(BitmexTableMessage),
113    /// Initial welcome message received when connecting to the WebSocket.
114    Welcome {
115        /// Welcome message text.
116        info: String,
117        /// API version string.
118        version: String,
119        /// Server timestamp.
120        timestamp: DateTime<Utc>,
121        /// Link to API documentation.
122        docs: String,
123        /// Whether heartbeat is enabled for this connection.
124        #[serde(rename = "heartbeatEnabled")]
125        heartbeat_enabled: bool,
126        /// Rate limit information.
127        limit: BitmexRateLimit,
128        /// Application name (testnet only).
129        #[serde(rename = "appName")]
130        app_name: Option<String>,
131    },
132    /// Subscription response messages.
133    Subscription {
134        /// Whether the subscription request was successful.
135        success: bool,
136        /// The subscription topic if successful.
137        subscribe: Option<String>,
138        /// Original request metadata (present for subscribe/auth/unsubscribe).
139        request: Option<BitmexHttpRequest>,
140        /// Error message if subscription failed.
141        error: Option<String>,
142    },
143    /// WebSocket error message.
144    Error {
145        status: u16,
146        error: String,
147        meta: HashMap<String, String>,
148        request: BitmexHttpRequest,
149    },
150    /// Indicates a WebSocket reconnection has occurred.
151    #[serde(skip)]
152    Reconnected,
153}
154
155#[derive(Clone, Debug, Deserialize, PartialEq, Eq)]
156pub struct BitmexHttpRequest {
157    pub op: String,
158    pub args: Vec<Value>,
159}
160
161/// Rate limit information from BitMEX API.
162#[derive(Debug, Deserialize)]
163pub struct BitmexRateLimit {
164    /// Number of requests remaining in the current time window.
165    pub remaining: Option<i32>,
166}
167
168/// Represents table-based messages.
169#[derive(Debug, Display, Deserialize)]
170#[serde(rename_all = "camelCase")]
171#[serde(tag = "table")]
172pub enum BitmexTableMessage {
173    OrderBookL2 {
174        action: BitmexAction,
175        data: Vec<BitmexOrderBookMsg>,
176    },
177    OrderBookL2_25 {
178        action: BitmexAction,
179        data: Vec<BitmexOrderBookMsg>,
180    },
181    OrderBook10 {
182        action: BitmexAction,
183        data: Vec<BitmexOrderBook10Msg>,
184    },
185    Quote {
186        action: BitmexAction,
187        data: Vec<BitmexQuoteMsg>,
188    },
189    Trade {
190        action: BitmexAction,
191        data: Vec<BitmexTradeMsg>,
192    },
193    TradeBin1m {
194        action: BitmexAction,
195        data: Vec<BitmexTradeBinMsg>,
196    },
197    TradeBin5m {
198        action: BitmexAction,
199        data: Vec<BitmexTradeBinMsg>,
200    },
201    TradeBin1h {
202        action: BitmexAction,
203        data: Vec<BitmexTradeBinMsg>,
204    },
205    TradeBin1d {
206        action: BitmexAction,
207        data: Vec<BitmexTradeBinMsg>,
208    },
209    Instrument {
210        action: BitmexAction,
211        data: Vec<BitmexInstrumentMsg>,
212    },
213    Order {
214        action: BitmexAction,
215        #[serde(deserialize_with = "deserialize_order_data")]
216        data: Vec<OrderData>,
217    },
218    Execution {
219        action: BitmexAction,
220        data: Vec<BitmexExecutionMsg>,
221    },
222    Position {
223        action: BitmexAction,
224        data: Vec<BitmexPositionMsg>,
225    },
226    Wallet {
227        action: BitmexAction,
228        data: Vec<BitmexWalletMsg>,
229    },
230    Margin {
231        action: BitmexAction,
232        data: Vec<BitmexMarginMsg>,
233    },
234    Funding {
235        action: BitmexAction,
236        data: Vec<BitmexFundingMsg>,
237    },
238    Insurance {
239        action: BitmexAction,
240        data: Vec<BitmexInsuranceMsg>,
241    },
242    Liquidation {
243        action: BitmexAction,
244        data: Vec<BitmexLiquidationMsg>,
245    },
246}
247
248/// Represents a single order book entry in the BitMEX order book.
249#[derive(Clone, Debug, Deserialize)]
250#[serde(rename_all = "camelCase")]
251pub struct BitmexOrderBookMsg {
252    /// The instrument symbol (e.g., "XBTUSD").
253    pub symbol: Ustr,
254    /// Unique order ID.
255    pub id: u64,
256    /// Side of the order ("Buy" or "Sell").
257    pub side: BitmexSide,
258    /// Size of the order, can be None for deletes.
259    pub size: Option<u64>,
260    /// Price level of the order.
261    pub price: f64,
262    /// Timestamp of the update.
263    pub timestamp: DateTime<Utc>,
264    /// Timestamp of the transaction.
265    pub transact_time: DateTime<Utc>,
266}
267
268/// Represents a single order book entry in the BitMEX order book.
269#[derive(Clone, Debug, Deserialize)]
270#[serde(rename_all = "camelCase")]
271pub struct BitmexOrderBook10Msg {
272    /// The instrument symbol (e.g., "XBTUSD").
273    pub symbol: Ustr,
274    /// Array of bid levels, each containing [price, size].
275    pub bids: Vec<[f64; 2]>,
276    /// Array of ask levels, each containing [price, size].
277    pub asks: Vec<[f64; 2]>,
278    /// Timestamp of the orderbook snapshot.
279    pub timestamp: DateTime<Utc>,
280}
281
282/// Represents a top-of-book quote.
283#[derive(Clone, Debug, Deserialize)]
284#[serde(rename_all = "camelCase")]
285pub struct BitmexQuoteMsg {
286    /// The instrument symbol (e.g., "XBTUSD").
287    pub symbol: Ustr,
288    /// Price of best bid.
289    pub bid_price: Option<f64>,
290    /// Size of best bid.
291    pub bid_size: Option<u64>,
292    /// Price of best ask.
293    pub ask_price: Option<f64>,
294    /// Size of best ask.
295    pub ask_size: Option<u64>,
296    /// Timestamp of the quote.
297    pub timestamp: DateTime<Utc>,
298}
299
300/// Represents a single trade execution on BitMEX.
301#[derive(Clone, Debug, Deserialize)]
302#[serde(rename_all = "camelCase")]
303pub struct BitmexTradeMsg {
304    /// Timestamp of the trade.
305    pub timestamp: DateTime<Utc>,
306    /// The instrument symbol.
307    pub symbol: Ustr,
308    /// Side of the trade ("Buy" or "Sell").
309    pub side: BitmexSide,
310    /// Size of the trade.
311    pub size: u64,
312    /// Price the trade executed at.
313    pub price: f64,
314    /// Direction of the tick ("`PlusTick`", "`MinusTick`", "`ZeroPlusTick`", "`ZeroMinusTick`").
315    pub tick_direction: BitmexTickDirection,
316    /// Unique trade match ID.
317    #[serde(rename = "trdMatchID")]
318    pub trd_match_id: Option<Uuid>,
319    /// Gross value of the trade in satoshis.
320    pub gross_value: Option<i64>,
321    /// Home currency value of the trade.
322    pub home_notional: Option<f64>,
323    /// Foreign currency value of the trade.
324    pub foreign_notional: Option<f64>,
325    /// Trade type.
326    #[serde(rename = "trdType")]
327    pub trade_type: Ustr, // TODO: Add enum
328}
329
330#[derive(Clone, Debug, Deserialize)]
331#[serde(rename_all = "camelCase")]
332pub struct BitmexTradeBinMsg {
333    /// Start time of the bin
334    pub timestamp: DateTime<Utc>,
335    /// Trading instrument symbol
336    pub symbol: Ustr,
337    /// Opening price for the period
338    pub open: f64,
339    /// Highest price for the period
340    pub high: f64,
341    /// Lowest price for the period
342    pub low: f64,
343    /// Closing price for the period
344    pub close: f64,
345    /// Number of trades in the period
346    pub trades: i64,
347    /// Volume traded in the period
348    pub volume: i64,
349    /// Volume weighted average price
350    pub vwap: f64,
351    /// Size of the last trade in the period
352    pub last_size: i64,
353    /// Turnover in satoshis
354    pub turnover: i64,
355    /// Home currency volume
356    pub home_notional: f64,
357    /// Foreign currency volume
358    pub foreign_notional: f64,
359}
360
361/// Represents a single order book entry in the BitMEX order book.
362#[derive(Clone, Debug, Deserialize)]
363#[serde(rename_all = "camelCase")]
364pub struct BitmexInstrumentMsg {
365    /// The instrument symbol (e.g., "XBTUSD").
366    pub symbol: Ustr,
367    /// Last traded price for the instrument.
368    pub last_price: Option<f64>,
369    /// Last tick direction for the instrument.
370    pub last_tick_direction: Option<BitmexTickDirection>,
371    /// Mark price.
372    pub mark_price: Option<f64>,
373    /// Index price.
374    pub index_price: Option<f64>,
375    /// Indicative settlement price.
376    pub indicative_settle_price: Option<f64>,
377    /// Open interest for the instrument.
378    pub open_interest: Option<i64>,
379    /// Open value for the instrument.
380    pub open_value: Option<i64>,
381    /// Fair basis.
382    pub fair_basis: Option<f64>,
383    /// Fair basis rate.
384    pub fair_basis_rate: Option<f64>,
385    /// Fair price.
386    pub fair_price: Option<f64>,
387    /// Mark method.
388    pub mark_method: Option<Ustr>,
389    /// Indicative tax rate.
390    pub indicative_tax_rate: Option<f64>,
391    /// Timestamp of the update.
392    pub timestamp: DateTime<Utc>,
393}
394
395/// Represents an order update message with only changed fields.
396/// Used for `update` actions where only modified fields are sent.
397#[derive(Clone, Debug, Deserialize)]
398#[serde(rename_all = "camelCase")]
399pub struct BitmexOrderUpdateMsg {
400    #[serde(rename = "orderID")]
401    pub order_id: Uuid,
402    #[serde(rename = "clOrdID")]
403    pub cl_ord_id: Option<Ustr>,
404    pub account: i64,
405    pub symbol: Ustr,
406    pub side: Option<BitmexSide>,
407    pub price: Option<f64>,
408    pub currency: Option<Ustr>,
409    pub text: Option<Ustr>,
410    pub transact_time: Option<DateTime<Utc>>,
411    pub timestamp: Option<DateTime<Utc>>,
412    pub leaves_qty: Option<i64>,
413    pub cum_qty: Option<i64>,
414    pub ord_status: Option<BitmexOrderStatus>,
415}
416
417/// Represents a full order message from the WebSocket stream.
418/// Used for `insert` and `partial` actions where all fields are present.
419#[derive(Clone, Debug, Deserialize)]
420#[serde(rename_all = "camelCase")]
421pub struct BitmexOrderMsg {
422    #[serde(rename = "orderID")]
423    pub order_id: Uuid,
424    #[serde(rename = "clOrdID")]
425    pub cl_ord_id: Option<Ustr>,
426    #[serde(rename = "clOrdLinkID")]
427    pub cl_ord_link_id: Option<Ustr>,
428    pub account: i64,
429    pub symbol: Ustr,
430    pub side: BitmexSide,
431    pub order_qty: i64,
432    pub price: Option<f64>,
433    pub display_qty: Option<i64>,
434    pub stop_px: Option<f64>,
435    pub peg_offset_value: Option<f64>,
436    pub peg_price_type: Option<BitmexPegPriceType>,
437    pub currency: Ustr,
438    pub settl_currency: Ustr,
439    pub ord_type: Option<BitmexOrderType>,
440    pub time_in_force: Option<BitmexTimeInForce>,
441    #[serde(default, deserialize_with = "deserialize_exec_instructions")]
442    pub exec_inst: Option<Vec<BitmexExecInstruction>>,
443    pub contingency_type: Option<BitmexContingencyType>,
444    pub ord_status: BitmexOrderStatus,
445    pub triggered: Option<Ustr>,
446    pub working_indicator: bool,
447    pub ord_rej_reason: Option<Ustr>,
448    pub leaves_qty: i64,
449    pub cum_qty: i64,
450    pub avg_px: Option<f64>,
451    pub text: Option<Ustr>,
452    pub transact_time: DateTime<Utc>,
453    pub timestamp: DateTime<Utc>,
454}
455
456/// Wrapper enum for order data that can be either full or update messages.
457#[derive(Clone, Debug)]
458pub enum OrderData {
459    Full(BitmexOrderMsg),
460    Update(BitmexOrderUpdateMsg),
461}
462
463/// Custom deserializer for order data that tries to deserialize as full message first,
464/// then falls back to update message if fields are missing.
465fn deserialize_order_data<'de, D>(deserializer: D) -> Result<Vec<OrderData>, D::Error>
466where
467    D: Deserializer<'de>,
468{
469    let raw_values: Vec<serde_json::Value> = Vec::deserialize(deserializer)?;
470    let mut result = Vec::new();
471
472    for value in raw_values {
473        // Try to deserialize as full message first
474        if let Ok(full_msg) = serde_json::from_value::<BitmexOrderMsg>(value.clone()) {
475            result.push(OrderData::Full(full_msg));
476        } else if let Ok(update_msg) = serde_json::from_value::<BitmexOrderUpdateMsg>(value) {
477            result.push(OrderData::Update(update_msg));
478        } else {
479            return Err(de::Error::custom(
480                "Failed to deserialize order data as either full or update message",
481            ));
482        }
483    }
484
485    Ok(result)
486}
487
488/// Raw Order and Balance Data.
489#[derive(Clone, Debug, Deserialize)]
490#[serde(rename_all = "camelCase")]
491pub struct BitmexExecutionMsg {
492    #[serde(rename = "execID")]
493    pub exec_id: Option<Uuid>,
494    #[serde(rename = "orderID")]
495    pub order_id: Option<Uuid>,
496    #[serde(rename = "clOrdID")]
497    pub cl_ord_id: Option<Ustr>,
498    #[serde(rename = "clOrdLinkID")]
499    pub cl_ord_link_id: Option<Ustr>,
500    pub account: Option<i64>,
501    pub symbol: Option<Ustr>,
502    pub side: Option<BitmexSide>,
503    pub last_qty: Option<i64>,
504    pub last_px: Option<f64>,
505    pub underlying_last_px: Option<f64>,
506    pub last_mkt: Option<Ustr>,
507    pub last_liquidity_ind: Option<BitmexLiquidityIndicator>,
508    pub order_qty: Option<i64>,
509    pub price: Option<f64>,
510    pub display_qty: Option<i64>,
511    pub stop_px: Option<f64>,
512    pub peg_offset_value: Option<f64>,
513    pub peg_price_type: Option<BitmexPegPriceType>,
514    pub currency: Option<Ustr>,
515    pub settl_currency: Option<Ustr>,
516    pub exec_type: Option<BitmexExecType>,
517    pub ord_type: Option<BitmexOrderType>,
518    pub time_in_force: Option<BitmexTimeInForce>,
519    #[serde(default, deserialize_with = "deserialize_exec_instructions")]
520    pub exec_inst: Option<Vec<BitmexExecInstruction>>,
521    pub contingency_type: Option<BitmexContingencyType>,
522    pub ex_destination: Option<Ustr>,
523    pub ord_status: Option<BitmexOrderStatus>,
524    pub triggered: Option<Ustr>,
525    pub working_indicator: Option<bool>,
526    pub ord_rej_reason: Option<Ustr>,
527    pub leaves_qty: Option<i64>,
528    pub cum_qty: Option<i64>,
529    pub avg_px: Option<f64>,
530    pub commission: Option<f64>,
531    pub trade_publish_indicator: Option<Ustr>,
532    pub multi_leg_reporting_type: Option<Ustr>,
533    pub text: Option<Ustr>,
534    #[serde(rename = "trdMatchID")]
535    pub trd_match_id: Option<Uuid>,
536    pub exec_cost: Option<i64>,
537    pub exec_comm: Option<i64>,
538    pub home_notional: Option<f64>,
539    pub foreign_notional: Option<f64>,
540    pub transact_time: Option<DateTime<Utc>>,
541    pub timestamp: Option<DateTime<Utc>>,
542}
543
544/// Position status.
545#[derive(Clone, Debug, Deserialize)]
546#[serde(rename_all = "camelCase")]
547pub struct BitmexPositionMsg {
548    pub account: i64,
549    pub symbol: Ustr,
550    pub currency: Option<Ustr>,
551    pub underlying: Option<Ustr>,
552    pub quote_currency: Option<Ustr>,
553    pub commission: Option<f64>,
554    pub init_margin_req: Option<f64>,
555    pub maint_margin_req: Option<f64>,
556    pub risk_limit: Option<i64>,
557    pub leverage: Option<f64>,
558    pub cross_margin: Option<bool>,
559    pub deleverage_percentile: Option<f64>,
560    pub rebalanced_pnl: Option<i64>,
561    pub prev_realised_pnl: Option<i64>,
562    pub prev_unrealised_pnl: Option<i64>,
563    pub prev_close_price: Option<f64>,
564    pub opening_timestamp: Option<DateTime<Utc>>,
565    pub opening_qty: Option<i64>,
566    pub opening_cost: Option<i64>,
567    pub opening_comm: Option<i64>,
568    pub open_order_buy_qty: Option<i64>,
569    pub open_order_buy_cost: Option<i64>,
570    pub open_order_buy_premium: Option<i64>,
571    pub open_order_sell_qty: Option<i64>,
572    pub open_order_sell_cost: Option<i64>,
573    pub open_order_sell_premium: Option<i64>,
574    pub exec_buy_qty: Option<i64>,
575    pub exec_buy_cost: Option<i64>,
576    pub exec_sell_qty: Option<i64>,
577    pub exec_sell_cost: Option<i64>,
578    pub exec_qty: Option<i64>,
579    pub exec_cost: Option<i64>,
580    pub exec_comm: Option<i64>,
581    pub current_timestamp: Option<DateTime<Utc>>,
582    pub current_qty: Option<i64>,
583    pub current_cost: Option<i64>,
584    pub current_comm: Option<i64>,
585    pub realised_cost: Option<i64>,
586    pub unrealised_cost: Option<i64>,
587    pub gross_open_cost: Option<i64>,
588    pub gross_open_premium: Option<i64>,
589    pub gross_exec_cost: Option<i64>,
590    pub is_open: Option<bool>,
591    pub mark_price: Option<f64>,
592    pub mark_value: Option<i64>,
593    pub risk_value: Option<i64>,
594    pub home_notional: Option<f64>,
595    pub foreign_notional: Option<f64>,
596    pub pos_state: Option<Ustr>,
597    pub pos_cost: Option<i64>,
598    pub pos_cost2: Option<i64>,
599    pub pos_cross: Option<i64>,
600    pub pos_init: Option<i64>,
601    pub pos_comm: Option<i64>,
602    pub pos_loss: Option<i64>,
603    pub pos_margin: Option<i64>,
604    pub pos_maint: Option<i64>,
605    pub pos_allowance: Option<i64>,
606    pub taxable_margin: Option<i64>,
607    pub init_margin: Option<i64>,
608    pub maint_margin: Option<i64>,
609    pub session_margin: Option<i64>,
610    pub target_excess_margin: Option<i64>,
611    pub var_margin: Option<i64>,
612    pub realised_gross_pnl: Option<i64>,
613    pub realised_tax: Option<i64>,
614    pub realised_pnl: Option<i64>,
615    pub unrealised_gross_pnl: Option<i64>,
616    pub long_bankrupt: Option<i64>,
617    pub short_bankrupt: Option<i64>,
618    pub tax_base: Option<i64>,
619    pub indicative_tax_rate: Option<f64>,
620    pub indicative_tax: Option<i64>,
621    pub unrealised_tax: Option<i64>,
622    pub unrealised_pnl: Option<i64>,
623    pub unrealised_pnl_pcnt: Option<f64>,
624    pub unrealised_roe_pcnt: Option<f64>,
625    pub avg_cost_price: Option<f64>,
626    pub avg_entry_price: Option<f64>,
627    pub break_even_price: Option<f64>,
628    pub margin_call_price: Option<f64>,
629    pub liquidation_price: Option<f64>,
630    pub bankrupt_price: Option<f64>,
631    pub timestamp: Option<DateTime<Utc>>,
632    pub last_price: Option<f64>,
633    pub last_value: Option<i64>,
634}
635
636#[derive(Clone, Debug, Deserialize)]
637#[serde(rename_all = "camelCase")]
638pub struct BitmexWalletMsg {
639    pub account: i64,
640    pub currency: Ustr,
641    pub prev_deposited: Option<i64>,
642    pub prev_withdrawn: Option<i64>,
643    pub prev_transfer_in: Option<i64>,
644    pub prev_transfer_out: Option<i64>,
645    pub prev_amount: Option<i64>,
646    pub prev_timestamp: Option<DateTime<Utc>>,
647    pub delta_deposited: Option<i64>,
648    pub delta_withdrawn: Option<i64>,
649    pub delta_transfer_in: Option<i64>,
650    pub delta_transfer_out: Option<i64>,
651    pub delta_amount: Option<i64>,
652    pub deposited: Option<i64>,
653    pub withdrawn: Option<i64>,
654    pub transfer_in: Option<i64>,
655    pub transfer_out: Option<i64>,
656    pub amount: Option<i64>,
657    pub pending_credit: Option<i64>,
658    pub pending_debit: Option<i64>,
659    pub confirmed_debit: Option<i64>,
660    pub timestamp: Option<DateTime<Utc>>,
661    pub addr: Option<Ustr>,
662    pub script: Option<Ustr>,
663    pub withdrawal_lock: Option<Vec<Ustr>>,
664}
665
666/// Represents margin account information
667#[derive(Clone, Debug, Deserialize)]
668#[serde(rename_all = "camelCase")]
669pub struct BitmexMarginMsg {
670    /// Account identifier
671    pub account: i64,
672    /// Currency of the margin account
673    pub currency: Ustr,
674    /// Risk limit for the account
675    pub risk_limit: Option<i64>,
676    /// Current amount in the account
677    pub amount: Option<i64>,
678    /// Previously realized PnL
679    pub prev_realised_pnl: Option<i64>,
680    /// Gross commission
681    pub gross_comm: Option<i64>,
682    /// Gross open cost
683    pub gross_open_cost: Option<i64>,
684    /// Gross open premium
685    pub gross_open_premium: Option<i64>,
686    /// Gross execution cost
687    pub gross_exec_cost: Option<i64>,
688    /// Gross mark value
689    pub gross_mark_value: Option<i64>,
690    /// Risk value
691    pub risk_value: Option<i64>,
692    /// Initial margin requirement
693    pub init_margin: Option<i64>,
694    /// Maintenance margin requirement
695    pub maint_margin: Option<i64>,
696    /// Target excess margin
697    pub target_excess_margin: Option<i64>,
698    /// Realized profit and loss
699    pub realised_pnl: Option<i64>,
700    /// Unrealized profit and loss
701    pub unrealised_pnl: Option<i64>,
702    /// Wallet balance
703    pub wallet_balance: Option<i64>,
704    /// Margin balance
705    pub margin_balance: Option<i64>,
706    /// Margin leverage
707    pub margin_leverage: Option<f64>,
708    /// Margin used percentage
709    pub margin_used_pcnt: Option<f64>,
710    /// Excess margin
711    pub excess_margin: Option<i64>,
712    /// Available margin
713    pub available_margin: Option<i64>,
714    /// Withdrawable margin
715    pub withdrawable_margin: Option<i64>,
716    /// Maker fee discount
717    pub maker_fee_discount: Option<f64>,
718    /// Taker fee discount
719    pub taker_fee_discount: Option<f64>,
720    /// Timestamp of the margin update
721    pub timestamp: DateTime<Utc>,
722    /// Foreign margin balance
723    pub foreign_margin_balance: Option<i64>,
724    /// Foreign margin requirement
725    pub foreign_requirement: Option<i64>,
726}
727
728/// Represents a funding rate update.
729#[derive(Clone, Debug, Deserialize)]
730#[serde(rename_all = "camelCase")]
731pub struct BitmexFundingMsg {
732    /// Timestamp of the funding update.
733    pub timestamp: DateTime<Utc>,
734    /// The instrument symbol the funding applies to.
735    pub symbol: Ustr,
736    /// The funding rate for this interval.
737    pub funding_rate: f64,
738    /// The daily funding rate.
739    pub funding_rate_daily: f64,
740}
741
742/// Represents an insurance fund update.
743#[derive(Clone, Debug, Deserialize)]
744#[serde(rename_all = "camelCase")]
745pub struct BitmexInsuranceMsg {
746    /// The currency of the insurance fund.
747    pub currency: Ustr,
748    /// Timestamp of the update.
749    pub timestamp: DateTime<Utc>,
750    /// Current balance of the insurance wallet.
751    pub wallet_balance: i64,
752}
753
754/// Represents a liquidation order.
755#[derive(Clone, Debug, Deserialize)]
756#[serde(rename_all = "camelCase")]
757pub struct BitmexLiquidationMsg {
758    /// Unique order ID of the liquidation.
759    pub order_id: Ustr,
760    /// The instrument symbol being liquidated.
761    pub symbol: Ustr,
762    /// Side of the liquidation ("Buy" or "Sell").
763    pub side: BitmexSide,
764    /// Price of the liquidation order.
765    pub price: f64,
766    /// Remaining quantity to be executed.
767    pub leaves_qty: i64,
768}