1use std::{fmt::Debug, path::PathBuf, time::Duration};
17
18use nautilus_core::python::to_pyvalue_err;
19use nautilus_model::{
20 data::{Data, FundingRateUpdate, OrderBookDelta, OrderBookDepth, 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_depth_from_snapshot5, load_depth_from_snapshot25, load_funding_rates,
29 load_options_chain, load_quotes, load_trades,
30 },
31 stream::{
32 stream_batched_deltas, stream_deltas, stream_depth_from_snapshot5,
33 stream_depth_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_depth_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_depth_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<OrderBookDepth>> {
116 load_depth_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_depth_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_depth_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<OrderBookDepth>> {
139 load_depth_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 "TardisDeltaStreamIterator"
278);
279
280#[pyfunction(name = "stream_tardis_deltas")]
287#[pyo3_stub_gen::derive::gen_stub_pyfunction(module = "nautilus_trader.adapters.tardis")]
288#[pyo3(signature = (filepath, chunk_size=100_000, price_precision=None, size_precision=None, instrument_id=None, limit=None))]
289pub fn py_stream_tardis_deltas(
290 filepath: PathBuf,
291 chunk_size: usize,
292 price_precision: Option<u8>,
293 size_precision: Option<u8>,
294 instrument_id: Option<InstrumentId>,
295 limit: Option<usize>,
296) -> PyResult<TardisDeltaStreamIterator> {
297 let stream = stream_deltas(
298 filepath,
299 chunk_size,
300 price_precision,
301 size_precision,
302 instrument_id,
303 limit,
304 )
305 .map_err(to_pyvalue_err)?;
306
307 Ok(TardisDeltaStreamIterator {
308 stream: Box::new(stream),
309 })
310}
311
312#[pyclass(unsendable)]
313#[pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.adapters.tardis")]
314pub struct TardisBatchedDeltasStreamIterator {
315 stream: Box<dyn Iterator<Item = anyhow::Result<Vec<Py<PyAny>>>>>,
316}
317
318impl Debug for TardisBatchedDeltasStreamIterator {
319 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
320 write!(f, "TardisBatchedDeltasStreamIterator {{ stream: ... }}")
321 }
322}
323
324#[pymethods]
325#[pyo3_stub_gen::derive::gen_stub_pymethods]
326impl TardisBatchedDeltasStreamIterator {
327 const fn __iter__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> {
328 slf
329 }
330
331 fn __next__(&mut self) -> PyResult<Option<Vec<Py<PyAny>>>> {
332 match self.stream.next() {
333 Some(Ok(batch)) => Ok(Some(batch)),
334 Some(Err(e)) => Err(to_pyvalue_err(e)),
335 None => Ok(None),
336 }
337 }
338}
339
340#[pyfunction(name = "stream_tardis_batched_deltas")]
347#[pyo3_stub_gen::derive::gen_stub_pyfunction(module = "nautilus_trader.adapters.tardis")]
348#[pyo3(signature = (filepath, chunk_size=100_000, price_precision=None, size_precision=None, instrument_id=None, limit=None))]
349pub fn py_stream_tardis_batched_deltas(
350 filepath: PathBuf,
351 chunk_size: usize,
352 price_precision: Option<u8>,
353 size_precision: Option<u8>,
354 instrument_id: Option<InstrumentId>,
355 limit: Option<usize>,
356) -> PyResult<TardisBatchedDeltasStreamIterator> {
357 let stream = stream_batched_deltas(
358 filepath,
359 chunk_size,
360 price_precision,
361 size_precision,
362 instrument_id,
363 limit,
364 )
365 .map_err(to_pyvalue_err)?;
366
367 Ok(TardisBatchedDeltasStreamIterator {
368 stream: Box::new(stream),
369 })
370}
371
372impl_tardis_stream_iterator!(
373 TardisQuoteStreamIterator,
374 QuoteTick,
375 "TardisQuoteStreamIterator"
376);
377
378#[pyfunction(name = "stream_tardis_quotes")]
385#[pyo3_stub_gen::derive::gen_stub_pyfunction(module = "nautilus_trader.adapters.tardis")]
386#[pyo3(signature = (filepath, chunk_size=100_000, price_precision=None, size_precision=None, instrument_id=None, limit=None))]
387pub fn py_stream_tardis_quotes(
388 filepath: PathBuf,
389 chunk_size: usize,
390 price_precision: Option<u8>,
391 size_precision: Option<u8>,
392 instrument_id: Option<InstrumentId>,
393 limit: Option<usize>,
394) -> PyResult<TardisQuoteStreamIterator> {
395 let stream = stream_quotes(
396 filepath,
397 chunk_size,
398 price_precision,
399 size_precision,
400 instrument_id,
401 limit,
402 )
403 .map_err(to_pyvalue_err)?;
404
405 Ok(TardisQuoteStreamIterator {
406 stream: Box::new(stream),
407 })
408}
409
410#[pyclass(unsendable)]
411#[pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.adapters.tardis")]
412pub struct TardisOptionsChainStreamIterator {
413 stream: Box<dyn Iterator<Item = anyhow::Result<Vec<Data>>>>,
414}
415
416impl Debug for TardisOptionsChainStreamIterator {
417 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
418 write!(f, "TardisOptionsChainStreamIterator {{ stream: ... }}")
419 }
420}
421
422#[pymethods]
423#[pyo3_stub_gen::derive::gen_stub_pymethods]
424impl TardisOptionsChainStreamIterator {
425 const fn __iter__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> {
426 slf
427 }
428
429 fn __next__(&mut self, py: Python<'_>) -> PyResult<Option<Vec<Py<PyAny>>>> {
430 match self.stream.next() {
431 Some(Ok(chunk)) => chunk
432 .into_iter()
433 .map(|data| options_chain_data_to_pyobject(py, data))
434 .collect::<PyResult<Vec<_>>>()
435 .map(Some),
436 Some(Err(e)) => Err(to_pyvalue_err(e)),
437 None => Ok(None),
438 }
439 }
440}
441
442#[pyfunction(name = "stream_tardis_options_chain")]
449#[pyo3_stub_gen::derive::gen_stub_pyfunction(module = "nautilus_trader.adapters.tardis")]
450#[pyo3(signature = (filepath, chunk_size=100_000, underlyings=None, price_precision=None, size_precision=None, limit=None))]
451pub fn py_stream_tardis_options_chain(
452 filepath: PathBuf,
453 chunk_size: usize,
454 underlyings: Option<Vec<String>>,
455 price_precision: Option<u8>,
456 size_precision: Option<u8>,
457 limit: Option<usize>,
458) -> PyResult<TardisOptionsChainStreamIterator> {
459 let stream = stream_options_chain(
460 filepath,
461 chunk_size,
462 underlyings,
463 price_precision,
464 size_precision,
465 limit,
466 )
467 .map_err(to_pyvalue_err)?;
468
469 Ok(TardisOptionsChainStreamIterator {
470 stream: Box::new(stream),
471 })
472}
473
474impl_tardis_stream_iterator!(
475 TardisTradeStreamIterator,
476 TradeTick,
477 "TardisTradeStreamIterator"
478);
479
480#[pyfunction(name = "stream_tardis_trades")]
487#[pyo3_stub_gen::derive::gen_stub_pyfunction(module = "nautilus_trader.adapters.tardis")]
488#[pyo3(signature = (filepath, chunk_size=100_000, price_precision=None, size_precision=None, instrument_id=None, limit=None))]
489pub fn py_stream_tardis_trades(
490 filepath: PathBuf,
491 chunk_size: usize,
492 price_precision: Option<u8>,
493 size_precision: Option<u8>,
494 instrument_id: Option<InstrumentId>,
495 limit: Option<usize>,
496) -> PyResult<TardisTradeStreamIterator> {
497 let stream = stream_trades(
498 filepath,
499 chunk_size,
500 price_precision,
501 size_precision,
502 instrument_id,
503 limit,
504 )
505 .map_err(to_pyvalue_err)?;
506
507 Ok(TardisTradeStreamIterator {
508 stream: Box::new(stream),
509 })
510}
511
512impl_tardis_stream_iterator!(
513 TardisDepthStreamIterator,
514 OrderBookDepth,
515 "TardisDepthStreamIterator"
516);
517
518#[pyfunction(name = "stream_tardis_depth_from_snapshot5")]
525#[pyo3_stub_gen::derive::gen_stub_pyfunction(module = "nautilus_trader.adapters.tardis")]
526#[pyo3(signature = (filepath, chunk_size=100_000, price_precision=None, size_precision=None, instrument_id=None, limit=None))]
527pub fn py_stream_tardis_depth_from_snapshot5(
528 filepath: PathBuf,
529 chunk_size: usize,
530 price_precision: Option<u8>,
531 size_precision: Option<u8>,
532 instrument_id: Option<InstrumentId>,
533 limit: Option<usize>,
534) -> PyResult<TardisDepthStreamIterator> {
535 let stream = stream_depth_from_snapshot5(
536 filepath,
537 chunk_size,
538 price_precision,
539 size_precision,
540 instrument_id,
541 limit,
542 )
543 .map_err(to_pyvalue_err)?;
544
545 Ok(TardisDepthStreamIterator {
546 stream: Box::new(stream),
547 })
548}
549
550#[pyfunction(name = "stream_tardis_depth_from_snapshot25")]
557#[pyo3_stub_gen::derive::gen_stub_pyfunction(module = "nautilus_trader.adapters.tardis")]
558#[pyo3(signature = (filepath, chunk_size=100_000, price_precision=None, size_precision=None, instrument_id=None, limit=None))]
559pub fn py_stream_tardis_depth_from_snapshot25(
560 filepath: PathBuf,
561 chunk_size: usize,
562 price_precision: Option<u8>,
563 size_precision: Option<u8>,
564 instrument_id: Option<InstrumentId>,
565 limit: Option<usize>,
566) -> PyResult<TardisDepthStreamIterator> {
567 let stream = stream_depth_from_snapshot25(
568 filepath,
569 chunk_size,
570 price_precision,
571 size_precision,
572 instrument_id,
573 limit,
574 )
575 .map_err(to_pyvalue_err)?;
576
577 Ok(TardisDepthStreamIterator {
578 stream: Box::new(stream),
579 })
580}
581
582impl_tardis_stream_iterator!(
583 TardisFundingRateStreamIterator,
584 FundingRateUpdate,
585 "TardisFundingRateStreamIterator"
586);
587
588#[pyfunction(name = "stream_tardis_funding_rates")]
595#[pyo3_stub_gen::derive::gen_stub_pyfunction(module = "nautilus_trader.adapters.tardis")]
596#[pyo3(signature = (filepath, chunk_size=100_000, instrument_id=None, limit=None))]
597pub fn py_stream_tardis_funding_rates(
598 filepath: PathBuf,
599 chunk_size: usize,
600 instrument_id: Option<InstrumentId>,
601 limit: Option<usize>,
602) -> PyResult<TardisFundingRateStreamIterator> {
603 let stream =
604 stream_funding_rates(filepath, chunk_size, instrument_id, limit).map_err(to_pyvalue_err)?;
605
606 Ok(TardisFundingRateStreamIterator {
607 stream: Box::new(stream),
608 })
609}