nautilus_hyperliquid/python/
config.rs1use 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 #[new]
42 #[pyo3(signature = (
43 environment = None,
44 private_key = None,
45 base_url_http = None,
46 base_url_ws = None,
47 proxy_url = None,
48 http_timeout_secs = None,
49 ws_timeout_secs = None,
50 update_instruments_interval_mins = None,
51 transport_backend = None,
52 stale_stream_receive_timeout_secs = None,
53 stream_health_check_interval_secs = None,
54 stale_stream_warning_cooldown_secs = None,
55 stale_stream_recovery_enabled = None,
56 stale_stream_recovery_cooldown_secs = None,
57 stale_stream_max_targeted_resubscribes = None,
58 book_snapshot_timeout_secs = None,
59 ))]
60 #[expect(clippy::too_many_arguments)]
61 fn py_new(
62 environment: Option<HyperliquidEnvironment>,
63 private_key: Option<String>,
64 base_url_http: Option<String>,
65 base_url_ws: Option<String>,
66 proxy_url: Option<String>,
67 http_timeout_secs: Option<u64>,
68 ws_timeout_secs: Option<u64>,
69 update_instruments_interval_mins: Option<u64>,
70 transport_backend: Option<TransportBackend>,
71 stale_stream_receive_timeout_secs: Option<u64>,
72 stream_health_check_interval_secs: Option<u64>,
73 stale_stream_warning_cooldown_secs: Option<u64>,
74 stale_stream_recovery_enabled: Option<bool>,
75 stale_stream_recovery_cooldown_secs: Option<u64>,
76 stale_stream_max_targeted_resubscribes: Option<u32>,
77 book_snapshot_timeout_secs: Option<u64>,
78 ) -> Self {
79 let defaults = Self::default();
80 Self {
81 private_key: private_key.map(SecretString::from),
82 base_url_http,
83 base_url_ws,
84 proxy_url: proxy_url.map(SecretString::from),
85 environment: environment.unwrap_or(defaults.environment),
86 http_timeout_secs: http_timeout_secs.unwrap_or(defaults.http_timeout_secs),
87 ws_timeout_secs: ws_timeout_secs.unwrap_or(defaults.ws_timeout_secs),
88 stale_stream_receive_timeout_secs: stale_stream_receive_timeout_secs
89 .unwrap_or(defaults.stale_stream_receive_timeout_secs),
90 stream_health_check_interval_secs: stream_health_check_interval_secs
91 .unwrap_or(defaults.stream_health_check_interval_secs),
92 stale_stream_warning_cooldown_secs: stale_stream_warning_cooldown_secs
93 .unwrap_or(defaults.stale_stream_warning_cooldown_secs),
94 stale_stream_recovery_enabled: stale_stream_recovery_enabled
95 .unwrap_or(defaults.stale_stream_recovery_enabled),
96 stale_stream_recovery_cooldown_secs: stale_stream_recovery_cooldown_secs
97 .unwrap_or(defaults.stale_stream_recovery_cooldown_secs),
98 stale_stream_max_targeted_resubscribes: stale_stream_max_targeted_resubscribes
99 .unwrap_or(defaults.stale_stream_max_targeted_resubscribes),
100 book_snapshot_timeout_secs: book_snapshot_timeout_secs
101 .unwrap_or(defaults.book_snapshot_timeout_secs),
102 update_instruments_interval_mins: update_instruments_interval_mins
103 .unwrap_or(defaults.update_instruments_interval_mins),
104 transport_backend: transport_backend.unwrap_or(defaults.transport_backend),
105 }
106 }
107
108 #[getter]
109 const fn has_proxy_url(&self) -> bool {
110 self.proxy_url.is_some()
111 }
112
113 fn __repr__(&self) -> String {
114 stringify!(HyperliquidDataClientConfig).to_string()
115 }
116}
117
118#[pymethods]
119#[pyo3_stub_gen::derive::gen_stub_pymethods]
120impl HyperliquidExecutionClientConfig {
121 #[new]
123 #[pyo3(signature = (
124 account_id = None,
125 private_key = None,
126 vault_address = None,
127 account_address = None,
128 environment = None,
129 base_url_http = None,
130 base_url_ws = None,
131 base_url_exchange = None,
132 proxy_url = None,
133 http_timeout_secs = None,
134 max_retries = None,
135 retry_delay_initial_ms = None,
136 retry_delay_max_ms = None,
137 normalize_prices = None,
138 market_order_slippage_bps = None,
139 include_builder_attribution = None,
140 ws_post_timeout_secs = None,
141 transport_backend = None,
142 scheduled_cancel_timeout_secs = None,
143 ))]
144 #[expect(clippy::too_many_arguments)]
145 fn py_new(
146 account_id: Option<AccountId>,
147 private_key: Option<String>,
148 vault_address: Option<String>,
149 account_address: Option<String>,
150 environment: Option<HyperliquidEnvironment>,
151 base_url_http: Option<String>,
152 base_url_ws: Option<String>,
153 base_url_exchange: Option<String>,
154 proxy_url: Option<String>,
155 http_timeout_secs: Option<u64>,
156 max_retries: Option<u32>,
157 retry_delay_initial_ms: Option<u64>,
158 retry_delay_max_ms: Option<u64>,
159 normalize_prices: Option<bool>,
160 market_order_slippage_bps: Option<u32>,
161 include_builder_attribution: Option<bool>,
162 ws_post_timeout_secs: Option<u64>,
163 transport_backend: Option<TransportBackend>,
164 scheduled_cancel_timeout_secs: Option<u64>,
165 ) -> Self {
166 let defaults = Self::default();
167 Self {
168 account_id: account_id.unwrap_or(defaults.account_id),
169 private_key: private_key.map(SecretString::from),
170 vault_address,
171 account_address,
172 base_url_http,
173 base_url_ws,
174 base_url_exchange,
175 proxy_url: proxy_url.map(SecretString::from),
176 environment: environment.unwrap_or(defaults.environment),
177 http_timeout_secs: http_timeout_secs.unwrap_or(defaults.http_timeout_secs),
178 max_retries: max_retries.unwrap_or(defaults.max_retries),
179 retry_delay_initial_ms: retry_delay_initial_ms
180 .unwrap_or(defaults.retry_delay_initial_ms),
181 retry_delay_max_ms: retry_delay_max_ms.unwrap_or(defaults.retry_delay_max_ms),
182 normalize_prices: normalize_prices.unwrap_or(defaults.normalize_prices),
183 market_order_slippage_bps: market_order_slippage_bps
184 .unwrap_or(defaults.market_order_slippage_bps),
185 include_builder_attribution: include_builder_attribution
186 .unwrap_or(defaults.include_builder_attribution),
187 ws_post_timeout_secs: ws_post_timeout_secs.unwrap_or(defaults.ws_post_timeout_secs),
188 transport_backend: transport_backend.unwrap_or(defaults.transport_backend),
189 outcome_settlement_poll_secs: defaults.outcome_settlement_poll_secs,
190 scheduled_cancel_timeout_secs,
191 }
192 }
193
194 #[getter]
195 const fn has_proxy_url(&self) -> bool {
196 self.proxy_url.is_some()
197 }
198
199 fn __repr__(&self) -> String {
200 stringify!(HyperliquidExecutionClientConfig).to_string()
201 }
202}
203
204#[cfg(test)]
205mod tests {
206 use pyo3::types::PyDict;
207 use rstest::rstest;
208
209 use super::*;
210
211 #[rstest]
212 #[case::default(None)]
213 #[case::enabled(Some(60))]
214 fn test_python_scheduled_cancel(#[case] timeout_secs: Option<u64>) {
215 Python::initialize();
216 Python::attach(|py| {
217 let kwargs = PyDict::new(py);
218 if let Some(timeout_secs) = timeout_secs {
219 kwargs
220 .set_item("scheduled_cancel_timeout_secs", timeout_secs)
221 .unwrap();
222 }
223
224 let config = py
225 .get_type::<HyperliquidExecutionClientConfig>()
226 .call((), Some(&kwargs))
227 .unwrap();
228 let getter = config
229 .getattr("scheduled_cancel_timeout_secs")
230 .unwrap()
231 .extract::<Option<u64>>()
232 .unwrap();
233 let rust_config = config
234 .extract::<PyRef<HyperliquidExecutionClientConfig>>()
235 .unwrap();
236
237 assert_eq!(getter, timeout_secs);
238 assert_eq!(rust_config.scheduled_cancel_timeout_secs, timeout_secs);
239 });
240 }
241}