nautilus_interactive_brokers/
config.rs1use std::collections::{HashMap, HashSet};
19
20use nautilus_model::identifiers::InstrumentId;
21use serde::{Deserialize, Serialize};
22
23use crate::common::consts::{DEFAULT_CLIENT_ID, DEFAULT_HOST, DEFAULT_PORT};
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
27#[cfg_attr(
28 feature = "python",
29 pyo3::pyclass(
30 module = "nautilus_trader.adapters.interactive_brokers",
31 from_py_object,
32 rename_all = "SCREAMING_SNAKE_CASE"
33 )
34)]
35#[cfg_attr(
36 feature = "python",
37 pyo3_stub_gen::derive::gen_stub_pyclass_enum(
38 module = "nautilus_trader.adapters.interactive_brokers"
39 )
40)]
41#[derive(Default)]
42pub enum MarketDataType {
43 #[default]
45 Realtime = 1,
46 Frozen = 2,
48 Delayed = 3,
50 DelayedFrozen = 4,
52}
53
54impl From<MarketDataType> for ibapi::market_data::MarketDataType {
55 fn from(data_type: MarketDataType) -> Self {
56 match data_type {
57 MarketDataType::Realtime => Self::Realtime,
58 MarketDataType::Frozen => Self::Frozen,
59 MarketDataType::Delayed => Self::Delayed,
60 MarketDataType::DelayedFrozen => Self::DelayedFrozen,
61 }
62 }
63}
64
65#[derive(Debug, Clone, Serialize, Deserialize, bon::Builder)]
67#[serde(default)]
68#[cfg_attr(
69 feature = "python",
70 pyo3::pyclass(
71 module = "nautilus_trader.adapters.interactive_brokers",
72 subclass,
73 from_py_object
74 )
75)]
76#[cfg_attr(
77 feature = "python",
78 pyo3_stub_gen::derive::gen_stub_pyclass(
79 module = "nautilus_trader.adapters.interactive_brokers"
80 )
81)]
82pub struct InteractiveBrokersDataClientConfig {
83 #[builder(default = DEFAULT_HOST.to_string())]
85 pub host: String,
86 #[builder(default = DEFAULT_PORT)]
88 pub port: u16,
89 #[builder(default = DEFAULT_CLIENT_ID)]
91 pub client_id: i32,
92 #[builder(default = true)]
94 pub use_regular_trading_hours: bool,
95 #[builder(default)]
97 pub market_data_type: MarketDataType,
98 #[builder(default)]
100 pub ignore_quote_tick_size_updates: bool,
101 #[builder(default = 300)]
103 pub connection_timeout: u64,
104 #[builder(default = 60)]
108 pub request_timeout: u64,
109 #[builder(default)]
111 pub handle_revised_bars: bool,
112 #[builder(default = true)]
114 pub batch_quotes: bool,
115 #[builder(default)]
117 pub instrument_provider: InteractiveBrokersInstrumentProviderConfig,
118}
119
120impl Default for InteractiveBrokersDataClientConfig {
121 fn default() -> Self {
122 Self::builder().build()
123 }
124}
125
126#[derive(Debug, Clone, Serialize, Deserialize, bon::Builder)]
128#[serde(default)]
129#[cfg_attr(
130 feature = "python",
131 pyo3::pyclass(
132 module = "nautilus_trader.adapters.interactive_brokers",
133 subclass,
134 from_py_object
135 )
136)]
137#[cfg_attr(
138 feature = "python",
139 pyo3_stub_gen::derive::gen_stub_pyclass(
140 module = "nautilus_trader.adapters.interactive_brokers"
141 )
142)]
143pub struct InteractiveBrokersExecutionClientConfig {
144 #[builder(default = DEFAULT_HOST.to_string())]
146 pub host: String,
147 #[builder(default = DEFAULT_PORT)]
149 pub port: u16,
150 #[builder(default = DEFAULT_CLIENT_ID)]
152 pub client_id: i32,
153 pub account_id: Option<String>,
155 #[builder(default = 300)]
157 pub connection_timeout: u64,
158 #[builder(default = 60)]
160 pub request_timeout: u64,
161 #[builder(default)]
163 pub fetch_all_open_orders: bool,
164 #[builder(default)]
166 pub track_option_exercise_from_position_update: bool,
167 #[builder(default)]
169 pub instrument_provider: InteractiveBrokersInstrumentProviderConfig,
170}
171
172impl Default for InteractiveBrokersExecutionClientConfig {
173 fn default() -> Self {
174 Self::builder().build()
175 }
176}
177
178#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
180#[cfg_attr(
181 feature = "python",
182 pyo3::pyclass(
183 module = "nautilus_trader.adapters.interactive_brokers",
184 from_py_object,
185 rename_all = "SCREAMING_SNAKE_CASE"
186 )
187)]
188#[cfg_attr(
189 feature = "python",
190 pyo3_stub_gen::derive::gen_stub_pyclass_enum(
191 module = "nautilus_trader.adapters.interactive_brokers"
192 )
193)]
194#[derive(Default)]
195pub enum SymbologyMethod {
196 #[serde(rename = "simplified")]
198 #[default]
199 Simplified,
200 #[serde(rename = "raw")]
202 Raw,
203}
204
205#[derive(Debug, Clone, Serialize, Deserialize, bon::Builder)]
207#[serde(default)]
208#[cfg_attr(
209 feature = "python",
210 pyo3::pyclass(
211 module = "nautilus_trader.adapters.interactive_brokers",
212 subclass,
213 from_py_object
214 )
215)]
216#[cfg_attr(
217 feature = "python",
218 pyo3_stub_gen::derive::gen_stub_pyclass(
219 module = "nautilus_trader.adapters.interactive_brokers"
220 )
221)]
222pub struct InteractiveBrokersInstrumentProviderConfig {
223 #[builder(default)]
225 pub symbology_method: SymbologyMethod,
226 #[builder(default)]
228 pub load_ids: HashSet<InstrumentId>,
229 #[builder(default)]
231 pub load_contracts: Vec<serde_json::Value>,
232 pub min_expiry_days: Option<u32>,
234 pub max_expiry_days: Option<u32>,
236 pub build_options_chain: Option<bool>,
238 pub build_futures_chain: Option<bool>,
240 pub cache_validity_days: Option<u32>,
242 #[builder(default)]
244 pub convert_exchange_to_mic_venue: bool,
245 #[builder(default)]
247 pub symbol_to_mic_venue: HashMap<String, String>,
248 #[builder(default)]
250 pub filter_sec_types: HashSet<String>,
251 pub filter_callable: Option<String>,
255 pub cache_path: Option<String>,
258}
259
260impl Default for InteractiveBrokersInstrumentProviderConfig {
261 fn default() -> Self {
262 Self::builder().build()
263 }
264}
265
266#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
268#[cfg_attr(
269 feature = "python",
270 pyo3::pyclass(
271 module = "nautilus_trader.adapters.interactive_brokers",
272 from_py_object,
273 rename_all = "SCREAMING_SNAKE_CASE"
274 )
275)]
276#[cfg_attr(
277 feature = "python",
278 pyo3_stub_gen::derive::gen_stub_pyclass_enum(
279 module = "nautilus_trader.adapters.interactive_brokers"
280 )
281)]
282#[derive(Default)]
283pub enum TradingMode {
284 #[serde(rename = "paper")]
286 #[default]
287 Paper,
288 #[serde(rename = "live")]
290 Live,
291}
292
293#[derive(Debug, Clone, Serialize, Deserialize, bon::Builder)]
298#[serde(default)]
299#[cfg_attr(
300 feature = "python",
301 pyo3::pyclass(
302 module = "nautilus_trader.adapters.interactive_brokers",
303 subclass,
304 from_py_object
305 )
306)]
307#[cfg_attr(
308 feature = "python",
309 pyo3_stub_gen::derive::gen_stub_pyclass(
310 module = "nautilus_trader.adapters.interactive_brokers"
311 )
312)]
313pub struct DockerizedIBGatewayConfig {
314 pub username: Option<String>,
316 pub password: Option<String>,
318 #[builder(default)]
320 pub trading_mode: TradingMode,
321 #[builder(default = true)]
323 pub read_only_api: bool,
324 #[builder(default = 300)]
326 pub timeout: u64,
327 #[builder(default = "ghcr.io/gnzsnz/ib-gateway:stable".to_string())]
329 pub container_image: String,
330 pub vnc_port: Option<u16>,
332}
333
334impl DockerizedIBGatewayConfig {
335 pub fn mask_sensitive_info(value: &str) -> String {
337 if value.len() <= 2 {
338 "*".repeat(value.len())
339 } else {
340 format!(
341 "{}{}{}",
342 &value[0..1],
343 "*".repeat(value.len() - 2),
344 &value[value.len() - 1..]
345 )
346 }
347 }
348
349 pub fn validate(&self) -> anyhow::Result<()> {
355 if self.timeout == 0 {
356 anyhow::bail!("Timeout must be greater than 0");
357 }
358
359 if self.timeout > 3600 {
360 anyhow::bail!("Timeout must be less than 3600 seconds");
361 }
362
363 if let Some(port) = self.vnc_port
364 && (!(5900..=5999).contains(&port))
365 {
366 anyhow::bail!("VNC port must be between 5900 and 5999");
367 }
368
369 Ok(())
370 }
371}
372
373impl Default for DockerizedIBGatewayConfig {
374 fn default() -> Self {
375 Self::builder()
376 .maybe_username(std::env::var("TWS_USERNAME").ok())
377 .maybe_password(std::env::var("TWS_PASSWORD").ok())
378 .build()
379 }
380}