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