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_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"

View File

@ -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<Box<dyn Connector>>,
inner: Arc<Box<dyn RestConnector>>,
}
impl Client {
@ -168,8 +169,9 @@ impl Client {
pub async fn trading_fees(&self) -> Result<Vec<TradingFees>, 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<Option<Vec<Position>>, 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>;
}
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<Option<Vec<Position>>, 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<TradingPairTicker, BoxError> {
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<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())
}
@ -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<Option<Vec<Trade>>, 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<Option<Vec<OrderDetails>>, 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<Vec<TradingFees>, 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())