First steps towards REST + WS connectors (BFX lib still in the works)

This commit is contained in:
Giulio De Pasquale 2021-02-27 20:49:56 +00:00
parent 354ef407f3
commit f43fcb0206
2 changed files with 84 additions and 21 deletions

47
Cargo.lock generated
View File

@ -109,7 +109,7 @@ dependencies = [
"serde_derive", "serde_derive",
"serde_json", "serde_json",
"tokio", "tokio",
"tungstenite", "tungstenite 0.13.0",
"url", "url",
] ]
@ -1189,7 +1189,7 @@ dependencies = [
"regex", "regex",
"tokio", "tokio",
"tokio-tungstenite", "tokio-tungstenite",
"tungstenite", "tungstenite 0.12.0",
] ]
[[package]] [[package]]
@ -1353,6 +1353,26 @@ dependencies = [
"winapi", "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]] [[package]]
name = "thread_local" name = "thread_local"
version = "1.0.1" version = "1.0.1"
@ -1450,7 +1470,7 @@ dependencies = [
"log 0.4.11", "log 0.4.11",
"pin-project 1.0.2", "pin-project 1.0.2",
"tokio", "tokio",
"tungstenite", "tungstenite 0.12.0",
] ]
[[package]] [[package]]
@ -1531,6 +1551,27 @@ dependencies = [
"utf-8", "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]] [[package]]
name = "typenum" name = "typenum"
version = "1.12.0" version = "1.12.0"

View File

@ -4,11 +4,12 @@ use std::str::FromStr;
use std::sync::Arc; use std::sync::Arc;
use async_trait::async_trait; use async_trait::async_trait;
use bitfinex::api::Bitfinex; use bitfinex::api::RestClient;
use bitfinex::book::BookPrecision; use bitfinex::book::BookPrecision;
use bitfinex::orders::{CancelOrderForm, OrderMeta}; use bitfinex::orders::{CancelOrderForm, OrderMeta};
use bitfinex::responses::{OrderResponse, TradeResponse}; use bitfinex::responses::{OrderResponse, TradeResponse};
use bitfinex::ticker::TradingPairTicker; use bitfinex::ticker::TradingPairTicker;
use bitfinex::websockets::WebSocketClient;
use futures_retry::RetryPolicy; use futures_retry::RetryPolicy;
use log::trace; use log::trace;
use tokio::macros::support::Future; use tokio::macros::support::Future;
@ -36,7 +37,7 @@ pub enum ExchangeDetails {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Client { pub struct Client {
exchange: Exchange, exchange: Exchange,
inner: Arc<Box<dyn Connector>>, inner: Arc<Box<dyn RestConnector>>,
} }
impl Client { impl Client {
@ -168,8 +169,9 @@ impl Client {
pub async fn trading_fees(&self) -> Result<Vec<TradingFees>, BoxError> { self.inner.trading_fees().await } pub async fn trading_fees(&self) -> Result<Vec<TradingFees>, BoxError> { self.inner.trading_fees().await }
} }
/// This trait represents a REST API service.
#[async_trait] #[async_trait]
pub trait Connector: Send + Sync { pub trait RestConnector: Send + Sync {
fn name(&self) -> String; fn name(&self) -> String;
async fn active_positions(&self, pair: &SymbolPair) -> Result<Option<Vec<Position>>, BoxError>; async fn active_positions(&self, pair: &SymbolPair) -> Result<Option<Vec<Position>>, BoxError>;
async fn current_prices(&self, pair: &SymbolPair) -> Result<TradingPairTicker, BoxError>; async fn current_prices(&self, pair: &SymbolPair) -> Result<TradingPairTicker, BoxError>;
@ -193,18 +195,26 @@ pub trait Connector: Send + Sync {
async fn trading_fees(&self) -> Result<Vec<TradingFees>, BoxError>; async fn trading_fees(&self) -> Result<Vec<TradingFees>, BoxError>;
} }
impl Debug for dyn Connector { impl Debug for dyn RestConnector {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
write!(f, "{}", self.name()) 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 * BITFINEX
**************/ **************/
pub struct BitfinexConnector { pub struct BitfinexConnector {
bfx: Bitfinex, rest: bitfinex::api::RestClient,
ws: bitfinex::websockets::WebSocketClient,
} }
impl BitfinexConnector { impl BitfinexConnector {
@ -219,7 +229,8 @@ impl BitfinexConnector {
pub fn new(api_key: &str, api_secret: &str) -> Self { pub fn new(api_key: &str, api_secret: &str) -> Self {
BitfinexConnector { 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] #[async_trait]
impl Connector for BitfinexConnector { impl RestConnector for BitfinexConnector {
fn name(&self) -> String { fn name(&self) -> String {
"Bitfinex".into() "Bitfinex REST".into()
} }
async fn active_positions(&self, pair: &SymbolPair) -> Result<Option<Vec<Position>>, BoxError> { async fn active_positions(&self, pair: &SymbolPair) -> Result<Option<Vec<Position>>, BoxError> {
let active_positions = 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 let positions: Vec<_> = active_positions
.into_iter() .into_iter()
@ -282,7 +293,7 @@ impl Connector for BitfinexConnector {
async fn current_prices(&self, pair: &SymbolPair) -> Result<TradingPairTicker, BoxError> { async fn current_prices(&self, pair: &SymbolPair) -> Result<TradingPairTicker, BoxError> {
let symbol_name = BitfinexConnector::format_trading_pair(pair); 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) Ok(ticker)
} }
@ -291,7 +302,7 @@ impl Connector for BitfinexConnector {
let symbol_name = BitfinexConnector::format_trading_pair(pair); let symbol_name = BitfinexConnector::format_trading_pair(pair);
let response = BitfinexConnector::retry_nonce(|| { let response = BitfinexConnector::retry_nonce(|| {
self.bfx.book.trading_pair(&symbol_name, BookPrecision::P0) self.rest.book.trading_pair(&symbol_name, BookPrecision::P0)
}) })
.await?; .await?;
@ -308,7 +319,7 @@ impl Connector for BitfinexConnector {
} }
async fn active_orders(&self, _: &SymbolPair) -> Result<Vec<ActiveOrder>, BoxError> { async fn active_orders(&self, _: &SymbolPair) -> Result<Vec<ActiveOrder>, 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()) Ok(response.iter().map(Into::into).collect())
} }
@ -351,7 +362,7 @@ impl Connector for BitfinexConnector {
}; };
let response = 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 // parsing response into ActiveOrder and adding leverage from order form
let order_response: ActiveOrder = (&response).try_into()?; let order_response: ActiveOrder = (&response).try_into()?;
@ -364,7 +375,7 @@ impl Connector for BitfinexConnector {
let cancel_form = order.into(); let cancel_form = order.into();
let response = 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()?) Ok((&response).try_into()?)
} }
@ -377,7 +388,7 @@ impl Connector for BitfinexConnector {
amount: f64, amount: f64,
) -> Result<(), BoxError> { ) -> Result<(), BoxError> {
BitfinexConnector::retry_nonce(|| { BitfinexConnector::retry_nonce(|| {
self.bfx.account.transfer_between_wallets( self.rest.account.transfer_between_wallets(
from.into(), from.into(),
to.into(), to.into(),
symbol.to_string(), symbol.to_string(),
@ -394,7 +405,7 @@ impl Connector for BitfinexConnector {
order: &OrderDetails, order: &OrderDetails,
) -> Result<Option<Vec<Trade>>, BoxError> { ) -> Result<Option<Vec<Trade>>, BoxError> {
let response = BitfinexConnector::retry_nonce(|| { let response = BitfinexConnector::retry_nonce(|| {
self.bfx self.rest
.trades .trades
.generated_by_order(order.pair().trading_repr(), order.id()) .generated_by_order(order.pair().trading_repr(), order.id())
}) })
@ -412,7 +423,7 @@ impl Connector for BitfinexConnector {
pair: &SymbolPair, pair: &SymbolPair,
) -> Result<Option<Vec<OrderDetails>>, BoxError> { ) -> Result<Option<Vec<OrderDetails>>, BoxError> {
let response = 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?; .await?;
let mapped_vec: Vec<_> = response.iter().map(Into::into).collect(); let mapped_vec: Vec<_> = response.iter().map(Into::into).collect();
@ -422,7 +433,7 @@ impl Connector for BitfinexConnector {
async fn trading_fees(&self) -> Result<Vec<TradingFees>, BoxError> { async fn trading_fees(&self) -> Result<Vec<TradingFees>, BoxError> {
let mut fees = vec![]; let mut fees = vec![];
let accountfees = let accountfees =
BitfinexConnector::retry_nonce(|| self.bfx.account.account_summary()).await?; BitfinexConnector::retry_nonce(|| self.rest.account.account_summary()).await?;
// Derivatives // Derivatives
let derivative_taker = TradingFees::Taker { 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 { impl From<&ActiveOrder> for CancelOrderForm {
fn from(o: &ActiveOrder) -> Self { fn from(o: &ActiveOrder) -> Self {
Self::from_id(o.id()) Self::from_id(o.id())