nautilus_model/python/data/
depth.rs1use std::{
17 collections::{HashMap, hash_map::DefaultHasher},
18 hash::{Hash, Hasher},
19};
20
21use nautilus_core::{
22 python::{
23 IntoPyObjectNautilusExt,
24 serialization::{from_dict_pyo3, to_dict_pyo3},
25 to_pyvalue_err,
26 },
27 serialization::{
28 Serializable,
29 msgpack::{FromMsgPack, ToMsgPack},
30 },
31};
32use pyo3::{prelude::*, pyclass::CompareOp, types::PyDict};
33
34use super::data_to_pycapsule;
35use crate::{
36 data::{
37 Data,
38 depth::{DEPTH10_LEN, OrderBookDepth10},
39 order::BookOrder,
40 },
41 enums::OrderSide,
42 identifiers::InstrumentId,
43 python::common::PY_MODULE_MODEL,
44 types::{Price, Quantity},
45};
46
47#[pymethods]
48#[pyo3_stub_gen::derive::gen_stub_pymethods]
49impl OrderBookDepth10 {
50 #[expect(clippy::too_many_arguments)]
60 #[new]
61 fn py_new(
62 instrument_id: InstrumentId,
63 bids: [BookOrder; DEPTH10_LEN],
64 asks: [BookOrder; DEPTH10_LEN],
65 bid_counts: [u32; DEPTH10_LEN],
66 ask_counts: [u32; DEPTH10_LEN],
67 flags: u8,
68 sequence: u64,
69 ts_event: u64,
70 ts_init: u64,
71 ) -> Self {
72 Self::new(
73 instrument_id,
74 bids,
75 asks,
76 bid_counts,
77 ask_counts,
78 flags,
79 sequence,
80 ts_event.into(),
81 ts_init.into(),
82 )
83 }
84
85 fn __richcmp__(&self, other: &Self, op: CompareOp, py: Python<'_>) -> Py<PyAny> {
86 match op {
87 CompareOp::Eq => self.eq(other).into_py_any_unwrap(py),
88 CompareOp::Ne => self.ne(other).into_py_any_unwrap(py),
89 _ => py.NotImplemented(),
90 }
91 }
92
93 fn __hash__(&self) -> isize {
94 let mut h = DefaultHasher::new();
95 self.hash(&mut h);
96 h.finish() as isize
97 }
98
99 fn __repr__(&self) -> String {
100 format!("{self:?}")
101 }
102
103 fn __str__(&self) -> String {
104 self.to_string()
105 }
106
107 #[getter]
108 #[pyo3(name = "instrument_id")]
109 fn py_instrument_id(&self) -> InstrumentId {
110 self.instrument_id
111 }
112
113 #[getter]
114 #[pyo3(name = "bids")]
115 fn py_bids(&self) -> [BookOrder; DEPTH10_LEN] {
116 self.bids
117 }
118
119 #[getter]
120 #[pyo3(name = "asks")]
121 fn py_asks(&self) -> [BookOrder; DEPTH10_LEN] {
122 self.asks
123 }
124
125 #[getter]
126 #[pyo3(name = "bid_counts")]
127 fn py_bid_counts(&self) -> [u32; DEPTH10_LEN] {
128 self.bid_counts
129 }
130
131 #[getter]
132 #[pyo3(name = "ask_counts")]
133 fn py_ask_counts(&self) -> [u32; DEPTH10_LEN] {
134 self.ask_counts
135 }
136
137 #[getter]
138 #[pyo3(name = "flags")]
139 fn py_flags(&self) -> u8 {
140 self.flags
141 }
142
143 #[getter]
144 #[pyo3(name = "sequence")]
145 fn py_sequence(&self) -> u64 {
146 self.sequence
147 }
148
149 #[getter]
150 #[pyo3(name = "ts_event")]
151 fn py_ts_event(&self) -> u64 {
152 self.ts_event.as_u64()
153 }
154
155 #[getter]
156 #[pyo3(name = "ts_init")]
157 fn py_ts_init(&self) -> u64 {
158 self.ts_init.as_u64()
159 }
160
161 #[staticmethod]
162 #[pyo3(name = "fully_qualified_name")]
163 fn py_fully_qualified_name() -> String {
164 format!("{}:{}", PY_MODULE_MODEL, stringify!(OrderBookDepth10))
165 }
166
167 #[staticmethod]
169 #[pyo3(name = "get_metadata")]
170 fn py_get_metadata(
171 instrument_id: &InstrumentId,
172 price_precision: u8,
173 size_precision: u8,
174 ) -> HashMap<String, String> {
175 Self::get_metadata(instrument_id, price_precision, size_precision)
176 }
177
178 #[staticmethod]
180 #[pyo3(name = "get_fields")]
181 fn py_get_fields(py: Python<'_>) -> PyResult<Bound<'_, PyDict>> {
182 let py_dict = PyDict::new(py);
183 for (k, v) in Self::get_fields() {
184 py_dict.set_item(k, v)?;
185 }
186
187 Ok(py_dict)
188 }
189
190 #[staticmethod]
192 #[pyo3(name = "get_stub")]
193 fn py_get_stub() -> Self {
194 let instrument_id = InstrumentId::from("AAPL.XNAS");
195 let flags = 0;
196 let sequence = 0;
197 let ts_event = 1;
198 let ts_init = 2;
199
200 let mut bids: [BookOrder; DEPTH10_LEN] = [BookOrder::default(); DEPTH10_LEN];
201 let mut asks: [BookOrder; DEPTH10_LEN] = [BookOrder::default(); DEPTH10_LEN];
202
203 let mut price = 99.00;
205 let mut quantity = 100.0;
206
207 for (i, order) in bids.iter_mut().take(DEPTH10_LEN).enumerate() {
208 *order = BookOrder::new(
209 OrderSide::Buy,
210 Price::new(price, 2),
211 Quantity::new(quantity, 0),
212 (i + 1) as u64,
213 );
214
215 price -= 1.0;
216 quantity += 100.0;
217 }
218
219 let mut price = 100.00;
221 let mut quantity = 100.0;
222
223 for (i, order) in asks.iter_mut().take(DEPTH10_LEN).enumerate() {
224 *order = BookOrder::new(
225 OrderSide::Sell,
226 Price::new(price, 2),
227 Quantity::new(quantity, 0),
228 (i + 11) as u64,
229 );
230
231 price += 1.0;
232 quantity += 100.0;
233 }
234
235 let bid_counts: [u32; 10] = [1; 10];
236 let ask_counts: [u32; 10] = [1; 10];
237
238 Self::new(
239 instrument_id,
240 bids,
241 asks,
242 bid_counts,
243 ask_counts,
244 flags,
245 sequence,
246 ts_event.into(),
247 ts_init.into(),
248 )
249 }
250
251 #[staticmethod]
253 #[pyo3(name = "from_dict")]
254 fn py_from_dict(py: Python<'_>, values: Py<PyDict>) -> PyResult<Self> {
255 from_dict_pyo3(py, values)
256 }
257
258 #[pyo3(name = "as_pycapsule")]
274 fn py_as_pycapsule(&self, py: Python<'_>) -> Py<PyAny> {
275 data_to_pycapsule(py, Data::from(*self))
276 }
277
278 #[pyo3(name = "to_dict")]
280 fn py_to_dict(&self, py: Python<'_>) -> PyResult<Py<PyDict>> {
281 to_dict_pyo3(py, self)
282 }
283
284 #[pyo3(name = "to_json_bytes")]
286 fn py_to_json_bytes(&self, py: Python<'_>) -> Py<PyAny> {
287 self.to_json_bytes().unwrap().into_py_any_unwrap(py)
288 }
289
290 #[pyo3(name = "to_msgpack_bytes")]
292 fn py_to_msgpack_bytes(&self, py: Python<'_>) -> Py<PyAny> {
293 self.to_msgpack_bytes().unwrap().into_py_any_unwrap(py)
294 }
295}
296
297#[pymethods]
298impl OrderBookDepth10 {
299 #[staticmethod]
300 #[pyo3(name = "from_json")]
301 fn py_from_json(data: &[u8]) -> PyResult<Self> {
302 Self::from_json_bytes(data).map_err(to_pyvalue_err)
303 }
304
305 #[staticmethod]
306 #[pyo3(name = "from_msgpack")]
307 fn py_from_msgpack(data: &[u8]) -> PyResult<Self> {
308 Self::from_msgpack_bytes(data).map_err(to_pyvalue_err)
309 }
310}