Skip to main content

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}