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