diff --git a/Cargo.lock b/Cargo.lock index ffcf564..651f262 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -109,7 +109,7 @@ dependencies = [ "serde_derive", "serde_json", "tokio", - "tungstenite", + "tungstenite 0.13.0", "url", ] @@ -1189,7 +1189,7 @@ dependencies = [ "regex", "tokio", "tokio-tungstenite", - "tungstenite", + "tungstenite 0.12.0", ] [[package]] @@ -1353,6 +1353,26 @@ dependencies = [ "winapi", ] +[[package]] +name = "thiserror" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0f4a65597094d4483ddaed134f409b2cb7c1beccf25201a9f73c719254fa98e" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7765189610d8241a44529806d6fd1f2e0a08734313a35d5b3a556f92b381f3c0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "thread_local" version = "1.0.1" @@ -1450,7 +1470,7 @@ dependencies = [ "log 0.4.11", "pin-project 1.0.2", "tokio", - "tungstenite", + "tungstenite 0.12.0", ] [[package]] @@ -1531,6 +1551,27 @@ dependencies = [ "utf-8", ] +[[package]] +name = "tungstenite" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fe8dada8c1a3aeca77d6b51a4f1314e0f4b8e438b7b1b71e3ddaca8080e4093" +dependencies = [ + "base64", + "byteorder", + "bytes 1.0.1", + "http", + "httparse", + "input_buffer", + "log 0.4.11", + "native-tls", + "rand 0.8.2", + "sha-1", + "thiserror", + "url", + "utf-8", +] + [[package]] name = "typenum" version = "1.12.0" diff --git a/src/connectors.rs b/src/connectors.rs index 0980143..62a45a8 100644 --- a/src/connectors.rs +++ b/src/connectors.rs @@ -4,11 +4,12 @@ use std::str::FromStr; use std::sync::Arc; use async_trait::async_trait; -use bitfinex::api::Bitfinex; +use bitfinex::api::RestClient; use bitfinex::book::BookPrecision; use bitfinex::orders::{CancelOrderForm, OrderMeta}; use bitfinex::responses::{OrderResponse, TradeResponse}; use bitfinex::ticker::TradingPairTicker; +use bitfinex::websockets::WebSocketClient; use futures_retry::RetryPolicy; use log::trace; use tokio::macros::support::Future; @@ -36,7 +37,7 @@ pub enum ExchangeDetails { #[derive(Clone, Debug)] pub struct Client { exchange: Exchange, - inner: Arc>, + inner: Arc>, } impl Client { @@ -168,8 +169,9 @@ impl Client { pub async fn trading_fees(&self) -> Result, BoxError> { self.inner.trading_fees().await } } +/// This trait represents a REST API service. #[async_trait] -pub trait Connector: Send + Sync { +pub trait RestConnector: Send + Sync { fn name(&self) -> String; async fn active_positions(&self, pair: &SymbolPair) -> Result>, BoxError>; async fn current_prices(&self, pair: &SymbolPair) -> Result; @@ -193,18 +195,26 @@ pub trait Connector: Send + Sync { async fn trading_fees(&self) -> Result, BoxError>; } -impl Debug for dyn Connector { +impl Debug for dyn RestConnector { fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { write!(f, "{}", self.name()) } } +/// This trait represents a WebSocket API service. +#[async_trait] +pub trait WebSocketConnector: Send + Sync { + fn name(&self) -> String; + async fn connect(&self) -> Result<(), BoxError>; +} + /************** * BITFINEX **************/ pub struct BitfinexConnector { - bfx: Bitfinex, + rest: bitfinex::api::RestClient, + ws: bitfinex::websockets::WebSocketClient, } impl BitfinexConnector { @@ -219,7 +229,8 @@ impl BitfinexConnector { pub fn new(api_key: &str, api_secret: &str) -> Self { BitfinexConnector { - bfx: Bitfinex::new(Some(api_key.into()), Some(api_secret.into())), + rest: RestClient::new(Some(api_key.into()), Some(api_secret.into())), + ws: WebSocketClient::new(), } } @@ -260,14 +271,14 @@ impl BitfinexConnector { } #[async_trait] -impl Connector for BitfinexConnector { +impl RestConnector for BitfinexConnector { fn name(&self) -> String { - "Bitfinex".into() + "Bitfinex REST".into() } async fn active_positions(&self, pair: &SymbolPair) -> Result>, BoxError> { let active_positions = - BitfinexConnector::retry_nonce(|| self.bfx.positions.active_positions()).await?; + BitfinexConnector::retry_nonce(|| self.rest.positions.active_positions()).await?; let positions: Vec<_> = active_positions .into_iter() @@ -282,7 +293,7 @@ impl Connector for BitfinexConnector { async fn current_prices(&self, pair: &SymbolPair) -> Result { let symbol_name = BitfinexConnector::format_trading_pair(pair); - let ticker: TradingPairTicker = self.bfx.ticker.trading_pair(symbol_name).await?; + let ticker: TradingPairTicker = self.rest.ticker.trading_pair(symbol_name).await?; Ok(ticker) } @@ -291,7 +302,7 @@ impl Connector for BitfinexConnector { let symbol_name = BitfinexConnector::format_trading_pair(pair); let response = BitfinexConnector::retry_nonce(|| { - self.bfx.book.trading_pair(&symbol_name, BookPrecision::P0) + self.rest.book.trading_pair(&symbol_name, BookPrecision::P0) }) .await?; @@ -308,7 +319,7 @@ impl Connector for BitfinexConnector { } async fn active_orders(&self, _: &SymbolPair) -> Result, BoxError> { - let response = BitfinexConnector::retry_nonce(|| self.bfx.orders.active_orders()).await?; + let response = BitfinexConnector::retry_nonce(|| self.rest.orders.active_orders()).await?; Ok(response.iter().map(Into::into).collect()) } @@ -351,7 +362,7 @@ impl Connector for BitfinexConnector { }; let response = - BitfinexConnector::retry_nonce(|| self.bfx.orders.submit_order(&order_form)).await?; + BitfinexConnector::retry_nonce(|| self.rest.orders.submit_order(&order_form)).await?; // parsing response into ActiveOrder and adding leverage from order form let order_response: ActiveOrder = (&response).try_into()?; @@ -364,7 +375,7 @@ impl Connector for BitfinexConnector { let cancel_form = order.into(); let response = - BitfinexConnector::retry_nonce(|| self.bfx.orders.cancel_order(&cancel_form)).await?; + BitfinexConnector::retry_nonce(|| self.rest.orders.cancel_order(&cancel_form)).await?; Ok((&response).try_into()?) } @@ -377,7 +388,7 @@ impl Connector for BitfinexConnector { amount: f64, ) -> Result<(), BoxError> { BitfinexConnector::retry_nonce(|| { - self.bfx.account.transfer_between_wallets( + self.rest.account.transfer_between_wallets( from.into(), to.into(), symbol.to_string(), @@ -394,7 +405,7 @@ impl Connector for BitfinexConnector { order: &OrderDetails, ) -> Result>, BoxError> { let response = BitfinexConnector::retry_nonce(|| { - self.bfx + self.rest .trades .generated_by_order(order.pair().trading_repr(), order.id()) }) @@ -412,7 +423,7 @@ impl Connector for BitfinexConnector { pair: &SymbolPair, ) -> Result>, BoxError> { let response = - BitfinexConnector::retry_nonce(|| self.bfx.orders.history(Some(pair.trading_repr()))) + BitfinexConnector::retry_nonce(|| self.rest.orders.history(Some(pair.trading_repr()))) .await?; let mapped_vec: Vec<_> = response.iter().map(Into::into).collect(); @@ -422,7 +433,7 @@ impl Connector for BitfinexConnector { async fn trading_fees(&self) -> Result, BoxError> { let mut fees = vec![]; let accountfees = - BitfinexConnector::retry_nonce(|| self.bfx.account.account_summary()).await?; + BitfinexConnector::retry_nonce(|| self.rest.account.account_summary()).await?; // Derivatives let derivative_taker = TradingFees::Taker { @@ -464,6 +475,17 @@ impl Connector for BitfinexConnector { } } +#[async_trait] +impl WebSocketConnector for BitfinexConnector { + fn name(&self) -> String { + "Bitfinex WS".into() + } + + async fn connect(&self) -> Result<(), BoxError> { + Ok(()) + } +} + impl From<&ActiveOrder> for CancelOrderForm { fn from(o: &ActiveOrder) -> Self { Self::from_id(o.id())