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}