Skip to content

Commit 121f00b

Browse files
committed
feat: recv_window handling
1 parent 713497f commit 121f00b

File tree

9 files changed

+69
-35
lines changed

9 files changed

+69
-35
lines changed

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-4631-lightblue)
5+
![Lines Of Code](https://img.shields.io/badge/LoC-4655-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/binance/market_futures.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ async fn main() {
2121

2222
if let (Ok(key), Ok(secret)) = (env::var("BINANCE_TIGER_READ_KEY"), env::var("BINANCE_TIGER_READ_SECRET")) {
2323
c.auth(key, secret.into());
24+
c.set_recv_window(10000);
2425
let balance_usdt = c.asset_balance("USDT".into(), m).await.unwrap();
2526
dbg!(&balance_usdt);
2627
let balances = c.balances(m).await.unwrap();

examples/data.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use v_exchanges::{binance::Binance, bitmex::Bitmex};
1+
use v_exchanges::{adapters::binance::BinanceOption, binance::Binance, bitmex::Bitmex};
22

33
/// 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.
44
#[tokio::main]

v_exchanges/src/binance/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ impl Exchange for Binance {
3232
self.update_default_option(BinanceOption::Secret(secret));
3333
}
3434

35+
fn set_recv_window(&mut self, recv_window: u16) {
36+
self.update_default_option(BinanceOption::RecvWindow(recv_window));
37+
}
38+
3539
async fn exchange_info(&self, am: AbsMarket) -> Result<ExchangeInfo> {
3640
match am {
3741
AbsMarket::Binance(m) => match m {

v_exchanges/src/bybit/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ impl Exchange for Bybit {
3838
self.update_default_option(BybitOption::Secret(secret));
3939
}
4040

41+
fn set_recv_window(&mut self, recv_window: u16) {
42+
self.update_default_option(BybitOption::RecvWindow(recv_window));
43+
}
44+
4145
async fn exchange_info(&self, am: AbsMarket) -> Result<ExchangeInfo> {
4246
match am {
4347
AbsMarket::Bybit(_) => todo!(),

v_exchanges/src/core.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,19 @@ use v_utils::{
1212

1313
#[async_trait::async_trait]
1414
pub trait Exchange: std::fmt::Debug + Send {
15+
// dev {{{
1516
/// will always be `Some` when created from `AbsMarket`. When creating client manually could lead to weird errors from this method being used elsewhere, like displaying a `AbsMarket` object.
1617
fn source_market(&self) -> AbsMarket;
1718
fn exchange_name(&self) -> &'static str {
1819
self.source_market().exchange_name()
1920
}
21+
//,}}}
2022

23+
// Config {{{
2124
fn auth(&mut self, key: String, secret: SecretString);
25+
/// Set number of **milliseconds** the request is valid for. Recv Window of over a minute does not make sense, thus it's expressed as u16.
26+
fn set_recv_window(&mut self, recv_window: u16);
27+
//,}}}
2228

2329
async fn exchange_info(&self, m: AbsMarket) -> Result<ExchangeInfo>;
2430

v_exchanges/src/mexc/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ impl Exchange for Mexc {
3838
self.update_default_option(MexcOption::Secret(secret));
3939
}
4040

41+
fn set_recv_window(&mut self, recv_window: u16) {
42+
self.update_default_option(MexcOption::RecvWindow(recv_window));
43+
}
44+
4145
async fn exchange_info(&self, am: AbsMarket) -> Result<ExchangeInfo> {
4246
match am {
4347
AbsMarket::Mexc(_) => todo!(),

v_exchanges_adapters/src/exchanges/binance.rs

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ pub enum BinanceOption {
2626
Key(String),
2727
/// Api secret
2828
Secret(SecretString),
29+
/// Number of milliseconds the request is valid for. Only applicable for signed requests.
30+
RecvWindow(u16),
2931
/// Base url for HTTP requests
3032
HttpUrl(BinanceHttpUrl),
3133
/// Authentication type for HTTP requests
@@ -49,6 +51,8 @@ pub struct BinanceOptions {
4951
/// see [BinanceOption::Secret]
5052
#[debug("[REDACTED]")]
5153
pub secret: Option<SecretString>,
54+
// see [BinanceOption::RecvWindow]
55+
pub recv_window: Option<u16>,
5256
/// see [BinanceOption::HttpUrl]
5357
pub http_url: BinanceHttpUrl,
5458
/// see [BinanceOption::HttpAuth]
@@ -62,7 +66,7 @@ pub struct BinanceOptions {
6266
}
6367

6468
/// A `enum` that represents the base url of the Binance REST API.
65-
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
69+
#[derive(Debug, Eq, PartialEq, Copy, Clone, Default)]
6670
#[non_exhaustive]
6771
pub enum BinanceHttpUrl {
6872
/// `https://api.binance.com`
@@ -88,11 +92,12 @@ pub enum BinanceHttpUrl {
8892
/// `https://eapi.binance.com`
8993
EuropeanOptions,
9094
/// The url will not be modified by [BinanceRequestHandler]
95+
#[default]
9196
None,
9297
}
9398

9499
/// A `enum` that represents the base url of the Binance WebSocket API
95-
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
100+
#[derive(Debug, Eq, PartialEq, Copy, Clone, Default)]
96101
#[non_exhaustive]
97102
pub enum BinanceWebSocketUrl {
98103
/// `wss://stream.binance.com:9443`
@@ -120,13 +125,15 @@ pub enum BinanceWebSocketUrl {
120125
/// `wss://nbstream.binance.com`
121126
EuropeanOptions,
122127
/// The url will not be modified by [BinanceRequestHandler]
128+
#[default]
123129
None,
124130
}
125131

126-
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
132+
#[derive(Debug, Eq, PartialEq, Copy, Clone, Default)]
127133
pub enum BinanceAuth {
128134
Sign,
129135
Key,
136+
#[default]
130137
None,
131138
}
132139

@@ -173,7 +180,6 @@ where
173180
config
174181
}
175182

176-
//XXX: could print creds
177183
#[tracing::instrument(skip_all, fields(?builder))]
178184
fn build_request(&self, mut builder: RequestBuilder, request_body: &Option<B>, _: u8) -> Result<Request, Self::BuildError> {
179185
if let Some(body) = request_body {
@@ -191,6 +197,9 @@ where
191197
let timestamp = time.as_millis();
192198

193199
builder = builder.query(&[("timestamp", timestamp)]);
200+
if let Some(recv_window) = self.options.recv_window {
201+
builder = builder.query(&[("recvWindow", recv_window)]);
202+
}
194203

195204
let secret = self.options.secret.as_ref().map(|s| s.expose_secret()).ok_or("API secret not set")?;
196205
let mut hmac = Hmac::<Sha256>::new_from_slice(secret.as_bytes()).unwrap(); // hmac accepts key of any length
@@ -323,6 +332,7 @@ impl HandlerOptions for BinanceOptions {
323332
match option {
324333
BinanceOption::Default => (),
325334
BinanceOption::Key(v) => self.key = Some(v),
335+
BinanceOption::RecvWindow(v) => self.recv_window = Some(v),
326336
BinanceOption::Secret(v) => self.secret = Some(v),
327337
BinanceOption::HttpUrl(v) => self.http_url = v,
328338
BinanceOption::HttpAuth(v) => self.http_auth = v,
@@ -345,10 +355,11 @@ impl Default for BinanceOptions {
345355
Self {
346356
key: None,
347357
secret: None,
348-
http_url: BinanceHttpUrl::None,
349-
http_auth: BinanceAuth::None,
358+
recv_window: None,
359+
http_url: BinanceHttpUrl::default(),
360+
http_auth: BinanceAuth::default(),
350361
request_config: RequestConfig::default(),
351-
websocket_url: BinanceWebSocketUrl::None,
362+
websocket_url: BinanceWebSocketUrl::default(),
352363
websocket_config,
353364
}
354365
}

v_exchanges_adapters/src/exchanges/bybit.rs

Lines changed: 30 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ pub enum BybitOption {
3232
/// Type of authentication used for HTTP requests.
3333
HttpAuth(BybitHttpAuth),
3434
/// receive window parameter used for requests
35-
RecvWindow(i32),
35+
RecvWindow(u16),
3636
/// [RequestConfig] used when sending requests.
3737
/// `url_prefix` will be overridden by [HttpUrl](Self::HttpUrl) unless `HttpUrl` is [BybitHttpUrl::None].
3838
RequestConfig(RequestConfig),
@@ -61,7 +61,7 @@ pub struct BybitOptions {
6161
/// see [BybitOption::HttpAuth]
6262
pub http_auth: BybitHttpAuth,
6363
/// see [BybitOption::RecvWindow]
64-
pub recv_window: Option<i32>,
64+
pub recv_window: Option<u16>,
6565
/// see [BybitOption::RequestConfig]
6666
pub request_config: RequestConfig,
6767
/// see [BybitOption::WebSocketUrl]
@@ -75,9 +75,10 @@ pub struct BybitOptions {
7575
}
7676

7777
/// A `enum` that represents the base url of the Bybit REST API.
78-
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
78+
#[derive(Debug, Eq, PartialEq, Copy, Clone, Default)]
7979
pub enum BybitHttpUrl {
8080
/// `https://api.bybit.com`
81+
#[default]
8182
Bybit,
8283
/// `https://api.bytick.com`
8384
Bytick,
@@ -88,9 +89,10 @@ pub enum BybitHttpUrl {
8889
}
8990

9091
/// A `enum` that represents the base url of the Bybit WebSocket API.
91-
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
92+
#[derive(Debug, Eq, PartialEq, Copy, Clone, Default)]
9293
pub enum BybitWebSocketUrl {
9394
/// `wss://stream.bybit.com`
95+
#[default]
9496
Bybit,
9597
/// `wss://stream.bytick.com`
9698
Bytick,
@@ -101,7 +103,7 @@ pub enum BybitWebSocketUrl {
101103
}
102104

103105
/// Represents the auth type.
104-
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
106+
#[derive(Debug, Eq, PartialEq, Copy, Clone, Default)]
105107
pub enum BybitHttpAuth {
106108
/// [Spot V1](https://bybit-exchange.github.io/docs-legacy/spot/v1/#t-introduction)
107109
SpotV1,
@@ -115,6 +117,7 @@ pub enum BybitHttpAuth {
115117
/// [V3](https://bybit-exchange.github.io/docs/v3/intro) and [V5](https://bybit-exchange.github.io/docs/v5/intro)
116118
V3AndAbove,
117119
/// No authentication (for public APIs)
120+
#[default]
118121
None,
119122
}
120123

@@ -215,7 +218,7 @@ where
215218
timestamp: u128,
216219
mut hmac: Hmac<Sha256>,
217220
spot: bool,
218-
window: Option<i32>,
221+
window: Option<u16>,
219222
) -> Result<Request, <BybitRequestHandler<'a, R> as RequestHandler<B>>::BuildError>
220223
where
221224
B: Serialize, {
@@ -315,7 +318,7 @@ where
315318
timestamp: u128,
316319
mut hmac: Hmac<Sha256>,
317320
version_header: bool,
318-
window: Option<i32>,
321+
window: Option<u16>,
319322
) -> Result<Request, <BybitRequestHandler<'a, R> as RequestHandler<B>>::BuildError>
320323
where
321324
B: Serialize, {
@@ -470,6 +473,26 @@ impl BybitWebSocketUrl {
470473
}
471474
}
472475

476+
impl Default for BybitOptions {
477+
fn default() -> Self {
478+
let mut websocket_config = WebSocketConfig::new();
479+
websocket_config.ignore_duplicate_during_reconnection = true;
480+
481+
Self {
482+
websocket_config,
483+
key: None,
484+
secret: None,
485+
http_url: BybitHttpUrl::default(),
486+
http_auth: BybitHttpAuth::default(),
487+
recv_window: None,
488+
request_config: Default::default(),
489+
websocket_url: BybitWebSocketUrl::default(),
490+
websocket_auth: false,
491+
websocket_topics: Vec::new(),
492+
}
493+
}
494+
}
495+
473496
impl HandlerOptions for BybitOptions {
474497
type OptionItem = BybitOption;
475498

@@ -494,25 +517,6 @@ impl HandlerOptions for BybitOptions {
494517
}
495518
}
496519

497-
impl Default for BybitOptions {
498-
fn default() -> Self {
499-
let mut websocket_config = WebSocketConfig::new();
500-
websocket_config.ignore_duplicate_during_reconnection = true;
501-
Self {
502-
key: None,
503-
secret: None,
504-
http_url: BybitHttpUrl::Bybit,
505-
http_auth: BybitHttpAuth::None,
506-
recv_window: None,
507-
request_config: RequestConfig::default(),
508-
websocket_url: BybitWebSocketUrl::Bybit,
509-
websocket_auth: false,
510-
websocket_topics: vec![],
511-
websocket_config,
512-
}
513-
}
514-
}
515-
516520
impl<'a, R, B> HttpOption<'a, R, B> for BybitOption
517521
where
518522
R: DeserializeOwned + 'a,

0 commit comments

Comments
 (0)