nautilus_model/python/data/mod.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
// -------------------------------------------------------------------------------------------------
// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
// You may not use this file except in compliance with the License.
// You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// -------------------------------------------------------------------------------------------------
//! Data types for the trading domain model.
pub mod bar;
pub mod delta;
pub mod deltas;
pub mod depth;
pub mod greeks;
pub mod order;
pub mod quote;
pub mod status;
pub mod trade;
use indexmap::IndexMap;
#[cfg(feature = "ffi")]
use nautilus_core::ffi::cvec::CVec;
use pyo3::{exceptions::PyValueError, prelude::*, types::PyCapsule};
use crate::data::{
bar::Bar, delta::OrderBookDelta, is_monotonically_increasing_by_init, quote::QuoteTick,
trade::TradeTick, Data, DataType,
};
const ERROR_MONOTONICITY: &str = "`data` was not monotonically increasing by the `ts_init` field";
#[pymethods]
impl DataType {
#[new]
#[pyo3(signature = (type_name, metadata=None))]
fn py_new(type_name: &str, metadata: Option<IndexMap<String, String>>) -> Self {
Self::new(type_name, metadata)
}
#[getter]
#[pyo3(name = "type_name")]
fn py_type_name(&self) -> &str {
self.type_name()
}
#[getter]
#[pyo3(name = "metadata")]
fn py_metadata(&self) -> Option<IndexMap<String, String>> {
self.metadata().cloned()
}
#[getter]
#[pyo3(name = "topic")]
fn py_topic(&self) -> &str {
self.topic()
}
}
/// Creates a Python `PyCapsule` object containing a Rust `Data` instance.
///
/// This function takes ownership of the `Data` instance and encapsulates it within
/// a `PyCapsule` object, allowing the Rust data to be passed into the Python runtime.
///
/// # Panics
///
/// This function will panic if the `PyCapsule` creation fails, which may occur if
/// there are issues with memory allocation or if the `Data` instance cannot be
/// properly encapsulated.
///
/// # Safety
///
/// This function is safe as long as the `Data` instance does not violate Rust's
/// safety guarantees (e.g., no invalid memory access). Users of the
/// `PyCapsule` in Python must ensure they understand how to extract and use the
/// encapsulated `Data` safely, especially when converting the capsule back to a
/// Rust data structure.
#[must_use]
pub fn data_to_pycapsule(py: Python, data: Data) -> PyObject {
let capsule = PyCapsule::new_bound(py, data, None).expect("Error creating `PyCapsule`");
capsule.into_py(py)
}
/// Drops a `PyCapsule` containing a `CVec` structure.
///
/// This function safely extracts and drops the `CVec` instance encapsulated within
/// a `PyCapsule` object. It is intended for cleaning up after the `Data` instances
/// have been transferred into Python and are no longer needed.
///
/// # Panics
///
/// This function panics:
/// - If the capsule cannot be downcast to a `PyCapsule`, indicating a type mismatch
/// or improper capsule handling.
///
/// # Safety
///
/// This function is unsafe as it involves raw pointer dereferencing and manual memory
/// management. The caller must ensure the `PyCapsule` contains a valid `CVec` pointer.
/// Incorrect usage can lead to memory corruption or undefined behavior.
#[pyfunction]
#[cfg(feature = "ffi")]
pub fn drop_cvec_pycapsule(capsule: &Bound<'_, PyAny>) {
let capsule: &Bound<'_, PyCapsule> = capsule
.downcast::<PyCapsule>()
.expect("Error on downcast to `&PyCapsule`");
let cvec: &CVec = unsafe { &*(capsule.pointer() as *const CVec) };
let data: Vec<Data> =
unsafe { Vec::from_raw_parts(cvec.ptr.cast::<Data>(), cvec.len, cvec.cap) };
drop(data);
}
#[pyfunction]
#[cfg(not(feature = "ffi"))]
pub fn drop_cvec_pycapsule(_capsule: &Bound<'_, PyAny>) {
panic!("`ffi` feature is not enabled");
}
/// Transforms the given `data` Python objects into a vector of [`OrderBookDelta`] objects.
pub fn pyobjects_to_order_book_deltas(
data: Vec<Bound<'_, PyAny>>,
) -> PyResult<Vec<OrderBookDelta>> {
let deltas: Vec<OrderBookDelta> = data
.into_iter()
.map(|obj| OrderBookDelta::from_pyobject(&obj))
.collect::<PyResult<Vec<OrderBookDelta>>>()?;
// Validate monotonically increasing
if !is_monotonically_increasing_by_init(&deltas) {
return Err(PyValueError::new_err(ERROR_MONOTONICITY));
}
Ok(deltas)
}
/// Transforms the given `data` Python objects into a vector of [`QuoteTick`] objects.
pub fn pyobjects_to_quote_ticks(data: Vec<Bound<'_, PyAny>>) -> PyResult<Vec<QuoteTick>> {
let ticks: Vec<QuoteTick> = data
.into_iter()
.map(|obj| QuoteTick::from_pyobject(&obj))
.collect::<PyResult<Vec<QuoteTick>>>()?;
// Validate monotonically increasing
if !is_monotonically_increasing_by_init(&ticks) {
return Err(PyValueError::new_err(ERROR_MONOTONICITY));
}
Ok(ticks)
}
/// Transforms the given `data` Python objects into a vector of [`TradeTick`] objects.
pub fn pyobjects_to_trade_ticks(data: Vec<Bound<'_, PyAny>>) -> PyResult<Vec<TradeTick>> {
let ticks: Vec<TradeTick> = data
.into_iter()
.map(|obj| TradeTick::from_pyobject(&obj))
.collect::<PyResult<Vec<TradeTick>>>()?;
// Validate monotonically increasing
if !is_monotonically_increasing_by_init(&ticks) {
return Err(PyValueError::new_err(ERROR_MONOTONICITY));
}
Ok(ticks)
}
/// Transforms the given `data` Python objects into a vector of [`Bar`] objects.
pub fn pyobjects_to_bars(data: Vec<Bound<'_, PyAny>>) -> PyResult<Vec<Bar>> {
let bars: Vec<Bar> = data
.into_iter()
.map(|obj| Bar::from_pyobject(&obj))
.collect::<PyResult<Vec<Bar>>>()?;
// Validate monotonically increasing
if !is_monotonically_increasing_by_init(&bars) {
return Err(PyValueError::new_err(ERROR_MONOTONICITY));
}
Ok(bars)
}