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