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)]
128pub struct DydxWsMessageGeneral {
129 #[serde(rename = "type")]
130 pub msg_type: Option<DydxWsMessageType>,
131 pub connection_id: Option<String>,
132 pub message_id: Option<u64>,
133 pub channel: Option<DydxWsChannel>,
134 pub id: Option<String>,
135 pub message: Option<String>,
136}
137
138#[derive(Debug, Clone, Serialize, Deserialize)]
147#[serde(tag = "channel")]
148pub enum DydxWsFeedMessage {
149 #[serde(rename = "v4_subaccounts")]
151 Subaccounts(DydxWsSubaccountsMessage),
152 #[serde(rename = "v4_orderbook")]
154 Orderbook(DydxWsOrderbookMessage),
155 #[serde(rename = "v4_trades")]
157 Trades(DydxWsTradesMessage),
158 #[serde(rename = "v4_markets")]
160 Markets(DydxWsMarketsMessage),
161 #[serde(rename = "v4_candles")]
163 Candles(DydxWsCandlesMessage),
164 #[serde(rename = "v4_parent_subaccounts")]
166 ParentSubaccounts(DydxWsParentSubaccountsMessage),
167 #[serde(rename = "v4_block_height")]
169 BlockHeight(DydxWsBlockHeightMessage),
170}
171
172#[derive(Debug, Clone, Serialize, Deserialize)]
174#[serde(tag = "type")]
175pub enum DydxWsSubaccountsMessage {
176 #[serde(rename = "subscribed")]
178 Subscribed(DydxWsSubaccountsSubscribed),
179 #[serde(rename = "channel_data")]
181 ChannelData(DydxWsSubaccountsChannelData),
182 #[serde(rename = "unsubscribed")]
184 Unsubscribed(DydxWsSubscriptionMsg),
185}
186
187#[derive(Debug, Clone, Serialize, Deserialize)]
189#[serde(tag = "type")]
190pub enum DydxWsOrderbookMessage {
191 #[serde(rename = "subscribed")]
193 Subscribed(DydxWsChannelDataMsg),
194 #[serde(rename = "channel_data")]
196 ChannelData(DydxWsChannelDataMsg),
197 #[serde(rename = "channel_batch_data")]
199 ChannelBatchData(DydxWsChannelBatchDataMsg),
200 #[serde(rename = "unsubscribed")]
202 Unsubscribed(DydxWsSubscriptionMsg),
203}
204
205#[derive(Debug, Clone, Serialize, Deserialize)]
207#[serde(tag = "type")]
208pub enum DydxWsTradesMessage {
209 #[serde(rename = "subscribed")]
211 Subscribed(DydxWsChannelDataMsg),
212 #[serde(rename = "channel_data")]
214 ChannelData(DydxWsChannelDataMsg),
215 #[serde(rename = "unsubscribed")]
217 Unsubscribed(DydxWsSubscriptionMsg),
218}
219
220#[derive(Debug, Clone, Serialize, Deserialize)]
222#[serde(tag = "type")]
223pub enum DydxWsMarketsMessage {
224 #[serde(rename = "subscribed")]
226 Subscribed(DydxWsChannelDataMsg),
227 #[serde(rename = "channel_data")]
229 ChannelData(DydxWsChannelDataMsg),
230 #[serde(rename = "unsubscribed")]
232 Unsubscribed(DydxWsSubscriptionMsg),
233}
234
235#[derive(Debug, Clone, Serialize, Deserialize)]
237#[serde(tag = "type")]
238pub enum DydxWsCandlesMessage {
239 #[serde(rename = "subscribed")]
241 Subscribed(DydxWsChannelDataMsg),
242 #[serde(rename = "channel_data")]
244 ChannelData(DydxWsChannelDataMsg),
245 #[serde(rename = "unsubscribed")]
247 Unsubscribed(DydxWsSubscriptionMsg),
248}
249
250#[derive(Debug, Clone, Serialize, Deserialize)]
252#[serde(tag = "type")]
253pub enum DydxWsParentSubaccountsMessage {
254 #[serde(rename = "subscribed")]
256 Subscribed(DydxWsChannelDataMsg),
257 #[serde(rename = "channel_data")]
259 ChannelData(DydxWsChannelDataMsg),
260 #[serde(rename = "unsubscribed")]
262 Unsubscribed(DydxWsSubscriptionMsg),
263}
264
265#[derive(Debug, Clone, Serialize, Deserialize)]
267#[serde(tag = "type")]
268pub enum DydxWsBlockHeightMessage {
269 #[serde(rename = "subscribed")]
271 Subscribed(DydxWsBlockHeightSubscribedData),
272 #[serde(rename = "channel_data")]
274 ChannelData(DydxWsBlockHeightChannelData),
275 #[serde(rename = "unsubscribed")]
277 Unsubscribed(DydxWsSubscriptionMsg),
278}
279
280#[derive(Debug, Clone, Serialize, Deserialize)]
282pub struct DydxWsGenericMsg {
283 #[serde(rename = "type")]
285 pub msg_type: DydxWsMessageType,
286 #[serde(default, skip_serializing_if = "Option::is_none")]
288 pub connection_id: Option<String>,
289 #[serde(default, skip_serializing_if = "Option::is_none")]
291 pub message_id: Option<u64>,
292 #[serde(default, skip_serializing_if = "Option::is_none")]
294 pub channel: Option<DydxWsChannel>,
295 #[serde(default, skip_serializing_if = "Option::is_none")]
297 pub id: Option<String>,
298 #[serde(default, skip_serializing_if = "Option::is_none")]
300 pub message: Option<String>,
301}
302
303impl DydxWsGenericMsg {
304 #[must_use]
306 pub fn is_error(&self) -> bool {
307 self.msg_type == DydxWsMessageType::Error
308 }
309
310 #[must_use]
312 pub fn is_subscribed(&self) -> bool {
313 self.msg_type == DydxWsMessageType::Subscribed
314 }
315
316 #[must_use]
318 pub fn is_unsubscribed(&self) -> bool {
319 self.msg_type == DydxWsMessageType::Unsubscribed
320 }
321
322 #[must_use]
324 pub fn is_connected(&self) -> bool {
325 self.msg_type == DydxWsMessageType::Connected
326 }
327
328 #[must_use]
330 pub fn is_channel_data(&self) -> bool {
331 self.msg_type == DydxWsMessageType::ChannelData
332 }
333
334 #[must_use]
336 pub fn is_channel_batch_data(&self) -> bool {
337 self.msg_type == DydxWsMessageType::ChannelBatchData
338 }
339
340 #[must_use]
342 pub fn is_unknown(&self) -> bool {
343 self.msg_type == DydxWsMessageType::Unknown
344 }
345}
346
347#[derive(Debug, Clone, Serialize, Deserialize)]
349pub struct DydxBlockHeightSubscribedContents {
350 pub height: String,
351 pub time: DateTime<Utc>,
352}
353
354#[derive(Debug, Clone, Serialize, Deserialize)]
356pub struct DydxWsBlockHeightSubscribedData {
357 pub connection_id: String,
358 pub message_id: u64,
359 pub id: String,
360 pub contents: DydxBlockHeightSubscribedContents,
361}
362
363#[derive(Debug, Clone, Serialize, Deserialize)]
365pub struct DydxBlockHeightChannelContents {
366 #[serde(rename = "blockHeight")]
367 pub block_height: String,
368 pub time: DateTime<Utc>,
369}
370
371#[derive(Debug, Clone, Serialize, Deserialize)]
373pub struct DydxWsBlockHeightChannelData {
374 pub connection_id: String,
375 pub message_id: u64,
376 pub id: String,
377 pub version: String,
378 pub contents: DydxBlockHeightChannelContents,
379}
380
381#[derive(Debug, Clone, Serialize, Deserialize)]
383pub struct DydxOraclePriceMarketFull {
384 #[serde(rename = "oraclePrice")]
385 pub oracle_price: String,
386 #[serde(rename = "effectiveAt")]
387 pub effective_at: String,
388 #[serde(rename = "effectiveAtHeight")]
389 pub effective_at_height: String,
390 #[serde(rename = "marketId")]
391 pub market_id: u32,
392}
393
394#[derive(Debug, Clone, Serialize, Deserialize)]
396#[serde(rename_all = "camelCase")]
397pub struct DydxOraclePriceMarket {
398 pub oracle_price: String,
400}
401
402#[derive(Debug, Clone, Serialize, Deserialize)]
407#[serde(rename_all = "camelCase")]
408pub struct DydxMarketTradingUpdate {
409 #[serde(default, skip_serializing_if = "Option::is_none")]
411 pub ticker: Option<String>,
412 #[serde(default, skip_serializing_if = "Option::is_none")]
414 pub status: Option<crate::common::enums::DydxMarketStatus>,
415 #[serde(default, skip_serializing_if = "Option::is_none")]
417 pub clob_pair_id: Option<String>,
418 #[serde(default, skip_serializing_if = "Option::is_none")]
420 pub atomic_resolution: Option<i32>,
421 #[serde(default, skip_serializing_if = "Option::is_none")]
423 pub quantum_conversion_exponent: Option<i32>,
424 #[serde(default, skip_serializing_if = "Option::is_none")]
426 pub step_base_quantums: Option<i32>,
427 #[serde(default, skip_serializing_if = "Option::is_none")]
429 pub subticks_per_tick: Option<i32>,
430 #[serde(default, skip_serializing_if = "Option::is_none")]
432 pub initial_margin_fraction: Option<String>,
433 #[serde(default, skip_serializing_if = "Option::is_none")]
435 pub maintenance_margin_fraction: Option<String>,
436 #[serde(default, skip_serializing_if = "Option::is_none")]
438 pub base_asset: Option<String>,
439 #[serde(default, skip_serializing_if = "Option::is_none")]
441 pub quote_asset: Option<String>,
442 #[serde(default, skip_serializing_if = "Option::is_none")]
444 pub open_interest: Option<String>,
445 #[serde(default, skip_serializing_if = "Option::is_none")]
447 pub price_change_24h: Option<String>,
448 #[serde(default, skip_serializing_if = "Option::is_none")]
450 pub volume_24h: Option<String>,
451 #[serde(default, skip_serializing_if = "Option::is_none")]
453 pub trades_24h: Option<u64>,
454 #[serde(default, skip_serializing_if = "Option::is_none")]
456 pub max_position_size: Option<String>,
457 #[serde(default, skip_serializing_if = "Option::is_none")]
459 pub incremental_position_size: Option<String>,
460 #[serde(default, skip_serializing_if = "Option::is_none")]
462 pub base_position_size: Option<String>,
463 #[serde(default, skip_serializing_if = "Option::is_none")]
465 pub next_funding_rate: Option<String>,
466}
467
468#[derive(Debug, Clone, Serialize, Deserialize)]
470pub struct DydxMarketMessageContents {
471 #[serde(rename = "oraclePrices")]
472 pub oracle_prices: Option<HashMap<String, DydxOraclePriceMarketFull>>,
473 pub trading: Option<Value>,
474}
475
476#[derive(Debug, Clone, Serialize, Deserialize)]
478pub struct DydxWsMarketChannelData {
479 #[serde(rename = "type")]
480 pub msg_type: DydxWsMessageType,
481 pub channel: DydxWsChannel,
482 pub contents: DydxMarketMessageContents,
483 pub version: String,
484 pub message_id: u64,
485 pub connection_id: Option<String>,
486 pub id: Option<String>,
487}
488
489#[derive(Debug, Clone, Serialize, Deserialize)]
491pub struct DydxWsMarketSubscribed {
492 #[serde(rename = "type")]
493 pub msg_type: DydxWsMessageType,
494 pub connection_id: String,
495 pub message_id: u64,
496 pub channel: DydxWsChannel,
497 pub contents: Value,
498}
499
500#[derive(Debug, Clone, Serialize, Deserialize)]
502#[serde(rename_all = "camelCase")]
503pub struct DydxMarketsContents {
504 #[serde(skip_serializing_if = "Option::is_none")]
506 pub oracle_prices: Option<HashMap<String, DydxOraclePriceMarket>>,
507 #[serde(default, skip_serializing_if = "Option::is_none")]
509 pub trading: Option<HashMap<String, DydxMarketTradingUpdate>>,
510}
511
512#[derive(Debug, Clone, Serialize, Deserialize)]
514#[serde(rename_all = "camelCase")]
515pub struct DydxTrade {
516 pub id: String,
518 pub side: OrderSide,
520 pub size: String,
522 pub price: String,
524 pub created_at: DateTime<Utc>,
526 #[serde(rename = "type")]
528 pub trade_type: DydxTradeType,
529 #[serde(skip_serializing_if = "Option::is_none")]
531 pub created_at_height: Option<String>,
532}
533
534#[derive(Debug, Clone, Serialize, Deserialize)]
536pub struct DydxTradeContents {
537 pub trades: Vec<DydxTrade>,
539}
540
541#[derive(Debug, Clone, Serialize, Deserialize)]
543#[serde(rename_all = "camelCase")]
544pub struct DydxCandle {
545 #[serde(default)]
547 pub base_token_volume: Option<String>,
548 pub close: String,
550 pub high: String,
552 pub low: String,
554 pub open: String,
556 pub resolution: DydxCandleResolution,
558 pub started_at: DateTime<Utc>,
560 pub starting_open_interest: String,
562 pub ticker: Ustr,
564 pub trades: i64,
566 pub usd_volume: String,
568 #[serde(skip_serializing_if = "Option::is_none")]
570 pub orderbook_mid_price_close: Option<String>,
571 #[serde(skip_serializing_if = "Option::is_none")]
573 pub orderbook_mid_price_open: Option<String>,
574}
575
576pub type PriceLevel = (String, String);
578
579#[derive(Debug, Clone, Serialize, Deserialize)]
581pub struct DydxOrderbookContents {
582 #[serde(skip_serializing_if = "Option::is_none")]
584 pub bids: Option<Vec<PriceLevel>>,
585 #[serde(skip_serializing_if = "Option::is_none")]
587 pub asks: Option<Vec<PriceLevel>>,
588}
589
590#[derive(Debug, Clone, Serialize, Deserialize)]
592pub struct DydxPriceLevel {
593 pub price: String,
595 pub size: String,
597}
598
599#[derive(Debug, Clone, Serialize, Deserialize)]
601pub struct DydxOrderbookSnapshotContents {
602 #[serde(skip_serializing_if = "Option::is_none")]
604 pub bids: Option<Vec<DydxPriceLevel>>,
605 #[serde(skip_serializing_if = "Option::is_none")]
607 pub asks: Option<Vec<DydxPriceLevel>>,
608}
609
610#[derive(Debug, Clone, Serialize, Deserialize)]
612pub struct DydxAssetBalance {
613 pub symbol: Ustr,
614 pub side: DydxPositionSide,
615 pub size: String,
616 #[serde(rename = "assetId")]
617 pub asset_id: String,
618}
619
620#[derive(Debug, Clone, Serialize, Deserialize)]
622pub struct DydxPerpetualPosition {
623 pub market: Ustr,
624 pub status: DydxPositionStatus,
625 pub side: DydxPositionSide,
626 pub size: String,
627 #[serde(rename = "maxSize")]
628 pub max_size: String,
629 #[serde(rename = "entryPrice")]
630 pub entry_price: String,
631 #[serde(rename = "exitPrice")]
632 pub exit_price: Option<String>,
633 #[serde(rename = "realizedPnl")]
634 pub realized_pnl: String,
635 #[serde(rename = "unrealizedPnl")]
636 pub unrealized_pnl: String,
637 #[serde(rename = "createdAt")]
638 pub created_at: String,
639 #[serde(rename = "closedAt")]
640 pub closed_at: Option<String>,
641 #[serde(rename = "sumOpen")]
642 pub sum_open: String,
643 #[serde(rename = "sumClose")]
644 pub sum_close: String,
645 #[serde(rename = "netFunding")]
646 pub net_funding: String,
647}
648
649#[derive(Debug, Clone, Serialize, Deserialize)]
651pub struct DydxSubaccountInfo {
652 pub address: String,
653 #[serde(rename = "subaccountNumber")]
654 pub subaccount_number: u32,
655 pub equity: String,
656 #[serde(rename = "freeCollateral")]
657 pub free_collateral: String,
658 #[serde(rename = "openPerpetualPositions")]
659 pub open_perpetual_positions: Option<HashMap<String, DydxPerpetualPosition>>,
660 #[serde(rename = "assetPositions")]
661 pub asset_positions: Option<HashMap<String, DydxAssetBalance>>,
662 #[serde(rename = "marginEnabled")]
663 pub margin_enabled: bool,
664 #[serde(rename = "updatedAtHeight")]
665 pub updated_at_height: String,
666 #[serde(rename = "latestProcessedBlockHeight")]
667 pub latest_processed_block_height: String,
668}
669
670#[derive(Debug, Clone, Serialize, Deserialize)]
672pub struct DydxWsOrderSubaccountMessageContents {
673 pub id: String,
674 #[serde(rename = "subaccountId")]
675 pub subaccount_id: String,
676 #[serde(rename = "clientId")]
677 pub client_id: String,
678 #[serde(rename = "clobPairId")]
679 pub clob_pair_id: String,
680 pub side: OrderSide,
681 pub size: String,
682 pub price: String,
683 pub status: DydxOrderStatus,
684 #[serde(rename = "type")]
685 pub order_type: DydxOrderType,
686 #[serde(rename = "timeInForce")]
687 pub time_in_force: DydxTimeInForce,
688 #[serde(rename = "postOnly")]
689 pub post_only: bool,
690 #[serde(rename = "reduceOnly")]
691 pub reduce_only: bool,
692 #[serde(rename = "orderFlags")]
693 pub order_flags: String,
694 #[serde(rename = "goodTilBlock")]
695 pub good_til_block: Option<String>,
696 #[serde(rename = "goodTilBlockTime")]
697 pub good_til_block_time: Option<String>,
698 #[serde(rename = "createdAtHeight")]
699 pub created_at_height: Option<String>,
700 #[serde(rename = "clientMetadata")]
701 pub client_metadata: Option<String>,
702 #[serde(rename = "triggerPrice")]
703 pub trigger_price: Option<String>,
704 #[serde(rename = "totalFilled")]
705 pub total_filled: Option<String>,
706 #[serde(rename = "updatedAt")]
707 pub updated_at: Option<String>,
708 #[serde(rename = "updatedAtHeight")]
709 pub updated_at_height: Option<String>,
710}
711
712#[derive(Debug, Clone, Serialize, Deserialize)]
714pub struct DydxWsFillSubaccountMessageContents {
715 pub id: String,
716 #[serde(rename = "subaccountId")]
717 pub subaccount_id: String,
718 pub side: OrderSide,
719 pub liquidity: DydxLiquidity,
720 #[serde(rename = "type")]
721 pub fill_type: DydxFillType,
722 #[serde(alias = "ticker")]
723 pub market: Ustr,
724 #[serde(rename = "marketType", default)]
725 pub market_type: Option<DydxTickerType>,
726 pub price: String,
727 pub size: String,
728 pub fee: String,
729 #[serde(rename = "createdAt")]
730 pub created_at: String,
731 #[serde(rename = "createdAtHeight")]
732 pub created_at_height: Option<String>,
733 #[serde(rename = "orderId")]
734 pub order_id: Option<String>,
735 #[serde(rename = "clientMetadata")]
736 pub client_metadata: Option<String>,
737}
738
739#[derive(Debug, Clone, Serialize, Deserialize)]
741pub struct DydxWsSubaccountsSubscribedContents {
742 pub subaccount: Option<DydxSubaccountInfo>,
743}
744
745#[derive(Debug, Clone, Serialize, Deserialize)]
747pub struct DydxWsSubaccountsSubscribed {
748 pub connection_id: String,
749 pub message_id: u64,
750 pub id: String,
751 pub contents: DydxWsSubaccountsSubscribedContents,
752}
753
754#[derive(Debug, Clone, Serialize, Deserialize)]
756pub struct DydxWsSubaccountsChannelContents {
757 pub orders: Option<Vec<DydxWsOrderSubaccountMessageContents>>,
758 pub fills: Option<Vec<DydxWsFillSubaccountMessageContents>>,
759}
760
761#[derive(Debug, Clone, Serialize, Deserialize)]
763pub struct DydxWsSubaccountsChannelData {
764 pub connection_id: String,
765 pub message_id: u64,
766 pub id: String,
767 pub version: String,
768 pub contents: DydxWsSubaccountsChannelContents,
769}