1use 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#[derive(Debug, Clone, Serialize, Deserialize)]
42pub struct DydxSubscription {
43 #[serde(rename = "type")]
45 pub op: DydxWsOperation,
46 pub channel: DydxWsChannel,
48 #[serde(skip_serializing_if = "Option::is_none")]
50 pub id: Option<String>,
51}
52
53#[derive(Debug, Clone, Serialize, Deserialize)]
55pub struct DydxWsSubscriptionMsg {
56 #[serde(rename = "type")]
58 pub msg_type: DydxWsMessageType,
59 pub connection_id: String,
61 pub message_id: u64,
63 pub channel: DydxWsChannel,
65 #[serde(default, skip_serializing_if = "Option::is_none")]
67 pub id: Option<String>,
68}
69
70#[derive(Debug, Clone, Serialize, Deserialize)]
72pub struct DydxWsConnectedMsg {
73 #[serde(rename = "type")]
75 pub msg_type: DydxWsMessageType,
76 pub connection_id: String,
78 pub message_id: u64,
80}
81
82#[derive(Debug, Clone, Serialize, Deserialize)]
88pub struct DydxWsChannelDataMsg {
89 #[serde(rename = "type")]
91 pub msg_type: DydxWsMessageType,
92 pub connection_id: String,
94 pub message_id: u64,
96 pub channel: DydxWsChannel,
98 #[serde(default, skip_serializing_if = "Option::is_none")]
100 pub id: Option<String>,
101 pub contents: Value,
103 #[serde(default, skip_serializing_if = "Option::is_none")]
105 pub version: Option<String>,
106}
107
108#[derive(Debug, Clone, Serialize, Deserialize)]
110pub struct DydxWsChannelBatchDataMsg {
111 #[serde(rename = "type")]
113 pub msg_type: DydxWsMessageType,
114 pub connection_id: String,
116 pub message_id: u64,
118 pub channel: DydxWsChannel,
120 #[serde(default, skip_serializing_if = "Option::is_none")]
122 pub id: Option<String>,
123 pub contents: Value,
125 #[serde(default, skip_serializing_if = "Option::is_none")]
127 pub version: Option<String>,
128}
129
130#[derive(Debug, Clone, Serialize, Deserialize)]
132pub struct DydxWsMessageGeneral {
133 #[serde(rename = "type")]
134 pub msg_type: Option<DydxWsMessageType>,
135 pub connection_id: Option<String>,
136 pub message_id: Option<u64>,
137 pub channel: Option<DydxWsChannel>,
138 pub id: Option<String>,
139 pub message: Option<String>,
140}
141
142#[derive(Debug, Clone, Serialize, Deserialize)]
144pub struct DydxWsGenericMsg {
145 #[serde(rename = "type")]
147 pub msg_type: DydxWsMessageType,
148 #[serde(default, skip_serializing_if = "Option::is_none")]
150 pub connection_id: Option<String>,
151 #[serde(default, skip_serializing_if = "Option::is_none")]
153 pub message_id: Option<u64>,
154 #[serde(default, skip_serializing_if = "Option::is_none")]
156 pub channel: Option<DydxWsChannel>,
157 #[serde(default, skip_serializing_if = "Option::is_none")]
159 pub id: Option<String>,
160 #[serde(default, skip_serializing_if = "Option::is_none")]
162 pub message: Option<String>,
163}
164
165impl DydxWsGenericMsg {
166 #[must_use]
168 pub fn is_error(&self) -> bool {
169 self.msg_type == DydxWsMessageType::Error
170 }
171
172 #[must_use]
174 pub fn is_subscribed(&self) -> bool {
175 self.msg_type == DydxWsMessageType::Subscribed
176 }
177
178 #[must_use]
180 pub fn is_unsubscribed(&self) -> bool {
181 self.msg_type == DydxWsMessageType::Unsubscribed
182 }
183
184 #[must_use]
186 pub fn is_connected(&self) -> bool {
187 self.msg_type == DydxWsMessageType::Connected
188 }
189
190 #[must_use]
192 pub fn is_channel_data(&self) -> bool {
193 self.msg_type == DydxWsMessageType::ChannelData
194 }
195
196 #[must_use]
198 pub fn is_channel_batch_data(&self) -> bool {
199 self.msg_type == DydxWsMessageType::ChannelBatchData
200 }
201
202 #[must_use]
204 pub fn is_unknown(&self) -> bool {
205 self.msg_type == DydxWsMessageType::Unknown
206 }
207}
208
209#[derive(Debug, Clone, Serialize, Deserialize)]
215pub struct DydxBlockHeightSubscribedContents {
216 pub height: String,
217 pub time: DateTime<Utc>,
218}
219
220#[derive(Debug, Clone, Serialize, Deserialize)]
222pub struct DydxWsBlockHeightSubscribedData {
223 #[serde(rename = "type")]
224 pub msg_type: DydxWsMessageType,
225 pub connection_id: String,
226 pub message_id: u64,
227 pub channel: DydxWsChannel,
228 pub id: String,
229 pub contents: DydxBlockHeightSubscribedContents,
230}
231
232#[derive(Debug, Clone, Serialize, Deserialize)]
234pub struct DydxBlockHeightChannelContents {
235 #[serde(rename = "blockHeight")]
236 pub block_height: String,
237 pub time: DateTime<Utc>,
238}
239
240#[derive(Debug, Clone, Serialize, Deserialize)]
242pub struct DydxWsBlockHeightChannelData {
243 #[serde(rename = "type")]
244 pub msg_type: DydxWsMessageType,
245 pub connection_id: String,
246 pub message_id: u64,
247 pub id: String,
248 pub channel: DydxWsChannel,
249 pub version: String,
250 pub contents: DydxBlockHeightChannelContents,
251}
252
253#[derive(Debug, Clone, Serialize, Deserialize)]
259pub struct DydxOraclePriceMarketFull {
260 #[serde(rename = "oraclePrice")]
261 pub oracle_price: String,
262 #[serde(rename = "effectiveAt")]
263 pub effective_at: String,
264 #[serde(rename = "effectiveAtHeight")]
265 pub effective_at_height: String,
266 #[serde(rename = "marketId")]
267 pub market_id: u32,
268}
269
270#[derive(Debug, Clone, Serialize, Deserialize)]
272#[serde(rename_all = "camelCase")]
273pub struct DydxOraclePriceMarket {
274 pub oracle_price: String,
276}
277
278#[derive(Debug, Clone, Serialize, Deserialize)]
280pub struct DydxMarketMessageContents {
281 #[serde(rename = "oraclePrices")]
282 pub oracle_prices: Option<HashMap<String, DydxOraclePriceMarketFull>>,
283 pub trading: Option<Value>,
284}
285
286#[derive(Debug, Clone, Serialize, Deserialize)]
288pub struct DydxWsMarketChannelData {
289 #[serde(rename = "type")]
290 pub msg_type: DydxWsMessageType,
291 pub channel: DydxWsChannel,
292 pub contents: DydxMarketMessageContents,
293 pub version: String,
294 pub message_id: u64,
295 pub connection_id: Option<String>,
296 pub id: Option<String>,
297}
298
299#[derive(Debug, Clone, Serialize, Deserialize)]
301pub struct DydxWsMarketSubscribed {
302 #[serde(rename = "type")]
303 pub msg_type: DydxWsMessageType,
304 pub connection_id: String,
305 pub message_id: u64,
306 pub channel: DydxWsChannel,
307 pub contents: Value,
308}
309
310#[derive(Debug, Clone, Serialize, Deserialize)]
312#[serde(rename_all = "camelCase")]
313pub struct DydxMarketsContents {
314 #[serde(skip_serializing_if = "Option::is_none")]
316 pub oracle_prices: Option<HashMap<String, DydxOraclePriceMarket>>,
317}
318
319#[derive(Debug, Clone, Serialize, Deserialize)]
325#[serde(rename_all = "camelCase")]
326pub struct DydxTrade {
327 pub id: String,
329 pub side: OrderSide,
331 pub size: String,
333 pub price: String,
335 pub created_at: DateTime<Utc>,
337 #[serde(rename = "type")]
339 pub trade_type: DydxTradeType,
340 #[serde(skip_serializing_if = "Option::is_none")]
342 pub created_at_height: Option<String>,
343}
344
345#[derive(Debug, Clone, Serialize, Deserialize)]
347pub struct DydxTradeContents {
348 pub trades: Vec<DydxTrade>,
350}
351
352#[derive(Debug, Clone, Serialize, Deserialize)]
358#[serde(rename_all = "camelCase")]
359pub struct DydxCandle {
360 pub base_token_volume: String,
362 pub close: String,
364 pub high: String,
366 pub low: String,
368 pub open: String,
370 pub resolution: DydxCandleResolution,
372 pub started_at: DateTime<Utc>,
374 pub starting_open_interest: String,
376 pub ticker: String,
378 pub trades: i64,
380 pub usd_volume: String,
382 #[serde(skip_serializing_if = "Option::is_none")]
384 pub orderbook_mid_price_close: Option<String>,
385 #[serde(skip_serializing_if = "Option::is_none")]
387 pub orderbook_mid_price_open: Option<String>,
388}
389
390pub type PriceLevel = (String, String);
396
397#[derive(Debug, Clone, Serialize, Deserialize)]
399pub struct DydxOrderbookContents {
400 #[serde(skip_serializing_if = "Option::is_none")]
402 pub bids: Option<Vec<PriceLevel>>,
403 #[serde(skip_serializing_if = "Option::is_none")]
405 pub asks: Option<Vec<PriceLevel>>,
406}
407
408#[derive(Debug, Clone, Serialize, Deserialize)]
410pub struct DydxPriceLevel {
411 pub price: String,
413 pub size: String,
415}
416
417#[derive(Debug, Clone, Serialize, Deserialize)]
419pub struct DydxOrderbookSnapshotContents {
420 #[serde(skip_serializing_if = "Option::is_none")]
422 pub bids: Option<Vec<DydxPriceLevel>>,
423 #[serde(skip_serializing_if = "Option::is_none")]
425 pub asks: Option<Vec<DydxPriceLevel>>,
426}
427
428#[derive(Debug, Clone, Serialize, Deserialize)]
434pub struct DydxAssetBalance {
435 pub symbol: Ustr,
436 pub side: OrderSide,
437 pub size: String,
438 #[serde(rename = "assetId")]
439 pub asset_id: String,
440}
441
442#[derive(Debug, Clone, Serialize, Deserialize)]
444pub struct DydxPerpetualPosition {
445 pub market: Ustr,
446 pub status: DydxPositionStatus,
447 pub side: PositionSide,
448 pub size: String,
449 #[serde(rename = "maxSize")]
450 pub max_size: String,
451 #[serde(rename = "entryPrice")]
452 pub entry_price: String,
453 #[serde(rename = "exitPrice")]
454 pub exit_price: Option<String>,
455 #[serde(rename = "realizedPnl")]
456 pub realized_pnl: String,
457 #[serde(rename = "unrealizedPnl")]
458 pub unrealized_pnl: String,
459 #[serde(rename = "createdAt")]
460 pub created_at: String,
461 #[serde(rename = "closedAt")]
462 pub closed_at: Option<String>,
463 #[serde(rename = "sumOpen")]
464 pub sum_open: String,
465 #[serde(rename = "sumClose")]
466 pub sum_close: String,
467 #[serde(rename = "netFunding")]
468 pub net_funding: String,
469}
470
471#[derive(Debug, Clone, Serialize, Deserialize)]
473pub struct DydxSubaccountInfo {
474 pub address: String,
475 #[serde(rename = "subaccountNumber")]
476 pub subaccount_number: u32,
477 pub equity: String,
478 #[serde(rename = "freeCollateral")]
479 pub free_collateral: String,
480 #[serde(rename = "openPerpetualPositions")]
481 pub open_perpetual_positions: Option<HashMap<String, DydxPerpetualPosition>>,
482 #[serde(rename = "assetPositions")]
483 pub asset_positions: Option<HashMap<String, DydxAssetBalance>>,
484 #[serde(rename = "marginEnabled")]
485 pub margin_enabled: bool,
486 #[serde(rename = "updatedAtHeight")]
487 pub updated_at_height: String,
488 #[serde(rename = "latestProcessedBlockHeight")]
489 pub latest_processed_block_height: String,
490}
491
492#[derive(Debug, Clone, Serialize, Deserialize)]
494pub struct DydxWsOrderSubaccountMessageContents {
495 pub id: String,
496 #[serde(rename = "subaccountId")]
497 pub subaccount_id: String,
498 #[serde(rename = "clientId")]
499 pub client_id: String,
500 #[serde(rename = "clobPairId")]
501 pub clob_pair_id: String,
502 pub side: OrderSide,
503 pub size: String,
504 pub price: String,
505 pub status: DydxOrderStatus,
506 #[serde(rename = "type")]
507 pub order_type: DydxOrderType,
508 #[serde(rename = "timeInForce")]
509 pub time_in_force: DydxTimeInForce,
510 #[serde(rename = "postOnly")]
511 pub post_only: bool,
512 #[serde(rename = "reduceOnly")]
513 pub reduce_only: bool,
514 #[serde(rename = "orderFlags")]
515 pub order_flags: String,
516 #[serde(rename = "goodTilBlock")]
517 pub good_til_block: Option<String>,
518 #[serde(rename = "goodTilBlockTime")]
519 pub good_til_block_time: Option<String>,
520 #[serde(rename = "createdAtHeight")]
521 pub created_at_height: String,
522 #[serde(rename = "clientMetadata")]
523 pub client_metadata: String,
524 #[serde(rename = "triggerPrice")]
525 pub trigger_price: Option<String>,
526 #[serde(rename = "totalFilled")]
527 pub total_filled: String,
528 #[serde(rename = "updatedAt")]
529 pub updated_at: Option<String>,
530 #[serde(rename = "updatedAtHeight")]
531 pub updated_at_height: Option<String>,
532}
533
534#[derive(Debug, Clone, Serialize, Deserialize)]
536pub struct DydxWsFillSubaccountMessageContents {
537 pub id: String,
538 #[serde(rename = "subaccountId")]
539 pub subaccount_id: String,
540 pub side: OrderSide,
541 pub liquidity: DydxLiquidity,
542 #[serde(rename = "type")]
543 pub fill_type: DydxFillType,
544 pub market: Ustr,
545 #[serde(rename = "marketType")]
546 pub market_type: DydxTickerType,
547 pub price: String,
548 pub size: String,
549 pub fee: String,
550 #[serde(rename = "createdAt")]
551 pub created_at: String,
552 #[serde(rename = "createdAtHeight")]
553 pub created_at_height: String,
554 #[serde(rename = "orderId")]
555 pub order_id: String,
556 #[serde(rename = "clientMetadata")]
557 pub client_metadata: String,
558}
559
560#[derive(Debug, Clone, Serialize, Deserialize)]
562pub struct DydxWsSubaccountsSubscribedContents {
563 pub subaccount: DydxSubaccountInfo,
564}
565
566#[derive(Debug, Clone, Serialize, Deserialize)]
568pub struct DydxWsSubaccountsSubscribed {
569 #[serde(rename = "type")]
570 pub msg_type: DydxWsMessageType,
571 pub connection_id: String,
572 pub message_id: u64,
573 pub channel: DydxWsChannel,
574 pub id: String,
575 pub contents: DydxWsSubaccountsSubscribedContents,
576}
577
578#[derive(Debug, Clone, Serialize, Deserialize)]
580pub struct DydxWsSubaccountsChannelContents {
581 pub orders: Option<Vec<DydxWsOrderSubaccountMessageContents>>,
582 pub fills: Option<Vec<DydxWsFillSubaccountMessageContents>>,
583}
584
585#[derive(Debug, Clone, Serialize, Deserialize)]
587pub struct DydxWsSubaccountsChannelData {
588 #[serde(rename = "type")]
589 pub msg_type: DydxWsMessageType,
590 pub connection_id: String,
591 pub message_id: u64,
592 pub id: String,
593 pub channel: DydxWsChannel,
594 pub version: String,
595 pub contents: DydxWsSubaccountsChannelContents,
596}