nautilus_hyperliquid/common/
converters.rs1use nautilus_model::enums::{OrderType, TimeInForce};
22use rust_decimal::Decimal;
23
24use super::enums::{
25 HyperliquidConditionalOrderType, HyperliquidOrderType, HyperliquidTimeInForce, HyperliquidTpSl,
26};
27
28pub fn nautilus_order_type_to_hyperliquid(
34 order_type: OrderType,
35 time_in_force: Option<TimeInForce>,
36 trigger_price: Option<Decimal>,
37) -> HyperliquidOrderType {
38 match order_type {
39 OrderType::Limit => {
41 let tif = time_in_force.map_or(
42 HyperliquidTimeInForce::Gtc,
43 nautilus_time_in_force_to_hyperliquid,
44 );
45 HyperliquidOrderType::Limit { tif }
46 }
47
48 OrderType::StopMarket => {
50 let trigger_px = trigger_price
51 .expect("Trigger price required for StopMarket order")
52 .to_string();
53 HyperliquidOrderType::Trigger {
54 is_market: true,
55 trigger_px,
56 tpsl: HyperliquidTpSl::Sl,
57 }
58 }
59
60 OrderType::StopLimit => {
62 let trigger_px = trigger_price
63 .expect("Trigger price required for StopLimit order")
64 .to_string();
65 HyperliquidOrderType::Trigger {
66 is_market: false,
67 trigger_px,
68 tpsl: HyperliquidTpSl::Sl,
69 }
70 }
71
72 OrderType::MarketIfTouched => {
74 let trigger_px = trigger_price
75 .expect("Trigger price required for MarketIfTouched order")
76 .to_string();
77 HyperliquidOrderType::Trigger {
78 is_market: true,
79 trigger_px,
80 tpsl: HyperliquidTpSl::Tp,
81 }
82 }
83
84 OrderType::LimitIfTouched => {
86 let trigger_px = trigger_price
87 .expect("Trigger price required for LimitIfTouched order")
88 .to_string();
89 HyperliquidOrderType::Trigger {
90 is_market: false,
91 trigger_px,
92 tpsl: HyperliquidTpSl::Tp,
93 }
94 }
95
96 OrderType::TrailingStopMarket => {
98 let trigger_px = trigger_price
99 .expect("Trigger price required for TrailingStopMarket order")
100 .to_string();
101 HyperliquidOrderType::Trigger {
102 is_market: true,
103 trigger_px,
104 tpsl: HyperliquidTpSl::Sl,
105 }
106 }
107
108 OrderType::TrailingStopLimit => {
110 let trigger_px = trigger_price
111 .expect("Trigger price required for TrailingStopLimit order")
112 .to_string();
113 HyperliquidOrderType::Trigger {
114 is_market: false,
115 trigger_px,
116 tpsl: HyperliquidTpSl::Sl,
117 }
118 }
119
120 OrderType::Market => {
122 panic!("Market orders should be handled separately via immediate execution")
123 }
124
125 _ => panic!("Unsupported order type: {order_type:?}"),
127 }
128}
129
130pub fn hyperliquid_order_type_to_nautilus(hl_order_type: &HyperliquidOrderType) -> OrderType {
132 match hl_order_type {
133 HyperliquidOrderType::Limit { .. } => OrderType::Limit,
134 HyperliquidOrderType::Trigger {
135 is_market, tpsl, ..
136 } => match (is_market, tpsl) {
137 (true, HyperliquidTpSl::Sl) => OrderType::StopMarket,
138 (false, HyperliquidTpSl::Sl) => OrderType::StopLimit,
139 (true, HyperliquidTpSl::Tp) => OrderType::MarketIfTouched,
140 (false, HyperliquidTpSl::Tp) => OrderType::LimitIfTouched,
141 },
142 }
143}
144
145pub fn hyperliquid_conditional_to_nautilus(
147 conditional_type: HyperliquidConditionalOrderType,
148) -> OrderType {
149 OrderType::from(conditional_type)
150}
151
152pub fn nautilus_to_hyperliquid_conditional(
158 order_type: OrderType,
159) -> HyperliquidConditionalOrderType {
160 HyperliquidConditionalOrderType::from(order_type)
161}
162
163pub fn nautilus_time_in_force_to_hyperliquid(tif: TimeInForce) -> HyperliquidTimeInForce {
165 match tif {
166 TimeInForce::Gtc => HyperliquidTimeInForce::Gtc,
167 TimeInForce::Ioc => HyperliquidTimeInForce::Ioc,
168 TimeInForce::Fok => HyperliquidTimeInForce::Ioc, TimeInForce::Gtd => HyperliquidTimeInForce::Gtc, TimeInForce::Day => HyperliquidTimeInForce::Gtc, TimeInForce::AtTheOpen => HyperliquidTimeInForce::Gtc, TimeInForce::AtTheClose => HyperliquidTimeInForce::Gtc, }
174}
175
176pub fn hyperliquid_time_in_force_to_nautilus(hl_tif: HyperliquidTimeInForce) -> TimeInForce {
178 match hl_tif {
179 HyperliquidTimeInForce::Gtc => TimeInForce::Gtc,
180 HyperliquidTimeInForce::Ioc => TimeInForce::Ioc,
181 HyperliquidTimeInForce::Alo => TimeInForce::Gtc, }
183}
184
185pub fn determine_tpsl_type(order_type: OrderType, is_buy: bool) -> HyperliquidTpSl {
197 match order_type {
198 OrderType::StopMarket
199 | OrderType::StopLimit
200 | OrderType::TrailingStopMarket
201 | OrderType::TrailingStopLimit => HyperliquidTpSl::Sl,
202 OrderType::MarketIfTouched | OrderType::LimitIfTouched => HyperliquidTpSl::Tp,
203 _ => {
204 if is_buy {
206 HyperliquidTpSl::Sl
207 } else {
208 HyperliquidTpSl::Tp
209 }
210 }
211 }
212}
213
214#[cfg(test)]
219mod tests {
220 use rstest::rstest;
221
222 use super::*;
223
224 #[rstest]
225 fn test_nautilus_to_hyperliquid_limit_order() {
226 let result =
227 nautilus_order_type_to_hyperliquid(OrderType::Limit, Some(TimeInForce::Gtc), None);
228
229 match result {
230 HyperliquidOrderType::Limit { tif } => {
231 assert_eq!(tif, HyperliquidTimeInForce::Gtc);
232 }
233 _ => panic!("Expected Limit order type"),
234 }
235 }
236
237 #[rstest]
238 fn test_nautilus_to_hyperliquid_stop_market() {
239 let result = nautilus_order_type_to_hyperliquid(
240 OrderType::StopMarket,
241 None,
242 Some(Decimal::new(49000, 0)),
243 );
244
245 match result {
246 HyperliquidOrderType::Trigger {
247 is_market,
248 trigger_px,
249 tpsl,
250 } => {
251 assert!(is_market);
252 assert_eq!(trigger_px, "49000");
253 assert_eq!(tpsl, HyperliquidTpSl::Sl);
254 }
255 _ => panic!("Expected Trigger order type"),
256 }
257 }
258
259 #[rstest]
260 fn test_nautilus_to_hyperliquid_stop_limit() {
261 let result = nautilus_order_type_to_hyperliquid(
262 OrderType::StopLimit,
263 None,
264 Some(Decimal::new(49000, 0)),
265 );
266
267 match result {
268 HyperliquidOrderType::Trigger {
269 is_market,
270 trigger_px,
271 tpsl,
272 } => {
273 assert!(!is_market);
274 assert_eq!(trigger_px, "49000");
275 assert_eq!(tpsl, HyperliquidTpSl::Sl);
276 }
277 _ => panic!("Expected Trigger order type"),
278 }
279 }
280
281 #[rstest]
282 fn test_nautilus_to_hyperliquid_take_profit_market() {
283 let result = nautilus_order_type_to_hyperliquid(
284 OrderType::MarketIfTouched,
285 None,
286 Some(Decimal::new(51000, 0)),
287 );
288
289 match result {
290 HyperliquidOrderType::Trigger {
291 is_market,
292 trigger_px,
293 tpsl,
294 } => {
295 assert!(is_market);
296 assert_eq!(trigger_px, "51000");
297 assert_eq!(tpsl, HyperliquidTpSl::Tp);
298 }
299 _ => panic!("Expected Trigger order type"),
300 }
301 }
302
303 #[rstest]
304 fn test_nautilus_to_hyperliquid_take_profit_limit() {
305 let result = nautilus_order_type_to_hyperliquid(
306 OrderType::LimitIfTouched,
307 None,
308 Some(Decimal::new(51000, 0)),
309 );
310
311 match result {
312 HyperliquidOrderType::Trigger {
313 is_market,
314 trigger_px,
315 tpsl,
316 } => {
317 assert!(!is_market);
318 assert_eq!(trigger_px, "51000");
319 assert_eq!(tpsl, HyperliquidTpSl::Tp);
320 }
321 _ => panic!("Expected Trigger order type"),
322 }
323 }
324
325 #[rstest]
326 fn test_hyperliquid_to_nautilus_limit() {
327 let hl_order = HyperliquidOrderType::Limit {
328 tif: HyperliquidTimeInForce::Gtc,
329 };
330 assert_eq!(
331 hyperliquid_order_type_to_nautilus(&hl_order),
332 OrderType::Limit
333 );
334 }
335
336 #[rstest]
337 fn test_hyperliquid_to_nautilus_stop_market() {
338 let hl_order = HyperliquidOrderType::Trigger {
339 is_market: true,
340 trigger_px: "49000".to_string(),
341 tpsl: HyperliquidTpSl::Sl,
342 };
343 assert_eq!(
344 hyperliquid_order_type_to_nautilus(&hl_order),
345 OrderType::StopMarket
346 );
347 }
348
349 #[rstest]
350 fn test_hyperliquid_to_nautilus_stop_limit() {
351 let hl_order = HyperliquidOrderType::Trigger {
352 is_market: false,
353 trigger_px: "49000".to_string(),
354 tpsl: HyperliquidTpSl::Sl,
355 };
356 assert_eq!(
357 hyperliquid_order_type_to_nautilus(&hl_order),
358 OrderType::StopLimit
359 );
360 }
361
362 #[rstest]
363 fn test_hyperliquid_to_nautilus_take_profit_market() {
364 let hl_order = HyperliquidOrderType::Trigger {
365 is_market: true,
366 trigger_px: "51000".to_string(),
367 tpsl: HyperliquidTpSl::Tp,
368 };
369 assert_eq!(
370 hyperliquid_order_type_to_nautilus(&hl_order),
371 OrderType::MarketIfTouched
372 );
373 }
374
375 #[rstest]
376 fn test_hyperliquid_to_nautilus_take_profit_limit() {
377 let hl_order = HyperliquidOrderType::Trigger {
378 is_market: false,
379 trigger_px: "51000".to_string(),
380 tpsl: HyperliquidTpSl::Tp,
381 };
382 assert_eq!(
383 hyperliquid_order_type_to_nautilus(&hl_order),
384 OrderType::LimitIfTouched
385 );
386 }
387
388 #[rstest]
389 fn test_time_in_force_conversions() {
390 assert_eq!(
392 nautilus_time_in_force_to_hyperliquid(TimeInForce::Gtc),
393 HyperliquidTimeInForce::Gtc
394 );
395 assert_eq!(
396 nautilus_time_in_force_to_hyperliquid(TimeInForce::Ioc),
397 HyperliquidTimeInForce::Ioc
398 );
399 assert_eq!(
400 nautilus_time_in_force_to_hyperliquid(TimeInForce::Fok),
401 HyperliquidTimeInForce::Ioc
402 );
403
404 assert_eq!(
406 hyperliquid_time_in_force_to_nautilus(HyperliquidTimeInForce::Gtc),
407 TimeInForce::Gtc
408 );
409 assert_eq!(
410 hyperliquid_time_in_force_to_nautilus(HyperliquidTimeInForce::Ioc),
411 TimeInForce::Ioc
412 );
413 assert_eq!(
414 hyperliquid_time_in_force_to_nautilus(HyperliquidTimeInForce::Alo),
415 TimeInForce::Gtc
416 );
417 }
418
419 #[rstest]
420 fn test_conditional_order_type_conversions() {
421 assert_eq!(
423 hyperliquid_conditional_to_nautilus(HyperliquidConditionalOrderType::StopMarket),
424 OrderType::StopMarket
425 );
426 assert_eq!(
427 hyperliquid_conditional_to_nautilus(HyperliquidConditionalOrderType::StopLimit),
428 OrderType::StopLimit
429 );
430 assert_eq!(
431 hyperliquid_conditional_to_nautilus(HyperliquidConditionalOrderType::TakeProfitMarket),
432 OrderType::MarketIfTouched
433 );
434 assert_eq!(
435 hyperliquid_conditional_to_nautilus(HyperliquidConditionalOrderType::TakeProfitLimit),
436 OrderType::LimitIfTouched
437 );
438
439 assert_eq!(
441 nautilus_to_hyperliquid_conditional(OrderType::StopMarket),
442 HyperliquidConditionalOrderType::StopMarket
443 );
444 assert_eq!(
445 nautilus_to_hyperliquid_conditional(OrderType::StopLimit),
446 HyperliquidConditionalOrderType::StopLimit
447 );
448 assert_eq!(
449 nautilus_to_hyperliquid_conditional(OrderType::MarketIfTouched),
450 HyperliquidConditionalOrderType::TakeProfitMarket
451 );
452 assert_eq!(
453 nautilus_to_hyperliquid_conditional(OrderType::LimitIfTouched),
454 HyperliquidConditionalOrderType::TakeProfitLimit
455 );
456 }
457
458 #[rstest]
459 fn test_determine_tpsl_type() {
460 assert_eq!(
462 determine_tpsl_type(OrderType::StopMarket, true),
463 HyperliquidTpSl::Sl
464 );
465 assert_eq!(
466 determine_tpsl_type(OrderType::StopLimit, false),
467 HyperliquidTpSl::Sl
468 );
469
470 assert_eq!(
472 determine_tpsl_type(OrderType::MarketIfTouched, true),
473 HyperliquidTpSl::Tp
474 );
475 assert_eq!(
476 determine_tpsl_type(OrderType::LimitIfTouched, false),
477 HyperliquidTpSl::Tp
478 );
479
480 assert_eq!(
482 determine_tpsl_type(OrderType::TrailingStopMarket, true),
483 HyperliquidTpSl::Sl
484 );
485 assert_eq!(
486 determine_tpsl_type(OrderType::TrailingStopLimit, false),
487 HyperliquidTpSl::Sl
488 );
489 }
490}