Skip to main content

rustrade_data/exchange/binance/
mod.rs

1use self::{
2    book::l1::BinanceOrderBookL1, channel::BinanceChannel, futures::BinanceFuturesUsd,
3    kline::BinanceKline, market::BinanceMarket, spot::BinanceSpot,
4    subscription::BinanceSubResponse, trade::BinanceTrade,
5};
6use crate::{
7    ExchangeWsStream, NoInitialSnapshots,
8    exchange::{Connector, ExchangeServer, ExchangeSub, StreamSelector},
9    instrument::InstrumentData,
10    subscriber::{WebSocketSubscriber, validator::WebSocketSubValidator},
11    subscription::{Map, book::OrderBooksL1, candle::Candles, trade::PublicTrades},
12    transformer::stateless::StatelessTransformer,
13};
14use rustrade_instrument::exchange::ExchangeId;
15use rustrade_integration::protocol::websocket::{WebSocketSerdeParser, WsMessage};
16use std::{fmt::Debug, marker::PhantomData};
17use url::Url;
18
19/// OrderBook types common to both [`BinanceSpot`] and
20/// [`BinanceFuturesUsd`].
21pub mod book;
22
23/// Defines the type that translates a Barter [`Subscription`](crate::subscription::Subscription)
24/// into an exchange [`Connector`] specific channel used for generating [`Connector::requests`].
25pub mod channel;
26
27/// Dedicated error type for the Binance historical klines REST client.
28mod error;
29pub use error::BinanceDataError;
30
31/// Historical klines (OHLCV candles) via Binance's public, unauthenticated REST
32/// endpoints — spot (`/api/v3/klines`) and futures continuous
33/// (`/fapi/v1/continuousKlines`).
34pub mod historical;
35pub use historical::BinanceHistoricalClient;
36
37/// [`ExchangeServer`] and [`StreamSelector`] implementations for
38/// [`BinanceFuturesUsd`].
39pub mod futures;
40
41/// Live kline (candle) wire models common to [`BinanceSpot`] (`@kline_`)
42/// and [`BinanceFuturesUsd`] (`@continuousKline_`).
43pub mod kline;
44
45/// Defines the type that translates a Barter [`Subscription`](crate::subscription::Subscription)
46/// into an exchange [`Connector`] specific market used for generating [`Connector::requests`].
47pub mod market;
48
49/// [`ExchangeServer`] and [`StreamSelector`] implementations for
50/// [`BinanceSpot`].
51pub mod spot;
52
53/// [`Subscription`](crate::subscription::Subscription) response type and response
54/// [`Validator`](rustrade_integration::Validator) common to both [`BinanceSpot`]
55/// and [`BinanceFuturesUsd`].
56pub mod subscription;
57
58/// Public trade types common to both [`BinanceSpot`] and
59/// [`BinanceFuturesUsd`].
60pub mod trade;
61
62/// Convenient type alias for a Binance [`ExchangeWsStream`] using [`WebSocketSerdeParser`].
63pub type BinanceWsStream<Transformer> = ExchangeWsStream<WebSocketSerdeParser, Transformer>;
64
65/// Generic [`Binance<Server>`](Binance) exchange.
66///
67/// ### Notes
68/// A `Server` [`ExchangeServer`] implementations exists for
69/// [`BinanceSpot`] and [`BinanceFuturesUsd`].
70#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default)]
71pub struct Binance<Server> {
72    server: PhantomData<Server>,
73}
74
75impl<Server> Connector for Binance<Server>
76where
77    Server: ExchangeServer,
78{
79    const ID: ExchangeId = Server::ID;
80    type Channel = BinanceChannel;
81    type Market = BinanceMarket;
82    type Subscriber = WebSocketSubscriber;
83    type SubValidator = WebSocketSubValidator;
84    type SubResponse = BinanceSubResponse;
85
86    fn url() -> Result<Url, url::ParseError> {
87        Url::parse(Server::websocket_url())
88    }
89
90    fn requests(exchange_subs: Vec<ExchangeSub<Self::Channel, Self::Market>>) -> Vec<WsMessage> {
91        let stream_names = exchange_subs
92            .into_iter()
93            .map(|sub| {
94                // Note:
95                // Market must be lowercase when subscribing, but lowercase in general since
96                // Binance sends message with uppercase MARKET (eg/ BTCUSDT).
97                format!(
98                    "{}{}",
99                    sub.market.as_ref().to_lowercase(),
100                    sub.channel.as_ref()
101                )
102            })
103            .collect::<Vec<String>>();
104
105        vec![WsMessage::text(
106            serde_json::json!({
107                "method": "SUBSCRIBE",
108                "params": stream_names,
109                "id": 1
110            })
111            .to_string(),
112        )]
113    }
114
115    fn expected_responses<InstrumentKey>(_: &Map<InstrumentKey>) -> usize {
116        1
117    }
118}
119
120// `PublicTrades` and `OrderBooksL1` are implemented per-server (NOT blanket over
121// `Binance<Server>`) so they ride only the `/public`-tier server types — `BinanceSpot` (`/ws`)
122// and `BinanceFuturesUsd` (`/public/ws`). The market-tier `BinanceFuturesUsdMarket` (`/market/ws`)
123// deliberately has no such impl, making a trade/L1 subscription on that tier — which Binance
124// would silently dead-stream — a compile error instead. This mirrors `OrderBooksL2`, which is
125// already per-server.
126impl<Instrument> StreamSelector<Instrument, PublicTrades> for BinanceSpot
127where
128    Instrument: InstrumentData,
129{
130    type SnapFetcher = NoInitialSnapshots;
131    type Stream =
132        BinanceWsStream<StatelessTransformer<Self, Instrument::Key, PublicTrades, BinanceTrade>>;
133}
134
135impl<Instrument> StreamSelector<Instrument, PublicTrades> for BinanceFuturesUsd
136where
137    Instrument: InstrumentData,
138{
139    type SnapFetcher = NoInitialSnapshots;
140    type Stream =
141        BinanceWsStream<StatelessTransformer<Self, Instrument::Key, PublicTrades, BinanceTrade>>;
142}
143
144impl<Instrument> StreamSelector<Instrument, OrderBooksL1> for BinanceSpot
145where
146    Instrument: InstrumentData,
147{
148    type SnapFetcher = NoInitialSnapshots;
149    type Stream = BinanceWsStream<
150        StatelessTransformer<Self, Instrument::Key, OrderBooksL1, BinanceOrderBookL1>,
151    >;
152}
153
154impl<Instrument> StreamSelector<Instrument, OrderBooksL1> for BinanceFuturesUsd
155where
156    Instrument: InstrumentData,
157{
158    type SnapFetcher = NoInitialSnapshots;
159    type Stream = BinanceWsStream<
160        StatelessTransformer<Self, Instrument::Key, OrderBooksL1, BinanceOrderBookL1>,
161    >;
162}
163
164// Live spot klines: `@kline_<interval>` on `BinanceSpot`'s `/ws` tier.
165impl<Instrument> StreamSelector<Instrument, Candles> for BinanceSpot
166where
167    Instrument: InstrumentData,
168{
169    type SnapFetcher = NoInitialSnapshots;
170    type Stream =
171        BinanceWsStream<StatelessTransformer<Self, Instrument::Key, Candles, BinanceKline>>;
172}
173
174impl<'de, Server> serde::Deserialize<'de> for Binance<Server>
175where
176    Server: ExchangeServer,
177{
178    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
179    where
180        D: serde::de::Deserializer<'de>,
181    {
182        let input = <String as serde::Deserialize>::deserialize(deserializer)?;
183
184        if input.as_str() == Self::ID.as_str() {
185            Ok(Self::default())
186        } else {
187            Err(serde::de::Error::invalid_value(
188                serde::de::Unexpected::Str(input.as_str()),
189                &Self::ID.as_str(),
190            ))
191        }
192    }
193}
194
195impl<Server> serde::Serialize for Binance<Server>
196where
197    Server: ExchangeServer,
198{
199    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
200    where
201        S: serde::ser::Serializer,
202    {
203        serializer.serialize_str(Self::ID.as_str())
204    }
205}