Skip to content

Commit 3cb78c2

Browse files
authored
001 binance ws fix (#43)
* fix(binance): refactor WebSocket client to fix subscription payload and state management Refactor BinanceWs to use MessageRouter and SubscriptionManager. Fix subscription payload format to satisfy Binance requirements. Implement watch_*_internal helper methods. Update Binance exchange implementation to use new internal methods. Ensure synchronous subscriptions() method. Add integration test for payload verification. * chore(websocket): refactor binance websocket * fix(websocket): clean unused fn
1 parent df6899e commit 3cb78c2

12 files changed

Lines changed: 1381 additions & 660 deletions

File tree

‎.gitignore‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,6 @@ _bmad/
4747
_bmad-output/
4848
docs/
4949
AGENTS.md
50+
CLAUDE.md
51+
GEMINI.md
52+

‎ccxt-core/src/base_exchange/mod.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ use tokio::sync::{Mutex, RwLock};
3232
use tracing::{debug, info, warn};
3333

3434
/// Base exchange implementation
35-
#[derive(Debug)]
35+
#[derive(Debug, Clone)]
3636
pub struct BaseExchange {
3737
/// Exchange configuration
3838
pub config: ExchangeConfig,

‎ccxt-core/src/http_client/builder.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use std::sync::Arc;
88
use super::config::HttpConfig;
99

1010
/// HTTP client with retry and rate limiting support
11-
#[derive(Debug)]
11+
#[derive(Debug, Clone)]
1212
pub struct HttpClient {
1313
client: Client,
1414
config: HttpConfig,

‎ccxt-core/src/retry_strategy.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ impl RetryConfig {
174174
}
175175

176176
/// Retry strategy.
177-
#[derive(Debug)]
177+
#[derive(Debug, Clone)]
178178
pub struct RetryStrategy {
179179
config: RetryConfig,
180180
}

‎ccxt-exchanges/src/binance/mod.rs‎

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ pub use signed_request::{HttpMethod, SignedRequestBuilder};
3333
pub use time_sync::{TimeSyncConfig, TimeSyncManager};
3434
pub use urls::BinanceUrls;
3535
/// Binance exchange structure.
36-
#[derive(Debug)]
36+
#[derive(Debug, Clone)]
3737
pub struct Binance {
3838
/// Base exchange instance.
3939
base: BaseExchange,
@@ -42,9 +42,13 @@ pub struct Binance {
4242
/// Time synchronization manager for caching server time offset.
4343
time_sync: Arc<TimeSyncManager>,
4444
/// Persistent WebSocket connection reference for state tracking.
45+
#[deprecated(note = "Use connection_manager instead")]
4546
ws_connection: Arc<RwLock<Option<ws::BinanceWs>>>,
47+
/// Centralized WebSocket connection manager
48+
pub connection_manager: Arc<ws::BinanceConnectionManager>,
4649
}
4750

51+
#[allow(deprecated)]
4852
impl Binance {
4953
/// Creates a new Binance instance using the builder pattern.
5054
///
@@ -93,11 +97,27 @@ impl Binance {
9397
let options = BinanceOptions::default();
9498
let time_sync = Arc::new(TimeSyncManager::new());
9599

100+
// Determine WS URL for ConnectionManager
101+
let urls = if base.config.sandbox {
102+
BinanceUrls::testnet()
103+
} else {
104+
BinanceUrls::production()
105+
};
106+
// Apply overrides logic similar to self.urls()
107+
let ws_url = if let Some(url) = base.config.url_overrides.get("ws") {
108+
url.clone()
109+
} else {
110+
urls.ws.clone()
111+
};
112+
113+
let connection_manager = Arc::new(ws::BinanceConnectionManager::new(ws_url));
114+
96115
Ok(Self {
97116
base,
98117
options,
99118
time_sync,
100119
ws_connection: Arc::new(RwLock::new(None)),
120+
connection_manager,
101121
})
102122
}
103123

@@ -136,11 +156,44 @@ impl Binance {
136156
};
137157
let time_sync = Arc::new(TimeSyncManager::with_config(time_sync_config));
138158

159+
// Determine WS URL
160+
let mut urls = if base.config.sandbox {
161+
BinanceUrls::testnet()
162+
} else {
163+
BinanceUrls::production()
164+
};
165+
166+
// Apply overrides
167+
if let Some(ws_url) = base.config.url_overrides.get("ws") {
168+
urls.ws.clone_from(ws_url);
169+
}
170+
if let Some(ws_fapi_url) = base.config.url_overrides.get("wsFapi") {
171+
urls.ws_fapi.clone_from(ws_fapi_url);
172+
}
173+
if let Some(ws_dapi_url) = base.config.url_overrides.get("wsDapi") {
174+
urls.ws_dapi.clone_from(ws_dapi_url);
175+
}
176+
if let Some(ws_eapi_url) = base.config.url_overrides.get("wsEapi") {
177+
urls.ws_eapi.clone_from(ws_eapi_url);
178+
}
179+
180+
let ws_url = match options.default_type {
181+
DefaultType::Swap | DefaultType::Futures => match options.default_sub_type {
182+
Some(DefaultSubType::Inverse) => urls.ws_dapi.clone(),
183+
_ => urls.ws_fapi.clone(),
184+
},
185+
DefaultType::Option => urls.ws_eapi.clone(),
186+
_ => urls.ws.clone(),
187+
};
188+
189+
let connection_manager = Arc::new(ws::BinanceConnectionManager::new(ws_url));
190+
139191
Ok(Self {
140192
base,
141193
options,
142194
time_sync,
143195
ws_connection: Arc::new(RwLock::new(None)),
196+
connection_manager,
144197
})
145198
}
146199

@@ -174,11 +227,28 @@ impl Binance {
174227
};
175228
let time_sync = Arc::new(TimeSyncManager::with_config(time_sync_config));
176229

230+
// Determine WS URL (Futures/Swap)
231+
let mut urls = if base.config.sandbox {
232+
BinanceUrls::testnet()
233+
} else {
234+
BinanceUrls::production()
235+
};
236+
237+
if let Some(ws_fapi_url) = base.config.url_overrides.get("wsFapi") {
238+
urls.ws_fapi.clone_from(ws_fapi_url);
239+
}
240+
241+
// Default to FAPI/Linear for new_swap if not configured otherwise
242+
let ws_url = urls.ws_fapi.clone();
243+
244+
let connection_manager = Arc::new(ws::BinanceConnectionManager::new(ws_url));
245+
177246
Ok(Self {
178247
base,
179248
options,
180249
time_sync,
181250
ws_connection: Arc::new(RwLock::new(None)),
251+
connection_manager,
182252
})
183253
}
184254

0 commit comments

Comments
 (0)