Skip to main content

nautilus_model/
enums.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2026 Nautech Systems Pty Ltd. All rights reserved.
3//  https://nautechsystems.io
4//
5//  Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
6//  You may not use this file except in compliance with the License.
7//  You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
8//
9//  Unless required by applicable law or agreed to in writing, software
10//  distributed under the License is distributed on an "AS IS" BASIS,
11//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//  See the License for the specific language governing permissions and
13//  limitations under the License.
14// -------------------------------------------------------------------------------------------------
15
16//! Enumerations for the trading domain model.
17
18use std::str::FromStr;
19
20use serde::{Deserialize, Deserializer, Serialize, Serializer};
21use strum::{AsRefStr, Display, EnumIter, EnumString, FromRepr};
22
23use crate::enum_strum_serde;
24
25/// Provides conversion from a `u8` value to an enum type.
26pub trait FromU8 {
27    /// Converts a `u8` value to the implementing type.
28    ///
29    /// Returns `None` if the value is not a valid representation.
30    fn from_u8(value: u8) -> Option<Self>
31    where
32        Self: Sized;
33}
34
35/// Provides conversion from a `u16` value to an enum type.
36pub trait FromU16 {
37    /// Converts a `u16` value to the implementing type.
38    ///
39    /// Returns `None` if the value is not a valid representation.
40    fn from_u16(value: u16) -> Option<Self>
41    where
42        Self: Sized;
43}
44
45/// An account type provided by a trading venue or broker.
46#[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.core.nautilus_pyo3.model.enums",
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    /// An account with unleveraged cash assets only.
81    Cash = 1,
82    /// An account which facilitates trading on margin, using account assets as collateral.
83    Margin = 2,
84    /// An account specific to betting markets.
85    Betting = 3,
86    /// An account which represents a blockchain wallet,
87    Wallet = 4,
88}
89
90/// An aggregation source for derived data.
91#[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.core.nautilus_pyo3.model.enums",
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    /// The data is externally aggregated (outside the Nautilus system boundary).
126    External = 1,
127    /// The data is internally aggregated (inside the Nautilus system boundary).
128    Internal = 2,
129}
130
131/// The side for the aggressing order of a trade in a market.
132#[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.core.nautilus_pyo3.model.enums",
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    /// There was no specific aggressor for the trade.
168    #[default]
169    NoAggressor = 0,
170    /// The BUY order was the aggressor for the trade.
171    Buyer = 1,
172    /// The SELL order was the aggressor for the trade.
173    Seller = 2,
174}
175
176impl FromU8 for AggressorSide {
177    fn from_u8(value: u8) -> Option<Self> {
178        match value {
179            0 => Some(Self::NoAggressor),
180            1 => Some(Self::Buyer),
181            2 => Some(Self::Seller),
182            _ => None,
183        }
184    }
185}
186
187/// A broad financial market asset class.
188#[repr(C)]
189#[derive(
190    Copy,
191    Clone,
192    Debug,
193    Display,
194    Hash,
195    PartialEq,
196    Eq,
197    PartialOrd,
198    Ord,
199    AsRefStr,
200    FromRepr,
201    EnumIter,
202    EnumString,
203)]
204#[strum(ascii_case_insensitive)]
205#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
206#[cfg_attr(
207    feature = "python",
208    pyo3::pyclass(
209        frozen,
210        eq,
211        eq_int,
212        module = "nautilus_trader.core.nautilus_pyo3.model.enums",
213        from_py_object,
214        rename_all = "SCREAMING_SNAKE_CASE",
215    )
216)]
217#[cfg_attr(
218    feature = "python",
219    pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.model")
220)]
221#[allow(non_camel_case_types)]
222pub enum AssetClass {
223    /// Foreign exchange (FOREX) assets.
224    FX = 1,
225    /// Equity / stock assets.
226    Equity = 2,
227    /// Commodity assets.
228    Commodity = 3,
229    /// Debt based assets.
230    Debt = 4,
231    /// Index based assets (baskets).
232    Index = 5,
233    /// Cryptocurrency or crypto token assets.
234    Cryptocurrency = 6,
235    /// Alternative assets.
236    Alternative = 7,
237}
238
239impl FromU8 for AssetClass {
240    fn from_u8(value: u8) -> Option<Self> {
241        match value {
242            1 => Some(Self::FX),
243            2 => Some(Self::Equity),
244            3 => Some(Self::Commodity),
245            4 => Some(Self::Debt),
246            5 => Some(Self::Index),
247            6 => Some(Self::Cryptocurrency),
248            7 => Some(Self::Alternative),
249            _ => None,
250        }
251    }
252}
253
254/// The aggregation method through which a bar is generated and closed.
255#[repr(C)]
256#[derive(
257    Copy,
258    Clone,
259    Debug,
260    Display,
261    Hash,
262    PartialEq,
263    Eq,
264    PartialOrd,
265    Ord,
266    AsRefStr,
267    FromRepr,
268    EnumIter,
269    EnumString,
270)]
271#[strum(ascii_case_insensitive)]
272#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
273#[cfg_attr(
274    feature = "python",
275    pyo3::pyclass(
276        frozen,
277        eq,
278        eq_int,
279        module = "nautilus_trader.core.nautilus_pyo3.model.enums",
280        from_py_object,
281        rename_all = "SCREAMING_SNAKE_CASE",
282    )
283)]
284#[cfg_attr(
285    feature = "python",
286    pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.model")
287)]
288pub enum BarAggregation {
289    /// Based on a number of ticks.
290    Tick = 1,
291    /// Based on the buy/sell imbalance of ticks.
292    TickImbalance = 2,
293    /// Based on sequential buy/sell runs of ticks.
294    TickRuns = 3,
295    /// Based on traded volume.
296    Volume = 4,
297    /// Based on the buy/sell imbalance of traded volume.
298    VolumeImbalance = 5,
299    /// Based on sequential runs of buy/sell traded volume.
300    VolumeRuns = 6,
301    /// Based on the 'notional' value of the instrument.
302    Value = 7,
303    /// Based on the buy/sell imbalance of trading by notional value.
304    ValueImbalance = 8,
305    /// Based on sequential buy/sell runs of trading by notional value.
306    ValueRuns = 9,
307    /// Based on time intervals with millisecond granularity.
308    Millisecond = 10,
309    /// Based on time intervals with second granularity.
310    Second = 11,
311    /// Based on time intervals with minute granularity.
312    Minute = 12,
313    /// Based on time intervals with hour granularity.
314    Hour = 13,
315    /// Based on time intervals with day granularity.
316    Day = 14,
317    /// Based on time intervals with week granularity.
318    Week = 15,
319    /// Based on time intervals with month granularity.
320    Month = 16,
321    /// Based on time intervals with year granularity.
322    Year = 17,
323    /// Based on fixed price movements (brick size).
324    Renko = 18,
325}
326
327/// The interval type for bar aggregation.
328#[repr(C)]
329#[derive(
330    Copy,
331    Clone,
332    Debug,
333    Default,
334    Display,
335    Hash,
336    PartialEq,
337    Eq,
338    PartialOrd,
339    Ord,
340    AsRefStr,
341    FromRepr,
342    EnumIter,
343    EnumString,
344)]
345#[strum(ascii_case_insensitive)]
346#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
347#[cfg_attr(
348    feature = "python",
349    pyo3::pyclass(
350        frozen,
351        eq,
352        eq_int,
353        module = "nautilus_trader.core.nautilus_pyo3.model.enums",
354        from_py_object,
355        rename_all = "SCREAMING_SNAKE_CASE",
356    )
357)]
358#[cfg_attr(
359    feature = "python",
360    pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.model")
361)]
362pub enum BarIntervalType {
363    /// Left-open interval `(start, end]`: start is exclusive, end is inclusive (default).
364    #[default]
365    LeftOpen = 1,
366    /// Right-open interval `[start, end)`: start is inclusive, end is exclusive.
367    RightOpen = 2,
368}
369
370/// Represents the side of a bet in a betting market.
371#[repr(C)]
372#[derive(
373    Copy,
374    Clone,
375    Debug,
376    Display,
377    Hash,
378    PartialEq,
379    Eq,
380    PartialOrd,
381    Ord,
382    AsRefStr,
383    FromRepr,
384    EnumIter,
385    EnumString,
386)]
387#[strum(ascii_case_insensitive)]
388#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
389#[cfg_attr(
390    feature = "python",
391    pyo3::pyclass(
392        frozen,
393        eq,
394        eq_int,
395        module = "nautilus_trader.core.nautilus_pyo3.model.enums",
396        from_py_object,
397        rename_all = "SCREAMING_SNAKE_CASE",
398    )
399)]
400#[cfg_attr(
401    feature = "python",
402    pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.model")
403)]
404pub enum BetSide {
405    /// A "Back" bet signifies support for a specific outcome.
406    Back = 1,
407    /// A "Lay" bet signifies opposition to a specific outcome.
408    Lay = 2,
409}
410
411impl BetSide {
412    /// Returns the opposite betting side.
413    #[must_use]
414    pub fn opposite(&self) -> Self {
415        match self {
416            Self::Back => Self::Lay,
417            Self::Lay => Self::Back,
418        }
419    }
420}
421
422impl From<OrderSide> for BetSide {
423    /// Returns the equivalent [`BetSide`] for a given [`OrderSide`].
424    ///
425    /// # Panics
426    ///
427    /// Panics if `side` is [`OrderSide::NoOrderSide`].
428    fn from(side: OrderSide) -> Self {
429        match side {
430            OrderSide::Buy => Self::Back,
431            OrderSide::Sell => Self::Lay,
432            OrderSide::NoOrderSide => panic!("Invalid `OrderSide` for `BetSide`, was {side}"),
433        }
434    }
435}
436
437/// The type of order book action for an order book event.
438#[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.core.nautilus_pyo3.model.enums",
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    /// An order is added to the book.
473    Add = 1,
474    /// An existing order in the book is updated/modified.
475    Update = 2,
476    /// An existing order in the book is deleted/canceled.
477    Delete = 3,
478    /// The state of the order book is cleared.
479    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/// The order book type, representing the type of levels granularity and delta updating heuristics.
495#[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.core.nautilus_pyo3.model.enums",
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    /// Top-of-book best bid/ask, one level per side.
531    L1_MBP = 1,
532    /// Market by price, one order per level (aggregated).
533    L2_MBP = 2,
534    /// Market by order, multiple orders per level (full granularity).
535    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/// The order contingency type which specifies the behavior of linked orders.
550///
551/// [FIX 5.0 SP2 : ContingencyType <1385> field](https://www.onixs.biz/fix-dictionary/5.0.sp2/tagnum_1385.html).
552#[repr(C)]
553#[derive(
554    Copy,
555    Clone,
556    Debug,
557    Default,
558    Display,
559    Hash,
560    PartialEq,
561    Eq,
562    PartialOrd,
563    Ord,
564    AsRefStr,
565    FromRepr,
566    EnumIter,
567    EnumString,
568)]
569#[strum(ascii_case_insensitive)]
570#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
571#[cfg_attr(
572    feature = "python",
573    pyo3::pyclass(
574        frozen,
575        eq,
576        eq_int,
577        module = "nautilus_trader.core.nautilus_pyo3.model.enums",
578        from_py_object,
579        rename_all = "SCREAMING_SNAKE_CASE",
580    )
581)]
582#[cfg_attr(
583    feature = "python",
584    pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.model")
585)]
586pub enum ContingencyType {
587    /// Not a contingent order.
588    #[default]
589    NoContingency = 0,
590    /// One-Cancels-the-Other.
591    Oco = 1,
592    /// One-Triggers-the-Other.
593    Oto = 2,
594    /// One-Updates-the-Other (by proportional quantity).
595    Ouo = 3,
596}
597
598/// The price-adjustment scheme applied when stitching segment contracts into a
599/// continuous future series.
600///
601/// The direction (backward vs. forward) selects the anchor contract:
602/// - Backward modes anchor on the most recent contract; prices in older
603///   segments are shifted into the latest contract's frame.
604/// - Forward modes anchor on the first contract; prices in later segments
605///   are shifted into the first contract's frame.
606///
607/// The kind (spread vs. ratio) selects how each transition's offset is combined:
608/// - Spread modes accumulate additive offsets (`post_price - pre_price`).
609/// - Ratio modes accumulate multiplicative factors (`post_price / pre_price`)
610///   and require strictly positive prices.
611#[repr(C)]
612#[derive(
613    Copy,
614    Clone,
615    Debug,
616    Default,
617    Display,
618    Hash,
619    PartialEq,
620    Eq,
621    PartialOrd,
622    Ord,
623    AsRefStr,
624    FromRepr,
625    EnumIter,
626    EnumString,
627)]
628#[strum(ascii_case_insensitive)]
629#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
630#[cfg_attr(
631    feature = "python",
632    pyo3::pyclass(
633        frozen,
634        eq,
635        eq_int,
636        module = "nautilus_trader.core.nautilus_pyo3.model.enums",
637        from_py_object,
638        rename_all = "SCREAMING_SNAKE_CASE",
639    )
640)]
641#[cfg_attr(
642    feature = "python",
643    pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.model")
644)]
645pub enum ContinuousFutureAdjustmentType {
646    /// Additive adjustment, anchored on the most recent contract.
647    #[default]
648    BackwardSpread = 1,
649    /// Additive adjustment, anchored on the first contract.
650    ForwardSpread = 2,
651    /// Multiplicative adjustment, anchored on the most recent contract.
652    BackwardRatio = 3,
653    /// Multiplicative adjustment, anchored on the first contract.
654    ForwardRatio = 4,
655}
656
657impl ContinuousFutureAdjustmentType {
658    /// Returns whether this mode accumulates multiplicative factors.
659    #[must_use]
660    pub const fn is_ratio(&self) -> bool {
661        matches!(self, Self::BackwardRatio | Self::ForwardRatio)
662    }
663
664    /// Returns whether this mode anchors on the most recent contract.
665    #[must_use]
666    pub const fn is_backward(&self) -> bool {
667        matches!(self, Self::BackwardSpread | Self::BackwardRatio)
668    }
669}
670
671/// The broad currency type.
672#[repr(C)]
673#[derive(
674    Copy,
675    Clone,
676    Debug,
677    Display,
678    Hash,
679    PartialEq,
680    Eq,
681    PartialOrd,
682    Ord,
683    AsRefStr,
684    FromRepr,
685    EnumIter,
686    EnumString,
687)]
688#[strum(ascii_case_insensitive)]
689#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
690#[cfg_attr(
691    feature = "python",
692    pyo3::pyclass(
693        frozen,
694        eq,
695        eq_int,
696        module = "nautilus_trader.core.nautilus_pyo3.model.enums",
697        from_py_object,
698        rename_all = "SCREAMING_SNAKE_CASE",
699    )
700)]
701#[cfg_attr(
702    feature = "python",
703    pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.model")
704)]
705pub enum CurrencyType {
706    /// A type of cryptocurrency or crypto token.
707    Crypto = 1,
708    /// A type of currency issued by governments which is not backed by a commodity.
709    Fiat = 2,
710    /// A type of currency that is based on the value of an underlying commodity.
711    CommodityBacked = 3,
712}
713
714/// The instrument class.
715#[repr(C)]
716#[derive(
717    Copy,
718    Clone,
719    Debug,
720    Display,
721    Hash,
722    PartialEq,
723    Eq,
724    PartialOrd,
725    Ord,
726    AsRefStr,
727    FromRepr,
728    EnumIter,
729    EnumString,
730)]
731#[strum(ascii_case_insensitive)]
732#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
733#[cfg_attr(
734    feature = "python",
735    pyo3::pyclass(
736        frozen,
737        eq,
738        eq_int,
739        module = "nautilus_trader.core.nautilus_pyo3.model.enums",
740        from_py_object,
741        rename_all = "SCREAMING_SNAKE_CASE",
742    )
743)]
744#[cfg_attr(
745    feature = "python",
746    pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.model")
747)]
748pub enum InstrumentClass {
749    /// A spot market instrument class. The current market price of an instrument that is bought or sold for immediate delivery and payment.
750    Spot = 1,
751    /// A swap instrument class. A derivative contract through which two parties exchange the cash flows or liabilities from two different financial instruments.
752    Swap = 2,
753    /// A futures contract instrument class. A legal agreement to buy or sell an asset at a predetermined price at a specified time in the future.
754    Future = 3,
755    /// A futures spread instrument class. A strategy involving the use of futures contracts to take advantage of price differentials between different contract months, underlying assets, or marketplaces.
756    FuturesSpread = 4,
757    /// A forward derivative instrument class. A customized contract between two parties to buy or sell an asset at a specified price on a future date.
758    Forward = 5,
759    /// A contract-for-difference (CFD) instrument class. A contract between an investor and a CFD broker to exchange the difference in the value of a financial product between the time the contract opens and closes.
760    Cfd = 6,
761    /// A bond instrument class. A type of debt investment where an investor loans money to an entity (typically corporate or governmental) which borrows the funds for a defined period of time at a variable or fixed interest rate.
762    Bond = 7,
763    /// An option contract instrument class. A type of derivative that gives the holder the right, but not the obligation, to buy or sell an underlying asset at a predetermined price before or at a certain future date.
764    Option = 8,
765    /// An option spread instrument class. A strategy involving the purchase and/or sale of multiple option contracts on the same underlying asset with different strike prices or expiration dates to hedge risk or speculate on price movements.
766    OptionSpread = 9,
767    /// A warrant instrument class. A derivative that gives the holder the right, but not the obligation, to buy or sell a security - most commonly an equity - at a certain price before expiration.
768    Warrant = 10,
769    /// A sports betting instrument class. A financialized derivative that allows wagering on the outcome of sports events using structured contracts or prediction markets.
770    SportsBetting = 11,
771    /// A binary option instrument class. A type of derivative where the payoff is either a fixed monetary amount or nothing, depending on whether the price of an underlying asset is above or below a predetermined level at expiration.
772    BinaryOption = 12,
773}
774
775impl InstrumentClass {
776    /// Returns whether this instrument class has an expiration.
777    #[must_use]
778    pub const fn has_expiration(&self) -> bool {
779        matches!(
780            self,
781            Self::Future | Self::FuturesSpread | Self::Option | Self::OptionSpread
782        )
783    }
784
785    /// Returns whether this instrument class allows negative prices.
786    #[must_use]
787    pub const fn allows_negative_price(&self) -> bool {
788        matches!(
789            self,
790            Self::Option | Self::FuturesSpread | Self::OptionSpread
791        )
792    }
793
794    /// Returns the [`InstrumentClass`] for the parent-symbol suffix, if recognised.
795    ///
796    /// Matches strict uppercase forms only. Both Databento-style abbreviations
797    /// (`FUT`, `OPT`) and long forms (`FUTURE`, `OPTION`) are accepted.
798    #[must_use]
799    pub fn try_from_parent_suffix(suffix: &str) -> Option<Self> {
800        match suffix {
801            "FUT" | "FUTURE" => Some(Self::Future),
802            "OPT" | "OPTION" => Some(Self::Option),
803            _ => None,
804        }
805    }
806
807    /// Returns the canonical parent-symbol suffix for this class, if one exists.
808    ///
809    /// Always emits the short form (`FUT`, `OPT`) so that adapters constructing
810    /// parent ids produce a single canonical string per class.
811    #[must_use]
812    pub const fn parent_suffix(self) -> Option<&'static str> {
813        match self {
814            Self::Future => Some("FUT"),
815            Self::Option => Some("OPT"),
816            _ => None,
817        }
818    }
819}
820
821/// The type of event for an instrument close.
822#[repr(C)]
823#[derive(
824    Copy,
825    Clone,
826    Debug,
827    Display,
828    Hash,
829    PartialEq,
830    Eq,
831    PartialOrd,
832    Ord,
833    AsRefStr,
834    FromRepr,
835    EnumIter,
836    EnumString,
837)]
838#[strum(ascii_case_insensitive)]
839#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
840#[cfg_attr(
841    feature = "python",
842    pyo3::pyclass(
843        frozen,
844        eq,
845        eq_int,
846        module = "nautilus_trader.core.nautilus_pyo3.model.enums",
847        from_py_object,
848        rename_all = "SCREAMING_SNAKE_CASE",
849    )
850)]
851#[cfg_attr(
852    feature = "python",
853    pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.model")
854)]
855pub enum InstrumentCloseType {
856    /// When the market session ended.
857    EndOfSession = 1,
858    /// When the instrument expiration was reached.
859    ContractExpired = 2,
860}
861
862/// Convert the given `value` to an [`InstrumentCloseType`].
863impl FromU8 for InstrumentCloseType {
864    fn from_u8(value: u8) -> Option<Self> {
865        match value {
866            1 => Some(Self::EndOfSession),
867            2 => Some(Self::ContractExpired),
868            _ => None,
869        }
870    }
871}
872
873/// The liquidity side for a trade.
874#[repr(C)]
875#[derive(
876    Copy,
877    Clone,
878    Debug,
879    Display,
880    Hash,
881    PartialEq,
882    Eq,
883    PartialOrd,
884    Ord,
885    AsRefStr,
886    FromRepr,
887    EnumIter,
888    EnumString,
889)]
890#[strum(ascii_case_insensitive)]
891#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
892#[cfg_attr(
893    feature = "python",
894    pyo3::pyclass(
895        frozen,
896        eq,
897        eq_int,
898        module = "nautilus_trader.core.nautilus_pyo3.model.enums",
899        from_py_object,
900        rename_all = "SCREAMING_SNAKE_CASE",
901    )
902)]
903#[cfg_attr(
904    feature = "python",
905    pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.model")
906)]
907pub enum LiquiditySide {
908    /// No liquidity side specified.
909    NoLiquiditySide = 0,
910    /// The order passively provided liquidity to the market to complete the trade (made a market).
911    Maker = 1,
912    /// The order aggressively took liquidity from the market to complete the trade.
913    Taker = 2,
914}
915
916/// The status of an individual market on a trading venue.
917#[repr(C)]
918#[derive(
919    Copy,
920    Clone,
921    Debug,
922    Display,
923    Hash,
924    PartialEq,
925    Eq,
926    PartialOrd,
927    Ord,
928    AsRefStr,
929    FromRepr,
930    EnumIter,
931    EnumString,
932)]
933#[strum(ascii_case_insensitive)]
934#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
935#[cfg_attr(
936    feature = "python",
937    pyo3::pyclass(
938        frozen,
939        eq,
940        eq_int,
941        module = "nautilus_trader.core.nautilus_pyo3.model.enums",
942        from_py_object,
943        rename_all = "SCREAMING_SNAKE_CASE",
944    )
945)]
946#[cfg_attr(
947    feature = "python",
948    pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.model")
949)]
950pub enum MarketStatus {
951    /// The instrument is trading.
952    Open = 1,
953    /// The instrument is in a pre-open period.
954    Closed = 2,
955    /// Trading in the instrument has been paused.
956    Paused = 3,
957    /// Trading in the instrument has been halted.
958    // Halted = 4,  # TODO: Unfortunately can't use this yet due to Cython (C enum namespacing)
959    /// Trading in the instrument has been suspended.
960    Suspended = 5,
961    /// Trading in the instrument is not available.
962    NotAvailable = 6,
963}
964
965/// An action affecting the status of an individual market on a trading venue.
966#[repr(C)]
967#[derive(
968    Copy,
969    Clone,
970    Debug,
971    Display,
972    Hash,
973    PartialEq,
974    Eq,
975    PartialOrd,
976    Ord,
977    AsRefStr,
978    FromRepr,
979    EnumIter,
980    EnumString,
981)]
982#[strum(ascii_case_insensitive)]
983#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
984#[cfg_attr(
985    feature = "python",
986    pyo3::pyclass(
987        frozen,
988        eq,
989        eq_int,
990        module = "nautilus_trader.core.nautilus_pyo3.model.enums",
991        from_py_object,
992        rename_all = "SCREAMING_SNAKE_CASE",
993    )
994)]
995#[cfg_attr(
996    feature = "python",
997    pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.model")
998)]
999pub enum MarketStatusAction {
1000    /// No change.
1001    None = 0,
1002    /// The instrument is in a pre-open period.
1003    PreOpen = 1,
1004    /// The instrument is in a pre-cross period.
1005    PreCross = 2,
1006    /// The instrument is quoting but not trading.
1007    Quoting = 3,
1008    /// The instrument is in a cross/auction.
1009    Cross = 4,
1010    /// The instrument is being opened through a trading rotation.
1011    Rotation = 5,
1012    /// A new price indication is available for the instrument.
1013    NewPriceIndication = 6,
1014    /// The instrument is trading.
1015    Trading = 7,
1016    /// Trading in the instrument has been halted.
1017    Halt = 8,
1018    /// Trading in the instrument has been paused.
1019    Pause = 9,
1020    /// Trading in the instrument has been suspended.
1021    Suspend = 10,
1022    /// The instrument is in a pre-close period.
1023    PreClose = 11,
1024    /// Trading in the instrument has closed.
1025    Close = 12,
1026    /// The instrument is in a post-close period.
1027    PostClose = 13,
1028    /// A change in short-selling restrictions.
1029    ShortSellRestrictionChange = 14,
1030    /// The instrument is not available for trading, either trading has closed or been halted.
1031    NotAvailableForTrading = 15,
1032}
1033
1034/// Convert the given `value` to an [`OrderSide`].
1035impl FromU16 for MarketStatusAction {
1036    fn from_u16(value: u16) -> Option<Self> {
1037        match value {
1038            0 => Some(Self::None),
1039            1 => Some(Self::PreOpen),
1040            2 => Some(Self::PreCross),
1041            3 => Some(Self::Quoting),
1042            4 => Some(Self::Cross),
1043            5 => Some(Self::Rotation),
1044            6 => Some(Self::NewPriceIndication),
1045            7 => Some(Self::Trading),
1046            8 => Some(Self::Halt),
1047            9 => Some(Self::Pause),
1048            10 => Some(Self::Suspend),
1049            11 => Some(Self::PreClose),
1050            12 => Some(Self::Close),
1051            13 => Some(Self::PostClose),
1052            14 => Some(Self::ShortSellRestrictionChange),
1053            15 => Some(Self::NotAvailableForTrading),
1054            _ => None,
1055        }
1056    }
1057}
1058
1059/// The order management system (OMS) type for a trading venue or trading strategy.
1060#[repr(C)]
1061#[derive(
1062    Copy,
1063    Clone,
1064    Debug,
1065    Default,
1066    Display,
1067    Hash,
1068    PartialEq,
1069    Eq,
1070    PartialOrd,
1071    Ord,
1072    AsRefStr,
1073    FromRepr,
1074    EnumIter,
1075    EnumString,
1076)]
1077#[strum(ascii_case_insensitive)]
1078#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
1079#[cfg_attr(
1080    feature = "python",
1081    pyo3::pyclass(
1082        frozen,
1083        eq,
1084        eq_int,
1085        module = "nautilus_trader.core.nautilus_pyo3.model.enums",
1086        from_py_object,
1087        rename_all = "SCREAMING_SNAKE_CASE",
1088    )
1089)]
1090#[cfg_attr(
1091    feature = "python",
1092    pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.model")
1093)]
1094pub enum OmsType {
1095    /// There is no specific type of order management specified (will defer to the venue OMS).
1096    #[default]
1097    Unspecified = 0,
1098    /// The netting type where there is one position per instrument.
1099    Netting = 1,
1100    /// The hedging type where there can be multiple positions per instrument.
1101    /// This can be in LONG/SHORT directions, by position/ticket ID, or tracked virtually by
1102    /// Nautilus.
1103    Hedging = 2,
1104}
1105
1106/// The kind of option contract.
1107#[repr(C)]
1108#[derive(
1109    Copy,
1110    Clone,
1111    Debug,
1112    Display,
1113    Hash,
1114    PartialEq,
1115    Eq,
1116    PartialOrd,
1117    Ord,
1118    AsRefStr,
1119    FromRepr,
1120    EnumIter,
1121    EnumString,
1122)]
1123#[strum(ascii_case_insensitive)]
1124#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
1125#[cfg_attr(
1126    feature = "python",
1127    pyo3::pyclass(
1128        frozen,
1129        eq,
1130        eq_int,
1131        module = "nautilus_trader.core.nautilus_pyo3.model.enums",
1132        from_py_object,
1133        rename_all = "SCREAMING_SNAKE_CASE",
1134    )
1135)]
1136#[cfg_attr(
1137    feature = "python",
1138    pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.model")
1139)]
1140pub enum OptionKind {
1141    /// A Call option gives the holder the right, but not the obligation, to buy an underlying asset at a specified strike price within a specified period of time.
1142    Call = 1,
1143    /// A Put option gives the holder the right, but not the obligation, to sell an underlying asset at a specified strike price within a specified period of time.
1144    Put = 2,
1145}
1146
1147/// The numeraire convention for option greeks published by a venue.
1148///
1149/// Crypto option venues commonly publish two parallel greek sets for the same
1150/// instrument: Black-Scholes greeks in USD, and price-adjusted greeks denominated
1151/// in the underlying/coin units. Deribit and OKX both expose the distinction;
1152/// see the OKX reference for the canonical definition:
1153/// <https://www.okx.com/docs-v5/en/#public-data-websocket-option-market-data>.
1154///
1155/// This is orthogonal to the percent-greeks transformation in the internal
1156/// [`GreeksCalculator`](../../../nautilus_common/greeks/struct.GreeksCalculator.html),
1157/// which rescales the delta/gamma input step rather than the numeraire.
1158#[repr(C)]
1159#[derive(
1160    Copy,
1161    Clone,
1162    Debug,
1163    Default,
1164    Display,
1165    Hash,
1166    PartialEq,
1167    Eq,
1168    PartialOrd,
1169    Ord,
1170    AsRefStr,
1171    FromRepr,
1172    EnumIter,
1173    EnumString,
1174)]
1175#[strum(ascii_case_insensitive)]
1176#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
1177#[cfg_attr(
1178    feature = "python",
1179    pyo3::pyclass(
1180        frozen,
1181        eq,
1182        eq_int,
1183        module = "nautilus_trader.core.nautilus_pyo3.model.enums",
1184        from_py_object,
1185        rename_all = "SCREAMING_SNAKE_CASE",
1186    )
1187)]
1188#[cfg_attr(
1189    feature = "python",
1190    pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.model")
1191)]
1192pub enum GreeksConvention {
1193    /// Black-Scholes greeks in USD.
1194    #[default]
1195    BlackScholes = 1,
1196    /// Price-adjusted greeks in the underlying/coin units.
1197    PriceAdjusted = 2,
1198}
1199
1200/// Defines when OTO (One-Triggers-Other) child orders are released.
1201#[repr(C)]
1202#[derive(
1203    Copy,
1204    Clone,
1205    Debug,
1206    Default,
1207    Display,
1208    Hash,
1209    PartialEq,
1210    Eq,
1211    PartialOrd,
1212    Ord,
1213    AsRefStr,
1214    FromRepr,
1215    EnumIter,
1216    EnumString,
1217)]
1218#[strum(ascii_case_insensitive)]
1219#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
1220#[cfg_attr(
1221    feature = "python",
1222    pyo3::pyclass(
1223        frozen,
1224        eq,
1225        eq_int,
1226        module = "nautilus_trader.core.nautilus_pyo3.model.enums",
1227        from_py_object,
1228        rename_all = "SCREAMING_SNAKE_CASE",
1229    )
1230)]
1231#[cfg_attr(
1232    feature = "python",
1233    pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.model")
1234)]
1235pub enum OtoTriggerMode {
1236    /// Release child order(s) pro-rata to each partial fill (default).
1237    #[default]
1238    Partial = 0,
1239    /// Release child order(s) only once the parent is fully filled.
1240    Full = 1,
1241}
1242
1243/// The order side for a specific order, or action related to orders.
1244#[repr(C)]
1245#[derive(
1246    Copy,
1247    Clone,
1248    Debug,
1249    Default,
1250    Display,
1251    Hash,
1252    PartialEq,
1253    Eq,
1254    PartialOrd,
1255    Ord,
1256    AsRefStr,
1257    FromRepr,
1258    EnumIter,
1259    EnumString,
1260)]
1261#[strum(ascii_case_insensitive)]
1262#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
1263#[cfg_attr(
1264    feature = "python",
1265    pyo3::pyclass(
1266        frozen,
1267        eq,
1268        eq_int,
1269        module = "nautilus_trader.core.nautilus_pyo3.model.enums",
1270        from_py_object,
1271        rename_all = "SCREAMING_SNAKE_CASE",
1272    )
1273)]
1274#[cfg_attr(
1275    feature = "python",
1276    pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.model")
1277)]
1278pub enum OrderSide {
1279    /// No order side is specified.
1280    #[default]
1281    NoOrderSide = 0,
1282    /// The order is a BUY.
1283    Buy = 1,
1284    /// The order is a SELL.
1285    Sell = 2,
1286}
1287
1288impl OrderSide {
1289    /// Returns the specified [`OrderSideSpecified`] (BUY or SELL) for this side.
1290    ///
1291    /// # Panics
1292    ///
1293    /// Panics if `self` is [`OrderSide::NoOrderSide`].
1294    #[must_use]
1295    pub fn as_specified(&self) -> OrderSideSpecified {
1296        match &self {
1297            Self::Buy => OrderSideSpecified::Buy,
1298            Self::Sell => OrderSideSpecified::Sell,
1299            Self::NoOrderSide => panic!("Order invariant failed: side must be `Buy` or `Sell`"),
1300        }
1301    }
1302}
1303
1304/// Convert the given `value` to an [`OrderSide`].
1305impl FromU8 for OrderSide {
1306    fn from_u8(value: u8) -> Option<Self> {
1307        match value {
1308            0 => Some(Self::NoOrderSide),
1309            1 => Some(Self::Buy),
1310            2 => Some(Self::Sell),
1311            _ => None,
1312        }
1313    }
1314}
1315
1316/// The specified order side (BUY or SELL).
1317#[repr(C)]
1318#[derive(
1319    Copy,
1320    Clone,
1321    Debug,
1322    Display,
1323    Hash,
1324    PartialEq,
1325    Eq,
1326    PartialOrd,
1327    Ord,
1328    AsRefStr,
1329    FromRepr,
1330    EnumIter,
1331    EnumString,
1332)]
1333#[strum(ascii_case_insensitive)]
1334#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
1335pub enum OrderSideSpecified {
1336    /// The order is a BUY.
1337    Buy = 1,
1338    /// The order is a SELL.
1339    Sell = 2,
1340}
1341
1342impl OrderSideSpecified {
1343    /// Returns the opposite order side.
1344    #[must_use]
1345    pub fn opposite(&self) -> Self {
1346        match &self {
1347            Self::Buy => Self::Sell,
1348            Self::Sell => Self::Buy,
1349        }
1350    }
1351
1352    /// Converts this specified side into an [`OrderSide`].
1353    #[must_use]
1354    pub fn as_order_side(&self) -> OrderSide {
1355        match &self {
1356            Self::Buy => OrderSide::Buy,
1357            Self::Sell => OrderSide::Sell,
1358        }
1359    }
1360}
1361
1362/// The status for a specific order.
1363///
1364/// An order is considered _open_ for the following status:
1365///  - `ACCEPTED`
1366///  - `TRIGGERED`
1367///  - `PENDING_UPDATE`
1368///  - `PENDING_CANCEL`
1369///  - `PARTIALLY_FILLED`
1370///
1371/// An order is considered _in-flight_ for the following status:
1372///  - `SUBMITTED`
1373///  - `PENDING_UPDATE`
1374///  - `PENDING_CANCEL`
1375///
1376/// An order is considered _closed_ for the following status:
1377///  - `DENIED`
1378///  - `REJECTED`
1379///  - `CANCELED`
1380///  - `EXPIRED`
1381///  - `FILLED`
1382///  - `VOIDED`
1383#[repr(C)]
1384#[derive(
1385    Copy,
1386    Clone,
1387    Debug,
1388    Display,
1389    Hash,
1390    PartialEq,
1391    Eq,
1392    PartialOrd,
1393    Ord,
1394    AsRefStr,
1395    FromRepr,
1396    EnumIter,
1397    EnumString,
1398)]
1399#[strum(ascii_case_insensitive)]
1400#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
1401#[cfg_attr(
1402    feature = "python",
1403    pyo3::pyclass(
1404        frozen,
1405        eq,
1406        eq_int,
1407        module = "nautilus_trader.core.nautilus_pyo3.model.enums",
1408        from_py_object,
1409        rename_all = "SCREAMING_SNAKE_CASE",
1410    )
1411)]
1412#[cfg_attr(
1413    feature = "python",
1414    pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.model")
1415)]
1416pub enum OrderStatus {
1417    /// The order is initialized (instantiated) within the Nautilus system.
1418    Initialized = 1,
1419    /// The order was denied by the Nautilus system, either for being invalid, unprocessable, or exceeding a risk limit.
1420    Denied = 2,
1421    /// The order became emulated by the Nautilus system in the `OrderEmulator` component.
1422    Emulated = 3,
1423    /// The order was released by the Nautilus system from the `OrderEmulator` component.
1424    Released = 4,
1425    /// The order was submitted by the Nautilus system to the external service or trading venue (awaiting acknowledgement).
1426    Submitted = 5,
1427    /// The order was acknowledged by the trading venue as being received and valid (may now be working).
1428    Accepted = 6,
1429    /// The order was rejected by the trading venue.
1430    Rejected = 7,
1431    /// The order was canceled (closed/done).
1432    Canceled = 8,
1433    /// The order reached a GTD expiration (closed/done).
1434    Expired = 9,
1435    /// The order STOP price was triggered on a trading venue.
1436    Triggered = 10,
1437    /// The order is currently pending a request to modify on a trading venue.
1438    PendingUpdate = 11,
1439    /// The order is currently pending a request to cancel on a trading venue.
1440    PendingCancel = 12,
1441    /// The order has been partially filled on a trading venue.
1442    PartiallyFilled = 13,
1443    /// The order has been completely filled on a trading venue (closed/done).
1444    Filled = 14,
1445    /// The order is terminal after an authoritative venue void or fill correction.
1446    Voided = 15,
1447}
1448
1449impl OrderStatus {
1450    /// Returns whether the order status represents an open/working order.
1451    #[must_use]
1452    pub const fn is_open(self) -> bool {
1453        matches!(
1454            self,
1455            Self::Submitted
1456                | Self::Accepted
1457                | Self::Triggered
1458                | Self::PendingUpdate
1459                | Self::PendingCancel
1460                | Self::PartiallyFilled
1461        )
1462    }
1463
1464    /// Returns whether the order status represents a terminal (closed) state.
1465    #[must_use]
1466    pub const fn is_closed(self) -> bool {
1467        matches!(
1468            self,
1469            Self::Denied
1470                | Self::Rejected
1471                | Self::Canceled
1472                | Self::Expired
1473                | Self::Filled
1474                | Self::Voided
1475        )
1476    }
1477
1478    /// Returns whether the order can be cancelled from this status.
1479    #[must_use]
1480    pub const fn is_cancellable(self) -> bool {
1481        matches!(
1482            self,
1483            Self::Accepted | Self::Triggered | Self::PendingUpdate | Self::PartiallyFilled
1484        )
1485    }
1486}
1487
1488/// The type of order.
1489#[repr(C)]
1490#[derive(
1491    Copy,
1492    Clone,
1493    Debug,
1494    Display,
1495    Hash,
1496    PartialEq,
1497    Eq,
1498    PartialOrd,
1499    Ord,
1500    AsRefStr,
1501    FromRepr,
1502    EnumIter,
1503    EnumString,
1504)]
1505#[strum(ascii_case_insensitive)]
1506#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
1507#[cfg_attr(
1508    feature = "python",
1509    pyo3::pyclass(
1510        frozen,
1511        eq,
1512        eq_int,
1513        module = "nautilus_trader.core.nautilus_pyo3.model.enums",
1514        from_py_object,
1515        rename_all = "SCREAMING_SNAKE_CASE",
1516    )
1517)]
1518#[cfg_attr(
1519    feature = "python",
1520    pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.model")
1521)]
1522pub enum OrderType {
1523    /// A market order to buy or sell at the best available price in the current market.
1524    Market = 1,
1525    /// A limit order to buy or sell at a specific price or better.
1526    Limit = 2,
1527    /// A stop market order to buy or sell once the price reaches the specified stop/trigger price. When the stop price is reached, the order effectively becomes a market order.
1528    StopMarket = 3,
1529    /// A stop limit order to buy or sell which combines the features of a stop order and a limit order. Once the stop/trigger price is reached, a stop-limit order effectively becomes a limit order.
1530    StopLimit = 4,
1531    /// A market-to-limit order is a market order that is to be executed as a limit order at the current best market price after reaching the market.
1532    MarketToLimit = 5,
1533    /// A market-if-touched order effectively becomes a market order when the specified trigger price is reached.
1534    MarketIfTouched = 6,
1535    /// A limit-if-touched order effectively becomes a limit order when the specified trigger price is reached.
1536    LimitIfTouched = 7,
1537    /// A trailing stop market order sets the stop/trigger price at a fixed "trailing offset" amount from the market.
1538    TrailingStopMarket = 8,
1539    /// A trailing stop limit order combines the features of a trailing stop order with those of a limit order.
1540    TrailingStopLimit = 9,
1541}
1542
1543/// The type of position adjustment.
1544#[repr(C)]
1545#[derive(
1546    Copy,
1547    Clone,
1548    Debug,
1549    Display,
1550    Hash,
1551    PartialEq,
1552    Eq,
1553    PartialOrd,
1554    Ord,
1555    AsRefStr,
1556    FromRepr,
1557    EnumIter,
1558    EnumString,
1559)]
1560#[strum(ascii_case_insensitive)]
1561#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
1562#[cfg_attr(
1563    feature = "python",
1564    pyo3::pyclass(
1565        frozen,
1566        eq,
1567        eq_int,
1568        module = "nautilus_trader.core.nautilus_pyo3.model.enums",
1569        from_py_object,
1570        rename_all = "SCREAMING_SNAKE_CASE",
1571    )
1572)]
1573#[cfg_attr(
1574    feature = "python",
1575    pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.model")
1576)]
1577pub enum PositionAdjustmentType {
1578    /// Commission adjustment affecting position quantity.
1579    Commission = 1,
1580    /// Funding payment affecting position realized PnL.
1581    Funding = 2,
1582}
1583
1584impl FromU8 for PositionAdjustmentType {
1585    fn from_u8(value: u8) -> Option<Self> {
1586        match value {
1587            1 => Some(Self::Commission),
1588            2 => Some(Self::Funding),
1589            _ => None,
1590        }
1591    }
1592}
1593
1594/// The market side for a specific position, or action related to positions.
1595#[repr(C)]
1596#[derive(
1597    Copy,
1598    Clone,
1599    Debug,
1600    Default,
1601    Display,
1602    Hash,
1603    PartialEq,
1604    Eq,
1605    PartialOrd,
1606    Ord,
1607    AsRefStr,
1608    FromRepr,
1609    EnumIter,
1610    EnumString,
1611)]
1612#[strum(ascii_case_insensitive)]
1613#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
1614#[cfg_attr(
1615    feature = "python",
1616    pyo3::pyclass(
1617        frozen,
1618        eq,
1619        eq_int,
1620        module = "nautilus_trader.core.nautilus_pyo3.model.enums",
1621        from_py_object,
1622        rename_all = "SCREAMING_SNAKE_CASE",
1623    )
1624)]
1625#[cfg_attr(
1626    feature = "python",
1627    pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.model")
1628)]
1629pub enum PositionSide {
1630    /// No position side is specified (only valid in the context of a filter for actions involving positions).
1631    #[default]
1632    NoPositionSide = 0,
1633    /// A neural/flat position, where no position is currently held in the market.
1634    Flat = 1,
1635    /// A long position in the market, typically acquired through one or many BUY orders.
1636    Long = 2,
1637    /// A short position in the market, typically acquired through one or many SELL orders.
1638    Short = 3,
1639}
1640
1641impl PositionSide {
1642    /// Returns the specified [`PositionSideSpecified`] (`Long`, `Short`, or `Flat`) for this side.
1643    ///
1644    /// # Panics
1645    ///
1646    /// Panics if `self` is [`PositionSide::NoPositionSide`].
1647    #[must_use]
1648    pub fn as_specified(&self) -> PositionSideSpecified {
1649        match &self {
1650            Self::Long => PositionSideSpecified::Long,
1651            Self::Short => PositionSideSpecified::Short,
1652            Self::Flat => PositionSideSpecified::Flat,
1653            Self::NoPositionSide => {
1654                panic!("Position invariant failed: side must be `Long`, `Short`, or `Flat`")
1655            }
1656        }
1657    }
1658}
1659
1660/// The market side for a specific position, or action related to positions.
1661#[repr(C)]
1662#[derive(
1663    Copy,
1664    Clone,
1665    Debug,
1666    Display,
1667    Hash,
1668    PartialEq,
1669    Eq,
1670    PartialOrd,
1671    Ord,
1672    AsRefStr,
1673    FromRepr,
1674    EnumIter,
1675    EnumString,
1676)]
1677#[strum(ascii_case_insensitive)]
1678#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
1679#[cfg_attr(
1680    feature = "python",
1681    pyo3::pyclass(
1682        frozen,
1683        eq,
1684        eq_int,
1685        module = "nautilus_trader.core.nautilus_pyo3.model.enums",
1686        from_py_object,
1687        rename_all = "SCREAMING_SNAKE_CASE",
1688    )
1689)]
1690#[cfg_attr(
1691    feature = "python",
1692    pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.model")
1693)]
1694pub enum PositionSideSpecified {
1695    /// A neural/flat position, where no position is currently held in the market.
1696    Flat = 1,
1697    /// A long position in the market, typically acquired through one or many BUY orders.
1698    Long = 2,
1699    /// A short position in the market, typically acquired through one or many SELL orders.
1700    Short = 3,
1701}
1702
1703impl PositionSideSpecified {
1704    /// Converts this specified side into a [`PositionSide`].
1705    #[must_use]
1706    pub fn as_position_side(&self) -> PositionSide {
1707        match &self {
1708            Self::Long => PositionSide::Long,
1709            Self::Short => PositionSide::Short,
1710            Self::Flat => PositionSide::Flat,
1711        }
1712    }
1713}
1714
1715/// The type of price for an instrument in a market.
1716#[repr(C)]
1717#[derive(
1718    Copy,
1719    Clone,
1720    Debug,
1721    Display,
1722    Hash,
1723    PartialEq,
1724    Eq,
1725    PartialOrd,
1726    Ord,
1727    AsRefStr,
1728    FromRepr,
1729    EnumIter,
1730    EnumString,
1731)]
1732#[strum(ascii_case_insensitive)]
1733#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
1734#[cfg_attr(
1735    feature = "python",
1736    pyo3::pyclass(
1737        frozen,
1738        eq,
1739        eq_int,
1740        module = "nautilus_trader.core.nautilus_pyo3.model.enums",
1741        from_py_object,
1742        rename_all = "SCREAMING_SNAKE_CASE",
1743    )
1744)]
1745#[cfg_attr(
1746    feature = "python",
1747    pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.model")
1748)]
1749pub enum PriceType {
1750    // TODO: Revisit during v2 cutover after Cython and legacy FFI removal. Make bar price
1751    // sources consistent with mark/index price subscriptions, including `PriceType::Index` and
1752    // internal bar aggregation from mark/index updates. Document the source derivation order.
1753    /// The best quoted price at which buyers are willing to buy a quantity of an instrument.
1754    /// Often considered the best bid in the order book.
1755    Bid = 1,
1756    /// The best quoted price at which sellers are willing to sell a quantity of an instrument.
1757    /// Often considered the best ask in the order book.
1758    Ask = 2,
1759    /// The arithmetic midpoint between the best bid and ask quotes.
1760    Mid = 3,
1761    /// The price at which the last trade of an instrument was executed.
1762    Last = 4,
1763    /// A reference price reflecting an instrument's fair value, often used for portfolio
1764    /// calculations and risk management.
1765    Mark = 5,
1766}
1767
1768/// A record flag bit field, indicating event end and data information.
1769#[repr(C)]
1770#[derive(
1771    Copy,
1772    Clone,
1773    Debug,
1774    Display,
1775    Hash,
1776    PartialEq,
1777    Eq,
1778    PartialOrd,
1779    Ord,
1780    AsRefStr,
1781    FromRepr,
1782    EnumIter,
1783    EnumString,
1784)]
1785#[strum(ascii_case_insensitive)]
1786#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
1787#[cfg_attr(
1788    feature = "python",
1789    pyo3::pyclass(
1790        frozen,
1791        eq,
1792        eq_int,
1793        module = "nautilus_trader.core.nautilus_pyo3.model.enums",
1794        from_py_object,
1795        rename_all = "SCREAMING_SNAKE_CASE",
1796    )
1797)]
1798#[cfg_attr(
1799    feature = "python",
1800    pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.model")
1801)]
1802#[allow(non_camel_case_types)]
1803pub enum RecordFlag {
1804    /// Last message in the book event or packet from the venue for a given `instrument_id`.
1805    F_LAST = 1 << 7, // 128
1806    /// Top-of-book message, not an individual order.
1807    F_TOB = 1 << 6, // 64
1808    /// Message sourced from a replay, such as a snapshot server.
1809    F_SNAPSHOT = 1 << 5, // 32
1810    /// Aggregated price level message, not an individual order.
1811    F_MBP = 1 << 4, // 16
1812    /// Reserved for future use.
1813    RESERVED_2 = 1 << 3, // 8
1814    /// Reserved for future use.
1815    RESERVED_1 = 1 << 2, // 4
1816}
1817
1818impl RecordFlag {
1819    /// Checks if the flag matches a given value.
1820    #[must_use]
1821    pub fn matches(self, value: u8) -> bool {
1822        (self as u8) & value != 0
1823    }
1824}
1825
1826/// The 'Time in Force' instruction for an order.
1827#[repr(C)]
1828#[derive(
1829    Copy,
1830    Clone,
1831    Debug,
1832    Display,
1833    Hash,
1834    PartialEq,
1835    Eq,
1836    PartialOrd,
1837    Ord,
1838    AsRefStr,
1839    FromRepr,
1840    EnumIter,
1841    EnumString,
1842)]
1843#[strum(ascii_case_insensitive)]
1844#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
1845#[cfg_attr(
1846    feature = "python",
1847    pyo3::pyclass(
1848        frozen,
1849        eq,
1850        eq_int,
1851        module = "nautilus_trader.core.nautilus_pyo3.model.enums",
1852        from_py_object,
1853        rename_all = "SCREAMING_SNAKE_CASE",
1854    )
1855)]
1856#[cfg_attr(
1857    feature = "python",
1858    pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.model")
1859)]
1860pub enum TimeInForce {
1861    /// Good Till Cancel (GTC) - Remains active until canceled.
1862    Gtc = 1,
1863    /// Immediate or Cancel (IOC) - Executes immediately to the extent possible, with any unfilled portion canceled.
1864    Ioc = 2,
1865    /// Fill or Kill (FOK) - Executes in its entirety immediately or is canceled if full execution is not possible.
1866    Fok = 3,
1867    /// Good Till Date (GTD) - Remains active until the specified expiration date or time is reached.
1868    Gtd = 4,
1869    /// Day - Remains active until the close of the current trading session.
1870    Day = 5,
1871    /// At the Opening (ATO) - Executes at the market opening or expires if not filled.
1872    AtTheOpen = 6,
1873    /// At the Closing (ATC) - Executes at the market close or expires if not filled.
1874    AtTheClose = 7,
1875}
1876
1877/// The trading state for a node.
1878#[repr(C)]
1879#[derive(
1880    Copy,
1881    Clone,
1882    Debug,
1883    Display,
1884    Hash,
1885    PartialEq,
1886    Eq,
1887    PartialOrd,
1888    Ord,
1889    AsRefStr,
1890    FromRepr,
1891    EnumIter,
1892    EnumString,
1893)]
1894#[strum(ascii_case_insensitive)]
1895#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
1896#[cfg_attr(
1897    feature = "python",
1898    pyo3::pyclass(
1899        frozen,
1900        eq,
1901        eq_int,
1902        module = "nautilus_trader.core.nautilus_pyo3.model.enums",
1903        from_py_object,
1904        rename_all = "SCREAMING_SNAKE_CASE",
1905    )
1906)]
1907#[cfg_attr(
1908    feature = "python",
1909    pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.model")
1910)]
1911pub enum TradingState {
1912    /// Normal trading operations.
1913    Active = 1,
1914    /// Trading is completely halted, no new order commands will be emitted.
1915    Halted = 2,
1916    /// Only order commands which would cancel order, or reduce position sizes are permitted.
1917    Reducing = 3,
1918}
1919
1920/// The trailing offset type for an order type which specifies a trailing stop/trigger or limit price.
1921#[repr(C)]
1922#[derive(
1923    Copy,
1924    Clone,
1925    Debug,
1926    Default,
1927    Display,
1928    Hash,
1929    PartialEq,
1930    Eq,
1931    PartialOrd,
1932    Ord,
1933    AsRefStr,
1934    FromRepr,
1935    EnumIter,
1936    EnumString,
1937)]
1938#[strum(ascii_case_insensitive)]
1939#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
1940#[cfg_attr(
1941    feature = "python",
1942    pyo3::pyclass(
1943        frozen,
1944        eq,
1945        eq_int,
1946        module = "nautilus_trader.core.nautilus_pyo3.model.enums",
1947        from_py_object,
1948        rename_all = "SCREAMING_SNAKE_CASE",
1949    )
1950)]
1951#[cfg_attr(
1952    feature = "python",
1953    pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.model")
1954)]
1955pub enum TrailingOffsetType {
1956    /// No trailing offset type is specified (invalid for trailing type orders).
1957    #[default]
1958    NoTrailingOffset = 0,
1959    /// The trailing offset is based on a market price.
1960    Price = 1,
1961    /// The trailing offset is based on a percentage represented in basis points, of a market price.
1962    BasisPoints = 2,
1963    /// The trailing offset is based on the number of ticks from a market price.
1964    Ticks = 3,
1965    /// The trailing offset is based on a price tier set by a specific trading venue.
1966    PriceTier = 4,
1967}
1968
1969/// The trigger type for the stop/trigger price of an order.
1970#[repr(C)]
1971#[derive(
1972    Copy,
1973    Clone,
1974    Debug,
1975    Default,
1976    Display,
1977    Hash,
1978    PartialEq,
1979    Eq,
1980    PartialOrd,
1981    Ord,
1982    AsRefStr,
1983    FromRepr,
1984    EnumIter,
1985    EnumString,
1986)]
1987#[strum(ascii_case_insensitive)]
1988#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
1989#[cfg_attr(
1990    feature = "python",
1991    pyo3::pyclass(
1992        frozen,
1993        eq,
1994        eq_int,
1995        module = "nautilus_trader.core.nautilus_pyo3.model.enums",
1996        from_py_object,
1997        rename_all = "SCREAMING_SNAKE_CASE",
1998    )
1999)]
2000#[cfg_attr(
2001    feature = "python",
2002    pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.model")
2003)]
2004pub enum TriggerType {
2005    /// No trigger type is specified (invalid for orders with a trigger).
2006    #[default]
2007    NoTrigger = 0,
2008    /// The default trigger type set by the trading venue.
2009    Default = 1,
2010    /// Based on the last traded price for the instrument.
2011    LastPrice = 2,
2012    /// Based on the mark price for the instrument.
2013    MarkPrice = 3,
2014    /// Based on the index price for the instrument.
2015    IndexPrice = 4,
2016    /// Based on the top-of-book quoted prices for the instrument.
2017    BidAsk = 5,
2018    /// Based on a 'double match' of the last traded price for the instrument
2019    DoubleLast = 6,
2020    /// Based on a 'double match' of the bid/ask price for the instrument
2021    DoubleBidAsk = 7,
2022    /// Based on both the [`TriggerType::LastPrice`] and [`TriggerType::BidAsk`].
2023    LastOrBidAsk = 8,
2024    /// Based on the mid-point of the [`TriggerType::BidAsk`].
2025    MidPoint = 9,
2026}
2027
2028enum_strum_serde!(AccountType);
2029enum_strum_serde!(AggregationSource);
2030enum_strum_serde!(AggressorSide);
2031enum_strum_serde!(AssetClass);
2032enum_strum_serde!(BarAggregation);
2033enum_strum_serde!(BarIntervalType);
2034enum_strum_serde!(BookAction);
2035enum_strum_serde!(BookType);
2036enum_strum_serde!(ContingencyType);
2037enum_strum_serde!(ContinuousFutureAdjustmentType);
2038enum_strum_serde!(CurrencyType);
2039enum_strum_serde!(GreeksConvention);
2040enum_strum_serde!(InstrumentClass);
2041enum_strum_serde!(InstrumentCloseType);
2042enum_strum_serde!(LiquiditySide);
2043enum_strum_serde!(MarketStatus);
2044enum_strum_serde!(MarketStatusAction);
2045enum_strum_serde!(OmsType);
2046enum_strum_serde!(OptionKind);
2047enum_strum_serde!(OrderSide);
2048enum_strum_serde!(OrderSideSpecified);
2049enum_strum_serde!(OrderStatus);
2050enum_strum_serde!(OrderType);
2051enum_strum_serde!(PositionAdjustmentType);
2052enum_strum_serde!(PositionSide);
2053enum_strum_serde!(PositionSideSpecified);
2054enum_strum_serde!(PriceType);
2055enum_strum_serde!(RecordFlag);
2056enum_strum_serde!(TimeInForce);
2057enum_strum_serde!(TradingState);
2058enum_strum_serde!(TrailingOffsetType);
2059enum_strum_serde!(TriggerType);
2060
2061#[cfg(test)]
2062mod tests {
2063    use rstest::rstest;
2064
2065    use super::*;
2066
2067    #[rstest]
2068    #[case::no_aggressor(0, Some(AggressorSide::NoAggressor))]
2069    #[case::buyer(1, Some(AggressorSide::Buyer))]
2070    #[case::seller(2, Some(AggressorSide::Seller))]
2071    #[case::invalid(3, None)]
2072    #[case::max_u8(255, None)]
2073    fn test_aggressor_side_from_u8(#[case] value: u8, #[case] expected: Option<AggressorSide>) {
2074        assert_eq!(AggressorSide::from_u8(value), expected);
2075    }
2076
2077    #[rstest]
2078    #[case(GreeksConvention::BlackScholes, "\"BLACK_SCHOLES\"")]
2079    #[case(GreeksConvention::PriceAdjusted, "\"PRICE_ADJUSTED\"")]
2080    fn test_greeks_convention_serde_roundtrip(
2081        #[case] input: GreeksConvention,
2082        #[case] expected: &str,
2083    ) {
2084        let json = serde_json::to_string(&input).unwrap();
2085        assert_eq!(json, expected);
2086        let parsed: GreeksConvention = serde_json::from_str(expected).unwrap();
2087        assert_eq!(parsed, input);
2088    }
2089
2090    #[rstest]
2091    fn test_greeks_convention_default_is_black_scholes() {
2092        assert_eq!(GreeksConvention::default(), GreeksConvention::BlackScholes);
2093    }
2094
2095    #[rstest]
2096    #[case(ContinuousFutureAdjustmentType::BackwardSpread, false, true)]
2097    #[case(ContinuousFutureAdjustmentType::ForwardSpread, false, false)]
2098    #[case(ContinuousFutureAdjustmentType::BackwardRatio, true, true)]
2099    #[case(ContinuousFutureAdjustmentType::ForwardRatio, true, false)]
2100    fn test_continuous_future_adjustment_type_predicates(
2101        #[case] mode: ContinuousFutureAdjustmentType,
2102        #[case] expected_is_ratio: bool,
2103        #[case] expected_is_backward: bool,
2104    ) {
2105        assert_eq!(mode.is_ratio(), expected_is_ratio);
2106        assert_eq!(mode.is_backward(), expected_is_backward);
2107    }
2108
2109    #[rstest]
2110    #[case(ContinuousFutureAdjustmentType::BackwardSpread, "\"BACKWARD_SPREAD\"")]
2111    #[case(ContinuousFutureAdjustmentType::ForwardSpread, "\"FORWARD_SPREAD\"")]
2112    #[case(ContinuousFutureAdjustmentType::BackwardRatio, "\"BACKWARD_RATIO\"")]
2113    #[case(ContinuousFutureAdjustmentType::ForwardRatio, "\"FORWARD_RATIO\"")]
2114    fn test_continuous_future_adjustment_type_serde_roundtrip(
2115        #[case] input: ContinuousFutureAdjustmentType,
2116        #[case] expected: &str,
2117    ) {
2118        let json = serde_json::to_string(&input).unwrap();
2119        assert_eq!(json, expected);
2120        let parsed: ContinuousFutureAdjustmentType = serde_json::from_str(expected).unwrap();
2121        assert_eq!(parsed, input);
2122    }
2123
2124    #[rstest]
2125    fn test_continuous_future_adjustment_type_default_is_backward_spread() {
2126        assert_eq!(
2127            ContinuousFutureAdjustmentType::default(),
2128            ContinuousFutureAdjustmentType::BackwardSpread,
2129        );
2130    }
2131
2132    #[rstest]
2133    #[case(InstrumentClass::Option, true)]
2134    #[case(InstrumentClass::FuturesSpread, true)]
2135    #[case(InstrumentClass::OptionSpread, true)]
2136    #[case(InstrumentClass::Spot, false)]
2137    #[case(InstrumentClass::Swap, false)]
2138    #[case(InstrumentClass::Future, false)]
2139    #[case(InstrumentClass::Forward, false)]
2140    #[case(InstrumentClass::Cfd, false)]
2141    #[case(InstrumentClass::Bond, false)]
2142    #[case(InstrumentClass::Warrant, false)]
2143    #[case(InstrumentClass::SportsBetting, false)]
2144    #[case(InstrumentClass::BinaryOption, false)]
2145    fn test_instrument_class_allows_negative_price(
2146        #[case] class: InstrumentClass,
2147        #[case] expected: bool,
2148    ) {
2149        assert_eq!(class.allows_negative_price(), expected);
2150    }
2151
2152    #[rstest]
2153    #[case("FUT", Some(InstrumentClass::Future))]
2154    #[case("FUTURE", Some(InstrumentClass::Future))]
2155    #[case("OPT", Some(InstrumentClass::Option))]
2156    #[case("OPTION", Some(InstrumentClass::Option))]
2157    #[case("fut", None)]
2158    #[case("Fut", None)]
2159    #[case("option", None)]
2160    #[case("Option", None)]
2161    #[case("SPREAD", None)]
2162    #[case("UNKNOWN", None)]
2163    #[case("", None)]
2164    fn test_instrument_class_try_from_parent_suffix(
2165        #[case] suffix: &str,
2166        #[case] expected: Option<InstrumentClass>,
2167    ) {
2168        assert_eq!(InstrumentClass::try_from_parent_suffix(suffix), expected);
2169    }
2170
2171    #[rstest]
2172    #[case(InstrumentClass::Future, Some("FUT"))]
2173    #[case(InstrumentClass::Option, Some("OPT"))]
2174    #[case(InstrumentClass::Spot, None)]
2175    #[case(InstrumentClass::Swap, None)]
2176    #[case(InstrumentClass::FuturesSpread, None)]
2177    #[case(InstrumentClass::Forward, None)]
2178    #[case(InstrumentClass::Cfd, None)]
2179    #[case(InstrumentClass::Bond, None)]
2180    #[case(InstrumentClass::OptionSpread, None)]
2181    #[case(InstrumentClass::Warrant, None)]
2182    #[case(InstrumentClass::SportsBetting, None)]
2183    #[case(InstrumentClass::BinaryOption, None)]
2184    fn test_instrument_class_parent_suffix(
2185        #[case] class: InstrumentClass,
2186        #[case] expected: Option<&'static str>,
2187    ) {
2188        assert_eq!(class.parent_suffix(), expected);
2189    }
2190
2191    #[rstest]
2192    #[case(InstrumentClass::Future)]
2193    #[case(InstrumentClass::Option)]
2194    fn test_instrument_class_parent_suffix_roundtrip(#[case] class: InstrumentClass) {
2195        let suffix = class.parent_suffix().unwrap();
2196        assert_eq!(InstrumentClass::try_from_parent_suffix(suffix), Some(class));
2197    }
2198}