barter_data/exchange/bitfinex/
mod.rs

1//!
2//! ### Notes
3//! #### SubscripionId
4//! - Successful Bitfinex subscription responses contain a numeric `CHANNEL_ID` that must be used to
5//!   identify future messages relating to that subscription (not persistent across connections).
6//! - To identify the initial subscription response containing the `CHANNEL_ID`, the "channel" &
7//!   "market" identifiers can be used for the `SubscriptionId(channel|market)`
8//!   (eg/ SubscriptionId("trades|tBTCUSD")).
9//! - Once the subscription has been validated and the `CHANNEL_ID` determined, each `SubscriptionId`
10//!   in the `SubscriptionIds` `HashMap` is mutated to become `SubscriptionId(CHANNEL_ID)`.
11//!   eg/ SubscriptionId("trades|tBTCUSD") -> SubscriptionId(69)
12//!
13//! #### Connection Limits
14//! - The user is allowed up to 20 connections per minute on the public API.
15//! - Each connection can be used to connect up to 25 different channels.
16//!
17//! #### Trade Variants
18//! - Bitfinex trades subscriptions results in receiving tag="te" & tag="tu" trades.
19//! - Both appear to be identical payloads, but "te" arriving marginally faster.
20//! - Therefore, tag="tu" trades are filtered out and considered only as additional Heartbeats.
21
22use self::{
23    channel::BitfinexChannel, market::BitfinexMarket, message::BitfinexMessage,
24    subscription::BitfinexPlatformEvent, validator::BitfinexWebSocketSubValidator,
25};
26use crate::{
27    ExchangeWsStream, NoInitialSnapshots,
28    exchange::{Connector, ExchangeSub, StreamSelector},
29    instrument::InstrumentData,
30    subscriber::WebSocketSubscriber,
31    subscription::trade::PublicTrades,
32    transformer::stateless::StatelessTransformer,
33};
34use barter_instrument::exchange::ExchangeId;
35use barter_integration::{error::SocketError, protocol::websocket::WsMessage};
36use barter_macro::{DeExchange, SerExchange};
37use derive_more::Display;
38use serde_json::json;
39use url::Url;
40
41/// Defines the type that translates a Barter [`Subscription`](crate::subscription::Subscription)
42/// into an exchange [`Connector`] specific channel used for generating [`Connector::requests`].
43pub mod channel;
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/// [`BitfinexMessage`] type for [`Bitfinex`].
50pub mod message;
51
52/// [`Subscription`](crate::subscription::Subscription) response types and response
53/// [`Validator`](barter_integration::Validator) for [`Bitfinex`].
54pub mod subscription;
55
56/// Public trade types for [`Bitfinex`].
57pub mod trade;
58
59/// Custom `SubscriptionValidator` implementation for [`Bitfinex`].
60pub mod validator;
61
62/// [`Bitfinex`] server base url.
63///
64/// See docs: <https://docs.bitfinex.com/docs/ws-general>
65pub const BASE_URL_BITFINEX: &str = "wss://api-pub.bitfinex.com/ws/2";
66
67/// [`Bitfinex`] exchange.
68///
69/// See docs: <https://docs.bitfinex.com/docs/ws-general>
70#[derive(
71    Copy,
72    Clone,
73    Eq,
74    PartialEq,
75    Ord,
76    PartialOrd,
77    Hash,
78    Debug,
79    Default,
80    Display,
81    DeExchange,
82    SerExchange,
83)]
84pub struct Bitfinex;
85
86impl Connector for Bitfinex {
87    const ID: ExchangeId = ExchangeId::Bitfinex;
88    type Channel = BitfinexChannel;
89    type Market = BitfinexMarket;
90    type Subscriber = WebSocketSubscriber;
91    type SubValidator = BitfinexWebSocketSubValidator;
92    type SubResponse = BitfinexPlatformEvent;
93
94    fn url() -> Result<Url, SocketError> {
95        Url::parse(BASE_URL_BITFINEX).map_err(SocketError::UrlParse)
96    }
97
98    fn requests(exchange_subs: Vec<ExchangeSub<Self::Channel, Self::Market>>) -> Vec<WsMessage> {
99        exchange_subs
100            .into_iter()
101            .map(|ExchangeSub { channel, market }| {
102                WsMessage::text(
103                    json!({
104                        "event": "subscribe",
105                        "channel": channel.as_ref(),
106                        "symbol": market.as_ref(),
107                    })
108                    .to_string(),
109                )
110            })
111            .collect()
112    }
113}
114
115impl<Instrument> StreamSelector<Instrument, PublicTrades> for Bitfinex
116where
117    Instrument: InstrumentData,
118{
119    type SnapFetcher = NoInitialSnapshots;
120    type Stream = ExchangeWsStream<
121        StatelessTransformer<Self, Instrument::Key, PublicTrades, BitfinexMessage>,
122    >;
123}