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 = "apply_delta")]
131 fn py_apply_delta(&mut self, delta: &OrderBookDelta) {
132 self.apply_delta(delta);
133 }
134
135 #[pyo3(name = "apply_deltas")]
136 fn py_apply_deltas(&mut self, deltas: &OrderBookDeltas) {
137 self.apply_deltas(deltas);
138 }
139
140 #[pyo3(name = "apply_depth")]
141 fn py_apply_depth(&mut self, depth: &OrderBookDepth10) {
142 self.apply_depth(depth);
143 }
144
145 #[pyo3(name = "check_integrity")]
146 fn py_check_integrity(&mut self) -> PyResult<()> {
147 book_check_integrity(self).map_err(to_pyruntime_err)
148 }
149
150 #[pyo3(name = "bids")]
151 #[pyo3(signature = (depth=None))]
152 fn py_bids(&self, depth: Option<usize>) -> Vec<BookLevel> {
153 self.bids(depth)
154 .map(|level_ref| (*level_ref).clone())
155 .collect()
156 }
157
158 #[pyo3(name = "asks")]
159 #[pyo3(signature = (depth=None))]
160 fn py_asks(&self, depth: Option<usize>) -> Vec<BookLevel> {
161 self.asks(depth)
162 .map(|level_ref| (*level_ref).clone())
163 .collect()
164 }
165
166 #[pyo3(name = "bids_to_dict")]
167 #[pyo3(signature = (depth=None))]
168 fn py_bids_to_dict(&self, depth: Option<usize>) -> IndexMap<Decimal, Decimal> {
169 self.bids_as_map(depth)
170 }
171
172 #[pyo3(name = "asks_to_dict")]
173 #[pyo3(signature = (depth=None))]
174 fn py_asks_to_dict(&self, depth: Option<usize>) -> IndexMap<Decimal, Decimal> {
175 self.asks_as_map(depth)
176 }
177
178 #[pyo3(name = "group_bids")]
179 #[pyo3(signature = (group_size, depth=None))]
180 pub fn py_group_bids(
181 &self,
182 group_size: Decimal,
183 depth: Option<usize>,
184 ) -> IndexMap<Decimal, Decimal> {
185 self.group_bids(group_size, depth)
186 }
187
188 #[pyo3(name = "group_asks")]
189 #[pyo3(signature = (group_size, depth=None))]
190 pub fn py_group_asks(
191 &self,
192 group_size: Decimal,
193 depth: Option<usize>,
194 ) -> IndexMap<Decimal, Decimal> {
195 self.group_asks(group_size, depth)
196 }
197
198 #[pyo3(name = "bids_filtered_to_dict")]
199 #[pyo3(signature = (depth=None, own_book=None, status=None, accepted_buffer_ns=None, ts_now=None))]
200 fn py_bids_filtered_to_dict(
201 &self,
202 depth: Option<usize>,
203 own_book: Option<&OwnOrderBook>,
204 status: Option<HashSet<OrderStatus>>,
205 accepted_buffer_ns: Option<u64>,
206 ts_now: Option<u64>,
207 ) -> IndexMap<Decimal, Decimal> {
208 self.bids_filtered_as_map(depth, own_book, status, accepted_buffer_ns, ts_now)
209 }
210
211 #[pyo3(name = "asks_filtered_to_dict")]
212 #[pyo3(signature = (depth=None, own_book=None, status=None, accepted_buffer_ns=None, ts_now=None))]
213 fn py_asks_filtered_to_dict(
214 &self,
215 depth: Option<usize>,
216 own_book: Option<&OwnOrderBook>,
217 status: Option<HashSet<OrderStatus>>,
218 accepted_buffer_ns: Option<u64>,
219 ts_now: Option<u64>,
220 ) -> IndexMap<Decimal, Decimal> {
221 self.asks_filtered_as_map(depth, own_book, status, accepted_buffer_ns, ts_now)
222 }
223
224 #[pyo3(name = "group_bids_filtered")]
225 #[pyo3(signature = (group_size, depth=None, own_book=None, status=None, accepted_buffer_ns=None, ts_now=None))]
226 fn py_group_bids_filered(
227 &self,
228 group_size: Decimal,
229 depth: Option<usize>,
230 own_book: Option<&OwnOrderBook>,
231 status: Option<HashSet<OrderStatus>>,
232 accepted_buffer_ns: Option<u64>,
233 ts_now: Option<u64>,
234 ) -> IndexMap<Decimal, Decimal> {
235 self.group_bids_filtered(
236 group_size,
237 depth,
238 own_book,
239 status,
240 accepted_buffer_ns,
241 ts_now,
242 )
243 }
244
245 #[pyo3(name = "group_asks_filtered")]
246 #[pyo3(signature = (group_size, depth=None, own_book=None, status=None, accepted_buffer_ns=None, ts_now=None))]
247 fn py_group_asks_filtered(
248 &self,
249 group_size: Decimal,
250 depth: Option<usize>,
251 own_book: Option<&OwnOrderBook>,
252 status: Option<HashSet<OrderStatus>>,
253 accepted_buffer_ns: Option<u64>,
254 ts_now: Option<u64>,
255 ) -> IndexMap<Decimal, Decimal> {
256 self.group_asks_filtered(
257 group_size,
258 depth,
259 own_book,
260 status,
261 accepted_buffer_ns,
262 ts_now,
263 )
264 }
265
266 #[pyo3(name = "best_bid_price")]
267 fn py_best_bid_price(&self) -> Option<Price> {
268 self.best_bid_price()
269 }
270
271 #[pyo3(name = "best_ask_price")]
272 fn py_best_ask_price(&self) -> Option<Price> {
273 self.best_ask_price()
274 }
275
276 #[pyo3(name = "best_bid_size")]
277 fn py_best_bid_size(&self) -> Option<Quantity> {
278 self.best_bid_size()
279 }
280
281 #[pyo3(name = "best_ask_size")]
282 fn py_best_ask_size(&self) -> Option<Quantity> {
283 self.best_ask_size()
284 }
285
286 #[pyo3(name = "spread")]
287 fn py_spread(&self) -> Option<f64> {
288 self.spread()
289 }
290
291 #[pyo3(name = "midpoint")]
292 fn py_midpoint(&self) -> Option<f64> {
293 self.midpoint()
294 }
295
296 #[pyo3(name = "get_avg_px_for_quantity")]
297 fn py_get_avg_px_for_quantity(&self, qty: Quantity, order_side: OrderSide) -> f64 {
298 self.get_avg_px_for_quantity(qty, order_side)
299 }
300
301 #[pyo3(name = "get_avg_px_qty_for_exposure")]
302 fn py_get_avg_px_qty_for_exposure(
303 &self,
304 qty: Quantity,
305 order_side: OrderSide,
306 ) -> (f64, f64, f64) {
307 self.get_avg_px_qty_for_exposure(qty, order_side)
308 }
309
310 #[pyo3(name = "get_quantity_for_price")]
311 fn py_get_quantity_for_price(&self, price: Price, order_side: OrderSide) -> f64 {
312 self.get_quantity_for_price(price, order_side)
313 }
314
315 #[pyo3(name = "simulate_fills")]
316 fn py_simulate_fills(&self, order: &BookOrder) -> Vec<(Price, Quantity)> {
317 self.simulate_fills(order)
318 }
319
320 #[pyo3(name = "pprint")]
321 #[pyo3(signature = (num_levels=3))]
322 fn py_pprint(&self, num_levels: usize) -> String {
323 self.pprint(num_levels)
324 }
325}
326
327#[pyfunction()]
328#[pyo3(name = "update_book_with_quote_tick")]
329pub fn py_update_book_with_quote_tick(book: &mut OrderBook, quote: &QuoteTick) -> PyResult<()> {
330 book.update_quote_tick(quote).map_err(to_pyvalue_err)
331}
332
333#[pyfunction()]
334#[pyo3(name = "update_book_with_trade_tick")]
335pub fn py_update_book_with_trade_tick(book: &mut OrderBook, trade: &TradeTick) -> PyResult<()> {
336 book.update_trade_tick(trade).map_err(to_pyvalue_err)
337}