Skip to main content

nautilus_interactive_brokers/common/enums/
order.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
16use std::{fmt::Display, str::FromStr};
17
18use nautilus_model::enums::{
19    OrderSide, OrderStatus as NautilusOrderStatus, OrderType as NautilusOrderType,
20    TimeInForce as NautilusTimeInForce,
21};
22
23/// Interactive Brokers execution/action side values.
24#[derive(Debug, Clone, Copy, PartialEq, Eq)]
25#[cfg_attr(
26    feature = "python",
27    pyo3::pyclass(
28        module = "nautilus_trader.adapters.interactive_brokers",
29        from_py_object,
30        rename_all = "SCREAMING_SNAKE_CASE"
31    )
32)]
33#[cfg_attr(
34    feature = "python",
35    pyo3_stub_gen::derive::gen_stub_pyclass_enum(
36        module = "nautilus_trader.adapters.interactive_brokers"
37    )
38)]
39pub enum IbAction {
40    /// Buy action from order data.
41    Buy,
42    /// Bought side from execution data.
43    Bought,
44    /// Sell action from order data.
45    Sell,
46    /// Sold side from execution data.
47    Sold,
48    /// Sell short action from order data.
49    SellShort,
50    /// Sell long action from order data.
51    SellLong,
52}
53
54impl IbAction {
55    /// Converts the IB action to a Nautilus order side.
56    #[must_use]
57    pub const fn order_side(self) -> OrderSide {
58        match self {
59            Self::Buy | Self::Bought => OrderSide::Buy,
60            Self::Sell | Self::Sold | Self::SellShort | Self::SellLong => OrderSide::Sell,
61        }
62    }
63
64    /// Returns `1` for buy/bought and `-1` for sell/sold.
65    #[must_use]
66    pub const fn signed_multiplier(self) -> i32 {
67        match self {
68            Self::Buy | Self::Bought => 1,
69            Self::Sell | Self::Sold | Self::SellShort | Self::SellLong => -1,
70        }
71    }
72
73    /// Converts to the rust-ibapi order action enum.
74    #[must_use]
75    pub const fn ibapi_action(self) -> ibapi::orders::Action {
76        match self {
77            Self::Buy | Self::Bought => ibapi::orders::Action::Buy,
78            Self::Sell | Self::Sold => ibapi::orders::Action::Sell,
79            Self::SellShort => ibapi::orders::Action::SellShort,
80            Self::SellLong => ibapi::orders::Action::SellLong,
81        }
82    }
83}
84
85impl FromStr for IbAction {
86    type Err = anyhow::Error;
87
88    fn from_str(value: &str) -> Result<Self, Self::Err> {
89        match value {
90            "BUY" => Ok(Self::Buy),
91            "BOT" => Ok(Self::Bought),
92            "SELL" => Ok(Self::Sell),
93            "SLD" => Ok(Self::Sold),
94            "SSHORT" => Ok(Self::SellShort),
95            "SLONG" => Ok(Self::SellLong),
96            _ => anyhow::bail!("Unknown IB action: {value}"),
97        }
98    }
99}
100
101impl From<ibapi::orders::Action> for IbAction {
102    fn from(value: ibapi::orders::Action) -> Self {
103        match value {
104            ibapi::orders::Action::Buy => Self::Buy,
105            ibapi::orders::Action::Sell => Self::Sell,
106            ibapi::orders::Action::SellShort => Self::SellShort,
107            ibapi::orders::Action::SellLong => Self::SellLong,
108        }
109    }
110}
111
112impl Display for IbAction {
113    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
114        f.write_str(match self {
115            Self::Buy => "BUY",
116            Self::Bought => "BOT",
117            Self::Sell => "SELL",
118            Self::Sold => "SLD",
119            Self::SellShort => "SSHORT",
120            Self::SellLong => "SLONG",
121        })
122    }
123}
124
125/// Interactive Brokers order status values.
126#[derive(Debug, Clone, Copy, PartialEq, Eq)]
127#[cfg_attr(
128    feature = "python",
129    pyo3::pyclass(
130        module = "nautilus_trader.adapters.interactive_brokers",
131        from_py_object,
132        rename_all = "SCREAMING_SNAKE_CASE"
133    )
134)]
135#[cfg_attr(
136    feature = "python",
137    pyo3_stub_gen::derive::gen_stub_pyclass_enum(
138        module = "nautilus_trader.adapters.interactive_brokers"
139    )
140)]
141pub enum IbOrderStatus {
142    /// Order is pending API processing.
143    ApiPending,
144    /// Order is pending submission.
145    PendingSubmit,
146    /// Order has been pre-submitted.
147    PreSubmitted,
148    /// Order has been submitted.
149    Submitted,
150    /// Order is pending cancellation.
151    PendingCancel,
152    /// Order was cancelled by the API.
153    ApiCancelled,
154    /// Order was cancelled.
155    Cancelled,
156    /// Order has been fully filled.
157    Filled,
158    /// Order is inactive.
159    Inactive,
160}
161
162impl IbOrderStatus {
163    /// Converts the IB order status to a Nautilus order status.
164    #[must_use]
165    pub const fn nautilus_status(self) -> NautilusOrderStatus {
166        match self {
167            Self::ApiPending | Self::PendingSubmit | Self::PreSubmitted => {
168                NautilusOrderStatus::Submitted
169            }
170            Self::Submitted => NautilusOrderStatus::Accepted,
171            Self::PendingCancel => NautilusOrderStatus::PendingCancel,
172            Self::ApiCancelled | Self::Cancelled => NautilusOrderStatus::Canceled,
173            Self::Filled => NautilusOrderStatus::Filled,
174            Self::Inactive => NautilusOrderStatus::Rejected,
175        }
176    }
177
178    /// Returns whether this status means an order should be treated as accepted.
179    #[must_use]
180    pub const fn is_accepted(self) -> bool {
181        matches!(self, Self::Submitted | Self::PreSubmitted)
182    }
183
184    /// Returns whether this status is terminal for pending spread fill handling.
185    #[must_use]
186    pub const fn is_terminal(self) -> bool {
187        matches!(
188            self,
189            Self::Filled | Self::ApiCancelled | Self::Cancelled | Self::Inactive
190        )
191    }
192}
193
194impl FromStr for IbOrderStatus {
195    type Err = anyhow::Error;
196
197    fn from_str(value: &str) -> Result<Self, Self::Err> {
198        match value {
199            "ApiPending" => Ok(Self::ApiPending),
200            "PendingSubmit" => Ok(Self::PendingSubmit),
201            "PreSubmitted" => Ok(Self::PreSubmitted),
202            "Submitted" => Ok(Self::Submitted),
203            "PendingCancel" => Ok(Self::PendingCancel),
204            "ApiCancelled" => Ok(Self::ApiCancelled),
205            "Cancelled" => Ok(Self::Cancelled),
206            "Filled" => Ok(Self::Filled),
207            "Inactive" => Ok(Self::Inactive),
208            _ => anyhow::bail!("Unknown IB order status: {value}"),
209        }
210    }
211}
212
213impl Display for IbOrderStatus {
214    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
215        f.write_str(match self {
216            Self::ApiPending => "ApiPending",
217            Self::PendingSubmit => "PendingSubmit",
218            Self::PreSubmitted => "PreSubmitted",
219            Self::Submitted => "Submitted",
220            Self::PendingCancel => "PendingCancel",
221            Self::ApiCancelled => "ApiCancelled",
222            Self::Cancelled => "Cancelled",
223            Self::Filled => "Filled",
224            Self::Inactive => "Inactive",
225        })
226    }
227}
228
229/// Interactive Brokers order type values.
230#[derive(Debug, Clone, Copy, PartialEq, Eq)]
231#[cfg_attr(
232    feature = "python",
233    pyo3::pyclass(
234        module = "nautilus_trader.adapters.interactive_brokers",
235        from_py_object,
236        rename_all = "SCREAMING_SNAKE_CASE"
237    )
238)]
239#[cfg_attr(
240    feature = "python",
241    pyo3_stub_gen::derive::gen_stub_pyclass_enum(
242        module = "nautilus_trader.adapters.interactive_brokers"
243    )
244)]
245pub enum IbOrderType {
246    /// Market order.
247    Market,
248    /// Market-on-close order.
249    MarketOnClose,
250    /// Limit order.
251    Limit,
252    /// Limit-on-close order.
253    LimitOnClose,
254    /// Stop market order.
255    Stop,
256    /// Stop limit order.
257    StopLimit,
258    /// Trailing stop market order.
259    TrailingStop,
260    /// Trailing stop limit order.
261    TrailingStopLimit,
262    /// Market-if-touched order.
263    MarketIfTouched,
264    /// Limit-if-touched order.
265    LimitIfTouched,
266    /// Market-to-limit order.
267    MarketToLimit,
268    /// Market order with price protection.
269    MarketWithProtection,
270    /// Stop order with price protection.
271    StopWithProtection,
272    /// Midprice order targeting the NBBO midpoint.
273    Midprice,
274    /// Pegged-to-market order.
275    PeggedToMarket,
276    /// Pegged-to-stock order.
277    PeggedToStock,
278    /// Pegged-to-midpoint order.
279    PeggedToMidpoint,
280    /// Pegged-to-benchmark order.
281    PeggedToBenchmark,
282    /// Peg-to-best order.
283    PegBest,
284    /// Relative order.
285    Relative,
286    /// Passive relative order.
287    PassiveRelative,
288    /// Volatility order.
289    Volatility,
290    /// Box-top order.
291    BoxTop,
292    /// Relative plus limit combo order.
293    RelativeLimitCombo,
294    /// Relative plus market combo order.
295    RelativeMarketCombo,
296}
297
298impl IbOrderType {
299    /// Returns the IB wire string.
300    #[must_use]
301    pub const fn as_str(self) -> &'static str {
302        match self {
303            Self::Market => "MKT",
304            Self::MarketOnClose => "MOC",
305            Self::Limit => "LMT",
306            Self::LimitOnClose => "LOC",
307            Self::Stop => "STP",
308            Self::StopLimit => "STP LMT",
309            Self::TrailingStop => "TRAIL",
310            Self::TrailingStopLimit => "TRAIL LIMIT",
311            Self::MarketIfTouched => "MIT",
312            Self::LimitIfTouched => "LIT",
313            Self::MarketToLimit => "MTL",
314            Self::MarketWithProtection => "MKT PRT",
315            Self::StopWithProtection => "STP PRT",
316            Self::Midprice => "MIDPRICE",
317            Self::PeggedToMarket => "PEG MKT",
318            Self::PeggedToStock => "PEG STK",
319            Self::PeggedToMidpoint => "PEG MID",
320            Self::PeggedToBenchmark => "PEG BENCH",
321            Self::PegBest => "PEG BEST",
322            Self::Relative => "REL",
323            Self::PassiveRelative => "PASSV REL",
324            Self::Volatility => "VOL",
325            Self::BoxTop => "BOX TOP",
326            Self::RelativeLimitCombo => "REL + LMT",
327            Self::RelativeMarketCombo => "REL + MKT",
328        }
329    }
330
331    /// Converts the IB order type to a Nautilus order type.
332    #[must_use]
333    pub const fn nautilus_order_type(self) -> NautilusOrderType {
334        match self {
335            Self::Market | Self::MarketOnClose => NautilusOrderType::Market,
336            Self::Limit | Self::LimitOnClose => NautilusOrderType::Limit,
337            Self::Stop => NautilusOrderType::StopMarket,
338            Self::StopLimit => NautilusOrderType::StopLimit,
339            Self::TrailingStop => NautilusOrderType::TrailingStopMarket,
340            Self::TrailingStopLimit => NautilusOrderType::TrailingStopLimit,
341            Self::MarketIfTouched => NautilusOrderType::MarketIfTouched,
342            Self::LimitIfTouched => NautilusOrderType::LimitIfTouched,
343            Self::MarketToLimit => NautilusOrderType::MarketToLimit,
344            Self::MarketWithProtection
345            | Self::Midprice
346            | Self::PeggedToMarket
347            | Self::PeggedToStock
348            | Self::PeggedToMidpoint
349            | Self::PeggedToBenchmark
350            | Self::PegBest
351            | Self::Relative
352            | Self::PassiveRelative
353            | Self::Volatility
354            | Self::BoxTop
355            | Self::RelativeMarketCombo => NautilusOrderType::Market,
356            Self::RelativeLimitCombo => NautilusOrderType::Limit,
357            Self::StopWithProtection => NautilusOrderType::StopMarket,
358        }
359    }
360
361    /// Converts a Nautilus order type and time-in-force pair to the IB order type.
362    #[must_use]
363    pub const fn from_nautilus(
364        order_type: NautilusOrderType,
365        time_in_force: NautilusTimeInForce,
366    ) -> Self {
367        match order_type {
368            NautilusOrderType::Market => {
369                if matches!(time_in_force, NautilusTimeInForce::AtTheClose) {
370                    Self::MarketOnClose
371                } else {
372                    Self::Market
373                }
374            }
375            NautilusOrderType::Limit => {
376                if matches!(time_in_force, NautilusTimeInForce::AtTheClose) {
377                    Self::LimitOnClose
378                } else {
379                    Self::Limit
380                }
381            }
382            NautilusOrderType::StopMarket => Self::Stop,
383            NautilusOrderType::StopLimit => Self::StopLimit,
384            NautilusOrderType::MarketIfTouched => Self::MarketIfTouched,
385            NautilusOrderType::LimitIfTouched => Self::LimitIfTouched,
386            NautilusOrderType::TrailingStopMarket => Self::TrailingStop,
387            NautilusOrderType::TrailingStopLimit => Self::TrailingStopLimit,
388            NautilusOrderType::MarketToLimit => Self::MarketToLimit,
389        }
390    }
391}
392
393impl FromStr for IbOrderType {
394    type Err = anyhow::Error;
395
396    fn from_str(value: &str) -> Result<Self, Self::Err> {
397        match value {
398            "MKT" => Ok(Self::Market),
399            "MOC" => Ok(Self::MarketOnClose),
400            "LMT" => Ok(Self::Limit),
401            "LOC" => Ok(Self::LimitOnClose),
402            "STP" => Ok(Self::Stop),
403            "STP LMT" => Ok(Self::StopLimit),
404            "TRAIL" => Ok(Self::TrailingStop),
405            "TRAIL LIMIT" => Ok(Self::TrailingStopLimit),
406            "MIT" => Ok(Self::MarketIfTouched),
407            "LIT" => Ok(Self::LimitIfTouched),
408            "MTL" => Ok(Self::MarketToLimit),
409            "MKT PRT" => Ok(Self::MarketWithProtection),
410            "STP PRT" => Ok(Self::StopWithProtection),
411            "MIDPRICE" => Ok(Self::Midprice),
412            "PEG MKT" => Ok(Self::PeggedToMarket),
413            "PEG STK" => Ok(Self::PeggedToStock),
414            "PEG MID" | "PEGMID" => Ok(Self::PeggedToMidpoint),
415            "PEG BENCH" | "PEGBENCH" => Ok(Self::PeggedToBenchmark),
416            "PEG BEST" | "PEGBEST" => Ok(Self::PegBest),
417            "REL" => Ok(Self::Relative),
418            "PASSV REL" => Ok(Self::PassiveRelative),
419            "VOL" => Ok(Self::Volatility),
420            "BOX TOP" => Ok(Self::BoxTop),
421            "REL + LMT" => Ok(Self::RelativeLimitCombo),
422            "REL + MKT" => Ok(Self::RelativeMarketCombo),
423            _ => anyhow::bail!("Unknown IB order type: {value}"),
424        }
425    }
426}
427
428impl Display for IbOrderType {
429    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
430        f.write_str(self.as_str())
431    }
432}
433
434/// Interactive Brokers time-in-force values.
435#[derive(Debug, Clone, Copy, PartialEq, Eq)]
436#[cfg_attr(
437    feature = "python",
438    pyo3::pyclass(
439        module = "nautilus_trader.adapters.interactive_brokers",
440        from_py_object,
441        rename_all = "SCREAMING_SNAKE_CASE"
442    )
443)]
444#[cfg_attr(
445    feature = "python",
446    pyo3_stub_gen::derive::gen_stub_pyclass_enum(
447        module = "nautilus_trader.adapters.interactive_brokers"
448    )
449)]
450pub enum IbTimeInForce {
451    /// Day order.
452    Day,
453    /// Good-till-cancelled order.
454    GoodTilCanceled,
455    /// Immediate-or-cancel order.
456    ImmediateOrCancel,
457    /// Good-till-date order.
458    GoodTilDate,
459    /// Opening auction order.
460    OnOpen,
461    /// Fill-or-kill order.
462    FillOrKill,
463    /// Day-till-cancelled order.
464    DayTilCanceled,
465    /// Auction order.
466    Auction,
467}
468
469impl IbTimeInForce {
470    /// Converts this IB time-in-force to a Nautilus time-in-force.
471    #[must_use]
472    pub const fn nautilus_time_in_force(self) -> NautilusTimeInForce {
473        match self {
474            Self::Day | Self::DayTilCanceled | Self::Auction => NautilusTimeInForce::Day,
475            Self::GoodTilCanceled => NautilusTimeInForce::Gtc,
476            Self::ImmediateOrCancel => NautilusTimeInForce::Ioc,
477            Self::GoodTilDate => NautilusTimeInForce::Gtd,
478            Self::OnOpen => NautilusTimeInForce::AtTheOpen,
479            Self::FillOrKill => NautilusTimeInForce::Fok,
480        }
481    }
482
483    /// Converts a Nautilus time-in-force to the corresponding IB time-in-force.
484    #[must_use]
485    pub const fn from_nautilus(time_in_force: NautilusTimeInForce) -> Self {
486        match time_in_force {
487            NautilusTimeInForce::Day | NautilusTimeInForce::AtTheClose => Self::Day,
488            NautilusTimeInForce::Gtc => Self::GoodTilCanceled,
489            NautilusTimeInForce::Ioc => Self::ImmediateOrCancel,
490            NautilusTimeInForce::Fok => Self::FillOrKill,
491            NautilusTimeInForce::Gtd => Self::GoodTilDate,
492            NautilusTimeInForce::AtTheOpen => Self::OnOpen,
493        }
494    }
495
496    /// Converts this adapter enum to the rust-ibapi enum.
497    #[must_use]
498    pub const fn ibapi_time_in_force(self) -> ibapi::orders::TimeInForce {
499        match self {
500            Self::Day => ibapi::orders::TimeInForce::Day,
501            Self::GoodTilCanceled => ibapi::orders::TimeInForce::GoodTilCanceled,
502            Self::ImmediateOrCancel => ibapi::orders::TimeInForce::ImmediateOrCancel,
503            Self::GoodTilDate => ibapi::orders::TimeInForce::GoodTilDate,
504            Self::OnOpen => ibapi::orders::TimeInForce::OnOpen,
505            Self::FillOrKill => ibapi::orders::TimeInForce::FillOrKill,
506            Self::DayTilCanceled => ibapi::orders::TimeInForce::DayTilCanceled,
507            Self::Auction => ibapi::orders::TimeInForce::Auction,
508        }
509    }
510}
511
512impl From<ibapi::orders::TimeInForce> for IbTimeInForce {
513    fn from(value: ibapi::orders::TimeInForce) -> Self {
514        match value {
515            ibapi::orders::TimeInForce::Day => Self::Day,
516            ibapi::orders::TimeInForce::GoodTilCanceled => Self::GoodTilCanceled,
517            ibapi::orders::TimeInForce::ImmediateOrCancel => Self::ImmediateOrCancel,
518            ibapi::orders::TimeInForce::GoodTilDate => Self::GoodTilDate,
519            ibapi::orders::TimeInForce::OnOpen => Self::OnOpen,
520            ibapi::orders::TimeInForce::FillOrKill => Self::FillOrKill,
521            ibapi::orders::TimeInForce::DayTilCanceled => Self::DayTilCanceled,
522            ibapi::orders::TimeInForce::Auction => Self::Auction,
523        }
524    }
525}
526
527impl FromStr for IbTimeInForce {
528    type Err = anyhow::Error;
529
530    fn from_str(value: &str) -> Result<Self, Self::Err> {
531        match value {
532            "DAY" => Ok(Self::Day),
533            "GTC" => Ok(Self::GoodTilCanceled),
534            "IOC" => Ok(Self::ImmediateOrCancel),
535            "GTD" => Ok(Self::GoodTilDate),
536            "OPG" => Ok(Self::OnOpen),
537            "FOK" => Ok(Self::FillOrKill),
538            "DTC" => Ok(Self::DayTilCanceled),
539            "AUC" => Ok(Self::Auction),
540            _ => anyhow::bail!("Unknown IB time in force: {value}"),
541        }
542    }
543}
544
545impl Display for IbTimeInForce {
546    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
547        f.write_str(match self {
548            Self::Day => "DAY",
549            Self::GoodTilCanceled => "GTC",
550            Self::ImmediateOrCancel => "IOC",
551            Self::GoodTilDate => "GTD",
552            Self::OnOpen => "OPG",
553            Self::FillOrKill => "FOK",
554            Self::DayTilCanceled => "DTC",
555            Self::Auction => "AUC",
556        })
557    }
558}
559
560/// Interactive Brokers builder time-in-force values.
561#[derive(Debug, Clone, Copy, PartialEq, Eq)]
562#[cfg_attr(
563    feature = "python",
564    pyo3::pyclass(
565        module = "nautilus_trader.adapters.interactive_brokers",
566        from_py_object,
567        rename_all = "SCREAMING_SNAKE_CASE"
568    )
569)]
570#[cfg_attr(
571    feature = "python",
572    pyo3_stub_gen::derive::gen_stub_pyclass_enum(
573        module = "nautilus_trader.adapters.interactive_brokers"
574    )
575)]
576pub enum IbBuilderTimeInForce {
577    Day,
578    GoodTillCancel,
579    ImmediateOrCancel,
580    GoodTillDate,
581    FillOrKill,
582    GoodTillCrossing,
583    DayTillCanceled,
584    Auction,
585    OpeningAuction,
586}
587
588impl IbBuilderTimeInForce {
589    #[must_use]
590    pub const fn as_str(self) -> &'static str {
591        match self {
592            Self::Day => "DAY",
593            Self::GoodTillCancel => "GTC",
594            Self::ImmediateOrCancel => "IOC",
595            Self::GoodTillDate => "GTD",
596            Self::FillOrKill => "FOK",
597            Self::GoodTillCrossing => "GTX",
598            Self::DayTillCanceled => "DTC",
599            Self::Auction => "AUC",
600            Self::OpeningAuction => "OPG",
601        }
602    }
603
604    #[must_use]
605    pub fn ibapi_builder_time_in_force(
606        self,
607        good_till_date: Option<String>,
608    ) -> ibapi::orders::builder::TimeInForce {
609        match self {
610            Self::Day => ibapi::orders::builder::TimeInForce::Day,
611            Self::GoodTillCancel => ibapi::orders::builder::TimeInForce::GoodTillCancel,
612            Self::ImmediateOrCancel => ibapi::orders::builder::TimeInForce::ImmediateOrCancel,
613            Self::GoodTillDate => ibapi::orders::builder::TimeInForce::GoodTillDate {
614                date: good_till_date.unwrap_or_default(),
615            },
616            Self::FillOrKill => ibapi::orders::builder::TimeInForce::FillOrKill,
617            Self::GoodTillCrossing => ibapi::orders::builder::TimeInForce::GoodTillCrossing,
618            Self::DayTillCanceled => ibapi::orders::builder::TimeInForce::DayTillCanceled,
619            Self::Auction => ibapi::orders::builder::TimeInForce::Auction,
620            Self::OpeningAuction => ibapi::orders::builder::TimeInForce::OpeningAuction,
621        }
622    }
623}
624
625impl FromStr for IbBuilderTimeInForce {
626    type Err = anyhow::Error;
627
628    fn from_str(value: &str) -> Result<Self, Self::Err> {
629        match value {
630            "DAY" => Ok(Self::Day),
631            "GTC" => Ok(Self::GoodTillCancel),
632            "IOC" => Ok(Self::ImmediateOrCancel),
633            "GTD" => Ok(Self::GoodTillDate),
634            "FOK" => Ok(Self::FillOrKill),
635            "GTX" => Ok(Self::GoodTillCrossing),
636            "DTC" => Ok(Self::DayTillCanceled),
637            "AUC" => Ok(Self::Auction),
638            "OPG" => Ok(Self::OpeningAuction),
639            _ => anyhow::bail!("Unknown IB builder time in force: {value}"),
640        }
641    }
642}
643
644impl Display for IbBuilderTimeInForce {
645    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
646        f.write_str(self.as_str())
647    }
648}
649
650/// Interactive Brokers combo-leg open/close values.
651#[derive(Debug, Clone, Copy, PartialEq, Eq)]
652#[cfg_attr(
653    feature = "python",
654    pyo3::pyclass(
655        module = "nautilus_trader.adapters.interactive_brokers",
656        from_py_object,
657        rename_all = "SCREAMING_SNAKE_CASE"
658    )
659)]
660#[cfg_attr(
661    feature = "python",
662    pyo3_stub_gen::derive::gen_stub_pyclass_enum(
663        module = "nautilus_trader.adapters.interactive_brokers"
664    )
665)]
666pub enum IbComboLegOpenClose {
667    Same,
668    Open,
669    Close,
670    Unknown,
671}
672
673impl IbComboLegOpenClose {
674    /// Returns the IB integer code.
675    #[must_use]
676    pub const fn as_i32(self) -> i32 {
677        match self {
678            Self::Same => 0,
679            Self::Open => 1,
680            Self::Close => 2,
681            Self::Unknown => 3,
682        }
683    }
684
685    /// Converts to the rust-ibapi combo-leg open/close enum.
686    #[must_use]
687    pub const fn ibapi_combo_leg_open_close(self) -> ibapi::contracts::ComboLegOpenClose {
688        match self {
689            Self::Same => ibapi::contracts::ComboLegOpenClose::Same,
690            Self::Open => ibapi::contracts::ComboLegOpenClose::Open,
691            Self::Close => ibapi::contracts::ComboLegOpenClose::Close,
692            Self::Unknown => ibapi::contracts::ComboLegOpenClose::Unknown,
693        }
694    }
695}
696
697impl From<ibapi::contracts::ComboLegOpenClose> for IbComboLegOpenClose {
698    fn from(value: ibapi::contracts::ComboLegOpenClose) -> Self {
699        match value {
700            ibapi::contracts::ComboLegOpenClose::Same => Self::Same,
701            ibapi::contracts::ComboLegOpenClose::Open => Self::Open,
702            ibapi::contracts::ComboLegOpenClose::Close => Self::Close,
703            ibapi::contracts::ComboLegOpenClose::Unknown => Self::Unknown,
704        }
705    }
706}
707
708/// Interactive Brokers conditional order condition types.
709#[derive(Debug, Clone, Copy, PartialEq, Eq)]
710#[cfg_attr(
711    feature = "python",
712    pyo3::pyclass(
713        module = "nautilus_trader.adapters.interactive_brokers",
714        from_py_object,
715        rename_all = "SCREAMING_SNAKE_CASE"
716    )
717)]
718#[cfg_attr(
719    feature = "python",
720    pyo3_stub_gen::derive::gen_stub_pyclass_enum(
721        module = "nautilus_trader.adapters.interactive_brokers"
722    )
723)]
724pub enum IbConditionKind {
725    /// Price condition.
726    Price,
727    /// Time condition.
728    Time,
729    /// Margin condition.
730    Margin,
731    /// Execution condition.
732    Execution,
733    /// Volume condition.
734    Volume,
735    /// Percent-change condition.
736    PercentChange,
737}
738
739impl IbConditionKind {
740    /// Returns the JSON/tag string accepted by the adapter.
741    #[must_use]
742    pub const fn as_str(self) -> &'static str {
743        match self {
744            Self::Price => "price",
745            Self::Time => "time",
746            Self::Margin => "margin",
747            Self::Execution => "execution",
748            Self::Volume => "volume",
749            Self::PercentChange => "percent_change",
750        }
751    }
752}
753
754impl FromStr for IbConditionKind {
755    type Err = anyhow::Error;
756
757    fn from_str(value: &str) -> Result<Self, Self::Err> {
758        match value.to_ascii_lowercase().as_str() {
759            "price" => Ok(Self::Price),
760            "time" => Ok(Self::Time),
761            "margin" => Ok(Self::Margin),
762            "execution" => Ok(Self::Execution),
763            "volume" => Ok(Self::Volume),
764            "percent_change" | "percentchange" => Ok(Self::PercentChange),
765            _ => anyhow::bail!("Unknown IB condition kind: {value}"),
766        }
767    }
768}
769
770impl Display for IbConditionKind {
771    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
772        f.write_str(self.as_str())
773    }
774}
775
776/// Interactive Brokers conditional order conjunction values.
777#[derive(Debug, Clone, Copy, PartialEq, Eq)]
778#[cfg_attr(
779    feature = "python",
780    pyo3::pyclass(
781        module = "nautilus_trader.adapters.interactive_brokers",
782        from_py_object,
783        rename_all = "SCREAMING_SNAKE_CASE"
784    )
785)]
786#[cfg_attr(
787    feature = "python",
788    pyo3_stub_gen::derive::gen_stub_pyclass_enum(
789        module = "nautilus_trader.adapters.interactive_brokers"
790    )
791)]
792pub enum IbConditionConjunction {
793    /// Logical AND.
794    And,
795    /// Logical OR.
796    Or,
797}
798
799impl IbConditionConjunction {
800    /// Returns the JSON/tag string accepted by the adapter.
801    #[must_use]
802    pub const fn as_str(self) -> &'static str {
803        match self {
804            Self::And => "and",
805            Self::Or => "or",
806        }
807    }
808
809    /// Returns whether this conjunction is the rust-ibapi `is_conjunction` flag.
810    #[must_use]
811    pub const fn is_conjunction(self) -> bool {
812        matches!(self, Self::And)
813    }
814}
815
816impl FromStr for IbConditionConjunction {
817    type Err = anyhow::Error;
818
819    fn from_str(value: &str) -> Result<Self, Self::Err> {
820        match value.to_ascii_lowercase().as_str() {
821            "and" | "a" => Ok(Self::And),
822            "or" | "o" => Ok(Self::Or),
823            _ => anyhow::bail!("Unknown IB condition conjunction: {value}"),
824        }
825    }
826}
827
828impl Display for IbConditionConjunction {
829    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
830        f.write_str(self.as_str())
831    }
832}
833
834/// Interactive Brokers price-condition trigger methods.
835#[derive(Debug, Clone, Copy, PartialEq, Eq)]
836#[cfg_attr(
837    feature = "python",
838    pyo3::pyclass(
839        module = "nautilus_trader.adapters.interactive_brokers",
840        from_py_object,
841        rename_all = "SCREAMING_SNAKE_CASE"
842    )
843)]
844#[cfg_attr(
845    feature = "python",
846    pyo3_stub_gen::derive::gen_stub_pyclass_enum(
847        module = "nautilus_trader.adapters.interactive_brokers"
848    )
849)]
850pub enum IbTriggerMethod {
851    /// Default method.
852    Default,
853    /// Two consecutive bid or ask prices.
854    DoubleBidAsk,
855    /// Last traded price.
856    Last,
857    /// Two consecutive last prices.
858    DoubleLast,
859    /// Current bid or ask price.
860    BidAsk,
861    /// Last price or bid/ask if no last price is available.
862    LastOrBidAsk,
863    /// Midpoint between bid and ask.
864    Midpoint,
865}
866
867impl IbTriggerMethod {
868    /// Returns the IB integer code.
869    #[must_use]
870    pub const fn as_i32(self) -> i32 {
871        match self {
872            Self::Default => 0,
873            Self::DoubleBidAsk => 1,
874            Self::Last => 2,
875            Self::DoubleLast => 3,
876            Self::BidAsk => 4,
877            Self::LastOrBidAsk => 7,
878            Self::Midpoint => 8,
879        }
880    }
881
882    /// Converts to the rust-ibapi trigger method.
883    #[must_use]
884    pub const fn ibapi_trigger_method(self) -> ibapi::orders::conditions::TriggerMethod {
885        match self {
886            Self::Default => ibapi::orders::conditions::TriggerMethod::Default,
887            Self::DoubleBidAsk => ibapi::orders::conditions::TriggerMethod::DoubleBidAsk,
888            Self::Last => ibapi::orders::conditions::TriggerMethod::Last,
889            Self::DoubleLast => ibapi::orders::conditions::TriggerMethod::DoubleLast,
890            Self::BidAsk => ibapi::orders::conditions::TriggerMethod::BidAsk,
891            Self::LastOrBidAsk => ibapi::orders::conditions::TriggerMethod::LastOrBidAsk,
892            Self::Midpoint => ibapi::orders::conditions::TriggerMethod::Midpoint,
893        }
894    }
895}
896
897impl From<i32> for IbTriggerMethod {
898    fn from(value: i32) -> Self {
899        match value {
900            1 => Self::DoubleBidAsk,
901            2 => Self::Last,
902            3 => Self::DoubleLast,
903            4 => Self::BidAsk,
904            7 => Self::LastOrBidAsk,
905            8 => Self::Midpoint,
906            _ => Self::Default,
907        }
908    }
909}
910
911impl From<IbTriggerMethod> for i32 {
912    fn from(value: IbTriggerMethod) -> Self {
913        value.as_i32()
914    }
915}
916
917impl From<ibapi::orders::conditions::TriggerMethod> for IbTriggerMethod {
918    fn from(value: ibapi::orders::conditions::TriggerMethod) -> Self {
919        i32::from(value).into()
920    }
921}
922
923impl Display for IbTriggerMethod {
924    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
925        write!(f, "{}", self.as_i32())
926    }
927}
928
929/// Interactive Brokers one-cancels-all behavior.
930#[derive(Debug, Clone, Copy, PartialEq, Eq)]
931#[cfg_attr(
932    feature = "python",
933    pyo3::pyclass(
934        module = "nautilus_trader.adapters.interactive_brokers",
935        from_py_object,
936        rename_all = "SCREAMING_SNAKE_CASE"
937    )
938)]
939#[cfg_attr(
940    feature = "python",
941    pyo3_stub_gen::derive::gen_stub_pyclass_enum(
942        module = "nautilus_trader.adapters.interactive_brokers"
943    )
944)]
945pub enum IbOcaType {
946    /// Not part of an OCA group.
947    None,
948    /// Cancel remaining orders with block.
949    CancelWithBlock,
950    /// Proportionally reduce remaining orders with block.
951    ReduceWithBlock,
952    /// Proportionally reduce remaining orders without block.
953    ReduceWithoutBlock,
954}
955
956impl IbOcaType {
957    /// Returns the IB integer code.
958    #[must_use]
959    pub const fn as_i32(self) -> i32 {
960        match self {
961            Self::None => 0,
962            Self::CancelWithBlock => 1,
963            Self::ReduceWithBlock => 2,
964            Self::ReduceWithoutBlock => 3,
965        }
966    }
967
968    /// Converts to the rust-ibapi OCA type.
969    #[must_use]
970    pub const fn ibapi_oca_type(self) -> ibapi::orders::OcaType {
971        match self {
972            Self::None => ibapi::orders::OcaType::None,
973            Self::CancelWithBlock => ibapi::orders::OcaType::CancelWithBlock,
974            Self::ReduceWithBlock => ibapi::orders::OcaType::ReduceWithBlock,
975            Self::ReduceWithoutBlock => ibapi::orders::OcaType::ReduceWithoutBlock,
976        }
977    }
978}
979
980impl From<i32> for IbOcaType {
981    fn from(value: i32) -> Self {
982        match value {
983            1 => Self::CancelWithBlock,
984            2 => Self::ReduceWithBlock,
985            3 => Self::ReduceWithoutBlock,
986            _ => Self::None,
987        }
988    }
989}
990
991impl From<IbOcaType> for i32 {
992    fn from(value: IbOcaType) -> Self {
993        value.as_i32()
994    }
995}
996
997impl From<ibapi::orders::OcaType> for IbOcaType {
998    fn from(value: ibapi::orders::OcaType) -> Self {
999        i32::from(value).into()
1000    }
1001}
1002
1003impl Display for IbOcaType {
1004    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1005        write!(f, "{}", self.as_i32())
1006    }
1007}
1008
1009/// Interactive Brokers execution liquidity values.
1010#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1011#[cfg_attr(
1012    feature = "python",
1013    pyo3::pyclass(
1014        module = "nautilus_trader.adapters.interactive_brokers",
1015        from_py_object,
1016        rename_all = "SCREAMING_SNAKE_CASE"
1017    )
1018)]
1019#[cfg_attr(
1020    feature = "python",
1021    pyo3_stub_gen::derive::gen_stub_pyclass_enum(
1022        module = "nautilus_trader.adapters.interactive_brokers"
1023    )
1024)]
1025pub enum IbLiquidity {
1026    None,
1027    AddedLiquidity,
1028    RemovedLiquidity,
1029    LiquidityRoutedOut,
1030}
1031
1032impl IbLiquidity {
1033    /// Returns the IB integer code.
1034    #[must_use]
1035    pub const fn as_i32(self) -> i32 {
1036        match self {
1037            Self::None => 0,
1038            Self::AddedLiquidity => 1,
1039            Self::RemovedLiquidity => 2,
1040            Self::LiquidityRoutedOut => 3,
1041        }
1042    }
1043
1044    /// Converts to the rust-ibapi execution liquidity enum.
1045    #[must_use]
1046    pub fn ibapi_liquidity(self) -> ibapi::orders::Liquidity {
1047        ibapi::orders::Liquidity::from(self.as_i32())
1048    }
1049}
1050
1051impl From<i32> for IbLiquidity {
1052    fn from(value: i32) -> Self {
1053        match value {
1054            1 => Self::AddedLiquidity,
1055            2 => Self::RemovedLiquidity,
1056            3 => Self::LiquidityRoutedOut,
1057            _ => Self::None,
1058        }
1059    }
1060}
1061
1062impl From<ibapi::orders::Liquidity> for IbLiquidity {
1063    fn from(value: ibapi::orders::Liquidity) -> Self {
1064        match value {
1065            ibapi::orders::Liquidity::None => Self::None,
1066            ibapi::orders::Liquidity::AddedLiquidity => Self::AddedLiquidity,
1067            ibapi::orders::Liquidity::RemovedLiquidity => Self::RemovedLiquidity,
1068            ibapi::orders::Liquidity::LiquidityRoutedOut => Self::LiquidityRoutedOut,
1069        }
1070    }
1071}
1072
1073impl Display for IbLiquidity {
1074    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1075        write!(f, "{}", self.as_i32())
1076    }
1077}