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 common::{
38 consts::{HYPERLIQUID, HYPERLIQUID_POST_ONLY_WOULD_MATCH},
39 enums::{
40 HyperliquidConditionalOrderType, HyperliquidEnvironment, HyperliquidProductType,
41 HyperliquidTpSl, HyperliquidTrailingOffsetType,
42 },
43 },
44 config::{HyperliquidDataClientConfig, HyperliquidExecClientConfig},
45 data_types::{HyperliquidAllMids, register_hyperliquid_custom_data},
46 factories::{
47 HyperliquidDataClientFactory, HyperliquidExecFactoryConfig,
48 HyperliquidExecutionClientFactory,
49 },
50 http::{HyperliquidHttpClient, models::Cloid},
51 websocket::HyperliquidWebSocketClient,
52};
53
54#[pyfunction]
59#[pyo3_stub_gen::derive::gen_stub_pyfunction(module = "nautilus_trader.hyperliquid")]
60#[pyo3(name = "hyperliquid_cloid_from_client_order_id")]
61fn py_hyperliquid_cloid_from_client_order_id(client_order_id: ClientOrderId) -> String {
62 Cloid::from_client_order_id(client_order_id).to_hex()
63}
64
65#[pyfunction]
71#[pyo3_stub_gen::derive::gen_stub_pyfunction(module = "nautilus_trader.hyperliquid")]
72#[pyo3(name = "hyperliquid_product_type_from_symbol")]
73fn py_hyperliquid_product_type_from_symbol(symbol: &str) -> PyResult<HyperliquidProductType> {
74 HyperliquidProductType::from_symbol(symbol).map_err(to_pyvalue_err)
75}
76
77#[expect(clippy::needless_pass_by_value)]
78fn extract_hyperliquid_data_factory(
79 py: Python<'_>,
80 factory: Py<PyAny>,
81) -> PyResult<Box<dyn DataClientFactory>> {
82 match factory.extract::<HyperliquidDataClientFactory>(py) {
83 Ok(f) => Ok(Box::new(f)),
84 Err(e) => Err(to_pyvalue_err(format!(
85 "Failed to extract HyperliquidDataClientFactory: {e}"
86 ))),
87 }
88}
89
90#[expect(clippy::needless_pass_by_value)]
91fn extract_hyperliquid_exec_factory(
92 py: Python<'_>,
93 factory: Py<PyAny>,
94) -> PyResult<Box<dyn ExecutionClientFactory>> {
95 match factory.extract::<HyperliquidExecutionClientFactory>(py) {
96 Ok(f) => Ok(Box::new(f)),
97 Err(e) => Err(to_pyvalue_err(format!(
98 "Failed to extract HyperliquidExecutionClientFactory: {e}"
99 ))),
100 }
101}
102
103#[expect(clippy::needless_pass_by_value)]
104fn extract_hyperliquid_data_config(
105 py: Python<'_>,
106 config: Py<PyAny>,
107) -> PyResult<Box<dyn ClientConfig>> {
108 match config.extract::<HyperliquidDataClientConfig>(py) {
109 Ok(c) => Ok(Box::new(c)),
110 Err(e) => Err(to_pyvalue_err(format!(
111 "Failed to extract HyperliquidDataClientConfig: {e}"
112 ))),
113 }
114}
115
116#[expect(clippy::needless_pass_by_value)]
117fn extract_hyperliquid_exec_config(
118 py: Python<'_>,
119 config: Py<PyAny>,
120) -> PyResult<Box<dyn ClientConfig>> {
121 match config.extract::<HyperliquidExecFactoryConfig>(py) {
122 Ok(c) => Ok(Box::new(c)),
123 Err(e) => Err(to_pyvalue_err(format!(
124 "Failed to extract HyperliquidExecFactoryConfig: {e}"
125 ))),
126 }
127}
128
129#[pymodule]
131pub fn hyperliquid(m: &Bound<'_, PyModule>) -> PyResult<()> {
132 m.add(
133 "HYPERLIQUID_POST_ONLY_WOULD_MATCH",
134 HYPERLIQUID_POST_ONLY_WOULD_MATCH,
135 )?;
136 m.add_class::<HyperliquidHttpClient>()?;
137 m.add_class::<HyperliquidWebSocketClient>()?;
138 m.add_class::<HyperliquidProductType>()?;
139 m.add_class::<HyperliquidTpSl>()?;
140 m.add_class::<HyperliquidConditionalOrderType>()?;
141 m.add_class::<HyperliquidTrailingOffsetType>()?;
142 m.add_class::<HyperliquidEnvironment>()?;
143 m.add_function(wrap_pyfunction!(urls::py_get_hyperliquid_http_base_url, m)?)?;
144 m.add_function(wrap_pyfunction!(urls::py_get_hyperliquid_ws_url, m)?)?;
145 m.add_function(wrap_pyfunction!(
146 py_hyperliquid_product_type_from_symbol,
147 m
148 )?)?;
149 m.add_function(wrap_pyfunction!(
150 py_hyperliquid_cloid_from_client_order_id,
151 m
152 )?)?;
153 m.add_class::<HyperliquidDataClientConfig>()?;
154 m.add_class::<HyperliquidExecClientConfig>()?;
155 m.add_class::<HyperliquidExecFactoryConfig>()?;
156 m.add_class::<HyperliquidDataClientFactory>()?;
157 m.add_class::<HyperliquidExecutionClientFactory>()?;
158 m.add_class::<HyperliquidAllMids>()?;
159
160 register_hyperliquid_custom_data();
161 let _result = ensure_rust_extractor_registered::<HyperliquidAllMids>();
162
163 let registry = get_global_pyo3_registry();
164
165 if let Err(e) = registry
166 .register_factory_extractor(HYPERLIQUID.to_string(), extract_hyperliquid_data_factory)
167 {
168 return Err(to_pyruntime_err(format!(
169 "Failed to register Hyperliquid data factory extractor: {e}"
170 )));
171 }
172
173 if let Err(e) = registry
174 .register_exec_factory_extractor(HYPERLIQUID.to_string(), extract_hyperliquid_exec_factory)
175 {
176 return Err(to_pyruntime_err(format!(
177 "Failed to register Hyperliquid exec factory extractor: {e}"
178 )));
179 }
180
181 if let Err(e) = registry.register_config_extractor(
182 "HyperliquidDataClientConfig".to_string(),
183 extract_hyperliquid_data_config,
184 ) {
185 return Err(to_pyruntime_err(format!(
186 "Failed to register Hyperliquid data config extractor: {e}"
187 )));
188 }
189
190 if let Err(e) = registry.register_config_extractor(
191 "HyperliquidExecFactoryConfig".to_string(),
192 extract_hyperliquid_exec_config,
193 ) {
194 return Err(to_pyruntime_err(format!(
195 "Failed to register Hyperliquid exec config extractor: {e}"
196 )));
197 }
198
199 Ok(())
200}