nautilus_interactive_brokers/python/
providers.rs1use nautilus_core::python::to_pyruntime_err;
19use nautilus_model::{identifiers::InstrumentId, python::instruments::instrument_any_to_pyobject};
20use pyo3::{prelude::*, types::PyList};
21
22use crate::{
23 providers::instruments::InteractiveBrokersInstrumentProvider,
24 python::conversion::{contract_details_to_pyobject, py_to_contract},
25};
26
27#[cfg(feature = "python")]
28#[pymethods]
29impl InteractiveBrokersInstrumentProvider {
30 #[new]
31 fn py_new(config: crate::config::InteractiveBrokersInstrumentProviderConfig) -> Self {
32 Self::new(config)
33 }
34
35 fn __repr__(&self) -> String {
36 format!("{self:?}")
37 }
38
39 #[pyo3(name = "find")]
41 fn py_find(&self, py: Python, instrument_id: InstrumentId) -> PyResult<Option<Py<PyAny>>> {
42 match self.find(&instrument_id) {
43 Some(instrument) => Ok(Some(instrument_any_to_pyobject(py, instrument)?)),
44 None => Ok(None),
45 }
46 }
47
48 #[pyo3(name = "find_by_contract_id")]
50 fn py_find_by_contract_id(&self, py: Python, contract_id: i32) -> PyResult<Option<Py<PyAny>>> {
51 match self.find_by_contract_id(contract_id) {
52 Some(instrument) => Ok(Some(instrument_any_to_pyobject(py, instrument)?)),
53 None => Ok(None),
54 }
55 }
56
57 #[pyo3(name = "get_all")]
59 fn py_get_all<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyList>> {
60 let instruments = self.get_all();
61 let py_instruments: PyResult<Vec<_>> = instruments
62 .into_iter()
63 .map(|inst| instrument_any_to_pyobject(py, inst))
64 .collect();
65 PyList::new(py, py_instruments?)
66 }
67
68 #[pyo3(name = "count")]
70 fn py_count(&self) -> usize {
71 self.count()
72 }
73
74 #[pyo3(name = "get_price_magnifier")]
76 fn py_get_price_magnifier(&self, instrument_id: InstrumentId) -> i32 {
77 self.get_price_magnifier(&instrument_id)
78 }
79
80 #[pyo3(name = "fetch_contract_details")]
85 fn py_fetch_contract_details(&self) {}
86
87 #[pyo3(name = "determine_venue")]
89 #[allow(clippy::needless_pass_by_value)]
90 fn py_determine_venue(&self, py: Python<'_>, contract: Py<PyAny>) -> PyResult<String> {
91 let rust_contract = py_to_contract(contract.bind(py))?;
92 Ok(self.determine_venue(&rust_contract, None).to_string())
93 }
94
95 #[pyo3(name = "instrument_id_to_ib_contract_details")]
97 fn py_instrument_id_to_ib_contract_details(
98 &self,
99 py: Python<'_>,
100 instrument_id: InstrumentId,
101 ) -> PyResult<Option<Py<PyAny>>> {
102 self.instrument_id_to_ib_contract_details(&instrument_id)
103 .as_ref()
104 .map(|details| contract_details_to_pyobject(py, details))
105 .transpose()
106 }
107
108 #[pyo3(name = "batch_load")]
114 #[allow(clippy::needless_pass_by_value)]
115 fn py_batch_load<'py>(
116 &self,
117 py: Python<'py>,
118 instrument_ids: Vec<InstrumentId>,
119 ) -> PyResult<Bound<'py, PyAny>> {
120 let _ = instrument_ids;
121 pyo3_async_runtimes::tokio::future_into_py(py, async move {
124 Err::<usize, _>(to_pyruntime_err(
125 "batch_load requires an IB client. Use data_client.batch_load() or execution_client.batch_load() instead.",
126 ))
127 })
128 }
129
130 #[pyo3(signature = (underlying_symbol, expiry_min=None, expiry_max=None))]
135 fn py_fetch_option_chain_by_range<'py>(
136 &self,
137 py: Python<'py>,
138 underlying_symbol: String,
139 expiry_min: Option<String>,
140 expiry_max: Option<String>,
141 ) -> PyResult<Bound<'py, PyAny>> {
142 let _ = (underlying_symbol, expiry_min, expiry_max);
143 pyo3_async_runtimes::tokio::future_into_py(py, async move {
146 Err::<usize, _>(to_pyruntime_err(
147 "fetch_option_chain_by_range requires an IB client. Use data_client.fetch_option_chain_by_range() instead.",
148 ))
149 })
150 }
151
152 #[pyo3(signature = (underlying_symbol, expiry_min=None, expiry_max=None))]
157 fn py_fetch_futures_chain<'py>(
158 &self,
159 py: Python<'py>,
160 underlying_symbol: String,
161 expiry_min: Option<String>,
162 expiry_max: Option<String>,
163 ) -> PyResult<Bound<'py, PyAny>> {
164 let _ = (underlying_symbol, expiry_min, expiry_max);
165 pyo3_async_runtimes::tokio::future_into_py(py, async move {
168 Err::<usize, _>(to_pyruntime_err(
169 "fetch_futures_chain requires an IB client. Use data_client.fetch_futures_chain() instead.",
170 ))
171 })
172 }
173
174 #[pyo3(signature = (bag_contract))]
179 #[allow(clippy::needless_pass_by_value)]
180 fn py_fetch_bag_contract<'py>(
181 &self,
182 py: Python<'py>,
183 bag_contract: String, ) -> PyResult<Bound<'py, PyAny>> {
185 let _ = bag_contract;
186 pyo3_async_runtimes::tokio::future_into_py(py, async move {
189 Err::<(), _>(to_pyruntime_err(
190 "fetch_bag_contract requires an IB client. Use data_client.fetch_bag_contract() instead.",
191 ))
192 })
193 }
194
195 #[pyo3(name = "save_cache")]
205 fn py_save_cache<'py>(
206 &self,
207 py: Python<'py>,
208 cache_path: String,
209 ) -> PyResult<Bound<'py, PyAny>> {
210 let provider = self.clone();
211 pyo3_async_runtimes::tokio::future_into_py(py, async move {
212 provider
213 .save_cache(&cache_path)
214 .await
215 .map_err(to_pyruntime_err)
216 })
217 }
218
219 #[pyo3(name = "load_cache")]
233 fn py_load_cache<'py>(
234 &self,
235 py: Python<'py>,
236 cache_path: String,
237 ) -> PyResult<Bound<'py, PyAny>> {
238 let provider = self.clone();
239 pyo3_async_runtimes::tokio::future_into_py(py, async move {
240 provider
241 .load_cache(&cache_path)
242 .await
243 .map_err(to_pyruntime_err)
244 })
245 }
246}