1use nautilus_model::{
19 data::BarSpecification,
20 enums::{
21 AggressorSide, BarAggregation, OrderSide, OrderStatus, OrderType, PositionSide, TimeInForce,
22 },
23};
24use serde::{Deserialize, Serialize};
25use strum::{AsRefStr, Display, EnumIter, EnumString};
26
27use super::consts::{
28 AX_HTTP_SANDBOX_URL, AX_HTTP_URL, AX_ORDERS_SANDBOX_URL, AX_ORDERS_URL, AX_WS_PRIVATE_URL,
29 AX_WS_PUBLIC_URL, AX_WS_SANDBOX_PRIVATE_URL, AX_WS_SANDBOX_PUBLIC_URL,
30};
31
32#[derive(
34 Clone,
35 Copy,
36 Debug,
37 Default,
38 Display,
39 Eq,
40 PartialEq,
41 Hash,
42 AsRefStr,
43 EnumIter,
44 EnumString,
45 Serialize,
46 Deserialize,
47)]
48#[strum(ascii_case_insensitive)]
49#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
50#[cfg_attr(
51 feature = "python",
52 pyo3::pyclass(
53 eq,
54 eq_int,
55 frozen,
56 hash,
57 module = "nautilus_trader.core.nautilus_pyo3.architect"
58 )
59)]
60pub enum AxEnvironment {
61 #[default]
63 Sandbox,
64 Production,
66}
67
68impl AxEnvironment {
69 #[must_use]
71 pub const fn http_url(&self) -> &'static str {
72 match self {
73 Self::Sandbox => AX_HTTP_SANDBOX_URL,
74 Self::Production => AX_HTTP_URL,
75 }
76 }
77
78 #[must_use]
80 pub const fn orders_url(&self) -> &'static str {
81 match self {
82 Self::Sandbox => AX_ORDERS_SANDBOX_URL,
83 Self::Production => AX_ORDERS_URL,
84 }
85 }
86
87 #[must_use]
89 pub const fn ws_md_url(&self) -> &'static str {
90 match self {
91 Self::Sandbox => AX_WS_SANDBOX_PUBLIC_URL,
92 Self::Production => AX_WS_PUBLIC_URL,
93 }
94 }
95
96 #[must_use]
98 pub const fn ws_orders_url(&self) -> &'static str {
99 match self {
100 Self::Sandbox => AX_WS_SANDBOX_PRIVATE_URL,
101 Self::Production => AX_WS_PRIVATE_URL,
102 }
103 }
104}
105
106#[derive(
111 Clone,
112 Copy,
113 Debug,
114 Display,
115 Eq,
116 PartialEq,
117 Hash,
118 AsRefStr,
119 EnumIter,
120 EnumString,
121 Serialize,
122 Deserialize,
123)]
124#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
125#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
126#[cfg_attr(
127 feature = "python",
128 pyo3::pyclass(eq, eq_int, module = "nautilus_trader.core.nautilus_pyo3.architect")
129)]
130pub enum AxInstrumentState {
131 PreOpen,
133 Open,
135 Suspended,
137 Delisted,
139 Unknown,
141}
142
143#[derive(
148 Clone,
149 Copy,
150 Debug,
151 Display,
152 Eq,
153 PartialEq,
154 Hash,
155 AsRefStr,
156 EnumIter,
157 EnumString,
158 Serialize,
159 Deserialize,
160)]
161#[cfg_attr(
162 feature = "python",
163 pyo3::pyclass(eq, eq_int, module = "nautilus_trader.core.nautilus_pyo3.architect")
164)]
165pub enum AxOrderSide {
166 #[serde(rename = "B", alias = "Buy")]
168 #[strum(serialize = "B")]
169 Buy,
170 #[serde(rename = "S", alias = "Sell")]
172 #[strum(serialize = "S")]
173 Sell,
174}
175
176impl From<AxOrderSide> for AggressorSide {
177 fn from(side: AxOrderSide) -> Self {
178 match side {
179 AxOrderSide::Buy => Self::Buyer,
180 AxOrderSide::Sell => Self::Seller,
181 }
182 }
183}
184
185impl From<AxOrderSide> for OrderSide {
186 fn from(side: AxOrderSide) -> Self {
187 match side {
188 AxOrderSide::Buy => Self::Buy,
189 AxOrderSide::Sell => Self::Sell,
190 }
191 }
192}
193
194impl From<AxOrderSide> for PositionSide {
195 fn from(side: AxOrderSide) -> Self {
196 match side {
197 AxOrderSide::Buy => Self::Long,
198 AxOrderSide::Sell => Self::Short,
199 }
200 }
201}
202
203impl TryFrom<OrderSide> for AxOrderSide {
204 type Error = &'static str;
205
206 fn try_from(side: OrderSide) -> Result<Self, Self::Error> {
207 match side {
208 OrderSide::Buy => Ok(Self::Buy),
209 OrderSide::Sell => Ok(Self::Sell),
210 _ => Err("Invalid order side for AX"),
211 }
212 }
213}
214
215#[derive(
220 Clone,
221 Copy,
222 Debug,
223 Display,
224 Eq,
225 PartialEq,
226 Hash,
227 AsRefStr,
228 EnumIter,
229 EnumString,
230 Serialize,
231 Deserialize,
232)]
233#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
234#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
235#[cfg_attr(
236 feature = "python",
237 pyo3::pyclass(eq, eq_int, module = "nautilus_trader.core.nautilus_pyo3.architect")
238)]
239pub enum AxOrderStatus {
240 Pending,
242 Accepted,
244 PartiallyFilled,
246 Filled,
248 Canceling,
250 Canceled,
252 Rejected,
254 Expired,
256 Replaced,
258 DoneForDay,
260 Out,
262 ReconciledOut,
264 Stale,
266 Unknown,
268}
269
270impl From<AxOrderStatus> for OrderStatus {
271 fn from(status: AxOrderStatus) -> Self {
272 match status {
273 AxOrderStatus::Pending => Self::Submitted,
274 AxOrderStatus::Accepted => Self::Accepted,
275 AxOrderStatus::PartiallyFilled => Self::PartiallyFilled,
276 AxOrderStatus::Filled => Self::Filled,
277 AxOrderStatus::Canceling => Self::PendingCancel,
278 AxOrderStatus::Canceled => Self::Canceled,
279 AxOrderStatus::Rejected => Self::Rejected,
280 AxOrderStatus::Expired => Self::Expired,
281 AxOrderStatus::Replaced => Self::Accepted,
282 AxOrderStatus::DoneForDay => Self::Canceled,
283 AxOrderStatus::Out => Self::Canceled,
284 AxOrderStatus::ReconciledOut => Self::Canceled,
285 AxOrderStatus::Stale => Self::Accepted,
286 AxOrderStatus::Unknown => Self::Initialized,
287 }
288 }
289}
290
291#[derive(
296 Clone,
297 Copy,
298 Debug,
299 Display,
300 Eq,
301 PartialEq,
302 Hash,
303 AsRefStr,
304 EnumIter,
305 EnumString,
306 Serialize,
307 Deserialize,
308)]
309#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
310#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
311#[cfg_attr(
312 feature = "python",
313 pyo3::pyclass(eq, eq_int, module = "nautilus_trader.core.nautilus_pyo3.architect")
314)]
315pub enum AxTimeInForce {
316 Gtc,
318 Gtd,
320 Day,
322 Ioc,
324 Fok,
326 Ato,
328 Atc,
330}
331
332impl From<AxTimeInForce> for TimeInForce {
333 fn from(tif: AxTimeInForce) -> Self {
334 match tif {
335 AxTimeInForce::Gtc => Self::Gtc,
336 AxTimeInForce::Gtd => Self::Gtd,
337 AxTimeInForce::Day => Self::Day,
338 AxTimeInForce::Ioc => Self::Ioc,
339 AxTimeInForce::Fok => Self::Fok,
340 AxTimeInForce::Ato => Self::AtTheOpen,
341 AxTimeInForce::Atc => Self::AtTheClose,
342 }
343 }
344}
345
346impl TryFrom<TimeInForce> for AxTimeInForce {
347 type Error = &'static str;
348
349 fn try_from(tif: TimeInForce) -> Result<Self, Self::Error> {
350 match tif {
351 TimeInForce::Gtc => Ok(Self::Gtc),
352 TimeInForce::Gtd => Ok(Self::Gtd),
353 TimeInForce::Day => Ok(Self::Day),
354 TimeInForce::Ioc => Ok(Self::Ioc),
355 TimeInForce::Fok => Ok(Self::Fok),
356 TimeInForce::AtTheOpen => Ok(Self::Ato),
357 TimeInForce::AtTheClose => Ok(Self::Atc),
358 }
359 }
360}
361
362#[derive(
367 Clone,
368 Copy,
369 Debug,
370 Display,
371 Eq,
372 PartialEq,
373 Hash,
374 AsRefStr,
375 EnumIter,
376 EnumString,
377 Serialize,
378 Deserialize,
379)]
380#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
381#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
382#[cfg_attr(
383 feature = "python",
384 pyo3::pyclass(eq, eq_int, module = "nautilus_trader.core.nautilus_pyo3.architect")
385)]
386pub enum AxOrderType {
387 Market,
389 Limit,
391 StopLossLimit,
393 TakeProfitLimit,
396}
397
398impl From<AxOrderType> for OrderType {
399 fn from(order_type: AxOrderType) -> Self {
400 match order_type {
401 AxOrderType::Market => Self::Market,
402 AxOrderType::Limit => Self::Limit,
403 AxOrderType::StopLossLimit => Self::StopLimit,
404 AxOrderType::TakeProfitLimit => Self::LimitIfTouched,
405 }
406 }
407}
408
409impl TryFrom<OrderType> for AxOrderType {
410 type Error = &'static str;
411
412 fn try_from(order_type: OrderType) -> Result<Self, Self::Error> {
413 match order_type {
414 OrderType::Market => Ok(Self::Market),
415 OrderType::Limit => Ok(Self::Limit),
416 OrderType::StopLimit => Ok(Self::StopLossLimit),
417 OrderType::LimitIfTouched => Ok(Self::TakeProfitLimit),
418 _ => Err("Unsupported order type for AX"),
419 }
420 }
421}
422
423#[derive(
428 Clone,
429 Copy,
430 Debug,
431 Display,
432 Eq,
433 PartialEq,
434 Hash,
435 AsRefStr,
436 EnumIter,
437 EnumString,
438 Serialize,
439 Deserialize,
440)]
441#[strum(ascii_case_insensitive)]
442#[cfg_attr(
443 feature = "python",
444 pyo3::pyclass(
445 eq,
446 eq_int,
447 frozen,
448 hash,
449 module = "nautilus_trader.core.nautilus_pyo3.architect"
450 )
451)]
452pub enum AxMarketDataLevel {
453 #[serde(rename = "LEVEL_1")]
455 #[strum(serialize = "LEVEL_1")]
456 Level1,
457 #[serde(rename = "LEVEL_2")]
459 #[strum(serialize = "LEVEL_2")]
460 Level2,
461 #[serde(rename = "LEVEL_3")]
463 #[strum(serialize = "LEVEL_3")]
464 Level3,
465}
466
467#[derive(
472 Clone,
473 Copy,
474 Debug,
475 Display,
476 Eq,
477 PartialEq,
478 Hash,
479 AsRefStr,
480 EnumIter,
481 EnumString,
482 Serialize,
483 Deserialize,
484)]
485pub enum AxCandleWidth {
486 #[serde(rename = "1s")]
488 #[strum(serialize = "1s")]
489 Seconds1,
490 #[serde(rename = "5s")]
492 #[strum(serialize = "5s")]
493 Seconds5,
494 #[serde(rename = "1m")]
496 #[strum(serialize = "1m")]
497 Minutes1,
498 #[serde(rename = "5m")]
500 #[strum(serialize = "5m")]
501 Minutes5,
502 #[serde(rename = "15m")]
504 #[strum(serialize = "15m")]
505 Minutes15,
506 #[serde(rename = "1h")]
508 #[strum(serialize = "1h")]
509 Hours1,
510 #[serde(rename = "1d")]
512 #[strum(serialize = "1d")]
513 Days1,
514}
515
516impl TryFrom<&BarSpecification> for AxCandleWidth {
517 type Error = anyhow::Error;
518
519 fn try_from(spec: &BarSpecification) -> Result<Self, Self::Error> {
520 let step = spec.step.get();
521 match (step, spec.aggregation) {
522 (1, BarAggregation::Second) => Ok(Self::Seconds1),
523 (5, BarAggregation::Second) => Ok(Self::Seconds5),
524 (1, BarAggregation::Minute) => Ok(Self::Minutes1),
525 (5, BarAggregation::Minute) => Ok(Self::Minutes5),
526 (15, BarAggregation::Minute) => Ok(Self::Minutes15),
527 (1, BarAggregation::Hour) => Ok(Self::Hours1),
528 (1, BarAggregation::Day) => Ok(Self::Days1),
529 _ => anyhow::bail!(
530 "Unsupported bar specification for AX: {step}-{:?}",
531 spec.aggregation,
532 ),
533 }
534 }
535}
536
537#[derive(
542 Clone,
543 Copy,
544 Debug,
545 Display,
546 Eq,
547 PartialEq,
548 Hash,
549 AsRefStr,
550 EnumIter,
551 EnumString,
552 Serialize,
553 Deserialize,
554)]
555pub enum AxMdRequestType {
556 #[serde(rename = "subscribe")]
558 #[strum(serialize = "subscribe")]
559 Subscribe,
560 #[serde(rename = "unsubscribe")]
562 #[strum(serialize = "unsubscribe")]
563 Unsubscribe,
564 #[serde(rename = "subscribe_candles")]
566 #[strum(serialize = "subscribe_candles")]
567 SubscribeCandles,
568 #[serde(rename = "unsubscribe_candles")]
570 #[strum(serialize = "unsubscribe_candles")]
571 UnsubscribeCandles,
572}
573
574#[derive(
579 Clone,
580 Copy,
581 Debug,
582 Display,
583 Eq,
584 PartialEq,
585 Hash,
586 AsRefStr,
587 EnumIter,
588 EnumString,
589 Serialize,
590 Deserialize,
591)]
592pub enum AxOrderRequestType {
593 #[serde(rename = "p")]
595 #[strum(serialize = "p")]
596 PlaceOrder,
597 #[serde(rename = "x")]
599 #[strum(serialize = "x")]
600 CancelOrder,
601 #[serde(rename = "o")]
603 #[strum(serialize = "o")]
604 GetOpenOrders,
605}
606
607#[derive(
612 Clone,
613 Copy,
614 Debug,
615 Display,
616 Eq,
617 PartialEq,
618 Hash,
619 AsRefStr,
620 EnumIter,
621 EnumString,
622 Serialize,
623 Deserialize,
624)]
625#[cfg_attr(
626 feature = "python",
627 pyo3::pyclass(eq, eq_int, module = "nautilus_trader.core.nautilus_pyo3.architect")
628)]
629pub enum AxMdWsMessageType {
630 #[serde(rename = "h")]
632 #[strum(serialize = "h")]
633 Heartbeat,
634 #[serde(rename = "s")]
636 #[strum(serialize = "s")]
637 Ticker,
638 #[serde(rename = "t")]
640 #[strum(serialize = "t")]
641 Trade,
642 #[serde(rename = "c")]
644 #[strum(serialize = "c")]
645 Candle,
646 #[serde(rename = "1")]
648 #[strum(serialize = "1")]
649 BookLevel1,
650 #[serde(rename = "2")]
652 #[strum(serialize = "2")]
653 BookLevel2,
654 #[serde(rename = "3")]
656 #[strum(serialize = "3")]
657 BookLevel3,
658}
659
660#[derive(
665 Clone,
666 Copy,
667 Debug,
668 Display,
669 Eq,
670 PartialEq,
671 Hash,
672 AsRefStr,
673 EnumIter,
674 EnumString,
675 Serialize,
676 Deserialize,
677)]
678#[cfg_attr(
679 feature = "python",
680 pyo3::pyclass(eq, eq_int, module = "nautilus_trader.core.nautilus_pyo3.architect")
681)]
682pub enum AxOrderWsMessageType {
683 #[serde(rename = "h")]
685 #[strum(serialize = "h")]
686 Heartbeat,
687 #[serde(rename = "e")]
689 #[strum(serialize = "e")]
690 CancelRejected,
691 #[serde(rename = "n")]
693 #[strum(serialize = "n")]
694 OrderAcknowledged,
695 #[serde(rename = "c")]
697 #[strum(serialize = "c")]
698 OrderCanceled,
699 #[serde(rename = "r")]
701 #[strum(serialize = "r")]
702 OrderReplaced,
703 #[serde(rename = "j")]
705 #[strum(serialize = "j")]
706 OrderRejected,
707 #[serde(rename = "x")]
709 #[strum(serialize = "x")]
710 OrderExpired,
711 #[serde(rename = "d")]
713 #[strum(serialize = "d")]
714 OrderDoneForDay,
715 #[serde(rename = "p")]
717 #[strum(serialize = "p")]
718 OrderPartiallyFilled,
719 #[serde(rename = "f")]
721 #[strum(serialize = "f")]
722 OrderFilled,
723}
724
725#[derive(
730 Clone,
731 Copy,
732 Debug,
733 Display,
734 Eq,
735 PartialEq,
736 Hash,
737 AsRefStr,
738 EnumIter,
739 EnumString,
740 Serialize,
741 Deserialize,
742)]
743#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
744#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
745#[cfg_attr(
746 feature = "python",
747 pyo3::pyclass(eq, eq_int, module = "nautilus_trader.core.nautilus_pyo3.architect")
748)]
749pub enum AxCancelReason {
750 UserRequested,
752 #[serde(other)]
754 Unknown,
755}
756
757#[derive(
762 Clone,
763 Copy,
764 Debug,
765 Display,
766 Eq,
767 PartialEq,
768 Hash,
769 AsRefStr,
770 EnumIter,
771 EnumString,
772 Serialize,
773 Deserialize,
774)]
775#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
776#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
777#[cfg_attr(
778 feature = "python",
779 pyo3::pyclass(eq, eq_int, module = "nautilus_trader.core.nautilus_pyo3.architect")
780)]
781pub enum AxCancelRejectionReason {
782 OrderNotFound,
784 #[serde(other)]
786 Unknown,
787}
788
789#[cfg(test)]
790mod tests {
791 use rstest::rstest;
792
793 use super::*;
794
795 #[rstest]
796 #[case(AxInstrumentState::Open, "\"OPEN\"")]
797 #[case(AxInstrumentState::PreOpen, "\"PRE_OPEN\"")]
798 #[case(AxInstrumentState::Suspended, "\"SUSPENDED\"")]
799 #[case(AxInstrumentState::Delisted, "\"DELISTED\"")]
800 fn test_instrument_state_serialization(
801 #[case] state: AxInstrumentState,
802 #[case] expected: &str,
803 ) {
804 let json = serde_json::to_string(&state).unwrap();
805 assert_eq!(json, expected);
806
807 let parsed: AxInstrumentState = serde_json::from_str(&json).unwrap();
808 assert_eq!(parsed, state);
809 }
810
811 #[rstest]
812 #[case(AxOrderSide::Buy, "\"B\"")]
813 #[case(AxOrderSide::Sell, "\"S\"")]
814 fn test_order_side_serialization(#[case] side: AxOrderSide, #[case] expected: &str) {
815 let json = serde_json::to_string(&side).unwrap();
816 assert_eq!(json, expected);
817
818 let parsed: AxOrderSide = serde_json::from_str(&json).unwrap();
819 assert_eq!(parsed, side);
820 }
821
822 #[rstest]
823 #[case(AxOrderStatus::Pending, "\"PENDING\"")]
824 #[case(AxOrderStatus::Accepted, "\"ACCEPTED\"")]
825 #[case(AxOrderStatus::PartiallyFilled, "\"PARTIALLY_FILLED\"")]
826 #[case(AxOrderStatus::Filled, "\"FILLED\"")]
827 #[case(AxOrderStatus::Canceling, "\"CANCELING\"")]
828 #[case(AxOrderStatus::Canceled, "\"CANCELED\"")]
829 #[case(AxOrderStatus::Out, "\"OUT\"")]
830 #[case(AxOrderStatus::ReconciledOut, "\"RECONCILED_OUT\"")]
831 #[case(AxOrderStatus::Stale, "\"STALE\"")]
832 fn test_order_status_serialization(#[case] status: AxOrderStatus, #[case] expected: &str) {
833 let json = serde_json::to_string(&status).unwrap();
834 assert_eq!(json, expected);
835
836 let parsed: AxOrderStatus = serde_json::from_str(&json).unwrap();
837 assert_eq!(parsed, status);
838 }
839
840 #[rstest]
841 #[case(AxTimeInForce::Gtc, "\"GTC\"")]
842 #[case(AxTimeInForce::Ioc, "\"IOC\"")]
843 #[case(AxTimeInForce::Day, "\"DAY\"")]
844 #[case(AxTimeInForce::Gtd, "\"GTD\"")]
845 #[case(AxTimeInForce::Fok, "\"FOK\"")]
846 #[case(AxTimeInForce::Ato, "\"ATO\"")]
847 #[case(AxTimeInForce::Atc, "\"ATC\"")]
848 fn test_time_in_force_serialization(#[case] tif: AxTimeInForce, #[case] expected: &str) {
849 let json = serde_json::to_string(&tif).unwrap();
850 assert_eq!(json, expected);
851
852 let parsed: AxTimeInForce = serde_json::from_str(&json).unwrap();
853 assert_eq!(parsed, tif);
854 }
855
856 #[rstest]
857 #[case(AxOrderType::Market, "\"MARKET\"")]
858 #[case(AxOrderType::Limit, "\"LIMIT\"")]
859 #[case(AxOrderType::StopLossLimit, "\"STOP_LOSS_LIMIT\"")]
860 #[case(AxOrderType::TakeProfitLimit, "\"TAKE_PROFIT_LIMIT\"")]
861 fn test_order_type_serialization(#[case] order_type: AxOrderType, #[case] expected: &str) {
862 let json = serde_json::to_string(&order_type).unwrap();
863 assert_eq!(json, expected);
864
865 let parsed: AxOrderType = serde_json::from_str(&json).unwrap();
866 assert_eq!(parsed, order_type);
867 }
868
869 #[rstest]
870 #[case(AxMarketDataLevel::Level1, "\"LEVEL_1\"")]
871 #[case(AxMarketDataLevel::Level2, "\"LEVEL_2\"")]
872 #[case(AxMarketDataLevel::Level3, "\"LEVEL_3\"")]
873 fn test_market_data_level_serialization(
874 #[case] level: AxMarketDataLevel,
875 #[case] expected: &str,
876 ) {
877 let json = serde_json::to_string(&level).unwrap();
878 assert_eq!(json, expected);
879
880 let parsed: AxMarketDataLevel = serde_json::from_str(&json).unwrap();
881 assert_eq!(parsed, level);
882 }
883
884 #[rstest]
885 #[case(AxCandleWidth::Seconds1, "\"1s\"")]
886 #[case(AxCandleWidth::Minutes1, "\"1m\"")]
887 #[case(AxCandleWidth::Minutes5, "\"5m\"")]
888 #[case(AxCandleWidth::Hours1, "\"1h\"")]
889 #[case(AxCandleWidth::Days1, "\"1d\"")]
890 fn test_candle_width_serialization(#[case] width: AxCandleWidth, #[case] expected: &str) {
891 let json = serde_json::to_string(&width).unwrap();
892 assert_eq!(json, expected);
893
894 let parsed: AxCandleWidth = serde_json::from_str(&json).unwrap();
895 assert_eq!(parsed, width);
896 }
897
898 #[rstest]
899 #[case(AxMdWsMessageType::Heartbeat, "\"h\"")]
900 #[case(AxMdWsMessageType::Ticker, "\"s\"")]
901 #[case(AxMdWsMessageType::Trade, "\"t\"")]
902 #[case(AxMdWsMessageType::Candle, "\"c\"")]
903 #[case(AxMdWsMessageType::BookLevel1, "\"1\"")]
904 #[case(AxMdWsMessageType::BookLevel2, "\"2\"")]
905 #[case(AxMdWsMessageType::BookLevel3, "\"3\"")]
906 fn test_md_ws_message_type_serialization(
907 #[case] msg_type: AxMdWsMessageType,
908 #[case] expected: &str,
909 ) {
910 let json = serde_json::to_string(&msg_type).unwrap();
911 assert_eq!(json, expected);
912
913 let parsed: AxMdWsMessageType = serde_json::from_str(&json).unwrap();
914 assert_eq!(parsed, msg_type);
915 }
916
917 #[rstest]
918 #[case(AxOrderWsMessageType::Heartbeat, "\"h\"")]
919 #[case(AxOrderWsMessageType::OrderAcknowledged, "\"n\"")]
920 #[case(AxOrderWsMessageType::OrderCanceled, "\"c\"")]
921 #[case(AxOrderWsMessageType::OrderFilled, "\"f\"")]
922 #[case(AxOrderWsMessageType::OrderPartiallyFilled, "\"p\"")]
923 fn test_order_ws_message_type_serialization(
924 #[case] msg_type: AxOrderWsMessageType,
925 #[case] expected: &str,
926 ) {
927 let json = serde_json::to_string(&msg_type).unwrap();
928 assert_eq!(json, expected);
929
930 let parsed: AxOrderWsMessageType = serde_json::from_str(&json).unwrap();
931 assert_eq!(parsed, msg_type);
932 }
933
934 #[rstest]
935 #[case(AxMdRequestType::Subscribe, "\"subscribe\"")]
936 #[case(AxMdRequestType::Unsubscribe, "\"unsubscribe\"")]
937 #[case(AxMdRequestType::SubscribeCandles, "\"subscribe_candles\"")]
938 #[case(AxMdRequestType::UnsubscribeCandles, "\"unsubscribe_candles\"")]
939 fn test_md_request_type_serialization(
940 #[case] request_type: AxMdRequestType,
941 #[case] expected: &str,
942 ) {
943 let json = serde_json::to_string(&request_type).unwrap();
944 assert_eq!(json, expected);
945
946 let parsed: AxMdRequestType = serde_json::from_str(&json).unwrap();
947 assert_eq!(parsed, request_type);
948 }
949
950 #[rstest]
951 #[case(AxOrderRequestType::PlaceOrder, "\"p\"")]
952 #[case(AxOrderRequestType::CancelOrder, "\"x\"")]
953 #[case(AxOrderRequestType::GetOpenOrders, "\"o\"")]
954 fn test_order_request_type_serialization(
955 #[case] request_type: AxOrderRequestType,
956 #[case] expected: &str,
957 ) {
958 let json = serde_json::to_string(&request_type).unwrap();
959 assert_eq!(json, expected);
960
961 let parsed: AxOrderRequestType = serde_json::from_str(&json).unwrap();
962 assert_eq!(parsed, request_type);
963 }
964}