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