1#![allow(unsafe_code)]
58
59use std::marker::PhantomData;
60
61use nautilus_common::{signal::Signal, timer::TimeEvent};
62use nautilus_model::{
63 data::{
64 Bar, FundingRateUpdate, IndexPriceUpdate, InstrumentClose, InstrumentStatus,
65 MarkPriceUpdate, OptionChainSlice, OptionGreeks, OrderBookDelta, OrderBookDeltas,
66 OrderBookDepth10, QuoteTick, TradeTick,
67 },
68 events::{OrderCanceled, OrderFilled},
69 instruments::InstrumentAny,
70 orderbook::OrderBook,
71};
72
73use crate::{
74 boundary::{BorrowedStr, PluginError, PluginErrorCode, PluginResult, Slice},
75 host::{HostContext, HostVTable},
76 normalize::BoundaryNormalize,
77 panic::{guard, guard_infallible},
78 surfaces::{
79 book::{OrderBookDeltasHandle, OrderBookHandle},
80 custom_data::PluginCustomDataRef,
81 instrument::InstrumentAnyHandle,
82 option_chain::OptionChainSliceHandle,
83 },
84};
85
86#[repr(C)]
91pub struct PluginActorHandle {
92 _opaque: [u8; 0],
93}
94
95#[repr(C)]
110pub struct ActorVTable {
111 pub create: Option<
118 unsafe extern "C" fn(
119 host: *const HostVTable,
120 ctx: *const HostContext,
121 config_json: BorrowedStr<'_>,
122 ) -> *mut PluginActorHandle,
123 >,
124
125 pub drop_handle: Option<unsafe extern "C" fn(handle: *mut PluginActorHandle)>,
127
128 pub type_name: Option<unsafe extern "C" fn() -> BorrowedStr<'static>>,
130
131 pub on_start: Option<unsafe extern "C" fn(handle: *mut PluginActorHandle) -> PluginResult<()>>,
132 pub on_stop: Option<unsafe extern "C" fn(handle: *mut PluginActorHandle) -> PluginResult<()>>,
133 pub on_resume: Option<unsafe extern "C" fn(handle: *mut PluginActorHandle) -> PluginResult<()>>,
134 pub on_reset: Option<unsafe extern "C" fn(handle: *mut PluginActorHandle) -> PluginResult<()>>,
135 pub on_dispose:
136 Option<unsafe extern "C" fn(handle: *mut PluginActorHandle) -> PluginResult<()>>,
137 pub on_degrade:
138 Option<unsafe extern "C" fn(handle: *mut PluginActorHandle) -> PluginResult<()>>,
139 pub on_fault: Option<unsafe extern "C" fn(handle: *mut PluginActorHandle) -> PluginResult<()>>,
140
141 pub on_time_event: Option<
142 unsafe extern "C" fn(
143 handle: *mut PluginActorHandle,
144 event: *const TimeEvent,
145 ) -> PluginResult<()>,
146 >,
147 pub on_data: Option<
148 unsafe extern "C" fn(
149 handle: *mut PluginActorHandle,
150 data: PluginCustomDataRef,
151 ) -> PluginResult<()>,
152 >,
153
154 pub on_instrument: Option<
155 unsafe extern "C" fn(
156 handle: *mut PluginActorHandle,
157 instrument: *const InstrumentAnyHandle,
158 ) -> PluginResult<()>,
159 >,
160 pub on_book_deltas: Option<
161 unsafe extern "C" fn(
162 handle: *mut PluginActorHandle,
163 deltas: *const OrderBookDeltasHandle,
164 ) -> PluginResult<()>,
165 >,
166 pub on_book: Option<
167 unsafe extern "C" fn(
168 handle: *mut PluginActorHandle,
169 book: *const OrderBookHandle,
170 ) -> PluginResult<()>,
171 >,
172 pub on_quote: Option<
173 unsafe extern "C" fn(
174 handle: *mut PluginActorHandle,
175 quote: *const QuoteTick,
176 ) -> PluginResult<()>,
177 >,
178 pub on_trade: Option<
179 unsafe extern "C" fn(
180 handle: *mut PluginActorHandle,
181 trade: *const TradeTick,
182 ) -> PluginResult<()>,
183 >,
184 pub on_bar: Option<
185 unsafe extern "C" fn(handle: *mut PluginActorHandle, bar: *const Bar) -> PluginResult<()>,
186 >,
187 pub on_mark_price: Option<
188 unsafe extern "C" fn(
189 handle: *mut PluginActorHandle,
190 mark_price: *const MarkPriceUpdate,
191 ) -> PluginResult<()>,
192 >,
193 pub on_index_price: Option<
194 unsafe extern "C" fn(
195 handle: *mut PluginActorHandle,
196 index_price: *const IndexPriceUpdate,
197 ) -> PluginResult<()>,
198 >,
199 pub on_funding_rate: Option<
200 unsafe extern "C" fn(
201 handle: *mut PluginActorHandle,
202 funding_rate: *const FundingRateUpdate,
203 ) -> PluginResult<()>,
204 >,
205 pub on_option_greeks: Option<
206 unsafe extern "C" fn(
207 handle: *mut PluginActorHandle,
208 greeks: *const OptionGreeks,
209 ) -> PluginResult<()>,
210 >,
211 pub on_option_chain: Option<
212 unsafe extern "C" fn(
213 handle: *mut PluginActorHandle,
214 chain: *const OptionChainSliceHandle,
215 ) -> PluginResult<()>,
216 >,
217 pub on_instrument_status: Option<
218 unsafe extern "C" fn(
219 handle: *mut PluginActorHandle,
220 status: *const InstrumentStatus,
221 ) -> PluginResult<()>,
222 >,
223 pub on_instrument_close: Option<
224 unsafe extern "C" fn(
225 handle: *mut PluginActorHandle,
226 close: *const InstrumentClose,
227 ) -> PluginResult<()>,
228 >,
229
230 pub on_order_filled: Option<
231 unsafe extern "C" fn(
232 handle: *mut PluginActorHandle,
233 event: *const OrderFilled,
234 ) -> PluginResult<()>,
235 >,
236 pub on_order_canceled: Option<
237 unsafe extern "C" fn(
238 handle: *mut PluginActorHandle,
239 event: *const OrderCanceled,
240 ) -> PluginResult<()>,
241 >,
242
243 pub on_signal: Option<
244 unsafe extern "C" fn(
245 handle: *mut PluginActorHandle,
246 signal: *const Signal,
247 ) -> PluginResult<()>,
248 >,
249
250 pub on_historical_book_deltas: Option<
251 unsafe extern "C" fn(
252 handle: *mut PluginActorHandle,
253 deltas: Slice<'_, OrderBookDelta>,
254 ) -> PluginResult<()>,
255 >,
256 pub on_historical_book_depth: Option<
257 unsafe extern "C" fn(
258 handle: *mut PluginActorHandle,
259 depths: Slice<'_, OrderBookDepth10>,
260 ) -> PluginResult<()>,
261 >,
262 pub on_historical_quotes: Option<
263 unsafe extern "C" fn(
264 handle: *mut PluginActorHandle,
265 quotes: Slice<'_, QuoteTick>,
266 ) -> PluginResult<()>,
267 >,
268 pub on_historical_trades: Option<
269 unsafe extern "C" fn(
270 handle: *mut PluginActorHandle,
271 trades: Slice<'_, TradeTick>,
272 ) -> PluginResult<()>,
273 >,
274 pub on_historical_bars: Option<
275 unsafe extern "C" fn(
276 handle: *mut PluginActorHandle,
277 bars: Slice<'_, Bar>,
278 ) -> PluginResult<()>,
279 >,
280 pub on_historical_mark_prices: Option<
281 unsafe extern "C" fn(
282 handle: *mut PluginActorHandle,
283 mark_prices: Slice<'_, MarkPriceUpdate>,
284 ) -> PluginResult<()>,
285 >,
286 pub on_historical_index_prices: Option<
287 unsafe extern "C" fn(
288 handle: *mut PluginActorHandle,
289 index_prices: Slice<'_, IndexPriceUpdate>,
290 ) -> PluginResult<()>,
291 >,
292 pub on_historical_funding_rates: Option<
293 unsafe extern "C" fn(
294 handle: *mut PluginActorHandle,
295 funding_rates: Slice<'_, FundingRateUpdate>,
296 ) -> PluginResult<()>,
297 >,
298}
299
300pub trait PluginActor: 'static + Send + Sized {
306 const TYPE_NAME: &'static str;
308
309 fn new(host: *const HostVTable, ctx: *const HostContext, config_json: &str) -> Self;
316
317 #[allow(unused_variables)]
318 fn on_start(&mut self) -> anyhow::Result<()> {
319 Ok(())
320 }
321
322 #[allow(unused_variables)]
323 fn on_stop(&mut self) -> anyhow::Result<()> {
324 Ok(())
325 }
326
327 #[allow(unused_variables)]
328 fn on_resume(&mut self) -> anyhow::Result<()> {
329 Ok(())
330 }
331
332 #[allow(unused_variables)]
333 fn on_reset(&mut self) -> anyhow::Result<()> {
334 Ok(())
335 }
336
337 #[allow(unused_variables)]
338 fn on_dispose(&mut self) -> anyhow::Result<()> {
339 Ok(())
340 }
341
342 #[allow(unused_variables)]
343 fn on_degrade(&mut self) -> anyhow::Result<()> {
344 Ok(())
345 }
346
347 #[allow(unused_variables)]
348 fn on_fault(&mut self) -> anyhow::Result<()> {
349 Ok(())
350 }
351
352 #[allow(unused_variables)]
353 fn on_time_event(&mut self, event: &TimeEvent) -> anyhow::Result<()> {
354 Ok(())
355 }
356
357 #[allow(unused_variables)]
358 fn on_data(&mut self, data: PluginCustomDataRef) -> anyhow::Result<()> {
359 Ok(())
360 }
361
362 #[allow(unused_variables)]
363 fn on_instrument(&mut self, instrument: &InstrumentAny) -> anyhow::Result<()> {
364 Ok(())
365 }
366
367 #[allow(unused_variables)]
368 fn on_book_deltas(&mut self, deltas: &OrderBookDeltas) -> anyhow::Result<()> {
369 Ok(())
370 }
371
372 #[allow(unused_variables)]
373 fn on_book(&mut self, book: &OrderBook) -> anyhow::Result<()> {
374 Ok(())
375 }
376
377 #[allow(unused_variables)]
378 fn on_quote(&mut self, quote: &QuoteTick) -> anyhow::Result<()> {
379 Ok(())
380 }
381
382 #[allow(unused_variables)]
383 fn on_trade(&mut self, trade: &TradeTick) -> anyhow::Result<()> {
384 Ok(())
385 }
386
387 #[allow(unused_variables)]
388 fn on_bar(&mut self, bar: &Bar) -> anyhow::Result<()> {
389 Ok(())
390 }
391
392 #[allow(unused_variables)]
393 fn on_mark_price(&mut self, mark_price: &MarkPriceUpdate) -> anyhow::Result<()> {
394 Ok(())
395 }
396
397 #[allow(unused_variables)]
398 fn on_index_price(&mut self, index_price: &IndexPriceUpdate) -> anyhow::Result<()> {
399 Ok(())
400 }
401
402 #[allow(unused_variables)]
403 fn on_funding_rate(&mut self, funding_rate: &FundingRateUpdate) -> anyhow::Result<()> {
404 Ok(())
405 }
406
407 #[allow(unused_variables)]
408 fn on_option_greeks(&mut self, greeks: &OptionGreeks) -> anyhow::Result<()> {
409 Ok(())
410 }
411
412 #[allow(unused_variables)]
413 fn on_option_chain(&mut self, chain: &OptionChainSlice) -> anyhow::Result<()> {
414 Ok(())
415 }
416
417 #[allow(unused_variables)]
418 fn on_instrument_status(&mut self, status: &InstrumentStatus) -> anyhow::Result<()> {
419 Ok(())
420 }
421
422 #[allow(unused_variables)]
423 fn on_instrument_close(&mut self, close: &InstrumentClose) -> anyhow::Result<()> {
424 Ok(())
425 }
426
427 #[allow(unused_variables)]
428 fn on_order_filled(&mut self, event: &OrderFilled) -> anyhow::Result<()> {
429 Ok(())
430 }
431
432 #[allow(unused_variables)]
433 fn on_order_canceled(&mut self, event: &OrderCanceled) -> anyhow::Result<()> {
434 Ok(())
435 }
436
437 #[allow(unused_variables)]
438 fn on_signal(&mut self, signal: &Signal) -> anyhow::Result<()> {
439 Ok(())
440 }
441
442 #[allow(unused_variables)]
443 fn on_historical_book_deltas(&mut self, deltas: &[OrderBookDelta]) -> anyhow::Result<()> {
444 Ok(())
445 }
446
447 #[allow(unused_variables)]
448 fn on_historical_book_depth(&mut self, depths: &[OrderBookDepth10]) -> anyhow::Result<()> {
449 Ok(())
450 }
451
452 #[allow(unused_variables)]
453 fn on_historical_quotes(&mut self, quotes: &[QuoteTick]) -> anyhow::Result<()> {
454 Ok(())
455 }
456
457 #[allow(unused_variables)]
458 fn on_historical_trades(&mut self, trades: &[TradeTick]) -> anyhow::Result<()> {
459 Ok(())
460 }
461
462 #[allow(unused_variables)]
463 fn on_historical_bars(&mut self, bars: &[Bar]) -> anyhow::Result<()> {
464 Ok(())
465 }
466
467 #[allow(unused_variables)]
468 fn on_historical_mark_prices(&mut self, mark_prices: &[MarkPriceUpdate]) -> anyhow::Result<()> {
469 Ok(())
470 }
471
472 #[allow(unused_variables)]
473 fn on_historical_index_prices(
474 &mut self,
475 index_prices: &[IndexPriceUpdate],
476 ) -> anyhow::Result<()> {
477 Ok(())
478 }
479
480 #[allow(unused_variables)]
481 fn on_historical_funding_rates(
482 &mut self,
483 funding_rates: &[FundingRateUpdate],
484 ) -> anyhow::Result<()> {
485 Ok(())
486 }
487}
488
489#[must_use]
494pub fn actor_vtable<T>() -> *const ActorVTable
495where
496 T: PluginActor,
497{
498 &VTableTag::<T>::VTABLE
499}
500
501struct VTableTag<T>(PhantomData<T>);
502
503impl<T> VTableTag<T>
504where
505 T: PluginActor,
506{
507 const VTABLE: ActorVTable = ActorVTable {
508 create: Some(create_thunk::<T>),
509 drop_handle: Some(drop_handle_thunk::<T>),
510 type_name: Some(type_name_thunk::<T>),
511 on_start: Some(on_start_thunk::<T>),
512 on_stop: Some(on_stop_thunk::<T>),
513 on_resume: Some(on_resume_thunk::<T>),
514 on_reset: Some(on_reset_thunk::<T>),
515 on_dispose: Some(on_dispose_thunk::<T>),
516 on_degrade: Some(on_degrade_thunk::<T>),
517 on_fault: Some(on_fault_thunk::<T>),
518 on_time_event: Some(on_time_event_thunk::<T>),
519 on_data: Some(on_data_thunk::<T>),
520 on_instrument: Some(on_instrument_thunk::<T>),
521 on_book_deltas: Some(on_book_deltas_thunk::<T>),
522 on_book: Some(on_book_thunk::<T>),
523 on_quote: Some(on_quote_thunk::<T>),
524 on_trade: Some(on_trade_thunk::<T>),
525 on_bar: Some(on_bar_thunk::<T>),
526 on_mark_price: Some(on_mark_price_thunk::<T>),
527 on_index_price: Some(on_index_price_thunk::<T>),
528 on_funding_rate: Some(on_funding_rate_thunk::<T>),
529 on_option_greeks: Some(on_option_greeks_thunk::<T>),
530 on_option_chain: Some(on_option_chain_thunk::<T>),
531 on_instrument_status: Some(on_instrument_status_thunk::<T>),
532 on_instrument_close: Some(on_instrument_close_thunk::<T>),
533 on_order_filled: Some(on_order_filled_thunk::<T>),
534 on_order_canceled: Some(on_order_canceled_thunk::<T>),
535 on_signal: Some(on_signal_thunk::<T>),
536 on_historical_book_deltas: Some(on_historical_book_deltas_thunk::<T>),
537 on_historical_book_depth: Some(on_historical_book_depth_thunk::<T>),
538 on_historical_quotes: Some(on_historical_quotes_thunk::<T>),
539 on_historical_trades: Some(on_historical_trades_thunk::<T>),
540 on_historical_bars: Some(on_historical_bars_thunk::<T>),
541 on_historical_mark_prices: Some(on_historical_mark_prices_thunk::<T>),
542 on_historical_index_prices: Some(on_historical_index_prices_thunk::<T>),
543 on_historical_funding_rates: Some(on_historical_funding_rates_thunk::<T>),
544 };
545}
546
547unsafe extern "C" fn create_thunk<T: PluginActor>(
548 host: *const HostVTable,
549 ctx: *const HostContext,
550 config_json: BorrowedStr<'_>,
551) -> *mut PluginActorHandle {
552 guard_infallible("actor::create", || {
553 let cfg = unsafe { config_json.as_str() };
556 Box::into_raw(Box::new(T::new(host, ctx, cfg))).cast::<PluginActorHandle>()
557 })
558}
559
560unsafe extern "C" fn drop_handle_thunk<T: PluginActor>(handle: *mut PluginActorHandle) {
561 if handle.is_null() {
562 return;
563 }
564 guard_infallible("actor::drop", || {
565 unsafe {
567 drop(Box::from_raw(handle.cast::<T>()));
568 }
569 });
570}
571
572unsafe extern "C" fn type_name_thunk<T: PluginActor>() -> BorrowedStr<'static> {
573 BorrowedStr::from_str(T::TYPE_NAME)
574}
575
576fn handle_as_mut<'a, T: PluginActor>(handle: *mut PluginActorHandle) -> &'a mut T {
577 unsafe { &mut *handle.cast::<T>() }
580}
581
582fn ok_or_err<E: ::core::fmt::Display>(r: Result<(), E>) -> Result<(), PluginError> {
583 r.map_err(|e| PluginError::new(PluginErrorCode::Generic, e.to_string()))
584}
585
586macro_rules! lifecycle_thunk {
587 ($name:ident, $method:ident) => {
588 unsafe extern "C" fn $name<T: PluginActor>(
589 handle: *mut PluginActorHandle,
590 ) -> PluginResult<()> {
591 guard(|| {
592 let actor = handle_as_mut::<T>(handle);
593 ok_or_err(actor.$method())
594 })
595 }
596 };
597}
598
599lifecycle_thunk!(on_start_thunk, on_start);
600lifecycle_thunk!(on_stop_thunk, on_stop);
601lifecycle_thunk!(on_resume_thunk, on_resume);
602lifecycle_thunk!(on_reset_thunk, on_reset);
603lifecycle_thunk!(on_dispose_thunk, on_dispose);
604lifecycle_thunk!(on_degrade_thunk, on_degrade);
605lifecycle_thunk!(on_fault_thunk, on_fault);
606
607macro_rules! event_thunk {
608 ($name:ident, $method:ident, $ty:ty) => {
609 unsafe extern "C" fn $name<T: PluginActor>(
610 handle: *mut PluginActorHandle,
611 value: *const $ty,
612 ) -> PluginResult<()> {
613 guard(|| {
614 let v = unsafe { &*value }.boundary_normalized();
617 let actor = handle_as_mut::<T>(handle);
618 ok_or_err(actor.$method(&v))
619 })
620 }
621 };
622}
623
624event_thunk!(on_time_event_thunk, on_time_event, TimeEvent);
625
626unsafe extern "C" fn on_data_thunk<T: PluginActor>(
627 handle: *mut PluginActorHandle,
628 data: PluginCustomDataRef,
629) -> PluginResult<()> {
630 guard(|| {
631 let actor = handle_as_mut::<T>(handle);
632 ok_or_err(actor.on_data(data))
633 })
634}
635
636unsafe extern "C" fn on_instrument_thunk<T: PluginActor>(
637 handle: *mut PluginActorHandle,
638 instrument: *const InstrumentAnyHandle,
639) -> PluginResult<()> {
640 guard(|| {
641 let v: InstrumentAny = unsafe { (*instrument).instrument() }.boundary_normalized();
644 let actor = handle_as_mut::<T>(handle);
645 ok_or_err(actor.on_instrument(&v))
646 })
647}
648
649unsafe extern "C" fn on_book_deltas_thunk<T: PluginActor>(
650 handle: *mut PluginActorHandle,
651 deltas: *const OrderBookDeltasHandle,
652) -> PluginResult<()> {
653 guard(|| {
654 let v: OrderBookDeltas = unsafe { (*deltas).deltas() }.boundary_normalized();
657 let actor = handle_as_mut::<T>(handle);
658 ok_or_err(actor.on_book_deltas(&v))
659 })
660}
661
662unsafe extern "C" fn on_book_thunk<T: PluginActor>(
663 handle: *mut PluginActorHandle,
664 book: *const OrderBookHandle,
665) -> PluginResult<()> {
666 guard(|| {
667 let v: OrderBook = unsafe { (*book).book() }.boundary_normalized();
670 let actor = handle_as_mut::<T>(handle);
671 ok_or_err(actor.on_book(&v))
672 })
673}
674
675event_thunk!(on_quote_thunk, on_quote, QuoteTick);
676event_thunk!(on_trade_thunk, on_trade, TradeTick);
677event_thunk!(on_bar_thunk, on_bar, Bar);
678event_thunk!(on_mark_price_thunk, on_mark_price, MarkPriceUpdate);
679event_thunk!(on_index_price_thunk, on_index_price, IndexPriceUpdate);
680event_thunk!(on_funding_rate_thunk, on_funding_rate, FundingRateUpdate);
681event_thunk!(on_option_greeks_thunk, on_option_greeks, OptionGreeks);
682
683unsafe extern "C" fn on_option_chain_thunk<T: PluginActor>(
684 handle: *mut PluginActorHandle,
685 chain: *const OptionChainSliceHandle,
686) -> PluginResult<()> {
687 guard(|| {
688 let v: OptionChainSlice = unsafe { (*chain).chain() }.boundary_normalized();
691 let actor = handle_as_mut::<T>(handle);
692 ok_or_err(actor.on_option_chain(&v))
693 })
694}
695
696event_thunk!(
697 on_instrument_status_thunk,
698 on_instrument_status,
699 InstrumentStatus
700);
701event_thunk!(
702 on_instrument_close_thunk,
703 on_instrument_close,
704 InstrumentClose
705);
706event_thunk!(on_order_filled_thunk, on_order_filled, OrderFilled);
707event_thunk!(on_order_canceled_thunk, on_order_canceled, OrderCanceled);
708event_thunk!(on_signal_thunk, on_signal, Signal);
709
710macro_rules! slice_thunk {
711 ($name:ident, $method:ident, $ty:ty) => {
712 unsafe extern "C" fn $name<T: PluginActor>(
713 handle: *mut PluginActorHandle,
714 values: Slice<'_, $ty>,
715 ) -> PluginResult<()> {
716 guard(|| {
717 let v: Vec<$ty> = unsafe { values.as_slice() }
720 .iter()
721 .map(BoundaryNormalize::boundary_normalized)
722 .collect();
723 let actor = handle_as_mut::<T>(handle);
724 ok_or_err(actor.$method(&v))
725 })
726 }
727 };
728}
729
730slice_thunk!(
731 on_historical_book_deltas_thunk,
732 on_historical_book_deltas,
733 OrderBookDelta
734);
735slice_thunk!(
736 on_historical_book_depth_thunk,
737 on_historical_book_depth,
738 OrderBookDepth10
739);
740slice_thunk!(on_historical_quotes_thunk, on_historical_quotes, QuoteTick);
741slice_thunk!(on_historical_trades_thunk, on_historical_trades, TradeTick);
742slice_thunk!(on_historical_bars_thunk, on_historical_bars, Bar);
743slice_thunk!(
744 on_historical_mark_prices_thunk,
745 on_historical_mark_prices,
746 MarkPriceUpdate
747);
748slice_thunk!(
749 on_historical_index_prices_thunk,
750 on_historical_index_prices,
751 IndexPriceUpdate
752);
753slice_thunk!(
754 on_historical_funding_rates_thunk,
755 on_historical_funding_rates,
756 FundingRateUpdate
757);