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