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)]
215mod tests {
216 use rstest::rstest;
217
218 use super::*;
219
220 #[rstest]
221 fn test_nautilus_to_hyperliquid_limit_order() {
222 let result =
223 nautilus_order_type_to_hyperliquid(OrderType::Limit, Some(TimeInForce::Gtc), None);
224
225 match result {
226 HyperliquidOrderType::Limit { tif } => {
227 assert_eq!(tif, HyperliquidTimeInForce::Gtc);
228 }
229 _ => panic!("Expected Limit order type"),
230 }
231 }
232
233 #[rstest]
234 fn test_nautilus_to_hyperliquid_stop_market() {
235 let result = nautilus_order_type_to_hyperliquid(
236 OrderType::StopMarket,
237 None,
238 Some(Decimal::new(49000, 0)),
239 );
240
241 match result {
242 HyperliquidOrderType::Trigger {
243 is_market,
244 trigger_px,
245 tpsl,
246 } => {
247 assert!(is_market);
248 assert_eq!(trigger_px, "49000");
249 assert_eq!(tpsl, HyperliquidTpSl::Sl);
250 }
251 _ => panic!("Expected Trigger order type"),
252 }
253 }
254
255 #[rstest]
256 fn test_nautilus_to_hyperliquid_stop_limit() {
257 let result = nautilus_order_type_to_hyperliquid(
258 OrderType::StopLimit,
259 None,
260 Some(Decimal::new(49000, 0)),
261 );
262
263 match result {
264 HyperliquidOrderType::Trigger {
265 is_market,
266 trigger_px,
267 tpsl,
268 } => {
269 assert!(!is_market);
270 assert_eq!(trigger_px, "49000");
271 assert_eq!(tpsl, HyperliquidTpSl::Sl);
272 }
273 _ => panic!("Expected Trigger order type"),
274 }
275 }
276
277 #[rstest]
278 fn test_nautilus_to_hyperliquid_take_profit_market() {
279 let result = nautilus_order_type_to_hyperliquid(
280 OrderType::MarketIfTouched,
281 None,
282 Some(Decimal::new(51000, 0)),
283 );
284
285 match result {
286 HyperliquidOrderType::Trigger {
287 is_market,
288 trigger_px,
289 tpsl,
290 } => {
291 assert!(is_market);
292 assert_eq!(trigger_px, "51000");
293 assert_eq!(tpsl, HyperliquidTpSl::Tp);
294 }
295 _ => panic!("Expected Trigger order type"),
296 }
297 }
298
299 #[rstest]
300 fn test_nautilus_to_hyperliquid_take_profit_limit() {
301 let result = nautilus_order_type_to_hyperliquid(
302 OrderType::LimitIfTouched,
303 None,
304 Some(Decimal::new(51000, 0)),
305 );
306
307 match result {
308 HyperliquidOrderType::Trigger {
309 is_market,
310 trigger_px,
311 tpsl,
312 } => {
313 assert!(!is_market);
314 assert_eq!(trigger_px, "51000");
315 assert_eq!(tpsl, HyperliquidTpSl::Tp);
316 }
317 _ => panic!("Expected Trigger order type"),
318 }
319 }
320
321 #[rstest]
322 fn test_hyperliquid_to_nautilus_limit() {
323 let hl_order = HyperliquidOrderType::Limit {
324 tif: HyperliquidTimeInForce::Gtc,
325 };
326 assert_eq!(
327 hyperliquid_order_type_to_nautilus(&hl_order),
328 OrderType::Limit
329 );
330 }
331
332 #[rstest]
333 fn test_hyperliquid_to_nautilus_stop_market() {
334 let hl_order = HyperliquidOrderType::Trigger {
335 is_market: true,
336 trigger_px: "49000".to_string(),
337 tpsl: HyperliquidTpSl::Sl,
338 };
339 assert_eq!(
340 hyperliquid_order_type_to_nautilus(&hl_order),
341 OrderType::StopMarket
342 );
343 }
344
345 #[rstest]
346 fn test_hyperliquid_to_nautilus_stop_limit() {
347 let hl_order = HyperliquidOrderType::Trigger {
348 is_market: false,
349 trigger_px: "49000".to_string(),
350 tpsl: HyperliquidTpSl::Sl,
351 };
352 assert_eq!(
353 hyperliquid_order_type_to_nautilus(&hl_order),
354 OrderType::StopLimit
355 );
356 }
357
358 #[rstest]
359 fn test_hyperliquid_to_nautilus_take_profit_market() {
360 let hl_order = HyperliquidOrderType::Trigger {
361 is_market: true,
362 trigger_px: "51000".to_string(),
363 tpsl: HyperliquidTpSl::Tp,
364 };
365 assert_eq!(
366 hyperliquid_order_type_to_nautilus(&hl_order),
367 OrderType::MarketIfTouched
368 );
369 }
370
371 #[rstest]
372 fn test_hyperliquid_to_nautilus_take_profit_limit() {
373 let hl_order = HyperliquidOrderType::Trigger {
374 is_market: false,
375 trigger_px: "51000".to_string(),
376 tpsl: HyperliquidTpSl::Tp,
377 };
378 assert_eq!(
379 hyperliquid_order_type_to_nautilus(&hl_order),
380 OrderType::LimitIfTouched
381 );
382 }
383
384 #[rstest]
385 fn test_time_in_force_conversions() {
386 assert_eq!(
388 nautilus_time_in_force_to_hyperliquid(TimeInForce::Gtc),
389 HyperliquidTimeInForce::Gtc
390 );
391 assert_eq!(
392 nautilus_time_in_force_to_hyperliquid(TimeInForce::Ioc),
393 HyperliquidTimeInForce::Ioc
394 );
395 assert_eq!(
396 nautilus_time_in_force_to_hyperliquid(TimeInForce::Fok),
397 HyperliquidTimeInForce::Ioc
398 );
399
400 assert_eq!(
402 hyperliquid_time_in_force_to_nautilus(HyperliquidTimeInForce::Gtc),
403 TimeInForce::Gtc
404 );
405 assert_eq!(
406 hyperliquid_time_in_force_to_nautilus(HyperliquidTimeInForce::Ioc),
407 TimeInForce::Ioc
408 );
409 assert_eq!(
410 hyperliquid_time_in_force_to_nautilus(HyperliquidTimeInForce::Alo),
411 TimeInForce::Gtc
412 );
413 }
414
415 #[rstest]
416 fn test_conditional_order_type_conversions() {
417 assert_eq!(
419 hyperliquid_conditional_to_nautilus(HyperliquidConditionalOrderType::StopMarket),
420 OrderType::StopMarket
421 );
422 assert_eq!(
423 hyperliquid_conditional_to_nautilus(HyperliquidConditionalOrderType::StopLimit),
424 OrderType::StopLimit
425 );
426 assert_eq!(
427 hyperliquid_conditional_to_nautilus(HyperliquidConditionalOrderType::TakeProfitMarket),
428 OrderType::MarketIfTouched
429 );
430 assert_eq!(
431 hyperliquid_conditional_to_nautilus(HyperliquidConditionalOrderType::TakeProfitLimit),
432 OrderType::LimitIfTouched
433 );
434
435 assert_eq!(
437 nautilus_to_hyperliquid_conditional(OrderType::StopMarket),
438 HyperliquidConditionalOrderType::StopMarket
439 );
440 assert_eq!(
441 nautilus_to_hyperliquid_conditional(OrderType::StopLimit),
442 HyperliquidConditionalOrderType::StopLimit
443 );
444 assert_eq!(
445 nautilus_to_hyperliquid_conditional(OrderType::MarketIfTouched),
446 HyperliquidConditionalOrderType::TakeProfitMarket
447 );
448 assert_eq!(
449 nautilus_to_hyperliquid_conditional(OrderType::LimitIfTouched),
450 HyperliquidConditionalOrderType::TakeProfitLimit
451 );
452 }
453
454 #[rstest]
455 fn test_determine_tpsl_type() {
456 assert_eq!(
458 determine_tpsl_type(OrderType::StopMarket, true),
459 HyperliquidTpSl::Sl
460 );
461 assert_eq!(
462 determine_tpsl_type(OrderType::StopLimit, false),
463 HyperliquidTpSl::Sl
464 );
465
466 assert_eq!(
468 determine_tpsl_type(OrderType::MarketIfTouched, true),
469 HyperliquidTpSl::Tp
470 );
471 assert_eq!(
472 determine_tpsl_type(OrderType::LimitIfTouched, false),
473 HyperliquidTpSl::Tp
474 );
475
476 assert_eq!(
478 determine_tpsl_type(OrderType::TrailingStopMarket, true),
479 HyperliquidTpSl::Sl
480 );
481 assert_eq!(
482 determine_tpsl_type(OrderType::TrailingStopLimit, false),
483 HyperliquidTpSl::Sl
484 );
485 }
486}