Skip to main content

nautilus_hyperliquid/python/
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//! Python bindings for Hyperliquid configuration.
17
18use nautilus_core::string::secret::SecretString;
19use nautilus_model::identifiers::AccountId;
20use nautilus_network::websocket::TransportBackend;
21use pyo3::prelude::*;
22
23use crate::{
24    common::enums::HyperliquidEnvironment,
25    config::{HyperliquidDataClientConfig, HyperliquidExecutionClientConfig},
26};
27
28#[pymethods]
29#[pyo3_stub_gen::derive::gen_stub_pymethods]
30impl HyperliquidDataClientConfig {
31    /// Configuration for the Hyperliquid data client.
32    ///
33    /// The `stale_stream_*` options control the stream health monitor. With recovery
34    /// enabled, a stale stream is warned about first, targeted-resubscribed once per
35    /// recovery cooldown (preserving its original `l2Book` options), and escalated to
36    /// a full WebSocket reconnect after `stale_stream_max_targeted_resubscribes`
37    /// failed attempts; fresh data resets the ladder. See the Hyperliquid integration
38    /// guide ("Stream health and recovery") for details.
39    #[new]
40    #[pyo3(signature = (
41        environment = None,
42        private_key = None,
43        base_url_http = None,
44        base_url_ws = None,
45        proxy_url = None,
46        http_timeout_secs = None,
47        ws_timeout_secs = None,
48        update_instruments_interval_mins = None,
49        transport_backend = None,
50        stale_stream_receive_timeout_secs = None,
51        stream_health_check_interval_secs = None,
52        stale_stream_warning_cooldown_secs = None,
53        stale_stream_recovery_enabled = None,
54        stale_stream_recovery_cooldown_secs = None,
55        stale_stream_max_targeted_resubscribes = None,
56    ))]
57    #[expect(clippy::too_many_arguments)]
58    fn py_new(
59        environment: Option<HyperliquidEnvironment>,
60        private_key: Option<String>,
61        base_url_http: Option<String>,
62        base_url_ws: Option<String>,
63        proxy_url: Option<String>,
64        http_timeout_secs: Option<u64>,
65        ws_timeout_secs: Option<u64>,
66        update_instruments_interval_mins: Option<u64>,
67        transport_backend: Option<TransportBackend>,
68        stale_stream_receive_timeout_secs: Option<u64>,
69        stream_health_check_interval_secs: Option<u64>,
70        stale_stream_warning_cooldown_secs: Option<u64>,
71        stale_stream_recovery_enabled: Option<bool>,
72        stale_stream_recovery_cooldown_secs: Option<u64>,
73        stale_stream_max_targeted_resubscribes: Option<u32>,
74    ) -> Self {
75        let defaults = Self::default();
76        Self {
77            private_key: private_key.map(SecretString::from),
78            base_url_http,
79            base_url_ws,
80            proxy_url: proxy_url.map(SecretString::from),
81            environment: environment.unwrap_or(defaults.environment),
82            http_timeout_secs: http_timeout_secs.unwrap_or(defaults.http_timeout_secs),
83            ws_timeout_secs: ws_timeout_secs.unwrap_or(defaults.ws_timeout_secs),
84            stale_stream_receive_timeout_secs: stale_stream_receive_timeout_secs
85                .unwrap_or(defaults.stale_stream_receive_timeout_secs),
86            stream_health_check_interval_secs: stream_health_check_interval_secs
87                .unwrap_or(defaults.stream_health_check_interval_secs),
88            stale_stream_warning_cooldown_secs: stale_stream_warning_cooldown_secs
89                .unwrap_or(defaults.stale_stream_warning_cooldown_secs),
90            stale_stream_recovery_enabled: stale_stream_recovery_enabled
91                .unwrap_or(defaults.stale_stream_recovery_enabled),
92            stale_stream_recovery_cooldown_secs: stale_stream_recovery_cooldown_secs
93                .unwrap_or(defaults.stale_stream_recovery_cooldown_secs),
94            stale_stream_max_targeted_resubscribes: stale_stream_max_targeted_resubscribes
95                .unwrap_or(defaults.stale_stream_max_targeted_resubscribes),
96            update_instruments_interval_mins: update_instruments_interval_mins
97                .unwrap_or(defaults.update_instruments_interval_mins),
98            transport_backend: transport_backend.unwrap_or(defaults.transport_backend),
99        }
100    }
101
102    #[getter]
103    const fn has_proxy_url(&self) -> bool {
104        self.proxy_url.is_some()
105    }
106
107    fn __repr__(&self) -> String {
108        stringify!(HyperliquidDataClientConfig).to_string()
109    }
110}
111
112#[pymethods]
113#[pyo3_stub_gen::derive::gen_stub_pymethods]
114impl HyperliquidExecutionClientConfig {
115    /// Configuration for the Hyperliquid execution client.
116    #[new]
117    #[pyo3(signature = (
118        account_id = None,
119        private_key = None,
120        vault_address = None,
121        account_address = None,
122        environment = None,
123        base_url_http = None,
124        base_url_ws = None,
125        base_url_exchange = None,
126        proxy_url = None,
127        http_timeout_secs = None,
128        max_retries = None,
129        retry_delay_initial_ms = None,
130        retry_delay_max_ms = None,
131        normalize_prices = None,
132        market_order_slippage_bps = None,
133        include_builder_attribution = None,
134        ws_post_timeout_secs = None,
135        transport_backend = None,
136    ))]
137    #[expect(clippy::too_many_arguments)]
138    fn py_new(
139        account_id: Option<AccountId>,
140        private_key: Option<String>,
141        vault_address: Option<String>,
142        account_address: Option<String>,
143        environment: Option<HyperliquidEnvironment>,
144        base_url_http: Option<String>,
145        base_url_ws: Option<String>,
146        base_url_exchange: Option<String>,
147        proxy_url: Option<String>,
148        http_timeout_secs: Option<u64>,
149        max_retries: Option<u32>,
150        retry_delay_initial_ms: Option<u64>,
151        retry_delay_max_ms: Option<u64>,
152        normalize_prices: Option<bool>,
153        market_order_slippage_bps: Option<u32>,
154        include_builder_attribution: Option<bool>,
155        ws_post_timeout_secs: Option<u64>,
156        transport_backend: Option<TransportBackend>,
157    ) -> Self {
158        let defaults = Self::default();
159        Self {
160            account_id: account_id.unwrap_or(defaults.account_id),
161            private_key: private_key.map(SecretString::from),
162            vault_address,
163            account_address,
164            base_url_http,
165            base_url_ws,
166            base_url_exchange,
167            proxy_url: proxy_url.map(SecretString::from),
168            environment: environment.unwrap_or(defaults.environment),
169            http_timeout_secs: http_timeout_secs.unwrap_or(defaults.http_timeout_secs),
170            max_retries: max_retries.unwrap_or(defaults.max_retries),
171            retry_delay_initial_ms: retry_delay_initial_ms
172                .unwrap_or(defaults.retry_delay_initial_ms),
173            retry_delay_max_ms: retry_delay_max_ms.unwrap_or(defaults.retry_delay_max_ms),
174            normalize_prices: normalize_prices.unwrap_or(defaults.normalize_prices),
175            market_order_slippage_bps: market_order_slippage_bps
176                .unwrap_or(defaults.market_order_slippage_bps),
177            include_builder_attribution: include_builder_attribution
178                .unwrap_or(defaults.include_builder_attribution),
179            ws_post_timeout_secs: ws_post_timeout_secs.unwrap_or(defaults.ws_post_timeout_secs),
180            transport_backend: transport_backend.unwrap_or(defaults.transport_backend),
181            outcome_settlement_poll_secs: defaults.outcome_settlement_poll_secs,
182        }
183    }
184
185    #[getter]
186    const fn has_proxy_url(&self) -> bool {
187        self.proxy_url.is_some()
188    }
189
190    fn __repr__(&self) -> String {
191        stringify!(HyperliquidExecutionClientConfig).to_string()
192    }
193}