nautilus_dydx/websocket/
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//! WebSocket message types for dYdX public and private channels.
17
18use std::collections::HashMap;
19
20use chrono::{DateTime, Utc};
21use nautilus_model::enums::{OrderSide, PositionSide};
22use serde::{Deserialize, Serialize};
23use serde_json::Value;
24use ustr::Ustr;
25
26use super::enums::{DydxWsChannel, DydxWsMessageType, DydxWsOperation};
27use crate::common::enums::{
28    DydxCandleResolution, DydxFillType, DydxLiquidity, DydxOrderStatus, DydxOrderType,
29    DydxPositionStatus, DydxTickerType, DydxTimeInForce, DydxTradeType,
30};
31
32/// dYdX WebSocket subscription message.
33///
34/// # References
35///
36/// <https://docs.dydx.trade/developers/indexer/websockets>
37#[derive(Debug, Clone, Serialize, Deserialize)]
38pub struct DydxSubscription {
39    /// The operation type (subscribe/unsubscribe).
40    #[serde(rename = "type")]
41    pub op: DydxWsOperation,
42    /// The channel to subscribe to.
43    pub channel: DydxWsChannel,
44    /// Optional channel-specific identifier (e.g., market symbol).
45    #[serde(skip_serializing_if = "Option::is_none")]
46    pub id: Option<String>,
47}
48
49/// Generic subscription/unsubscription confirmation message.
50#[derive(Debug, Clone, Serialize, Deserialize)]
51pub struct DydxWsSubscriptionMsg {
52    /// The message type ("subscribed" or "unsubscribed").
53    #[serde(rename = "type")]
54    pub msg_type: DydxWsMessageType,
55    /// The connection ID.
56    pub connection_id: String,
57    /// The message sequence number.
58    pub message_id: u64,
59    /// The channel name.
60    pub channel: DydxWsChannel,
61    /// Optional channel-specific identifier.
62    #[serde(default, skip_serializing_if = "Option::is_none")]
63    pub id: Option<String>,
64}
65
66/// Connection established message.
67#[derive(Debug, Clone, Serialize, Deserialize)]
68pub struct DydxWsConnectedMsg {
69    /// The message type ("connected").
70    #[serde(rename = "type")]
71    pub msg_type: DydxWsMessageType,
72    /// The connection ID assigned by the server.
73    pub connection_id: String,
74    /// The message sequence number.
75    pub message_id: u64,
76}
77
78/// Single channel data update message.
79#[derive(Debug, Clone, Serialize, Deserialize)]
80pub struct DydxWsChannelDataMsg {
81    /// The message type ("channel_data").
82    #[serde(rename = "type")]
83    pub msg_type: DydxWsMessageType,
84    /// The connection ID.
85    pub connection_id: String,
86    /// The message sequence number.
87    pub message_id: u64,
88    /// The channel name.
89    pub channel: DydxWsChannel,
90    /// Optional channel-specific identifier.
91    #[serde(default, skip_serializing_if = "Option::is_none")]
92    pub id: Option<String>,
93    /// The payload data (format depends on channel).
94    pub contents: Value,
95    /// API version.
96    #[serde(default, skip_serializing_if = "Option::is_none")]
97    pub version: Option<String>,
98}
99
100/// Batch channel data update message (multiple updates in one message).
101#[derive(Debug, Clone, Serialize, Deserialize)]
102pub struct DydxWsChannelBatchDataMsg {
103    /// The message type ("channel_batch_data").
104    #[serde(rename = "type")]
105    pub msg_type: DydxWsMessageType,
106    /// The connection ID.
107    pub connection_id: String,
108    /// The message sequence number.
109    pub message_id: u64,
110    /// The channel name.
111    pub channel: DydxWsChannel,
112    /// Optional channel-specific identifier.
113    #[serde(default, skip_serializing_if = "Option::is_none")]
114    pub id: Option<String>,
115    /// Array of payload data.
116    pub contents: Value,
117    /// API version.
118    #[serde(default, skip_serializing_if = "Option::is_none")]
119    pub version: Option<String>,
120}
121
122/// General WebSocket message structure for routing.
123#[derive(Debug, Clone, Serialize, Deserialize)]
124pub struct DydxWsMessageGeneral {
125    #[serde(rename = "type")]
126    pub msg_type: Option<DydxWsMessageType>,
127    pub connection_id: Option<String>,
128    pub message_id: Option<u64>,
129    pub channel: Option<DydxWsChannel>,
130    pub id: Option<String>,
131    pub message: Option<String>,
132}
133
134/// Two-level WebSocket message envelope matching dYdX protocol.
135///
136/// First level: Routes by channel field (v4_subaccounts, v4_orderbook, etc.)
137/// Second level: Each channel variant contains type-tagged messages
138///
139/// # References
140///
141/// <https://github.com/dydxprotocol/v4-clients/blob/main/v4-client-rs/client/src/indexer/sock/messages.rs#L253>
142#[derive(Debug, Clone, Serialize, Deserialize)]
143#[serde(tag = "channel")]
144pub enum DydxWsFeedMessage {
145    /// Subaccount updates (orders, fills, positions).
146    #[serde(rename = "v4_subaccounts")]
147    Subaccounts(DydxWsSubaccountsMessage),
148    /// Order book snapshots and updates.
149    #[serde(rename = "v4_orderbook")]
150    Orderbook(DydxWsOrderbookMessage),
151    /// Trade stream for specific market.
152    #[serde(rename = "v4_trades")]
153    Trades(DydxWsTradesMessage),
154    /// Market data for all markets.
155    #[serde(rename = "v4_markets")]
156    Markets(DydxWsMarketsMessage),
157    /// Candlestick/kline data.
158    #[serde(rename = "v4_candles")]
159    Candles(DydxWsCandlesMessage),
160    /// Parent subaccount updates (for isolated positions).
161    #[serde(rename = "v4_parent_subaccounts")]
162    ParentSubaccounts(DydxWsParentSubaccountsMessage),
163    /// Block height updates from chain.
164    #[serde(rename = "v4_block_height")]
165    BlockHeight(DydxWsBlockHeightMessage),
166}
167
168/// Subaccounts channel messages (second level, type-tagged).
169#[derive(Debug, Clone, Serialize, Deserialize)]
170#[serde(tag = "type")]
171pub enum DydxWsSubaccountsMessage {
172    /// Initial subscription confirmation.
173    #[serde(rename = "subscribed")]
174    Subscribed(DydxWsSubaccountsSubscribed),
175    /// Channel data update.
176    #[serde(rename = "channel_data")]
177    ChannelData(DydxWsSubaccountsChannelData),
178}
179
180/// Orderbook channel messages (second level, type-tagged).
181#[derive(Debug, Clone, Serialize, Deserialize)]
182#[serde(tag = "type")]
183pub enum DydxWsOrderbookMessage {
184    /// Initial subscription confirmation.
185    #[serde(rename = "subscribed")]
186    Subscribed(DydxWsChannelDataMsg),
187    /// Channel data update.
188    #[serde(rename = "channel_data")]
189    ChannelData(DydxWsChannelDataMsg),
190    /// Batch channel data.
191    #[serde(rename = "channel_batch_data")]
192    ChannelBatchData(DydxWsChannelBatchDataMsg),
193}
194
195/// Trades channel messages (second level, type-tagged).
196#[derive(Debug, Clone, Serialize, Deserialize)]
197#[serde(tag = "type")]
198pub enum DydxWsTradesMessage {
199    /// Initial subscription confirmation.
200    #[serde(rename = "subscribed")]
201    Subscribed(DydxWsChannelDataMsg),
202    /// Channel data update.
203    #[serde(rename = "channel_data")]
204    ChannelData(DydxWsChannelDataMsg),
205}
206
207/// Markets channel messages (second level, type-tagged).
208#[derive(Debug, Clone, Serialize, Deserialize)]
209#[serde(tag = "type")]
210pub enum DydxWsMarketsMessage {
211    /// Initial subscription confirmation.
212    #[serde(rename = "subscribed")]
213    Subscribed(DydxWsChannelDataMsg),
214    /// Channel data update.
215    #[serde(rename = "channel_data")]
216    ChannelData(DydxWsChannelDataMsg),
217}
218
219/// Candles channel messages (second level, type-tagged).
220#[derive(Debug, Clone, Serialize, Deserialize)]
221#[serde(tag = "type")]
222pub enum DydxWsCandlesMessage {
223    /// Initial subscription confirmation.
224    #[serde(rename = "subscribed")]
225    Subscribed(DydxWsChannelDataMsg),
226    /// Channel data update.
227    #[serde(rename = "channel_data")]
228    ChannelData(DydxWsChannelDataMsg),
229}
230
231/// Parent subaccounts channel messages (second level, type-tagged).
232#[derive(Debug, Clone, Serialize, Deserialize)]
233#[serde(tag = "type")]
234pub enum DydxWsParentSubaccountsMessage {
235    /// Initial subscription confirmation.
236    #[serde(rename = "subscribed")]
237    Subscribed(DydxWsChannelDataMsg),
238    /// Channel data update.
239    #[serde(rename = "channel_data")]
240    ChannelData(DydxWsChannelDataMsg),
241}
242
243/// Block height channel messages (second level, type-tagged).
244#[derive(Debug, Clone, Serialize, Deserialize)]
245#[serde(tag = "type")]
246pub enum DydxWsBlockHeightMessage {
247    /// Initial subscription confirmation.
248    #[serde(rename = "subscribed")]
249    Subscribed(DydxWsBlockHeightSubscribedData),
250    /// Channel data update.
251    #[serde(rename = "channel_data")]
252    ChannelData(DydxWsBlockHeightChannelData),
253}
254
255/// Generic message structure for initial classification (fallback for non-channel messages).
256#[derive(Debug, Clone, Serialize, Deserialize)]
257pub struct DydxWsGenericMsg {
258    /// The message type.
259    #[serde(rename = "type")]
260    pub msg_type: DydxWsMessageType,
261    /// Optional connection ID.
262    #[serde(default, skip_serializing_if = "Option::is_none")]
263    pub connection_id: Option<String>,
264    /// Optional message sequence number.
265    #[serde(default, skip_serializing_if = "Option::is_none")]
266    pub message_id: Option<u64>,
267    /// Optional channel name.
268    #[serde(default, skip_serializing_if = "Option::is_none")]
269    pub channel: Option<DydxWsChannel>,
270    /// Optional channel-specific identifier.
271    #[serde(default, skip_serializing_if = "Option::is_none")]
272    pub id: Option<String>,
273    /// Optional error message.
274    #[serde(default, skip_serializing_if = "Option::is_none")]
275    pub message: Option<String>,
276}
277
278impl DydxWsGenericMsg {
279    /// Returns `true` if this message is an error.
280    #[must_use]
281    pub fn is_error(&self) -> bool {
282        self.msg_type == DydxWsMessageType::Error
283    }
284
285    /// Returns `true` if this message is a subscription confirmation.
286    #[must_use]
287    pub fn is_subscribed(&self) -> bool {
288        self.msg_type == DydxWsMessageType::Subscribed
289    }
290
291    /// Returns `true` if this message is an unsubscription confirmation.
292    #[must_use]
293    pub fn is_unsubscribed(&self) -> bool {
294        self.msg_type == DydxWsMessageType::Unsubscribed
295    }
296
297    /// Returns `true` if this message is a connection notification.
298    #[must_use]
299    pub fn is_connected(&self) -> bool {
300        self.msg_type == DydxWsMessageType::Connected
301    }
302
303    /// Returns `true` if this message is channel data.
304    #[must_use]
305    pub fn is_channel_data(&self) -> bool {
306        self.msg_type == DydxWsMessageType::ChannelData
307    }
308
309    /// Returns `true` if this message is batch channel data.
310    #[must_use]
311    pub fn is_channel_batch_data(&self) -> bool {
312        self.msg_type == DydxWsMessageType::ChannelBatchData
313    }
314
315    /// Returns `true` if this message is an unknown/unrecognized type.
316    #[must_use]
317    pub fn is_unknown(&self) -> bool {
318        self.msg_type == DydxWsMessageType::Unknown
319    }
320}
321
322/// Block height subscription confirmed contents.
323#[derive(Debug, Clone, Serialize, Deserialize)]
324pub struct DydxBlockHeightSubscribedContents {
325    pub height: String,
326    pub time: DateTime<Utc>,
327}
328
329/// Block height subscription confirmed message.
330#[derive(Debug, Clone, Serialize, Deserialize)]
331pub struct DydxWsBlockHeightSubscribedData {
332    #[serde(rename = "type")]
333    pub msg_type: DydxWsMessageType,
334    pub connection_id: String,
335    pub message_id: u64,
336    pub channel: DydxWsChannel,
337    pub id: String,
338    pub contents: DydxBlockHeightSubscribedContents,
339}
340
341/// Block height channel data contents.
342#[derive(Debug, Clone, Serialize, Deserialize)]
343pub struct DydxBlockHeightChannelContents {
344    #[serde(rename = "blockHeight")]
345    pub block_height: String,
346    pub time: DateTime<Utc>,
347}
348
349/// Block height channel data message.
350#[derive(Debug, Clone, Serialize, Deserialize)]
351pub struct DydxWsBlockHeightChannelData {
352    #[serde(rename = "type")]
353    pub msg_type: DydxWsMessageType,
354    pub connection_id: String,
355    pub message_id: u64,
356    pub id: String,
357    pub channel: DydxWsChannel,
358    pub version: String,
359    pub contents: DydxBlockHeightChannelContents,
360}
361
362/// Oracle price data for a market (full format from subscribed message).
363#[derive(Debug, Clone, Serialize, Deserialize)]
364pub struct DydxOraclePriceMarketFull {
365    #[serde(rename = "oraclePrice")]
366    pub oracle_price: String,
367    #[serde(rename = "effectiveAt")]
368    pub effective_at: String,
369    #[serde(rename = "effectiveAtHeight")]
370    pub effective_at_height: String,
371    #[serde(rename = "marketId")]
372    pub market_id: u32,
373}
374
375/// Oracle price data for a market (simple format from channel_data).
376#[derive(Debug, Clone, Serialize, Deserialize)]
377#[serde(rename_all = "camelCase")]
378pub struct DydxOraclePriceMarket {
379    /// Oracle price.
380    pub oracle_price: String,
381}
382
383/// Market message contents.
384#[derive(Debug, Clone, Serialize, Deserialize)]
385pub struct DydxMarketMessageContents {
386    #[serde(rename = "oraclePrices")]
387    pub oracle_prices: Option<HashMap<String, DydxOraclePriceMarketFull>>,
388    pub trading: Option<Value>,
389}
390
391/// Markets channel data message.
392#[derive(Debug, Clone, Serialize, Deserialize)]
393pub struct DydxWsMarketChannelData {
394    #[serde(rename = "type")]
395    pub msg_type: DydxWsMessageType,
396    pub channel: DydxWsChannel,
397    pub contents: DydxMarketMessageContents,
398    pub version: String,
399    pub message_id: u64,
400    pub connection_id: Option<String>,
401    pub id: Option<String>,
402}
403
404/// Markets subscription confirmed message.
405#[derive(Debug, Clone, Serialize, Deserialize)]
406pub struct DydxWsMarketSubscribed {
407    #[serde(rename = "type")]
408    pub msg_type: DydxWsMessageType,
409    pub connection_id: String,
410    pub message_id: u64,
411    pub channel: DydxWsChannel,
412    pub contents: Value,
413}
414
415/// Contents of v4_markets channel_data message (simple format).
416#[derive(Debug, Clone, Serialize, Deserialize)]
417#[serde(rename_all = "camelCase")]
418pub struct DydxMarketsContents {
419    /// Oracle prices by market symbol.
420    #[serde(skip_serializing_if = "Option::is_none")]
421    pub oracle_prices: Option<HashMap<String, DydxOraclePriceMarket>>,
422}
423
424/// Trade message from v4_trades channel.
425#[derive(Debug, Clone, Serialize, Deserialize)]
426#[serde(rename_all = "camelCase")]
427pub struct DydxTrade {
428    /// Trade ID.
429    pub id: String,
430    /// Order side (BUY/SELL).
431    pub side: OrderSide,
432    /// Trade size.
433    pub size: String,
434    /// Trade price.
435    pub price: String,
436    /// Trade timestamp.
437    pub created_at: DateTime<Utc>,
438    /// Trade type.
439    #[serde(rename = "type")]
440    pub trade_type: DydxTradeType,
441    /// Block height (optional).
442    #[serde(skip_serializing_if = "Option::is_none")]
443    pub created_at_height: Option<String>,
444}
445
446/// Contents of v4_trades channel_data message.
447#[derive(Debug, Clone, Serialize, Deserialize)]
448pub struct DydxTradeContents {
449    /// Array of trades.
450    pub trades: Vec<DydxTrade>,
451}
452
453/// Candle/bar data from v4_candles channel.
454#[derive(Debug, Clone, Serialize, Deserialize)]
455#[serde(rename_all = "camelCase")]
456pub struct DydxCandle {
457    /// Base token volume.
458    pub base_token_volume: String,
459    /// Close price.
460    pub close: String,
461    /// High price.
462    pub high: String,
463    /// Low price.
464    pub low: String,
465    /// Open price.
466    pub open: String,
467    /// Resolution/timeframe.
468    pub resolution: DydxCandleResolution,
469    /// Start time.
470    pub started_at: DateTime<Utc>,
471    /// Starting open interest.
472    pub starting_open_interest: String,
473    /// Market ticker.
474    pub ticker: String,
475    /// Number of trades.
476    pub trades: i64,
477    /// USD volume.
478    pub usd_volume: String,
479    /// Orderbook mid price at close (optional).
480    #[serde(skip_serializing_if = "Option::is_none")]
481    pub orderbook_mid_price_close: Option<String>,
482    /// Orderbook mid price at open (optional).
483    #[serde(skip_serializing_if = "Option::is_none")]
484    pub orderbook_mid_price_open: Option<String>,
485}
486
487/// Order book price level (price, size tuple).
488pub type PriceLevel = (String, String);
489
490/// Contents of v4_orderbook channel_data/channel_batch_data messages.
491#[derive(Debug, Clone, Serialize, Deserialize)]
492pub struct DydxOrderbookContents {
493    /// Bid price levels.
494    #[serde(skip_serializing_if = "Option::is_none")]
495    pub bids: Option<Vec<PriceLevel>>,
496    /// Ask price levels.
497    #[serde(skip_serializing_if = "Option::is_none")]
498    pub asks: Option<Vec<PriceLevel>>,
499}
500
501/// Price level for orderbook snapshot (structured format).
502#[derive(Debug, Clone, Serialize, Deserialize)]
503pub struct DydxPriceLevel {
504    /// Price.
505    pub price: String,
506    /// Size.
507    pub size: String,
508}
509
510/// Contents of v4_orderbook subscribed (snapshot) message.
511#[derive(Debug, Clone, Serialize, Deserialize)]
512pub struct DydxOrderbookSnapshotContents {
513    /// Bid price levels.
514    #[serde(skip_serializing_if = "Option::is_none")]
515    pub bids: Option<Vec<DydxPriceLevel>>,
516    /// Ask price levels.
517    #[serde(skip_serializing_if = "Option::is_none")]
518    pub asks: Option<Vec<DydxPriceLevel>>,
519}
520
521/// Subaccount balance update.
522#[derive(Debug, Clone, Serialize, Deserialize)]
523pub struct DydxAssetBalance {
524    pub symbol: Ustr,
525    pub side: OrderSide,
526    pub size: String,
527    #[serde(rename = "assetId")]
528    pub asset_id: String,
529}
530
531/// Subaccount perpetual position.
532#[derive(Debug, Clone, Serialize, Deserialize)]
533pub struct DydxPerpetualPosition {
534    pub market: Ustr,
535    pub status: DydxPositionStatus,
536    pub side: PositionSide,
537    pub size: String,
538    #[serde(rename = "maxSize")]
539    pub max_size: String,
540    #[serde(rename = "entryPrice")]
541    pub entry_price: String,
542    #[serde(rename = "exitPrice")]
543    pub exit_price: Option<String>,
544    #[serde(rename = "realizedPnl")]
545    pub realized_pnl: String,
546    #[serde(rename = "unrealizedPnl")]
547    pub unrealized_pnl: String,
548    #[serde(rename = "createdAt")]
549    pub created_at: String,
550    #[serde(rename = "closedAt")]
551    pub closed_at: Option<String>,
552    #[serde(rename = "sumOpen")]
553    pub sum_open: String,
554    #[serde(rename = "sumClose")]
555    pub sum_close: String,
556    #[serde(rename = "netFunding")]
557    pub net_funding: String,
558}
559
560/// Subaccount information.
561#[derive(Debug, Clone, Serialize, Deserialize)]
562pub struct DydxSubaccountInfo {
563    pub address: String,
564    #[serde(rename = "subaccountNumber")]
565    pub subaccount_number: u32,
566    pub equity: String,
567    #[serde(rename = "freeCollateral")]
568    pub free_collateral: String,
569    #[serde(rename = "openPerpetualPositions")]
570    pub open_perpetual_positions: Option<HashMap<String, DydxPerpetualPosition>>,
571    #[serde(rename = "assetPositions")]
572    pub asset_positions: Option<HashMap<String, DydxAssetBalance>>,
573    #[serde(rename = "marginEnabled")]
574    pub margin_enabled: bool,
575    #[serde(rename = "updatedAtHeight")]
576    pub updated_at_height: String,
577    #[serde(rename = "latestProcessedBlockHeight")]
578    pub latest_processed_block_height: String,
579}
580
581/// Order message from WebSocket.
582#[derive(Debug, Clone, Serialize, Deserialize)]
583pub struct DydxWsOrderSubaccountMessageContents {
584    pub id: String,
585    #[serde(rename = "subaccountId")]
586    pub subaccount_id: String,
587    #[serde(rename = "clientId")]
588    pub client_id: String,
589    #[serde(rename = "clobPairId")]
590    pub clob_pair_id: String,
591    pub side: OrderSide,
592    pub size: String,
593    pub price: String,
594    pub status: DydxOrderStatus,
595    #[serde(rename = "type")]
596    pub order_type: DydxOrderType,
597    #[serde(rename = "timeInForce")]
598    pub time_in_force: DydxTimeInForce,
599    #[serde(rename = "postOnly")]
600    pub post_only: bool,
601    #[serde(rename = "reduceOnly")]
602    pub reduce_only: bool,
603    #[serde(rename = "orderFlags")]
604    pub order_flags: String,
605    #[serde(rename = "goodTilBlock")]
606    pub good_til_block: Option<String>,
607    #[serde(rename = "goodTilBlockTime")]
608    pub good_til_block_time: Option<String>,
609    #[serde(rename = "createdAtHeight")]
610    pub created_at_height: String,
611    #[serde(rename = "clientMetadata")]
612    pub client_metadata: String,
613    #[serde(rename = "triggerPrice")]
614    pub trigger_price: Option<String>,
615    #[serde(rename = "totalFilled")]
616    pub total_filled: String,
617    #[serde(rename = "updatedAt")]
618    pub updated_at: Option<String>,
619    #[serde(rename = "updatedAtHeight")]
620    pub updated_at_height: Option<String>,
621}
622
623/// Fill message from WebSocket.
624#[derive(Debug, Clone, Serialize, Deserialize)]
625pub struct DydxWsFillSubaccountMessageContents {
626    pub id: String,
627    #[serde(rename = "subaccountId")]
628    pub subaccount_id: String,
629    pub side: OrderSide,
630    pub liquidity: DydxLiquidity,
631    #[serde(rename = "type")]
632    pub fill_type: DydxFillType,
633    pub market: Ustr,
634    #[serde(rename = "marketType")]
635    pub market_type: DydxTickerType,
636    pub price: String,
637    pub size: String,
638    pub fee: String,
639    #[serde(rename = "createdAt")]
640    pub created_at: String,
641    #[serde(rename = "createdAtHeight")]
642    pub created_at_height: String,
643    #[serde(rename = "orderId")]
644    pub order_id: String,
645    #[serde(rename = "clientMetadata")]
646    pub client_metadata: String,
647}
648
649/// Subaccount subscription contents.
650#[derive(Debug, Clone, Serialize, Deserialize)]
651pub struct DydxWsSubaccountsSubscribedContents {
652    pub subaccount: DydxSubaccountInfo,
653}
654
655/// Subaccounts subscription confirmed message.
656#[derive(Debug, Clone, Serialize, Deserialize)]
657pub struct DydxWsSubaccountsSubscribed {
658    #[serde(rename = "type")]
659    pub msg_type: DydxWsMessageType,
660    pub connection_id: String,
661    pub message_id: u64,
662    pub channel: DydxWsChannel,
663    pub id: String,
664    pub contents: DydxWsSubaccountsSubscribedContents,
665}
666
667/// Subaccounts channel data contents.
668#[derive(Debug, Clone, Serialize, Deserialize)]
669pub struct DydxWsSubaccountsChannelContents {
670    pub orders: Option<Vec<DydxWsOrderSubaccountMessageContents>>,
671    pub fills: Option<Vec<DydxWsFillSubaccountMessageContents>>,
672}
673
674/// Subaccounts channel data message.
675#[derive(Debug, Clone, Serialize, Deserialize)]
676pub struct DydxWsSubaccountsChannelData {
677    #[serde(rename = "type")]
678    pub msg_type: DydxWsMessageType,
679    pub connection_id: String,
680    pub message_id: u64,
681    pub id: String,
682    pub channel: DydxWsChannel,
683    pub version: String,
684    pub contents: DydxWsSubaccountsChannelContents,
685}