Skip to main content

nautilus_bitmex/
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 types for the BitMEX adapter clients.
17
18use nautilus_model::identifiers::AccountId;
19
20use crate::common::{
21    consts::{BITMEX_HTTP_TESTNET_URL, BITMEX_HTTP_URL, BITMEX_WS_TESTNET_URL, BITMEX_WS_URL},
22    credential::credential_env_vars,
23};
24
25/// Configuration for the BitMEX live data client.
26#[derive(Clone, Debug)]
27#[cfg_attr(
28    feature = "python",
29    pyo3::pyclass(module = "nautilus_trader.core.nautilus_pyo3.bitmex", from_py_object)
30)]
31pub struct BitmexDataClientConfig {
32    /// Optional API key used for authenticated REST/WebSocket requests.
33    pub api_key: Option<String>,
34    /// Optional API secret used for authenticated REST/WebSocket requests.
35    pub api_secret: Option<String>,
36    /// Optional override for the REST base URL.
37    pub base_url_http: Option<String>,
38    /// Optional override for the WebSocket URL.
39    pub base_url_ws: Option<String>,
40    /// Optional HTTP proxy URL for general HTTP client operations.
41    pub http_proxy_url: Option<String>,
42    /// Optional WebSocket proxy URL for WebSocket client.
43    ///
44    /// Note: WebSocket proxy support is not yet implemented. This field is reserved
45    /// for future functionality. Use `http_proxy_url` for REST API proxy support.
46    pub ws_proxy_url: Option<String>,
47    /// Optional REST timeout in seconds.
48    pub http_timeout_secs: Option<u64>,
49    /// Optional maximum retry attempts for REST requests.
50    pub max_retries: Option<u32>,
51    /// Optional initial retry backoff in milliseconds.
52    pub retry_delay_initial_ms: Option<u64>,
53    /// Optional maximum retry backoff in milliseconds.
54    pub retry_delay_max_ms: Option<u64>,
55    /// Optional heartbeat interval (seconds) for the WebSocket client.
56    pub heartbeat_interval_secs: Option<u64>,
57    /// Optional receive window in milliseconds for signed requests (default 10_000).
58    ///
59    /// This value determines how far in the future the `api-expires` timestamp will be set
60    /// for signed REST requests. BitMEX uses seconds-granularity Unix timestamps in the
61    /// `api-expires` header, calculated as: `current_timestamp + (recv_window_ms / 1000)`.
62    ///
63    /// **Note**: This parameter is specified in milliseconds for consistency with other
64    /// adapter configurations (e.g., Bybit's `recv_window_ms`), but BitMEX only supports
65    /// seconds-granularity timestamps. The value is converted via integer division, so
66    /// 10000ms becomes 10 seconds, 15500ms becomes 15 seconds, etc.
67    ///
68    /// A larger window provides more tolerance for clock skew and network latency, but
69    /// increases the replay attack window. The default of 10 seconds should be sufficient
70    /// for most deployments. Consider increasing this value (e.g., to 30_000ms = 30s) if you
71    /// experience request expiration errors due to clock drift or high network latency.
72    pub recv_window_ms: Option<u64>,
73    /// When `true`, only active instruments are requested during bootstrap.
74    pub active_only: bool,
75    /// Optional interval (minutes) for instrument refresh from REST.
76    pub update_instruments_interval_mins: Option<u64>,
77    /// When `true`, use BitMEX testnet endpoints by default.
78    pub use_testnet: bool,
79    /// Maximum number of requests per second (burst limit).
80    pub max_requests_per_second: Option<u32>,
81    /// Maximum number of requests per minute (rolling window).
82    pub max_requests_per_minute: Option<u32>,
83}
84
85impl Default for BitmexDataClientConfig {
86    fn default() -> Self {
87        Self {
88            api_key: None,
89            api_secret: None,
90            base_url_http: None,
91            base_url_ws: None,
92            http_proxy_url: None,
93            ws_proxy_url: None,
94            http_timeout_secs: Some(60),
95            max_retries: Some(3),
96            retry_delay_initial_ms: Some(1_000),
97            retry_delay_max_ms: Some(10_000),
98            heartbeat_interval_secs: None,
99            recv_window_ms: Some(10_000),
100            active_only: true,
101            update_instruments_interval_mins: None,
102            use_testnet: false,
103            max_requests_per_second: Some(10),
104            max_requests_per_minute: Some(120),
105        }
106    }
107}
108
109impl BitmexDataClientConfig {
110    /// Creates a configuration with default values.
111    #[must_use]
112    pub fn new() -> Self {
113        Self::default()
114    }
115
116    /// Returns `true` if both API key and secret are available
117    /// (either explicitly set or resolvable from environment variables).
118    #[must_use]
119    pub fn has_api_credentials(&self) -> bool {
120        let (key_var, secret_var) = credential_env_vars(self.use_testnet);
121        let has_key = self.api_key.is_some() || std::env::var(key_var).is_ok();
122        let has_secret = self.api_secret.is_some() || std::env::var(secret_var).is_ok();
123        has_key && has_secret
124    }
125
126    /// Returns the REST base URL, considering overrides and the testnet flag.
127    #[must_use]
128    pub fn http_base_url(&self) -> String {
129        self.base_url_http.clone().unwrap_or_else(|| {
130            if self.use_testnet {
131                BITMEX_HTTP_TESTNET_URL.to_string()
132            } else {
133                BITMEX_HTTP_URL.to_string()
134            }
135        })
136    }
137
138    /// Returns the WebSocket URL, considering overrides and the testnet flag.
139    #[must_use]
140    pub fn ws_url(&self) -> String {
141        self.base_url_ws.clone().unwrap_or_else(|| {
142            if self.use_testnet {
143                BITMEX_WS_TESTNET_URL.to_string()
144            } else {
145                BITMEX_WS_URL.to_string()
146            }
147        })
148    }
149}
150
151/// Configuration for the BitMEX live execution client.
152#[derive(Clone, Debug)]
153#[cfg_attr(
154    feature = "python",
155    pyo3::pyclass(module = "nautilus_trader.core.nautilus_pyo3.bitmex", from_py_object)
156)]
157pub struct BitmexExecClientConfig {
158    /// API key used for authenticated requests.
159    pub api_key: Option<String>,
160    /// API secret used for authenticated requests.
161    pub api_secret: Option<String>,
162    /// Optional override for the REST base URL.
163    pub base_url_http: Option<String>,
164    /// Optional override for the WebSocket URL.
165    pub base_url_ws: Option<String>,
166    /// Optional HTTP proxy URL for general HTTP client operations.
167    pub http_proxy_url: Option<String>,
168    /// Optional WebSocket proxy URL for WebSocket client.
169    ///
170    /// Note: WebSocket proxy support is not yet implemented. This field is reserved
171    /// for future functionality. Use `http_proxy_url` for REST API proxy support.
172    pub ws_proxy_url: Option<String>,
173    /// Optional REST timeout in seconds.
174    pub http_timeout_secs: Option<u64>,
175    /// Optional maximum retry attempts for REST requests.
176    pub max_retries: Option<u32>,
177    /// Optional initial retry backoff in milliseconds.
178    pub retry_delay_initial_ms: Option<u64>,
179    /// Optional maximum retry backoff in milliseconds.
180    pub retry_delay_max_ms: Option<u64>,
181    /// Optional heartbeat interval (seconds) for the WebSocket client.
182    pub heartbeat_interval_secs: Option<u64>,
183    /// Optional receive window in milliseconds for signed requests (default 10000).
184    ///
185    /// This value determines how far in the future the `api-expires` timestamp will be set
186    /// for signed REST requests. BitMEX uses seconds-granularity Unix timestamps in the
187    /// `api-expires` header, calculated as: `current_timestamp + (recv_window_ms / 1000)`.
188    ///
189    /// **Note**: This parameter is specified in milliseconds for consistency with other
190    /// adapter configurations (e.g., Bybit's `recv_window_ms`), but BitMEX only supports
191    /// seconds-granularity timestamps. The value is converted via integer division, so
192    /// 10000ms becomes 10 seconds, 15500ms becomes 15 seconds, etc.
193    ///
194    /// A larger window provides more tolerance for clock skew and network latency, but
195    /// increases the replay attack window. The default of 10 seconds should be sufficient
196    /// for most deployments. Consider increasing this value (e.g., to 30000ms = 30s) if you
197    /// experience request expiration errors due to clock drift or high network latency.
198    pub recv_window_ms: Option<u64>,
199    /// When `true`, only active instruments are requested during bootstrap.
200    pub active_only: bool,
201    /// When `true`, use BitMEX testnet endpoints by default.
202    pub use_testnet: bool,
203    /// Optional account identifier to associate with the execution client.
204    pub account_id: Option<AccountId>,
205    /// Maximum number of requests per second (burst limit).
206    pub max_requests_per_second: Option<u32>,
207    /// Maximum number of requests per minute (rolling window).
208    pub max_requests_per_minute: Option<u32>,
209    /// Number of HTTP clients in the submit broadcaster pool (defaults to 1).
210    pub submitter_pool_size: Option<usize>,
211    /// Number of HTTP clients in the cancel broadcaster pool (defaults to 1).
212    pub canceller_pool_size: Option<usize>,
213    /// Optional list of proxy URLs for submit broadcaster pool (path diversity).
214    pub submitter_proxy_urls: Option<Vec<String>>,
215    /// Optional list of proxy URLs for cancel broadcaster pool (path diversity).
216    pub canceller_proxy_urls: Option<Vec<String>>,
217    /// Optional dead man's switch timeout in seconds.
218    ///
219    /// When set, a background task periodically calls the BitMEX `cancelAllAfter` endpoint
220    /// to keep a server-side timer alive. If the client loses connectivity the timer expires
221    /// and BitMEX cancels all open orders. Calling with `timeout=0` disarms the switch.
222    /// The refresh interval is derived as `timeout / 4` (minimum 1 second).
223    pub deadmans_switch_timeout_secs: Option<u64>,
224}
225
226impl Default for BitmexExecClientConfig {
227    fn default() -> Self {
228        Self {
229            api_key: None,
230            api_secret: None,
231            base_url_http: None,
232            base_url_ws: None,
233            http_proxy_url: None,
234            ws_proxy_url: None,
235            http_timeout_secs: Some(60),
236            max_retries: Some(3),
237            retry_delay_initial_ms: Some(1_000),
238            retry_delay_max_ms: Some(10_000),
239            heartbeat_interval_secs: Some(5),
240            recv_window_ms: Some(10_000),
241            active_only: true,
242            use_testnet: false,
243            account_id: None,
244            max_requests_per_second: Some(10),
245            max_requests_per_minute: Some(120),
246            submitter_pool_size: None,
247            canceller_pool_size: None,
248            submitter_proxy_urls: None,
249            canceller_proxy_urls: None,
250            deadmans_switch_timeout_secs: None,
251        }
252    }
253}
254
255impl BitmexExecClientConfig {
256    /// Creates a configuration with default values.
257    #[must_use]
258    pub fn new() -> Self {
259        Self::default()
260    }
261
262    /// Returns `true` if both API key and secret are available
263    /// (either explicitly set or resolvable from environment variables).
264    #[must_use]
265    pub fn has_api_credentials(&self) -> bool {
266        let (key_var, secret_var) = credential_env_vars(self.use_testnet);
267        let has_key = self.api_key.is_some() || std::env::var(key_var).is_ok();
268        let has_secret = self.api_secret.is_some() || std::env::var(secret_var).is_ok();
269        has_key && has_secret
270    }
271
272    /// Returns the REST base URL, considering overrides and the testnet flag.
273    #[must_use]
274    pub fn http_base_url(&self) -> String {
275        self.base_url_http.clone().unwrap_or_else(|| {
276            if self.use_testnet {
277                BITMEX_HTTP_TESTNET_URL.to_string()
278            } else {
279                BITMEX_HTTP_URL.to_string()
280            }
281        })
282    }
283
284    /// Returns the WebSocket URL, considering overrides and the testnet flag.
285    #[must_use]
286    pub fn ws_url(&self) -> String {
287        self.base_url_ws.clone().unwrap_or_else(|| {
288            if self.use_testnet {
289                BITMEX_WS_TESTNET_URL.to_string()
290            } else {
291                BITMEX_WS_URL.to_string()
292            }
293        })
294    }
295}