Skip to main content

nautilus_interactive_brokers/python/
config.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 for Interactive Brokers configuration types.
17
18use nautilus_model::identifiers::InstrumentId;
19use pyo3::prelude::*;
20
21use crate::config::{
22    DockerizedIBGatewayConfig, InteractiveBrokersDataClientConfig,
23    InteractiveBrokersExecClientConfig, InteractiveBrokersInstrumentProviderConfig, MarketDataType,
24    TradingMode,
25};
26
27#[pymethods]
28impl MarketDataType {
29    #[classattr]
30    const REALTIME: i32 = 1;
31
32    #[classattr]
33    const FROZEN: i32 = 2;
34
35    #[classattr]
36    const DELAYED: i32 = 3;
37
38    #[classattr]
39    const DELAYED_FROZEN: i32 = 4;
40}
41
42#[pymethods]
43impl InteractiveBrokersDataClientConfig {
44    /// Creates a new `InteractiveBrokersDataClientConfig` instance.
45    #[new]
46    #[pyo3(signature = (host=None, port=None, client_id=None, use_regular_trading_hours=None, market_data_type=None, ignore_quote_tick_size_updates=None, connection_timeout=None, request_timeout=None, handle_revised_bars=None, batch_quotes=None))]
47    #[allow(clippy::too_many_arguments)]
48    fn py_new(
49        host: Option<String>,
50        port: Option<u16>,
51        client_id: Option<i32>,
52        use_regular_trading_hours: Option<bool>,
53        market_data_type: Option<MarketDataType>,
54        ignore_quote_tick_size_updates: Option<bool>,
55        connection_timeout: Option<u64>,
56        request_timeout: Option<u64>,
57        handle_revised_bars: Option<bool>,
58        batch_quotes: Option<bool>,
59    ) -> Self {
60        Self {
61            host: host.unwrap_or_else(|| crate::common::consts::DEFAULT_HOST.to_string()),
62            port: port.unwrap_or(crate::common::consts::DEFAULT_PORT),
63            client_id: client_id.unwrap_or(crate::common::consts::DEFAULT_CLIENT_ID),
64            use_regular_trading_hours: use_regular_trading_hours.unwrap_or(true),
65            market_data_type: market_data_type.unwrap_or_default(),
66            ignore_quote_tick_size_updates: ignore_quote_tick_size_updates.unwrap_or(false),
67            connection_timeout: connection_timeout.unwrap_or(300),
68            request_timeout: request_timeout.unwrap_or(60),
69            handle_revised_bars: handle_revised_bars.unwrap_or(false),
70            batch_quotes: batch_quotes.unwrap_or(true),
71        }
72    }
73
74    /// Returns the host.
75    #[getter]
76    fn host(&self) -> &str {
77        &self.host
78    }
79
80    /// Returns the port.
81    #[getter]
82    fn port(&self) -> u16 {
83        self.port
84    }
85
86    /// Returns the client ID.
87    #[getter]
88    fn client_id(&self) -> i32 {
89        self.client_id
90    }
91
92    /// Returns whether to use regular trading hours only.
93    #[getter]
94    fn use_regular_trading_hours(&self) -> bool {
95        self.use_regular_trading_hours
96    }
97
98    /// Returns the market data type.
99    #[getter]
100    fn market_data_type(&self) -> MarketDataType {
101        self.market_data_type
102    }
103
104    /// Returns whether to ignore quote tick size updates.
105    #[getter]
106    fn ignore_quote_tick_size_updates(&self) -> bool {
107        self.ignore_quote_tick_size_updates
108    }
109
110    /// Returns the connection timeout in seconds.
111    #[getter]
112    fn connection_timeout(&self) -> u64 {
113        self.connection_timeout
114    }
115
116    /// Returns the request timeout in seconds.
117    #[getter]
118    fn request_timeout(&self) -> u64 {
119        self.request_timeout
120    }
121
122    /// Returns whether to handle revised bars.
123    #[getter]
124    fn handle_revised_bars(&self) -> bool {
125        self.handle_revised_bars
126    }
127
128    /// Returns whether to use batch quotes.
129    #[getter]
130    fn batch_quotes(&self) -> bool {
131        self.batch_quotes
132    }
133}
134
135#[pymethods]
136impl InteractiveBrokersExecClientConfig {
137    /// Creates a new `InteractiveBrokersExecClientConfig` instance.
138    #[new]
139    #[pyo3(signature = (host=None, port=None, client_id=None, account_id=None, connection_timeout=None, request_timeout=None, fetch_all_open_orders=None, track_option_exercise_from_position_update=None))]
140    fn py_new(
141        host: Option<String>,
142        port: Option<u16>,
143        client_id: Option<i32>,
144        account_id: Option<String>,
145        connection_timeout: Option<u64>,
146        request_timeout: Option<u64>,
147        fetch_all_open_orders: Option<bool>,
148        track_option_exercise_from_position_update: Option<bool>,
149    ) -> Self {
150        Self {
151            host: host.unwrap_or_else(|| crate::common::consts::DEFAULT_HOST.to_string()),
152            port: port.unwrap_or(crate::common::consts::DEFAULT_PORT),
153            client_id: client_id.unwrap_or(crate::common::consts::DEFAULT_CLIENT_ID),
154            account_id,
155            connection_timeout: connection_timeout.unwrap_or(300),
156            request_timeout: request_timeout.unwrap_or(60),
157            fetch_all_open_orders: fetch_all_open_orders.unwrap_or(false),
158            track_option_exercise_from_position_update: track_option_exercise_from_position_update
159                .unwrap_or(false),
160        }
161    }
162
163    /// Returns the host.
164    #[getter]
165    fn host(&self) -> &str {
166        &self.host
167    }
168
169    /// Returns the port.
170    #[getter]
171    fn port(&self) -> u16 {
172        self.port
173    }
174
175    /// Returns the client ID.
176    #[getter]
177    fn client_id(&self) -> i32 {
178        self.client_id
179    }
180
181    /// Returns the account ID.
182    #[getter]
183    fn account_id(&self) -> Option<String> {
184        self.account_id.clone()
185    }
186
187    /// Returns the connection timeout in seconds.
188    #[getter]
189    fn connection_timeout(&self) -> u64 {
190        self.connection_timeout
191    }
192
193    /// Returns the request timeout in seconds.
194    #[getter]
195    fn request_timeout(&self) -> u64 {
196        self.request_timeout
197    }
198
199    /// Returns whether to fetch all open orders.
200    #[getter]
201    fn fetch_all_open_orders(&self) -> bool {
202        self.fetch_all_open_orders
203    }
204
205    /// Returns whether to track option exercise from position updates.
206    #[getter]
207    fn track_option_exercise_from_position_update(&self) -> bool {
208        self.track_option_exercise_from_position_update
209    }
210}
211
212#[pymethods]
213impl InteractiveBrokersInstrumentProviderConfig {
214    /// Creates a new `InteractiveBrokersInstrumentProviderConfig` instance.
215    #[new]
216    #[pyo3(signature = (symbology_method=None, load_ids=None, load_contracts=None, min_expiry_days=None, max_expiry_days=None, build_options_chain=None, build_futures_chain=None, cache_validity_days=None, convert_exchange_to_mic_venue=None, symbol_to_mic_venue=None, filter_sec_types=None, cache_path=None))]
217    #[allow(clippy::too_many_arguments)]
218    fn py_new(
219        py: Python<'_>,
220        symbology_method: Option<crate::config::SymbologyMethod>,
221        load_ids: Option<std::collections::HashSet<InstrumentId>>,
222        load_contracts: Option<Py<pyo3::types::PyList>>,
223        min_expiry_days: Option<u32>,
224        max_expiry_days: Option<u32>,
225        build_options_chain: Option<bool>,
226        build_futures_chain: Option<bool>,
227        cache_validity_days: Option<u32>,
228        convert_exchange_to_mic_venue: Option<bool>,
229        symbol_to_mic_venue: Option<std::collections::HashMap<String, String>>,
230        filter_sec_types: Option<std::collections::HashSet<String>>,
231        cache_path: Option<String>,
232    ) -> PyResult<Self> {
233        Ok(Self {
234            symbology_method: symbology_method.unwrap_or_default(),
235            load_ids: load_ids.unwrap_or_default(),
236            load_contracts: if let Some(c) = load_contracts {
237                crate::python::conversion::py_list_to_json_values(c.bind(py))?
238            } else {
239                Vec::new()
240            },
241            min_expiry_days,
242            max_expiry_days,
243            build_options_chain,
244            build_futures_chain,
245            cache_validity_days,
246            convert_exchange_to_mic_venue: convert_exchange_to_mic_venue.unwrap_or(false),
247            symbol_to_mic_venue: symbol_to_mic_venue.unwrap_or_default(),
248            filter_sec_types: filter_sec_types.unwrap_or_default(),
249            cache_path,
250        })
251    }
252
253    /// Returns the symbology method.
254    #[getter]
255    fn symbology_method(&self) -> crate::config::SymbologyMethod {
256        self.symbology_method
257    }
258
259    /// Returns the instrument IDs to load on startup.
260    #[getter]
261    fn load_ids(&self) -> std::collections::HashSet<InstrumentId> {
262        self.load_ids.clone()
263    }
264
265    /// Returns the IB contracts to load on startup.
266    #[getter]
267    fn load_contracts(&self, py: Python<'_>) -> PyResult<Py<pyo3::types::PyList>> {
268        let json_mod = py.import("json")?;
269        let list = pyo3::types::PyList::empty(py);
270
271        for value in &self.load_contracts {
272            let json_str = value.to_string();
273            let dict = json_mod.call_method1("loads", (json_str,))?;
274            list.append(dict)?;
275        }
276        Ok(list.unbind())
277    }
278
279    /// Returns the minimum expiry days.
280    #[getter]
281    fn min_expiry_days(&self) -> Option<u32> {
282        self.min_expiry_days
283    }
284
285    /// Returns the maximum expiry days.
286    #[getter]
287    fn max_expiry_days(&self) -> Option<u32> {
288        self.max_expiry_days
289    }
290
291    /// Returns whether to build full options chain.
292    #[getter]
293    fn build_options_chain(&self) -> Option<bool> {
294        self.build_options_chain
295    }
296
297    /// Returns whether to build full futures chain.
298    #[getter]
299    fn build_futures_chain(&self) -> Option<bool> {
300        self.build_futures_chain
301    }
302
303    /// Returns the cache validity in days.
304    #[getter]
305    fn cache_validity_days(&self) -> Option<u32> {
306        self.cache_validity_days
307    }
308
309    /// Returns whether to convert IB exchanges to MIC venues.
310    #[getter]
311    fn convert_exchange_to_mic_venue(&self) -> bool {
312        self.convert_exchange_to_mic_venue
313    }
314
315    /// Returns the symbol to MIC venue mapping.
316    #[getter]
317    fn symbol_to_mic_venue(&self) -> std::collections::HashMap<String, String> {
318        self.symbol_to_mic_venue.clone()
319    }
320
321    /// Returns the filter security types.
322    #[getter]
323    fn filter_sec_types(&self) -> Vec<String> {
324        self.filter_sec_types.iter().cloned().collect()
325    }
326
327    /// Returns the cache path for persistent instrument caching.
328    #[getter]
329    fn cache_path(&self) -> Option<String> {
330        self.cache_path.clone()
331    }
332
333    /// Sets the cache path for persistent instrument caching.
334    #[setter]
335    fn set_cache_path(&mut self, cache_path: Option<String>) {
336        self.cache_path = cache_path;
337    }
338}
339
340#[pymethods]
341impl DockerizedIBGatewayConfig {
342    /// Creates a new `DockerizedIBGatewayConfig` instance.
343    #[new]
344    #[pyo3(signature = (username=None, password=None, trading_mode=None, read_only_api=None, timeout=None, container_image=None, vnc_port=None))]
345    fn py_new(
346        username: Option<String>,
347        password: Option<String>,
348        trading_mode: Option<TradingMode>,
349        read_only_api: Option<bool>,
350        timeout: Option<u64>,
351        container_image: Option<String>,
352        vnc_port: Option<u16>,
353    ) -> Self {
354        Self {
355            username,
356            password,
357            trading_mode: trading_mode.unwrap_or_default(),
358            read_only_api: read_only_api.unwrap_or(true),
359            timeout: timeout.unwrap_or(300),
360            container_image: container_image
361                .unwrap_or_else(|| "ghcr.io/gnzsnz/ib-gateway:stable".to_string()),
362            vnc_port,
363        }
364    }
365
366    /// Returns the username.
367    #[getter]
368    fn username(&self) -> Option<String> {
369        self.username.clone()
370    }
371
372    /// Returns the password (masked for security).
373    #[getter]
374    fn password(&self) -> Option<String> {
375        self.password.as_ref().map(|_| "********".to_string())
376    }
377
378    /// Returns the trading mode.
379    #[getter]
380    fn trading_mode(&self) -> TradingMode {
381        self.trading_mode
382    }
383
384    /// Returns whether read-only API is enabled.
385    #[getter]
386    fn read_only_api(&self) -> bool {
387        self.read_only_api
388    }
389
390    /// Returns the timeout in seconds.
391    #[getter]
392    fn timeout(&self) -> u64 {
393        self.timeout
394    }
395
396    /// Returns the container image.
397    #[getter]
398    fn container_image(&self) -> &str {
399        &self.container_image
400    }
401
402    /// Returns the VNC port.
403    #[getter]
404    fn vnc_port(&self) -> Option<u16> {
405        self.vnc_port
406    }
407}