1use std::{fmt::Debug, path::PathBuf, time::Duration};
17
18use nautilus_core::python::to_pyvalue_err;
19use nautilus_model::{
20 data::{Data, FundingRateUpdate, OrderBookDelta, OrderBookDepth10, QuoteTick, TradeTick},
21 identifiers::InstrumentId,
22};
23use pyo3::prelude::*;
24
25use crate::csv::{
26 convert::{TardisOptionsChainCSVConverterConfig, convert_options_chain_csv},
27 load::{
28 load_deltas, load_depth10_from_snapshot5, load_depth10_from_snapshot25, load_funding_rates,
29 load_options_chain, load_quotes, load_trades,
30 },
31 stream::{
32 stream_batched_deltas, stream_deltas, stream_depth10_from_snapshot5,
33 stream_depth10_from_snapshot25, stream_funding_rates, stream_options_chain, stream_quotes,
34 stream_trades,
35 },
36};
37
38macro_rules! impl_tardis_stream_iterator {
39 ($struct_name:ident, $data_type:ty, $type_name:expr) => {
40 #[pyclass(unsendable)]
41 #[pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.adapters.tardis")]
42 pub struct $struct_name {
43 stream: Box<dyn Iterator<Item = anyhow::Result<Vec<$data_type>>>>,
44 }
45
46 impl Debug for $struct_name {
47 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
48 write!(f, "{} {{ stream: ... }}", $type_name)
49 }
50 }
51
52 #[pymethods]
53 #[pyo3_stub_gen::derive::gen_stub_pymethods]
54 impl $struct_name {
55 const fn __iter__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> {
56 slf
57 }
58
59 fn __next__(&mut self) -> PyResult<Option<Vec<$data_type>>> {
60 match self.stream.next() {
61 Some(Ok(chunk)) => Ok(Some(chunk)),
62 Some(Err(e)) => Err(to_pyvalue_err(e)),
63 None => Ok(None),
64 }
65 }
66 }
67 };
68}
69
70fn options_chain_data_to_pyobject(py: Python<'_>, data: Data) -> PyResult<Py<PyAny>> {
71 match data {
72 Data::Quote(quote) => Py::new(py, quote).map(|value| value.into_any()),
73 Data::OptionGreeks(greeks) => Py::new(py, greeks).map(|value| value.into_any()),
74 data => Err(to_pyvalue_err(format!(
75 "Unsupported options_chain data type: {data:?}"
76 ))),
77 }
78}
79
80#[pyfunction(name = "load_tardis_deltas")]
84#[pyo3_stub_gen::derive::gen_stub_pyfunction(module = "nautilus_trader.adapters.tardis")]
85#[pyo3(signature = (filepath, price_precision=None, size_precision=None, instrument_id=None, limit=None))]
86pub fn py_load_tardis_deltas(
87 filepath: PathBuf,
88 price_precision: Option<u8>,
89 size_precision: Option<u8>,
90 instrument_id: Option<InstrumentId>,
91 limit: Option<usize>,
92) -> PyResult<Vec<OrderBookDelta>> {
93 load_deltas(
94 filepath,
95 price_precision,
96 size_precision,
97 instrument_id,
98 limit,
99 )
100 .map_err(to_pyvalue_err)
101}
102
103#[pyfunction(name = "load_tardis_depth10_from_snapshot5")]
107#[pyo3_stub_gen::derive::gen_stub_pyfunction(module = "nautilus_trader.adapters.tardis")]
108#[pyo3(signature = (filepath, price_precision=None, size_precision=None, instrument_id=None, limit=None))]
109pub fn py_load_tardis_depth10_from_snapshot5(
110 filepath: PathBuf,
111 price_precision: Option<u8>,
112 size_precision: Option<u8>,
113 instrument_id: Option<InstrumentId>,
114 limit: Option<usize>,
115) -> PyResult<Vec<OrderBookDepth10>> {
116 load_depth10_from_snapshot5(
117 filepath,
118 price_precision,
119 size_precision,
120 instrument_id,
121 limit,
122 )
123 .map_err(to_pyvalue_err)
124}
125
126#[pyfunction(name = "load_tardis_depth10_from_snapshot25")]
130#[pyo3_stub_gen::derive::gen_stub_pyfunction(module = "nautilus_trader.adapters.tardis")]
131#[pyo3(signature = (filepath, price_precision=None, size_precision=None, instrument_id=None, limit=None))]
132pub fn py_load_tardis_depth10_from_snapshot25(
133 filepath: PathBuf,
134 price_precision: Option<u8>,
135 size_precision: Option<u8>,
136 instrument_id: Option<InstrumentId>,
137 limit: Option<usize>,
138) -> PyResult<Vec<OrderBookDepth10>> {
139 load_depth10_from_snapshot25(
140 filepath,
141 price_precision,
142 size_precision,
143 instrument_id,
144 limit,
145 )
146 .map_err(to_pyvalue_err)
147}
148
149#[pyfunction(name = "load_tardis_quotes")]
153#[pyo3_stub_gen::derive::gen_stub_pyfunction(module = "nautilus_trader.adapters.tardis")]
154#[pyo3(signature = (filepath, price_precision=None, size_precision=None, instrument_id=None, limit=None))]
155pub fn py_load_tardis_quotes(
156 filepath: PathBuf,
157 price_precision: Option<u8>,
158 size_precision: Option<u8>,
159 instrument_id: Option<InstrumentId>,
160 limit: Option<usize>,
161) -> PyResult<Vec<QuoteTick>> {
162 load_quotes(
163 filepath,
164 price_precision,
165 size_precision,
166 instrument_id,
167 limit,
168 )
169 .map_err(to_pyvalue_err)
170}
171
172#[pyfunction(name = "load_tardis_trades")]
176#[pyo3_stub_gen::derive::gen_stub_pyfunction(module = "nautilus_trader.adapters.tardis")]
177#[pyo3(signature = (filepath, price_precision=None, size_precision=None, instrument_id=None, limit=None))]
178pub fn py_load_tardis_trades(
179 filepath: PathBuf,
180 price_precision: Option<u8>,
181 size_precision: Option<u8>,
182 instrument_id: Option<InstrumentId>,
183 limit: Option<usize>,
184) -> PyResult<Vec<TradeTick>> {
185 load_trades(
186 filepath,
187 price_precision,
188 size_precision,
189 instrument_id,
190 limit,
191 )
192 .map_err(to_pyvalue_err)
193}
194
195#[pyfunction(name = "load_tardis_funding_rates")]
199#[pyo3_stub_gen::derive::gen_stub_pyfunction(module = "nautilus_trader.adapters.tardis")]
200#[pyo3(signature = (filepath, instrument_id=None, limit=None))]
201pub fn py_load_tardis_funding_rates(
202 filepath: PathBuf,
203 instrument_id: Option<InstrumentId>,
204 limit: Option<usize>,
205) -> PyResult<Vec<FundingRateUpdate>> {
206 load_funding_rates(filepath, instrument_id, limit).map_err(to_pyvalue_err)
207}
208
209#[pyfunction(name = "load_tardis_options_chain")]
213#[pyo3_stub_gen::derive::gen_stub_pyfunction(module = "nautilus_trader.adapters.tardis")]
214#[pyo3(signature = (filepath, underlyings=None, price_precision=None, size_precision=None, limit=None))]
215pub fn py_load_tardis_options_chain(
216 py: Python<'_>,
217 filepath: PathBuf,
218 underlyings: Option<Vec<String>>,
219 price_precision: Option<u8>,
220 size_precision: Option<u8>,
221 limit: Option<usize>,
222) -> PyResult<Vec<Py<PyAny>>> {
223 load_options_chain(
224 filepath,
225 underlyings,
226 price_precision,
227 size_precision,
228 limit,
229 )
230 .map_err(to_pyvalue_err)?
231 .into_iter()
232 .map(|data| options_chain_data_to_pyobject(py, data))
233 .collect()
234}
235
236#[pyfunction(name = "convert_tardis_options_chain_csv")]
242#[pyo3_stub_gen::derive::gen_stub_pyfunction(module = "nautilus_trader.adapters.tardis")]
243#[pyo3(signature = (filepaths, catalog_path, underlyings=None, snapshot_interval_ms=None, extract_bbo_as_quotes=true, write_instruments=true, price_precision=None, size_precision=None))]
244#[allow(
245 clippy::too_many_arguments,
246 reason = "PyO3 exposes these keyword arguments as function parameters"
247)]
248pub fn py_convert_tardis_options_chain_csv(
249 py: Python<'_>,
250 filepaths: Vec<PathBuf>,
251 catalog_path: PathBuf,
252 underlyings: Option<Vec<String>>,
253 snapshot_interval_ms: Option<u64>,
254 extract_bbo_as_quotes: bool,
255 write_instruments: bool,
256 price_precision: Option<u8>,
257 size_precision: Option<u8>,
258) -> PyResult<()> {
259 let config = TardisOptionsChainCSVConverterConfig {
260 filepaths,
261 catalog_path,
262 underlyings,
263 snapshot_interval: snapshot_interval_ms.map(Duration::from_millis),
264 extract_bbo_as_quotes,
265 write_instruments,
266 price_precision,
267 size_precision,
268 };
269
270 py.detach(|| convert_options_chain_csv(&config))
271 .map_err(to_pyvalue_err)
272}
273
274impl_tardis_stream_iterator!(
275 TardisDeltaStreamIterator,
276 OrderBookDelta,
277 "TardisDeltasStreamIterator"
278);
279
280#[pyfunction(name = "stream_tardis_deltas")]
286#[pyo3_stub_gen::derive::gen_stub_pyfunction(module = "nautilus_trader.adapters.tardis")]
287#[pyo3(signature = (filepath, chunk_size=100_000, price_precision=None, size_precision=None, instrument_id=None, limit=None))]
288pub fn py_stream_tardis_deltas(
289 filepath: PathBuf,
290 chunk_size: usize,
291 price_precision: Option<u8>,
292 size_precision: Option<u8>,
293 instrument_id: Option<InstrumentId>,
294 limit: Option<usize>,
295) -> PyResult<TardisDeltaStreamIterator> {
296 let stream = stream_deltas(
297 filepath,
298 chunk_size,
299 price_precision,
300 size_precision,
301 instrument_id,
302 limit,
303 )
304 .map_err(to_pyvalue_err)?;
305
306 Ok(TardisDeltaStreamIterator {
307 stream: Box::new(stream),
308 })
309}
310
311#[pyclass(unsendable)]
312#[pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.adapters.tardis")]
313pub struct TardisBatchedDeltasStreamIterator {
314 stream: Box<dyn Iterator<Item = anyhow::Result<Vec<Py<PyAny>>>>>,
315}
316
317impl Debug for TardisBatchedDeltasStreamIterator {
318 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
319 write!(f, "TardisBatchedDeltasStreamIterator {{ stream: ... }}")
320 }
321}
322
323#[pymethods]
324#[pyo3_stub_gen::derive::gen_stub_pymethods]
325impl TardisBatchedDeltasStreamIterator {
326 const fn __iter__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> {
327 slf
328 }
329
330 fn __next__(&mut self) -> PyResult<Option<Vec<Py<PyAny>>>> {
331 match self.stream.next() {
332 Some(Ok(batch)) => Ok(Some(batch)),
333 Some(Err(e)) => Err(to_pyvalue_err(e)),
334 None => Ok(None),
335 }
336 }
337}
338
339#[pyfunction(name = "stream_tardis_batched_deltas")]
345#[pyo3_stub_gen::derive::gen_stub_pyfunction(module = "nautilus_trader.adapters.tardis")]
346#[pyo3(signature = (filepath, chunk_size=100_000, price_precision=None, size_precision=None, instrument_id=None, limit=None))]
347pub fn py_stream_tardis_batched_deltas(
348 filepath: PathBuf,
349 chunk_size: usize,
350 price_precision: Option<u8>,
351 size_precision: Option<u8>,
352 instrument_id: Option<InstrumentId>,
353 limit: Option<usize>,
354) -> PyResult<TardisBatchedDeltasStreamIterator> {
355 let stream = stream_batched_deltas(
356 filepath,
357 chunk_size,
358 price_precision,
359 size_precision,
360 instrument_id,
361 limit,
362 )
363 .map_err(to_pyvalue_err)?;
364
365 Ok(TardisBatchedDeltasStreamIterator {
366 stream: Box::new(stream),
367 })
368}
369
370impl_tardis_stream_iterator!(
371 TardisQuoteStreamIterator,
372 QuoteTick,
373 "TardisQuoteStreamIterator"
374);
375
376#[pyfunction(name = "stream_tardis_quotes")]
382#[pyo3_stub_gen::derive::gen_stub_pyfunction(module = "nautilus_trader.adapters.tardis")]
383#[pyo3(signature = (filepath, chunk_size=100_000, price_precision=None, size_precision=None, instrument_id=None, limit=None))]
384pub fn py_stream_tardis_quotes(
385 filepath: PathBuf,
386 chunk_size: usize,
387 price_precision: Option<u8>,
388 size_precision: Option<u8>,
389 instrument_id: Option<InstrumentId>,
390 limit: Option<usize>,
391) -> PyResult<TardisQuoteStreamIterator> {
392 let stream = stream_quotes(
393 filepath,
394 chunk_size,
395 price_precision,
396 size_precision,
397 instrument_id,
398 limit,
399 )
400 .map_err(to_pyvalue_err)?;
401
402 Ok(TardisQuoteStreamIterator {
403 stream: Box::new(stream),
404 })
405}
406
407#[pyclass(unsendable)]
408#[pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.adapters.tardis")]
409pub struct TardisOptionsChainStreamIterator {
410 stream: Box<dyn Iterator<Item = anyhow::Result<Vec<Data>>>>,
411}
412
413impl Debug for TardisOptionsChainStreamIterator {
414 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
415 write!(f, "TardisOptionsChainStreamIterator {{ stream: ... }}")
416 }
417}
418
419#[pymethods]
420#[pyo3_stub_gen::derive::gen_stub_pymethods]
421impl TardisOptionsChainStreamIterator {
422 const fn __iter__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> {
423 slf
424 }
425
426 fn __next__(&mut self, py: Python<'_>) -> PyResult<Option<Vec<Py<PyAny>>>> {
427 match self.stream.next() {
428 Some(Ok(chunk)) => chunk
429 .into_iter()
430 .map(|data| options_chain_data_to_pyobject(py, data))
431 .collect::<PyResult<Vec<_>>>()
432 .map(Some),
433 Some(Err(e)) => Err(to_pyvalue_err(e)),
434 None => Ok(None),
435 }
436 }
437}
438
439#[pyfunction(name = "stream_tardis_options_chain")]
445#[pyo3_stub_gen::derive::gen_stub_pyfunction(module = "nautilus_trader.adapters.tardis")]
446#[pyo3(signature = (filepath, chunk_size=100_000, underlyings=None, price_precision=None, size_precision=None, limit=None))]
447pub fn py_stream_tardis_options_chain(
448 filepath: PathBuf,
449 chunk_size: usize,
450 underlyings: Option<Vec<String>>,
451 price_precision: Option<u8>,
452 size_precision: Option<u8>,
453 limit: Option<usize>,
454) -> PyResult<TardisOptionsChainStreamIterator> {
455 let stream = stream_options_chain(
456 filepath,
457 chunk_size,
458 underlyings,
459 price_precision,
460 size_precision,
461 limit,
462 )
463 .map_err(to_pyvalue_err)?;
464
465 Ok(TardisOptionsChainStreamIterator {
466 stream: Box::new(stream),
467 })
468}
469
470impl_tardis_stream_iterator!(
471 TardisTradeStreamIterator,
472 TradeTick,
473 "TardisTradeStreamIterator"
474);
475
476#[pyfunction(name = "stream_tardis_trades")]
482#[pyo3_stub_gen::derive::gen_stub_pyfunction(module = "nautilus_trader.adapters.tardis")]
483#[pyo3(signature = (filepath, chunk_size=100_000, price_precision=None, size_precision=None, instrument_id=None, limit=None))]
484pub fn py_stream_tardis_trades(
485 filepath: PathBuf,
486 chunk_size: usize,
487 price_precision: Option<u8>,
488 size_precision: Option<u8>,
489 instrument_id: Option<InstrumentId>,
490 limit: Option<usize>,
491) -> PyResult<TardisTradeStreamIterator> {
492 let stream = stream_trades(
493 filepath,
494 chunk_size,
495 price_precision,
496 size_precision,
497 instrument_id,
498 limit,
499 )
500 .map_err(to_pyvalue_err)?;
501
502 Ok(TardisTradeStreamIterator {
503 stream: Box::new(stream),
504 })
505}
506
507impl_tardis_stream_iterator!(
508 TardisDepth10StreamIterator,
509 OrderBookDepth10,
510 "TardisDepth10StreamIterator"
511);
512
513#[pyfunction(name = "stream_tardis_depth10_from_snapshot5")]
519#[pyo3_stub_gen::derive::gen_stub_pyfunction(module = "nautilus_trader.adapters.tardis")]
520#[pyo3(signature = (filepath, chunk_size=100_000, price_precision=None, size_precision=None, instrument_id=None, limit=None))]
521pub fn py_stream_tardis_depth10_from_snapshot5(
522 filepath: PathBuf,
523 chunk_size: usize,
524 price_precision: Option<u8>,
525 size_precision: Option<u8>,
526 instrument_id: Option<InstrumentId>,
527 limit: Option<usize>,
528) -> PyResult<TardisDepth10StreamIterator> {
529 let stream = stream_depth10_from_snapshot5(
530 filepath,
531 chunk_size,
532 price_precision,
533 size_precision,
534 instrument_id,
535 limit,
536 )
537 .map_err(to_pyvalue_err)?;
538
539 Ok(TardisDepth10StreamIterator {
540 stream: Box::new(stream),
541 })
542}
543
544#[pyfunction(name = "stream_tardis_depth10_from_snapshot25")]
550#[pyo3_stub_gen::derive::gen_stub_pyfunction(module = "nautilus_trader.adapters.tardis")]
551#[pyo3(signature = (filepath, chunk_size=100_000, price_precision=None, size_precision=None, instrument_id=None, limit=None))]
552pub fn py_stream_tardis_depth10_from_snapshot25(
553 filepath: PathBuf,
554 chunk_size: usize,
555 price_precision: Option<u8>,
556 size_precision: Option<u8>,
557 instrument_id: Option<InstrumentId>,
558 limit: Option<usize>,
559) -> PyResult<TardisDepth10StreamIterator> {
560 let stream = stream_depth10_from_snapshot25(
561 filepath,
562 chunk_size,
563 price_precision,
564 size_precision,
565 instrument_id,
566 limit,
567 )
568 .map_err(to_pyvalue_err)?;
569
570 Ok(TardisDepth10StreamIterator {
571 stream: Box::new(stream),
572 })
573}
574
575impl_tardis_stream_iterator!(
576 TardisFundingRateStreamIterator,
577 FundingRateUpdate,
578 "TardisFundingRateStreamIterator"
579);
580
581#[pyfunction(name = "stream_tardis_funding_rates")]
587#[pyo3_stub_gen::derive::gen_stub_pyfunction(module = "nautilus_trader.adapters.tardis")]
588#[pyo3(signature = (filepath, chunk_size=100_000, instrument_id=None, limit=None))]
589pub fn py_stream_tardis_funding_rates(
590 filepath: PathBuf,
591 chunk_size: usize,
592 instrument_id: Option<InstrumentId>,
593 limit: Option<usize>,
594) -> PyResult<TardisFundingRateStreamIterator> {
595 let stream =
596 stream_funding_rates(filepath, chunk_size, instrument_id, limit).map_err(to_pyvalue_err)?;
597
598 Ok(TardisFundingRateStreamIterator {
599 stream: Box::new(stream),
600 })
601}