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 #[allow(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 let mut order_id = 1;
207
208 for order in bids.iter_mut().take(DEPTH10_LEN) {
209 *order = BookOrder::new(
210 OrderSide::Buy,
211 Price::new(price, 2),
212 Quantity::new(quantity, 0),
213 order_id,
214 );
215
216 price -= 1.0;
217 quantity += 100.0;
218 order_id += 1;
219 }
220
221 let mut price = 100.00;
223 let mut quantity = 100.0;
224 let mut order_id = 11;
225
226 for order in asks.iter_mut().take(DEPTH10_LEN) {
227 *order = BookOrder::new(
228 OrderSide::Sell,
229 Price::new(price, 2),
230 Quantity::new(quantity, 0),
231 order_id,
232 );
233
234 price += 1.0;
235 quantity += 100.0;
236 order_id += 1;
237 }
238
239 let bid_counts: [u32; 10] = [1; 10];
240 let ask_counts: [u32; 10] = [1; 10];
241
242 Self::new(
243 instrument_id,
244 bids,
245 asks,
246 bid_counts,
247 ask_counts,
248 flags,
249 sequence,
250 ts_event.into(),
251 ts_init.into(),
252 )
253 }
254
255 #[staticmethod]
257 #[pyo3(name = "from_dict")]
258 fn py_from_dict(py: Python<'_>, values: Py<PyDict>) -> PyResult<Self> {
259 from_dict_pyo3(py, values)
260 }
261
262 #[pyo3(name = "as_pycapsule")]
278 fn py_as_pycapsule(&self, py: Python<'_>) -> Py<PyAny> {
279 data_to_pycapsule(py, Data::from(*self))
280 }
281
282 #[pyo3(name = "to_dict")]
284 fn py_to_dict(&self, py: Python<'_>) -> PyResult<Py<PyDict>> {
285 to_dict_pyo3(py, self)
286 }
287
288 #[pyo3(name = "to_json_bytes")]
290 fn py_to_json_bytes(&self, py: Python<'_>) -> Py<PyAny> {
291 self.to_json_bytes().unwrap().into_py_any_unwrap(py)
292 }
293
294 #[pyo3(name = "to_msgpack_bytes")]
296 fn py_to_msgpack_bytes(&self, py: Python<'_>) -> Py<PyAny> {
297 self.to_msgpack_bytes().unwrap().into_py_any_unwrap(py)
298 }
299}
300
301#[pymethods]
302impl OrderBookDepth10 {
303 #[staticmethod]
304 #[pyo3(name = "from_json")]
305 fn py_from_json(data: &[u8]) -> PyResult<Self> {
306 Self::from_json_bytes(data).map_err(to_pyvalue_err)
307 }
308
309 #[staticmethod]
310 #[pyo3(name = "from_msgpack")]
311 fn py_from_msgpack(data: &[u8]) -> PyResult<Self> {
312 Self::from_msgpack_bytes(data).map_err(to_pyvalue_err)
313 }
314}