nautilus_hyperliquid/
factories.rs1use std::{any::Any, cell::RefCell, rc::Rc};
19
20use nautilus_common::{
21 cache::CacheView,
22 clients::{DataClient, ExecutionClient},
23 clock::Clock,
24 factories::{ClientConfig, DataClientFactory, ExecutionClientFactory},
25};
26use nautilus_live::ExecutionClientCore;
27use nautilus_model::{
28 enums::{AccountType, OmsType},
29 identifiers::{ClientId, TraderId},
30};
31
32use crate::{
33 common::consts::{HYPERLIQUID, HYPERLIQUID_VENUE},
34 config::{HyperliquidDataClientConfig, HyperliquidExecutionClientConfig},
35 data::HyperliquidDataClient,
36 execution::HyperliquidExecutionClient,
37};
38
39impl ClientConfig for HyperliquidDataClientConfig {
40 fn as_any(&self) -> &dyn Any {
41 self
42 }
43}
44
45impl ClientConfig for HyperliquidExecutionClientConfig {
46 fn as_any(&self) -> &dyn Any {
47 self
48 }
49}
50
51#[derive(Debug, Clone)]
53#[cfg_attr(
54 feature = "python",
55 pyo3::pyclass(module = "nautilus_trader.adapters.hyperliquid", from_py_object)
56)]
57#[cfg_attr(
58 feature = "python",
59 pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.adapters.hyperliquid")
60)]
61pub struct HyperliquidDataClientFactory;
62
63impl HyperliquidDataClientFactory {
64 #[must_use]
66 pub const fn new() -> Self {
67 Self
68 }
69}
70
71impl Default for HyperliquidDataClientFactory {
72 fn default() -> Self {
73 Self::new()
74 }
75}
76
77impl DataClientFactory for HyperliquidDataClientFactory {
78 fn create(
79 &self,
80 name: &str,
81 config: &dyn ClientConfig,
82 _cache: CacheView,
83 _clock: Rc<RefCell<dyn Clock>>,
84 ) -> anyhow::Result<Box<dyn DataClient>> {
85 let hyperliquid_config = config
86 .as_any()
87 .downcast_ref::<HyperliquidDataClientConfig>()
88 .ok_or_else(|| {
89 anyhow::anyhow!(
90 "Invalid config type for HyperliquidDataClientFactory. Expected HyperliquidDataClientConfig, was {config:?}",
91 )
92 })?
93 .clone();
94
95 let client_id = ClientId::from(name);
96 let client = HyperliquidDataClient::new(client_id, hyperliquid_config)?;
97 Ok(Box::new(client))
98 }
99
100 fn name(&self) -> &'static str {
101 HYPERLIQUID
102 }
103
104 fn config_type(&self) -> &'static str {
105 "HyperliquidDataClientConfig"
106 }
107}
108
109#[derive(Debug, Clone)]
111#[cfg_attr(
112 feature = "python",
113 pyo3::pyclass(module = "nautilus_trader.adapters.hyperliquid", from_py_object)
114)]
115#[cfg_attr(
116 feature = "python",
117 pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.adapters.hyperliquid")
118)]
119pub struct HyperliquidExecutionClientFactory;
120
121impl HyperliquidExecutionClientFactory {
122 #[must_use]
124 pub const fn new() -> Self {
125 Self
126 }
127}
128
129impl Default for HyperliquidExecutionClientFactory {
130 fn default() -> Self {
131 Self::new()
132 }
133}
134
135impl ExecutionClientFactory for HyperliquidExecutionClientFactory {
136 fn create(
137 &self,
138 trader_id: TraderId,
139 name: &str,
140 config: &dyn ClientConfig,
141 cache: CacheView,
142 _clock: Rc<RefCell<dyn Clock>>,
143 ) -> anyhow::Result<Box<dyn ExecutionClient>> {
144 let hyperliquid_config = config
145 .as_any()
146 .downcast_ref::<HyperliquidExecutionClientConfig>()
147 .ok_or_else(|| {
148 anyhow::anyhow!(
149 "Invalid config type for HyperliquidExecutionClientFactory. Expected HyperliquidExecutionClientConfig, was {config:?}",
150 )
151 })?
152 .clone();
153
154 let oms_type = OmsType::Netting;
156
157 let account_type = AccountType::Margin;
159
160 let core = ExecutionClientCore::new(
161 trader_id,
162 ClientId::from(name),
163 *HYPERLIQUID_VENUE,
164 oms_type,
165 hyperliquid_config.account_id,
166 account_type,
167 None,
168 cache,
169 );
170
171 let client = HyperliquidExecutionClient::new(core, hyperliquid_config)?;
172 Ok(Box::new(client))
173 }
174
175 fn name(&self) -> &'static str {
176 HYPERLIQUID
177 }
178
179 fn config_type(&self) -> &'static str {
180 "HyperliquidExecutionClientConfig"
181 }
182}
183
184#[cfg(test)]
185mod tests {
186 use std::{cell::RefCell, rc::Rc};
187
188 use nautilus_common::{
189 cache::Cache,
190 clock::TestClock,
191 factories::{ClientConfig, DataClientFactory, ExecutionClientFactory},
192 };
193 use nautilus_model::identifiers::TraderId;
194 use rstest::rstest;
195
196 use super::*;
197 use crate::config::{HyperliquidDataClientConfig, HyperliquidExecutionClientConfig};
198
199 #[rstest]
200 fn test_hyperliquid_data_client_factory_creation() {
201 let factory = HyperliquidDataClientFactory::new();
202 assert_eq!(factory.name(), HYPERLIQUID);
203 assert_eq!(factory.config_type(), "HyperliquidDataClientConfig");
204 }
205
206 #[rstest]
207 fn test_hyperliquid_data_client_factory_default() {
208 let factory = HyperliquidDataClientFactory;
209 assert_eq!(factory.name(), HYPERLIQUID);
210 }
211
212 #[rstest]
213 fn test_hyperliquid_execution_client_factory_creation() {
214 let factory = HyperliquidExecutionClientFactory::new();
215 assert_eq!(factory.name(), HYPERLIQUID);
216 assert_eq!(factory.config_type(), "HyperliquidExecutionClientConfig");
217 }
218
219 #[rstest]
220 fn test_hyperliquid_execution_client_factory_default() {
221 let factory = HyperliquidExecutionClientFactory;
222 assert_eq!(factory.name(), HYPERLIQUID);
223 }
224
225 #[rstest]
226 fn test_hyperliquid_data_client_config_implements_client_config() {
227 let config = HyperliquidDataClientConfig::default();
228 let boxed_config: Box<dyn ClientConfig> = Box::new(config);
229 let downcasted = boxed_config
230 .as_any()
231 .downcast_ref::<HyperliquidDataClientConfig>();
232
233 assert!(downcasted.is_some());
234 }
235
236 #[rstest]
237 fn test_hyperliquid_exec_client_config_implements_client_config() {
238 let config = HyperliquidExecutionClientConfig::builder()
239 .private_key("test_private_key".into())
240 .build();
241
242 let boxed_config: Box<dyn ClientConfig> = Box::new(config);
243 let downcasted = boxed_config
244 .as_any()
245 .downcast_ref::<HyperliquidExecutionClientConfig>();
246
247 assert!(downcasted.is_some());
248 }
249
250 #[rstest]
251 fn test_hyperliquid_data_client_factory_rejects_wrong_config_type() {
252 let factory = HyperliquidDataClientFactory::new();
253 let wrong_config = HyperliquidExecutionClientConfig::builder()
254 .private_key("test_private_key".into())
255 .build();
256
257 let cache = Rc::new(RefCell::new(Cache::default()));
258 let clock = Rc::new(RefCell::new(TestClock::new()));
259
260 let result = factory.create("HYPERLIQUID-TEST", &wrong_config, cache.into(), clock);
261 assert!(result.is_err());
262 assert!(
263 result
264 .err()
265 .unwrap()
266 .to_string()
267 .contains("Invalid config type")
268 );
269 }
270
271 #[rstest]
272 fn test_hyperliquid_execution_client_factory_rejects_wrong_config_type() {
273 let factory = HyperliquidExecutionClientFactory::new();
274 let wrong_config = HyperliquidDataClientConfig::default();
275
276 let cache = Rc::new(RefCell::new(Cache::default()));
277
278 let result = factory.create(
279 TraderId::from("TRADER-001"),
280 "HYPERLIQUID-TEST",
281 &wrong_config,
282 cache.into(),
283 Rc::new(RefCell::new(TestClock::new())),
284 );
285 assert!(result.is_err());
286 assert!(
287 result
288 .err()
289 .unwrap()
290 .to_string()
291 .contains("Invalid config type")
292 );
293 }
294}