Skip to content

Commit

Permalink
Add OrderBookDeltas to_pyo3 conversion
Browse files Browse the repository at this point in the history
  • Loading branch information
cjdsellers committed Feb 22, 2024
1 parent dc7e837 commit 3d54cec
Show file tree
Hide file tree
Showing 8 changed files with 111 additions and 53 deletions.
15 changes: 14 additions & 1 deletion nautilus_core/model/src/python/data/deltas.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@
use std::{
collections::hash_map::DefaultHasher,
hash::{Hash, Hasher},
ops::Deref,
};

use nautilus_core::time::UnixNanos;
use pyo3::{prelude::*, pyclass::CompareOp};
use pyo3::{prelude::*, pyclass::CompareOp, types::PyCapsule};

use crate::{
data::{delta::OrderBookDelta, deltas::OrderBookDeltas},
ffi::data::deltas::OrderBookDeltas_API,
identifiers::instrument_id::InstrumentId,
python::common::PY_MODULE_MODEL,
};
Expand Down Expand Up @@ -99,6 +101,17 @@ impl OrderBookDeltas {
format!("{}:{}", PY_MODULE_MODEL, stringify!(OrderBookDeltas))
}

#[staticmethod]
#[pyo3(name = "from_pycapsule")]
pub fn py_from_pycapsule(capsule: &PyAny) -> OrderBookDeltas {
let capsule: &PyCapsule = capsule
.downcast()
.expect("Error on downcast to `&PyCapsule`");
let data: &OrderBookDeltas_API =
unsafe { &*(capsule.pointer() as *const OrderBookDeltas_API) };
data.deref().clone()
}

// /// Creates a `PyCapsule` containing a raw pointer to a `Data::Delta` object.
// ///
// /// This function takes the current object (assumed to be of a type that can be represented as
Expand Down
20 changes: 11 additions & 9 deletions nautilus_trader/common/actor.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,14 @@ from nautilus_trader.portfolio.base cimport PortfolioFacade

cdef class Actor(Component):
cdef object _executor
cdef set _warning_events
cdef dict _signal_classes
cdef dict _pending_requests
cdef list _indicators
cdef dict _indicators_for_quotes
cdef dict _indicators_for_trades
cdef dict _indicators_for_bars
cdef set[type] _warning_events
cdef dict[str, type] _signal_classes
cdef dict[UUID4, object] _pending_requests
cdef list[Indicator] _indicators
cdef dict[InstrumentId, list[Indicator]] _indicators_for_quotes
cdef dict[InstrumentId, list[Indicator]] _indicators_for_trades
cdef dict[BarType, list[Indicator]] _indicators_for_bars
cdef set[type] _pyo3_conversion_types

cdef readonly PortfolioFacade portfolio
"""The read-only portfolio for the actor.\n\n:returns: `PortfolioFacade`"""
Expand Down Expand Up @@ -89,7 +90,7 @@ cdef class Actor(Component):
cpdef void on_instrument_status(self, InstrumentStatus data)
cpdef void on_instrument_close(self, InstrumentClose data)
cpdef void on_instrument(self, Instrument instrument)
cpdef void on_order_book_deltas(self, OrderBookDeltas deltas)
cpdef void on_order_book_deltas(self, deltas)
cpdef void on_order_book(self, OrderBook order_book)
cpdef void on_quote_tick(self, QuoteTick tick)
cpdef void on_trade_tick(self, TradeTick tick)
Expand Down Expand Up @@ -144,6 +145,7 @@ cdef class Actor(Component):
dict kwargs=*,
ClientId client_id=*,
bint managed=*,
bint pyo3_conversion=*,
)
cpdef void subscribe_order_book_snapshots(
self,
Expand Down Expand Up @@ -231,7 +233,7 @@ cdef class Actor(Component):
cpdef void handle_instrument(self, Instrument instrument)
cpdef void handle_instruments(self, list instruments)
cpdef void handle_order_book(self, OrderBook order_book)
cpdef void handle_order_book_deltas(self, OrderBookDeltas deltas)
cpdef void handle_order_book_deltas(self, deltas)
cpdef void handle_quote_tick(self, QuoteTick tick)
cpdef void handle_quote_ticks(self, list ticks)
cpdef void handle_trade_tick(self, TradeTick tick)
Expand Down
24 changes: 19 additions & 5 deletions nautilus_trader/common/actor.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,8 @@ cdef class Actor(Component):
self._indicators_for_trades: dict[InstrumentId, list[Indicator]] = {}
self._indicators_for_bars: dict[BarType, list[Indicator]] = {}

self._pyo3_conversion_types = set()

# Configuration
self.config = config

Expand Down Expand Up @@ -382,13 +384,13 @@ cdef class Actor(Component):
"""
# Optionally override in subclass

cpdef void on_order_book_deltas(self, OrderBookDeltas deltas):
cpdef void on_order_book_deltas(self, deltas):
"""
Actions to be performed when running and receives order book deltas.
Parameters
----------
deltas : OrderBookDeltas
deltas : OrderBookDeltas or nautilus_pyo3.OrderBookDeltas
The order book deltas received.
Warnings
Expand Down Expand Up @@ -1185,6 +1187,7 @@ cdef class Actor(Component):
dict kwargs = None,
ClientId client_id = None,
bint managed = True,
bint pyo3_conversion = False,
):
"""
Subscribe to the order book data stream, being a snapshot then deltas
Expand All @@ -1205,11 +1208,17 @@ cdef class Actor(Component):
If ``None`` then will be inferred from the venue in the instrument ID.
managed : bool, default True
If an order book should be managed by the data engine based on the subscribed feed.
pyo3_conversion : bool, default False
If received deltas should be converted to `nautilus_pyo3.OrderBookDeltas`
prior to being passed to the `on_order_book_deltas` handler.
"""
Condition.not_none(instrument_id, "instrument_id")
Condition.true(self.trader_id is not None, "The actor has not been registered")

