deribit_base/model/
config.rs

1/******************************************************************************
2   Author: Joaquín Béjar García
3   Email: jb@taunais.com
4   Date: 21/7/25
5******************************************************************************/
6use pretty_simple_display::{DebugPretty, DisplaySimple};
7use serde::{Deserialize, Serialize};
8
9/// Deribit API configuration
10#[derive(DebugPretty, DisplaySimple, Clone, Serialize, Deserialize)]
11pub struct DeribitConfig {
12    /// Client ID for API authentication
13    pub client_id: String,
14    /// Client secret for API authentication
15    pub client_secret: String,
16    /// Whether to use testnet
17    pub test_net: bool,
18    /// Request timeout in seconds
19    pub timeout_seconds: u64,
20    /// Maximum number of retries
21    pub max_retries: u32,
22    /// Rate limit per second
23    pub rate_limit: Option<u32>,
24    /// User agent string
25    pub user_agent: Option<String>,
26}
27
28impl DeribitConfig {
29    /// Create a new configuration for production
30    pub fn new(client_id: String, client_secret: String) -> Self {
31        Self {
32            client_id,
33            client_secret,
34            test_net: false,
35            timeout_seconds: 30,
36            max_retries: 3,
37            rate_limit: None,
38            user_agent: None,
39        }
40    }
41
42    /// Create a new configuration for testnet
43    pub fn testnet(client_id: String, client_secret: String) -> Self {
44        Self {
45            client_id,
46            client_secret,
47            test_net: true,
48            timeout_seconds: 30,
49            max_retries: 3,
50            rate_limit: None,
51            user_agent: None,
52        }
53    }
54
55    /// Set timeout in seconds
56    pub fn with_timeout(mut self, timeout_seconds: u64) -> Self {
57        self.timeout_seconds = timeout_seconds;
58        self
59    }
60
61    /// Set maximum retries
62    pub fn with_max_retries(mut self, max_retries: u32) -> Self {
63        self.max_retries = max_retries;
64        self
65    }
66
67    /// Set rate limit
68    pub fn with_rate_limit(mut self, rate_limit: u32) -> Self {
69        self.rate_limit = Some(rate_limit);
70        self
71    }
72
73    /// Set user agent
74    pub fn with_user_agent(mut self, user_agent: String) -> Self {
75        self.user_agent = Some(user_agent);
76        self
77    }
78
79    /// Get the base URL for HTTP API
80    pub fn base_url(&self) -> &'static str {
81        if self.test_net {
82            DeribitUrls::TEST_BASE_URL
83        } else {
84            DeribitUrls::PROD_BASE_URL
85        }
86    }
87
88    /// Get the WebSocket URL
89    pub fn ws_url(&self) -> &'static str {
90        if self.test_net {
91            DeribitUrls::TEST_WS_URL
92        } else {
93            DeribitUrls::PROD_WS_URL
94        }
95    }
96
97    /// Get the API URL for HTTP requests
98    pub fn api_url(&self) -> String {
99        format!("{}/api/v2", self.base_url())
100    }
101}
102
103impl Default for DeribitConfig {
104    fn default() -> Self {
105        Self {
106            client_id: String::new(),
107            client_secret: String::new(),
108            test_net: true, // Default to testnet for safety
109            timeout_seconds: 30,
110            max_retries: 3,
111            rate_limit: None,
112            user_agent: Some("deribit-rust-client/1.0".to_string()),
113        }
114    }
115}
116
117/// Deribit API URLs
118pub struct DeribitUrls;
119
120impl DeribitUrls {
121    /// Production base URL
122    pub const PROD_BASE_URL: &'static str = "https://www.deribit.com";
123    /// Test base URL
124    pub const TEST_BASE_URL: &'static str = "https://test.deribit.com";
125    /// Production WebSocket URL
126    pub const PROD_WS_URL: &'static str = "wss://www.deribit.com/ws/api/v2";
127    /// Test WebSocket URL
128    pub const TEST_WS_URL: &'static str = "wss://test.deribit.com/ws/api/v2";
129}
130
131/// Connection configuration for WebSocket
132#[derive(DebugPretty, DisplaySimple, Clone, Serialize, Deserialize)]
133pub struct WebSocketConfig {
134    /// Base configuration
135    pub base: DeribitConfig,
136    /// Ping interval in seconds
137    pub ping_interval: u64,
138    /// Pong timeout in seconds
139    pub pong_timeout: u64,
140    /// Reconnect attempts
141    pub reconnect_attempts: u32,
142    /// Reconnect delay in seconds
143    pub reconnect_delay: u64,
144    /// Maximum message size
145    pub max_message_size: usize,
146    /// Enable compression
147    pub compression: bool,
148}
149
150impl WebSocketConfig {
151    /// Create new WebSocket configuration
152    pub fn new(base: DeribitConfig) -> Self {
153        Self {
154            base,
155            ping_interval: 30,
156            pong_timeout: 10,
157            reconnect_attempts: 5,
158            reconnect_delay: 5,
159            max_message_size: 1024 * 1024, // 1MB
160            compression: true,
161        }
162    }
163
164    /// Set ping interval
165    pub fn with_ping_interval(mut self, ping_interval: u64) -> Self {
166        self.ping_interval = ping_interval;
167        self
168    }
169
170    /// Set pong timeout
171    pub fn with_pong_timeout(mut self, pong_timeout: u64) -> Self {
172        self.pong_timeout = pong_timeout;
173        self
174    }
175
176    /// Set reconnect attempts
177    pub fn with_reconnect_attempts(mut self, reconnect_attempts: u32) -> Self {
178        self.reconnect_attempts = reconnect_attempts;
179        self
180    }
181
182    /// Set reconnect delay
183    pub fn with_reconnect_delay(mut self, reconnect_delay: u64) -> Self {
184        self.reconnect_delay = reconnect_delay;
185        self
186    }
187
188    /// Enable/disable compression
189    pub fn with_compression(mut self, compression: bool) -> Self {
190        self.compression = compression;
191        self
192    }
193}
194
195/// HTTP client configuration
196#[derive(DebugPretty, DisplaySimple, Clone, Serialize, Deserialize)]
197pub struct HttpConfig {
198    /// Base configuration
199    pub base: DeribitConfig,
200    /// Connection pool size
201    pub pool_size: Option<usize>,
202    /// Keep alive timeout
203    pub keep_alive: Option<u64>,
204    /// Enable HTTP/2
205    pub http2: bool,
206    /// Enable gzip compression
207    pub gzip: bool,
208}
209
210impl HttpConfig {
211    /// Create new HTTP configuration
212    pub fn new(base: DeribitConfig) -> Self {
213        Self {
214            base,
215            pool_size: None,
216            keep_alive: Some(30),
217            http2: true,
218            gzip: true,
219        }
220    }
221
222    /// Set connection pool size
223    pub fn with_pool_size(mut self, pool_size: usize) -> Self {
224        self.pool_size = Some(pool_size);
225        self
226    }
227
228    /// Set keep alive timeout
229    pub fn with_keep_alive(mut self, keep_alive: u64) -> Self {
230        self.keep_alive = Some(keep_alive);
231        self
232    }
233
234    /// Enable/disable HTTP/2
235    pub fn with_http2(mut self, http2: bool) -> Self {
236        self.http2 = http2;
237        self
238    }
239
240    /// Enable/disable gzip compression
241    pub fn with_gzip(mut self, gzip: bool) -> Self {
242        self.gzip = gzip;
243        self
244    }
245}
246
247#[cfg(test)]
248mod tests {
249    use super::*;
250
251    #[test]
252    fn test_deribit_config_new() {
253        let config = DeribitConfig::new("client123".to_string(), "secret456".to_string());
254        assert_eq!(config.client_id, "client123");
255        assert_eq!(config.client_secret, "secret456");
256        assert!(!config.test_net);
257        assert_eq!(config.timeout_seconds, 30);
258        assert_eq!(config.max_retries, 3);
259        assert_eq!(config.rate_limit, None);
260        assert_eq!(config.user_agent, None);
261    }
262
263    #[test]
264    fn test_deribit_config_testnet() {
265        let config = DeribitConfig::testnet("client123".to_string(), "secret456".to_string());
266        assert_eq!(config.client_id, "client123");
267        assert_eq!(config.client_secret, "secret456");
268        assert!(config.test_net);
269        assert_eq!(config.timeout_seconds, 30);
270        assert_eq!(config.max_retries, 3);
271        assert_eq!(config.rate_limit, None);
272        assert_eq!(config.user_agent, None);
273    }
274
275    #[test]
276    fn test_deribit_config_with_timeout() {
277        let config =
278            DeribitConfig::new("client".to_string(), "secret".to_string()).with_timeout(60);
279        assert_eq!(config.timeout_seconds, 60);
280    }
281
282    #[test]
283    fn test_deribit_config_with_max_retries() {
284        let config =
285            DeribitConfig::new("client".to_string(), "secret".to_string()).with_max_retries(5);
286        assert_eq!(config.max_retries, 5);
287    }
288
289    #[test]
290    fn test_deribit_config_with_rate_limit() {
291        let config =
292            DeribitConfig::new("client".to_string(), "secret".to_string()).with_rate_limit(10);
293        assert_eq!(config.rate_limit, Some(10));
294    }
295
296    #[test]
297    fn test_deribit_config_with_user_agent() {
298        let config = DeribitConfig::new("client".to_string(), "secret".to_string())
299            .with_user_agent("custom-agent".to_string());
300        assert_eq!(config.user_agent, Some("custom-agent".to_string()));
301    }
302
303    #[test]
304    fn test_deribit_config_base_url() {
305        let prod_config = DeribitConfig::new("client".to_string(), "secret".to_string());
306        assert_eq!(prod_config.base_url(), DeribitUrls::PROD_BASE_URL);
307
308        let test_config = DeribitConfig::testnet("client".to_string(), "secret".to_string());
309        assert_eq!(test_config.base_url(), DeribitUrls::TEST_BASE_URL);
310    }
311
312    #[test]
313    fn test_deribit_config_ws_url() {
314        let prod_config = DeribitConfig::new("client".to_string(), "secret".to_string());
315        assert_eq!(prod_config.ws_url(), DeribitUrls::PROD_WS_URL);
316
317        let test_config = DeribitConfig::testnet("client".to_string(), "secret".to_string());
318        assert_eq!(test_config.ws_url(), DeribitUrls::TEST_WS_URL);
319    }
320
321    #[test]
322    fn test_deribit_config_api_url() {
323        let prod_config = DeribitConfig::new("client".to_string(), "secret".to_string());
324        assert_eq!(prod_config.api_url(), "https://www.deribit.com/api/v2");
325
326        let test_config = DeribitConfig::testnet("client".to_string(), "secret".to_string());
327        assert_eq!(test_config.api_url(), "https://test.deribit.com/api/v2");
328    }
329
330    #[test]
331    fn test_deribit_config_default() {
332        let config = DeribitConfig::default();
333        assert_eq!(config.client_id, "");
334        assert_eq!(config.client_secret, "");
335        assert!(config.test_net);
336        assert_eq!(config.timeout_seconds, 30);
337        assert_eq!(config.max_retries, 3);
338        assert_eq!(config.rate_limit, None);
339        assert_eq!(
340            config.user_agent,
341            Some("deribit-rust-client/1.0".to_string())
342        );
343    }
344
345    #[test]
346    fn test_deribit_urls_constants() {
347        assert_eq!(DeribitUrls::PROD_BASE_URL, "https://www.deribit.com");
348        assert_eq!(DeribitUrls::TEST_BASE_URL, "https://test.deribit.com");
349        assert_eq!(DeribitUrls::PROD_WS_URL, "wss://www.deribit.com/ws/api/v2");
350        assert_eq!(DeribitUrls::TEST_WS_URL, "wss://test.deribit.com/ws/api/v2");
351    }
352
353    #[test]
354    fn test_websocket_config_new() {
355        let base = DeribitConfig::new("client".to_string(), "secret".to_string());
356        let ws_config = WebSocketConfig::new(base.clone());
357
358        assert_eq!(ws_config.base.client_id, base.client_id);
359        assert_eq!(ws_config.ping_interval, 30);
360        assert_eq!(ws_config.pong_timeout, 10);
361        assert_eq!(ws_config.reconnect_attempts, 5);
362        assert_eq!(ws_config.reconnect_delay, 5);
363        assert_eq!(ws_config.max_message_size, 1024 * 1024);
364        assert!(ws_config.compression);
365    }
366
367    #[test]
368    fn test_websocket_config_with_ping_interval() {
369        let base = DeribitConfig::new("client".to_string(), "secret".to_string());
370        let ws_config = WebSocketConfig::new(base).with_ping_interval(60);
371        assert_eq!(ws_config.ping_interval, 60);
372    }
373
374    #[test]
375    fn test_websocket_config_with_pong_timeout() {
376        let base = DeribitConfig::new("client".to_string(), "secret".to_string());
377        let ws_config = WebSocketConfig::new(base).with_pong_timeout(20);
378        assert_eq!(ws_config.pong_timeout, 20);
379    }
380
381    #[test]
382    fn test_websocket_config_with_reconnect_attempts() {
383        let base = DeribitConfig::new("client".to_string(), "secret".to_string());
384        let ws_config = WebSocketConfig::new(base).with_reconnect_attempts(10);
385        assert_eq!(ws_config.reconnect_attempts, 10);
386    }
387
388    #[test]
389    fn test_websocket_config_with_reconnect_delay() {
390        let base = DeribitConfig::new("client".to_string(), "secret".to_string());
391        let ws_config = WebSocketConfig::new(base).with_reconnect_delay(15);
392        assert_eq!(ws_config.reconnect_delay, 15);
393    }
394
395    #[test]
396    fn test_websocket_config_with_compression() {
397        let base = DeribitConfig::new("client".to_string(), "secret".to_string());
398        let ws_config = WebSocketConfig::new(base).with_compression(false);
399        assert!(!ws_config.compression);
400    }
401
402    #[test]
403    fn test_http_config_new() {
404        let base = DeribitConfig::new("client".to_string(), "secret".to_string());
405        let http_config = HttpConfig::new(base.clone());
406
407        assert_eq!(http_config.base.client_id, base.client_id);
408        assert_eq!(http_config.pool_size, None);
409        assert_eq!(http_config.keep_alive, Some(30));
410        assert!(http_config.http2);
411        assert!(http_config.gzip);
412    }
413
414    #[test]
415    fn test_http_config_with_pool_size() {
416        let base = DeribitConfig::new("client".to_string(), "secret".to_string());
417        let http_config = HttpConfig::new(base).with_pool_size(20);
418        assert_eq!(http_config.pool_size, Some(20));
419    }
420
421    #[test]
422    fn test_http_config_with_keep_alive() {
423        let base = DeribitConfig::new("client".to_string(), "secret".to_string());
424        let http_config = HttpConfig::new(base).with_keep_alive(60);
425        assert_eq!(http_config.keep_alive, Some(60));
426    }
427
428    #[test]
429    fn test_http_config_with_http2() {
430        let base = DeribitConfig::new("client".to_string(), "secret".to_string());
431        let http_config = HttpConfig::new(base).with_http2(false);
432        assert!(!http_config.http2);
433    }
434
435    #[test]
436    fn test_http_config_with_gzip() {
437        let base = DeribitConfig::new("client".to_string(), "secret".to_string());
438        let http_config = HttpConfig::new(base).with_gzip(false);
439        assert!(!http_config.gzip);
440    }
441
442    #[test]
443    fn test_config_serialization() {
444        let config = DeribitConfig::new("client".to_string(), "secret".to_string());
445        let json = serde_json::to_string(&config).unwrap();
446        let deserialized: DeribitConfig = serde_json::from_str(&json).unwrap();
447        assert_eq!(config.client_id, deserialized.client_id);
448        assert_eq!(config.client_secret, deserialized.client_secret);
449    }
450
451    #[test]
452    fn test_websocket_config_serialization() {
453        let base = DeribitConfig::new("client".to_string(), "secret".to_string());
454        let ws_config = WebSocketConfig::new(base);
455        let json = serde_json::to_string(&ws_config).unwrap();
456        let deserialized: WebSocketConfig = serde_json::from_str(&json).unwrap();
457        assert_eq!(ws_config.ping_interval, deserialized.ping_interval);
458    }
459
460    #[test]
461    fn test_http_config_serialization() {
462        let base = DeribitConfig::new("client".to_string(), "secret".to_string());
463        let http_config = HttpConfig::new(base);
464        let json = serde_json::to_string(&http_config).unwrap();
465        let deserialized: HttpConfig = serde_json::from_str(&json).unwrap();
466        assert_eq!(http_config.http2, deserialized.http2);
467    }
468
469    #[test]
470    fn test_debug_and_display_implementations() {
471        let config = DeribitConfig::new("client".to_string(), "secret".to_string());
472        let debug_str = format!("{:?}", config);
473        let display_str = format!("{}", config);
474
475        assert!(debug_str.contains("client"));
476        assert!(display_str.contains("client"));
477    }
478}