nautilus_model/python/orderbook/
own.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2025 Nautech Systems Pty Ltd. All rights reserved.
3//  https://nautechsystems.io
4//
5//  Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
6//  You may not use this file except in compliance with the License.
7//  You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
8//
9//  Unless required by applicable law or agreed to in writing, software
10//  distributed under the License is distributed on an "AS IS" BASIS,
11//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//  See the License for the specific language governing permissions and
13//  limitations under the License.
14// -------------------------------------------------------------------------------------------------
15
16use std::{
17    collections::{HashSet, hash_map::DefaultHasher},
18    hash::{Hash, Hasher},
19};
20
21use indexmap::IndexMap;
22use nautilus_core::python::{IntoPyObjectNautilusExt, to_pyruntime_err};
23use pyo3::{Python, prelude::*, pyclass::CompareOp};
24use rust_decimal::Decimal;
25
26use crate::{
27    enums::{OrderSide, OrderStatus, OrderType, TimeInForce},
28    identifiers::{ClientOrderId, InstrumentId, TraderId, VenueOrderId},
29    orderbook::{OwnBookOrder, own::OwnOrderBook},
30    types::{Price, Quantity},
31};
32
33#[pymethods]
34impl OwnBookOrder {
35    #[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))]
36    #[new]
37    #[allow(clippy::too_many_arguments)]
38    fn py_new(
39        trader_id: TraderId,
40        client_order_id: ClientOrderId,
41        side: OrderSide,
42        price: Price,
43        size: Quantity,
44        order_type: OrderType,
45        time_in_force: TimeInForce,
46        status: OrderStatus,
47        ts_last: u64,
48        ts_accepted: u64,
49        ts_submitted: u64,
50        ts_init: u64,
51        venue_order_id: Option<VenueOrderId>,
52    ) -> PyResult<Self> {
53        Ok(OwnBookOrder::new(
54            trader_id,
55            client_order_id,
56            venue_order_id,
57            side.as_specified(),
58            price,
59            size,
60            order_type,
61            time_in_force,
62            status,
63            ts_last.into(),
64            ts_accepted.into(),
65            ts_submitted.into(),
66            ts_init.into(),
67        ))
68    }
69
70    fn __richcmp__(&self, other: &Self, op: CompareOp, py: Python<'_>) -> Py<PyAny> {
71        match op {
72            CompareOp::Eq => self.eq(other).into_py_any_unwrap(py),
73            CompareOp::Ne => self.ne(other).into_py_any_unwrap(py),
74            _ => py.NotImplemented(),
75        }
76    }
77
78    fn __hash__(&self) -> isize {
79        let mut hasher = DefaultHasher::new();
80        self.hash(&mut hasher);
81        hasher.finish() as isize
82    }
83
84    fn __repr__(&self) -> String {
85        format!("{self:?}")
86    }
87
88    fn __str__(&self) -> String {
89        self.to_string()
90    }
91
92    #[getter]
93    #[pyo3(name = "client_order_id")]
94    fn py_client_order_id(&self) -> ClientOrderId {
95        self.client_order_id
96    }
97
98    #[getter]
99    #[pyo3(name = "side")]
100    fn py_side(&self) -> OrderSide {
101        self.side.as_order_side()
102    }
103
104    #[getter]
105    #[pyo3(name = "price")]
106    fn py_price(&self) -> Price {
107        self.price
108    }
109
110    #[getter]
111    #[pyo3(name = "size")]
112    fn py_size(&self) -> Quantity {
113        self.size
114    }
115
116    #[getter]
117    #[pyo3(name = "order_type")]
118    fn py_order_type(&self) -> OrderType {
119        self.order_type
120    }
121
122    #[getter]
123    #[pyo3(name = "time_in_force")]
124    fn py_time_in_force(&self) -> TimeInForce {
125        self.time_in_force
126    }
127
128    #[getter]
129    #[pyo3(name = "status")]
130    fn py_status(&self) -> OrderStatus {
131        self.status
132    }
133
134    #[getter]
135    #[pyo3(name = "ts_last")]
136    fn py_ts_last(&self) -> u64 {
137        self.ts_last.into()
138    }
139
140    #[getter]
141    #[pyo3(name = "ts_init")]
142    fn py_ts_init(&self) -> u64 {
143        self.ts_init.into()
144    }
145
146    #[pyo3(name = "exposure")]
147    fn py_exposure(&self) -> f64 {
148        self.exposure()
149    }
150
151    #[pyo3(name = "signed_size")]
152    fn py_signed_size(&self) -> f64 {
153        self.signed_size()
154    }
155}
156
157#[pymethods]
158impl OwnOrderBook {
159    #[new]
160    fn py_new(instrument_id: InstrumentId) -> Self {
161        Self::new(instrument_id)
162    }
163
164    fn __repr__(&self) -> String {
165        format!("{self:?}")
166    }
167
168    fn __str__(&self) -> String {
169        self.to_string()
170    }
171
172    #[getter]
173    #[pyo3(name = "instrument_id")]
174    fn py_instrument_id(&self) -> InstrumentId {
175        self.instrument_id
176    }
177
178    #[getter]
179    #[pyo3(name = "ts_last")]
180    fn py_ts_last(&self) -> u64 {
181        self.ts_last.as_u64()
182    }
183
184    #[getter]
185    #[pyo3(name = "update_count")]
186    fn py_update_count(&self) -> u64 {
187        self.update_count
188    }
189
190    #[pyo3(name = "reset")]
191    fn py_reset(&mut self) {
192        self.reset();
193    }
194
195    #[pyo3(name = "add")]
196    fn py_add(&mut self, order: OwnBookOrder) {
197        self.add(order);
198    }
199
200    #[pyo3(name = "update")]
201    fn py_update(&mut self, order: OwnBookOrder) -> PyResult<()> {
202        self.update(order).map_err(to_pyruntime_err)
203    }
204
205    #[pyo3(name = "delete")]
206    fn py_delete(&mut self, order: OwnBookOrder) -> PyResult<()> {
207        self.delete(order).map_err(to_pyruntime_err)
208    }
209
210    #[pyo3(name = "clear")]
211    fn py_clear(&mut self) {
212        self.clear();
213    }
214
215    #[pyo3(name = "bid_client_order_ids")]
216    pub fn py_bid_client_order_ids(&self) -> Vec<ClientOrderId> {
217        self.bid_client_order_ids()
218    }
219
220    #[pyo3(name = "ask_client_order_ids")]
221    pub fn py_ask_client_order_ids(&self) -> Vec<ClientOrderId> {
222        self.ask_client_order_ids()
223    }
224
225    #[pyo3(name = "is_order_in_book")]
226    pub fn py_is_order_in_book(&self, client_order_id: &ClientOrderId) -> bool {
227        self.is_order_in_book(client_order_id)
228    }
229
230    #[pyo3(name = "orders_to_list")]
231    fn py_orders_to_list(&self) -> Vec<OwnBookOrder> {
232        let total_orders = self.bids.cache.len() + self.asks.cache.len();
233        let mut all_orders = Vec::with_capacity(total_orders);
234
235        all_orders.extend(
236            self.bids()
237                .flat_map(|level| level.orders.values().cloned())
238                .chain(self.asks().flat_map(|level| level.orders.values().cloned())),
239        );
240
241        all_orders
242    }
243
244    #[pyo3(name = "bids_to_list")]
245    fn py_bids_to_list(&self) -> Vec<OwnBookOrder> {
246        self.bids()
247            .flat_map(|level| level.orders.values().cloned())
248            .collect()
249    }
250
251    #[pyo3(name = "asks_to_list")]
252    fn py_asks_to_list(&self) -> Vec<OwnBookOrder> {
253        self.asks()
254            .flat_map(|level| level.orders.values().cloned())
255            .collect()
256    }
257
258    #[pyo3(name = "bids_to_dict")]
259    #[pyo3(signature = (status=None, accepted_buffer_ns=None, ts_now=None))]
260    fn py_bids_to_dict(
261        &self,
262        status: Option<HashSet<OrderStatus>>,
263        accepted_buffer_ns: Option<u64>,
264        ts_now: Option<u64>,
265    ) -> IndexMap<Decimal, Vec<OwnBookOrder>> {
266        self.bids_as_map(status, accepted_buffer_ns, ts_now)
267    }
268
269    #[pyo3(name = "asks_to_dict")]
270    #[pyo3(signature = (status=None, accepted_buffer_ns=None, ts_now=None))]
271    fn py_asks_to_dict(
272        &self,
273        status: Option<HashSet<OrderStatus>>,
274        accepted_buffer_ns: Option<u64>,
275        ts_now: Option<u64>,
276    ) -> IndexMap<Decimal, Vec<OwnBookOrder>> {
277        self.asks_as_map(status, accepted_buffer_ns, ts_now)
278    }
279
280    #[pyo3(name = "bid_quantity")]
281    #[pyo3(signature = (status=None, accepted_buffer_ns=None, ts_now=None))]
282    fn py_bid_quantity(
283        &self,
284        status: Option<HashSet<OrderStatus>>,
285        accepted_buffer_ns: Option<u64>,
286        ts_now: Option<u64>,
287    ) -> IndexMap<Decimal, Decimal> {
288        self.bid_quantity(status, accepted_buffer_ns, ts_now)
289    }
290
291    #[pyo3(name = "ask_quantity")]
292    #[pyo3(signature = (status=None, accepted_buffer_ns=None, ts_now=None))]
293    fn py_ask_quantity(
294        &self,
295        status: Option<HashSet<OrderStatus>>,
296        accepted_buffer_ns: Option<u64>,
297        ts_now: Option<u64>,
298    ) -> IndexMap<Decimal, Decimal> {
299        self.ask_quantity(status, accepted_buffer_ns, ts_now)
300    }
301
302    #[pyo3(name = "group_bids")]
303    #[pyo3(signature = (group_size, depth=None, status=None, accepted_buffer_ns=None, ts_now=None))]
304    fn py_group_bids(
305        &self,
306        group_size: Decimal,
307        depth: Option<usize>,
308        status: Option<HashSet<OrderStatus>>,
309        accepted_buffer_ns: Option<u64>,
310        ts_now: Option<u64>,
311    ) -> IndexMap<Decimal, Decimal> {
312        self.group_bids(group_size, depth, status, accepted_buffer_ns, ts_now)
313    }
314
315    #[pyo3(name = "group_asks")]
316    #[pyo3(signature = (group_size, depth=None, status=None, accepted_buffer_ns=None, ts_now=None))]
317    fn py_group_asks(
318        &self,
319        group_size: Decimal,
320        depth: Option<usize>,
321        status: Option<HashSet<OrderStatus>>,
322        accepted_buffer_ns: Option<u64>,
323        ts_now: Option<u64>,
324    ) -> IndexMap<Decimal, Decimal> {
325        self.group_asks(group_size, depth, status, accepted_buffer_ns, ts_now)
326    }
327
328    #[pyo3(name = "audit_open_orders")]
329    fn py_audit_open_orders(&mut self, open_order_ids: HashSet<ClientOrderId>) {
330        self.audit_open_orders(&open_order_ids)
331    }
332
333    #[pyo3(name = "pprint")]
334    #[pyo3(signature = (num_levels=3))]
335    fn py_pprint(&self, num_levels: usize) -> String {
336        self.pprint(num_levels)
337    }
338}