First steps towards REST + WS connectors (BFX lib still in the works)
This commit is contained in:
parent
354ef407f3
commit
f43fcb0206
47
Cargo.lock
generated
47
Cargo.lock
generated
@ -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"
|
||||||
|
@ -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())
|
||||||
|
Loading…
Reference in New Issue
Block a user