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}