1use std::{
22 fmt::{Debug, Display},
23 hash::Hash,
24 ops::{Deref, DerefMut},
25};
26
27use ahash::AHashSet;
28#[cfg(feature = "defi")]
29use nautilus_common::messages::defi::DefiSubscribeCommand;
30use nautilus_common::{
31 clients::{DataClient, log_command_error},
32 enums::LogColor,
33 log_info,
34 messages::data::{
35 RequestBars, RequestBookDepth, RequestBookSnapshot, RequestCustomData, RequestFundingRates,
36 RequestInstrument, RequestInstruments, RequestOptionChainReferencePrice, RequestQuotes,
37 RequestTrades, SubscribeBars, SubscribeBookDeltas, SubscribeBookDepth10, SubscribeCommand,
38 SubscribeCustomData, SubscribeFundingRates, SubscribeIndexPrices, SubscribeInstrument,
39 SubscribeInstrumentClose, SubscribeInstrumentStatus, SubscribeInstruments,
40 SubscribeMarkPrices, SubscribeOptionGreeks, SubscribeQuotes, SubscribeTrades,
41 UnsubscribeBars, UnsubscribeBookDeltas, UnsubscribeBookDepth10, UnsubscribeCommand,
42 UnsubscribeCustomData, UnsubscribeFundingRates, UnsubscribeIndexPrices,
43 UnsubscribeInstrument, UnsubscribeInstrumentClose, UnsubscribeInstrumentStatus,
44 UnsubscribeInstruments, UnsubscribeMarkPrices, UnsubscribeOptionGreeks, UnsubscribeQuotes,
45 UnsubscribeTrades,
46 },
47};
48#[cfg(feature = "defi")]
49use nautilus_model::defi::Blockchain;
50use nautilus_model::{
51 data::{BarType, DataType},
52 identifiers::{ClientId, InstrumentId, Venue},
53};
54
55#[cfg(feature = "defi")]
56#[allow(unused_imports)] use crate::defi::client as _;
58#[cfg(feature = "defi")]
59use crate::subscription::DefiSubscriptionKey;
60use crate::subscription::{SubscriptionKey, SubscriptionRegistry, SubscriptionRelease};
61
62pub struct DataClientAdapter {
64 pub(crate) client: Box<dyn DataClient>,
65 pub client_id: ClientId,
66 pub venue: Option<Venue>,
67 pub handles_book_deltas: bool,
68 pub handles_book_snapshots: bool,
69 pub subscriptions_custom: AHashSet<DataType>,
70 pub subscriptions_book_deltas: AHashSet<InstrumentId>,
71 pub subscriptions_book_depth10: AHashSet<InstrumentId>,
72 pub subscriptions_quotes: AHashSet<InstrumentId>,
73 pub subscriptions_trades: AHashSet<InstrumentId>,
74 pub subscriptions_bars: AHashSet<BarType>,
75 pub subscriptions_instrument_status: AHashSet<InstrumentId>,
76 pub subscriptions_instrument_close: AHashSet<InstrumentId>,
77 pub subscriptions_instrument: AHashSet<InstrumentId>,
78 pub subscriptions_instrument_venue: AHashSet<Venue>,
79 pub subscriptions_mark_prices: AHashSet<InstrumentId>,
80 pub subscriptions_index_prices: AHashSet<InstrumentId>,
81 pub subscriptions_funding_rates: AHashSet<InstrumentId>,
82 pub subscriptions_option_greeks: AHashSet<InstrumentId>,
83 subscriptions_active: SubscriptionRegistry<SubscriptionKey, SubscribeCommand>,
84 #[cfg(feature = "defi")]
85 pub(crate) subscriptions_active_defi:
86 SubscriptionRegistry<DefiSubscriptionKey, DefiSubscribeCommand>,
87 #[cfg(feature = "defi")]
88 pub subscriptions_blocks: AHashSet<Blockchain>,
89 #[cfg(feature = "defi")]
90 pub subscriptions_pools: AHashSet<InstrumentId>,
91 #[cfg(feature = "defi")]
92 pub subscriptions_pool_swaps: AHashSet<InstrumentId>,
93 #[cfg(feature = "defi")]
94 pub subscriptions_pool_liquidity_updates: AHashSet<InstrumentId>,
95 #[cfg(feature = "defi")]
96 pub subscriptions_pool_fee_collects: AHashSet<InstrumentId>,
97 #[cfg(feature = "defi")]
98 pub subscriptions_pool_flash: AHashSet<InstrumentId>,
99}
100
101impl Deref for DataClientAdapter {
102 type Target = Box<dyn DataClient>;
103
104 fn deref(&self) -> &Self::Target {
105 &self.client
106 }
107}
108
109impl DerefMut for DataClientAdapter {
110 fn deref_mut(&mut self) -> &mut Self::Target {
111 &mut self.client
112 }
113}
114
115impl Debug for DataClientAdapter {
116 #[rustfmt::skip]
117 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
118 f.debug_struct(stringify!(DataClientAdapter))
119 .field("client_id", &self.client_id)
120 .field("venue", &self.venue)
121 .field("handles_book_deltas", &self.handles_book_deltas)
122 .field("handles_book_snapshots", &self.handles_book_snapshots)
123 .field("subscriptions_custom", &self.subscriptions_custom)
124 .field("subscriptions_book_deltas", &self.subscriptions_book_deltas)
125 .field("subscriptions_book_depth10", &self.subscriptions_book_depth10)
126 .field("subscriptions_quotes", &self.subscriptions_quotes)
127 .field("subscriptions_trades", &self.subscriptions_trades)
128 .field("subscriptions_bars", &self.subscriptions_bars)
129 .field("subscriptions_mark_prices", &self.subscriptions_mark_prices)
130 .field("subscriptions_index_prices", &self.subscriptions_index_prices)
131 .field("subscriptions_instrument_status", &self.subscriptions_instrument_status)
132 .field("subscriptions_instrument_close", &self.subscriptions_instrument_close)
133 .field("subscriptions_instrument", &self.subscriptions_instrument)
134 .field("subscriptions_instrument_venue", &self.subscriptions_instrument_venue)
135 .finish()
136 }
137}
138
139impl DataClientAdapter {
140 #[must_use]
142 pub fn new(
143 client_id: ClientId,
144 venue: Option<Venue>,
145 handles_order_book_deltas: bool,
146 handles_order_book_snapshots: bool,
147 client: Box<dyn DataClient>,
148 ) -> Self {
149 Self {
150 client,
151 client_id,
152 venue,
153 handles_book_deltas: handles_order_book_deltas,
154 handles_book_snapshots: handles_order_book_snapshots,
155 subscriptions_custom: AHashSet::new(),
156 subscriptions_book_deltas: AHashSet::new(),
157 subscriptions_book_depth10: AHashSet::new(),
158 subscriptions_quotes: AHashSet::new(),
159 subscriptions_trades: AHashSet::new(),
160 subscriptions_mark_prices: AHashSet::new(),
161 subscriptions_index_prices: AHashSet::new(),
162 subscriptions_funding_rates: AHashSet::new(),
163 subscriptions_option_greeks: AHashSet::new(),
164 subscriptions_bars: AHashSet::new(),
165 subscriptions_instrument_status: AHashSet::new(),
166 subscriptions_instrument_close: AHashSet::new(),
167 subscriptions_instrument: AHashSet::new(),
168 subscriptions_instrument_venue: AHashSet::new(),
169 subscriptions_active: SubscriptionRegistry::default(),
170 #[cfg(feature = "defi")]
171 subscriptions_active_defi: SubscriptionRegistry::default(),
172 #[cfg(feature = "defi")]
173 subscriptions_blocks: AHashSet::new(),
174 #[cfg(feature = "defi")]
175 subscriptions_pools: AHashSet::new(),
176 #[cfg(feature = "defi")]
177 subscriptions_pool_swaps: AHashSet::new(),
178 #[cfg(feature = "defi")]
179 subscriptions_pool_liquidity_updates: AHashSet::new(),
180 #[cfg(feature = "defi")]
181 subscriptions_pool_fee_collects: AHashSet::new(),
182 #[cfg(feature = "defi")]
183 subscriptions_pool_flash: AHashSet::new(),
184 }
185 }
186
187 #[expect(clippy::borrowed_box)]
188 #[must_use]
189 pub fn get_client(&self) -> &Box<dyn DataClient> {
190 &self.client
191 }
192
193 pub async fn connect(&mut self) -> anyhow::Result<()> {
199 self.client.connect().await
200 }
201
202 pub async fn disconnect(&mut self) -> anyhow::Result<()> {
208 self.client.disconnect().await
209 }
210
211 #[inline]
212 pub fn execute_subscribe(&mut self, cmd: SubscribeCommand) {
213 self.execute_subscribe_with_retained(cmd.clone(), cmd, false);
214 }
215
216 pub(crate) fn execute_subscribe_intent(&mut self, cmd: SubscribeCommand) {
217 self.execute_subscribe_with_retained(cmd.clone(), cmd, true);
218 }
219
220 pub(crate) fn execute_subscribe_with_retained(
221 &mut self,
222 cmd: SubscribeCommand,
223 retained: SubscribeCommand,
224 retain_on_failure: bool,
225 ) {
226 let key = SubscriptionKey::from_subscribe(&retained);
227 if self.has_active_subscription(&retained) {
228 self.subscriptions_active
229 .retain(key, retained.command_id(), retained);
230 return;
231 }
232
233 let cmd_debug = format!("{cmd:?}");
234 let result = match cmd {
235 SubscribeCommand::Data(cmd) => self.subscribe(cmd),
236 SubscribeCommand::Instrument(cmd) => self.subscribe_instrument(cmd),
237 SubscribeCommand::Instruments(cmd) => self.subscribe_instruments(cmd),
238 SubscribeCommand::BookDeltas(cmd) => self.subscribe_book_deltas(cmd),
239 SubscribeCommand::BookDepth10(cmd) => self.subscribe_book_depth10(cmd),
240 SubscribeCommand::BookSnapshots(_) => Ok(()), SubscribeCommand::Quotes(cmd) => self.subscribe_quotes(cmd),
242 SubscribeCommand::Trades(cmd) => self.subscribe_trades(cmd),
243 SubscribeCommand::MarkPrices(cmd) => self.subscribe_mark_prices(cmd),
244 SubscribeCommand::IndexPrices(cmd) => self.subscribe_index_prices(cmd),
245 SubscribeCommand::FundingRates(cmd) => self.subscribe_funding_rates(cmd),
246 SubscribeCommand::Bars(cmd) => self.subscribe_bars(cmd),
247 SubscribeCommand::InstrumentStatus(cmd) => self.subscribe_instrument_status(cmd),
248 SubscribeCommand::InstrumentClose(cmd) => self.subscribe_instrument_close(cmd),
249 SubscribeCommand::OptionGreeks(cmd) => self.subscribe_option_greeks(cmd),
250 SubscribeCommand::OptionChain(_) => Ok(()), };
252
253 if let Err(e) = result {
254 if retain_on_failure {
256 self.subscriptions_active
257 .retain(key, retained.command_id(), retained);
258 }
259
260 log_command_error(&cmd_debug, &e);
261 return;
262 }
263
264 if let Some(subscription) = self.subscriptions_active.get_mut(&key) {
266 subscription.command = retained.clone();
267 }
268 self.subscriptions_active
269 .retain(key, retained.command_id(), retained);
270 }
271
272 #[must_use]
273 #[rustfmt::skip]
274 pub(crate) fn has_active_subscription(&self, cmd: &SubscribeCommand) -> bool {
275 match cmd {
276 SubscribeCommand::Data(cmd) => self.subscriptions_custom.contains(&cmd.data_type),
277 SubscribeCommand::Instrument(cmd) => self.subscriptions_instrument.contains(&cmd.instrument_id),
278 SubscribeCommand::Instruments(cmd) => self.subscriptions_instrument_venue.contains(&cmd.venue),
279 SubscribeCommand::BookDeltas(cmd) => self.subscriptions_book_deltas.contains(&cmd.instrument_id),
280 SubscribeCommand::BookDepth10(cmd) => self.subscriptions_book_depth10.contains(&cmd.instrument_id),
281 SubscribeCommand::Quotes(cmd) => self.subscriptions_quotes.contains(&cmd.instrument_id),
282 SubscribeCommand::Trades(cmd) => self.subscriptions_trades.contains(&cmd.instrument_id),
283 SubscribeCommand::Bars(cmd) => self.subscriptions_bars.contains(&cmd.bar_type),
284 SubscribeCommand::MarkPrices(cmd) => self.subscriptions_mark_prices.contains(&cmd.instrument_id),
285 SubscribeCommand::IndexPrices(cmd) => self.subscriptions_index_prices.contains(&cmd.instrument_id),
286 SubscribeCommand::FundingRates(cmd) => self.subscriptions_funding_rates.contains(&cmd.instrument_id),
287 SubscribeCommand::InstrumentStatus(cmd) => self.subscriptions_instrument_status.contains(&cmd.instrument_id),
288 SubscribeCommand::InstrumentClose(cmd) => self.subscriptions_instrument_close.contains(&cmd.instrument_id),
289 SubscribeCommand::OptionGreeks(cmd) => self.subscriptions_option_greeks.contains(&cmd.instrument_id),
290 SubscribeCommand::BookSnapshots(_) | SubscribeCommand::OptionChain(_) => self.subscriptions_active.contains(&SubscriptionKey::from_subscribe(cmd)),
291 }
292 }
293
294 pub(crate) fn clear_subscription_state(&mut self) {
295 self.subscriptions_custom.clear();
296 self.subscriptions_book_deltas.clear();
297 self.subscriptions_book_depth10.clear();
298 self.subscriptions_quotes.clear();
299 self.subscriptions_trades.clear();
300 self.subscriptions_bars.clear();
301 self.subscriptions_instrument_status.clear();
302 self.subscriptions_instrument_close.clear();
303 self.subscriptions_instrument.clear();
304 self.subscriptions_instrument_venue.clear();
305 self.subscriptions_mark_prices.clear();
306 self.subscriptions_index_prices.clear();
307 self.subscriptions_funding_rates.clear();
308 self.subscriptions_option_greeks.clear();
309 self.subscriptions_active.clear();
310
311 #[cfg(feature = "defi")]
312 {
313 self.subscriptions_active_defi.clear();
314 self.subscriptions_blocks.clear();
315 self.subscriptions_pools.clear();
316 self.subscriptions_pool_swaps.clear();
317 self.subscriptions_pool_liquidity_updates.clear();
318 self.subscriptions_pool_fee_collects.clear();
319 self.subscriptions_pool_flash.clear();
320 }
321 }
322
323 #[inline]
324 pub fn execute_unsubscribe(&mut self, cmd: &UnsubscribeCommand) {
325 let key = SubscriptionKey::from_unsubscribe(cmd);
326 let command = match self.subscriptions_active.release(&key) {
327 SubscriptionRelease::Retained => return,
328 SubscriptionRelease::Final(subscribe) => {
329 subscribe.into_unsubscribe(cmd.command_id(), cmd.ts_init(), cmd.correlation_id())
330 }
331 SubscriptionRelease::Untracked => cmd.clone(),
332 };
333
334 if let Err(e) = match &command {
335 UnsubscribeCommand::Data(cmd) => self.unsubscribe(cmd),
336 UnsubscribeCommand::Instrument(cmd) => self.unsubscribe_instrument(cmd),
337 UnsubscribeCommand::Instruments(cmd) => self.unsubscribe_instruments(cmd),
338 UnsubscribeCommand::BookDeltas(cmd) => self.unsubscribe_book_deltas(cmd),
339 UnsubscribeCommand::BookDepth10(cmd) => self.unsubscribe_book_depth10(cmd),
340 UnsubscribeCommand::BookSnapshots(_) => Ok(()), UnsubscribeCommand::Quotes(cmd) => self.unsubscribe_quotes(cmd),
342 UnsubscribeCommand::Trades(cmd) => self.unsubscribe_trades(cmd),
343 UnsubscribeCommand::Bars(cmd) => self.unsubscribe_bars(cmd),
344 UnsubscribeCommand::MarkPrices(cmd) => self.unsubscribe_mark_prices(cmd),
345 UnsubscribeCommand::IndexPrices(cmd) => self.unsubscribe_index_prices(cmd),
346 UnsubscribeCommand::FundingRates(cmd) => self.unsubscribe_funding_rates(cmd),
347 UnsubscribeCommand::InstrumentStatus(cmd) => self.unsubscribe_instrument_status(cmd),
348 UnsubscribeCommand::InstrumentClose(cmd) => self.unsubscribe_instrument_close(cmd),
349 UnsubscribeCommand::OptionGreeks(cmd) => self.unsubscribe_option_greeks(cmd),
350 UnsubscribeCommand::OptionChain(_) => Ok(()), } {
352 log_command_error(&command, &e);
353 } else {
354 self.subscriptions_active.remove(&key);
355 }
356 }
357
358 pub fn subscribe(&mut self, cmd: SubscribeCustomData) -> anyhow::Result<()> {
364 let data_type = cmd.data_type.clone();
365 Self::execute_tracked_subscribe(
366 self.client.as_mut(),
367 &mut self.subscriptions_custom,
368 data_type,
369 "",
370 |client| client.subscribe(cmd),
371 )
372 }
373
374 pub fn unsubscribe(&mut self, cmd: &UnsubscribeCustomData) -> anyhow::Result<()> {
380 Self::execute_tracked_unsubscribe(
381 self.client.as_mut(),
382 &mut self.subscriptions_custom,
383 &cmd.data_type,
384 "",
385 |client| client.unsubscribe(cmd),
386 )
387 }
388
389 fn subscribe_instruments(&mut self, cmd: SubscribeInstruments) -> anyhow::Result<()> {
395 Self::execute_tracked_subscribe(
396 self.client.as_mut(),
397 &mut self.subscriptions_instrument_venue,
398 cmd.venue,
399 "instruments",
400 |client| client.subscribe_instruments(cmd),
401 )
402 }
403
404 fn unsubscribe_instruments(&mut self, cmd: &UnsubscribeInstruments) -> anyhow::Result<()> {
410 Self::execute_tracked_unsubscribe(
411 self.client.as_mut(),
412 &mut self.subscriptions_instrument_venue,
413 &cmd.venue,
414 "instruments",
415 |client| client.unsubscribe_instruments(cmd),
416 )
417 }
418
419 fn subscribe_instrument(&mut self, cmd: SubscribeInstrument) -> anyhow::Result<()> {
425 Self::execute_tracked_subscribe(
426 self.client.as_mut(),
427 &mut self.subscriptions_instrument,
428 cmd.instrument_id,
429 "instrument",
430 |client| client.subscribe_instrument(cmd),
431 )
432 }
433
434 fn unsubscribe_instrument(&mut self, cmd: &UnsubscribeInstrument) -> anyhow::Result<()> {
440 Self::execute_tracked_unsubscribe(
441 self.client.as_mut(),
442 &mut self.subscriptions_instrument,
443 &cmd.instrument_id,
444 "instrument",
445 |client| client.unsubscribe_instrument(cmd),
446 )
447 }
448
449 fn subscribe_book_deltas(&mut self, cmd: SubscribeBookDeltas) -> anyhow::Result<()> {
455 Self::execute_tracked_subscribe(
456 self.client.as_mut(),
457 &mut self.subscriptions_book_deltas,
458 cmd.instrument_id,
459 "order book deltas",
460 |client| client.subscribe_book_deltas(cmd),
461 )
462 }
463
464 fn unsubscribe_book_deltas(&mut self, cmd: &UnsubscribeBookDeltas) -> anyhow::Result<()> {
470 Self::execute_tracked_unsubscribe(
471 self.client.as_mut(),
472 &mut self.subscriptions_book_deltas,
473 &cmd.instrument_id,
474 "order book deltas",
475 |client| client.unsubscribe_book_deltas(cmd),
476 )
477 }
478
479 fn subscribe_book_depth10(&mut self, cmd: SubscribeBookDepth10) -> anyhow::Result<()> {
485 Self::execute_tracked_subscribe(
486 self.client.as_mut(),
487 &mut self.subscriptions_book_depth10,
488 cmd.instrument_id,
489 "order book depth",
490 |client| client.subscribe_book_depth10(cmd),
491 )
492 }
493
494 fn unsubscribe_book_depth10(&mut self, cmd: &UnsubscribeBookDepth10) -> anyhow::Result<()> {
500 Self::execute_tracked_unsubscribe(
501 self.client.as_mut(),
502 &mut self.subscriptions_book_depth10,
503 &cmd.instrument_id,
504 "order book depth",
505 |client| client.unsubscribe_book_depth10(cmd),
506 )
507 }
508
509 fn subscribe_quotes(&mut self, cmd: SubscribeQuotes) -> anyhow::Result<()> {
515 Self::execute_tracked_subscribe(
516 self.client.as_mut(),
517 &mut self.subscriptions_quotes,
518 cmd.instrument_id,
519 "quotes",
520 |client| client.subscribe_quotes(cmd),
521 )
522 }
523
524 fn unsubscribe_quotes(&mut self, cmd: &UnsubscribeQuotes) -> anyhow::Result<()> {
530 Self::execute_tracked_unsubscribe(
531 self.client.as_mut(),
532 &mut self.subscriptions_quotes,
533 &cmd.instrument_id,
534 "quotes",
535 |client| client.unsubscribe_quotes(cmd),
536 )
537 }
538
539 fn subscribe_trades(&mut self, cmd: SubscribeTrades) -> anyhow::Result<()> {
545 Self::execute_tracked_subscribe(
546 self.client.as_mut(),
547 &mut self.subscriptions_trades,
548 cmd.instrument_id,
549 "trades",
550 |client| client.subscribe_trades(cmd),
551 )
552 }
553
554 fn unsubscribe_trades(&mut self, cmd: &UnsubscribeTrades) -> anyhow::Result<()> {
560 Self::execute_tracked_unsubscribe(
561 self.client.as_mut(),
562 &mut self.subscriptions_trades,
563 &cmd.instrument_id,
564 "trades",
565 |client| client.unsubscribe_trades(cmd),
566 )
567 }
568
569 fn subscribe_bars(&mut self, cmd: SubscribeBars) -> anyhow::Result<()> {
575 Self::execute_tracked_subscribe(
576 self.client.as_mut(),
577 &mut self.subscriptions_bars,
578 cmd.bar_type,
579 "bars",
580 |client| client.subscribe_bars(cmd),
581 )
582 }
583
584 fn unsubscribe_bars(&mut self, cmd: &UnsubscribeBars) -> anyhow::Result<()> {
590 Self::execute_tracked_unsubscribe(
591 self.client.as_mut(),
592 &mut self.subscriptions_bars,
593 &cmd.bar_type,
594 "bars",
595 |client| client.unsubscribe_bars(cmd),
596 )
597 }
598
599 fn subscribe_mark_prices(&mut self, cmd: SubscribeMarkPrices) -> anyhow::Result<()> {
605 Self::execute_tracked_subscribe(
606 self.client.as_mut(),
607 &mut self.subscriptions_mark_prices,
608 cmd.instrument_id,
609 "mark prices",
610 |client| client.subscribe_mark_prices(cmd),
611 )
612 }
613
614 fn unsubscribe_mark_prices(&mut self, cmd: &UnsubscribeMarkPrices) -> anyhow::Result<()> {
620 Self::execute_tracked_unsubscribe(
621 self.client.as_mut(),
622 &mut self.subscriptions_mark_prices,
623 &cmd.instrument_id,
624 "mark prices",
625 |client| client.unsubscribe_mark_prices(cmd),
626 )
627 }
628
629 fn subscribe_index_prices(&mut self, cmd: SubscribeIndexPrices) -> anyhow::Result<()> {
635 Self::execute_tracked_subscribe(
636 self.client.as_mut(),
637 &mut self.subscriptions_index_prices,
638 cmd.instrument_id,
639 "index prices",
640 |client| client.subscribe_index_prices(cmd),
641 )
642 }
643
644 fn unsubscribe_index_prices(&mut self, cmd: &UnsubscribeIndexPrices) -> anyhow::Result<()> {
650 Self::execute_tracked_unsubscribe(
651 self.client.as_mut(),
652 &mut self.subscriptions_index_prices,
653 &cmd.instrument_id,
654 "index prices",
655 |client| client.unsubscribe_index_prices(cmd),
656 )
657 }
658
659 fn subscribe_funding_rates(&mut self, cmd: SubscribeFundingRates) -> anyhow::Result<()> {
665 Self::execute_tracked_subscribe(
666 self.client.as_mut(),
667 &mut self.subscriptions_funding_rates,
668 cmd.instrument_id,
669 "funding rates",
670 |client| client.subscribe_funding_rates(cmd),
671 )
672 }
673
674 fn unsubscribe_funding_rates(&mut self, cmd: &UnsubscribeFundingRates) -> anyhow::Result<()> {
680 Self::execute_tracked_unsubscribe(
681 self.client.as_mut(),
682 &mut self.subscriptions_funding_rates,
683 &cmd.instrument_id,
684 "funding rates",
685 |client| client.unsubscribe_funding_rates(cmd),
686 )
687 }
688
689 fn subscribe_instrument_status(
695 &mut self,
696 cmd: SubscribeInstrumentStatus,
697 ) -> anyhow::Result<()> {
698 Self::execute_tracked_subscribe(
699 self.client.as_mut(),
700 &mut self.subscriptions_instrument_status,
701 cmd.instrument_id,
702 "instrument status",
703 |client| client.subscribe_instrument_status(cmd),
704 )
705 }
706
707 fn unsubscribe_instrument_status(
713 &mut self,
714 cmd: &UnsubscribeInstrumentStatus,
715 ) -> anyhow::Result<()> {
716 Self::execute_tracked_unsubscribe(
717 self.client.as_mut(),
718 &mut self.subscriptions_instrument_status,
719 &cmd.instrument_id,
720 "instrument status",
721 |client| client.unsubscribe_instrument_status(cmd),
722 )
723 }
724
725 fn subscribe_instrument_close(&mut self, cmd: SubscribeInstrumentClose) -> anyhow::Result<()> {
731 Self::execute_tracked_subscribe(
732 self.client.as_mut(),
733 &mut self.subscriptions_instrument_close,
734 cmd.instrument_id,
735 "instrument close",
736 |client| client.subscribe_instrument_close(cmd),
737 )
738 }
739
740 fn unsubscribe_instrument_close(
746 &mut self,
747 cmd: &UnsubscribeInstrumentClose,
748 ) -> anyhow::Result<()> {
749 Self::execute_tracked_unsubscribe(
750 self.client.as_mut(),
751 &mut self.subscriptions_instrument_close,
752 &cmd.instrument_id,
753 "instrument close",
754 |client| client.unsubscribe_instrument_close(cmd),
755 )
756 }
757
758 fn subscribe_option_greeks(&mut self, cmd: SubscribeOptionGreeks) -> anyhow::Result<()> {
764 Self::execute_tracked_subscribe(
765 self.client.as_mut(),
766 &mut self.subscriptions_option_greeks,
767 cmd.instrument_id,
768 "option greeks",
769 |client| client.subscribe_option_greeks(cmd),
770 )
771 }
772
773 fn unsubscribe_option_greeks(&mut self, cmd: &UnsubscribeOptionGreeks) -> anyhow::Result<()> {
779 Self::execute_tracked_unsubscribe(
780 self.client.as_mut(),
781 &mut self.subscriptions_option_greeks,
782 &cmd.instrument_id,
783 "option greeks",
784 |client| client.unsubscribe_option_greeks(cmd),
785 )
786 }
787
788 pub(crate) fn execute_tracked_subscribe<T>(
789 client: &mut dyn DataClient,
790 set: &mut AHashSet<T>,
791 key: T,
792 data_type: &str,
793 subscribe: impl FnOnce(&mut dyn DataClient) -> anyhow::Result<()>,
794 ) -> anyhow::Result<()>
795 where
796 T: Eq + Hash + Display,
797 {
798 if set.contains(&key) {
799 return Ok(());
800 }
801
802 subscribe(client)?;
803
804 if data_type.is_empty() {
805 log_info!("Subscribed {key}", color = LogColor::Blue);
806 } else {
807 log_info!("Subscribed {key} {data_type}", color = LogColor::Blue);
808 }
809 set.insert(key);
810 Ok(())
811 }
812
813 pub(crate) fn execute_tracked_unsubscribe<T>(
814 client: &mut dyn DataClient,
815 set: &mut AHashSet<T>,
816 key: &T,
817 data_type: &str,
818 unsubscribe: impl FnOnce(&mut dyn DataClient) -> anyhow::Result<()>,
819 ) -> anyhow::Result<()>
820 where
821 T: Eq + Hash + Display,
822 {
823 if !set.contains(key) {
824 return Ok(());
825 }
826
827 unsubscribe(client)?;
828 set.remove(key);
829 if data_type.is_empty() {
830 log_info!("Unsubscribed {key}", color = LogColor::Blue);
831 } else {
832 log_info!("Unsubscribed {key} {data_type}", color = LogColor::Blue);
833 }
834 Ok(())
835 }
836
837 pub fn request_data(&self, req: RequestCustomData) -> anyhow::Result<()> {
843 self.client.request_data(req)
844 }
845
846 pub fn request_instrument(&self, req: RequestInstrument) -> anyhow::Result<()> {
852 self.client.request_instrument(req)
853 }
854
855 pub fn request_instruments(&self, req: RequestInstruments) -> anyhow::Result<()> {
861 self.client.request_instruments(req)
862 }
863
864 pub fn request_book_snapshot(&self, req: RequestBookSnapshot) -> anyhow::Result<()> {
870 self.client.request_book_snapshot(req)
871 }
872
873 pub fn request_quotes(&self, req: RequestQuotes) -> anyhow::Result<()> {
879 self.client.request_quotes(req)
880 }
881
882 pub fn request_trades(&self, req: RequestTrades) -> anyhow::Result<()> {
888 self.client.request_trades(req)
889 }
890
891 pub fn request_funding_rates(&self, req: RequestFundingRates) -> anyhow::Result<()> {
897 self.client.request_funding_rates(req)
898 }
899
900 pub fn request_option_chain_reference_price(
906 &self,
907 req: RequestOptionChainReferencePrice,
908 ) -> anyhow::Result<()> {
909 self.client.request_option_chain_reference_price(req)
910 }
911
912 pub fn request_bars(&self, req: RequestBars) -> anyhow::Result<()> {
918 self.client.request_bars(req)
919 }
920
921 pub fn request_book_depth(&self, req: RequestBookDepth) -> anyhow::Result<()> {
927 self.client.request_book_depth(req)
928 }
929}