nautilus_model/python/orderbook/
level.rs1use std::{
17 collections::hash_map::DefaultHasher,
18 hash::{Hash, Hasher},
19};
20
21use nautilus_core::python::IntoPyObjectNautilusExt;
22use pyo3::{prelude::*, pyclass::CompareOp};
23
24use crate::{
25 data::order::BookOrder,
26 enums::OrderSide,
27 orderbook::BookLevel,
28 python::types::fixed::FloatArithmetic,
29 types::{price::Price, quantity::QuantityRaw},
30};
31
32#[pymethods]
33#[pyo3_stub_gen::derive::gen_stub_pymethods]
34impl BookLevel {
35 fn __repr__(&self) -> String {
36 format!("{self:?}")
37 }
38
39 fn __str__(&self) -> String {
40 format!("{self:?}")
42 }
43
44 fn __richcmp__(&self, other: &Self, op: CompareOp, py: Python<'_>) -> Py<PyAny> {
45 match op {
46 CompareOp::Eq => self.eq(other).into_py_any_unwrap(py),
47 CompareOp::Ne => self.ne(other).into_py_any_unwrap(py),
48 CompareOp::Ge if self.side() == other.side() => self.ge(other).into_py_any_unwrap(py),
49 CompareOp::Gt if self.side() == other.side() => self.gt(other).into_py_any_unwrap(py),
50 CompareOp::Le if self.side() == other.side() => self.le(other).into_py_any_unwrap(py),
51 CompareOp::Lt if self.side() == other.side() => self.lt(other).into_py_any_unwrap(py),
52 CompareOp::Ge | CompareOp::Gt | CompareOp::Le | CompareOp::Lt => py.NotImplemented(),
53 }
54 }
55
56 fn __hash__(&self) -> isize {
57 let mut hasher = DefaultHasher::new();
58 self.side().hash(&mut hasher);
59 self.price.value.hash(&mut hasher);
60 hasher.finish() as isize
61 }
62
63 #[getter]
64 #[pyo3(name = "price")]
65 fn py_price(&self) -> Price {
66 self.price.value
67 }
68
69 #[getter]
70 #[pyo3(name = "side")]
71 fn py_side(&self) -> OrderSide {
72 self.side()
73 }
74
75 #[pyo3(name = "len")]
77 fn py_len(&self) -> usize {
78 self.len()
79 }
80
81 #[pyo3(name = "is_empty")]
83 fn py_is_empty(&self) -> bool {
84 self.is_empty()
85 }
86
87 #[pyo3(name = "size")]
89 fn py_size(&self) -> PyResult<f64> {
90 check_sizes_float_precision(self)?;
91 Ok(self.size())
92 }
93
94 #[pyo3(name = "size_raw")]
96 fn py_size_raw(&self) -> QuantityRaw {
97 self.size_raw()
98 }
99
100 #[pyo3(name = "exposure")]
102 fn py_exposure(&self) -> PyResult<f64> {
103 for order in self.orders.values() {
104 order.price.check_float_precision()?;
105 order.size.check_float_precision()?;
106 }
107
108 Ok(self.exposure())
109 }
110
111 #[pyo3(name = "exposure_raw")]
119 fn py_exposure_raw(&self) -> QuantityRaw {
120 self.exposure_raw()
121 }
122
123 #[pyo3(name = "first")]
124 fn py_fist(&self) -> Option<BookOrder> {
125 self.first().copied()
126 }
127
128 #[pyo3(name = "get_orders")]
130 fn py_get_orders(&self) -> Vec<BookOrder> {
131 self.get_orders()
132 }
133}
134
135pub(super) fn check_sizes_float_precision(level: &BookLevel) -> PyResult<()> {
136 for order in level.orders.values() {
137 order.size.check_float_precision()?;
138 }
139
140 Ok(())
141}