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