nautilus_model/python/orderbook/
own.rs1use std::{
17 collections::{HashSet, hash_map::DefaultHasher},
18 hash::{Hash, Hasher},
19};
20
21use ahash::AHashSet;
22use indexmap::IndexMap;
23use nautilus_core::python::{IntoPyObjectNautilusExt, to_pyruntime_err, to_pyvalue_err};
24use pyo3::{Python, prelude::*, pyclass::CompareOp};
25use rust_decimal::Decimal;
26
27use crate::{
28 enums::{OrderSide, OrderStatus, OrderType, TimeInForce},
29 identifiers::{ClientOrderId, InstrumentId, TraderId, VenueOrderId},
30 orderbook::{OwnBookOrder, own::OwnOrderBook},
31 types::{Price, Quantity},
32};
33
34#[pymethods]
35#[pyo3_stub_gen::derive::gen_stub_pymethods]
36impl OwnBookOrder {
37 #[pyo3(signature = (trader_id, client_order_id, side, price, size, order_type, time_in_force, status, ts_last, ts_accepted, ts_submitted, ts_init, venue_order_id=None))]
42 #[new]
43 #[expect(clippy::too_many_arguments)]
44 fn py_new(
45 trader_id: TraderId,
46 client_order_id: ClientOrderId,
47 side: OrderSide,
48 price: Price,
49 size: Quantity,
50 order_type: OrderType,
51 time_in_force: TimeInForce,
52 status: OrderStatus,
53 ts_last: u64,
54 ts_accepted: u64,
55 ts_submitted: u64,
56 ts_init: u64,
57 venue_order_id: Option<VenueOrderId>,
58 ) -> Self {
59 Self::new(
60 trader_id,
61 client_order_id,
62 venue_order_id,
63 side.as_specified(),
64 price,
65 size,
66 order_type,
67 time_in_force,
68 status,
69 ts_last.into(),
70 ts_accepted.into(),
71 ts_submitted.into(),
72 ts_init.into(),
73 )
74 }
75
76 fn __richcmp__(&self, other: &Self, op: CompareOp, py: Python<'_>) -> Py<PyAny> {
77 match op {
78 CompareOp::Eq => self.eq(other).into_py_any_unwrap(py),
79 CompareOp::Ne => self.ne(other).into_py_any_unwrap(py),
80 _ => py.NotImplemented(),
81 }
82 }
83
84 fn __hash__(&self) -> isize {
85 let mut hasher = DefaultHasher::new();
86 self.hash(&mut hasher);
87 hasher.finish() as isize
88 }
89
90 fn __repr__(&self) -> String {
91 format!("{self:?}")
92 }
93
94 fn __str__(&self) -> String {
95 self.to_string()
96 }
97
98 #[getter]
99 #[pyo3(name = "client_order_id")]
100 fn py_client_order_id(&self) -> ClientOrderId {
101 self.client_order_id
102 }
103
104 #[getter]
105 #[pyo3(name = "side")]
106 fn py_side(&self) -> OrderSide {
107 self.side.as_order_side()
108 }
109
110 #[getter]
111 #[pyo3(name = "price")]
112 fn py_price(&self) -> Price {
113 self.price
114 }
115
116 #[getter]
117 #[pyo3(name = "size")]
118 fn py_size(&self) -> Quantity {
119 self.size
120 }
121
122 #[getter]
123 #[pyo3(name = "order_type")]
124 fn py_order_type(&self) -> OrderType {
125 self.order_type
126 }
127
128 #[getter]
129 #[pyo3(name = "time_in_force")]
130 fn py_time_in_force(&self) -> TimeInForce {
131 self.time_in_force
132 }
133
134 #[getter]
135 #[pyo3(name = "status")]
136 fn py_status(&self) -> OrderStatus {
137 self.status
138 }
139
140 #[getter]
141 #[pyo3(name = "ts_last")]
142 fn py_ts_last(&self) -> u64 {
143 self.ts_last.into()
144 }
145
146 #[getter]
147 #[pyo3(name = "ts_init")]
148 fn py_ts_init(&self) -> u64 {
149 self.ts_init.into()
150 }
151
152 #[pyo3(name = "exposure")]
154 fn py_exposure(&self) -> f64 {
155 self.exposure()
156 }
157
158 #[pyo3(name = "signed_size")]
160 fn py_signed_size(&self) -> f64 {
161 self.signed_size()
162 }
163}
164
165#[pymethods]
166#[pyo3_stub_gen::derive::gen_stub_pymethods]
167impl OwnOrderBook {
168 #[new]
170 fn py_new(instrument_id: InstrumentId) -> Self {
171 Self::new(instrument_id)
172 }
173
174 fn __repr__(&self) -> String {
175 format!("{self:?}")
176 }
177
178 fn __str__(&self) -> String {
179 self.to_string()
180 }
181
182 #[getter]
183 #[pyo3(name = "instrument_id")]
184 fn py_instrument_id(&self) -> InstrumentId {
185 self.instrument_id
186 }
187
188 #[getter]
189 #[pyo3(name = "ts_last")]
190 fn py_ts_last(&self) -> u64 {
191 self.ts_last.as_u64()
192 }
193
194 #[getter]
195 #[pyo3(name = "update_count")]
196 fn py_update_count(&self) -> u64 {
197 self.update_count
198 }
199
200 #[pyo3(name = "reset")]
202 fn py_reset(&mut self) {
203 self.reset();
204 }
205
206 #[pyo3(name = "add")]
208 fn py_add(&mut self, order: OwnBookOrder) {
209 self.add(order);
210 }
211
212 #[pyo3(name = "update")]
218 fn py_update(&mut self, order: OwnBookOrder) -> PyResult<()> {
219 self.update(order).map_err(to_pyruntime_err)
220 }
221
222 #[pyo3(name = "delete")]
228 fn py_delete(&mut self, order: OwnBookOrder) -> PyResult<()> {
229 self.delete(order).map_err(to_pyruntime_err)
230 }
231
232 #[pyo3(name = "clear")]
234 fn py_clear(&mut self) {
235 self.clear();
236 }
237
238 #[pyo3(name = "bid_client_order_ids")]
240 #[must_use]
241 pub fn py_bid_client_order_ids(&self) -> Vec<ClientOrderId> {
242 self.bid_client_order_ids()
243 }
244
245 #[pyo3(name = "ask_client_order_ids")]
247 #[must_use]
248 pub fn py_ask_client_order_ids(&self) -> Vec<ClientOrderId> {
249 self.ask_client_order_ids()
250 }
251
252 #[pyo3(name = "is_order_in_book")]
254 #[must_use]
255 pub fn py_is_order_in_book(&self, client_order_id: &ClientOrderId) -> bool {
256 self.is_order_in_book(client_order_id)
257 }
258
259 #[pyo3(name = "orders_to_list")]
260 fn py_orders_to_list(&self) -> Vec<OwnBookOrder> {
261 let total_orders = self.bids.cache.len() + self.asks.cache.len();
262 let mut all_orders = Vec::with_capacity(total_orders);
263
264 all_orders.extend(
265 self.bids()
266 .flat_map(|level| level.orders.values().copied())
267 .chain(self.asks().flat_map(|level| level.orders.values().copied())),
268 );
269
270 all_orders
271 }
272
273 #[pyo3(name = "bids_to_list")]
274 fn py_bids_to_list(&self) -> Vec<OwnBookOrder> {
275 self.bids()
276 .flat_map(|level| level.orders.values().copied())
277 .collect()
278 }
279
280 #[pyo3(name = "asks_to_list")]
281 fn py_asks_to_list(&self) -> Vec<OwnBookOrder> {
282 self.asks()
283 .flat_map(|level| level.orders.values().copied())
284 .collect()
285 }
286
287 #[pyo3(name = "bids_to_dict")]
288 #[pyo3(signature = (status=None, accepted_buffer_ns=None, ts_now=None))]
289 fn py_bids_to_dict(
290 &self,
291 status: Option<HashSet<OrderStatus>>,
292 accepted_buffer_ns: Option<u64>,
293 ts_now: Option<u64>,
294 ) -> IndexMap<Decimal, Vec<OwnBookOrder>> {
295 let status_set: Option<AHashSet<OrderStatus>> = status.map(|s| s.into_iter().collect());
296 self.bids_as_map(status_set.as_ref(), accepted_buffer_ns, ts_now)
297 }
298
299 #[pyo3(name = "asks_to_dict")]
300 #[pyo3(signature = (status=None, accepted_buffer_ns=None, ts_now=None))]
301 fn py_asks_to_dict(
302 &self,
303 status: Option<HashSet<OrderStatus>>,
304 accepted_buffer_ns: Option<u64>,
305 ts_now: Option<u64>,
306 ) -> IndexMap<Decimal, Vec<OwnBookOrder>> {
307 let status_set: Option<AHashSet<OrderStatus>> = status.map(|s| s.into_iter().collect());
308 self.asks_as_map(status_set.as_ref(), accepted_buffer_ns, ts_now)
309 }
310
311 #[pyo3(name = "bid_quantity")]
319 #[pyo3(signature = (status=None, depth=None, group_size=None, accepted_buffer_ns=None, ts_now=None))]
320 fn py_bid_quantity(
321 &self,
322 status: Option<HashSet<OrderStatus>>,
323 depth: Option<usize>,
324 group_size: Option<Decimal>,
325 accepted_buffer_ns: Option<u64>,
326 ts_now: Option<u64>,
327 ) -> IndexMap<Decimal, Decimal> {
328 let status_set: Option<AHashSet<OrderStatus>> = status.map(|s| s.into_iter().collect());
329 self.bid_quantity(
330 status_set.as_ref(),
331 depth,
332 group_size,
333 accepted_buffer_ns,
334 ts_now,
335 )
336 }
337
338 #[pyo3(name = "ask_quantity")]
346 #[pyo3(signature = (status=None, depth=None, group_size=None, accepted_buffer_ns=None, ts_now=None))]
347 fn py_ask_quantity(
348 &self,
349 status: Option<HashSet<OrderStatus>>,
350 depth: Option<usize>,
351 group_size: Option<Decimal>,
352 accepted_buffer_ns: Option<u64>,
353 ts_now: Option<u64>,
354 ) -> IndexMap<Decimal, Decimal> {
355 let status_set: Option<AHashSet<OrderStatus>> = status.map(|s| s.into_iter().collect());
356 self.ask_quantity(
357 status_set.as_ref(),
358 depth,
359 group_size,
360 accepted_buffer_ns,
361 ts_now,
362 )
363 }
364
365 #[pyo3(name = "combined_with_opposite")]
375 fn py_combined_with_opposite(&self, opposite: &Self) -> PyResult<Self> {
376 self.combined_with_opposite(opposite)
377 .map_err(to_pyvalue_err)
378 }
379
380 #[pyo3(name = "audit_open_orders")]
381 fn py_audit_open_orders(&mut self, open_order_ids: HashSet<ClientOrderId>) {
382 self.audit_open_orders(&open_order_ids.into_iter().collect());
383 }
384
385 #[pyo3(name = "pprint")]
387 #[pyo3(signature = (num_levels=3, group_size=None))]
388 fn py_pprint(&self, num_levels: usize, group_size: Option<Decimal>) -> String {
389 self.pprint(num_levels, group_size)
390 }
391}