nautilus_model/ffi/orderbook/
book.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2025 Nautech Systems Pty Ltd. All rights reserved.
3//  https://nautechsystems.io
4//
5//  Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
6//  You may not use this file except in compliance with the License.
7//  You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
8//
9//  Unless required by applicable law or agreed to in writing, software
10//  distributed under the License is distributed on an "AS IS" BASIS,
11//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//  See the License for the specific language governing permissions and
13//  limitations under the License.
14// -------------------------------------------------------------------------------------------------
15
16use std::{
17    ffi::c_char,
18    ops::{Deref, DerefMut},
19};
20
21use nautilus_core::ffi::{cvec::CVec, string::str_to_cstr};
22
23use super::level::BookLevel_API;
24use crate::{
25    data::{
26        BookOrder, OrderBookDelta, OrderBookDeltas_API, OrderBookDepth10, QuoteTick, TradeTick,
27    },
28    enums::{BookType, OrderSide},
29    identifiers::InstrumentId,
30    orderbook::{analysis::book_check_integrity, OrderBook},
31    types::{Price, Quantity},
32};
33
34/// C compatible Foreign Function Interface (FFI) for an underlying `OrderBook`.
35///
36/// This struct wraps `OrderBook` in a way that makes it compatible with C function
37/// calls, enabling interaction with `OrderBook` in a C environment.
38///
39/// It implements the `Deref` trait, allowing instances of `OrderBook_API` to be
40/// dereferenced to `OrderBook`, providing access to `OrderBook`'s methods without
41/// having to manually access the underlying `OrderBook` instance.
42#[repr(C)]
43#[allow(non_camel_case_types)]
44pub struct OrderBook_API(Box<OrderBook>);
45
46impl Deref for OrderBook_API {
47    type Target = OrderBook;
48
49    fn deref(&self) -> &Self::Target {
50        &self.0
51    }
52}
53
54impl DerefMut for OrderBook_API {
55    fn deref_mut(&mut self) -> &mut Self::Target {
56        &mut self.0
57    }
58}
59
60#[no_mangle]
61pub extern "C" fn orderbook_new(instrument_id: InstrumentId, book_type: BookType) -> OrderBook_API {
62    OrderBook_API(Box::new(OrderBook::new(instrument_id, book_type)))
63}
64
65#[no_mangle]
66pub extern "C" fn orderbook_drop(book: OrderBook_API) {
67    drop(book); // Memory freed here
68}
69
70#[no_mangle]
71pub extern "C" fn orderbook_reset(book: &mut OrderBook_API) {
72    book.reset();
73}
74
75#[no_mangle]
76pub extern "C" fn orderbook_instrument_id(book: &OrderBook_API) -> InstrumentId {
77    book.instrument_id
78}
79
80#[no_mangle]
81pub extern "C" fn orderbook_book_type(book: &OrderBook_API) -> BookType {
82    book.book_type
83}
84
85#[no_mangle]
86pub extern "C" fn orderbook_sequence(book: &OrderBook_API) -> u64 {
87    book.sequence
88}
89
90#[no_mangle]
91pub extern "C" fn orderbook_ts_last(book: &OrderBook_API) -> u64 {
92    book.ts_last.into()
93}
94
95#[no_mangle]
96pub extern "C" fn orderbook_count(book: &OrderBook_API) -> u64 {
97    book.count
98}
99
100#[no_mangle]
101#[cfg_attr(feature = "high-precision", allow(improper_ctypes_definitions))]
102pub extern "C" fn orderbook_add(
103    book: &mut OrderBook_API,
104    order: BookOrder,
105    flags: u8,
106    sequence: u64,
107    ts_event: u64,
108) {
109    book.add(order, flags, sequence, ts_event.into());
110}
111
112#[no_mangle]
113#[cfg_attr(feature = "high-precision", allow(improper_ctypes_definitions))]
114pub extern "C" fn orderbook_update(
115    book: &mut OrderBook_API,
116    order: BookOrder,
117    flags: u8,
118    sequence: u64,
119    ts_event: u64,
120) {
121    book.update(order, flags, sequence, ts_event.into());
122}
123
124#[no_mangle]
125#[cfg_attr(feature = "high-precision", allow(improper_ctypes_definitions))]
126pub extern "C" fn orderbook_delete(
127    book: &mut OrderBook_API,
128    order: BookOrder,
129    flags: u8,
130    sequence: u64,
131    ts_event: u64,
132) {
133    book.delete(order, flags, sequence, ts_event.into());
134}
135
136#[no_mangle]
137pub extern "C" fn orderbook_clear(book: &mut OrderBook_API, sequence: u64, ts_event: u64) {
138    book.clear(sequence, ts_event.into());
139}
140
141#[no_mangle]
142pub extern "C" fn orderbook_clear_bids(book: &mut OrderBook_API, sequence: u64, ts_event: u64) {
143    book.clear_bids(sequence, ts_event.into());
144}
145
146#[no_mangle]
147pub extern "C" fn orderbook_clear_asks(book: &mut OrderBook_API, sequence: u64, ts_event: u64) {
148    book.clear_asks(sequence, ts_event.into());
149}
150
151#[no_mangle]
152pub extern "C" fn orderbook_apply_delta(book: &mut OrderBook_API, delta: &OrderBookDelta) {
153    book.apply_delta(delta);
154}
155
156#[no_mangle]
157pub extern "C" fn orderbook_apply_deltas(book: &mut OrderBook_API, deltas: &OrderBookDeltas_API) {
158    // Clone will actually copy the contents of the `deltas` vec
159    book.apply_deltas(deltas.deref());
160}
161
162#[no_mangle]
163pub extern "C" fn orderbook_apply_depth(book: &mut OrderBook_API, depth: &OrderBookDepth10) {
164    book.apply_depth(depth);
165}
166
167#[no_mangle]
168pub extern "C" fn orderbook_bids(book: &mut OrderBook_API) -> CVec {
169    book.bids
170        .levels
171        .values()
172        .map(|level| BookLevel_API::new(level.clone()))
173        .collect::<Vec<BookLevel_API>>()
174        .into()
175}
176
177#[no_mangle]
178pub extern "C" fn orderbook_asks(book: &mut OrderBook_API) -> CVec {
179    book.asks
180        .levels
181        .values()
182        .map(|level| BookLevel_API::new(level.clone()))
183        .collect::<Vec<BookLevel_API>>()
184        .into()
185}
186
187#[no_mangle]
188pub extern "C" fn orderbook_has_bid(book: &mut OrderBook_API) -> u8 {
189    u8::from(book.has_bid())
190}
191
192#[no_mangle]
193pub extern "C" fn orderbook_has_ask(book: &mut OrderBook_API) -> u8 {
194    u8::from(book.has_ask())
195}
196
197#[no_mangle]
198#[cfg_attr(feature = "high-precision", allow(improper_ctypes_definitions))]
199pub extern "C" fn orderbook_best_bid_price(book: &mut OrderBook_API) -> Price {
200    book.best_bid_price()
201        .expect("Error: No bid orders for best bid price")
202}
203
204#[no_mangle]
205#[cfg_attr(feature = "high-precision", allow(improper_ctypes_definitions))]
206pub extern "C" fn orderbook_best_ask_price(book: &mut OrderBook_API) -> Price {
207    book.best_ask_price()
208        .expect("Error: No ask orders for best ask price")
209}
210
211#[no_mangle]
212#[cfg_attr(feature = "high-precision", allow(improper_ctypes_definitions))]
213pub extern "C" fn orderbook_best_bid_size(book: &mut OrderBook_API) -> Quantity {
214    book.best_bid_size()
215        .expect("Error: No bid orders for best bid size")
216}
217
218#[no_mangle]
219#[cfg_attr(feature = "high-precision", allow(improper_ctypes_definitions))]
220pub extern "C" fn orderbook_best_ask_size(book: &mut OrderBook_API) -> Quantity {
221    book.best_ask_size()
222        .expect("Error: No ask orders for best ask size")
223}
224
225#[no_mangle]
226pub extern "C" fn orderbook_spread(book: &mut OrderBook_API) -> f64 {
227    book.spread()
228        .expect("Error: Unable to calculate `spread` (no bid or ask)")
229}
230
231#[no_mangle]
232pub extern "C" fn orderbook_midpoint(book: &mut OrderBook_API) -> f64 {
233    book.midpoint()
234        .expect("Error: Unable to calculate `midpoint` (no bid or ask)")
235}
236
237#[no_mangle]
238#[cfg_attr(feature = "high-precision", allow(improper_ctypes_definitions))]
239pub extern "C" fn orderbook_get_avg_px_for_quantity(
240    book: &mut OrderBook_API,
241    qty: Quantity,
242    order_side: OrderSide,
243) -> f64 {
244    book.get_avg_px_for_quantity(qty, order_side)
245}
246
247#[no_mangle]
248#[cfg_attr(feature = "high-precision", allow(improper_ctypes_definitions))]
249pub extern "C" fn orderbook_get_quantity_for_price(
250    book: &mut OrderBook_API,
251    price: Price,
252    order_side: OrderSide,
253) -> f64 {
254    book.get_quantity_for_price(price, order_side)
255}
256
257/// Updates the order book with a quote tick.
258///
259/// # Panics
260///
261/// This function panics:
262/// - If book type is not `L1_MBP`.
263#[no_mangle]
264pub extern "C" fn orderbook_update_quote_tick(book: &mut OrderBook_API, quote: &QuoteTick) {
265    book.update_quote_tick(quote).unwrap();
266}
267
268/// Updates the order book with a trade tick.
269///
270/// # Panics
271///
272/// This function panics:
273/// - If book type is not `L1_MBP`.
274#[no_mangle]
275pub extern "C" fn orderbook_update_trade_tick(book: &mut OrderBook_API, trade: &TradeTick) {
276    book.update_trade_tick(trade).unwrap();
277}
278
279#[no_mangle]
280#[cfg_attr(feature = "high-precision", allow(improper_ctypes_definitions))]
281pub extern "C" fn orderbook_simulate_fills(book: &OrderBook_API, order: BookOrder) -> CVec {
282    book.simulate_fills(&order).into()
283}
284
285#[no_mangle]
286pub extern "C" fn orderbook_check_integrity(book: &OrderBook_API) -> u8 {
287    u8::from(book_check_integrity(book).is_ok())
288}
289
290// TODO: This struct implementation potentially leaks memory
291// TODO: Skip clippy check for now since it requires large modification
292#[allow(clippy::drop_non_drop)]
293#[no_mangle]
294pub extern "C" fn vec_fills_drop(v: CVec) {
295    let CVec { ptr, len, cap } = v;
296    let data: Vec<(Price, Quantity)> =
297        unsafe { Vec::from_raw_parts(ptr.cast::<(Price, Quantity)>(), len, cap) };
298    drop(data); // Memory freed here
299}
300
301/// Returns a pretty printed `OrderBook` number of levels per side, as a C string pointer.
302#[no_mangle]
303pub extern "C" fn orderbook_pprint_to_cstr(
304    book: &OrderBook_API,
305    num_levels: usize,
306) -> *const c_char {
307    str_to_cstr(&book.pprint(num_levels))
308}