if pyo3_conversion:
self._pyo3_conversion_types.add(OrderBookDeltas)

self._msgbus.subscribe(
topic=f"data.book.deltas"
f".{instrument_id.venue}"
Expand Down Expand Up @@ -2396,15 +2405,17 @@ cdef class Actor(Component):
for i in range(length):
self.handle_instrument(instruments[i])

cpdef void handle_order_book_deltas(self, OrderBookDeltas deltas):
cpdef void handle_order_book_deltas(self, deltas):
"""
Handle the given order book deltas.
Passes to `on_order_book_delta` if state is ``RUNNING``.
Passes to `on_order_book_deltas` if state is ``RUNNING``.
The `deltas` will be `nautilus_pyo3.OrderBookDeltas` if the
pyo3_conversion flag was set for the subscription.
Parameters
----------
deltas : OrderBookDeltas
deltas : OrderBookDeltas or nautilus_pyo3.OrderBookDeltas
The order book deltas received.
Warnings
Expand All @@ -2414,6 +2425,9 @@ cdef class Actor(Component):
"""
Condition.not_none(deltas, "deltas")

if OrderBookDeltas in self._pyo3_conversion_types:
deltas = deltas.to_pyo3()

if self._fsm.state == ComponentState.RUNNING:
try:
self.on_order_book_deltas(deltas)
Expand Down
10 changes: 3 additions & 7 deletions nautilus_trader/examples/strategies/orderbook_imbalance_rust.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@
from nautilus_trader.core.nautilus_pyo3 import OrderBookMbp
from nautilus_trader.core.rust.common import LogColor
from nautilus_trader.model.book import OrderBook
from nautilus_trader.model.data import OrderBookDelta
from nautilus_trader.model.data import OrderBookDeltas
from nautilus_trader.model.data import QuoteTick
from nautilus_trader.model.enums import BookType
from nautilus_trader.model.enums import OrderSide
Expand Down Expand Up @@ -138,18 +136,16 @@ def on_start(self) -> None:
self.instrument.id,
self.book_type,
managed=False, # <-- Manually applying deltas to book
pyo3_conversion=True, # <--- Will automatically convert to pyo3 objects
)

self._last_trigger_timestamp = self.clock.utc_now()

def on_order_book_deltas(self, deltas: OrderBookDeltas) -> None:
def on_order_book_deltas(self, pyo3_deltas: nautilus_pyo3.OrderBookDeltas) -> None:
"""
Actions to be performed when order book deltas are received.
"""
# Convert to pyo3 objects (the efficiency of this can improve)
pyo3_deltas = OrderBookDelta.to_pyo3_list(deltas.deltas)
for pyo3_delta in pyo3_deltas:
self.book.apply_delta(pyo3_delta)
self.book.apply_deltas(pyo3_deltas)
self.imbalance.handle_book_mbp(self.book)
self.check_trigger()

Expand Down
4 changes: 3 additions & 1 deletion nautilus_trader/model/data.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ cpdef list capsule_to_list(capsule)
cpdef Data capsule_to_data(capsule)

cdef inline void capsule_destructor(object capsule):
cdef CVec* cvec = <CVec*>PyCapsule_GetPointer(capsule, NULL)
cdef CVec *cvec = <CVec *>PyCapsule_GetPointer(capsule, NULL)
PyMem_Free(cvec[0].ptr) # de-allocate buffer
PyMem_Free(cvec) # de-allocate cvec

Expand Down Expand Up @@ -244,6 +244,8 @@ cdef class OrderBookDeltas(Data):
@staticmethod
cdef dict to_dict_c(OrderBookDeltas obj)

cpdef to_pyo3(self)


cdef class OrderBookDepth10(Data):
cdef OrderBookDepth10_t _mem
Expand Down
Loading

0 comments on commit 3d54cec

Please sign in to comment.