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)]
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")]
54 pub msg_type: DydxWsMessageType,
55 pub connection_id: String,
57 pub message_id: u64,
59 pub channel: DydxWsChannel,
61 #[serde(default, skip_serializing_if = "Option::is_none")]
63 pub id: Option<String>,
64}
65
66#[derive(Debug, Clone, Serialize, Deserialize)]
68pub struct DydxWsConnectedMsg {
69 #[serde(rename = "type")]
71 pub msg_type: DydxWsMessageType,
72 pub connection_id: String,
74 pub message_id: u64,
76}
77
78#[derive(Debug, Clone, Serialize, Deserialize)]
80pub struct DydxWsChannelDataMsg {
81 #[serde(rename = "type")]
83 pub msg_type: DydxWsMessageType,
84 pub connection_id: String,
86 pub message_id: u64,
88 pub channel: DydxWsChannel,
90 #[serde(default, skip_serializing_if = "Option::is_none")]
92 pub id: Option<String>,
93 pub contents: Value,
95 #[serde(default, skip_serializing_if = "Option::is_none")]
97 pub version: Option<String>,
98}
99
100#[derive(Debug, Clone, Serialize, Deserialize)]
102pub struct DydxWsChannelBatchDataMsg {
103 #[serde(rename = "type")]
105 pub msg_type: DydxWsMessageType,
106 pub connection_id: String,
108 pub message_id: u64,
110 pub channel: DydxWsChannel,
112 #[serde(default, skip_serializing_if = "Option::is_none")]
114 pub id: Option<String>,
115 pub contents: Value,
117 #[serde(default, skip_serializing_if = "Option::is_none")]
119 pub version: Option<String>,
120}
121
122#[derive(Debug, Clone, Serialize, Deserialize)]
124pub struct DydxWsMessageGeneral {
125 #[serde(rename = "type")]
126 pub msg_type: Option<DydxWsMessageType>,
127 pub connection_id: Option<String>,
128 pub message_id: Option<u64>,
129 pub channel: Option<DydxWsChannel>,
130 pub id: Option<String>,
131 pub message: Option<String>,
132}
133
134#[derive(Debug, Clone, Serialize, Deserialize)]
143#[serde(tag = "channel")]
144pub enum DydxWsFeedMessage {
145 #[serde(rename = "v4_subaccounts")]
147 Subaccounts(DydxWsSubaccountsMessage),
148 #[serde(rename = "v4_orderbook")]
150 Orderbook(DydxWsOrderbookMessage),
151 #[serde(rename = "v4_trades")]
153 Trades(DydxWsTradesMessage),
154 #[serde(rename = "v4_markets")]
156 Markets(DydxWsMarketsMessage),
157 #[serde(rename = "v4_candles")]
159 Candles(DydxWsCandlesMessage),
160 #[serde(rename = "v4_parent_subaccounts")]
162 ParentSubaccounts(DydxWsParentSubaccountsMessage),
163 #[serde(rename = "v4_block_height")]
165 BlockHeight(DydxWsBlockHeightMessage),
166}
167
168#[derive(Debug, Clone, Serialize, Deserialize)]
170#[serde(tag = "type")]
171pub enum DydxWsSubaccountsMessage {
172 #[serde(rename = "subscribed")]
174 Subscribed(DydxWsSubaccountsSubscribed),
175 #[serde(rename = "channel_data")]
177 ChannelData(DydxWsSubaccountsChannelData),
178}
179
180#[derive(Debug, Clone, Serialize, Deserialize)]
182#[serde(tag = "type")]
183pub enum DydxWsOrderbookMessage {
184 #[serde(rename = "subscribed")]
186 Subscribed(DydxWsChannelDataMsg),
187 #[serde(rename = "channel_data")]
189 ChannelData(DydxWsChannelDataMsg),
190 #[serde(rename = "channel_batch_data")]
192 ChannelBatchData(DydxWsChannelBatchDataMsg),
193}
194
195#[derive(Debug, Clone, Serialize, Deserialize)]
197#[serde(tag = "type")]
198pub enum DydxWsTradesMessage {
199 #[serde(rename = "subscribed")]
201 Subscribed(DydxWsChannelDataMsg),
202 #[serde(rename = "channel_data")]
204 ChannelData(DydxWsChannelDataMsg),
205}
206
207#[derive(Debug, Clone, Serialize, Deserialize)]
209#[serde(tag = "type")]
210pub enum DydxWsMarketsMessage {
211 #[serde(rename = "subscribed")]
213 Subscribed(DydxWsChannelDataMsg),
214 #[serde(rename = "channel_data")]
216 ChannelData(DydxWsChannelDataMsg),
217}
218
219#[derive(Debug, Clone, Serialize, Deserialize)]
221#[serde(tag = "type")]
222pub enum DydxWsCandlesMessage {
223 #[serde(rename = "subscribed")]
225 Subscribed(DydxWsChannelDataMsg),
226 #[serde(rename = "channel_data")]
228 ChannelData(DydxWsChannelDataMsg),
229}
230
231#[derive(Debug, Clone, Serialize, Deserialize)]
233#[serde(tag = "type")]
234pub enum DydxWsParentSubaccountsMessage {
235 #[serde(rename = "subscribed")]
237 Subscribed(DydxWsChannelDataMsg),
238 #[serde(rename = "channel_data")]
240 ChannelData(DydxWsChannelDataMsg),
241}
242
243#[derive(Debug, Clone, Serialize, Deserialize)]
245#[serde(tag = "type")]
246pub enum DydxWsBlockHeightMessage {
247 #[serde(rename = "subscribed")]
249 Subscribed(DydxWsBlockHeightSubscribedData),
250 #[serde(rename = "channel_data")]
252 ChannelData(DydxWsBlockHeightChannelData),
253}
254
255#[derive(Debug, Clone, Serialize, Deserialize)]
257pub struct DydxWsGenericMsg {
258 #[serde(rename = "type")]
260 pub msg_type: DydxWsMessageType,
261 #[serde(default, skip_serializing_if = "Option::is_none")]
263 pub connection_id: Option<String>,
264 #[serde(default, skip_serializing_if = "Option::is_none")]
266 pub message_id: Option<u64>,
267 #[serde(default, skip_serializing_if = "Option::is_none")]
269 pub channel: Option<DydxWsChannel>,
270 #[serde(default, skip_serializing_if = "Option::is_none")]
272 pub id: Option<String>,
273 #[serde(default, skip_serializing_if = "Option::is_none")]
275 pub message: Option<String>,
276}
277
278impl DydxWsGenericMsg {
279 #[must_use]
281 pub fn is_error(&self) -> bool {
282 self.msg_type == DydxWsMessageType::Error
283 }
284
285 #[must_use]
287 pub fn is_subscribed(&self) -> bool {
288 self.msg_type == DydxWsMessageType::Subscribed
289 }
290
291 #[must_use]
293 pub fn is_unsubscribed(&self) -> bool {
294 self.msg_type == DydxWsMessageType::Unsubscribed
295 }
296
297 #[must_use]
299 pub fn is_connected(&self) -> bool {
300 self.msg_type == DydxWsMessageType::Connected
301 }
302
303 #[must_use]
305 pub fn is_channel_data(&self) -> bool {
306 self.msg_type == DydxWsMessageType::ChannelData
307 }
308
309 #[must_use]
311 pub fn is_channel_batch_data(&self) -> bool {
312 self.msg_type == DydxWsMessageType::ChannelBatchData
313 }
314
315 #[must_use]
317 pub fn is_unknown(&self) -> bool {
318 self.msg_type == DydxWsMessageType::Unknown
319 }
320}
321
322#[derive(Debug, Clone, Serialize, Deserialize)]
324pub struct DydxBlockHeightSubscribedContents {
325 pub height: String,
326 pub time: DateTime<Utc>,
327}
328
329#[derive(Debug, Clone, Serialize, Deserialize)]
331pub struct DydxWsBlockHeightSubscribedData {
332 #[serde(rename = "type")]
333 pub msg_type: DydxWsMessageType,
334 pub connection_id: String,
335 pub message_id: u64,
336 pub channel: DydxWsChannel,
337 pub id: String,
338 pub contents: DydxBlockHeightSubscribedContents,
339}
340
341#[derive(Debug, Clone, Serialize, Deserialize)]
343pub struct DydxBlockHeightChannelContents {
344 #[serde(rename = "blockHeight")]
345 pub block_height: String,
346 pub time: DateTime<Utc>,
347}
348
349#[derive(Debug, Clone, Serialize, Deserialize)]
351pub struct DydxWsBlockHeightChannelData {
352 #[serde(rename = "type")]
353 pub msg_type: DydxWsMessageType,
354 pub connection_id: String,
355 pub message_id: u64,
356 pub id: String,
357 pub channel: DydxWsChannel,
358 pub version: String,
359 pub contents: DydxBlockHeightChannelContents,
360}
361
362#[derive(Debug, Clone, Serialize, Deserialize)]
364pub struct DydxOraclePriceMarketFull {
365 #[serde(rename = "oraclePrice")]
366 pub oracle_price: String,
367 #[serde(rename = "effectiveAt")]
368 pub effective_at: String,
369 #[serde(rename = "effectiveAtHeight")]
370 pub effective_at_height: String,
371 #[serde(rename = "marketId")]
372 pub market_id: u32,
373}
374
375#[derive(Debug, Clone, Serialize, Deserialize)]
377#[serde(rename_all = "camelCase")]
378pub struct DydxOraclePriceMarket {
379 pub oracle_price: String,
381}
382
383#[derive(Debug, Clone, Serialize, Deserialize)]
385pub struct DydxMarketMessageContents {
386 #[serde(rename = "oraclePrices")]
387 pub oracle_prices: Option<HashMap<String, DydxOraclePriceMarketFull>>,
388 pub trading: Option<Value>,
389}
390
391#[derive(Debug, Clone, Serialize, Deserialize)]
393pub struct DydxWsMarketChannelData {
394 #[serde(rename = "type")]
395 pub msg_type: DydxWsMessageType,
396 pub channel: DydxWsChannel,
397 pub contents: DydxMarketMessageContents,
398 pub version: String,
399 pub message_id: u64,
400 pub connection_id: Option<String>,
401 pub id: Option<String>,
402}
403
404#[derive(Debug, Clone, Serialize, Deserialize)]
406pub struct DydxWsMarketSubscribed {
407 #[serde(rename = "type")]
408 pub msg_type: DydxWsMessageType,
409 pub connection_id: String,
410 pub message_id: u64,
411 pub channel: DydxWsChannel,
412 pub contents: Value,
413}
414
415#[derive(Debug, Clone, Serialize, Deserialize)]
417#[serde(rename_all = "camelCase")]
418pub struct DydxMarketsContents {
419 #[serde(skip_serializing_if = "Option::is_none")]
421 pub oracle_prices: Option<HashMap<String, DydxOraclePriceMarket>>,
422}
423
424#[derive(Debug, Clone, Serialize, Deserialize)]
426#[serde(rename_all = "camelCase")]
427pub struct DydxTrade {
428 pub id: String,
430 pub side: OrderSide,
432 pub size: String,
434 pub price: String,
436 pub created_at: DateTime<Utc>,
438 #[serde(rename = "type")]
440 pub trade_type: DydxTradeType,
441 #[serde(skip_serializing_if = "Option::is_none")]
443 pub created_at_height: Option<String>,
444}
445
446#[derive(Debug, Clone, Serialize, Deserialize)]
448pub struct DydxTradeContents {
449 pub trades: Vec<DydxTrade>,
451}
452
453#[derive(Debug, Clone, Serialize, Deserialize)]
455#[serde(rename_all = "camelCase")]
456pub struct DydxCandle {
457 pub base_token_volume: String,
459 pub close: String,
461 pub high: String,
463 pub low: String,
465 pub open: String,
467 pub resolution: DydxCandleResolution,
469 pub started_at: DateTime<Utc>,
471 pub starting_open_interest: String,
473 pub ticker: String,
475 pub trades: i64,
477 pub usd_volume: String,
479 #[serde(skip_serializing_if = "Option::is_none")]
481 pub orderbook_mid_price_close: Option<String>,
482 #[serde(skip_serializing_if = "Option::is_none")]
484 pub orderbook_mid_price_open: Option<String>,
485}
486
487pub type PriceLevel = (String, String);
489
490#[derive(Debug, Clone, Serialize, Deserialize)]
492pub struct DydxOrderbookContents {
493 #[serde(skip_serializing_if = "Option::is_none")]
495 pub bids: Option<Vec<PriceLevel>>,
496 #[serde(skip_serializing_if = "Option::is_none")]
498 pub asks: Option<Vec<PriceLevel>>,
499}
500
501#[derive(Debug, Clone, Serialize, Deserialize)]
503pub struct DydxPriceLevel {
504 pub price: String,
506 pub size: String,
508}
509
510#[derive(Debug, Clone, Serialize, Deserialize)]
512pub struct DydxOrderbookSnapshotContents {
513 #[serde(skip_serializing_if = "Option::is_none")]
515 pub bids: Option<Vec<DydxPriceLevel>>,
516 #[serde(skip_serializing_if = "Option::is_none")]
518 pub asks: Option<Vec<DydxPriceLevel>>,
519}
520
521#[derive(Debug, Clone, Serialize, Deserialize)]
523pub struct DydxAssetBalance {
524 pub symbol: Ustr,
525 pub side: OrderSide,
526 pub size: String,
527 #[serde(rename = "assetId")]
528 pub asset_id: String,
529}
530
531#[derive(Debug, Clone, Serialize, Deserialize)]
533pub struct DydxPerpetualPosition {
534 pub market: Ustr,
535 pub status: DydxPositionStatus,
536 pub side: PositionSide,
537 pub size: String,
538 #[serde(rename = "maxSize")]
539 pub max_size: String,
540 #[serde(rename = "entryPrice")]
541 pub entry_price: String,
542 #[serde(rename = "exitPrice")]
543 pub exit_price: Option<String>,
544 #[serde(rename = "realizedPnl")]
545 pub realized_pnl: String,
546 #[serde(rename = "unrealizedPnl")]
547 pub unrealized_pnl: String,
548 #[serde(rename = "createdAt")]
549 pub created_at: String,
550 #[serde(rename = "closedAt")]
551 pub closed_at: Option<String>,
552 #[serde(rename = "sumOpen")]
553 pub sum_open: String,
554 #[serde(rename = "sumClose")]
555 pub sum_close: String,
556 #[serde(rename = "netFunding")]
557 pub net_funding: String,
558}
559
560#[derive(Debug, Clone, Serialize, Deserialize)]
562pub struct DydxSubaccountInfo {
563 pub address: String,
564 #[serde(rename = "subaccountNumber")]
565 pub subaccount_number: u32,
566 pub equity: String,
567 #[serde(rename = "freeCollateral")]
568 pub free_collateral: String,
569 #[serde(rename = "openPerpetualPositions")]
570 pub open_perpetual_positions: Option<HashMap<String, DydxPerpetualPosition>>,
571 #[serde(rename = "assetPositions")]
572 pub asset_positions: Option<HashMap<String, DydxAssetBalance>>,
573 #[serde(rename = "marginEnabled")]
574 pub margin_enabled: bool,
575 #[serde(rename = "updatedAtHeight")]
576 pub updated_at_height: String,
577 #[serde(rename = "latestProcessedBlockHeight")]
578 pub latest_processed_block_height: String,
579}
580
581#[derive(Debug, Clone, Serialize, Deserialize)]
583pub struct DydxWsOrderSubaccountMessageContents {
584 pub id: String,
585 #[serde(rename = "subaccountId")]
586 pub subaccount_id: String,
587 #[serde(rename = "clientId")]
588 pub client_id: String,
589 #[serde(rename = "clobPairId")]
590 pub clob_pair_id: String,
591 pub side: OrderSide,
592 pub size: String,
593 pub price: String,
594 pub status: DydxOrderStatus,
595 #[serde(rename = "type")]
596 pub order_type: DydxOrderType,
597 #[serde(rename = "timeInForce")]
598 pub time_in_force: DydxTimeInForce,
599 #[serde(rename = "postOnly")]
600 pub post_only: bool,
601 #[serde(rename = "reduceOnly")]
602 pub reduce_only: bool,
603 #[serde(rename = "orderFlags")]
604 pub order_flags: String,
605 #[serde(rename = "goodTilBlock")]
606 pub good_til_block: Option<String>,
607 #[serde(rename = "goodTilBlockTime")]
608 pub good_til_block_time: Option<String>,
609 #[serde(rename = "createdAtHeight")]
610 pub created_at_height: String,
611 #[serde(rename = "clientMetadata")]
612 pub client_metadata: String,
613 #[serde(rename = "triggerPrice")]
614 pub trigger_price: Option<String>,
615 #[serde(rename = "totalFilled")]
616 pub total_filled: String,
617 #[serde(rename = "updatedAt")]
618 pub updated_at: Option<String>,
619 #[serde(rename = "updatedAtHeight")]
620 pub updated_at_height: Option<String>,
621}
622
623#[derive(Debug, Clone, Serialize, Deserialize)]
625pub struct DydxWsFillSubaccountMessageContents {
626 pub id: String,
627 #[serde(rename = "subaccountId")]
628 pub subaccount_id: String,
629 pub side: OrderSide,
630 pub liquidity: DydxLiquidity,
631 #[serde(rename = "type")]
632 pub fill_type: DydxFillType,
633 pub market: Ustr,
634 #[serde(rename = "marketType")]
635 pub market_type: DydxTickerType,
636 pub price: String,
637 pub size: String,
638 pub fee: String,
639 #[serde(rename = "createdAt")]
640 pub created_at: String,
641 #[serde(rename = "createdAtHeight")]
642 pub created_at_height: String,
643 #[serde(rename = "orderId")]
644 pub order_id: String,
645 #[serde(rename = "clientMetadata")]
646 pub client_metadata: String,
647}
648
649#[derive(Debug, Clone, Serialize, Deserialize)]
651pub struct DydxWsSubaccountsSubscribedContents {
652 pub subaccount: DydxSubaccountInfo,
653}
654
655#[derive(Debug, Clone, Serialize, Deserialize)]
657pub struct DydxWsSubaccountsSubscribed {
658 #[serde(rename = "type")]
659 pub msg_type: DydxWsMessageType,
660 pub connection_id: String,
661 pub message_id: u64,
662 pub channel: DydxWsChannel,
663 pub id: String,
664 pub contents: DydxWsSubaccountsSubscribedContents,
665}
666
667#[derive(Debug, Clone, Serialize, Deserialize)]
669pub struct DydxWsSubaccountsChannelContents {
670 pub orders: Option<Vec<DydxWsOrderSubaccountMessageContents>>,
671 pub fills: Option<Vec<DydxWsFillSubaccountMessageContents>>,
672}
673
674#[derive(Debug, Clone, Serialize, Deserialize)]
676pub struct DydxWsSubaccountsChannelData {
677 #[serde(rename = "type")]
678 pub msg_type: DydxWsMessageType,
679 pub connection_id: String,
680 pub message_id: u64,
681 pub id: String,
682 pub channel: DydxWsChannel,
683 pub version: String,
684 pub contents: DydxWsSubaccountsChannelContents,
685}