pub mod data; pub mod perp; use std::collections::BTreeMap;
mod market;
mod spot;
mod ws;
use adapters::{
Client, GetOptions,
binance::{BinanceOption, BinanceOptions},
};
use secrecy::SecretString;
use v_utils::trades::{Pair, Timeframe};
use crate::{
BatchTrades, BookUpdate, ExchangeError, ExchangeInfo, ExchangeName, ExchangeResult, ExchangeStream, Klines, MethodError, PrecisionPriceQty, RequestRange,
core::{ExchangeImpl, Instrument, PersonalInfo, Symbol},
};
#[derive(Clone, Debug, Default, derive_more::Deref, derive_more::DerefMut)]
pub struct Binance {
#[deref]
#[deref_mut]
pub client: Client,
pub info_cache: BTreeMap<Instrument, ExchangeInfo>,
}
#[async_trait::async_trait]
impl ExchangeImpl for Binance {
fn info_cache_mut(&mut self) -> &mut BTreeMap<Instrument, ExchangeInfo> {
&mut self.info_cache
}
fn name(&self) -> ExchangeName {
ExchangeName::Binance
}
fn auth(&mut self, pubkey: String, secret: SecretString) {
self.update_default_option(BinanceOption::Pubkey(pubkey));
self.update_default_option(BinanceOption::Secret(secret));
}
fn set_recv_window(&mut self, recv_window: std::time::Duration) {
self.update_default_option(BinanceOption::RecvWindow(recv_window));
}
fn default_recv_window(&self) -> Option<std::time::Duration> {
GetOptions::<BinanceOptions>::default_options(&**self).recv_window
}
async fn exchange_info(&self, instrument: Instrument) -> ExchangeResult<ExchangeInfo> {
match instrument {
Instrument::Perp => perp::general::exchange_info(self).await,
_ => unimplemented!(),
}
}
async fn klines(&self, symbol: Symbol, tf: Timeframe, range: RequestRange) -> ExchangeResult<Klines> {
match symbol.instrument {
Instrument::Spot | Instrument::Margin => market::klines(self, symbol, tf.try_into()?, range).await,
Instrument::Perp => market::klines(self, symbol, tf.try_into()?, range).await,
_ => Err(ExchangeError::Method(MethodError::new_method_not_implemented(self.name(), symbol.instrument))),
}
}
async fn prices(&self, pairs: Option<Vec<Pair>>, instrument: Instrument) -> ExchangeResult<BTreeMap<Pair, f64>> {
match instrument {
Instrument::Spot | Instrument::Margin => spot::market::prices(self, pairs).await,
Instrument::Perp => perp::market::prices(self, pairs).await,
_ => Err(ExchangeError::Method(MethodError::new_method_not_implemented(self.name(), instrument))),
}
}
async fn open_interest(&self, symbol: Symbol, tf: Timeframe, range: RequestRange) -> ExchangeResult<Vec<crate::core::OpenInterest>> {
match symbol.instrument {
Instrument::Perp => market::open_interest(self, symbol, tf.try_into()?, range).await,
_ => Err(ExchangeError::Method(MethodError::new_method_not_supported(self.name(), symbol.instrument))),
}
}
async fn personal_info(&self, instrument: Instrument, recv_window: Option<std::time::Duration>) -> ExchangeResult<PersonalInfo> {
match instrument {
Instrument::Perp => {
let prices = self.prices(None, instrument).await?;
perp::account::personal_info(self, recv_window, &prices).await
}
Instrument::Spot | Instrument::Margin => spot::account::personal_info(self, recv_window).await,
_ => Err(ExchangeError::Method(MethodError::new_method_not_implemented(self.name(), instrument))),
}
}
async fn ws_trades(&mut self, pairs: &[Pair], instrument: Instrument) -> Result<Box<dyn ExchangeStream<Item = BatchTrades>>, ExchangeError> {
match instrument {
Instrument::Perp | Instrument::Spot | Instrument::Margin => {
if !self.info_cache.contains_key(&instrument) {
let info = ExchangeImpl::exchange_info(&*self, instrument).await?;
self.info_cache.insert(instrument, info);
}
let exchange = self.name();
let pair_precisions: BTreeMap<Pair, PrecisionPriceQty> = {
let info = self.info_cache.get(&instrument).expect("just inserted or was present");
pairs
.iter()
.map(|pair| {
info.pairs
.get(pair)
.ok_or_else(|| ExchangeError::Method(MethodError::new_pair_not_listed(exchange, instrument, *pair)))
.map(|pi| {
(
*pair,
PrecisionPriceQty {
price: pi.price_precision,
qty: pi.qty_precision,
},
)
})
})
.collect::<ExchangeResult<_>>()?
};
let connection = ws::TradesConnection::try_new(self, pairs, instrument, pair_precisions)?;
Ok(Box::new(connection))
}
_ => Err(ExchangeError::Method(MethodError::new_method_not_implemented(self.name(), instrument))),
}
}
async fn ws_book(&mut self, pairs: &[Pair], instrument: Instrument) -> Result<Box<dyn ExchangeStream<Item = BookUpdate>>, ExchangeError> {
match instrument {
Instrument::Perp | Instrument::Spot | Instrument::Margin => {
if !self.info_cache.contains_key(&instrument) {
let info = ExchangeImpl::exchange_info(&*self, instrument).await?;
self.info_cache.insert(instrument, info);
}
let exchange = self.name();
let pair_precisions: BTreeMap<Pair, PrecisionPriceQty> = {
let info = self.info_cache.get(&instrument).expect("just inserted or was present");
pairs
.iter()
.map(|pair| {
info.pairs
.get(pair)
.ok_or_else(|| ExchangeError::Method(MethodError::new_pair_not_listed(exchange, instrument, *pair)))
.map(|pi| {
(
*pair,
PrecisionPriceQty {
price: pi.price_precision,
qty: pi.qty_precision,
},
)
})
})
.collect::<ExchangeResult<_>>()?
};
let connection = ws::BookConnection::try_new(self, pairs, instrument, pair_precisions)?;
Ok(Box::new(connection))
}
_ => Err(ExchangeError::Method(MethodError::new_method_not_implemented(self.name(), instrument))),
}
}
}
crate::define_provider_timeframe!(
BinanceTimeframe,
[
"1s", "5s", "15s", "30s", "1m", "3m", "5m", "15m", "30m", "1h", "2h", "4h", "6h", "8h", "12h", "1d", "3d", "1w", "1M"
]
);