1use nautilus_model::enums::{LiquiditySide, OrderSide, OrderStatus, OrderType, PositionSide};
19use serde::{Deserialize, Serialize};
20use strum::{AsRefStr, Display, EnumIter, EnumString};
21
22use crate::error::DydxError;
23
24#[derive(
26 Copy,
27 Clone,
28 Debug,
29 Display,
30 PartialEq,
31 Eq,
32 Hash,
33 AsRefStr,
34 EnumIter,
35 EnumString,
36 Serialize,
37 Deserialize,
38)]
39#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
40pub enum DydxOrderStatus {
41 Open,
43 Filled,
45 Canceled,
47 BestEffortCanceled,
49 PartiallyFilled,
51 BestEffortOpened,
53 Untriggered,
55}
56
57impl From<DydxOrderStatus> for OrderStatus {
58 fn from(value: DydxOrderStatus) -> Self {
59 match value {
60 DydxOrderStatus::Open | DydxOrderStatus::BestEffortOpened => Self::Accepted,
61 DydxOrderStatus::PartiallyFilled => Self::PartiallyFilled,
62 DydxOrderStatus::Filled => Self::Filled,
63 DydxOrderStatus::Canceled | DydxOrderStatus::BestEffortCanceled => Self::Canceled,
64 DydxOrderStatus::Untriggered => Self::PendingUpdate,
65 }
66 }
67}
68
69#[derive(
71 Copy,
72 Clone,
73 Debug,
74 Display,
75 PartialEq,
76 Eq,
77 Hash,
78 AsRefStr,
79 EnumIter,
80 EnumString,
81 Serialize,
82 Deserialize,
83)]
84#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
85pub enum DydxTimeInForce {
86 Gtt,
88 Fok,
90 Ioc,
92}
93
94#[derive(
96 Copy,
97 Clone,
98 Debug,
99 Display,
100 PartialEq,
101 Eq,
102 Hash,
103 AsRefStr,
104 EnumIter,
105 EnumString,
106 Serialize,
107 Deserialize,
108)]
109#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
110#[cfg_attr(
111 feature = "python",
112 pyo3::pyclass(module = "nautilus_trader.core.nautilus_pyo3.dydx", eq, eq_int)
113)]
114pub enum DydxOrderSide {
115 Buy,
117 Sell,
119}
120
121impl TryFrom<OrderSide> for DydxOrderSide {
122 type Error = DydxError;
123
124 fn try_from(value: OrderSide) -> Result<Self, Self::Error> {
125 match value {
126 OrderSide::Buy => Ok(Self::Buy),
127 OrderSide::Sell => Ok(Self::Sell),
128 _ => Err(DydxError::InvalidOrderSide(format!("{value:?}"))),
129 }
130 }
131}
132
133impl DydxOrderSide {
134 pub fn try_from_order_side(value: OrderSide) -> anyhow::Result<Self> {
140 Self::try_from(value).map_err(|e| anyhow::anyhow!("{e}"))
141 }
142}
143
144impl From<DydxOrderSide> for OrderSide {
145 fn from(side: DydxOrderSide) -> Self {
146 match side {
147 DydxOrderSide::Buy => Self::Buy,
148 DydxOrderSide::Sell => Self::Sell,
149 }
150 }
151}
152
153#[derive(
155 Copy,
156 Clone,
157 Debug,
158 Display,
159 PartialEq,
160 Eq,
161 Hash,
162 AsRefStr,
163 EnumIter,
164 EnumString,
165 Serialize,
166 Deserialize,
167)]
168#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
169#[cfg_attr(
170 feature = "python",
171 pyo3::pyclass(module = "nautilus_trader.core.nautilus_pyo3.dydx", eq, eq_int)
172)]
173pub enum DydxOrderType {
174 Limit,
176 Market,
178 StopLimit,
180 StopMarket,
182 TakeProfitLimit,
184 TakeProfitMarket,
186 TrailingStop,
188}
189
190impl TryFrom<OrderType> for DydxOrderType {
191 type Error = DydxError;
192
193 fn try_from(value: OrderType) -> Result<Self, Self::Error> {
194 match value {
195 OrderType::Market => Ok(Self::Market),
196 OrderType::Limit => Ok(Self::Limit),
197 OrderType::StopMarket => Ok(Self::StopMarket),
198 OrderType::StopLimit => Ok(Self::StopLimit),
199 OrderType::MarketIfTouched => Ok(Self::TakeProfitMarket),
200 OrderType::LimitIfTouched => Ok(Self::TakeProfitLimit),
201 OrderType::TrailingStopMarket | OrderType::TrailingStopLimit => Ok(Self::TrailingStop),
202 OrderType::MarketToLimit => Err(DydxError::UnsupportedOrderType(format!("{value:?}"))),
203 }
204 }
205}
206
207impl DydxOrderType {
208 pub fn try_from_order_type(value: OrderType) -> anyhow::Result<Self> {
214 Self::try_from(value).map_err(|e| anyhow::anyhow!("{e}"))
215 }
216
217 #[must_use]
219 pub const fn is_conditional(&self) -> bool {
220 matches!(
221 self,
222 Self::StopLimit
223 | Self::StopMarket
224 | Self::TakeProfitLimit
225 | Self::TakeProfitMarket
226 | Self::TrailingStop
227 )
228 }
229
230 #[must_use]
232 pub const fn condition_type(&self) -> DydxConditionType {
233 match self {
234 Self::StopLimit | Self::StopMarket => DydxConditionType::StopLoss,
235 Self::TakeProfitLimit | Self::TakeProfitMarket => DydxConditionType::TakeProfit,
236 _ => DydxConditionType::Unspecified,
237 }
238 }
239
240 #[must_use]
242 pub const fn is_market_execution(&self) -> bool {
243 matches!(
244 self,
245 Self::Market | Self::StopMarket | Self::TakeProfitMarket
246 )
247 }
248}
249
250impl From<DydxOrderType> for OrderType {
251 fn from(value: DydxOrderType) -> Self {
252 match value {
253 DydxOrderType::Market => Self::Market,
254 DydxOrderType::Limit => Self::Limit,
255 DydxOrderType::StopMarket => Self::StopMarket,
256 DydxOrderType::StopLimit => Self::StopLimit,
257 DydxOrderType::TakeProfitMarket => Self::MarketIfTouched,
258 DydxOrderType::TakeProfitLimit => Self::LimitIfTouched,
259 DydxOrderType::TrailingStop => Self::TrailingStopMarket,
260 }
261 }
262}
263
264#[derive(
266 Copy,
267 Clone,
268 Debug,
269 Display,
270 PartialEq,
271 Eq,
272 Hash,
273 AsRefStr,
274 EnumIter,
275 EnumString,
276 Serialize,
277 Deserialize,
278)]
279#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
280pub enum DydxOrderExecution {
281 Default,
283 Ioc,
285 Fok,
287 PostOnly,
289}
290
291#[derive(
293 Copy, Clone, Debug, Display, PartialEq, Eq, Hash, AsRefStr, EnumIter, Serialize, Deserialize,
294)]
295pub enum DydxOrderFlags {
296 ShortTerm = 0,
298 Conditional = 32,
300 LongTerm = 64,
302}
303
304#[derive(
310 Copy,
311 Clone,
312 Debug,
313 Display,
314 PartialEq,
315 Eq,
316 Hash,
317 AsRefStr,
318 EnumIter,
319 EnumString,
320 Serialize,
321 Deserialize,
322)]
323#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
324pub enum DydxConditionType {
325 Unspecified,
327 StopLoss,
329 TakeProfit,
331}
332
333#[derive(
335 Copy,
336 Clone,
337 Debug,
338 Display,
339 PartialEq,
340 Eq,
341 Hash,
342 AsRefStr,
343 EnumIter,
344 EnumString,
345 Serialize,
346 Deserialize,
347)]
348#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
349pub enum DydxPositionStatus {
350 Open,
352 Closed,
354 Liquidated,
356}
357
358impl From<DydxPositionStatus> for PositionSide {
359 fn from(value: DydxPositionStatus) -> Self {
360 match value {
361 DydxPositionStatus::Open => Self::Long, DydxPositionStatus::Closed => Self::Flat,
363 DydxPositionStatus::Liquidated => Self::Flat,
364 }
365 }
366}
367
368#[derive(
370 Copy,
371 Clone,
372 Debug,
373 Display,
374 PartialEq,
375 Eq,
376 Hash,
377 AsRefStr,
378 EnumIter,
379 EnumString,
380 Serialize,
381 Deserialize,
382)]
383#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
384pub enum DydxMarketStatus {
385 Active,
387 Paused,
389 CancelOnly,
391 PostOnly,
393 Initializing,
395 FinalSettlement,
397}
398
399#[derive(
401 Copy,
402 Clone,
403 Debug,
404 Display,
405 PartialEq,
406 Eq,
407 Hash,
408 AsRefStr,
409 EnumIter,
410 EnumString,
411 Serialize,
412 Deserialize,
413)]
414#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
415pub enum DydxFillType {
416 Limit,
418 Liquidated,
420 Liquidation,
422 Deleveraged,
424 Offsetting,
426}
427
428#[derive(
430 Copy,
431 Clone,
432 Debug,
433 Display,
434 PartialEq,
435 Eq,
436 Hash,
437 AsRefStr,
438 EnumIter,
439 EnumString,
440 Serialize,
441 Deserialize,
442)]
443#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
444pub enum DydxLiquidity {
445 Maker,
447 Taker,
449}
450
451impl From<DydxLiquidity> for LiquiditySide {
452 fn from(value: DydxLiquidity) -> Self {
453 match value {
454 DydxLiquidity::Maker => Self::Maker,
455 DydxLiquidity::Taker => Self::Taker,
456 }
457 }
458}
459
460impl From<LiquiditySide> for DydxLiquidity {
461 fn from(value: LiquiditySide) -> Self {
462 match value {
463 LiquiditySide::Maker => Self::Maker,
464 LiquiditySide::Taker => Self::Taker,
465 LiquiditySide::NoLiquiditySide => Self::Taker, }
467 }
468}
469
470#[derive(
472 Copy,
473 Clone,
474 Debug,
475 Display,
476 PartialEq,
477 Eq,
478 Hash,
479 AsRefStr,
480 EnumIter,
481 EnumString,
482 Serialize,
483 Deserialize,
484)]
485#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
486pub enum DydxTickerType {
487 Perpetual,
489}
490
491#[derive(
495 Copy,
496 Clone,
497 Debug,
498 Display,
499 PartialEq,
500 Eq,
501 Hash,
502 AsRefStr,
503 EnumIter,
504 EnumString,
505 Serialize,
506 Deserialize,
507)]
508#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
509pub enum DydxTradeType {
510 Limit,
512 Market,
514 Liquidated,
516 TwapSuborder,
518 StopLimit,
520 TakeProfitLimit,
522}
523
524#[derive(
526 Copy,
527 Clone,
528 Debug,
529 Display,
530 PartialEq,
531 Eq,
532 Hash,
533 AsRefStr,
534 EnumIter,
535 EnumString,
536 Serialize,
537 Deserialize,
538)]
539#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
540#[derive(Default)]
541pub enum DydxCandleResolution {
542 #[serde(rename = "1MIN")]
544 #[strum(serialize = "1MIN")]
545 #[default]
546 OneMinute,
547 #[serde(rename = "5MINS")]
549 #[strum(serialize = "5MINS")]
550 FiveMinutes,
551 #[serde(rename = "15MINS")]
553 #[strum(serialize = "15MINS")]
554 FifteenMinutes,
555 #[serde(rename = "30MINS")]
557 #[strum(serialize = "30MINS")]
558 ThirtyMinutes,
559 #[serde(rename = "1HOUR")]
561 #[strum(serialize = "1HOUR")]
562 OneHour,
563 #[serde(rename = "4HOURS")]
565 #[strum(serialize = "4HOURS")]
566 FourHours,
567 #[serde(rename = "1DAY")]
569 #[strum(serialize = "1DAY")]
570 OneDay,
571}
572
573#[cfg(test)]
578mod tests {
579 use rstest::rstest;
580
581 use super::*;
582
583 #[rstest]
584 fn test_order_status_conversion() {
585 assert_eq!(
586 OrderStatus::from(DydxOrderStatus::Open),
587 OrderStatus::Accepted
588 );
589 assert_eq!(
590 OrderStatus::from(DydxOrderStatus::Filled),
591 OrderStatus::Filled
592 );
593 assert_eq!(
594 OrderStatus::from(DydxOrderStatus::Canceled),
595 OrderStatus::Canceled
596 );
597 }
598
599 #[rstest]
600 fn test_liquidity_conversion() {
601 assert_eq!(
602 LiquiditySide::from(DydxLiquidity::Maker),
603 LiquiditySide::Maker
604 );
605 assert_eq!(
606 LiquiditySide::from(DydxLiquidity::Taker),
607 LiquiditySide::Taker
608 );
609 }
610
611 #[rstest]
612 fn test_order_type_is_conditional() {
613 assert!(DydxOrderType::StopLimit.is_conditional());
614 assert!(DydxOrderType::StopMarket.is_conditional());
615 assert!(DydxOrderType::TakeProfitLimit.is_conditional());
616 assert!(DydxOrderType::TakeProfitMarket.is_conditional());
617 assert!(DydxOrderType::TrailingStop.is_conditional());
618 assert!(!DydxOrderType::Limit.is_conditional());
619 assert!(!DydxOrderType::Market.is_conditional());
620 }
621
622 #[rstest]
623 fn test_condition_type_mapping() {
624 assert_eq!(
625 DydxOrderType::StopLimit.condition_type(),
626 DydxConditionType::StopLoss
627 );
628 assert_eq!(
629 DydxOrderType::StopMarket.condition_type(),
630 DydxConditionType::StopLoss
631 );
632 assert_eq!(
633 DydxOrderType::TakeProfitLimit.condition_type(),
634 DydxConditionType::TakeProfit
635 );
636 assert_eq!(
637 DydxOrderType::TakeProfitMarket.condition_type(),
638 DydxConditionType::TakeProfit
639 );
640 assert_eq!(
641 DydxOrderType::Limit.condition_type(),
642 DydxConditionType::Unspecified
643 );
644 }
645
646 #[rstest]
647 fn test_is_market_execution() {
648 assert!(DydxOrderType::Market.is_market_execution());
649 assert!(DydxOrderType::StopMarket.is_market_execution());
650 assert!(DydxOrderType::TakeProfitMarket.is_market_execution());
651 assert!(!DydxOrderType::Limit.is_market_execution());
652 assert!(!DydxOrderType::StopLimit.is_market_execution());
653 assert!(!DydxOrderType::TakeProfitLimit.is_market_execution());
654 }
655
656 #[rstest]
657 fn test_order_type_to_nautilus() {
658 assert_eq!(OrderType::from(DydxOrderType::Market), OrderType::Market);
659 assert_eq!(OrderType::from(DydxOrderType::Limit), OrderType::Limit);
660 assert_eq!(
661 OrderType::from(DydxOrderType::StopMarket),
662 OrderType::StopMarket
663 );
664 assert_eq!(
665 OrderType::from(DydxOrderType::StopLimit),
666 OrderType::StopLimit
667 );
668 }
669
670 #[rstest]
671 fn test_order_side_conversion_from_nautilus() {
672 assert_eq!(
673 DydxOrderSide::try_from(OrderSide::Buy).unwrap(),
674 DydxOrderSide::Buy
675 );
676 assert_eq!(
677 DydxOrderSide::try_from(OrderSide::Sell).unwrap(),
678 DydxOrderSide::Sell
679 );
680 assert!(DydxOrderSide::try_from(OrderSide::NoOrderSide).is_err());
681 }
682
683 #[rstest]
684 fn test_order_side_conversion_to_nautilus() {
685 assert_eq!(OrderSide::from(DydxOrderSide::Buy), OrderSide::Buy);
686 assert_eq!(OrderSide::from(DydxOrderSide::Sell), OrderSide::Sell);
687 }
688
689 #[rstest]
690 fn test_order_type_conversion_from_nautilus() {
691 assert_eq!(
692 DydxOrderType::try_from(OrderType::Market).unwrap(),
693 DydxOrderType::Market
694 );
695 assert_eq!(
696 DydxOrderType::try_from(OrderType::Limit).unwrap(),
697 DydxOrderType::Limit
698 );
699 assert_eq!(
700 DydxOrderType::try_from(OrderType::StopMarket).unwrap(),
701 DydxOrderType::StopMarket
702 );
703 assert_eq!(
704 DydxOrderType::try_from(OrderType::StopLimit).unwrap(),
705 DydxOrderType::StopLimit
706 );
707 assert!(DydxOrderType::try_from(OrderType::MarketToLimit).is_err());
708 }
709
710 #[rstest]
711 fn test_order_type_conversion_to_nautilus() {
712 assert_eq!(OrderType::from(DydxOrderType::Market), OrderType::Market);
713 assert_eq!(OrderType::from(DydxOrderType::Limit), OrderType::Limit);
714 assert_eq!(
715 OrderType::from(DydxOrderType::StopMarket),
716 OrderType::StopMarket
717 );
718 assert_eq!(
719 OrderType::from(DydxOrderType::StopLimit),
720 OrderType::StopLimit
721 );
722 }
723}