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