nautilus_interactive_brokers/python/
historical.rs1use ibapi::contracts::Contract;
19use jiff::Timestamp;
20use nautilus_common::live::get_runtime;
21use nautilus_core::python::{to_pyruntime_err, to_pyvalue_err};
22use nautilus_model::{
23 data::{Bar, Data},
24 identifiers::InstrumentId,
25 instruments::any::InstrumentAny,
26 python::{data::data_to_pyobject, instruments::instrument_any_to_pyobject},
27};
28use pyo3::{prelude::*, types::PyList};
29
30use crate::{
31 common::enums::IbHistoricalTickType, historical::HistoricalInteractiveBrokersClient,
32 python::conversion::py_list_to_contracts,
33};
34
35#[pymethods]
36#[pyo3_stub_gen::derive::gen_stub_pymethods]
37impl HistoricalInteractiveBrokersClient {
38 #[new]
39 #[allow(clippy::needless_pass_by_value)]
40 fn py_new(
41 instrument_provider: crate::providers::instruments::InteractiveBrokersInstrumentProvider,
42 config: crate::config::InteractiveBrokersDataClientConfig,
43 ) -> PyResult<Self> {
44 get_runtime()
45 .block_on(Self::connect_with_provider(instrument_provider, config))
46 .map_err(to_pyruntime_err)
47 }
48
49 fn __repr__(&self) -> String {
50 format!("{self:?}")
51 }
52
53 #[pyo3(signature = (bar_specifications, end_date_time, start_date_time=None, duration=None, contracts=None, instrument_ids=None, use_rth=true, timeout=60))]
75 #[pyo3(name = "request_bars")]
76 #[allow(clippy::too_many_arguments)]
77 #[allow(clippy::needless_pass_by_value)]
78 fn py_request_bars<'py>(
79 &self,
80 py: Python<'py>,
81 bar_specifications: Vec<String>,
82 end_date_time: Timestamp,
83 start_date_time: Option<Timestamp>,
84 duration: Option<String>,
85 contracts: Option<Py<PyList>>,
86 instrument_ids: Option<Vec<InstrumentId>>,
87 use_rth: bool,
88 timeout: u64,
89 ) -> PyResult<Bound<'py, PyAny>> {
90 let client = self.clone();
91 let bar_specs = bar_specifications;
92 let duration_str = duration;
93
94 let contracts_vec: Option<Vec<Contract>> = if let Some(py_contracts) = contracts.as_ref() {
96 let py_contracts_bound = py_contracts.bind(py);
97 match py_list_to_contracts(py_contracts_bound) {
98 Ok(contracts) => Some(contracts),
99 Err(e) => {
100 return Err(to_pyvalue_err(format!("Failed to convert contracts: {e}")));
101 }
102 }
103 } else {
104 None
105 };
106
107 pyo3_async_runtimes::tokio::future_into_py(py, async move {
108 let bar_specs_refs: Vec<&str> = bar_specs.iter().map(|s| s.as_str()).collect();
110 let bars: Vec<Bar> = client
111 .request_bars(
112 bar_specs_refs,
113 end_date_time,
114 start_date_time,
115 duration_str.as_deref(),
116 contracts_vec,
117 instrument_ids,
118 use_rth,
119 timeout,
120 )
121 .await
122 .map_err(to_pyruntime_err)?;
123 Ok(bars)
125 })
126 }
127
128 #[pyo3(signature = (tick_type, start_date_time, end_date_time, contracts=None, instrument_ids=None, use_rth=true, timeout=60, limit=0))]
141 #[pyo3(name = "request_ticks")]
142 #[allow(clippy::too_many_arguments)]
143 #[allow(clippy::needless_pass_by_value)]
144 fn py_request_ticks<'py>(
145 &self,
146 py: Python<'py>,
147 tick_type: IbHistoricalTickType,
148 start_date_time: Timestamp,
149 end_date_time: Timestamp,
150 contracts: Option<Py<PyList>>,
151 instrument_ids: Option<Vec<InstrumentId>>,
152 use_rth: bool,
153 timeout: u64,
154 limit: usize,
155 ) -> PyResult<Bound<'py, PyAny>> {
156 let client = self.clone();
157
158 let contracts_vec: Option<Vec<Contract>> = if let Some(py_contracts) = contracts.as_ref() {
160 let py_contracts_bound = py_contracts.bind(py);
161 match py_list_to_contracts(py_contracts_bound) {
162 Ok(contracts) => Some(contracts),
163 Err(e) => {
164 return Err(to_pyvalue_err(format!("Failed to convert contracts: {e}")));
165 }
166 }
167 } else {
168 None
169 };
170
171 pyo3_async_runtimes::tokio::future_into_py(py, async move {
172 let data_vec: Vec<Data> = client
173 .request_ticks(
174 tick_type,
175 start_date_time,
176 end_date_time,
177 contracts_vec,
178 instrument_ids,
179 use_rth,
180 timeout,
181 limit,
182 )
183 .await
184 .map_err(to_pyruntime_err)?;
185 Python::attach(|py| -> PyResult<Py<PyList>> {
186 let py_list = PyList::empty(py);
187 for data in data_vec {
188 py_list.append(data_to_pyobject(py, data)?)?;
189 }
190 Ok(py_list.into())
191 })
192 })
193 }
194
195 #[pyo3(signature = (instrument_ids=None, contracts=None))]
202 #[pyo3(name = "request_instruments")]
203 #[allow(clippy::needless_pass_by_value)]
204 fn py_request_instruments<'py>(
205 &self,
206 py: Python<'py>,
207 instrument_ids: Option<Vec<InstrumentId>>,
208 contracts: Option<Py<PyList>>,
209 ) -> PyResult<Bound<'py, PyAny>> {
210 let client = self.clone();
211
212 let contracts_vec: Option<Vec<Contract>> = if let Some(py_contracts) = contracts.as_ref() {
214 let py_contracts_bound = py_contracts.bind(py);
215 match py_list_to_contracts(py_contracts_bound) {
216 Ok(contracts) => Some(contracts),
217 Err(e) => {
218 return Err(to_pyvalue_err(format!("Failed to convert contracts: {e}")));
219 }
220 }
221 } else {
222 None
223 };
224
225 pyo3_async_runtimes::tokio::future_into_py(py, async move {
226 let instruments: Vec<InstrumentAny> = client
227 .request_instruments(instrument_ids, contracts_vec)
228 .await
229 .map_err(to_pyruntime_err)?;
230 Python::attach(|py| -> PyResult<Py<PyList>> {
232 let py_list = PyList::empty(py);
233
234 for instrument in instruments {
235 let py_obj =
236 instrument_any_to_pyobject(py, instrument).map_err(to_pyruntime_err)?;
237 py_list.append(py_obj)?;
238 }
239 Ok(py_list.into())
240 })
241 })
242 }
243}