nautilus_interactive_brokers/python/
historical.rs1use chrono::{DateTime, Utc};
19use ibapi::contracts::Contract;
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_pycapsule, 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]
36impl HistoricalInteractiveBrokersClient {
37 #[new]
38 #[allow(clippy::needless_pass_by_value)]
39 fn py_new(
40 instrument_provider: crate::providers::instruments::InteractiveBrokersInstrumentProvider,
41 config: crate::config::InteractiveBrokersDataClientConfig,
42 ) -> PyResult<Self> {
43 get_runtime()
44 .block_on(Self::connect_with_provider(instrument_provider, config))
45 .map_err(to_pyruntime_err)
46 }
47
48 fn __repr__(&self) -> String {
49 format!("{self:?}")
50 }
51
52 #[pyo3(signature = (bar_specifications, end_date_time, start_date_time=None, duration=None, contracts=None, instrument_ids=None, use_rth=true, timeout=60))]
65 #[pyo3(name = "request_bars")]
66 #[allow(clippy::too_many_arguments)]
67 #[allow(clippy::needless_pass_by_value)]
68 fn py_request_bars<'py>(
69 &self,
70 py: Python<'py>,
71 bar_specifications: Vec<String>,
72 end_date_time: DateTime<Utc>,
73 start_date_time: Option<DateTime<Utc>>,
74 duration: Option<String>,
75 contracts: Option<Py<PyList>>,
76 instrument_ids: Option<Vec<InstrumentId>>,
77 use_rth: bool,
78 timeout: u64,
79 ) -> PyResult<Bound<'py, PyAny>> {
80 let client = self.clone();
81 let bar_specs = bar_specifications;
82 let duration_str = duration;
83
84 let contracts_vec: Option<Vec<Contract>> = if let Some(py_contracts) = contracts.as_ref() {
86 let py_contracts_bound = py_contracts.bind(py);
87 match py_list_to_contracts(py_contracts_bound) {
88 Ok(contracts) => Some(contracts),
89 Err(e) => {
90 return Err(to_pyvalue_err(format!("Failed to convert contracts: {e}")));
91 }
92 }
93 } else {
94 None
95 };
96
97 pyo3_async_runtimes::tokio::future_into_py(py, async move {
98 let bar_specs_refs: Vec<&str> = bar_specs.iter().map(|s| s.as_str()).collect();
100 let bars: Vec<Bar> = client
101 .request_bars(
102 bar_specs_refs,
103 end_date_time,
104 start_date_time,
105 duration_str.as_deref(),
106 contracts_vec,
107 instrument_ids,
108 use_rth,
109 timeout,
110 )
111 .await
112 .map_err(to_pyruntime_err)?;
113 Ok(bars)
115 })
116 }
117
118 #[pyo3(signature = (tick_type, start_date_time, end_date_time, contracts=None, instrument_ids=None, use_rth=true, timeout=60, limit=0))]
131 #[pyo3(name = "request_ticks")]
132 #[allow(clippy::too_many_arguments)]
133 #[allow(clippy::needless_pass_by_value)]
134 fn py_request_ticks<'py>(
135 &self,
136 py: Python<'py>,
137 tick_type: IbHistoricalTickType,
138 start_date_time: DateTime<Utc>,
139 end_date_time: DateTime<Utc>,
140 contracts: Option<Py<PyList>>,
141 instrument_ids: Option<Vec<InstrumentId>>,
142 use_rth: bool,
143 timeout: u64,
144 limit: usize,
145 ) -> PyResult<Bound<'py, PyAny>> {
146 let client = self.clone();
147
148 let contracts_vec: Option<Vec<Contract>> = if let Some(py_contracts) = contracts.as_ref() {
150 let py_contracts_bound = py_contracts.bind(py);
151 match py_list_to_contracts(py_contracts_bound) {
152 Ok(contracts) => Some(contracts),
153 Err(e) => {
154 return Err(to_pyvalue_err(format!("Failed to convert contracts: {e}")));
155 }
156 }
157 } else {
158 None
159 };
160
161 pyo3_async_runtimes::tokio::future_into_py(py, async move {
162 let data_vec: Vec<Data> = client
163 .request_ticks(
164 tick_type,
165 start_date_time,
166 end_date_time,
167 contracts_vec,
168 instrument_ids,
169 use_rth,
170 timeout,
171 limit,
172 )
173 .await
174 .map_err(to_pyruntime_err)?;
175 Python::attach(|py| -> PyResult<Py<PyList>> {
177 let py_list = PyList::empty(py);
178 for data in data_vec {
179 let py_capsule = data_to_pycapsule(py, data);
180 py_list.append(py_capsule)?;
181 }
182 Ok(py_list.into())
183 })
184 })
185 }
186
187 #[pyo3(signature = (instrument_ids=None, contracts=None))]
194 #[pyo3(name = "request_instruments")]
195 #[allow(clippy::needless_pass_by_value)]
196 fn py_request_instruments<'py>(
197 &self,
198 py: Python<'py>,
199 instrument_ids: Option<Vec<InstrumentId>>,
200 contracts: Option<Py<PyList>>,
201 ) -> PyResult<Bound<'py, PyAny>> {
202 let client = self.clone();
203
204 let contracts_vec: Option<Vec<Contract>> = if let Some(py_contracts) = contracts.as_ref() {
206 let py_contracts_bound = py_contracts.bind(py);
207 match py_list_to_contracts(py_contracts_bound) {
208 Ok(contracts) => Some(contracts),
209 Err(e) => {
210 return Err(to_pyvalue_err(format!("Failed to convert contracts: {e}")));
211 }
212 }
213 } else {
214 None
215 };
216
217 pyo3_async_runtimes::tokio::future_into_py(py, async move {
218 let instruments: Vec<InstrumentAny> = client
219 .request_instruments(instrument_ids, contracts_vec)
220 .await
221 .map_err(to_pyruntime_err)?;
222 Python::attach(|py| -> PyResult<Py<PyList>> {
224 let py_list = PyList::empty(py);
225
226 for instrument in instruments {
227 let py_obj =
228 instrument_any_to_pyobject(py, instrument).map_err(to_pyruntime_err)?;
229 py_list.append(py_obj)?;
230 }
231 Ok(py_list.into())
232 })
233 })
234 }
235}