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