nautilus_hyperliquid/python/
mod.rs1#![expect(
19 clippy::missing_errors_doc,
20 reason = "errors documented on underlying Rust methods"
21)]
22
23pub mod config;
24pub mod enums;
25pub mod factories;
26pub mod http;
27pub mod urls;
28pub mod websocket;
29
30use nautilus_common::factories::{ClientConfig, DataClientFactory, ExecutionClientFactory};
31use nautilus_core::python::{to_pyruntime_err, to_pyvalue_err};
32use nautilus_model::{data::ensure_rust_extractor_registered, identifiers::ClientOrderId};
33use nautilus_system::get_global_pyo3_registry;
34use pyo3::prelude::*;
35
36use crate::{
37 account::resolve_execution_account_address,
38 common::{
39 builder_fee::{approve_from_env, revoke_from_env},
40 consts::{HYPERLIQUID, HYPERLIQUID_POST_ONLY_WOULD_MATCH},
41 enums::{
42 HyperliquidConditionalOrderType, HyperliquidEnvironment, HyperliquidProductType,
43 HyperliquidTpSl, HyperliquidTrailingOffsetType,
44 },
45 },
46 config::{HyperliquidDataClientConfig, HyperliquidExecClientConfig},
47 data_types::{
48 HyperliquidAllDexsAssetCtxs, HyperliquidAllMids, HyperliquidOpenInterest,
49 register_hyperliquid_custom_data,
50 },
51 factories::{
52 HyperliquidDataClientFactory, HyperliquidExecFactoryConfig,
53 HyperliquidExecutionClientFactory,
54 },
55 http::{HyperliquidHttpClient, models::Cloid},
56 websocket::HyperliquidWebSocketClient,
57};
58
59#[pyfunction]
73#[pyo3_stub_gen::derive::gen_stub_pyfunction(module = "nautilus_trader.adapters.hyperliquid")]
74#[pyo3(name = "builder_fee_approve")]
75fn py_builder_fee_approve() -> PyResult<bool> {
76 std::thread::spawn(move || {
77 let runtime = tokio::runtime::Builder::new_current_thread()
78 .enable_all()
79 .build()
80 .map_err(|e| to_pyruntime_err(format!("Failed to create runtime: {e}")))?;
81
82 Ok(runtime.block_on(approve_from_env(true)))
83 })
84 .join()
85 .map_err(|_| to_pyruntime_err("Thread panicked"))?
86}
87
88#[pyfunction]
102#[pyo3_stub_gen::derive::gen_stub_pyfunction(module = "nautilus_trader.adapters.hyperliquid")]
103#[pyo3(name = "builder_fee_revoke")]
104fn py_builder_fee_revoke() -> PyResult<bool> {
105 std::thread::spawn(move || {
106 let runtime = tokio::runtime::Builder::new_current_thread()
107 .enable_all()
108 .build()
109 .map_err(|e| to_pyruntime_err(format!("Failed to create runtime: {e}")))?;
110
111 Ok(runtime.block_on(revoke_from_env(true)))
112 })
113 .join()
114 .map_err(|_| to_pyruntime_err("Thread panicked"))?
115}
116
117#[pyfunction]
122#[pyo3_stub_gen::derive::gen_stub_pyfunction(module = "nautilus_trader.adapters.hyperliquid")]
123#[pyo3(name = "hyperliquid_cloid_from_client_order_id")]
124fn py_hyperliquid_cloid_from_client_order_id(client_order_id: ClientOrderId) -> String {
125 Cloid::from_client_order_id(client_order_id).to_hex()
126}
127
128#[pyfunction]
134#[pyo3_stub_gen::derive::gen_stub_pyfunction(module = "nautilus_trader.adapters.hyperliquid")]
135#[pyo3(name = "hyperliquid_product_type_from_symbol")]
136fn py_hyperliquid_product_type_from_symbol(symbol: &str) -> PyResult<HyperliquidProductType> {
137 HyperliquidProductType::from_symbol(symbol).map_err(to_pyvalue_err)
138}
139
140#[pyfunction]
146#[pyo3_stub_gen::derive::gen_stub_pyfunction(module = "nautilus_trader.adapters.hyperliquid")]
147#[pyo3(name = "hyperliquid_resolve_execution_account_address", signature = (private_key=None, vault_address=None, account_address=None, environment=HyperliquidEnvironment::Mainnet))]
148fn py_hyperliquid_resolve_execution_account_address(
149 private_key: Option<&str>,
150 vault_address: Option<&str>,
151 account_address: Option<&str>,
152 environment: HyperliquidEnvironment,
153) -> PyResult<Option<String>> {
154 resolve_execution_account_address(private_key, vault_address, account_address, environment)
155 .map_err(to_pyvalue_err)
156}
157
158#[expect(clippy::needless_pass_by_value)]
159fn extract_hyperliquid_data_factory(
160 py: Python<'_>,
161 factory: Py<PyAny>,
162) -> PyResult<Box<dyn DataClientFactory>> {
163 match factory.extract::<HyperliquidDataClientFactory>(py) {
164 Ok(f) => Ok(Box::new(f)),
165 Err(e) => Err(to_pyvalue_err(format!(
166 "Failed to extract HyperliquidDataClientFactory: {e}"
167 ))),
168 }
169}
170
171#[expect(clippy::needless_pass_by_value)]
172fn extract_hyperliquid_exec_factory(
173 py: Python<'_>,
174 factory: Py<PyAny>,
175) -> PyResult<Box<dyn ExecutionClientFactory>> {
176 match factory.extract::<HyperliquidExecutionClientFactory>(py) {
177 Ok(f) => Ok(Box::new(f)),
178 Err(e) => Err(to_pyvalue_err(format!(
179 "Failed to extract HyperliquidExecutionClientFactory: {e}"
180 ))),
181 }
182}
183
184#[expect(clippy::needless_pass_by_value)]
185fn extract_hyperliquid_data_config(
186 py: Python<'_>,
187 config: Py<PyAny>,
188) -> PyResult<Box<dyn ClientConfig>> {
189 match config.extract::<HyperliquidDataClientConfig>(py) {
190 Ok(c) => Ok(Box::new(c)),
191 Err(e) => Err(to_pyvalue_err(format!(
192 "Failed to extract HyperliquidDataClientConfig: {e}"
193 ))),
194 }
195}
196
197#[expect(clippy::needless_pass_by_value)]
198fn extract_hyperliquid_exec_config(
199 py: Python<'_>,
200 config: Py<PyAny>,
201) -> PyResult<Box<dyn ClientConfig>> {
202 match config.extract::<HyperliquidExecFactoryConfig>(py) {
203 Ok(c) => Ok(Box::new(c)),
204 Err(e) => Err(to_pyvalue_err(format!(
205 "Failed to extract HyperliquidExecFactoryConfig: {e}"
206 ))),
207 }
208}
209
210#[pymodule]
212pub fn hyperliquid(m: &Bound<'_, PyModule>) -> PyResult<()> {
213 m.add(
214 "HYPERLIQUID_POST_ONLY_WOULD_MATCH",
215 HYPERLIQUID_POST_ONLY_WOULD_MATCH,
216 )?;
217 m.add_class::<HyperliquidHttpClient>()?;
218 m.add_class::<HyperliquidWebSocketClient>()?;
219 m.add_class::<HyperliquidProductType>()?;
220 m.add_class::<HyperliquidTpSl>()?;
221 m.add_class::<HyperliquidConditionalOrderType>()?;
222 m.add_class::<HyperliquidTrailingOffsetType>()?;
223 m.add_class::<HyperliquidEnvironment>()?;
224 m.add_function(wrap_pyfunction!(urls::py_get_hyperliquid_http_base_url, m)?)?;
225 m.add_function(wrap_pyfunction!(urls::py_get_hyperliquid_ws_url, m)?)?;
226 m.add_function(wrap_pyfunction!(
227 py_hyperliquid_product_type_from_symbol,
228 m
229 )?)?;
230 m.add_function(wrap_pyfunction!(
231 py_hyperliquid_cloid_from_client_order_id,
232 m
233 )?)?;
234 m.add_function(wrap_pyfunction!(
235 py_hyperliquid_resolve_execution_account_address,
236 m
237 )?)?;
238 m.add_function(wrap_pyfunction!(py_builder_fee_approve, m)?)?;
239 m.add_function(wrap_pyfunction!(py_builder_fee_revoke, m)?)?;
240 m.add_class::<HyperliquidDataClientConfig>()?;
241 m.add_class::<HyperliquidExecClientConfig>()?;
242 m.add_class::<HyperliquidExecFactoryConfig>()?;
243 m.add_class::<HyperliquidDataClientFactory>()?;
244 m.add_class::<HyperliquidExecutionClientFactory>()?;
245 m.add_class::<HyperliquidAllDexsAssetCtxs>()?;
246 m.add_class::<HyperliquidAllMids>()?;
247 m.add_class::<HyperliquidOpenInterest>()?;
248
249 register_hyperliquid_custom_data();
250 let _result = ensure_rust_extractor_registered::<HyperliquidAllDexsAssetCtxs>();
251 let _result = ensure_rust_extractor_registered::<HyperliquidAllMids>();
252 let _result = ensure_rust_extractor_registered::<HyperliquidOpenInterest>();
253
254 let registry = get_global_pyo3_registry();
255
256 if let Err(e) = registry
257 .register_factory_extractor(HYPERLIQUID.to_string(), extract_hyperliquid_data_factory)
258 {
259 return Err(to_pyruntime_err(format!(
260 "Failed to register Hyperliquid data factory extractor: {e}"
261 )));
262 }
263
264 if let Err(e) = registry
265 .register_exec_factory_extractor(HYPERLIQUID.to_string(), extract_hyperliquid_exec_factory)
266 {
267 return Err(to_pyruntime_err(format!(
268 "Failed to register Hyperliquid exec factory extractor: {e}"
269 )));
270 }
271
272 if let Err(e) = registry.register_config_extractor(
273 "HyperliquidDataClientConfig".to_string(),
274 extract_hyperliquid_data_config,
275 ) {
276 return Err(to_pyruntime_err(format!(
277 "Failed to register Hyperliquid data config extractor: {e}"
278 )));
279 }
280
281 if let Err(e) = registry.register_config_extractor(
282 "HyperliquidExecFactoryConfig".to_string(),
283 extract_hyperliquid_exec_config,
284 ) {
285 return Err(to_pyruntime_err(format!(
286 "Failed to register Hyperliquid exec config extractor: {e}"
287 )));
288 }
289
290 Ok(())
291}