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