Skip to main content

nautilus_okx/common/
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 mapping OKX concepts onto idiomatic Nautilus variants.
17
18use nautilus_model::enums::{
19    AggressorSide, GreeksConvention, LiquiditySide, OptionKind, OrderSide, OrderStatus, OrderType,
20    PositionSide, TriggerType,
21};
22use serde::{Deserialize, Serialize};
23use strum::{AsRefStr, Display, EnumIter, EnumString};
24
25use crate::common::consts::{OKX_ADVANCE_ALGO_ORDER_TYPES, OKX_CONDITIONAL_ORDER_TYPES};
26
27/// Represents the type of book action.
28#[derive(
29    Copy,
30    Clone,
31    Debug,
32    Display,
33    PartialEq,
34    Eq,
35    Hash,
36    AsRefStr,
37    EnumIter,
38    EnumString,
39    Serialize,
40    Deserialize,
41)]
42#[serde(rename_all = "lowercase")]
43pub enum OKXBookAction {
44    /// Incremental update.
45    Update,
46    /// Full snapshot.
47    Snapshot,
48}
49
50/// Represents the possible states of an order throughout its lifecycle.
51#[derive(
52    Copy,
53    Clone,
54    Debug,
55    Display,
56    PartialEq,
57    Eq,
58    Hash,
59    AsRefStr,
60    EnumIter,
61    EnumString,
62    Serialize,
63    Deserialize,
64)]
65pub enum OKXCandleConfirm {
66    /// K-line is incomplete.
67    #[serde(rename = "0")]
68    Partial,
69    /// K-line is completed.
70    #[serde(rename = "1")]
71    Closed,
72}
73
74/// Represents the side of an order or trade (Buy/Sell).
75#[derive(
76    Copy,
77    Clone,
78    Debug,
79    Display,
80    PartialEq,
81    Eq,
82    Hash,
83    AsRefStr,
84    EnumIter,
85    EnumString,
86    Serialize,
87    Deserialize,
88)]
89#[serde(rename_all = "snake_case")]
90pub enum OKXSide {
91    /// Buy side of a trade or order.
92    Buy,
93    /// Sell side of a trade or order.
94    Sell,
95}
96
97impl From<OrderSide> for OKXSide {
98    fn from(value: OrderSide) -> Self {
99        match value {
100            OrderSide::Buy => Self::Buy,
101            OrderSide::Sell => Self::Sell,
102        }
103    }
104}
105
106impl From<OKXSide> for AggressorSide {
107    fn from(value: OKXSide) -> Self {
108        match value {
109            OKXSide::Buy => Self::Buy,
110            OKXSide::Sell => Self::Sell,
111        }
112    }
113}
114
115/// Represents the available order types on OKX.
116#[derive(
117    Copy,
118    Clone,
119    Debug,
120    Display,
121    PartialEq,
122    Eq,
123    Hash,
124    AsRefStr,
125    EnumIter,
126    EnumString,
127    Serialize,
128    Deserialize,
129)]
130#[serde(rename_all = "snake_case")]
131pub enum OKXOrderType {
132    /// Market order, executed immediately at current market price.
133    Market,
134    /// Limit order, executed only at specified price or better.
135    Limit,
136    /// Retail Price Improvement order.
137    #[serde(alias = "elp")]
138    Rpi,
139    PostOnly,        // limit only, requires "px" to be provided
140    Fok,             // Market order if "px" is not provided, otherwise limit order
141    Ioc,             // Market order if "px" is not provided, otherwise limit order
142    OptimalLimitIoc, // Market order with immediate-or-cancel order
143    Mmp,             // Market Maker Protection (only applicable to Option in Portfolio Margin mode)
144    MmpAndPostOnly, // Market Maker Protection and Post-only order(only applicable to Option in Portfolio Margin mode)
145    OpFok,          // Fill-or-Kill for options (only applicable to Option)
146    Trigger,        // Conditional/algo order (stop orders, etc.)
147    /// Forward-compatible fallback for order types OKX adds later.
148    #[serde(other)]
149    Other,
150}
151
152/// Represents the possible states of an order throughout its lifecycle.
153#[derive(
154    Copy,
155    Clone,
156    Debug,
157    Display,
158    PartialEq,
159    Eq,
160    Hash,
161    AsRefStr,
162    EnumIter,
163    EnumString,
164    Serialize,
165    Deserialize,
166)]
167#[serde(rename_all = "snake_case")]
168#[cfg_attr(
169    feature = "python",
170    pyo3::pyclass(
171        eq,
172        eq_int,
173        module = "nautilus_trader.adapters.okx",
174        from_py_object,
175        rename_all = "SCREAMING_SNAKE_CASE",
176    )
177)]
178#[cfg_attr(
179    feature = "python",
180    pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.adapters.okx")
181)]
182pub enum OKXOrderStatus {
183    Canceled,
184    Live,
185    PartiallyFilled,
186    Filled,
187    MmpCanceled,
188    /// Forward-compatible fallback for order states OKX adds later.
189    #[serde(other)]
190    Unknown,
191}
192
193impl TryFrom<OrderStatus> for OKXOrderStatus {
194    type Error = OrderStatus;
195
196    /// Converts a Nautilus [`OrderStatus`] into the matching [`OKXOrderStatus`].
197    ///
198    /// Returns the original [`OrderStatus`] in the error case for any variant
199    /// that has no representable OKX equivalent (e.g. `Submitted`, `PendingNew`,
200    /// `Triggered`, `PendingCancel`, `Expired`, `Rejected`).
201    fn try_from(value: OrderStatus) -> Result<Self, Self::Error> {
202        match value {
203            OrderStatus::Canceled => Ok(Self::Canceled),
204            OrderStatus::Accepted => Ok(Self::Live),
205            OrderStatus::PartiallyFilled => Ok(Self::PartiallyFilled),
206            OrderStatus::Filled => Ok(Self::Filled),
207            other => Err(other),
208        }
209    }
210}
211
212/// Represents the type of execution that generated a trade.
213#[derive(
214    Copy,
215    Clone,
216    Debug,
217    Default,
218    Display,
219    PartialEq,
220    Eq,
221    Hash,
222    AsRefStr,
223    EnumIter,
224    EnumString,
225    Serialize,
226    Deserialize,
227)]
228pub enum OKXExecType {
229    #[serde(rename = "")]
230    #[default]
231    None,
232    #[serde(rename = "T")]
233    Taker,
234    #[serde(rename = "M")]
235    Maker,
236}
237
238impl From<LiquiditySide> for OKXExecType {
239    fn from(value: LiquiditySide) -> Self {
240        match value {
241            LiquiditySide::NoLiquiditySide => Self::None,
242            LiquiditySide::Taker => Self::Taker,
243            LiquiditySide::Maker => Self::Maker,
244        }
245    }
246}
247
248/// Represents instrument types on OKX.
249#[derive(
250    Copy,
251    Clone,
252    Debug,
253    Display,
254    Default,
255    PartialEq,
256    Eq,
257    Hash,
258    AsRefStr,
259    EnumIter,
260    EnumString,
261    Serialize,
262    Deserialize,
263)]
264#[serde(rename_all = "UPPERCASE")]
265#[cfg_attr(
266    feature = "python",
267    pyo3::pyclass(
268        eq,
269        eq_int,
270        module = "nautilus_trader.adapters.okx",
271        from_py_object,
272        rename_all = "SCREAMING_SNAKE_CASE",
273    )
274)]
275#[cfg_attr(
276    feature = "python",
277    pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.adapters.okx")
278)]
279pub enum OKXInstrumentType {
280    #[default]
281    /// Any product type.
282    Any,
283    /// Spot products.
284    Spot,
285    /// Margin products.
286    Margin,
287    /// Swap products.
288    Swap,
289    /// Futures products.
290    Futures,
291    /// Option products.
292    Option,
293    /// Event contract products.
294    Events,
295}
296
297/// Represents an OKX instrument category code (the `instCategory` field).
298///
299/// OKX also returns a deprecated `category` field that is effectively always
300/// `"1"`; `instCategory` is the meaningful value that drives asset-class
301/// mapping. Unknown or future codes fall back to
302/// [`OKXInstrumentCategory::Unknown`] rather than failing to parse.
303#[derive(
304    Copy,
305    Clone,
306    Debug,
307    Default,
308    Display,
309    PartialEq,
310    Eq,
311    Hash,
312    AsRefStr,
313    EnumIter,
314    EnumString,
315    Serialize,
316    Deserialize,
317)]
318pub enum OKXInstrumentCategory {
319    /// Cryptocurrency (`"1"`).
320    #[serde(rename = "1")]
321    #[strum(serialize = "1")]
322    Crypto,
323    /// Equity-linked (`"3"`).
324    #[serde(rename = "3")]
325    #[strum(serialize = "3")]
326    Equity,
327    /// Commodity-linked (`"4"`).
328    #[serde(rename = "4")]
329    #[strum(serialize = "4")]
330    Commodity,
331    /// FX-linked (`"5"`).
332    #[serde(rename = "5")]
333    #[strum(serialize = "5")]
334    Fx,
335    /// Debt-linked (`"6"`).
336    #[serde(rename = "6")]
337    #[strum(serialize = "6")]
338    Debt,
339    /// Unknown or future category code.
340    #[default]
341    #[serde(other)]
342    #[strum(serialize = "")]
343    Unknown,
344}
345
346/// Represents an instrument status on OKX.
347#[derive(
348    Copy,
349    Clone,
350    Debug,
351    Display,
352    PartialEq,
353    Eq,
354    Hash,
355    AsRefStr,
356    EnumIter,
357    EnumString,
358    Serialize,
359    Deserialize,
360)]
361#[serde(rename_all = "snake_case")]
362pub enum OKXInstrumentStatus {
363    Live,
364    Suspend,
365    Preopen,
366    Test,
367    PostOnly,
368    Rebase,
369    Settling,
370    /// Unknown or future status.
371    #[serde(other)]
372    Unknown,
373}
374
375/// Represents a spread type on OKX.
376#[derive(
377    Copy,
378    Clone,
379    Debug,
380    Display,
381    PartialEq,
382    Eq,
383    Hash,
384    AsRefStr,
385    EnumIter,
386    EnumString,
387    Serialize,
388    Deserialize,
389)]
390#[serde(rename_all = "snake_case")]
391pub enum OKXSpreadType {
392    Linear,
393    Inverse,
394    Hybrid,
395    /// Unknown or future spread type.
396    #[serde(other)]
397    Unknown,
398}
399
400/// Represents a spread status on OKX.
401#[derive(
402    Copy,
403    Clone,
404    Debug,
405    Display,
406    PartialEq,
407    Eq,
408    Hash,
409    AsRefStr,
410    EnumIter,
411    EnumString,
412    Serialize,
413    Deserialize,
414)]
415#[serde(rename_all = "snake_case")]
416pub enum OKXSpreadState {
417    Live,
418    Suspend,
419    Expired,
420    /// Unknown or future status.
421    #[serde(other)]
422    Unknown,
423}
424
425/// Represents an instrument contract type on OKX.
426#[derive(
427    Copy,
428    Clone,
429    Default,
430    Debug,
431    Display,
432    PartialEq,
433    Eq,
434    Hash,
435    AsRefStr,
436    EnumIter,
437    EnumString,
438    Serialize,
439    Deserialize,
440)]
441#[serde(rename_all = "snake_case")]
442#[cfg_attr(
443    feature = "python",
444    pyo3::pyclass(
445        eq,
446        eq_int,
447        module = "nautilus_trader.adapters.okx",
448        from_py_object,
449        rename_all = "SCREAMING_SNAKE_CASE",
450    )
451)]
452#[cfg_attr(
453    feature = "python",
454    pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.adapters.okx")
455)]
456pub enum OKXContractType {
457    #[serde(rename = "")]
458    #[default]
459    None,
460    Linear,
461    Inverse,
462}
463
464/// Represents an option type on OKX.
465#[derive(
466    Copy,
467    Clone,
468    Debug,
469    Display,
470    PartialEq,
471    Eq,
472    Hash,
473    AsRefStr,
474    EnumIter,
475    EnumString,
476    Serialize,
477    Deserialize,
478)]
479pub enum OKXOptionType {
480    #[serde(rename = "")]
481    None,
482    #[serde(rename = "C")]
483    Call,
484    #[serde(rename = "P")]
485    Put,
486}
487
488impl TryFrom<OKXOptionType> for OptionKind {
489    type Error = OKXOptionType;
490
491    /// Converts an OKX option type into the matching Nautilus [`OptionKind`].
492    ///
493    /// Returns the source variant in the error case for [`OKXOptionType::None`]
494    /// (sent by OKX as an empty `optType` for non-option instruments and the
495    /// occasional malformed payload). Callers should skip such instruments
496    /// rather than treating the unknown variant as a default option kind.
497    fn try_from(option_type: OKXOptionType) -> Result<Self, Self::Error> {
498        match option_type {
499            OKXOptionType::Call => Ok(Self::Call),
500            OKXOptionType::Put => Ok(Self::Put),
501            other => Err(other),
502        }
503    }
504}
505
506/// Represents the convention used for option greeks on OKX.
507///
508/// OKX publishes two parallel greek sets on `opt-summary` and related endpoints:
509/// Black-Scholes greeks denominated in USD, and price-adjusted greeks denominated
510/// in the underlying/coin units.
511#[derive(
512    Copy,
513    Clone,
514    Debug,
515    Default,
516    Display,
517    PartialEq,
518    Eq,
519    Hash,
520    AsRefStr,
521    EnumIter,
522    EnumString,
523    Serialize,
524    Deserialize,
525)]
526#[serde(rename_all = "UPPERCASE")]
527#[strum(serialize_all = "UPPERCASE", ascii_case_insensitive)]
528#[cfg_attr(
529    feature = "python",
530    pyo3::pyclass(
531        eq,
532        eq_int,
533        module = "nautilus_trader.adapters.okx",
534        from_py_object,
535        rename_all = "SCREAMING_SNAKE_CASE",
536    )
537)]
538#[cfg_attr(
539    feature = "python",
540    pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.adapters.okx")
541)]
542pub enum OKXGreeksType {
543    /// Black-Scholes greeks in USD.
544    #[default]
545    Bs = 0,
546    /// Price-adjusted greeks in the underlying/coin units.
547    Pa = 1,
548}
549
550impl From<u8> for OKXGreeksType {
551    fn from(value: u8) -> Self {
552        match value {
553            0 => Self::Bs,
554            1 => Self::Pa,
555            _ => {
556                log::warn!("Invalid OKXGreeksType {value}, defaulting to Bs");
557                Self::Bs
558            }
559        }
560    }
561}
562
563impl From<GreeksConvention> for OKXGreeksType {
564    fn from(convention: GreeksConvention) -> Self {
565        match convention {
566            GreeksConvention::BlackScholes => Self::Bs,
567            GreeksConvention::PriceAdjusted => Self::Pa,
568        }
569    }
570}
571
572impl From<OKXGreeksType> for GreeksConvention {
573    fn from(greeks_type: OKXGreeksType) -> Self {
574        match greeks_type {
575            OKXGreeksType::Bs => Self::BlackScholes,
576            OKXGreeksType::Pa => Self::PriceAdjusted,
577        }
578    }
579}
580
581/// Represents the trading mode for OKX orders.
582#[derive(
583    Copy,
584    Clone,
585    Debug,
586    Display,
587    Default,
588    PartialEq,
589    Eq,
590    Hash,
591    AsRefStr,
592    EnumIter,
593    EnumString,
594    Serialize,
595    Deserialize,
596)]
597#[serde(rename_all = "snake_case")]
598#[strum(ascii_case_insensitive)]
599#[cfg_attr(
600    feature = "python",
601    pyo3::pyclass(
602        eq,
603        eq_int,
604        module = "nautilus_trader.adapters.okx",
605        from_py_object,
606        rename_all = "SCREAMING_SNAKE_CASE",
607    )
608)]
609#[cfg_attr(
610    feature = "python",
611    pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.adapters.okx")
612)]
613pub enum OKXTradeMode {
614    #[default]
615    Cash,
616    Isolated,
617    Cross,
618    #[strum(serialize = "spot_isolated")]
619    SpotIsolated,
620}
621
622/// Represents an OKX account mode.
623///
624/// # References
625///
626/// <https://www.okx.com/docs-v5/en/#overview-account-mode>
627#[derive(
628    Copy,
629    Clone,
630    Debug,
631    Display,
632    PartialEq,
633    Eq,
634    Hash,
635    AsRefStr,
636    EnumIter,
637    EnumString,
638    Serialize,
639    Deserialize,
640)]
641pub enum OKXAccountMode {
642    #[serde(rename = "Spot mode")]
643    Spot,
644    #[serde(rename = "Spot and futures mode")]
645    SpotAndFutures,
646    #[serde(rename = "Multi-currency margin mode")]
647    MultiCurrencyMarginMode,
648    #[serde(rename = "Portfolio margin mode")]
649    PortfolioMarginMode,
650}
651
652/// Represents the margin mode for OKX accounts.
653///
654/// # Reference
655///
656/// - <https://www.okx.com/en-au/help/iv-isolated-margin-mode>
657/// - <https://www.okx.com/en-au/help/iii-single-currency-margin-cross-margin-trading>
658/// - <https://www.okx.com/en-au/help/iv-multi-currency-margin-mode-cross-margin-trading>
659#[derive(
660    Copy,
661    Clone,
662    Default,
663    Debug,
664    Display,
665    PartialEq,
666    Eq,
667    Hash,
668    AsRefStr,
669    EnumIter,
670    EnumString,
671    Serialize,
672    Deserialize,
673)]
674#[serde(rename_all = "snake_case")]
675#[cfg_attr(
676    feature = "python",
677    pyo3::pyclass(
678        eq,
679        eq_int,
680        module = "nautilus_trader.adapters.okx",
681        from_py_object,
682        rename_all = "SCREAMING_SNAKE_CASE",
683    )
684)]
685#[cfg_attr(
686    feature = "python",
687    pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.adapters.okx")
688)]
689pub enum OKXMarginMode {
690    #[serde(rename = "")]
691    #[default]
692    None,
693    Isolated,
694    Cross,
695}
696
697/// Represents the position mode for OKX accounts.
698///
699/// # References
700///
701/// <https://www.okx.com/docs-v5/en/#trading-account-rest-api-set-position-mode>
702#[derive(
703    Copy,
704    Clone,
705    Default,
706    Debug,
707    Display,
708    PartialEq,
709    Eq,
710    Hash,
711    AsRefStr,
712    EnumIter,
713    EnumString,
714    Serialize,
715    Deserialize,
716)]
717#[cfg_attr(
718    feature = "python",
719    pyo3::pyclass(
720        eq,
721        eq_int,
722        module = "nautilus_trader.adapters.okx",
723        from_py_object,
724        rename_all = "SCREAMING_SNAKE_CASE",
725    )
726)]
727#[cfg_attr(
728    feature = "python",
729    pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.adapters.okx")
730)]
731pub enum OKXPositionMode {
732    #[default]
733    #[serde(rename = "net_mode")]
734    NetMode,
735    #[serde(rename = "long_short_mode")]
736    LongShortMode,
737}
738
739#[derive(
740    Copy,
741    Clone,
742    Debug,
743    Display,
744    PartialEq,
745    Eq,
746    Hash,
747    AsRefStr,
748    EnumIter,
749    EnumString,
750    Serialize,
751    Deserialize,
752)]
753#[serde(rename_all = "snake_case")]
754pub enum OKXPositionSide {
755    #[serde(rename = "")]
756    None,
757    Net,
758    Long,
759    Short,
760}
761
762#[derive(
763    Copy,
764    Clone,
765    Debug,
766    Default,
767    Display,
768    PartialEq,
769    Eq,
770    Hash,
771    AsRefStr,
772    EnumIter,
773    EnumString,
774    Serialize,
775    Deserialize,
776)]
777#[serde(rename_all = "snake_case")]
778pub enum OKXSelfTradePreventionMode {
779    #[default]
780    #[serde(rename = "")]
781    None,
782    CancelMaker,
783    CancelTaker,
784    CancelBoth,
785}
786
787#[derive(
788    Copy,
789    Clone,
790    Debug,
791    Display,
792    PartialEq,
793    Eq,
794    Hash,
795    AsRefStr,
796    EnumIter,
797    EnumString,
798    Serialize,
799    Deserialize,
800)]
801#[serde(rename_all = "snake_case")]
802pub enum OKXTakeProfitKind {
803    #[serde(rename = "")]
804    None,
805    Condition,
806    Limit,
807}
808
809#[derive(
810    Copy,
811    Clone,
812    Debug,
813    Default,
814    Display,
815    PartialEq,
816    Eq,
817    Hash,
818    AsRefStr,
819    EnumIter,
820    EnumString,
821    Serialize,
822    Deserialize,
823)]
824#[serde(rename_all = "snake_case")]
825#[strum(serialize_all = "snake_case", ascii_case_insensitive)]
826pub enum OKXTriggerType {
827    #[default]
828    #[serde(rename = "")]
829    None,
830    Last,
831    Index,
832    Mark,
833}
834
835impl From<TriggerType> for OKXTriggerType {
836    fn from(value: TriggerType) -> Self {
837        match value {
838            TriggerType::LastPrice => Self::Last,
839            TriggerType::MarkPrice => Self::Mark,
840            TriggerType::IndexPrice => Self::Index,
841            _ => Self::Last,
842        }
843    }
844}
845
846#[cfg(test)]
847mod tests {
848    use std::str::FromStr;
849
850    use nautilus_model::enums::{GreeksConvention, OptionKind, OrderStatus, OrderType};
851    use rstest::rstest;
852
853    use super::{
854        OKXAlgoOrderStatus, OKXAlgoOrderType, OKXGreeksType, OKXOptionType, OKXOrderStatus,
855        OKXOrderType, OKXRpiPermission, OKXTriggerType,
856    };
857
858    #[rstest]
859    fn test_okx_trigger_type_from_str_accepts_snake_case_values() {
860        assert_eq!(
861            OKXTriggerType::from_str("last").unwrap(),
862            OKXTriggerType::Last
863        );
864        assert_eq!(
865            OKXTriggerType::from_str("mark").unwrap(),
866            OKXTriggerType::Mark
867        );
868        assert_eq!(
869            OKXTriggerType::from_str("index").unwrap(),
870            OKXTriggerType::Index
871        );
872    }
873
874    #[rstest]
875    #[case(OKXGreeksType::Bs, "\"BS\"")]
876    #[case(OKXGreeksType::Pa, "\"PA\"")]
877    fn test_greeks_type_serde_roundtrip(#[case] input: OKXGreeksType, #[case] expected: &str) {
878        let json = serde_json::to_string(&input).unwrap();
879        assert_eq!(json, expected);
880        let parsed: OKXGreeksType = serde_json::from_str(expected).unwrap();
881        assert_eq!(parsed, input);
882    }
883
884    #[rstest]
885    fn test_greeks_type_default_is_bs() {
886        assert_eq!(OKXGreeksType::default(), OKXGreeksType::Bs);
887    }
888
889    #[rstest]
890    fn test_greeks_type_from_u8() {
891        assert_eq!(OKXGreeksType::from(0_u8), OKXGreeksType::Bs);
892        assert_eq!(OKXGreeksType::from(1_u8), OKXGreeksType::Pa);
893        assert_eq!(OKXGreeksType::from(99_u8), OKXGreeksType::Bs);
894    }
895
896    #[rstest]
897    #[case(GreeksConvention::BlackScholes, OKXGreeksType::Bs)]
898    #[case(GreeksConvention::PriceAdjusted, OKXGreeksType::Pa)]
899    fn test_greeks_type_convention_roundtrip(
900        #[case] convention: GreeksConvention,
901        #[case] expected: OKXGreeksType,
902    ) {
903        let mapped: OKXGreeksType = convention.into();
904        assert_eq!(mapped, expected);
905        let back: GreeksConvention = mapped.into();
906        assert_eq!(back, convention);
907    }
908
909    #[rstest]
910    fn test_op_fok_serializes_to_snake_case() {
911        let json = serde_json::to_string(&OKXOrderType::OpFok).unwrap();
912        assert_eq!(json, "\"op_fok\"");
913    }
914
915    #[rstest]
916    fn test_op_fok_deserializes_from_snake_case() {
917        let parsed: OKXOrderType = serde_json::from_str("\"op_fok\"").unwrap();
918        assert_eq!(parsed, OKXOrderType::OpFok);
919    }
920
921    #[rstest]
922    fn test_op_fok_converts_to_limit_order_type() {
923        let order_type: OrderType = OKXOrderType::OpFok.try_into().unwrap();
924        assert_eq!(order_type, OrderType::Limit);
925    }
926
927    #[rstest]
928    fn test_rpi_order_type_serializes_current_name_and_reads_legacy_alias() {
929        assert_eq!(
930            serde_json::to_string(&OKXOrderType::Rpi).unwrap(),
931            "\"rpi\""
932        );
933        assert_eq!(
934            serde_json::from_str::<OKXOrderType>("\"elp\"").unwrap(),
935            OKXOrderType::Rpi
936        );
937        assert_eq!(
938            OrderType::try_from(OKXOrderType::Rpi).unwrap(),
939            OrderType::Limit
940        );
941    }
942
943    #[rstest]
944    #[case("\"0\"", OKXRpiPermission::Disabled)]
945    #[case("\"1\"", OKXRpiPermission::Enabled)]
946    #[case("\"2\"", OKXRpiPermission::Permitted)]
947    fn test_rpi_permission_deserializes_string_codes(
948        #[case] json: &str,
949        #[case] expected: OKXRpiPermission,
950    ) {
951        assert_eq!(
952            serde_json::from_str::<OKXRpiPermission>(json).unwrap(),
953            expected
954        );
955    }
956
957    #[rstest]
958    #[case::call(OKXOptionType::Call, Ok(OptionKind::Call))]
959    #[case::put(OKXOptionType::Put, Ok(OptionKind::Put))]
960    #[case::none(OKXOptionType::None, Err(OKXOptionType::None))]
961    fn test_try_from_okx_option_type(
962        #[case] input: OKXOptionType,
963        #[case] expected: Result<OptionKind, OKXOptionType>,
964    ) {
965        let actual: Result<OptionKind, OKXOptionType> = input.try_into();
966        assert_eq!(actual, expected);
967    }
968
969    #[rstest]
970    #[case::canceled(OrderStatus::Canceled, Ok(OKXOrderStatus::Canceled))]
971    #[case::accepted(OrderStatus::Accepted, Ok(OKXOrderStatus::Live))]
972    #[case::partially_filled(OrderStatus::PartiallyFilled, Ok(OKXOrderStatus::PartiallyFilled))]
973    #[case::filled(OrderStatus::Filled, Ok(OKXOrderStatus::Filled))]
974    #[case::submitted(OrderStatus::Submitted, Err(OrderStatus::Submitted))]
975    #[case::pending_update(OrderStatus::PendingUpdate, Err(OrderStatus::PendingUpdate))]
976    #[case::pending_cancel(OrderStatus::PendingCancel, Err(OrderStatus::PendingCancel))]
977    #[case::triggered(OrderStatus::Triggered, Err(OrderStatus::Triggered))]
978    #[case::expired(OrderStatus::Expired, Err(OrderStatus::Expired))]
979    #[case::rejected(OrderStatus::Rejected, Err(OrderStatus::Rejected))]
980    fn test_try_from_order_status(
981        #[case] input: OrderStatus,
982        #[case] expected: Result<OKXOrderStatus, OrderStatus>,
983    ) {
984        let actual: Result<OKXOrderStatus, OrderStatus> = input.try_into();
985        assert_eq!(actual, expected);
986    }
987
988    #[rstest]
989    #[case::live(OKXOrderStatus::Live, Ok(OrderStatus::Accepted))]
990    #[case::partially_filled(OKXOrderStatus::PartiallyFilled, Ok(OrderStatus::PartiallyFilled))]
991    #[case::filled(OKXOrderStatus::Filled, Ok(OrderStatus::Filled))]
992    #[case::canceled(OKXOrderStatus::Canceled, Ok(OrderStatus::Canceled))]
993    #[case::mmp_canceled(OKXOrderStatus::MmpCanceled, Ok(OrderStatus::Canceled))]
994    #[case::unknown(OKXOrderStatus::Unknown, Err(OKXOrderStatus::Unknown))]
995    fn test_try_from_okx_order_status(
996        #[case] input: OKXOrderStatus,
997        #[case] expected: Result<OrderStatus, OKXOrderStatus>,
998    ) {
999        let actual: Result<OrderStatus, OKXOrderStatus> = input.try_into();
1000        assert_eq!(actual, expected);
1001    }
1002
1003    #[rstest]
1004    #[case::live(OKXAlgoOrderStatus::Live, Ok(OrderStatus::Accepted))]
1005    #[case::pause(OKXAlgoOrderStatus::Pause, Ok(OrderStatus::Accepted))]
1006    #[case::effective(OKXAlgoOrderStatus::Effective, Ok(OrderStatus::Triggered))]
1007    #[case::order_placed(OKXAlgoOrderStatus::OrderPlaced, Ok(OrderStatus::Triggered))]
1008    #[case::partially_effective(OKXAlgoOrderStatus::PartiallyEffective, Ok(OrderStatus::Triggered))]
1009    #[case::filled(OKXAlgoOrderStatus::Filled, Ok(OrderStatus::Filled))]
1010    #[case::canceled(OKXAlgoOrderStatus::Canceled, Ok(OrderStatus::Canceled))]
1011    #[case::order_failed(OKXAlgoOrderStatus::OrderFailed, Ok(OrderStatus::Rejected))]
1012    #[case::partially_failed(OKXAlgoOrderStatus::PartiallyFailed, Ok(OrderStatus::Rejected))]
1013    #[case::unknown(OKXAlgoOrderStatus::Unknown, Err(OKXAlgoOrderStatus::Unknown))]
1014    fn test_try_from_okx_algo_order_status(
1015        #[case] input: OKXAlgoOrderStatus,
1016        #[case] expected: Result<OrderStatus, OKXAlgoOrderStatus>,
1017    ) {
1018        let actual: Result<OrderStatus, OKXAlgoOrderStatus> = input.try_into();
1019        assert_eq!(actual, expected);
1020    }
1021
1022    #[rstest]
1023    fn test_okx_order_status_deserializes_unknown_state_as_unknown() {
1024        let parsed: OKXOrderStatus = serde_json::from_str("\"future_state\"").unwrap();
1025        assert_eq!(parsed, OKXOrderStatus::Unknown);
1026
1027        let parsed: OKXOrderStatus = serde_json::from_str("\"mmp_canceled\"").unwrap();
1028        assert_eq!(parsed, OKXOrderStatus::MmpCanceled);
1029    }
1030
1031    #[rstest]
1032    fn test_okx_algo_order_status_deserializes_unknown_state_as_unknown() {
1033        let parsed: OKXAlgoOrderStatus = serde_json::from_str("\"future_state\"").unwrap();
1034        assert_eq!(parsed, OKXAlgoOrderStatus::Unknown);
1035    }
1036
1037    #[rstest]
1038    fn test_okx_order_type_deserializes_unknown_type_as_other() {
1039        let parsed: OKXOrderType = serde_json::from_str("\"future_ord_type\"").unwrap();
1040        assert_eq!(parsed, OKXOrderType::Other);
1041    }
1042
1043    #[rstest]
1044    fn test_okx_algo_order_type_deserializes_chase_and_unknown() {
1045        let parsed: OKXAlgoOrderType = serde_json::from_str("\"chase\"").unwrap();
1046        assert_eq!(parsed, OKXAlgoOrderType::Chase);
1047
1048        let parsed: OKXAlgoOrderType = serde_json::from_str("\"future_algo_type\"").unwrap();
1049        assert_eq!(parsed, OKXAlgoOrderType::Other);
1050    }
1051}
1052
1053/// Represents the target currency for order quantity.
1054#[derive(
1055    Copy,
1056    Clone,
1057    Debug,
1058    Display,
1059    PartialEq,
1060    Eq,
1061    Hash,
1062    AsRefStr,
1063    EnumIter,
1064    EnumString,
1065    Serialize,
1066    Deserialize,
1067)]
1068#[serde(rename_all = "snake_case")]
1069#[strum(serialize_all = "snake_case")]
1070pub enum OKXTargetCurrency {
1071    /// Base currency.
1072    BaseCcy,
1073    /// Quote currency.
1074    QuoteCcy,
1075}
1076
1077/// Represents an OKX order book channel.
1078#[derive(Copy, Clone, Debug, PartialEq, Eq)]
1079pub enum OKXBookChannel {
1080    /// Standard depth-first book channel (`books`).
1081    Book,
1082    /// Low-latency 400-depth channel (`books-l2-tbt`).
1083    BookL2Tbt,
1084    /// Low-latency 50-depth channel (`books50-l2-tbt`).
1085    Books50L2Tbt,
1086    /// Retail Price Improvement 400-depth channel (`books-rpi`).
1087    BooksRpi,
1088    /// Spread 5-depth snapshot channel (`sprd-books5`).
1089    SprdBooks5,
1090}
1091
1092/// Represents an account's RPI permission for an instrument.
1093#[derive(
1094    Copy,
1095    Clone,
1096    Debug,
1097    Display,
1098    PartialEq,
1099    Eq,
1100    Hash,
1101    AsRefStr,
1102    EnumIter,
1103    EnumString,
1104    Serialize,
1105    Deserialize,
1106)]
1107pub enum OKXRpiPermission {
1108    /// RPI is not enabled for the instrument.
1109    #[serde(rename = "0")]
1110    Disabled,
1111    /// RPI is enabled, but the account cannot place RPI orders.
1112    #[serde(rename = "1")]
1113    Enabled,
1114    /// RPI is enabled and the account can place RPI orders.
1115    #[serde(rename = "2")]
1116    Permitted,
1117}
1118
1119/// Represents OKX VIP level tiers for trading fee structure and API limits.
1120///
1121/// VIP levels determine:
1122/// - Trading fee discounts.
1123/// - API rate limits.
1124/// - Access to advanced order book channels (L2/L3 depth).
1125///
1126/// Higher VIP levels (VIP4+) get access to:
1127/// - "books50-l2-tbt" channel (50 depth, 10ms updates).
1128/// - "bbo-tbt" channel (1 depth, 10ms updates).
1129///
1130/// VIP5+ get access to:
1131/// - "books-l2-tbt" channel (400 depth, 10ms updates).
1132#[derive(
1133    Copy,
1134    Clone,
1135    Debug,
1136    Display,
1137    PartialEq,
1138    Eq,
1139    PartialOrd,
1140    Ord,
1141    Hash,
1142    AsRefStr,
1143    EnumIter,
1144    EnumString,
1145    Serialize,
1146    Deserialize,
1147)]
1148#[cfg_attr(
1149    feature = "python",
1150    pyo3::pyclass(
1151        module = "nautilus_trader.adapters.okx",
1152        from_py_object,
1153        rename_all = "SCREAMING_SNAKE_CASE",
1154    )
1155)]
1156#[cfg_attr(
1157    feature = "python",
1158    pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.adapters.okx")
1159)]
1160pub enum OKXVipLevel {
1161    /// VIP level 0 (default tier).
1162    #[serde(rename = "0")]
1163    #[strum(serialize = "0")]
1164    Vip0 = 0,
1165    /// VIP level 1.
1166    #[serde(rename = "1")]
1167    #[strum(serialize = "1")]
1168    Vip1 = 1,
1169    /// VIP level 2.
1170    #[serde(rename = "2")]
1171    #[strum(serialize = "2")]
1172    Vip2 = 2,
1173    /// VIP level 3.
1174    #[serde(rename = "3")]
1175    #[strum(serialize = "3")]
1176    Vip3 = 3,
1177    /// VIP level 4 (can access books50-l2-tbt channel).
1178    #[serde(rename = "4")]
1179    #[strum(serialize = "4")]
1180    Vip4 = 4,
1181    /// VIP level 5 (can access books-l2-tbt channel).
1182    #[serde(rename = "5")]
1183    #[strum(serialize = "5")]
1184    Vip5 = 5,
1185    /// VIP level 6.
1186    #[serde(rename = "6")]
1187    #[strum(serialize = "6")]
1188    Vip6 = 6,
1189    /// VIP level 7.
1190    #[serde(rename = "7")]
1191    #[strum(serialize = "7")]
1192    Vip7 = 7,
1193    /// VIP level 8.
1194    #[serde(rename = "8")]
1195    #[strum(serialize = "8")]
1196    Vip8 = 8,
1197    /// VIP level 9 (highest tier).
1198    #[serde(rename = "9")]
1199    #[strum(serialize = "9")]
1200    Vip9 = 9,
1201}
1202
1203impl From<u8> for OKXVipLevel {
1204    fn from(value: u8) -> Self {
1205        match value {
1206            0 => Self::Vip0,
1207            1 => Self::Vip1,
1208            2 => Self::Vip2,
1209            3 => Self::Vip3,
1210            4 => Self::Vip4,
1211            5 => Self::Vip5,
1212            6 => Self::Vip6,
1213            7 => Self::Vip7,
1214            8 => Self::Vip8,
1215            9 => Self::Vip9,
1216            _ => {
1217                log::warn!("Invalid VIP level {value}, defaulting to Vip0");
1218                Self::Vip0
1219            }
1220        }
1221    }
1222}
1223
1224impl From<OKXSide> for OrderSide {
1225    fn from(side: OKXSide) -> Self {
1226        match side {
1227            OKXSide::Buy => Self::Buy,
1228            OKXSide::Sell => Self::Sell,
1229        }
1230    }
1231}
1232
1233impl From<OKXExecType> for LiquiditySide {
1234    fn from(exec: OKXExecType) -> Self {
1235        match exec {
1236            OKXExecType::Maker => Self::Maker,
1237            OKXExecType::Taker => Self::Taker,
1238            OKXExecType::None => Self::NoLiquiditySide,
1239        }
1240    }
1241}
1242
1243impl From<OKXPositionSide> for PositionSide {
1244    fn from(side: OKXPositionSide) -> Self {
1245        match side {
1246            OKXPositionSide::Long => Self::Long,
1247            OKXPositionSide::Short => Self::Short,
1248            _ => Self::Flat,
1249        }
1250    }
1251}
1252
1253impl TryFrom<OKXOrderStatus> for OrderStatus {
1254    type Error = OKXOrderStatus;
1255
1256    /// Converts an OKX order status into the matching Nautilus [`OrderStatus`].
1257    ///
1258    /// Returns the source variant in the error case for [`OKXOrderStatus::Unknown`],
1259    /// which carries any order state OKX adds after this mapping was written.
1260    fn try_from(value: OKXOrderStatus) -> Result<Self, Self::Error> {
1261        match value {
1262            OKXOrderStatus::Live => Ok(Self::Accepted),
1263            OKXOrderStatus::PartiallyFilled => Ok(Self::PartiallyFilled),
1264            OKXOrderStatus::Filled => Ok(Self::Filled),
1265            OKXOrderStatus::Canceled | OKXOrderStatus::MmpCanceled => Ok(Self::Canceled),
1266            OKXOrderStatus::Unknown => Err(value),
1267        }
1268    }
1269}
1270
1271impl TryFrom<OKXAlgoOrderStatus> for OrderStatus {
1272    type Error = OKXAlgoOrderStatus;
1273
1274    /// Converts an OKX algo order status into the matching Nautilus [`OrderStatus`].
1275    ///
1276    /// Returns the source variant in the error case for [`OKXAlgoOrderStatus::Unknown`],
1277    /// which carries any algo order state OKX adds after this mapping was written.
1278    fn try_from(value: OKXAlgoOrderStatus) -> Result<Self, Self::Error> {
1279        match value {
1280            OKXAlgoOrderStatus::Live | OKXAlgoOrderStatus::Pause => Ok(Self::Accepted),
1281            OKXAlgoOrderStatus::Effective
1282            | OKXAlgoOrderStatus::OrderPlaced
1283            | OKXAlgoOrderStatus::PartiallyEffective => Ok(Self::Triggered),
1284            OKXAlgoOrderStatus::Filled => Ok(Self::Filled),
1285            OKXAlgoOrderStatus::Canceled => Ok(Self::Canceled),
1286            OKXAlgoOrderStatus::OrderFailed | OKXAlgoOrderStatus::PartiallyFailed => {
1287                Ok(Self::Rejected)
1288            }
1289            OKXAlgoOrderStatus::Unknown => Err(value),
1290        }
1291    }
1292}
1293
1294impl TryFrom<OKXOrderType> for OrderType {
1295    type Error = OKXOrderType;
1296
1297    /// Converts an OKX order type into the matching Nautilus [`OrderType`].
1298    ///
1299    /// Returns the source variant in the error case for [`OKXOrderType::Other`],
1300    /// which carries any order type OKX adds after this mapping was written.
1301    fn try_from(value: OKXOrderType) -> Result<Self, Self::Error> {
1302        match value {
1303            OKXOrderType::Market => Ok(Self::Market),
1304            OKXOrderType::Limit
1305            | OKXOrderType::Rpi
1306            | OKXOrderType::PostOnly
1307            | OKXOrderType::OptimalLimitIoc
1308            | OKXOrderType::Mmp
1309            | OKXOrderType::MmpAndPostOnly
1310            | OKXOrderType::Fok
1311            | OKXOrderType::OpFok
1312            | OKXOrderType::Ioc => Ok(Self::Limit),
1313            OKXOrderType::Trigger => Ok(Self::StopMarket),
1314            OKXOrderType::Other => Err(value),
1315        }
1316    }
1317}
1318
1319impl From<OrderType> for OKXOrderType {
1320    fn from(value: OrderType) -> Self {
1321        match value {
1322            OrderType::Market => Self::Market,
1323            OrderType::Limit => Self::Limit,
1324            OrderType::MarketToLimit => Self::Ioc,
1325            // Conditional orders will be handled separately via algo orders
1326            OrderType::StopMarket
1327            | OrderType::StopLimit
1328            | OrderType::MarketIfTouched
1329            | OrderType::LimitIfTouched
1330            | OrderType::TrailingStopMarket => {
1331                panic!("Conditional order types must use OKXAlgoOrderType")
1332            }
1333            _ => panic!("Invalid `OrderType` cannot be represented on OKX: {value:?}"),
1334        }
1335    }
1336}
1337
1338impl From<PositionSide> for OKXPositionSide {
1339    fn from(value: PositionSide) -> Self {
1340        match value {
1341            PositionSide::Long => Self::Long,
1342            PositionSide::Short => Self::Short,
1343            PositionSide::Flat => Self::None,
1344        }
1345    }
1346}
1347
1348#[derive(
1349    Copy,
1350    Clone,
1351    Debug,
1352    Display,
1353    PartialEq,
1354    Eq,
1355    Hash,
1356    AsRefStr,
1357    EnumIter,
1358    EnumString,
1359    Serialize,
1360    Deserialize,
1361)]
1362#[serde(rename_all = "snake_case")]
1363pub enum OKXAlgoOrderType {
1364    Conditional,
1365    Oco,
1366    Trigger,
1367    MoveOrderStop,
1368    Iceberg,
1369    Twap,
1370    Chase,
1371    /// Forward-compatible fallback for algo order types OKX adds later.
1372    #[serde(other)]
1373    Other,
1374}
1375
1376/// Helper to determine if an order type requires algo order handling.
1377pub fn is_conditional_order(order_type: OrderType) -> bool {
1378    OKX_CONDITIONAL_ORDER_TYPES.contains(&order_type)
1379}
1380
1381/// Helper to determine if an order type requires the advance algo cancel endpoint.
1382pub fn is_advance_algo_order(order_type: OrderType) -> bool {
1383    OKX_ADVANCE_ALGO_ORDER_TYPES.contains(&order_type)
1384}
1385
1386/// Converts Nautilus conditional order types to OKX algo order type.
1387///
1388/// # Errors
1389///
1390/// Returns an error if the provided `order_type` is not a conditional order type.
1391pub fn conditional_order_to_algo_type(order_type: OrderType) -> anyhow::Result<OKXAlgoOrderType> {
1392    match order_type {
1393        OrderType::StopMarket
1394        | OrderType::StopLimit
1395        | OrderType::MarketIfTouched
1396        | OrderType::LimitIfTouched => Ok(OKXAlgoOrderType::Trigger),
1397        OrderType::TrailingStopMarket => Ok(OKXAlgoOrderType::MoveOrderStop),
1398        _ => anyhow::bail!("Not a conditional order type: {order_type:?}"),
1399    }
1400}
1401
1402/// Represents the state of an algo (trigger/OCO/conditional) order on OKX.
1403#[derive(
1404    Copy,
1405    Clone,
1406    Debug,
1407    Display,
1408    PartialEq,
1409    Eq,
1410    Hash,
1411    AsRefStr,
1412    EnumIter,
1413    EnumString,
1414    Serialize,
1415    Deserialize,
1416)]
1417#[serde(rename_all = "snake_case")]
1418#[cfg_attr(
1419    feature = "python",
1420    pyo3::pyclass(
1421        eq,
1422        eq_int,
1423        module = "nautilus_trader.adapters.okx",
1424        from_py_object,
1425        rename_all = "SCREAMING_SNAKE_CASE",
1426    )
1427)]
1428#[cfg_attr(
1429    feature = "python",
1430    pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.adapters.okx")
1431)]
1432pub enum OKXAlgoOrderStatus {
1433    Live,
1434    Pause,
1435    Effective,
1436    OrderPlaced,
1437    PartiallyEffective,
1438    Canceled,
1439    Filled,
1440    OrderFailed,
1441    PartiallyFailed,
1442    /// Forward-compatible fallback for algo order states OKX adds later.
1443    #[serde(other)]
1444    Unknown,
1445}
1446
1447/// Represents the category of an order on OKX.
1448///
1449/// The category field indicates whether an order is a normal trade, liquidation,
1450/// auto-deleveraging (ADL) event, or algorithmic order type. This is critical for
1451/// risk management and proper handling of exchange-generated orders.
1452///
1453/// # References
1454///
1455/// <https://www.okx.com/docs-v5/en/#order-book-trading-ws-order-channel>
1456#[derive(
1457    Copy,
1458    Clone,
1459    Debug,
1460    Display,
1461    PartialEq,
1462    Eq,
1463    Hash,
1464    AsRefStr,
1465    EnumIter,
1466    EnumString,
1467    Serialize,
1468    Deserialize,
1469)]
1470#[serde(rename_all = "snake_case")]
1471pub enum OKXOrderCategory {
1472    /// Normal trading order.
1473    Normal,
1474    /// Full liquidation order (position completely closed by exchange).
1475    FullLiquidation,
1476    /// Partial liquidation order (position partially closed by exchange).
1477    PartialLiquidation,
1478    /// Auto-deleveraging order (position closed to offset counterparty liquidation).
1479    Adl,
1480    /// Time-Weighted Average Price algorithmic order.
1481    Twap,
1482    /// Iceberg algorithmic order (hidden quantity).
1483    Iceberg,
1484    /// One-Cancels-the-Other algorithmic order.
1485    Oco,
1486    /// Conditional/trigger order.
1487    Conditional,
1488    /// Move order stop algorithmic order.
1489    MoveOrderStop,
1490    /// Delivery and exercise (for futures/options settlement).
1491    Ddh,
1492    /// Event contract settlement fill.
1493    Delivery,
1494    /// Unknown or future category (graceful fallback).
1495    #[serde(other)]
1496    Other,
1497}
1498
1499#[derive(
1500    Copy,
1501    Clone,
1502    Debug,
1503    Display,
1504    PartialEq,
1505    Eq,
1506    Hash,
1507    AsRefStr,
1508    EnumIter,
1509    EnumString,
1510    Serialize,
1511    Deserialize,
1512)]
1513pub enum OKXBarSize {
1514    #[serde(rename = "1s")]
1515    Second1,
1516    #[serde(rename = "1m")]
1517    Minute1,
1518    #[serde(rename = "3m")]
1519    Minute3,
1520    #[serde(rename = "5m")]
1521    Minute5,
1522    #[serde(rename = "15m")]
1523    Minute15,
1524    #[serde(rename = "30m")]
1525    Minute30,
1526    #[serde(rename = "1H")]
1527    Hour1,
1528    #[serde(rename = "2H")]
1529    Hour2,
1530    #[serde(rename = "4H")]
1531    Hour4,
1532    #[serde(rename = "6H")]
1533    Hour6,
1534    #[serde(rename = "12H")]
1535    Hour12,
1536    #[serde(rename = "1D")]
1537    Day1,
1538    #[serde(rename = "2D")]
1539    Day2,
1540    #[serde(rename = "3D")]
1541    Day3,
1542    #[serde(rename = "5D")]
1543    Day5,
1544    #[serde(rename = "1W")]
1545    Week1,
1546    #[serde(rename = "1M")]
1547    Month1,
1548    #[serde(rename = "3M")]
1549    Month3,
1550}
1551
1552/// Options price type for order pricing.
1553#[derive(
1554    Copy,
1555    Clone,
1556    Debug,
1557    Default,
1558    Display,
1559    PartialEq,
1560    Eq,
1561    Hash,
1562    AsRefStr,
1563    EnumIter,
1564    EnumString,
1565    Serialize,
1566    Deserialize,
1567)]
1568#[serde(rename_all = "snake_case")]
1569pub enum OKXPriceType {
1570    /// No price type specified.
1571    #[default]
1572    #[serde(rename = "")]
1573    None,
1574    /// Standard price.
1575    Px,
1576    /// Price in USD.
1577    Usd,
1578    /// Price in implied volatility.
1579    Vol,
1580}
1581
1582/// Funding rate settlement state.
1583#[derive(
1584    Copy,
1585    Clone,
1586    Debug,
1587    Default,
1588    Display,
1589    PartialEq,
1590    Eq,
1591    Hash,
1592    AsRefStr,
1593    EnumIter,
1594    EnumString,
1595    Serialize,
1596    Deserialize,
1597)]
1598#[serde(rename_all = "snake_case")]
1599pub enum OKXSettlementState {
1600    /// No settlement state.
1601    #[default]
1602    #[serde(rename = "")]
1603    None,
1604    /// Settlement in progress.
1605    Processing,
1606    /// Settlement completed.
1607    Settled,
1608}
1609
1610/// Quick margin type for order margin management.
1611#[derive(
1612    Copy,
1613    Clone,
1614    Debug,
1615    Default,
1616    Display,
1617    PartialEq,
1618    Eq,
1619    Hash,
1620    AsRefStr,
1621    EnumIter,
1622    EnumString,
1623    Serialize,
1624    Deserialize,
1625)]
1626#[serde(rename_all = "snake_case")]
1627pub enum OKXQuickMarginType {
1628    /// No quick margin type.
1629    #[default]
1630    #[serde(rename = "")]
1631    None,
1632    /// Manual margin management.
1633    Manual,
1634    /// Auto borrow margin.
1635    AutoBorrow,
1636    /// Auto repay margin.
1637    AutoRepay,
1638}
1639
1640/// OKX API environment.
1641#[derive(
1642    Copy,
1643    Clone,
1644    Debug,
1645    Default,
1646    Display,
1647    PartialEq,
1648    Eq,
1649    Hash,
1650    AsRefStr,
1651    EnumIter,
1652    EnumString,
1653    Serialize,
1654    Deserialize,
1655)]
1656#[serde(rename_all = "lowercase")]
1657#[strum(ascii_case_insensitive, serialize_all = "lowercase")]
1658#[cfg_attr(
1659    feature = "python",
1660    pyo3::pyclass(
1661        eq,
1662        eq_int,
1663        module = "nautilus_trader.adapters.okx",
1664        from_py_object,
1665        rename_all = "SCREAMING_SNAKE_CASE",
1666    )
1667)]
1668#[cfg_attr(
1669    feature = "python",
1670    pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.adapters.okx")
1671)]
1672pub enum OKXEnvironment {
1673    /// Live trading environment.
1674    #[default]
1675    Live,
1676    /// Demo trading environment.
1677    Demo,
1678}
1679
1680/// OKX API region.
1681///
1682/// Selects the regional endpoint set. OKX serves region-specific hosts and an
1683/// API key registered in one region is rejected by another region's endpoints
1684/// (returning `API key doesn't exist`).
1685#[derive(
1686    Copy,
1687    Clone,
1688    Debug,
1689    Default,
1690    Display,
1691    PartialEq,
1692    Eq,
1693    Hash,
1694    AsRefStr,
1695    EnumIter,
1696    EnumString,
1697    Serialize,
1698    Deserialize,
1699)]
1700#[serde(rename_all = "lowercase")]
1701#[strum(ascii_case_insensitive, serialize_all = "lowercase")]
1702#[cfg_attr(
1703    feature = "python",
1704    pyo3::pyclass(
1705        eq,
1706        eq_int,
1707        module = "nautilus_trader.adapters.okx",
1708        from_py_object,
1709        rename_all = "SCREAMING_SNAKE_CASE",
1710    )
1711)]
1712#[cfg_attr(
1713    feature = "python",
1714    pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.adapters.okx")
1715)]
1716pub enum OKXRegion {
1717    /// Global endpoints (accounts registered on www.okx.com).
1718    #[default]
1719    Global,
1720    /// European Economic Area endpoints (accounts registered on my.okx.com).
1721    Eea,
1722    /// United States and Australia endpoints (accounts registered on app.okx.com).
1723    Us,
1724}