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 indexmap::IndexMap;
28use jiff::Timestamp;
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 binding::DataActorBinding,
52 indicators::{Indicators, SharedActorIndicator},
53 registry::try_get_actor_unchecked,
54};
55#[cfg(feature = "defi")]
56use crate::defi;
57#[cfg(feature = "defi")]
58#[allow(unused_imports)]
59use crate::defi::data_actor as _; #[cfg(feature = "python")]
61use crate::python::msgbus::PyMessageBusScope;
62use crate::{
63 cache::{Cache, CacheApi},
64 clock::{Clock, ClockApi},
65 component::Component,
66 enums::{ComponentState, ComponentTrigger},
67 logging::{CMD, RECV, REQ, SEND},
68 messages::{
69 data::{
70 BarsResponse, BookDeltasResponse, BookDepthResponse, BookResponse, CustomDataResponse,
71 DataCommand, FundingRatesResponse, InstrumentResponse, InstrumentsResponse,
72 QuotesResponse, RequestBars, RequestBookDeltas, RequestBookDepth, RequestBookSnapshot,
73 RequestCommand, RequestCustomData, RequestFundingRates, RequestInstrument,
74 RequestInstruments, RequestQuotes, RequestTrades, SubscribeBars, SubscribeBookDeltas,
75 SubscribeBookDepth10, SubscribeBookSnapshots, SubscribeCommand, SubscribeCustomData,
76 SubscribeFundingRates, SubscribeIndexPrices, SubscribeInstrument,
77 SubscribeInstrumentClose, SubscribeInstrumentStatus, SubscribeInstruments,
78 SubscribeMarkPrices, SubscribeOptionChain, SubscribeOptionGreeks, SubscribeQuotes,
79 SubscribeTrades, TradesResponse, UnsubscribeBars, UnsubscribeBookDeltas,
80 UnsubscribeBookDepth10, UnsubscribeBookSnapshots, UnsubscribeCommand,
81 UnsubscribeCustomData, UnsubscribeFundingRates, UnsubscribeIndexPrices,
82 UnsubscribeInstrument, UnsubscribeInstrumentClose, UnsubscribeInstrumentStatus,
83 UnsubscribeInstruments, UnsubscribeMarkPrices, UnsubscribeOptionChain,
84 UnsubscribeOptionGreeks, UnsubscribeQuotes, UnsubscribeTrades, is_parent_subscription,
85 },
86 system::{QueueStateChanged, ShutdownSystem, SocketStateChanged},
87 },
88 msgbus::{
89 self, MStr, Pattern, ShareableMessageHandler, Topic, TypedHandler, get_message_bus,
90 switchboard::{
91 MessagingSwitchboard, get_bars_topic, get_book_deltas_pattern, get_book_deltas_topic,
92 get_book_depth10_pattern, get_book_depth10_topic, get_book_snapshots_topic,
93 get_custom_topic, get_funding_rate_topic, get_index_price_topic,
94 get_instrument_close_topic, get_instrument_status_topic, get_instrument_topic,
95 get_instruments_pattern, get_mark_price_topic, get_option_chain_topic,
96 get_option_greeks_topic, get_quotes_topic, get_signal_pattern, get_trades_topic,
97 },
98 },
99 runner::SystemChannel,
100 signal::Signal,
101 timer::{TimeEvent, TimeEventCallback},
102};
103#[cfg(feature = "live")]
104use crate::{
105 live::try_get_system_command_sender,
106 messages::{
107 SystemCommand,
108 system::{ReconnectSocket, socket_endpoint},
109 },
110};
111
112#[derive(Debug, Clone, Deserialize, Serialize)]
114#[serde(default, deny_unknown_fields)]
115#[cfg_attr(
116 feature = "python",
117 pyo3::pyclass(module = "nautilus_trader.common", subclass, from_py_object)
118)]
119#[cfg_attr(
120 feature = "python",
121 pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.common")
122)]
123pub struct DataActorConfig {
124 pub actor_id: Option<ActorId>,
126 pub log_events: bool,
128 pub log_commands: bool,
130}
131
132impl Default for DataActorConfig {
133 fn default() -> Self {
134 Self {
135 actor_id: None,
136 log_events: true,
137 log_commands: true,
138 }
139 }
140}
141
142#[derive(Debug, Clone, Deserialize, Serialize)]
144#[serde(deny_unknown_fields)]
145#[cfg_attr(
146 feature = "python",
147 pyo3::pyclass(module = "nautilus_trader.common", from_py_object)
148)]
149#[cfg_attr(
150 feature = "python",
151 pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.common")
152)]
153pub struct ImportableActorConfig {
154 pub actor_path: String,
156 pub config_path: String,
158 pub config: HashMap<String, serde_json::Value>,
160}
161
162type RequestCallback = Arc<dyn Fn(UUID4) + Send + Sync>;
163
164pub trait DataActorNative {
176 fn core(&self) -> &DataActorCore;
178
179 fn core_mut(&mut self) -> &mut DataActorCore;
181
182 fn clock_mut(&mut self) -> RefMut<'_, dyn Clock> {
188 let core = self.core_mut();
189 core.clock
190 .as_ref()
191 .unwrap_or_else(|| {
192 panic!(
193 "DataActor {} must be registered before calling `clock_mut()` - trader_id: {:?}",
194 core.actor_id, core.trader_id
195 )
196 })
197 .borrow_mut()
198 }
199
200 fn clock_rc(&self) -> Rc<RefCell<dyn Clock>> {
206 self.core()
207 .clock
208 .as_ref()
209 .expect("DataActor must be registered before accessing clock")
210 .clone()
211 }
212
213 fn cache_ref(&self) -> Ref<'_, Cache> {
219 self.core()
220 .cache
221 .as_ref()
222 .expect("DataActor must be registered before accessing cache")
223 .borrow()
224 }
225
226 fn cache_rc(&self) -> Rc<RefCell<Cache>> {
232 self.core()
233 .cache
234 .as_ref()
235 .expect("DataActor must be registered before accessing cache")
236 .clone()
237 }
238}
239
240pub trait DataActor {
247 fn actor_id(&self) -> ActorId
253 where
254 Self: DataActorBinding,
255 {
256 self.binding_actor_id()
257 }
258
259 fn trader_id(&self) -> Option<TraderId>
261 where
262 Self: DataActorNative,
263 {
264 self.core().trader_id()
265 }
266
267 fn is_registered(&self) -> bool
269 where
270 Self: DataActorNative,
271 {
272 self.core().is_registered()
273 }
274
275 fn config(&self) -> &DataActorConfig
277 where
278 Self: DataActorNative,
279 {
280 &self.core().config
281 }
282
283 fn on_save(&self) -> anyhow::Result<IndexMap<String, Vec<u8>>> {
289 Ok(IndexMap::new())
290 }
291
292 #[allow(unused_variables)]
298 fn on_load(&mut self, state: IndexMap<String, Vec<u8>>) -> anyhow::Result<()> {
299 Ok(())
300 }
301
302 fn on_start(&mut self) -> anyhow::Result<()> {
308 log::warn!(
309 "The `on_start` handler was called when not overridden, \
310 it's expected that any actions required when starting the actor \
311 occur here, such as subscribing/requesting data"
312 );
313 Ok(())
314 }
315
316 fn on_stop(&mut self) -> anyhow::Result<()> {
322 log::warn!(
323 "The `on_stop` handler was called when not overridden, \
324 it's expected that any actions required when stopping the actor \
325 occur here, such as unsubscribing from data",
326 );
327 Ok(())
328 }
329
330 fn on_resume(&mut self) -> anyhow::Result<()> {
336 log::warn!(
337 "The `on_resume` handler was called when not overridden, \
338 it's expected that any actions required when resuming the actor \
339 following a stop occur here"
340 );
341 Ok(())
342 }
343
344 fn on_reset(&mut self) -> anyhow::Result<()> {
350 log::warn!(
351 "The `on_reset` handler was called when not overridden, \
352 it's expected that any actions required when resetting the actor \
353 occur here, such as resetting indicators and other state"
354 );
355 Ok(())
356 }
357
358 fn on_dispose(&mut self) -> anyhow::Result<()> {
364 Ok(())
365 }
366
367 fn on_degrade(&mut self) -> anyhow::Result<()> {
373 Ok(())
374 }
375
376 fn on_fault(&mut self) -> anyhow::Result<()> {
382 Ok(())
383 }
384
385 #[allow(unused_variables)]
391 fn on_time_event(&mut self, event: &TimeEvent) -> anyhow::Result<()> {
392 Ok(())
393 }
394
395 #[allow(unused_variables)]
401 fn on_data(&mut self, data: &CustomData) -> anyhow::Result<()> {
402 Ok(())
403 }
404
405 #[allow(unused_variables)]
411 fn on_signal(&mut self, signal: &Signal) -> anyhow::Result<()> {
412 Ok(())
413 }
414
415 #[allow(unused_variables)]
421 fn on_queue_state(&mut self, event: &QueueStateChanged) -> anyhow::Result<()> {
422 Ok(())
423 }
424
425 #[allow(unused_variables)]
431 fn on_socket_state(&mut self, event: &SocketStateChanged) -> anyhow::Result<()> {
432 Ok(())
433 }
434
435 #[allow(unused_variables)]
441 fn on_instrument(&mut self, instrument: &InstrumentAny) -> anyhow::Result<()> {
442 Ok(())
443 }
444
445 #[allow(unused_variables)]
451 fn on_book_deltas(&mut self, deltas: &OrderBookDeltas) -> anyhow::Result<()> {
452 Ok(())
453 }
454
455 #[allow(unused_variables)]
461 fn on_book_depth(&mut self, depth: &OrderBookDepth10) -> anyhow::Result<()> {
462 Ok(())
463 }
464
465 #[allow(unused_variables)]
471 fn on_book(&mut self, order_book: &OrderBook) -> anyhow::Result<()> {
472 Ok(())
473 }
474
475 #[allow(unused_variables)]
481 fn on_quote(&mut self, quote: &QuoteTick) -> anyhow::Result<()> {
482 Ok(())
483 }
484
485 #[allow(unused_variables)]
491 fn on_trade(&mut self, tick: &TradeTick) -> anyhow::Result<()> {
492 Ok(())
493 }
494
495 #[allow(unused_variables)]
501 fn on_bar(&mut self, bar: &Bar) -> anyhow::Result<()> {
502 Ok(())
503 }
504
505 #[allow(unused_variables)]
511 fn on_mark_price(&mut self, mark_price: &MarkPriceUpdate) -> anyhow::Result<()> {
512 Ok(())
513 }
514
515 #[allow(unused_variables)]
521 fn on_index_price(&mut self, index_price: &IndexPriceUpdate) -> anyhow::Result<()> {
522 Ok(())
523 }
524
525 #[allow(unused_variables)]
531 fn on_funding_rate(&mut self, funding_rate: &FundingRateUpdate) -> anyhow::Result<()> {
532 Ok(())
533 }
534
535 #[allow(unused_variables)]
541 fn on_option_greeks(&mut self, greeks: &OptionGreeks) -> anyhow::Result<()> {
542 Ok(())
543 }
544
545 #[allow(unused_variables)]
551 fn on_option_chain(&mut self, slice: &OptionChainSlice) -> anyhow::Result<()> {
552 Ok(())
553 }
554
555 #[allow(unused_variables)]
561 fn on_instrument_status(&mut self, data: &InstrumentStatus) -> anyhow::Result<()> {
562 Ok(())
563 }
564
565 #[allow(unused_variables)]
571 fn on_instrument_close(&mut self, update: &InstrumentClose) -> anyhow::Result<()> {
572 Ok(())
573 }
574
575 #[cfg(feature = "defi")]
576 #[allow(unused_variables)]
582 fn on_block(&mut self, block: &Block) -> anyhow::Result<()> {
583 Ok(())
584 }
585
586 #[cfg(feature = "defi")]
587 #[allow(unused_variables)]
593 fn on_pool(&mut self, pool: &Pool) -> anyhow::Result<()> {
594 Ok(())
595 }
596
597 #[cfg(feature = "defi")]
598 #[allow(unused_variables)]
604 fn on_pool_swap(&mut self, swap: &PoolSwap) -> anyhow::Result<()> {
605 Ok(())
606 }
607
608 #[cfg(feature = "defi")]
609 #[allow(unused_variables)]
615 fn on_pool_liquidity_update(&mut self, update: &PoolLiquidityUpdate) -> anyhow::Result<()> {
616 Ok(())
617 }
618
619 #[cfg(feature = "defi")]
620 #[allow(unused_variables)]
626 fn on_pool_fee_collect(&mut self, collect: &PoolFeeCollect) -> anyhow::Result<()> {
627 Ok(())
628 }
629
630 #[cfg(feature = "defi")]
631 #[allow(unused_variables)]
637 fn on_pool_flash(&mut self, flash: &PoolFlash) -> anyhow::Result<()> {
638 Ok(())
639 }
640
641 #[allow(unused_variables)]
650 fn on_historical_data(&mut self, data: &dyn Any) -> anyhow::Result<()> {
651 Ok(())
652 }
653
654 #[allow(unused_variables)]
660 fn on_historical_book_deltas(&mut self, deltas: &[OrderBookDelta]) -> anyhow::Result<()> {
661 Ok(())
662 }
663
664 #[allow(unused_variables)]
670 fn on_historical_book_depth(&mut self, depths: &[OrderBookDepth10]) -> anyhow::Result<()> {
671 Ok(())
672 }
673
674 #[allow(unused_variables)]
680 fn on_historical_quotes(&mut self, quotes: &[QuoteTick]) -> anyhow::Result<()> {
681 Ok(())
682 }
683
684 #[allow(unused_variables)]
690 fn on_historical_trades(&mut self, trades: &[TradeTick]) -> anyhow::Result<()> {
691 Ok(())
692 }
693
694 #[allow(unused_variables)]
700 fn on_historical_bars(&mut self, bars: &[Bar]) -> anyhow::Result<()> {
701 Ok(())
702 }
703
704 #[allow(unused_variables)]
710 fn on_historical_mark_prices(&mut self, mark_prices: &[MarkPriceUpdate]) -> anyhow::Result<()> {
711 Ok(())
712 }
713
714 #[allow(unused_variables)]
720 fn on_historical_index_prices(
721 &mut self,
722 index_prices: &[IndexPriceUpdate],
723 ) -> anyhow::Result<()> {
724 Ok(())
725 }
726
727 #[allow(unused_variables)]
733 fn on_historical_funding_rates(
734 &mut self,
735 funding_rates: &[FundingRateUpdate],
736 ) -> anyhow::Result<()> {
737 Ok(())
738 }
739
740 fn clock(&self) -> ClockApi<'_>
747 where
748 Self: DataActorBinding,
749 {
750 self.binding_clock()
751 }
752
753 fn cache(&self) -> CacheApi<'_>
755 where
756 Self: DataActorNative,
757 {
758 self.core().cache_api()
759 }
760
761 fn shutdown_system(&self, reason: Option<String>)
767 where
768 Self: DataActorNative,
769 {
770 self.core().shutdown_system(reason);
771 }
772
773 fn publish_data(&self, data_type: &DataType, data: &CustomData)
782 where
783 Self: DataActorNative,
784 {
785 self.core().publish_data(data_type, data);
786 }
787
788 fn publish_signal(&self, name: &str, value: String, ts_event: UnixNanos)
794 where
795 Self: DataActorNative,
796 {
797 self.core().publish_signal(name, value, ts_event);
798 }
799
800 fn add_synthetic(&self, synthetic: SyntheticInstrument) -> anyhow::Result<()>
812 where
813 Self: DataActorNative,
814 {
815 self.core().add_synthetic(synthetic)
816 }
817
818 fn update_synthetic(&self, synthetic: SyntheticInstrument) -> anyhow::Result<()>
830 where
831 Self: DataActorNative,
832 {
833 self.core().update_synthetic(synthetic)
834 }
835
836 fn handle_time_event(&mut self, event: &TimeEvent)
838 where
839 Self: Component,
840 {
841 log_received(&event);
842
843 if self.not_running() {
844 log_not_running(&event);
845 return;
846 }
847
848 if let Err(e) = DataActor::on_time_event(self, event) {
849 log_error(&e);
850 }
851 }
852
853 fn handle_data(&mut self, data: &CustomData)
855 where
856 Self: Component,
857 {
858 log_received(&data);
859
860 if self.not_running() {
861 log_not_running(&data);
862 return;
863 }
864
865 if let Err(e) = self.on_data(data) {
866 log_error(&e);
867 }
868 }
869
870 fn handle_signal(&mut self, signal: &Signal)
872 where
873 Self: Component,
874 {
875 log_received(&signal);
876
877 if self.not_running() {
878 log_not_running(&signal);
879 return;
880 }
881
882 if let Err(e) = self.on_signal(signal) {
883 log_error(&e);
884 }
885 }
886
887 fn handle_queue_state(&mut self, event: &QueueStateChanged)
889 where
890 Self: Component,
891 {
892 log_received(&event);
893
894 if self.not_running() {
895 log_not_running(&event);
896 return;
897 }
898
899 if let Err(e) = self.on_queue_state(event) {
900 log_error(&e);
901 }
902 }
903
904 fn handle_socket_state(&mut self, event: &SocketStateChanged)
906 where
907 Self: Component,
908 {
909 log_received(&event);
910
911 if self.not_running() {
912 log_not_running(&event);
913 return;
914 }
915
916 if let Err(e) = self.on_socket_state(event) {
917 log_error(&e);
918 }
919 }
920
921 fn handle_instrument(&mut self, instrument: &InstrumentAny)
923 where
924 Self: Component,
925 {
926 log_received(&instrument);
927
928 if self.not_running() {
929 log_not_running(&instrument);
930 return;
931 }
932
933 if let Err(e) = self.on_instrument(instrument) {
934 log_error(&e);
935 }
936 }
937
938 fn handle_book_deltas(&mut self, deltas: &OrderBookDeltas)
940 where
941 Self: Component,
942 {
943 log_received(&deltas);
944
945 if self.not_running() {
946 log_not_running(&deltas);
947 return;
948 }
949
950 if let Err(e) = self.on_book_deltas(deltas) {
951 log_error(&e);
952 }
953 }
954
955 fn handle_book_depth(&mut self, depth: &OrderBookDepth10)
957 where
958 Self: Component,
959 {
960 log_received(&depth);
961
962 if self.not_running() {
963 log_not_running(&depth);
964 return;
965 }
966
967 if let Err(e) = self.on_book_depth(depth) {
968 log_error(&e);
969 }
970 }
971
972 fn handle_book(&mut self, book: &OrderBook)
974 where
975 Self: Component,
976 {
977 log_received(&book);
978
979 if self.not_running() {
980 log_not_running(&book);
981 return;
982 }
983
984 if let Err(e) = self.on_book(book) {
985 log_error(&e);
986 }
987 }
988
989 fn handle_quote(&mut self, quote: &QuoteTick)
991 where
992 Self: DataActorNative + Component,
993 {
994 log_received("e);
995
996 if let Err(e) = self.core().handle_indicators_for_quote(quote) {
997 log_error(&e);
998 return;
999 }
1000
1001 if self.not_running() {
1002 log_not_running("e);
1003 return;
1004 }
1005
1006 if let Err(e) = self.on_quote(quote) {
1007 log_error(&e);
1008 }
1009 }
1010
1011 fn handle_trade(&mut self, trade: &TradeTick)
1013 where
1014 Self: DataActorNative + Component,
1015 {
1016 log_received(&trade);
1017
1018 if let Err(e) = self.core().handle_indicators_for_trade(trade) {
1019 log_error(&e);
1020 return;
1021 }
1022
1023 if self.not_running() {
1024 log_not_running(&trade);
1025 return;
1026 }
1027
1028 if let Err(e) = self.on_trade(trade) {
1029 log_error(&e);
1030 }
1031 }
1032
1033 fn handle_bar(&mut self, bar: &Bar)
1035 where
1036 Self: DataActorNative + Component,
1037 {
1038 log_received(&bar);
1039
1040 if let Err(e) = self.core().handle_indicators_for_bar(bar) {
1041 log_error(&e);
1042 return;
1043 }
1044
1045 if self.not_running() {
1046 log_not_running(&bar);
1047 return;
1048 }
1049
1050 if let Err(e) = self.on_bar(bar) {
1051 log_error(&e);
1052 }
1053 }
1054
1055 fn handle_mark_price(&mut self, mark_price: &MarkPriceUpdate)
1057 where
1058 Self: Component,
1059 {
1060 log_received(&mark_price);
1061
1062 if self.not_running() {
1063 log_not_running(&mark_price);
1064 return;
1065 }
1066
1067 if let Err(e) = self.on_mark_price(mark_price) {
1068 log_error(&e);
1069 }
1070 }
1071
1072 fn handle_index_price(&mut self, index_price: &IndexPriceUpdate)
1074 where
1075 Self: Component,
1076 {
1077 log_received(&index_price);
1078
1079 if self.not_running() {
1080 log_not_running(&index_price);
1081 return;
1082 }
1083
1084 if let Err(e) = self.on_index_price(index_price) {
1085 log_error(&e);
1086 }
1087 }
1088
1089 fn handle_funding_rate(&mut self, funding_rate: &FundingRateUpdate)
1091 where
1092 Self: Component,
1093 {
1094 log_received(&funding_rate);
1095
1096 if self.not_running() {
1097 log_not_running(&funding_rate);
1098 return;
1099 }
1100
1101 if let Err(e) = self.on_funding_rate(funding_rate) {
1102 log_error(&e);
1103 }
1104 }
1105
1106 fn handle_option_greeks(&mut self, greeks: &OptionGreeks)
1108 where
1109 Self: Component,
1110 {
1111 log_received(&greeks);
1112
1113 if self.not_running() {
1114 log_not_running(&greeks);
1115 return;
1116 }
1117
1118 if let Err(e) = self.on_option_greeks(greeks) {
1119 log_error(&e);
1120 }
1121 }
1122
1123 fn handle_option_chain(&mut self, slice: &OptionChainSlice)
1125 where
1126 Self: Component,
1127 {
1128 log_received(&slice);
1129
1130 if self.not_running() {
1131 log_not_running(&slice);
1132 return;
1133 }
1134
1135 if let Err(e) = self.on_option_chain(slice) {
1136 log_error(&e);
1137 }
1138 }
1139
1140 fn handle_instrument_status(&mut self, status: &InstrumentStatus)
1142 where
1143 Self: Component,
1144 {
1145 log_received(&status);
1146
1147 if self.not_running() {
1148 log_not_running(&status);
1149 return;
1150 }
1151
1152 if let Err(e) = self.on_instrument_status(status) {
1153 log_error(&e);
1154 }
1155 }
1156
1157 fn handle_instrument_close(&mut self, close: &InstrumentClose)
1159 where
1160 Self: Component,
1161 {
1162 log_received(&close);
1163
1164 if self.not_running() {
1165 log_not_running(&close);
1166 return;
1167 }
1168
1169 if let Err(e) = self.on_instrument_close(close) {
1170 log_error(&e);
1171 }
1172 }
1173
1174 #[cfg(feature = "defi")]
1175 fn handle_block(&mut self, block: &Block)
1177 where
1178 Self: Component,
1179 {
1180 log_received(&block);
1181
1182 if self.not_running() {
1183 log_not_running(&block);
1184 return;
1185 }
1186
1187 if let Err(e) = self.on_block(block) {
1188 log_error(&e);
1189 }
1190 }
1191
1192 #[cfg(feature = "defi")]
1193 fn handle_pool(&mut self, pool: &Pool)
1195 where
1196 Self: Component,
1197 {
1198 log_received(&pool);
1199
1200 if self.not_running() {
1201 log_not_running(&pool);
1202 return;
1203 }
1204
1205 if let Err(e) = self.on_pool(pool) {
1206 log_error(&e);
1207 }
1208 }
1209
1210 #[cfg(feature = "defi")]
1211 fn handle_pool_swap(&mut self, swap: &PoolSwap)
1213 where
1214 Self: Component,
1215 {
1216 log_received(&swap);
1217
1218 if self.not_running() {
1219 log_not_running(&swap);
1220 return;
1221 }
1222
1223 if let Err(e) = self.on_pool_swap(swap) {
1224 log_error(&e);
1225 }
1226 }
1227
1228 #[cfg(feature = "defi")]
1229 fn handle_pool_liquidity_update(&mut self, update: &PoolLiquidityUpdate)
1231 where
1232 Self: Component,
1233 {
1234 log_received(&update);
1235
1236 if self.not_running() {
1237 log_not_running(&update);
1238 return;
1239 }
1240
1241 if let Err(e) = self.on_pool_liquidity_update(update) {
1242 log_error(&e);
1243 }
1244 }
1245
1246 #[cfg(feature = "defi")]
1247 fn handle_pool_fee_collect(&mut self, collect: &PoolFeeCollect)
1249 where
1250 Self: Component,
1251 {
1252 log_received(&collect);
1253
1254 if self.not_running() {
1255 log_not_running(&collect);
1256 return;
1257 }
1258
1259 if let Err(e) = self.on_pool_fee_collect(collect) {
1260 log_error(&e);
1261 }
1262 }
1263
1264 #[cfg(feature = "defi")]
1265 fn handle_pool_flash(&mut self, flash: &PoolFlash)
1267 where
1268 Self: Component,
1269 {
1270 log_received(&flash);
1271
1272 if self.not_running() {
1273 log_not_running(&flash);
1274 return;
1275 }
1276
1277 if let Err(e) = self.on_pool_flash(flash) {
1278 log_error(&e);
1279 }
1280 }
1281
1282 fn handle_historical_data(&mut self, data: &dyn Any) {
1284 log_received(&data);
1285
1286 if let Err(e) = self.on_historical_data(data) {
1287 log_error(&e);
1288 }
1289 }
1290
1291 fn handle_data_response(&mut self, resp: &CustomDataResponse) {
1293 if let Some(data) = resp.data.as_ref().downcast_ref::<Vec<CustomData>>() {
1294 log_received_bulk("CustomDataResponse", &resp.correlation_id, data.len());
1295 log::trace!("{RECV} {resp:?}");
1296 } else {
1297 log_received(&resp);
1298 }
1299
1300 if let Err(e) = self.on_historical_data(resp.data.as_ref()) {
1301 log_error(&e);
1302 }
1303 }
1304
1305 fn handle_instrument_response(&mut self, resp: &InstrumentResponse) {
1307 log_received(&resp);
1308
1309 if let Err(e) = self.on_instrument(&resp.data) {
1310 log_error(&e);
1311 }
1312 }
1313
1314 fn handle_instruments_response(&mut self, resp: &InstrumentsResponse) {
1316 log_received_bulk("InstrumentsResponse", &resp.correlation_id, resp.data.len());
1317 log::trace!("{RECV} {resp:?}");
1318
1319 for inst in &resp.data {
1320 if let Err(e) = self.on_instrument(inst) {
1321 log_error(&e);
1322 }
1323 }
1324 }
1325
1326 fn handle_book_response(&mut self, resp: &BookResponse) {
1328 log_received(&resp);
1329
1330 if let Err(e) = self.on_book(&resp.data) {
1331 log_error(&e);
1332 }
1333 }
1334
1335 fn handle_book_deltas_response(&mut self, resp: &BookDeltasResponse) {
1337 log_received_bulk("BookDeltasResponse", &resp.correlation_id, resp.data.len());
1338 log::trace!("{RECV} {resp:?}");
1339
1340 if let Err(e) = self.on_historical_book_deltas(&resp.data) {
1341 log_error(&e);
1342 }
1343 }
1344
1345 fn handle_book_depth_response(&mut self, resp: &BookDepthResponse) {
1347 log_received_bulk("BookDepthResponse", &resp.correlation_id, resp.data.len());
1348 log::trace!("{RECV} {resp:?}");
1349
1350 if let Err(e) = self.on_historical_book_depth(&resp.data) {
1351 log_error(&e);
1352 }
1353 }
1354
1355 fn handle_quotes_response(&mut self, resp: &QuotesResponse)
1357 where
1358 Self: DataActorNative,
1359 {
1360 log_received_bulk("QuotesResponse", &resp.correlation_id, resp.data.len());
1361 log::trace!("{RECV} {resp:?}");
1362
1363 if let Err(e) = self.core().handle_indicators_for_quotes(&resp.data) {
1364 log_error(&e);
1365 return;
1366 }
1367
1368 if let Err(e) = self.on_historical_quotes(&resp.data) {
1369 log_error(&e);
1370 }
1371 }
1372
1373 fn handle_trades_response(&mut self, resp: &TradesResponse)
1375 where
1376 Self: DataActorNative,
1377 {
1378 log_received_bulk("TradesResponse", &resp.correlation_id, resp.data.len());
1379 log::trace!("{RECV} {resp:?}");
1380
1381 if let Err(e) = self.core().handle_indicators_for_trades(&resp.data) {
1382 log_error(&e);
1383 return;
1384 }
1385
1386 if let Err(e) = self.on_historical_trades(&resp.data) {
1387 log_error(&e);
1388 }
1389 }
1390
1391 fn handle_bars_response(&mut self, resp: &BarsResponse)
1393 where
1394 Self: DataActorNative,
1395 {
1396 log_received_bulk("BarsResponse", &resp.correlation_id, resp.data.len());
1397 log::trace!("{RECV} {resp:?}");
1398
1399 if let Err(e) = self.core().handle_indicators_for_bars(&resp.data) {
1400 log_error(&e);
1401 return;
1402 }
1403
1404 if let Err(e) = self.on_historical_bars(&resp.data) {
1405 log_error(&e);
1406 }
1407 }
1408
1409 fn handle_funding_rates_response(&mut self, resp: &FundingRatesResponse) {
1411 log_received_bulk(
1412 "FundingRatesResponse",
1413 &resp.correlation_id,
1414 resp.data.len(),
1415 );
1416 log::trace!("{RECV} {resp:?}");
1417
1418 if let Err(e) = self.on_historical_funding_rates(&resp.data) {
1419 log_error(&e);
1420 }
1421 }
1422
1423 fn subscribe_data(
1425 &mut self,
1426 data_type: DataType,
1427 client_id: Option<ClientId>,
1428 params: Option<Params>,
1429 ) where
1430 Self: DataActorNative,
1431 Self: 'static + Debug + Sized,
1432 {
1433 let actor_id = self.core().actor_id().inner();
1434 let handler = ShareableMessageHandler::from_typed(move |data: &CustomData| {
1435 if let Some(mut actor) = try_get_actor_unchecked::<Self>(&actor_id) {
1436 actor.handle_data(data);
1437 } else {
1438 log::error!("Actor {actor_id} not found for data handling");
1439 }
1440 });
1441
1442 DataActorCore::subscribe_data(self.core_mut(), handler, data_type, client_id, params);
1443 }
1444
1445 fn subscribe_signal(&mut self, name: &str, priority: Option<u32>)
1460 where
1461 Self: DataActorNative,
1462 Self: 'static + Debug + Sized,
1463 {
1464 let actor_id = self.core().actor_id().inner();
1465 let handler = ShareableMessageHandler::from_typed(move |data: &CustomData| {
1468 if let Some(signal) = data.data.as_any().downcast_ref::<Signal>() {
1469 if let Some(mut actor) = try_get_actor_unchecked::<Self>(&actor_id) {
1470 actor.handle_signal(signal);
1471 } else {
1472 log::error!("Actor {actor_id} not found for signal handling");
1473 }
1474 }
1475 });
1476
1477 DataActorCore::subscribe_signal(self.core_mut(), handler, name, priority);
1478 }
1479
1480 fn subscribe_queue_state(&mut self, channel: Option<SystemChannel>, priority: Option<u32>)
1488 where
1489 Self: DataActorNative,
1490 Self: 'static + Debug + Sized,
1491 {
1492 let actor_id = self.core().actor_id().inner();
1493 let handler = ShareableMessageHandler::from_typed(move |event: &QueueStateChanged| {
1494 if let Some(mut actor) = try_get_actor_unchecked::<Self>(&actor_id) {
1495 actor.handle_queue_state(event);
1496 } else {
1497 log::error!("Actor {actor_id} not found for queue state change handling");
1498 }
1499 });
1500
1501 DataActorCore::subscribe_queue_state(self.core_mut(), handler, channel, priority);
1502 }
1503
1504 fn subscribe_socket_state(
1513 &mut self,
1514 client_id: Option<ClientId>,
1515 endpoint: Option<&str>,
1516 priority: Option<u32>,
1517 ) where
1518 Self: DataActorNative,
1519 Self: 'static + Debug + Sized,
1520 {
1521 let actor_id = self.core().actor_id().inner();
1522 let handler = ShareableMessageHandler::from_typed(move |event: &SocketStateChanged| {
1523 if let Some(mut actor) = try_get_actor_unchecked::<Self>(&actor_id) {
1524 actor.handle_socket_state(event);
1525 } else {
1526 log::error!("Actor {actor_id} not found for socket state change handling");
1527 }
1528 });
1529
1530 DataActorCore::subscribe_socket_state(
1531 self.core_mut(),
1532 handler,
1533 client_id,
1534 endpoint,
1535 priority,
1536 );
1537 }
1538
1539 fn subscribe_quotes(
1541 &mut self,
1542 instrument_id: InstrumentId,
1543 client_id: Option<ClientId>,
1544 params: Option<Params>,
1545 ) where
1546 Self: DataActorNative,
1547 Self: 'static + Debug + Sized,
1548 {
1549 let actor_id = self.core().actor_id().inner();
1550 let topic = get_quotes_topic(instrument_id);
1551
1552 let handler = TypedHandler::from(move |quote: &QuoteTick| {
1553 if let Some(mut actor) = try_get_actor_unchecked::<Self>(&actor_id) {
1554 actor.handle_quote(quote);
1555 } else {
1556 log::error!("Actor {actor_id} not found for quote handling");
1557 }
1558 });
1559
1560 DataActorCore::subscribe_quotes(
1561 self.core_mut(),
1562 topic,
1563 handler,
1564 instrument_id,
1565 client_id,
1566 params,
1567 );
1568 }
1569
1570 fn subscribe_instruments(
1572 &mut self,
1573 venue: Venue,
1574 client_id: Option<ClientId>,
1575 params: Option<Params>,
1576 ) where
1577 Self: DataActorNative,
1578 Self: 'static + Debug + Sized,
1579 {
1580 let actor_id = self.core().actor_id().inner();
1581 let pattern = get_instruments_pattern(venue);
1582
1583 let handler = TypedHandler::from(move |instrument: &InstrumentAny| {
1584 if let Some(mut actor) = try_get_actor_unchecked::<Self>(&actor_id) {
1585 actor.handle_instrument(instrument);
1586 } else {
1587 log::error!("Actor {actor_id} not found for instruments handling");
1588 }
1589 });
1590
1591 DataActorCore::subscribe_instruments(
1592 self.core_mut(),
1593 pattern,
1594 handler,
1595 venue,
1596 client_id,
1597 params,
1598 );
1599 }
1600
1601 fn subscribe_instrument(
1603 &mut self,
1604 instrument_id: InstrumentId,
1605 client_id: Option<ClientId>,
1606 params: Option<Params>,
1607 ) where
1608 Self: DataActorNative,
1609 Self: 'static + Debug + Sized,
1610 {
1611 let actor_id = self.core().actor_id().inner();
1612 let topic = get_instrument_topic(instrument_id);
1613
1614 let handler = TypedHandler::from(move |instrument: &InstrumentAny| {
1615 if let Some(mut actor) = try_get_actor_unchecked::<Self>(&actor_id) {
1616 actor.handle_instrument(instrument);
1617 } else {
1618 log::error!("Actor {actor_id} not found for instrument handling");
1619 }
1620 });
1621
1622 DataActorCore::subscribe_instrument(
1623 self.core_mut(),
1624 topic,
1625 handler,
1626 instrument_id,
1627 client_id,
1628 params,
1629 );
1630 }
1631
1632 fn subscribe_book_deltas(
1638 &mut self,
1639 instrument_id: InstrumentId,
1640 book_type: BookType,
1641 depth: Option<NonZeroUsize>,
1642 client_id: Option<ClientId>,
1643 managed: bool,
1644 params: Option<Params>,
1645 ) where
1646 Self: DataActorNative,
1647 Self: 'static + Debug + Sized,
1648 {
1649 let actor_id = self.core().actor_id().inner();
1650 let is_parent = is_parent_subscription(params.as_ref());
1651 let pattern = if is_parent {
1652 get_book_deltas_pattern(instrument_id)
1653 } else {
1654 get_book_deltas_topic(instrument_id).into()
1655 };
1656
1657 let handler = TypedHandler::from(move |deltas: &OrderBookDeltas| {
1658 if let Some(mut actor) = try_get_actor_unchecked::<Self>(&actor_id) {
1659 actor.handle_book_deltas(deltas);
1660 } else {
1661 log::error!("Actor {actor_id} not found for book deltas handling");
1662 }
1663 });
1664
1665 DataActorCore::subscribe_book_deltas(
1666 self.core_mut(),
1667 pattern,
1668 handler,
1669 instrument_id,
1670 book_type,
1671 depth,
1672 client_id,
1673 managed,
1674 params,
1675 );
1676 }
1677
1678 fn subscribe_book_depth10(
1684 &mut self,
1685 instrument_id: InstrumentId,
1686 book_type: BookType,
1687 client_id: Option<ClientId>,
1688 managed: bool,
1689 params: Option<Params>,
1690 ) where
1691 Self: DataActorNative,
1692 Self: 'static + Debug + Sized,
1693 {
1694 let actor_id = self.core().actor_id().inner();
1695 let pattern = if is_parent_subscription(params.as_ref()) {
1696 get_book_depth10_pattern(instrument_id)
1697 } else {
1698 get_book_depth10_topic(instrument_id).into()
1699 };
1700
1701 let handler = TypedHandler::from(move |depth: &OrderBookDepth10| {
1702 if let Some(mut actor) = try_get_actor_unchecked::<Self>(&actor_id) {
1703 actor.handle_book_depth(depth);
1704 } else {
1705 log::error!("Actor {actor_id} not found for book depth handling");
1706 }
1707 });
1708
1709 DataActorCore::subscribe_book_depth10(
1710 self.core_mut(),
1711 pattern,
1712 handler,
1713 instrument_id,
1714 book_type,
1715 client_id,
1716 managed,
1717 params,
1718 );
1719 }
1720
1721 fn subscribe_book_at_interval(
1723 &mut self,
1724 instrument_id: InstrumentId,
1725 book_type: BookType,
1726 depth: Option<NonZeroUsize>,
1727 interval_ms: NonZeroUsize,
1728 client_id: Option<ClientId>,
1729 params: Option<Params>,
1730 ) where
1731 Self: DataActorNative,
1732 Self: 'static + Debug + Sized,
1733 {
1734 let actor_id = self.core().actor_id().inner();
1735 let topic = get_book_snapshots_topic(instrument_id, interval_ms);
1736
1737 let handler = TypedHandler::from(move |book: &OrderBook| {
1738 if let Some(mut actor) = try_get_actor_unchecked::<Self>(&actor_id) {
1739 actor.handle_book(book);
1740 } else {
1741 log::error!("Actor {actor_id} not found for book handling");
1742 }
1743 });
1744
1745 DataActorCore::subscribe_book_at_interval(
1746 self.core_mut(),
1747 topic,
1748 handler,
1749 instrument_id,
1750 book_type,
1751 depth,
1752 interval_ms,
1753 client_id,
1754 params,
1755 );
1756 }
1757
1758 fn subscribe_trades(
1760 &mut self,
1761 instrument_id: InstrumentId,
1762 client_id: Option<ClientId>,
1763 params: Option<Params>,
1764 ) where
1765 Self: DataActorNative,
1766 Self: 'static + Debug + Sized,
1767 {
1768 let actor_id = self.core().actor_id().inner();
1769 let topic = get_trades_topic(instrument_id);
1770
1771 let handler = TypedHandler::from(move |trade: &TradeTick| {
1772 if let Some(mut actor) = try_get_actor_unchecked::<Self>(&actor_id) {
1773 actor.handle_trade(trade);
1774 } else {
1775 log::error!("Actor {actor_id} not found for trade handling");
1776 }
1777 });
1778
1779 DataActorCore::subscribe_trades(
1780 self.core_mut(),
1781 topic,
1782 handler,
1783 instrument_id,
1784 client_id,
1785 params,
1786 );
1787 }
1788
1789 fn subscribe_bars(
1791 &mut self,
1792 bar_type: BarType,
1793 client_id: Option<ClientId>,
1794 params: Option<Params>,
1795 ) where
1796 Self: DataActorNative,
1797 Self: 'static + Debug + Sized,
1798 {
1799 let actor_id = self.core().actor_id().inner();
1800 let topic = get_bars_topic(bar_type.standard());
1802
1803 let handler = TypedHandler::from(move |bar: &Bar| {
1804 if let Some(mut actor) = try_get_actor_unchecked::<Self>(&actor_id) {
1805 actor.handle_bar(bar);
1806 } else {
1807 log::error!("Actor {actor_id} not found for bar handling");
1808 }
1809 });
1810
1811 DataActorCore::subscribe_bars(self.core_mut(), topic, handler, bar_type, client_id, params);
1812 }
1813
1814 fn subscribe_mark_prices(
1816 &mut self,
1817 instrument_id: InstrumentId,
1818 client_id: Option<ClientId>,
1819 params: Option<Params>,
1820 ) where
1821 Self: DataActorNative,
1822 Self: 'static + Debug + Sized,
1823 {
1824 let actor_id = self.core().actor_id().inner();
1825 let topic = get_mark_price_topic(instrument_id);
1826
1827 let handler = TypedHandler::from(move |mark_price: &MarkPriceUpdate| {
1828 if let Some(mut actor) = try_get_actor_unchecked::<Self>(&actor_id) {
1829 actor.handle_mark_price(mark_price);
1830 } else {
1831 log::error!("Actor {actor_id} not found for mark price handling");
1832 }
1833 });
1834
1835 DataActorCore::subscribe_mark_prices(
1836 self.core_mut(),
1837 topic,
1838 handler,
1839 instrument_id,
1840 client_id,
1841 params,
1842 );
1843 }
1844
1845 fn subscribe_index_prices(
1847 &mut self,
1848 instrument_id: InstrumentId,
1849 client_id: Option<ClientId>,
1850 params: Option<Params>,
1851 ) where
1852 Self: DataActorNative,
1853 Self: 'static + Debug + Sized,
1854 {
1855 let actor_id = self.core().actor_id().inner();
1856 let topic = get_index_price_topic(instrument_id);
1857
1858 let handler = TypedHandler::from(move |index_price: &IndexPriceUpdate| {
1859 if let Some(mut actor) = try_get_actor_unchecked::<Self>(&actor_id) {
1860 actor.handle_index_price(index_price);
1861 } else {
1862 log::error!("Actor {actor_id} not found for index price handling");
1863 }
1864 });
1865
1866 DataActorCore::subscribe_index_prices(
1867 self.core_mut(),
1868 topic,
1869 handler,
1870 instrument_id,
1871 client_id,
1872 params,
1873 );
1874 }
1875
1876 fn subscribe_funding_rates(
1878 &mut self,
1879 instrument_id: InstrumentId,
1880 client_id: Option<ClientId>,
1881 params: Option<Params>,
1882 ) where
1883 Self: DataActorNative,
1884 Self: 'static + Debug + Sized,
1885 {
1886 let actor_id = self.core().actor_id().inner();
1887 let topic = get_funding_rate_topic(instrument_id);
1888
1889 let handler = TypedHandler::from(move |funding_rate: &FundingRateUpdate| {
1890 if let Some(mut actor) = try_get_actor_unchecked::<Self>(&actor_id) {
1891 actor.handle_funding_rate(funding_rate);
1892 } else {
1893 log::error!("Actor {actor_id} not found for funding rate handling");
1894 }
1895 });
1896
1897 DataActorCore::subscribe_funding_rates(
1898 self.core_mut(),
1899 topic,
1900 handler,
1901 instrument_id,
1902 client_id,
1903 params,
1904 );
1905 }
1906
1907 fn subscribe_option_greeks(
1909 &mut self,
1910 instrument_id: InstrumentId,
1911 client_id: Option<ClientId>,
1912 params: Option<Params>,
1913 ) where
1914 Self: DataActorNative,
1915 Self: 'static + Debug + Sized,
1916 {
1917 let actor_id = self.core().actor_id().inner();
1918 let topic = get_option_greeks_topic(instrument_id);
1919
1920 let handler = TypedHandler::from(move |option_greeks: &OptionGreeks| {
1921 if let Some(mut actor) = try_get_actor_unchecked::<Self>(&actor_id) {
1922 actor.handle_option_greeks(option_greeks);
1923 } else {
1924 log::error!("Actor {actor_id} not found for option greeks handling");
1925 }
1926 });
1927
1928 DataActorCore::subscribe_option_greeks(
1929 self.core_mut(),
1930 topic,
1931 handler,
1932 instrument_id,
1933 client_id,
1934 params,
1935 );
1936 }
1937
1938 fn subscribe_instrument_status(
1940 &mut self,
1941 instrument_id: InstrumentId,
1942 client_id: Option<ClientId>,
1943 params: Option<Params>,
1944 ) where
1945 Self: DataActorNative,
1946 Self: 'static + Debug + Sized,
1947 {
1948 let actor_id = self.core().actor_id().inner();
1949 let topic = get_instrument_status_topic(instrument_id);
1950
1951 let handler = ShareableMessageHandler::from_typed(move |status: &InstrumentStatus| {
1952 if let Some(mut actor) = try_get_actor_unchecked::<Self>(&actor_id) {
1953 actor.handle_instrument_status(status);
1954 } else {
1955 log::error!("Actor {actor_id} not found for instrument status handling");
1956 }
1957 });
1958
1959 DataActorCore::subscribe_instrument_status(
1960 self.core_mut(),
1961 topic,
1962 handler,
1963 instrument_id,
1964 client_id,
1965 params,
1966 );
1967 }
1968
1969 fn subscribe_instrument_close(
1971 &mut self,
1972 instrument_id: InstrumentId,
1973 client_id: Option<ClientId>,
1974 params: Option<Params>,
1975 ) where
1976 Self: DataActorNative,
1977 Self: 'static + Debug + Sized,
1978 {
1979 let actor_id = self.core().actor_id().inner();
1980 let topic = get_instrument_close_topic(instrument_id);
1981
1982 let handler = ShareableMessageHandler::from_typed(move |close: &InstrumentClose| {
1983 if let Some(mut actor) = try_get_actor_unchecked::<Self>(&actor_id) {
1984 actor.handle_instrument_close(close);
1985 } else {
1986 log::error!("Actor {actor_id} not found for instrument close handling");
1987 }
1988 });
1989
1990 DataActorCore::subscribe_instrument_close(
1991 self.core_mut(),
1992 topic,
1993 handler,
1994 instrument_id,
1995 client_id,
1996 params,
1997 );
1998 }
1999
2000 fn subscribe_option_chain(
2005 &mut self,
2006 series_id: OptionSeriesId,
2007 strike_range: StrikeRange,
2008 snapshot_interval_ms: Option<u64>,
2009 client_id: Option<ClientId>,
2010 params: Option<Params>,
2011 ) where
2012 Self: DataActorNative,
2013 Self: 'static + Debug + Sized,
2014 {
2015 let actor_id = self.core().actor_id().inner();
2016 let topic = get_option_chain_topic(series_id);
2017
2018 let handler = TypedHandler::from(move |slice: &OptionChainSlice| {
2019 if let Some(mut actor) = try_get_actor_unchecked::<Self>(&actor_id) {
2020 actor.handle_option_chain(slice);
2021 } else {
2022 log::error!("Actor {actor_id} not found for option chain handling");
2023 }
2024 });
2025
2026 DataActorCore::subscribe_option_chain(
2027 self.core_mut(),
2028 topic,
2029 handler,
2030 series_id,
2031 strike_range,
2032 snapshot_interval_ms,
2033 client_id,
2034 params,
2035 );
2036 }
2037
2038 #[cfg(feature = "defi")]
2039 fn subscribe_blocks(
2041 &mut self,
2042 chain: Blockchain,
2043 client_id: Option<ClientId>,
2044 params: Option<Params>,
2045 ) where
2046 Self: DataActorNative,
2047 Self: 'static + Debug + Sized,
2048 {
2049 let actor_id = self.core().actor_id().inner();
2050 let topic = defi::switchboard::get_defi_blocks_topic(chain);
2051
2052 let handler = TypedHandler::from(move |block: &Block| {
2053 if let Some(mut actor) = try_get_actor_unchecked::<Self>(&actor_id) {
2054 actor.handle_block(block);
2055 } else {
2056 log::error!("Actor {actor_id} not found for block handling");
2057 }
2058 });
2059
2060 DataActorCore::subscribe_blocks(self.core_mut(), topic, handler, chain, client_id, params);
2061 }
2062
2063 #[cfg(feature = "defi")]
2064 fn subscribe_pool(
2066 &mut self,
2067 instrument_id: InstrumentId,
2068 client_id: Option<ClientId>,
2069 params: Option<Params>,
2070 ) where
2071 Self: DataActorNative,
2072 Self: 'static + Debug + Sized,
2073 {
2074 let actor_id = self.core().actor_id().inner();
2075 let topic = defi::switchboard::get_defi_pool_topic(instrument_id);
2076
2077 let handler = TypedHandler::from(move |pool: &Pool| {
2078 if let Some(mut actor) = try_get_actor_unchecked::<Self>(&actor_id) {
2079 actor.handle_pool(pool);
2080 } else {
2081 log::error!("Actor {actor_id} not found for pool handling");
2082 }
2083 });
2084
2085 DataActorCore::subscribe_pool(
2086 self.core_mut(),
2087 topic,
2088 handler,
2089 instrument_id,
2090 client_id,
2091 params,
2092 );
2093 }
2094
2095 #[cfg(feature = "defi")]
2096 fn subscribe_pool_swaps(
2098 &mut self,
2099 instrument_id: InstrumentId,
2100 client_id: Option<ClientId>,
2101 params: Option<Params>,
2102 ) where
2103 Self: DataActorNative,
2104 Self: 'static + Debug + Sized,
2105 {
2106 let actor_id = self.core().actor_id().inner();
2107 let topic = defi::switchboard::get_defi_pool_swaps_topic(instrument_id);
2108
2109 let handler = TypedHandler::from(move |swap: &PoolSwap| {
2110 if let Some(mut actor) = try_get_actor_unchecked::<Self>(&actor_id) {
2111 actor.handle_pool_swap(swap);
2112 } else {
2113 log::error!("Actor {actor_id} not found for pool swap handling");
2114 }
2115 });
2116
2117 DataActorCore::subscribe_pool_swaps(
2118 self.core_mut(),
2119 topic,
2120 handler,
2121 instrument_id,
2122 client_id,
2123 params,
2124 );
2125 }
2126
2127 #[cfg(feature = "defi")]
2128 fn subscribe_pool_liquidity_updates(
2130 &mut self,
2131 instrument_id: InstrumentId,
2132 client_id: Option<ClientId>,
2133 params: Option<Params>,
2134 ) where
2135 Self: DataActorNative,
2136 Self: 'static + Debug + Sized,
2137 {
2138 let actor_id = self.core().actor_id().inner();
2139 let topic = defi::switchboard::get_defi_liquidity_topic(instrument_id);
2140
2141 let handler = TypedHandler::from(move |update: &PoolLiquidityUpdate| {
2142 if let Some(mut actor) = try_get_actor_unchecked::<Self>(&actor_id) {
2143 actor.handle_pool_liquidity_update(update);
2144 } else {
2145 log::error!("Actor {actor_id} not found for pool liquidity update handling");
2146 }
2147 });
2148
2149 DataActorCore::subscribe_pool_liquidity_updates(
2150 self.core_mut(),
2151 topic,
2152 handler,
2153 instrument_id,
2154 client_id,
2155 params,
2156 );
2157 }
2158
2159 #[cfg(feature = "defi")]
2160 fn subscribe_pool_fee_collects(
2162 &mut self,
2163 instrument_id: InstrumentId,
2164 client_id: Option<ClientId>,
2165 params: Option<Params>,
2166 ) where
2167 Self: DataActorNative,
2168 Self: 'static + Debug + Sized,
2169 {
2170 let actor_id = self.core().actor_id().inner();
2171 let topic = defi::switchboard::get_defi_collect_topic(instrument_id);
2172
2173 let handler = TypedHandler::from(move |collect: &PoolFeeCollect| {
2174 if let Some(mut actor) = try_get_actor_unchecked::<Self>(&actor_id) {
2175 actor.handle_pool_fee_collect(collect);
2176 } else {
2177 log::error!("Actor {actor_id} not found for pool fee collect handling");
2178 }
2179 });
2180
2181 DataActorCore::subscribe_pool_fee_collects(
2182 self.core_mut(),
2183 topic,
2184 handler,
2185 instrument_id,
2186 client_id,
2187 params,
2188 );
2189 }
2190
2191 #[cfg(feature = "defi")]
2192 fn subscribe_pool_flash_events(
2194 &mut self,
2195 instrument_id: InstrumentId,
2196 client_id: Option<ClientId>,
2197 params: Option<Params>,
2198 ) where
2199 Self: DataActorNative,
2200 Self: 'static + Debug + Sized,
2201 {
2202 let actor_id = self.core().actor_id().inner();
2203 let topic = defi::switchboard::get_defi_flash_topic(instrument_id);
2204
2205 let handler = TypedHandler::from(move |flash: &PoolFlash| {
2206 if let Some(mut actor) = try_get_actor_unchecked::<Self>(&actor_id) {
2207 actor.handle_pool_flash(flash);
2208 } else {
2209 log::error!("Actor {actor_id} not found for pool flash handling");
2210 }
2211 });
2212
2213 DataActorCore::subscribe_pool_flash_events(
2214 self.core_mut(),
2215 topic,
2216 handler,
2217 instrument_id,
2218 client_id,
2219 params,
2220 );
2221 }
2222
2223 fn unsubscribe_data(
2225 &mut self,
2226 data_type: DataType,
2227 client_id: Option<ClientId>,
2228 params: Option<Params>,
2229 ) where
2230 Self: DataActorNative,
2231 Self: 'static + Debug + Sized,
2232 {
2233 DataActorCore::unsubscribe_data(self.core_mut(), data_type, client_id, params);
2234 }
2235
2236 fn unsubscribe_signal(&mut self, name: &str)
2238 where
2239 Self: DataActorNative,
2240 Self: 'static + Debug + Sized,
2241 {
2242 DataActorCore::unsubscribe_signal(self.core_mut(), name);
2243 }
2244
2245 fn unsubscribe_queue_state(&mut self, channel: Option<SystemChannel>)
2249 where
2250 Self: DataActorNative,
2251 Self: 'static + Debug + Sized,
2252 {
2253 DataActorCore::unsubscribe_queue_state(self.core_mut(), channel);
2254 }
2255
2256 fn unsubscribe_socket_state(&mut self, client_id: Option<ClientId>, endpoint: Option<&str>)
2260 where
2261 Self: DataActorNative,
2262 Self: 'static + Debug + Sized,
2263 {
2264 DataActorCore::unsubscribe_socket_state(self.core_mut(), client_id, endpoint);
2265 }
2266
2267 fn unsubscribe_instruments(
2269 &mut self,
2270 venue: Venue,
2271 client_id: Option<ClientId>,
2272 params: Option<Params>,
2273 ) where
2274 Self: DataActorNative,
2275 Self: 'static + Debug + Sized,
2276 {
2277 DataActorCore::unsubscribe_instruments(self.core_mut(), venue, client_id, params);
2278 }
2279
2280 fn unsubscribe_instrument(
2282 &mut self,
2283 instrument_id: InstrumentId,
2284 client_id: Option<ClientId>,
2285 params: Option<Params>,
2286 ) where
2287 Self: DataActorNative,
2288 Self: 'static + Debug + Sized,
2289 {
2290 DataActorCore::unsubscribe_instrument(self.core_mut(), instrument_id, client_id, params);
2291 }
2292
2293 fn unsubscribe_book_deltas(
2295 &mut self,
2296 instrument_id: InstrumentId,
2297 client_id: Option<ClientId>,
2298 params: Option<Params>,
2299 ) where
2300 Self: DataActorNative,
2301 Self: 'static + Debug + Sized,
2302 {
2303 DataActorCore::unsubscribe_book_deltas(self.core_mut(), instrument_id, client_id, params);
2304 }
2305
2306 fn unsubscribe_book_depth10(
2308 &mut self,
2309 instrument_id: InstrumentId,
2310 client_id: Option<ClientId>,
2311 params: Option<Params>,
2312 ) where
2313 Self: DataActorNative,
2314 Self: 'static + Debug + Sized,
2315 {
2316 DataActorCore::unsubscribe_book_depth10(self.core_mut(), instrument_id, client_id, params);
2317 }
2318
2319 fn unsubscribe_book_at_interval(
2321 &mut self,
2322 instrument_id: InstrumentId,
2323 interval_ms: NonZeroUsize,
2324 client_id: Option<ClientId>,
2325 params: Option<Params>,
2326 ) where
2327 Self: DataActorNative,
2328 Self: 'static + Debug + Sized,
2329 {
2330 DataActorCore::unsubscribe_book_at_interval(
2331 self.core_mut(),
2332 instrument_id,
2333 interval_ms,
2334 client_id,
2335 params,
2336 );
2337 }
2338
2339 fn unsubscribe_quotes(
2341 &mut self,
2342 instrument_id: InstrumentId,
2343 client_id: Option<ClientId>,
2344 params: Option<Params>,
2345 ) where
2346 Self: DataActorNative,
2347 Self: 'static + Debug + Sized,
2348 {
2349 DataActorCore::unsubscribe_quotes(self.core_mut(), instrument_id, client_id, params);
2350 }
2351
2352 fn unsubscribe_trades(
2354 &mut self,
2355 instrument_id: InstrumentId,
2356 client_id: Option<ClientId>,
2357 params: Option<Params>,
2358 ) where
2359 Self: DataActorNative,
2360 Self: 'static + Debug + Sized,
2361 {
2362 DataActorCore::unsubscribe_trades(self.core_mut(), instrument_id, client_id, params);
2363 }
2364
2365 fn unsubscribe_bars(
2367 &mut self,
2368 bar_type: BarType,
2369 client_id: Option<ClientId>,
2370 params: Option<Params>,
2371 ) where
2372 Self: DataActorNative,
2373 Self: 'static + Debug + Sized,
2374 {
2375 DataActorCore::unsubscribe_bars(self.core_mut(), bar_type, client_id, params);
2376 }
2377
2378 fn unsubscribe_mark_prices(
2380 &mut self,
2381 instrument_id: InstrumentId,
2382 client_id: Option<ClientId>,
2383 params: Option<Params>,
2384 ) where
2385 Self: DataActorNative,
2386 Self: 'static + Debug + Sized,
2387 {
2388 DataActorCore::unsubscribe_mark_prices(self.core_mut(), instrument_id, client_id, params);
2389 }
2390
2391 fn unsubscribe_index_prices(
2393 &mut self,
2394 instrument_id: InstrumentId,
2395 client_id: Option<ClientId>,
2396 params: Option<Params>,
2397 ) where
2398 Self: DataActorNative,
2399 Self: 'static + Debug + Sized,
2400 {
2401 DataActorCore::unsubscribe_index_prices(self.core_mut(), instrument_id, client_id, params);
2402 }
2403
2404 fn unsubscribe_funding_rates(
2406 &mut self,
2407 instrument_id: InstrumentId,
2408 client_id: Option<ClientId>,
2409 params: Option<Params>,
2410 ) where
2411 Self: DataActorNative,
2412 Self: 'static + Debug + Sized,
2413 {
2414 DataActorCore::unsubscribe_funding_rates(self.core_mut(), instrument_id, client_id, params);
2415 }
2416
2417 fn unsubscribe_option_greeks(
2419 &mut self,
2420 instrument_id: InstrumentId,
2421 client_id: Option<ClientId>,
2422 params: Option<Params>,
2423 ) where
2424 Self: DataActorNative,
2425 Self: 'static + Debug + Sized,
2426 {
2427 DataActorCore::unsubscribe_option_greeks(self.core_mut(), instrument_id, client_id, params);
2428 }
2429
2430 fn unsubscribe_instrument_status(
2432 &mut self,
2433 instrument_id: InstrumentId,
2434 client_id: Option<ClientId>,
2435 params: Option<Params>,
2436 ) where
2437 Self: DataActorNative,
2438 Self: 'static + Debug + Sized,
2439 {
2440 DataActorCore::unsubscribe_instrument_status(
2441 self.core_mut(),
2442 instrument_id,
2443 client_id,
2444 params,
2445 );
2446 }
2447
2448 fn unsubscribe_instrument_close(
2450 &mut self,
2451 instrument_id: InstrumentId,
2452 client_id: Option<ClientId>,
2453 params: Option<Params>,
2454 ) where
2455 Self: DataActorNative,
2456 Self: 'static + Debug + Sized,
2457 {
2458 DataActorCore::unsubscribe_instrument_close(
2459 self.core_mut(),
2460 instrument_id,
2461 client_id,
2462 params,
2463 );
2464 }
2465
2466 fn unsubscribe_option_chain(&mut self, series_id: OptionSeriesId, client_id: Option<ClientId>)
2468 where
2469 Self: DataActorNative,
2470 Self: 'static + Debug + Sized,
2471 {
2472 DataActorCore::unsubscribe_option_chain(self.core_mut(), series_id, client_id);
2473 }
2474
2475 #[cfg(feature = "defi")]
2476 fn unsubscribe_blocks(
2478 &mut self,
2479 chain: Blockchain,
2480 client_id: Option<ClientId>,
2481 params: Option<Params>,
2482 ) where
2483 Self: DataActorNative,
2484 Self: 'static + Debug + Sized,
2485 {
2486 DataActorCore::unsubscribe_blocks(self.core_mut(), chain, client_id, params);
2487 }
2488
2489 #[cfg(feature = "defi")]
2490 fn unsubscribe_pool(
2492 &mut self,
2493 instrument_id: InstrumentId,
2494 client_id: Option<ClientId>,
2495 params: Option<Params>,
2496 ) where
2497 Self: DataActorNative,
2498 Self: 'static + Debug + Sized,
2499 {
2500 DataActorCore::unsubscribe_pool(self.core_mut(), instrument_id, client_id, params);
2501 }
2502
2503 #[cfg(feature = "defi")]
2504 fn unsubscribe_pool_swaps(
2506 &mut self,
2507 instrument_id: InstrumentId,
2508 client_id: Option<ClientId>,
2509 params: Option<Params>,
2510 ) where
2511 Self: DataActorNative,
2512 Self: 'static + Debug + Sized,
2513 {
2514 DataActorCore::unsubscribe_pool_swaps(self.core_mut(), instrument_id, client_id, params);
2515 }
2516
2517 #[cfg(feature = "defi")]
2518 fn unsubscribe_pool_liquidity_updates(
2520 &mut self,
2521 instrument_id: InstrumentId,
2522 client_id: Option<ClientId>,
2523 params: Option<Params>,
2524 ) where
2525 Self: DataActorNative,
2526 Self: 'static + Debug + Sized,
2527 {
2528 DataActorCore::unsubscribe_pool_liquidity_updates(
2529 self.core_mut(),
2530 instrument_id,
2531 client_id,
2532 params,
2533 );
2534 }
2535
2536 #[cfg(feature = "defi")]
2537 fn unsubscribe_pool_fee_collects(
2539 &mut self,
2540 instrument_id: InstrumentId,
2541 client_id: Option<ClientId>,
2542 params: Option<Params>,
2543 ) where
2544 Self: DataActorNative,
2545 Self: 'static + Debug + Sized,
2546 {
2547 DataActorCore::unsubscribe_pool_fee_collects(
2548 self.core_mut(),
2549 instrument_id,
2550 client_id,
2551 params,
2552 );
2553 }
2554
2555 #[cfg(feature = "defi")]
2556 fn unsubscribe_pool_flash_events(
2558 &mut self,
2559 instrument_id: InstrumentId,
2560 client_id: Option<ClientId>,
2561 params: Option<Params>,
2562 ) where
2563 Self: DataActorNative,
2564 Self: 'static + Debug + Sized,
2565 {
2566 DataActorCore::unsubscribe_pool_flash_events(
2567 self.core_mut(),
2568 instrument_id,
2569 client_id,
2570 params,
2571 );
2572 }
2573
2574 fn request_data(
2580 &mut self,
2581 data_type: DataType,
2582 client_id: ClientId,
2583 start: Option<Timestamp>,
2584 end: Option<Timestamp>,
2585 limit: Option<NonZeroUsize>,
2586 params: Option<Params>,
2587 ) -> anyhow::Result<UUID4>
2588 where
2589 Self: DataActorNative,
2590 Self: 'static + Debug + Sized,
2591 {
2592 let actor_id = self.core().actor_id().inner();
2593 let handler = ShareableMessageHandler::from_typed(move |resp: &CustomDataResponse| {
2594 if let Some(mut actor) = try_get_actor_unchecked::<Self>(&actor_id) {
2595 actor.handle_data_response(resp);
2596 } else {
2597 log::error!("Actor {actor_id} not found for data response handling");
2598 }
2599 });
2600
2601 DataActorCore::request_data(
2602 self.core_mut(),
2603 data_type,
2604 client_id,
2605 start,
2606 end,
2607 limit,
2608 params,
2609 handler,
2610 )
2611 }
2612
2613 fn request_instrument(
2619 &mut self,
2620 instrument_id: InstrumentId,
2621 start: Option<Timestamp>,
2622 end: Option<Timestamp>,
2623 client_id: Option<ClientId>,
2624 params: Option<Params>,
2625 ) -> anyhow::Result<UUID4>
2626 where
2627 Self: DataActorNative,
2628 Self: 'static + Debug + Sized,
2629 {
2630 let actor_id = self.core().actor_id().inner();
2631 let handler = ShareableMessageHandler::from_typed(move |resp: &InstrumentResponse| {
2632 if let Some(mut actor) = try_get_actor_unchecked::<Self>(&actor_id) {
2633 actor.handle_instrument_response(resp);
2634 } else {
2635 log::error!("Actor {actor_id} not found for instrument response handling");
2636 }
2637 });
2638
2639 DataActorCore::request_instrument(
2640 self.core_mut(),
2641 instrument_id,
2642 start,
2643 end,
2644 client_id,
2645 params,
2646 handler,
2647 )
2648 }
2649
2650 fn request_instruments(
2656 &mut self,
2657 venue: Option<Venue>,
2658 start: Option<Timestamp>,
2659 end: Option<Timestamp>,
2660 client_id: Option<ClientId>,
2661 params: Option<Params>,
2662 ) -> anyhow::Result<UUID4>
2663 where
2664 Self: DataActorNative,
2665 Self: 'static + Debug + Sized,
2666 {
2667 let actor_id = self.core().actor_id().inner();
2668 let handler = ShareableMessageHandler::from_typed(move |resp: &InstrumentsResponse| {
2669 if let Some(mut actor) = try_get_actor_unchecked::<Self>(&actor_id) {
2670 actor.handle_instruments_response(resp);
2671 } else {
2672 log::error!("Actor {actor_id} not found for instruments response handling");
2673 }
2674 });
2675
2676 DataActorCore::request_instruments(
2677 self.core_mut(),
2678 venue,
2679 start,
2680 end,
2681 client_id,
2682 params,
2683 handler,
2684 )
2685 }
2686
2687 fn request_book_snapshot(
2693 &mut self,
2694 instrument_id: InstrumentId,
2695 depth: Option<NonZeroUsize>,
2696 client_id: Option<ClientId>,
2697 params: Option<Params>,
2698 ) -> anyhow::Result<UUID4>
2699 where
2700 Self: DataActorNative,
2701 Self: 'static + Debug + Sized,
2702 {
2703 let actor_id = self.core().actor_id().inner();
2704 let handler = ShareableMessageHandler::from_typed(move |resp: &BookResponse| {
2705 if let Some(mut actor) = try_get_actor_unchecked::<Self>(&actor_id) {
2706 actor.handle_book_response(resp);
2707 } else {
2708 log::error!("Actor {actor_id} not found for book response handling");
2709 }
2710 });
2711
2712 DataActorCore::request_book_snapshot(
2713 self.core_mut(),
2714 instrument_id,
2715 depth,
2716 client_id,
2717 params,
2718 handler,
2719 )
2720 }
2721
2722 fn request_book_deltas(
2728 &mut self,
2729 instrument_id: InstrumentId,
2730 start: Option<Timestamp>,
2731 end: Option<Timestamp>,
2732 limit: Option<NonZeroUsize>,
2733 client_id: Option<ClientId>,
2734 params: Option<Params>,
2735 ) -> anyhow::Result<UUID4>
2736 where
2737 Self: DataActorNative,
2738 Self: 'static + Debug + Sized,
2739 {
2740 let actor_id = self.core().actor_id().inner();
2741 let handler = ShareableMessageHandler::from_typed(move |resp: &BookDeltasResponse| {
2742 if let Some(mut actor) = try_get_actor_unchecked::<Self>(&actor_id) {
2743 actor.handle_book_deltas_response(resp);
2744 } else {
2745 log::error!("Actor {actor_id} not found for book deltas response handling");
2746 }
2747 });
2748
2749 DataActorCore::request_book_deltas(
2750 self.core_mut(),
2751 instrument_id,
2752 start,
2753 end,
2754 limit,
2755 client_id,
2756 params,
2757 handler,
2758 )
2759 }
2760
2761 #[expect(clippy::too_many_arguments)]
2767 fn request_book_depth(
2768 &mut self,
2769 instrument_id: InstrumentId,
2770 start: Option<Timestamp>,
2771 end: Option<Timestamp>,
2772 limit: Option<NonZeroUsize>,
2773 depth: Option<NonZeroUsize>,
2774 client_id: Option<ClientId>,
2775 params: Option<Params>,
2776 ) -> anyhow::Result<UUID4>
2777 where
2778 Self: DataActorNative,
2779 Self: 'static + Debug + Sized,
2780 {
2781 let actor_id = self.core().actor_id().inner();
2782 let handler = ShareableMessageHandler::from_typed(move |resp: &BookDepthResponse| {
2783 if let Some(mut actor) = try_get_actor_unchecked::<Self>(&actor_id) {
2784 actor.handle_book_depth_response(resp);
2785 } else {
2786 log::error!("Actor {actor_id} not found for book depth response handling");
2787 }
2788 });
2789
2790 DataActorCore::request_book_depth(
2791 self.core_mut(),
2792 instrument_id,
2793 start,
2794 end,
2795 limit,
2796 depth,
2797 client_id,
2798 params,
2799 handler,
2800 )
2801 }
2802
2803 fn request_quotes(
2809 &mut self,
2810 instrument_id: InstrumentId,
2811 start: Option<Timestamp>,
2812 end: Option<Timestamp>,
2813 limit: Option<NonZeroUsize>,
2814 client_id: Option<ClientId>,
2815 params: Option<Params>,
2816 ) -> anyhow::Result<UUID4>
2817 where
2818 Self: DataActorNative,
2819 Self: 'static + Debug + Sized,
2820 {
2821 let actor_id = self.core().actor_id().inner();
2822 let handler = ShareableMessageHandler::from_typed(move |resp: &QuotesResponse| {
2823 if let Some(mut actor) = try_get_actor_unchecked::<Self>(&actor_id) {
2824 actor.handle_quotes_response(resp);
2825 } else {
2826 log::error!("Actor {actor_id} not found for quotes response handling");
2827 }
2828 });
2829
2830 DataActorCore::request_quotes(
2831 self.core_mut(),
2832 instrument_id,
2833 start,
2834 end,
2835 limit,
2836 client_id,
2837 params,
2838 handler,
2839 )
2840 }
2841
2842 fn request_trades(
2848 &mut self,
2849 instrument_id: InstrumentId,
2850 start: Option<Timestamp>,
2851 end: Option<Timestamp>,
2852 limit: Option<NonZeroUsize>,
2853 client_id: Option<ClientId>,
2854 params: Option<Params>,
2855 ) -> anyhow::Result<UUID4>
2856 where
2857 Self: DataActorNative,
2858 Self: 'static + Debug + Sized,
2859 {
2860 let actor_id = self.core().actor_id().inner();
2861 let handler = ShareableMessageHandler::from_typed(move |resp: &TradesResponse| {
2862 if let Some(mut actor) = try_get_actor_unchecked::<Self>(&actor_id) {
2863 actor.handle_trades_response(resp);
2864 } else {
2865 log::error!("Actor {actor_id} not found for trades response handling");
2866 }
2867 });
2868
2869 DataActorCore::request_trades(
2870 self.core_mut(),
2871 instrument_id,
2872 start,
2873 end,
2874 limit,
2875 client_id,
2876 params,
2877 handler,
2878 )
2879 }
2880
2881 fn request_bars(
2887 &mut self,
2888 bar_type: BarType,
2889 start: Option<Timestamp>,
2890 end: Option<Timestamp>,
2891 limit: Option<NonZeroUsize>,
2892 client_id: Option<ClientId>,
2893 params: Option<Params>,
2894 ) -> anyhow::Result<UUID4>
2895 where
2896 Self: DataActorNative,
2897 Self: 'static + Debug + Sized,
2898 {
2899 let actor_id = self.core().actor_id().inner();
2900 let handler = ShareableMessageHandler::from_typed(move |resp: &BarsResponse| {
2901 if let Some(mut actor) = try_get_actor_unchecked::<Self>(&actor_id) {
2902 actor.handle_bars_response(resp);
2903 } else {
2904 log::error!("Actor {actor_id} not found for bars response handling");
2905 }
2906 });
2907
2908 DataActorCore::request_bars(
2909 self.core_mut(),
2910 bar_type,
2911 start,
2912 end,
2913 limit,
2914 client_id,
2915 params,
2916 handler,
2917 )
2918 }
2919
2920 fn request_funding_rates(
2926 &mut self,
2927 instrument_id: InstrumentId,
2928 start: Option<Timestamp>,
2929 end: Option<Timestamp>,
2930 limit: Option<NonZeroUsize>,
2931 client_id: Option<ClientId>,
2932 params: Option<Params>,
2933 ) -> anyhow::Result<UUID4>
2934 where
2935 Self: DataActorNative,
2936 Self: 'static + Debug + Sized,
2937 {
2938 let actor_id = self.core().actor_id().inner();
2939 let handler = ShareableMessageHandler::from_typed(move |resp: &FundingRatesResponse| {
2940 if let Some(mut actor) = try_get_actor_unchecked::<Self>(&actor_id) {
2941 actor.handle_funding_rates_response(resp);
2942 } else {
2943 log::error!("Actor {actor_id} not found for funding rates response handling");
2944 }
2945 });
2946
2947 DataActorCore::request_funding_rates(
2948 self.core_mut(),
2949 instrument_id,
2950 start,
2951 end,
2952 limit,
2953 client_id,
2954 params,
2955 handler,
2956 )
2957 }
2958
2959 #[cfg(feature = "live")]
2970 fn reconnect_socket(&self, client_id: ClientId, endpoint: &str) -> anyhow::Result<()>
2971 where
2972 Self: DataActorNative,
2973 {
2974 DataActorCore::reconnect_socket(self.core(), client_id, endpoint)
2975 }
2976}
2977
2978impl<T> Actor for T
2980where
2981 T: DataActor + DataActorNative + Debug + 'static,
2982{
2983 fn id(&self) -> Ustr {
2984 self.core().actor_id.inner()
2985 }
2986
2987 #[allow(unused_variables)]
2988 fn handle(&mut self, msg: &dyn Any) {
2989 }
2991
2992 fn as_any(&self) -> &dyn Any {
2993 self
2994 }
2995}
2996
2997impl<T> Component for T
2998where
2999 T: DataActor + DataActorNative + Debug + 'static,
3000{
3001 fn component_id(&self) -> ComponentId {
3002 ComponentId::from(self.core().actor_id)
3003 }
3004
3005 fn release_subscriptions(&mut self) {
3006 self.core_mut().unsubscribe_all();
3007 }
3008
3009 fn state(&self) -> ComponentState {
3010 self.core().state
3011 }
3012
3013 fn transition_state(&mut self, trigger: ComponentTrigger) -> anyhow::Result<()> {
3014 let core = self.core_mut();
3015 core.state = core.state.transition(&trigger)?;
3016
3017 #[cfg(feature = "python")]
3018 if core.state == ComponentState::Disposed {
3019 core.message_bus.invalidate();
3020 }
3021
3022 log::info!(
3023 component = core.actor_id.inner().as_str();
3024 "{}",
3025 core.state.variant_name()
3026 );
3027 Ok(())
3028 }
3029
3030 fn register(
3031 &mut self,
3032 trader_id: TraderId,
3033 clock: Rc<RefCell<dyn Clock>>,
3034 cache: Rc<RefCell<Cache>>,
3035 ) -> anyhow::Result<()> {
3036 DataActorCore::register(self.core_mut(), trader_id, clock.clone(), cache)?;
3037
3038 let actor_id = self.core().actor_id().inner();
3040 let callback = TimeEventCallback::from(move |event: TimeEvent| {
3041 if let Some(mut actor) = try_get_actor_unchecked::<Self>(&actor_id) {
3042 actor.handle_time_event(&event);
3043 } else {
3044 log::error!("Actor {actor_id} not found for time event handling");
3045 }
3046 });
3047
3048 clock.borrow_mut().register_default_handler(callback);
3049
3050 self.initialize()
3051 }
3052
3053 fn on_start(&mut self) -> anyhow::Result<()> {
3054 DataActor::on_start(self)
3055 }
3056
3057 fn on_stop(&mut self) -> anyhow::Result<()> {
3058 DataActor::on_stop(self)
3059 }
3060
3061 fn on_resume(&mut self) -> anyhow::Result<()> {
3062 DataActor::on_resume(self)
3063 }
3064
3065 fn on_degrade(&mut self) -> anyhow::Result<()> {
3066 DataActor::on_degrade(self)
3067 }
3068
3069 fn on_fault(&mut self) -> anyhow::Result<()> {
3070 DataActor::on_fault(self)
3071 }
3072
3073 fn on_reset(&mut self) -> anyhow::Result<()> {
3074 DataActor::on_reset(self)
3075 }
3076
3077 fn on_dispose(&mut self) -> anyhow::Result<()> {
3078 DataActor::on_dispose(self)
3079 }
3080}
3081
3082#[derive(Clone)]
3084#[allow(
3085 dead_code,
3086 reason = "TODO: Under development (pending_requests, signal_classes)"
3087)]
3088pub struct DataActorCore {
3089 pub actor_id: ActorId,
3091 pub config: DataActorConfig,
3093 trader_id: Option<TraderId>,
3094 clock: Option<Rc<RefCell<dyn Clock>>>, cache: Option<Rc<RefCell<Cache>>>, state: ComponentState,
3097 topic_handlers: AHashMap<MStr<Pattern>, Subscription<ShareableMessageHandler>>,
3098 instrument_handlers: AHashMap<MStr<Pattern>, Subscription<TypedHandler<InstrumentAny>>>,
3099 deltas_handlers: AHashMap<MStr<Pattern>, Subscription<TypedHandler<OrderBookDeltas>>>,
3100 depth10_handlers: AHashMap<MStr<Pattern>, Subscription<TypedHandler<OrderBookDepth10>>>,
3101 book_handlers: AHashMap<MStr<Topic>, Subscription<TypedHandler<OrderBook>>>,
3102 quote_handlers: AHashMap<MStr<Topic>, Subscription<TypedHandler<QuoteTick>>>,
3103 trade_handlers: AHashMap<MStr<Topic>, Subscription<TypedHandler<TradeTick>>>,
3104 bar_handlers: AHashMap<MStr<Topic>, Subscription<TypedHandler<Bar>>>,
3105 mark_price_handlers: AHashMap<MStr<Topic>, Subscription<TypedHandler<MarkPriceUpdate>>>,
3106 index_price_handlers: AHashMap<MStr<Topic>, Subscription<TypedHandler<IndexPriceUpdate>>>,
3107 funding_rate_handlers: AHashMap<MStr<Topic>, Subscription<TypedHandler<FundingRateUpdate>>>,
3108 option_greeks_handlers: AHashMap<MStr<Topic>, Subscription<TypedHandler<OptionGreeks>>>,
3109 option_chain_handlers: AHashMap<MStr<Topic>, Subscription<TypedHandler<OptionChainSlice>>>,
3110 indicators: Indicators,
3111 warning_events: AHashSet<String>, pending_requests: AHashMap<UUID4, Option<RequestCallback>>,
3113 signal_classes: AHashMap<String, String>,
3114 #[cfg(feature = "defi")]
3115 block_handlers: AHashMap<MStr<Topic>, Subscription<TypedHandler<Block>>>,
3116 #[cfg(feature = "defi")]
3117 pool_handlers: AHashMap<MStr<Topic>, Subscription<TypedHandler<Pool>>>,
3118 #[cfg(feature = "defi")]
3119 pool_swap_handlers: AHashMap<MStr<Topic>, Subscription<TypedHandler<PoolSwap>>>,
3120 #[cfg(feature = "defi")]
3121 pool_liquidity_handlers: AHashMap<MStr<Topic>, Subscription<TypedHandler<PoolLiquidityUpdate>>>,
3122 #[cfg(feature = "defi")]
3123 pool_collect_handlers: AHashMap<MStr<Topic>, Subscription<TypedHandler<PoolFeeCollect>>>,
3124 #[cfg(feature = "defi")]
3125 pool_flash_handlers: AHashMap<MStr<Topic>, Subscription<TypedHandler<PoolFlash>>>,
3126 #[cfg(feature = "python")]
3127 message_bus: Rc<PyMessageBusScope>,
3128}
3129
3130#[derive(Clone)]
3131struct Subscription<T> {
3132 handler: T,
3133 command: Option<DataCommand>,
3134}
3135
3136impl Debug for DataActorCore {
3137 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3138 f.debug_struct(stringify!(DataActorCore))
3139 .field("actor_id", &self.actor_id)
3140 .field("config", &self.config)
3141 .field("state", &self.state)
3142 .field("trader_id", &self.trader_id)
3143 .finish()
3144 }
3145}
3146
3147impl DataActorCore {
3148 pub(crate) fn add_subscription_any(
3151 &mut self,
3152 topic: impl Into<MStr<Pattern>>,
3153 handler: ShareableMessageHandler,
3154 priority: Option<u32>,
3155 command: Option<DataCommand>,
3156 ) -> bool {
3157 let pattern: MStr<Pattern> = topic.into();
3158 if let Some(subscription) = self.topic_handlers.get_mut(&pattern) {
3159 if subscription.command.is_none() && command.is_some() {
3160 subscription.command = command;
3161 return true;
3162 }
3163
3164 log::warn!(
3165 "Actor {} attempted duplicate subscription to topic '{pattern}'",
3166 self.actor_id,
3167 );
3168 return false;
3169 }
3170
3171 self.topic_handlers.insert(
3172 pattern,
3173 Subscription {
3174 handler: handler.clone(),
3175 command,
3176 },
3177 );
3178 msgbus::subscribe_any(pattern, handler, priority);
3179 true
3180 }
3181
3182 pub(crate) fn remove_subscription_any(
3186 &mut self,
3187 topic: impl Into<MStr<Pattern>>,
3188 ) -> Option<DataCommand> {
3189 let pattern: MStr<Pattern> = topic.into();
3190 if let Some(subscription) = self.topic_handlers.remove(&pattern) {
3191 msgbus::unsubscribe_any(pattern, &subscription.handler);
3192 subscription.command
3193 } else {
3194 log::warn!(
3195 "Actor {} attempted to unsubscribe from topic '{pattern}' when not subscribed",
3196 self.actor_id,
3197 );
3198 None
3199 }
3200 }
3201
3202 pub(crate) fn add_quote_subscription(
3203 &mut self,
3204 topic: MStr<Topic>,
3205 handler: TypedHandler<QuoteTick>,
3206 command: DataCommand,
3207 ) -> bool {
3208 if self.quote_handlers.contains_key(&topic) {
3209 log::warn!(
3210 "Actor {} attempted duplicate quote subscription to '{topic}'",
3211 self.actor_id
3212 );
3213 return false;
3214 }
3215 self.quote_handlers.insert(
3216 topic,
3217 Subscription {
3218 handler: handler.clone(),
3219 command: Some(command),
3220 },
3221 );
3222 msgbus::subscribe_quotes(topic.into(), handler, None);
3223 true
3224 }
3225
3226 #[allow(dead_code)]
3227 pub(crate) fn remove_quote_subscription(&mut self, topic: MStr<Topic>) -> Option<DataCommand> {
3228 let subscription = self.quote_handlers.remove(&topic)?;
3229 msgbus::unsubscribe_quotes(topic.into(), &subscription.handler);
3230 subscription.command
3231 }
3232
3233 pub(crate) fn add_trade_subscription(
3234 &mut self,
3235 topic: MStr<Topic>,
3236 handler: TypedHandler<TradeTick>,
3237 command: DataCommand,
3238 ) -> bool {
3239 if self.trade_handlers.contains_key(&topic) {
3240 log::warn!(
3241 "Actor {} attempted duplicate trade subscription to '{topic}'",
3242 self.actor_id
3243 );
3244 return false;
3245 }
3246 self.trade_handlers.insert(
3247 topic,
3248 Subscription {
3249 handler: handler.clone(),
3250 command: Some(command),
3251 },
3252 );
3253 msgbus::subscribe_trades(topic.into(), handler, None);
3254 true
3255 }
3256
3257 #[allow(dead_code)]
3258 pub(crate) fn remove_trade_subscription(&mut self, topic: MStr<Topic>) -> Option<DataCommand> {
3259 let subscription = self.trade_handlers.remove(&topic)?;
3260 msgbus::unsubscribe_trades(topic.into(), &subscription.handler);
3261 subscription.command
3262 }
3263
3264 pub(crate) fn add_bar_subscription(
3265 &mut self,
3266 topic: MStr<Topic>,
3267 handler: TypedHandler<Bar>,
3268 command: DataCommand,
3269 ) -> bool {
3270 if self.bar_handlers.contains_key(&topic) {
3271 log::warn!(
3272 "Actor {} attempted duplicate bar subscription to '{topic}'",
3273 self.actor_id
3274 );
3275 return false;
3276 }
3277 self.bar_handlers.insert(
3278 topic,
3279 Subscription {
3280 handler: handler.clone(),
3281 command: Some(command),
3282 },
3283 );
3284 msgbus::subscribe_bars(topic.into(), handler, None);
3285 true
3286 }
3287
3288 #[allow(dead_code)]
3289 pub(crate) fn remove_bar_subscription(&mut self, topic: MStr<Topic>) -> Option<DataCommand> {
3290 let subscription = self.bar_handlers.remove(&topic)?;
3291 msgbus::unsubscribe_bars(topic.into(), &subscription.handler);
3292 subscription.command
3293 }
3294
3295 pub(crate) fn add_deltas_subscription(
3296 &mut self,
3297 pattern: MStr<Pattern>,
3298 handler: TypedHandler<OrderBookDeltas>,
3299 command: DataCommand,
3300 ) -> bool {
3301 if self.deltas_handlers.contains_key(&pattern) {
3302 log::warn!(
3303 "Actor {} attempted duplicate deltas subscription to '{pattern}'",
3304 self.actor_id
3305 );
3306 return false;
3307 }
3308 self.deltas_handlers.insert(
3309 pattern,
3310 Subscription {
3311 handler: handler.clone(),
3312 command: Some(command),
3313 },
3314 );
3315 msgbus::subscribe_book_deltas(pattern, handler, None);
3316 true
3317 }
3318
3319 #[allow(dead_code)]
3320 pub(crate) fn remove_deltas_subscription(
3321 &mut self,
3322 pattern: MStr<Pattern>,
3323 ) -> Option<DataCommand> {
3324 let subscription = self.deltas_handlers.remove(&pattern)?;
3325 msgbus::unsubscribe_book_deltas(pattern, &subscription.handler);
3326 subscription.command
3327 }
3328
3329 pub(crate) fn add_depth10_subscription(
3330 &mut self,
3331 pattern: MStr<Pattern>,
3332 handler: TypedHandler<OrderBookDepth10>,
3333 command: DataCommand,
3334 ) -> bool {
3335 if self.depth10_handlers.contains_key(&pattern) {
3336 log::warn!(
3337 "Actor {} attempted duplicate depth10 subscription to '{pattern}'",
3338 self.actor_id
3339 );
3340 return false;
3341 }
3342 self.depth10_handlers.insert(
3343 pattern,
3344 Subscription {
3345 handler: handler.clone(),
3346 command: Some(command),
3347 },
3348 );
3349 msgbus::subscribe_book_depth10(pattern, handler, None);
3350 true
3351 }
3352
3353 pub(crate) fn remove_depth10_subscription(
3354 &mut self,
3355 pattern: MStr<Pattern>,
3356 ) -> Option<DataCommand> {
3357 let subscription = self.depth10_handlers.remove(&pattern)?;
3358 msgbus::unsubscribe_book_depth10(pattern, &subscription.handler);
3359 subscription.command
3360 }
3361
3362 pub(crate) fn add_instrument_subscription(
3363 &mut self,
3364 pattern: MStr<Pattern>,
3365 handler: TypedHandler<InstrumentAny>,
3366 command: DataCommand,
3367 ) -> bool {
3368 if self.instrument_handlers.contains_key(&pattern) {
3369 log::warn!(
3370 "Actor {} attempted duplicate instrument subscription to '{pattern}'",
3371 self.actor_id
3372 );
3373 return false;
3374 }
3375 self.instrument_handlers.insert(
3376 pattern,
3377 Subscription {
3378 handler: handler.clone(),
3379 command: Some(command),
3380 },
3381 );
3382 msgbus::subscribe_instruments(pattern, handler, None);
3383 true
3384 }
3385
3386 #[allow(dead_code)]
3387 pub(crate) fn remove_instrument_subscription(
3388 &mut self,
3389 pattern: MStr<Pattern>,
3390 ) -> Option<DataCommand> {
3391 let subscription = self.instrument_handlers.remove(&pattern)?;
3392 msgbus::unsubscribe_instruments(pattern, &subscription.handler);
3393 subscription.command
3394 }
3395
3396 pub(crate) fn add_instrument_close_subscription(
3397 &mut self,
3398 topic: MStr<Topic>,
3399 handler: ShareableMessageHandler,
3400 command: DataCommand,
3401 ) -> bool {
3402 let pattern: MStr<Pattern> = topic.into();
3403 if self.topic_handlers.contains_key(&pattern) {
3404 log::warn!(
3405 "Actor {} attempted duplicate instrument close subscription to '{topic}'",
3406 self.actor_id
3407 );
3408 return false;
3409 }
3410 self.topic_handlers.insert(
3411 pattern,
3412 Subscription {
3413 handler: handler.clone(),
3414 command: Some(command),
3415 },
3416 );
3417 msgbus::subscribe_any(pattern, handler, None);
3418 true
3419 }
3420
3421 #[allow(dead_code)]
3422 pub(crate) fn remove_instrument_close_subscription(
3423 &mut self,
3424 topic: MStr<Topic>,
3425 ) -> Option<DataCommand> {
3426 let pattern: MStr<Pattern> = topic.into();
3427 let subscription = self.topic_handlers.remove(&pattern)?;
3428 msgbus::unsubscribe_any(pattern, &subscription.handler);
3429 subscription.command
3430 }
3431
3432 pub(crate) fn add_book_snapshot_subscription(
3433 &mut self,
3434 topic: MStr<Topic>,
3435 handler: TypedHandler<OrderBook>,
3436 command: DataCommand,
3437 ) -> bool {
3438 if self.book_handlers.contains_key(&topic) {
3439 log::warn!(
3440 "Actor {} attempted duplicate book snapshot subscription to '{topic}'",
3441 self.actor_id
3442 );
3443 return false;
3444 }
3445 self.book_handlers.insert(
3446 topic,
3447 Subscription {
3448 handler: handler.clone(),
3449 command: Some(command),
3450 },
3451 );
3452 msgbus::subscribe_book_snapshots(topic.into(), handler, None);
3453 true
3454 }
3455
3456 #[allow(dead_code)]
3457 pub(crate) fn remove_book_snapshot_subscription(
3458 &mut self,
3459 topic: MStr<Topic>,
3460 ) -> Option<DataCommand> {
3461 let subscription = self.book_handlers.remove(&topic)?;
3462 msgbus::unsubscribe_book_snapshots(topic.into(), &subscription.handler);
3463 subscription.command
3464 }
3465
3466 pub(crate) fn add_mark_price_subscription(
3467 &mut self,
3468 topic: MStr<Topic>,
3469 handler: TypedHandler<MarkPriceUpdate>,
3470 command: DataCommand,
3471 ) -> bool {
3472 if self.mark_price_handlers.contains_key(&topic) {
3473 log::warn!(
3474 "Actor {} attempted duplicate mark price subscription to '{topic}'",
3475 self.actor_id
3476 );
3477 return false;
3478 }
3479 self.mark_price_handlers.insert(
3480 topic,
3481 Subscription {
3482 handler: handler.clone(),
3483 command: Some(command),
3484 },
3485 );
3486 msgbus::subscribe_mark_prices(topic.into(), handler, None);
3487 true
3488 }
3489
3490 #[allow(dead_code)]
3491 pub(crate) fn remove_mark_price_subscription(
3492 &mut self,
3493 topic: MStr<Topic>,
3494 ) -> Option<DataCommand> {
3495 let subscription = self.mark_price_handlers.remove(&topic)?;
3496 msgbus::unsubscribe_mark_prices(topic.into(), &subscription.handler);
3497 subscription.command
3498 }
3499
3500 pub(crate) fn add_index_price_subscription(
3501 &mut self,
3502 topic: MStr<Topic>,
3503 handler: TypedHandler<IndexPriceUpdate>,
3504 command: DataCommand,
3505 ) -> bool {
3506 if self.index_price_handlers.contains_key(&topic) {
3507 log::warn!(
3508 "Actor {} attempted duplicate index price subscription to '{topic}'",
3509 self.actor_id
3510 );
3511 return false;
3512 }
3513 self.index_price_handlers.insert(
3514 topic,
3515 Subscription {
3516 handler: handler.clone(),
3517 command: Some(command),
3518 },
3519 );
3520 msgbus::subscribe_index_prices(topic.into(), handler, None);
3521 true
3522 }
3523
3524 #[allow(dead_code)]
3525 pub(crate) fn remove_index_price_subscription(
3526 &mut self,
3527 topic: MStr<Topic>,
3528 ) -> Option<DataCommand> {
3529 let subscription = self.index_price_handlers.remove(&topic)?;
3530 msgbus::unsubscribe_index_prices(topic.into(), &subscription.handler);
3531 subscription.command
3532 }
3533
3534 pub(crate) fn add_funding_rate_subscription(
3535 &mut self,
3536 topic: MStr<Topic>,
3537 handler: TypedHandler<FundingRateUpdate>,
3538 command: DataCommand,
3539 ) -> bool {
3540 if self.funding_rate_handlers.contains_key(&topic) {
3541 log::warn!(
3542 "Actor {} attempted duplicate funding rate subscription to '{topic}'",
3543 self.actor_id
3544 );
3545 return false;
3546 }
3547 self.funding_rate_handlers.insert(
3548 topic,
3549 Subscription {
3550 handler: handler.clone(),
3551 command: Some(command),
3552 },
3553 );
3554 msgbus::subscribe_funding_rates(topic.into(), handler, None);
3555 true
3556 }
3557
3558 #[allow(dead_code)]
3559 pub(crate) fn remove_funding_rate_subscription(
3560 &mut self,
3561 topic: MStr<Topic>,
3562 ) -> Option<DataCommand> {
3563 let subscription = self.funding_rate_handlers.remove(&topic)?;
3564 msgbus::unsubscribe_funding_rates(topic.into(), &subscription.handler);
3565 subscription.command
3566 }
3567
3568 pub(crate) fn add_option_greeks_subscription(
3569 &mut self,
3570 topic: MStr<Topic>,
3571 handler: TypedHandler<OptionGreeks>,
3572 command: DataCommand,
3573 ) -> bool {
3574 if self.option_greeks_handlers.contains_key(&topic) {
3575 log::warn!(
3576 "Actor {} attempted duplicate option greeks subscription to '{topic}'",
3577 self.actor_id
3578 );
3579 return false;
3580 }
3581 self.option_greeks_handlers.insert(
3582 topic,
3583 Subscription {
3584 handler: handler.clone(),
3585 command: Some(command),
3586 },
3587 );
3588 msgbus::subscribe_option_greeks(topic.into(), handler, None);
3589 true
3590 }
3591
3592 #[allow(dead_code)]
3593 pub(crate) fn remove_option_greeks_subscription(
3594 &mut self,
3595 topic: MStr<Topic>,
3596 ) -> Option<DataCommand> {
3597 let subscription = self.option_greeks_handlers.remove(&topic)?;
3598 msgbus::unsubscribe_option_greeks(topic.into(), &subscription.handler);
3599 subscription.command
3600 }
3601
3602 pub(crate) fn set_option_chain_subscription(
3603 &mut self,
3604 topic: MStr<Topic>,
3605 handler: TypedHandler<OptionChainSlice>,
3606 command: DataCommand,
3607 ) {
3608 if let Some(subscription) = self.option_chain_handlers.get_mut(&topic) {
3609 subscription.command = Some(command);
3610 return;
3611 }
3612
3613 self.option_chain_handlers.insert(
3614 topic,
3615 Subscription {
3616 handler: handler.clone(),
3617 command: Some(command),
3618 },
3619 );
3620 msgbus::subscribe_option_chain(topic.into(), handler, None);
3621 }
3622
3623 pub(crate) fn remove_option_chain_subscription(
3624 &mut self,
3625 topic: MStr<Topic>,
3626 ) -> Option<DataCommand> {
3627 let subscription = self.option_chain_handlers.remove(&topic)?;
3628 msgbus::unsubscribe_option_chain(topic.into(), &subscription.handler);
3629 subscription.command
3630 }
3631
3632 #[cfg(feature = "defi")]
3633 pub(crate) fn add_block_subscription(
3634 &mut self,
3635 topic: MStr<Topic>,
3636 handler: TypedHandler<Block>,
3637 command: DataCommand,
3638 ) -> bool {
3639 if self.block_handlers.contains_key(&topic) {
3640 log::warn!(
3641 "Actor {} attempted duplicate block subscription to '{topic}'",
3642 self.actor_id
3643 );
3644 return false;
3645 }
3646 self.block_handlers.insert(
3647 topic,
3648 Subscription {
3649 handler: handler.clone(),
3650 command: Some(command),
3651 },
3652 );
3653 msgbus::subscribe_defi_blocks(topic.into(), handler, None);
3654 true
3655 }
3656
3657 #[cfg(feature = "defi")]
3658 #[allow(dead_code)]
3659 pub(crate) fn remove_block_subscription(&mut self, topic: MStr<Topic>) -> Option<DataCommand> {
3660 let subscription = self.block_handlers.remove(&topic)?;
3661 msgbus::unsubscribe_defi_blocks(topic.into(), &subscription.handler);
3662 subscription.command
3663 }
3664
3665 #[cfg(feature = "defi")]
3666 pub(crate) fn add_pool_subscription(
3667 &mut self,
3668 topic: MStr<Topic>,
3669 handler: TypedHandler<Pool>,
3670 command: DataCommand,
3671 ) -> bool {
3672 if self.pool_handlers.contains_key(&topic) {
3673 log::warn!(
3674 "Actor {} attempted duplicate pool subscription to '{topic}'",
3675 self.actor_id
3676 );
3677 return false;
3678 }
3679 self.pool_handlers.insert(
3680 topic,
3681 Subscription {
3682 handler: handler.clone(),
3683 command: Some(command),
3684 },
3685 );
3686 msgbus::subscribe_defi_pools(topic.into(), handler, None);
3687 true
3688 }
3689
3690 #[cfg(feature = "defi")]
3691 #[allow(dead_code)]
3692 pub(crate) fn remove_pool_subscription(&mut self, topic: MStr<Topic>) -> Option<DataCommand> {
3693 let subscription = self.pool_handlers.remove(&topic)?;
3694 msgbus::unsubscribe_defi_pools(topic.into(), &subscription.handler);
3695 subscription.command
3696 }
3697
3698 #[cfg(feature = "defi")]
3699 pub(crate) fn add_pool_swap_subscription(
3700 &mut self,
3701 topic: MStr<Topic>,
3702 handler: TypedHandler<PoolSwap>,
3703 command: DataCommand,
3704 ) -> bool {
3705 if self.pool_swap_handlers.contains_key(&topic) {
3706 log::warn!(
3707 "Actor {} attempted duplicate pool swap subscription to '{topic}'",
3708 self.actor_id
3709 );
3710 return false;
3711 }
3712 self.pool_swap_handlers.insert(
3713 topic,
3714 Subscription {
3715 handler: handler.clone(),
3716 command: Some(command),
3717 },
3718 );
3719 msgbus::subscribe_defi_swaps(topic.into(), handler, None);
3720 true
3721 }
3722
3723 #[cfg(feature = "defi")]
3724 #[allow(dead_code)]
3725 pub(crate) fn remove_pool_swap_subscription(
3726 &mut self,
3727 topic: MStr<Topic>,
3728 ) -> Option<DataCommand> {
3729 let subscription = self.pool_swap_handlers.remove(&topic)?;
3730 msgbus::unsubscribe_defi_swaps(topic.into(), &subscription.handler);
3731 subscription.command
3732 }
3733
3734 #[cfg(feature = "defi")]
3735 pub(crate) fn add_pool_liquidity_subscription(
3736 &mut self,
3737 topic: MStr<Topic>,
3738 handler: TypedHandler<PoolLiquidityUpdate>,
3739 command: DataCommand,
3740 ) -> bool {
3741 if self.pool_liquidity_handlers.contains_key(&topic) {
3742 log::warn!(
3743 "Actor {} attempted duplicate pool liquidity subscription to '{topic}'",
3744 self.actor_id
3745 );
3746 return false;
3747 }
3748 self.pool_liquidity_handlers.insert(
3749 topic,
3750 Subscription {
3751 handler: handler.clone(),
3752 command: Some(command),
3753 },
3754 );
3755 msgbus::subscribe_defi_liquidity(topic.into(), handler, None);
3756 true
3757 }
3758
3759 #[cfg(feature = "defi")]
3760 #[allow(dead_code)]
3761 pub(crate) fn remove_pool_liquidity_subscription(
3762 &mut self,
3763 topic: MStr<Topic>,
3764 ) -> Option<DataCommand> {
3765 let subscription = self.pool_liquidity_handlers.remove(&topic)?;
3766 msgbus::unsubscribe_defi_liquidity(topic.into(), &subscription.handler);
3767 subscription.command
3768 }
3769
3770 #[cfg(feature = "defi")]
3771 pub(crate) fn add_pool_collect_subscription(
3772 &mut self,
3773 topic: MStr<Topic>,
3774 handler: TypedHandler<PoolFeeCollect>,
3775 command: DataCommand,
3776 ) -> bool {
3777 if self.pool_collect_handlers.contains_key(&topic) {
3778 log::warn!(
3779 "Actor {} attempted duplicate pool collect subscription to '{topic}'",
3780 self.actor_id
3781 );
3782 return false;
3783 }
3784 self.pool_collect_handlers.insert(
3785 topic,
3786 Subscription {
3787 handler: handler.clone(),
3788 command: Some(command),
3789 },
3790 );
3791 msgbus::subscribe_defi_collects(topic.into(), handler, None);
3792 true
3793 }
3794
3795 #[cfg(feature = "defi")]
3796 #[allow(dead_code)]
3797 pub(crate) fn remove_pool_collect_subscription(
3798 &mut self,
3799 topic: MStr<Topic>,
3800 ) -> Option<DataCommand> {
3801 let subscription = self.pool_collect_handlers.remove(&topic)?;
3802 msgbus::unsubscribe_defi_collects(topic.into(), &subscription.handler);
3803 subscription.command
3804 }
3805
3806 #[cfg(feature = "defi")]
3807 pub(crate) fn add_pool_flash_subscription(
3808 &mut self,
3809 topic: MStr<Topic>,
3810 handler: TypedHandler<PoolFlash>,
3811 command: DataCommand,
3812 ) -> bool {
3813 if self.pool_flash_handlers.contains_key(&topic) {
3814 log::warn!(
3815 "Actor {} attempted duplicate pool flash subscription to '{topic}'",
3816 self.actor_id
3817 );
3818 return false;
3819 }
3820 self.pool_flash_handlers.insert(
3821 topic,
3822 Subscription {
3823 handler: handler.clone(),
3824 command: Some(command),
3825 },
3826 );
3827 msgbus::subscribe_defi_flash(topic.into(), handler, None);
3828 true
3829 }
3830
3831 #[cfg(feature = "defi")]
3832 #[allow(dead_code)]
3833 pub(crate) fn remove_pool_flash_subscription(
3834 &mut self,
3835 topic: MStr<Topic>,
3836 ) -> Option<DataCommand> {
3837 let subscription = self.pool_flash_handlers.remove(&topic)?;
3838 msgbus::unsubscribe_defi_flash(topic.into(), &subscription.handler);
3839 subscription.command
3840 }
3841
3842 pub(crate) fn unsubscribe_all(&mut self) {
3847 let mut commands = Vec::new();
3848
3849 Self::drain_subscriptions(
3850 std::mem::take(&mut self.topic_handlers),
3851 &mut commands,
3852 msgbus::unsubscribe_any,
3853 );
3854 Self::drain_subscriptions(
3855 std::mem::take(&mut self.instrument_handlers),
3856 &mut commands,
3857 msgbus::unsubscribe_instruments,
3858 );
3859 Self::drain_subscriptions(
3860 std::mem::take(&mut self.deltas_handlers),
3861 &mut commands,
3862 msgbus::unsubscribe_book_deltas,
3863 );
3864 Self::drain_subscriptions(
3865 std::mem::take(&mut self.depth10_handlers),
3866 &mut commands,
3867 msgbus::unsubscribe_book_depth10,
3868 );
3869 Self::drain_subscriptions(
3870 std::mem::take(&mut self.book_handlers),
3871 &mut commands,
3872 |topic, handler| msgbus::unsubscribe_book_snapshots(topic.into(), handler),
3873 );
3874 Self::drain_subscriptions(
3875 std::mem::take(&mut self.quote_handlers),
3876 &mut commands,
3877 |topic, handler| msgbus::unsubscribe_quotes(topic.into(), handler),
3878 );
3879 Self::drain_subscriptions(
3880 std::mem::take(&mut self.trade_handlers),
3881 &mut commands,
3882 |topic, handler| msgbus::unsubscribe_trades(topic.into(), handler),
3883 );
3884 Self::drain_subscriptions(
3885 std::mem::take(&mut self.bar_handlers),
3886 &mut commands,
3887 |topic, handler| msgbus::unsubscribe_bars(topic.into(), handler),
3888 );
3889 Self::drain_subscriptions(
3890 std::mem::take(&mut self.mark_price_handlers),
3891 &mut commands,
3892 |topic, handler| msgbus::unsubscribe_mark_prices(topic.into(), handler),
3893 );
3894 Self::drain_subscriptions(
3895 std::mem::take(&mut self.index_price_handlers),
3896 &mut commands,
3897 |topic, handler| msgbus::unsubscribe_index_prices(topic.into(), handler),
3898 );
3899 Self::drain_subscriptions(
3900 std::mem::take(&mut self.funding_rate_handlers),
3901 &mut commands,
3902 |topic, handler| msgbus::unsubscribe_funding_rates(topic.into(), handler),
3903 );
3904 Self::drain_subscriptions(
3905 std::mem::take(&mut self.option_greeks_handlers),
3906 &mut commands,
3907 |topic, handler| msgbus::unsubscribe_option_greeks(topic.into(), handler),
3908 );
3909 Self::drain_subscriptions(
3910 std::mem::take(&mut self.option_chain_handlers),
3911 &mut commands,
3912 |topic, handler| msgbus::unsubscribe_option_chain(topic.into(), handler),
3913 );
3914
3915 #[cfg(feature = "defi")]
3916 self.unsubscribe_all_defi(&mut commands);
3917
3918 for command in commands {
3919 if let Some(command) = command.into_unsubscribe(UUID4::new(), self.timestamp_ns()) {
3920 self.send_data_cmd(command);
3921 }
3922 }
3923 #[cfg(feature = "python")]
3924 self.message_bus.clear();
3925 }
3926
3927 #[cfg(feature = "defi")]
3928 fn unsubscribe_all_defi(&mut self, commands: &mut Vec<DataCommand>) {
3929 Self::drain_subscriptions(
3930 std::mem::take(&mut self.block_handlers),
3931 commands,
3932 |topic, handler| msgbus::unsubscribe_defi_blocks(topic.into(), handler),
3933 );
3934 Self::drain_subscriptions(
3935 std::mem::take(&mut self.pool_handlers),
3936 commands,
3937 |topic, handler| msgbus::unsubscribe_defi_pools(topic.into(), handler),
3938 );
3939 Self::drain_subscriptions(
3940 std::mem::take(&mut self.pool_swap_handlers),
3941 commands,
3942 |topic, handler| msgbus::unsubscribe_defi_swaps(topic.into(), handler),
3943 );
3944 Self::drain_subscriptions(
3945 std::mem::take(&mut self.pool_liquidity_handlers),
3946 commands,
3947 |topic, handler| msgbus::unsubscribe_defi_liquidity(topic.into(), handler),
3948 );
3949 Self::drain_subscriptions(
3950 std::mem::take(&mut self.pool_collect_handlers),
3951 commands,
3952 |topic, handler| msgbus::unsubscribe_defi_collects(topic.into(), handler),
3953 );
3954 Self::drain_subscriptions(
3955 std::mem::take(&mut self.pool_flash_handlers),
3956 commands,
3957 |topic, handler| msgbus::unsubscribe_defi_flash(topic.into(), handler),
3958 );
3959 }
3960
3961 fn drain_subscriptions<K, T>(
3962 subscriptions: AHashMap<K, Subscription<T>>,
3963 commands: &mut Vec<DataCommand>,
3964 mut unsubscribe: impl FnMut(K, &T),
3965 ) where
3966 K: AsRef<str>,
3967 {
3968 let mut subscriptions = subscriptions.into_iter().collect::<Vec<_>>();
3969 subscriptions.sort_unstable_by(|(left, _), (right, _)| left.as_ref().cmp(right.as_ref()));
3970
3971 for (key, subscription) in subscriptions {
3972 unsubscribe(key, &subscription.handler);
3973 commands.extend(subscription.command);
3974 }
3975 }
3976
3977 pub fn new(config: DataActorConfig) -> Self {
3979 let actor_id = config.actor_id.unwrap_or_else(Self::default_actor_id);
3980
3981 Self {
3982 actor_id,
3983 config,
3984 trader_id: None, clock: None, cache: None, state: ComponentState::default(),
3988 topic_handlers: AHashMap::new(),
3989 instrument_handlers: AHashMap::new(),
3990 deltas_handlers: AHashMap::new(),
3991 depth10_handlers: AHashMap::new(),
3992 book_handlers: AHashMap::new(),
3993 quote_handlers: AHashMap::new(),
3994 trade_handlers: AHashMap::new(),
3995 bar_handlers: AHashMap::new(),
3996 mark_price_handlers: AHashMap::new(),
3997 index_price_handlers: AHashMap::new(),
3998 funding_rate_handlers: AHashMap::new(),
3999 option_greeks_handlers: AHashMap::new(),
4000 option_chain_handlers: AHashMap::new(),
4001 indicators: Indicators::default(),
4002 warning_events: AHashSet::new(),
4003 pending_requests: AHashMap::new(),
4004 signal_classes: AHashMap::new(),
4005 #[cfg(feature = "defi")]
4006 block_handlers: AHashMap::new(),
4007 #[cfg(feature = "defi")]
4008 pool_handlers: AHashMap::new(),
4009 #[cfg(feature = "defi")]
4010 pool_swap_handlers: AHashMap::new(),
4011 #[cfg(feature = "defi")]
4012 pool_liquidity_handlers: AHashMap::new(),
4013 #[cfg(feature = "defi")]
4014 pool_collect_handlers: AHashMap::new(),
4015 #[cfg(feature = "defi")]
4016 pool_flash_handlers: AHashMap::new(),
4017 #[cfg(feature = "python")]
4018 message_bus: Rc::default(),
4019 }
4020 }
4021
4022 #[must_use]
4024 pub fn registered_indicators(&self) -> Vec<SharedActorIndicator> {
4025 self.indicators.registered_indicators()
4026 }
4027
4028 pub fn indicators_initialized(&self) -> anyhow::Result<bool> {
4034 self.indicators.initialized()
4035 }
4036
4037 pub fn register_indicator_for_quote_ticks(
4039 &mut self,
4040 instrument_id: InstrumentId,
4041 indicator: SharedActorIndicator,
4042 ) {
4043 self.indicators
4044 .register_indicator_for_quote_ticks(instrument_id, indicator);
4045 }
4046
4047 pub fn register_indicator_for_trade_ticks(
4049 &mut self,
4050 instrument_id: InstrumentId,
4051 indicator: SharedActorIndicator,
4052 ) {
4053 self.indicators
4054 .register_indicator_for_trade_ticks(instrument_id, indicator);
4055 }
4056
4057 pub fn register_indicator_for_bars(
4059 &mut self,
4060 bar_type: BarType,
4061 indicator: SharedActorIndicator,
4062 ) {
4063 self.indicators
4064 .register_indicator_for_bars(bar_type, indicator);
4065 }
4066
4067 pub(crate) fn handle_indicators_for_quote(&self, quote: &QuoteTick) -> anyhow::Result<()> {
4068 self.indicators.handle_quote(quote)
4069 }
4070
4071 pub(crate) fn handle_indicators_for_quotes(&self, quotes: &[QuoteTick]) -> anyhow::Result<()> {
4072 self.indicators.handle_quotes(quotes)
4073 }
4074
4075 pub(crate) fn handle_indicators_for_trade(&self, trade: &TradeTick) -> anyhow::Result<()> {
4076 self.indicators.handle_trade(trade)
4077 }
4078
4079 pub(crate) fn handle_indicators_for_trades(&self, trades: &[TradeTick]) -> anyhow::Result<()> {
4080 self.indicators.handle_trades(trades)
4081 }
4082
4083 pub(crate) fn handle_indicators_for_bar(&self, bar: &Bar) -> anyhow::Result<()> {
4084 self.indicators.handle_bar(bar)
4085 }
4086
4087 pub(crate) fn handle_indicators_for_bars(&self, bars: &[Bar]) -> anyhow::Result<()> {
4088 self.indicators.handle_bars(bars)
4089 }
4090
4091 #[must_use]
4093 pub fn mem_address(&self) -> String {
4094 format!("{self:p}")
4095 }
4096
4097 pub fn state(&self) -> ComponentState {
4099 self.state
4100 }
4101
4102 pub fn trader_id(&self) -> Option<TraderId> {
4104 self.trader_id
4105 }
4106
4107 pub fn actor_id(&self) -> ActorId {
4109 self.actor_id
4110 }
4111
4112 fn default_actor_id() -> ActorId {
4113 ActorId::from(stringify!(DataActor))
4114 }
4115
4116 pub fn timestamp_ns(&self) -> UnixNanos {
4118 self.clock_ref().timestamp_ns()
4119 }
4120
4121 pub(super) fn clock_api(&self) -> ClockApi<'_> {
4122 let clock = self.clock.as_ref().unwrap_or_else(|| {
4123 panic!(
4124 "DataActor {} must be registered before calling `clock()` - trader_id: {:?}",
4125 self.actor_id, self.trader_id
4126 )
4127 });
4128 ClockApi::new(clock.as_ref())
4129 }
4130
4131 fn clock_ref(&self) -> Ref<'_, dyn Clock> {
4132 self.clock
4133 .as_ref()
4134 .unwrap_or_else(|| {
4135 panic!(
4136 "DataActor {} must be registered before calling `clock_ref()` - trader_id: {:?}",
4137 self.actor_id, self.trader_id
4138 )
4139 })
4140 .borrow()
4141 }
4142
4143 fn cache_api(&self) -> CacheApi<'_> {
4144 let cache = self.cache.as_ref().unwrap_or_else(|| {
4145 panic!(
4146 "DataActor {} must be registered before calling `cache()` - trader_id: {:?}",
4147 self.actor_id, self.trader_id
4148 )
4149 });
4150 CacheApi::new(cache.as_ref())
4151 }
4152
4153 pub fn register(
4160 &mut self,
4161 trader_id: TraderId,
4162 clock: Rc<RefCell<dyn Clock>>,
4163 cache: Rc<RefCell<Cache>>,
4164 ) -> anyhow::Result<()> {
4165 if let Some(existing_trader_id) = self.trader_id {
4166 anyhow::bail!(
4167 "DataActor {} already registered with trader {existing_trader_id}",
4168 self.actor_id
4169 );
4170 }
4171
4172 {
4174 let _timestamp = clock.borrow().timestamp_ns();
4175 }
4176
4177 {
4179 let _cache_borrow = cache.borrow();
4180 }
4181
4182 #[cfg(feature = "python")]
4183 self.message_bus.register();
4184
4185 self.trader_id = Some(trader_id);
4186 self.clock = Some(clock);
4187 self.cache = Some(cache);
4188
4189 if !self.is_properly_registered() {
4191 anyhow::bail!(
4192 "DataActor {} registration incomplete - validation failed",
4193 self.actor_id
4194 );
4195 }
4196
4197 log::debug!("Registered {} with trader {trader_id}", self.actor_id);
4198 Ok(())
4199 }
4200
4201 pub fn register_warning_event(&mut self, event_type: &str) {
4203 self.warning_events.insert(event_type.to_string());
4204 log::debug!("Registered event type '{event_type}' for warning logs");
4205 }
4206
4207 pub fn deregister_warning_event(&mut self, event_type: &str) {
4209 self.warning_events.remove(event_type);
4210 log::debug!("Deregistered event type '{event_type}' from warning logs");
4211 }
4212
4213 #[cfg(feature = "python")]
4215 pub fn message_bus(&self) -> Rc<PyMessageBusScope> {
4216 Rc::clone(&self.message_bus)
4217 }
4218
4219 pub fn is_registered(&self) -> bool {
4220 self.trader_id.is_some()
4221 }
4222
4223 pub(crate) fn check_registered(&self) {
4224 assert!(
4225 self.is_registered(),
4226 "Actor has not been registered with a Trader"
4227 );
4228 }
4229
4230 fn is_properly_registered(&self) -> bool {
4232 self.trader_id.is_some() && self.clock.is_some() && self.cache.is_some()
4233 }
4234
4235 pub(crate) fn send_data_cmd(&self, command: DataCommand) {
4236 if self.config.log_commands {
4237 log::info!("{CMD}{SEND} {command:?}");
4238 }
4239
4240 let endpoint = MessagingSwitchboard::data_engine_queue_execute();
4241 msgbus::send_data_command(endpoint, command);
4242 }
4243
4244 pub(crate) fn send_unsubscribe_cmd(
4245 &self,
4246 retained: Option<DataCommand>,
4247 fallback: DataCommand,
4248 ) {
4249 let Some(retained) = retained else {
4250 return;
4251 };
4252 let command = retained
4253 .into_unsubscribe(UUID4::new(), self.timestamp_ns())
4254 .unwrap_or(fallback);
4255 self.send_data_cmd(command);
4256 }
4257
4258 #[allow(dead_code)]
4259 fn send_data_req(&self, request: &RequestCommand) {
4260 if self.config.log_commands {
4261 log::info!("{REQ}{SEND} {request:?}");
4262 }
4263
4264 let endpoint = MessagingSwitchboard::data_engine_queue_execute();
4267 msgbus::send_any(endpoint, request.as_any());
4268 }
4269
4270 pub fn shutdown_system(&self, reason: Option<String>) {
4276 self.check_registered();
4277
4278 let command = ShutdownSystem::new(
4280 self.trader_id().unwrap(),
4281 self.actor_id.inner(),
4282 reason,
4283 UUID4::new(),
4284 self.timestamp_ns(),
4285 None, );
4287
4288 let topic = MessagingSwitchboard::shutdown_system_topic();
4289 msgbus::publish_any(topic, command.as_any());
4290 }
4291
4292 pub fn publish_data(&self, data_type: &DataType, data: &CustomData) {
4302 self.check_registered();
4303
4304 let topic = get_custom_topic(data_type);
4305 msgbus::publish_any(topic, data);
4306 }
4307
4308 pub fn publish_signal(&self, name: &str, value: String, ts_event: UnixNanos) {
4320 self.check_registered();
4321
4322 let now = self.timestamp_ns();
4323 let ts_event = if ts_event.as_u64() == 0 {
4324 now
4325 } else {
4326 ts_event
4327 };
4328 let signal = Signal::new(Ustr::from(name), value, ts_event, now);
4329
4330 let data_type = DataType::new(
4331 &format!(
4332 "Signal{}",
4333 nautilus_core::string::conversions::title_case(name)
4334 ),
4335 None,
4336 None,
4337 );
4338 let data = CustomData::new(Arc::new(signal), data_type);
4339 let topic = get_custom_topic(&data.data_type);
4340 msgbus::publish_any(topic, &data);
4341 }
4342
4343 pub fn add_synthetic(&self, synthetic: SyntheticInstrument) -> anyhow::Result<()> {
4351 self.check_registered();
4352
4353 let cache = self.cache_rc();
4354 if cache.borrow().synthetic(&synthetic.id).is_some() {
4355 anyhow::bail!("`synthetic` {} already exists", synthetic.id);
4356 }
4357 cache.borrow_mut().add_synthetic(synthetic)
4358 }
4359
4360 pub fn update_synthetic(&self, synthetic: SyntheticInstrument) -> anyhow::Result<()> {
4368 self.check_registered();
4369
4370 let cache = self.cache_rc();
4371 if cache.borrow().synthetic(&synthetic.id).is_none() {
4372 anyhow::bail!("`synthetic` {} does not exist", synthetic.id);
4373 }
4374 cache.borrow_mut().add_synthetic(synthetic)
4375 }
4376
4377 pub fn subscribe_data(
4383 &mut self,
4384 handler: ShareableMessageHandler,
4385 data_type: DataType,
4386 client_id: Option<ClientId>,
4387 params: Option<Params>,
4388 ) {
4389 assert!(
4390 self.is_properly_registered(),
4391 "DataActor {} is not properly registered - trader_id: {:?}, clock: {}, cache: {}",
4392 self.actor_id,
4393 self.trader_id,
4394 self.clock.is_some(),
4395 self.cache.is_some()
4396 );
4397
4398 let topic = get_custom_topic(&data_type);
4399
4400 if client_id.is_none() {
4402 self.add_subscription_any(topic, handler, None, None);
4403 return;
4404 }
4405
4406 let command = DataCommand::Subscribe(SubscribeCommand::Data(SubscribeCustomData {
4407 data_type,
4408 client_id,
4409 venue: None,
4410 command_id: UUID4::new(),
4411 ts_init: self.timestamp_ns(),
4412 correlation_id: None,
4413 params,
4414 }));
4415
4416 if self.add_subscription_any(topic, handler, None, Some(command.clone())) {
4417 self.send_data_cmd(command);
4418 }
4419 }
4420
4421 pub fn subscribe_signal(
4429 &mut self,
4430 handler: ShareableMessageHandler,
4431 name: &str,
4432 priority: Option<u32>,
4433 ) {
4434 self.check_registered();
4435
4436 let pattern = get_signal_pattern(name);
4437 if self.topic_handlers.contains_key(&pattern) {
4438 log::warn!(
4439 "Actor {} attempted duplicate signal subscription to '{pattern}'",
4440 self.actor_id,
4441 );
4442 return;
4443 }
4444 self.topic_handlers.insert(
4445 pattern,
4446 Subscription {
4447 handler: handler.clone(),
4448 command: None,
4449 },
4450 );
4451 msgbus::subscribe_any(pattern, handler, priority);
4452 }
4453
4454 pub fn subscribe_queue_state(
4460 &mut self,
4461 handler: ShareableMessageHandler,
4462 channel: Option<SystemChannel>,
4463 priority: Option<u32>,
4464 ) {
4465 self.check_registered();
4466
4467 let topic = MessagingSwitchboard::queue_state_changed_pattern(channel);
4468 self.add_subscription_any(topic, handler, priority, None);
4469 }
4470
4471 pub fn subscribe_socket_state(
4477 &mut self,
4478 handler: ShareableMessageHandler,
4479 client_id: Option<ClientId>,
4480 endpoint: Option<&str>,
4481 priority: Option<u32>,
4482 ) {
4483 self.check_registered();
4484
4485 let topic = MessagingSwitchboard::socket_state_changed_pattern(client_id, endpoint);
4486 self.add_subscription_any(topic, handler, priority, None);
4487 }
4488
4489 pub fn subscribe_quotes(
4491 &mut self,
4492 topic: MStr<Topic>,
4493 handler: TypedHandler<QuoteTick>,
4494 instrument_id: InstrumentId,
4495 client_id: Option<ClientId>,
4496 params: Option<Params>,
4497 ) {
4498 self.check_registered();
4499
4500 let command = DataCommand::Subscribe(SubscribeCommand::Quotes(SubscribeQuotes {
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 if self.add_quote_subscription(topic, handler, command.clone()) {
4511 self.send_data_cmd(command);
4512 }
4513 }
4514
4515 pub fn subscribe_instruments(
4517 &mut self,
4518 pattern: MStr<Pattern>,
4519 handler: TypedHandler<InstrumentAny>,
4520 venue: Venue,
4521 client_id: Option<ClientId>,
4522 params: Option<Params>,
4523 ) {
4524 self.check_registered();
4525
4526 let command = DataCommand::Subscribe(SubscribeCommand::Instruments(SubscribeInstruments {
4527 client_id,
4528 venue,
4529 command_id: UUID4::new(),
4530 ts_init: self.timestamp_ns(),
4531 correlation_id: None,
4532 params,
4533 }));
4534
4535 if self.add_instrument_subscription(pattern, handler, command.clone()) {
4536 self.send_data_cmd(command);
4537 }
4538 }
4539
4540 pub fn subscribe_instrument(
4542 &mut self,
4543 topic: MStr<Topic>,
4544 handler: TypedHandler<InstrumentAny>,
4545 instrument_id: InstrumentId,
4546 client_id: Option<ClientId>,
4547 params: Option<Params>,
4548 ) {
4549 self.check_registered();
4550
4551 let command = DataCommand::Subscribe(SubscribeCommand::Instrument(SubscribeInstrument {
4552 instrument_id,
4553 client_id,
4554 venue: Some(instrument_id.venue),
4555 command_id: UUID4::new(),
4556 ts_init: self.timestamp_ns(),
4557 correlation_id: None,
4558 params,
4559 }));
4560
4561 if self.add_instrument_subscription(topic.into(), handler, command.clone()) {
4562 self.send_data_cmd(command);
4563 }
4564 }
4565
4566 #[expect(clippy::too_many_arguments)]
4568 pub fn subscribe_book_deltas(
4569 &mut self,
4570 pattern: MStr<Pattern>,
4571 handler: TypedHandler<OrderBookDeltas>,
4572 instrument_id: InstrumentId,
4573 book_type: BookType,
4574 depth: Option<NonZeroUsize>,
4575 client_id: Option<ClientId>,
4576 managed: bool,
4577 params: Option<Params>,
4578 ) {
4579 self.check_registered();
4580
4581 let command = DataCommand::Subscribe(SubscribeCommand::BookDeltas(SubscribeBookDeltas {
4582 instrument_id,
4583 book_type,
4584 client_id,
4585 venue: Some(instrument_id.venue),
4586 command_id: UUID4::new(),
4587 ts_init: self.timestamp_ns(),
4588 depth,
4589 managed,
4590 correlation_id: None,
4591 params,
4592 }));
4593
4594 if self.add_deltas_subscription(pattern, handler, command.clone()) {
4595 self.send_data_cmd(command);
4596 }
4597 }
4598
4599 #[expect(clippy::too_many_arguments)]
4601 pub fn subscribe_book_depth10(
4602 &mut self,
4603 pattern: MStr<Pattern>,
4604 handler: TypedHandler<OrderBookDepth10>,
4605 instrument_id: InstrumentId,
4606 book_type: BookType,
4607 client_id: Option<ClientId>,
4608 managed: bool,
4609 params: Option<Params>,
4610 ) {
4611 self.check_registered();
4612
4613 let command = DataCommand::Subscribe(SubscribeCommand::BookDepth10(SubscribeBookDepth10 {
4614 instrument_id,
4615 book_type,
4616 client_id,
4617 venue: Some(instrument_id.venue),
4618 command_id: UUID4::new(),
4619 ts_init: self.timestamp_ns(),
4620 depth: NonZeroUsize::new(10),
4621 managed,
4622 correlation_id: None,
4623 params,
4624 }));
4625
4626 if self.add_depth10_subscription(pattern, handler, command.clone()) {
4627 self.send_data_cmd(command);
4628 }
4629 }
4630
4631 #[expect(clippy::too_many_arguments)]
4633 pub fn subscribe_book_at_interval(
4634 &mut self,
4635 topic: MStr<Topic>,
4636 handler: TypedHandler<OrderBook>,
4637 instrument_id: InstrumentId,
4638 book_type: BookType,
4639 depth: Option<NonZeroUsize>,
4640 interval_ms: NonZeroUsize,
4641 client_id: Option<ClientId>,
4642 params: Option<Params>,
4643 ) {
4644 self.check_registered();
4645
4646 let command =
4647 DataCommand::Subscribe(SubscribeCommand::BookSnapshots(SubscribeBookSnapshots {
4648 instrument_id,
4649 book_type,
4650 client_id,
4651 venue: Some(instrument_id.venue),
4652 command_id: UUID4::new(),
4653 ts_init: self.timestamp_ns(),
4654 depth,
4655 interval_ms,
4656 correlation_id: None,
4657 params,
4658 }));
4659
4660 if self.add_book_snapshot_subscription(topic, handler, command.clone()) {
4661 self.send_data_cmd(command);
4662 }
4663 }
4664
4665 pub fn subscribe_trades(
4667 &mut self,
4668 topic: MStr<Topic>,
4669 handler: TypedHandler<TradeTick>,
4670 instrument_id: InstrumentId,
4671 client_id: Option<ClientId>,
4672 params: Option<Params>,
4673 ) {
4674 self.check_registered();
4675
4676 let command = DataCommand::Subscribe(SubscribeCommand::Trades(SubscribeTrades {
4677 instrument_id,
4678 client_id,
4679 venue: Some(instrument_id.venue),
4680 command_id: UUID4::new(),
4681 ts_init: self.timestamp_ns(),
4682 correlation_id: None,
4683 params,
4684 }));
4685
4686 if self.add_trade_subscription(topic, handler, command.clone()) {
4687 self.send_data_cmd(command);
4688 }
4689 }
4690
4691 pub fn subscribe_bars(
4693 &mut self,
4694 topic: MStr<Topic>,
4695 handler: TypedHandler<Bar>,
4696 bar_type: BarType,
4697 client_id: Option<ClientId>,
4698 params: Option<Params>,
4699 ) {
4700 self.check_registered();
4701
4702 let command = DataCommand::Subscribe(SubscribeCommand::Bars(SubscribeBars {
4703 bar_type,
4704 client_id,
4705 venue: Some(bar_type.instrument_id().venue),
4706 command_id: UUID4::new(),
4707 ts_init: self.timestamp_ns(),
4708 correlation_id: None,
4709 params,
4710 }));
4711
4712 if self.add_bar_subscription(topic, handler, command.clone()) {
4713 self.send_data_cmd(command);
4714 }
4715 }
4716
4717 pub fn subscribe_mark_prices(
4719 &mut self,
4720 topic: MStr<Topic>,
4721 handler: TypedHandler<MarkPriceUpdate>,
4722 instrument_id: InstrumentId,
4723 client_id: Option<ClientId>,
4724 params: Option<Params>,
4725 ) {
4726 self.check_registered();
4727
4728 let command = DataCommand::Subscribe(SubscribeCommand::MarkPrices(SubscribeMarkPrices {
4729 instrument_id,
4730 client_id,
4731 venue: Some(instrument_id.venue),
4732 command_id: UUID4::new(),
4733 ts_init: self.timestamp_ns(),
4734 correlation_id: None,
4735 params,
4736 }));
4737
4738 if self.add_mark_price_subscription(topic, handler, command.clone()) {
4739 self.send_data_cmd(command);
4740 }
4741 }
4742
4743 pub fn subscribe_index_prices(
4745 &mut self,
4746 topic: MStr<Topic>,
4747 handler: TypedHandler<IndexPriceUpdate>,
4748 instrument_id: InstrumentId,
4749 client_id: Option<ClientId>,
4750 params: Option<Params>,
4751 ) {
4752 self.check_registered();
4753
4754 let command = DataCommand::Subscribe(SubscribeCommand::IndexPrices(SubscribeIndexPrices {
4755 instrument_id,
4756 client_id,
4757 venue: Some(instrument_id.venue),
4758 command_id: UUID4::new(),
4759 ts_init: self.timestamp_ns(),
4760 correlation_id: None,
4761 params,
4762 }));
4763
4764 if self.add_index_price_subscription(topic, handler, command.clone()) {
4765 self.send_data_cmd(command);
4766 }
4767 }
4768
4769 pub fn subscribe_funding_rates(
4771 &mut self,
4772 topic: MStr<Topic>,
4773 handler: TypedHandler<FundingRateUpdate>,
4774 instrument_id: InstrumentId,
4775 client_id: Option<ClientId>,
4776 params: Option<Params>,
4777 ) {
4778 self.check_registered();
4779
4780 let command =
4781 DataCommand::Subscribe(SubscribeCommand::FundingRates(SubscribeFundingRates {
4782 instrument_id,
4783 client_id,
4784 venue: Some(instrument_id.venue),
4785 command_id: UUID4::new(),
4786 ts_init: self.timestamp_ns(),
4787 correlation_id: None,
4788 params,
4789 }));
4790
4791 if self.add_funding_rate_subscription(topic, handler, command.clone()) {
4792 self.send_data_cmd(command);
4793 }
4794 }
4795
4796 pub fn subscribe_option_greeks(
4798 &mut self,
4799 topic: MStr<Topic>,
4800 handler: TypedHandler<OptionGreeks>,
4801 instrument_id: InstrumentId,
4802 client_id: Option<ClientId>,
4803 params: Option<Params>,
4804 ) {
4805 self.check_registered();
4806
4807 let command =
4808 DataCommand::Subscribe(SubscribeCommand::OptionGreeks(SubscribeOptionGreeks {
4809 instrument_id,
4810 client_id,
4811 venue: Some(instrument_id.venue),
4812 command_id: UUID4::new(),
4813 ts_init: self.timestamp_ns(),
4814 correlation_id: None,
4815 params,
4816 }));
4817
4818 if self.add_option_greeks_subscription(topic, handler, command.clone()) {
4819 self.send_data_cmd(command);
4820 }
4821 }
4822
4823 pub fn subscribe_instrument_status(
4825 &mut self,
4826 topic: MStr<Topic>,
4827 handler: ShareableMessageHandler,
4828 instrument_id: InstrumentId,
4829 client_id: Option<ClientId>,
4830 params: Option<Params>,
4831 ) {
4832 self.check_registered();
4833
4834 let command = DataCommand::Subscribe(SubscribeCommand::InstrumentStatus(
4835 SubscribeInstrumentStatus {
4836 instrument_id,
4837 client_id,
4838 venue: Some(instrument_id.venue),
4839 command_id: UUID4::new(),
4840 ts_init: self.timestamp_ns(),
4841 correlation_id: None,
4842 params,
4843 },
4844 ));
4845
4846 if self.add_subscription_any(topic, handler, None, Some(command.clone())) {
4847 self.send_data_cmd(command);
4848 }
4849 }
4850
4851 pub fn subscribe_instrument_close(
4853 &mut self,
4854 topic: MStr<Topic>,
4855 handler: ShareableMessageHandler,
4856 instrument_id: InstrumentId,
4857 client_id: Option<ClientId>,
4858 params: Option<Params>,
4859 ) {
4860 self.check_registered();
4861
4862 let command = DataCommand::Subscribe(SubscribeCommand::InstrumentClose(
4863 SubscribeInstrumentClose {
4864 instrument_id,
4865 client_id,
4866 venue: Some(instrument_id.venue),
4867 command_id: UUID4::new(),
4868 ts_init: self.timestamp_ns(),
4869 correlation_id: None,
4870 params,
4871 },
4872 ));
4873
4874 if self.add_instrument_close_subscription(topic, handler, command.clone()) {
4875 self.send_data_cmd(command);
4876 }
4877 }
4878
4879 #[expect(
4881 clippy::too_many_arguments,
4882 reason = "subscription command mirrors the option chain request fields"
4883 )]
4884 pub fn subscribe_option_chain(
4885 &mut self,
4886 topic: MStr<Topic>,
4887 handler: TypedHandler<OptionChainSlice>,
4888 series_id: OptionSeriesId,
4889 strike_range: StrikeRange,
4890 snapshot_interval_ms: Option<u64>,
4891 client_id: Option<ClientId>,
4892 params: Option<Params>,
4893 ) {
4894 self.check_registered();
4895
4896 let correlation_id = self
4897 .option_chain_handlers
4898 .get(&topic)
4899 .and_then(|subscription| match subscription.command.as_ref() {
4900 Some(DataCommand::Subscribe(SubscribeCommand::OptionChain(command))) => {
4901 Some(command.correlation_id.unwrap_or(command.command_id))
4902 }
4903 _ => None,
4904 });
4905
4906 let mut subscribe = SubscribeOptionChain::new(
4907 series_id,
4908 strike_range,
4909 snapshot_interval_ms,
4910 UUID4::new(),
4911 self.timestamp_ns(),
4912 client_id,
4913 Some(series_id.venue),
4914 params,
4915 );
4916 subscribe.correlation_id = correlation_id;
4917 let command = DataCommand::Subscribe(SubscribeCommand::OptionChain(subscribe));
4918
4919 self.set_option_chain_subscription(topic, handler, command.clone());
4920 self.send_data_cmd(command);
4921 }
4922
4923 pub fn unsubscribe_data(
4925 &mut self,
4926 data_type: DataType,
4927 client_id: Option<ClientId>,
4928 params: Option<Params>,
4929 ) {
4930 self.check_registered();
4931
4932 let topic = get_custom_topic(&data_type);
4933 let retained = self.remove_subscription_any(topic);
4934
4935 if client_id.is_none() && retained.is_none() {
4936 return;
4937 }
4938
4939 let command = UnsubscribeCommand::Data(UnsubscribeCustomData {
4940 data_type,
4941 client_id,
4942 venue: None,
4943 command_id: UUID4::new(),
4944 ts_init: self.timestamp_ns(),
4945 correlation_id: None,
4946 params,
4947 });
4948
4949 self.send_unsubscribe_cmd(retained, DataCommand::Unsubscribe(command));
4950 }
4951
4952 pub fn unsubscribe_signal(&mut self, name: &str) {
4958 self.check_registered();
4959
4960 let pattern = get_signal_pattern(name);
4961 if let Some(subscription) = self.topic_handlers.remove(&pattern) {
4962 msgbus::unsubscribe_any(pattern, &subscription.handler);
4963 } else {
4964 log::warn!(
4965 "Actor {} attempted to unsubscribe from signal pattern '{pattern}' when not subscribed",
4966 self.actor_id,
4967 );
4968 }
4969 }
4970
4971 pub fn unsubscribe_queue_state(&mut self, channel: Option<SystemChannel>) {
4977 self.check_registered();
4978
4979 let topic = MessagingSwitchboard::queue_state_changed_pattern(channel);
4980 let _ = self.remove_subscription_any(topic);
4981 }
4982
4983 pub fn unsubscribe_socket_state(
4989 &mut self,
4990 client_id: Option<ClientId>,
4991 endpoint: Option<&str>,
4992 ) {
4993 self.check_registered();
4994
4995 let topic = MessagingSwitchboard::socket_state_changed_pattern(client_id, endpoint);
4996 let _ = self.remove_subscription_any(topic);
4997 }
4998
4999 pub fn unsubscribe_instruments(
5001 &mut self,
5002 venue: Venue,
5003 client_id: Option<ClientId>,
5004 params: Option<Params>,
5005 ) {
5006 self.check_registered();
5007
5008 let pattern = get_instruments_pattern(venue);
5009 let retained = self.remove_instrument_subscription(pattern);
5010
5011 let command = UnsubscribeCommand::Instruments(UnsubscribeInstruments {
5012 client_id,
5013 venue,
5014 command_id: UUID4::new(),
5015 ts_init: self.timestamp_ns(),
5016 correlation_id: None,
5017 params,
5018 });
5019
5020 self.send_unsubscribe_cmd(retained, DataCommand::Unsubscribe(command));
5021 }
5022
5023 pub fn unsubscribe_instrument(
5025 &mut self,
5026 instrument_id: InstrumentId,
5027 client_id: Option<ClientId>,
5028 params: Option<Params>,
5029 ) {
5030 self.check_registered();
5031
5032 let topic = get_instrument_topic(instrument_id);
5033 let retained = self.remove_instrument_subscription(topic.into());
5034
5035 let command = UnsubscribeCommand::Instrument(UnsubscribeInstrument {
5036 instrument_id,
5037 client_id,
5038 venue: Some(instrument_id.venue),
5039 command_id: UUID4::new(),
5040 ts_init: self.timestamp_ns(),
5041 correlation_id: None,
5042 params,
5043 });
5044
5045 self.send_unsubscribe_cmd(retained, DataCommand::Unsubscribe(command));
5046 }
5047
5048 pub fn unsubscribe_book_deltas(
5050 &mut self,
5051 instrument_id: InstrumentId,
5052 client_id: Option<ClientId>,
5053 params: Option<Params>,
5054 ) {
5055 self.check_registered();
5056
5057 let pattern = if is_parent_subscription(params.as_ref()) {
5058 get_book_deltas_pattern(instrument_id)
5059 } else {
5060 get_book_deltas_topic(instrument_id).into()
5061 };
5062 let retained = self.remove_deltas_subscription(pattern);
5063
5064 let command = UnsubscribeCommand::BookDeltas(UnsubscribeBookDeltas {
5065 instrument_id,
5066 client_id,
5067 venue: Some(instrument_id.venue),
5068 command_id: UUID4::new(),
5069 ts_init: self.timestamp_ns(),
5070 correlation_id: None,
5071 params,
5072 });
5073
5074 self.send_unsubscribe_cmd(retained, DataCommand::Unsubscribe(command));
5075 }
5076
5077 pub fn unsubscribe_book_depth10(
5079 &mut self,
5080 instrument_id: InstrumentId,
5081 client_id: Option<ClientId>,
5082 params: Option<Params>,
5083 ) {
5084 self.check_registered();
5085
5086 let pattern = if is_parent_subscription(params.as_ref()) {
5087 get_book_depth10_pattern(instrument_id)
5088 } else {
5089 get_book_depth10_topic(instrument_id).into()
5090 };
5091 let retained = self.remove_depth10_subscription(pattern);
5092
5093 let command = UnsubscribeCommand::BookDepth10(UnsubscribeBookDepth10 {
5094 instrument_id,
5095 client_id,
5096 venue: Some(instrument_id.venue),
5097 command_id: UUID4::new(),
5098 ts_init: self.timestamp_ns(),
5099 correlation_id: None,
5100 params,
5101 });
5102
5103 self.send_unsubscribe_cmd(retained, DataCommand::Unsubscribe(command));
5104 }
5105
5106 pub fn unsubscribe_book_at_interval(
5108 &mut self,
5109 instrument_id: InstrumentId,
5110 interval_ms: NonZeroUsize,
5111 client_id: Option<ClientId>,
5112 params: Option<Params>,
5113 ) {
5114 self.check_registered();
5115
5116 let topic = get_book_snapshots_topic(instrument_id, interval_ms);
5117 let retained = self.remove_book_snapshot_subscription(topic);
5118
5119 let command = UnsubscribeCommand::BookSnapshots(UnsubscribeBookSnapshots {
5120 instrument_id,
5121 interval_ms,
5122 client_id,
5123 venue: Some(instrument_id.venue),
5124 command_id: UUID4::new(),
5125 ts_init: self.timestamp_ns(),
5126 correlation_id: None,
5127 params,
5128 });
5129
5130 self.send_unsubscribe_cmd(retained, DataCommand::Unsubscribe(command));
5131 }
5132
5133 pub fn unsubscribe_quotes(
5135 &mut self,
5136 instrument_id: InstrumentId,
5137 client_id: Option<ClientId>,
5138 params: Option<Params>,
5139 ) {
5140 self.check_registered();
5141
5142 let topic = get_quotes_topic(instrument_id);
5143 let retained = self.remove_quote_subscription(topic);
5144
5145 let command = UnsubscribeCommand::Quotes(UnsubscribeQuotes {
5146 instrument_id,
5147 client_id,
5148 venue: Some(instrument_id.venue),
5149 command_id: UUID4::new(),
5150 ts_init: self.timestamp_ns(),
5151 correlation_id: None,
5152 params,
5153 });
5154
5155 self.send_unsubscribe_cmd(retained, DataCommand::Unsubscribe(command));
5156 }
5157
5158 pub fn unsubscribe_trades(
5160 &mut self,
5161 instrument_id: InstrumentId,
5162 client_id: Option<ClientId>,
5163 params: Option<Params>,
5164 ) {
5165 self.check_registered();
5166
5167 let topic = get_trades_topic(instrument_id);
5168 let retained = self.remove_trade_subscription(topic);
5169
5170 let command = UnsubscribeCommand::Trades(UnsubscribeTrades {
5171 instrument_id,
5172 client_id,
5173 venue: Some(instrument_id.venue),
5174 command_id: UUID4::new(),
5175 ts_init: self.timestamp_ns(),
5176 correlation_id: None,
5177 params,
5178 });
5179
5180 self.send_unsubscribe_cmd(retained, DataCommand::Unsubscribe(command));
5181 }
5182
5183 pub fn unsubscribe_bars(
5185 &mut self,
5186 bar_type: BarType,
5187 client_id: Option<ClientId>,
5188 params: Option<Params>,
5189 ) {
5190 self.check_registered();
5191
5192 let topic = get_bars_topic(bar_type.standard());
5194 let retained = self.remove_bar_subscription(topic);
5195
5196 let command = UnsubscribeCommand::Bars(UnsubscribeBars {
5197 bar_type,
5198 client_id,
5199 venue: Some(bar_type.instrument_id().venue),
5200 command_id: UUID4::new(),
5201 ts_init: self.timestamp_ns(),
5202 correlation_id: None,
5203 params,
5204 });
5205
5206 self.send_unsubscribe_cmd(retained, DataCommand::Unsubscribe(command));
5207 }
5208
5209 pub fn unsubscribe_mark_prices(
5211 &mut self,
5212 instrument_id: InstrumentId,
5213 client_id: Option<ClientId>,
5214 params: Option<Params>,
5215 ) {
5216 self.check_registered();
5217
5218 let topic = get_mark_price_topic(instrument_id);
5219 let retained = self.remove_mark_price_subscription(topic);
5220
5221 let command = UnsubscribeCommand::MarkPrices(UnsubscribeMarkPrices {
5222 instrument_id,
5223 client_id,
5224 venue: Some(instrument_id.venue),
5225 command_id: UUID4::new(),
5226 ts_init: self.timestamp_ns(),
5227 correlation_id: None,
5228 params,
5229 });
5230
5231 self.send_unsubscribe_cmd(retained, DataCommand::Unsubscribe(command));
5232 }
5233
5234 pub fn unsubscribe_index_prices(
5236 &mut self,
5237 instrument_id: InstrumentId,
5238 client_id: Option<ClientId>,
5239 params: Option<Params>,
5240 ) {
5241 self.check_registered();
5242
5243 let topic = get_index_price_topic(instrument_id);
5244 let retained = self.remove_index_price_subscription(topic);
5245
5246 let command = UnsubscribeCommand::IndexPrices(UnsubscribeIndexPrices {
5247 instrument_id,
5248 client_id,
5249 venue: Some(instrument_id.venue),
5250 command_id: UUID4::new(),
5251 ts_init: self.timestamp_ns(),
5252 correlation_id: None,
5253 params,
5254 });
5255
5256 self.send_unsubscribe_cmd(retained, DataCommand::Unsubscribe(command));
5257 }
5258
5259 pub fn unsubscribe_funding_rates(
5261 &mut self,
5262 instrument_id: InstrumentId,
5263 client_id: Option<ClientId>,
5264 params: Option<Params>,
5265 ) {
5266 self.check_registered();
5267
5268 let topic = get_funding_rate_topic(instrument_id);
5269 let retained = self.remove_funding_rate_subscription(topic);
5270
5271 let command = UnsubscribeCommand::FundingRates(UnsubscribeFundingRates {
5272 instrument_id,
5273 client_id,
5274 venue: Some(instrument_id.venue),
5275 command_id: UUID4::new(),
5276 ts_init: self.timestamp_ns(),
5277 correlation_id: None,
5278 params,
5279 });
5280
5281 self.send_unsubscribe_cmd(retained, DataCommand::Unsubscribe(command));
5282 }
5283
5284 pub fn unsubscribe_option_greeks(
5286 &mut self,
5287 instrument_id: InstrumentId,
5288 client_id: Option<ClientId>,
5289 params: Option<Params>,
5290 ) {
5291 self.check_registered();
5292
5293 let topic = get_option_greeks_topic(instrument_id);
5294 let retained = self.remove_option_greeks_subscription(topic);
5295
5296 let command = UnsubscribeCommand::OptionGreeks(UnsubscribeOptionGreeks {
5297 instrument_id,
5298 client_id,
5299 venue: Some(instrument_id.venue),
5300 command_id: UUID4::new(),
5301 ts_init: self.timestamp_ns(),
5302 correlation_id: None,
5303 params,
5304 });
5305
5306 self.send_unsubscribe_cmd(retained, DataCommand::Unsubscribe(command));
5307 }
5308
5309 pub fn unsubscribe_instrument_status(
5311 &mut self,
5312 instrument_id: InstrumentId,
5313 client_id: Option<ClientId>,
5314 params: Option<Params>,
5315 ) {
5316 self.check_registered();
5317
5318 let topic = get_instrument_status_topic(instrument_id);
5319 let retained = self.remove_subscription_any(topic);
5320
5321 let command = UnsubscribeCommand::InstrumentStatus(UnsubscribeInstrumentStatus {
5322 instrument_id,
5323 client_id,
5324 venue: Some(instrument_id.venue),
5325 command_id: UUID4::new(),
5326 ts_init: self.timestamp_ns(),
5327 correlation_id: None,
5328 params,
5329 });
5330
5331 self.send_unsubscribe_cmd(retained, DataCommand::Unsubscribe(command));
5332 }
5333
5334 pub fn unsubscribe_instrument_close(
5336 &mut self,
5337 instrument_id: InstrumentId,
5338 client_id: Option<ClientId>,
5339 params: Option<Params>,
5340 ) {
5341 self.check_registered();
5342
5343 let topic = get_instrument_close_topic(instrument_id);
5344 let retained = self.remove_instrument_close_subscription(topic);
5345
5346 let command = UnsubscribeCommand::InstrumentClose(UnsubscribeInstrumentClose {
5347 instrument_id,
5348 client_id,
5349 venue: Some(instrument_id.venue),
5350 command_id: UUID4::new(),
5351 ts_init: self.timestamp_ns(),
5352 correlation_id: None,
5353 params,
5354 });
5355
5356 self.send_unsubscribe_cmd(retained, DataCommand::Unsubscribe(command));
5357 }
5358
5359 pub fn unsubscribe_option_chain(
5361 &mut self,
5362 series_id: OptionSeriesId,
5363 client_id: Option<ClientId>,
5364 ) {
5365 self.check_registered();
5366
5367 let topic = get_option_chain_topic(series_id);
5368 let retained = self.remove_option_chain_subscription(topic);
5369
5370 let command = UnsubscribeCommand::OptionChain(UnsubscribeOptionChain::new(
5371 series_id,
5372 UUID4::new(),
5373 self.timestamp_ns(),
5374 client_id,
5375 Some(series_id.venue),
5376 ));
5377
5378 self.send_unsubscribe_cmd(retained, DataCommand::Unsubscribe(command));
5379 }
5380
5381 #[expect(clippy::too_many_arguments)]
5387 pub fn request_data(
5388 &self,
5389 data_type: DataType,
5390 client_id: ClientId,
5391 start: Option<Timestamp>,
5392 end: Option<Timestamp>,
5393 limit: Option<NonZeroUsize>,
5394 params: Option<Params>,
5395 handler: ShareableMessageHandler,
5396 ) -> anyhow::Result<UUID4> {
5397 self.check_registered();
5398
5399 let now = self.clock_ref().utc_now();
5400 check_timestamps(now, start, end)?;
5401
5402 let request_id = UUID4::new();
5403 let command = RequestCommand::Data(RequestCustomData {
5404 client_id,
5405 data_type,
5406 start,
5407 end,
5408 limit,
5409 request_id,
5410 ts_init: self.timestamp_ns(),
5411 params,
5412 });
5413
5414 get_message_bus()
5415 .borrow_mut()
5416 .register_response_handler(command.request_id(), handler)?;
5417
5418 self.send_data_cmd(DataCommand::Request(command));
5419
5420 Ok(request_id)
5421 }
5422
5423 pub fn request_instrument(
5429 &self,
5430 instrument_id: InstrumentId,
5431 start: Option<Timestamp>,
5432 end: Option<Timestamp>,
5433 client_id: Option<ClientId>,
5434 params: Option<Params>,
5435 handler: ShareableMessageHandler,
5436 ) -> anyhow::Result<UUID4> {
5437 self.check_registered();
5438
5439 let now = self.clock_ref().utc_now();
5440 check_timestamps(now, start, end)?;
5441
5442 let request_id = UUID4::new();
5443 let command = RequestCommand::Instrument(RequestInstrument {
5444 instrument_id,
5445 start,
5446 end,
5447 client_id,
5448 request_id,
5449 ts_init: now.into(),
5450 params,
5451 });
5452
5453 get_message_bus()
5454 .borrow_mut()
5455 .register_response_handler(command.request_id(), handler)?;
5456
5457 self.send_data_cmd(DataCommand::Request(command));
5458
5459 Ok(request_id)
5460 }
5461
5462 pub fn request_instruments(
5468 &self,
5469 venue: Option<Venue>,
5470 start: Option<Timestamp>,
5471 end: Option<Timestamp>,
5472 client_id: Option<ClientId>,
5473 params: Option<Params>,
5474 handler: ShareableMessageHandler,
5475 ) -> anyhow::Result<UUID4> {
5476 self.check_registered();
5477
5478 let now = self.clock_ref().utc_now();
5479 check_timestamps(now, start, end)?;
5480
5481 let request_id = UUID4::new();
5482 let command = RequestCommand::Instruments(RequestInstruments {
5483 venue,
5484 start,
5485 end,
5486 client_id,
5487 request_id,
5488 ts_init: now.into(),
5489 params,
5490 });
5491
5492 get_message_bus()
5493 .borrow_mut()
5494 .register_response_handler(command.request_id(), handler)?;
5495
5496 self.send_data_cmd(DataCommand::Request(command));
5497
5498 Ok(request_id)
5499 }
5500
5501 pub fn request_book_snapshot(
5507 &self,
5508 instrument_id: InstrumentId,
5509 depth: Option<NonZeroUsize>,
5510 client_id: Option<ClientId>,
5511 params: Option<Params>,
5512 handler: ShareableMessageHandler,
5513 ) -> anyhow::Result<UUID4> {
5514 self.check_registered();
5515
5516 let request_id = UUID4::new();
5517 let command = RequestCommand::BookSnapshot(RequestBookSnapshot {
5518 instrument_id,
5519 depth,
5520 client_id,
5521 request_id,
5522 ts_init: self.timestamp_ns(),
5523 params,
5524 });
5525
5526 get_message_bus()
5527 .borrow_mut()
5528 .register_response_handler(command.request_id(), handler)?;
5529
5530 self.send_data_cmd(DataCommand::Request(command));
5531
5532 Ok(request_id)
5533 }
5534
5535 #[expect(clippy::too_many_arguments)]
5541 pub fn request_book_deltas(
5542 &self,
5543 instrument_id: InstrumentId,
5544 start: Option<Timestamp>,
5545 end: Option<Timestamp>,
5546 limit: Option<NonZeroUsize>,
5547 client_id: Option<ClientId>,
5548 params: Option<Params>,
5549 handler: ShareableMessageHandler,
5550 ) -> anyhow::Result<UUID4> {
5551 self.check_registered();
5552
5553 let now = self.clock_ref().utc_now();
5554 check_timestamps(now, start, end)?;
5555
5556 let request_id = UUID4::new();
5557 let command = RequestCommand::BookDeltas(RequestBookDeltas {
5558 instrument_id,
5559 start,
5560 end,
5561 limit,
5562 client_id,
5563 request_id,
5564 ts_init: now.into(),
5565 params,
5566 });
5567
5568 get_message_bus()
5569 .borrow_mut()
5570 .register_response_handler(command.request_id(), handler)?;
5571
5572 self.send_data_cmd(DataCommand::Request(command));
5573
5574 Ok(request_id)
5575 }
5576
5577 #[expect(clippy::too_many_arguments)]
5583 pub fn request_book_depth(
5584 &self,
5585 instrument_id: InstrumentId,
5586 start: Option<Timestamp>,
5587 end: Option<Timestamp>,
5588 limit: Option<NonZeroUsize>,
5589 depth: Option<NonZeroUsize>,
5590 client_id: Option<ClientId>,
5591 params: Option<Params>,
5592 handler: ShareableMessageHandler,
5593 ) -> anyhow::Result<UUID4> {
5594 self.check_registered();
5595
5596 let now = self.clock_ref().utc_now();
5597 check_timestamps(now, start, end)?;
5598
5599 let request_id = UUID4::new();
5600 let command = RequestCommand::BookDepth(RequestBookDepth {
5601 instrument_id,
5602 start,
5603 end,
5604 limit,
5605 depth,
5606 client_id,
5607 request_id,
5608 ts_init: now.into(),
5609 params,
5610 });
5611
5612 get_message_bus()
5613 .borrow_mut()
5614 .register_response_handler(command.request_id(), handler)?;
5615
5616 self.send_data_cmd(DataCommand::Request(command));
5617
5618 Ok(request_id)
5619 }
5620
5621 #[expect(clippy::too_many_arguments)]
5627 pub fn request_quotes(
5628 &self,
5629 instrument_id: InstrumentId,
5630 start: Option<Timestamp>,
5631 end: Option<Timestamp>,
5632 limit: Option<NonZeroUsize>,
5633 client_id: Option<ClientId>,
5634 params: Option<Params>,
5635 handler: ShareableMessageHandler,
5636 ) -> anyhow::Result<UUID4> {
5637 self.check_registered();
5638
5639 let now = self.clock_ref().utc_now();
5640 check_timestamps(now, start, end)?;
5641
5642 let request_id = UUID4::new();
5643 let command = RequestCommand::Quotes(RequestQuotes {
5644 instrument_id,
5645 start,
5646 end,
5647 limit,
5648 client_id,
5649 request_id,
5650 ts_init: now.into(),
5651 params,
5652 });
5653
5654 get_message_bus()
5655 .borrow_mut()
5656 .register_response_handler(command.request_id(), handler)?;
5657
5658 self.send_data_cmd(DataCommand::Request(command));
5659
5660 Ok(request_id)
5661 }
5662
5663 #[expect(clippy::too_many_arguments)]
5669 pub fn request_trades(
5670 &self,
5671 instrument_id: InstrumentId,
5672 start: Option<Timestamp>,
5673 end: Option<Timestamp>,
5674 limit: Option<NonZeroUsize>,
5675 client_id: Option<ClientId>,
5676 params: Option<Params>,
5677 handler: ShareableMessageHandler,
5678 ) -> anyhow::Result<UUID4> {
5679 self.check_registered();
5680
5681 let now = self.clock_ref().utc_now();
5682 check_timestamps(now, start, end)?;
5683
5684 let request_id = UUID4::new();
5685 let command = RequestCommand::Trades(RequestTrades {
5686 instrument_id,
5687 start,
5688 end,
5689 limit,
5690 client_id,
5691 request_id,
5692 ts_init: now.into(),
5693 params,
5694 });
5695
5696 get_message_bus()
5697 .borrow_mut()
5698 .register_response_handler(command.request_id(), handler)?;
5699
5700 self.send_data_cmd(DataCommand::Request(command));
5701
5702 Ok(request_id)
5703 }
5704
5705 #[expect(clippy::too_many_arguments)]
5711 pub fn request_bars(
5712 &self,
5713 bar_type: BarType,
5714 start: Option<Timestamp>,
5715 end: Option<Timestamp>,
5716 limit: Option<NonZeroUsize>,
5717 client_id: Option<ClientId>,
5718 params: Option<Params>,
5719 handler: ShareableMessageHandler,
5720 ) -> anyhow::Result<UUID4> {
5721 self.check_registered();
5722
5723 anyhow::ensure!(
5724 bar_type.is_standard(),
5725 "Composite bar types are not supported for `request_bars`, was {bar_type}; \
5726 request aggregation via the `bar_types` params instead",
5727 );
5728
5729 let now = self.clock_ref().utc_now();
5730 check_timestamps(now, start, end)?;
5731
5732 let request_id = UUID4::new();
5733 let command = RequestCommand::Bars(RequestBars {
5734 bar_type,
5735 start,
5736 end,
5737 limit,
5738 client_id,
5739 request_id,
5740 ts_init: now.into(),
5741 params,
5742 });
5743
5744 get_message_bus()
5745 .borrow_mut()
5746 .register_response_handler(command.request_id(), handler)?;
5747
5748 self.send_data_cmd(DataCommand::Request(command));
5749
5750 Ok(request_id)
5751 }
5752
5753 #[expect(clippy::too_many_arguments)]
5759 pub fn request_funding_rates(
5760 &self,
5761 instrument_id: InstrumentId,
5762 start: Option<Timestamp>,
5763 end: Option<Timestamp>,
5764 limit: Option<NonZeroUsize>,
5765 client_id: Option<ClientId>,
5766 params: Option<Params>,
5767 handler: ShareableMessageHandler,
5768 ) -> anyhow::Result<UUID4> {
5769 self.check_registered();
5770
5771 let now = self.clock_ref().utc_now();
5772 check_timestamps(now, start, end)?;
5773
5774 let request_id = UUID4::new();
5775 let command = RequestCommand::FundingRates(RequestFundingRates {
5776 instrument_id,
5777 start,
5778 end,
5779 limit,
5780 client_id,
5781 request_id,
5782 ts_init: now.into(),
5783 params,
5784 });
5785
5786 get_message_bus()
5787 .borrow_mut()
5788 .register_response_handler(command.request_id(), handler)?;
5789
5790 self.send_data_cmd(DataCommand::Request(command));
5791
5792 Ok(request_id)
5793 }
5794
5795 #[cfg(feature = "live")]
5802 pub fn reconnect_socket(&self, client_id: ClientId, endpoint: &str) -> anyhow::Result<()> {
5803 let endpoint = socket_endpoint(endpoint)?;
5804
5805 if !self.is_properly_registered() {
5806 anyhow::bail!(
5807 "Actor {} has not been registered with a Trader",
5808 self.actor_id
5809 );
5810 }
5811
5812 let sender = try_get_system_command_sender()
5813 .ok_or_else(|| anyhow::anyhow!("Live runner system command channel is unavailable"))?;
5814 let trader_id = self
5815 .trader_id
5816 .ok_or_else(|| anyhow::anyhow!("Actor {} has no trader ID", self.actor_id))?;
5817 let command = ReconnectSocket::new(trader_id, client_id, endpoint, self.timestamp_ns());
5818 sender
5819 .send(SystemCommand::ReconnectSocket(command))
5820 .map_err(|_| anyhow::anyhow!("Live runner system command channel is closed"))?;
5821 Ok(())
5822 }
5823
5824 #[cfg(test)]
5825 pub fn quote_handler_count(&self) -> usize {
5826 self.quote_handlers.len()
5827 }
5828
5829 #[cfg(test)]
5830 pub fn trade_handler_count(&self) -> usize {
5831 self.trade_handlers.len()
5832 }
5833
5834 #[cfg(test)]
5835 pub fn bar_handler_count(&self) -> usize {
5836 self.bar_handlers.len()
5837 }
5838
5839 #[cfg(test)]
5840 pub fn deltas_handler_count(&self) -> usize {
5841 self.deltas_handlers.len()
5842 }
5843
5844 #[cfg(test)]
5845 pub fn depth10_handler_count(&self) -> usize {
5846 self.depth10_handlers.len()
5847 }
5848
5849 #[cfg(test)]
5850 pub fn has_quote_handler(&self, topic: &str) -> bool {
5851 self.quote_handlers
5852 .contains_key(&MStr::<Topic>::from(topic))
5853 }
5854
5855 #[cfg(test)]
5856 pub fn has_trade_handler(&self, topic: &str) -> bool {
5857 self.trade_handlers
5858 .contains_key(&MStr::<Topic>::from(topic))
5859 }
5860
5861 #[cfg(test)]
5862 pub fn has_bar_handler(&self, topic: &str) -> bool {
5863 self.bar_handlers.contains_key(&MStr::<Topic>::from(topic))
5864 }
5865
5866 #[cfg(test)]
5867 pub fn has_deltas_handler(&self, pattern: &str) -> bool {
5868 self.deltas_handlers
5869 .contains_key(&MStr::<Pattern>::from(pattern))
5870 }
5871
5872 #[cfg(test)]
5873 pub fn has_depth10_handler(&self, pattern: &str) -> bool {
5874 self.depth10_handlers
5875 .contains_key(&MStr::<Pattern>::from(pattern))
5876 }
5877}
5878
5879impl DataActorNative for DataActorCore {
5880 fn core(&self) -> &DataActorCore {
5881 self
5882 }
5883
5884 fn core_mut(&mut self) -> &mut DataActorCore {
5885 self
5886 }
5887}
5888
5889fn check_timestamps(
5890 now: Timestamp,
5891 start: Option<Timestamp>,
5892 end: Option<Timestamp>,
5893) -> anyhow::Result<()> {
5894 if let Some(start) = start {
5895 check_predicate_true(start <= now, "start was > now")?;
5896 }
5897
5898 if let Some(end) = end {
5899 check_predicate_true(end <= now, "end was > now")?;
5900 }
5901
5902 if let (Some(start), Some(end)) = (start, end) {
5903 check_predicate_true(start <= end, "start was > end")?;
5904 }
5905
5906 Ok(())
5907}
5908
5909fn log_error(e: &anyhow::Error) {
5910 log::error!("{e}");
5911}
5912
5913fn log_not_running<T>(msg: &T)
5914where
5915 T: Debug,
5916{
5917 log::trace!("Received message when not running - skipping {msg:?}");
5918}
5919
5920fn log_received<T>(msg: &T)
5921where
5922 T: Debug,
5923{
5924 log::debug!("{RECV} {msg:?}");
5925}
5926
5927fn log_received_bulk(kind: &str, correlation_id: &UUID4, records: usize) {
5928 log::debug!("{RECV} {kind} correlation_id={correlation_id} records={records}");
5929}