1use std::fmt::Display;
17
18use alloy_primitives::{Address, keccak256};
19use nautilus_model::identifiers::ClientOrderId;
20use rust_decimal::Decimal;
21use serde::{Deserialize, Deserializer, Serialize, Serializer};
22use ustr::Ustr;
23
24use crate::common::enums::{
25 HyperliquidFillDirection, HyperliquidLeverageType,
26 HyperliquidOrderStatus as HyperliquidOrderStatusEnum, HyperliquidPositionType, HyperliquidSide,
27};
28
29pub type HyperliquidCandleSnapshot = Vec<HyperliquidCandle>;
31
32#[derive(Clone, PartialEq, Eq, Hash, Debug)]
34pub struct Cloid(pub [u8; 16]);
35
36impl Cloid {
37 pub fn from_hex<S: AsRef<str>>(s: S) -> Result<Self, String> {
43 let hex_str = s.as_ref();
44 let without_prefix = hex_str
45 .strip_prefix("0x")
46 .ok_or("CLOID must start with '0x'")?;
47
48 if without_prefix.len() != 32 {
49 return Err("CLOID must be exactly 32 hex characters (128 bits)".to_string());
50 }
51
52 let mut bytes = [0u8; 16];
53 for i in 0..16 {
54 let byte_str = &without_prefix[i * 2..i * 2 + 2];
55 bytes[i] = u8::from_str_radix(byte_str, 16)
56 .map_err(|_| "Invalid hex character in CLOID".to_string())?;
57 }
58
59 Ok(Self(bytes))
60 }
61
62 #[must_use]
67 pub fn from_client_order_id(client_order_id: ClientOrderId) -> Self {
68 let hash = keccak256(client_order_id.as_str().as_bytes());
69 let mut bytes = [0u8; 16];
70 bytes.copy_from_slice(&hash[..16]);
71 Self(bytes)
72 }
73
74 pub fn to_hex(&self) -> String {
76 let mut result = String::with_capacity(34);
77 result.push_str("0x");
78 for byte in &self.0 {
79 result.push_str(&format!("{byte:02x}"));
80 }
81 result
82 }
83}
84
85impl Display for Cloid {
86 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
87 write!(f, "{}", self.to_hex())
88 }
89}
90
91impl Serialize for Cloid {
92 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
93 where
94 S: Serializer,
95 {
96 serializer.serialize_str(&self.to_hex())
97 }
98}
99
100impl<'de> Deserialize<'de> for Cloid {
101 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
102 where
103 D: Deserializer<'de>,
104 {
105 let s = String::deserialize(deserializer)?;
106 Self::from_hex(&s).map_err(serde::de::Error::custom)
107 }
108}
109
110pub type AssetId = u32;
115
116pub type OrderId = u64;
118
119#[derive(Debug, Clone, Serialize, Deserialize)]
121#[serde(rename_all = "camelCase")]
122pub struct HyperliquidAssetInfo {
123 pub name: Ustr,
125 pub sz_decimals: u32,
127 #[serde(default)]
129 pub max_leverage: Option<u32>,
130 #[serde(default)]
132 pub only_isolated: Option<bool>,
133 #[serde(default)]
135 pub is_delisted: Option<bool>,
136}
137
138#[derive(Debug, Clone, Serialize, Deserialize)]
140#[serde(rename_all = "camelCase")]
141pub struct PerpMeta {
142 pub universe: Vec<PerpAsset>,
144 #[serde(default)]
146 pub margin_tables: Vec<(u32, MarginTable)>,
147}
148
149#[derive(Debug, Clone, Serialize, Deserialize)]
151#[serde(rename_all = "camelCase")]
152pub struct PerpAsset {
153 pub name: String,
155 pub sz_decimals: u32,
157 #[serde(default)]
159 pub max_leverage: Option<u32>,
160 #[serde(default)]
162 pub only_isolated: Option<bool>,
163 #[serde(default)]
165 pub is_delisted: Option<bool>,
166}
167
168#[derive(Debug, Clone, Serialize, Deserialize)]
170#[serde(rename_all = "camelCase")]
171pub struct MarginTable {
172 pub description: String,
174 #[serde(default)]
176 pub margin_tiers: Vec<MarginTier>,
177}
178
179#[derive(Debug, Clone, Serialize, Deserialize)]
181#[serde(rename_all = "camelCase")]
182pub struct MarginTier {
183 pub lower_bound: String,
185 pub max_leverage: u32,
187}
188
189#[derive(Debug, Clone, Serialize, Deserialize)]
191#[serde(rename_all = "camelCase")]
192pub struct SpotMeta {
193 pub tokens: Vec<SpotToken>,
195 pub universe: Vec<SpotPair>,
197}
198
199#[derive(Debug, Clone, Serialize, Deserialize)]
201#[serde(rename_all = "snake_case")]
202pub struct EvmContract {
203 pub address: Address,
205 pub evm_extra_wei_decimals: i32,
207}
208
209#[derive(Debug, Clone, Serialize, Deserialize)]
211#[serde(rename_all = "camelCase")]
212pub struct SpotToken {
213 pub name: String,
215 pub sz_decimals: u32,
217 pub wei_decimals: u32,
219 pub index: u32,
221 pub token_id: String,
223 pub is_canonical: bool,
225 #[serde(default)]
227 pub evm_contract: Option<EvmContract>,
228 #[serde(default)]
230 pub full_name: Option<String>,
231 #[serde(default)]
233 pub deployer_trading_fee_share: Option<String>,
234}
235
236#[derive(Debug, Clone, Serialize, Deserialize)]
238#[serde(rename_all = "camelCase")]
239pub struct SpotPair {
240 pub name: String,
242 pub tokens: [u32; 2],
244 pub index: u32,
246 pub is_canonical: bool,
248}
249
250#[derive(Debug, Clone, Serialize, Deserialize)]
253#[serde(untagged)]
254pub enum PerpMetaAndCtxs {
255 Payload(Box<(PerpMeta, Vec<PerpAssetCtx>)>),
257}
258
259#[derive(Debug, Clone, Serialize, Deserialize)]
261#[serde(rename_all = "camelCase")]
262pub struct PerpAssetCtx {
263 #[serde(default)]
265 pub mark_px: Option<String>,
266 #[serde(default)]
268 pub mid_px: Option<String>,
269 #[serde(default)]
271 pub funding: Option<String>,
272 #[serde(default)]
274 pub open_interest: Option<String>,
275}
276
277#[derive(Debug, Clone, Serialize, Deserialize)]
280#[serde(untagged)]
281pub enum SpotMetaAndCtxs {
282 Payload(Box<(SpotMeta, Vec<SpotAssetCtx>)>),
284}
285
286#[derive(Debug, Clone, Serialize, Deserialize)]
288#[serde(rename_all = "camelCase")]
289pub struct SpotAssetCtx {
290 #[serde(default)]
292 pub mark_px: Option<String>,
293 #[serde(default)]
295 pub mid_px: Option<String>,
296 #[serde(default)]
298 pub day_volume: Option<String>,
299}
300
301#[derive(Debug, Clone, Serialize, Deserialize)]
303pub struct HyperliquidL2Book {
304 pub coin: Ustr,
306 pub levels: Vec<Vec<HyperliquidLevel>>,
308 pub time: u64,
310}
311
312#[derive(Debug, Clone, Serialize, Deserialize)]
314pub struct HyperliquidLevel {
315 pub px: String,
317 pub sz: String,
319}
320
321pub type HyperliquidFills = Vec<HyperliquidFill>;
325
326#[derive(Debug, Clone, Serialize, Deserialize)]
328pub struct HyperliquidMeta {
329 #[serde(default)]
330 pub universe: Vec<HyperliquidAssetInfo>,
331}
332
333#[derive(Debug, Clone, Serialize, Deserialize)]
335#[serde(rename_all = "camelCase")]
336pub struct HyperliquidCandle {
337 #[serde(rename = "t")]
339 pub timestamp: u64,
340 #[serde(rename = "T")]
342 pub end_timestamp: u64,
343 #[serde(rename = "o")]
345 pub open: String,
346 #[serde(rename = "h")]
348 pub high: String,
349 #[serde(rename = "l")]
351 pub low: String,
352 #[serde(rename = "c")]
354 pub close: String,
355 #[serde(rename = "v")]
357 pub volume: String,
358 #[serde(rename = "n", default)]
360 pub num_trades: Option<u64>,
361}
362
363#[derive(Debug, Clone, Serialize, Deserialize)]
365pub struct HyperliquidFill {
366 pub coin: Ustr,
368 pub px: String,
370 pub sz: String,
372 pub side: HyperliquidSide,
374 pub time: u64,
376 #[serde(rename = "startPosition")]
378 pub start_position: String,
379 pub dir: HyperliquidFillDirection,
381 #[serde(rename = "closedPnl")]
383 pub closed_pnl: String,
384 pub hash: String,
386 pub oid: u64,
388 pub crossed: bool,
390 pub fee: String,
392 #[serde(rename = "feeToken")]
394 pub fee_token: Ustr,
395}
396
397#[derive(Debug, Clone, Serialize, Deserialize)]
399pub struct HyperliquidOrderStatus {
400 #[serde(default)]
401 pub statuses: Vec<HyperliquidOrderStatusEntry>,
402}
403
404#[derive(Debug, Clone, Serialize, Deserialize)]
406pub struct HyperliquidOrderStatusEntry {
407 pub order: HyperliquidOrderInfo,
409 pub status: HyperliquidOrderStatusEnum,
411 #[serde(rename = "statusTimestamp")]
413 pub status_timestamp: u64,
414}
415
416#[derive(Debug, Clone, Serialize, Deserialize)]
418pub struct HyperliquidOrderInfo {
419 pub coin: Ustr,
421 pub side: HyperliquidSide,
423 #[serde(rename = "limitPx")]
425 pub limit_px: String,
426 pub sz: String,
428 pub oid: u64,
430 pub timestamp: u64,
432 #[serde(rename = "origSz")]
434 pub orig_sz: String,
435}
436
437#[derive(Debug, Clone, Serialize)]
439pub struct HyperliquidSignature {
440 pub r: String,
442 pub s: String,
444 pub v: u64,
446}
447
448impl HyperliquidSignature {
449 pub fn from_hex(sig_hex: &str) -> Result<Self, String> {
451 let sig_hex = sig_hex.strip_prefix("0x").unwrap_or(sig_hex);
452
453 if sig_hex.len() != 130 {
454 return Err(format!(
455 "Invalid signature length: expected 130 hex chars, was {}",
456 sig_hex.len()
457 ));
458 }
459
460 let r = format!("0x{}", &sig_hex[0..64]);
461 let s = format!("0x{}", &sig_hex[64..128]);
462 let v = u64::from_str_radix(&sig_hex[128..130], 16)
463 .map_err(|e| format!("Failed to parse v component: {e}"))?;
464
465 Ok(Self { r, s, v })
466 }
467}
468
469#[derive(Debug, Clone, Serialize)]
471pub struct HyperliquidExchangeRequest<T> {
472 #[serde(rename = "action")]
474 pub action: T,
475 #[serde(rename = "nonce")]
477 pub nonce: u64,
478 #[serde(rename = "signature")]
480 pub signature: HyperliquidSignature,
481 #[serde(rename = "vaultAddress", skip_serializing_if = "Option::is_none")]
483 pub vault_address: Option<String>,
484 #[serde(rename = "expiresAfter", skip_serializing_if = "Option::is_none")]
486 pub expires_after: Option<u64>,
487}
488
489impl<T> HyperliquidExchangeRequest<T>
490where
491 T: Serialize,
492{
493 pub fn new(action: T, nonce: u64, signature: String) -> Result<Self, String> {
495 Ok(Self {
496 action,
497 nonce,
498 signature: HyperliquidSignature::from_hex(&signature)?,
499 vault_address: None,
500 expires_after: None,
501 })
502 }
503
504 pub fn with_vault(
506 action: T,
507 nonce: u64,
508 signature: String,
509 vault_address: String,
510 ) -> Result<Self, String> {
511 Ok(Self {
512 action,
513 nonce,
514 signature: HyperliquidSignature::from_hex(&signature)?,
515 vault_address: Some(vault_address),
516 expires_after: None,
517 })
518 }
519
520 pub fn to_sign_value(&self) -> serde_json::Result<serde_json::Value> {
522 serde_json::to_value(self)
523 }
524}
525
526#[derive(Debug, Clone, Serialize, Deserialize)]
528#[serde(untagged)]
529pub enum HyperliquidExchangeResponse {
530 Status {
532 status: String,
534 response: serde_json::Value,
536 },
537 Error {
539 error: String,
541 },
542}
543
544impl HyperliquidExchangeResponse {
545 pub fn is_ok(&self) -> bool {
546 matches!(self, Self::Status { status, .. } if status == RESPONSE_STATUS_OK)
547 }
548}
549
550pub const RESPONSE_STATUS_OK: &str = "ok";
552
553#[cfg(test)]
554mod tests {
555 use rstest::rstest;
556
557 use super::*;
558
559 #[rstest]
560 fn test_meta_deserialization() {
561 let json = r#"{"universe": [{"name": "BTC", "szDecimals": 5}]}"#;
562
563 let meta: HyperliquidMeta = serde_json::from_str(json).unwrap();
564
565 assert_eq!(meta.universe.len(), 1);
566 assert_eq!(meta.universe[0].name, "BTC");
567 assert_eq!(meta.universe[0].sz_decimals, 5);
568 }
569
570 #[rstest]
571 fn test_l2_book_deserialization() {
572 let json = r#"{"coin": "BTC", "levels": [[{"px": "50000", "sz": "1.5"}], [{"px": "50100", "sz": "2.0"}]], "time": 1234567890}"#;
573
574 let book: HyperliquidL2Book = serde_json::from_str(json).unwrap();
575
576 assert_eq!(book.coin, "BTC");
577 assert_eq!(book.levels.len(), 2);
578 assert_eq!(book.time, 1234567890);
579 }
580
581 #[rstest]
582 fn test_exchange_response_deserialization() {
583 let json = r#"{"status": "ok", "response": {"type": "order"}}"#;
584
585 let response: HyperliquidExchangeResponse = serde_json::from_str(json).unwrap();
586 assert!(response.is_ok());
587 }
588
589 #[rstest]
590 fn test_msgpack_serialization_matches_python() {
591 let action = HyperliquidExecAction::Order {
596 orders: vec![],
597 grouping: HyperliquidExecGrouping::Na,
598 builder: None,
599 };
600
601 let json = serde_json::to_string(&action).unwrap();
603 assert!(
604 json.contains(r#""type":"order""#),
605 "JSON should have type tag: {json}"
606 );
607
608 let msgpack_bytes = rmp_serde::to_vec_named(&action).unwrap();
610
611 let decoded: serde_json::Value = rmp_serde::from_slice(&msgpack_bytes).unwrap();
613
614 assert!(
616 decoded.get("type").is_some(),
617 "MsgPack should have type tag. Decoded: {decoded:?}"
618 );
619 assert_eq!(
620 decoded.get("type").unwrap().as_str().unwrap(),
621 "order",
622 "Type should be 'order'"
623 );
624 assert!(decoded.get("orders").is_some(), "Should have orders field");
625 assert!(
626 decoded.get("grouping").is_some(),
627 "Should have grouping field"
628 );
629 }
630}
631
632#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
636pub enum HyperliquidExecTif {
637 #[serde(rename = "Alo")]
639 Alo,
640 #[serde(rename = "Ioc")]
642 Ioc,
643 #[serde(rename = "Gtc")]
645 Gtc,
646}
647
648#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
650pub enum HyperliquidExecTpSl {
651 #[serde(rename = "tp")]
653 Tp,
654 #[serde(rename = "sl")]
656 Sl,
657}
658
659#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
661pub enum HyperliquidExecGrouping {
662 #[serde(rename = "na")]
664 #[default]
665 Na,
666 #[serde(rename = "normalTpsl")]
668 NormalTpsl,
669 #[serde(rename = "positionTpsl")]
671 PositionTpsl,
672}
673
674#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
676#[serde(untagged)]
677pub enum HyperliquidExecOrderKind {
678 Limit {
680 limit: HyperliquidExecLimitParams,
682 },
683 Trigger {
685 trigger: HyperliquidExecTriggerParams,
687 },
688}
689
690#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
692pub struct HyperliquidExecLimitParams {
693 pub tif: HyperliquidExecTif,
695}
696
697#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
699#[serde(rename_all = "camelCase")]
700pub struct HyperliquidExecTriggerParams {
701 pub is_market: bool,
703 #[serde(
705 serialize_with = "crate::common::parse::serialize_decimal_as_str",
706 deserialize_with = "crate::common::parse::deserialize_decimal_from_str"
707 )]
708 pub trigger_px: Decimal,
709 pub tpsl: HyperliquidExecTpSl,
711}
712
713#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
718pub struct HyperliquidExecBuilderFee {
719 #[serde(rename = "b")]
721 pub address: String,
722 #[serde(rename = "f")]
724 pub fee_tenths_bp: u32,
725}
726
727#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
732pub struct HyperliquidExecPlaceOrderRequest {
733 #[serde(rename = "a")]
735 pub asset: AssetId,
736 #[serde(rename = "b")]
738 pub is_buy: bool,
739 #[serde(
741 rename = "p",
742 serialize_with = "crate::common::parse::serialize_decimal_as_str",
743 deserialize_with = "crate::common::parse::deserialize_decimal_from_str"
744 )]
745 pub price: Decimal,
746 #[serde(
748 rename = "s",
749 serialize_with = "crate::common::parse::serialize_decimal_as_str",
750 deserialize_with = "crate::common::parse::deserialize_decimal_from_str"
751 )]
752 pub size: Decimal,
753 #[serde(rename = "r")]
755 pub reduce_only: bool,
756 #[serde(rename = "t")]
758 pub kind: HyperliquidExecOrderKind,
759 #[serde(rename = "c", skip_serializing_if = "Option::is_none")]
761 pub cloid: Option<Cloid>,
762}
763
764#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
766pub struct HyperliquidExecCancelOrderRequest {
767 #[serde(rename = "a")]
769 pub asset: AssetId,
770 #[serde(rename = "o")]
772 pub oid: OrderId,
773}
774
775#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
780pub struct HyperliquidExecCancelByCloidRequest {
781 pub asset: AssetId,
783 pub cloid: Cloid,
785}
786
787#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
789pub struct HyperliquidExecModifyOrderRequest {
790 #[serde(rename = "a")]
792 pub asset: AssetId,
793 #[serde(rename = "o")]
795 pub oid: OrderId,
796 #[serde(
798 rename = "p",
799 skip_serializing_if = "Option::is_none",
800 serialize_with = "crate::common::parse::serialize_optional_decimal_as_str",
801 deserialize_with = "crate::common::parse::deserialize_optional_decimal_from_str"
802 )]
803 pub price: Option<Decimal>,
804 #[serde(
806 rename = "s",
807 skip_serializing_if = "Option::is_none",
808 serialize_with = "crate::common::parse::serialize_optional_decimal_as_str",
809 deserialize_with = "crate::common::parse::deserialize_optional_decimal_from_str"
810 )]
811 pub size: Option<Decimal>,
812 #[serde(rename = "r", skip_serializing_if = "Option::is_none")]
814 pub reduce_only: Option<bool>,
815 #[serde(rename = "t", skip_serializing_if = "Option::is_none")]
817 pub kind: Option<HyperliquidExecOrderKind>,
818}
819
820#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
822pub struct HyperliquidExecTwapRequest {
823 #[serde(rename = "a")]
825 pub asset: AssetId,
826 #[serde(rename = "b")]
828 pub is_buy: bool,
829 #[serde(
831 rename = "s",
832 serialize_with = "crate::common::parse::serialize_decimal_as_str",
833 deserialize_with = "crate::common::parse::deserialize_decimal_from_str"
834 )]
835 pub size: Decimal,
836 #[serde(rename = "m")]
838 pub duration_ms: u64,
839}
840
841#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
847#[serde(tag = "type")]
848pub enum HyperliquidExecAction {
849 #[serde(rename = "order")]
851 Order {
852 orders: Vec<HyperliquidExecPlaceOrderRequest>,
854 #[serde(default)]
856 grouping: HyperliquidExecGrouping,
857 #[serde(skip_serializing_if = "Option::is_none")]
859 builder: Option<HyperliquidExecBuilderFee>,
860 },
861
862 #[serde(rename = "cancel")]
864 Cancel {
865 cancels: Vec<HyperliquidExecCancelOrderRequest>,
867 },
868
869 #[serde(rename = "cancelByCloid")]
871 CancelByCloid {
872 cancels: Vec<HyperliquidExecCancelByCloidRequest>,
874 },
875
876 #[serde(rename = "modify")]
878 Modify {
879 #[serde(flatten)]
881 modify: HyperliquidExecModifyOrderRequest,
882 },
883
884 #[serde(rename = "batchModify")]
886 BatchModify {
887 modifies: Vec<HyperliquidExecModifyOrderRequest>,
889 },
890
891 #[serde(rename = "scheduleCancel")]
893 ScheduleCancel {
894 #[serde(skip_serializing_if = "Option::is_none")]
897 time: Option<u64>,
898 },
899
900 #[serde(rename = "updateLeverage")]
902 UpdateLeverage {
903 #[serde(rename = "a")]
905 asset: AssetId,
906 #[serde(rename = "isCross")]
908 is_cross: bool,
909 #[serde(rename = "leverage")]
911 leverage: u32,
912 },
913
914 #[serde(rename = "updateIsolatedMargin")]
916 UpdateIsolatedMargin {
917 #[serde(rename = "a")]
919 asset: AssetId,
920 #[serde(
922 rename = "delta",
923 serialize_with = "crate::common::parse::serialize_decimal_as_str",
924 deserialize_with = "crate::common::parse::deserialize_decimal_from_str"
925 )]
926 delta: Decimal,
927 },
928
929 #[serde(rename = "usdClassTransfer")]
931 UsdClassTransfer {
932 from: String,
934 to: String,
936 #[serde(
938 serialize_with = "crate::common::parse::serialize_decimal_as_str",
939 deserialize_with = "crate::common::parse::deserialize_decimal_from_str"
940 )]
941 amount: Decimal,
942 },
943
944 #[serde(rename = "twapPlace")]
946 TwapPlace {
947 #[serde(flatten)]
949 twap: HyperliquidExecTwapRequest,
950 },
951
952 #[serde(rename = "twapCancel")]
954 TwapCancel {
955 #[serde(rename = "a")]
957 asset: AssetId,
958 #[serde(rename = "t")]
960 twap_id: u64,
961 },
962
963 #[serde(rename = "noop")]
965 Noop,
966}
967
968#[derive(Debug, Clone, Serialize)]
973#[serde(rename_all = "camelCase")]
974pub struct HyperliquidExecRequest {
975 pub action: HyperliquidExecAction,
977 pub nonce: u64,
979 pub signature: String,
981 #[serde(skip_serializing_if = "Option::is_none")]
983 pub vault_address: Option<String>,
984 #[serde(skip_serializing_if = "Option::is_none")]
987 pub expires_after: Option<u64>,
988}
989
990#[derive(Debug, Clone, Serialize, Deserialize)]
992pub struct HyperliquidExecResponse {
993 pub status: String,
995 pub response: HyperliquidExecResponseData,
997}
998
999#[derive(Debug, Clone, Serialize, Deserialize)]
1001#[serde(tag = "type")]
1002pub enum HyperliquidExecResponseData {
1003 #[serde(rename = "order")]
1005 Order {
1006 data: HyperliquidExecOrderResponseData,
1008 },
1009 #[serde(rename = "cancel")]
1011 Cancel {
1012 data: HyperliquidExecCancelResponseData,
1014 },
1015 #[serde(rename = "modify")]
1017 Modify {
1018 data: HyperliquidExecModifyResponseData,
1020 },
1021 #[serde(rename = "default")]
1023 Default,
1024 #[serde(other)]
1026 Unknown,
1027}
1028
1029#[derive(Debug, Clone, Serialize, Deserialize)]
1031pub struct HyperliquidExecOrderResponseData {
1032 pub statuses: Vec<HyperliquidExecOrderStatus>,
1034}
1035
1036#[derive(Debug, Clone, Serialize, Deserialize)]
1038pub struct HyperliquidExecCancelResponseData {
1039 pub statuses: Vec<HyperliquidExecCancelStatus>,
1041}
1042
1043#[derive(Debug, Clone, Serialize, Deserialize)]
1045pub struct HyperliquidExecModifyResponseData {
1046 pub statuses: Vec<HyperliquidExecModifyStatus>,
1048}
1049
1050#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1052#[serde(untagged)]
1053pub enum HyperliquidExecOrderStatus {
1054 Resting {
1056 resting: HyperliquidExecRestingInfo,
1058 },
1059 Filled {
1061 filled: HyperliquidExecFilledInfo,
1063 },
1064 Error {
1066 error: String,
1068 },
1069}
1070
1071#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1073pub struct HyperliquidExecRestingInfo {
1074 pub oid: OrderId,
1076}
1077
1078#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1080pub struct HyperliquidExecFilledInfo {
1081 #[serde(
1083 rename = "totalSz",
1084 serialize_with = "crate::common::parse::serialize_decimal_as_str",
1085 deserialize_with = "crate::common::parse::deserialize_decimal_from_str"
1086 )]
1087 pub total_sz: Decimal,
1088 #[serde(
1090 rename = "avgPx",
1091 serialize_with = "crate::common::parse::serialize_decimal_as_str",
1092 deserialize_with = "crate::common::parse::deserialize_decimal_from_str"
1093 )]
1094 pub avg_px: Decimal,
1095 pub oid: OrderId,
1097}
1098
1099#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1101#[serde(untagged)]
1102pub enum HyperliquidExecCancelStatus {
1103 Success(String), Error {
1107 error: String,
1109 },
1110}
1111
1112#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1114#[serde(untagged)]
1115pub enum HyperliquidExecModifyStatus {
1116 Success(String), Error {
1120 error: String,
1122 },
1123}
1124
1125#[derive(Debug, Clone, Serialize, Deserialize)]
1128#[serde(rename_all = "camelCase")]
1129pub struct ClearinghouseState {
1130 #[serde(default)]
1132 pub asset_positions: Vec<AssetPosition>,
1133 #[serde(default)]
1135 pub cross_margin_summary: Option<CrossMarginSummary>,
1136 #[serde(
1138 default,
1139 serialize_with = "crate::common::parse::serialize_optional_decimal_as_str",
1140 deserialize_with = "crate::common::parse::deserialize_optional_decimal_from_str"
1141 )]
1142 pub withdrawable: Option<Decimal>,
1143 #[serde(default)]
1145 pub time: Option<u64>,
1146}
1147
1148#[derive(Debug, Clone, Serialize, Deserialize)]
1150#[serde(rename_all = "camelCase")]
1151pub struct AssetPosition {
1152 pub position: PositionData,
1154 #[serde(rename = "type")]
1156 pub position_type: HyperliquidPositionType,
1157}
1158
1159#[derive(Debug, Clone, Serialize, Deserialize)]
1161#[serde(rename_all = "camelCase")]
1162pub struct LeverageInfo {
1163 #[serde(rename = "type")]
1164 pub leverage_type: HyperliquidLeverageType,
1165 pub value: u32,
1167}
1168
1169#[derive(Debug, Clone, Serialize, Deserialize)]
1171#[serde(rename_all = "camelCase")]
1172pub struct CumFundingInfo {
1173 #[serde(
1175 rename = "allTime",
1176 serialize_with = "crate::common::parse::serialize_decimal_as_str",
1177 deserialize_with = "crate::common::parse::deserialize_decimal_from_str"
1178 )]
1179 pub all_time: Decimal,
1180 #[serde(
1182 rename = "sinceOpen",
1183 serialize_with = "crate::common::parse::serialize_decimal_as_str",
1184 deserialize_with = "crate::common::parse::deserialize_decimal_from_str"
1185 )]
1186 pub since_open: Decimal,
1187 #[serde(
1189 rename = "sinceChange",
1190 serialize_with = "crate::common::parse::serialize_decimal_as_str",
1191 deserialize_with = "crate::common::parse::deserialize_decimal_from_str"
1192 )]
1193 pub since_change: Decimal,
1194}
1195
1196#[derive(Debug, Clone, Serialize, Deserialize)]
1198#[serde(rename_all = "camelCase")]
1199pub struct PositionData {
1200 pub coin: Ustr,
1202 #[serde(rename = "cumFunding")]
1204 pub cum_funding: CumFundingInfo,
1205 #[serde(
1207 rename = "entryPx",
1208 serialize_with = "crate::common::parse::serialize_optional_decimal_as_str",
1209 deserialize_with = "crate::common::parse::deserialize_optional_decimal_from_str",
1210 default
1211 )]
1212 pub entry_px: Option<Decimal>,
1213 pub leverage: LeverageInfo,
1215 #[serde(
1217 rename = "liquidationPx",
1218 serialize_with = "crate::common::parse::serialize_optional_decimal_as_str",
1219 deserialize_with = "crate::common::parse::deserialize_optional_decimal_from_str",
1220 default
1221 )]
1222 pub liquidation_px: Option<Decimal>,
1223 #[serde(
1225 rename = "marginUsed",
1226 serialize_with = "crate::common::parse::serialize_decimal_as_str",
1227 deserialize_with = "crate::common::parse::deserialize_decimal_from_str"
1228 )]
1229 pub margin_used: Decimal,
1230 #[serde(rename = "maxLeverage", default)]
1232 pub max_leverage: Option<u32>,
1233 #[serde(
1235 rename = "positionValue",
1236 serialize_with = "crate::common::parse::serialize_decimal_as_str",
1237 deserialize_with = "crate::common::parse::deserialize_decimal_from_str"
1238 )]
1239 pub position_value: Decimal,
1240 #[serde(
1242 rename = "returnOnEquity",
1243 serialize_with = "crate::common::parse::serialize_decimal_as_str",
1244 deserialize_with = "crate::common::parse::deserialize_decimal_from_str"
1245 )]
1246 pub return_on_equity: Decimal,
1247 #[serde(
1249 rename = "szi",
1250 serialize_with = "crate::common::parse::serialize_decimal_as_str",
1251 deserialize_with = "crate::common::parse::deserialize_decimal_from_str"
1252 )]
1253 pub szi: Decimal,
1254 #[serde(
1256 rename = "unrealizedPnl",
1257 serialize_with = "crate::common::parse::serialize_decimal_as_str",
1258 deserialize_with = "crate::common::parse::deserialize_decimal_from_str"
1259 )]
1260 pub unrealized_pnl: Decimal,
1261}
1262
1263#[derive(Debug, Clone, Serialize, Deserialize)]
1265#[serde(rename_all = "camelCase")]
1266pub struct CrossMarginSummary {
1267 #[serde(
1269 rename = "accountValue",
1270 serialize_with = "crate::common::parse::serialize_decimal_as_str",
1271 deserialize_with = "crate::common::parse::deserialize_decimal_from_str"
1272 )]
1273 pub account_value: Decimal,
1274 #[serde(
1276 rename = "totalNtlPos",
1277 serialize_with = "crate::common::parse::serialize_decimal_as_str",
1278 deserialize_with = "crate::common::parse::deserialize_decimal_from_str"
1279 )]
1280 pub total_ntl_pos: Decimal,
1281 #[serde(
1283 rename = "totalRawUsd",
1284 serialize_with = "crate::common::parse::serialize_decimal_as_str",
1285 deserialize_with = "crate::common::parse::deserialize_decimal_from_str"
1286 )]
1287 pub total_raw_usd: Decimal,
1288 #[serde(
1290 rename = "totalMarginUsed",
1291 serialize_with = "crate::common::parse::serialize_decimal_as_str",
1292 deserialize_with = "crate::common::parse::deserialize_decimal_from_str"
1293 )]
1294 pub total_margin_used: Decimal,
1295 #[serde(
1297 rename = "withdrawable",
1298 default,
1299 serialize_with = "crate::common::parse::serialize_optional_decimal_as_str",
1300 deserialize_with = "crate::common::parse::deserialize_optional_decimal_from_str"
1301 )]
1302 pub withdrawable: Option<Decimal>,
1303}