#![allow(unsafe_code)]
use std::marker::PhantomData;
use nautilus_common::{signal::Signal, timer::TimeEvent};
use nautilus_model::{
data::{
Bar, FundingRateUpdate, IndexPriceUpdate, InstrumentClose, InstrumentStatus,
MarkPriceUpdate, OptionChainSlice, OptionGreeks, OrderBookDelta, OrderBookDeltas,
OrderBookDepth10, QuoteTick, TradeTick,
},
events::{OrderCanceled, OrderFilled},
instruments::InstrumentAny,
orderbook::OrderBook,
};
use crate::{
boundary::{BorrowedStr, PluginError, PluginErrorCode, PluginResult, Slice},
host::{HostContext, HostVTable},
normalize::BoundaryNormalize,
panic::{guard, guard_infallible},
surfaces::{
book::{OrderBookDeltasHandle, OrderBookHandle},
custom_data::PluginCustomDataRef,
instrument::InstrumentAnyHandle,
option_chain::OptionChainSliceHandle,
},
};
#[repr(C)]
pub struct PluginActorHandle {
_opaque: [u8; 0],
}
#[repr(C)]
pub struct ActorVTable {
pub create: Option<
unsafe extern "C" fn(
host: *const HostVTable,
ctx: *const HostContext,
config_json: BorrowedStr<'_>,
) -> *mut PluginActorHandle,
>,
pub drop_handle: Option<unsafe extern "C" fn(handle: *mut PluginActorHandle)>,
pub type_name: Option<unsafe extern "C" fn() -> BorrowedStr<'static>>,
pub on_start: Option<unsafe extern "C" fn(handle: *mut PluginActorHandle) -> PluginResult<()>>,
pub on_stop: Option<unsafe extern "C" fn(handle: *mut PluginActorHandle) -> PluginResult<()>>,
pub on_resume: Option<unsafe extern "C" fn(handle: *mut PluginActorHandle) -> PluginResult<()>>,
pub on_reset: Option<unsafe extern "C" fn(handle: *mut PluginActorHandle) -> PluginResult<()>>,
pub on_dispose:
Option<unsafe extern "C" fn(handle: *mut PluginActorHandle) -> PluginResult<()>>,
pub on_degrade:
Option<unsafe extern "C" fn(handle: *mut PluginActorHandle) -> PluginResult<()>>,
pub on_fault: Option<unsafe extern "C" fn(handle: *mut PluginActorHandle) -> PluginResult<()>>,
pub on_time_event: Option<
unsafe extern "C" fn(
handle: *mut PluginActorHandle,
event: *const TimeEvent,
) -> PluginResult<()>,
>,
pub on_data: Option<
unsafe extern "C" fn(
handle: *mut PluginActorHandle,
data: PluginCustomDataRef,
) -> PluginResult<()>,
>,
pub on_instrument: Option<
unsafe extern "C" fn(
handle: *mut PluginActorHandle,
instrument: *const InstrumentAnyHandle,
) -> PluginResult<()>,
>,
pub on_book_deltas: Option<
unsafe extern "C" fn(
handle: *mut PluginActorHandle,
deltas: *const OrderBookDeltasHandle,
) -> PluginResult<()>,
>,
pub on_book: Option<
unsafe extern "C" fn(
handle: *mut PluginActorHandle,
book: *const OrderBookHandle,
) -> PluginResult<()>,
>,
pub on_quote: Option<
unsafe extern "C" fn(
handle: *mut PluginActorHandle,
quote: *const QuoteTick,
) -> PluginResult<()>,
>,
pub on_trade: Option<
unsafe extern "C" fn(
handle: *mut PluginActorHandle,
trade: *const TradeTick,
) -> PluginResult<()>,
>,
pub on_bar: Option<
unsafe extern "C" fn(handle: *mut PluginActorHandle, bar: *const Bar) -> PluginResult<()>,
>,
pub on_mark_price: Option<
unsafe extern "C" fn(
handle: *mut PluginActorHandle,
mark_price: *const MarkPriceUpdate,
) -> PluginResult<()>,
>,
pub on_index_price: Option<
unsafe extern "C" fn(
handle: *mut PluginActorHandle,
index_price: *const IndexPriceUpdate,
) -> PluginResult<()>,
>,
pub on_funding_rate: Option<
unsafe extern "C" fn(
handle: *mut PluginActorHandle,
funding_rate: *const FundingRateUpdate,
) -> PluginResult<()>,
>,
pub on_option_greeks: Option<
unsafe extern "C" fn(
handle: *mut PluginActorHandle,
greeks: *const OptionGreeks,
) -> PluginResult<()>,
>,
pub on_option_chain: Option<
unsafe extern "C" fn(
handle: *mut PluginActorHandle,
chain: *const OptionChainSliceHandle,
) -> PluginResult<()>,
>,
pub on_instrument_status: Option<
unsafe extern "C" fn(
handle: *mut PluginActorHandle,
status: *const InstrumentStatus,
) -> PluginResult<()>,
>,
pub on_instrument_close: Option<
unsafe extern "C" fn(
handle: *mut PluginActorHandle,
close: *const InstrumentClose,
) -> PluginResult<()>,
>,
pub on_order_filled: Option<
unsafe extern "C" fn(
handle: *mut PluginActorHandle,
event: *const OrderFilled,
) -> PluginResult<()>,
>,
pub on_order_canceled: Option<
unsafe extern "C" fn(
handle: *mut PluginActorHandle,
event: *const OrderCanceled,
) -> PluginResult<()>,
>,
pub on_signal: Option<
unsafe extern "C" fn(
handle: *mut PluginActorHandle,
signal: *const Signal,
) -> PluginResult<()>,
>,
pub on_historical_book_deltas: Option<
unsafe extern "C" fn(
handle: *mut PluginActorHandle,
deltas: Slice<'_, OrderBookDelta>,
) -> PluginResult<()>,
>,
pub on_historical_book_depth: Option<
unsafe extern "C" fn(
handle: *mut PluginActorHandle,
depths: Slice<'_, OrderBookDepth10>,
) -> PluginResult<()>,
>,
pub on_historical_quotes: Option<
unsafe extern "C" fn(
handle: *mut PluginActorHandle,
quotes: Slice<'_, QuoteTick>,
) -> PluginResult<()>,
>,
pub on_historical_trades: Option<
unsafe extern "C" fn(
handle: *mut PluginActorHandle,
trades: Slice<'_, TradeTick>,
) -> PluginResult<()>,
>,
pub on_historical_bars: Option<
unsafe extern "C" fn(
handle: *mut PluginActorHandle,
bars: Slice<'_, Bar>,
) -> PluginResult<()>,
>,
pub on_historical_mark_prices: Option<
unsafe extern "C" fn(
handle: *mut PluginActorHandle,
mark_prices: Slice<'_, MarkPriceUpdate>,
) -> PluginResult<()>,
>,
pub on_historical_index_prices: Option<
unsafe extern "C" fn(
handle: *mut PluginActorHandle,
index_prices: Slice<'_, IndexPriceUpdate>,
) -> PluginResult<()>,
>,
pub on_historical_funding_rates: Option<
unsafe extern "C" fn(
handle: *mut PluginActorHandle,
funding_rates: Slice<'_, FundingRateUpdate>,
) -> PluginResult<()>,
>,
}
pub trait PluginActor: 'static + Send + Sized {
const TYPE_NAME: &'static str;
fn new(host: *const HostVTable, ctx: *const HostContext, config_json: &str) -> Self;
#[allow(unused_variables)]
fn on_start(&mut self) -> anyhow::Result<()> {
Ok(())
}
#[allow(unused_variables)]
fn on_stop(&mut self) -> anyhow::Result<()> {
Ok(())
}
#[allow(unused_variables)]
fn on_resume(&mut self) -> anyhow::Result<()> {
Ok(())
}
#[allow(unused_variables)]
fn on_reset(&mut self) -> anyhow::Result<()> {
Ok(())
}
#[allow(unused_variables)]
fn on_dispose(&mut self) -> anyhow::Result<()> {
Ok(())
}
#[allow(unused_variables)]
fn on_degrade(&mut self) -> anyhow::Result<()> {
Ok(())
}
#[allow(unused_variables)]
fn on_fault(&mut self) -> anyhow::Result<()> {
Ok(())
}
#[allow(unused_variables)]
fn on_time_event(&mut self, event: &TimeEvent) -> anyhow::Result<()> {
Ok(())
}
#[allow(unused_variables)]
fn on_data(&mut self, data: PluginCustomDataRef) -> anyhow::Result<()> {
Ok(())
}
#[allow(unused_variables)]
fn on_instrument(&mut self, instrument: &InstrumentAny) -> anyhow::Result<()> {
Ok(())
}
#[allow(unused_variables)]
fn on_book_deltas(&mut self, deltas: &OrderBookDeltas) -> anyhow::Result<()> {
Ok(())
}
#[allow(unused_variables)]
fn on_book(&mut self, book: &OrderBook) -> anyhow::Result<()> {
Ok(())
}
#[allow(unused_variables)]
fn on_quote(&mut self, quote: &QuoteTick) -> anyhow::Result<()> {
Ok(())
}
#[allow(unused_variables)]
fn on_trade(&mut self, trade: &TradeTick) -> anyhow::Result<()> {
Ok(())
}
#[allow(unused_variables)]
fn on_bar(&mut self, bar: &Bar) -> anyhow::Result<()> {
Ok(())
}
#[allow(unused_variables)]
fn on_mark_price(&mut self, mark_price: &MarkPriceUpdate) -> anyhow::Result<()> {
Ok(())
}
#[allow(unused_variables)]
fn on_index_price(&mut self, index_price: &IndexPriceUpdate) -> anyhow::Result<()> {
Ok(())
}
#[allow(unused_variables)]
fn on_funding_rate(&mut self, funding_rate: &FundingRateUpdate) -> anyhow::Result<()> {
Ok(())
}
#[allow(unused_variables)]
fn on_option_greeks(&mut self, greeks: &OptionGreeks) -> anyhow::Result<()> {
Ok(())
}
#[allow(unused_variables)]
fn on_option_chain(&mut self, chain: &OptionChainSlice) -> anyhow::Result<()> {
Ok(())
}
#[allow(unused_variables)]
fn on_instrument_status(&mut self, status: &InstrumentStatus) -> anyhow::Result<()> {
Ok(())
}
#[allow(unused_variables)]
fn on_instrument_close(&mut self, close: &InstrumentClose) -> anyhow::Result<()> {
Ok(())
}
#[allow(unused_variables)]
fn on_order_filled(&mut self, event: &OrderFilled) -> anyhow::Result<()> {
Ok(())
}
#[allow(unused_variables)]
fn on_order_canceled(&mut self, event: &OrderCanceled) -> anyhow::Result<()> {
Ok(())
}
#[allow(unused_variables)]
fn on_signal(&mut self, signal: &Signal) -> anyhow::Result<()> {
Ok(())
}
#[allow(unused_variables)]
fn on_historical_book_deltas(&mut self, deltas: &[OrderBookDelta]) -> anyhow::Result<()> {
Ok(())
}
#[allow(unused_variables)]
fn on_historical_book_depth(&mut self, depths: &[OrderBookDepth10]) -> anyhow::Result<()> {
Ok(())
}
#[allow(unused_variables)]
fn on_historical_quotes(&mut self, quotes: &[QuoteTick]) -> anyhow::Result<()> {
Ok(())
}
#[allow(unused_variables)]
fn on_historical_trades(&mut self, trades: &[TradeTick]) -> anyhow::Result<()> {
Ok(())
}
#[allow(unused_variables)]
fn on_historical_bars(&mut self, bars: &[Bar]) -> anyhow::Result<()> {
Ok(())
}
#[allow(unused_variables)]
fn on_historical_mark_prices(&mut self, mark_prices: &[MarkPriceUpdate]) -> anyhow::Result<()> {
Ok(())
}
#[allow(unused_variables)]
fn on_historical_index_prices(
&mut self,
index_prices: &[IndexPriceUpdate],
) -> anyhow::Result<()> {
Ok(())
}
#[allow(unused_variables)]
fn on_historical_funding_rates(
&mut self,
funding_rates: &[FundingRateUpdate],
) -> anyhow::Result<()> {
Ok(())
}
}
#[must_use]
pub fn actor_vtable<T>() -> *const ActorVTable
where
T: PluginActor,
{
&VTableTag::<T>::VTABLE
}
struct VTableTag<T>(PhantomData<T>);
impl<T> VTableTag<T>
where
T: PluginActor,
{
const VTABLE: ActorVTable = ActorVTable {
create: Some(create_thunk::<T>),
drop_handle: Some(drop_handle_thunk::<T>),
type_name: Some(type_name_thunk::<T>),
on_start: Some(on_start_thunk::<T>),
on_stop: Some(on_stop_thunk::<T>),
on_resume: Some(on_resume_thunk::<T>),
on_reset: Some(on_reset_thunk::<T>),
on_dispose: Some(on_dispose_thunk::<T>),
on_degrade: Some(on_degrade_thunk::<T>),
on_fault: Some(on_fault_thunk::<T>),
on_time_event: Some(on_time_event_thunk::<T>),
on_data: Some(on_data_thunk::<T>),
on_instrument: Some(on_instrument_thunk::<T>),
on_book_deltas: Some(on_book_deltas_thunk::<T>),
on_book: Some(on_book_thunk::<T>),
on_quote: Some(on_quote_thunk::<T>),
on_trade: Some(on_trade_thunk::<T>),
on_bar: Some(on_bar_thunk::<T>),
on_mark_price: Some(on_mark_price_thunk::<T>),
on_index_price: Some(on_index_price_thunk::<T>),
on_funding_rate: Some(on_funding_rate_thunk::<T>),
on_option_greeks: Some(on_option_greeks_thunk::<T>),
on_option_chain: Some(on_option_chain_thunk::<T>),
on_instrument_status: Some(on_instrument_status_thunk::<T>),
on_instrument_close: Some(on_instrument_close_thunk::<T>),
on_order_filled: Some(on_order_filled_thunk::<T>),
on_order_canceled: Some(on_order_canceled_thunk::<T>),
on_signal: Some(on_signal_thunk::<T>),
on_historical_book_deltas: Some(on_historical_book_deltas_thunk::<T>),
on_historical_book_depth: Some(on_historical_book_depth_thunk::<T>),
on_historical_quotes: Some(on_historical_quotes_thunk::<T>),
on_historical_trades: Some(on_historical_trades_thunk::<T>),
on_historical_bars: Some(on_historical_bars_thunk::<T>),
on_historical_mark_prices: Some(on_historical_mark_prices_thunk::<T>),
on_historical_index_prices: Some(on_historical_index_prices_thunk::<T>),
on_historical_funding_rates: Some(on_historical_funding_rates_thunk::<T>),
};
}
unsafe extern "C" fn create_thunk<T: PluginActor>(
host: *const HostVTable,
ctx: *const HostContext,
config_json: BorrowedStr<'_>,
) -> *mut PluginActorHandle {
guard_infallible("actor::create", || {
let cfg = unsafe { config_json.as_str() };
Box::into_raw(Box::new(T::new(host, ctx, cfg))).cast::<PluginActorHandle>()
})
}
unsafe extern "C" fn drop_handle_thunk<T: PluginActor>(handle: *mut PluginActorHandle) {
if handle.is_null() {
return;
}
guard_infallible("actor::drop", || {
unsafe {
drop(Box::from_raw(handle.cast::<T>()));
}
});
}
unsafe extern "C" fn type_name_thunk<T: PluginActor>() -> BorrowedStr<'static> {
BorrowedStr::from_str(T::TYPE_NAME)
}
fn handle_as_mut<'a, T: PluginActor>(handle: *mut PluginActorHandle) -> &'a mut T {
unsafe { &mut *handle.cast::<T>() }
}
fn ok_or_err<E: ::core::fmt::Display>(r: Result<(), E>) -> Result<(), PluginError> {
r.map_err(|e| PluginError::new(PluginErrorCode::Generic, e.to_string()))
}
macro_rules! lifecycle_thunk {
($name:ident, $method:ident) => {
unsafe extern "C" fn $name<T: PluginActor>(
handle: *mut PluginActorHandle,
) -> PluginResult<()> {
guard(|| {
let actor = handle_as_mut::<T>(handle);
ok_or_err(actor.$method())
})
}
};
}
lifecycle_thunk!(on_start_thunk, on_start);
lifecycle_thunk!(on_stop_thunk, on_stop);
lifecycle_thunk!(on_resume_thunk, on_resume);
lifecycle_thunk!(on_reset_thunk, on_reset);
lifecycle_thunk!(on_dispose_thunk, on_dispose);
lifecycle_thunk!(on_degrade_thunk, on_degrade);
lifecycle_thunk!(on_fault_thunk, on_fault);
macro_rules! event_thunk {
($name:ident, $method:ident, $ty:ty) => {
unsafe extern "C" fn $name<T: PluginActor>(
handle: *mut PluginActorHandle,
value: *const $ty,
) -> PluginResult<()> {
guard(|| {
let v = unsafe { &*value }.boundary_normalized();
let actor = handle_as_mut::<T>(handle);
ok_or_err(actor.$method(&v))
})
}
};
}
event_thunk!(on_time_event_thunk, on_time_event, TimeEvent);
unsafe extern "C" fn on_data_thunk<T: PluginActor>(
handle: *mut PluginActorHandle,
data: PluginCustomDataRef,
) -> PluginResult<()> {
guard(|| {
let actor = handle_as_mut::<T>(handle);
ok_or_err(actor.on_data(data))
})
}
unsafe extern "C" fn on_instrument_thunk<T: PluginActor>(
handle: *mut PluginActorHandle,
instrument: *const InstrumentAnyHandle,
) -> PluginResult<()> {
guard(|| {
let v: InstrumentAny = unsafe { (*instrument).instrument() }.boundary_normalized();
let actor = handle_as_mut::<T>(handle);
ok_or_err(actor.on_instrument(&v))
})
}
unsafe extern "C" fn on_book_deltas_thunk<T: PluginActor>(
handle: *mut PluginActorHandle,
deltas: *const OrderBookDeltasHandle,
) -> PluginResult<()> {
guard(|| {
let v: OrderBookDeltas = unsafe { (*deltas).deltas() }.boundary_normalized();
let actor = handle_as_mut::<T>(handle);
ok_or_err(actor.on_book_deltas(&v))
})
}
unsafe extern "C" fn on_book_thunk<T: PluginActor>(
handle: *mut PluginActorHandle,
book: *const OrderBookHandle,
) -> PluginResult<()> {
guard(|| {
let v: OrderBook = unsafe { (*book).book() }.boundary_normalized();
let actor = handle_as_mut::<T>(handle);
ok_or_err(actor.on_book(&v))
})
}
event_thunk!(on_quote_thunk, on_quote, QuoteTick);
event_thunk!(on_trade_thunk, on_trade, TradeTick);
event_thunk!(on_bar_thunk, on_bar, Bar);
event_thunk!(on_mark_price_thunk, on_mark_price, MarkPriceUpdate);
event_thunk!(on_index_price_thunk, on_index_price, IndexPriceUpdate);
event_thunk!(on_funding_rate_thunk, on_funding_rate, FundingRateUpdate);
event_thunk!(on_option_greeks_thunk, on_option_greeks, OptionGreeks);
unsafe extern "C" fn on_option_chain_thunk<T: PluginActor>(
handle: *mut PluginActorHandle,
chain: *const OptionChainSliceHandle,
) -> PluginResult<()> {
guard(|| {
let v: OptionChainSlice = unsafe { (*chain).chain() }.boundary_normalized();
let actor = handle_as_mut::<T>(handle);
ok_or_err(actor.on_option_chain(&v))
})
}
event_thunk!(
on_instrument_status_thunk,
on_instrument_status,
InstrumentStatus
);
event_thunk!(
on_instrument_close_thunk,
on_instrument_close,
InstrumentClose
);
event_thunk!(on_order_filled_thunk, on_order_filled, OrderFilled);
event_thunk!(on_order_canceled_thunk, on_order_canceled, OrderCanceled);
event_thunk!(on_signal_thunk, on_signal, Signal);
macro_rules! slice_thunk {
($name:ident, $method:ident, $ty:ty) => {
unsafe extern "C" fn $name<T: PluginActor>(
handle: *mut PluginActorHandle,
values: Slice<'_, $ty>,
) -> PluginResult<()> {
guard(|| {
let v: Vec<$ty> = unsafe { values.as_slice() }
.iter()
.map(BoundaryNormalize::boundary_normalized)
.collect();
let actor = handle_as_mut::<T>(handle);
ok_or_err(actor.$method(&v))
})
}
};
}
slice_thunk!(
on_historical_book_deltas_thunk,
on_historical_book_deltas,
OrderBookDelta
);
slice_thunk!(
on_historical_book_depth_thunk,
on_historical_book_depth,
OrderBookDepth10
);
slice_thunk!(on_historical_quotes_thunk, on_historical_quotes, QuoteTick);
slice_thunk!(on_historical_trades_thunk, on_historical_trades, TradeTick);
slice_thunk!(on_historical_bars_thunk, on_historical_bars, Bar);
slice_thunk!(
on_historical_mark_prices_thunk,
on_historical_mark_prices,
MarkPriceUpdate
);
slice_thunk!(
on_historical_index_prices_thunk,
on_historical_index_prices,
IndexPriceUpdate
);
slice_thunk!(
on_historical_funding_rates_thunk,
on_historical_funding_rates,
FundingRateUpdate
);