Skip to main content

binance_api_client/
config.rs

1use std::time::Duration;
2
3/// Production REST API base URL.
4pub const REST_API_ENDPOINT: &str = "https://api.binance.com";
5
6/// Production WebSocket base URL.
7pub const WS_ENDPOINT: &str = "wss://stream.binance.com:9443";
8
9/// Testnet REST API base URL.
10pub const TESTNET_REST_API_ENDPOINT: &str = "https://testnet.binance.vision";
11
12/// Testnet WebSocket base URL.
13pub const TESTNET_WS_ENDPOINT: &str = "wss://testnet.binance.vision";
14
15/// Binance.US REST API base URL.
16pub const BINANCE_US_REST_API_ENDPOINT: &str = "https://api.binance.us";
17
18/// Binance.US WebSocket base URL.
19pub const BINANCE_US_WS_ENDPOINT: &str = "wss://stream.binance.us:9443";
20
21/// Default recv_window in milliseconds.
22pub const DEFAULT_RECV_WINDOW: u64 = 5000;
23
24/// Configuration for the Binance client.
25#[derive(Clone, Debug, PartialEq, Eq)]
26pub struct Config {
27    /// REST API base URL.
28    pub rest_api_endpoint: String,
29
30    /// WebSocket base URL.
31    pub ws_endpoint: String,
32
33    /// Receive window in milliseconds.
34    /// This is the number of milliseconds after the timestamp
35    /// that the request is valid for.
36    pub recv_window: u64,
37
38    /// Request timeout duration.
39    pub timeout: Option<Duration>,
40
41    /// Whether this is configured for Binance.US.
42    pub binance_us: bool,
43}
44
45impl Config {
46    /// Create a new configuration builder.
47    pub fn builder() -> ConfigBuilder {
48        ConfigBuilder::default()
49    }
50
51    /// Create a configuration for the testnet.
52    pub fn testnet() -> Self {
53        Config {
54            rest_api_endpoint: TESTNET_REST_API_ENDPOINT.to_string(),
55            ws_endpoint: TESTNET_WS_ENDPOINT.to_string(),
56            recv_window: DEFAULT_RECV_WINDOW,
57            timeout: None,
58            binance_us: false,
59        }
60    }
61
62    /// Create a configuration for Binance.US.
63    pub fn binance_us() -> Self {
64        Config {
65            rest_api_endpoint: BINANCE_US_REST_API_ENDPOINT.to_string(),
66            ws_endpoint: BINANCE_US_WS_ENDPOINT.to_string(),
67            recv_window: DEFAULT_RECV_WINDOW,
68            timeout: None,
69            binance_us: true,
70        }
71    }
72}
73
74impl Default for Config {
75    /// Create a configuration with production defaults.
76    fn default() -> Self {
77        Config {
78            rest_api_endpoint: REST_API_ENDPOINT.to_string(),
79            ws_endpoint: WS_ENDPOINT.to_string(),
80            recv_window: DEFAULT_RECV_WINDOW,
81            timeout: None,
82            binance_us: false,
83        }
84    }
85}
86
87/// Builder for creating a custom Config.
88#[derive(Clone, Debug, Default)]
89pub struct ConfigBuilder {
90    rest_api_endpoint: Option<String>,
91    ws_endpoint: Option<String>,
92    recv_window: Option<u64>,
93    timeout: Option<Duration>,
94    binance_us: bool,
95}
96
97impl ConfigBuilder {
98    /// Set the REST API endpoint.
99    pub fn rest_api_endpoint(mut self, endpoint: impl Into<String>) -> Self {
100        self.rest_api_endpoint = Some(endpoint.into());
101        self
102    }
103
104    /// Set the WebSocket endpoint.
105    pub fn ws_endpoint(mut self, endpoint: impl Into<String>) -> Self {
106        self.ws_endpoint = Some(endpoint.into());
107        self
108    }
109
110    /// Set the receive window in milliseconds.
111    pub fn recv_window(mut self, recv_window: u64) -> Self {
112        self.recv_window = Some(recv_window);
113        self
114    }
115
116    /// Set the request timeout.
117    pub fn timeout(mut self, timeout: Duration) -> Self {
118        self.timeout = Some(timeout);
119        self
120    }
121
122    /// Set the request timeout from seconds.
123    pub fn timeout_secs(self, secs: u64) -> Self {
124        self.timeout(Duration::from_secs(secs))
125    }
126
127    /// Configure for Binance.US.
128    pub fn binance_us(mut self, is_binance_us: bool) -> Self {
129        self.binance_us = is_binance_us;
130        self
131    }
132
133    /// Build the configuration.
134    pub fn build(self) -> Config {
135        let (default_rest, default_ws) = if self.binance_us {
136            (BINANCE_US_REST_API_ENDPOINT, BINANCE_US_WS_ENDPOINT)
137        } else {
138            (REST_API_ENDPOINT, WS_ENDPOINT)
139        };
140
141        Config {
142            rest_api_endpoint: self
143                .rest_api_endpoint
144                .unwrap_or_else(|| default_rest.to_string()),
145            ws_endpoint: self.ws_endpoint.unwrap_or_else(|| default_ws.to_string()),
146            recv_window: self.recv_window.unwrap_or(DEFAULT_RECV_WINDOW),
147            timeout: self.timeout,
148            binance_us: self.binance_us,
149        }
150    }
151}
152
153#[cfg(test)]
154mod tests {
155    use super::*;
156
157    #[test]
158    fn test_default_config() {
159        let config = Config::default();
160        assert_eq!(config.rest_api_endpoint, REST_API_ENDPOINT);
161        assert_eq!(config.ws_endpoint, WS_ENDPOINT);
162        assert_eq!(config.recv_window, DEFAULT_RECV_WINDOW);
163        assert!(config.timeout.is_none());
164        assert!(!config.binance_us);
165    }
166
167    #[test]
168    fn test_testnet_config() {
169        let config = Config::testnet();
170        assert_eq!(config.rest_api_endpoint, TESTNET_REST_API_ENDPOINT);
171        assert_eq!(config.ws_endpoint, TESTNET_WS_ENDPOINT);
172        assert_eq!(config.recv_window, DEFAULT_RECV_WINDOW);
173        assert!(!config.binance_us);
174    }
175
176    #[test]
177    fn test_binance_us_config() {
178        let config = Config::binance_us();
179        assert_eq!(config.rest_api_endpoint, BINANCE_US_REST_API_ENDPOINT);
180        assert_eq!(config.ws_endpoint, BINANCE_US_WS_ENDPOINT);
181        assert!(config.binance_us);
182    }
183
184    #[test]
185    fn test_config_builder() {
186        let config = Config::builder()
187            .rest_api_endpoint("https://custom.api.com")
188            .ws_endpoint("wss://custom.ws.com")
189            .recv_window(3000)
190            .timeout_secs(30)
191            .build();
192
193        assert_eq!(config.rest_api_endpoint, "https://custom.api.com");
194        assert_eq!(config.ws_endpoint, "wss://custom.ws.com");
195        assert_eq!(config.recv_window, 3000);
196        assert_eq!(config.timeout, Some(Duration::from_secs(30)));
197    }
198
199    #[test]
200    fn test_config_builder_binance_us_defaults() {
201        let config = Config::builder().binance_us(true).build();
202
203        assert_eq!(config.rest_api_endpoint, BINANCE_US_REST_API_ENDPOINT);
204        assert_eq!(config.ws_endpoint, BINANCE_US_WS_ENDPOINT);
205        assert!(config.binance_us);
206    }
207}