1use std::{borrow::Cow, fmt::Display, marker::PhantomData, str::FromStr};
19
20use serde::{Deserialize, Deserializer, Serialize, Serializer};
21use strum::{AsRefStr, Display, EnumIter, EnumString, FromRepr};
22
23use crate::enum_strum_serde;
24
25pub trait FromU8 {
27 fn from_u8(value: u8) -> Option<Self>
31 where
32 Self: Sized;
33}
34
35pub trait FromU16 {
37 fn from_u16(value: u16) -> Option<Self>
41 where
42 Self: Sized;
43}
44
45#[repr(C)]
47#[derive(
48 Copy,
49 Clone,
50 Debug,
51 Display,
52 Hash,
53 PartialEq,
54 Eq,
55 PartialOrd,
56 Ord,
57 AsRefStr,
58 FromRepr,
59 EnumIter,
60 EnumString,
61)]
62#[strum(ascii_case_insensitive)]
63#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
64#[cfg_attr(
65 feature = "python",
66 pyo3::pyclass(
67 frozen,
68 eq,
69 eq_int,
70 module = "nautilus_trader.model",
71 from_py_object,
72 rename_all = "SCREAMING_SNAKE_CASE",
73 )
74)]
75#[cfg_attr(
76 feature = "python",
77 pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.model")
78)]
79pub enum AccountType {
80 Cash = 1,
82 Margin = 2,
84 Betting = 3,
86 Wallet = 4,
88}
89
90#[repr(C)]
92#[derive(
93 Copy,
94 Clone,
95 Debug,
96 Display,
97 Hash,
98 PartialEq,
99 Eq,
100 PartialOrd,
101 Ord,
102 AsRefStr,
103 FromRepr,
104 EnumIter,
105 EnumString,
106)]
107#[strum(ascii_case_insensitive)]
108#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
109#[cfg_attr(
110 feature = "python",
111 pyo3::pyclass(
112 frozen,
113 eq,
114 eq_int,
115 module = "nautilus_trader.model",
116 from_py_object,
117 rename_all = "SCREAMING_SNAKE_CASE",
118 )
119)]
120#[cfg_attr(
121 feature = "python",
122 pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.model")
123)]
124pub enum AggregationSource {
125 External = 1,
127 Internal = 2,
129}
130
131#[repr(C)]
133#[derive(
134 Copy,
135 Clone,
136 Debug,
137 Default,
138 Display,
139 Hash,
140 PartialEq,
141 Eq,
142 PartialOrd,
143 Ord,
144 AsRefStr,
145 FromRepr,
146 EnumIter,
147 EnumString,
148)]
149#[strum(ascii_case_insensitive)]
150#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
151#[cfg_attr(
152 feature = "python",
153 pyo3::pyclass(
154 frozen,
155 eq,
156 eq_int,
157 module = "nautilus_trader.model",
158 from_py_object,
159 rename_all = "SCREAMING_SNAKE_CASE",
160 )
161)]
162#[cfg_attr(
163 feature = "python",
164 pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.model")
165)]
166pub enum AggressorSide {
167 #[default]
169 NoAggressor = 0,
170 #[strum(serialize = "BUYER", to_string = "BUY")]
174 Buy = 1,
175 #[strum(serialize = "SELLER", to_string = "SELL")]
179 Sell = 2,
180}
181
182impl FromU8 for AggressorSide {
183 fn from_u8(value: u8) -> Option<Self> {
184 match value {
185 0 => Some(Self::NoAggressor),
186 1 => Some(Self::Buy),
187 2 => Some(Self::Sell),
188 _ => None,
189 }
190 }
191}
192
193#[repr(C)]
195#[derive(
196 Copy,
197 Clone,
198 Debug,
199 Display,
200 Hash,
201 PartialEq,
202 Eq,
203 PartialOrd,
204 Ord,
205 AsRefStr,
206 FromRepr,
207 EnumIter,
208 EnumString,
209)]
210#[strum(ascii_case_insensitive)]
211#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
212#[cfg_attr(
213 feature = "python",
214 pyo3::pyclass(
215 frozen,
216 eq,
217 eq_int,
218 module = "nautilus_trader.model",
219 from_py_object,
220 rename_all = "SCREAMING_SNAKE_CASE",
221 )
222)]
223#[cfg_attr(
224 feature = "python",
225 pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.model")
226)]
227#[allow(non_camel_case_types)]
228pub enum AssetClass {
229 FX = 1,
231 Equity = 2,
233 Commodity = 3,
235 Debt = 4,
237 Index = 5,
239 Cryptocurrency = 6,
241 Alternative = 7,
243}
244
245impl FromU8 for AssetClass {
246 fn from_u8(value: u8) -> Option<Self> {
247 match value {
248 1 => Some(Self::FX),
249 2 => Some(Self::Equity),
250 3 => Some(Self::Commodity),
251 4 => Some(Self::Debt),
252 5 => Some(Self::Index),
253 6 => Some(Self::Cryptocurrency),
254 7 => Some(Self::Alternative),
255 _ => None,
256 }
257 }
258}
259
260#[repr(C)]
262#[derive(
263 Copy,
264 Clone,
265 Debug,
266 Display,
267 Hash,
268 PartialEq,
269 Eq,
270 PartialOrd,
271 Ord,
272 AsRefStr,
273 FromRepr,
274 EnumIter,
275 EnumString,
276)]
277#[strum(ascii_case_insensitive)]
278#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
279#[cfg_attr(
280 feature = "python",
281 pyo3::pyclass(
282 frozen,
283 eq,
284 eq_int,
285 module = "nautilus_trader.model",
286 from_py_object,
287 rename_all = "SCREAMING_SNAKE_CASE",
288 )
289)]
290#[cfg_attr(
291 feature = "python",
292 pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.model")
293)]
294pub enum BarAggregation {
295 Tick = 1,
297 TickImbalance = 2,
299 TickRuns = 3,
301 Volume = 4,
303 VolumeImbalance = 5,
305 VolumeRuns = 6,
307 Value = 7,
309 ValueImbalance = 8,
311 ValueRuns = 9,
313 Millisecond = 10,
315 Second = 11,
317 Minute = 12,
319 Hour = 13,
321 Day = 14,
323 Week = 15,
325 Month = 16,
327 Year = 17,
329 Renko = 18,
331}
332
333#[repr(C)]
335#[derive(
336 Copy,
337 Clone,
338 Debug,
339 Default,
340 Display,
341 Hash,
342 PartialEq,
343 Eq,
344 PartialOrd,
345 Ord,
346 AsRefStr,
347 FromRepr,
348 EnumIter,
349 EnumString,
350)]
351#[strum(ascii_case_insensitive)]
352#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
353#[cfg_attr(
354 feature = "python",
355 pyo3::pyclass(
356 frozen,
357 eq,
358 eq_int,
359 module = "nautilus_trader.model",
360 from_py_object,
361 rename_all = "SCREAMING_SNAKE_CASE",
362 )
363)]
364#[cfg_attr(
365 feature = "python",
366 pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.model")
367)]
368pub enum BarIntervalType {
369 #[default]
371 LeftOpen = 1,
372 RightOpen = 2,
374}
375
376#[repr(C)]
378#[derive(
379 Copy,
380 Clone,
381 Debug,
382 Display,
383 Hash,
384 PartialEq,
385 Eq,
386 PartialOrd,
387 Ord,
388 AsRefStr,
389 FromRepr,
390 EnumIter,
391 EnumString,
392)]
393#[strum(ascii_case_insensitive)]
394#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
395#[cfg_attr(
396 feature = "python",
397 pyo3::pyclass(
398 frozen,
399 eq,
400 eq_int,
401 module = "nautilus_trader.model",
402 from_py_object,
403 rename_all = "SCREAMING_SNAKE_CASE",
404 )
405)]
406#[cfg_attr(
407 feature = "python",
408 pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.model")
409)]
410pub enum BetSide {
411 Back = 1,
413 Lay = 2,
415}
416
417impl BetSide {
418 #[must_use]
420 pub fn opposite(&self) -> Self {
421 match self {
422 Self::Back => Self::Lay,
423 Self::Lay => Self::Back,
424 }
425 }
426}
427
428impl From<OrderSide> for BetSide {
429 fn from(side: OrderSide) -> Self {
430 match side {
431 OrderSide::Buy => Self::Back,
432 OrderSide::Sell => Self::Lay,
433 }
434 }
435}
436
437#[repr(C)]
439#[derive(
440 Copy,
441 Clone,
442 Debug,
443 Display,
444 Hash,
445 PartialEq,
446 Eq,
447 PartialOrd,
448 Ord,
449 AsRefStr,
450 FromRepr,
451 EnumIter,
452 EnumString,
453)]
454#[strum(ascii_case_insensitive)]
455#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
456#[cfg_attr(
457 feature = "python",
458 pyo3::pyclass(
459 frozen,
460 eq,
461 eq_int,
462 module = "nautilus_trader.model",
463 from_py_object,
464 rename_all = "SCREAMING_SNAKE_CASE",
465 )
466)]
467#[cfg_attr(
468 feature = "python",
469 pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.model")
470)]
471pub enum BookAction {
472 Add = 1,
474 Update = 2,
476 Delete = 3,
478 Clear = 4,
480}
481
482impl FromU8 for BookAction {
483 fn from_u8(value: u8) -> Option<Self> {
484 match value {
485 1 => Some(Self::Add),
486 2 => Some(Self::Update),
487 3 => Some(Self::Delete),
488 4 => Some(Self::Clear),
489 _ => None,
490 }
491 }
492}
493
494#[repr(C)]
496#[derive(
497 Copy,
498 Clone,
499 Debug,
500 Display,
501 Hash,
502 PartialEq,
503 Eq,
504 PartialOrd,
505 Ord,
506 AsRefStr,
507 FromRepr,
508 EnumIter,
509 EnumString,
510)]
511#[strum(ascii_case_insensitive)]
512#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
513#[cfg_attr(
514 feature = "python",
515 pyo3::pyclass(
516 frozen,
517 eq,
518 eq_int,
519 module = "nautilus_trader.model",
520 from_py_object,
521 rename_all = "SCREAMING_SNAKE_CASE",
522 )
523)]
524#[cfg_attr(
525 feature = "python",
526 pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.model")
527)]
528#[allow(non_camel_case_types)]
529pub enum BookType {
530 L1_MBP = 1,
532 L2_MBP = 2,
534 L3_MBO = 3,
536}
537
538impl FromU8 for BookType {
539 fn from_u8(value: u8) -> Option<Self> {
540 match value {
541 1 => Some(Self::L1_MBP),
542 2 => Some(Self::L2_MBP),
543 3 => Some(Self::L3_MBO),
544 _ => None,
545 }
546 }
547}
548
549#[repr(C)]
556#[derive(
557 Copy,
558 Clone,
559 Debug,
560 Display,
561 Hash,
562 PartialEq,
563 Eq,
564 PartialOrd,
565 Ord,
566 AsRefStr,
567 FromRepr,
568 EnumIter,
569 EnumString,
570)]
571#[strum(ascii_case_insensitive)]
572#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
573#[cfg_attr(
574 feature = "python",
575 pyo3::pyclass(
576 frozen,
577 eq,
578 eq_int,
579 module = "nautilus_trader.model",
580 from_py_object,
581 rename_all = "SCREAMING_SNAKE_CASE",
582 )
583)]
584#[cfg_attr(
585 feature = "python",
586 pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.model")
587)]
588pub enum ContingencyType {
589 Oco = 1,
591 Oto = 2,
593 Ouo = 3,
595}
596
597#[repr(C)]
611#[derive(
612 Copy,
613 Clone,
614 Debug,
615 Default,
616 Display,
617 Hash,
618 PartialEq,
619 Eq,
620 PartialOrd,
621 Ord,
622 AsRefStr,
623 FromRepr,
624 EnumIter,
625 EnumString,
626)]
627#[strum(ascii_case_insensitive)]
628#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
629#[cfg_attr(
630 feature = "python",
631 pyo3::pyclass(
632 frozen,
633 eq,
634 eq_int,
635 module = "nautilus_trader.model",
636 from_py_object,
637 rename_all = "SCREAMING_SNAKE_CASE",
638 )
639)]
640#[cfg_attr(
641 feature = "python",
642 pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.model")
643)]
644pub enum ContinuousFutureAdjustmentType {
645 #[default]
647 BackwardSpread = 1,
648 ForwardSpread = 2,
650 BackwardRatio = 3,
652 ForwardRatio = 4,
654}
655
656impl ContinuousFutureAdjustmentType {
657 #[must_use]
659 pub const fn is_ratio(&self) -> bool {
660 matches!(self, Self::BackwardRatio | Self::ForwardRatio)
661 }
662
663 #[must_use]
665 pub const fn is_backward(&self) -> bool {
666 matches!(self, Self::BackwardSpread | Self::BackwardRatio)
667 }
668}
669
670#[repr(C)]
672#[derive(
673 Copy,
674 Clone,
675 Debug,
676 Display,
677 Hash,
678 PartialEq,
679 Eq,
680 PartialOrd,
681 Ord,
682 AsRefStr,
683 FromRepr,
684 EnumIter,
685 EnumString,
686)]
687#[strum(ascii_case_insensitive)]
688#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
689#[cfg_attr(
690 feature = "python",
691 pyo3::pyclass(
692 frozen,
693 eq,
694 eq_int,
695 module = "nautilus_trader.model",
696 from_py_object,
697 rename_all = "SCREAMING_SNAKE_CASE",
698 )
699)]
700#[cfg_attr(
701 feature = "python",
702 pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.model")
703)]
704pub enum CurrencyType {
705 Crypto = 1,
707 Fiat = 2,
709 CommodityBacked = 3,
711}
712
713#[repr(C)]
715#[derive(
716 Copy,
717 Clone,
718 Debug,
719 Display,
720 Hash,
721 PartialEq,
722 Eq,
723 PartialOrd,
724 Ord,
725 AsRefStr,
726 FromRepr,
727 EnumIter,
728 EnumString,
729)]
730#[strum(ascii_case_insensitive)]
731#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
732#[cfg_attr(
733 feature = "python",
734 pyo3::pyclass(
735 frozen,
736 eq,
737 eq_int,
738 module = "nautilus_trader.model",
739 from_py_object,
740 rename_all = "SCREAMING_SNAKE_CASE",
741 )
742)]
743#[cfg_attr(
744 feature = "python",
745 pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.model")
746)]
747pub enum InstrumentClass {
748 Spot = 1,
750 Swap = 2,
752 Future = 3,
754 FuturesSpread = 4,
756 Forward = 5,
758 Cfd = 6,
760 Bond = 7,
762 Option = 8,
764 OptionSpread = 9,
766 Warrant = 10,
768 SportsBetting = 11,
770 BinaryOption = 12,
772}
773
774impl InstrumentClass {
775 #[must_use]
777 pub const fn has_expiration(&self) -> bool {
778 matches!(
779 self,
780 Self::Future | Self::FuturesSpread | Self::Option | Self::OptionSpread
781 )
782 }
783
784 #[must_use]
786 pub const fn allows_negative_price(&self) -> bool {
787 matches!(
788 self,
789 Self::Option | Self::FuturesSpread | Self::OptionSpread
790 )
791 }
792
793 #[must_use]
798 pub fn try_from_parent_suffix(suffix: &str) -> Option<Self> {
799 match suffix {
800 "FUT" | "FUTURE" => Some(Self::Future),
801 "OPT" | "OPTION" => Some(Self::Option),
802 _ => None,
803 }
804 }
805
806 #[must_use]
811 pub const fn parent_suffix(self) -> Option<&'static str> {
812 match self {
813 Self::Future => Some("FUT"),
814 Self::Option => Some("OPT"),
815 _ => None,
816 }
817 }
818}
819
820#[repr(C)]
822#[derive(
823 Copy,
824 Clone,
825 Debug,
826 Display,
827 Hash,
828 PartialEq,
829 Eq,
830 PartialOrd,
831 Ord,
832 AsRefStr,
833 FromRepr,
834 EnumIter,
835 EnumString,
836)]
837#[strum(ascii_case_insensitive)]
838#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
839#[cfg_attr(
840 feature = "python",
841 pyo3::pyclass(
842 frozen,
843 eq,
844 eq_int,
845 module = "nautilus_trader.model",
846 from_py_object,
847 rename_all = "SCREAMING_SNAKE_CASE",
848 )
849)]
850#[cfg_attr(
851 feature = "python",
852 pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.model")
853)]
854pub enum InstrumentCloseType {
855 EndOfSession = 1,
857 ContractExpired = 2,
859}
860
861impl FromU8 for InstrumentCloseType {
863 fn from_u8(value: u8) -> Option<Self> {
864 match value {
865 1 => Some(Self::EndOfSession),
866 2 => Some(Self::ContractExpired),
867 _ => None,
868 }
869 }
870}
871
872#[repr(C)]
874#[derive(
875 Copy,
876 Clone,
877 Debug,
878 Display,
879 Hash,
880 PartialEq,
881 Eq,
882 PartialOrd,
883 Ord,
884 AsRefStr,
885 FromRepr,
886 EnumIter,
887 EnumString,
888)]
889#[strum(ascii_case_insensitive)]
890#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
891#[cfg_attr(
892 feature = "python",
893 pyo3::pyclass(
894 frozen,
895 eq,
896 eq_int,
897 module = "nautilus_trader.model",
898 from_py_object,
899 rename_all = "SCREAMING_SNAKE_CASE",
900 )
901)]
902#[cfg_attr(
903 feature = "python",
904 pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.model")
905)]
906pub enum LiquiditySide {
907 NoLiquiditySide = 0,
909 Maker = 1,
911 Taker = 2,
913}
914
915#[repr(C)]
917#[derive(
918 Copy,
919 Clone,
920 Debug,
921 Display,
922 Hash,
923 PartialEq,
924 Eq,
925 PartialOrd,
926 Ord,
927 AsRefStr,
928 FromRepr,
929 EnumIter,
930 EnumString,
931)]
932#[strum(ascii_case_insensitive)]
933#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
934#[cfg_attr(
935 feature = "python",
936 pyo3::pyclass(
937 frozen,
938 eq,
939 eq_int,
940 module = "nautilus_trader.model",
941 from_py_object,
942 rename_all = "SCREAMING_SNAKE_CASE",
943 )
944)]
945#[cfg_attr(
946 feature = "python",
947 pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.model")
948)]
949pub enum MarketStatus {
950 Open = 1,
952 Closed = 2,
954 Paused = 3,
956 Halted = 4,
958 Suspended = 5,
960 NotAvailable = 6,
962}
963
964#[repr(C)]
966#[derive(
967 Copy,
968 Clone,
969 Debug,
970 Display,
971 Hash,
972 PartialEq,
973 Eq,
974 PartialOrd,
975 Ord,
976 AsRefStr,
977 FromRepr,
978 EnumIter,
979 EnumString,
980)]
981#[strum(ascii_case_insensitive)]
982#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
983#[cfg_attr(
984 feature = "python",
985 pyo3::pyclass(
986 frozen,
987 eq,
988 eq_int,
989 module = "nautilus_trader.model",
990 from_py_object,
991 rename_all = "SCREAMING_SNAKE_CASE",
992 )
993)]
994#[cfg_attr(
995 feature = "python",
996 pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.model")
997)]
998pub enum MarketStatusAction {
999 None = 0,
1001 PreOpen = 1,
1003 PreCross = 2,
1005 Quoting = 3,
1007 Cross = 4,
1009 Rotation = 5,
1011 NewPriceIndication = 6,
1013 Trading = 7,
1015 Halt = 8,
1017 Pause = 9,
1019 Suspend = 10,
1021 PreClose = 11,
1023 Close = 12,
1025 PostClose = 13,
1027 ShortSellRestrictionChange = 14,
1029 NotAvailableForTrading = 15,
1031}
1032
1033impl FromU16 for MarketStatusAction {
1035 fn from_u16(value: u16) -> Option<Self> {
1036 match value {
1037 0 => Some(Self::None),
1038 1 => Some(Self::PreOpen),
1039 2 => Some(Self::PreCross),
1040 3 => Some(Self::Quoting),
1041 4 => Some(Self::Cross),
1042 5 => Some(Self::Rotation),
1043 6 => Some(Self::NewPriceIndication),
1044 7 => Some(Self::Trading),
1045 8 => Some(Self::Halt),
1046 9 => Some(Self::Pause),
1047 10 => Some(Self::Suspend),
1048 11 => Some(Self::PreClose),
1049 12 => Some(Self::Close),
1050 13 => Some(Self::PostClose),
1051 14 => Some(Self::ShortSellRestrictionChange),
1052 15 => Some(Self::NotAvailableForTrading),
1053 _ => None,
1054 }
1055 }
1056}
1057
1058#[repr(C)]
1060#[derive(
1061 Copy,
1062 Clone,
1063 Debug,
1064 Default,
1065 Display,
1066 Hash,
1067 PartialEq,
1068 Eq,
1069 PartialOrd,
1070 Ord,
1071 AsRefStr,
1072 FromRepr,
1073 EnumIter,
1074 EnumString,
1075)]
1076#[strum(ascii_case_insensitive)]
1077#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
1078#[cfg_attr(
1079 feature = "python",
1080 pyo3::pyclass(
1081 frozen,
1082 eq,
1083 eq_int,
1084 module = "nautilus_trader.model",
1085 from_py_object,
1086 rename_all = "SCREAMING_SNAKE_CASE",
1087 )
1088)]
1089#[cfg_attr(
1090 feature = "python",
1091 pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.model")
1092)]
1093pub enum OmsType {
1094 #[default]
1096 Unspecified = 0,
1097 Netting = 1,
1099 Hedging = 2,
1103}
1104
1105#[repr(C)]
1107#[derive(
1108 Copy,
1109 Clone,
1110 Debug,
1111 Display,
1112 Hash,
1113 PartialEq,
1114 Eq,
1115 PartialOrd,
1116 Ord,
1117 AsRefStr,
1118 FromRepr,
1119 EnumIter,
1120 EnumString,
1121)]
1122#[strum(ascii_case_insensitive)]
1123#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
1124#[cfg_attr(
1125 feature = "python",
1126 pyo3::pyclass(
1127 frozen,
1128 eq,
1129 eq_int,
1130 module = "nautilus_trader.model",
1131 from_py_object,
1132 rename_all = "SCREAMING_SNAKE_CASE",
1133 )
1134)]
1135#[cfg_attr(
1136 feature = "python",
1137 pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.model")
1138)]
1139pub enum OptionKind {
1140 Call = 1,
1142 Put = 2,
1144}
1145
1146#[repr(C)]
1158#[derive(
1159 Copy,
1160 Clone,
1161 Debug,
1162 Default,
1163 Display,
1164 Hash,
1165 PartialEq,
1166 Eq,
1167 PartialOrd,
1168 Ord,
1169 AsRefStr,
1170 FromRepr,
1171 EnumIter,
1172 EnumString,
1173)]
1174#[strum(ascii_case_insensitive)]
1175#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
1176#[cfg_attr(
1177 feature = "python",
1178 pyo3::pyclass(
1179 frozen,
1180 eq,
1181 eq_int,
1182 module = "nautilus_trader.model",
1183 from_py_object,
1184 rename_all = "SCREAMING_SNAKE_CASE",
1185 )
1186)]
1187#[cfg_attr(
1188 feature = "python",
1189 pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.model")
1190)]
1191pub enum GreeksConvention {
1192 #[default]
1194 BlackScholes = 1,
1195 PriceAdjusted = 2,
1197}
1198
1199#[repr(C)]
1201#[derive(
1202 Copy,
1203 Clone,
1204 Debug,
1205 Default,
1206 Display,
1207 Hash,
1208 PartialEq,
1209 Eq,
1210 PartialOrd,
1211 Ord,
1212 AsRefStr,
1213 FromRepr,
1214 EnumIter,
1215 EnumString,
1216)]
1217#[strum(ascii_case_insensitive)]
1218#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
1219#[cfg_attr(
1220 feature = "python",
1221 pyo3::pyclass(
1222 frozen,
1223 eq,
1224 eq_int,
1225 module = "nautilus_trader.model",
1226 from_py_object,
1227 rename_all = "SCREAMING_SNAKE_CASE",
1228 )
1229)]
1230#[cfg_attr(
1231 feature = "python",
1232 pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.model")
1233)]
1234pub enum OtoTriggerMode {
1235 #[default]
1237 Partial = 0,
1238 Full = 1,
1240}
1241
1242#[repr(C)]
1247#[derive(
1248 Copy,
1249 Clone,
1250 Debug,
1251 Display,
1252 Hash,
1253 PartialEq,
1254 Eq,
1255 PartialOrd,
1256 Ord,
1257 AsRefStr,
1258 FromRepr,
1259 EnumIter,
1260 EnumString,
1261)]
1262#[strum(ascii_case_insensitive)]
1263#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
1264#[cfg_attr(
1265 feature = "python",
1266 pyo3::pyclass(
1267 frozen,
1268 eq,
1269 eq_int,
1270 module = "nautilus_trader.model",
1271 from_py_object,
1272 rename_all = "SCREAMING_SNAKE_CASE",
1273 )
1274)]
1275#[cfg_attr(
1276 feature = "python",
1277 pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.model")
1278)]
1279pub enum OrderSide {
1280 Buy = 1,
1282 Sell = 2,
1284}
1285
1286impl OrderSide {
1287 #[must_use]
1289 pub fn opposite(&self) -> Self {
1290 match &self {
1291 Self::Buy => Self::Sell,
1292 Self::Sell => Self::Buy,
1293 }
1294 }
1295}
1296
1297#[repr(C)]
1319#[derive(
1320 Copy,
1321 Clone,
1322 Debug,
1323 Display,
1324 Hash,
1325 PartialEq,
1326 Eq,
1327 PartialOrd,
1328 Ord,
1329 AsRefStr,
1330 FromRepr,
1331 EnumIter,
1332 EnumString,
1333)]
1334#[strum(ascii_case_insensitive)]
1335#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
1336#[cfg_attr(
1337 feature = "python",
1338 pyo3::pyclass(
1339 frozen,
1340 eq,
1341 eq_int,
1342 module = "nautilus_trader.model",
1343 from_py_object,
1344 rename_all = "SCREAMING_SNAKE_CASE",
1345 )
1346)]
1347#[cfg_attr(
1348 feature = "python",
1349 pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.model")
1350)]
1351pub enum OrderStatus {
1352 Initialized = 1,
1354 Denied = 2,
1356 Emulated = 3,
1358 Released = 4,
1360 Submitted = 5,
1362 Accepted = 6,
1364 Rejected = 7,
1366 Canceled = 8,
1368 Expired = 9,
1370 Triggered = 10,
1372 PendingUpdate = 11,
1374 PendingCancel = 12,
1376 PartiallyFilled = 13,
1378 Filled = 14,
1380 Voided = 15,
1382}
1383
1384impl OrderStatus {
1385 #[must_use]
1387 pub const fn is_open(self) -> bool {
1388 matches!(
1389 self,
1390 Self::Submitted
1391 | Self::Accepted
1392 | Self::Triggered
1393 | Self::PendingUpdate
1394 | Self::PendingCancel
1395 | Self::PartiallyFilled
1396 )
1397 }
1398
1399 #[must_use]
1401 pub const fn is_closed(self) -> bool {
1402 matches!(
1403 self,
1404 Self::Denied
1405 | Self::Rejected
1406 | Self::Canceled
1407 | Self::Expired
1408 | Self::Filled
1409 | Self::Voided
1410 )
1411 }
1412
1413 #[must_use]
1415 pub const fn is_cancellable(self) -> bool {
1416 matches!(
1417 self,
1418 Self::Accepted | Self::Triggered | Self::PendingUpdate | Self::PartiallyFilled
1419 )
1420 }
1421}
1422
1423#[repr(C)]
1425#[derive(
1426 Copy,
1427 Clone,
1428 Debug,
1429 Display,
1430 Hash,
1431 PartialEq,
1432 Eq,
1433 PartialOrd,
1434 Ord,
1435 AsRefStr,
1436 FromRepr,
1437 EnumIter,
1438 EnumString,
1439)]
1440#[strum(ascii_case_insensitive)]
1441#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
1442#[cfg_attr(
1443 feature = "python",
1444 pyo3::pyclass(
1445 frozen,
1446 eq,
1447 eq_int,
1448 module = "nautilus_trader.model",
1449 from_py_object,
1450 rename_all = "SCREAMING_SNAKE_CASE",
1451 )
1452)]
1453#[cfg_attr(
1454 feature = "python",
1455 pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.model")
1456)]
1457pub enum OrderType {
1458 Market = 1,
1460 Limit = 2,
1462 StopMarket = 3,
1464 StopLimit = 4,
1466 MarketToLimit = 5,
1468 MarketIfTouched = 6,
1470 LimitIfTouched = 7,
1472 TrailingStopMarket = 8,
1474 TrailingStopLimit = 9,
1476}
1477
1478#[repr(C)]
1480#[derive(
1481 Copy,
1482 Clone,
1483 Debug,
1484 Display,
1485 Hash,
1486 PartialEq,
1487 Eq,
1488 PartialOrd,
1489 Ord,
1490 AsRefStr,
1491 FromRepr,
1492 EnumIter,
1493 EnumString,
1494)]
1495#[strum(ascii_case_insensitive)]
1496#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
1497#[cfg_attr(
1498 feature = "python",
1499 pyo3::pyclass(
1500 frozen,
1501 eq,
1502 eq_int,
1503 module = "nautilus_trader.model",
1504 from_py_object,
1505 rename_all = "SCREAMING_SNAKE_CASE",
1506 )
1507)]
1508#[cfg_attr(
1509 feature = "python",
1510 pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.model")
1511)]
1512pub enum PositionAdjustmentType {
1513 Commission = 1,
1515 Funding = 2,
1517}
1518
1519impl FromU8 for PositionAdjustmentType {
1520 fn from_u8(value: u8) -> Option<Self> {
1521 match value {
1522 1 => Some(Self::Commission),
1523 2 => Some(Self::Funding),
1524 _ => None,
1525 }
1526 }
1527}
1528
1529#[repr(C)]
1534#[derive(
1535 Copy,
1536 Clone,
1537 Debug,
1538 Display,
1539 Hash,
1540 PartialEq,
1541 Eq,
1542 PartialOrd,
1543 Ord,
1544 AsRefStr,
1545 FromRepr,
1546 EnumIter,
1547 EnumString,
1548)]
1549#[strum(ascii_case_insensitive)]
1550#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
1551#[cfg_attr(
1552 feature = "python",
1553 pyo3::pyclass(
1554 frozen,
1555 eq,
1556 eq_int,
1557 module = "nautilus_trader.model",
1558 from_py_object,
1559 rename_all = "SCREAMING_SNAKE_CASE",
1560 )
1561)]
1562#[cfg_attr(
1563 feature = "python",
1564 pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.model")
1565)]
1566pub enum PositionSide {
1567 Flat = 1,
1569 Long = 2,
1571 Short = 3,
1573}
1574
1575pub mod serde_option_order_side {
1577 use serde::{Deserializer, Serializer};
1578
1579 use super::{OrderSide, deserialize_optional_enum, serialize_optional_enum};
1580
1581 pub fn serialize<S>(value: &Option<OrderSide>, serializer: S) -> Result<S::Ok, S::Error>
1587 where
1588 S: Serializer,
1589 {
1590 serialize_optional_enum(value.as_ref(), serializer, "NO_ORDER_SIDE")
1591 }
1592
1593 pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<OrderSide>, D::Error>
1599 where
1600 D: Deserializer<'de>,
1601 {
1602 deserialize_optional_enum(
1603 deserializer,
1604 "NO_ORDER_SIDE",
1605 "BUY, SELL, NO_ORDER_SIDE, or null",
1606 )
1607 }
1608}
1609
1610pub mod serde_option_position_side {
1612 use serde::{Deserializer, Serializer};
1613
1614 use super::{PositionSide, deserialize_optional_enum, serialize_optional_enum};
1615
1616 pub fn serialize<S>(value: &Option<PositionSide>, serializer: S) -> Result<S::Ok, S::Error>
1622 where
1623 S: Serializer,
1624 {
1625 serialize_optional_enum(value.as_ref(), serializer, "NO_POSITION_SIDE")
1626 }
1627
1628 pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<PositionSide>, D::Error>
1634 where
1635 D: Deserializer<'de>,
1636 {
1637 deserialize_optional_enum(
1638 deserializer,
1639 "NO_POSITION_SIDE",
1640 "FLAT, LONG, SHORT, NO_POSITION_SIDE, or null",
1641 )
1642 }
1643}
1644
1645pub mod serde_option_contingency_type {
1647 use serde::{Deserializer, Serializer};
1648
1649 use super::{ContingencyType, deserialize_optional_enum, serialize_optional_enum};
1650
1651 pub fn serialize<S>(value: &Option<ContingencyType>, serializer: S) -> Result<S::Ok, S::Error>
1657 where
1658 S: Serializer,
1659 {
1660 serialize_optional_enum(value.as_ref(), serializer, "NO_CONTINGENCY")
1661 }
1662
1663 pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<ContingencyType>, D::Error>
1669 where
1670 D: Deserializer<'de>,
1671 {
1672 deserialize_optional_enum(
1673 deserializer,
1674 "NO_CONTINGENCY",
1675 "OCO, OTO, OUO, NO_CONTINGENCY, or null",
1676 )
1677 }
1678}
1679
1680pub mod serde_option_trailing_offset_type {
1683 use serde::{Deserializer, Serializer};
1684
1685 use super::{TrailingOffsetType, deserialize_optional_enum, serialize_optional_enum};
1686
1687 pub fn serialize<S>(
1693 value: &Option<TrailingOffsetType>,
1694 serializer: S,
1695 ) -> Result<S::Ok, S::Error>
1696 where
1697 S: Serializer,
1698 {
1699 serialize_optional_enum(value.as_ref(), serializer, "NO_TRAILING_OFFSET")
1700 }
1701
1702 pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<TrailingOffsetType>, D::Error>
1708 where
1709 D: Deserializer<'de>,
1710 {
1711 deserialize_optional_enum(
1712 deserializer,
1713 "NO_TRAILING_OFFSET",
1714 "PRICE, BASIS_POINTS, TICKS, PRICE_TIER, NO_TRAILING_OFFSET, or null",
1715 )
1716 }
1717}
1718
1719pub mod serde_option_trigger_type {
1721 use serde::{Deserializer, Serializer};
1722
1723 use super::{TriggerType, deserialize_optional_enum, serialize_optional_enum};
1724
1725 pub fn serialize<S>(value: &Option<TriggerType>, serializer: S) -> Result<S::Ok, S::Error>
1731 where
1732 S: Serializer,
1733 {
1734 serialize_optional_enum(value.as_ref(), serializer, "NO_TRIGGER")
1735 }
1736
1737 pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<TriggerType>, D::Error>
1743 where
1744 D: Deserializer<'de>,
1745 {
1746 deserialize_optional_enum(
1747 deserializer,
1748 "NO_TRIGGER",
1749 "a trigger type, NO_TRIGGER, or null",
1750 )
1751 }
1752}
1753
1754fn serialize_optional_enum<S, T>(
1755 value: Option<&T>,
1756 serializer: S,
1757 none_token: &'static str,
1758) -> Result<S::Ok, S::Error>
1759where
1760 S: Serializer,
1761 T: AsRef<str>,
1762{
1763 serializer.serialize_str(value.map_or(none_token, AsRef::as_ref))
1764}
1765
1766fn deserialize_optional_enum<'de, D, T>(
1767 deserializer: D,
1768 none_token: &'static str,
1769 expected: &'static str,
1770) -> Result<Option<T>, D::Error>
1771where
1772 D: Deserializer<'de>,
1773 T: FromStr,
1774 T::Err: Display,
1775{
1776 struct OptionalEnumVisitor<T> {
1777 none_token: &'static str,
1778 expected: &'static str,
1779 marker: PhantomData<T>,
1780 }
1781
1782 impl<'de, T> serde::de::Visitor<'de> for OptionalEnumVisitor<T>
1783 where
1784 T: FromStr,
1785 T::Err: Display,
1786 {
1787 type Value = Option<T>;
1788
1789 fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1790 formatter.write_str(self.expected)
1791 }
1792
1793 fn visit_none<E>(self) -> Result<Self::Value, E> {
1794 Ok(None)
1795 }
1796
1797 fn visit_unit<E>(self) -> Result<Self::Value, E> {
1798 Ok(None)
1799 }
1800
1801 fn visit_some<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
1802 where
1803 D: Deserializer<'de>,
1804 {
1805 let value = Cow::<'de, str>::deserialize(deserializer)?;
1806 if value.eq_ignore_ascii_case(self.none_token) {
1807 Ok(None)
1808 } else {
1809 T::from_str(&value)
1810 .map(Some)
1811 .map_err(serde::de::Error::custom)
1812 }
1813 }
1814 }
1815
1816 deserializer.deserialize_option(OptionalEnumVisitor {
1817 none_token,
1818 expected,
1819 marker: PhantomData,
1820 })
1821}
1822
1823#[repr(C)]
1825#[derive(
1826 Copy,
1827 Clone,
1828 Debug,
1829 Display,
1830 Hash,
1831 PartialEq,
1832 Eq,
1833 PartialOrd,
1834 Ord,
1835 AsRefStr,
1836 FromRepr,
1837 EnumIter,
1838 EnumString,
1839)]
1840#[strum(ascii_case_insensitive)]
1841#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
1842#[cfg_attr(
1843 feature = "python",
1844 pyo3::pyclass(
1845 frozen,
1846 eq,
1847 eq_int,
1848 module = "nautilus_trader.model",
1849 from_py_object,
1850 rename_all = "SCREAMING_SNAKE_CASE",
1851 )
1852)]
1853#[cfg_attr(
1854 feature = "python",
1855 pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.model")
1856)]
1857pub enum PriceType {
1858 Bid = 1,
1864 Ask = 2,
1867 Mid = 3,
1869 Last = 4,
1871 Mark = 5,
1874}
1875
1876#[repr(C)]
1878#[derive(
1879 Copy,
1880 Clone,
1881 Debug,
1882 Display,
1883 Hash,
1884 PartialEq,
1885 Eq,
1886 PartialOrd,
1887 Ord,
1888 AsRefStr,
1889 FromRepr,
1890 EnumIter,
1891 EnumString,
1892)]
1893#[strum(ascii_case_insensitive)]
1894#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
1895#[cfg_attr(
1896 feature = "python",
1897 pyo3::pyclass(
1898 frozen,
1899 eq,
1900 eq_int,
1901 module = "nautilus_trader.model",
1902 from_py_object,
1903 rename_all = "SCREAMING_SNAKE_CASE",
1904 )
1905)]
1906#[cfg_attr(
1907 feature = "python",
1908 pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.model")
1909)]
1910#[allow(non_camel_case_types)]
1911pub enum RecordFlag {
1912 F_LAST = 1 << 7, F_TOB = 1 << 6, F_SNAPSHOT = 1 << 5, F_MBP = 1 << 4, RESERVED_2 = 1 << 3, RESERVED_1 = 1 << 2, }
1925
1926impl RecordFlag {
1927 #[must_use]
1929 pub fn matches(self, value: u8) -> bool {
1930 (self as u8) & value != 0
1931 }
1932}
1933
1934#[repr(C)]
1936#[derive(
1937 Copy,
1938 Clone,
1939 Debug,
1940 Display,
1941 Hash,
1942 PartialEq,
1943 Eq,
1944 PartialOrd,
1945 Ord,
1946 AsRefStr,
1947 FromRepr,
1948 EnumIter,
1949 EnumString,
1950)]
1951#[strum(ascii_case_insensitive)]
1952#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
1953#[cfg_attr(
1954 feature = "python",
1955 pyo3::pyclass(
1956 frozen,
1957 eq,
1958 eq_int,
1959 module = "nautilus_trader.model",
1960 from_py_object,
1961 rename_all = "SCREAMING_SNAKE_CASE",
1962 )
1963)]
1964#[cfg_attr(
1965 feature = "python",
1966 pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.model")
1967)]
1968pub enum TimeInForce {
1969 Gtc = 1,
1971 Ioc = 2,
1973 Fok = 3,
1975 Gtd = 4,
1977 Day = 5,
1979 AtTheOpen = 6,
1981 AtTheClose = 7,
1983}
1984
1985#[repr(C)]
1987#[derive(
1988 Copy,
1989 Clone,
1990 Debug,
1991 Display,
1992 Hash,
1993 PartialEq,
1994 Eq,
1995 PartialOrd,
1996 Ord,
1997 AsRefStr,
1998 FromRepr,
1999 EnumIter,
2000 EnumString,
2001)]
2002#[strum(ascii_case_insensitive)]
2003#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
2004#[cfg_attr(
2005 feature = "python",
2006 pyo3::pyclass(
2007 frozen,
2008 eq,
2009 eq_int,
2010 module = "nautilus_trader.model",
2011 from_py_object,
2012 rename_all = "SCREAMING_SNAKE_CASE",
2013 )
2014)]
2015#[cfg_attr(
2016 feature = "python",
2017 pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.model")
2018)]
2019pub enum TradingState {
2020 Active = 1,
2022 Halted = 2,
2024 Reducing = 3,
2026}
2027
2028#[repr(C)]
2033#[derive(
2034 Copy,
2035 Clone,
2036 Debug,
2037 Display,
2038 Hash,
2039 PartialEq,
2040 Eq,
2041 PartialOrd,
2042 Ord,
2043 AsRefStr,
2044 FromRepr,
2045 EnumIter,
2046 EnumString,
2047)]
2048#[strum(ascii_case_insensitive)]
2049#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
2050#[cfg_attr(
2051 feature = "python",
2052 pyo3::pyclass(
2053 frozen,
2054 eq,
2055 eq_int,
2056 module = "nautilus_trader.model",
2057 from_py_object,
2058 rename_all = "SCREAMING_SNAKE_CASE",
2059 )
2060)]
2061#[cfg_attr(
2062 feature = "python",
2063 pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.model")
2064)]
2065pub enum TrailingOffsetType {
2066 Price = 1,
2068 BasisPoints = 2,
2070 Ticks = 3,
2072 PriceTier = 4,
2074}
2075
2076#[repr(C)]
2081#[derive(
2082 Copy,
2083 Clone,
2084 Debug,
2085 Display,
2086 Hash,
2087 PartialEq,
2088 Eq,
2089 PartialOrd,
2090 Ord,
2091 AsRefStr,
2092 FromRepr,
2093 EnumIter,
2094 EnumString,
2095)]
2096#[strum(ascii_case_insensitive)]
2097#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
2098#[cfg_attr(
2099 feature = "python",
2100 pyo3::pyclass(
2101 frozen,
2102 eq,
2103 eq_int,
2104 module = "nautilus_trader.model",
2105 from_py_object,
2106 rename_all = "SCREAMING_SNAKE_CASE",
2107 )
2108)]
2109#[cfg_attr(
2110 feature = "python",
2111 pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.model")
2112)]
2113pub enum TriggerType {
2114 Default = 1,
2116 LastPrice = 2,
2118 MarkPrice = 3,
2120 IndexPrice = 4,
2122 BidAsk = 5,
2124 DoubleLast = 6,
2126 DoubleBidAsk = 7,
2128 LastOrBidAsk = 8,
2130 MidPoint = 9,
2132}
2133
2134enum_strum_serde!(AccountType);
2135enum_strum_serde!(AggregationSource);
2136enum_strum_serde!(AggressorSide);
2137enum_strum_serde!(AssetClass);
2138enum_strum_serde!(BarAggregation);
2139enum_strum_serde!(BarIntervalType);
2140enum_strum_serde!(BookAction);
2141enum_strum_serde!(BookType);
2142enum_strum_serde!(ContingencyType);
2143enum_strum_serde!(ContinuousFutureAdjustmentType);
2144enum_strum_serde!(CurrencyType);
2145enum_strum_serde!(GreeksConvention);
2146enum_strum_serde!(InstrumentClass);
2147enum_strum_serde!(InstrumentCloseType);
2148enum_strum_serde!(LiquiditySide);
2149enum_strum_serde!(MarketStatus);
2150enum_strum_serde!(MarketStatusAction);
2151enum_strum_serde!(OmsType);
2152enum_strum_serde!(OptionKind);
2153enum_strum_serde!(OrderSide);
2154enum_strum_serde!(OrderStatus);
2155enum_strum_serde!(OrderType);
2156enum_strum_serde!(PositionAdjustmentType);
2157enum_strum_serde!(PositionSide);
2158enum_strum_serde!(PriceType);
2159enum_strum_serde!(RecordFlag);
2160enum_strum_serde!(TimeInForce);
2161enum_strum_serde!(TradingState);
2162enum_strum_serde!(TrailingOffsetType);
2163enum_strum_serde!(TriggerType);
2164
2165#[cfg(test)]
2166mod tests {
2167 use rstest::rstest;
2168
2169 use super::*;
2170
2171 #[derive(Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
2172 struct OptionalSides {
2173 #[serde(with = "serde_option_order_side")]
2174 order: Option<OrderSide>,
2175 #[serde(with = "serde_option_position_side")]
2176 position: Option<PositionSide>,
2177 }
2178
2179 #[derive(Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
2180 struct OptionalOrderTypes {
2181 #[serde(with = "serde_option_contingency_type")]
2182 contingency: Option<ContingencyType>,
2183 #[serde(with = "serde_option_trailing_offset_type")]
2184 trailing_offset: Option<TrailingOffsetType>,
2185 #[serde(with = "serde_option_trigger_type")]
2186 trigger: Option<TriggerType>,
2187 }
2188
2189 #[rstest]
2190 fn test_optional_sides_serde_preserves_legacy_none_tokens() {
2191 let value = OptionalSides {
2192 order: None,
2193 position: None,
2194 };
2195
2196 let json = serde_json::to_string(&value).unwrap();
2197 let decoded: OptionalSides = serde_json::from_str(&json).unwrap();
2198
2199 assert_eq!(
2200 json,
2201 r#"{"order":"NO_ORDER_SIDE","position":"NO_POSITION_SIDE"}"#
2202 );
2203 assert_eq!(decoded, value);
2204 }
2205
2206 #[rstest]
2207 fn test_optional_sides_serde_accepts_null_and_valid_sides() {
2208 let json = r#"{"order":null,"position":"LONG"}"#;
2209 let decoded: OptionalSides = serde_json::from_str(json).unwrap();
2210
2211 assert_eq!(
2212 decoded,
2213 OptionalSides {
2214 order: None,
2215 position: Some(PositionSide::Long),
2216 }
2217 );
2218 }
2219
2220 #[rstest]
2221 fn test_optional_order_types_serde_preserves_legacy_none_tokens() {
2222 let value = OptionalOrderTypes {
2223 contingency: None,
2224 trailing_offset: None,
2225 trigger: None,
2226 };
2227
2228 let json = serde_json::to_string(&value).unwrap();
2229 let decoded: OptionalOrderTypes = serde_json::from_str(&json).unwrap();
2230
2231 assert_eq!(
2232 json,
2233 r#"{"contingency":"NO_CONTINGENCY","trailing_offset":"NO_TRAILING_OFFSET","trigger":"NO_TRIGGER"}"#,
2234 );
2235 assert_eq!(decoded, value);
2236 }
2237
2238 #[rstest]
2239 fn test_optional_order_types_serde_accepts_null_and_valid_values() {
2240 let json = r#"{"contingency":null,"trailing_offset":"PRICE","trigger":"LAST_PRICE"}"#;
2241 let decoded: OptionalOrderTypes = serde_json::from_str(json).unwrap();
2242
2243 assert_eq!(
2244 decoded,
2245 OptionalOrderTypes {
2246 contingency: None,
2247 trailing_offset: Some(TrailingOffsetType::Price),
2248 trigger: Some(TriggerType::LastPrice),
2249 },
2250 );
2251 }
2252
2253 #[rstest]
2254 #[case(r#"{"contingency":"INVALID","trailing_offset":"NO_TRAILING_OFFSET","trigger":"NO_TRIGGER"}"#)]
2255 #[case(
2256 r#"{"contingency":"NO_CONTINGENCY","trailing_offset":"INVALID","trigger":"NO_TRIGGER"}"#
2257 )]
2258 #[case(r#"{"contingency":"NO_CONTINGENCY","trailing_offset":"NO_TRAILING_OFFSET","trigger":"INVALID"}"#)]
2259 fn test_optional_order_types_serde_rejects_invalid_values(#[case] json: &str) {
2260 assert!(serde_json::from_str::<OptionalOrderTypes>(json).is_err());
2261 }
2262
2263 #[rstest]
2264 #[case::no_aggressor(0, Some(AggressorSide::NoAggressor))]
2265 #[case::buy(1, Some(AggressorSide::Buy))]
2266 #[case::sell(2, Some(AggressorSide::Sell))]
2267 #[case::invalid(3, None)]
2268 #[case::max_u8(255, None)]
2269 fn test_aggressor_side_from_u8(#[case] value: u8, #[case] expected: Option<AggressorSide>) {
2270 assert_eq!(AggressorSide::from_u8(value), expected);
2271 }
2272
2273 #[rstest]
2274 #[case(AggressorSide::NoAggressor, "NO_AGGRESSOR")]
2275 #[case(AggressorSide::Buy, "BUY")]
2276 #[case(AggressorSide::Sell, "SELL")]
2277 fn test_aggressor_side_to_string(#[case] value: AggressorSide, #[case] expected: &str) {
2278 assert_eq!(value.to_string(), expected);
2279 assert_eq!(value.as_ref(), expected);
2280 }
2281
2282 #[rstest]
2283 #[case(AggressorSide::NoAggressor, "NO_AGGRESSOR")]
2284 #[case(AggressorSide::Buy, "BUY")]
2285 #[case(AggressorSide::Sell, "SELL")]
2286 #[case(AggressorSide::Buy, "BUYER")]
2287 #[case(AggressorSide::Sell, "SELLER")]
2288 #[case(AggressorSide::Buy, "buy")]
2289 #[case(AggressorSide::Sell, "seller")]
2290 fn test_aggressor_side_from_str(#[case] expected: AggressorSide, #[case] value: &str) {
2291 assert_eq!(AggressorSide::from_str(value), Ok(expected));
2292 }
2293
2294 #[rstest]
2295 #[case(AggressorSide::Buy, "\"BUY\"")]
2296 #[case(AggressorSide::Sell, "\"SELL\"")]
2297 #[case(AggressorSide::NoAggressor, "\"NO_AGGRESSOR\"")]
2298 fn test_aggressor_side_serde_roundtrip(#[case] input: AggressorSide, #[case] expected: &str) {
2299 let json = serde_json::to_string(&input).unwrap();
2300 assert_eq!(json, expected);
2301 let parsed: AggressorSide = serde_json::from_str(expected).unwrap();
2302 assert_eq!(parsed, input);
2303 }
2304
2305 #[rstest]
2306 #[case("BUYER", AggressorSide::Buy)]
2307 #[case("SELLER", AggressorSide::Sell)]
2308 fn test_aggressor_side_serde_accepts_historical(
2309 #[case] value: &str,
2310 #[case] expected: AggressorSide,
2311 ) {
2312 let parsed: AggressorSide = serde_json::from_str(&format!("\"{value}\"")).unwrap();
2313 assert_eq!(parsed, expected);
2314 }
2315
2316 #[rstest]
2317 #[case(GreeksConvention::BlackScholes, "\"BLACK_SCHOLES\"")]
2318 #[case(GreeksConvention::PriceAdjusted, "\"PRICE_ADJUSTED\"")]
2319 fn test_greeks_convention_serde_roundtrip(
2320 #[case] input: GreeksConvention,
2321 #[case] expected: &str,
2322 ) {
2323 let json = serde_json::to_string(&input).unwrap();
2324 assert_eq!(json, expected);
2325 let parsed: GreeksConvention = serde_json::from_str(expected).unwrap();
2326 assert_eq!(parsed, input);
2327 }
2328
2329 #[rstest]
2330 fn test_greeks_convention_default_is_black_scholes() {
2331 assert_eq!(GreeksConvention::default(), GreeksConvention::BlackScholes);
2332 }
2333
2334 #[rstest]
2335 #[case(ContinuousFutureAdjustmentType::BackwardSpread, false, true)]
2336 #[case(ContinuousFutureAdjustmentType::ForwardSpread, false, false)]
2337 #[case(ContinuousFutureAdjustmentType::BackwardRatio, true, true)]
2338 #[case(ContinuousFutureAdjustmentType::ForwardRatio, true, false)]
2339 fn test_continuous_future_adjustment_type_predicates(
2340 #[case] mode: ContinuousFutureAdjustmentType,
2341 #[case] expected_is_ratio: bool,
2342 #[case] expected_is_backward: bool,
2343 ) {
2344 assert_eq!(mode.is_ratio(), expected_is_ratio);
2345 assert_eq!(mode.is_backward(), expected_is_backward);
2346 }
2347
2348 #[rstest]
2349 #[case(ContinuousFutureAdjustmentType::BackwardSpread, "\"BACKWARD_SPREAD\"")]
2350 #[case(ContinuousFutureAdjustmentType::ForwardSpread, "\"FORWARD_SPREAD\"")]
2351 #[case(ContinuousFutureAdjustmentType::BackwardRatio, "\"BACKWARD_RATIO\"")]
2352 #[case(ContinuousFutureAdjustmentType::ForwardRatio, "\"FORWARD_RATIO\"")]
2353 fn test_continuous_future_adjustment_type_serde_roundtrip(
2354 #[case] input: ContinuousFutureAdjustmentType,
2355 #[case] expected: &str,
2356 ) {
2357 let json = serde_json::to_string(&input).unwrap();
2358 assert_eq!(json, expected);
2359 let parsed: ContinuousFutureAdjustmentType = serde_json::from_str(expected).unwrap();
2360 assert_eq!(parsed, input);
2361 }
2362
2363 #[rstest]
2364 fn test_continuous_future_adjustment_type_default_is_backward_spread() {
2365 assert_eq!(
2366 ContinuousFutureAdjustmentType::default(),
2367 ContinuousFutureAdjustmentType::BackwardSpread,
2368 );
2369 }
2370
2371 #[rstest]
2372 #[case(InstrumentClass::Option, true)]
2373 #[case(InstrumentClass::FuturesSpread, true)]
2374 #[case(InstrumentClass::OptionSpread, true)]
2375 #[case(InstrumentClass::Spot, false)]
2376 #[case(InstrumentClass::Swap, false)]
2377 #[case(InstrumentClass::Future, false)]
2378 #[case(InstrumentClass::Forward, false)]
2379 #[case(InstrumentClass::Cfd, false)]
2380 #[case(InstrumentClass::Bond, false)]
2381 #[case(InstrumentClass::Warrant, false)]
2382 #[case(InstrumentClass::SportsBetting, false)]
2383 #[case(InstrumentClass::BinaryOption, false)]
2384 fn test_instrument_class_allows_negative_price(
2385 #[case] class: InstrumentClass,
2386 #[case] expected: bool,
2387 ) {
2388 assert_eq!(class.allows_negative_price(), expected);
2389 }
2390
2391 #[rstest]
2392 #[case("FUT", Some(InstrumentClass::Future))]
2393 #[case("FUTURE", Some(InstrumentClass::Future))]
2394 #[case("OPT", Some(InstrumentClass::Option))]
2395 #[case("OPTION", Some(InstrumentClass::Option))]
2396 #[case("fut", None)]
2397 #[case("Fut", None)]
2398 #[case("option", None)]
2399 #[case("Option", None)]
2400 #[case("SPREAD", None)]
2401 #[case("UNKNOWN", None)]
2402 #[case("", None)]
2403 fn test_instrument_class_try_from_parent_suffix(
2404 #[case] suffix: &str,
2405 #[case] expected: Option<InstrumentClass>,
2406 ) {
2407 assert_eq!(InstrumentClass::try_from_parent_suffix(suffix), expected);
2408 }
2409
2410 #[rstest]
2411 #[case(InstrumentClass::Future, Some("FUT"))]
2412 #[case(InstrumentClass::Option, Some("OPT"))]
2413 #[case(InstrumentClass::Spot, None)]
2414 #[case(InstrumentClass::Swap, None)]
2415 #[case(InstrumentClass::FuturesSpread, None)]
2416 #[case(InstrumentClass::Forward, None)]
2417 #[case(InstrumentClass::Cfd, None)]
2418 #[case(InstrumentClass::Bond, None)]
2419 #[case(InstrumentClass::OptionSpread, None)]
2420 #[case(InstrumentClass::Warrant, None)]
2421 #[case(InstrumentClass::SportsBetting, None)]
2422 #[case(InstrumentClass::BinaryOption, None)]
2423 fn test_instrument_class_parent_suffix(
2424 #[case] class: InstrumentClass,
2425 #[case] expected: Option<&'static str>,
2426 ) {
2427 assert_eq!(class.parent_suffix(), expected);
2428 }
2429
2430 #[rstest]
2431 #[case(InstrumentClass::Future)]
2432 #[case(InstrumentClass::Option)]
2433 fn test_instrument_class_parent_suffix_roundtrip(#[case] class: InstrumentClass) {
2434 let suffix = class.parent_suffix().unwrap();
2435 assert_eq!(InstrumentClass::try_from_parent_suffix(suffix), Some(class));
2436 }
2437}