1use nautilus_model::enums::{AggressorSide, OrderSide, OrderStatus, PositionSide, TimeInForce};
19use serde::{Deserialize, Serialize};
20use strum::{AsRefStr, Display, EnumIter, EnumString};
21
22use super::consts::{
23 AX_HTTP_SANDBOX_URL, AX_HTTP_URL, AX_ORDERS_SANDBOX_URL, AX_ORDERS_URL, AX_WS_PRIVATE_URL,
24 AX_WS_PUBLIC_URL, AX_WS_SANDBOX_PRIVATE_URL, AX_WS_SANDBOX_PUBLIC_URL,
25};
26
27#[derive(
29 Clone,
30 Copy,
31 Debug,
32 Default,
33 Display,
34 Eq,
35 PartialEq,
36 Hash,
37 AsRefStr,
38 EnumIter,
39 EnumString,
40 Serialize,
41 Deserialize,
42)]
43#[cfg_attr(
44 feature = "python",
45 pyo3::pyclass(eq, eq_int, module = "nautilus_trader.core.nautilus_pyo3.architect")
46)]
47pub enum AxEnvironment {
48 #[default]
50 Sandbox,
51 Production,
53}
54
55impl AxEnvironment {
56 #[must_use]
58 pub const fn http_url(&self) -> &'static str {
59 match self {
60 Self::Sandbox => AX_HTTP_SANDBOX_URL,
61 Self::Production => AX_HTTP_URL,
62 }
63 }
64
65 #[must_use]
67 pub const fn orders_url(&self) -> &'static str {
68 match self {
69 Self::Sandbox => AX_ORDERS_SANDBOX_URL,
70 Self::Production => AX_ORDERS_URL,
71 }
72 }
73
74 #[must_use]
76 pub const fn ws_md_url(&self) -> &'static str {
77 match self {
78 Self::Sandbox => AX_WS_SANDBOX_PUBLIC_URL,
79 Self::Production => AX_WS_PUBLIC_URL,
80 }
81 }
82
83 #[must_use]
85 pub const fn ws_orders_url(&self) -> &'static str {
86 match self {
87 Self::Sandbox => AX_WS_SANDBOX_PRIVATE_URL,
88 Self::Production => AX_WS_PRIVATE_URL,
89 }
90 }
91}
92
93#[derive(
98 Clone,
99 Copy,
100 Debug,
101 Display,
102 Eq,
103 PartialEq,
104 Hash,
105 AsRefStr,
106 EnumIter,
107 EnumString,
108 Serialize,
109 Deserialize,
110)]
111#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
112#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
113#[cfg_attr(
114 feature = "python",
115 pyo3::pyclass(eq, eq_int, module = "nautilus_trader.core.nautilus_pyo3.architect")
116)]
117pub enum AxInstrumentState {
118 PreOpen,
120 Open,
122 Suspended,
124 Delisted,
126 Unknown,
128}
129
130#[derive(
135 Clone,
136 Copy,
137 Debug,
138 Display,
139 Eq,
140 PartialEq,
141 Hash,
142 AsRefStr,
143 EnumIter,
144 EnumString,
145 Serialize,
146 Deserialize,
147)]
148#[cfg_attr(
149 feature = "python",
150 pyo3::pyclass(eq, eq_int, module = "nautilus_trader.core.nautilus_pyo3.architect")
151)]
152pub enum AxOrderSide {
153 #[serde(rename = "B")]
155 #[strum(serialize = "B")]
156 Buy,
157 #[serde(rename = "S")]
159 #[strum(serialize = "S")]
160 Sell,
161}
162
163impl From<AxOrderSide> for AggressorSide {
164 fn from(side: AxOrderSide) -> Self {
165 match side {
166 AxOrderSide::Buy => Self::Buyer,
167 AxOrderSide::Sell => Self::Seller,
168 }
169 }
170}
171
172impl From<AxOrderSide> for OrderSide {
173 fn from(side: AxOrderSide) -> Self {
174 match side {
175 AxOrderSide::Buy => Self::Buy,
176 AxOrderSide::Sell => Self::Sell,
177 }
178 }
179}
180
181impl From<AxOrderSide> for PositionSide {
182 fn from(side: AxOrderSide) -> Self {
183 match side {
184 AxOrderSide::Buy => Self::Long,
185 AxOrderSide::Sell => Self::Short,
186 }
187 }
188}
189
190#[derive(
195 Clone,
196 Copy,
197 Debug,
198 Display,
199 Eq,
200 PartialEq,
201 Hash,
202 AsRefStr,
203 EnumIter,
204 EnumString,
205 Serialize,
206 Deserialize,
207)]
208#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
209#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
210#[cfg_attr(
211 feature = "python",
212 pyo3::pyclass(eq, eq_int, module = "nautilus_trader.core.nautilus_pyo3.architect")
213)]
214pub enum AxOrderStatus {
215 Pending,
217 Accepted,
219 PartiallyFilled,
221 Filled,
223 Canceled,
225 Rejected,
227 Expired,
229 Replaced,
231 DoneForDay,
233 Unknown,
235}
236
237impl From<AxOrderStatus> for OrderStatus {
238 fn from(status: AxOrderStatus) -> Self {
239 match status {
240 AxOrderStatus::Pending => Self::Submitted,
241 AxOrderStatus::Accepted => Self::Accepted,
242 AxOrderStatus::PartiallyFilled => Self::PartiallyFilled,
243 AxOrderStatus::Filled => Self::Filled,
244 AxOrderStatus::Canceled => Self::Canceled,
245 AxOrderStatus::Rejected => Self::Rejected,
246 AxOrderStatus::Expired => Self::Expired,
247 AxOrderStatus::Replaced => Self::Accepted,
248 AxOrderStatus::DoneForDay => Self::Canceled,
249 AxOrderStatus::Unknown => Self::Initialized,
250 }
251 }
252}
253
254#[derive(
259 Clone,
260 Copy,
261 Debug,
262 Display,
263 Eq,
264 PartialEq,
265 Hash,
266 AsRefStr,
267 EnumIter,
268 EnumString,
269 Serialize,
270 Deserialize,
271)]
272#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
273#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
274#[cfg_attr(
275 feature = "python",
276 pyo3::pyclass(eq, eq_int, module = "nautilus_trader.core.nautilus_pyo3.architect")
277)]
278pub enum AxTimeInForce {
279 Gtc,
281 Ioc,
283 Day,
285}
286
287impl From<AxTimeInForce> for TimeInForce {
288 fn from(tif: AxTimeInForce) -> Self {
289 match tif {
290 AxTimeInForce::Gtc => Self::Gtc,
291 AxTimeInForce::Ioc => Self::Ioc,
292 AxTimeInForce::Day => Self::Day,
293 }
294 }
295}
296
297#[derive(
302 Clone,
303 Copy,
304 Debug,
305 Display,
306 Eq,
307 PartialEq,
308 Hash,
309 AsRefStr,
310 EnumIter,
311 EnumString,
312 Serialize,
313 Deserialize,
314)]
315#[cfg_attr(
316 feature = "python",
317 pyo3::pyclass(eq, eq_int, module = "nautilus_trader.core.nautilus_pyo3.architect")
318)]
319pub enum AxMarketDataLevel {
320 #[serde(rename = "LEVEL_1")]
322 #[strum(serialize = "LEVEL_1")]
323 Level1,
324 #[serde(rename = "LEVEL_2")]
326 #[strum(serialize = "LEVEL_2")]
327 Level2,
328 #[serde(rename = "LEVEL_3")]
330 #[strum(serialize = "LEVEL_3")]
331 Level3,
332}
333
334#[derive(
339 Clone,
340 Copy,
341 Debug,
342 Display,
343 Eq,
344 PartialEq,
345 Hash,
346 AsRefStr,
347 EnumIter,
348 EnumString,
349 Serialize,
350 Deserialize,
351)]
352#[cfg_attr(
353 feature = "python",
354 pyo3::pyclass(eq, eq_int, module = "nautilus_trader.core.nautilus_pyo3.architect")
355)]
356pub enum AxCandleWidth {
357 #[serde(rename = "1s")]
359 #[strum(serialize = "1s")]
360 Seconds1,
361 #[serde(rename = "5s")]
363 #[strum(serialize = "5s")]
364 Seconds5,
365 #[serde(rename = "1m")]
367 #[strum(serialize = "1m")]
368 Minutes1,
369 #[serde(rename = "5m")]
371 #[strum(serialize = "5m")]
372 Minutes5,
373 #[serde(rename = "15m")]
375 #[strum(serialize = "15m")]
376 Minutes15,
377 #[serde(rename = "1h")]
379 #[strum(serialize = "1h")]
380 Hours1,
381 #[serde(rename = "1d")]
383 #[strum(serialize = "1d")]
384 Days1,
385}
386
387#[derive(
392 Clone,
393 Copy,
394 Debug,
395 Display,
396 Eq,
397 PartialEq,
398 Hash,
399 AsRefStr,
400 EnumIter,
401 EnumString,
402 Serialize,
403 Deserialize,
404)]
405#[cfg_attr(
406 feature = "python",
407 pyo3::pyclass(eq, eq_int, module = "nautilus_trader.core.nautilus_pyo3.architect")
408)]
409pub enum AxMdWsMessageType {
410 #[serde(rename = "h")]
412 #[strum(serialize = "h")]
413 Heartbeat,
414 #[serde(rename = "s")]
416 #[strum(serialize = "s")]
417 Ticker,
418 #[serde(rename = "t")]
420 #[strum(serialize = "t")]
421 Trade,
422 #[serde(rename = "c")]
424 #[strum(serialize = "c")]
425 Candle,
426 #[serde(rename = "1")]
428 #[strum(serialize = "1")]
429 BookLevel1,
430 #[serde(rename = "2")]
432 #[strum(serialize = "2")]
433 BookLevel2,
434 #[serde(rename = "3")]
436 #[strum(serialize = "3")]
437 BookLevel3,
438}
439
440#[derive(
445 Clone,
446 Copy,
447 Debug,
448 Display,
449 Eq,
450 PartialEq,
451 Hash,
452 AsRefStr,
453 EnumIter,
454 EnumString,
455 Serialize,
456 Deserialize,
457)]
458#[cfg_attr(
459 feature = "python",
460 pyo3::pyclass(eq, eq_int, module = "nautilus_trader.core.nautilus_pyo3.architect")
461)]
462pub enum AxOrderWsMessageType {
463 #[serde(rename = "h")]
465 #[strum(serialize = "h")]
466 Heartbeat,
467 #[serde(rename = "e")]
469 #[strum(serialize = "e")]
470 CancelRejected,
471 #[serde(rename = "n")]
473 #[strum(serialize = "n")]
474 OrderAcknowledged,
475 #[serde(rename = "c")]
477 #[strum(serialize = "c")]
478 OrderCanceled,
479 #[serde(rename = "r")]
481 #[strum(serialize = "r")]
482 OrderReplaced,
483 #[serde(rename = "j")]
485 #[strum(serialize = "j")]
486 OrderRejected,
487 #[serde(rename = "x")]
489 #[strum(serialize = "x")]
490 OrderExpired,
491 #[serde(rename = "d")]
493 #[strum(serialize = "d")]
494 OrderDoneForDay,
495 #[serde(rename = "p")]
497 #[strum(serialize = "p")]
498 OrderPartiallyFilled,
499 #[serde(rename = "f")]
501 #[strum(serialize = "f")]
502 OrderFilled,
503}
504
505#[derive(
510 Clone,
511 Copy,
512 Debug,
513 Display,
514 Eq,
515 PartialEq,
516 Hash,
517 AsRefStr,
518 EnumIter,
519 EnumString,
520 Serialize,
521 Deserialize,
522)]
523#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
524#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
525#[cfg_attr(
526 feature = "python",
527 pyo3::pyclass(eq, eq_int, module = "nautilus_trader.core.nautilus_pyo3.architect")
528)]
529pub enum AxCancelReason {
530 UserRequested,
532}
533
534#[derive(
539 Clone,
540 Copy,
541 Debug,
542 Display,
543 Eq,
544 PartialEq,
545 Hash,
546 AsRefStr,
547 EnumIter,
548 EnumString,
549 Serialize,
550 Deserialize,
551)]
552#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
553#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
554#[cfg_attr(
555 feature = "python",
556 pyo3::pyclass(eq, eq_int, module = "nautilus_trader.core.nautilus_pyo3.architect")
557)]
558pub enum AxCancelRejectionReason {
559 OrderNotFound,
561}
562
563#[cfg(test)]
564mod tests {
565 use rstest::rstest;
566
567 use super::*;
568
569 #[rstest]
570 #[case(AxInstrumentState::Open, "\"OPEN\"")]
571 #[case(AxInstrumentState::PreOpen, "\"PRE_OPEN\"")]
572 #[case(AxInstrumentState::Suspended, "\"SUSPENDED\"")]
573 #[case(AxInstrumentState::Delisted, "\"DELISTED\"")]
574 fn test_instrument_state_serialization(
575 #[case] state: AxInstrumentState,
576 #[case] expected: &str,
577 ) {
578 let json = serde_json::to_string(&state).unwrap();
579 assert_eq!(json, expected);
580
581 let parsed: AxInstrumentState = serde_json::from_str(&json).unwrap();
582 assert_eq!(parsed, state);
583 }
584
585 #[rstest]
586 #[case(AxOrderSide::Buy, "\"B\"")]
587 #[case(AxOrderSide::Sell, "\"S\"")]
588 fn test_order_side_serialization(#[case] side: AxOrderSide, #[case] expected: &str) {
589 let json = serde_json::to_string(&side).unwrap();
590 assert_eq!(json, expected);
591
592 let parsed: AxOrderSide = serde_json::from_str(&json).unwrap();
593 assert_eq!(parsed, side);
594 }
595
596 #[rstest]
597 #[case(AxOrderStatus::Pending, "\"PENDING\"")]
598 #[case(AxOrderStatus::Accepted, "\"ACCEPTED\"")]
599 #[case(AxOrderStatus::PartiallyFilled, "\"PARTIALLY_FILLED\"")]
600 #[case(AxOrderStatus::Filled, "\"FILLED\"")]
601 #[case(AxOrderStatus::Canceled, "\"CANCELED\"")]
602 fn test_order_status_serialization(#[case] status: AxOrderStatus, #[case] expected: &str) {
603 let json = serde_json::to_string(&status).unwrap();
604 assert_eq!(json, expected);
605
606 let parsed: AxOrderStatus = serde_json::from_str(&json).unwrap();
607 assert_eq!(parsed, status);
608 }
609
610 #[rstest]
611 #[case(AxTimeInForce::Gtc, "\"GTC\"")]
612 #[case(AxTimeInForce::Ioc, "\"IOC\"")]
613 #[case(AxTimeInForce::Day, "\"DAY\"")]
614 fn test_time_in_force_serialization(#[case] tif: AxTimeInForce, #[case] expected: &str) {
615 let json = serde_json::to_string(&tif).unwrap();
616 assert_eq!(json, expected);
617
618 let parsed: AxTimeInForce = serde_json::from_str(&json).unwrap();
619 assert_eq!(parsed, tif);
620 }
621
622 #[rstest]
623 #[case(AxMarketDataLevel::Level1, "\"LEVEL_1\"")]
624 #[case(AxMarketDataLevel::Level2, "\"LEVEL_2\"")]
625 #[case(AxMarketDataLevel::Level3, "\"LEVEL_3\"")]
626 fn test_market_data_level_serialization(
627 #[case] level: AxMarketDataLevel,
628 #[case] expected: &str,
629 ) {
630 let json = serde_json::to_string(&level).unwrap();
631 assert_eq!(json, expected);
632
633 let parsed: AxMarketDataLevel = serde_json::from_str(&json).unwrap();
634 assert_eq!(parsed, level);
635 }
636
637 #[rstest]
638 #[case(AxCandleWidth::Seconds1, "\"1s\"")]
639 #[case(AxCandleWidth::Minutes1, "\"1m\"")]
640 #[case(AxCandleWidth::Minutes5, "\"5m\"")]
641 #[case(AxCandleWidth::Hours1, "\"1h\"")]
642 #[case(AxCandleWidth::Days1, "\"1d\"")]
643 fn test_candle_width_serialization(#[case] width: AxCandleWidth, #[case] expected: &str) {
644 let json = serde_json::to_string(&width).unwrap();
645 assert_eq!(json, expected);
646
647 let parsed: AxCandleWidth = serde_json::from_str(&json).unwrap();
648 assert_eq!(parsed, width);
649 }
650
651 #[rstest]
652 #[case(AxMdWsMessageType::Heartbeat, "\"h\"")]
653 #[case(AxMdWsMessageType::Ticker, "\"s\"")]
654 #[case(AxMdWsMessageType::Trade, "\"t\"")]
655 #[case(AxMdWsMessageType::Candle, "\"c\"")]
656 #[case(AxMdWsMessageType::BookLevel1, "\"1\"")]
657 #[case(AxMdWsMessageType::BookLevel2, "\"2\"")]
658 #[case(AxMdWsMessageType::BookLevel3, "\"3\"")]
659 fn test_md_ws_message_type_serialization(
660 #[case] msg_type: AxMdWsMessageType,
661 #[case] expected: &str,
662 ) {
663 let json = serde_json::to_string(&msg_type).unwrap();
664 assert_eq!(json, expected);
665
666 let parsed: AxMdWsMessageType = serde_json::from_str(&json).unwrap();
667 assert_eq!(parsed, msg_type);
668 }
669
670 #[rstest]
671 #[case(AxOrderWsMessageType::Heartbeat, "\"h\"")]
672 #[case(AxOrderWsMessageType::OrderAcknowledged, "\"n\"")]
673 #[case(AxOrderWsMessageType::OrderCanceled, "\"c\"")]
674 #[case(AxOrderWsMessageType::OrderFilled, "\"f\"")]
675 #[case(AxOrderWsMessageType::OrderPartiallyFilled, "\"p\"")]
676 fn test_order_ws_message_type_serialization(
677 #[case] msg_type: AxOrderWsMessageType,
678 #[case] expected: &str,
679 ) {
680 let json = serde_json::to_string(&msg_type).unwrap();
681 assert_eq!(json, expected);
682
683 let parsed: AxOrderWsMessageType = serde_json::from_str(&json).unwrap();
684 assert_eq!(parsed, msg_type);
685 }
686}