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