1use std::collections::HashSet;
17
18use indexmap::IndexMap;
19use nautilus_core::python::{to_pyruntime_err, to_pyvalue_err};
20use pyo3::prelude::*;
21use rust_decimal::Decimal;
22
23use crate::{
24 data::{BookOrder, OrderBookDelta, OrderBookDeltas, OrderBookDepth10, QuoteTick, TradeTick},
25 enums::{BookType, OrderSide, OrderStatus},
26 identifiers::InstrumentId,
27 orderbook::{BookLevel, OrderBook, analysis::book_check_integrity, own::OwnOrderBook},
28 types::{Price, Quantity},
29};
30
31#[pymethods]
32impl OrderBook {
33 #[new]
34 fn py_new(instrument_id: InstrumentId, book_type: BookType) -> Self {
35 Self::new(instrument_id, book_type)
36 }
37
38 fn __repr__(&self) -> String {
39 format!("{self:?}")
40 }
41
42 fn __str__(&self) -> String {
43 format!("{self:?}")
45 }
46
47 #[getter]
48 #[pyo3(name = "instrument_id")]
49 fn py_instrument_id(&self) -> InstrumentId {
50 self.instrument_id
51 }
52
53 #[getter]
54 #[pyo3(name = "book_type")]
55 fn py_book_type(&self) -> BookType {
56 self.book_type
57 }
58
59 #[getter]
60 #[pyo3(name = "sequence")]
61 fn py_sequence(&self) -> u64 {
62 self.sequence
63 }
64
65 #[getter]
66 #[pyo3(name = "ts_event")]
67 fn py_ts_event(&self) -> u64 {
68 self.ts_last.as_u64()
69 }
70
71 #[getter]
72 #[pyo3(name = "ts_init")]
73 fn py_ts_init(&self) -> u64 {
74 self.ts_last.as_u64()
75 }
76
77 #[getter]
78 #[pyo3(name = "ts_last")]
79 fn py_ts_last(&self) -> u64 {
80 self.ts_last.as_u64()
81 }
82
83 #[getter]
84 #[pyo3(name = "update_count")]
85 fn py_update_count(&self) -> u64 {
86 self.update_count
87 }
88
89 #[pyo3(name = "reset")]
90 fn py_reset(&mut self) {
91 self.reset();
92 }
93
94 #[pyo3(name = "add")]
95 #[pyo3(signature = (order, flags, sequence, ts_event))]
96 fn py_add(&mut self, order: BookOrder, flags: u8, sequence: u64, ts_event: u64) {
97 self.add(order, flags, sequence, ts_event.into());
98 }
99
100 #[pyo3(name = "update")]
101 #[pyo3(signature = (order, flags, sequence, ts_event))]
102 fn py_update(&mut self, order: BookOrder, flags: u8, sequence: u64, ts_event: u64) {
103 self.update(order, flags, sequence, ts_event.into());
104 }
105
106 #[pyo3(name = "delete")]
107 #[pyo3(signature = (order, flags, sequence, ts_event))]
108 fn py_delete(&mut self, order: BookOrder, flags: u8, sequence: u64, ts_event: u64) {
109 self.delete(order, flags, sequence, ts_event.into());
110 }
111
112 #[pyo3(name = "clear")]
113 #[pyo3(signature = (sequence, ts_event))]
114 fn py_clear(&mut self, sequence: u64, ts_event: u64) {
115 self.clear(sequence, ts_event.into());
116 }
117
118 #[pyo3(name = "clear_bids")]
119 #[pyo3(signature = (sequence, ts_event))]
120 fn py_clear_bids(&mut self, sequence: u64, ts_event: u64) {
121 self.clear_bids(sequence, ts_event.into());
122 }
123
124 #[pyo3(name = "clear_asks")]
125 #[pyo3(signature = (sequence, ts_event))]
126 fn py_clear_asks(&mut self, sequence: u64, ts_event: u64) {
127 self.clear_asks(sequence, ts_event.into());
128 }
129
130 #[pyo3(name = "clear_stale_levels")]
131 #[pyo3(signature = (side=None))]
132 fn py_clear_stale_levels(&mut self, side: Option<OrderSide>) -> Option<Vec<BookLevel>> {
133 self.clear_stale_levels(side)
134 }
135
136 #[pyo3(name = "apply_delta")]
137 fn py_apply_delta(&mut self, delta: &OrderBookDelta) {
138 self.apply_delta(delta);
139 }
140
141 #[pyo3(name = "apply_deltas")]
142 fn py_apply_deltas(&mut self, deltas: &OrderBookDeltas) {
143 self.apply_deltas(deltas);
144 }
145
146 #[pyo3(name = "apply_depth")]
147 fn py_apply_depth(&mut self, depth: &OrderBookDepth10) {
148 self.apply_depth(depth);
149 }
150
151 #[pyo3(name = "check_integrity")]
152 fn py_check_integrity(&mut self) -> PyResult<()> {
153 book_check_integrity(self).map_err(to_pyruntime_err)
154 }
155
156 #[pyo3(name = "bids")]
157 #[pyo3(signature = (depth=None))]
158 fn py_bids(&self, depth: Option<usize>) -> Vec<BookLevel> {
159 self.bids(depth)
160 .map(|level_ref| (*level_ref).clone())
161 .collect()
162 }
163
164 #[pyo3(name = "asks")]
165 #[pyo3(signature = (depth=None))]
166 fn py_asks(&self, depth: Option<usize>) -> Vec<BookLevel> {
167 self.asks(depth)
168 .map(|level_ref| (*level_ref).clone())
169 .collect()
170 }
171
172 #[pyo3(name = "bids_to_dict")]
173 #[pyo3(signature = (depth=None))]
174 fn py_bids_to_dict(&self, depth: Option<usize>) -> IndexMap<Decimal, Decimal> {
175 self.bids_as_map(depth)
176 }
177
178 #[pyo3(name = "asks_to_dict")]
179 #[pyo3(signature = (depth=None))]
180 fn py_asks_to_dict(&self, depth: Option<usize>) -> IndexMap<Decimal, Decimal> {
181 self.asks_as_map(depth)
182 }
183
184 #[pyo3(name = "group_bids")]
185 #[pyo3(signature = (group_size, depth=None))]
186 pub fn py_group_bids(
187 &self,
188 group_size: Decimal,
189 depth: Option<usize>,
190 ) -> IndexMap<Decimal, Decimal> {
191 self.group_bids(group_size, depth)
192 }
193
194 #[pyo3(name = "group_asks")]
195 #[pyo3(signature = (group_size, depth=None))]
196 pub fn py_group_asks(
197 &self,
198 group_size: Decimal,
199 depth: Option<usize>,
200 ) -> IndexMap<Decimal, Decimal> {
201 self.group_asks(group_size, depth)
202 }
203
204 #[pyo3(name = "bids_filtered_to_dict")]
205 #[pyo3(signature = (depth=None, own_book=None, status=None, accepted_buffer_ns=None, ts_now=None))]
206 fn py_bids_filtered_to_dict(
207 &self,
208 depth: Option<usize>,
209 own_book: Option<&OwnOrderBook>,
210 status: Option<HashSet<OrderStatus>>,
211 accepted_buffer_ns: Option<u64>,
212 ts_now: Option<u64>,
213 ) -> IndexMap<Decimal, Decimal> {
214 self.bids_filtered_as_map(depth, own_book, status, accepted_buffer_ns, ts_now)
215 }
216
217 #[pyo3(name = "asks_filtered_to_dict")]
218 #[pyo3(signature = (depth=None, own_book=None, status=None, accepted_buffer_ns=None, ts_now=None))]
219 fn py_asks_filtered_to_dict(
220 &self,
221 depth: Option<usize>,
222 own_book: Option<&OwnOrderBook>,
223 status: Option<HashSet<OrderStatus>>,
224 accepted_buffer_ns: Option<u64>,
225 ts_now: Option<u64>,
226 ) -> IndexMap<Decimal, Decimal> {
227 self.asks_filtered_as_map(depth, own_book, status, accepted_buffer_ns, ts_now)
228 }
229
230 #[pyo3(name = "group_bids_filtered")]
231 #[pyo3(signature = (group_size, depth=None, own_book=None, status=None, accepted_buffer_ns=None, ts_now=None))]
232 fn py_group_bids_filered(
233 &self,
234 group_size: Decimal,
235 depth: Option<usize>,
236 own_book: Option<&OwnOrderBook>,
237 status: Option<HashSet<OrderStatus>>,
238 accepted_buffer_ns: Option<u64>,
239 ts_now: Option<u64>,
240 ) -> IndexMap<Decimal, Decimal> {
241 self.group_bids_filtered(
242 group_size,
243 depth,
244 own_book,
245 status,
246 accepted_buffer_ns,
247 ts_now,
248 )
249 }
250
251 #[pyo3(name = "group_asks_filtered")]
252 #[pyo3(signature = (group_size, depth=None, own_book=None, status=None, accepted_buffer_ns=None, ts_now=None))]
253 fn py_group_asks_filtered(
254 &self,
255 group_size: Decimal,
256 depth: Option<usize>,
257 own_book: Option<&OwnOrderBook>,
258 status: Option<HashSet<OrderStatus>>,
259 accepted_buffer_ns: Option<u64>,
260 ts_now: Option<u64>,
261 ) -> IndexMap<Decimal, Decimal> {
262 self.group_asks_filtered(
263 group_size,
264 depth,
265 own_book,
266 status,
267 accepted_buffer_ns,
268 ts_now,
269 )
270 }
271
272 #[pyo3(name = "best_bid_price")]
273 fn py_best_bid_price(&self) -> Option<Price> {
274 self.best_bid_price()
275 }
276
277 #[pyo3(name = "best_ask_price")]
278 fn py_best_ask_price(&self) -> Option<Price> {
279 self.best_ask_price()
280 }
281
282 #[pyo3(name = "best_bid_size")]
283 fn py_best_bid_size(&self) -> Option<Quantity> {
284 self.best_bid_size()
285 }
286
287 #[pyo3(name = "best_ask_size")]
288 fn py_best_ask_size(&self) -> Option<Quantity> {
289 self.best_ask_size()
290 }
291
292 #[pyo3(name = "spread")]
293 fn py_spread(&self) -> Option<f64> {
294 self.spread()
295 }
296
297 #[pyo3(name = "midpoint")]
298 fn py_midpoint(&self) -> Option<f64> {
299 self.midpoint()
300 }
301
302 #[pyo3(name = "get_avg_px_for_quantity")]
303 fn py_get_avg_px_for_quantity(&self, qty: Quantity, order_side: OrderSide) -> f64 {
304 self.get_avg_px_for_quantity(qty, order_side)
305 }
306
307 #[pyo3(name = "get_avg_px_qty_for_exposure")]
308 fn py_get_avg_px_qty_for_exposure(
309 &self,
310 qty: Quantity,
311 order_side: OrderSide,
312 ) -> (f64, f64, f64) {
313 self.get_avg_px_qty_for_exposure(qty, order_side)
314 }
315
316 #[pyo3(name = "get_quantity_for_price")]
317 fn py_get_quantity_for_price(&self, price: Price, order_side: OrderSide) -> f64 {
318 self.get_quantity_for_price(price, order_side)
319 }
320
321 #[pyo3(name = "simulate_fills")]
322 fn py_simulate_fills(&self, order: &BookOrder) -> Vec<(Price, Quantity)> {
323 self.simulate_fills(order)
324 }
325
326 #[pyo3(name = "pprint")]
327 #[pyo3(signature = (num_levels=3, group_size=None))]
328 fn py_pprint(&self, num_levels: usize, group_size: Option<Decimal>) -> String {
329 self.pprint(num_levels, group_size)
330 }
331}
332
333#[pyfunction()]
339#[pyo3(name = "update_book_with_quote_tick")]
340pub fn py_update_book_with_quote_tick(book: &mut OrderBook, quote: &QuoteTick) -> PyResult<()> {
341 book.update_quote_tick(quote).map_err(to_pyvalue_err)
342}
343
344#[pyfunction()]
350#[pyo3(name = "update_book_with_trade_tick")]
351pub fn py_update_book_with_trade_tick(book: &mut OrderBook, trade: &TradeTick) -> PyResult<()> {
352 book.update_trade_tick(trade).map_err(to_pyvalue_err)
353}