rustrade_data/exchange/binance/
mod.rs1use 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::{
12 Map,
13 book::OrderBooksL1,
14 candle::{CandleInterval, Candles},
15 trade::PublicTrades,
16 },
17 transformer::stateless::StatelessTransformer,
18};
19use rustrade_instrument::exchange::ExchangeId;
20use rustrade_integration::protocol::websocket::{WebSocketSerdeParser, WsMessage};
21use std::{fmt::Debug, marker::PhantomData};
22use url::Url;
23
24pub mod book;
27
28pub mod channel;
31
32mod error;
34pub use error::BinanceDataError;
35
36pub mod historical;
40pub use historical::BinanceHistoricalClient;
41
42pub mod futures;
45
46pub mod kline;
49
50pub mod market;
53
54pub mod spot;
57
58pub mod subscription;
62
63pub mod trade;
66
67pub type BinanceWsStream<Transformer> = ExchangeWsStream<WebSocketSerdeParser, Transformer>;
69
70#[must_use]
98pub fn supports_candle_interval(interval: CandleInterval) -> bool {
99 match interval {
100 CandleInterval::Sec5 | CandleInterval::Sec15 | CandleInterval::Sec30 => false,
101 CandleInterval::Sec1
102 | CandleInterval::Min1
103 | CandleInterval::Min3
104 | CandleInterval::Min5
105 | CandleInterval::Min15
106 | CandleInterval::Min30
107 | CandleInterval::Hour1
108 | CandleInterval::Hour2
109 | CandleInterval::Hour4
110 | CandleInterval::Hour6
111 | CandleInterval::Hour8
112 | CandleInterval::Hour12
113 | CandleInterval::Day1
114 | CandleInterval::Day3
115 | CandleInterval::Week1
116 | CandleInterval::Month1 => true,
117 }
118}
119
120#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default)]
126pub struct Binance<Server> {
127 server: PhantomData<Server>,
128}
129
130impl<Server> Connector for Binance<Server>
131where
132 Server: ExchangeServer,
133{
134 const ID: ExchangeId = Server::ID;
135 type Channel = BinanceChannel;
136 type Market = BinanceMarket;
137 type Subscriber = WebSocketSubscriber;
138 type SubValidator = WebSocketSubValidator;
139 type SubResponse = BinanceSubResponse;
140
141 fn url() -> Result<Url, url::ParseError> {
142 Url::parse(Server::websocket_url())
143 }
144
145 fn requests(exchange_subs: Vec<ExchangeSub<Self::Channel, Self::Market>>) -> Vec<WsMessage> {
146 let stream_names = exchange_subs
147 .into_iter()
148 .map(|sub| {
149 format!(
153 "{}{}",
154 sub.market.as_ref().to_lowercase(),
155 sub.channel.as_ref()
156 )
157 })
158 .collect::<Vec<String>>();
159
160 vec![WsMessage::text(
161 serde_json::json!({
162 "method": "SUBSCRIBE",
163 "params": stream_names,
164 "id": 1
165 })
166 .to_string(),
167 )]
168 }
169
170 fn expected_responses<InstrumentKey>(_: &Map<InstrumentKey>) -> usize {
171 1
172 }
173}
174
175impl<Instrument> StreamSelector<Instrument, PublicTrades> for BinanceSpot
182where
183 Instrument: InstrumentData,
184{
185 type SnapFetcher = NoInitialSnapshots;
186 type Stream =
187 BinanceWsStream<StatelessTransformer<Self, Instrument::Key, PublicTrades, BinanceTrade>>;
188}
189
190impl<Instrument> StreamSelector<Instrument, PublicTrades> for BinanceFuturesUsd
191where
192 Instrument: InstrumentData,
193{
194 type SnapFetcher = NoInitialSnapshots;
195 type Stream =
196 BinanceWsStream<StatelessTransformer<Self, Instrument::Key, PublicTrades, BinanceTrade>>;
197}
198
199impl<Instrument> StreamSelector<Instrument, OrderBooksL1> for BinanceSpot
200where
201 Instrument: InstrumentData,
202{
203 type SnapFetcher = NoInitialSnapshots;
204 type Stream = BinanceWsStream<
205 StatelessTransformer<Self, Instrument::Key, OrderBooksL1, BinanceOrderBookL1>,
206 >;
207}
208
209impl<Instrument> StreamSelector<Instrument, OrderBooksL1> for BinanceFuturesUsd
210where
211 Instrument: InstrumentData,
212{
213 type SnapFetcher = NoInitialSnapshots;
214 type Stream = BinanceWsStream<
215 StatelessTransformer<Self, Instrument::Key, OrderBooksL1, BinanceOrderBookL1>,
216 >;
217}
218
219impl<Instrument> StreamSelector<Instrument, Candles> for BinanceSpot
221where
222 Instrument: InstrumentData,
223{
224 type SnapFetcher = NoInitialSnapshots;
225 type Stream =
226 BinanceWsStream<StatelessTransformer<Self, Instrument::Key, Candles, BinanceKline>>;
227}
228
229impl<'de, Server> serde::Deserialize<'de> for Binance<Server>
230where
231 Server: ExchangeServer,
232{
233 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
234 where
235 D: serde::de::Deserializer<'de>,
236 {
237 let input = <String as serde::Deserialize>::deserialize(deserializer)?;
238
239 if input.as_str() == Self::ID.as_str() {
240 Ok(Self::default())
241 } else {
242 Err(serde::de::Error::invalid_value(
243 serde::de::Unexpected::Str(input.as_str()),
244 &Self::ID.as_str(),
245 ))
246 }
247 }
248}
249
250impl<Server> serde::Serialize for Binance<Server>
251where
252 Server: ExchangeServer,
253{
254 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
255 where
256 S: serde::ser::Serializer,
257 {
258 serializer.serialize_str(Self::ID.as_str())
259 }
260}
261
262#[cfg(test)]
263mod tests {
264 use super::*;
265
266 #[test]
270 fn supports_candle_interval_rejects_only_the_sub_minute_intervals_binance_lacks() {
271 for interval in CandleInterval::ALL {
272 let expected = !matches!(
273 interval,
274 CandleInterval::Sec5 | CandleInterval::Sec15 | CandleInterval::Sec30
275 );
276 assert_eq!(
277 supports_candle_interval(interval),
278 expected,
279 "interval {interval}"
280 );
281 }
282 }
283}