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.core.nautilus_pyo3.interactive_brokers",
31 from_py_object
32 )
33)]
34#[derive(Default)]
35pub enum MarketDataType {
36 #[default]
38 Realtime = 1,
39 Frozen = 2,
41 Delayed = 3,
43 DelayedFrozen = 4,
45}
46
47impl From<MarketDataType> for ibapi::market_data::MarketDataType {
48 fn from(data_type: MarketDataType) -> Self {
49 match data_type {
50 MarketDataType::Realtime => Self::Realtime,
51 MarketDataType::Frozen => Self::Frozen,
52 MarketDataType::Delayed => Self::Delayed,
53 MarketDataType::DelayedFrozen => Self::DelayedFrozen,
54 }
55 }
56}
57
58#[derive(Debug, Clone, Serialize, Deserialize, bon::Builder)]
60#[serde(default)]
61#[cfg_attr(
62 feature = "python",
63 pyo3::pyclass(
64 module = "nautilus_trader.core.nautilus_pyo3.interactive_brokers",
65 subclass,
66 from_py_object
67 )
68)]
69pub struct InteractiveBrokersDataClientConfig {
70 #[builder(default = DEFAULT_HOST.to_string())]
72 pub host: String,
73 #[builder(default = DEFAULT_PORT)]
75 pub port: u16,
76 #[builder(default = DEFAULT_CLIENT_ID)]
78 pub client_id: i32,
79 #[builder(default = true)]
81 pub use_regular_trading_hours: bool,
82 #[builder(default)]
84 pub market_data_type: MarketDataType,
85 #[builder(default)]
87 pub ignore_quote_tick_size_updates: bool,
88 #[builder(default = 300)]
90 pub connection_timeout: u64,
91 #[builder(default = 60)]
95 pub request_timeout: u64,
96 #[builder(default)]
98 pub handle_revised_bars: bool,
99 #[builder(default = true)]
101 pub batch_quotes: bool,
102 #[builder(default)]
104 pub instrument_provider: InteractiveBrokersInstrumentProviderConfig,
105}
106
107impl Default for InteractiveBrokersDataClientConfig {
108 fn default() -> Self {
109 Self::builder().build()
110 }
111}
112
113#[derive(Debug, Clone, Serialize, Deserialize, bon::Builder)]
115#[serde(default)]
116#[cfg_attr(
117 feature = "python",
118 pyo3::pyclass(
119 module = "nautilus_trader.core.nautilus_pyo3.interactive_brokers",
120 subclass,
121 from_py_object
122 )
123)]
124pub struct InteractiveBrokersExecClientConfig {
125 #[builder(default = DEFAULT_HOST.to_string())]
127 pub host: String,
128 #[builder(default = DEFAULT_PORT)]
130 pub port: u16,
131 #[builder(default = DEFAULT_CLIENT_ID)]
133 pub client_id: i32,
134 pub account_id: Option<String>,
136 #[builder(default = 300)]
138 pub connection_timeout: u64,
139 #[builder(default = 60)]
141 pub request_timeout: u64,
142 #[builder(default)]
144 pub fetch_all_open_orders: bool,
145 #[builder(default)]
147 pub track_option_exercise_from_position_update: bool,
148 #[builder(default)]
150 pub instrument_provider: InteractiveBrokersInstrumentProviderConfig,
151}
152
153impl Default for InteractiveBrokersExecClientConfig {
154 fn default() -> Self {
155 Self::builder().build()
156 }
157}
158
159#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
161#[cfg_attr(
162 feature = "python",
163 pyo3::pyclass(
164 module = "nautilus_trader.core.nautilus_pyo3.interactive_brokers",
165 from_py_object
166 )
167)]
168#[derive(Default)]
169pub enum SymbologyMethod {
170 #[serde(rename = "simplified")]
172 #[default]
173 Simplified,
174 #[serde(rename = "raw")]
176 Raw,
177}
178
179#[derive(Debug, Clone, Serialize, Deserialize, bon::Builder)]
181#[serde(default)]
182#[cfg_attr(
183 feature = "python",
184 pyo3::pyclass(
185 module = "nautilus_trader.core.nautilus_pyo3.interactive_brokers",
186 subclass,
187 from_py_object
188 )
189)]
190pub struct InteractiveBrokersInstrumentProviderConfig {
191 #[builder(default)]
193 pub symbology_method: SymbologyMethod,
194 #[builder(default)]
196 pub load_ids: HashSet<InstrumentId>,
197 #[builder(default)]
199 pub load_contracts: Vec<serde_json::Value>,
200 pub min_expiry_days: Option<u32>,
202 pub max_expiry_days: Option<u32>,
204 pub build_options_chain: Option<bool>,
206 pub build_futures_chain: Option<bool>,
208 pub cache_validity_days: Option<u32>,
210 #[builder(default)]
212 pub convert_exchange_to_mic_venue: bool,
213 #[builder(default)]
215 pub symbol_to_mic_venue: HashMap<String, String>,
216 #[builder(default)]
218 pub filter_sec_types: HashSet<String>,
219 pub filter_callable: Option<String>,
223 pub cache_path: Option<String>,
226}
227
228impl Default for InteractiveBrokersInstrumentProviderConfig {
229 fn default() -> Self {
230 Self::builder().build()
231 }
232}
233
234#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
236#[cfg_attr(
237 feature = "python",
238 pyo3::pyclass(
239 module = "nautilus_trader.core.nautilus_pyo3.interactive_brokers",
240 from_py_object
241 )
242)]
243#[derive(Default)]
244pub enum TradingMode {
245 #[serde(rename = "paper")]
247 #[default]
248 Paper,
249 #[serde(rename = "live")]
251 Live,
252}
253
254#[derive(Debug, Clone, Serialize, Deserialize, bon::Builder)]
259#[serde(default)]
260#[cfg_attr(
261 feature = "python",
262 pyo3::pyclass(
263 module = "nautilus_trader.core.nautilus_pyo3.interactive_brokers",
264 subclass,
265 from_py_object
266 )
267)]
268pub struct DockerizedIBGatewayConfig {
269 pub username: Option<String>,
271 pub password: Option<String>,
273 #[builder(default)]
275 pub trading_mode: TradingMode,
276 #[builder(default = true)]
278 pub read_only_api: bool,
279 #[builder(default = 300)]
281 pub timeout: u64,
282 #[builder(default = "ghcr.io/gnzsnz/ib-gateway:stable".to_string())]
284 pub container_image: String,
285 pub vnc_port: Option<u16>,
287}
288
289impl DockerizedIBGatewayConfig {
290 pub fn mask_sensitive_info(value: &str) -> String {
292 if value.len() <= 2 {
293 "*".repeat(value.len())
294 } else {
295 format!(
296 "{}{}{}",
297 &value[0..1],
298 "*".repeat(value.len() - 2),
299 &value[value.len() - 1..]
300 )
301 }
302 }
303
304 pub fn validate(&self) -> anyhow::Result<()> {
310 if self.timeout == 0 {
311 anyhow::bail!("Timeout must be greater than 0");
312 }
313
314 if self.timeout > 3600 {
315 anyhow::bail!("Timeout must be less than 3600 seconds");
316 }
317
318 if let Some(port) = self.vnc_port
319 && (!(5900..=5999).contains(&port))
320 {
321 anyhow::bail!("VNC port must be between 5900 and 5999");
322 }
323
324 Ok(())
325 }
326}
327
328impl Default for DockerizedIBGatewayConfig {
329 fn default() -> Self {
330 Self::builder()
331 .maybe_username(std::env::var("TWS_USERNAME").ok())
332 .maybe_password(std::env::var("TWS_PASSWORD").ok())
333 .build()
334 }
335}