nautilus_testkit/
common.rs1use std::{
17 fs::File,
18 path::{Path, PathBuf},
19 sync::OnceLock,
20};
21
22use nautilus_core::paths::get_test_data_path;
23use nautilus_model::{
24 data::OrderBookDelta,
25 instruments::{InstrumentAny, stubs::equity_aapl_itch},
26};
27use nautilus_serialization::arrow::DecodeFromRecordBatch;
28use parquet::arrow::arrow_reader::ParquetRecordBatchReaderBuilder;
29
30use crate::files::ensure_file_exists_or_download_http;
31
32#[must_use]
38pub fn get_test_data_file_path(path: &str) -> String {
39 get_test_data_path()
40 .join(path)
41 .to_str()
42 .unwrap()
43 .to_string()
44}
45
46#[must_use]
52#[allow(unused_mut)]
53pub fn get_nautilus_test_data_file_path(filename: &str) -> String {
54 let mut path = get_test_data_path().join("nautilus");
55
56 #[cfg(feature = "high-precision")]
57 {
58 path = path.join("128-bit");
59 }
60 #[cfg(not(feature = "high-precision"))]
61 {
62 path = path.join("64-bit");
63 }
64
65 path.join(filename).to_str().unwrap().to_string()
66}
67
68#[must_use]
70pub fn get_test_data_large_checksums_filepath() -> PathBuf {
71 get_test_data_path().join("large").join("checksums.json")
72}
73
74#[must_use]
80pub fn ensure_test_data_exists(filename: &str, url: &str) -> PathBuf {
81 let filepath = get_test_data_path().join("large").join(filename);
82 let checksums_filepath = get_test_data_large_checksums_filepath();
83 ensure_file_exists_or_download_http(&filepath, url, Some(&checksums_filepath), None).unwrap();
84 filepath
85}
86
87#[must_use]
93pub fn ensure_itch_aapl_deltas_parquet() -> PathBuf {
94 ensure_test_data_exists(
95 "itch_AAPL.XNAS_2019-01-30_deltas.parquet",
96 "https://test-data.nautechsystems.io/large/itch_AAPL.XNAS_2019-01-30_deltas.parquet",
97 )
98}
99
100#[must_use]
106pub fn ensure_tardis_deribit_deltas_parquet() -> PathBuf {
107 ensure_test_data_exists(
108 "tardis_BTC-PERPETUAL.DERIBIT_2020-04-01_deltas.parquet",
109 "https://test-data.nautechsystems.io/large/tardis_BTC-PERPETUAL.DERIBIT_2020-04-01_deltas.parquet",
110 )
111}
112
113#[must_use]
120pub fn ensure_histdata_eurusd_quotes_parquet() -> PathBuf {
121 ensure_test_data_exists(
122 "histdata_EURUSD.SIM_2020-01_quotes.parquet",
123 "https://test-data.nautechsystems.io/large/histdata_EURUSD.SIM_2020-01_quotes.parquet",
124 )
125}
126
127#[must_use]
134pub fn ensure_histdata_eurusd_instrument_parquet() -> PathBuf {
135 ensure_test_data_exists(
136 "histdata_EURUSD.SIM_2020-01_instrument.parquet",
137 "https://test-data.nautechsystems.io/large/histdata_EURUSD.SIM_2020-01_instrument.parquet",
138 )
139}
140
141#[must_use]
143pub fn get_tardis_deribit_book_l2_path() -> PathBuf {
144 get_test_data_path()
145 .join("tardis")
146 .join("deribit_incremental_book_L2_BTC-PERPETUAL.csv")
147}
148
149#[must_use]
151pub fn get_tardis_binance_snapshot5_path() -> PathBuf {
152 get_test_data_path()
153 .join("tardis")
154 .join("binance-futures_book_snapshot_5_BTCUSDT.csv")
155}
156
157#[must_use]
159pub fn get_tardis_binance_snapshot25_path() -> PathBuf {
160 get_test_data_path()
161 .join("tardis")
162 .join("binance-futures_book_snapshot_25_BTCUSDT.csv")
163}
164
165#[must_use]
167pub fn get_tardis_huobi_quotes_path() -> PathBuf {
168 get_test_data_path()
169 .join("tardis")
170 .join("huobi-dm-swap_quotes_BTC-USD.csv")
171}
172
173#[must_use]
175pub fn get_tardis_bitmex_trades_path() -> PathBuf {
176 get_test_data_path()
177 .join("tardis")
178 .join("bitmex_trades_XBTUSD.csv")
179}
180
181#[must_use]
184pub fn itch_aapl_equity() -> InstrumentAny {
185 InstrumentAny::Equity(equity_aapl_itch())
186}
187
188#[must_use]
192pub fn load_itch_aapl_deltas(limit: Option<usize>) -> Vec<OrderBookDelta> {
193 static PATH: OnceLock<PathBuf> = OnceLock::new();
194 let filepath = PATH.get_or_init(ensure_itch_aapl_deltas_parquet);
195 load_deltas_from_parquet(filepath, limit)
196}
197
198#[must_use]
202pub fn load_tardis_deribit_deltas(limit: Option<usize>) -> Vec<OrderBookDelta> {
203 static PATH: OnceLock<PathBuf> = OnceLock::new();
204 let filepath = PATH.get_or_init(ensure_tardis_deribit_deltas_parquet);
205 load_deltas_from_parquet(filepath, limit)
206}
207
208fn load_deltas_from_parquet(filepath: &Path, limit: Option<usize>) -> Vec<OrderBookDelta> {
209 let file = File::open(filepath).unwrap();
210 let mut builder = ParquetRecordBatchReaderBuilder::try_new(file).unwrap();
211 let metadata = builder.schema().metadata().clone();
212
213 if let Some(limit) = limit {
214 builder = builder.with_limit(limit);
215 }
216 let reader = builder.build().unwrap();
217
218 let mut deltas = Vec::new();
219
220 for batch_result in reader {
221 let batch = batch_result.unwrap();
222 let batch_deltas = OrderBookDelta::decode_batch(&metadata, batch).unwrap();
223 deltas.extend(batch_deltas);
224 }
225 deltas
226}