deribit_base/
constants.rs

1/******************************************************************************
2   Author: Joaquín Béjar García
3   Email: jb@taunais.com
4   Date: 22/7/25
5******************************************************************************/
6
7//! Deribit API Constants
8//!
9//! This module contains all the constants used throughout the Deribit API implementation,
10//! including URLs, limits, timeouts, and other configuration values based on the
11//! official Deribit API v2.1.1 documentation.
12
13// =============================================================================
14// API ENDPOINTS
15// =============================================================================
16
17/// Deribit production WebSocket URL
18pub const DERIBIT_WS_URL_PROD: &str = "wss://www.deribit.com/ws/api/v2";
19
20/// Deribit test WebSocket URL
21pub const DERIBIT_WS_URL_TEST: &str = "wss://test.deribit.com/ws/api/v2";
22
23/// Deribit production HTTP API URL
24pub const DERIBIT_HTTP_URL_PROD: &str = "https://www.deribit.com/api/v2";
25
26/// Deribit test HTTP API URL
27pub const DERIBIT_HTTP_URL_TEST: &str = "https://test.deribit.com/api/v2";
28
29// =============================================================================
30// RATE LIMITS
31// =============================================================================
32
33/// Maximum requests per second for authenticated users
34pub const MAX_REQUESTS_PER_SECOND_AUTH: u32 = 20;
35
36/// Maximum requests per second for non-authenticated users
37pub const MAX_REQUESTS_PER_SECOND_UNAUTH: u32 = 10;
38
39/// Maximum subscriptions per connection
40pub const MAX_SUBSCRIPTIONS_PER_CONNECTION: u32 = 200;
41
42/// Maximum message size in bytes
43pub const MAX_MESSAGE_SIZE_BYTES: usize = 65536; // 64KB
44
45// =============================================================================
46// TIMEOUTS
47// =============================================================================
48
49/// Default connection timeout in milliseconds
50pub const DEFAULT_CONNECTION_TIMEOUT_MS: u64 = 5000;
51
52/// Default request timeout in milliseconds
53pub const DEFAULT_REQUEST_TIMEOUT_MS: u64 = 10000;
54
55/// Heartbeat interval in milliseconds
56pub const HEARTBEAT_INTERVAL_MS: u64 = 10000;
57
58/// Maximum time to wait for heartbeat response in milliseconds
59pub const HEARTBEAT_TIMEOUT_MS: u64 = 5000;
60
61// =============================================================================
62// CURRENCIES
63// =============================================================================
64
65/// Supported cryptocurrencies
66pub const SUPPORTED_CRYPTOCURRENCIES: &[&str] = &["BTC", "ETH", "SOL", "USDC", "USDT", "EURR"];
67
68/// Bitcoin currency code
69pub const CURRENCY_BTC: &str = "BTC";
70
71/// Ethereum currency code
72pub const CURRENCY_ETH: &str = "ETH";
73
74/// Solana currency code
75pub const CURRENCY_SOL: &str = "SOL";
76
77/// USD Coin currency code
78pub const CURRENCY_USDC: &str = "USDC";
79
80/// Tether currency code
81pub const CURRENCY_USDT: &str = "USDT";
82
83/// Euro stablecoin currency code
84pub const CURRENCY_EURR: &str = "EURR";
85
86// =============================================================================
87// INSTRUMENT TYPES
88// =============================================================================
89
90/// Future instrument type
91pub const INSTRUMENT_TYPE_FUTURE: &str = "future";
92
93/// Option instrument type
94pub const INSTRUMENT_TYPE_OPTION: &str = "option";
95
96/// Perpetual instrument type
97pub const INSTRUMENT_TYPE_PERPETUAL: &str = "perpetual";
98
99/// Spot instrument type
100pub const INSTRUMENT_TYPE_SPOT: &str = "spot";
101
102/// Future combo instrument type
103pub const INSTRUMENT_TYPE_FUTURE_COMBO: &str = "future_combo";
104
105/// Option combo instrument type
106pub const INSTRUMENT_TYPE_OPTION_COMBO: &str = "option_combo";
107
108// =============================================================================
109// ORDER LIMITS
110// =============================================================================
111
112/// Minimum order amount for BTC
113pub const MIN_ORDER_AMOUNT_BTC: f64 = 0.0001;
114
115/// Minimum order amount for ETH
116pub const MIN_ORDER_AMOUNT_ETH: f64 = 0.001;
117
118/// Minimum order amount for SOL
119pub const MIN_ORDER_AMOUNT_SOL: f64 = 0.1;
120
121/// Maximum order amount (no specific limit, but practical limit)
122pub const MAX_ORDER_AMOUNT: f64 = 1_000_000.0;
123
124/// Maximum number of open orders per instrument
125pub const MAX_OPEN_ORDERS_PER_INSTRUMENT: u32 = 500;
126
127/// Maximum number of open orders total
128pub const MAX_OPEN_ORDERS_TOTAL: u32 = 2000;
129
130// =============================================================================
131// PRECISION
132// =============================================================================
133
134/// Price precision for BTC instruments (8 decimal places)
135pub const PRICE_PRECISION_BTC: u8 = 8;
136
137/// Price precision for ETH instruments (4 decimal places)
138pub const PRICE_PRECISION_ETH: u8 = 4;
139
140/// Price precision for SOL instruments (4 decimal places)
141pub const PRICE_PRECISION_SOL: u8 = 4;
142
143/// Amount precision for BTC (4 decimal places)
144pub const AMOUNT_PRECISION_BTC: u8 = 4;
145
146/// Amount precision for ETH (3 decimal places)
147pub const AMOUNT_PRECISION_ETH: u8 = 3;
148
149/// Amount precision for SOL (1 decimal place)
150pub const AMOUNT_PRECISION_SOL: u8 = 1;
151
152// =============================================================================
153// JSON-RPC
154// =============================================================================
155
156/// JSON-RPC version
157pub const JSONRPC_VERSION: &str = "2.0";
158
159/// Default JSON-RPC request ID
160pub const DEFAULT_REQUEST_ID: u64 = 1;
161
162// =============================================================================
163// WEBSOCKET CHANNELS
164// =============================================================================
165
166/// Book channel prefix
167pub const CHANNEL_BOOK: &str = "book";
168
169/// Trades channel prefix
170pub const CHANNEL_TRADES: &str = "trades";
171
172/// Ticker channel prefix
173pub const CHANNEL_TICKER: &str = "ticker";
174
175/// Quote channel prefix
176pub const CHANNEL_QUOTE: &str = "quote";
177
178/// User orders channel
179pub const CHANNEL_USER_ORDERS: &str = "user.orders";
180
181/// User trades channel
182pub const CHANNEL_USER_TRADES: &str = "user.trades";
183
184/// User portfolio channel
185pub const CHANNEL_USER_PORTFOLIO: &str = "user.portfolio";
186
187// =============================================================================
188// FIX PROTOCOL
189// =============================================================================
190
191/// FIX version 4.4
192pub const FIX_VERSION: &str = "FIX.4.4";
193
194/// FIX message delimiter (SOH - Start of Header)
195pub const FIX_DELIMITER: char = '\x01';
196
197/// FIX message delimiter as string
198pub const FIX_DELIMITER_STR: &str = "\x01";
199
200/// Default FIX heartbeat interval in seconds
201pub const FIX_HEARTBEAT_INTERVAL: u32 = 30;
202
203// =============================================================================
204// ERROR HANDLING
205// =============================================================================
206
207/// Maximum retry attempts for failed requests
208pub const MAX_RETRY_ATTEMPTS: u8 = 3;
209
210/// Base delay for exponential backoff in milliseconds
211pub const RETRY_BASE_DELAY_MS: u64 = 1000;
212
213/// Maximum delay for exponential backoff in milliseconds
214pub const RETRY_MAX_DELAY_MS: u64 = 30000;
215
216// =============================================================================
217// MARKET DATA
218// =============================================================================
219
220/// Maximum depth levels for order book
221pub const MAX_ORDER_BOOK_DEPTH: u32 = 10000;
222
223/// Default order book depth
224pub const DEFAULT_ORDER_BOOK_DEPTH: u32 = 20;
225
226/// Maximum number of recent trades to fetch
227pub const MAX_RECENT_TRADES: u32 = 10000;
228
229/// Default number of recent trades
230pub const DEFAULT_RECENT_TRADES: u32 = 100;
231
232// =============================================================================
233// AUTHENTICATION
234// =============================================================================
235
236/// Access token expiration time in seconds (8 hours)
237pub const ACCESS_TOKEN_EXPIRATION_SEC: u64 = 28800;
238
239/// Refresh token expiration time in seconds (30 days)
240pub const REFRESH_TOKEN_EXPIRATION_SEC: u64 = 2592000;
241
242/// Minimum time before token expiration to refresh (5 minutes)
243pub const TOKEN_REFRESH_BUFFER_SEC: u64 = 300;
244
245// =============================================================================
246// UTILITY FUNCTIONS
247// =============================================================================
248
249/// Get minimum order amount for a given currency
250pub fn get_min_order_amount(currency: &str) -> f64 {
251    match currency {
252        CURRENCY_BTC => MIN_ORDER_AMOUNT_BTC,
253        CURRENCY_ETH => MIN_ORDER_AMOUNT_ETH,
254        CURRENCY_SOL => MIN_ORDER_AMOUNT_SOL,
255        _ => MIN_ORDER_AMOUNT_BTC, // Default to BTC
256    }
257}
258
259/// Get price precision for a given currency
260pub fn get_price_precision(currency: &str) -> u8 {
261    match currency {
262        CURRENCY_BTC => PRICE_PRECISION_BTC,
263        CURRENCY_ETH => PRICE_PRECISION_ETH,
264        CURRENCY_SOL => PRICE_PRECISION_SOL,
265        _ => PRICE_PRECISION_BTC, // Default to BTC
266    }
267}
268
269/// Get amount precision for a given currency
270pub fn get_amount_precision(currency: &str) -> u8 {
271    match currency {
272        CURRENCY_BTC => AMOUNT_PRECISION_BTC,
273        CURRENCY_ETH => AMOUNT_PRECISION_ETH,
274        CURRENCY_SOL => AMOUNT_PRECISION_SOL,
275        _ => AMOUNT_PRECISION_BTC, // Default to BTC
276    }
277}
278
279/// Check if a currency is supported
280pub fn is_supported_currency(currency: &str) -> bool {
281    matches!(
282        currency,
283        CURRENCY_BTC | CURRENCY_ETH | CURRENCY_SOL | CURRENCY_USDC | CURRENCY_USDT | CURRENCY_EURR
284    )
285}
286
287#[cfg(test)]
288mod tests {
289    use super::*;
290
291    #[test]
292    fn test_api_urls() {
293        assert!(DERIBIT_WS_URL_PROD.starts_with("wss://"));
294        assert!(DERIBIT_WS_URL_TEST.starts_with("wss://"));
295        assert!(DERIBIT_HTTP_URL_PROD.starts_with("https://"));
296        assert!(DERIBIT_HTTP_URL_TEST.starts_with("https://"));
297
298        assert!(DERIBIT_WS_URL_PROD.contains("www.deribit.com"));
299        assert!(DERIBIT_WS_URL_TEST.contains("test.deribit.com"));
300    }
301
302    #[test]
303    fn test_supported_currencies() {
304        assert!(is_supported_currency(CURRENCY_BTC));
305        assert!(is_supported_currency(CURRENCY_ETH));
306        assert!(is_supported_currency(CURRENCY_SOL));
307        assert!(is_supported_currency(CURRENCY_USDC));
308        assert!(is_supported_currency(CURRENCY_USDT));
309        assert!(is_supported_currency(CURRENCY_EURR));
310
311        assert!(!is_supported_currency("INVALID"));
312        assert!(!is_supported_currency("XRP"));
313    }
314
315    #[test]
316    fn test_min_order_amounts() {
317        assert_eq!(get_min_order_amount(CURRENCY_BTC), MIN_ORDER_AMOUNT_BTC);
318        assert_eq!(get_min_order_amount(CURRENCY_ETH), MIN_ORDER_AMOUNT_ETH);
319        assert_eq!(get_min_order_amount(CURRENCY_SOL), MIN_ORDER_AMOUNT_SOL);
320
321        // Test default fallback
322        assert_eq!(get_min_order_amount("UNKNOWN"), MIN_ORDER_AMOUNT_BTC);
323    }
324
325    #[test]
326    fn test_precision_functions() {
327        assert_eq!(get_price_precision(CURRENCY_BTC), PRICE_PRECISION_BTC);
328        assert_eq!(get_price_precision(CURRENCY_ETH), PRICE_PRECISION_ETH);
329        assert_eq!(get_price_precision(CURRENCY_SOL), PRICE_PRECISION_SOL);
330
331        assert_eq!(get_amount_precision(CURRENCY_BTC), AMOUNT_PRECISION_BTC);
332        assert_eq!(get_amount_precision(CURRENCY_ETH), AMOUNT_PRECISION_ETH);
333        assert_eq!(get_amount_precision(CURRENCY_SOL), AMOUNT_PRECISION_SOL);
334    }
335
336    #[test]
337    fn test_rate_limits() {
338        // Validate rate limit relationships - these are important business logic constraints
339        #[allow(clippy::assertions_on_constants)]
340        {
341            assert!(MAX_REQUESTS_PER_SECOND_AUTH > MAX_REQUESTS_PER_SECOND_UNAUTH);
342            assert!(MAX_SUBSCRIPTIONS_PER_CONNECTION > 0);
343            assert!(MAX_MESSAGE_SIZE_BYTES > 0);
344        }
345
346        // Runtime validation of actual values
347        assert_eq!(MAX_REQUESTS_PER_SECOND_AUTH, 20);
348        assert_eq!(MAX_REQUESTS_PER_SECOND_UNAUTH, 10);
349        assert_eq!(MAX_SUBSCRIPTIONS_PER_CONNECTION, 200);
350        assert_eq!(MAX_MESSAGE_SIZE_BYTES, 65536);
351    }
352
353    #[test]
354    fn test_timeouts() {
355        // Validate timeout relationships - these are important for connection stability
356        #[allow(clippy::assertions_on_constants)]
357        {
358            assert!(DEFAULT_CONNECTION_TIMEOUT_MS > 0);
359            assert!(DEFAULT_REQUEST_TIMEOUT_MS > DEFAULT_CONNECTION_TIMEOUT_MS);
360            assert!(HEARTBEAT_INTERVAL_MS > HEARTBEAT_TIMEOUT_MS);
361        }
362
363        // Runtime validation of actual values
364        assert_eq!(DEFAULT_CONNECTION_TIMEOUT_MS, 5000);
365        assert_eq!(DEFAULT_REQUEST_TIMEOUT_MS, 10000);
366        assert_eq!(HEARTBEAT_INTERVAL_MS, 10000);
367        assert_eq!(HEARTBEAT_TIMEOUT_MS, 5000);
368    }
369
370    #[test]
371    fn test_order_limits() {
372        // Validate order limit relationships - these are important trading constraints
373        #[allow(clippy::assertions_on_constants)]
374        {
375            assert!(MIN_ORDER_AMOUNT_BTC > 0.0);
376            assert!(MIN_ORDER_AMOUNT_ETH > 0.0);
377            assert!(MIN_ORDER_AMOUNT_SOL > 0.0);
378            assert!(MAX_ORDER_AMOUNT > MIN_ORDER_AMOUNT_BTC);
379            assert!(MAX_OPEN_ORDERS_TOTAL > MAX_OPEN_ORDERS_PER_INSTRUMENT);
380        }
381
382        // Runtime validation using helper functions
383        assert_eq!(get_min_order_amount(CURRENCY_BTC), MIN_ORDER_AMOUNT_BTC);
384        assert_eq!(get_min_order_amount(CURRENCY_ETH), MIN_ORDER_AMOUNT_ETH);
385        assert_eq!(get_min_order_amount(CURRENCY_SOL), MIN_ORDER_AMOUNT_SOL);
386    }
387
388    #[test]
389    fn test_jsonrpc_constants() {
390        assert_eq!(JSONRPC_VERSION, "2.0");
391
392        // Validate request ID is positive - important for protocol compliance
393        #[allow(clippy::assertions_on_constants)]
394        {
395            assert!(DEFAULT_REQUEST_ID > 0);
396        }
397
398        // Runtime validation
399        assert_eq!(DEFAULT_REQUEST_ID, 1);
400    }
401
402    #[test]
403    fn test_fix_constants() {
404        assert_eq!(FIX_VERSION, "FIX.4.4");
405        assert_eq!(FIX_DELIMITER, '\x01');
406        assert_eq!(FIX_DELIMITER_STR, "\x01");
407
408        // Validate heartbeat interval is positive - important for FIX protocol
409        #[allow(clippy::assertions_on_constants)]
410        {
411            assert!(FIX_HEARTBEAT_INTERVAL > 0);
412        }
413
414        // Runtime validation
415        assert_eq!(FIX_HEARTBEAT_INTERVAL, 30);
416    }
417
418    #[test]
419    fn test_channel_names() {
420        // Validate channel names are not empty - important for WebSocket subscriptions
421        #[allow(clippy::const_is_empty)]
422        {
423            assert!(!CHANNEL_BOOK.is_empty());
424            assert!(!CHANNEL_TRADES.is_empty());
425            assert!(!CHANNEL_TICKER.is_empty());
426            assert!(!CHANNEL_QUOTE.is_empty());
427        }
428
429        // Runtime validation of actual channel names
430        assert_eq!(CHANNEL_BOOK, "book");
431        assert_eq!(CHANNEL_TRADES, "trades");
432        assert_eq!(CHANNEL_TICKER, "ticker");
433        assert_eq!(CHANNEL_QUOTE, "quote");
434
435        // Validate user channel prefixes
436        assert!(CHANNEL_USER_ORDERS.starts_with("user."));
437        assert!(CHANNEL_USER_TRADES.starts_with("user."));
438        assert!(CHANNEL_USER_PORTFOLIO.starts_with("user."));
439    }
440
441    #[test]
442    fn test_instrument_types() {
443        let types = [
444            INSTRUMENT_TYPE_FUTURE,
445            INSTRUMENT_TYPE_OPTION,
446            INSTRUMENT_TYPE_PERPETUAL,
447            INSTRUMENT_TYPE_SPOT,
448            INSTRUMENT_TYPE_FUTURE_COMBO,
449            INSTRUMENT_TYPE_OPTION_COMBO,
450        ];
451
452        for instrument_type in types {
453            assert!(!instrument_type.is_empty());
454        }
455    }
456
457    #[test]
458    fn test_authentication_constants() {
459        // Validate token expiration relationships - critical for authentication security
460        #[allow(clippy::assertions_on_constants)]
461        {
462            assert!(ACCESS_TOKEN_EXPIRATION_SEC > 0);
463            assert!(REFRESH_TOKEN_EXPIRATION_SEC > ACCESS_TOKEN_EXPIRATION_SEC);
464            assert!(TOKEN_REFRESH_BUFFER_SEC < ACCESS_TOKEN_EXPIRATION_SEC);
465        }
466
467        // Runtime validation of actual values
468        assert_eq!(ACCESS_TOKEN_EXPIRATION_SEC, 28800); // 8 hours
469        assert_eq!(REFRESH_TOKEN_EXPIRATION_SEC, 2592000); // 30 days
470        assert_eq!(TOKEN_REFRESH_BUFFER_SEC, 300); // 5 minutes
471    }
472
473    #[test]
474    fn test_market_data_constants() {
475        // Validate market data limit relationships - important for performance
476        #[allow(clippy::assertions_on_constants)]
477        {
478            assert!(MAX_ORDER_BOOK_DEPTH > DEFAULT_ORDER_BOOK_DEPTH);
479            assert!(MAX_RECENT_TRADES > DEFAULT_RECENT_TRADES);
480            assert!(DEFAULT_ORDER_BOOK_DEPTH > 0);
481            assert!(DEFAULT_RECENT_TRADES > 0);
482        }
483
484        // Runtime validation of actual values
485        assert_eq!(MAX_ORDER_BOOK_DEPTH, 10000);
486        assert_eq!(DEFAULT_ORDER_BOOK_DEPTH, 20);
487        assert_eq!(MAX_RECENT_TRADES, 10000);
488        assert_eq!(DEFAULT_RECENT_TRADES, 100);
489    }
490
491    #[test]
492    fn test_error_handling_constants() {
493        // Validate retry logic relationships - important for resilience
494        #[allow(clippy::assertions_on_constants)]
495        {
496            assert!(MAX_RETRY_ATTEMPTS > 0);
497            assert!(RETRY_BASE_DELAY_MS > 0);
498            assert!(RETRY_MAX_DELAY_MS > RETRY_BASE_DELAY_MS);
499        }
500
501        // Runtime validation of actual values
502        assert_eq!(MAX_RETRY_ATTEMPTS, 3);
503        assert_eq!(RETRY_BASE_DELAY_MS, 1000);
504        assert_eq!(RETRY_MAX_DELAY_MS, 30000);
505    }
506}