nautilus_dydx/schemas/
ws.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//! WebSocket message schemas for dYdX v4.
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 strum::{AsRefStr, Display, EnumString, FromRepr};
25use ustr::Ustr;
26
27use crate::{
28    common::enums::{
29        DydxFillType, DydxLiquidity, DydxOrderStatus, DydxOrderType, DydxPositionStatus,
30        DydxTickerType, DydxTimeInForce,
31    },
32    websocket::enums::DydxWsChannel,
33};
34
35/// WebSocket message types for dYdX.
36#[derive(
37    Clone,
38    Copy,
39    Debug,
40    PartialEq,
41    Eq,
42    Hash,
43    Display,
44    AsRefStr,
45    EnumString,
46    FromRepr,
47    Serialize,
48    Deserialize,
49)]
50#[serde(rename_all = "snake_case")]
51#[strum(serialize_all = "snake_case")]
52pub enum DydxWsMessageType {
53    /// Connection established.
54    Connected,
55    /// Subscription confirmed.
56    Subscribed,
57    /// Unsubscription confirmed.
58    Unsubscribed,
59    /// Channel data update.
60    ChannelData,
61    /// Batch channel data update.
62    ChannelBatchData,
63    /// Error message.
64    Error,
65}
66
67/// General WebSocket message structure for routing.
68#[derive(Debug, Clone, Serialize, Deserialize)]
69pub struct DydxWsMessageGeneral {
70    #[serde(rename = "type")]
71    pub msg_type: Option<DydxWsMessageType>,
72    pub connection_id: Option<String>,
73    pub message_id: Option<u64>,
74    pub channel: Option<DydxWsChannel>,
75    pub id: Option<String>,
76    pub message: Option<String>,
77}
78
79/// Block height subscription confirmed contents.
80#[derive(Debug, Clone, Serialize, Deserialize)]
81pub struct DydxBlockHeightSubscribedContents {
82    pub height: String,
83    pub time: DateTime<Utc>,
84}
85
86/// Block height subscription confirmed message.
87#[derive(Debug, Clone, Serialize, Deserialize)]
88pub struct DydxWsBlockHeightSubscribedData {
89    #[serde(rename = "type")]
90    pub msg_type: DydxWsMessageType,
91    pub connection_id: String,
92    pub message_id: u64,
93    pub channel: DydxWsChannel,
94    pub id: String,
95    pub contents: DydxBlockHeightSubscribedContents,
96}
97
98/// Block height channel data contents.
99#[derive(Debug, Clone, Serialize, Deserialize)]
100pub struct DydxBlockHeightChannelContents {
101    #[serde(rename = "blockHeight")]
102    pub block_height: String,
103    pub time: DateTime<Utc>,
104}
105
106/// Block height channel data message.
107#[derive(Debug, Clone, Serialize, Deserialize)]
108pub struct DydxWsBlockHeightChannelData {
109    #[serde(rename = "type")]
110    pub msg_type: DydxWsMessageType,
111    pub connection_id: String,
112    pub message_id: u64,
113    pub id: String,
114    pub channel: DydxWsChannel,
115    pub version: String,
116    pub contents: DydxBlockHeightChannelContents,
117}
118
119/// Oracle price data for a market.
120#[derive(Debug, Clone, Serialize, Deserialize)]
121pub struct DydxOraclePriceMarket {
122    #[serde(rename = "oraclePrice")]
123    pub oracle_price: String,
124    #[serde(rename = "effectiveAt")]
125    pub effective_at: String,
126    #[serde(rename = "effectiveAtHeight")]
127    pub effective_at_height: String,
128    #[serde(rename = "marketId")]
129    pub market_id: u32,
130}
131
132/// Market message contents.
133#[derive(Debug, Clone, Serialize, Deserialize)]
134pub struct DydxMarketMessageContents {
135    #[serde(rename = "oraclePrices")]
136    pub oracle_prices: Option<HashMap<String, DydxOraclePriceMarket>>,
137    pub trading: Option<Value>,
138}
139
140/// Markets channel data message.
141#[derive(Debug, Clone, Serialize, Deserialize)]
142pub struct DydxWsMarketChannelData {
143    #[serde(rename = "type")]
144    pub msg_type: DydxWsMessageType,
145    pub channel: DydxWsChannel,
146    pub contents: DydxMarketMessageContents,
147    pub version: String,
148    pub message_id: u64,
149    pub connection_id: Option<String>,
150    pub id: Option<String>,
151}
152
153/// Markets subscription confirmed message.
154#[derive(Debug, Clone, Serialize, Deserialize)]
155pub struct DydxWsMarketSubscribedData {
156    #[serde(rename = "type")]
157    pub msg_type: DydxWsMessageType,
158    pub connection_id: String,
159    pub message_id: u64,
160    pub channel: DydxWsChannel,
161    pub contents: Value, // Full market data structure
162}
163
164/// Subaccount balance update.
165#[derive(Debug, Clone, Serialize, Deserialize)]
166pub struct DydxAssetBalance {
167    pub symbol: Ustr,
168    pub side: OrderSide,
169    pub size: String,
170    #[serde(rename = "assetId")]
171    pub asset_id: String,
172}
173
174/// Subaccount perpetual position.
175#[derive(Debug, Clone, Serialize, Deserialize)]
176pub struct DydxPerpetualPosition {
177    pub market: Ustr,
178    pub status: DydxPositionStatus,
179    pub side: PositionSide,
180    pub size: String,
181    #[serde(rename = "maxSize")]
182    pub max_size: String,
183    #[serde(rename = "entryPrice")]
184    pub entry_price: String,
185    #[serde(rename = "exitPrice")]
186    pub exit_price: Option<String>,
187    #[serde(rename = "realizedPnl")]
188    pub realized_pnl: String,
189    #[serde(rename = "unrealizedPnl")]
190    pub unrealized_pnl: String,
191    #[serde(rename = "createdAt")]
192    pub created_at: String,
193    #[serde(rename = "closedAt")]
194    pub closed_at: Option<String>,
195    #[serde(rename = "sumOpen")]
196    pub sum_open: String,
197    #[serde(rename = "sumClose")]
198    pub sum_close: String,
199    #[serde(rename = "netFunding")]
200    pub net_funding: String,
201}
202
203/// Order message from WebSocket.
204#[derive(Debug, Clone, Serialize, Deserialize)]
205pub struct DydxWsOrderSubaccountMessageContents {
206    pub id: String,
207    #[serde(rename = "subaccountId")]
208    pub subaccount_id: String,
209    #[serde(rename = "clientId")]
210    pub client_id: String,
211    #[serde(rename = "clobPairId")]
212    pub clob_pair_id: String,
213    pub side: OrderSide,
214    pub size: String,
215    pub price: String,
216    pub status: DydxOrderStatus,
217    #[serde(rename = "type")]
218    pub order_type: DydxOrderType,
219    #[serde(rename = "timeInForce")]
220    pub time_in_force: DydxTimeInForce,
221    #[serde(rename = "postOnly")]
222    pub post_only: bool,
223    #[serde(rename = "reduceOnly")]
224    pub reduce_only: bool,
225    #[serde(rename = "orderFlags")]
226    pub order_flags: String,
227    #[serde(rename = "goodTilBlock")]
228    pub good_til_block: Option<String>,
229    #[serde(rename = "goodTilBlockTime")]
230    pub good_til_block_time: Option<String>,
231    #[serde(rename = "createdAtHeight")]
232    pub created_at_height: String,
233    #[serde(rename = "clientMetadata")]
234    pub client_metadata: String,
235    #[serde(rename = "triggerPrice")]
236    pub trigger_price: Option<String>,
237    #[serde(rename = "totalFilled")]
238    pub total_filled: String,
239    #[serde(rename = "updatedAt")]
240    pub updated_at: Option<String>,
241    #[serde(rename = "updatedAtHeight")]
242    pub updated_at_height: Option<String>,
243}
244
245/// Fill message from WebSocket.
246#[derive(Debug, Clone, Serialize, Deserialize)]
247pub struct DydxWsFillSubaccountMessageContents {
248    pub id: String,
249    #[serde(rename = "subaccountId")]
250    pub subaccount_id: String,
251    pub side: OrderSide,
252    pub liquidity: DydxLiquidity,
253    #[serde(rename = "type")]
254    pub fill_type: DydxFillType,
255    pub market: Ustr,
256    #[serde(rename = "marketType")]
257    pub market_type: DydxTickerType,
258    pub price: String,
259    pub size: String,
260    pub fee: String,
261    #[serde(rename = "createdAt")]
262    pub created_at: String,
263    #[serde(rename = "createdAtHeight")]
264    pub created_at_height: String,
265    #[serde(rename = "orderId")]
266    pub order_id: String,
267    #[serde(rename = "clientMetadata")]
268    pub client_metadata: String,
269}
270
271/// Subaccount subscription contents.
272#[derive(Debug, Clone, Serialize, Deserialize)]
273pub struct DydxWsSubaccountsSubscribedContents {
274    pub subaccount: DydxSubaccountInfo,
275}
276
277/// Subaccount information.
278#[derive(Debug, Clone, Serialize, Deserialize)]
279pub struct DydxSubaccountInfo {
280    pub address: String,
281    #[serde(rename = "subaccountNumber")]
282    pub subaccount_number: u32,
283    pub equity: String,
284    #[serde(rename = "freeCollateral")]
285    pub free_collateral: String,
286    #[serde(rename = "openPerpetualPositions")]
287    pub open_perpetual_positions: Option<HashMap<String, DydxPerpetualPosition>>,
288    #[serde(rename = "assetPositions")]
289    pub asset_positions: Option<HashMap<String, DydxAssetBalance>>,
290    #[serde(rename = "marginEnabled")]
291    pub margin_enabled: bool,
292    #[serde(rename = "updatedAtHeight")]
293    pub updated_at_height: String,
294    #[serde(rename = "latestProcessedBlockHeight")]
295    pub latest_processed_block_height: String,
296}
297
298/// Subaccounts subscription confirmed message.
299#[derive(Debug, Clone, Serialize, Deserialize)]
300pub struct DydxWsSubaccountsSubscribed {
301    #[serde(rename = "type")]
302    pub msg_type: DydxWsMessageType,
303    pub connection_id: String,
304    pub message_id: u64,
305    pub channel: DydxWsChannel,
306    pub id: String,
307    pub contents: DydxWsSubaccountsSubscribedContents,
308}
309
310/// Subaccounts channel data contents.
311#[derive(Debug, Clone, Serialize, Deserialize)]
312pub struct DydxWsSubaccountsChannelContents {
313    pub orders: Option<Vec<DydxWsOrderSubaccountMessageContents>>,
314    pub fills: Option<Vec<DydxWsFillSubaccountMessageContents>>,
315}
316
317/// Subaccounts channel data message.
318#[derive(Debug, Clone, Serialize, Deserialize)]
319pub struct DydxWsSubaccountsChannelData {
320    #[serde(rename = "type")]
321    pub msg_type: DydxWsMessageType,
322    pub connection_id: String,
323    pub message_id: u64,
324    pub id: String,
325    pub channel: DydxWsChannel,
326    pub version: String,
327    pub contents: DydxWsSubaccountsChannelContents,
328}