Skip to main content

nautilus_binance/common/
consts.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2026 Nautech Systems Pty Ltd. All rights reserved.
3//  https://nautechsystems.io
4//
5//  Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
6//  You may not use this file except in compliance with the License.
7//  You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
8//
9//  Unless required by applicable law or agreed to in writing, software
10//  distributed under the License is distributed on an "AS IS" BASIS,
11//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//  See the License for the specific language governing permissions and
13//  limitations under the License.
14// -------------------------------------------------------------------------------------------------
15
16//! Binance venue constants and API endpoints.
17
18use std::{num::NonZeroU32, sync::LazyLock, time::Duration};
19
20use nautilus_model::identifiers::{ClientId, Venue};
21use nautilus_network::ratelimiter::quota::Quota;
22use ustr::Ustr;
23
24use super::enums::{BinanceRateLimitInterval, BinanceRateLimitType};
25
26/// The Binance venue identifier string.
27pub const BINANCE: &str = "BINANCE";
28
29/// Static venue instance for Binance.
30pub static BINANCE_VENUE: LazyLock<Venue> = LazyLock::new(|| Venue::new(Ustr::from(BINANCE)));
31
32/// Static client ID instance for Binance.
33pub static BINANCE_CLIENT_ID: LazyLock<ClientId> =
34    LazyLock::new(|| ClientId::new(Ustr::from(BINANCE)));
35
36/// Binance Link and Trade broker ID for Spot.
37///
38/// <https://developers.binance.com/docs/binance_link/link-and-trade>
39pub const BINANCE_NAUTILUS_SPOT_BROKER_ID: &str = "TD67BGP9";
40
41/// Binance Link and Trade broker ID for Futures.
42///
43/// <https://developers.binance.com/docs/binance_link/link-and-trade>
44pub const BINANCE_NAUTILUS_FUTURES_BROKER_ID: &str = "aHRE4BCj";
45
46/// Binance Spot API base URL (live exchange).
47pub const BINANCE_SPOT_HTTP_URL: &str = "https://api.binance.com";
48
49/// Binance US Spot API base URL.
50pub const BINANCE_US_SPOT_HTTP_URL: &str = "https://api.binance.us";
51
52/// Binance USD-M Futures API base URL (live exchange).
53pub const BINANCE_FUTURES_USD_HTTP_URL: &str = "https://fapi.binance.com";
54
55/// Binance COIN-M Futures API base URL (live exchange).
56pub const BINANCE_FUTURES_COIN_HTTP_URL: &str = "https://dapi.binance.com";
57
58/// Default WebSocket heartbeat interval in seconds.
59///
60/// Binance caps ping/pong frames at 5 per second, so this sits well inside the limit while keeping
61/// the derived liveness window tight.
62pub const BINANCE_WS_HEARTBEAT_SECS: u64 = 20;
63
64/// Binance European Options API base URL (live exchange).
65pub const BINANCE_OPTIONS_HTTP_URL: &str = "https://eapi.binance.com";
66
67/// Binance European Options API base URL (testnet/demo).
68pub const BINANCE_OPTIONS_TESTNET_HTTP_URL: &str = "https://testnet.binancefuture.com";
69
70/// Binance Spot API base URL (testnet).
71pub const BINANCE_SPOT_TESTNET_HTTP_URL: &str = "https://testnet.binance.vision";
72
73/// Binance USD-M Futures API base URL (testnet).
74pub const BINANCE_FUTURES_USD_TESTNET_HTTP_URL: &str = "https://testnet.binancefuture.com";
75
76/// Binance COIN-M Futures API base URL (testnet).
77pub const BINANCE_FUTURES_COIN_TESTNET_HTTP_URL: &str = "https://testnet.binancefuture.com";
78
79/// Binance Spot API base URL (demo).
80pub const BINANCE_SPOT_DEMO_HTTP_URL: &str = "https://demo-api.binance.com";
81
82/// Binance USD-M Futures API base URL (demo).
83pub const BINANCE_FUTURES_USD_DEMO_HTTP_URL: &str = "https://demo-fapi.binance.com";
84
85/// Binance COIN-M Futures API base URL (demo).
86pub const BINANCE_FUTURES_COIN_DEMO_HTTP_URL: &str = "https://demo-dapi.binance.com";
87
88/// Binance Spot WebSocket base URL (live exchange).
89pub const BINANCE_SPOT_WS_URL: &str = "wss://stream.binance.com:9443/ws";
90
91/// Binance US Spot public WebSocket base URL.
92pub const BINANCE_US_SPOT_WS_URL: &str = "wss://stream.binance.us:9443/ws";
93
94/// Binance US Spot user data WebSocket root URL.
95pub const BINANCE_US_SPOT_USER_WS_URL: &str = "wss://stream.binance.us:443";
96
97/// Binance USD-M Futures WebSocket base URL (live exchange).
98pub const BINANCE_FUTURES_USD_WS_URL: &str = "wss://fstream.binance.com/market/ws";
99
100/// Binance USD-M Futures WebSocket public stream URL (live exchange, high-frequency book data).
101pub const BINANCE_FUTURES_USD_WS_PUBLIC_URL: &str = "wss://fstream.binance.com/public/ws";
102
103/// Binance USD-M Futures WebSocket private stream URL (live exchange).
104pub const BINANCE_FUTURES_USD_WS_PRIVATE_URL: &str = "wss://fstream.binance.com/private/ws";
105
106/// Binance COIN-M Futures WebSocket base URL (live exchange).
107pub const BINANCE_FUTURES_COIN_WS_URL: &str = "wss://dstream.binance.com/ws";
108
109/// Binance European Options WebSocket base URL (live exchange).
110pub const BINANCE_OPTIONS_WS_URL: &str = "wss://nbstream.binance.com/eoptions";
111
112/// Binance European Options WebSocket base URL (testnet/demo).
113pub const BINANCE_OPTIONS_TESTNET_WS_URL: &str = "wss://fstream.binancefuture.com/market/ws";
114
115/// Binance European Options WebSocket public stream URL (testnet/demo).
116pub const BINANCE_OPTIONS_TESTNET_WS_PUBLIC_URL: &str = "wss://fstream.binancefuture.com/public/ws";
117
118/// Binance European Options WebSocket private stream URL (testnet/demo).
119pub const BINANCE_OPTIONS_TESTNET_WS_PRIVATE_URL: &str =
120    "wss://fstream.binancefuture.com/private/ws";
121
122/// Binance Spot SBE WebSocket stream URL (live exchange).
123pub const BINANCE_SPOT_SBE_WS_URL: &str = "wss://stream-sbe.binance.com/ws";
124
125/// Binance Spot SBE WebSocket API URL (live exchange).
126pub const BINANCE_SPOT_SBE_WS_API_URL: &str =
127    "wss://ws-api.binance.com:443/ws-api/v3?responseFormat=sbe&sbeSchemaId=3&sbeSchemaVersion=5";
128
129/// Binance USD-M Futures WebSocket Trading API URL (live exchange).
130pub const BINANCE_FUTURES_USD_WS_API_URL: &str = "wss://ws-fapi.binance.com/ws-fapi/v1";
131
132/// Binance USD-M Futures WebSocket Trading API URL (testnet).
133pub const BINANCE_FUTURES_USD_WS_API_TESTNET_URL: &str =
134    "wss://testnet.binancefuture.com/ws-fapi/v1";
135
136/// Binance Spot SBE WebSocket API URL (testnet).
137pub const BINANCE_SPOT_SBE_WS_API_TESTNET_URL: &str = "wss://ws-api.testnet.binance.vision/ws-api/v3?responseFormat=sbe&sbeSchemaId=3&sbeSchemaVersion=5";
138
139/// Binance Spot SBE WebSocket API URL (demo).
140pub const BINANCE_SPOT_SBE_WS_API_DEMO_URL: &str =
141    "wss://demo-ws-api.binance.com/ws-api/v3?responseFormat=sbe&sbeSchemaId=3&sbeSchemaVersion=5";
142
143/// Binance Spot WebSocket base URL (testnet).
144pub const BINANCE_SPOT_TESTNET_WS_URL: &str = "wss://stream.testnet.binance.vision/ws";
145
146/// Binance Spot WebSocket base URL (demo).
147pub const BINANCE_SPOT_DEMO_WS_URL: &str = "wss://demo-stream.binance.com/ws";
148
149/// Binance USD-M Futures WebSocket base URL (demo).
150pub const BINANCE_FUTURES_USD_DEMO_WS_URL: &str = "wss://demo-fstream.binance.com/ws";
151
152/// Binance COIN-M Futures WebSocket base URL (demo).
153pub const BINANCE_FUTURES_COIN_DEMO_WS_URL: &str = "wss://demo-dstream.binance.com/ws";
154
155/// Binance USD-M Futures WebSocket base URL (testnet).
156pub const BINANCE_FUTURES_USD_TESTNET_WS_URL: &str = "wss://fstream.binancefuture.com/ws";
157
158/// Binance COIN-M Futures WebSocket base URL (testnet).
159pub const BINANCE_FUTURES_COIN_TESTNET_WS_URL: &str = "wss://dstream.binancefuture.com/ws";
160
161/// HTTP header name for the Binance API key.
162pub const BINANCE_API_KEY_HEADER: &str = "X-MBX-APIKEY";
163
164/// HTTP response header carrying the venue's minimum retry delay on HTTP 429 and 418 responses.
165pub const BINANCE_RETRY_AFTER_HEADER: &str = "Retry-After";
166
167/// Binance Spot API version path.
168pub const BINANCE_SPOT_API_PATH: &str = "/api/v3";
169
170/// Binance USD-M Futures API version path.
171pub const BINANCE_FAPI_PATH: &str = "/fapi/v1";
172
173/// Binance COIN-M Futures API version path.
174pub const BINANCE_DAPI_PATH: &str = "/dapi/v1";
175
176/// Binance European Options API version path.
177pub const BINANCE_EAPI_PATH: &str = "/eapi/v1";
178
179/// Binance SAPI version path for wallet, margin, and other account management endpoints.
180pub const BINANCE_SAPI_PATH: &str = "/sapi/v1";
181
182/// Describes a static rate limit quota for a product type.
183#[derive(Clone, Copy, Debug)]
184pub struct BinanceRateLimitQuota {
185    /// Rate limit type.
186    pub rate_limit_type: BinanceRateLimitType,
187    /// Time interval unit.
188    pub interval: BinanceRateLimitInterval,
189    /// Number of intervals.
190    pub interval_num: u32,
191    /// Maximum allowed requests for the interval.
192    pub limit: u32,
193}
194
195/// Spot & margin REST limits (default IP weights).
196///
197/// References:
198/// - <https://developers.binance.com/docs/binance-spot-api-docs/limits>
199pub const BINANCE_SPOT_RATE_LIMITS: &[BinanceRateLimitQuota] = &[
200    BinanceRateLimitQuota {
201        rate_limit_type: BinanceRateLimitType::RequestWeight,
202        interval: BinanceRateLimitInterval::Minute,
203        interval_num: 1,
204        limit: 1_200,
205    },
206    BinanceRateLimitQuota {
207        rate_limit_type: BinanceRateLimitType::Orders,
208        interval: BinanceRateLimitInterval::Second,
209        interval_num: 1,
210        limit: 10,
211    },
212    BinanceRateLimitQuota {
213        rate_limit_type: BinanceRateLimitType::Orders,
214        interval: BinanceRateLimitInterval::Day,
215        interval_num: 1,
216        limit: 100_000,
217    },
218];
219
220/// USD-M Futures REST limits (default IP weights).
221///
222/// References:
223/// - <https://developers.binance.com/docs/derivatives/usds-margined-futures/general-info#limits>
224pub const BINANCE_FAPI_RATE_LIMITS: &[BinanceRateLimitQuota] = &[
225    BinanceRateLimitQuota {
226        rate_limit_type: BinanceRateLimitType::RequestWeight,
227        interval: BinanceRateLimitInterval::Minute,
228        interval_num: 1,
229        limit: 2_400,
230    },
231    BinanceRateLimitQuota {
232        rate_limit_type: BinanceRateLimitType::Orders,
233        interval: BinanceRateLimitInterval::Second,
234        interval_num: 10,
235        limit: 300,
236    },
237    BinanceRateLimitQuota {
238        rate_limit_type: BinanceRateLimitType::Orders,
239        interval: BinanceRateLimitInterval::Minute,
240        interval_num: 1,
241        limit: 1_200,
242    },
243];
244
245/// COIN-M Futures REST limits (shared with USD-M Futures).
246///
247/// References:
248/// - <https://developers.binance.com/docs/derivatives/coin-margined-futures/Important-CM-UM-Integration-Notice#a3-um-and-cm-share-the-same-rate-limit-pools>
249pub const BINANCE_DAPI_RATE_LIMITS: &[BinanceRateLimitQuota] = &[
250    BinanceRateLimitQuota {
251        rate_limit_type: BinanceRateLimitType::RequestWeight,
252        interval: BinanceRateLimitInterval::Minute,
253        interval_num: 1,
254        limit: 2_400,
255    },
256    BinanceRateLimitQuota {
257        rate_limit_type: BinanceRateLimitType::Orders,
258        interval: BinanceRateLimitInterval::Second,
259        interval_num: 10,
260        limit: 300,
261    },
262    BinanceRateLimitQuota {
263        rate_limit_type: BinanceRateLimitType::Orders,
264        interval: BinanceRateLimitInterval::Minute,
265        interval_num: 1,
266        limit: 1_200,
267    },
268];
269
270/// Options REST limits (default IP weights).
271///
272/// References:
273/// - <https://developers.binance.com/docs/derivatives/european-options/general-info#limits>
274pub const BINANCE_EAPI_RATE_LIMITS: &[BinanceRateLimitQuota] = &[
275    BinanceRateLimitQuota {
276        rate_limit_type: BinanceRateLimitType::RequestWeight,
277        interval: BinanceRateLimitInterval::Minute,
278        interval_num: 1,
279        limit: 3_000,
280    },
281    BinanceRateLimitQuota {
282        rate_limit_type: BinanceRateLimitType::Orders,
283        interval: BinanceRateLimitInterval::Second,
284        interval_num: 1,
285        limit: 5,
286    },
287    BinanceRateLimitQuota {
288        rate_limit_type: BinanceRateLimitType::Orders,
289        interval: BinanceRateLimitInterval::Minute,
290        interval_num: 1,
291        limit: 200,
292    },
293];
294
295/// WebSocket subscription rate limit: one message per 300 ms with no burst.
296///
297/// Binance limits incoming WebSocket messages (subscribe/unsubscribe) to 5 per second and
298/// closes the connection with code 1008 ("Too many requests") when that is exceeded. A
299/// `Quota::per_second(5)` token bucket allows a burst of 5 followed by one refill every
300/// 200 ms, so up to 9 messages can land within the first rolling second and trip the
301/// limit. A fixed 300 ms period (about 3.3 messages per second) spaces every message
302/// evenly and stays under the venue limit with headroom.
303pub static BINANCE_WS_SUBSCRIPTION_QUOTA: LazyLock<Quota> =
304    LazyLock::new(|| Quota::with_period(Duration::from_millis(300)).expect("valid constant"));
305
306/// WebSocket connection rate limit: 1 per second (conservative).
307///
308/// Binance limits connections to 300 per 5 minutes per IP. This conservative quota
309/// of 1 per second helps avoid hitting the connection limit during reconnection storms.
310pub static BINANCE_WS_CONNECTION_QUOTA: LazyLock<Quota> = LazyLock::new(|| {
311    Quota::per_second(NonZeroU32::new(1).expect("non-zero")).expect("valid constant")
312});
313
314/// Pre-interned rate limit key for WebSocket subscription operations.
315pub static BINANCE_RATE_LIMIT_KEY_SUBSCRIPTION: LazyLock<[Ustr; 1]> =
316    LazyLock::new(|| [Ustr::from("subscription")]);
317
318/// Binance error code for GTX (post-only) order rejection.
319///
320/// Returned when a GTX order would immediately match as taker.
321pub const BINANCE_GTX_ORDER_REJECT_CODE: i64 = -5022;
322
323/// Binance error code for new order rejected.
324///
325/// For spot LIMIT_MAKER orders, this code is returned with the message
326/// "Order would immediately match and take." to indicate a post-only rejection.
327pub const BINANCE_NEW_ORDER_REJECTED_CODE: i64 = -2010;
328
329/// Binance error code returned when an order is not found.
330pub const BINANCE_NO_SUCH_ORDER_CODE: i64 = -2013;
331
332/// Binance error code returned when order execution status is unknown.
333pub const BINANCE_UNEXPECTED_RESPONSE_CODE: i64 = -1006;
334
335/// Binance error code returned when order execution status is unknown.
336pub const BINANCE_STATUS_UNKNOWN_CODE: i64 = -1007;
337
338/// Binance USD-M Futures error code for `dualSidePosition` sync rejection between
339/// UM (USDM) and CM (Coin-M) accounts on Portfolio Margin.
340///
341/// Returned when an order or position-mode change is incompatible with the
342/// account-level hedge mode that PM keeps in sync across UM/CM.
343pub const BINANCE_FUTURES_DUAL_SIDE_SYNC_REJECT_CODE: i64 = -4531;
344
345/// Binance Spot LIMIT_MAKER rejection message.
346///
347/// This message is specific to post-only (LIMIT_MAKER) orders that would match immediately.
348pub const BINANCE_SPOT_POST_ONLY_REJECT_MSG: &str = "Order would immediately match and take.";
349
350/// Valid order book depth levels for Binance.
351pub const BINANCE_BOOK_DEPTHS: [u32; 7] = [5, 10, 20, 50, 100, 500, 1000];
352
353#[cfg(test)]
354mod tests {
355    use rstest::rstest;
356
357    use super::*;
358
359    #[rstest]
360    fn test_venue_error_code_values() {
361        assert_eq!(BINANCE_GTX_ORDER_REJECT_CODE, -5022);
362        assert_eq!(BINANCE_NEW_ORDER_REJECTED_CODE, -2010);
363        assert_eq!(BINANCE_NO_SUCH_ORDER_CODE, -2013);
364        assert_eq!(BINANCE_UNEXPECTED_RESPONSE_CODE, -1006);
365        assert_eq!(BINANCE_STATUS_UNKNOWN_CODE, -1007);
366        assert_eq!(BINANCE_FUTURES_DUAL_SIDE_SYNC_REJECT_CODE, -4531);
367    }
368
369    #[rstest]
370    #[case(BINANCE_SPOT_SBE_WS_API_URL)]
371    #[case(BINANCE_SPOT_SBE_WS_API_TESTNET_URL)]
372    #[case(BINANCE_SPOT_SBE_WS_API_DEMO_URL)]
373    fn test_spot_sbe_ws_api_urls_use_current_schema(#[case] url: &str) {
374        assert!(url.ends_with("sbeSchemaId=3&sbeSchemaVersion=5"));
375    }
376
377    #[rstest]
378    fn test_ws_subscription_quota_paces_one_message_per_period() {
379        let quota = *BINANCE_WS_SUBSCRIPTION_QUOTA;
380        assert_eq!(quota.burst_size().get(), 1);
381        assert_eq!(quota.replenish_interval(), Duration::from_millis(300));
382    }
383}