Skip to main content

nautilus_deribit/
config.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//! Configuration structures for the Deribit adapter.
17
18use nautilus_model::identifiers::AccountId;
19use nautilus_network::websocket::TransportBackend;
20use serde::{Deserialize, Serialize};
21
22use crate::{
23    common::{
24        credential::credential_env_vars,
25        enums::DeribitEnvironment,
26        urls::{get_http_base_url, get_ws_url},
27    },
28    http::models::DeribitProductType,
29};
30
31/// Configuration for the Deribit data client.
32#[derive(Debug, Clone, Serialize, Deserialize, bon::Builder)]
33#[serde(default, deny_unknown_fields)]
34#[cfg_attr(
35    feature = "python",
36    pyo3::pyclass(module = "nautilus_trader.adapters.deribit", from_py_object)
37)]
38#[cfg_attr(
39    feature = "python",
40    pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.adapters.deribit")
41)]
42pub struct DeribitDataClientConfig {
43    /// Optional API key for authenticated endpoints.
44    pub api_key: Option<String>,
45    /// Optional API secret for authenticated endpoints.
46    pub api_secret: Option<String>,
47    /// Product types to load (e.g., Future, Option, Spot).
48    #[builder(default = vec![DeribitProductType::Future])]
49    pub product_types: Vec<DeribitProductType>,
50    /// Optional override for the HTTP base URL.
51    pub base_url_http: Option<String>,
52    /// Optional override for the WebSocket URL.
53    pub base_url_ws: Option<String>,
54    /// Optional proxy URL for HTTP and WebSocket transports.
55    pub proxy_url: Option<String>,
56    /// The Deribit environment (mainnet or testnet).
57    #[builder(default)]
58    pub environment: DeribitEnvironment,
59    /// HTTP timeout in seconds.
60    #[builder(default = 60)]
61    pub http_timeout_secs: u64,
62    /// Maximum retry attempts for requests.
63    #[builder(default = 3)]
64    pub max_retries: u32,
65    /// Initial retry delay in milliseconds.
66    #[builder(default = 1_000)]
67    pub retry_delay_initial_ms: u64,
68    /// Maximum retry delay in milliseconds.
69    #[builder(default = 10_000)]
70    pub retry_delay_max_ms: u64,
71    /// Heartbeat interval in seconds for WebSocket connection.
72    #[builder(default = 30)]
73    pub heartbeat_interval_secs: u64,
74    /// Optional WebSocket authentication timeout (seconds), defaulting to
75    /// `AUTHENTICATION_TIMEOUT_SECS` when unset.
76    pub auth_timeout_secs: Option<u64>,
77    /// Interval for refreshing instruments (in minutes).
78    #[builder(default = 60)]
79    pub update_instruments_interval_mins: u64,
80    /// If `true`, subscribes for uncached instruments lazy-load via HTTP; otherwise fail fast.
81    #[builder(default = false)]
82    pub auto_load_missing_instruments: bool,
83    /// WebSocket transport backend (defaults to `Tungstenite`).
84    #[builder(default)]
85    pub transport_backend: TransportBackend,
86}
87
88#[cfg(feature = "python")]
89nautilus_core::impl_pyo3_config_getters!(DeribitDataClientConfig {
90    product_types: Vec<DeribitProductType>,
91    environment: DeribitEnvironment,
92    base_url_http: Option<String>,
93    base_url_ws: Option<String>,
94    http_timeout_secs: u64,
95    max_retries: u32,
96    retry_delay_initial_ms: u64,
97    retry_delay_max_ms: u64,
98    heartbeat_interval_secs: u64,
99    auth_timeout_secs: Option<u64>,
100    update_instruments_interval_mins: u64,
101    auto_load_missing_instruments: bool,
102    transport_backend: TransportBackend,
103});
104
105impl Default for DeribitDataClientConfig {
106    fn default() -> Self {
107        Self::builder().build()
108    }
109}
110
111impl DeribitDataClientConfig {
112    /// Creates a new configuration with default settings.
113    #[must_use]
114    pub fn new() -> Self {
115        Self::default()
116    }
117
118    /// Returns `true` when API credentials are available (in config or env vars).
119    #[must_use]
120    pub fn has_api_credentials(&self) -> bool {
121        let (key_env, secret_env) = credential_env_vars(self.environment);
122        let has_key = self.api_key.is_some() || std::env::var(key_env).is_ok();
123        let has_secret = self.api_secret.is_some() || std::env::var(secret_env).is_ok();
124        has_key && has_secret
125    }
126
127    /// Returns the HTTP base URL, falling back to the default when unset.
128    #[must_use]
129    pub fn http_base_url(&self) -> String {
130        self.base_url_http
131            .clone()
132            .unwrap_or_else(|| get_http_base_url(self.environment).to_string())
133    }
134
135    /// Returns the WebSocket URL, respecting the environment and overrides.
136    #[must_use]
137    pub fn ws_url(&self) -> String {
138        self.base_url_ws
139            .clone()
140            .unwrap_or_else(|| get_ws_url(self.environment).to_string())
141    }
142}
143
144/// Configuration for the Deribit execution client.
145#[derive(Debug, Clone, Serialize, Deserialize, bon::Builder)]
146#[serde(default, deny_unknown_fields)]
147#[cfg_attr(
148    feature = "python",
149    pyo3::pyclass(module = "nautilus_trader.adapters.deribit", from_py_object)
150)]
151#[cfg_attr(
152    feature = "python",
153    pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.adapters.deribit")
154)]
155pub struct DeribitExecutionClientConfig {
156    /// The account ID for this client.
157    #[builder(default = AccountId::from("DERIBIT-001"))]
158    pub account_id: AccountId,
159    /// Optional API key for authenticated endpoints.
160    pub api_key: Option<String>,
161    /// Optional API secret for authenticated endpoints.
162    pub api_secret: Option<String>,
163    /// Product types to load (e.g., Future, Option, Spot).
164    #[builder(default = vec![DeribitProductType::Future])]
165    pub product_types: Vec<DeribitProductType>,
166    /// Optional override for the HTTP base URL.
167    pub base_url_http: Option<String>,
168    /// Optional override for the WebSocket URL.
169    pub base_url_ws: Option<String>,
170    /// Optional proxy URL for HTTP and WebSocket transports.
171    pub proxy_url: Option<String>,
172    /// The Deribit environment (mainnet or testnet).
173    #[builder(default)]
174    pub environment: DeribitEnvironment,
175    /// HTTP timeout in seconds.
176    #[builder(default = 60)]
177    pub http_timeout_secs: u64,
178    /// Maximum retry attempts for requests.
179    #[builder(default = 3)]
180    pub max_retries: u32,
181    /// Initial retry delay in milliseconds.
182    #[builder(default = 1_000)]
183    pub retry_delay_initial_ms: u64,
184    /// Maximum retry delay in milliseconds.
185    #[builder(default = 10_000)]
186    pub retry_delay_max_ms: u64,
187    /// Optional WebSocket authentication timeout (seconds), defaulting to
188    /// `AUTHENTICATION_TIMEOUT_SECS` when unset.
189    pub auth_timeout_secs: Option<u64>,
190    /// WebSocket transport backend (defaults to `Tungstenite`).
191    #[builder(default)]
192    pub transport_backend: TransportBackend,
193}
194
195#[cfg(feature = "python")]
196nautilus_core::impl_pyo3_config_getters!(DeribitExecutionClientConfig {
197    account_id: AccountId,
198    product_types: Vec<DeribitProductType>,
199    environment: DeribitEnvironment,
200    base_url_http: Option<String>,
201    base_url_ws: Option<String>,
202    http_timeout_secs: u64,
203    max_retries: u32,
204    retry_delay_initial_ms: u64,
205    retry_delay_max_ms: u64,
206    auth_timeout_secs: Option<u64>,
207    transport_backend: TransportBackend,
208});
209
210impl Default for DeribitExecutionClientConfig {
211    fn default() -> Self {
212        Self::builder().build()
213    }
214}
215
216impl DeribitExecutionClientConfig {
217    /// Returns `true` when API credentials are available (in config or env vars).
218    #[must_use]
219    pub fn has_api_credentials(&self) -> bool {
220        let (key_env, secret_env) = credential_env_vars(self.environment);
221        let has_key = self.api_key.is_some() || std::env::var(key_env).is_ok();
222        let has_secret = self.api_secret.is_some() || std::env::var(secret_env).is_ok();
223        has_key && has_secret
224    }
225
226    /// Returns the HTTP base URL, falling back to the default when unset.
227    #[must_use]
228    pub fn http_base_url(&self) -> String {
229        self.base_url_http
230            .clone()
231            .unwrap_or_else(|| get_http_base_url(self.environment).to_string())
232    }
233
234    /// Returns the WebSocket URL, respecting the environment and overrides.
235    #[must_use]
236    pub fn ws_url(&self) -> String {
237        self.base_url_ws
238            .clone()
239            .unwrap_or_else(|| get_ws_url(self.environment).to_string())
240    }
241}
242
243#[cfg(test)]
244mod tests {
245    use rstest::rstest;
246
247    use super::*;
248
249    #[rstest]
250    fn test_default_config() {
251        let config = DeribitDataClientConfig::default();
252        assert_eq!(config.environment, DeribitEnvironment::Mainnet);
253        assert_eq!(config.product_types.len(), 1);
254        assert_eq!(config.http_timeout_secs, 60);
255    }
256
257    #[rstest]
258    fn test_http_base_url_default() {
259        let config = DeribitDataClientConfig::default();
260        assert_eq!(config.http_base_url(), "https://www.deribit.com");
261    }
262
263    #[rstest]
264    fn test_http_base_url_testnet() {
265        let config = DeribitDataClientConfig {
266            environment: DeribitEnvironment::Testnet,
267            ..Default::default()
268        };
269        assert_eq!(config.http_base_url(), "https://test.deribit.com");
270    }
271
272    #[rstest]
273    fn test_ws_url_default() {
274        let config = DeribitDataClientConfig::default();
275        assert_eq!(config.ws_url(), "wss://www.deribit.com/ws/api/v2");
276    }
277
278    #[rstest]
279    fn test_ws_url_testnet() {
280        let config = DeribitDataClientConfig {
281            environment: DeribitEnvironment::Testnet,
282            ..Default::default()
283        };
284        assert_eq!(config.ws_url(), "wss://test.deribit.com/ws/api/v2");
285    }
286
287    #[rstest]
288    fn test_has_api_credentials_in_config() {
289        let config = DeribitDataClientConfig {
290            api_key: Some("test_key".to_string()),
291            api_secret: Some("test_secret".to_string()),
292            ..Default::default()
293        };
294        assert!(config.has_api_credentials());
295    }
296
297    #[rstest]
298    fn test_data_config_toml_minimal() {
299        let config: DeribitDataClientConfig = toml::from_str(
300            r#"
301environment = "testnet"
302product_types = ["future", "option"]
303heartbeat_interval_secs = 15
304auto_load_missing_instruments = true
305"#,
306        )
307        .unwrap();
308
309        assert_eq!(config.environment, DeribitEnvironment::Testnet);
310        assert_eq!(
311            config.product_types,
312            vec![DeribitProductType::Future, DeribitProductType::Option]
313        );
314        assert_eq!(config.heartbeat_interval_secs, 15);
315        assert!(config.auto_load_missing_instruments);
316    }
317
318    #[rstest]
319    fn test_exec_config_toml_empty_uses_defaults() {
320        let config: DeribitExecutionClientConfig = toml::from_str("").unwrap();
321        let expected = DeribitExecutionClientConfig::default();
322        assert_eq!(config.account_id, expected.account_id);
323        assert_eq!(config.environment, expected.environment);
324        assert_eq!(config.product_types, expected.product_types);
325        assert_eq!(config.http_timeout_secs, expected.http_timeout_secs);
326        assert_eq!(config.max_retries, expected.max_retries);
327        assert_eq!(config.transport_backend, expected.transport_backend);
328    }
329}