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