1// -------------------------------------------------------------------------------------------------
2// Copyright (C) 2015-2025 Nautech Systems Pty Ltd. All rights reserved.
3// https://nautechsystems.io
4//
5// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
6// You may not use this file except in compliance with the License.
7// You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14// -------------------------------------------------------------------------------------------------
1516use nautilus_core::{
17 ffi::cvec::CVec,
18 python::{IntoPyObjectNautilusExt, to_pyruntime_err},
19};
20use nautilus_model::data::{Bar, OrderBookDelta, OrderBookDepth10, QuoteTick, TradeTick};
21use pyo3::{prelude::*, types::PyCapsule};
2223use crate::backend::session::{DataBackendSession, DataQueryResult};
2425#[repr(C)]
26#[pyclass(eq, eq_int)]
27#[derive(Clone, Copy, Debug, PartialEq, Eq)]
28pub enum NautilusDataType {
29// Custom = 0, # First slot reserved for custom data
30OrderBookDelta = 1,
31 OrderBookDepth10 = 2,
32 QuoteTick = 3,
33 TradeTick = 4,
34 Bar = 5,
35}
3637#[pymethods]
38impl DataBackendSession {
39#[new]
40 #[pyo3(signature=(chunk_size=10_000))]
41fn new_session(chunk_size: usize) -> Self {
42Self::new(chunk_size)
43 }
4445/// Query a file for its records. the caller must specify `T` to indicate
46 /// the kind of data expected from this query.
47 ///
48 /// table_name: Logical table_name assigned to this file. Queries to this file should address the
49 /// file by its table name.
50 /// file_path: Path to file
51 /// sql_query: A custom sql query to retrieve records from file. If no query is provided a default
52 /// query "SELECT * FROM <table_name>" is run.
53 ///
54 /// # Safety
55 ///
56 /// The file data must be ordered by the ts_init in ascending order for this
57 /// to work correctly.
58#[pyo3(name = "add_file")]
59 #[pyo3(signature = (data_type, table_name, file_path, sql_query=None))]
60fn add_file_py(
61mut slf: PyRefMut<'_, Self>,
62 data_type: NautilusDataType,
63 table_name: &str,
64 file_path: &str,
65 sql_query: Option<&str>,
66 ) -> PyResult<()> {
67let _guard = slf.runtime.enter();
6869match data_type {
70 NautilusDataType::OrderBookDelta => slf
71 .add_file::<OrderBookDelta>(table_name, file_path, sql_query)
72 .map_err(to_pyruntime_err),
73 NautilusDataType::OrderBookDepth10 => slf
74 .add_file::<OrderBookDepth10>(table_name, file_path, sql_query)
75 .map_err(to_pyruntime_err),
76 NautilusDataType::QuoteTick => slf
77 .add_file::<QuoteTick>(table_name, file_path, sql_query)
78 .map_err(to_pyruntime_err),
79 NautilusDataType::TradeTick => slf
80 .add_file::<TradeTick>(table_name, file_path, sql_query)
81 .map_err(to_pyruntime_err),
82 NautilusDataType::Bar => slf
83 .add_file::<Bar>(table_name, file_path, sql_query)
84 .map_err(to_pyruntime_err),
85 }
86 }
8788fn to_query_result(mut slf: PyRefMut<'_, Self>) -> DataQueryResult {
89let query_result = slf.get_query_result();
90 DataQueryResult::new(query_result, slf.chunk_size)
91 }
92}
9394#[pymethods]
95impl DataQueryResult {
96/// The reader implements an iterator.
97const fn __iter__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> {
98 slf
99 }
100101/// Each iteration returns a chunk of values read from the parquet file.
102fn __next__(mut slf: PyRefMut<'_, Self>) -> PyResult<Option<PyObject>> {
103match slf.next() {
104Some(acc) if !acc.is_empty() => {
105let cvec = slf.set_chunk(acc);
106 Python::with_gil(|py| match PyCapsule::new::<CVec>(py, cvec, None) {
107Ok(capsule) => Ok(Some(capsule.into_py_any_unwrap(py))),
108Err(e) => Err(to_pyruntime_err(e)),
109 })
110 }
111_ => Ok(None),
112 }
113 }
114}