Skip to main content

nautilus_dydx/websocket/
enums.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//! Enums for dYdX WebSocket operations, channels, and message types.
17
18use chrono::{DateTime, Utc};
19use serde::{Deserialize, Serialize};
20use serde_json::Value;
21use strum::{AsRefStr, Display, EnumString, FromRepr};
22
23use super::{
24    error::DydxWebSocketError,
25    messages::{
26        DydxCandle, DydxMarketsContents, DydxOrderbookContents, DydxOrderbookSnapshotContents,
27        DydxTradeContents, DydxWsConnectedMsg, DydxWsSubaccountsChannelData,
28        DydxWsSubaccountsSubscribed, DydxWsSubscriptionMsg,
29    },
30};
31
32/// WebSocket operation types for dYdX.
33#[derive(
34    Clone,
35    Copy,
36    Debug,
37    PartialEq,
38    Eq,
39    Hash,
40    Display,
41    AsRefStr,
42    EnumString,
43    FromRepr,
44    Serialize,
45    Deserialize,
46)]
47#[serde(rename_all = "snake_case")]
48#[strum(serialize_all = "snake_case")]
49pub enum DydxWsOperation {
50    /// Subscribes to a channel.
51    Subscribe,
52    /// Unsubscribes from a channel.
53    Unsubscribe,
54    /// Ping keepalive message.
55    Ping,
56    /// Pong response to ping.
57    Pong,
58}
59
60/// dYdX WebSocket channel identifiers.
61///
62/// # References
63///
64/// <https://docs.dydx.trade/developers/indexer/websockets>
65#[derive(
66    Clone,
67    Copy,
68    Debug,
69    Default,
70    PartialEq,
71    Eq,
72    Hash,
73    Display,
74    AsRefStr,
75    EnumString,
76    FromRepr,
77    Serialize,
78    Deserialize,
79)]
80#[serde(rename_all = "snake_case")]
81#[strum(serialize_all = "snake_case")]
82pub enum DydxWsChannel {
83    /// Market data for all markets.
84    #[serde(rename = "v4_markets")]
85    #[strum(serialize = "v4_markets")]
86    Markets,
87    /// Trade stream for specific market.
88    #[serde(rename = "v4_trades")]
89    #[strum(serialize = "v4_trades")]
90    Trades,
91    /// Order book snapshots and updates.
92    #[serde(rename = "v4_orderbook")]
93    #[strum(serialize = "v4_orderbook")]
94    Orderbook,
95    /// Candlestick/kline data.
96    #[serde(rename = "v4_candles")]
97    #[strum(serialize = "v4_candles")]
98    Candles,
99    /// Subaccount updates (orders, fills, positions).
100    #[serde(rename = "v4_subaccounts")]
101    #[strum(serialize = "v4_subaccounts")]
102    Subaccounts,
103    /// Parent subaccount updates (for isolated positions).
104    #[serde(rename = "v4_parent_subaccounts")]
105    #[strum(serialize = "v4_parent_subaccounts")]
106    ParentSubaccounts,
107    /// Block height updates from chain.
108    #[serde(rename = "v4_block_height")]
109    #[strum(serialize = "v4_block_height")]
110    BlockHeight,
111    /// Unknown/unrecognized channel type (default when field is missing).
112    #[default]
113    #[serde(other)]
114    #[strum(to_string = "unknown")]
115    Unknown,
116}
117
118impl DydxWsChannel {
119    /// Returns `true` if this is a private channel requiring authentication.
120    #[must_use]
121    pub const fn is_private(&self) -> bool {
122        matches!(self, Self::Subaccounts | Self::ParentSubaccounts)
123    }
124
125    /// Returns `true` if this is a public channel.
126    #[must_use]
127    pub const fn is_public(&self) -> bool {
128        !self.is_private()
129    }
130
131    /// Returns `true` if this is an unknown/unrecognized channel type.
132    #[must_use]
133    pub const fn is_unknown(&self) -> bool {
134        matches!(self, Self::Unknown)
135    }
136}
137
138/// WebSocket message types for dYdX.
139#[derive(
140    Clone,
141    Copy,
142    Debug,
143    Default,
144    PartialEq,
145    Eq,
146    Hash,
147    Display,
148    AsRefStr,
149    EnumString,
150    FromRepr,
151    Serialize,
152    Deserialize,
153)]
154#[serde(rename_all = "snake_case")]
155#[strum(serialize_all = "snake_case")]
156pub enum DydxWsMessageType {
157    /// Connection established.
158    Connected,
159    /// Subscription confirmed.
160    Subscribed,
161    /// Unsubscription confirmed.
162    Unsubscribed,
163    /// Channel data update (default for missing type field).
164    #[default]
165    ChannelData,
166    /// Batch channel data update.
167    ChannelBatchData,
168    /// Error message.
169    Error,
170    /// Unknown/unrecognized message type.
171    #[serde(other)]
172    #[strum(to_string = "unknown")]
173    Unknown,
174}
175
176/// Control messages for the fallback parsing path.
177///
178/// Channel data is handled directly via `DydxWsFeedMessage` in `handle_feed_message()`.
179#[derive(Debug, Clone)]
180pub enum DydxWsMessage {
181    /// Subscription acknowledgement.
182    Subscribed(DydxWsSubscriptionMsg),
183    /// Unsubscription acknowledgement.
184    Unsubscribed(DydxWsSubscriptionMsg),
185    /// Subaccounts subscription with initial account state.
186    SubaccountsSubscribed(DydxWsSubaccountsSubscribed),
187    /// Connected acknowledgement with connection_id.
188    Connected(DydxWsConnectedMsg),
189    /// Error received from the venue or client lifecycle.
190    Error(DydxWebSocketError),
191    /// Raw message payload that does not yet have a typed representation.
192    Raw(Value),
193    /// Notification that the underlying connection reconnected.
194    Reconnected,
195    /// Explicit pong event (text-based heartbeat acknowledgement).
196    Pong,
197}
198
199/// Venue-specific message emitted by the handler to consumers.
200///
201/// The handler deserializes raw WebSocket JSON into these typed variants
202/// without converting to Nautilus domain types. Consumers (data client,
203/// execution client, Python bindings) perform the final conversion using
204/// their own instrument caches.
205#[derive(Debug, Clone)]
206pub enum DydxWsOutputMessage {
207    /// Trade data for a market.
208    Trades {
209        id: String,
210        contents: DydxTradeContents,
211    },
212    /// Order book snapshot (initial subscription).
213    OrderbookSnapshot {
214        id: String,
215        contents: DydxOrderbookSnapshotContents,
216    },
217    /// Order book delta update.
218    OrderbookUpdate {
219        id: String,
220        contents: DydxOrderbookContents,
221    },
222    /// Order book batch update (multiple deltas).
223    OrderbookBatch {
224        id: String,
225        updates: Vec<DydxOrderbookContents>,
226    },
227    /// Candle data for a market.
228    Candles { id: String, contents: DydxCandle },
229    /// Markets channel data (oracle prices, trading, instrument status).
230    Markets(DydxMarketsContents),
231    /// Subaccount subscription with initial account state.
232    SubaccountSubscribed(Box<DydxWsSubaccountsSubscribed>),
233    /// Subaccount channel data (orders, fills).
234    SubaccountsChannelData(Box<DydxWsSubaccountsChannelData>),
235    /// Block height update from chain.
236    BlockHeight { height: u64, time: DateTime<Utc> },
237    /// Error from the venue or handler.
238    Error(DydxWebSocketError),
239    /// Reconnection notification.
240    Reconnected,
241}