1use std::{
17 any::Any,
18 cell::{Ref, RefCell, RefMut},
19 collections::HashMap,
20 fmt::Debug,
21 num::NonZeroUsize,
22 rc::Rc,
23 sync::Arc,
24};
25
26use ahash::{AHashMap, AHashSet};
27use chrono::{DateTime, Utc};
28use indexmap::IndexMap;
29use nautilus_core::{Params, UUID4, UnixNanos, correctness::check_predicate_true};
30#[cfg(feature = "defi")]
31use nautilus_model::defi::{
32 Block, Blockchain, Pool, PoolLiquidityUpdate, PoolSwap, data::PoolFeeCollect, data::PoolFlash,
33};
34use nautilus_model::{
35 data::{
36 Bar, BarType, CustomData, DataType, FundingRateUpdate, IndexPriceUpdate, InstrumentStatus,
37 MarkPriceUpdate, OrderBookDelta, OrderBookDeltas, OrderBookDepth10, QuoteTick, TradeTick,
38 close::InstrumentClose,
39 option_chain::{OptionChainSlice, OptionGreeks, StrikeRange},
40 },
41 enums::BookType,
42 identifiers::{ActorId, ClientId, ComponentId, InstrumentId, OptionSeriesId, TraderId, Venue},
43 instruments::{InstrumentAny, SyntheticInstrument},
44 orderbook::OrderBook,
45};
46use serde::{Deserialize, Serialize};
47use ustr::Ustr;
48
49use super::{
50 Actor,
51 indicators::{Indicators, SharedActorIndicator},
52 registry::{get_actor_unchecked, try_get_actor_unchecked},
53};
54#[cfg(feature = "defi")]
55use crate::defi;
56#[cfg(feature = "defi")]
57#[allow(unused_imports)]
58use crate::defi::data_actor as _; use crate::{
60 cache::{Cache, CacheApi},
61 clock::{Clock, ClockApi},
62 component::Component,
63 enums::{ComponentState, ComponentTrigger},
64 logging::{CMD, RECV, REQ, SEND},
65 messages::{
66 data::{
67 BarsResponse, BookDeltasResponse, BookDepthResponse, BookResponse, CustomDataResponse,
68 DataCommand, FundingRatesResponse, InstrumentResponse, InstrumentsResponse,
69 QuotesResponse, RequestBars, RequestBookDeltas, RequestBookDepth, RequestBookSnapshot,
70 RequestCommand, RequestCustomData, RequestFundingRates, RequestInstrument,
71 RequestInstruments, RequestQuotes, RequestTrades, SubscribeBars, SubscribeBookDeltas,
72 SubscribeBookDepth10, SubscribeBookSnapshots, SubscribeCommand, SubscribeCustomData,
73 SubscribeFundingRates, SubscribeIndexPrices, SubscribeInstrument,
74 SubscribeInstrumentClose, SubscribeInstrumentStatus, SubscribeInstruments,
75 SubscribeMarkPrices, SubscribeOptionChain, SubscribeOptionGreeks, SubscribeQuotes,
76 SubscribeTrades, TradesResponse, UnsubscribeBars, UnsubscribeBookDeltas,
77 UnsubscribeBookDepth10, UnsubscribeBookSnapshots, UnsubscribeCommand,
78 UnsubscribeCustomData, UnsubscribeFundingRates, UnsubscribeIndexPrices,
79 UnsubscribeInstrument, UnsubscribeInstrumentClose, UnsubscribeInstrumentStatus,
80 UnsubscribeInstruments, UnsubscribeMarkPrices, UnsubscribeOptionChain,
81 UnsubscribeOptionGreeks, UnsubscribeQuotes, UnsubscribeTrades, is_parent_subscription,
82 },
83 system::ShutdownSystem,
84 },
85 msgbus::{
86 self, MStr, Pattern, ShareableMessageHandler, Topic, TypedHandler, get_message_bus,
87 switchboard::{
88 MessagingSwitchboard, get_bars_topic, get_book_deltas_pattern, get_book_deltas_topic,
89 get_book_depth10_pattern, get_book_depth10_topic, get_book_snapshots_topic,
90 get_custom_topic, get_funding_rate_topic, get_index_price_topic,
91 get_instrument_close_topic, get_instrument_status_topic, get_instrument_topic,
92 get_instruments_pattern, get_mark_price_topic, get_option_chain_topic,
93 get_option_greeks_topic, get_quotes_topic, get_signal_pattern, get_trades_topic,
94 },
95 },
96 signal::Signal,
97 timer::{TimeEvent, TimeEventCallback},
98};
99
100#[derive(Debug, Clone, Deserialize, Serialize)]
102#[serde(default, deny_unknown_fields)]
103#[cfg_attr(
104 feature = "python",
105 pyo3::pyclass(
106 module = "nautilus_trader.core.nautilus_pyo3.common",
107 subclass,
108 from_py_object
109 )
110)]
111#[cfg_attr(
112 feature = "python",
113 pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.common")
114)]
115pub struct DataActorConfig {
116 pub actor_id: Option<ActorId>,
118 pub log_events: bool,
120 pub log_commands: bool,
122}
123
124impl Default for DataActorConfig {
125 fn default() -> Self {
126 Self {
127 actor_id: None,
128 log_events: true,
129 log_commands: true,
130 }
131 }
132}
133
134#[derive(Debug, Clone, Deserialize, Serialize)]
136#[serde(deny_unknown_fields)]
137#[cfg_attr(
138 feature = "python",
139 pyo3::pyclass(module = "nautilus_trader.core.nautilus_pyo3.common", from_py_object)
140)]
141#[cfg_attr(
142 feature = "python",
143 pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.common")
144)]
145pub struct ImportableActorConfig {
146 pub actor_path: String,
148 pub config_path: String,
150 pub config: HashMap<String, serde_json::Value>,
152}
153
154type RequestCallback = Arc<dyn Fn(UUID4) + Send + Sync>;
155
156pub trait DataActorNative {
168 fn core(&self) -> &DataActorCore;
170
171 fn core_mut(&mut self) -> &mut DataActorCore;
173
174 fn clock_mut(&mut self) -> RefMut<'_, dyn Clock> {
180 let core = self.core_mut();
181 core.clock
182 .as_ref()
183 .unwrap_or_else(|| {
184 panic!(
185 "DataActor {} must be registered before calling `clock_mut()` - trader_id: {:?}",
186 core.actor_id, core.trader_id
187 )
188 })
189 .borrow_mut()
190 }
191
192 fn clock_rc(&self) -> Rc<RefCell<dyn Clock>> {
198 self.core()
199 .clock
200 .as_ref()
201 .expect("DataActor must be registered before accessing clock")
202 .clone()
203 }
204
205 fn cache_ref(&self) -> Ref<'_, Cache> {
211 self.core()
212 .cache
213 .as_ref()
214 .expect("DataActor must be registered before accessing cache")
215 .borrow()
216 }
217
218 fn cache_rc(&self) -> Rc<RefCell<Cache>> {
224 self.core()
225 .cache
226 .as_ref()
227 .expect("DataActor must be registered before accessing cache")
228 .clone()
229 }
230}
231
232pub trait DataActor: Component {
240 fn actor_id(&self) -> ActorId
242 where
243 Self: DataActorNative,
244 {
245 self.core().actor_id()
246 }
247
248 fn trader_id(&self) -> Option<TraderId>
250 where
251 Self: DataActorNative,
252 {
253 self.core().trader_id()
254 }
255
256 fn is_registered(&self) -> bool
258 where
259 Self: DataActorNative,
260 {
261 self.core().is_registered()
262 }
263
264 fn config(&self) -> &DataActorConfig
266 where
267 Self: DataActorNative,
268 {
269 &self.core().config
270 }
271
272 fn on_save(&self) -> anyhow::Result<IndexMap<String, Vec<u8>>> {
278 Ok(IndexMap::new())
279 }
280
281 #[allow(unused_variables)]
287 fn on_load(&mut self, state: IndexMap<String, Vec<u8>>) -> anyhow::Result<()> {
288 Ok(())
289 }
290
291 fn on_start(&mut self) -> anyhow::Result<()> {
297 log::warn!(
298 "The `on_start` handler was called when not overridden, \
299 it's expected that any actions required when starting the actor \
300 occur here, such as subscribing/requesting data"
301 );
302 Ok(())
303 }
304
305 fn on_stop(&mut self) -> anyhow::Result<()> {
311 log::warn!(
312 "The `on_stop` handler was called when not overridden, \
313 it's expected that any actions required when stopping the actor \
314 occur here, such as unsubscribing from data",
315 );
316 Ok(())
317 }
318
319 fn on_resume(&mut self) -> anyhow::Result<()> {
325 log::warn!(
326 "The `on_resume` handler was called when not overridden, \
327 it's expected that any actions required when resuming the actor \
328 following a stop occur here"
329 );
330 Ok(())
331 }
332
333 fn on_reset(&mut self) -> anyhow::Result<()> {
339 log::warn!(
340 "The `on_reset` handler was called when not overridden, \
341 it's expected that any actions required when resetting the actor \
342 occur here, such as resetting indicators and other state"
343 );
344 Ok(())
345 }
346
347 fn on_dispose(&mut self) -> anyhow::Result<()> {
353 Ok(())
354 }
355
356 fn on_degrade(&mut self) -> anyhow::Result<()> {
362 Ok(())
363 }
364
365 fn on_fault(&mut self) -> anyhow::Result<()> {
371 Ok(())
372 }
373
374 #[allow(unused_variables)]
380 fn on_time_event(&mut self, event: &TimeEvent) -> anyhow::Result<()> {
381 Ok(())
382 }
383
384 #[allow(unused_variables)]
390 fn on_data(&mut self, data: &CustomData) -> anyhow::Result<()> {
391 Ok(())
392 }
393
394 #[allow(unused_variables)]
400 fn on_signal(&mut self, signal: &Signal) -> anyhow::Result<()> {
401 Ok(())
402 }
403
404 #[allow(unused_variables)]
410 fn on_instrument(&mut self, instrument: &InstrumentAny) -> anyhow::Result<()> {
411 Ok(())
412 }
413
414 #[allow(unused_variables)]
420 fn on_book_deltas(&mut self, deltas: &OrderBookDeltas) -> anyhow::Result<()> {
421 Ok(())
422 }
423
424 #[allow(unused_variables)]
430 fn on_book_depth(&mut self, depth: &OrderBookDepth10) -> anyhow::Result<()> {
431 Ok(())
432 }
433
434 #[allow(unused_variables)]
440 fn on_book(&mut self, order_book: &OrderBook) -> anyhow::Result<()> {
441 Ok(())
442 }
443
444 #[allow(unused_variables)]
450 fn on_quote(&mut self, quote: &QuoteTick) -> anyhow::Result<()> {
451 Ok(())
452 }
453
454 #[allow(unused_variables)]
460 fn on_trade(&mut self, tick: &TradeTick) -> anyhow::Result<()> {
461 Ok(())
462 }
463
464 #[allow(unused_variables)]
470 fn on_bar(&mut self, bar: &Bar) -> anyhow::Result<()> {
471 Ok(())
472 }
473
474 #[allow(unused_variables)]
480 fn on_mark_price(&mut self, mark_price: &MarkPriceUpdate) -> anyhow::Result<()> {
481 Ok(())
482 }
483
484 #[allow(unused_variables)]
490 fn on_index_price(&mut self, index_price: &IndexPriceUpdate) -> anyhow::Result<()> {
491 Ok(())
492 }
493
494 #[allow(unused_variables)]
500 fn on_funding_rate(&mut self, funding_rate: &FundingRateUpdate) -> anyhow::Result<()> {
501 Ok(())
502 }
503
504 #[allow(unused_variables)]
510 fn on_option_greeks(&mut self, greeks: &OptionGreeks) -> anyhow::Result<()> {
511 Ok(())
512 }
513
514 #[allow(unused_variables)]
520 fn on_option_chain(&mut self, slice: &OptionChainSlice) -> anyhow::Result<()> {
521 Ok(())
522 }
523
524 #[allow(unused_variables)]
530 fn on_instrument_status(&mut self, data: &InstrumentStatus) -> anyhow::Result<()> {
531 Ok(())
532 }
533
534 #[allow(unused_variables)]
540 fn on_instrument_close(&mut self, update: &InstrumentClose) -> anyhow::Result<()> {
541 Ok(())
542 }
543
544 #[cfg(feature = "defi")]
545 #[allow(unused_variables)]
551 fn on_block(&mut self, block: &Block) -> anyhow::Result<()> {
552 Ok(())
553 }
554
555 #[cfg(feature = "defi")]
556 #[allow(unused_variables)]
562 fn on_pool(&mut self, pool: &Pool) -> anyhow::Result<()> {
563 Ok(())
564 }
565
566 #[cfg(feature = "defi")]
567 #[allow(unused_variables)]
573 fn on_pool_swap(&mut self, swap: &PoolSwap) -> anyhow::Result<()> {
574 Ok(())
575 }
576
577 #[cfg(feature = "defi")]
578 #[allow(unused_variables)]
584 fn on_pool_liquidity_update(&mut self, update: &PoolLiquidityUpdate) -> anyhow::Result<()> {
585 Ok(())
586 }
587
588 #[cfg(feature = "defi")]
589 #[allow(unused_variables)]
595 fn on_pool_fee_collect(&mut self, collect: &PoolFeeCollect) -> anyhow::Result<()> {
596 Ok(())
597 }
598
599 #[cfg(feature = "defi")]
600 #[allow(unused_variables)]
606 fn on_pool_flash(&mut self, flash: &PoolFlash) -> anyhow::Result<()> {
607 Ok(())
608 }
609
610 #[allow(unused_variables)]
616 fn on_historical_data(&mut self, data: &dyn Any) -> anyhow::Result<()> {
617 Ok(())
618 }
619
620 #[allow(unused_variables)]
626 fn on_historical_book_deltas(&mut self, deltas: &[OrderBookDelta]) -> anyhow::Result<()> {
627 Ok(())
628 }
629
630 #[allow(unused_variables)]
636 fn on_historical_book_depth(&mut self, depths: &[OrderBookDepth10]) -> anyhow::Result<()> {
637 Ok(())
638 }
639
640 #[allow(unused_variables)]
646 fn on_historical_quotes(&mut self, quotes: &[QuoteTick]) -> anyhow::Result<()> {
647 Ok(())
648 }
649
650 #[allow(unused_variables)]
656 fn on_historical_trades(&mut self, trades: &[TradeTick]) -> anyhow::Result<()> {
657 Ok(())
658 }
659
660 #[allow(unused_variables)]
666 fn on_historical_bars(&mut self, bars: &[Bar]) -> anyhow::Result<()> {
667 Ok(())
668 }
669
670 #[allow(unused_variables)]
676 fn on_historical_mark_prices(&mut self, mark_prices: &[MarkPriceUpdate]) -> anyhow::Result<()> {
677 Ok(())
678 }
679
680 #[allow(unused_variables)]
686 fn on_historical_index_prices(
687 &mut self,
688 index_prices: &[IndexPriceUpdate],
689 ) -> anyhow::Result<()> {
690 Ok(())
691 }
692
693 #[allow(unused_variables)]
699 fn on_historical_funding_rates(
700 &mut self,
701 funding_rates: &[FundingRateUpdate],
702 ) -> anyhow::Result<()> {
703 Ok(())
704 }
705
706 fn clock(&self) -> ClockApi<'_>
708 where
709 Self: DataActorNative,
710 {
711 self.core().clock_api()
712 }
713
714 fn cache(&self) -> CacheApi<'_>
716 where
717 Self: DataActorNative,
718 {
719 self.core().cache_api()
720 }
721
722 fn shutdown_system(&self, reason: Option<String>)
728 where
729 Self: DataActorNative,
730 {
731 self.core().shutdown_system(reason);
732 }
733
734 fn publish_data(&self, data_type: &DataType, data: &CustomData)
743 where
744 Self: DataActorNative,
745 {
746 self.core().publish_data(data_type, data);
747 }
748
749 fn publish_signal(&self, name: &str, value: String, ts_event: UnixNanos)
755 where
756 Self: DataActorNative,
757 {
758 self.core().publish_signal(name, value, ts_event);
759 }
760
761 fn add_synthetic(&self, synthetic: SyntheticInstrument) -> anyhow::Result<()>
773 where
774 Self: DataActorNative,
775 {
776 self.core().add_synthetic(synthetic)
777 }
778
779 fn update_synthetic(&self, synthetic: SyntheticInstrument) -> anyhow::Result<()>
791 where
792 Self: DataActorNative,
793 {
794 self.core().update_synthetic(synthetic)
795 }
796
797 fn handle_time_event(&mut self, event: &TimeEvent) {
799 log_received(&event);
800
801 if self.not_running() {
802 log_not_running(&event);
803 return;
804 }
805
806 if let Err(e) = DataActor::on_time_event(self, event) {
807 log_error(&e);
808 }
809 }
810
811 fn handle_data(&mut self, data: &CustomData) {
813 log_received(&data);
814
815 if self.not_running() {
816 log_not_running(&data);
817 return;
818 }
819
820 if let Err(e) = self.on_data(data) {
821 log_error(&e);
822 }
823 }
824
825 fn handle_signal(&mut self, signal: &Signal) {
827 log_received(&signal);
828
829 if self.not_running() {
830 log_not_running(&signal);
831 return;
832 }
833
834 if let Err(e) = self.on_signal(signal) {
835 log_error(&e);
836 }
837 }
838
839 fn handle_instrument(&mut self, instrument: &InstrumentAny) {
841 log_received(&instrument);
842
843 if self.not_running() {
844 log_not_running(&instrument);
845 return;
846 }
847
848 if let Err(e) = self.on_instrument(instrument) {
849 log_error(&e);
850 }
851 }
852
853 fn handle_book_deltas(&mut self, deltas: &OrderBookDeltas) {
855 log_received(&deltas);
856
857 if self.not_running() {
858 log_not_running(&deltas);
859 return;
860 }
861
862 if let Err(e) = self.on_book_deltas(deltas) {
863 log_error(&e);
864 }
865 }
866
867 fn handle_book_depth(&mut self, depth: &OrderBookDepth10) {
869 log_received(&depth);
870
871 if self.not_running() {
872 log_not_running(&depth);
873 return;
874 }
875
876 if let Err(e) = self.on_book_depth(depth) {
877 log_error(&e);
878 }
879 }
880
881 fn handle_book(&mut self, book: &OrderBook) {
883 log_received(&book);
884
885 if self.not_running() {
886 log_not_running(&book);
887 return;
888 }
889
890 if let Err(e) = self.on_book(book) {
891 log_error(&e);
892 }
893 }
894
895 fn handle_quote(&mut self, quote: &QuoteTick)
897 where
898 Self: DataActorNative,
899 {
900 log_received("e);
901
902 if let Err(e) = self.core().handle_indicators_for_quote(quote) {
903 log_error(&e);
904 return;
905 }
906
907 if self.not_running() {
908 log_not_running("e);
909 return;
910 }
911
912 if let Err(e) = self.on_quote(quote) {
913 log_error(&e);
914 }
915 }
916
917 fn handle_trade(&mut self, trade: &TradeTick)
919 where
920 Self: DataActorNative,
921 {
922 log_received(&trade);
923
924 if let Err(e) = self.core().handle_indicators_for_trade(trade) {
925 log_error(&e);
926 return;
927 }
928
929 if self.not_running() {
930 log_not_running(&trade);
931 return;
932 }
933
934 if let Err(e) = self.on_trade(trade) {
935 log_error(&e);
936 }
937 }
938
939 fn handle_bar(&mut self, bar: &Bar)
941 where
942 Self: DataActorNative,
943 {
944 log_received(&bar);
945
946 if let Err(e) = self.core().handle_indicators_for_bar(bar) {
947 log_error(&e);
948 return;
949 }
950
951 if self.not_running() {
952 log_not_running(&bar);
953 return;
954 }
955
956 if let Err(e) = self.on_bar(bar) {
957 log_error(&e);
958 }
959 }
960
961 fn handle_mark_price(&mut self, mark_price: &MarkPriceUpdate) {
963 log_received(&mark_price);
964
965 if self.not_running() {
966 log_not_running(&mark_price);
967 return;
968 }
969
970 if let Err(e) = self.on_mark_price(mark_price) {
971 log_error(&e);
972 }
973 }
974
975 fn handle_index_price(&mut self, index_price: &IndexPriceUpdate) {
977 log_received(&index_price);
978
979 if self.not_running() {
980 log_not_running(&index_price);
981 return;
982 }
983
984 if let Err(e) = self.on_index_price(index_price) {
985 log_error(&e);
986 }
987 }
988
989 fn handle_funding_rate(&mut self, funding_rate: &FundingRateUpdate) {
991 log_received(&funding_rate);
992
993 if self.not_running() {
994 log_not_running(&funding_rate);
995 return;
996 }
997
998 if let Err(e) = self.on_funding_rate(funding_rate) {
999 log_error(&e);
1000 }
1001 }
1002
1003 fn handle_option_greeks(&mut self, greeks: &OptionGreeks) {
1005 log_received(&greeks);
1006
1007 if self.not_running() {
1008 log_not_running(&greeks);
1009 return;
1010 }
1011
1012 if let Err(e) = self.on_option_greeks(greeks) {
1013 log_error(&e);
1014 }
1015 }
1016
1017 fn handle_option_chain(&mut self, slice: &OptionChainSlice) {
1019 log_received(&slice);
1020
1021 if self.not_running() {
1022 log_not_running(&slice);
1023 return;
1024 }
1025
1026 if let Err(e) = self.on_option_chain(slice) {
1027 log_error(&e);
1028 }
1029 }
1030
1031 fn handle_instrument_status(&mut self, status: &InstrumentStatus) {
1033 log_received(&status);
1034
1035 if self.not_running() {
1036 log_not_running(&status);
1037 return;
1038 }
1039
1040 if let Err(e) = self.on_instrument_status(status) {
1041 log_error(&e);
1042 }
1043 }
1044
1045 fn handle_instrument_close(&mut self, close: &InstrumentClose) {
1047 log_received(&close);
1048
1049 if self.not_running() {
1050 log_not_running(&close);
1051 return;
1052 }
1053
1054 if let Err(e) = self.on_instrument_close(close) {
1055 log_error(&e);
1056 }
1057 }
1058
1059 #[cfg(feature = "defi")]
1060 fn handle_block(&mut self, block: &Block) {
1062 log_received(&block);
1063
1064 if self.not_running() {
1065 log_not_running(&block);
1066 return;
1067 }
1068
1069 if let Err(e) = self.on_block(block) {
1070 log_error(&e);
1071 }
1072 }
1073
1074 #[cfg(feature = "defi")]
1075 fn handle_pool(&mut self, pool: &Pool) {
1077 log_received(&pool);
1078
1079 if self.not_running() {
1080 log_not_running(&pool);
1081 return;
1082 }
1083
1084 if let Err(e) = self.on_pool(pool) {
1085 log_error(&e);
1086 }
1087 }
1088
1089 #[cfg(feature = "defi")]
1090 fn handle_pool_swap(&mut self, swap: &PoolSwap) {
1092 log_received(&swap);
1093
1094 if self.not_running() {
1095 log_not_running(&swap);
1096 return;
1097 }
1098
1099 if let Err(e) = self.on_pool_swap(swap) {
1100 log_error(&e);
1101 }
1102 }
1103
1104 #[cfg(feature = "defi")]
1105 fn handle_pool_liquidity_update(&mut self, update: &PoolLiquidityUpdate) {
1107 log_received(&update);
1108
1109 if self.not_running() {
1110 log_not_running(&update);
1111 return;
1112 }
1113
1114 if let Err(e) = self.on_pool_liquidity_update(update) {
1115 log_error(&e);
1116 }
1117 }
1118
1119 #[cfg(feature = "defi")]
1120 fn handle_pool_fee_collect(&mut self, collect: &PoolFeeCollect) {
1122 log_received(&collect);
1123
1124 if self.not_running() {
1125 log_not_running(&collect);
1126 return;
1127 }
1128
1129 if let Err(e) = self.on_pool_fee_collect(collect) {
1130 log_error(&e);
1131 }
1132 }
1133
1134 #[cfg(feature = "defi")]
1135 fn handle_pool_flash(&mut self, flash: &PoolFlash) {
1137 log_received(&flash);
1138
1139 if self.not_running() {
1140 log_not_running(&flash);
1141 return;
1142 }
1143
1144 if let Err(e) = self.on_pool_flash(flash) {
1145 log_error(&e);
1146 }
1147 }
1148
1149 fn handle_historical_data(&mut self, data: &dyn Any) {
1151 log_received(&data);
1152
1153 if let Err(e) = self.on_historical_data(data) {
1154 log_error(&e);
1155 }
1156 }
1157
1158 fn handle_data_response(&mut self, resp: &CustomDataResponse) {
1160 log_received(&resp);
1161
1162 if let Err(e) = self.on_historical_data(resp.data.as_ref()) {
1163 log_error(&e);
1164 }
1165 }
1166
1167 fn handle_instrument_response(&mut self, resp: &InstrumentResponse) {
1169 log_received(&resp);
1170
1171 if let Err(e) = self.on_instrument(&resp.data) {
1172 log_error(&e);
1173 }
1174 }
1175
1176 fn handle_instruments_response(&mut self, resp: &InstrumentsResponse) {
1178 log_received_bulk("InstrumentsResponse", &resp.correlation_id, resp.data.len());
1179 log::trace!("{RECV} {resp:?}");
1180
1181 for inst in &resp.data {
1182 if let Err(e) = self.on_instrument(inst) {
1183 log_error(&e);
1184 }
1185 }
1186 }
1187
1188 fn handle_book_response(&mut self, resp: &BookResponse) {
1190 log_received(&resp);
1191
1192 if let Err(e) = self.on_book(&resp.data) {
1193 log_error(&e);
1194 }
1195 }
1196
1197 fn handle_book_deltas_response(&mut self, resp: &BookDeltasResponse) {
1199 log_received_bulk("BookDeltasResponse", &resp.correlation_id, resp.data.len());
1200 log::trace!("{RECV} {resp:?}");
1201
1202 if let Err(e) = self.on_historical_book_deltas(&resp.data) {
1203 log_error(&e);
1204 }
1205 }
1206
1207 fn handle_book_depth_response(&mut self, resp: &BookDepthResponse) {
1209 log_received_bulk("BookDepthResponse", &resp.correlation_id, resp.data.len());
1210 log::trace!("{RECV} {resp:?}");
1211
1212 if let Err(e) = self.on_historical_book_depth(&resp.data) {
1213 log_error(&e);
1214 }
1215 }
1216
1217 fn handle_quotes_response(&mut self, resp: &QuotesResponse)
1219 where
1220 Self: DataActorNative,
1221 {
1222 log_received_bulk("QuotesResponse", &resp.correlation_id, resp.data.len());
1223 log::trace!("{RECV} {resp:?}");
1224
1225 if let Err(e) = self.core().handle_indicators_for_quotes(&resp.data) {
1226 log_error(&e);
1227 return;
1228 }
1229
1230 if let Err(e) = self.on_historical_quotes(&resp.data) {
1231 log_error(&e);
1232 }
1233 }
1234
1235 fn handle_trades_response(&mut self, resp: &TradesResponse)
1237 where
1238 Self: DataActorNative,
1239 {
1240 log_received_bulk("TradesResponse", &resp.correlation_id, resp.data.len());
1241 log::trace!("{RECV} {resp:?}");
1242
1243 if let Err(e) = self.core().handle_indicators_for_trades(&resp.data) {
1244 log_error(&e);
1245 return;
1246 }
1247
1248 if let Err(e) = self.on_historical_trades(&resp.data) {
1249 log_error(&e);
1250 }
1251 }
1252
1253 fn handle_bars_response(&mut self, resp: &BarsResponse)
1255 where
1256 Self: DataActorNative,
1257 {
1258 log_received_bulk("BarsResponse", &resp.correlation_id, resp.data.len());
1259 log::trace!("{RECV} {resp:?}");
1260
1261 if let Err(e) = self.core().handle_indicators_for_bars(&resp.data) {
1262 log_error(&e);
1263 return;
1264 }
1265
1266 if let Err(e) = self.on_historical_bars(&resp.data) {
1267 log_error(&e);
1268 }
1269 }
1270
1271 fn handle_funding_rates_response(&mut self, resp: &FundingRatesResponse) {
1273 log_received_bulk(
1274 "FundingRatesResponse",
1275 &resp.correlation_id,
1276 resp.data.len(),
1277 );
1278 log::trace!("{RECV} {resp:?}");
1279
1280 if let Err(e) = self.on_historical_funding_rates(&resp.data) {
1281 log_error(&e);
1282 }
1283 }
1284
1285 fn subscribe_data(
1287 &mut self,
1288 data_type: DataType,
1289 client_id: Option<ClientId>,
1290 params: Option<Params>,
1291 ) where
1292 Self: DataActorNative,
1293 Self: 'static + Debug + Sized,
1294 {
1295 let actor_id = self.core().actor_id().inner();
1296 let handler = ShareableMessageHandler::from_typed(move |data: &CustomData| {
1297 get_actor_unchecked::<Self>(&actor_id).handle_data(data);
1298 });
1299
1300 DataActorCore::subscribe_data(self.core_mut(), handler, data_type, client_id, params);
1301 }
1302
1303 fn subscribe_signal(&mut self, name: &str, priority: Option<u32>)
1318 where
1319 Self: DataActorNative,
1320 Self: 'static + Debug + Sized,
1321 {
1322 let actor_id = self.core().actor_id().inner();
1323 let handler = ShareableMessageHandler::from_typed(move |data: &CustomData| {
1326 if let Some(signal) = data.data.as_any().downcast_ref::<Signal>() {
1327 if let Some(mut actor) = try_get_actor_unchecked::<Self>(&actor_id) {
1328 actor.handle_signal(signal);
1329 } else {
1330 log::error!("Actor {actor_id} not found for signal handling");
1331 }
1332 }
1333 });
1334
1335 DataActorCore::subscribe_signal(self.core_mut(), handler, name, priority);
1336 }
1337
1338 fn subscribe_quotes(
1340 &mut self,
1341 instrument_id: InstrumentId,
1342 client_id: Option<ClientId>,
1343 params: Option<Params>,
1344 ) where
1345 Self: DataActorNative,
1346 Self: 'static + Debug + Sized,
1347 {
1348 let actor_id = self.core().actor_id().inner();
1349 let topic = get_quotes_topic(instrument_id);
1350
1351 let handler = TypedHandler::from(move |quote: &QuoteTick| {
1352 if let Some(mut actor) = try_get_actor_unchecked::<Self>(&actor_id) {
1353 actor.handle_quote(quote);
1354 } else {
1355 log::error!("Actor {actor_id} not found for quote handling");
1356 }
1357 });
1358
1359 DataActorCore::subscribe_quotes(
1360 self.core_mut(),
1361 topic,
1362 handler,
1363 instrument_id,
1364 client_id,
1365 params,
1366 );
1367 }
1368
1369 fn subscribe_instruments(
1371 &mut self,
1372 venue: Venue,
1373 client_id: Option<ClientId>,
1374 params: Option<Params>,
1375 ) where
1376 Self: DataActorNative,
1377 Self: 'static + Debug + Sized,
1378 {
1379 let actor_id = self.core().actor_id().inner();
1380 let pattern = get_instruments_pattern(venue);
1381
1382 let handler = TypedHandler::from(move |instrument: &InstrumentAny| {
1383 if let Some(mut actor) = try_get_actor_unchecked::<Self>(&actor_id) {
1384 actor.handle_instrument(instrument);
1385 } else {
1386 log::error!("Actor {actor_id} not found for instruments handling");
1387 }
1388 });
1389
1390 DataActorCore::subscribe_instruments(
1391 self.core_mut(),
1392 pattern,
1393 handler,
1394 venue,
1395 client_id,
1396 params,
1397 );
1398 }
1399
1400 fn subscribe_instrument(
1402 &mut self,
1403 instrument_id: InstrumentId,
1404 client_id: Option<ClientId>,
1405 params: Option<Params>,
1406 ) where
1407 Self: DataActorNative,
1408 Self: 'static + Debug + Sized,
1409 {
1410 let actor_id = self.core().actor_id().inner();
1411 let topic = get_instrument_topic(instrument_id);
1412
1413 let handler = TypedHandler::from(move |instrument: &InstrumentAny| {
1414 if let Some(mut actor) = try_get_actor_unchecked::<Self>(&actor_id) {
1415 actor.handle_instrument(instrument);
1416 } else {
1417 log::error!("Actor {actor_id} not found for instrument handling");
1418 }
1419 });
1420
1421 DataActorCore::subscribe_instrument(
1422 self.core_mut(),
1423 topic,
1424 handler,
1425 instrument_id,
1426 client_id,
1427 params,
1428 );
1429 }
1430
1431 fn subscribe_book_deltas(
1437 &mut self,
1438 instrument_id: InstrumentId,
1439 book_type: BookType,
1440 depth: Option<NonZeroUsize>,
1441 client_id: Option<ClientId>,
1442 managed: bool,
1443 params: Option<Params>,
1444 ) where
1445 Self: DataActorNative,
1446 Self: 'static + Debug + Sized,
1447 {
1448 let actor_id = self.core().actor_id().inner();
1449 let is_parent = is_parent_subscription(params.as_ref());
1450 let pattern = if is_parent {
1451 get_book_deltas_pattern(instrument_id)
1452 } else {
1453 get_book_deltas_topic(instrument_id).into()
1454 };
1455
1456 let handler = TypedHandler::from(move |deltas: &OrderBookDeltas| {
1457 get_actor_unchecked::<Self>(&actor_id).handle_book_deltas(deltas);
1458 });
1459
1460 DataActorCore::subscribe_book_deltas(
1461 self.core_mut(),
1462 pattern,
1463 handler,
1464 instrument_id,
1465 book_type,
1466 depth,
1467 client_id,
1468 managed,
1469 params,
1470 );
1471 }
1472
1473 fn subscribe_book_depth10(
1479 &mut self,
1480 instrument_id: InstrumentId,
1481 book_type: BookType,
1482 client_id: Option<ClientId>,
1483 managed: bool,
1484 params: Option<Params>,
1485 ) where
1486 Self: DataActorNative,
1487 Self: 'static + Debug + Sized,
1488 {
1489 let actor_id = self.core().actor_id().inner();
1490 let pattern = if is_parent_subscription(params.as_ref()) {
1491 get_book_depth10_pattern(instrument_id)
1492 } else {
1493 get_book_depth10_topic(instrument_id).into()
1494 };
1495
1496 let handler = TypedHandler::from(move |depth: &OrderBookDepth10| {
1497 get_actor_unchecked::<Self>(&actor_id).handle_book_depth(depth);
1498 });
1499
1500 DataActorCore::subscribe_book_depth10(
1501 self.core_mut(),
1502 pattern,
1503 handler,
1504 instrument_id,
1505 book_type,
1506 client_id,
1507 managed,
1508 params,
1509 );
1510 }
1511
1512 fn subscribe_book_at_interval(
1514 &mut self,
1515 instrument_id: InstrumentId,
1516 book_type: BookType,
1517 depth: Option<NonZeroUsize>,
1518 interval_ms: NonZeroUsize,
1519 client_id: Option<ClientId>,
1520 params: Option<Params>,
1521 ) where
1522 Self: DataActorNative,
1523 Self: 'static + Debug + Sized,
1524 {
1525 let actor_id = self.core().actor_id().inner();
1526 let topic = get_book_snapshots_topic(instrument_id, interval_ms);
1527
1528 let handler = TypedHandler::from(move |book: &OrderBook| {
1529 get_actor_unchecked::<Self>(&actor_id).handle_book(book);
1530 });
1531
1532 DataActorCore::subscribe_book_at_interval(
1533 self.core_mut(),
1534 topic,
1535 handler,
1536 instrument_id,
1537 book_type,
1538 depth,
1539 interval_ms,
1540 client_id,
1541 params,
1542 );
1543 }
1544
1545 fn subscribe_trades(
1547 &mut self,
1548 instrument_id: InstrumentId,
1549 client_id: Option<ClientId>,
1550 params: Option<Params>,
1551 ) where
1552 Self: DataActorNative,
1553 Self: 'static + Debug + Sized,
1554 {
1555 let actor_id = self.core().actor_id().inner();
1556 let topic = get_trades_topic(instrument_id);
1557
1558 let handler = TypedHandler::from(move |trade: &TradeTick| {
1559 get_actor_unchecked::<Self>(&actor_id).handle_trade(trade);
1560 });
1561
1562 DataActorCore::subscribe_trades(
1563 self.core_mut(),
1564 topic,
1565 handler,
1566 instrument_id,
1567 client_id,
1568 params,
1569 );
1570 }
1571
1572 fn subscribe_bars(
1574 &mut self,
1575 bar_type: BarType,
1576 client_id: Option<ClientId>,
1577 params: Option<Params>,
1578 ) where
1579 Self: DataActorNative,
1580 Self: 'static + Debug + Sized,
1581 {
1582 let actor_id = self.core().actor_id().inner();
1583 let topic = get_bars_topic(bar_type.standard());
1585
1586 let handler = TypedHandler::from(move |bar: &Bar| {
1587 get_actor_unchecked::<Self>(&actor_id).handle_bar(bar);
1588 });
1589
1590 DataActorCore::subscribe_bars(self.core_mut(), topic, handler, bar_type, client_id, params);
1591 }
1592
1593 fn subscribe_mark_prices(
1595 &mut self,
1596 instrument_id: InstrumentId,
1597 client_id: Option<ClientId>,
1598 params: Option<Params>,
1599 ) where
1600 Self: DataActorNative,
1601 Self: 'static + Debug + Sized,
1602 {
1603 let actor_id = self.core().actor_id().inner();
1604 let topic = get_mark_price_topic(instrument_id);
1605
1606 let handler = TypedHandler::from(move |mark_price: &MarkPriceUpdate| {
1607 get_actor_unchecked::<Self>(&actor_id).handle_mark_price(mark_price);
1608 });
1609
1610 DataActorCore::subscribe_mark_prices(
1611 self.core_mut(),
1612 topic,
1613 handler,
1614 instrument_id,
1615 client_id,
1616 params,
1617 );
1618 }
1619
1620 fn subscribe_index_prices(
1622 &mut self,
1623 instrument_id: InstrumentId,
1624 client_id: Option<ClientId>,
1625 params: Option<Params>,
1626 ) where
1627 Self: DataActorNative,
1628 Self: 'static + Debug + Sized,
1629 {
1630 let actor_id = self.core().actor_id().inner();
1631 let topic = get_index_price_topic(instrument_id);
1632
1633 let handler = TypedHandler::from(move |index_price: &IndexPriceUpdate| {
1634 get_actor_unchecked::<Self>(&actor_id).handle_index_price(index_price);
1635 });
1636
1637 DataActorCore::subscribe_index_prices(
1638 self.core_mut(),
1639 topic,
1640 handler,
1641 instrument_id,
1642 client_id,
1643 params,
1644 );
1645 }
1646
1647 fn subscribe_funding_rates(
1649 &mut self,
1650 instrument_id: InstrumentId,
1651 client_id: Option<ClientId>,
1652 params: Option<Params>,
1653 ) where
1654 Self: DataActorNative,
1655 Self: 'static + Debug + Sized,
1656 {
1657 let actor_id = self.core().actor_id().inner();
1658 let topic = get_funding_rate_topic(instrument_id);
1659
1660 let handler = TypedHandler::from(move |funding_rate: &FundingRateUpdate| {
1661 get_actor_unchecked::<Self>(&actor_id).handle_funding_rate(funding_rate);
1662 });
1663
1664 DataActorCore::subscribe_funding_rates(
1665 self.core_mut(),
1666 topic,
1667 handler,
1668 instrument_id,
1669 client_id,
1670 params,
1671 );
1672 }
1673
1674 fn subscribe_option_greeks(
1676 &mut self,
1677 instrument_id: InstrumentId,
1678 client_id: Option<ClientId>,
1679 params: Option<Params>,
1680 ) where
1681 Self: DataActorNative,
1682 Self: 'static + Debug + Sized,
1683 {
1684 let actor_id = self.core().actor_id().inner();
1685 let topic = get_option_greeks_topic(instrument_id);
1686
1687 let handler = TypedHandler::from(move |option_greeks: &OptionGreeks| {
1688 if let Some(mut actor) = try_get_actor_unchecked::<Self>(&actor_id) {
1689 actor.handle_option_greeks(option_greeks);
1690 } else {
1691 log::error!("Actor {actor_id} not found for option greeks handling");
1692 }
1693 });
1694
1695 DataActorCore::subscribe_option_greeks(
1696 self.core_mut(),
1697 topic,
1698 handler,
1699 instrument_id,
1700 client_id,
1701 params,
1702 );
1703 }
1704
1705 fn subscribe_instrument_status(
1707 &mut self,
1708 instrument_id: InstrumentId,
1709 client_id: Option<ClientId>,
1710 params: Option<Params>,
1711 ) where
1712 Self: DataActorNative,
1713 Self: 'static + Debug + Sized,
1714 {
1715 let actor_id = self.core().actor_id().inner();
1716 let topic = get_instrument_status_topic(instrument_id);
1717
1718 let handler = ShareableMessageHandler::from_typed(move |status: &InstrumentStatus| {
1719 get_actor_unchecked::<Self>(&actor_id).handle_instrument_status(status);
1720 });
1721
1722 DataActorCore::subscribe_instrument_status(
1723 self.core_mut(),
1724 topic,
1725 handler,
1726 instrument_id,
1727 client_id,
1728 params,
1729 );
1730 }
1731
1732 fn subscribe_instrument_close(
1734 &mut self,
1735 instrument_id: InstrumentId,
1736 client_id: Option<ClientId>,
1737 params: Option<Params>,
1738 ) where
1739 Self: DataActorNative,
1740 Self: 'static + Debug + Sized,
1741 {
1742 let actor_id = self.core().actor_id().inner();
1743 let topic = get_instrument_close_topic(instrument_id);
1744
1745 let handler = ShareableMessageHandler::from_typed(move |close: &InstrumentClose| {
1746 get_actor_unchecked::<Self>(&actor_id).handle_instrument_close(close);
1747 });
1748
1749 DataActorCore::subscribe_instrument_close(
1750 self.core_mut(),
1751 topic,
1752 handler,
1753 instrument_id,
1754 client_id,
1755 params,
1756 );
1757 }
1758
1759 fn subscribe_option_chain(
1764 &mut self,
1765 series_id: OptionSeriesId,
1766 strike_range: StrikeRange,
1767 snapshot_interval_ms: Option<u64>,
1768 client_id: Option<ClientId>,
1769 params: Option<Params>,
1770 ) where
1771 Self: DataActorNative,
1772 Self: 'static + Debug + Sized,
1773 {
1774 let actor_id = self.core().actor_id().inner();
1775 let topic = get_option_chain_topic(series_id);
1776
1777 let handler = TypedHandler::from(move |slice: &OptionChainSlice| {
1778 if let Some(mut actor) = try_get_actor_unchecked::<Self>(&actor_id) {
1779 actor.handle_option_chain(slice);
1780 } else {
1781 log::error!("Actor {actor_id} not found for option chain handling");
1782 }
1783 });
1784
1785 DataActorCore::subscribe_option_chain(
1786 self.core_mut(),
1787 topic,
1788 handler,
1789 series_id,
1790 strike_range,
1791 snapshot_interval_ms,
1792 client_id,
1793 params,
1794 );
1795 }
1796
1797 #[cfg(feature = "defi")]
1798 fn subscribe_blocks(
1800 &mut self,
1801 chain: Blockchain,
1802 client_id: Option<ClientId>,
1803 params: Option<Params>,
1804 ) where
1805 Self: DataActorNative,
1806 Self: 'static + Debug + Sized,
1807 {
1808 let actor_id = self.core().actor_id().inner();
1809 let topic = defi::switchboard::get_defi_blocks_topic(chain);
1810
1811 let handler = TypedHandler::from(move |block: &Block| {
1812 get_actor_unchecked::<Self>(&actor_id).handle_block(block);
1813 });
1814
1815 DataActorCore::subscribe_blocks(self.core_mut(), topic, handler, chain, client_id, params);
1816 }
1817
1818 #[cfg(feature = "defi")]
1819 fn subscribe_pool(
1821 &mut self,
1822 instrument_id: InstrumentId,
1823 client_id: Option<ClientId>,
1824 params: Option<Params>,
1825 ) where
1826 Self: DataActorNative,
1827 Self: 'static + Debug + Sized,
1828 {
1829 let actor_id = self.core().actor_id().inner();
1830 let topic = defi::switchboard::get_defi_pool_topic(instrument_id);
1831
1832 let handler = TypedHandler::from(move |pool: &Pool| {
1833 get_actor_unchecked::<Self>(&actor_id).handle_pool(pool);
1834 });
1835
1836 DataActorCore::subscribe_pool(
1837 self.core_mut(),
1838 topic,
1839 handler,
1840 instrument_id,
1841 client_id,
1842 params,
1843 );
1844 }
1845
1846 #[cfg(feature = "defi")]
1847 fn subscribe_pool_swaps(
1849 &mut self,
1850 instrument_id: InstrumentId,
1851 client_id: Option<ClientId>,
1852 params: Option<Params>,
1853 ) where
1854 Self: DataActorNative,
1855 Self: 'static + Debug + Sized,
1856 {
1857 let actor_id = self.core().actor_id().inner();
1858 let topic = defi::switchboard::get_defi_pool_swaps_topic(instrument_id);
1859
1860 let handler = TypedHandler::from(move |swap: &PoolSwap| {
1861 get_actor_unchecked::<Self>(&actor_id).handle_pool_swap(swap);
1862 });
1863
1864 DataActorCore::subscribe_pool_swaps(
1865 self.core_mut(),
1866 topic,
1867 handler,
1868 instrument_id,
1869 client_id,
1870 params,
1871 );
1872 }
1873
1874 #[cfg(feature = "defi")]
1875 fn subscribe_pool_liquidity_updates(
1877 &mut self,
1878 instrument_id: InstrumentId,
1879 client_id: Option<ClientId>,
1880 params: Option<Params>,
1881 ) where
1882 Self: DataActorNative,
1883 Self: 'static + Debug + Sized,
1884 {
1885 let actor_id = self.core().actor_id().inner();
1886 let topic = defi::switchboard::get_defi_liquidity_topic(instrument_id);
1887
1888 let handler = TypedHandler::from(move |update: &PoolLiquidityUpdate| {
1889 get_actor_unchecked::<Self>(&actor_id).handle_pool_liquidity_update(update);
1890 });
1891
1892 DataActorCore::subscribe_pool_liquidity_updates(
1893 self.core_mut(),
1894 topic,
1895 handler,
1896 instrument_id,
1897 client_id,
1898 params,
1899 );
1900 }
1901
1902 #[cfg(feature = "defi")]
1903 fn subscribe_pool_fee_collects(
1905 &mut self,
1906 instrument_id: InstrumentId,
1907 client_id: Option<ClientId>,
1908 params: Option<Params>,
1909 ) where
1910 Self: DataActorNative,
1911 Self: 'static + Debug + Sized,
1912 {
1913 let actor_id = self.core().actor_id().inner();
1914 let topic = defi::switchboard::get_defi_collect_topic(instrument_id);
1915
1916 let handler = TypedHandler::from(move |collect: &PoolFeeCollect| {
1917 get_actor_unchecked::<Self>(&actor_id).handle_pool_fee_collect(collect);
1918 });
1919
1920 DataActorCore::subscribe_pool_fee_collects(
1921 self.core_mut(),
1922 topic,
1923 handler,
1924 instrument_id,
1925 client_id,
1926 params,
1927 );
1928 }
1929
1930 #[cfg(feature = "defi")]
1931 fn subscribe_pool_flash_events(
1933 &mut self,
1934 instrument_id: InstrumentId,
1935 client_id: Option<ClientId>,
1936 params: Option<Params>,
1937 ) where
1938 Self: DataActorNative,
1939 Self: 'static + Debug + Sized,
1940 {
1941 let actor_id = self.core().actor_id().inner();
1942 let topic = defi::switchboard::get_defi_flash_topic(instrument_id);
1943
1944 let handler = TypedHandler::from(move |flash: &PoolFlash| {
1945 get_actor_unchecked::<Self>(&actor_id).handle_pool_flash(flash);
1946 });
1947
1948 DataActorCore::subscribe_pool_flash_events(
1949 self.core_mut(),
1950 topic,
1951 handler,
1952 instrument_id,
1953 client_id,
1954 params,
1955 );
1956 }
1957
1958 fn unsubscribe_data(
1960 &mut self,
1961 data_type: DataType,
1962 client_id: Option<ClientId>,
1963 params: Option<Params>,
1964 ) where
1965 Self: DataActorNative,
1966 Self: 'static + Debug + Sized,
1967 {
1968 DataActorCore::unsubscribe_data(self.core_mut(), data_type, client_id, params);
1969 }
1970
1971 fn unsubscribe_signal(&mut self, name: &str)
1973 where
1974 Self: DataActorNative,
1975 Self: 'static + Debug + Sized,
1976 {
1977 DataActorCore::unsubscribe_signal(self.core_mut(), name);
1978 }
1979
1980 fn unsubscribe_instruments(
1982 &mut self,
1983 venue: Venue,
1984 client_id: Option<ClientId>,
1985 params: Option<Params>,
1986 ) where
1987 Self: DataActorNative,
1988 Self: 'static + Debug + Sized,
1989 {
1990 DataActorCore::unsubscribe_instruments(self.core_mut(), venue, client_id, params);
1991 }
1992
1993 fn unsubscribe_instrument(
1995 &mut self,
1996 instrument_id: InstrumentId,
1997 client_id: Option<ClientId>,
1998 params: Option<Params>,
1999 ) where
2000 Self: DataActorNative,
2001 Self: 'static + Debug + Sized,
2002 {
2003 DataActorCore::unsubscribe_instrument(self.core_mut(), instrument_id, client_id, params);
2004 }
2005
2006 fn unsubscribe_book_deltas(
2008 &mut self,
2009 instrument_id: InstrumentId,
2010 client_id: Option<ClientId>,
2011 params: Option<Params>,
2012 ) where
2013 Self: DataActorNative,
2014 Self: 'static + Debug + Sized,
2015 {
2016 DataActorCore::unsubscribe_book_deltas(self.core_mut(), instrument_id, client_id, params);
2017 }
2018
2019 fn unsubscribe_book_depth10(
2021 &mut self,
2022 instrument_id: InstrumentId,
2023 client_id: Option<ClientId>,
2024 params: Option<Params>,
2025 ) where
2026 Self: DataActorNative,
2027 Self: 'static + Debug + Sized,
2028 {
2029 DataActorCore::unsubscribe_book_depth10(self.core_mut(), instrument_id, client_id, params);
2030 }
2031
2032 fn unsubscribe_book_at_interval(
2034 &mut self,
2035 instrument_id: InstrumentId,
2036 interval_ms: NonZeroUsize,
2037 client_id: Option<ClientId>,
2038 params: Option<Params>,
2039 ) where
2040 Self: DataActorNative,
2041 Self: 'static + Debug + Sized,
2042 {
2043 DataActorCore::unsubscribe_book_at_interval(
2044 self.core_mut(),
2045 instrument_id,
2046 interval_ms,
2047 client_id,
2048 params,
2049 );
2050 }
2051
2052 fn unsubscribe_quotes(
2054 &mut self,
2055 instrument_id: InstrumentId,
2056 client_id: Option<ClientId>,
2057 params: Option<Params>,
2058 ) where
2059 Self: DataActorNative,
2060 Self: 'static + Debug + Sized,
2061 {
2062 DataActorCore::unsubscribe_quotes(self.core_mut(), instrument_id, client_id, params);
2063 }
2064
2065 fn unsubscribe_trades(
2067 &mut self,
2068 instrument_id: InstrumentId,
2069 client_id: Option<ClientId>,
2070 params: Option<Params>,
2071 ) where
2072 Self: DataActorNative,
2073 Self: 'static + Debug + Sized,
2074 {
2075 DataActorCore::unsubscribe_trades(self.core_mut(), instrument_id, client_id, params);
2076 }
2077
2078 fn unsubscribe_bars(
2080 &mut self,
2081 bar_type: BarType,
2082 client_id: Option<ClientId>,
2083 params: Option<Params>,
2084 ) where
2085 Self: DataActorNative,
2086 Self: 'static + Debug + Sized,
2087 {
2088 DataActorCore::unsubscribe_bars(self.core_mut(), bar_type, client_id, params);
2089 }
2090
2091 fn unsubscribe_mark_prices(
2093 &mut self,
2094 instrument_id: InstrumentId,
2095 client_id: Option<ClientId>,
2096 params: Option<Params>,
2097 ) where
2098 Self: DataActorNative,
2099 Self: 'static + Debug + Sized,
2100 {
2101 DataActorCore::unsubscribe_mark_prices(self.core_mut(), instrument_id, client_id, params);
2102 }
2103
2104 fn unsubscribe_index_prices(
2106 &mut self,
2107 instrument_id: InstrumentId,
2108 client_id: Option<ClientId>,
2109 params: Option<Params>,
2110 ) where
2111 Self: DataActorNative,
2112 Self: 'static + Debug + Sized,
2113 {
2114 DataActorCore::unsubscribe_index_prices(self.core_mut(), instrument_id, client_id, params);
2115 }
2116
2117 fn unsubscribe_funding_rates(
2119 &mut self,
2120 instrument_id: InstrumentId,
2121 client_id: Option<ClientId>,
2122 params: Option<Params>,
2123 ) where
2124 Self: DataActorNative,
2125 Self: 'static + Debug + Sized,
2126 {
2127 DataActorCore::unsubscribe_funding_rates(self.core_mut(), instrument_id, client_id, params);
2128 }
2129
2130 fn unsubscribe_option_greeks(
2132 &mut self,
2133 instrument_id: InstrumentId,
2134 client_id: Option<ClientId>,
2135 params: Option<Params>,
2136 ) where
2137 Self: DataActorNative,
2138 Self: 'static + Debug + Sized,
2139 {
2140 DataActorCore::unsubscribe_option_greeks(self.core_mut(), instrument_id, client_id, params);
2141 }
2142
2143 fn unsubscribe_instrument_status(
2145 &mut self,
2146 instrument_id: InstrumentId,
2147 client_id: Option<ClientId>,
2148 params: Option<Params>,
2149 ) where
2150 Self: DataActorNative,
2151 Self: 'static + Debug + Sized,
2152 {
2153 DataActorCore::unsubscribe_instrument_status(
2154 self.core_mut(),
2155 instrument_id,
2156 client_id,
2157 params,
2158 );
2159 }
2160
2161 fn unsubscribe_instrument_close(
2163 &mut self,
2164 instrument_id: InstrumentId,
2165 client_id: Option<ClientId>,
2166 params: Option<Params>,
2167 ) where
2168 Self: DataActorNative,
2169 Self: 'static + Debug + Sized,
2170 {
2171 DataActorCore::unsubscribe_instrument_close(
2172 self.core_mut(),
2173 instrument_id,
2174 client_id,
2175 params,
2176 );
2177 }
2178
2179 fn unsubscribe_option_chain(&mut self, series_id: OptionSeriesId, client_id: Option<ClientId>)
2181 where
2182 Self: DataActorNative,
2183 Self: 'static + Debug + Sized,
2184 {
2185 DataActorCore::unsubscribe_option_chain(self.core_mut(), series_id, client_id);
2186 }
2187
2188 #[cfg(feature = "defi")]
2189 fn unsubscribe_blocks(
2191 &mut self,
2192 chain: Blockchain,
2193 client_id: Option<ClientId>,
2194 params: Option<Params>,
2195 ) where
2196 Self: DataActorNative,
2197 Self: 'static + Debug + Sized,
2198 {
2199 DataActorCore::unsubscribe_blocks(self.core_mut(), chain, client_id, params);
2200 }
2201
2202 #[cfg(feature = "defi")]
2203 fn unsubscribe_pool(
2205 &mut self,
2206 instrument_id: InstrumentId,
2207 client_id: Option<ClientId>,
2208 params: Option<Params>,
2209 ) where
2210 Self: DataActorNative,
2211 Self: 'static + Debug + Sized,
2212 {
2213 DataActorCore::unsubscribe_pool(self.core_mut(), instrument_id, client_id, params);
2214 }
2215
2216 #[cfg(feature = "defi")]
2217 fn unsubscribe_pool_swaps(
2219 &mut self,
2220 instrument_id: InstrumentId,
2221 client_id: Option<ClientId>,
2222 params: Option<Params>,
2223 ) where
2224 Self: DataActorNative,
2225 Self: 'static + Debug + Sized,
2226 {
2227 DataActorCore::unsubscribe_pool_swaps(self.core_mut(), instrument_id, client_id, params);
2228 }
2229
2230 #[cfg(feature = "defi")]
2231 fn unsubscribe_pool_liquidity_updates(
2233 &mut self,
2234 instrument_id: InstrumentId,
2235 client_id: Option<ClientId>,
2236 params: Option<Params>,
2237 ) where
2238 Self: DataActorNative,
2239 Self: 'static + Debug + Sized,
2240 {
2241 DataActorCore::unsubscribe_pool_liquidity_updates(
2242 self.core_mut(),
2243 instrument_id,
2244 client_id,
2245 params,
2246 );
2247 }
2248
2249 #[cfg(feature = "defi")]
2250 fn unsubscribe_pool_fee_collects(
2252 &mut self,
2253 instrument_id: InstrumentId,
2254 client_id: Option<ClientId>,
2255 params: Option<Params>,
2256 ) where
2257 Self: DataActorNative,
2258 Self: 'static + Debug + Sized,
2259 {
2260 DataActorCore::unsubscribe_pool_fee_collects(
2261 self.core_mut(),
2262 instrument_id,
2263 client_id,
2264 params,
2265 );
2266 }
2267
2268 #[cfg(feature = "defi")]
2269 fn unsubscribe_pool_flash_events(
2271 &mut self,
2272 instrument_id: InstrumentId,
2273 client_id: Option<ClientId>,
2274 params: Option<Params>,
2275 ) where
2276 Self: DataActorNative,
2277 Self: 'static + Debug + Sized,
2278 {
2279 DataActorCore::unsubscribe_pool_flash_events(
2280 self.core_mut(),
2281 instrument_id,
2282 client_id,
2283 params,
2284 );
2285 }
2286
2287 fn request_data(
2293 &mut self,
2294 data_type: DataType,
2295 client_id: ClientId,
2296 start: Option<DateTime<Utc>>,
2297 end: Option<DateTime<Utc>>,
2298 limit: Option<NonZeroUsize>,
2299 params: Option<Params>,
2300 ) -> anyhow::Result<UUID4>
2301 where
2302 Self: DataActorNative,
2303 Self: 'static + Debug + Sized,
2304 {
2305 let actor_id = self.core().actor_id().inner();
2306 let handler = ShareableMessageHandler::from_typed(move |resp: &CustomDataResponse| {
2307 get_actor_unchecked::<Self>(&actor_id).handle_data_response(resp);
2308 });
2309
2310 DataActorCore::request_data(
2311 self.core_mut(),
2312 data_type,
2313 client_id,
2314 start,
2315 end,
2316 limit,
2317 params,
2318 handler,
2319 )
2320 }
2321
2322 fn request_instrument(
2328 &mut self,
2329 instrument_id: InstrumentId,
2330 start: Option<DateTime<Utc>>,
2331 end: Option<DateTime<Utc>>,
2332 client_id: Option<ClientId>,
2333 params: Option<Params>,
2334 ) -> anyhow::Result<UUID4>
2335 where
2336 Self: DataActorNative,
2337 Self: 'static + Debug + Sized,
2338 {
2339 let actor_id = self.core().actor_id().inner();
2340 let handler = ShareableMessageHandler::from_typed(move |resp: &InstrumentResponse| {
2341 get_actor_unchecked::<Self>(&actor_id).handle_instrument_response(resp);
2342 });
2343
2344 DataActorCore::request_instrument(
2345 self.core_mut(),
2346 instrument_id,
2347 start,
2348 end,
2349 client_id,
2350 params,
2351 handler,
2352 )
2353 }
2354
2355 fn request_instruments(
2361 &mut self,
2362 venue: Option<Venue>,
2363 start: Option<DateTime<Utc>>,
2364 end: Option<DateTime<Utc>>,
2365 client_id: Option<ClientId>,
2366 params: Option<Params>,
2367 ) -> anyhow::Result<UUID4>
2368 where
2369 Self: DataActorNative,
2370 Self: 'static + Debug + Sized,
2371 {
2372 let actor_id = self.core().actor_id().inner();
2373 let handler = ShareableMessageHandler::from_typed(move |resp: &InstrumentsResponse| {
2374 get_actor_unchecked::<Self>(&actor_id).handle_instruments_response(resp);
2375 });
2376
2377 DataActorCore::request_instruments(
2378 self.core_mut(),
2379 venue,
2380 start,
2381 end,
2382 client_id,
2383 params,
2384 handler,
2385 )
2386 }
2387
2388 fn request_book_snapshot(
2394 &mut self,
2395 instrument_id: InstrumentId,
2396 depth: Option<NonZeroUsize>,
2397 client_id: Option<ClientId>,
2398 params: Option<Params>,
2399 ) -> anyhow::Result<UUID4>
2400 where
2401 Self: DataActorNative,
2402 Self: 'static + Debug + Sized,
2403 {
2404 let actor_id = self.core().actor_id().inner();
2405 let handler = ShareableMessageHandler::from_typed(move |resp: &BookResponse| {
2406 get_actor_unchecked::<Self>(&actor_id).handle_book_response(resp);
2407 });
2408
2409 DataActorCore::request_book_snapshot(
2410 self.core_mut(),
2411 instrument_id,
2412 depth,
2413 client_id,
2414 params,
2415 handler,
2416 )
2417 }
2418
2419 fn request_book_deltas(
2425 &mut self,
2426 instrument_id: InstrumentId,
2427 start: Option<DateTime<Utc>>,
2428 end: Option<DateTime<Utc>>,
2429 limit: Option<NonZeroUsize>,
2430 client_id: Option<ClientId>,
2431 params: Option<Params>,
2432 ) -> anyhow::Result<UUID4>
2433 where
2434 Self: DataActorNative,
2435 Self: 'static + Debug + Sized,
2436 {
2437 let actor_id = self.core().actor_id().inner();
2438 let handler = ShareableMessageHandler::from_typed(move |resp: &BookDeltasResponse| {
2439 get_actor_unchecked::<Self>(&actor_id).handle_book_deltas_response(resp);
2440 });
2441
2442 DataActorCore::request_book_deltas(
2443 self.core_mut(),
2444 instrument_id,
2445 start,
2446 end,
2447 limit,
2448 client_id,
2449 params,
2450 handler,
2451 )
2452 }
2453
2454 #[expect(clippy::too_many_arguments)]
2460 fn request_book_depth(
2461 &mut self,
2462 instrument_id: InstrumentId,
2463 start: Option<DateTime<Utc>>,
2464 end: Option<DateTime<Utc>>,
2465 limit: Option<NonZeroUsize>,
2466 depth: Option<NonZeroUsize>,
2467 client_id: Option<ClientId>,
2468 params: Option<Params>,
2469 ) -> anyhow::Result<UUID4>
2470 where
2471 Self: DataActorNative,
2472 Self: 'static + Debug + Sized,
2473 {
2474 let actor_id = self.core().actor_id().inner();
2475 let handler = ShareableMessageHandler::from_typed(move |resp: &BookDepthResponse| {
2476 get_actor_unchecked::<Self>(&actor_id).handle_book_depth_response(resp);
2477 });
2478
2479 DataActorCore::request_book_depth(
2480 self.core_mut(),
2481 instrument_id,
2482 start,
2483 end,
2484 limit,
2485 depth,
2486 client_id,
2487 params,
2488 handler,
2489 )
2490 }
2491
2492 fn request_quotes(
2498 &mut self,
2499 instrument_id: InstrumentId,
2500 start: Option<DateTime<Utc>>,
2501 end: Option<DateTime<Utc>>,
2502 limit: Option<NonZeroUsize>,
2503 client_id: Option<ClientId>,
2504 params: Option<Params>,
2505 ) -> anyhow::Result<UUID4>
2506 where
2507 Self: DataActorNative,
2508 Self: 'static + Debug + Sized,
2509 {
2510 let actor_id = self.core().actor_id().inner();
2511 let handler = ShareableMessageHandler::from_typed(move |resp: &QuotesResponse| {
2512 get_actor_unchecked::<Self>(&actor_id).handle_quotes_response(resp);
2513 });
2514
2515 DataActorCore::request_quotes(
2516 self.core_mut(),
2517 instrument_id,
2518 start,
2519 end,
2520 limit,
2521 client_id,
2522 params,
2523 handler,
2524 )
2525 }
2526
2527 fn request_trades(
2533 &mut self,
2534 instrument_id: InstrumentId,
2535 start: Option<DateTime<Utc>>,
2536 end: Option<DateTime<Utc>>,
2537 limit: Option<NonZeroUsize>,
2538 client_id: Option<ClientId>,
2539 params: Option<Params>,
2540 ) -> anyhow::Result<UUID4>
2541 where
2542 Self: DataActorNative,
2543 Self: 'static + Debug + Sized,
2544 {
2545 let actor_id = self.core().actor_id().inner();
2546 let handler = ShareableMessageHandler::from_typed(move |resp: &TradesResponse| {
2547 get_actor_unchecked::<Self>(&actor_id).handle_trades_response(resp);
2548 });
2549
2550 DataActorCore::request_trades(
2551 self.core_mut(),
2552 instrument_id,
2553 start,
2554 end,
2555 limit,
2556 client_id,
2557 params,
2558 handler,
2559 )
2560 }
2561
2562 fn request_bars(
2568 &mut self,
2569 bar_type: BarType,
2570 start: Option<DateTime<Utc>>,
2571 end: Option<DateTime<Utc>>,
2572 limit: Option<NonZeroUsize>,
2573 client_id: Option<ClientId>,
2574 params: Option<Params>,
2575 ) -> anyhow::Result<UUID4>
2576 where
2577 Self: DataActorNative,
2578 Self: 'static + Debug + Sized,
2579 {
2580 let actor_id = self.core().actor_id().inner();
2581 let handler = ShareableMessageHandler::from_typed(move |resp: &BarsResponse| {
2582 get_actor_unchecked::<Self>(&actor_id).handle_bars_response(resp);
2583 });
2584
2585 DataActorCore::request_bars(
2586 self.core_mut(),
2587 bar_type,
2588 start,
2589 end,
2590 limit,
2591 client_id,
2592 params,
2593 handler,
2594 )
2595 }
2596
2597 fn request_funding_rates(
2603 &mut self,
2604 instrument_id: InstrumentId,
2605 start: Option<DateTime<Utc>>,
2606 end: Option<DateTime<Utc>>,
2607 limit: Option<NonZeroUsize>,
2608 client_id: Option<ClientId>,
2609 params: Option<Params>,
2610 ) -> anyhow::Result<UUID4>
2611 where
2612 Self: DataActorNative,
2613 Self: 'static + Debug + Sized,
2614 {
2615 let actor_id = self.core().actor_id().inner();
2616 let handler = ShareableMessageHandler::from_typed(move |resp: &FundingRatesResponse| {
2617 get_actor_unchecked::<Self>(&actor_id).handle_funding_rates_response(resp);
2618 });
2619
2620 DataActorCore::request_funding_rates(
2621 self.core_mut(),
2622 instrument_id,
2623 start,
2624 end,
2625 limit,
2626 client_id,
2627 params,
2628 handler,
2629 )
2630 }
2631}
2632
2633impl<T> Actor for T
2635where
2636 T: DataActor + DataActorNative + Debug + 'static,
2637{
2638 fn id(&self) -> Ustr {
2639 self.core().actor_id.inner()
2640 }
2641
2642 #[allow(unused_variables)]
2643 fn handle(&mut self, msg: &dyn Any) {
2644 }
2646
2647 fn as_any(&self) -> &dyn Any {
2648 self
2649 }
2650}
2651
2652impl<T> Component for T
2654where
2655 T: DataActor + DataActorNative + Debug + 'static,
2656{
2657 fn component_id(&self) -> ComponentId {
2658 ComponentId::new(self.core().actor_id.inner().as_str())
2659 }
2660
2661 fn state(&self) -> ComponentState {
2662 self.core().state
2663 }
2664
2665 fn transition_state(&mut self, trigger: ComponentTrigger) -> anyhow::Result<()> {
2666 let core = self.core_mut();
2667 core.state = core.state.transition(&trigger)?;
2668 log::info!(
2669 component = core.actor_id.inner().as_str();
2670 "{}",
2671 core.state.variant_name()
2672 );
2673 Ok(())
2674 }
2675
2676 fn register(
2677 &mut self,
2678 trader_id: TraderId,
2679 clock: Rc<RefCell<dyn Clock>>,
2680 cache: Rc<RefCell<Cache>>,
2681 ) -> anyhow::Result<()> {
2682 DataActorCore::register(self.core_mut(), trader_id, clock.clone(), cache)?;
2683
2684 let actor_id = self.core().actor_id().inner();
2686 let callback = TimeEventCallback::from(move |event: TimeEvent| {
2687 if let Some(mut actor) = try_get_actor_unchecked::<Self>(&actor_id) {
2688 actor.handle_time_event(&event);
2689 } else {
2690 log::error!("Actor {actor_id} not found for time event handling");
2691 }
2692 });
2693
2694 clock.borrow_mut().register_default_handler(callback);
2695
2696 self.initialize()
2697 }
2698
2699 fn on_start(&mut self) -> anyhow::Result<()> {
2700 DataActor::on_start(self)
2701 }
2702
2703 fn on_stop(&mut self) -> anyhow::Result<()> {
2704 DataActor::on_stop(self)
2705 }
2706
2707 fn on_resume(&mut self) -> anyhow::Result<()> {
2708 DataActor::on_resume(self)
2709 }
2710
2711 fn on_degrade(&mut self) -> anyhow::Result<()> {
2712 DataActor::on_degrade(self)
2713 }
2714
2715 fn on_fault(&mut self) -> anyhow::Result<()> {
2716 DataActor::on_fault(self)
2717 }
2718
2719 fn on_reset(&mut self) -> anyhow::Result<()> {
2720 DataActor::on_reset(self)
2721 }
2722
2723 fn on_dispose(&mut self) -> anyhow::Result<()> {
2724 DataActor::on_dispose(self)
2725 }
2726}
2727
2728#[derive(Clone)]
2730#[allow(
2731 dead_code,
2732 reason = "TODO: Under development (pending_requests, signal_classes)"
2733)]
2734pub struct DataActorCore {
2735 pub actor_id: ActorId,
2737 pub config: DataActorConfig,
2739 trader_id: Option<TraderId>,
2740 clock: Option<Rc<RefCell<dyn Clock>>>, cache: Option<Rc<RefCell<Cache>>>, state: ComponentState,
2743 topic_handlers: AHashMap<MStr<Pattern>, ShareableMessageHandler>,
2744 instrument_handlers: AHashMap<MStr<Pattern>, TypedHandler<InstrumentAny>>,
2745 deltas_handlers: AHashMap<MStr<Pattern>, TypedHandler<OrderBookDeltas>>,
2746 depth10_handlers: AHashMap<MStr<Pattern>, TypedHandler<OrderBookDepth10>>,
2747 book_handlers: AHashMap<MStr<Topic>, TypedHandler<OrderBook>>,
2748 quote_handlers: AHashMap<MStr<Topic>, TypedHandler<QuoteTick>>,
2749 trade_handlers: AHashMap<MStr<Topic>, TypedHandler<TradeTick>>,
2750 bar_handlers: AHashMap<MStr<Topic>, TypedHandler<Bar>>,
2751 mark_price_handlers: AHashMap<MStr<Topic>, TypedHandler<MarkPriceUpdate>>,
2752 index_price_handlers: AHashMap<MStr<Topic>, TypedHandler<IndexPriceUpdate>>,
2753 funding_rate_handlers: AHashMap<MStr<Topic>, TypedHandler<FundingRateUpdate>>,
2754 option_greeks_handlers: AHashMap<MStr<Topic>, TypedHandler<OptionGreeks>>,
2755 option_chain_handlers: AHashMap<MStr<Topic>, TypedHandler<OptionChainSlice>>,
2756 #[cfg(feature = "defi")]
2757 block_handlers: AHashMap<MStr<Topic>, TypedHandler<Block>>,
2758 #[cfg(feature = "defi")]
2759 pool_handlers: AHashMap<MStr<Topic>, TypedHandler<Pool>>,
2760 #[cfg(feature = "defi")]
2761 pool_swap_handlers: AHashMap<MStr<Topic>, TypedHandler<PoolSwap>>,
2762 #[cfg(feature = "defi")]
2763 pool_liquidity_handlers: AHashMap<MStr<Topic>, TypedHandler<PoolLiquidityUpdate>>,
2764 #[cfg(feature = "defi")]
2765 pool_collect_handlers: AHashMap<MStr<Topic>, TypedHandler<PoolFeeCollect>>,
2766 #[cfg(feature = "defi")]
2767 pool_flash_handlers: AHashMap<MStr<Topic>, TypedHandler<PoolFlash>>,
2768 warning_events: AHashSet<String>, pending_requests: AHashMap<UUID4, Option<RequestCallback>>,
2770 signal_classes: AHashMap<String, String>,
2771 indicators: Indicators,
2772}
2773
2774impl Debug for DataActorCore {
2775 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2776 f.debug_struct(stringify!(DataActorCore))
2777 .field("actor_id", &self.actor_id)
2778 .field("config", &self.config)
2779 .field("state", &self.state)
2780 .field("trader_id", &self.trader_id)
2781 .finish()
2782 }
2783}
2784
2785impl DataActorCore {
2786 pub(crate) fn add_subscription_any(
2790 &mut self,
2791 topic: MStr<Topic>,
2792 handler: ShareableMessageHandler,
2793 ) {
2794 let pattern: MStr<Pattern> = topic.into();
2795 if self.topic_handlers.contains_key(&pattern) {
2796 log::warn!(
2797 "Actor {} attempted duplicate subscription to topic '{topic}'",
2798 self.actor_id,
2799 );
2800 return;
2801 }
2802
2803 self.topic_handlers.insert(pattern, handler.clone());
2804 msgbus::subscribe_any(pattern, handler, None);
2805 }
2806
2807 pub(crate) fn remove_subscription_any(&mut self, topic: MStr<Topic>) {
2811 let pattern: MStr<Pattern> = topic.into();
2812 if let Some(handler) = self.topic_handlers.remove(&pattern) {
2813 msgbus::unsubscribe_any(pattern, &handler);
2814 } else {
2815 log::warn!(
2816 "Actor {} attempted to unsubscribe from topic '{topic}' when not subscribed",
2817 self.actor_id,
2818 );
2819 }
2820 }
2821
2822 pub(crate) fn add_quote_subscription(
2823 &mut self,
2824 topic: MStr<Topic>,
2825 handler: TypedHandler<QuoteTick>,
2826 ) {
2827 if self.quote_handlers.contains_key(&topic) {
2828 log::warn!(
2829 "Actor {} attempted duplicate quote subscription to '{topic}'",
2830 self.actor_id
2831 );
2832 return;
2833 }
2834 self.quote_handlers.insert(topic, handler.clone());
2835 msgbus::subscribe_quotes(topic.into(), handler, None);
2836 }
2837
2838 #[allow(dead_code)]
2839 pub(crate) fn remove_quote_subscription(&mut self, topic: MStr<Topic>) {
2840 if let Some(handler) = self.quote_handlers.remove(&topic) {
2841 msgbus::unsubscribe_quotes(topic.into(), &handler);
2842 }
2843 }
2844
2845 pub(crate) fn add_trade_subscription(
2846 &mut self,
2847 topic: MStr<Topic>,
2848 handler: TypedHandler<TradeTick>,
2849 ) {
2850 if self.trade_handlers.contains_key(&topic) {
2851 log::warn!(
2852 "Actor {} attempted duplicate trade subscription to '{topic}'",
2853 self.actor_id
2854 );
2855 return;
2856 }
2857 self.trade_handlers.insert(topic, handler.clone());
2858 msgbus::subscribe_trades(topic.into(), handler, None);
2859 }
2860
2861 #[allow(dead_code)]
2862 pub(crate) fn remove_trade_subscription(&mut self, topic: MStr<Topic>) {
2863 if let Some(handler) = self.trade_handlers.remove(&topic) {
2864 msgbus::unsubscribe_trades(topic.into(), &handler);
2865 }
2866 }
2867
2868 pub(crate) fn add_bar_subscription(&mut self, topic: MStr<Topic>, handler: TypedHandler<Bar>) {
2869 if self.bar_handlers.contains_key(&topic) {
2870 log::warn!(
2871 "Actor {} attempted duplicate bar subscription to '{topic}'",
2872 self.actor_id
2873 );
2874 return;
2875 }
2876 self.bar_handlers.insert(topic, handler.clone());
2877 msgbus::subscribe_bars(topic.into(), handler, None);
2878 }
2879
2880 #[allow(dead_code)]
2881 pub(crate) fn remove_bar_subscription(&mut self, topic: MStr<Topic>) {
2882 if let Some(handler) = self.bar_handlers.remove(&topic) {
2883 msgbus::unsubscribe_bars(topic.into(), &handler);
2884 }
2885 }
2886
2887 pub(crate) fn add_deltas_subscription(
2888 &mut self,
2889 pattern: MStr<Pattern>,
2890 handler: TypedHandler<OrderBookDeltas>,
2891 ) {
2892 if self.deltas_handlers.contains_key(&pattern) {
2893 log::warn!(
2894 "Actor {} attempted duplicate deltas subscription to '{pattern}'",
2895 self.actor_id
2896 );
2897 return;
2898 }
2899 self.deltas_handlers.insert(pattern, handler.clone());
2900 msgbus::subscribe_book_deltas(pattern, handler, None);
2901 }
2902
2903 #[allow(dead_code)]
2904 pub(crate) fn remove_deltas_subscription(&mut self, pattern: MStr<Pattern>) {
2905 if let Some(handler) = self.deltas_handlers.remove(&pattern) {
2906 msgbus::unsubscribe_book_deltas(pattern, &handler);
2907 }
2908 }
2909
2910 pub(crate) fn add_depth10_subscription(
2911 &mut self,
2912 pattern: MStr<Pattern>,
2913 handler: TypedHandler<OrderBookDepth10>,
2914 ) {
2915 if self.depth10_handlers.contains_key(&pattern) {
2916 log::warn!(
2917 "Actor {} attempted duplicate depth10 subscription to '{pattern}'",
2918 self.actor_id
2919 );
2920 return;
2921 }
2922 self.depth10_handlers.insert(pattern, handler.clone());
2923 msgbus::subscribe_book_depth10(pattern, handler, None);
2924 }
2925
2926 pub(crate) fn remove_depth10_subscription(&mut self, pattern: MStr<Pattern>) {
2927 if let Some(handler) = self.depth10_handlers.remove(&pattern) {
2928 msgbus::unsubscribe_book_depth10(pattern, &handler);
2929 }
2930 }
2931
2932 pub(crate) fn add_instrument_subscription(
2933 &mut self,
2934 pattern: MStr<Pattern>,
2935 handler: TypedHandler<InstrumentAny>,
2936 ) {
2937 if self.instrument_handlers.contains_key(&pattern) {
2938 log::warn!(
2939 "Actor {} attempted duplicate instrument subscription to '{pattern}'",
2940 self.actor_id
2941 );
2942 return;
2943 }
2944 self.instrument_handlers.insert(pattern, handler.clone());
2945 msgbus::subscribe_instruments(pattern, handler, None);
2946 }
2947
2948 #[allow(dead_code)]
2949 pub(crate) fn remove_instrument_subscription(&mut self, pattern: MStr<Pattern>) {
2950 if let Some(handler) = self.instrument_handlers.remove(&pattern) {
2951 msgbus::unsubscribe_instruments(pattern, &handler);
2952 }
2953 }
2954
2955 pub(crate) fn add_instrument_close_subscription(
2956 &mut self,
2957 topic: MStr<Topic>,
2958 handler: ShareableMessageHandler,
2959 ) {
2960 let pattern: MStr<Pattern> = topic.into();
2961 if self.topic_handlers.contains_key(&pattern) {
2962 log::warn!(
2963 "Actor {} attempted duplicate instrument close subscription to '{topic}'",
2964 self.actor_id
2965 );
2966 return;
2967 }
2968 self.topic_handlers.insert(pattern, handler.clone());
2969 msgbus::subscribe_any(pattern, handler, None);
2970 }
2971
2972 #[allow(dead_code)]
2973 pub(crate) fn remove_instrument_close_subscription(&mut self, topic: MStr<Topic>) {
2974 let pattern: MStr<Pattern> = topic.into();
2975 if let Some(handler) = self.topic_handlers.remove(&pattern) {
2976 msgbus::unsubscribe_any(pattern, &handler);
2977 }
2978 }
2979
2980 pub(crate) fn add_book_snapshot_subscription(
2981 &mut self,
2982 topic: MStr<Topic>,
2983 handler: TypedHandler<OrderBook>,
2984 ) {
2985 if self.book_handlers.contains_key(&topic) {
2986 log::warn!(
2987 "Actor {} attempted duplicate book snapshot subscription to '{topic}'",
2988 self.actor_id
2989 );
2990 return;
2991 }
2992 self.book_handlers.insert(topic, handler.clone());
2993 msgbus::subscribe_book_snapshots(topic.into(), handler, None);
2994 }
2995
2996 #[allow(dead_code)]
2997 pub(crate) fn remove_book_snapshot_subscription(&mut self, topic: MStr<Topic>) {
2998 if let Some(handler) = self.book_handlers.remove(&topic) {
2999 msgbus::unsubscribe_book_snapshots(topic.into(), &handler);
3000 }
3001 }
3002
3003 pub(crate) fn add_mark_price_subscription(
3004 &mut self,
3005 topic: MStr<Topic>,
3006 handler: TypedHandler<MarkPriceUpdate>,
3007 ) {
3008 if self.mark_price_handlers.contains_key(&topic) {
3009 log::warn!(
3010 "Actor {} attempted duplicate mark price subscription to '{topic}'",
3011 self.actor_id
3012 );
3013 return;
3014 }
3015 self.mark_price_handlers.insert(topic, handler.clone());
3016 msgbus::subscribe_mark_prices(topic.into(), handler, None);
3017 }
3018
3019 #[allow(dead_code)]
3020 pub(crate) fn remove_mark_price_subscription(&mut self, topic: MStr<Topic>) {
3021 if let Some(handler) = self.mark_price_handlers.remove(&topic) {
3022 msgbus::unsubscribe_mark_prices(topic.into(), &handler);
3023 }
3024 }
3025
3026 pub(crate) fn add_index_price_subscription(
3027 &mut self,
3028 topic: MStr<Topic>,
3029 handler: TypedHandler<IndexPriceUpdate>,
3030 ) {
3031 if self.index_price_handlers.contains_key(&topic) {
3032 log::warn!(
3033 "Actor {} attempted duplicate index price subscription to '{topic}'",
3034 self.actor_id
3035 );
3036 return;
3037 }
3038 self.index_price_handlers.insert(topic, handler.clone());
3039 msgbus::subscribe_index_prices(topic.into(), handler, None);
3040 }
3041
3042 #[allow(dead_code)]
3043 pub(crate) fn remove_index_price_subscription(&mut self, topic: MStr<Topic>) {
3044 if let Some(handler) = self.index_price_handlers.remove(&topic) {
3045 msgbus::unsubscribe_index_prices(topic.into(), &handler);
3046 }
3047 }
3048
3049 pub(crate) fn add_funding_rate_subscription(
3050 &mut self,
3051 topic: MStr<Topic>,
3052 handler: TypedHandler<FundingRateUpdate>,
3053 ) {
3054 if self.funding_rate_handlers.contains_key(&topic) {
3055 log::warn!(
3056 "Actor {} attempted duplicate funding rate subscription to '{topic}'",
3057 self.actor_id
3058 );
3059 return;
3060 }
3061 self.funding_rate_handlers.insert(topic, handler.clone());
3062 msgbus::subscribe_funding_rates(topic.into(), handler, None);
3063 }
3064
3065 #[allow(dead_code)]
3066 pub(crate) fn remove_funding_rate_subscription(&mut self, topic: MStr<Topic>) {
3067 if let Some(handler) = self.funding_rate_handlers.remove(&topic) {
3068 msgbus::unsubscribe_funding_rates(topic.into(), &handler);
3069 }
3070 }
3071
3072 pub(crate) fn add_option_greeks_subscription(
3073 &mut self,
3074 topic: MStr<Topic>,
3075 handler: TypedHandler<OptionGreeks>,
3076 ) {
3077 if self.option_greeks_handlers.contains_key(&topic) {
3078 log::warn!(
3079 "Actor {} attempted duplicate option greeks subscription to '{topic}'",
3080 self.actor_id
3081 );
3082 return;
3083 }
3084 self.option_greeks_handlers.insert(topic, handler.clone());
3085 msgbus::subscribe_option_greeks(topic.into(), handler, None);
3086 }
3087
3088 #[allow(dead_code)]
3089 pub(crate) fn remove_option_greeks_subscription(&mut self, topic: MStr<Topic>) {
3090 if let Some(handler) = self.option_greeks_handlers.remove(&topic) {
3091 msgbus::unsubscribe_option_greeks(topic.into(), &handler);
3092 }
3093 }
3094
3095 pub(crate) fn add_option_chain_subscription(
3096 &mut self,
3097 topic: MStr<Topic>,
3098 handler: TypedHandler<OptionChainSlice>,
3099 ) {
3100 if self.option_chain_handlers.contains_key(&topic) {
3101 log::warn!(
3102 "Actor {} attempted duplicate option chain subscription to '{topic}'",
3103 self.actor_id
3104 );
3105 return;
3106 }
3107 self.option_chain_handlers.insert(topic, handler.clone());
3108 msgbus::subscribe_option_chain(topic.into(), handler, None);
3109 }
3110
3111 pub(crate) fn remove_option_chain_subscription(&mut self, topic: MStr<Topic>) {
3112 if let Some(handler) = self.option_chain_handlers.remove(&topic) {
3113 msgbus::unsubscribe_option_chain(topic.into(), &handler);
3114 }
3115 }
3116
3117 #[cfg(feature = "defi")]
3118 pub(crate) fn add_block_subscription(
3119 &mut self,
3120 topic: MStr<Topic>,
3121 handler: TypedHandler<Block>,
3122 ) {
3123 if self.block_handlers.contains_key(&topic) {
3124 log::warn!(
3125 "Actor {} attempted duplicate block subscription to '{topic}'",
3126 self.actor_id
3127 );
3128 return;
3129 }
3130 self.block_handlers.insert(topic, handler.clone());
3131 msgbus::subscribe_defi_blocks(topic.into(), handler, None);
3132 }
3133
3134 #[cfg(feature = "defi")]
3135 #[allow(dead_code)]
3136 pub(crate) fn remove_block_subscription(&mut self, topic: MStr<Topic>) {
3137 if let Some(handler) = self.block_handlers.remove(&topic) {
3138 msgbus::unsubscribe_defi_blocks(topic.into(), &handler);
3139 }
3140 }
3141
3142 #[cfg(feature = "defi")]
3143 pub(crate) fn add_pool_subscription(
3144 &mut self,
3145 topic: MStr<Topic>,
3146 handler: TypedHandler<Pool>,
3147 ) {
3148 if self.pool_handlers.contains_key(&topic) {
3149 log::warn!(
3150 "Actor {} attempted duplicate pool subscription to '{topic}'",
3151 self.actor_id
3152 );
3153 return;
3154 }
3155 self.pool_handlers.insert(topic, handler.clone());
3156 msgbus::subscribe_defi_pools(topic.into(), handler, None);
3157 }
3158
3159 #[cfg(feature = "defi")]
3160 #[allow(dead_code)]
3161 pub(crate) fn remove_pool_subscription(&mut self, topic: MStr<Topic>) {
3162 if let Some(handler) = self.pool_handlers.remove(&topic) {
3163 msgbus::unsubscribe_defi_pools(topic.into(), &handler);
3164 }
3165 }
3166
3167 #[cfg(feature = "defi")]
3168 pub(crate) fn add_pool_swap_subscription(
3169 &mut self,
3170 topic: MStr<Topic>,
3171 handler: TypedHandler<PoolSwap>,
3172 ) {
3173 if self.pool_swap_handlers.contains_key(&topic) {
3174 log::warn!(
3175 "Actor {} attempted duplicate pool swap subscription to '{topic}'",
3176 self.actor_id
3177 );
3178 return;
3179 }
3180 self.pool_swap_handlers.insert(topic, handler.clone());
3181 msgbus::subscribe_defi_swaps(topic.into(), handler, None);
3182 }
3183
3184 #[cfg(feature = "defi")]
3185 #[allow(dead_code)]
3186 pub(crate) fn remove_pool_swap_subscription(&mut self, topic: MStr<Topic>) {
3187 if let Some(handler) = self.pool_swap_handlers.remove(&topic) {
3188 msgbus::unsubscribe_defi_swaps(topic.into(), &handler);
3189 }
3190 }
3191
3192 #[cfg(feature = "defi")]
3193 pub(crate) fn add_pool_liquidity_subscription(
3194 &mut self,
3195 topic: MStr<Topic>,
3196 handler: TypedHandler<PoolLiquidityUpdate>,
3197 ) {
3198 if self.pool_liquidity_handlers.contains_key(&topic) {
3199 log::warn!(
3200 "Actor {} attempted duplicate pool liquidity subscription to '{topic}'",
3201 self.actor_id
3202 );
3203 return;
3204 }
3205 self.pool_liquidity_handlers.insert(topic, handler.clone());
3206 msgbus::subscribe_defi_liquidity(topic.into(), handler, None);
3207 }
3208
3209 #[cfg(feature = "defi")]
3210 #[allow(dead_code)]
3211 pub(crate) fn remove_pool_liquidity_subscription(&mut self, topic: MStr<Topic>) {
3212 if let Some(handler) = self.pool_liquidity_handlers.remove(&topic) {
3213 msgbus::unsubscribe_defi_liquidity(topic.into(), &handler);
3214 }
3215 }
3216
3217 #[cfg(feature = "defi")]
3218 pub(crate) fn add_pool_collect_subscription(
3219 &mut self,
3220 topic: MStr<Topic>,
3221 handler: TypedHandler<PoolFeeCollect>,
3222 ) {
3223 if self.pool_collect_handlers.contains_key(&topic) {
3224 log::warn!(
3225 "Actor {} attempted duplicate pool collect subscription to '{topic}'",
3226 self.actor_id
3227 );
3228 return;
3229 }
3230 self.pool_collect_handlers.insert(topic, handler.clone());
3231 msgbus::subscribe_defi_collects(topic.into(), handler, None);
3232 }
3233
3234 #[cfg(feature = "defi")]
3235 #[allow(dead_code)]
3236 pub(crate) fn remove_pool_collect_subscription(&mut self, topic: MStr<Topic>) {
3237 if let Some(handler) = self.pool_collect_handlers.remove(&topic) {
3238 msgbus::unsubscribe_defi_collects(topic.into(), &handler);
3239 }
3240 }
3241
3242 #[cfg(feature = "defi")]
3243 pub(crate) fn add_pool_flash_subscription(
3244 &mut self,
3245 topic: MStr<Topic>,
3246 handler: TypedHandler<PoolFlash>,
3247 ) {
3248 if self.pool_flash_handlers.contains_key(&topic) {
3249 log::warn!(
3250 "Actor {} attempted duplicate pool flash subscription to '{topic}'",
3251 self.actor_id
3252 );
3253 return;
3254 }
3255 self.pool_flash_handlers.insert(topic, handler.clone());
3256 msgbus::subscribe_defi_flash(topic.into(), handler, None);
3257 }
3258
3259 #[cfg(feature = "defi")]
3260 #[allow(dead_code)]
3261 pub(crate) fn remove_pool_flash_subscription(&mut self, topic: MStr<Topic>) {
3262 if let Some(handler) = self.pool_flash_handlers.remove(&topic) {
3263 msgbus::unsubscribe_defi_flash(topic.into(), &handler);
3264 }
3265 }
3266
3267 pub fn new(config: DataActorConfig) -> Self {
3269 let actor_id = config
3270 .actor_id
3271 .unwrap_or_else(|| Self::default_actor_id(&config));
3272
3273 Self {
3274 actor_id,
3275 config,
3276 trader_id: None, clock: None, cache: None, state: ComponentState::default(),
3280 topic_handlers: AHashMap::new(),
3281 instrument_handlers: AHashMap::new(),
3282 deltas_handlers: AHashMap::new(),
3283 depth10_handlers: AHashMap::new(),
3284 book_handlers: AHashMap::new(),
3285 quote_handlers: AHashMap::new(),
3286 trade_handlers: AHashMap::new(),
3287 bar_handlers: AHashMap::new(),
3288 mark_price_handlers: AHashMap::new(),
3289 index_price_handlers: AHashMap::new(),
3290 funding_rate_handlers: AHashMap::new(),
3291 option_greeks_handlers: AHashMap::new(),
3292 option_chain_handlers: AHashMap::new(),
3293 #[cfg(feature = "defi")]
3294 block_handlers: AHashMap::new(),
3295 #[cfg(feature = "defi")]
3296 pool_handlers: AHashMap::new(),
3297 #[cfg(feature = "defi")]
3298 pool_swap_handlers: AHashMap::new(),
3299 #[cfg(feature = "defi")]
3300 pool_liquidity_handlers: AHashMap::new(),
3301 #[cfg(feature = "defi")]
3302 pool_collect_handlers: AHashMap::new(),
3303 #[cfg(feature = "defi")]
3304 pool_flash_handlers: AHashMap::new(),
3305 warning_events: AHashSet::new(),
3306 pending_requests: AHashMap::new(),
3307 signal_classes: AHashMap::new(),
3308 indicators: Indicators::default(),
3309 }
3310 }
3311
3312 #[must_use]
3314 pub fn registered_indicators(&self) -> Vec<SharedActorIndicator> {
3315 self.indicators.registered_indicators()
3316 }
3317
3318 pub fn indicators_initialized(&self) -> anyhow::Result<bool> {
3324 self.indicators.initialized()
3325 }
3326
3327 pub fn register_indicator_for_quote_ticks(
3329 &mut self,
3330 instrument_id: InstrumentId,
3331 indicator: SharedActorIndicator,
3332 ) {
3333 self.indicators
3334 .register_indicator_for_quote_ticks(instrument_id, indicator);
3335 }
3336
3337 pub fn register_indicator_for_trade_ticks(
3339 &mut self,
3340 instrument_id: InstrumentId,
3341 indicator: SharedActorIndicator,
3342 ) {
3343 self.indicators
3344 .register_indicator_for_trade_ticks(instrument_id, indicator);
3345 }
3346
3347 pub fn register_indicator_for_bars(
3349 &mut self,
3350 bar_type: BarType,
3351 indicator: SharedActorIndicator,
3352 ) {
3353 self.indicators
3354 .register_indicator_for_bars(bar_type, indicator);
3355 }
3356
3357 pub(crate) fn handle_indicators_for_quote(&self, quote: &QuoteTick) -> anyhow::Result<()> {
3358 self.indicators.handle_quote(quote)
3359 }
3360
3361 pub(crate) fn handle_indicators_for_quotes(&self, quotes: &[QuoteTick]) -> anyhow::Result<()> {
3362 self.indicators.handle_quotes(quotes)
3363 }
3364
3365 pub(crate) fn handle_indicators_for_trade(&self, trade: &TradeTick) -> anyhow::Result<()> {
3366 self.indicators.handle_trade(trade)
3367 }
3368
3369 pub(crate) fn handle_indicators_for_trades(&self, trades: &[TradeTick]) -> anyhow::Result<()> {
3370 self.indicators.handle_trades(trades)
3371 }
3372
3373 pub(crate) fn handle_indicators_for_bar(&self, bar: &Bar) -> anyhow::Result<()> {
3374 self.indicators.handle_bar(bar)
3375 }
3376
3377 pub(crate) fn handle_indicators_for_bars(&self, bars: &[Bar]) -> anyhow::Result<()> {
3378 self.indicators.handle_bars(bars)
3379 }
3380
3381 #[must_use]
3383 pub fn mem_address(&self) -> String {
3384 format!("{self:p}")
3385 }
3386
3387 pub fn state(&self) -> ComponentState {
3389 self.state
3390 }
3391
3392 pub fn trader_id(&self) -> Option<TraderId> {
3394 self.trader_id
3395 }
3396
3397 pub fn actor_id(&self) -> ActorId {
3399 self.actor_id
3400 }
3401
3402 fn default_actor_id(config: &DataActorConfig) -> ActorId {
3403 let memory_address = std::ptr::from_ref(config) as usize;
3404 ActorId::from(format!("{}-{memory_address}", stringify!(DataActor)))
3405 }
3406
3407 pub fn timestamp_ns(&self) -> UnixNanos {
3409 self.clock_ref().timestamp_ns()
3410 }
3411
3412 fn clock_api(&self) -> ClockApi<'_> {
3413 let clock = self.clock.as_ref().unwrap_or_else(|| {
3414 panic!(
3415 "DataActor {} must be registered before calling `clock()` - trader_id: {:?}",
3416 self.actor_id, self.trader_id
3417 )
3418 });
3419 ClockApi::new(clock.as_ref())
3420 }
3421
3422 fn clock_ref(&self) -> Ref<'_, dyn Clock> {
3423 self.clock
3424 .as_ref()
3425 .unwrap_or_else(|| {
3426 panic!(
3427 "DataActor {} must be registered before calling `clock_ref()` - trader_id: {:?}",
3428 self.actor_id, self.trader_id
3429 )
3430 })
3431 .borrow()
3432 }
3433
3434 fn cache_api(&self) -> CacheApi<'_> {
3435 let cache = self.cache.as_ref().unwrap_or_else(|| {
3436 panic!(
3437 "DataActor {} must be registered before calling `cache()` - trader_id: {:?}",
3438 self.actor_id, self.trader_id
3439 )
3440 });
3441 CacheApi::new(cache.as_ref())
3442 }
3443
3444 pub fn register(
3451 &mut self,
3452 trader_id: TraderId,
3453 clock: Rc<RefCell<dyn Clock>>,
3454 cache: Rc<RefCell<Cache>>,
3455 ) -> anyhow::Result<()> {
3456 if let Some(existing_trader_id) = self.trader_id {
3457 anyhow::bail!(
3458 "DataActor {} already registered with trader {existing_trader_id}",
3459 self.actor_id
3460 );
3461 }
3462
3463 {
3465 let _timestamp = clock.borrow().timestamp_ns();
3466 }
3467
3468 {
3470 let _cache_borrow = cache.borrow();
3471 }
3472
3473 self.trader_id = Some(trader_id);
3474 self.clock = Some(clock);
3475 self.cache = Some(cache);
3476
3477 if !self.is_properly_registered() {
3479 anyhow::bail!(
3480 "DataActor {} registration incomplete - validation failed",
3481 self.actor_id
3482 );
3483 }
3484
3485 log::debug!("Registered {} with trader {trader_id}", self.actor_id);
3486 Ok(())
3487 }
3488
3489 pub fn register_warning_event(&mut self, event_type: &str) {
3491 self.warning_events.insert(event_type.to_string());
3492 log::debug!("Registered event type '{event_type}' for warning logs");
3493 }
3494
3495 pub fn deregister_warning_event(&mut self, event_type: &str) {
3497 self.warning_events.remove(event_type);
3498 log::debug!("Deregistered event type '{event_type}' from warning logs");
3499 }
3500
3501 pub fn is_registered(&self) -> bool {
3502 self.trader_id.is_some()
3503 }
3504
3505 pub(crate) fn check_registered(&self) {
3506 assert!(
3507 self.is_registered(),
3508 "Actor has not been registered with a Trader"
3509 );
3510 }
3511
3512 fn is_properly_registered(&self) -> bool {
3514 self.trader_id.is_some() && self.clock.is_some() && self.cache.is_some()
3515 }
3516
3517 pub(crate) fn send_data_cmd(&self, command: DataCommand) {
3518 if self.config.log_commands {
3519 log::info!("{CMD}{SEND} {command:?}");
3520 }
3521
3522 let endpoint = MessagingSwitchboard::data_engine_queue_execute();
3523 msgbus::send_data_command(endpoint, command);
3524 }
3525
3526 #[allow(dead_code)]
3527 fn send_data_req(&self, request: &RequestCommand) {
3528 if self.config.log_commands {
3529 log::info!("{REQ}{SEND} {request:?}");
3530 }
3531
3532 let endpoint = MessagingSwitchboard::data_engine_queue_execute();
3535 msgbus::send_any(endpoint, request.as_any());
3536 }
3537
3538 pub fn shutdown_system(&self, reason: Option<String>) {
3544 self.check_registered();
3545
3546 let command = ShutdownSystem::new(
3548 self.trader_id().unwrap(),
3549 self.actor_id.inner(),
3550 reason,
3551 UUID4::new(),
3552 self.timestamp_ns(),
3553 None, );
3555
3556 let topic = MessagingSwitchboard::shutdown_system_topic();
3557 msgbus::publish_any(topic, command.as_any());
3558 }
3559
3560 pub fn publish_data(&self, data_type: &DataType, data: &CustomData) {
3570 self.check_registered();
3571
3572 let topic = get_custom_topic(data_type);
3573 msgbus::publish_any(topic, data);
3574 }
3575
3576 pub fn publish_signal(&self, name: &str, value: String, ts_event: UnixNanos) {
3588 self.check_registered();
3589
3590 let now = self.timestamp_ns();
3591 let ts_event = if ts_event.as_u64() == 0 {
3592 now
3593 } else {
3594 ts_event
3595 };
3596 let signal = Signal::new(Ustr::from(name), value, ts_event, now);
3597
3598 let data_type = DataType::new(
3599 &format!(
3600 "Signal{}",
3601 nautilus_core::string::conversions::title_case(name)
3602 ),
3603 None,
3604 None,
3605 );
3606 let data = CustomData::new(Arc::new(signal), data_type);
3607 let topic = get_custom_topic(&data.data_type);
3608 msgbus::publish_any(topic, &data);
3609 }
3610
3611 pub fn add_synthetic(&self, synthetic: SyntheticInstrument) -> anyhow::Result<()> {
3619 self.check_registered();
3620
3621 let cache = self.cache_rc();
3622 if cache.borrow().synthetic(&synthetic.id).is_some() {
3623 anyhow::bail!("`synthetic` {} already exists", synthetic.id);
3624 }
3625 cache.borrow_mut().add_synthetic(synthetic)
3626 }
3627
3628 pub fn update_synthetic(&self, synthetic: SyntheticInstrument) -> anyhow::Result<()> {
3636 self.check_registered();
3637
3638 let cache = self.cache_rc();
3639 if cache.borrow().synthetic(&synthetic.id).is_none() {
3640 anyhow::bail!("`synthetic` {} does not exist", synthetic.id);
3641 }
3642 cache.borrow_mut().add_synthetic(synthetic)
3643 }
3644
3645 pub fn subscribe_data(
3651 &mut self,
3652 handler: ShareableMessageHandler,
3653 data_type: DataType,
3654 client_id: Option<ClientId>,
3655 params: Option<Params>,
3656 ) {
3657 assert!(
3658 self.is_properly_registered(),
3659 "DataActor {} is not properly registered - trader_id: {:?}, clock: {}, cache: {}",
3660 self.actor_id,
3661 self.trader_id,
3662 self.clock.is_some(),
3663 self.cache.is_some()
3664 );
3665
3666 let topic = get_custom_topic(&data_type);
3667 self.add_subscription_any(topic, handler);
3668
3669 if client_id.is_none() {
3671 return;
3672 }
3673
3674 let command = SubscribeCommand::Data(SubscribeCustomData {
3675 data_type,
3676 client_id,
3677 venue: None,
3678 command_id: UUID4::new(),
3679 ts_init: self.timestamp_ns(),
3680 correlation_id: None,
3681 params,
3682 });
3683
3684 self.send_data_cmd(DataCommand::Subscribe(command));
3685 }
3686
3687 pub fn subscribe_signal(
3695 &mut self,
3696 handler: ShareableMessageHandler,
3697 name: &str,
3698 priority: Option<u32>,
3699 ) {
3700 self.check_registered();
3701
3702 let pattern = get_signal_pattern(name);
3703 if self.topic_handlers.contains_key(&pattern) {
3704 log::warn!(
3705 "Actor {} attempted duplicate signal subscription to '{pattern}'",
3706 self.actor_id,
3707 );
3708 return;
3709 }
3710 self.topic_handlers.insert(pattern, handler.clone());
3711 msgbus::subscribe_any(pattern, handler, priority);
3712 }
3713
3714 pub fn subscribe_quotes(
3716 &mut self,
3717 topic: MStr<Topic>,
3718 handler: TypedHandler<QuoteTick>,
3719 instrument_id: InstrumentId,
3720 client_id: Option<ClientId>,
3721 params: Option<Params>,
3722 ) {
3723 self.check_registered();
3724
3725 self.add_quote_subscription(topic, handler);
3726
3727 let command = SubscribeCommand::Quotes(SubscribeQuotes {
3728 instrument_id,
3729 client_id,
3730 venue: Some(instrument_id.venue),
3731 command_id: UUID4::new(),
3732 ts_init: self.timestamp_ns(),
3733 correlation_id: None,
3734 params,
3735 });
3736
3737 self.send_data_cmd(DataCommand::Subscribe(command));
3738 }
3739
3740 pub fn subscribe_instruments(
3742 &mut self,
3743 pattern: MStr<Pattern>,
3744 handler: TypedHandler<InstrumentAny>,
3745 venue: Venue,
3746 client_id: Option<ClientId>,
3747 params: Option<Params>,
3748 ) {
3749 self.check_registered();
3750
3751 self.add_instrument_subscription(pattern, handler);
3752
3753 let command = SubscribeCommand::Instruments(SubscribeInstruments {
3754 client_id,
3755 venue,
3756 command_id: UUID4::new(),
3757 ts_init: self.timestamp_ns(),
3758 correlation_id: None,
3759 params,
3760 });
3761
3762 self.send_data_cmd(DataCommand::Subscribe(command));
3763 }
3764
3765 pub fn subscribe_instrument(
3767 &mut self,
3768 topic: MStr<Topic>,
3769 handler: TypedHandler<InstrumentAny>,
3770 instrument_id: InstrumentId,
3771 client_id: Option<ClientId>,
3772 params: Option<Params>,
3773 ) {
3774 self.check_registered();
3775
3776 self.add_instrument_subscription(topic.into(), handler);
3777
3778 let command = SubscribeCommand::Instrument(SubscribeInstrument {
3779 instrument_id,
3780 client_id,
3781 venue: Some(instrument_id.venue),
3782 command_id: UUID4::new(),
3783 ts_init: self.timestamp_ns(),
3784 correlation_id: None,
3785 params,
3786 });
3787
3788 self.send_data_cmd(DataCommand::Subscribe(command));
3789 }
3790
3791 #[expect(clippy::too_many_arguments)]
3793 pub fn subscribe_book_deltas(
3794 &mut self,
3795 pattern: MStr<Pattern>,
3796 handler: TypedHandler<OrderBookDeltas>,
3797 instrument_id: InstrumentId,
3798 book_type: BookType,
3799 depth: Option<NonZeroUsize>,
3800 client_id: Option<ClientId>,
3801 managed: bool,
3802 params: Option<Params>,
3803 ) {
3804 self.check_registered();
3805
3806 self.add_deltas_subscription(pattern, handler);
3807
3808 let command = SubscribeCommand::BookDeltas(SubscribeBookDeltas {
3809 instrument_id,
3810 book_type,
3811 client_id,
3812 venue: Some(instrument_id.venue),
3813 command_id: UUID4::new(),
3814 ts_init: self.timestamp_ns(),
3815 depth,
3816 managed,
3817 correlation_id: None,
3818 params,
3819 });
3820
3821 self.send_data_cmd(DataCommand::Subscribe(command));
3822 }
3823
3824 #[expect(clippy::too_many_arguments)]
3826 pub fn subscribe_book_depth10(
3827 &mut self,
3828 pattern: MStr<Pattern>,
3829 handler: TypedHandler<OrderBookDepth10>,
3830 instrument_id: InstrumentId,
3831 book_type: BookType,
3832 client_id: Option<ClientId>,
3833 managed: bool,
3834 params: Option<Params>,
3835 ) {
3836 self.check_registered();
3837
3838 self.add_depth10_subscription(pattern, handler);
3839
3840 let command = SubscribeCommand::BookDepth10(SubscribeBookDepth10 {
3841 instrument_id,
3842 book_type,
3843 client_id,
3844 venue: Some(instrument_id.venue),
3845 command_id: UUID4::new(),
3846 ts_init: self.timestamp_ns(),
3847 depth: NonZeroUsize::new(10),
3848 managed,
3849 correlation_id: None,
3850 params,
3851 });
3852
3853 self.send_data_cmd(DataCommand::Subscribe(command));
3854 }
3855
3856 #[expect(clippy::too_many_arguments)]
3858 pub fn subscribe_book_at_interval(
3859 &mut self,
3860 topic: MStr<Topic>,
3861 handler: TypedHandler<OrderBook>,
3862 instrument_id: InstrumentId,
3863 book_type: BookType,
3864 depth: Option<NonZeroUsize>,
3865 interval_ms: NonZeroUsize,
3866 client_id: Option<ClientId>,
3867 params: Option<Params>,
3868 ) {
3869 self.check_registered();
3870
3871 self.add_book_snapshot_subscription(topic, handler);
3872
3873 let command = SubscribeCommand::BookSnapshots(SubscribeBookSnapshots {
3874 instrument_id,
3875 book_type,
3876 client_id,
3877 venue: Some(instrument_id.venue),
3878 command_id: UUID4::new(),
3879 ts_init: self.timestamp_ns(),
3880 depth,
3881 interval_ms,
3882 correlation_id: None,
3883 params,
3884 });
3885
3886 self.send_data_cmd(DataCommand::Subscribe(command));
3887 }
3888
3889 pub fn subscribe_trades(
3891 &mut self,
3892 topic: MStr<Topic>,
3893 handler: TypedHandler<TradeTick>,
3894 instrument_id: InstrumentId,
3895 client_id: Option<ClientId>,
3896 params: Option<Params>,
3897 ) {
3898 self.check_registered();
3899
3900 self.add_trade_subscription(topic, handler);
3901
3902 let command = SubscribeCommand::Trades(SubscribeTrades {
3903 instrument_id,
3904 client_id,
3905 venue: Some(instrument_id.venue),
3906 command_id: UUID4::new(),
3907 ts_init: self.timestamp_ns(),
3908 correlation_id: None,
3909 params,
3910 });
3911
3912 self.send_data_cmd(DataCommand::Subscribe(command));
3913 }
3914
3915 pub fn subscribe_bars(
3917 &mut self,
3918 topic: MStr<Topic>,
3919 handler: TypedHandler<Bar>,
3920 bar_type: BarType,
3921 client_id: Option<ClientId>,
3922 params: Option<Params>,
3923 ) {
3924 self.check_registered();
3925
3926 self.add_bar_subscription(topic, handler);
3927
3928 let command = SubscribeCommand::Bars(SubscribeBars {
3929 bar_type,
3930 client_id,
3931 venue: Some(bar_type.instrument_id().venue),
3932 command_id: UUID4::new(),
3933 ts_init: self.timestamp_ns(),
3934 correlation_id: None,
3935 params,
3936 });
3937
3938 self.send_data_cmd(DataCommand::Subscribe(command));
3939 }
3940
3941 pub fn subscribe_mark_prices(
3943 &mut self,
3944 topic: MStr<Topic>,
3945 handler: TypedHandler<MarkPriceUpdate>,
3946 instrument_id: InstrumentId,
3947 client_id: Option<ClientId>,
3948 params: Option<Params>,
3949 ) {
3950 self.check_registered();
3951
3952 self.add_mark_price_subscription(topic, handler);
3953
3954 let command = SubscribeCommand::MarkPrices(SubscribeMarkPrices {
3955 instrument_id,
3956 client_id,
3957 venue: Some(instrument_id.venue),
3958 command_id: UUID4::new(),
3959 ts_init: self.timestamp_ns(),
3960 correlation_id: None,
3961 params,
3962 });
3963
3964 self.send_data_cmd(DataCommand::Subscribe(command));
3965 }
3966
3967 pub fn subscribe_index_prices(
3969 &mut self,
3970 topic: MStr<Topic>,
3971 handler: TypedHandler<IndexPriceUpdate>,
3972 instrument_id: InstrumentId,
3973 client_id: Option<ClientId>,
3974 params: Option<Params>,
3975 ) {
3976 self.check_registered();
3977
3978 self.add_index_price_subscription(topic, handler);
3979
3980 let command = SubscribeCommand::IndexPrices(SubscribeIndexPrices {
3981 instrument_id,
3982 client_id,
3983 venue: Some(instrument_id.venue),
3984 command_id: UUID4::new(),
3985 ts_init: self.timestamp_ns(),
3986 correlation_id: None,
3987 params,
3988 });
3989
3990 self.send_data_cmd(DataCommand::Subscribe(command));
3991 }
3992
3993 pub fn subscribe_funding_rates(
3995 &mut self,
3996 topic: MStr<Topic>,
3997 handler: TypedHandler<FundingRateUpdate>,
3998 instrument_id: InstrumentId,
3999 client_id: Option<ClientId>,
4000 params: Option<Params>,
4001 ) {
4002 self.check_registered();
4003
4004 self.add_funding_rate_subscription(topic, handler);
4005
4006 let command = SubscribeCommand::FundingRates(SubscribeFundingRates {
4007 instrument_id,
4008 client_id,
4009 venue: Some(instrument_id.venue),
4010 command_id: UUID4::new(),
4011 ts_init: self.timestamp_ns(),
4012 correlation_id: None,
4013 params,
4014 });
4015
4016 self.send_data_cmd(DataCommand::Subscribe(command));
4017 }
4018
4019 pub fn subscribe_option_greeks(
4021 &mut self,
4022 topic: MStr<Topic>,
4023 handler: TypedHandler<OptionGreeks>,
4024 instrument_id: InstrumentId,
4025 client_id: Option<ClientId>,
4026 params: Option<Params>,
4027 ) {
4028 self.check_registered();
4029
4030 self.add_option_greeks_subscription(topic, handler);
4031
4032 let command = SubscribeCommand::OptionGreeks(SubscribeOptionGreeks {
4033 instrument_id,
4034 client_id,
4035 venue: Some(instrument_id.venue),
4036 command_id: UUID4::new(),
4037 ts_init: self.timestamp_ns(),
4038 correlation_id: None,
4039 params,
4040 });
4041
4042 self.send_data_cmd(DataCommand::Subscribe(command));
4043 }
4044
4045 pub fn subscribe_instrument_status(
4047 &mut self,
4048 topic: MStr<Topic>,
4049 handler: ShareableMessageHandler,
4050 instrument_id: InstrumentId,
4051 client_id: Option<ClientId>,
4052 params: Option<Params>,
4053 ) {
4054 self.check_registered();
4055
4056 self.add_subscription_any(topic, handler);
4057
4058 let command = SubscribeCommand::InstrumentStatus(SubscribeInstrumentStatus {
4059 instrument_id,
4060 client_id,
4061 venue: Some(instrument_id.venue),
4062 command_id: UUID4::new(),
4063 ts_init: self.timestamp_ns(),
4064 correlation_id: None,
4065 params,
4066 });
4067
4068 self.send_data_cmd(DataCommand::Subscribe(command));
4069 }
4070
4071 pub fn subscribe_instrument_close(
4073 &mut self,
4074 topic: MStr<Topic>,
4075 handler: ShareableMessageHandler,
4076 instrument_id: InstrumentId,
4077 client_id: Option<ClientId>,
4078 params: Option<Params>,
4079 ) {
4080 self.check_registered();
4081
4082 self.add_instrument_close_subscription(topic, handler);
4083
4084 let command = SubscribeCommand::InstrumentClose(SubscribeInstrumentClose {
4085 instrument_id,
4086 client_id,
4087 venue: Some(instrument_id.venue),
4088 command_id: UUID4::new(),
4089 ts_init: self.timestamp_ns(),
4090 correlation_id: None,
4091 params,
4092 });
4093
4094 self.send_data_cmd(DataCommand::Subscribe(command));
4095 }
4096
4097 #[expect(
4099 clippy::too_many_arguments,
4100 reason = "subscription command mirrors the option chain request fields"
4101 )]
4102 pub fn subscribe_option_chain(
4103 &mut self,
4104 topic: MStr<Topic>,
4105 handler: TypedHandler<OptionChainSlice>,
4106 series_id: OptionSeriesId,
4107 strike_range: StrikeRange,
4108 snapshot_interval_ms: Option<u64>,
4109 client_id: Option<ClientId>,
4110 params: Option<Params>,
4111 ) {
4112 self.check_registered();
4113
4114 self.add_option_chain_subscription(topic, handler);
4115
4116 let command = SubscribeCommand::OptionChain(SubscribeOptionChain::new(
4117 series_id,
4118 strike_range,
4119 snapshot_interval_ms,
4120 UUID4::new(),
4121 self.timestamp_ns(),
4122 client_id,
4123 Some(series_id.venue),
4124 params,
4125 ));
4126
4127 self.send_data_cmd(DataCommand::Subscribe(command));
4128 }
4129
4130 pub fn unsubscribe_data(
4132 &mut self,
4133 data_type: DataType,
4134 client_id: Option<ClientId>,
4135 params: Option<Params>,
4136 ) {
4137 self.check_registered();
4138
4139 let topic = get_custom_topic(&data_type);
4140 self.remove_subscription_any(topic);
4141
4142 if client_id.is_none() {
4143 return;
4144 }
4145
4146 let command = UnsubscribeCommand::Data(UnsubscribeCustomData {
4147 data_type,
4148 client_id,
4149 venue: None,
4150 command_id: UUID4::new(),
4151 ts_init: self.timestamp_ns(),
4152 correlation_id: None,
4153 params,
4154 });
4155
4156 self.send_data_cmd(DataCommand::Unsubscribe(command));
4157 }
4158
4159 pub fn unsubscribe_signal(&mut self, name: &str) {
4165 self.check_registered();
4166
4167 let pattern = get_signal_pattern(name);
4168 if let Some(handler) = self.topic_handlers.remove(&pattern) {
4169 msgbus::unsubscribe_any(pattern, &handler);
4170 } else {
4171 log::warn!(
4172 "Actor {} attempted to unsubscribe from signal pattern '{pattern}' when not subscribed",
4173 self.actor_id,
4174 );
4175 }
4176 }
4177
4178 pub fn unsubscribe_instruments(
4180 &mut self,
4181 venue: Venue,
4182 client_id: Option<ClientId>,
4183 params: Option<Params>,
4184 ) {
4185 self.check_registered();
4186
4187 let pattern = get_instruments_pattern(venue);
4188 self.remove_instrument_subscription(pattern);
4189
4190 let command = UnsubscribeCommand::Instruments(UnsubscribeInstruments {
4191 client_id,
4192 venue,
4193 command_id: UUID4::new(),
4194 ts_init: self.timestamp_ns(),
4195 correlation_id: None,
4196 params,
4197 });
4198
4199 self.send_data_cmd(DataCommand::Unsubscribe(command));
4200 }
4201
4202 pub fn unsubscribe_instrument(
4204 &mut self,
4205 instrument_id: InstrumentId,
4206 client_id: Option<ClientId>,
4207 params: Option<Params>,
4208 ) {
4209 self.check_registered();
4210
4211 let topic = get_instrument_topic(instrument_id);
4212 self.remove_instrument_subscription(topic.into());
4213
4214 let command = UnsubscribeCommand::Instrument(UnsubscribeInstrument {
4215 instrument_id,
4216 client_id,
4217 venue: Some(instrument_id.venue),
4218 command_id: UUID4::new(),
4219 ts_init: self.timestamp_ns(),
4220 correlation_id: None,
4221 params,
4222 });
4223
4224 self.send_data_cmd(DataCommand::Unsubscribe(command));
4225 }
4226
4227 pub fn unsubscribe_book_deltas(
4229 &mut self,
4230 instrument_id: InstrumentId,
4231 client_id: Option<ClientId>,
4232 params: Option<Params>,
4233 ) {
4234 self.check_registered();
4235
4236 let pattern = if is_parent_subscription(params.as_ref()) {
4237 get_book_deltas_pattern(instrument_id)
4238 } else {
4239 get_book_deltas_topic(instrument_id).into()
4240 };
4241 self.remove_deltas_subscription(pattern);
4242
4243 let command = UnsubscribeCommand::BookDeltas(UnsubscribeBookDeltas {
4244 instrument_id,
4245 client_id,
4246 venue: Some(instrument_id.venue),
4247 command_id: UUID4::new(),
4248 ts_init: self.timestamp_ns(),
4249 correlation_id: None,
4250 params,
4251 });
4252
4253 self.send_data_cmd(DataCommand::Unsubscribe(command));
4254 }
4255
4256 pub fn unsubscribe_book_depth10(
4258 &mut self,
4259 instrument_id: InstrumentId,
4260 client_id: Option<ClientId>,
4261 params: Option<Params>,
4262 ) {
4263 self.check_registered();
4264
4265 let pattern = if is_parent_subscription(params.as_ref()) {
4266 get_book_depth10_pattern(instrument_id)
4267 } else {
4268 get_book_depth10_topic(instrument_id).into()
4269 };
4270 self.remove_depth10_subscription(pattern);
4271
4272 let command = UnsubscribeCommand::BookDepth10(UnsubscribeBookDepth10 {
4273 instrument_id,
4274 client_id,
4275 venue: Some(instrument_id.venue),
4276 command_id: UUID4::new(),
4277 ts_init: self.timestamp_ns(),
4278 correlation_id: None,
4279 params,
4280 });
4281
4282 self.send_data_cmd(DataCommand::Unsubscribe(command));
4283 }
4284
4285 pub fn unsubscribe_book_at_interval(
4287 &mut self,
4288 instrument_id: InstrumentId,
4289 interval_ms: NonZeroUsize,
4290 client_id: Option<ClientId>,
4291 params: Option<Params>,
4292 ) {
4293 self.check_registered();
4294
4295 let topic = get_book_snapshots_topic(instrument_id, interval_ms);
4296 self.remove_book_snapshot_subscription(topic);
4297
4298 let command = UnsubscribeCommand::BookSnapshots(UnsubscribeBookSnapshots {
4299 instrument_id,
4300 interval_ms,
4301 client_id,
4302 venue: Some(instrument_id.venue),
4303 command_id: UUID4::new(),
4304 ts_init: self.timestamp_ns(),
4305 correlation_id: None,
4306 params,
4307 });
4308
4309 self.send_data_cmd(DataCommand::Unsubscribe(command));
4310 }
4311
4312 pub fn unsubscribe_quotes(
4314 &mut self,
4315 instrument_id: InstrumentId,
4316 client_id: Option<ClientId>,
4317 params: Option<Params>,
4318 ) {
4319 self.check_registered();
4320
4321 let topic = get_quotes_topic(instrument_id);
4322 self.remove_quote_subscription(topic);
4323
4324 let command = UnsubscribeCommand::Quotes(UnsubscribeQuotes {
4325 instrument_id,
4326 client_id,
4327 venue: Some(instrument_id.venue),
4328 command_id: UUID4::new(),
4329 ts_init: self.timestamp_ns(),
4330 correlation_id: None,
4331 params,
4332 });
4333
4334 self.send_data_cmd(DataCommand::Unsubscribe(command));
4335 }
4336
4337 pub fn unsubscribe_trades(
4339 &mut self,
4340 instrument_id: InstrumentId,
4341 client_id: Option<ClientId>,
4342 params: Option<Params>,
4343 ) {
4344 self.check_registered();
4345
4346 let topic = get_trades_topic(instrument_id);
4347 self.remove_trade_subscription(topic);
4348
4349 let command = UnsubscribeCommand::Trades(UnsubscribeTrades {
4350 instrument_id,
4351 client_id,
4352 venue: Some(instrument_id.venue),
4353 command_id: UUID4::new(),
4354 ts_init: self.timestamp_ns(),
4355 correlation_id: None,
4356 params,
4357 });
4358
4359 self.send_data_cmd(DataCommand::Unsubscribe(command));
4360 }
4361
4362 pub fn unsubscribe_bars(
4364 &mut self,
4365 bar_type: BarType,
4366 client_id: Option<ClientId>,
4367 params: Option<Params>,
4368 ) {
4369 self.check_registered();
4370
4371 let topic = get_bars_topic(bar_type.standard());
4373 self.remove_bar_subscription(topic);
4374
4375 let command = UnsubscribeCommand::Bars(UnsubscribeBars {
4376 bar_type,
4377 client_id,
4378 venue: Some(bar_type.instrument_id().venue),
4379 command_id: UUID4::new(),
4380 ts_init: self.timestamp_ns(),
4381 correlation_id: None,
4382 params,
4383 });
4384
4385 self.send_data_cmd(DataCommand::Unsubscribe(command));
4386 }
4387
4388 pub fn unsubscribe_mark_prices(
4390 &mut self,
4391 instrument_id: InstrumentId,
4392 client_id: Option<ClientId>,
4393 params: Option<Params>,
4394 ) {
4395 self.check_registered();
4396
4397 let topic = get_mark_price_topic(instrument_id);
4398 self.remove_mark_price_subscription(topic);
4399
4400 let command = UnsubscribeCommand::MarkPrices(UnsubscribeMarkPrices {
4401 instrument_id,
4402 client_id,
4403 venue: Some(instrument_id.venue),
4404 command_id: UUID4::new(),
4405 ts_init: self.timestamp_ns(),
4406 correlation_id: None,
4407 params,
4408 });
4409
4410 self.send_data_cmd(DataCommand::Unsubscribe(command));
4411 }
4412
4413 pub fn unsubscribe_index_prices(
4415 &mut self,
4416 instrument_id: InstrumentId,
4417 client_id: Option<ClientId>,
4418 params: Option<Params>,
4419 ) {
4420 self.check_registered();
4421
4422 let topic = get_index_price_topic(instrument_id);
4423 self.remove_index_price_subscription(topic);
4424
4425 let command = UnsubscribeCommand::IndexPrices(UnsubscribeIndexPrices {
4426 instrument_id,
4427 client_id,
4428 venue: Some(instrument_id.venue),
4429 command_id: UUID4::new(),
4430 ts_init: self.timestamp_ns(),
4431 correlation_id: None,
4432 params,
4433 });
4434
4435 self.send_data_cmd(DataCommand::Unsubscribe(command));
4436 }
4437
4438 pub fn unsubscribe_funding_rates(
4440 &mut self,
4441 instrument_id: InstrumentId,
4442 client_id: Option<ClientId>,
4443 params: Option<Params>,
4444 ) {
4445 self.check_registered();
4446
4447 let topic = get_funding_rate_topic(instrument_id);
4448 self.remove_funding_rate_subscription(topic);
4449
4450 let command = UnsubscribeCommand::FundingRates(UnsubscribeFundingRates {
4451 instrument_id,
4452 client_id,
4453 venue: Some(instrument_id.venue),
4454 command_id: UUID4::new(),
4455 ts_init: self.timestamp_ns(),
4456 correlation_id: None,
4457 params,
4458 });
4459
4460 self.send_data_cmd(DataCommand::Unsubscribe(command));
4461 }
4462
4463 pub fn unsubscribe_option_greeks(
4465 &mut self,
4466 instrument_id: InstrumentId,
4467 client_id: Option<ClientId>,
4468 params: Option<Params>,
4469 ) {
4470 self.check_registered();
4471
4472 let topic = get_option_greeks_topic(instrument_id);
4473 self.remove_option_greeks_subscription(topic);
4474
4475 let command = UnsubscribeCommand::OptionGreeks(UnsubscribeOptionGreeks {
4476 instrument_id,
4477 client_id,
4478 venue: Some(instrument_id.venue),
4479 command_id: UUID4::new(),
4480 ts_init: self.timestamp_ns(),
4481 correlation_id: None,
4482 params,
4483 });
4484
4485 self.send_data_cmd(DataCommand::Unsubscribe(command));
4486 }
4487
4488 pub fn unsubscribe_instrument_status(
4490 &mut self,
4491 instrument_id: InstrumentId,
4492 client_id: Option<ClientId>,
4493 params: Option<Params>,
4494 ) {
4495 self.check_registered();
4496
4497 let topic = get_instrument_status_topic(instrument_id);
4498 self.remove_subscription_any(topic);
4499
4500 let command = UnsubscribeCommand::InstrumentStatus(UnsubscribeInstrumentStatus {
4501 instrument_id,
4502 client_id,
4503 venue: Some(instrument_id.venue),
4504 command_id: UUID4::new(),
4505 ts_init: self.timestamp_ns(),
4506 correlation_id: None,
4507 params,
4508 });
4509
4510 self.send_data_cmd(DataCommand::Unsubscribe(command));
4511 }
4512
4513 pub fn unsubscribe_instrument_close(
4515 &mut self,
4516 instrument_id: InstrumentId,
4517 client_id: Option<ClientId>,
4518 params: Option<Params>,
4519 ) {
4520 self.check_registered();
4521
4522 let topic = get_instrument_close_topic(instrument_id);
4523 self.remove_instrument_close_subscription(topic);
4524
4525 let command = UnsubscribeCommand::InstrumentClose(UnsubscribeInstrumentClose {
4526 instrument_id,
4527 client_id,
4528 venue: Some(instrument_id.venue),
4529 command_id: UUID4::new(),
4530 ts_init: self.timestamp_ns(),
4531 correlation_id: None,
4532 params,
4533 });
4534
4535 self.send_data_cmd(DataCommand::Unsubscribe(command));
4536 }
4537
4538 pub fn unsubscribe_option_chain(
4540 &mut self,
4541 series_id: OptionSeriesId,
4542 client_id: Option<ClientId>,
4543 ) {
4544 self.check_registered();
4545
4546 let topic = get_option_chain_topic(series_id);
4547 self.remove_option_chain_subscription(topic);
4548
4549 let command = UnsubscribeCommand::OptionChain(UnsubscribeOptionChain::new(
4550 series_id,
4551 UUID4::new(),
4552 self.timestamp_ns(),
4553 client_id,
4554 Some(series_id.venue),
4555 ));
4556
4557 self.send_data_cmd(DataCommand::Unsubscribe(command));
4558 }
4559
4560 #[expect(clippy::too_many_arguments)]
4566 pub fn request_data(
4567 &self,
4568 data_type: DataType,
4569 client_id: ClientId,
4570 start: Option<DateTime<Utc>>,
4571 end: Option<DateTime<Utc>>,
4572 limit: Option<NonZeroUsize>,
4573 params: Option<Params>,
4574 handler: ShareableMessageHandler,
4575 ) -> anyhow::Result<UUID4> {
4576 self.check_registered();
4577
4578 let now = self.clock_ref().utc_now();
4579 check_timestamps(now, start, end)?;
4580
4581 let request_id = UUID4::new();
4582 let command = RequestCommand::Data(RequestCustomData {
4583 client_id,
4584 data_type,
4585 start,
4586 end,
4587 limit,
4588 request_id,
4589 ts_init: self.timestamp_ns(),
4590 params,
4591 });
4592
4593 get_message_bus()
4594 .borrow_mut()
4595 .register_response_handler(command.request_id(), handler)?;
4596
4597 self.send_data_cmd(DataCommand::Request(command));
4598
4599 Ok(request_id)
4600 }
4601
4602 pub fn request_instrument(
4608 &self,
4609 instrument_id: InstrumentId,
4610 start: Option<DateTime<Utc>>,
4611 end: Option<DateTime<Utc>>,
4612 client_id: Option<ClientId>,
4613 params: Option<Params>,
4614 handler: ShareableMessageHandler,
4615 ) -> anyhow::Result<UUID4> {
4616 self.check_registered();
4617
4618 let now = self.clock_ref().utc_now();
4619 check_timestamps(now, start, end)?;
4620
4621 let request_id = UUID4::new();
4622 let command = RequestCommand::Instrument(RequestInstrument {
4623 instrument_id,
4624 start,
4625 end,
4626 client_id,
4627 request_id,
4628 ts_init: now.into(),
4629 params,
4630 });
4631
4632 get_message_bus()
4633 .borrow_mut()
4634 .register_response_handler(command.request_id(), handler)?;
4635
4636 self.send_data_cmd(DataCommand::Request(command));
4637
4638 Ok(request_id)
4639 }
4640
4641 pub fn request_instruments(
4647 &self,
4648 venue: Option<Venue>,
4649 start: Option<DateTime<Utc>>,
4650 end: Option<DateTime<Utc>>,
4651 client_id: Option<ClientId>,
4652 params: Option<Params>,
4653 handler: ShareableMessageHandler,
4654 ) -> anyhow::Result<UUID4> {
4655 self.check_registered();
4656
4657 let now = self.clock_ref().utc_now();
4658 check_timestamps(now, start, end)?;
4659
4660 let request_id = UUID4::new();
4661 let command = RequestCommand::Instruments(RequestInstruments {
4662 venue,
4663 start,
4664 end,
4665 client_id,
4666 request_id,
4667 ts_init: now.into(),
4668 params,
4669 });
4670
4671 get_message_bus()
4672 .borrow_mut()
4673 .register_response_handler(command.request_id(), handler)?;
4674
4675 self.send_data_cmd(DataCommand::Request(command));
4676
4677 Ok(request_id)
4678 }
4679
4680 pub fn request_book_snapshot(
4686 &self,
4687 instrument_id: InstrumentId,
4688 depth: Option<NonZeroUsize>,
4689 client_id: Option<ClientId>,
4690 params: Option<Params>,
4691 handler: ShareableMessageHandler,
4692 ) -> anyhow::Result<UUID4> {
4693 self.check_registered();
4694
4695 let request_id = UUID4::new();
4696 let command = RequestCommand::BookSnapshot(RequestBookSnapshot {
4697 instrument_id,
4698 depth,
4699 client_id,
4700 request_id,
4701 ts_init: self.timestamp_ns(),
4702 params,
4703 });
4704
4705 get_message_bus()
4706 .borrow_mut()
4707 .register_response_handler(command.request_id(), handler)?;
4708
4709 self.send_data_cmd(DataCommand::Request(command));
4710
4711 Ok(request_id)
4712 }
4713
4714 #[expect(clippy::too_many_arguments)]
4720 pub fn request_book_deltas(
4721 &self,
4722 instrument_id: InstrumentId,
4723 start: Option<DateTime<Utc>>,
4724 end: Option<DateTime<Utc>>,
4725 limit: Option<NonZeroUsize>,
4726 client_id: Option<ClientId>,
4727 params: Option<Params>,
4728 handler: ShareableMessageHandler,
4729 ) -> anyhow::Result<UUID4> {
4730 self.check_registered();
4731
4732 let now = self.clock_ref().utc_now();
4733 check_timestamps(now, start, end)?;
4734
4735 let request_id = UUID4::new();
4736 let command = RequestCommand::BookDeltas(RequestBookDeltas {
4737 instrument_id,
4738 start,
4739 end,
4740 limit,
4741 client_id,
4742 request_id,
4743 ts_init: now.into(),
4744 params,
4745 });
4746
4747 get_message_bus()
4748 .borrow_mut()
4749 .register_response_handler(command.request_id(), handler)?;
4750
4751 self.send_data_cmd(DataCommand::Request(command));
4752
4753 Ok(request_id)
4754 }
4755
4756 #[expect(clippy::too_many_arguments)]
4762 pub fn request_book_depth(
4763 &self,
4764 instrument_id: InstrumentId,
4765 start: Option<DateTime<Utc>>,
4766 end: Option<DateTime<Utc>>,
4767 limit: Option<NonZeroUsize>,
4768 depth: Option<NonZeroUsize>,
4769 client_id: Option<ClientId>,
4770 params: Option<Params>,
4771 handler: ShareableMessageHandler,
4772 ) -> anyhow::Result<UUID4> {
4773 self.check_registered();
4774
4775 let now = self.clock_ref().utc_now();
4776 check_timestamps(now, start, end)?;
4777
4778 let request_id = UUID4::new();
4779 let command = RequestCommand::BookDepth(RequestBookDepth {
4780 instrument_id,
4781 start,
4782 end,
4783 limit,
4784 depth,
4785 client_id,
4786 request_id,
4787 ts_init: now.into(),
4788 params,
4789 });
4790
4791 get_message_bus()
4792 .borrow_mut()
4793 .register_response_handler(command.request_id(), handler)?;
4794
4795 self.send_data_cmd(DataCommand::Request(command));
4796
4797 Ok(request_id)
4798 }
4799
4800 #[expect(clippy::too_many_arguments)]
4806 pub fn request_quotes(
4807 &self,
4808 instrument_id: InstrumentId,
4809 start: Option<DateTime<Utc>>,
4810 end: Option<DateTime<Utc>>,
4811 limit: Option<NonZeroUsize>,
4812 client_id: Option<ClientId>,
4813 params: Option<Params>,
4814 handler: ShareableMessageHandler,
4815 ) -> anyhow::Result<UUID4> {
4816 self.check_registered();
4817
4818 let now = self.clock_ref().utc_now();
4819 check_timestamps(now, start, end)?;
4820
4821 let request_id = UUID4::new();
4822 let command = RequestCommand::Quotes(RequestQuotes {
4823 instrument_id,
4824 start,
4825 end,
4826 limit,
4827 client_id,
4828 request_id,
4829 ts_init: now.into(),
4830 params,
4831 });
4832
4833 get_message_bus()
4834 .borrow_mut()
4835 .register_response_handler(command.request_id(), handler)?;
4836
4837 self.send_data_cmd(DataCommand::Request(command));
4838
4839 Ok(request_id)
4840 }
4841
4842 #[expect(clippy::too_many_arguments)]
4848 pub fn request_trades(
4849 &self,
4850 instrument_id: InstrumentId,
4851 start: Option<DateTime<Utc>>,
4852 end: Option<DateTime<Utc>>,
4853 limit: Option<NonZeroUsize>,
4854 client_id: Option<ClientId>,
4855 params: Option<Params>,
4856 handler: ShareableMessageHandler,
4857 ) -> anyhow::Result<UUID4> {
4858 self.check_registered();
4859
4860 let now = self.clock_ref().utc_now();
4861 check_timestamps(now, start, end)?;
4862
4863 let request_id = UUID4::new();
4864 let command = RequestCommand::Trades(RequestTrades {
4865 instrument_id,
4866 start,
4867 end,
4868 limit,
4869 client_id,
4870 request_id,
4871 ts_init: now.into(),
4872 params,
4873 });
4874
4875 get_message_bus()
4876 .borrow_mut()
4877 .register_response_handler(command.request_id(), handler)?;
4878
4879 self.send_data_cmd(DataCommand::Request(command));
4880
4881 Ok(request_id)
4882 }
4883
4884 #[expect(clippy::too_many_arguments)]
4890 pub fn request_bars(
4891 &self,
4892 bar_type: BarType,
4893 start: Option<DateTime<Utc>>,
4894 end: Option<DateTime<Utc>>,
4895 limit: Option<NonZeroUsize>,
4896 client_id: Option<ClientId>,
4897 params: Option<Params>,
4898 handler: ShareableMessageHandler,
4899 ) -> anyhow::Result<UUID4> {
4900 self.check_registered();
4901
4902 anyhow::ensure!(
4903 bar_type.is_standard(),
4904 "Composite bar types are not supported for `request_bars`, was {bar_type}; \
4905 request aggregation via the `bar_types` params instead",
4906 );
4907
4908 let now = self.clock_ref().utc_now();
4909 check_timestamps(now, start, end)?;
4910
4911 let request_id = UUID4::new();
4912 let command = RequestCommand::Bars(RequestBars {
4913 bar_type,
4914 start,
4915 end,
4916 limit,
4917 client_id,
4918 request_id,
4919 ts_init: now.into(),
4920 params,
4921 });
4922
4923 get_message_bus()
4924 .borrow_mut()
4925 .register_response_handler(command.request_id(), handler)?;
4926
4927 self.send_data_cmd(DataCommand::Request(command));
4928
4929 Ok(request_id)
4930 }
4931
4932 #[expect(clippy::too_many_arguments)]
4938 pub fn request_funding_rates(
4939 &self,
4940 instrument_id: InstrumentId,
4941 start: Option<DateTime<Utc>>,
4942 end: Option<DateTime<Utc>>,
4943 limit: Option<NonZeroUsize>,
4944 client_id: Option<ClientId>,
4945 params: Option<Params>,
4946 handler: ShareableMessageHandler,
4947 ) -> anyhow::Result<UUID4> {
4948 self.check_registered();
4949
4950 let now = self.clock_ref().utc_now();
4951 check_timestamps(now, start, end)?;
4952
4953 let request_id = UUID4::new();
4954 let command = RequestCommand::FundingRates(RequestFundingRates {
4955 instrument_id,
4956 start,
4957 end,
4958 limit,
4959 client_id,
4960 request_id,
4961 ts_init: now.into(),
4962 params,
4963 });
4964
4965 get_message_bus()
4966 .borrow_mut()
4967 .register_response_handler(command.request_id(), handler)?;
4968
4969 self.send_data_cmd(DataCommand::Request(command));
4970
4971 Ok(request_id)
4972 }
4973
4974 #[cfg(test)]
4975 pub fn quote_handler_count(&self) -> usize {
4976 self.quote_handlers.len()
4977 }
4978
4979 #[cfg(test)]
4980 pub fn trade_handler_count(&self) -> usize {
4981 self.trade_handlers.len()
4982 }
4983
4984 #[cfg(test)]
4985 pub fn bar_handler_count(&self) -> usize {
4986 self.bar_handlers.len()
4987 }
4988
4989 #[cfg(test)]
4990 pub fn deltas_handler_count(&self) -> usize {
4991 self.deltas_handlers.len()
4992 }
4993
4994 #[cfg(test)]
4995 pub fn depth10_handler_count(&self) -> usize {
4996 self.depth10_handlers.len()
4997 }
4998
4999 #[cfg(test)]
5000 pub fn has_quote_handler(&self, topic: &str) -> bool {
5001 self.quote_handlers
5002 .contains_key(&MStr::<Topic>::from(topic))
5003 }
5004
5005 #[cfg(test)]
5006 pub fn has_trade_handler(&self, topic: &str) -> bool {
5007 self.trade_handlers
5008 .contains_key(&MStr::<Topic>::from(topic))
5009 }
5010
5011 #[cfg(test)]
5012 pub fn has_bar_handler(&self, topic: &str) -> bool {
5013 self.bar_handlers.contains_key(&MStr::<Topic>::from(topic))
5014 }
5015
5016 #[cfg(test)]
5017 pub fn has_deltas_handler(&self, pattern: &str) -> bool {
5018 self.deltas_handlers
5019 .contains_key(&MStr::<Pattern>::from(pattern))
5020 }
5021
5022 #[cfg(test)]
5023 pub fn has_depth10_handler(&self, pattern: &str) -> bool {
5024 self.depth10_handlers
5025 .contains_key(&MStr::<Pattern>::from(pattern))
5026 }
5027}
5028
5029impl DataActorNative for DataActorCore {
5030 fn core(&self) -> &DataActorCore {
5031 self
5032 }
5033
5034 fn core_mut(&mut self) -> &mut DataActorCore {
5035 self
5036 }
5037}
5038
5039fn check_timestamps(
5040 now: DateTime<Utc>,
5041 start: Option<DateTime<Utc>>,
5042 end: Option<DateTime<Utc>>,
5043) -> anyhow::Result<()> {
5044 if let Some(start) = start {
5045 check_predicate_true(start <= now, "start was > now")?;
5046 }
5047
5048 if let Some(end) = end {
5049 check_predicate_true(end <= now, "end was > now")?;
5050 }
5051
5052 if let (Some(start), Some(end)) = (start, end) {
5053 check_predicate_true(start <= end, "start was > end")?;
5054 }
5055
5056 Ok(())
5057}
5058
5059fn log_error(e: &anyhow::Error) {
5060 log::error!("{e}");
5061}
5062
5063fn log_not_running<T>(msg: &T)
5064where
5065 T: Debug,
5066{
5067 log::trace!("Received message when not running - skipping {msg:?}");
5068}
5069
5070fn log_received<T>(msg: &T)
5071where
5072 T: Debug,
5073{
5074 log::debug!("{RECV} {msg:?}");
5075}
5076
5077fn log_received_bulk(kind: &str, correlation_id: &UUID4, records: usize) {
5078 log::debug!("{RECV} {kind} correlation_id={correlation_id} records={records}");
5079}