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