Skip to main content

nautilus_deribit/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//! Core constants for the Deribit adapter.
17
18use std::{num::NonZeroU32, sync::LazyLock};
19
20use ahash::AHashSet;
21use nautilus_model::identifiers::Venue;
22use nautilus_network::ratelimiter::quota::Quota;
23use ustr::Ustr;
24
25/// Venue identifier string.
26pub const DERIBIT: &str = "DERIBIT";
27
28/// Static venue instance.
29pub static DERIBIT_VENUE: LazyLock<Venue> = LazyLock::new(|| Venue::new(Ustr::from(DERIBIT)));
30
31// Production URLs
32pub const DERIBIT_HTTP_URL: &str = "https://www.deribit.com";
33pub const DERIBIT_WS_URL: &str = "wss://www.deribit.com/ws/api/v2";
34
35// Testnet URLs
36pub const DERIBIT_TESTNET_HTTP_URL: &str = "https://test.deribit.com";
37pub const DERIBIT_TESTNET_WS_URL: &str = "wss://test.deribit.com/ws/api/v2";
38
39// API paths
40pub const DERIBIT_API_VERSION: &str = "v2";
41pub const DERIBIT_API_PATH: &str = "/api/v2";
42
43// JSON-RPC constants
44pub const JSONRPC_VERSION: &str = "2.0";
45
46/// Deribit error codes that should trigger retries.
47///
48/// Only retry on temporary network/system issues that are likely to resolve.
49/// Based on Deribit API documentation error codes.
50///
51/// # Error Code Categories
52///
53/// **Retriable (temporary issues):**
54/// - `10028`: "too_many_requests" - Rate limit exceeded
55/// - `10040`: "retry" - Explicitly says request should be retried
56/// - `10041`: "settlement_in_progress" - Settlement calculation in progress (few seconds)
57/// - `10047`: "matching_engine_queue_full" - Matching engine queue full
58/// - `10066`: "too_many_concurrent_requests" - Too many concurrent public requests
59/// - `11051`: "system_maintenance" - System under maintenance
60/// - `11094`: "internal_server_error" - Unhandled server error
61/// - `13028`: "temporarily_unavailable" - Service not responding or too slow
62/// - `13888`: "timed_out" - Server timeout processing request
63///
64/// **Non-retriable (permanent errors):**
65/// - `10000`: "authorization_required" - Auth issue, invalid signature
66/// - `10004`: "order_not_found" - Order can't be found
67/// - `10009`: "not_enough_funds" - Insufficient funds
68/// - `10020`: "invalid_or_unsupported_instrument" - Invalid instrument name
69/// - `10029`: "not_owner_of_order" - Attempt to operate with not own order
70/// - `11029`: "invalid_arguments" - Invalid input detected
71/// - `11050`: "bad_request" - Request not parsed properly
72/// - `13004`: "invalid_credentials" - Invalid API credentials
73/// - `13009`: "unauthorized" - Wrong/expired token or bad signature
74/// - `13020`: "not_found" - Instrument not found
75/// - `13021`: "forbidden" - Not enough permissions
76///
77/// # References
78///
79/// <https://docs.deribit.com/#rpc-error-codes>
80pub static DERIBIT_RETRY_ERROR_CODES: LazyLock<AHashSet<i64>> = LazyLock::new(|| {
81    let mut codes = AHashSet::new();
82
83    // Rate limiting (temporary - will resolve after backoff)
84    codes.insert(10028); // too_many_requests
85    codes.insert(10066); // too_many_concurrent_requests
86
87    // Explicit retry instruction
88    codes.insert(10040); // retry - API explicitly says to retry
89
90    // System issues (temporary - maintenance, settlement, or overload)
91    codes.insert(10041); // settlement_in_progress - daily settlement (few seconds)
92    codes.insert(10047); // matching_engine_queue_full
93    codes.insert(11051); // system_maintenance
94    codes.insert(11094); // internal_server_error
95    codes.insert(13028); // temporarily_unavailable
96
97    // Timeout (temporary - may succeed on retry)
98    codes.insert(13888); // timed_out
99
100    codes
101});
102
103/// Determines if a Deribit error code should trigger a retry.
104///
105/// # Arguments
106///
107/// * `error_code` - The Deribit error code from the JSON-RPC error response
108///
109/// # Returns
110///
111/// `true` if the error is temporary and should be retried, `false` otherwise
112pub fn should_retry_error_code(error_code: i64) -> bool {
113    DERIBIT_RETRY_ERROR_CODES.contains(&error_code)
114}
115
116/// Deribit error code for post-only order rejection.
117///
118/// Error code `11054` is returned when a post-only order would have
119/// immediately matched against an existing order (taking liquidity).
120pub const DERIBIT_POST_ONLY_ERROR_CODE: i64 = 11054;
121
122/// Default Deribit REST API rate limit: 20 requests per second sustained.
123///
124/// Deribit uses a credit-based system for non-matching engine requests:
125/// - Each request costs 500 credits
126/// - Maximum credits: 50,000
127/// - Refill rate: 10,000 credits/second (~20 sustained req/s)
128/// - Burst capacity: up to 100 requests (50,000 / 500)
129///
130/// # References
131///
132/// <https://docs.deribit.com/#rate-limits>
133pub static DERIBIT_HTTP_REST_QUOTA: LazyLock<Quota> = LazyLock::new(|| {
134    Quota::per_second(NonZeroU32::new(20).expect("20 is non-zero"))
135        .allow_burst(NonZeroU32::new(100).expect("100 is non-zero"))
136});
137
138/// Deribit matching engine (order operations) rate limit.
139///
140/// Matching engine requests (buy, sell, edit, cancel) have separate limits:
141/// - Default burst: 20
142/// - Default rate: 5 requests/second
143///
144/// Note: Actual limits vary by account tier based on 7-day trading volume.
145pub static DERIBIT_HTTP_ORDER_QUOTA: LazyLock<Quota> = LazyLock::new(|| {
146    Quota::per_second(NonZeroU32::new(5).expect("5 is non-zero"))
147        .allow_burst(NonZeroU32::new(20).expect("20 is non-zero"))
148});
149
150/// Conservative rate limit for account information endpoints.
151pub static DERIBIT_HTTP_ACCOUNT_QUOTA: LazyLock<Quota> =
152    LazyLock::new(|| Quota::per_second(NonZeroU32::new(5).expect("5 is non-zero")));
153
154/// Global rate limit key for Deribit HTTP requests.
155pub const DERIBIT_GLOBAL_RATE_KEY: &str = "deribit:global";
156
157/// Rate limit key for Deribit order operations (matching engine).
158pub const DERIBIT_ORDER_RATE_KEY: &str = "deribit:orders";
159
160/// Rate limit key for account information endpoints.
161pub const DERIBIT_ACCOUNT_RATE_KEY: &str = "deribit:account";
162
163/// Deribit WebSocket subscription rate limit.
164///
165/// Subscribe methods have custom rate limits:
166/// - Cost per request: 3,000 credits
167/// - Maximum credits: 30,000
168/// - Sustained rate: ~3.3 requests/second
169/// - Burst capacity: 10 requests
170///
171/// # References
172///
173/// <https://support.deribit.com/hc/en-us/articles/25944617523357-Rate-Limits>
174pub static DERIBIT_WS_SUBSCRIPTION_QUOTA: LazyLock<Quota> = LazyLock::new(|| {
175    Quota::per_second(NonZeroU32::new(3).expect("3 is non-zero"))
176        .allow_burst(NonZeroU32::new(10).expect("10 is non-zero"))
177});
178
179/// Deribit WebSocket order rate limit: 5 requests per second with 20 burst.
180///
181/// Matching engine operations (buy, sell, edit, cancel) have stricter limits.
182pub static DERIBIT_WS_ORDER_QUOTA: LazyLock<Quota> = LazyLock::new(|| {
183    Quota::per_second(NonZeroU32::new(5).expect("5 is non-zero"))
184        .allow_burst(NonZeroU32::new(20).expect("20 is non-zero"))
185});
186
187/// Rate limit key for WebSocket subscriptions.
188pub const DERIBIT_WS_SUBSCRIPTION_KEY: &str = "subscription";
189
190/// Rate limit key for WebSocket order operations.
191pub const DERIBIT_WS_ORDER_KEY: &str = "order";
192
193/// Pre-interned rate limit key for WebSocket order operations.
194pub static DERIBIT_RATE_LIMIT_KEY_ORDER: LazyLock<[Ustr; 1]> =
195    LazyLock::new(|| [Ustr::from(DERIBIT_WS_ORDER_KEY)]);
196
197/// Default grouping for aggregated order book subscriptions.
198pub const DERIBIT_BOOK_DEFAULT_GROUP: &str = "none";
199
200/// Default depth per side for aggregated order book subscriptions.
201pub const DERIBIT_BOOK_DEFAULT_DEPTH: u32 = 10;
202
203/// Supported aggregated order book depths for Deribit.
204pub const DERIBIT_BOOK_VALID_DEPTHS: [u32; 3] = [1, 10, 20];
205
206/// Default WebSocket heartbeat interval in seconds.
207///
208/// Deribit recommends heartbeats every 30-60 seconds. More frequent heartbeats
209/// may trigger stricter rate limits.
210///
211/// # References
212///
213/// <https://support.deribit.com/hc/en-us/articles/25944603459613>
214pub const DERIBIT_WS_HEARTBEAT_SECS: u64 = 30;