Skip to content

Commit

Permalink
Refine OrderBookDepth10 type and Databento parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
cjdsellers committed Jan 3, 2024
1 parent 210edd7 commit 5f74278
Show file tree
Hide file tree
Showing 12 changed files with 245 additions and 53 deletions.
12 changes: 10 additions & 2 deletions nautilus_core/adapters/src/databento/parsing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,8 @@ pub fn parse_mbp10_msg(
) -> Result<OrderBookDepth10> {
let mut bids = Vec::with_capacity(DEPTH10_LEN);
let mut asks = Vec::with_capacity(DEPTH10_LEN);
let mut bid_counts = Vec::with_capacity(DEPTH10_LEN);
let mut ask_counts = Vec::with_capacity(DEPTH10_LEN);

for level in &record.levels {
let bid_order = BookOrder::new(
Expand All @@ -377,15 +379,21 @@ pub fn parse_mbp10_msg(

bids.push(bid_order);
asks.push(ask_order);
bid_counts.push(level.bid_ct);
ask_counts.push(level.ask_ct);
}

let bids: [BookOrder; DEPTH10_LEN] = bids.try_into().expect("Bids `Vec` length mismatch");
let asks: [BookOrder; DEPTH10_LEN] = asks.try_into().expect("Asks `Vec` length mismatch");
let bids: [BookOrder; DEPTH10_LEN] = bids.try_into().expect("`bids` length != 10");
let asks: [BookOrder; DEPTH10_LEN] = asks.try_into().expect("`asks` length != 10");
let bid_counts: [u32; DEPTH10_LEN] = bid_counts.try_into().expect("`bid_counts` length != 10");
let ask_counts: [u32; DEPTH10_LEN] = ask_counts.try_into().expect("`ask_counts` length != 10");

let depth = OrderBookDepth10::new(
instrument_id,
bids,
asks,
bid_counts,
ask_counts,
record.flags,
record.sequence.into(),
record.ts_recv,
Expand Down
1 change: 1 addition & 0 deletions nautilus_core/model/cbindgen_cython.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ header = '"../includes/model.h"'
"libc.stdint" = [
"uint8_t",
"uint16_t",
"uint32_t",
"uint64_t",
"uintptr_t",
"int64_t",
Expand Down
15 changes: 15 additions & 0 deletions nautilus_core/model/src/data/depth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ pub struct OrderBookDepth10 {
pub bids: [BookOrder; DEPTH10_LEN],
/// The ask orders for the depth update.
pub asks: [BookOrder; DEPTH10_LEN],
/// The count of bid orders per level for the depth update.
pub bid_counts: [u32; DEPTH10_LEN],
/// The count of ask orders per level for the depth update.
pub ask_counts: [u32; DEPTH10_LEN],
/// A combination of packet end with matching engine status.
pub flags: u8,
/// The message sequence number assigned at the venue.
Expand All @@ -63,6 +67,8 @@ impl OrderBookDepth10 {
instrument_id: InstrumentId,
bids: [BookOrder; DEPTH10_LEN],
asks: [BookOrder; DEPTH10_LEN],
bid_counts: [u32; DEPTH10_LEN],
ask_counts: [u32; DEPTH10_LEN],
flags: u8,
sequence: u64,
ts_event: UnixNanos,
Expand All @@ -72,6 +78,8 @@ impl OrderBookDepth10 {
instrument_id,
bids,
asks,
bid_counts,
ask_counts,
flags,
sequence,
ts_event,
Expand Down Expand Up @@ -159,10 +167,15 @@ pub mod stubs {
order_id += 1;
}

let bid_counts: [u32; 10] = [1; 10];
let ask_counts: [u32; 10] = [1; 10];

OrderBookDepth10::new(
instrument_id,
bids,
asks,
bid_counts,
ask_counts,
flags,
sequence,
ts_event,
Expand Down Expand Up @@ -196,6 +209,8 @@ mod tests {
assert_eq!(depth.asks[0].price.as_f64(), 100.0);
assert_eq!(depth.bids[0].price.as_f64(), 99.0);
assert_eq!(depth.bids[9].price.as_f64(), 90.0);
assert_eq!(depth.bid_counts.len(), 10);
assert_eq!(depth.ask_counts.len(), 10);
assert_eq!(depth.flags, flags);
assert_eq!(depth.sequence, sequence);
assert_eq!(depth.ts_event, ts_event);
Expand Down
27 changes: 24 additions & 3 deletions nautilus_core/model/src/ffi/data/depth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,14 @@ use crate::{
/// # Safety
///
/// - Assumes `bids` and `asks` are valid pointers to arrays of `BookOrder` of length 10.
/// - Assumes Rust now takes ownership of the memory for `bids` and `asks`.
/// - Assumes `bid_counts` and `ask_counts` are valid pointers to arrays of `u32` of length 10.
#[no_mangle]
pub unsafe extern "C" fn orderbook_depth10_new(
instrument_id: InstrumentId,
bids_ptr: *const BookOrder,
asks_ptr: *const BookOrder,
bid_counts_ptr: *const u32,
ask_counts_ptr: *const u32,
flags: u8,
sequence: u64,
ts_event: UnixNanos,
Expand All @@ -46,16 +48,25 @@ pub unsafe extern "C" fn orderbook_depth10_new(
// The caller must guarantee that they point to arrays of `BookOrder` of at least `DEPTH10_LEN` length.
assert!(!bids_ptr.is_null());
assert!(!asks_ptr.is_null());
assert!(!bid_counts_ptr.is_null());
assert!(!ask_counts_ptr.is_null());

let bids_slice = std::slice::from_raw_parts(bids_ptr, DEPTH10_LEN);
let asks_slice = std::slice::from_raw_parts(asks_ptr, DEPTH10_LEN);
let bids: [BookOrder; DEPTH10_LEN] = bids_slice.try_into().expect("Slice length mismatch");
let asks: [BookOrder; DEPTH10_LEN] = asks_slice.try_into().expect("Slice length mismatch");
let bids: [BookOrder; DEPTH10_LEN] = bids_slice.try_into().expect("Slice length != 10");
let asks: [BookOrder; DEPTH10_LEN] = asks_slice.try_into().expect("Slice length != 10");

let bid_counts_slice = std::slice::from_raw_parts(bid_counts_ptr, DEPTH10_LEN);
let ask_counts_slice = std::slice::from_raw_parts(ask_counts_ptr, DEPTH10_LEN);
let bid_counts: [u32; DEPTH10_LEN] = bid_counts_slice.try_into().expect("Slice length != 10");
let ask_counts: [u32; DEPTH10_LEN] = ask_counts_slice.try_into().expect("Slice length != 10");

OrderBookDepth10::new(
instrument_id,
bids,
asks,
bid_counts,
ask_counts,
flags,
sequence,
ts_event,
Expand Down Expand Up @@ -84,3 +95,13 @@ pub extern "C" fn orderbook_depth10_bids_array(depth: &OrderBookDepth10) -> *con
pub extern "C" fn orderbook_depth10_asks_array(depth: &OrderBookDepth10) -> *const BookOrder {
depth.asks.as_ptr()
}

#[no_mangle]
pub extern "C" fn orderbook_depth10_bid_counts_array(depth: &OrderBookDepth10) -> *const u32 {
depth.bid_counts.as_ptr()
}

#[no_mangle]
pub extern "C" fn orderbook_depth10_ask_counts_array(depth: &OrderBookDepth10) -> *const u32 {
depth.ask_counts.as_ptr()
}
28 changes: 23 additions & 5 deletions nautilus_core/model/src/python/data/depth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,24 @@ use nautilus_core::time::UnixNanos;
use pyo3::{prelude::*, pyclass::CompareOp};

use crate::{
data::{depth::OrderBookDepth10, order::BookOrder},
data::{
depth::{OrderBookDepth10, DEPTH10_LEN},
order::BookOrder,
},
identifiers::instrument_id::InstrumentId,
python::PY_MODULE_MODEL,
};

#[pymethods]
impl OrderBookDepth10 {
#[allow(clippy::too_many_arguments)]
#[new]
fn py_new(
instrument_id: InstrumentId,
bids: [BookOrder; 10],
asks: [BookOrder; 10],
bids: [BookOrder; DEPTH10_LEN],
asks: [BookOrder; DEPTH10_LEN],
bid_counts: [u32; DEPTH10_LEN],
ask_counts: [u32; DEPTH10_LEN],
flags: u8,
sequence: u64,
ts_event: UnixNanos,
Expand All @@ -43,6 +49,8 @@ impl OrderBookDepth10 {
instrument_id,
bids,
asks,
bid_counts,
ask_counts,
flags,
sequence,
ts_event,
Expand Down Expand Up @@ -78,15 +86,25 @@ impl OrderBookDepth10 {
}

#[getter]
fn bids(&self) -> [BookOrder; 10] {
fn bids(&self) -> [BookOrder; DEPTH10_LEN] {
self.bids
}

#[getter]
fn asks(&self) -> [BookOrder; 10] {
fn asks(&self) -> [BookOrder; DEPTH10_LEN] {
self.asks
}

#[getter]
fn bid_counts(&self) -> [u32; DEPTH10_LEN] {
self.bid_counts
}

#[getter]
fn ask_counts(&self) -> [u32; DEPTH10_LEN] {
self.ask_counts
}

#[getter]
fn flags(&self) -> u8 {
self.flags
Expand Down
50 changes: 26 additions & 24 deletions nautilus_trader/adapters/databento/parsing.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,9 @@
from nautilus_trader.model.data import Bar
from nautilus_trader.model.data import BarSpecification
from nautilus_trader.model.data import BarType
from nautilus_trader.model.data import BookOrder
from nautilus_trader.model.data import OrderBookDelta
from nautilus_trader.model.data import OrderBookDeltas
from nautilus_trader.model.data import OrderBookDepth10
from nautilus_trader.model.data import QuoteTick
from nautilus_trader.model.data import TradeTick
from nautilus_trader.model.enums import AggregationSource
Expand Down Expand Up @@ -305,45 +306,46 @@ def parse_mbp10_msg(
record: databento.MBP10Msg,
instrument_id: InstrumentId,
ts_init: int,
) -> OrderBookDeltas:
bids: list[OrderBookDelta] = []
asks: list[OrderBookDelta] = []
) -> OrderBookDepth10:
bids: list[BookOrder] = []
asks: list[BookOrder] = []
bid_counts: list[int] = []
ask_counts: list[int] = []

for level in record.levels:
bid = OrderBookDelta.from_raw(
instrument_id=instrument_id,
action=BookAction.ADD,
bid = BookOrder.from_raw(
side=OrderSide.BUY,
price_raw=level.bid_px,
price_prec=USD.precision, # TODO(per instrument precision)
size_raw=int(level.bid_sz * FIXED_SCALAR), # No fractional sizes
price_prec=USD.precision,
size_raw=int(level.bid_sz * FIXED_SCALAR),
size_prec=0, # No fractional units
order_id=0, # No order ID for MBP level
flags=record.flags,
sequence=record.sequence,
ts_event=record.ts_recv, # More accurate and reliable timestamp
ts_init=ts_init,
)
bids.append(bid)
bid_counts.append(level.bid_ct)

ask = OrderBookDelta.from_raw(
instrument_id=instrument_id,
action=BookAction.ADD,
ask = BookOrder.from_raw(
side=OrderSide.SELL,
price_raw=level.ask_px,
price_prec=USD.precision, # TODO(per instrument precision)
size_raw=int(level.ask_sz * FIXED_SCALAR), # No fractional sizes
price_prec=USD.precision,
size_raw=int(level.ask_sz * FIXED_SCALAR),
size_prec=0, # No fractional units
order_id=0, # No order ID for MBP level
flags=record.flags,
sequence=record.sequence,
ts_event=record.ts_recv, # More accurate and reliable timestamp
ts_init=ts_init,
)
asks.append(ask)
ask_counts.append(level.ask_ct) # Currently a typo in type stub

clear = [OrderBookDelta.clear(instrument_id, record.ts_recv, record.ts_recv, record.sequence)]
return OrderBookDeltas(instrument_id=instrument_id, deltas=clear + bids + asks)
return OrderBookDepth10(
instrument_id=instrument_id,
bids=bids,
asks=asks,
bid_counts=bid_counts,
ask_counts=ask_counts,
flags=record.flags,
sequence=record.sequence,
ts_event=record.ts_recv,
ts_init=ts_init,
)


def parse_trade_msg(
Expand Down
16 changes: 15 additions & 1 deletion nautilus_trader/core/includes/model.h
Original file line number Diff line number Diff line change
Expand Up @@ -797,6 +797,14 @@ typedef struct OrderBookDepth10_t {
* The ask orders for the depth update.
*/
struct BookOrder_t asks[DEPTH10_LEN];
/**
* The count of bid orders per level for the depth update.
*/
uint32_t bid_counts[DEPTH10_LEN];
/**
* The count of ask orders per level for the depth update.
*/
uint32_t ask_counts[DEPTH10_LEN];
/**
* A combination of packet end with matching engine status.
*/
Expand Down Expand Up @@ -1390,11 +1398,13 @@ uint64_t orderbook_delta_hash(const struct OrderBookDelta_t *delta);
* # Safety
*
* - Assumes `bids` and `asks` are valid pointers to arrays of `BookOrder` of length 10.
* - Assumes Rust now takes ownership of the memory for `bids` and `asks`.
* - Assumes `bid_counts` and `ask_counts` are valid pointers to arrays of `u32` of length 10.
*/
struct OrderBookDepth10_t orderbook_depth10_new(struct InstrumentId_t instrument_id,
const struct BookOrder_t *bids_ptr,
const struct BookOrder_t *asks_ptr,
const uint32_t *bid_counts_ptr,
const uint32_t *ask_counts_ptr,
uint8_t flags,
uint64_t sequence,
uint64_t ts_event,
Expand All @@ -1409,6 +1419,10 @@ const struct BookOrder_t *orderbook_depth10_bids_array(const struct OrderBookDep

const struct BookOrder_t *orderbook_depth10_asks_array(const struct OrderBookDepth10_t *depth);

const uint32_t *orderbook_depth10_bid_counts_array(const struct OrderBookDepth10_t *depth);

const uint32_t *orderbook_depth10_ask_counts_array(const struct OrderBookDepth10_t *depth);

struct BookOrder_t book_order_from_raw(enum OrderSide order_side,
int64_t price_raw,
uint8_t price_prec,
Expand Down
14 changes: 12 additions & 2 deletions nautilus_trader/core/rust/model.pxd
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Warning, this file is autogenerated by cbindgen. Don't modify this manually. */

from libc.stdint cimport uint8_t, uint16_t, uint64_t, uintptr_t, int64_t
from libc.stdint cimport uint8_t, uint16_t, uint32_t, uint64_t, uintptr_t, int64_t
from nautilus_trader.core.rust.core cimport CVec, UUID4_t

cdef extern from "../includes/model.h":
Expand Down Expand Up @@ -435,6 +435,10 @@ cdef extern from "../includes/model.h":
BookOrder_t bids[DEPTH10_LEN];
# The ask orders for the depth update.
BookOrder_t asks[DEPTH10_LEN];
# The count of bid orders per level for the depth update.
uint32_t bid_counts[DEPTH10_LEN];
# The count of ask orders per level for the depth update.
uint32_t ask_counts[DEPTH10_LEN];
# A combination of packet end with matching engine status.
uint8_t flags;
# The message sequence number assigned at the venue.
Expand Down Expand Up @@ -835,10 +839,12 @@ cdef extern from "../includes/model.h":
# # Safety
#
# - Assumes `bids` and `asks` are valid pointers to arrays of `BookOrder` of length 10.
# - Assumes Rust now takes ownership of the memory for `bids` and `asks`.
# - Assumes `bid_counts` and `ask_counts` are valid pointers to arrays of `u32` of length 10.
OrderBookDepth10_t orderbook_depth10_new(InstrumentId_t instrument_id,
const BookOrder_t *bids_ptr,
const BookOrder_t *asks_ptr,
const uint32_t *bid_counts_ptr,
const uint32_t *ask_counts_ptr,
uint8_t flags,
uint64_t sequence,
uint64_t ts_event,
Expand All @@ -852,6 +858,10 @@ cdef extern from "../includes/model.h":

const BookOrder_t *orderbook_depth10_asks_array(const OrderBookDepth10_t *depth);

const uint32_t *orderbook_depth10_bid_counts_array(const OrderBookDepth10_t *depth);

const uint32_t *orderbook_depth10_ask_counts_array(const OrderBookDepth10_t *depth);

BookOrder_t book_order_from_raw(OrderSide order_side,
int64_t price_raw,
uint8_t price_prec,
Expand Down
Loading

0 comments on commit 5f74278

Please sign in to comment.