1use std::collections::HashMap;
19
20use chrono::{DateTime, Utc};
21use nautilus_model::enums::OrderSide;
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 DydxPositionSide, DydxPositionStatus, DydxTickerType, DydxTimeInForce, DydxTradeType,
30};
31
32#[derive(Debug, Clone, Serialize, Deserialize)]
38pub struct DydxSubscription {
39 #[serde(rename = "type")]
41 pub op: DydxWsOperation,
42 pub channel: DydxWsChannel,
44 #[serde(skip_serializing_if = "Option::is_none")]
46 pub id: Option<String>,
47}
48
49#[derive(Debug, Clone, Serialize, Deserialize)]
51pub struct DydxWsSubscriptionMsg {
52 #[serde(rename = "type", default)]
55 pub msg_type: DydxWsMessageType,
56 pub connection_id: String,
58 pub message_id: u64,
60 #[serde(default)]
62 pub channel: DydxWsChannel,
63 #[serde(default, skip_serializing_if = "Option::is_none")]
65 pub id: Option<String>,
66}
67
68#[derive(Debug, Clone, Serialize, Deserialize)]
70pub struct DydxWsConnectedMsg {
71 #[serde(rename = "type")]
73 pub msg_type: DydxWsMessageType,
74 pub connection_id: String,
76 pub message_id: u64,
78}
79
80#[derive(Debug, Clone, Serialize, Deserialize)]
82pub struct DydxWsChannelDataMsg {
83 #[serde(rename = "type", default)]
85 pub msg_type: DydxWsMessageType,
86 pub connection_id: String,
88 pub message_id: u64,
90 #[serde(default)]
92 pub channel: DydxWsChannel,
93 #[serde(default, skip_serializing_if = "Option::is_none")]
95 pub id: Option<String>,
96 pub contents: Value,
98 #[serde(default, skip_serializing_if = "Option::is_none")]
100 pub version: Option<String>,
101}
102
103#[derive(Debug, Clone, Serialize, Deserialize)]
105pub struct DydxWsChannelBatchDataMsg {
106 #[serde(rename = "type", default)]
108 pub msg_type: DydxWsMessageType,
109 pub connection_id: String,
111 pub message_id: u64,
113 #[serde(default)]
115 pub channel: DydxWsChannel,
116 #[serde(default, skip_serializing_if = "Option::is_none")]
118 pub id: Option<String>,
119 pub contents: Value,
121 #[serde(default, skip_serializing_if = "Option::is_none")]
123 pub version: Option<String>,
124}
125
126#[derive(Debug, Clone, Serialize, Deserialize)]
135#[serde(tag = "channel")]
136pub enum DydxWsFeedMessage {
137 #[serde(rename = "v4_subaccounts")]
139 Subaccounts(DydxWsSubaccountsMessage),
140 #[serde(rename = "v4_orderbook")]
142 Orderbook(DydxWsOrderbookMessage),
143 #[serde(rename = "v4_trades")]
145 Trades(DydxWsTradesMessage),
146 #[serde(rename = "v4_markets")]
148 Markets(DydxWsMarketsMessage),
149 #[serde(rename = "v4_candles")]
151 Candles(DydxWsCandlesMessage),
152 #[serde(rename = "v4_parent_subaccounts")]
154 ParentSubaccounts(DydxWsParentSubaccountsMessage),
155 #[serde(rename = "v4_block_height")]
157 BlockHeight(DydxWsBlockHeightMessage),
158}
159
160#[derive(Debug, Clone, Serialize, Deserialize)]
162#[serde(tag = "type")]
163pub enum DydxWsSubaccountsMessage {
164 #[serde(rename = "subscribed")]
166 Subscribed(DydxWsSubaccountsSubscribed),
167 #[serde(rename = "channel_data")]
169 ChannelData(DydxWsSubaccountsChannelData),
170 #[serde(rename = "unsubscribed")]
172 Unsubscribed(DydxWsSubscriptionMsg),
173}
174
175#[derive(Debug, Clone, Serialize, Deserialize)]
177#[serde(tag = "type")]
178pub enum DydxWsOrderbookMessage {
179 #[serde(rename = "subscribed")]
181 Subscribed(DydxWsChannelDataMsg),
182 #[serde(rename = "channel_data")]
184 ChannelData(DydxWsChannelDataMsg),
185 #[serde(rename = "channel_batch_data")]
187 ChannelBatchData(DydxWsChannelBatchDataMsg),
188 #[serde(rename = "unsubscribed")]
190 Unsubscribed(DydxWsSubscriptionMsg),
191}
192
193#[derive(Debug, Clone, Serialize, Deserialize)]
195#[serde(tag = "type")]
196pub enum DydxWsTradesMessage {
197 #[serde(rename = "subscribed")]
199 Subscribed(DydxWsChannelDataMsg),
200 #[serde(rename = "channel_data")]
202 ChannelData(DydxWsChannelDataMsg),
203 #[serde(rename = "unsubscribed")]
205 Unsubscribed(DydxWsSubscriptionMsg),
206}
207
208#[derive(Debug, Clone, Serialize, Deserialize)]
210#[serde(tag = "type")]
211pub enum DydxWsMarketsMessage {
212 #[serde(rename = "subscribed")]
214 Subscribed(DydxWsChannelDataMsg),
215 #[serde(rename = "channel_data")]
217 ChannelData(DydxWsChannelDataMsg),
218 #[serde(rename = "unsubscribed")]
220 Unsubscribed(DydxWsSubscriptionMsg),
221}
222
223#[derive(Debug, Clone, Serialize, Deserialize)]
225#[serde(tag = "type")]
226pub enum DydxWsCandlesMessage {
227 #[serde(rename = "subscribed")]
229 Subscribed(DydxWsChannelDataMsg),
230 #[serde(rename = "channel_data")]
232 ChannelData(DydxWsChannelDataMsg),
233 #[serde(rename = "unsubscribed")]
235 Unsubscribed(DydxWsSubscriptionMsg),
236}
237
238#[derive(Debug, Clone, Serialize, Deserialize)]
240#[serde(tag = "type")]
241pub enum DydxWsParentSubaccountsMessage {
242 #[serde(rename = "subscribed")]
244 Subscribed(DydxWsChannelDataMsg),
245 #[serde(rename = "channel_data")]
247 ChannelData(DydxWsChannelDataMsg),
248 #[serde(rename = "unsubscribed")]
250 Unsubscribed(DydxWsSubscriptionMsg),
251}
252
253#[derive(Debug, Clone, Serialize, Deserialize)]
255#[serde(tag = "type")]
256pub enum DydxWsBlockHeightMessage {
257 #[serde(rename = "subscribed")]
259 Subscribed(DydxWsBlockHeightSubscribedData),
260 #[serde(rename = "channel_data")]
262 ChannelData(DydxWsBlockHeightChannelData),
263 #[serde(rename = "unsubscribed")]
265 Unsubscribed(DydxWsSubscriptionMsg),
266}
267
268#[derive(Debug, Clone, Serialize, Deserialize)]
270pub struct DydxWsGenericMsg {
271 #[serde(rename = "type")]
273 pub msg_type: DydxWsMessageType,
274 #[serde(default, skip_serializing_if = "Option::is_none")]
276 pub connection_id: Option<String>,
277 #[serde(default, skip_serializing_if = "Option::is_none")]
279 pub message_id: Option<u64>,
280 #[serde(default, skip_serializing_if = "Option::is_none")]
282 pub channel: Option<DydxWsChannel>,
283 #[serde(default, skip_serializing_if = "Option::is_none")]
285 pub id: Option<String>,
286 #[serde(default, skip_serializing_if = "Option::is_none")]
288 pub message: Option<String>,
289}
290
291impl DydxWsGenericMsg {
292 #[must_use]
294 pub fn is_error(&self) -> bool {
295 self.msg_type == DydxWsMessageType::Error
296 }
297
298 #[must_use]
300 pub fn is_subscribed(&self) -> bool {
301 self.msg_type == DydxWsMessageType::Subscribed
302 }
303
304 #[must_use]
306 pub fn is_unsubscribed(&self) -> bool {
307 self.msg_type == DydxWsMessageType::Unsubscribed
308 }
309
310 #[must_use]
312 pub fn is_connected(&self) -> bool {
313 self.msg_type == DydxWsMessageType::Connected
314 }
315
316 #[must_use]
318 pub fn is_channel_data(&self) -> bool {
319 self.msg_type == DydxWsMessageType::ChannelData
320 }
321
322 #[must_use]
324 pub fn is_channel_batch_data(&self) -> bool {
325 self.msg_type == DydxWsMessageType::ChannelBatchData
326 }
327
328 #[must_use]
330 pub fn is_unknown(&self) -> bool {
331 self.msg_type == DydxWsMessageType::Unknown
332 }
333}
334
335#[derive(Debug, Clone, Serialize, Deserialize)]
337pub struct DydxBlockHeightSubscribedContents {
338 pub height: String,
339 pub time: DateTime<Utc>,
340}
341
342#[derive(Debug, Clone, Serialize, Deserialize)]
344pub struct DydxWsBlockHeightSubscribedData {
345 pub connection_id: String,
346 pub message_id: u64,
347 pub id: String,
348 pub contents: DydxBlockHeightSubscribedContents,
349}
350
351#[derive(Debug, Clone, Serialize, Deserialize)]
353pub struct DydxBlockHeightChannelContents {
354 #[serde(rename = "blockHeight")]
355 pub block_height: String,
356 pub time: DateTime<Utc>,
357}
358
359#[derive(Debug, Clone, Serialize, Deserialize)]
361pub struct DydxWsBlockHeightChannelData {
362 pub connection_id: String,
363 pub message_id: u64,
364 pub id: String,
365 pub version: String,
366 pub contents: DydxBlockHeightChannelContents,
367}
368
369#[derive(Debug, Clone, Serialize, Deserialize)]
371#[serde(rename_all = "camelCase")]
372pub struct DydxOraclePriceMarket {
373 pub oracle_price: String,
375}
376
377#[derive(Debug, Clone, Serialize, Deserialize)]
382#[serde(rename_all = "camelCase")]
383pub struct DydxMarketTradingUpdate {
384 #[serde(default, skip_serializing_if = "Option::is_none")]
386 pub ticker: Option<String>,
387 #[serde(default, skip_serializing_if = "Option::is_none")]
389 pub status: Option<crate::common::enums::DydxMarketStatus>,
390 #[serde(default, skip_serializing_if = "Option::is_none")]
392 pub clob_pair_id: Option<String>,
393 #[serde(default, skip_serializing_if = "Option::is_none")]
395 pub atomic_resolution: Option<i32>,
396 #[serde(default, skip_serializing_if = "Option::is_none")]
398 pub quantum_conversion_exponent: Option<i32>,
399 #[serde(default, skip_serializing_if = "Option::is_none")]
401 pub step_base_quantums: Option<i32>,
402 #[serde(default, skip_serializing_if = "Option::is_none")]
404 pub subticks_per_tick: Option<i32>,
405 #[serde(default, skip_serializing_if = "Option::is_none")]
407 pub initial_margin_fraction: Option<String>,
408 #[serde(default, skip_serializing_if = "Option::is_none")]
410 pub maintenance_margin_fraction: Option<String>,
411 #[serde(default, skip_serializing_if = "Option::is_none")]
413 pub base_asset: Option<String>,
414 #[serde(default, skip_serializing_if = "Option::is_none")]
416 pub quote_asset: Option<String>,
417 #[serde(default, skip_serializing_if = "Option::is_none")]
419 pub open_interest: Option<String>,
420 #[serde(default, skip_serializing_if = "Option::is_none")]
422 pub price_change_24h: Option<String>,
423 #[serde(default, skip_serializing_if = "Option::is_none")]
425 pub volume_24h: Option<String>,
426 #[serde(default, skip_serializing_if = "Option::is_none")]
428 pub trades_24h: Option<u64>,
429 #[serde(default, skip_serializing_if = "Option::is_none")]
431 pub max_position_size: Option<String>,
432 #[serde(default, skip_serializing_if = "Option::is_none")]
434 pub incremental_position_size: Option<String>,
435 #[serde(default, skip_serializing_if = "Option::is_none")]
437 pub base_position_size: Option<String>,
438 #[serde(default, skip_serializing_if = "Option::is_none")]
440 pub next_funding_rate: Option<String>,
441 #[serde(default, skip_serializing_if = "Option::is_none")]
443 pub oracle_price: Option<String>,
444}
445
446#[derive(Debug, Clone, Serialize, Deserialize)]
451#[serde(rename_all = "camelCase")]
452pub struct DydxMarketsContents {
453 #[serde(skip_serializing_if = "Option::is_none")]
455 pub oracle_prices: Option<HashMap<String, DydxOraclePriceMarket>>,
456 #[serde(default, skip_serializing_if = "Option::is_none")]
458 pub trading: Option<HashMap<String, DydxMarketTradingUpdate>>,
459 #[serde(default, skip_serializing_if = "Option::is_none")]
461 pub markets: Option<HashMap<String, DydxMarketTradingUpdate>>,
462}
463
464#[derive(Debug, Clone, Serialize, Deserialize)]
466#[serde(rename_all = "camelCase")]
467pub struct DydxTrade {
468 pub id: String,
470 pub side: OrderSide,
472 pub size: String,
474 pub price: String,
476 pub created_at: DateTime<Utc>,
478 #[serde(rename = "type")]
480 pub trade_type: DydxTradeType,
481 #[serde(skip_serializing_if = "Option::is_none")]
483 pub created_at_height: Option<String>,
484}
485
486#[derive(Debug, Clone, Serialize, Deserialize)]
488pub struct DydxTradeContents {
489 pub trades: Vec<DydxTrade>,
491}
492
493#[derive(Debug, Clone, Serialize, Deserialize)]
495#[serde(rename_all = "camelCase")]
496pub struct DydxCandle {
497 #[serde(default)]
499 pub base_token_volume: Option<String>,
500 pub close: String,
502 pub high: String,
504 pub low: String,
506 pub open: String,
508 pub resolution: DydxCandleResolution,
510 pub started_at: DateTime<Utc>,
512 pub starting_open_interest: String,
514 pub ticker: Ustr,
516 pub trades: i64,
518 pub usd_volume: String,
520 #[serde(skip_serializing_if = "Option::is_none")]
522 pub orderbook_mid_price_close: Option<String>,
523 #[serde(skip_serializing_if = "Option::is_none")]
525 pub orderbook_mid_price_open: Option<String>,
526}
527
528pub type PriceLevel = (String, String);
530
531#[derive(Debug, Clone, Serialize, Deserialize)]
533pub struct DydxOrderbookContents {
534 #[serde(skip_serializing_if = "Option::is_none")]
536 pub bids: Option<Vec<PriceLevel>>,
537 #[serde(skip_serializing_if = "Option::is_none")]
539 pub asks: Option<Vec<PriceLevel>>,
540}
541
542#[derive(Debug, Clone, Serialize, Deserialize)]
544pub struct DydxPriceLevel {
545 pub price: String,
547 pub size: String,
549}
550
551#[derive(Debug, Clone, Serialize, Deserialize)]
553pub struct DydxOrderbookSnapshotContents {
554 #[serde(skip_serializing_if = "Option::is_none")]
556 pub bids: Option<Vec<DydxPriceLevel>>,
557 #[serde(skip_serializing_if = "Option::is_none")]
559 pub asks: Option<Vec<DydxPriceLevel>>,
560}
561
562#[derive(Debug, Clone, Serialize, Deserialize)]
564pub struct DydxAssetBalance {
565 pub symbol: Ustr,
566 pub side: DydxPositionSide,
567 pub size: String,
568 #[serde(rename = "assetId")]
569 pub asset_id: String,
570}
571
572#[derive(Debug, Clone, Serialize, Deserialize)]
574pub struct DydxPerpetualPosition {
575 pub market: Ustr,
576 pub status: DydxPositionStatus,
577 pub side: DydxPositionSide,
578 pub size: String,
579 #[serde(rename = "maxSize")]
580 pub max_size: String,
581 #[serde(rename = "entryPrice")]
582 pub entry_price: String,
583 #[serde(rename = "exitPrice")]
584 pub exit_price: Option<String>,
585 #[serde(rename = "realizedPnl")]
586 pub realized_pnl: String,
587 #[serde(rename = "unrealizedPnl")]
588 pub unrealized_pnl: String,
589 #[serde(rename = "createdAt")]
590 pub created_at: String,
591 #[serde(rename = "closedAt")]
592 pub closed_at: Option<String>,
593 #[serde(rename = "sumOpen")]
594 pub sum_open: String,
595 #[serde(rename = "sumClose")]
596 pub sum_close: String,
597 #[serde(rename = "netFunding")]
598 pub net_funding: String,
599}
600
601#[derive(Debug, Clone, Serialize, Deserialize)]
603pub struct DydxSubaccountInfo {
604 pub address: String,
605 #[serde(rename = "subaccountNumber")]
606 pub subaccount_number: u32,
607 pub equity: String,
608 #[serde(rename = "freeCollateral")]
609 pub free_collateral: String,
610 #[serde(rename = "openPerpetualPositions")]
611 pub open_perpetual_positions: Option<HashMap<String, DydxPerpetualPosition>>,
612 #[serde(rename = "assetPositions")]
613 pub asset_positions: Option<HashMap<String, DydxAssetBalance>>,
614 #[serde(rename = "marginEnabled")]
615 pub margin_enabled: bool,
616 #[serde(rename = "updatedAtHeight")]
617 pub updated_at_height: String,
618 #[serde(rename = "latestProcessedBlockHeight")]
619 pub latest_processed_block_height: String,
620}
621
622#[derive(Debug, Clone, Serialize, Deserialize)]
624pub struct DydxWsOrderSubaccountMessageContents {
625 pub id: String,
626 #[serde(rename = "subaccountId")]
627 pub subaccount_id: String,
628 #[serde(rename = "clientId")]
629 pub client_id: String,
630 #[serde(rename = "clobPairId")]
631 pub clob_pair_id: String,
632 pub side: OrderSide,
633 pub size: String,
634 pub price: String,
635 pub status: DydxOrderStatus,
636 #[serde(rename = "type")]
637 pub order_type: DydxOrderType,
638 #[serde(rename = "timeInForce")]
639 pub time_in_force: DydxTimeInForce,
640 #[serde(rename = "postOnly")]
641 pub post_only: bool,
642 #[serde(rename = "reduceOnly")]
643 pub reduce_only: bool,
644 #[serde(rename = "orderFlags")]
645 pub order_flags: String,
646 #[serde(rename = "goodTilBlock")]
647 pub good_til_block: Option<String>,
648 #[serde(rename = "goodTilBlockTime")]
649 pub good_til_block_time: Option<String>,
650 #[serde(rename = "createdAtHeight")]
651 pub created_at_height: Option<String>,
652 #[serde(rename = "clientMetadata")]
653 pub client_metadata: Option<String>,
654 #[serde(rename = "triggerPrice")]
655 pub trigger_price: Option<String>,
656 #[serde(rename = "totalFilled")]
657 pub total_filled: Option<String>,
658 #[serde(rename = "updatedAt")]
659 pub updated_at: Option<String>,
660 #[serde(rename = "updatedAtHeight")]
661 pub updated_at_height: Option<String>,
662}
663
664#[derive(Debug, Clone, Serialize, Deserialize)]
666pub struct DydxWsFillSubaccountMessageContents {
667 pub id: String,
668 #[serde(rename = "subaccountId")]
669 pub subaccount_id: String,
670 pub side: OrderSide,
671 pub liquidity: DydxLiquidity,
672 #[serde(rename = "type")]
673 pub fill_type: DydxFillType,
674 #[serde(alias = "ticker")]
675 pub market: Ustr,
676 #[serde(rename = "marketType", default)]
677 pub market_type: Option<DydxTickerType>,
678 pub price: String,
679 pub size: String,
680 pub fee: String,
681 #[serde(rename = "createdAt")]
682 pub created_at: String,
683 #[serde(rename = "createdAtHeight")]
684 pub created_at_height: Option<String>,
685 #[serde(rename = "orderId")]
686 pub order_id: Option<String>,
687 #[serde(rename = "clientMetadata")]
688 pub client_metadata: Option<String>,
689}
690
691#[derive(Debug, Clone, Serialize, Deserialize)]
693pub struct DydxWsSubaccountsSubscribedContents {
694 pub subaccount: Option<DydxSubaccountInfo>,
695}
696
697#[derive(Debug, Clone, Serialize, Deserialize)]
699pub struct DydxWsSubaccountsSubscribed {
700 pub connection_id: String,
701 pub message_id: u64,
702 pub id: String,
703 pub contents: DydxWsSubaccountsSubscribedContents,
704}
705
706#[derive(Debug, Clone, Serialize, Deserialize)]
708pub struct DydxWsSubaccountsChannelContents {
709 pub orders: Option<Vec<DydxWsOrderSubaccountMessageContents>>,
710 pub fills: Option<Vec<DydxWsFillSubaccountMessageContents>>,
711}
712
713#[derive(Debug, Clone, Serialize, Deserialize)]
715pub struct DydxWsSubaccountsChannelData {
716 pub connection_id: String,
717 pub message_id: u64,
718 pub id: String,
719 pub version: String,
720 pub contents: DydxWsSubaccountsChannelContents,
721}