nautilus_hyperliquid/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 structures for the Hyperliquid adapter.
17
18use crate::common::consts::{info_url, ws_url};
19
20/// Configuration for the Hyperliquid data client.
21#[derive(Clone, Debug, bon::Builder)]
22#[cfg_attr(
23 feature = "python",
24 pyo3::pyclass(
25 module = "nautilus_trader.core.nautilus_pyo3.hyperliquid",
26 from_py_object
27 )
28)]
29#[cfg_attr(
30 feature = "python",
31 pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.hyperliquid")
32)]
33pub struct HyperliquidDataClientConfig {
34 /// Optional private key for authenticated endpoints.
35 pub private_key: Option<String>,
36 /// Override for the WebSocket URL.
37 pub base_url_ws: Option<String>,
38 /// Override for the HTTP info URL.
39 pub base_url_http: Option<String>,
40 /// Optional HTTP proxy URL.
41 pub http_proxy_url: Option<String>,
42 /// Optional WebSocket proxy URL.
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 /// When true the client will use Hyperliquid testnet endpoints.
48 #[builder(default)]
49 pub is_testnet: bool,
50 /// HTTP timeout in seconds.
51 #[builder(default = 60)]
52 pub http_timeout_secs: u64,
53 /// WebSocket timeout in seconds.
54 #[builder(default = 30)]
55 pub ws_timeout_secs: u64,
56 /// Interval for refreshing instruments in minutes.
57 #[builder(default = 60)]
58 pub update_instruments_interval_mins: u64,
59}
60
61impl Default for HyperliquidDataClientConfig {
62 fn default() -> Self {
63 Self::builder().build()
64 }
65}
66
67impl HyperliquidDataClientConfig {
68 /// Creates a new configuration with default settings.
69 #[must_use]
70 pub fn new() -> Self {
71 Self::default()
72 }
73
74 /// Returns `true` when private key is populated and non-empty.
75 #[must_use]
76 pub fn has_credentials(&self) -> bool {
77 self.private_key
78 .as_deref()
79 .is_some_and(|s| !s.trim().is_empty())
80 }
81
82 /// Returns the WebSocket URL, respecting the testnet flag and overrides.
83 #[must_use]
84 pub fn ws_url(&self) -> String {
85 self.base_url_ws
86 .clone()
87 .unwrap_or_else(|| ws_url(self.is_testnet).to_string())
88 }
89
90 /// Returns the HTTP info URL, respecting the testnet flag and overrides.
91 #[must_use]
92 pub fn http_url(&self) -> String {
93 self.base_url_http
94 .clone()
95 .unwrap_or_else(|| info_url(self.is_testnet).to_string())
96 }
97}
98
99/// Configuration for the Hyperliquid execution client.
100#[derive(Clone, Debug, bon::Builder)]
101#[cfg_attr(
102 feature = "python",
103 pyo3::pyclass(
104 module = "nautilus_trader.core.nautilus_pyo3.hyperliquid",
105 from_py_object
106 )
107)]
108#[cfg_attr(
109 feature = "python",
110 pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.hyperliquid")
111)]
112pub struct HyperliquidExecClientConfig {
113 /// Private key for signing transactions.
114 ///
115 /// If not provided, falls back to environment variable:
116 /// - Mainnet: `HYPERLIQUID_PK`
117 /// - Testnet: `HYPERLIQUID_TESTNET_PK`
118 pub private_key: Option<String>,
119 /// Optional vault address for vault operations.
120 pub vault_address: Option<String>,
121 /// Optional main account address when using an agent wallet (API sub-key).
122 /// When set, used for balance queries, position reports, and WS subscriptions
123 /// instead of the address derived from the private key.
124 pub account_address: Option<String>,
125 /// Override for the WebSocket URL.
126 pub base_url_ws: Option<String>,
127 /// Override for the HTTP info URL.
128 pub base_url_http: Option<String>,
129 /// Override for the exchange API URL.
130 pub base_url_exchange: Option<String>,
131 /// Optional HTTP proxy URL.
132 pub http_proxy_url: Option<String>,
133 /// Optional WebSocket proxy URL.
134 ///
135 /// Note: WebSocket proxy support is not yet implemented. This field is reserved
136 /// for future functionality. Use `http_proxy_url` for REST API proxy support.
137 pub ws_proxy_url: Option<String>,
138 /// When true the client will use Hyperliquid testnet endpoints.
139 #[builder(default)]
140 pub is_testnet: bool,
141 /// HTTP timeout in seconds.
142 #[builder(default = 60)]
143 pub http_timeout_secs: u64,
144 /// Maximum number of retry attempts for HTTP requests.
145 #[builder(default = 3)]
146 pub max_retries: u32,
147 /// Initial retry delay in milliseconds.
148 #[builder(default = 100)]
149 pub retry_delay_initial_ms: u64,
150 /// Maximum retry delay in milliseconds.
151 #[builder(default = 5000)]
152 pub retry_delay_max_ms: u64,
153 /// When true, normalize order prices to 5 significant figures
154 /// before submission (Hyperliquid requirement).
155 #[builder(default = true)]
156 pub normalize_prices: bool,
157}
158
159impl Default for HyperliquidExecClientConfig {
160 fn default() -> Self {
161 Self::builder().build()
162 }
163}
164
165impl HyperliquidExecClientConfig {
166 /// Creates a new configuration with the provided private key.
167 #[must_use]
168 pub fn new(private_key: Option<String>) -> Self {
169 Self {
170 private_key,
171 ..Self::default()
172 }
173 }
174
175 /// Returns `true` when private key is populated and non-empty.
176 #[must_use]
177 pub fn has_credentials(&self) -> bool {
178 self.private_key
179 .as_deref()
180 .is_some_and(|s| !s.trim().is_empty())
181 }
182
183 /// Returns the WebSocket URL, respecting the testnet flag and overrides.
184 #[must_use]
185 pub fn ws_url(&self) -> String {
186 self.base_url_ws
187 .clone()
188 .unwrap_or_else(|| ws_url(self.is_testnet).to_string())
189 }
190
191 /// Returns the HTTP info URL, respecting the testnet flag and overrides.
192 #[must_use]
193 pub fn http_url(&self) -> String {
194 self.base_url_http
195 .clone()
196 .unwrap_or_else(|| info_url(self.is_testnet).to_string())
197 }
198}
199
200#[cfg(test)]
201mod tests {
202 use rstest::rstest;
203
204 use super::*;
205
206 #[rstest]
207 fn test_exec_config_default_account_address_is_none() {
208 let config = HyperliquidExecClientConfig::default();
209 assert!(config.account_address.is_none());
210 }
211
212 #[rstest]
213 fn test_exec_config_with_account_address() {
214 let config = HyperliquidExecClientConfig {
215 account_address: Some("0x1234".to_string()),
216 ..HyperliquidExecClientConfig::default()
217 };
218 assert_eq!(config.account_address.as_deref(), Some("0x1234"));
219 }
220}