Skip to content

Commit fa483d5

Browse files
committed
refactor: _
1 parent 9c81fcb commit fa483d5

File tree

14 files changed

+224
-200
lines changed

14 files changed

+224
-200
lines changed

Cargo.lock

Lines changed: 97 additions & 91 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
![Minimum Supported Rust Version](https://img.shields.io/badge/nightly-1.85+-ab6000.svg)
33
[<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)
44
[<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)
5-
![Lines Of Code](https://img.shields.io/badge/LoC-3745-lightblue)
5+
![Lines Of Code](https://img.shields.io/badge/LoC-3757-lightblue)
66
<br>
77
[<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-->
88
[<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-->

examples/data.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,6 @@ async fn main() {
99
dbg!(&bvol);
1010

1111
let bn = Binance::default();
12-
let lsr = bn.lsr(("BTC", "USDT").into(), "5m".into(), 24 * 12 + 1, "Global".into()).await.unwrap();
12+
let lsr = bn.lsr(("BTC", "USDT").into(), "5m".into(), (24 * 12 + 1).into(), "Global".into()).await.unwrap();
1313
dbg!(&lsr[..2]);
1414
}

v_exchanges/src/binance/data.rs

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use v_utils::{
1010
};
1111

1212
use super::Binance;
13+
use crate::{core::RequestRange, utils::join_params};
1314

1415
#[derive(Clone, Debug, Display, FromStr)]
1516
pub enum LsrWho {
@@ -23,20 +24,19 @@ impl From<&str> for LsrWho {
2324
}
2425

2526
impl Binance {
26-
pub async fn lsr(&self, pair: Pair, tf: Timeframe, limit: u32, who: LsrWho) -> Result<Vec<Lsr>> {
27-
let allowed_range = 1..=500;
28-
//TODO!!: add a `limit outside of range` error, generic for all exchanges
29-
assert!(allowed_range.contains(&limit));
27+
pub async fn lsr(&self, pair: Pair, tf: Timeframe, range: RequestRange, who: LsrWho) -> Result<Vec<Lsr>> {
28+
range.ensure_allowed(0..=500, tf)?;
29+
let range_json = range.serialize();
3030

3131
let ending = match who {
3232
LsrWho::Global => "globalLongShortAccountRatio",
3333
LsrWho::Top => "topLongShortPositionRatio",
3434
};
35-
let params = json!({
35+
let base_json = json!({
3636
"symbol": pair.to_string(),
3737
"period": tf.format_binance()?,
38-
"limit": limit,
3938
});
39+
let params = join_params(base_json, range_json);
4040
let r: serde_json::Value = self
4141
.0
4242
.get(&format!("/futures/data/{ending}"), &params, [BinanceOption::HttpUrl(BinanceHttpUrl::FuturesUsdM)])
@@ -58,30 +58,30 @@ pub struct LsrResponse {
5858
pub struct Lsr {
5959
pub time: DateTime<Utc>,
6060
pub pair: Pair,
61-
pub p_long: Percent,
61+
pub long: Percent,
6262
}
6363
//Q: couldn't decide if `short()` and `long(0` should return `f64` or `Percent`. Postponing the decision.
6464
impl Lsr {
6565
pub fn ratio(&self) -> f64 {
66-
*self.p_long / self.short()
66+
*self.long / self.short()
6767
}
6868

6969
/// Percentage of short positions
7070
pub fn short(&self) -> f64 {
71-
1.0 - *self.p_long
71+
1.0 - *self.long
7272
}
7373

7474
/// Percentage of long positions. // here only for consistency with `short`
7575
pub fn long(&self) -> f64 {
76-
*self.p_long
76+
*self.long
7777
}
7878
}
7979
impl From<LsrResponse> for Lsr {
8080
fn from(r: LsrResponse) -> Self {
8181
Self {
8282
time: DateTime::from_timestamp_millis(r.timestamp).unwrap(),
8383
pair: Pair::from_str(&r.symbol).unwrap(),
84-
p_long: Percent::from_str(&r.long_account).unwrap(),
84+
long: Percent::from_str(&r.long_account).unwrap(),
8585
}
8686
}
8787
}

v_exchanges/src/binance/market.rs

Lines changed: 10 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -6,46 +6,24 @@ use eyre::Result;
66
use serde::{Deserialize, Serialize};
77
use serde_json::{Value, json};
88
use serde_with::{DisplayFromStr, serde_as};
9-
use v_exchanges_adapters::{
10-
binance::{BinanceHttpUrl, BinanceOption},
11-
errors::LimitOutOfRangeError,
12-
};
13-
use v_utils::{
14-
trades::{Kline, Ohlc, Pair, Timeframe},
15-
utils::filter_nulls,
16-
};
9+
use v_exchanges_adapters::binance::{BinanceHttpUrl, BinanceOption};
10+
use v_utils::trades::{Kline, Ohlc, Pair, Timeframe};
1711

1812
use crate::{
1913
binance::Market,
20-
core::{Klines, KlinesRequestRange},
14+
core::{Klines, RequestRange},
15+
utils::join_params,
2116
};
2217

2318
// klines {{{
24-
pub async fn klines(client: &v_exchanges_adapters::Client, pair: Pair, tf: Timeframe, range: KlinesRequestRange, market: Market) -> Result<Klines> {
25-
let range_json = match range {
26-
KlinesRequestRange::StartEnd { start, end } => json!({
27-
"startTime": start.timestamp_millis(),
28-
"endTime": end.map(|dt| dt.timestamp_millis()),
29-
}),
30-
KlinesRequestRange::Limit(limit) => {
31-
let allowed_range = 1..=1000;
32-
if !allowed_range.contains(&limit) {
33-
return Err(LimitOutOfRangeError::new(allowed_range, limit).into());
34-
}
35-
json!({
36-
"limit": limit,
37-
})
38-
}
39-
};
40-
let base_params = filter_nulls(json!({
19+
pub async fn klines(client: &v_exchanges_adapters::Client, pair: Pair, tf: Timeframe, range: RequestRange, market: Market) -> Result<Klines> {
20+
range.ensure_allowed(1..=1000, tf)?;
21+
let range_params = range.serialize();
22+
let base_params = json!({
4123
"symbol": pair.to_string(),
4224
"interval": tf.format_binance()?,
43-
}));
44-
45-
let mut base_map = base_params.as_object().unwrap().clone();
46-
let range_map = range_json.as_object().unwrap();
47-
base_map.extend(range_map.clone());
48-
let params = filter_nulls(serde_json::Value::Object(base_map));
25+
});
26+
let params = join_params(base_params, range_params);
4927

5028
let endpoint_prefix = match market {
5129
Market::Spot => "/api/v3",

v_exchanges/src/binance/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use eyre::Result;
88
use v_exchanges_adapters::Client;
99
use v_utils::trades::{Asset, Pair, Timeframe};
1010

11-
use crate::core::{AssetBalance, Exchange, ExchangeInfo, Klines, KlinesRequestRange};
11+
use crate::core::{AssetBalance, Exchange, ExchangeInfo, Klines, RequestRange};
1212

1313
#[derive(Clone, Debug, Default, Deref, DerefMut)]
1414
pub struct Binance(pub Client);
@@ -29,7 +29,7 @@ impl Exchange for Binance {
2929
}
3030
}
3131

32-
async fn klines(&self, pair: Pair, tf: Timeframe, range: KlinesRequestRange, m: Self::M) -> Result<Klines> {
32+
async fn klines(&self, pair: Pair, tf: Timeframe, range: RequestRange, m: Self::M) -> Result<Klines> {
3333
market::klines(&self.0, pair, tf, range, m).await
3434
}
3535

v_exchanges/src/bybit/market.rs

Lines changed: 5 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,31 +5,18 @@ use eyre::Result;
55
use serde::{Deserialize, Serialize};
66
use serde_json::{Value, json};
77
use serde_with::{DisplayFromStr, serde_as};
8-
use v_exchanges_adapters::{bybit::BybitOption, errors::LimitOutOfRangeError};
8+
use v_exchanges_adapters::bybit::BybitOption;
99
use v_utils::{
1010
trades::{Kline, Ohlc, Pair, Timeframe},
1111
utils::filter_nulls,
1212
};
1313

14-
use crate::core::{Klines, KlinesRequestRange};
14+
use crate::core::{Klines, RequestRange};
1515

1616
// klines {{{
17-
pub async fn klines(client: &v_exchanges_adapters::Client, pair: Pair, tf: Timeframe, range: KlinesRequestRange) -> Result<Klines> {
18-
let range_json = match range {
19-
KlinesRequestRange::StartEnd { start, end } => json!({
20-
"startTime": start.timestamp_millis(),
21-
"endTime": end.map(|dt| dt.timestamp_millis()),
22-
}),
23-
KlinesRequestRange::Limit(limit) => {
24-
let allowed_range = 1..=1000;
25-
if !allowed_range.contains(&limit) {
26-
return Err(LimitOutOfRangeError::new(allowed_range, limit).into());
27-
}
28-
json!({
29-
"limit": limit,
30-
})
31-
}
32-
};
17+
pub async fn klines(client: &v_exchanges_adapters::Client, pair: Pair, tf: Timeframe, range: RequestRange) -> Result<Klines> {
18+
range.ensure_allowed(1..=1000, tf)?;
19+
let range_json = range.serialize();
3320
let base_params = filter_nulls(json!({
3421
"category": "linear", // can be ["linear", "inverse", "spot"] afaiu, could drive some generics with this later, but for now hardcode
3522
"symbol": pair.to_string(),

v_exchanges/src/bybit/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use eyre::Result;
1010
use v_exchanges_adapters::Client;
1111
use v_utils::trades::{Asset, Pair, Timeframe};
1212

13-
use crate::core::{AssetBalance, Exchange, ExchangeInfo, Klines, KlinesRequestRange};
13+
use crate::core::{AssetBalance, Exchange, ExchangeInfo, Klines, RequestRange};
1414

1515
#[derive(Clone, Debug, Default, Deref, DerefMut)]
1616
pub struct Bybit(pub Client);
@@ -28,7 +28,7 @@ impl Exchange for Bybit {
2828
todo!();
2929
}
3030

31-
async fn klines(&self, pair: Pair, tf: Timeframe, range: KlinesRequestRange, m: Self::M) -> Result<Klines> {
31+
async fn klines(&self, pair: Pair, tf: Timeframe, range: RequestRange, m: Self::M) -> Result<Klines> {
3232
match m {
3333
Market::Linear => market::klines(&self.0, pair, tf, range).await,
3434
_ => unimplemented!(),

v_exchanges/src/core.rs

Lines changed: 82 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
1-
use std::collections::{HashMap, VecDeque};
1+
use std::collections::{BTreeMap, VecDeque};
22

33
use chrono::{DateTime, TimeDelta, Utc};
44
use derive_more::{Deref, DerefMut};
5-
use eyre::{Report, Result};
6-
use v_utils::trades::{Asset, Kline, Pair, Timeframe};
5+
use eyre::{Report, Result, bail};
6+
use serde_json::json;
7+
use v_utils::{
8+
trades::{Asset, Kline, Pair, Timeframe},
9+
utils::filter_nulls,
10+
};
711

812
//TODO!!!!!!!!!!!!!: klines switch to defining the range via an Enum over either limit either start and end times
913

@@ -15,7 +19,7 @@ pub trait Exchange {
1519
fn exchange_info(&self, m: Self::M) -> impl std::future::Future<Output = Result<ExchangeInfo>> + Send;
1620

1721
//? should I have Self::Pair too? Like to catch the non-existent ones immediately? Although this would increase the error surface on new listings.
18-
fn klines(&self, pair: Pair, tf: Timeframe, range: KlinesRequestRange, m: Self::M) -> impl std::future::Future<Output = Result<Klines>> + Send;
22+
fn klines(&self, pair: Pair, tf: Timeframe, range: RequestRange, m: Self::M) -> impl std::future::Future<Output = Result<Klines>> + Send;
1923

2024
/// If no pairs are specified, returns for all;
2125
fn prices(&self, pairs: Option<Vec<Pair>>, m: Self::M) -> impl std::future::Future<Output = Result<Vec<(Pair, f64)>>> + Send;
@@ -148,68 +152,122 @@ impl TryFrom<Klines> for FullKlines {
148152
todo!();
149153
}
150154
}
155+
//,}}}
151156

157+
// RequestRange {{{
152158
#[derive(Clone, Debug, Copy)]
153-
pub enum KlinesRequestRange {
159+
pub enum RequestRange {
154160
/// Preferred way of defining the range
155161
StartEnd { start: DateTime<Utc>, end: Option<DateTime<Utc>> },
156162
/// For quick and dirty
157163
//TODO!: have it contain an enum, with either exact value, either just `Max`, then each exchange matches on it
158164
Limit(u32),
159165
}
160-
impl Default for KlinesRequestRange {
166+
impl RequestRange {
167+
pub fn ensure_allowed(&self, allowed: std::ops::RangeInclusive<u32>, tf: Timeframe) -> Result<()> {
168+
match self {
169+
RequestRange::StartEnd { start, end } =>
170+
if let Some(end) = end {
171+
if start > end {
172+
bail!("Start time is greater than end time");
173+
}
174+
let effective_limit = ((*end - start).num_milliseconds() / tf.duration().num_milliseconds()) as u32;
175+
if effective_limit > *allowed.end() {
176+
return Err(OutOfRangeError::new(allowed, effective_limit).into());
177+
}
178+
},
179+
RequestRange::Limit(limit) =>
180+
if !allowed.contains(limit) {
181+
return Err(OutOfRangeError::new(allowed, *limit).into());
182+
},
183+
}
184+
Ok(())
185+
}
186+
187+
//XXX
188+
//TODO!!!!!!!!!: MUST be generic over Market. But with current Market representation is impossible.
189+
pub fn serialize(&self) -> serde_json::Value {
190+
filter_nulls(match self {
191+
RequestRange::StartEnd { start, end } => json!({
192+
"startTime": start.timestamp_millis(),
193+
"endTime": end.map(|dt| dt.timestamp_millis()),
194+
}),
195+
RequestRange::Limit(limit) => json!({
196+
"limit": limit,
197+
}),
198+
})
199+
}
200+
}
201+
impl Default for RequestRange {
161202
fn default() -> Self {
162-
KlinesRequestRange::StartEnd {
203+
RequestRange::StartEnd {
163204
start: DateTime::default(),
164205
end: None,
165206
}
166207
}
167208
}
168-
impl From<DateTime<Utc>> for KlinesRequestRange {
209+
impl From<DateTime<Utc>> for RequestRange {
169210
fn from(value: DateTime<Utc>) -> Self {
170-
KlinesRequestRange::StartEnd { start: value, end: None }
211+
RequestRange::StartEnd { start: value, end: None }
171212
}
172213
}
173214
/// funky
174-
impl From<TimeDelta> for KlinesRequestRange {
215+
impl From<TimeDelta> for RequestRange {
175216
fn from(value: TimeDelta) -> Self {
176217
let now = Utc::now();
177-
KlinesRequestRange::StartEnd { start: now - value, end: None }
218+
RequestRange::StartEnd { start: now - value, end: None }
178219
}
179220
}
180-
impl From<u32> for KlinesRequestRange {
221+
impl From<u32> for RequestRange {
181222
fn from(value: u32) -> Self {
182-
KlinesRequestRange::Limit(value)
223+
RequestRange::Limit(value)
183224
}
184225
}
185-
impl From<i32> for KlinesRequestRange {
226+
impl From<i32> for RequestRange {
186227
fn from(value: i32) -> Self {
187-
KlinesRequestRange::Limit(value as u32)
228+
RequestRange::Limit(value as u32)
188229
}
189230
}
190-
impl From<u16> for KlinesRequestRange {
231+
impl From<u16> for RequestRange {
191232
fn from(value: u16) -> Self {
192-
KlinesRequestRange::Limit(value as u32)
233+
RequestRange::Limit(value as u32)
193234
}
194235
}
195-
impl From<u8> for KlinesRequestRange {
236+
impl From<u8> for RequestRange {
196237
fn from(value: u8) -> Self {
197-
KlinesRequestRange::Limit(value as u32)
238+
RequestRange::Limit(value as u32)
198239
}
199240
}
200-
impl From<(DateTime<Utc>, DateTime<Utc>)> for KlinesRequestRange {
241+
impl From<(DateTime<Utc>, DateTime<Utc>)> for RequestRange {
201242
fn from(value: (DateTime<Utc>, DateTime<Utc>)) -> Self {
202-
KlinesRequestRange::StartEnd { start: value.0, end: Some(value.1) }
243+
RequestRange::StartEnd { start: value.0, end: Some(value.1) }
203244
}
204245
}
205-
impl From<(i64, i64)> for KlinesRequestRange {
246+
impl From<(i64, i64)> for RequestRange {
206247
fn from(value: (i64, i64)) -> Self {
207-
KlinesRequestRange::StartEnd {
248+
RequestRange::StartEnd {
208249
start: DateTime::from_timestamp_millis(value.0).unwrap(),
209250
end: Some(DateTime::from_timestamp_millis(value.1).unwrap()),
210251
}
211252
}
212253
}
254+
255+
#[derive(derive_more::Debug, derive_new::new)]
256+
pub struct OutOfRangeError {
257+
allowed: std::ops::RangeInclusive<u32>,
258+
provided: u32,
259+
}
260+
//TODO!: generalize to both create,display and check for Ranges defined by time too.
261+
impl std::fmt::Display for OutOfRangeError {
262+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
263+
write!(
264+
f,
265+
"Effective provided limit is out of range (could be translated from Start:End / tf). Allowed: {:?}, provided: {}",
266+
self.allowed, self.provided
267+
)
268+
}
269+
}
270+
impl std::error::Error for OutOfRangeError {}
213271
//,}}}
214272

215273
#[derive(Clone, Debug, Default, Copy)]
@@ -227,7 +285,7 @@ pub struct AssetBalance {
227285
#[derive(Clone, Debug, Default)]
228286
pub struct ExchangeInfo {
229287
pub server_time: DateTime<Utc>,
230-
pub pairs: HashMap<Pair, PairInfo>,
288+
pub pairs: BTreeMap<Pair, PairInfo>,
231289
}
232290
impl ExchangeInfo {
233291
pub fn usdt_pairs(&self) -> impl Iterator<Item = Pair> {

0 commit comments

Comments
 (0)