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