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, SubscribeBookDepth, SubscribeCommand,
38 SubscribeCustomData, SubscribeFundingRates, SubscribeIndexPrices, SubscribeInstrument,
39 SubscribeInstrumentClose, SubscribeInstrumentStatus, SubscribeInstruments,
40 SubscribeMarkPrices, SubscribeOptionGreeks, SubscribeQuotes, SubscribeTrades,
41 UnsubscribeBars, UnsubscribeBookDeltas, UnsubscribeBookDepth, 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_depth: 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_depth", &self.subscriptions_book_depth)
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_depth: 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 command_id = cmd.command_id();
234
235 let result = match cmd {
236 SubscribeCommand::Data(cmd) => self.subscribe(cmd),
237 SubscribeCommand::Instrument(cmd) => self.subscribe_instrument(cmd),
238 SubscribeCommand::Instruments(cmd) => self.subscribe_instruments(cmd),
239 SubscribeCommand::BookDeltas(cmd) => self.subscribe_book_deltas(cmd),
240 SubscribeCommand::BookDepth(cmd) => self.subscribe_book_depth(cmd),
241 SubscribeCommand::BookSnapshots(_) => Ok(()), SubscribeCommand::Quotes(cmd) => self.subscribe_quotes(cmd),
243 SubscribeCommand::Trades(cmd) => self.subscribe_trades(cmd),
244 SubscribeCommand::MarkPrices(cmd) => self.subscribe_mark_prices(cmd),
245 SubscribeCommand::IndexPrices(cmd) => self.subscribe_index_prices(cmd),
246 SubscribeCommand::FundingRates(cmd) => self.subscribe_funding_rates(cmd),
247 SubscribeCommand::Bars(cmd) => self.subscribe_bars(cmd),
248 SubscribeCommand::InstrumentStatus(cmd) => self.subscribe_instrument_status(cmd),
249 SubscribeCommand::InstrumentClose(cmd) => self.subscribe_instrument_close(cmd),
250 SubscribeCommand::OptionGreeks(cmd) => self.subscribe_option_greeks(cmd),
251 SubscribeCommand::OptionChain(_) => Ok(()), };
253
254 if let Err(e) = result {
255 let subscription = key.log_context();
256
257 if retain_on_failure {
259 self.subscriptions_active
260 .retain(key, retained.command_id(), retained);
261 }
262
263 log_command_error(
264 &format_args!(
265 "Subscribe {} {subscription} command {command_id}",
266 self.client_id()
267 ),
268 &e,
269 );
270 return;
271 }
272
273 if let Some(subscription) = self.subscriptions_active.get_mut(&key) {
275 subscription.command = retained.clone();
276 }
277 self.subscriptions_active
278 .retain(key, retained.command_id(), retained);
279 }
280
281 #[must_use]
282 #[rustfmt::skip]
283 pub(crate) fn has_active_subscription(&self, cmd: &SubscribeCommand) -> bool {
284 match cmd {
285 SubscribeCommand::Data(cmd) => self.subscriptions_custom.contains(&cmd.data_type),
286 SubscribeCommand::Instrument(cmd) => self.subscriptions_instrument.contains(&cmd.instrument_id),
287 SubscribeCommand::Instruments(cmd) => self.subscriptions_instrument_venue.contains(&cmd.venue),
288 SubscribeCommand::BookDeltas(cmd) => self.subscriptions_book_deltas.contains(&cmd.instrument_id),
289 SubscribeCommand::BookDepth(cmd) => self.subscriptions_book_depth.contains(&cmd.instrument_id),
290 SubscribeCommand::Quotes(cmd) => self.subscriptions_quotes.contains(&cmd.instrument_id),
291 SubscribeCommand::Trades(cmd) => self.subscriptions_trades.contains(&cmd.instrument_id),
292 SubscribeCommand::Bars(cmd) => self.subscriptions_bars.contains(&cmd.bar_type),
293 SubscribeCommand::MarkPrices(cmd) => self.subscriptions_mark_prices.contains(&cmd.instrument_id),
294 SubscribeCommand::IndexPrices(cmd) => self.subscriptions_index_prices.contains(&cmd.instrument_id),
295 SubscribeCommand::FundingRates(cmd) => self.subscriptions_funding_rates.contains(&cmd.instrument_id),
296 SubscribeCommand::InstrumentStatus(cmd) => self.subscriptions_instrument_status.contains(&cmd.instrument_id),
297 SubscribeCommand::InstrumentClose(cmd) => self.subscriptions_instrument_close.contains(&cmd.instrument_id),
298 SubscribeCommand::OptionGreeks(cmd) => self.subscriptions_option_greeks.contains(&cmd.instrument_id),
299 SubscribeCommand::BookSnapshots(_) | SubscribeCommand::OptionChain(_) => self.subscriptions_active.contains(&SubscriptionKey::from_subscribe(cmd)),
300 }
301 }
302
303 pub(crate) fn clear_subscription_state(&mut self) {
304 self.subscriptions_custom.clear();
305 self.subscriptions_book_deltas.clear();
306 self.subscriptions_book_depth.clear();
307 self.subscriptions_quotes.clear();
308 self.subscriptions_trades.clear();
309 self.subscriptions_bars.clear();
310 self.subscriptions_instrument_status.clear();
311 self.subscriptions_instrument_close.clear();
312 self.subscriptions_instrument.clear();
313 self.subscriptions_instrument_venue.clear();
314 self.subscriptions_mark_prices.clear();
315 self.subscriptions_index_prices.clear();
316 self.subscriptions_funding_rates.clear();
317 self.subscriptions_option_greeks.clear();
318 self.subscriptions_active.clear();
319
320 #[cfg(feature = "defi")]
321 {
322 self.subscriptions_active_defi.clear();
323 self.subscriptions_blocks.clear();
324 self.subscriptions_pools.clear();
325 self.subscriptions_pool_swaps.clear();
326 self.subscriptions_pool_liquidity_updates.clear();
327 self.subscriptions_pool_fee_collects.clear();
328 self.subscriptions_pool_flash.clear();
329 }
330 }
331
332 #[inline]
333 pub fn execute_unsubscribe(&mut self, cmd: &UnsubscribeCommand) {
334 let key = SubscriptionKey::from_unsubscribe(cmd);
335 let command = match self.subscriptions_active.release(&key) {
336 SubscriptionRelease::Retained => return,
337 SubscriptionRelease::Final(subscribe) => {
338 subscribe.into_unsubscribe(cmd.command_id(), cmd.ts_init(), cmd.correlation_id())
339 }
340 SubscriptionRelease::Untracked => cmd.clone(),
341 };
342
343 if let Err(e) = match &command {
344 UnsubscribeCommand::Data(cmd) => self.unsubscribe(cmd),
345 UnsubscribeCommand::Instrument(cmd) => self.unsubscribe_instrument(cmd),
346 UnsubscribeCommand::Instruments(cmd) => self.unsubscribe_instruments(cmd),
347 UnsubscribeCommand::BookDeltas(cmd) => self.unsubscribe_book_deltas(cmd),
348 UnsubscribeCommand::BookDepth(cmd) => self.unsubscribe_book_depth(cmd),
349 UnsubscribeCommand::BookSnapshots(_) => Ok(()), UnsubscribeCommand::Quotes(cmd) => self.unsubscribe_quotes(cmd),
351 UnsubscribeCommand::Trades(cmd) => self.unsubscribe_trades(cmd),
352 UnsubscribeCommand::Bars(cmd) => self.unsubscribe_bars(cmd),
353 UnsubscribeCommand::MarkPrices(cmd) => self.unsubscribe_mark_prices(cmd),
354 UnsubscribeCommand::IndexPrices(cmd) => self.unsubscribe_index_prices(cmd),
355 UnsubscribeCommand::FundingRates(cmd) => self.unsubscribe_funding_rates(cmd),
356 UnsubscribeCommand::InstrumentStatus(cmd) => self.unsubscribe_instrument_status(cmd),
357 UnsubscribeCommand::InstrumentClose(cmd) => self.unsubscribe_instrument_close(cmd),
358 UnsubscribeCommand::OptionGreeks(cmd) => self.unsubscribe_option_greeks(cmd),
359 UnsubscribeCommand::OptionChain(_) => Ok(()), } {
361 log_command_error(
362 &format_args!(
363 "Unsubscribe {} {} command {}",
364 self.client_id(),
365 key.log_context(),
366 cmd.command_id()
367 ),
368 &e,
369 );
370 } else {
371 self.subscriptions_active.remove(&key);
372 }
373 }
374
375 pub fn subscribe(&mut self, cmd: SubscribeCustomData) -> anyhow::Result<()> {
381 let data_type = cmd.data_type.clone();
382 Self::execute_tracked_subscribe(
383 self.client.as_mut(),
384 &mut self.subscriptions_custom,
385 data_type,
386 "",
387 |client| client.subscribe(cmd),
388 )
389 }
390
391 pub fn unsubscribe(&mut self, cmd: &UnsubscribeCustomData) -> anyhow::Result<()> {
397 Self::execute_tracked_unsubscribe(
398 self.client.as_mut(),
399 &mut self.subscriptions_custom,
400 &cmd.data_type,
401 "",
402 |client| client.unsubscribe(cmd),
403 )
404 }
405
406 fn subscribe_instruments(&mut self, cmd: SubscribeInstruments) -> anyhow::Result<()> {
412 Self::execute_tracked_subscribe(
413 self.client.as_mut(),
414 &mut self.subscriptions_instrument_venue,
415 cmd.venue,
416 "instruments",
417 |client| client.subscribe_instruments(cmd),
418 )
419 }
420
421 fn unsubscribe_instruments(&mut self, cmd: &UnsubscribeInstruments) -> anyhow::Result<()> {
427 Self::execute_tracked_unsubscribe(
428 self.client.as_mut(),
429 &mut self.subscriptions_instrument_venue,
430 &cmd.venue,
431 "instruments",
432 |client| client.unsubscribe_instruments(cmd),
433 )
434 }
435
436 fn subscribe_instrument(&mut self, cmd: SubscribeInstrument) -> anyhow::Result<()> {
442 Self::execute_tracked_subscribe(
443 self.client.as_mut(),
444 &mut self.subscriptions_instrument,
445 cmd.instrument_id,
446 "instrument",
447 |client| client.subscribe_instrument(cmd),
448 )
449 }
450
451 fn unsubscribe_instrument(&mut self, cmd: &UnsubscribeInstrument) -> anyhow::Result<()> {
457 Self::execute_tracked_unsubscribe(
458 self.client.as_mut(),
459 &mut self.subscriptions_instrument,
460 &cmd.instrument_id,
461 "instrument",
462 |client| client.unsubscribe_instrument(cmd),
463 )
464 }
465
466 fn subscribe_book_deltas(&mut self, cmd: SubscribeBookDeltas) -> anyhow::Result<()> {
472 Self::execute_tracked_subscribe(
473 self.client.as_mut(),
474 &mut self.subscriptions_book_deltas,
475 cmd.instrument_id,
476 "order book deltas",
477 |client| client.subscribe_book_deltas(cmd),
478 )
479 }
480
481 fn unsubscribe_book_deltas(&mut self, cmd: &UnsubscribeBookDeltas) -> anyhow::Result<()> {
487 Self::execute_tracked_unsubscribe(
488 self.client.as_mut(),
489 &mut self.subscriptions_book_deltas,
490 &cmd.instrument_id,
491 "order book deltas",
492 |client| client.unsubscribe_book_deltas(cmd),
493 )
494 }
495
496 fn subscribe_book_depth(&mut self, cmd: SubscribeBookDepth) -> anyhow::Result<()> {
502 Self::execute_tracked_subscribe(
503 self.client.as_mut(),
504 &mut self.subscriptions_book_depth,
505 cmd.instrument_id,
506 "order book depth",
507 |client| client.subscribe_book_depth(cmd),
508 )
509 }
510
511 fn unsubscribe_book_depth(&mut self, cmd: &UnsubscribeBookDepth) -> anyhow::Result<()> {
517 Self::execute_tracked_unsubscribe(
518 self.client.as_mut(),
519 &mut self.subscriptions_book_depth,
520 &cmd.instrument_id,
521 "order book depth",
522 |client| client.unsubscribe_book_depth(cmd),
523 )
524 }
525
526 fn subscribe_quotes(&mut self, cmd: SubscribeQuotes) -> anyhow::Result<()> {
532 Self::execute_tracked_subscribe(
533 self.client.as_mut(),
534 &mut self.subscriptions_quotes,
535 cmd.instrument_id,
536 "quotes",
537 |client| client.subscribe_quotes(cmd),
538 )
539 }
540
541 fn unsubscribe_quotes(&mut self, cmd: &UnsubscribeQuotes) -> anyhow::Result<()> {
547 Self::execute_tracked_unsubscribe(
548 self.client.as_mut(),
549 &mut self.subscriptions_quotes,
550 &cmd.instrument_id,
551 "quotes",
552 |client| client.unsubscribe_quotes(cmd),
553 )
554 }
555
556 fn subscribe_trades(&mut self, cmd: SubscribeTrades) -> anyhow::Result<()> {
562 Self::execute_tracked_subscribe(
563 self.client.as_mut(),
564 &mut self.subscriptions_trades,
565 cmd.instrument_id,
566 "trades",
567 |client| client.subscribe_trades(cmd),
568 )
569 }
570
571 fn unsubscribe_trades(&mut self, cmd: &UnsubscribeTrades) -> anyhow::Result<()> {
577 Self::execute_tracked_unsubscribe(
578 self.client.as_mut(),
579 &mut self.subscriptions_trades,
580 &cmd.instrument_id,
581 "trades",
582 |client| client.unsubscribe_trades(cmd),
583 )
584 }
585
586 fn subscribe_bars(&mut self, cmd: SubscribeBars) -> anyhow::Result<()> {
592 Self::execute_tracked_subscribe(
593 self.client.as_mut(),
594 &mut self.subscriptions_bars,
595 cmd.bar_type,
596 "bars",
597 |client| client.subscribe_bars(cmd),
598 )
599 }
600
601 fn unsubscribe_bars(&mut self, cmd: &UnsubscribeBars) -> anyhow::Result<()> {
607 Self::execute_tracked_unsubscribe(
608 self.client.as_mut(),
609 &mut self.subscriptions_bars,
610 &cmd.bar_type,
611 "bars",
612 |client| client.unsubscribe_bars(cmd),
613 )
614 }
615
616 fn subscribe_mark_prices(&mut self, cmd: SubscribeMarkPrices) -> anyhow::Result<()> {
622 Self::execute_tracked_subscribe(
623 self.client.as_mut(),
624 &mut self.subscriptions_mark_prices,
625 cmd.instrument_id,
626 "mark prices",
627 |client| client.subscribe_mark_prices(cmd),
628 )
629 }
630
631 fn unsubscribe_mark_prices(&mut self, cmd: &UnsubscribeMarkPrices) -> anyhow::Result<()> {
637 Self::execute_tracked_unsubscribe(
638 self.client.as_mut(),
639 &mut self.subscriptions_mark_prices,
640 &cmd.instrument_id,
641 "mark prices",
642 |client| client.unsubscribe_mark_prices(cmd),
643 )
644 }
645
646 fn subscribe_index_prices(&mut self, cmd: SubscribeIndexPrices) -> anyhow::Result<()> {
652 Self::execute_tracked_subscribe(
653 self.client.as_mut(),
654 &mut self.subscriptions_index_prices,
655 cmd.instrument_id,
656 "index prices",
657 |client| client.subscribe_index_prices(cmd),
658 )
659 }
660
661 fn unsubscribe_index_prices(&mut self, cmd: &UnsubscribeIndexPrices) -> anyhow::Result<()> {
667 Self::execute_tracked_unsubscribe(
668 self.client.as_mut(),
669 &mut self.subscriptions_index_prices,
670 &cmd.instrument_id,
671 "index prices",
672 |client| client.unsubscribe_index_prices(cmd),
673 )
674 }
675
676 fn subscribe_funding_rates(&mut self, cmd: SubscribeFundingRates) -> anyhow::Result<()> {
682 Self::execute_tracked_subscribe(
683 self.client.as_mut(),
684 &mut self.subscriptions_funding_rates,
685 cmd.instrument_id,
686 "funding rates",
687 |client| client.subscribe_funding_rates(cmd),
688 )
689 }
690
691 fn unsubscribe_funding_rates(&mut self, cmd: &UnsubscribeFundingRates) -> anyhow::Result<()> {
697 Self::execute_tracked_unsubscribe(
698 self.client.as_mut(),
699 &mut self.subscriptions_funding_rates,
700 &cmd.instrument_id,
701 "funding rates",
702 |client| client.unsubscribe_funding_rates(cmd),
703 )
704 }
705
706 fn subscribe_instrument_status(
712 &mut self,
713 cmd: SubscribeInstrumentStatus,
714 ) -> anyhow::Result<()> {
715 Self::execute_tracked_subscribe(
716 self.client.as_mut(),
717 &mut self.subscriptions_instrument_status,
718 cmd.instrument_id,
719 "instrument status",
720 |client| client.subscribe_instrument_status(cmd),
721 )
722 }
723
724 fn unsubscribe_instrument_status(
730 &mut self,
731 cmd: &UnsubscribeInstrumentStatus,
732 ) -> anyhow::Result<()> {
733 Self::execute_tracked_unsubscribe(
734 self.client.as_mut(),
735 &mut self.subscriptions_instrument_status,
736 &cmd.instrument_id,
737 "instrument status",
738 |client| client.unsubscribe_instrument_status(cmd),
739 )
740 }
741
742 fn subscribe_instrument_close(&mut self, cmd: SubscribeInstrumentClose) -> anyhow::Result<()> {
748 Self::execute_tracked_subscribe(
749 self.client.as_mut(),
750 &mut self.subscriptions_instrument_close,
751 cmd.instrument_id,
752 "instrument close",
753 |client| client.subscribe_instrument_close(cmd),
754 )
755 }
756
757 fn unsubscribe_instrument_close(
763 &mut self,
764 cmd: &UnsubscribeInstrumentClose,
765 ) -> anyhow::Result<()> {
766 Self::execute_tracked_unsubscribe(
767 self.client.as_mut(),
768 &mut self.subscriptions_instrument_close,
769 &cmd.instrument_id,
770 "instrument close",
771 |client| client.unsubscribe_instrument_close(cmd),
772 )
773 }
774
775 fn subscribe_option_greeks(&mut self, cmd: SubscribeOptionGreeks) -> anyhow::Result<()> {
781 Self::execute_tracked_subscribe(
782 self.client.as_mut(),
783 &mut self.subscriptions_option_greeks,
784 cmd.instrument_id,
785 "option greeks",
786 |client| client.subscribe_option_greeks(cmd),
787 )
788 }
789
790 fn unsubscribe_option_greeks(&mut self, cmd: &UnsubscribeOptionGreeks) -> anyhow::Result<()> {
796 Self::execute_tracked_unsubscribe(
797 self.client.as_mut(),
798 &mut self.subscriptions_option_greeks,
799 &cmd.instrument_id,
800 "option greeks",
801 |client| client.unsubscribe_option_greeks(cmd),
802 )
803 }
804
805 pub(crate) fn execute_tracked_subscribe<T>(
806 client: &mut dyn DataClient,
807 set: &mut AHashSet<T>,
808 key: T,
809 data_type: &str,
810 subscribe: impl FnOnce(&mut dyn DataClient) -> anyhow::Result<()>,
811 ) -> anyhow::Result<()>
812 where
813 T: Eq + Hash + Display,
814 {
815 if set.contains(&key) {
816 return Ok(());
817 }
818
819 subscribe(client)?;
820
821 if data_type.is_empty() {
822 log_info!("Subscribed {key}", color = LogColor::Blue);
823 } else {
824 log_info!("Subscribed {key} {data_type}", color = LogColor::Blue);
825 }
826 set.insert(key);
827 Ok(())
828 }
829
830 pub(crate) fn execute_tracked_unsubscribe<T>(
831 client: &mut dyn DataClient,
832 set: &mut AHashSet<T>,
833 key: &T,
834 data_type: &str,
835 unsubscribe: impl FnOnce(&mut dyn DataClient) -> anyhow::Result<()>,
836 ) -> anyhow::Result<()>
837 where
838 T: Eq + Hash + Display,
839 {
840 if !set.contains(key) {
841 return Ok(());
842 }
843
844 unsubscribe(client)?;
845 set.remove(key);
846 if data_type.is_empty() {
847 log_info!("Unsubscribed {key}", color = LogColor::Blue);
848 } else {
849 log_info!("Unsubscribed {key} {data_type}", color = LogColor::Blue);
850 }
851 Ok(())
852 }
853
854 pub fn request_data(&self, req: RequestCustomData) -> anyhow::Result<()> {
860 self.client.request_data(req)
861 }
862
863 pub fn request_instrument(&self, req: RequestInstrument) -> anyhow::Result<()> {
869 self.client.request_instrument(req)
870 }
871
872 pub fn request_instruments(&self, req: RequestInstruments) -> anyhow::Result<()> {
878 self.client.request_instruments(req)
879 }
880
881 pub fn request_book_snapshot(&self, req: RequestBookSnapshot) -> anyhow::Result<()> {
887 self.client.request_book_snapshot(req)
888 }
889
890 pub fn request_quotes(&self, req: RequestQuotes) -> anyhow::Result<()> {
896 self.client.request_quotes(req)
897 }
898
899 pub fn request_trades(&self, req: RequestTrades) -> anyhow::Result<()> {
905 self.client.request_trades(req)
906 }
907
908 pub fn request_funding_rates(&self, req: RequestFundingRates) -> anyhow::Result<()> {
914 self.client.request_funding_rates(req)
915 }
916
917 pub fn request_option_chain_reference_price(
923 &self,
924 req: RequestOptionChainReferencePrice,
925 ) -> anyhow::Result<()> {
926 self.client.request_option_chain_reference_price(req)
927 }
928
929 pub fn request_bars(&self, req: RequestBars) -> anyhow::Result<()> {
935 self.client.request_bars(req)
936 }
937
938 pub fn request_book_depth(&self, req: RequestBookDepth) -> anyhow::Result<()> {
944 self.client.request_book_depth(req)
945 }
946}