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}