rusty_common/websocket/exchanges/
config.rs

1//! Exchange-specific WebSocket configurations
2//!
3//! Provides detailed configuration for each exchange's WebSocket implementation,
4//! including compression settings, ping/pong behavior, and other quirks.
5
6use super::super::config::CompressionConfig;
7use smartstring::alias::String;
8
9/// Exchange-specific WebSocket configuration
10#[derive(Debug, Clone)]
11pub struct ExchangeConfig {
12    /// Exchange name
13    pub exchange: String,
14    /// Compression configuration
15    pub compression: CompressionConfig,
16    /// Custom ping message (if exchange requires specific format)
17    pub custom_ping_message: Option<String>,
18    /// Custom pong response (if exchange requires specific format)
19    pub custom_pong_response: Option<String>,
20    /// Whether to send unsolicited pongs
21    pub send_unsolicited_pongs: bool,
22    /// Ping interval override (milliseconds)
23    pub ping_interval_override: Option<u64>,
24}
25
26impl ExchangeConfig {
27    /// Create configuration for Binance
28    ///
29    /// Note: Binance doesn't support WebSocket compression
30    #[must_use]
31    pub fn binance() -> Self {
32        Self {
33            exchange: "binance".into(),
34            compression: CompressionConfig::disabled(),
35            custom_ping_message: None,
36            custom_pong_response: None,
37            send_unsolicited_pongs: true, // Binance allows unsolicited pongs
38            ping_interval_override: Some(180_000), // 3 minutes
39        }
40    }
41
42    /// Create configuration for Binance Futures
43    ///
44    /// Known issue: window_bits=15 causes JSON errors (missing last 2 bytes)
45    #[must_use]
46    pub fn binance_futures() -> Self {
47        Self {
48            exchange: "binance_futures".into(),
49            compression: CompressionConfig::disabled(), // Disabled due to known issues
50            custom_ping_message: None,
51            custom_pong_response: None,
52            send_unsolicited_pongs: true,
53            ping_interval_override: Some(180_000), // 3 minutes
54        }
55    }
56
57    /// Create configuration for Coinbase
58    #[must_use]
59    pub fn coinbase() -> Self {
60        Self {
61            exchange: "coinbase".into(),
62            compression: CompressionConfig {
63                enabled: true,
64                client_max_window_bits: 15,
65                server_max_window_bits: 15,
66                client_no_context_takeover: false,
67                server_no_context_takeover: false,
68            },
69            custom_ping_message: None,
70            custom_pong_response: None,
71            send_unsolicited_pongs: false,
72            ping_interval_override: None,
73        }
74    }
75
76    /// Create configuration for Upbit
77    #[must_use]
78    pub fn upbit() -> Self {
79        Self {
80            exchange: "upbit".into(),
81            compression: CompressionConfig {
82                enabled: true,
83                client_max_window_bits: 15,
84                server_max_window_bits: 15,
85                client_no_context_takeover: false,
86                server_no_context_takeover: false,
87            },
88            custom_ping_message: Some("PING".into()),
89            custom_pong_response: Some("{\"status\":\"UP\"}".into()),
90            send_unsolicited_pongs: false,
91            ping_interval_override: Some(60000), // 1 minute
92        }
93    }
94
95    /// Create configuration for Bybit
96    ///
97    /// Note: Bybit has known issues with compression negotiation
98    #[must_use]
99    pub fn bybit() -> Self {
100        Self {
101            exchange: "bybit".into(),
102            compression: CompressionConfig::disabled(), // Disabled due to buggy implementation
103            custom_ping_message: Some("{\"op\":\"ping\"}".into()),
104            custom_pong_response: Some("{\"op\":\"pong\"}".into()),
105            send_unsolicited_pongs: false,
106            ping_interval_override: Some(20000), // 20 seconds
107        }
108    }
109
110    /// Create configuration for Bithumb
111    ///
112    /// Note: No documented compression support
113    #[must_use]
114    pub fn bithumb() -> Self {
115        Self {
116            exchange: "bithumb".into(),
117            compression: CompressionConfig::disabled(),
118            custom_ping_message: Some("{\"cmd\":\"ping\"}".into()),
119            custom_pong_response: Some("{\"code\":\"0\",\"msg\":\"pong\"}".into()),
120            send_unsolicited_pongs: false,
121            ping_interval_override: None,
122        }
123    }
124
125    /// Get configuration for a specific exchange
126    #[must_use]
127    pub fn for_exchange(exchange: &str) -> Self {
128        match exchange.to_lowercase().as_str() {
129            "binance" => Self::binance(),
130            "binance_futures" => Self::binance_futures(),
131            "coinbase" => Self::coinbase(),
132            "upbit" => Self::upbit(),
133            "bybit" => Self::bybit(),
134            "bithumb" => Self::bithumb(),
135            _ => Self::default(),
136        }
137    }
138}
139
140impl Default for ExchangeConfig {
141    fn default() -> Self {
142        Self {
143            exchange: "generic".into(),
144            compression: CompressionConfig::disabled(), // Safer default
145            custom_ping_message: None,
146            custom_pong_response: None,
147            send_unsolicited_pongs: false,
148            ping_interval_override: None,
149        }
150    }
151}
152
153/// Summary of WebSocket compression support across exchanges
154///
155/// | Exchange | Compression Support | Notes |
156/// |----------|-------------------|-------|
157/// | Binance  | ❌ None          | Streams already optimized; Futures has window_bits=15 bug |
158/// | Coinbase | ✅ permessage-deflate | Standard support, no customization |
159/// | Upbit    | ✅ permessage-deflate | Full RFC 7692 support |
160/// | Bybit    | ❌ Disabled      | Known buggy implementation, causes NegotiationError |
161/// | Bithumb  | ❌ None          | No documented support in official API docs |
162pub const fn get_compression_summary() -> &'static str {
163    "
164    | Exchange | Compression Support | Notes |
165    |----------|-------------------|-------|
166    | Binance  | ❌ None          | Streams already optimized; Futures has window_bits=15 bug |
167    | Coinbase | ✅ permessage-deflate | Standard support, no customization |
168    | Upbit    | ✅ permessage-deflate | Full RFC 7692 support |
169    | Bybit    | ❌ Disabled      | Known buggy implementation, causes NegotiationError |
170    | Bithumb  | ❌ None          | No documented support in official API docs |
171    "
172}
173
174/// Check if an exchange supports WebSocket compression
175pub fn supports_websocket_compression(exchange: &str) -> bool {
176    matches!(exchange.to_lowercase().as_str(), "coinbase" | "upbit")
177}