Skip to main content

rustrade_data/streams/builder/
mod.rs

1use super::Streams;
2use crate::{
3    Identifier,
4    error::DataError,
5    exchange::StreamSelector,
6    instrument::InstrumentData,
7    streams::{
8        consumer::{MarketStreamResult, STREAM_RECONNECTION_POLICY, init_market_stream},
9        reconnect::stream::ReconnectingStream,
10    },
11    subscription::{Subscription, SubscriptionKind},
12};
13use rustrade_instrument::exchange::ExchangeId;
14use rustrade_integration::{Validator, channel::Channel};
15use std::{
16    collections::HashMap,
17    fmt::{Debug, Display},
18    future::Future,
19    pin::Pin,
20};
21
22/// Defines the [`MultiStreamBuilder`](multi::MultiStreamBuilder) API for ergonomically
23/// initialising a common [`Streams<Output>`](Streams) from multiple
24/// [`StreamBuilder<SubscriptionKind>`](StreamBuilder)s.
25pub mod multi;
26
27/// Defines the [`DynamicStreams`](dynamic::DynamicStreams) API for initialising an arbitrary number
28/// of `MarketStream`s from the [`ExchangeId`] and [`SubKind`](crate::subscription::SubKind) enums, rather than concrete
29/// types.
30pub mod dynamic;
31
32/// Communicative type alias representing the [`Future`] result of a [`Subscription`] validation
33/// call generated whilst executing [`StreamBuilder::subscribe`].
34pub type SubscribeFuture = Pin<Box<dyn Future<Output = Result<(), DataError>>>>;
35
36/// Builder to configure and initialise a [`Streams<MarketEvent<SubscriptionKind::Event>`](Streams) instance
37/// for a specific [`SubscriptionKind`].
38#[derive(Default)]
39pub struct StreamBuilder<InstrumentKey, Kind>
40where
41    Kind: SubscriptionKind,
42{
43    pub channels: HashMap<ExchangeId, Channel<MarketStreamResult<InstrumentKey, Kind::Event>>>,
44    pub futures: Vec<SubscribeFuture>,
45}
46
47impl<InstrumentKey, Kind> Debug for StreamBuilder<InstrumentKey, Kind>
48where
49    InstrumentKey: Debug,
50    Kind: SubscriptionKind,
51{
52    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
53        f.debug_struct("StreamBuilder<InstrumentKey, SubscriptionKind>")
54            .field("channels", &self.channels)
55            .field("num_futures", &self.futures.len())
56            .finish()
57    }
58}
59
60impl<InstrumentKey, Kind> StreamBuilder<InstrumentKey, Kind>
61where
62    Kind: SubscriptionKind,
63{
64    /// Construct a new [`Self`].
65    pub fn new() -> Self {
66        Self {
67            channels: HashMap::new(),
68            futures: Vec::new(),
69        }
70    }
71
72    /// Add a collection of [`Subscription`]s to the [`StreamBuilder`] that will be actioned on
73    /// a connection the `subscriber` provides.
74    ///
75    /// For almost every exchange that is a distinct
76    /// [`WebSocket`](rustrade_integration::protocol::websocket::WebSocket) per call. A subscriber
77    /// whose clones share one connection — for a provider that allows a key a single connection —
78    /// instead serves each call over a view of it; see
79    /// [`Subscriber::Transport`](crate::subscriber::Subscriber::Transport).
80    ///
81    /// The `subscriber` handles the connection and authentication.
82    /// For unauthenticated exchanges, use [`WebSocketSubscriber`](crate::subscriber::WebSocketSubscriber).
83    /// For authenticated exchanges like Alpaca, use the exchange-specific subscriber with credentials.
84    ///
85    /// Note that [`Subscription`]s are not actioned until the
86    /// [`init()`](StreamBuilder::init()) method is invoked.
87    pub fn subscribe<SubIter, Sub, Exchange, Instrument>(
88        mut self,
89        subscriber: Exchange::Subscriber,
90        subscriptions: SubIter,
91    ) -> Self
92    where
93        SubIter: IntoIterator<Item = Sub>,
94        Sub: Into<Subscription<Exchange, Instrument, Kind>>,
95        Exchange: StreamSelector<Instrument, Kind> + Ord + Send + Sync + 'static,
96        Instrument: InstrumentData<Key = InstrumentKey> + Ord + Display + 'static,
97        Instrument::Key: Debug + Clone + Send + 'static,
98        Kind: Ord + Display + Send + Sync + 'static,
99        Kind::Event: Clone + Send,
100        Subscription<Exchange, Instrument, Kind>:
101            Identifier<Exchange::Channel> + Identifier<Exchange::Market>,
102    {
103        // Construct Vec<Subscriptions> from input SubIter
104        let subscriptions = subscriptions.into_iter().map(Sub::into).collect::<Vec<_>>();
105
106        // Acquire channel Sender to send Market<Kind::Event> from consumer loop to user
107        // '--> Add ExchangeChannel Entry if this Exchange <--> SubscriptionKind combination is new
108        let exchange_tx = self.channels.entry(Exchange::ID).or_default().tx.clone();
109
110        // Add Future that once awaited will yield the Result<(), SocketError> of subscribing
111        self.futures.push(Box::pin(async move {
112            // Validate Subscriptions
113            let mut subscriptions = subscriptions
114                .into_iter()
115                .map(Subscription::validate)
116                .collect::<Result<Vec<_>, _>>()?;
117
118            // Remove duplicate Subscriptions
119            subscriptions.sort();
120            subscriptions.dedup();
121
122            // Initialise a MarketEvent `ReconnectingStream`
123            let stream =
124                init_market_stream(STREAM_RECONNECTION_POLICY, subscriber, subscriptions).await?;
125
126            // Forward MarketEvents to ExchangeTx
127            tokio::spawn(stream.forward_to(exchange_tx));
128
129            Ok(())
130        }));
131
132        self
133    }
134
135    /// Spawn a [`MarketStreamResult<SubscriptionKind::Event>`](MarketStreamResult) consumer loop
136    /// for each collection of [`Subscription`]s added to [`StreamBuilder`] via the
137    /// [`subscribe()`](StreamBuilder::subscribe()) method.
138    ///
139    /// Each consumer loop distributes consumed [`MarketStreamResult`] to
140    /// the [`Streams`] `HashMap` returned by this method.
141    pub async fn init(
142        self,
143    ) -> Result<Streams<MarketStreamResult<InstrumentKey, Kind::Event>>, DataError> {
144        // Await Stream initialisation perpetual and ensure success
145        futures::future::try_join_all(self.futures).await?;
146
147        // Construct Streams using each ExchangeChannel receiver
148        Ok(Streams {
149            streams: self
150                .channels
151                .into_iter()
152                .map(|(exchange, channel)| (exchange, channel.rx))
153                .collect(),
154        })
155    }
156}