nautilus_model/ffi/orderbook/
book.rs1use std::ffi::c_char;
17
18use nautilus_core::ffi::{abort_on_panic, cvec::CVec, string::str_to_cstr};
19
20use crate::{
21 data::{OrderBookDeltas, QuoteTick, TradeTick},
22 enums::{BookType, OrderSide},
23 ffi::{
24 data::{delta::OrderBookDeltaFfi, depth::OrderBookDepth10Ffi, order::BookOrderFfi},
25 enums::OrderSideOptional,
26 },
27 identifiers::InstrumentId,
28 orderbook::{BookLevel, OrderBook, analysis::book_check_integrity, ladder::BookPrice},
29 types::{ERROR_PRICE, Price, Quantity, price::PriceRaw},
30};
31
32#[unsafe(no_mangle)]
35pub extern "C" fn orderbook_new(
36 instrument_id: InstrumentId,
37 book_type: BookType,
38) -> *mut OrderBook {
39 Box::into_raw(Box::new(OrderBook::new(instrument_id, book_type)))
40}
41
42#[unsafe(no_mangle)]
51pub unsafe extern "C" fn orderbook_drop(book: *mut OrderBook) {
52 abort_on_panic(|| {
53 assert!(!book.is_null(), "`book` was NULL");
54 drop(unsafe { Box::from_raw(book) }); });
57}
58
59#[unsafe(no_mangle)]
60pub extern "C" fn orderbook_reset(book: &mut OrderBook) {
61 book.reset();
62}
63
64#[unsafe(no_mangle)]
65pub extern "C" fn orderbook_instrument_id(book: &OrderBook) -> InstrumentId {
66 book.instrument_id
67}
68
69#[unsafe(no_mangle)]
70pub extern "C" fn orderbook_book_type(book: &OrderBook) -> BookType {
71 book.book_type
72}
73
74#[unsafe(no_mangle)]
75pub extern "C" fn orderbook_sequence(book: &OrderBook) -> u64 {
76 book.sequence
77}
78
79#[unsafe(no_mangle)]
80pub extern "C" fn orderbook_ts_last(book: &OrderBook) -> u64 {
81 book.ts_last.into()
82}
83
84#[unsafe(no_mangle)]
85pub extern "C" fn orderbook_update_count(book: &OrderBook) -> u64 {
86 book.update_count
87}
88
89#[unsafe(no_mangle)]
90#[cfg_attr(feature = "high-precision", allow(improper_ctypes_definitions))]
91pub extern "C" fn orderbook_add(
92 book: &mut OrderBook,
93 order: BookOrderFfi,
94 flags: u8,
95 sequence: u64,
96 ts_event: u64,
97) {
98 book.add(order.into(), flags, sequence, ts_event.into());
99}
100
101#[unsafe(no_mangle)]
102#[cfg_attr(feature = "high-precision", allow(improper_ctypes_definitions))]
103pub extern "C" fn orderbook_update(
104 book: &mut OrderBook,
105 order: BookOrderFfi,
106 flags: u8,
107 sequence: u64,
108 ts_event: u64,
109) {
110 book.update(order.into(), flags, sequence, ts_event.into());
111}
112
113#[unsafe(no_mangle)]
114#[cfg_attr(feature = "high-precision", allow(improper_ctypes_definitions))]
115pub extern "C" fn orderbook_delete(
116 book: &mut OrderBook,
117 order: BookOrderFfi,
118 flags: u8,
119 sequence: u64,
120 ts_event: u64,
121) {
122 book.delete(order.into(), flags, sequence, ts_event.into());
123}
124
125#[unsafe(no_mangle)]
126pub extern "C" fn orderbook_clear(book: &mut OrderBook, sequence: u64, ts_event: u64) {
127 book.clear(sequence, ts_event.into());
128}
129
130#[unsafe(no_mangle)]
131pub extern "C" fn orderbook_clear_bids(book: &mut OrderBook, sequence: u64, ts_event: u64) {
132 book.clear_bids(sequence, ts_event.into());
133}
134
135#[unsafe(no_mangle)]
136pub extern "C" fn orderbook_clear_asks(book: &mut OrderBook, sequence: u64, ts_event: u64) {
137 book.clear_asks(sequence, ts_event.into());
138}
139
140#[unsafe(no_mangle)]
141pub extern "C" fn orderbook_apply_delta(book: &mut OrderBook, delta: &OrderBookDeltaFfi) {
142 if let Err(e) = book.apply_delta_unchecked(&(*delta).into()) {
143 log::error!("Failed to apply order book delta: {e}");
144 }
145}
146
147#[unsafe(no_mangle)]
148pub extern "C" fn orderbook_apply_deltas(book: &mut OrderBook, deltas: &OrderBookDeltas) {
149 if let Err(e) = book.apply_deltas_unchecked(deltas) {
151 log::error!("Failed to apply order book deltas: {e}");
152 }
153}
154
155#[unsafe(no_mangle)]
171pub extern "C" fn orderbook_to_snapshot_deltas(
172 book: &OrderBook,
173 ts_event: u64,
174 ts_init: u64,
175) -> *mut OrderBookDeltas {
176 use nautilus_core::UnixNanos;
177 Box::into_raw(Box::new(
178 book.to_deltas(UnixNanos::from(ts_event), UnixNanos::from(ts_init)),
179 ))
180}
181
182#[unsafe(no_mangle)]
183pub extern "C" fn orderbook_apply_depth(book: &mut OrderBook, depth: &OrderBookDepth10Ffi) {
184 if let Err(e) = book.apply_depth_unchecked(&(*depth).into()) {
185 log::error!("Failed to apply order book depth: {e}");
186 }
187}
188
189#[unsafe(no_mangle)]
190pub extern "C" fn orderbook_bids(book: &mut OrderBook) -> CVec {
191 book.bids
192 .levels
193 .values()
194 .map(|level| Box::into_raw(Box::new(level.clone())))
195 .collect::<Vec<*mut BookLevel>>()
196 .into()
197}
198
199#[unsafe(no_mangle)]
200pub extern "C" fn orderbook_asks(book: &mut OrderBook) -> CVec {
201 book.asks
202 .levels
203 .values()
204 .map(|level| Box::into_raw(Box::new(level.clone())))
205 .collect::<Vec<*mut BookLevel>>()
206 .into()
207}
208
209#[unsafe(no_mangle)]
210#[cfg_attr(feature = "high-precision", allow(improper_ctypes_definitions))]
211pub extern "C" fn orderbook_bids_down_to(
212 book: &mut OrderBook,
213 price_raw: PriceRaw,
214 price_prec: u8,
215) -> CVec {
216 let price = Price::from_raw(price_raw, price_prec);
217 let bound = BookPrice::new(price, OrderSide::Buy);
218 book.bids
219 .levels
220 .range(..=bound)
221 .map(|(_, level)| Box::into_raw(Box::new(level.clone())))
222 .collect::<Vec<*mut BookLevel>>()
223 .into()
224}
225
226#[unsafe(no_mangle)]
227#[cfg_attr(feature = "high-precision", allow(improper_ctypes_definitions))]
228pub extern "C" fn orderbook_asks_up_to(
229 book: &mut OrderBook,
230 price_raw: PriceRaw,
231 price_prec: u8,
232) -> CVec {
233 let price = Price::from_raw(price_raw, price_prec);
234 let bound = BookPrice::new(price, OrderSide::Sell);
235 book.asks
236 .levels
237 .range(..=bound)
238 .map(|(_, level)| Box::into_raw(Box::new(level.clone())))
239 .collect::<Vec<*mut BookLevel>>()
240 .into()
241}
242
243#[unsafe(no_mangle)]
244pub extern "C" fn orderbook_has_bid(book: &mut OrderBook) -> u8 {
245 u8::from(book.has_bid())
246}
247
248#[unsafe(no_mangle)]
249pub extern "C" fn orderbook_has_ask(book: &mut OrderBook) -> u8 {
250 u8::from(book.has_ask())
251}
252
253#[unsafe(no_mangle)]
257#[cfg_attr(feature = "high-precision", allow(improper_ctypes_definitions))]
258pub extern "C" fn orderbook_best_bid_price(book: &mut OrderBook) -> Price {
259 abort_on_panic(|| {
260 book.best_bid_price()
261 .expect("Error: No bid orders for best bid price")
262 })
263}
264
265#[unsafe(no_mangle)]
269#[cfg_attr(feature = "high-precision", allow(improper_ctypes_definitions))]
270pub extern "C" fn orderbook_best_ask_price(book: &mut OrderBook) -> Price {
271 abort_on_panic(|| {
272 book.best_ask_price()
273 .expect("Error: No ask orders for best ask price")
274 })
275}
276
277#[unsafe(no_mangle)]
281#[cfg_attr(feature = "high-precision", allow(improper_ctypes_definitions))]
282pub extern "C" fn orderbook_best_bid_size(book: &mut OrderBook) -> Quantity {
283 abort_on_panic(|| {
284 book.best_bid_size()
285 .expect("Error: No bid orders for best bid size")
286 })
287}
288
289#[unsafe(no_mangle)]
293#[cfg_attr(feature = "high-precision", allow(improper_ctypes_definitions))]
294pub extern "C" fn orderbook_best_ask_size(book: &mut OrderBook) -> Quantity {
295 abort_on_panic(|| {
296 book.best_ask_size()
297 .expect("Error: No ask orders for best ask size")
298 })
299}
300
301#[unsafe(no_mangle)]
305pub extern "C" fn orderbook_spread(book: &mut OrderBook) -> f64 {
306 abort_on_panic(|| {
307 book.spread()
308 .expect("Error: Unable to calculate `spread` (no bid or ask)")
309 })
310}
311
312#[unsafe(no_mangle)]
316pub extern "C" fn orderbook_midpoint(book: &mut OrderBook) -> f64 {
317 abort_on_panic(|| {
318 book.midpoint()
319 .expect("Error: Unable to calculate `midpoint` (no bid or ask)")
320 })
321}
322
323#[unsafe(no_mangle)]
327#[cfg_attr(feature = "high-precision", allow(improper_ctypes_definitions))]
328pub extern "C" fn orderbook_get_avg_px_for_quantity(
329 book: &mut OrderBook,
330 qty: Quantity,
331 order_side: OrderSideOptional,
332) -> f64 {
333 book.get_avg_px_for_quantity(
334 qty,
335 order_side
336 .as_option()
337 .expect("Order side must be Buy or Sell"),
338 )
339}
340
341#[unsafe(no_mangle)]
345#[cfg_attr(feature = "high-precision", allow(improper_ctypes_definitions))]
346pub extern "C" fn orderbook_get_worst_px_for_quantity(
347 book: &mut OrderBook,
348 qty: Quantity,
349 order_side: OrderSideOptional,
350) -> Price {
351 book.get_worst_px_for_quantity(
352 qty,
353 order_side
354 .as_option()
355 .expect("Order side must be Buy or Sell"),
356 )
357 .unwrap_or(ERROR_PRICE)
358}
359
360#[unsafe(no_mangle)]
364#[cfg_attr(feature = "high-precision", allow(improper_ctypes_definitions))]
365pub extern "C" fn orderbook_get_quantity_for_price(
366 book: &mut OrderBook,
367 price: Price,
368 order_side: OrderSideOptional,
369) -> f64 {
370 book.get_quantity_for_price(
371 price,
372 order_side
373 .as_option()
374 .expect("Order side must be Buy or Sell"),
375 )
376}
377
378#[unsafe(no_mangle)]
382#[cfg_attr(feature = "high-precision", allow(improper_ctypes_definitions))]
383pub extern "C" fn orderbook_get_quantity_at_level(
384 book: &OrderBook,
385 price: Price,
386 order_side: OrderSideOptional,
387 size_precision: u8,
388) -> Quantity {
389 book.get_quantity_at_level(
390 price,
391 order_side
392 .as_option()
393 .expect("Order side must be Buy or Sell"),
394 size_precision,
395 )
396}
397
398#[unsafe(no_mangle)]
404pub extern "C" fn orderbook_update_quote_tick(book: &mut OrderBook, quote: &QuoteTick) {
405 book.update_quote_tick(quote).unwrap();
406}
407
408#[unsafe(no_mangle)]
414pub extern "C" fn orderbook_update_trade_tick(book: &mut OrderBook, trade: &TradeTick) {
415 book.update_trade_tick(trade).unwrap();
416}
417
418#[unsafe(no_mangle)]
422#[cfg_attr(feature = "high-precision", allow(improper_ctypes_definitions))]
423pub extern "C" fn orderbook_simulate_fills(book: &OrderBook, order: BookOrderFfi) -> CVec {
424 book.simulate_fills(&order.into()).into()
425}
426
427#[unsafe(no_mangle)]
431#[cfg_attr(feature = "high-precision", allow(improper_ctypes_definitions))]
432pub extern "C" fn orderbook_get_all_crossed_levels(
433 book: &OrderBook,
434 order_side: OrderSideOptional,
435 price: Price,
436 size_precision: u8,
437) -> CVec {
438 book.get_all_crossed_levels(
439 order_side
440 .as_option()
441 .expect("Order side must be Buy or Sell"),
442 price,
443 size_precision,
444 )
445 .into()
446}
447
448#[unsafe(no_mangle)]
449pub extern "C" fn orderbook_check_integrity(book: &OrderBook) -> u8 {
450 u8::from(book_check_integrity(book).is_ok())
451}
452
453#[unsafe(no_mangle)]
457pub unsafe extern "C" fn vec_drop_fills(v: CVec) {
458 let data = unsafe { v.into_vec::<(Price, Quantity)>() };
459 drop(data); }
461
462#[unsafe(no_mangle)]
464pub extern "C" fn orderbook_pprint_to_cstr(book: &OrderBook, num_levels: usize) -> *const c_char {
465 str_to_cstr(&book.pprint(num_levels, None))
466}
467
468#[cfg(test)]
469mod cvec_tests {
470 use rstest::rstest;
471
472 use super::*;
473
474 #[rstest]
475 fn test_empty_fills_drop_returns_without_panic() {
476 unsafe { vec_drop_fills(CVec::empty()) };
477 }
478}