barter_data_sniper/streams/builder/dynamic.rs
1use crate::{
2 error::DataError,
3 exchange::{
4 binance::{futures::BinanceFuturesUsd, market::BinanceMarket, spot::BinanceSpot},
5 bitfinex::{market::BitfinexMarket, Bitfinex},
6 bitmex::{market::BitmexMarket, Bitmex},
7 bybit::{futures::BybitPerpetualsUsd, market::BybitMarket, spot::BybitSpot},
8 coinbase::{market::CoinbaseMarket, Coinbase},
9 gateio::{
10 future::{GateioFuturesBtc, GateioFuturesUsd},
11 market::GateioMarket,
12 option::GateioOptions,
13 perpetual::{GateioPerpetualsBtc, GateioPerpetualsUsd},
14 spot::GateioSpot,
15 },
16 kraken::{market::KrakenMarket, Kraken},
17 okx::{market::OkxMarket, Okx},
18 },
19 instrument::InstrumentData,
20 streams::{
21 consumer::{init_market_stream, MarketStreamResult, STREAM_RECONNECTION_POLICY},
22 reconnect::stream::ReconnectingStream,
23 },
24 subscription::{
25 book::{OrderBookEvent, OrderBookL1, OrderBooksL1},
26 liquidation::{Liquidation, Liquidations},
27 trade::{PublicTrade, PublicTrades},
28 SubKind, Subscription,
29 },
30 Identifier,
31};
32use barter_instrument_copy::exchange::ExchangeId;
33use barter_integration_copy::{
34 channel::{mpsc_unbounded, UnboundedRx, UnboundedTx},
35 error::SocketError,
36 Validator,
37};
38use fnv::FnvHashMap;
39use futures::{
40 stream::{select_all, SelectAll},
41 Stream,
42};
43use futures_util::{future::try_join_all, StreamExt};
44use itertools::Itertools;
45use std::sync::Arc;
46use tokio_stream::wrappers::UnboundedReceiverStream;
47use vecmap::VecMap;
48
49#[derive(Debug)]
50pub struct DynamicStreams<InstrumentKey> {
51 pub trades:
52 VecMap<ExchangeId, UnboundedReceiverStream<MarketStreamResult<InstrumentKey, PublicTrade>>>,
53 pub l1s:
54 VecMap<ExchangeId, UnboundedReceiverStream<MarketStreamResult<InstrumentKey, OrderBookL1>>>,
55 pub l2s: VecMap<
56 ExchangeId,
57 UnboundedReceiverStream<MarketStreamResult<InstrumentKey, OrderBookEvent>>,
58 >,
59 pub liquidations:
60 VecMap<ExchangeId, UnboundedReceiverStream<MarketStreamResult<InstrumentKey, Liquidation>>>,
61}
62
63impl<InstrumentKey> DynamicStreams<InstrumentKey> {
64 /// Initialise a set of `Streams` by providing one or more [`Subscription`] batches.
65 ///
66 /// Each batch (ie/ `impl Iterator<Item = Subscription>`) will initialise at-least-one
67 /// WebSocket `Stream` under the hood. If the batch contains more-than-one [`ExchangeId`] and/or
68 /// [`SubKind`], it will be further split under the hood for compile-time reasons.
69 ///
70 /// ## Examples
71 /// Please see barter-data-rs/examples/dynamic_multi_stream_multi_exchange.rs for a
72 /// comprehensive example of how to use this market data stream initialiser.
73 pub async fn init<SubBatchIter, SubIter, Sub, Instrument>(
74 subscription_batches: SubBatchIter,
75 ) -> Result<Self, DataError>
76 where
77 SubBatchIter: IntoIterator<Item = SubIter>,
78 SubIter: IntoIterator<Item = Sub>,
79 Sub: Into<Subscription<ExchangeId, Instrument, SubKind>>,
80 Instrument: InstrumentData<Key = InstrumentKey> + Ord + 'static,
81 InstrumentKey: Clone + Send + 'static,
82 Subscription<BinanceSpot, Instrument, PublicTrades>: Identifier<BinanceMarket>,
83 Subscription<BinanceSpot, Instrument, PublicTrades>: Identifier<BinanceMarket>,
84 Subscription<BinanceSpot, Instrument, OrderBooksL1>: Identifier<BinanceMarket>,
85 Subscription<BinanceFuturesUsd, Instrument, PublicTrades>: Identifier<BinanceMarket>,
86 Subscription<BinanceFuturesUsd, Instrument, OrderBooksL1>: Identifier<BinanceMarket>,
87 Subscription<BinanceFuturesUsd, Instrument, Liquidations>: Identifier<BinanceMarket>,
88 Subscription<Bitfinex, Instrument, PublicTrades>: Identifier<BitfinexMarket>,
89 Subscription<Bitmex, Instrument, PublicTrades>: Identifier<BitmexMarket>,
90 Subscription<BybitSpot, Instrument, PublicTrades>: Identifier<BybitMarket>,
91 Subscription<BybitPerpetualsUsd, Instrument, PublicTrades>: Identifier<BybitMarket>,
92 Subscription<Coinbase, Instrument, PublicTrades>: Identifier<CoinbaseMarket>,
93 Subscription<GateioSpot, Instrument, PublicTrades>: Identifier<GateioMarket>,
94 Subscription<GateioFuturesUsd, Instrument, PublicTrades>: Identifier<GateioMarket>,
95 Subscription<GateioFuturesBtc, Instrument, PublicTrades>: Identifier<GateioMarket>,
96 Subscription<GateioPerpetualsUsd, Instrument, PublicTrades>: Identifier<GateioMarket>,
97 Subscription<GateioPerpetualsBtc, Instrument, PublicTrades>: Identifier<GateioMarket>,
98 Subscription<GateioOptions, Instrument, PublicTrades>: Identifier<GateioMarket>,
99 Subscription<Kraken, Instrument, PublicTrades>: Identifier<KrakenMarket>,
100 Subscription<Kraken, Instrument, OrderBooksL1>: Identifier<KrakenMarket>,
101 Subscription<Okx, Instrument, PublicTrades>: Identifier<OkxMarket>,
102 {
103 // Validate & dedup Subscription batches
104 let batches = validate_batches(subscription_batches)?;
105
106 // Generate required Channels from Subscription batches
107 let channels = Channels::try_from(&batches)?;
108
109 let futures =
110 batches.into_iter().map(|mut batch| {
111 batch.sort_unstable_by_key(|sub| (sub.exchange, sub.kind));
112 let by_exchange_by_sub_kind =
113 batch.into_iter().chunk_by(|sub| (sub.exchange, sub.kind));
114
115 let batch_futures =
116 by_exchange_by_sub_kind
117 .into_iter()
118 .map(|((exchange, sub_kind), subs)| {
119 let subs = subs.into_iter().collect::<Vec<_>>();
120 let txs = Arc::clone(&channels.txs);
121 async move {
122 match (exchange, sub_kind) {
123 (ExchangeId::BinanceSpot, SubKind::PublicTrades) => {
124 init_market_stream(
125 STREAM_RECONNECTION_POLICY,
126 subs.into_iter()
127 .map(|sub| {
128 Subscription::new(
129 BinanceSpot::default(),
130 sub.instrument,
131 PublicTrades,
132 )
133 })
134 .collect(),
135 )
136 .await
137 .map(|stream| {
138 tokio::spawn(stream.boxed().forward_to(
139 txs.trades.get(&exchange).unwrap().clone(),
140 ))
141 })
142 }
143 (ExchangeId::BinanceSpot, SubKind::OrderBooksL1) => {
144 init_market_stream(
145 STREAM_RECONNECTION_POLICY,
146 subs.into_iter()
147 .map(|sub| {
148 Subscription::new(
149 BinanceSpot::default(),
150 sub.instrument,
151 OrderBooksL1,
152 )
153 })
154 .collect(),
155 )
156 .await
157 .map(|stream| {
158 tokio::spawn(stream.boxed().forward_to(
159 txs.l1s.get(&exchange).unwrap().clone(),
160 ))
161 })
162 }
163 (ExchangeId::BinanceFuturesUsd, SubKind::PublicTrades) => {
164 init_market_stream(
165 STREAM_RECONNECTION_POLICY,
166 subs.into_iter()
167 .map(|sub| {
168 Subscription::new(
169 BinanceFuturesUsd::default(),
170 sub.instrument,
171 PublicTrades,
172 )
173 })
174 .collect(),
175 )
176 .await
177 .map(|stream| {
178 tokio::spawn(stream.boxed().forward_to(
179 txs.trades.get(&exchange).unwrap().clone(),
180 ))
181 })
182 }
183 (ExchangeId::BinanceFuturesUsd, SubKind::OrderBooksL1) => {
184 init_market_stream(
185 STREAM_RECONNECTION_POLICY,
186 subs.into_iter()
187 .map(|sub| {
188 Subscription::<_, Instrument, _>::new(
189 BinanceFuturesUsd::default(),
190 sub.instrument,
191 OrderBooksL1,
192 )
193 })
194 .collect(),
195 )
196 .await
197 .map(|stream| {
198 tokio::spawn(stream.boxed().forward_to(
199 txs.l1s.get(&exchange).unwrap().clone(),
200 ))
201 })
202 }
203 (ExchangeId::BinanceFuturesUsd, SubKind::Liquidations) => {
204 init_market_stream(
205 STREAM_RECONNECTION_POLICY,
206 subs.into_iter()
207 .map(|sub| {
208 Subscription::<_, Instrument, _>::new(
209 BinanceFuturesUsd::default(),
210 sub.instrument,
211 Liquidations,
212 )
213 })
214 .collect(),
215 )
216 .await
217 .map(|stream| {
218 tokio::spawn(stream.boxed().forward_to(
219 txs.liquidations.get(&exchange).unwrap().clone(),
220 ))
221 })
222 }
223 (ExchangeId::Bitfinex, SubKind::PublicTrades) => {
224 init_market_stream(
225 STREAM_RECONNECTION_POLICY,
226 subs.into_iter()
227 .map(|sub| {
228 Subscription::new(
229 Bitfinex,
230 sub.instrument,
231 PublicTrades,
232 )
233 })
234 .collect(),
235 )
236 .await
237 .map(|stream| {
238 tokio::spawn(stream.boxed().forward_to(
239 txs.trades.get(&exchange).unwrap().clone(),
240 ))
241 })
242 }
243 (ExchangeId::Bitmex, SubKind::PublicTrades) => {
244 init_market_stream(
245 STREAM_RECONNECTION_POLICY,
246 subs.into_iter()
247 .map(|sub| {
248 Subscription::new(
249 Bitmex,
250 sub.instrument,
251 PublicTrades,
252 )
253 })
254 .collect(),
255 )
256 .await
257 .map(|stream| {
258 tokio::spawn(stream.boxed().forward_to(
259 txs.trades.get(&exchange).unwrap().clone(),
260 ))
261 })
262 }
263 (ExchangeId::BybitSpot, SubKind::PublicTrades) => {
264 init_market_stream(
265 STREAM_RECONNECTION_POLICY,
266 subs.into_iter()
267 .map(|sub| {
268 Subscription::new(
269 BybitSpot::default(),
270 sub.instrument,
271 PublicTrades,
272 )
273 })
274 .collect(),
275 )
276 .await
277 .map(|stream| {
278 tokio::spawn(stream.boxed().forward_to(
279 txs.trades.get(&exchange).unwrap().clone(),
280 ))
281 })
282 }
283 (ExchangeId::BybitPerpetualsUsd, SubKind::PublicTrades) => {
284 init_market_stream(
285 STREAM_RECONNECTION_POLICY,
286 subs.into_iter()
287 .map(|sub| {
288 Subscription::new(
289 BybitPerpetualsUsd::default(),
290 sub.instrument,
291 PublicTrades,
292 )
293 })
294 .collect(),
295 )
296 .await
297 .map(|stream| {
298 tokio::spawn(stream.boxed().forward_to(
299 txs.trades.get(&exchange).unwrap().clone(),
300 ))
301 })
302 }
303 (ExchangeId::Coinbase, SubKind::PublicTrades) => {
304 init_market_stream(
305 STREAM_RECONNECTION_POLICY,
306 subs.into_iter()
307 .map(|sub| {
308 Subscription::new(
309 Coinbase,
310 sub.instrument,
311 PublicTrades,
312 )
313 })
314 .collect(),
315 )
316 .await
317 .map(|stream| {
318 tokio::spawn(stream.boxed().forward_to(
319 txs.trades.get(&exchange).unwrap().clone(),
320 ))
321 })
322 }
323 (ExchangeId::GateioSpot, SubKind::PublicTrades) => {
324 init_market_stream(
325 STREAM_RECONNECTION_POLICY,
326 subs.into_iter()
327 .map(|sub| {
328 Subscription::new(
329 GateioSpot::default(),
330 sub.instrument,
331 PublicTrades,
332 )
333 })
334 .collect(),
335 )
336 .await
337 .map(|stream| {
338 tokio::spawn(stream.boxed().forward_to(
339 txs.trades.get(&exchange).unwrap().clone(),
340 ))
341 })
342 }
343 (ExchangeId::GateioFuturesUsd, SubKind::PublicTrades) => {
344 init_market_stream(
345 STREAM_RECONNECTION_POLICY,
346 subs.into_iter()
347 .map(|sub| {
348 Subscription::new(
349 GateioFuturesUsd::default(),
350 sub.instrument,
351 PublicTrades,
352 )
353 })
354 .collect(),
355 )
356 .await
357 .map(|stream| {
358 tokio::spawn(stream.boxed().forward_to(
359 txs.trades.get(&exchange).unwrap().clone(),
360 ))
361 })
362 }
363 (ExchangeId::GateioFuturesBtc, SubKind::PublicTrades) => {
364 init_market_stream(
365 STREAM_RECONNECTION_POLICY,
366 subs.into_iter()
367 .map(|sub| {
368 Subscription::new(
369 GateioFuturesBtc::default(),
370 sub.instrument,
371 PublicTrades,
372 )
373 })
374 .collect(),
375 )
376 .await
377 .map(|stream| {
378 tokio::spawn(stream.boxed().forward_to(
379 txs.trades.get(&exchange).unwrap().clone(),
380 ))
381 })
382 }
383 (ExchangeId::GateioPerpetualsUsd, SubKind::PublicTrades) => {
384 init_market_stream(
385 STREAM_RECONNECTION_POLICY,
386 subs.into_iter()
387 .map(|sub| {
388 Subscription::new(
389 GateioPerpetualsUsd::default(),
390 sub.instrument,
391 PublicTrades,
392 )
393 })
394 .collect(),
395 )
396 .await
397 .map(|stream| {
398 tokio::spawn(stream.boxed().forward_to(
399 txs.trades.get(&exchange).unwrap().clone(),
400 ))
401 })
402 }
403 (ExchangeId::GateioPerpetualsBtc, SubKind::PublicTrades) => {
404 init_market_stream(
405 STREAM_RECONNECTION_POLICY,
406 subs.into_iter()
407 .map(|sub| {
408 Subscription::new(
409 GateioPerpetualsBtc::default(),
410 sub.instrument,
411 PublicTrades,
412 )
413 })
414 .collect(),
415 )
416 .await
417 .map(|stream| {
418 tokio::spawn(stream.boxed().forward_to(
419 txs.trades.get(&exchange).unwrap().clone(),
420 ))
421 })
422 }
423 (ExchangeId::GateioOptions, SubKind::PublicTrades) => {
424 init_market_stream(
425 STREAM_RECONNECTION_POLICY,
426 subs.into_iter()
427 .map(|sub| {
428 Subscription::new(
429 GateioOptions::default(),
430 sub.instrument,
431 PublicTrades,
432 )
433 })
434 .collect(),
435 )
436 .await
437 .map(|stream| {
438 tokio::spawn(stream.boxed().forward_to(
439 txs.trades.get(&exchange).unwrap().clone(),
440 ))
441 })
442 }
443 (ExchangeId::Kraken, SubKind::PublicTrades) => {
444 init_market_stream(
445 STREAM_RECONNECTION_POLICY,
446 subs.into_iter()
447 .map(|sub| {
448 Subscription::new(
449 Kraken,
450 sub.instrument,
451 PublicTrades,
452 )
453 })
454 .collect(),
455 )
456 .await
457 .map(|stream| {
458 tokio::spawn(stream.boxed().forward_to(
459 txs.trades.get(&exchange).unwrap().clone(),
460 ))
461 })
462 }
463 (ExchangeId::Kraken, SubKind::OrderBooksL1) => {
464 init_market_stream(
465 STREAM_RECONNECTION_POLICY,
466 subs.into_iter()
467 .map(|sub| {
468 Subscription::new(
469 Kraken,
470 sub.instrument,
471 OrderBooksL1,
472 )
473 })
474 .collect(),
475 )
476 .await
477 .map(|stream| {
478 tokio::spawn(stream.boxed().forward_to(
479 txs.l1s.get(&exchange).unwrap().clone(),
480 ))
481 })
482 }
483 (ExchangeId::Okx, SubKind::PublicTrades) => init_market_stream(
484 STREAM_RECONNECTION_POLICY,
485 subs.into_iter()
486 .map(|sub| {
487 Subscription::new(Okx, sub.instrument, PublicTrades)
488 })
489 .collect(),
490 )
491 .await
492 .map(|stream| {
493 tokio::spawn(
494 stream.boxed().forward_to(
495 txs.trades.get(&exchange).unwrap().clone(),
496 ),
497 )
498 }),
499 (exchange, sub_kind) => {
500 Err(DataError::Unsupported { exchange, sub_kind })
501 }
502 }
503 }
504 });
505
506 try_join_all(batch_futures)
507 });
508
509 try_join_all(futures).await?;
510
511 Ok(Self {
512 trades: channels
513 .rxs
514 .trades
515 .into_iter()
516 .map(|(exchange, rx)| (exchange, rx.into_stream()))
517 .collect(),
518 l1s: channels
519 .rxs
520 .l1s
521 .into_iter()
522 .map(|(exchange, rx)| (exchange, rx.into_stream()))
523 .collect(),
524 l2s: channels
525 .rxs
526 .l2s
527 .into_iter()
528 .map(|(exchange, rx)| (exchange, rx.into_stream()))
529 .collect(),
530 liquidations: channels
531 .rxs
532 .liquidations
533 .into_iter()
534 .map(|(exchange, rx)| (exchange, rx.into_stream()))
535 .collect(),
536 })
537 }
538
539 /// Remove an exchange [`PublicTrade`] `Stream` from the [`DynamicStreams`] collection.
540 ///
541 /// Note that calling this method will permanently remove this `Stream` from [`Self`].
542 pub fn select_trades(
543 &mut self,
544 exchange: ExchangeId,
545 ) -> Option<UnboundedReceiverStream<MarketStreamResult<InstrumentKey, PublicTrade>>> {
546 self.trades.remove(&exchange)
547 }
548
549 /// Select and merge every exchange [`PublicTrade`] `Stream` using
550 /// [`SelectAll`](futures_util::stream::select_all).
551 pub fn select_all_trades(
552 &mut self,
553 ) -> SelectAll<UnboundedReceiverStream<MarketStreamResult<InstrumentKey, PublicTrade>>> {
554 select_all(std::mem::take(&mut self.trades).into_values())
555 }
556
557 /// Remove an exchange [`OrderBookL1`] `Stream` from the [`DynamicStreams`] collection.
558 ///
559 /// Note that calling this method will permanently remove this `Stream` from [`Self`].
560 pub fn select_l1s(
561 &mut self,
562 exchange: ExchangeId,
563 ) -> Option<UnboundedReceiverStream<MarketStreamResult<InstrumentKey, OrderBookL1>>> {
564 self.l1s.remove(&exchange)
565 }
566
567 /// Select and merge every exchange [`OrderBookL1`] `Stream` using
568 /// [`SelectAll`](futures_util::stream::select_all).
569 pub fn select_all_l1s(
570 &mut self,
571 ) -> SelectAll<UnboundedReceiverStream<MarketStreamResult<InstrumentKey, OrderBookL1>>> {
572 select_all(std::mem::take(&mut self.l1s).into_values())
573 }
574
575 /// Remove an exchange [`OrderBook`] `Stream` from the [`DynamicStreams`] collection.
576 ///
577 /// Note that calling this method will permanently remove this `Stream` from [`Self`].
578 pub fn select_l2s(
579 &mut self,
580 exchange: ExchangeId,
581 ) -> Option<UnboundedReceiverStream<MarketStreamResult<InstrumentKey, OrderBookEvent>>> {
582 self.l2s.remove(&exchange)
583 }
584
585 /// Select and merge every exchange [`OrderBook`] `Stream` using
586 /// [`SelectAll`](futures_util::stream::select_all).
587 pub fn select_all_l2s(
588 &mut self,
589 ) -> SelectAll<UnboundedReceiverStream<MarketStreamResult<InstrumentKey, OrderBookEvent>>> {
590 select_all(std::mem::take(&mut self.l2s).into_values())
591 }
592
593 /// Remove an exchange [`Liquidation`] `Stream` from the [`DynamicStreams`] collection.
594 ///
595 /// Note that calling this method will permanently remove this `Stream` from [`Self`].
596 pub fn select_liquidations(
597 &mut self,
598 exchange: ExchangeId,
599 ) -> Option<UnboundedReceiverStream<MarketStreamResult<InstrumentKey, Liquidation>>> {
600 self.liquidations.remove(&exchange)
601 }
602
603 /// Select and merge every exchange [`Liquidation`] `Stream` using
604 /// [`SelectAll`](futures_util::stream::select_all).
605 pub fn select_all_liquidations(
606 &mut self,
607 ) -> SelectAll<UnboundedReceiverStream<MarketStreamResult<InstrumentKey, Liquidation>>> {
608 select_all(std::mem::take(&mut self.liquidations).into_values())
609 }
610
611 /// Select and merge every exchange `Stream` for every data type using [`select_all`]
612 ///
613 /// Note that using [`MarketEvent<Instrument, DataKind>`] as the `Output` is suitable for most
614 /// use cases.
615 pub fn select_all<Output>(self) -> impl Stream<Item = Output>
616 where
617 InstrumentKey: Send + 'static,
618 Output: 'static,
619 MarketStreamResult<InstrumentKey, PublicTrade>: Into<Output>,
620 MarketStreamResult<InstrumentKey, OrderBookL1>: Into<Output>,
621 MarketStreamResult<InstrumentKey, OrderBookEvent>: Into<Output>,
622 MarketStreamResult<InstrumentKey, Liquidation>: Into<Output>,
623 {
624 let Self {
625 trades,
626 l1s,
627 l2s,
628 liquidations,
629 } = self;
630
631 let trades = trades
632 .into_values()
633 .map(|stream| stream.map(MarketStreamResult::into).boxed());
634
635 let l1s = l1s
636 .into_values()
637 .map(|stream| stream.map(MarketStreamResult::into).boxed());
638
639 let l2s = l2s
640 .into_values()
641 .map(|stream| stream.map(MarketStreamResult::into).boxed());
642
643 let liquidations = liquidations
644 .into_values()
645 .map(|stream| stream.map(MarketStreamResult::into).boxed());
646
647 let all = trades.chain(l1s).chain(l2s).chain(liquidations);
648
649 select_all(all)
650 }
651}
652
653pub fn validate_batches<SubBatchIter, SubIter, Sub, Instrument>(
654 batches: SubBatchIter,
655) -> Result<Vec<Vec<Subscription<ExchangeId, Instrument, SubKind>>>, DataError>
656where
657 SubBatchIter: IntoIterator<Item = SubIter>,
658 SubIter: IntoIterator<Item = Sub>,
659 Sub: Into<Subscription<ExchangeId, Instrument, SubKind>>,
660 Instrument: InstrumentData + Ord,
661{
662 batches
663 .into_iter()
664 .map(validate_subscriptions::<SubIter, Sub, Instrument>)
665 .collect()
666}
667
668pub fn validate_subscriptions<SubIter, Sub, Instrument>(
669 batch: SubIter,
670) -> Result<Vec<Subscription<ExchangeId, Instrument, SubKind>>, DataError>
671where
672 SubIter: IntoIterator<Item = Sub>,
673 Sub: Into<Subscription<ExchangeId, Instrument, SubKind>>,
674 Instrument: InstrumentData + Ord,
675{
676 // Validate Subscriptions
677 let mut batch = batch
678 .into_iter()
679 .map(Sub::into)
680 .map(Validator::validate)
681 .collect::<Result<Vec<_>, SocketError>>()?;
682
683 // Remove duplicate Subscriptions
684 batch.sort();
685 batch.dedup();
686
687 Ok(batch)
688}
689
690struct Channels<InstrumentKey> {
691 txs: Arc<Txs<InstrumentKey>>,
692 rxs: Rxs<InstrumentKey>,
693}
694
695impl<'a, Instrument> TryFrom<&'a Vec<Vec<Subscription<ExchangeId, Instrument, SubKind>>>>
696 for Channels<Instrument::Key>
697where
698 Instrument: InstrumentData,
699{
700 type Error = DataError;
701
702 fn try_from(
703 value: &'a Vec<Vec<Subscription<ExchangeId, Instrument, SubKind>>>,
704 ) -> Result<Self, Self::Error> {
705 let mut txs = Txs::default();
706 let mut rxs = Rxs::default();
707
708 for sub in value.iter().flatten() {
709 match sub.kind {
710 SubKind::PublicTrades => {
711 if let (None, None) =
712 (txs.trades.get(&sub.exchange), rxs.trades.get(&sub.exchange))
713 {
714 let (tx, rx) = mpsc_unbounded();
715 txs.trades.insert(sub.exchange, tx);
716 rxs.trades.insert(sub.exchange, rx);
717 }
718 }
719 SubKind::OrderBooksL1 => {
720 if let (None, None) = (txs.l1s.get(&sub.exchange), rxs.l1s.get(&sub.exchange)) {
721 let (tx, rx) = mpsc_unbounded();
722 txs.l1s.insert(sub.exchange, tx);
723 rxs.l1s.insert(sub.exchange, rx);
724 }
725 }
726 SubKind::OrderBooksL2 => {
727 if let (None, None) =
728 (txs.l2s.get(&sub.exchange), rxs.trades.get(&sub.exchange))
729 {
730 let (tx, rx) = mpsc_unbounded();
731 txs.l2s.insert(sub.exchange, tx);
732 rxs.l2s.insert(sub.exchange, rx);
733 }
734 }
735 SubKind::Liquidations => {
736 if let (None, None) = (
737 txs.liquidations.get(&sub.exchange),
738 rxs.liquidations.get(&sub.exchange),
739 ) {
740 let (tx, rx) = mpsc_unbounded();
741 txs.liquidations.insert(sub.exchange, tx);
742 rxs.liquidations.insert(sub.exchange, rx);
743 }
744 }
745 unsupported => return Err(DataError::UnsupportedSubKind(unsupported)),
746 }
747 }
748
749 Ok(Channels {
750 txs: Arc::new(txs),
751 rxs,
752 })
753 }
754}
755
756struct Txs<InstrumentKey> {
757 trades: FnvHashMap<ExchangeId, UnboundedTx<MarketStreamResult<InstrumentKey, PublicTrade>>>,
758 l1s: FnvHashMap<ExchangeId, UnboundedTx<MarketStreamResult<InstrumentKey, OrderBookL1>>>,
759 l2s: FnvHashMap<ExchangeId, UnboundedTx<MarketStreamResult<InstrumentKey, OrderBookEvent>>>,
760 liquidations:
761 FnvHashMap<ExchangeId, UnboundedTx<MarketStreamResult<InstrumentKey, Liquidation>>>,
762}
763
764impl<InstrumentKey> Default for Txs<InstrumentKey> {
765 fn default() -> Self {
766 Self {
767 trades: Default::default(),
768 l1s: Default::default(),
769 l2s: Default::default(),
770 liquidations: Default::default(),
771 }
772 }
773}
774
775struct Rxs<InstrumentKey> {
776 trades: FnvHashMap<ExchangeId, UnboundedRx<MarketStreamResult<InstrumentKey, PublicTrade>>>,
777 l1s: FnvHashMap<ExchangeId, UnboundedRx<MarketStreamResult<InstrumentKey, OrderBookL1>>>,
778 l2s: FnvHashMap<ExchangeId, UnboundedRx<MarketStreamResult<InstrumentKey, OrderBookEvent>>>,
779 liquidations:
780 FnvHashMap<ExchangeId, UnboundedRx<MarketStreamResult<InstrumentKey, Liquidation>>>,
781}
782
783impl<InstrumentKey> Default for Rxs<InstrumentKey> {
784 fn default() -> Self {
785 Self {
786 trades: Default::default(),
787 l1s: Default::default(),
788 l2s: Default::default(),
789 liquidations: Default::default(),
790 }
791 }
792}