nautilus_bitmex/
config.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2025 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 types for the BitMEX adapter clients.
17
18use nautilus_model::identifiers::AccountId;
19
20use crate::common::consts::{
21    BITMEX_HTTP_TESTNET_URL, BITMEX_HTTP_URL, BITMEX_WS_TESTNET_URL, BITMEX_WS_URL,
22};
23
24/// Configuration for the BitMEX live data client.
25#[derive(Clone, Debug)]
26pub struct BitmexDataClientConfig {
27    /// Optional API key used for authenticated REST/WebSocket requests.
28    pub api_key: Option<String>,
29    /// Optional API secret used for authenticated REST/WebSocket requests.
30    pub api_secret: Option<String>,
31    /// Optional override for the REST base URL.
32    pub base_url_http: Option<String>,
33    /// Optional override for the WebSocket URL.
34    pub base_url_ws: Option<String>,
35    /// Optional REST timeout in seconds.
36    pub http_timeout_secs: Option<u64>,
37    /// Optional maximum retry attempts for REST requests.
38    pub max_retries: Option<u32>,
39    /// Optional initial retry backoff in milliseconds.
40    pub retry_delay_initial_ms: Option<u64>,
41    /// Optional maximum retry backoff in milliseconds.
42    pub retry_delay_max_ms: Option<u64>,
43    /// Optional heartbeat interval (seconds) for the WebSocket client.
44    pub heartbeat_interval_secs: Option<u64>,
45    /// Optional receive window in milliseconds for signed requests (default 10_000).
46    ///
47    /// This value determines how far in the future the `api-expires` timestamp will be set
48    /// for signed REST requests. BitMEX uses seconds-granularity Unix timestamps in the
49    /// `api-expires` header, calculated as: `current_timestamp + (recv_window_ms / 1000)`.
50    ///
51    /// **Note**: This parameter is specified in milliseconds for consistency with other
52    /// adapter configurations (e.g., Bybit's `recv_window_ms`), but BitMEX only supports
53    /// seconds-granularity timestamps. The value is converted via integer division, so
54    /// 10000ms becomes 10 seconds, 15500ms becomes 15 seconds, etc.
55    ///
56    /// A larger window provides more tolerance for clock skew and network latency, but
57    /// increases the replay attack window. The default of 10 seconds should be sufficient
58    /// for most deployments. Consider increasing this value (e.g., to 30_000ms = 30s) if you
59    /// experience request expiration errors due to clock drift or high network latency.
60    pub recv_window_ms: Option<u64>,
61    /// When `true`, only active instruments are requested during bootstrap.
62    pub active_only: bool,
63    /// Optional interval (minutes) for instrument refresh from REST.
64    pub update_instruments_interval_mins: Option<u64>,
65    /// When `true`, use BitMEX testnet endpoints by default.
66    pub use_testnet: bool,
67    /// Maximum number of requests per second (burst limit).
68    pub max_requests_per_second: Option<u32>,
69    /// Maximum number of requests per minute (rolling window).
70    pub max_requests_per_minute: Option<u32>,
71}
72
73impl Default for BitmexDataClientConfig {
74    fn default() -> Self {
75        Self {
76            api_key: None,
77            api_secret: None,
78            base_url_http: None,
79            base_url_ws: None,
80            http_timeout_secs: Some(60),
81            max_retries: Some(3),
82            retry_delay_initial_ms: Some(1_000),
83            retry_delay_max_ms: Some(10_000),
84            heartbeat_interval_secs: None,
85            recv_window_ms: Some(10_000),
86            active_only: true,
87            update_instruments_interval_mins: None,
88            use_testnet: false,
89            max_requests_per_second: Some(10),
90            max_requests_per_minute: Some(120),
91        }
92    }
93}
94
95impl BitmexDataClientConfig {
96    /// Creates a configuration with default values.
97    #[must_use]
98    pub fn new() -> Self {
99        Self::default()
100    }
101
102    /// Returns `true` if both API key and secret are available.
103    #[must_use]
104    pub fn has_api_credentials(&self) -> bool {
105        self.api_key.is_some() && self.api_secret.is_some()
106    }
107
108    /// Returns the REST base URL, considering overrides and the testnet flag.
109    #[must_use]
110    pub fn http_base_url(&self) -> String {
111        self.base_url_http.clone().unwrap_or_else(|| {
112            if self.use_testnet {
113                BITMEX_HTTP_TESTNET_URL.to_string()
114            } else {
115                BITMEX_HTTP_URL.to_string()
116            }
117        })
118    }
119
120    /// Returns the WebSocket URL, considering overrides and the testnet flag.
121    #[must_use]
122    pub fn ws_url(&self) -> String {
123        self.base_url_ws.clone().unwrap_or_else(|| {
124            if self.use_testnet {
125                BITMEX_WS_TESTNET_URL.to_string()
126            } else {
127                BITMEX_WS_URL.to_string()
128            }
129        })
130    }
131}
132
133/// Configuration for the BitMEX live execution client.
134#[derive(Clone, Debug)]
135pub struct BitmexExecClientConfig {
136    /// API key used for authenticated requests.
137    pub api_key: Option<String>,
138    /// API secret used for authenticated requests.
139    pub api_secret: Option<String>,
140    /// Optional override for the REST base URL.
141    pub base_url_http: Option<String>,
142    /// Optional override for the WebSocket URL.
143    pub base_url_ws: Option<String>,
144    /// Optional REST timeout in seconds.
145    pub http_timeout_secs: Option<u64>,
146    /// Optional maximum retry attempts for REST requests.
147    pub max_retries: Option<u32>,
148    /// Optional initial retry backoff in milliseconds.
149    pub retry_delay_initial_ms: Option<u64>,
150    /// Optional maximum retry backoff in milliseconds.
151    pub retry_delay_max_ms: Option<u64>,
152    /// Optional heartbeat interval (seconds) for the WebSocket client.
153    pub heartbeat_interval_secs: Option<u64>,
154    /// Optional receive window in milliseconds for signed requests (default 10000).
155    ///
156    /// This value determines how far in the future the `api-expires` timestamp will be set
157    /// for signed REST requests. BitMEX uses seconds-granularity Unix timestamps in the
158    /// `api-expires` header, calculated as: `current_timestamp + (recv_window_ms / 1000)`.
159    ///
160    /// **Note**: This parameter is specified in milliseconds for consistency with other
161    /// adapter configurations (e.g., Bybit's `recv_window_ms`), but BitMEX only supports
162    /// seconds-granularity timestamps. The value is converted via integer division, so
163    /// 10000ms becomes 10 seconds, 15500ms becomes 15 seconds, etc.
164    ///
165    /// A larger window provides more tolerance for clock skew and network latency, but
166    /// increases the replay attack window. The default of 10 seconds should be sufficient
167    /// for most deployments. Consider increasing this value (e.g., to 30000ms = 30s) if you
168    /// experience request expiration errors due to clock drift or high network latency.
169    pub recv_window_ms: Option<u64>,
170    /// When `true`, only active instruments are requested during bootstrap.
171    pub active_only: bool,
172    /// When `true`, use BitMEX testnet endpoints by default.
173    pub use_testnet: bool,
174    /// Optional account identifier to associate with the execution client.
175    pub account_id: Option<AccountId>,
176    /// Maximum number of requests per second (burst limit).
177    pub max_requests_per_second: Option<u32>,
178    /// Maximum number of requests per minute (rolling window).
179    pub max_requests_per_minute: Option<u32>,
180}
181
182impl Default for BitmexExecClientConfig {
183    fn default() -> Self {
184        Self {
185            api_key: None,
186            api_secret: None,
187            base_url_http: None,
188            base_url_ws: None,
189            http_timeout_secs: Some(60),
190            max_retries: Some(3),
191            retry_delay_initial_ms: Some(1_000),
192            retry_delay_max_ms: Some(10_000),
193            heartbeat_interval_secs: Some(5),
194            recv_window_ms: Some(10_000),
195            active_only: true,
196            use_testnet: false,
197            account_id: None,
198            max_requests_per_second: Some(10),
199            max_requests_per_minute: Some(120),
200        }
201    }
202}
203
204impl BitmexExecClientConfig {
205    /// Creates a configuration with default values.
206    #[must_use]
207    pub fn new() -> Self {
208        Self::default()
209    }
210
211    /// Returns `true` if both API key and secret are available.
212    #[must_use]
213    pub fn has_api_credentials(&self) -> bool {
214        self.api_key.is_some() && self.api_secret.is_some()
215    }
216
217    /// Returns the REST base URL, considering overrides and the testnet flag.
218    #[must_use]
219    pub fn http_base_url(&self) -> String {
220        self.base_url_http.clone().unwrap_or_else(|| {
221            if self.use_testnet {
222                BITMEX_HTTP_TESTNET_URL.to_string()
223            } else {
224                BITMEX_HTTP_URL.to_string()
225            }
226        })
227    }
228
229    /// Returns the WebSocket URL, considering overrides and the testnet flag.
230    #[must_use]
231    pub fn ws_url(&self) -> String {
232        self.base_url_ws.clone().unwrap_or_else(|| {
233            if self.use_testnet {
234                BITMEX_WS_TESTNET_URL.to_string()
235            } else {
236                BITMEX_WS_URL.to_string()
237            }
238        })
239    }
240}