binance_api_client/lib.rs
1//! Async Rust client for the Binance API.
2//!
3//! This library provides a type-safe, async interface to the Binance cryptocurrency
4//! exchange API, supporting both REST and WebSocket endpoints.
5//!
6//! # Features
7//!
8//! - Full coverage of Binance Spot REST API
9//! - WebSocket support for real-time market data streams
10//! - User data stream support for account updates
11//! - Automatic request signing for authenticated endpoints
12//! - Production and testnet environment support
13//! - Binance.US support
14//!
15//! # Quick Start
16//!
17//! ## Public API (No Authentication Required)
18//!
19//! ```rust,ignore
20//! use binance_api_client::Binance;
21//!
22//! #[tokio::main]
23//! async fn main() -> binance_api_client::Result<()> {
24//! // Create a client for public endpoints
25//! let client = Binance::new_unauthenticated()?;
26//!
27//! // Ping the server
28//! client.market().ping().await?;
29//!
30//! // Get server time
31//! let time = client.market().server_time().await?;
32//! println!("Server time: {}", time.server_time);
33//!
34//! Ok(())
35//! }
36//! ```
37//!
38//! ## Authenticated API
39//!
40//! ```rust,ignore
41//! use binance_api_client::Binance;
42//!
43//! #[tokio::main]
44//! async fn main() -> binance_api_client::Result<()> {
45//! // Create an authenticated client
46//! let client = Binance::new("your_api_key", "your_secret_key")?;
47//!
48//! // Access account information
49//! let account = client.account().get_account().await?;
50//! println!("Account balances: {:?}", account.balances);
51//!
52//! Ok(())
53//! }
54//! ```
55//!
56//! ## Using Testnet
57//!
58//! ```rust,ignore
59//! use binance_api_client::{Binance, Config};
60//!
61//! #[tokio::main]
62//! async fn main() -> binance_api_client::Result<()> {
63//! let config = Config::testnet();
64//! let client = Binance::with_config(config, Some(("api_key", "secret_key")))?;
65//!
66//! // Now all requests go to testnet
67//! client.market().ping().await?;
68//!
69//! Ok(())
70//! }
71//! ```
72
73#![deny(
74 unstable_features,
75 unused_must_use,
76 unused_mut,
77 unused_imports,
78 unused_import_braces
79)]
80
81pub mod api;
82pub mod client;
83pub mod config;
84pub mod credentials;
85pub mod error;
86pub mod models;
87pub mod types;
88pub mod websocket;
89
90// Re-export main types at crate root
91pub use client::Client;
92pub use config::{Config, ConfigBuilder};
93pub use credentials::{Credentials, SignatureType};
94pub use error::{Error, Result};
95pub use websocket::{
96 ConnectionHealthMonitor, ConnectionState, DepthCache, DepthCacheConfig, DepthCacheManager,
97 DepthCacheState, ReconnectConfig, ReconnectingWebSocket, UserDataStreamManager,
98 WebSocketClient, WebSocketConnection, WebSocketEventStream,
99};
100
101// Re-export commonly used types
102pub use types::{
103 AccountType, CancelReplaceMode, CancelReplaceResult, CancelRestrictions, ContingencyType,
104 ExecutionType, KlineInterval, OcoOrderStatus, OcoStatus, OrderRateLimitExceededMode,
105 OrderResponseType, OrderSide, OrderStatus, OrderType, RateLimitInterval, RateLimitType,
106 SymbolPermission, SymbolStatus, TickerType, TimeInForce,
107};
108
109// Re-export commonly used models
110pub use models::{
111 // Account models
112 AccountCommission,
113 AccountInfo,
114 // Wallet models
115 AccountSnapshot,
116 AccountSnapshotType,
117 AccountStatus,
118 // Market models
119 AggTrade,
120 Allocation,
121 AmendListStatus,
122 AmendOrderResponse,
123 AmendedOrderInfo,
124 ApiKeyPermissions,
125 ApiTradingStatus,
126 AssetDetail,
127 AveragePrice,
128 Balance,
129 // Margin models
130 BnbBurnStatus,
131 BookTicker,
132 CancelOrderResponse,
133 CancelReplaceErrorData,
134 CancelReplaceErrorInfo,
135 CancelReplaceErrorResponse,
136 CancelReplaceResponse,
137 CancelReplaceSideResponse,
138 CoinInfo,
139 CoinNetwork,
140 DepositAddress,
141 DepositRecord,
142 DepositStatus,
143 ExchangeInfo,
144 Fill,
145 FundingAsset,
146 InterestHistoryRecord,
147 InterestRateRecord,
148 IsolatedAccountLimit,
149 IsolatedAssetDetails,
150 IsolatedMarginAccountAsset,
151 IsolatedMarginAccountDetails,
152 IsolatedMarginTransferType,
153 Kline,
154 ListenKey,
155 LoanRecord,
156 MarginAccountDetails,
157 MarginAsset,
158 MarginAssetInfo,
159 MarginOrderCancellation,
160 MarginOrderResult,
161 MarginOrderState,
162 MarginPairDetails,
163 MarginPriceIndex,
164 MarginTrade,
165 MarginTransferType,
166 MaxBorrowableAmount,
167 MaxTransferableAmount,
168 OcoOrder,
169 OcoOrderDetail,
170 OcoOrderReport,
171 Order,
172 OrderAck,
173 OrderAmendment,
174 OrderBook,
175 OrderBookEntry,
176 OrderFull,
177 OrderResponse,
178 OrderResult,
179 PreventedMatch,
180 RateLimit,
181 RecordsQueryResult,
182 RepayRecord,
183 RollingWindowTicker,
184 RollingWindowTickerMini,
185 ServerTime,
186 SideEffectType,
187 SorOrderCommissionRates,
188 SorOrderTestResponse,
189 Symbol,
190 SymbolFilter,
191 SystemStatus,
192 Ticker24h,
193 TickerPrice,
194 Trade,
195 TradeFee,
196 TradingDayTicker,
197 TradingDayTickerMini,
198 TransactionId,
199 TransferHistory,
200 TransferRecord,
201 TransferResponse,
202 UnfilledOrderCount,
203 UniversalTransferType,
204 UserTrade,
205 WalletBalance,
206 WithdrawRecord,
207 WithdrawResponse,
208 WithdrawStatus,
209 // WebSocket models
210 websocket::{
211 AccountBalance, AccountPositionEvent, AggTradeEvent, BalanceUpdateEvent, BookTickerEvent,
212 DepthEvent, DepthLevel, ExecutionReportEvent, KlineData, KlineEvent, ListStatusEvent,
213 ListStatusOrder, MiniTickerEvent, TickerEvent, TradeEvent, WebSocketEvent,
214 },
215};
216
217// Re-export order builders for convenience
218pub use api::{
219 CancelReplaceOrder, CancelReplaceOrderBuilder, NewOcoOrder, NewOpoOrder, NewOpocoOrder,
220 NewOrder, NewOtoOrder, NewOtocoOrder, OcoOrderBuilder, OpoOrderBuilder, OpocoOrderBuilder,
221 OrderBuilder, OtoOrderBuilder, OtocoOrderBuilder,
222};
223
224/// Main entry point for the Binance API client.
225///
226/// The `Binance` struct provides access to all API modules and handles
227/// configuration and authentication.
228#[derive(Clone)]
229pub struct Binance {
230 client: Client,
231}
232
233impl Binance {
234 /// Create a new authenticated Binance client with default production configuration.
235 ///
236 /// # Arguments
237 ///
238 /// * `api_key` - Your Binance API key
239 /// * `secret_key` - Your Binance secret key
240 ///
241 /// # Example
242 ///
243 /// ```rust,no_run
244 /// use binance_api_client::Binance;
245 ///
246 /// # fn run() -> binance_api_client::Result<()> {
247 /// let client = Binance::new("api_key", "secret_key")?;
248 /// # Ok(())
249 /// # }
250 /// ```
251 pub fn new(api_key: impl Into<String>, secret_key: impl Into<String>) -> Result<Self> {
252 let config = Config::default();
253 let credentials = Credentials::new(api_key, secret_key);
254 let client = Client::new(config, credentials)?;
255 Ok(Self { client })
256 }
257
258 /// Create a new unauthenticated Binance client for public endpoints only.
259 ///
260 /// This client can only access public market data endpoints.
261 /// For account and trading endpoints, use `Binance::new()` with credentials.
262 ///
263 /// # Example
264 ///
265 /// ```rust,no_run
266 /// use binance_api_client::Binance;
267 ///
268 /// # fn run() -> binance_api_client::Result<()> {
269 /// let client = Binance::new_unauthenticated()?;
270 /// # Ok(())
271 /// # }
272 /// ```
273 pub fn new_unauthenticated() -> Result<Self> {
274 let config = Config::default();
275 let client = Client::new_unauthenticated(config)?;
276 Ok(Self { client })
277 }
278
279 /// Create a new Binance client with custom configuration.
280 ///
281 /// # Arguments
282 ///
283 /// * `config` - Custom configuration (testnet, Binance.US, etc.)
284 /// * `credentials` - Optional credentials tuple (api_key, secret_key)
285 ///
286 /// # Example
287 ///
288 /// ```rust,no_run
289 /// use binance_api_client::{Binance, Config};
290 ///
291 /// # fn run() -> binance_api_client::Result<()> {
292 /// // Testnet with credentials
293 /// let config = Config::testnet();
294 /// let client = Binance::with_config(config, Some(("api_key", "secret_key")))?;
295 ///
296 /// // Production without credentials
297 /// let config = Config::default();
298 /// let client = Binance::with_config(config, None::<(&str, &str)>)?;
299 /// # Ok(())
300 /// # }
301 /// ```
302 pub fn with_config<S: Into<String>>(
303 config: Config,
304 credentials: Option<(S, S)>,
305 ) -> Result<Self> {
306 let client = match credentials {
307 Some((api_key, secret_key)) => {
308 let creds = Credentials::new(api_key, secret_key);
309 Client::new(config, creds)?
310 }
311 None => Client::new_unauthenticated(config)?,
312 };
313 Ok(Self { client })
314 }
315
316 /// Create a new Binance client from environment variables.
317 ///
318 /// Expects `BINANCE_API_KEY` and `BINANCE_SECRET_KEY` environment variables.
319 ///
320 /// # Example
321 ///
322 /// ```rust,no_run
323 /// use binance_api_client::Binance;
324 ///
325 /// # fn run() -> binance_api_client::Result<()> {
326 /// let client = Binance::from_env()?;
327 /// # Ok(())
328 /// # }
329 /// ```
330 pub fn from_env() -> Result<Self> {
331 let config = Config::default();
332 let credentials = Credentials::from_env()?;
333 let client = Client::new(config, credentials)?;
334 Ok(Self { client })
335 }
336
337 /// Create a new testnet Binance client with credentials.
338 ///
339 /// # Example
340 ///
341 /// ```rust,no_run
342 /// use binance_api_client::Binance;
343 ///
344 /// # fn run() -> binance_api_client::Result<()> {
345 /// let client = Binance::testnet("api_key", "secret_key")?;
346 /// # Ok(())
347 /// # }
348 /// ```
349 pub fn testnet(api_key: impl Into<String>, secret_key: impl Into<String>) -> Result<Self> {
350 let config = Config::testnet();
351 let credentials = Credentials::new(api_key, secret_key);
352 let client = Client::new(config, credentials)?;
353 Ok(Self { client })
354 }
355
356 /// Create a new testnet Binance client without credentials.
357 ///
358 /// # Example
359 ///
360 /// ```rust,no_run
361 /// use binance_api_client::Binance;
362 ///
363 /// # fn run() -> binance_api_client::Result<()> {
364 /// let client = Binance::testnet_unauthenticated()?;
365 /// # Ok(())
366 /// # }
367 /// ```
368 pub fn testnet_unauthenticated() -> Result<Self> {
369 let config = Config::testnet();
370 let client = Client::new_unauthenticated(config)?;
371 Ok(Self { client })
372 }
373
374 /// Create a new Binance.US client with credentials.
375 ///
376 /// # Example
377 ///
378 /// ```rust,no_run
379 /// use binance_api_client::Binance;
380 ///
381 /// # fn run() -> binance_api_client::Result<()> {
382 /// let client = Binance::binance_us("api_key", "secret_key")?;
383 /// # Ok(())
384 /// # }
385 /// ```
386 pub fn binance_us(api_key: impl Into<String>, secret_key: impl Into<String>) -> Result<Self> {
387 let config = Config::binance_us();
388 let credentials = Credentials::new(api_key, secret_key);
389 let client = Client::new(config, credentials)?;
390 Ok(Self { client })
391 }
392
393 /// Get the underlying HTTP client.
394 ///
395 /// This is useful for advanced use cases where you need direct access
396 /// to the client.
397 pub fn client(&self) -> &Client {
398 &self.client
399 }
400
401 /// Get the current configuration.
402 pub fn config(&self) -> &Config {
403 self.client.config()
404 }
405
406 /// Check if this client has credentials for authenticated endpoints.
407 pub fn has_credentials(&self) -> bool {
408 self.client.has_credentials()
409 }
410
411 /// Access market data API endpoints.
412 ///
413 /// Market data endpoints are public and don't require authentication.
414 ///
415 /// # Example
416 ///
417 /// ```rust,ignore
418 /// let client = Binance::new_unauthenticated()?;
419 ///
420 /// // Get current BTC price
421 /// let price = client.market().price("BTCUSDT").await?;
422 /// println!("BTC/USDT: {}", price.price);
423 ///
424 /// // Get order book
425 /// let depth = client.market().depth("BTCUSDT", Some(10)).await?;
426 /// ```
427 pub fn market(&self) -> api::Market {
428 api::Market::new(self.client.clone())
429 }
430
431 /// Access user data stream API endpoints.
432 ///
433 /// User data streams provide real-time updates for account balance changes,
434 /// order updates, and other account events via WebSocket.
435 ///
436 /// **Requires authentication.**
437 ///
438 /// # Example
439 ///
440 /// ```rust,ignore
441 /// let client = Binance::new("api_key", "secret_key")?;
442 ///
443 /// // Start a user data stream
444 /// let listen_key = client.user_stream().start().await?;
445 ///
446 /// // Keep it alive (call every 30 minutes)
447 /// client.user_stream().keepalive(&listen_key).await?;
448 ///
449 /// // Close when done
450 /// client.user_stream().close(&listen_key).await?;
451 /// ```
452 pub fn user_stream(&self) -> api::UserStream {
453 api::UserStream::new(self.client.clone())
454 }
455
456 /// Access account and trading API endpoints.
457 ///
458 /// Account and trading endpoints require authentication. Use these to
459 /// manage orders, query account balances, and view trade history.
460 ///
461 /// **Requires authentication.**
462 ///
463 /// # Example
464 ///
465 /// ```rust,ignore
466 /// let client = Binance::new("api_key", "secret_key")?;
467 ///
468 /// // Get account info
469 /// let account = client.account().get_account().await?;
470 ///
471 /// // Place a limit buy order
472 /// let order = client.account().limit_buy("BTCUSDT", "0.001", "50000.00").await?;
473 ///
474 /// // Or use the order builder for more control
475 /// use binance_api_client::{OrderBuilder, OrderSide, OrderType, TimeInForce};
476 ///
477 /// let order = OrderBuilder::new("BTCUSDT", OrderSide::Buy, OrderType::Limit)
478 /// .quantity("0.001")
479 /// .price("50000.00")
480 /// .time_in_force(TimeInForce::GTC)
481 /// .build();
482 ///
483 /// let response = client.account().create_order(&order).await?;
484 /// ```
485 pub fn account(&self) -> api::Account {
486 api::Account::new(self.client.clone())
487 }
488
489 /// Access wallet SAPI endpoints.
490 ///
491 /// Wallet endpoints provide access to deposit/withdrawal operations,
492 /// asset management, universal transfers, and account status.
493 ///
494 /// **Requires authentication.**
495 ///
496 /// # Example
497 ///
498 /// ```rust,ignore
499 /// let client = Binance::new("api_key", "secret_key")?;
500 ///
501 /// // Check system status
502 /// let status = client.wallet().system_status().await?;
503 /// if status.is_normal() {
504 /// println!("System is operational");
505 /// }
506 ///
507 /// // Get all coin information
508 /// let coins = client.wallet().all_coins().await?;
509 ///
510 /// // Get deposit address
511 /// let address = client.wallet().deposit_address("BTC", None).await?;
512 /// println!("Deposit BTC to: {}", address.address);
513 ///
514 /// // Get trade fees
515 /// let fees = client.wallet().trade_fee(Some("BTCUSDT")).await?;
516 /// ```
517 pub fn wallet(&self) -> api::Wallet {
518 api::Wallet::new(self.client.clone())
519 }
520
521 /// Access margin trading SAPI endpoints.
522 ///
523 /// Margin endpoints provide access to cross-margin and isolated margin
524 /// trading, loans, transfers, and account management.
525 ///
526 /// **Requires authentication.**
527 ///
528 /// # Example
529 ///
530 /// ```rust,ignore
531 /// let client = Binance::new("api_key", "secret_key")?;
532 ///
533 /// // Get cross-margin account details
534 /// let account = client.margin().account().await?;
535 /// println!("Margin level: {}", account.margin_level);
536 ///
537 /// // Check max borrowable
538 /// let max = client.margin().max_borrowable("BTC", None).await?;
539 /// println!("Max borrowable BTC: {}", max.amount);
540 ///
541 /// // Transfer to margin account
542 /// use binance_api_client::MarginTransferType;
543 /// let result = client.margin()
544 /// .transfer("USDT", "100.0", MarginTransferType::MainToMargin)
545 /// .await?;
546 ///
547 /// // Borrow
548 /// let loan = client.margin().loan("USDT", "50.0", false, None).await?;
549 /// ```
550 pub fn margin(&self) -> api::Margin {
551 api::Margin::new(self.client.clone())
552 }
553
554 /// Access WebSocket streaming API.
555 ///
556 /// The WebSocket client provides real-time market data streams including
557 /// trades, klines, tickers, and order book updates. It also supports
558 /// user data streams for account updates when connected with a listen key.
559 ///
560 /// # Example
561 ///
562 /// ```rust,ignore
563 /// use binance_api_client::Binance;
564 ///
565 /// let client = Binance::new_unauthenticated()?;
566 /// let ws = client.websocket();
567 ///
568 /// // Connect to aggregate trade stream
569 /// let stream_name = ws.agg_trade_stream("btcusdt");
570 /// let mut conn = ws.connect(&stream_name).await?;
571 ///
572 /// // Receive events
573 /// while let Some(event) = conn.next().await {
574 /// println!("{:?}", event?);
575 /// }
576 /// ```
577 pub fn websocket(&self) -> websocket::WebSocketClient {
578 websocket::WebSocketClient::new(self.client.config().clone())
579 }
580}
581
582impl std::fmt::Debug for Binance {
583 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
584 f.debug_struct("Binance")
585 .field("config", self.config())
586 .field("has_credentials", &self.has_credentials())
587 .finish()
588 }
589}
590
591#[cfg(test)]
592mod tests {
593 use super::*;
594
595 #[test]
596 fn test_new_unauthenticated() {
597 let client = Binance::new_unauthenticated().unwrap();
598 assert!(!client.has_credentials());
599 assert_eq!(client.config().rest_api_endpoint, "https://api.binance.com");
600 }
601
602 #[test]
603 fn test_new_authenticated() {
604 let client = Binance::new("api_key", "secret_key").unwrap();
605 assert!(client.has_credentials());
606 }
607
608 #[test]
609 fn test_testnet() {
610 let client = Binance::testnet("api_key", "secret_key").unwrap();
611 assert!(client.has_credentials());
612 assert_eq!(
613 client.config().rest_api_endpoint,
614 "https://testnet.binance.vision"
615 );
616 }
617
618 #[test]
619 fn test_testnet_unauthenticated() {
620 let client = Binance::testnet_unauthenticated().unwrap();
621 assert!(!client.has_credentials());
622 assert_eq!(
623 client.config().rest_api_endpoint,
624 "https://testnet.binance.vision"
625 );
626 }
627
628 #[test]
629 fn test_binance_us() {
630 let client = Binance::binance_us("api_key", "secret_key").unwrap();
631 assert!(client.has_credentials());
632 assert_eq!(client.config().rest_api_endpoint, "https://api.binance.us");
633 }
634
635 #[test]
636 fn test_with_config() {
637 let config = Config::builder()
638 .rest_api_endpoint("https://custom.api.com")
639 .build();
640 let client = Binance::with_config(config, Some(("api_key", "secret_key"))).unwrap();
641 assert!(client.has_credentials());
642 assert_eq!(client.config().rest_api_endpoint, "https://custom.api.com");
643 }
644
645 #[test]
646 fn test_with_config_no_credentials() {
647 let config = Config::default();
648 let client = Binance::with_config(config, None::<(&str, &str)>).unwrap();
649 assert!(!client.has_credentials());
650 }
651
652 #[test]
653 fn test_debug_output() {
654 let client = Binance::new("api_key", "secret_key").unwrap();
655 let debug_output = format!("{:?}", client);
656 assert!(debug_output.contains("Binance"));
657 assert!(debug_output.contains("has_credentials: true"));
658 // Should not contain the actual secret key
659 assert!(!debug_output.contains("secret_key"));
660 }
661}