Skip to main content

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}