Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
![Minimum Supported Rust Version](https://img.shields.io/badge/nightly-1.85+-ab6000.svg)
[<img alt="crates.io" src="https://img.shields.io/crates/v/v_exchanges.svg?color=fc8d62&logo=rust" height="20" style=flat-square>](https://crates.io/crates/v_exchanges)
[<img alt="docs.rs" src="https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs&style=flat-square" height="20">](https://docs.rs/v_exchanges)
![Lines Of Code](https://img.shields.io/badge/LoC-3769-lightblue)
![Lines Of Code](https://img.shields.io/badge/LoC-3864-lightblue)
<br>
[<img alt="ci errors" src="https://img.shields.io/github/actions/workflow/status/valeratrades/v_exchanges/errors.yml?branch=master&style=for-the-badge&style=flat-square&label=errors&labelColor=420d09" height="20">](https://github.com/valeratrades/v_exchanges/actions?query=branch%3Amaster) <!--NB: Won't find it if repo is private-->
[<img alt="ci warnings" src="https://img.shields.io/github/actions/workflow/status/valeratrades/v_exchanges/warnings.yml?branch=master&style=for-the-badge&style=flat-square&label=warnings&labelColor=d16002" height="20">](https://github.com/valeratrades/v_exchanges/actions?query=branch%3Amaster) <!--NB: Won't find it if repo is private-->
Expand Down
19 changes: 9 additions & 10 deletions examples/binance/market_futures.rs
Original file line number Diff line number Diff line change
@@ -1,28 +1,27 @@
use std::env;

use v_exchanges::{
binance,
core::{Exchange, MarketTrait as _},
prelude::*,
};

#[tokio::main]
async fn main() {
color_eyre::install().unwrap();
v_utils::utils::init_subscriber(v_utils::utils::LogDestination::xdg("v_exchanges"));

let m = binance::Market::Futures;
let mut bn = m.client();
let m: AbsMarket = "Binance/Futures".into();
let mut c = m.client();

let exchange_info = bn.exchange_info(m).await.unwrap();
dbg!( &exchange_info .pairs .iter() .take(2) .collect::<Vec<_>>());
let exchange_info = c.exchange_info(m).await.unwrap();
dbg!(&exchange_info.pairs.iter().take(2).collect::<Vec<_>>());

let klines = bn.klines(("BTC", "USDT").into(), "1m".into(), 2.into(), m).await.unwrap();
let price = bn.price(("BTC", "USDT").into(), m).await.unwrap();
let klines = c.klines(("BTC", "USDT").into(), "1m".into(), 2.into(), m).await.unwrap();
let price = c.price(("BTC", "USDT").into(), m).await.unwrap();
dbg!(&klines, price);

if let (Ok(key), Ok(secret)) = (env::var("BINANCE_TIGER_READ_KEY"), env::var("BINANCE_TIGER_READ_SECRET")) {
bn.auth(key, secret);
let balance = bn.asset_balance("USDT".into(), m).await.unwrap();
c.auth(key, secret);
let balance = c.asset_balance("USDT".into(), m).await.unwrap();
dbg!(&balance);
} else {
eprintln!("BINANCE_TIGER_READ_KEY or BINANCE_TIGER_READ_SECRET is missing, skipping private API methods.");
Expand Down
14 changes: 6 additions & 8 deletions examples/binance/market_spot.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
use v_exchanges::{
binance,
core::{Exchange, MarketTrait as _},
};
use v_exchanges::prelude::*;

#[tokio::main]
async fn main() {
Expand All @@ -10,12 +7,13 @@ async fn main() {

//let m: Market = "Binance/Spot".into(); // would be nice to be able to do it like this, without having to carry around exchange-specific type
// Currently if I want to pass around the market struct in my code after initializing it, I have to pass around eg `binance::Market`, which is a ridiculous thing to hardcode into function signatures
let m = binance::Market::Spot;
let bn = m.client();
//let m = binance::Market::Spot;
let m: AbsMarket = "Binance/Spot".into();
let c = m.client();

let spot_klines = bn.klines(("BTC", "USDT").into(), "1m".into(), 2.into(), m).await.unwrap();
let spot_klines = c.klines(("BTC", "USDT").into(), "1m".into(), 2.into(), m).await.unwrap();
dbg!(&spot_klines);

let spot_prices = bn.prices(None, m).await.unwrap();
let spot_prices = c.prices(None, m).await.unwrap();
dbg!(&spot_prices[..5]);
}
21 changes: 9 additions & 12 deletions examples/bybit/market.rs
Original file line number Diff line number Diff line change
@@ -1,37 +1,34 @@
use std::env;

use v_exchanges::{
bybit::{self, Bybit},
core::{Exchange, MarketTrait as _},
};
use v_exchanges::prelude::*;

#[tokio::main]
async fn main() {
color_eyre::install().unwrap();
v_utils::utils::init_subscriber(v_utils::utils::LogDestination::xdg("v_exchanges"));

//let m: Market = "Bybit/Linear".into(); // would be nice to be able to do it like this
let m = bybit::Market::Linear;
let mut bb = m.client();
let m: AbsMarket = "Bybit/Linear".into();
let mut c = m.client();

let klines = bb.klines(("BTC", "USDT").into(), "1m".into(), 2.into(), m).await.unwrap();
let klines = c.klines(("BTC", "USDT").into(), "1m".into(), 2.into(), m).await.unwrap();
dbg!(&klines);
let price = bb.price(("BTC", "USDT").into(), m).await.unwrap();
let price = c.price(("BTC", "USDT").into(), m).await.unwrap();
dbg!(&price);

if let (Ok(key), Ok(secret)) = (env::var("BYBIT_TIGER_READ_KEY"), env::var("BYBIT_TIGER_READ_SECRET")) {
bb.auth(key, secret);
private(&mut bb, m).await;
c.auth(key, secret);
private(&mut c, m).await;
} else {
eprintln!("BYBIT_TIGER_READ_KEY or BYBIT_TIGER_READ_SECRET is missing, skipping private API methods.");
}
}

async fn private(bb: &mut Bybit, m: bybit::Market) {
async fn private(c: &mut Box<dyn Exchange>, m: AbsMarket) {
//let key_permissions: serde_json::Value = bb.get_no_query("/v5/user/query-api", [BybitOption::HttpAuth(BybitHttpAuth::V3AndAbove)])
// .await
// .unwrap();

let balances = bb.balances(m).await.unwrap();
let balances = c.balances(m).await.unwrap();
dbg!(&balances);
}
1 change: 1 addition & 0 deletions examples/data.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use v_exchanges::{binance::Binance, bitmex::Bitmex};

/// things in here are not on [Exchange](v_exchanges::core::Exchange) trait, so can't use generics, must specify exact exchange client methods are referenced from.
#[tokio::main]
async fn main() {
color_eyre::install().unwrap();
Expand Down
2 changes: 1 addition & 1 deletion v_exchanges/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ rustdoc-args = ["--cfg", "docsrs"]
ignored = ["derive-new", "color-eyre", "serde", "tokio", "v_utils"]

[dependencies]
async-trait = "0.1.85"
chrono = { version = "0.4.39", features = ["serde"] }
derive-new.workspace = true
derive_more.workspace = true
Expand All @@ -40,7 +41,6 @@ tracing.workspace = true
v_utils = { workspace = true }

v_exchanges_adapters = { version = "^0.3.0", path = "../v_exchanges_adapters/", features = ["full"] }
async-trait = "0.1.85"

reqwest = { version = "^0.12.12", optional = true }

Expand Down
1 change: 0 additions & 1 deletion v_exchanges/src/binance/futures/general.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

use adapters::binance::{BinanceHttpUrl, BinanceOption};
use chrono::DateTime;
use eyre::Result;
Expand Down
76 changes: 48 additions & 28 deletions v_exchanges/src/binance/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,76 +3,96 @@ mod futures;
mod market;
mod spot;
use adapters::binance::BinanceOption;
use derive_more::{Deref, DerefMut, Display, FromStr};
use derive_more::{Deref, DerefMut};
use eyre::Result;
use v_exchanges_adapters::Client;
use v_utils::trades::{Asset, Pair, Timeframe};

use crate::core::{AssetBalance, Exchange, ExchangeInfo, Klines, RequestRange};
use crate::core::{AbsMarket, AssetBalance, Exchange, ExchangeInfo, Klines, RequestRange, WrongExchangeError};

#[derive(Clone, Debug, Default, Deref, DerefMut)]
pub struct Binance(pub Client);

//? currently client ends up importing this from crate::binance, but could it be possible to lift the [Client] reexport up, and still have the ability to call all exchange methods right on it?
#[async_trait::async_trait]
impl Exchange for Binance {
type M = Market;
fn exchange_name(&self) -> &'static str {
"Binance"
}

fn auth(&mut self, key: String, secret: String) {
self.update_default_option(BinanceOption::Key(key));
self.update_default_option(BinanceOption::Secret(secret));
}

async fn exchange_info(&self, m: Self::M) -> Result<ExchangeInfo> {
match m {
Market::Futures => futures::general::exchange_info(&self.0).await,
_ => unimplemented!(),
async fn exchange_info(&self, am: AbsMarket) -> Result<ExchangeInfo> {
match am {
AbsMarket::Binance(m) => match m {
Market::Futures => futures::general::exchange_info(&self.0).await,
_ => unimplemented!(),
},
_ => Err(WrongExchangeError::new(self.exchange_name(), am).into()),
}
}

async fn klines(&self, pair: Pair, tf: Timeframe, range: RequestRange, m: Self::M) -> Result<Klines> {
market::klines(&self.0, pair, tf, range, m).await
async fn klines(&self, pair: Pair, tf: Timeframe, range: RequestRange, am: AbsMarket) -> Result<Klines> {
match am {
AbsMarket::Binance(m) => market::klines(&self.0, pair, tf, range, m).await,
_ => Err(WrongExchangeError::new(self.exchange_name(), am).into()),
}
}

async fn prices(&self, pairs: Option<Vec<Pair>>, m: Self::M) -> Result<Vec<(Pair, f64)>> {
match m {
Market::Spot => spot::market::prices(&self.0, pairs).await,
_ => unimplemented!(),
async fn prices(&self, pairs: Option<Vec<Pair>>, am: AbsMarket) -> Result<Vec<(Pair, f64)>> {
match am {
AbsMarket::Binance(m) => match m {
Market::Spot => spot::market::prices(&self.0, pairs).await,
_ => unimplemented!(),
},
_ => Err(WrongExchangeError::new(self.exchange_name(), am).into()),
}
}

async fn price(&self, pair: Pair, m: Self::M) -> Result<f64> {
match m {
Market::Spot => spot::market::price(&self.0, pair).await,
Market::Futures => futures::market::price(&self.0, pair).await,
_ => unimplemented!(),
async fn price(&self, pair: Pair, am: AbsMarket) -> Result<f64> {
match am {
AbsMarket::Binance(m) => match m {
Market::Spot => spot::market::price(&self.0, pair).await,
Market::Futures => futures::market::price(&self.0, pair).await,
_ => unimplemented!(),
},
_ => Err(WrongExchangeError::new(self.exchange_name(), am).into()),
}
}

async fn asset_balance(&self, asset: Asset, m: Self::M) -> Result<AssetBalance> {
match m {
Market::Futures => futures::account::asset_balance(self, asset).await,
_ => unimplemented!(),
async fn asset_balance(&self, asset: Asset, am: AbsMarket) -> Result<AssetBalance> {
match am {
AbsMarket::Binance(m) => match m {
Market::Futures => futures::account::asset_balance(self, asset).await,
_ => unimplemented!(),
},
_ => Err(WrongExchangeError::new(self.exchange_name(), am).into()),
}
}

async fn balances(&self, m: Self::M) -> Result<Vec<AssetBalance>> {
match m {
Market::Futures => futures::account::balances(&self.0).await,
_ => unimplemented!(),
async fn balances(&self, am: AbsMarket) -> Result<Vec<AssetBalance>> {
match am {
AbsMarket::Binance(m) => match m {
Market::Futures => futures::account::balances(&self.0).await,
_ => unimplemented!(),
},
_ => Err(WrongExchangeError::new(self.exchange_name(), am).into()),
}
}
}

#[derive(Debug, Clone, Default, Copy, Display, FromStr)]
#[derive(Debug, Clone, Default, Copy, derive_more::Display, derive_more::FromStr)]
pub enum Market {
#[default]
Futures,
Spot,
Margin,
}
impl crate::core::MarketTrait for Market {
fn client(&self) -> Box<dyn Exchange<M = Self>> {
fn client(&self) -> Box<dyn Exchange> {
Box::new(Binance::default())
}

Expand Down
2 changes: 1 addition & 1 deletion v_exchanges/src/bybit/market.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::collections::VecDeque;

use chrono::{DateTime, TimeZone};
use chrono::DateTime;
use eyre::Result;
use serde::{Deserialize, Serialize};
use serde_json::{Value, json};
Expand Down
Loading
Loading