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