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::{
31 OwnBookOrder,
32 own::{OwnOrderBook, validate_accepted_buffer},
33 },
34 types::{Price, Quantity},
35};
36
37#[pymethods]
38#[pyo3_stub_gen::derive::gen_stub_pymethods]
39impl OwnBookOrder {
40 #[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))]
45 #[new]
46 #[expect(clippy::too_many_arguments)]
47 fn py_new(
48 trader_id: TraderId,
49 client_order_id: ClientOrderId,
50 side: OrderSide,
51 price: Price,
52 size: Quantity,
53 order_type: OrderType,
54 time_in_force: TimeInForce,
55 status: OrderStatus,
56 ts_last: u64,
57 ts_accepted: u64,
58 ts_submitted: u64,
59 ts_init: u64,
60 venue_order_id: Option<VenueOrderId>,
61 ) -> Self {
62 Self::new(
63 trader_id,
64 client_order_id,
65 venue_order_id,
66 side,
67 price,
68 size,
69 order_type,
70 time_in_force,
71 status,
72 ts_last.into(),
73 ts_accepted.into(),
74 ts_submitted.into(),
75 ts_init.into(),
76 )
77 }
78
79 fn __richcmp__(&self, other: &Self, op: CompareOp, py: Python<'_>) -> Py<PyAny> {
80 match op {
81 CompareOp::Eq => self.eq(other).into_py_any_unwrap(py),
82 CompareOp::Ne => self.ne(other).into_py_any_unwrap(py),
83 _ => py.NotImplemented(),
84 }
85 }
86
87 fn __hash__(&self) -> isize {
88 let mut hasher = DefaultHasher::new();
89 self.hash(&mut hasher);
90 hasher.finish() as isize
91 }
92
93 fn __repr__(&self) -> String {
94 format!("{self:?}")
95 }
96
97 fn __str__(&self) -> String {
98 self.to_string()
99 }
100
101 #[getter]
102 #[pyo3(name = "client_order_id")]
103 fn py_client_order_id(&self) -> ClientOrderId {
104 self.client_order_id
105 }
106
107 #[getter]
108 #[pyo3(name = "side")]
109 fn py_side(&self) -> OrderSide {
110 self.side
111 }
112
113 #[getter]
114 #[pyo3(name = "price")]
115 fn py_price(&self) -> Price {
116 self.price
117 }
118
119 #[getter]
120 #[pyo3(name = "size")]
121 fn py_size(&self) -> Quantity {
122 self.size
123 }
124
125 #[getter]
126 #[pyo3(name = "order_type")]
127 fn py_order_type(&self) -> OrderType {
128 self.order_type
129 }
130
131 #[getter]
132 #[pyo3(name = "time_in_force")]
133 fn py_time_in_force(&self) -> TimeInForce {
134 self.time_in_force
135 }
136
137 #[getter]
138 #[pyo3(name = "status")]
139 fn py_status(&self) -> OrderStatus {
140 self.status
141 }
142
143 #[getter]
144 #[pyo3(name = "ts_last")]
145 fn py_ts_last(&self) -> u64 {
146 self.ts_last.into()
147 }
148
149 #[getter]
150 #[pyo3(name = "ts_init")]
151 fn py_ts_init(&self) -> u64 {
152 self.ts_init.into()
153 }
154
155 #[pyo3(name = "exposure")]
157 fn py_exposure(&self) -> f64 {
158 self.exposure()
159 }
160
161 #[pyo3(name = "signed_size")]
163 fn py_signed_size(&self) -> f64 {
164 self.signed_size()
165 }
166}
167
168#[pymethods]
169#[pyo3_stub_gen::derive::gen_stub_pymethods]
170impl OwnOrderBook {
171 #[new]
173 fn py_new(instrument_id: InstrumentId) -> Self {
174 Self::new(instrument_id)
175 }
176
177 fn __repr__(&self) -> String {
178 format!("{self:?}")
179 }
180
181 fn __str__(&self) -> String {
182 self.to_string()
183 }
184
185 #[getter]
186 #[pyo3(name = "instrument_id")]
187 fn py_instrument_id(&self) -> InstrumentId {
188 self.instrument_id
189 }
190
191 #[getter]
192 #[pyo3(name = "ts_last")]
193 fn py_ts_last(&self) -> u64 {
194 self.ts_last.as_u64()
195 }
196
197 #[getter]
198 #[pyo3(name = "update_count")]
199 fn py_update_count(&self) -> u64 {
200 self.update_count
201 }
202
203 #[pyo3(name = "reset")]
205 fn py_reset(&mut self) {
206 self.reset();
207 }
208
209 #[pyo3(name = "add")]
211 fn py_add(&mut self, order: OwnBookOrder) {
212 self.add(order);
213 }
214
215 #[pyo3(name = "update")]
221 fn py_update(&mut self, order: OwnBookOrder) -> PyResult<()> {
222 self.update(order).map_err(to_pyruntime_err)
223 }
224
225 #[pyo3(name = "delete")]
231 fn py_delete(&mut self, order: OwnBookOrder) -> PyResult<()> {
232 self.delete(order).map_err(to_pyruntime_err)
233 }
234
235 #[pyo3(name = "clear")]
237 fn py_clear(&mut self) {
238 self.clear();
239 }
240
241 #[pyo3(name = "bid_client_order_ids")]
243 #[must_use]
244 pub fn py_bid_client_order_ids(&self) -> Vec<ClientOrderId> {
245 self.bid_client_order_ids()
246 }
247
248 #[pyo3(name = "ask_client_order_ids")]
250 #[must_use]
251 pub fn py_ask_client_order_ids(&self) -> Vec<ClientOrderId> {
252 self.ask_client_order_ids()
253 }
254
255 #[pyo3(name = "is_order_in_book")]
257 #[must_use]
258 pub fn py_is_order_in_book(&self, client_order_id: &ClientOrderId) -> bool {
259 self.is_order_in_book(client_order_id)
260 }
261
262 #[pyo3(name = "orders_to_list")]
263 fn py_orders_to_list(&self) -> Vec<OwnBookOrder> {
264 let total_orders = self.bids.cache.len() + self.asks.cache.len();
265 let mut all_orders = Vec::with_capacity(total_orders);
266
267 all_orders.extend(
268 self.bids()
269 .flat_map(|level| level.orders.values().copied())
270 .chain(self.asks().flat_map(|level| level.orders.values().copied())),
271 );
272
273 all_orders
274 }
275
276 #[pyo3(name = "bids_to_list")]
277 fn py_bids_to_list(&self) -> Vec<OwnBookOrder> {
278 self.bids()
279 .flat_map(|level| level.orders.values().copied())
280 .collect()
281 }
282
283 #[pyo3(name = "asks_to_list")]
284 fn py_asks_to_list(&self) -> Vec<OwnBookOrder> {
285 self.asks()
286 .flat_map(|level| level.orders.values().copied())
287 .collect()
288 }
289
290 #[pyo3(name = "bids_to_dict")]
291 #[pyo3(signature = (status=None, accepted_buffer_ns=None, ts_now=None))]
292 fn py_bids_to_dict(
293 &self,
294 status: Option<HashSet<OrderStatus>>,
295 accepted_buffer_ns: Option<u64>,
296 ts_now: Option<u64>,
297 ) -> PyResult<IndexMap<Decimal, Vec<OwnBookOrder>>> {
298 validate_accepted_buffer(accepted_buffer_ns, ts_now).map_err(to_pyvalue_err)?;
299 let status_set: Option<AHashSet<OrderStatus>> = status.map(|s| s.into_iter().collect());
300 Ok(self.bids_as_map(status_set.as_ref(), accepted_buffer_ns, ts_now))
301 }
302
303 #[pyo3(name = "asks_to_dict")]
304 #[pyo3(signature = (status=None, accepted_buffer_ns=None, ts_now=None))]
305 fn py_asks_to_dict(
306 &self,
307 status: Option<HashSet<OrderStatus>>,
308 accepted_buffer_ns: Option<u64>,
309 ts_now: Option<u64>,
310 ) -> PyResult<IndexMap<Decimal, Vec<OwnBookOrder>>> {
311 validate_accepted_buffer(accepted_buffer_ns, ts_now).map_err(to_pyvalue_err)?;
312 let status_set: Option<AHashSet<OrderStatus>> = status.map(|s| s.into_iter().collect());
313 Ok(self.asks_as_map(status_set.as_ref(), accepted_buffer_ns, ts_now))
314 }
315
316 #[pyo3(name = "bid_quantity")]
325 #[pyo3(signature = (status=None, depth=None, group_size=None, accepted_buffer_ns=None, ts_now=None))]
326 fn py_bid_quantity(
327 &self,
328 status: Option<HashSet<OrderStatus>>,
329 depth: Option<usize>,
330 group_size: Option<Decimal>,
331 accepted_buffer_ns: Option<u64>,
332 ts_now: Option<u64>,
333 ) -> PyResult<IndexMap<Decimal, Decimal>> {
334 validate_accepted_buffer(accepted_buffer_ns, ts_now).map_err(to_pyvalue_err)?;
335 let status_set: Option<AHashSet<OrderStatus>> = status.map(|s| s.into_iter().collect());
336 Ok(self.bid_quantity(
337 status_set.as_ref(),
338 depth,
339 group_size,
340 accepted_buffer_ns,
341 ts_now,
342 ))
343 }
344
345 #[pyo3(name = "ask_quantity")]
354 #[pyo3(signature = (status=None, depth=None, group_size=None, accepted_buffer_ns=None, ts_now=None))]
355 fn py_ask_quantity(
356 &self,
357 status: Option<HashSet<OrderStatus>>,
358 depth: Option<usize>,
359 group_size: Option<Decimal>,
360 accepted_buffer_ns: Option<u64>,
361 ts_now: Option<u64>,
362 ) -> PyResult<IndexMap<Decimal, Decimal>> {
363 validate_accepted_buffer(accepted_buffer_ns, ts_now).map_err(to_pyvalue_err)?;
364 let status_set: Option<AHashSet<OrderStatus>> = status.map(|s| s.into_iter().collect());
365 Ok(self.ask_quantity(
366 status_set.as_ref(),
367 depth,
368 group_size,
369 accepted_buffer_ns,
370 ts_now,
371 ))
372 }
373
374 #[pyo3(name = "combined_with_opposite")]
384 fn py_combined_with_opposite(&self, opposite: &Self) -> PyResult<Self> {
385 self.combined_with_opposite(opposite)
386 .map_err(to_pyvalue_err)
387 }
388
389 #[pyo3(name = "audit_open_orders")]
390 fn py_audit_open_orders(&mut self, open_order_ids: HashSet<ClientOrderId>) {
391 self.audit_open_orders(&open_order_ids.into_iter().collect());
392 }
393
394 #[pyo3(name = "pprint")]
396 #[pyo3(signature = (num_levels=3, group_size=None))]
397 fn py_pprint(&self, num_levels: usize, group_size: Option<Decimal>) -> String {
398 self.pprint(num_levels, group_size)
399 }
400}