Skip to main content

nautilus_interactive_brokers/python/
mod.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//! Python bindings from `pyo3`.
17
18use nautilus_common::factories::{ClientConfig, DataClientFactory, ExecutionClientFactory};
19use nautilus_core::python::{to_pyruntime_err, to_pyvalue_err};
20use nautilus_system::get_global_pyo3_registry;
21use pyo3::prelude::*;
22
23use crate::{
24    common::consts::IB,
25    config::{InteractiveBrokersDataClientConfig, InteractiveBrokersExecClientConfig},
26    factories::{InteractiveBrokersDataClientFactory, InteractiveBrokersExecutionClientFactory},
27};
28
29pub mod config;
30pub mod conversion;
31pub mod enums;
32pub mod factories;
33pub mod historical;
34pub mod providers;
35
36#[cfg(feature = "gateway")]
37pub mod gateway;
38
39#[expect(clippy::needless_pass_by_value)]
40fn extract_interactive_brokers_data_factory(
41    py: Python<'_>,
42    factory: Py<PyAny>,
43) -> PyResult<Box<dyn DataClientFactory>> {
44    match factory.extract::<InteractiveBrokersDataClientFactory>(py) {
45        Ok(f) => Ok(Box::new(f)),
46        Err(e) => Err(to_pyvalue_err(format!(
47            "Failed to extract InteractiveBrokersDataClientFactory: {e}"
48        ))),
49    }
50}
51
52#[expect(clippy::needless_pass_by_value)]
53fn extract_interactive_brokers_exec_factory(
54    py: Python<'_>,
55    factory: Py<PyAny>,
56) -> PyResult<Box<dyn ExecutionClientFactory>> {
57    match factory.extract::<InteractiveBrokersExecutionClientFactory>(py) {
58        Ok(f) => Ok(Box::new(f)),
59        Err(e) => Err(to_pyvalue_err(format!(
60            "Failed to extract InteractiveBrokersExecutionClientFactory: {e}"
61        ))),
62    }
63}
64
65#[expect(clippy::needless_pass_by_value)]
66fn extract_interactive_brokers_data_config(
67    py: Python<'_>,
68    config: Py<PyAny>,
69) -> PyResult<Box<dyn ClientConfig>> {
70    match config.extract::<InteractiveBrokersDataClientConfig>(py) {
71        Ok(c) => Ok(Box::new(c)),
72        Err(e) => Err(to_pyvalue_err(format!(
73            "Failed to extract InteractiveBrokersDataClientConfig: {e}"
74        ))),
75    }
76}
77
78#[expect(clippy::needless_pass_by_value)]
79fn extract_interactive_brokers_exec_config(
80    py: Python<'_>,
81    config: Py<PyAny>,
82) -> PyResult<Box<dyn ClientConfig>> {
83    match config.extract::<InteractiveBrokersExecClientConfig>(py) {
84        Ok(c) => Ok(Box::new(c)),
85        Err(e) => Err(to_pyvalue_err(format!(
86            "Failed to extract InteractiveBrokersExecClientConfig: {e}"
87        ))),
88    }
89}
90
91/// Loaded as `nautilus_pyo3.interactive_brokers`.
92///
93/// # Errors
94///
95/// Returns an error if any bindings fail to register with the Python module.
96#[pymodule]
97pub fn interactive_brokers(_py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> {
98    m.add_class::<crate::config::MarketDataType>()?;
99    m.add_class::<crate::common::enums::IbAccountSummaryEvent>()?;
100    m.add_class::<crate::common::enums::IbAccountUpdateEvent>()?;
101    m.add_class::<crate::common::enums::IbAccountUpdateMultiEvent>()?;
102    m.add_class::<crate::common::enums::IbAction>()?;
103    m.add_class::<crate::common::enums::IbArticleType>()?;
104    m.add_class::<crate::common::enums::IbAuctionStrategy>()?;
105    m.add_class::<crate::common::enums::IbAuctionType>()?;
106    m.add_class::<crate::common::enums::IbBondIdentifierKind>()?;
107    m.add_class::<crate::common::enums::IbBuilderTimeInForce>()?;
108    m.add_class::<crate::common::enums::IbCancelOrderEvent>()?;
109    m.add_class::<crate::common::enums::IbComboLegOpenClose>()?;
110    m.add_class::<crate::common::enums::IbConditionConjunction>()?;
111    m.add_class::<crate::common::enums::IbConditionKind>()?;
112    m.add_class::<crate::common::enums::IbExecutionsEvent>()?;
113    m.add_class::<crate::common::enums::IbExerciseAction>()?;
114    m.add_class::<crate::common::enums::IbExerciseOptionsEvent>()?;
115    m.add_class::<crate::common::enums::IbFundAssetType>()?;
116    m.add_class::<crate::common::enums::IbFundDistributionPolicyIndicator>()?;
117    m.add_class::<crate::common::enums::IbHistoricalBarSize>()?;
118    m.add_class::<crate::common::enums::IbHistoricalBarUpdateEvent>()?;
119    m.add_class::<crate::common::enums::IbHistoricalTickType>()?;
120    m.add_class::<crate::common::enums::IbHistoricalWhatToShow>()?;
121    m.add_class::<crate::common::enums::IbLegAction>()?;
122    m.add_class::<crate::common::enums::IbLiquidity>()?;
123    m.add_class::<crate::common::enums::IbMarketDepthEvent>()?;
124    m.add_class::<crate::common::enums::IbOcaType>()?;
125    m.add_class::<crate::common::enums::IbOrderOpenClose>()?;
126    m.add_class::<crate::common::enums::IbOrderOrigin>()?;
127    m.add_class::<crate::common::enums::IbOptionRight>()?;
128    m.add_class::<crate::common::enums::IbOrderStatus>()?;
129    m.add_class::<crate::common::enums::IbOrderType>()?;
130    m.add_class::<crate::common::enums::IbOrderUpdateEvent>()?;
131    m.add_class::<crate::common::enums::IbOrdersEvent>()?;
132    m.add_class::<crate::common::enums::IbPlaceOrderEvent>()?;
133    m.add_class::<crate::common::enums::IbPositionUpdateEvent>()?;
134    m.add_class::<crate::common::enums::IbPositionUpdateMultiEvent>()?;
135    m.add_class::<crate::common::enums::IbRealtimeBarSize>()?;
136    m.add_class::<crate::common::enums::IbRealtimeWhatToShow>()?;
137    m.add_class::<crate::common::enums::IbReferencePriceType>()?;
138    m.add_class::<crate::common::enums::IbRiskAversion>()?;
139    m.add_class::<crate::common::enums::IbRule80A>()?;
140    m.add_class::<crate::common::enums::IbSecurityType>()?;
141    m.add_class::<crate::common::enums::IbShortSaleSlot>()?;
142    m.add_class::<crate::common::enums::IbTickEvent>()?;
143    m.add_class::<crate::common::enums::IbTickType>()?;
144    m.add_class::<crate::common::enums::IbTimeInForce>()?;
145    m.add_class::<crate::common::enums::IbTradingHours>()?;
146    m.add_class::<crate::common::enums::IbTriggerMethod>()?;
147    m.add_class::<crate::common::enums::IbTwapStrategyType>()?;
148    m.add_class::<crate::common::enums::IbVolatilityType>()?;
149    m.add_class::<crate::config::SymbologyMethod>()?;
150    m.add_class::<crate::error::ErrorCategory>()?;
151    m.add_class::<crate::error::InteractiveBrokersErrorKind>()?;
152    m.add_class::<crate::config::InteractiveBrokersDataClientConfig>()?;
153    m.add_class::<crate::config::InteractiveBrokersExecClientConfig>()?;
154    m.add_class::<crate::config::InteractiveBrokersInstrumentProviderConfig>()?;
155    m.add_class::<crate::config::DockerizedIBGatewayConfig>()?;
156    m.add_class::<crate::config::TradingMode>()?;
157    m.add_class::<crate::factories::InteractiveBrokersDataClientFactory>()?;
158    m.add_class::<crate::factories::InteractiveBrokersExecutionClientFactory>()?;
159    m.add_class::<crate::historical::HistoricalInteractiveBrokersClient>()?;
160    m.add_class::<crate::providers::instruments::InteractiveBrokersInstrumentProvider>()?;
161
162    #[cfg(feature = "gateway")]
163    {
164        m.add_class::<crate::gateway::dockerized::ContainerStatus>()?;
165        m.add_class::<crate::gateway::dockerized::DockerizedIBGateway>()?;
166    }
167
168    let registry = get_global_pyo3_registry();
169
170    if let Err(e) = registry
171        .register_factory_extractor(IB.to_string(), extract_interactive_brokers_data_factory)
172    {
173        return Err(to_pyruntime_err(format!(
174            "Failed to register Interactive Brokers data factory extractor: {e}"
175        )));
176    }
177
178    if let Err(e) = registry
179        .register_exec_factory_extractor(IB.to_string(), extract_interactive_brokers_exec_factory)
180    {
181        return Err(to_pyruntime_err(format!(
182            "Failed to register Interactive Brokers exec factory extractor: {e}"
183        )));
184    }
185
186    if let Err(e) = registry.register_config_extractor(
187        "InteractiveBrokersDataClientConfig".to_string(),
188        extract_interactive_brokers_data_config,
189    ) {
190        return Err(to_pyruntime_err(format!(
191            "Failed to register Interactive Brokers data config extractor: {e}"
192        )));
193    }
194
195    if let Err(e) = registry.register_config_extractor(
196        "InteractiveBrokersExecClientConfig".to_string(),
197        extract_interactive_brokers_exec_config,
198    ) {
199        return Err(to_pyruntime_err(format!(
200            "Failed to register Interactive Brokers exec config extractor: {e}"
201        )));
202    }
203
204    Ok(())
205}