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