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(
44 order_type: OrderType,
45 time_in_force: Option<TimeInForce>,
46 trigger_price: Option<Decimal>,
47) -> HyperliquidOrderType {
48 match order_type {
49 OrderType::Limit => {
51 let tif = time_in_force
52 .map(nautilus_time_in_force_to_hyperliquid)
53 .unwrap_or(HyperliquidTimeInForce::Gtc);
54 HyperliquidOrderType::Limit { tif }
55 }
56
57 OrderType::StopMarket => {
59 let trigger_px = trigger_price
60 .expect("Trigger price required for StopMarket order")
61 .to_string();
62 HyperliquidOrderType::Trigger {
63 is_market: true,
64 trigger_px,
65 tpsl: HyperliquidTpSl::Sl,
66 }
67 }
68
69 OrderType::StopLimit => {
71 let trigger_px = trigger_price
72 .expect("Trigger price required for StopLimit order")
73 .to_string();
74 HyperliquidOrderType::Trigger {
75 is_market: false,
76 trigger_px,
77 tpsl: HyperliquidTpSl::Sl,
78 }
79 }
80
81 OrderType::MarketIfTouched => {
83 let trigger_px = trigger_price
84 .expect("Trigger price required for MarketIfTouched order")
85 .to_string();
86 HyperliquidOrderType::Trigger {
87 is_market: true,
88 trigger_px,
89 tpsl: HyperliquidTpSl::Tp,
90 }
91 }
92
93 OrderType::LimitIfTouched => {
95 let trigger_px = trigger_price
96 .expect("Trigger price required for LimitIfTouched order")
97 .to_string();
98 HyperliquidOrderType::Trigger {
99 is_market: false,
100 trigger_px,
101 tpsl: HyperliquidTpSl::Tp,
102 }
103 }
104
105 OrderType::TrailingStopMarket => {
107 let trigger_px = trigger_price
108 .expect("Trigger price required for TrailingStopMarket order")
109 .to_string();
110 HyperliquidOrderType::Trigger {
111 is_market: true,
112 trigger_px,
113 tpsl: HyperliquidTpSl::Sl,
114 }
115 }
116
117 OrderType::TrailingStopLimit => {
119 let trigger_px = trigger_price
120 .expect("Trigger price required for TrailingStopLimit order")
121 .to_string();
122 HyperliquidOrderType::Trigger {
123 is_market: false,
124 trigger_px,
125 tpsl: HyperliquidTpSl::Sl,
126 }
127 }
128
129 OrderType::Market => {
131 panic!("Market orders should be handled separately via immediate execution")
132 }
133
134 _ => panic!("Unsupported order type: {order_type:?}"),
136 }
137}
138
139pub fn hyperliquid_order_type_to_nautilus(hl_order_type: &HyperliquidOrderType) -> OrderType {
149 match hl_order_type {
150 HyperliquidOrderType::Limit { .. } => OrderType::Limit,
151 HyperliquidOrderType::Trigger {
152 is_market, tpsl, ..
153 } => match (is_market, tpsl) {
154 (true, HyperliquidTpSl::Sl) => OrderType::StopMarket,
155 (false, HyperliquidTpSl::Sl) => OrderType::StopLimit,
156 (true, HyperliquidTpSl::Tp) => OrderType::MarketIfTouched,
157 (false, HyperliquidTpSl::Tp) => OrderType::LimitIfTouched,
158 },
159 }
160}
161
162pub fn hyperliquid_conditional_to_nautilus(
172 conditional_type: HyperliquidConditionalOrderType,
173) -> OrderType {
174 OrderType::from(conditional_type)
175}
176
177pub fn nautilus_to_hyperliquid_conditional(
191 order_type: OrderType,
192) -> HyperliquidConditionalOrderType {
193 HyperliquidConditionalOrderType::from(order_type)
194}
195
196pub fn nautilus_time_in_force_to_hyperliquid(tif: TimeInForce) -> HyperliquidTimeInForce {
206 match tif {
207 TimeInForce::Gtc => HyperliquidTimeInForce::Gtc,
208 TimeInForce::Ioc => HyperliquidTimeInForce::Ioc,
209 TimeInForce::Fok => HyperliquidTimeInForce::Ioc, TimeInForce::Gtd => HyperliquidTimeInForce::Gtc, TimeInForce::Day => HyperliquidTimeInForce::Gtc, TimeInForce::AtTheOpen => HyperliquidTimeInForce::Gtc, TimeInForce::AtTheClose => HyperliquidTimeInForce::Gtc, }
215}
216
217pub fn hyperliquid_time_in_force_to_nautilus(hl_tif: HyperliquidTimeInForce) -> TimeInForce {
227 match hl_tif {
228 HyperliquidTimeInForce::Gtc => TimeInForce::Gtc,
229 HyperliquidTimeInForce::Ioc => TimeInForce::Ioc,
230 HyperliquidTimeInForce::Alo => TimeInForce::Gtc, }
232}
233
234pub fn determine_tpsl_type(order_type: OrderType, is_buy: bool) -> HyperliquidTpSl {
255 match order_type {
256 OrderType::StopMarket
257 | OrderType::StopLimit
258 | OrderType::TrailingStopMarket
259 | OrderType::TrailingStopLimit => HyperliquidTpSl::Sl,
260 OrderType::MarketIfTouched | OrderType::LimitIfTouched => HyperliquidTpSl::Tp,
261 _ => {
262 if is_buy {
264 HyperliquidTpSl::Sl
265 } else {
266 HyperliquidTpSl::Tp
267 }
268 }
269 }
270}
271
272#[cfg(test)]
277mod tests {
278 use rstest::rstest;
279
280 use super::*;
281
282 #[rstest]
283 fn test_nautilus_to_hyperliquid_limit_order() {
284 let result =
285 nautilus_order_type_to_hyperliquid(OrderType::Limit, Some(TimeInForce::Gtc), None);
286
287 match result {
288 HyperliquidOrderType::Limit { tif } => {
289 assert_eq!(tif, HyperliquidTimeInForce::Gtc);
290 }
291 _ => panic!("Expected Limit order type"),
292 }
293 }
294
295 #[rstest]
296 fn test_nautilus_to_hyperliquid_stop_market() {
297 let result = nautilus_order_type_to_hyperliquid(
298 OrderType::StopMarket,
299 None,
300 Some(Decimal::new(49000, 0)),
301 );
302
303 match result {
304 HyperliquidOrderType::Trigger {
305 is_market,
306 trigger_px,
307 tpsl,
308 } => {
309 assert!(is_market);
310 assert_eq!(trigger_px, "49000");
311 assert_eq!(tpsl, HyperliquidTpSl::Sl);
312 }
313 _ => panic!("Expected Trigger order type"),
314 }
315 }
316
317 #[rstest]
318 fn test_nautilus_to_hyperliquid_stop_limit() {
319 let result = nautilus_order_type_to_hyperliquid(
320 OrderType::StopLimit,
321 None,
322 Some(Decimal::new(49000, 0)),
323 );
324
325 match result {
326 HyperliquidOrderType::Trigger {
327 is_market,
328 trigger_px,
329 tpsl,
330 } => {
331 assert!(!is_market);
332 assert_eq!(trigger_px, "49000");
333 assert_eq!(tpsl, HyperliquidTpSl::Sl);
334 }
335 _ => panic!("Expected Trigger order type"),
336 }
337 }
338
339 #[rstest]
340 fn test_nautilus_to_hyperliquid_take_profit_market() {
341 let result = nautilus_order_type_to_hyperliquid(
342 OrderType::MarketIfTouched,
343 None,
344 Some(Decimal::new(51000, 0)),
345 );
346
347 match result {
348 HyperliquidOrderType::Trigger {
349 is_market,
350 trigger_px,
351 tpsl,
352 } => {
353 assert!(is_market);
354 assert_eq!(trigger_px, "51000");
355 assert_eq!(tpsl, HyperliquidTpSl::Tp);
356 }
357 _ => panic!("Expected Trigger order type"),
358 }
359 }
360
361 #[rstest]
362 fn test_nautilus_to_hyperliquid_take_profit_limit() {
363 let result = nautilus_order_type_to_hyperliquid(
364 OrderType::LimitIfTouched,
365 None,
366 Some(Decimal::new(51000, 0)),
367 );
368
369 match result {
370 HyperliquidOrderType::Trigger {
371 is_market,
372 trigger_px,
373 tpsl,
374 } => {
375 assert!(!is_market);
376 assert_eq!(trigger_px, "51000");
377 assert_eq!(tpsl, HyperliquidTpSl::Tp);
378 }
379 _ => panic!("Expected Trigger order type"),
380 }
381 }
382
383 #[rstest]
384 fn test_hyperliquid_to_nautilus_limit() {
385 let hl_order = HyperliquidOrderType::Limit {
386 tif: HyperliquidTimeInForce::Gtc,
387 };
388 assert_eq!(
389 hyperliquid_order_type_to_nautilus(&hl_order),
390 OrderType::Limit
391 );
392 }
393
394 #[rstest]
395 fn test_hyperliquid_to_nautilus_stop_market() {
396 let hl_order = HyperliquidOrderType::Trigger {
397 is_market: true,
398 trigger_px: "49000".to_string(),
399 tpsl: HyperliquidTpSl::Sl,
400 };
401 assert_eq!(
402 hyperliquid_order_type_to_nautilus(&hl_order),
403 OrderType::StopMarket
404 );
405 }
406
407 #[rstest]
408 fn test_hyperliquid_to_nautilus_stop_limit() {
409 let hl_order = HyperliquidOrderType::Trigger {
410 is_market: false,
411 trigger_px: "49000".to_string(),
412 tpsl: HyperliquidTpSl::Sl,
413 };
414 assert_eq!(
415 hyperliquid_order_type_to_nautilus(&hl_order),
416 OrderType::StopLimit
417 );
418 }
419
420 #[rstest]
421 fn test_hyperliquid_to_nautilus_take_profit_market() {
422 let hl_order = HyperliquidOrderType::Trigger {
423 is_market: true,
424 trigger_px: "51000".to_string(),
425 tpsl: HyperliquidTpSl::Tp,
426 };
427 assert_eq!(
428 hyperliquid_order_type_to_nautilus(&hl_order),
429 OrderType::MarketIfTouched
430 );
431 }
432
433 #[rstest]
434 fn test_hyperliquid_to_nautilus_take_profit_limit() {
435 let hl_order = HyperliquidOrderType::Trigger {
436 is_market: false,
437 trigger_px: "51000".to_string(),
438 tpsl: HyperliquidTpSl::Tp,
439 };
440 assert_eq!(
441 hyperliquid_order_type_to_nautilus(&hl_order),
442 OrderType::LimitIfTouched
443 );
444 }
445
446 #[rstest]
447 fn test_time_in_force_conversions() {
448 assert_eq!(
450 nautilus_time_in_force_to_hyperliquid(TimeInForce::Gtc),
451 HyperliquidTimeInForce::Gtc
452 );
453 assert_eq!(
454 nautilus_time_in_force_to_hyperliquid(TimeInForce::Ioc),
455 HyperliquidTimeInForce::Ioc
456 );
457 assert_eq!(
458 nautilus_time_in_force_to_hyperliquid(TimeInForce::Fok),
459 HyperliquidTimeInForce::Ioc
460 );
461
462 assert_eq!(
464 hyperliquid_time_in_force_to_nautilus(HyperliquidTimeInForce::Gtc),
465 TimeInForce::Gtc
466 );
467 assert_eq!(
468 hyperliquid_time_in_force_to_nautilus(HyperliquidTimeInForce::Ioc),
469 TimeInForce::Ioc
470 );
471 assert_eq!(
472 hyperliquid_time_in_force_to_nautilus(HyperliquidTimeInForce::Alo),
473 TimeInForce::Gtc
474 );
475 }
476
477 #[rstest]
478 fn test_conditional_order_type_conversions() {
479 assert_eq!(
481 hyperliquid_conditional_to_nautilus(HyperliquidConditionalOrderType::StopMarket),
482 OrderType::StopMarket
483 );
484 assert_eq!(
485 hyperliquid_conditional_to_nautilus(HyperliquidConditionalOrderType::StopLimit),
486 OrderType::StopLimit
487 );
488 assert_eq!(
489 hyperliquid_conditional_to_nautilus(HyperliquidConditionalOrderType::TakeProfitMarket),
490 OrderType::MarketIfTouched
491 );
492 assert_eq!(
493 hyperliquid_conditional_to_nautilus(HyperliquidConditionalOrderType::TakeProfitLimit),
494 OrderType::LimitIfTouched
495 );
496
497 assert_eq!(
499 nautilus_to_hyperliquid_conditional(OrderType::StopMarket),
500 HyperliquidConditionalOrderType::StopMarket
501 );
502 assert_eq!(
503 nautilus_to_hyperliquid_conditional(OrderType::StopLimit),
504 HyperliquidConditionalOrderType::StopLimit
505 );
506 assert_eq!(
507 nautilus_to_hyperliquid_conditional(OrderType::MarketIfTouched),
508 HyperliquidConditionalOrderType::TakeProfitMarket
509 );
510 assert_eq!(
511 nautilus_to_hyperliquid_conditional(OrderType::LimitIfTouched),
512 HyperliquidConditionalOrderType::TakeProfitLimit
513 );
514 }
515
516 #[rstest]
517 fn test_determine_tpsl_type() {
518 assert_eq!(
520 determine_tpsl_type(OrderType::StopMarket, true),
521 HyperliquidTpSl::Sl
522 );
523 assert_eq!(
524 determine_tpsl_type(OrderType::StopLimit, false),
525 HyperliquidTpSl::Sl
526 );
527
528 assert_eq!(
530 determine_tpsl_type(OrderType::MarketIfTouched, true),
531 HyperliquidTpSl::Tp
532 );
533 assert_eq!(
534 determine_tpsl_type(OrderType::LimitIfTouched, false),
535 HyperliquidTpSl::Tp
536 );
537
538 assert_eq!(
540 determine_tpsl_type(OrderType::TrailingStopMarket, true),
541 HyperliquidTpSl::Sl
542 );
543 assert_eq!(
544 determine_tpsl_type(OrderType::TrailingStopLimit, false),
545 HyperliquidTpSl::Sl
546 );
547 }
548}