ccxt_exchanges/binance/builder.rs
1//! Binance exchange builder pattern implementation.
2//!
3//! Provides a fluent API for constructing Binance exchange instances with
4//! type-safe configuration options.
5
6use super::{Binance, BinanceOptions};
7use ccxt_core::{ExchangeConfig, Result};
8use serde_json::Value;
9use std::collections::HashMap;
10
11/// Builder for creating Binance exchange instances.
12///
13/// Provides a fluent API for configuring all aspects of the Binance exchange,
14/// including authentication, connection settings, and Binance-specific options.
15///
16/// # Example
17///
18/// ```no_run
19/// use ccxt_exchanges::binance::BinanceBuilder;
20///
21/// let binance = BinanceBuilder::new()
22/// .api_key("your-api-key")
23/// .secret("your-secret")
24/// .sandbox(true)
25/// .timeout(30)
26/// .build()
27/// .unwrap();
28/// ```
29#[derive(Debug, Clone)]
30pub struct BinanceBuilder {
31 /// Exchange configuration
32 config: ExchangeConfig,
33 /// Binance-specific options
34 options: BinanceOptions,
35}
36
37impl Default for BinanceBuilder {
38 fn default() -> Self {
39 Self::new()
40 }
41}
42
43impl BinanceBuilder {
44 /// Creates a new builder with default configuration.
45 ///
46 /// # Example
47 ///
48 /// ```no_run
49 /// use ccxt_exchanges::binance::BinanceBuilder;
50 ///
51 /// let builder = BinanceBuilder::new();
52 /// ```
53 pub fn new() -> Self {
54 Self {
55 config: ExchangeConfig {
56 id: "binance".to_string(),
57 name: "Binance".to_string(),
58 ..Default::default()
59 },
60 options: BinanceOptions::default(),
61 }
62 }
63
64 /// Sets the API key for authentication.
65 ///
66 /// # Arguments
67 ///
68 /// * `key` - The API key string.
69 ///
70 /// # Example
71 ///
72 /// ```no_run
73 /// use ccxt_exchanges::binance::BinanceBuilder;
74 ///
75 /// let builder = BinanceBuilder::new()
76 /// .api_key("your-api-key");
77 /// ```
78 pub fn api_key(mut self, key: impl Into<String>) -> Self {
79 self.config.api_key = Some(key.into());
80 self
81 }
82
83 /// Sets the API secret for authentication.
84 ///
85 /// # Arguments
86 ///
87 /// * `secret` - The API secret string.
88 ///
89 /// # Example
90 ///
91 /// ```no_run
92 /// use ccxt_exchanges::binance::BinanceBuilder;
93 ///
94 /// let builder = BinanceBuilder::new()
95 /// .secret("your-secret");
96 /// ```
97 pub fn secret(mut self, secret: impl Into<String>) -> Self {
98 self.config.secret = Some(secret.into());
99 self
100 }
101
102 /// Enables or disables sandbox/testnet mode.
103 ///
104 /// When enabled, the exchange will connect to Binance's testnet
105 /// instead of the production environment.
106 ///
107 /// # Arguments
108 ///
109 /// * `enabled` - Whether to enable sandbox mode.
110 ///
111 /// # Example
112 ///
113 /// ```no_run
114 /// use ccxt_exchanges::binance::BinanceBuilder;
115 ///
116 /// let builder = BinanceBuilder::new()
117 /// .sandbox(true);
118 /// ```
119 pub fn sandbox(mut self, enabled: bool) -> Self {
120 self.config.sandbox = enabled;
121 self.options.test = enabled;
122 self
123 }
124
125 /// Sets the request timeout in seconds.
126 ///
127 /// # Arguments
128 ///
129 /// * `seconds` - Timeout duration in seconds.
130 ///
131 /// # Example
132 ///
133 /// ```no_run
134 /// use ccxt_exchanges::binance::BinanceBuilder;
135 ///
136 /// let builder = BinanceBuilder::new()
137 /// .timeout(60);
138 /// ```
139 pub fn timeout(mut self, seconds: u64) -> Self {
140 self.config.timeout = seconds;
141 self
142 }
143
144 /// Sets the receive window for signed requests.
145 ///
146 /// The receive window specifies how long a request is valid after
147 /// the timestamp. Default is 5000 milliseconds.
148 ///
149 /// # Arguments
150 ///
151 /// * `millis` - Receive window in milliseconds.
152 ///
153 /// # Example
154 ///
155 /// ```no_run
156 /// use ccxt_exchanges::binance::BinanceBuilder;
157 ///
158 /// let builder = BinanceBuilder::new()
159 /// .recv_window(10000);
160 /// ```
161 pub fn recv_window(mut self, millis: u64) -> Self {
162 self.options.recv_window = millis;
163 self
164 }
165
166 /// Sets the default trading type.
167 ///
168 /// Valid values: "spot", "margin", "future", "delivery", "option".
169 ///
170 /// # Arguments
171 ///
172 /// * `trading_type` - The default trading type.
173 ///
174 /// # Example
175 ///
176 /// ```no_run
177 /// use ccxt_exchanges::binance::BinanceBuilder;
178 ///
179 /// let builder = BinanceBuilder::new()
180 /// .default_type("future");
181 /// ```
182 pub fn default_type(mut self, trading_type: impl Into<String>) -> Self {
183 self.options.default_type = trading_type.into();
184 self
185 }
186
187 /// Enables or disables time synchronization adjustment.
188 ///
189 /// When enabled, the exchange will adjust for time differences
190 /// between the local system and Binance servers.
191 ///
192 /// # Arguments
193 ///
194 /// * `enabled` - Whether to enable time adjustment.
195 ///
196 /// # Example
197 ///
198 /// ```no_run
199 /// use ccxt_exchanges::binance::BinanceBuilder;
200 ///
201 /// let builder = BinanceBuilder::new()
202 /// .adjust_for_time_difference(true);
203 /// ```
204 pub fn adjust_for_time_difference(mut self, enabled: bool) -> Self {
205 self.options.adjust_for_time_difference = enabled;
206 self
207 }
208
209 /// Enables or disables demo environment.
210 ///
211 /// # Arguments
212 ///
213 /// * `enabled` - Whether to enable demo mode.
214 ///
215 /// # Example
216 ///
217 /// ```no_run
218 /// use ccxt_exchanges::binance::BinanceBuilder;
219 ///
220 /// let builder = BinanceBuilder::new()
221 /// .demo(true);
222 /// ```
223 pub fn demo(mut self, enabled: bool) -> Self {
224 self.options.demo = enabled;
225 self
226 }
227
228 /// Enables or disables rate limiting.
229 ///
230 /// # Arguments
231 ///
232 /// * `enabled` - Whether to enable rate limiting.
233 ///
234 /// # Example
235 ///
236 /// ```no_run
237 /// use ccxt_exchanges::binance::BinanceBuilder;
238 ///
239 /// let builder = BinanceBuilder::new()
240 /// .enable_rate_limit(true);
241 /// ```
242 pub fn enable_rate_limit(mut self, enabled: bool) -> Self {
243 self.config.enable_rate_limit = enabled;
244 self
245 }
246
247 /// Sets the HTTP proxy server URL.
248 ///
249 /// # Arguments
250 ///
251 /// * `proxy` - The proxy server URL.
252 ///
253 /// # Example
254 ///
255 /// ```no_run
256 /// use ccxt_exchanges::binance::BinanceBuilder;
257 ///
258 /// let builder = BinanceBuilder::new()
259 /// .proxy("http://proxy.example.com:8080");
260 /// ```
261 pub fn proxy(mut self, proxy: impl Into<String>) -> Self {
262 self.config.proxy = Some(proxy.into());
263 self
264 }
265
266 /// Enables or disables verbose logging.
267 ///
268 /// # Arguments
269 ///
270 /// * `enabled` - Whether to enable verbose logging.
271 ///
272 /// # Example
273 ///
274 /// ```no_run
275 /// use ccxt_exchanges::binance::BinanceBuilder;
276 ///
277 /// let builder = BinanceBuilder::new()
278 /// .verbose(true);
279 /// ```
280 pub fn verbose(mut self, enabled: bool) -> Self {
281 self.config.verbose = enabled;
282 self
283 }
284
285 /// Sets a custom option.
286 ///
287 /// # Arguments
288 ///
289 /// * `key` - Option key.
290 /// * `value` - Option value as JSON.
291 ///
292 /// # Example
293 ///
294 /// ```no_run
295 /// use ccxt_exchanges::binance::BinanceBuilder;
296 /// use serde_json::json;
297 ///
298 /// let builder = BinanceBuilder::new()
299 /// .option("customOption", json!("value"));
300 /// ```
301 pub fn option(mut self, key: impl Into<String>, value: Value) -> Self {
302 self.config.options.insert(key.into(), value);
303 self
304 }
305
306 /// Sets multiple custom options.
307 ///
308 /// # Arguments
309 ///
310 /// * `options` - HashMap of option key-value pairs.
311 ///
312 /// # Example
313 ///
314 /// ```no_run
315 /// use ccxt_exchanges::binance::BinanceBuilder;
316 /// use serde_json::json;
317 /// use std::collections::HashMap;
318 ///
319 /// let mut options = HashMap::new();
320 /// options.insert("option1".to_string(), json!("value1"));
321 /// options.insert("option2".to_string(), json!(42));
322 ///
323 /// let builder = BinanceBuilder::new()
324 /// .options(options);
325 /// ```
326 pub fn options(mut self, options: HashMap<String, Value>) -> Self {
327 self.config.options.extend(options);
328 self
329 }
330
331 /// Builds the Binance exchange instance.
332 ///
333 /// # Returns
334 ///
335 /// Returns a `Result` containing the configured `Binance` instance.
336 ///
337 /// # Errors
338 ///
339 /// Returns an error if the exchange cannot be initialized.
340 ///
341 /// # Example
342 ///
343 /// ```no_run
344 /// use ccxt_exchanges::binance::BinanceBuilder;
345 ///
346 /// let binance = BinanceBuilder::new()
347 /// .api_key("your-api-key")
348 /// .secret("your-secret")
349 /// .build()
350 /// .unwrap();
351 /// ```
352 pub fn build(self) -> Result<Binance> {
353 Binance::new_with_options(self.config, self.options)
354 }
355}
356
357#[cfg(test)]
358mod tests {
359 use super::*;
360
361 #[test]
362 fn test_builder_default() {
363 let builder = BinanceBuilder::new();
364 assert_eq!(builder.config.id, "binance");
365 assert_eq!(builder.config.name, "Binance");
366 assert!(!builder.config.sandbox);
367 assert_eq!(builder.options.default_type, "spot");
368 }
369
370 #[test]
371 fn test_builder_api_key() {
372 let builder = BinanceBuilder::new().api_key("test-key");
373 assert_eq!(builder.config.api_key, Some("test-key".to_string()));
374 }
375
376 #[test]
377 fn test_builder_secret() {
378 let builder = BinanceBuilder::new().secret("test-secret");
379 assert_eq!(builder.config.secret, Some("test-secret".to_string()));
380 }
381
382 #[test]
383 fn test_builder_sandbox() {
384 let builder = BinanceBuilder::new().sandbox(true);
385 assert!(builder.config.sandbox);
386 assert!(builder.options.test);
387 }
388
389 #[test]
390 fn test_builder_timeout() {
391 let builder = BinanceBuilder::new().timeout(60);
392 assert_eq!(builder.config.timeout, 60);
393 }
394
395 #[test]
396 fn test_builder_recv_window() {
397 let builder = BinanceBuilder::new().recv_window(10000);
398 assert_eq!(builder.options.recv_window, 10000);
399 }
400
401 #[test]
402 fn test_builder_default_type() {
403 let builder = BinanceBuilder::new().default_type("future");
404 assert_eq!(builder.options.default_type, "future");
405 }
406
407 #[test]
408 fn test_builder_chaining() {
409 let builder = BinanceBuilder::new()
410 .api_key("key")
411 .secret("secret")
412 .sandbox(true)
413 .timeout(30)
414 .recv_window(5000)
415 .default_type("spot");
416
417 assert_eq!(builder.config.api_key, Some("key".to_string()));
418 assert_eq!(builder.config.secret, Some("secret".to_string()));
419 assert!(builder.config.sandbox);
420 assert_eq!(builder.config.timeout, 30);
421 assert_eq!(builder.options.recv_window, 5000);
422 assert_eq!(builder.options.default_type, "spot");
423 }
424
425 #[test]
426 fn test_builder_build() {
427 let result = BinanceBuilder::new().build();
428 assert!(result.is_ok());
429
430 let binance = result.unwrap();
431 assert_eq!(binance.id(), "binance");
432 assert_eq!(binance.name(), "Binance");
433 }
434
435 #[test]
436 fn test_builder_build_with_credentials() {
437 let result = BinanceBuilder::new()
438 .api_key("test-key")
439 .secret("test-secret")
440 .build();
441
442 assert!(result.is_ok());
443 }
444}