nautilus_hyperliquid/common/types.rs
1// -------------------------------------------------------------------------------------------------
2// Copyright (C) 2015-2026 Nautech Systems Pty Ltd. All rights reserved.
3// https://nautechsystems.io
4//
5// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
6// You may not use this file except in compliance with the License.
7// You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14// -------------------------------------------------------------------------------------------------
15
16use std::fmt::Display;
17
18use serde::{Deserialize, Serialize};
19
20/// Represents an asset ID for Hyperliquid.
21///
22/// Asset IDs follow Hyperliquid's convention:
23/// - Perps: raw index into meta.universe
24/// - Spot: 10000 + index in spotMeta.universe
25/// - Builder perps: 100000 + dex_index * 10000 + meta_index
26#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
27pub struct HyperliquidAssetId(pub u32);
28
29impl HyperliquidAssetId {
30 /// Creates a perpetual asset ID from raw index.
31 pub fn perp(index: u32) -> Self {
32 Self(index)
33 }
34
35 /// Creates a spot asset ID (10000 + index).
36 pub fn spot(index: u32) -> Self {
37 Self(10_000 + index)
38 }
39
40 /// Creates a builder perpetual asset ID.
41 pub fn builder_perp(dex_index: u32, meta_index: u32) -> Self {
42 Self(100_000 + dex_index * 10_000 + meta_index)
43 }
44
45 /// Checks if this is a spot asset (>= 10000).
46 pub fn is_spot(self) -> bool {
47 self.0 >= 10_000 && self.0 < 100_000
48 }
49
50 /// Checks if this is a builder perp (>= 100000).
51 pub fn is_builder_perp(self) -> bool {
52 self.0 >= 100_000
53 }
54
55 /// Gets the base index for spot assets (asset_id - 10000).
56 /// For perps, returns the raw index.
57 pub fn base_index(self) -> u32 {
58 if self.is_spot() {
59 self.0 - 10_000
60 } else if self.is_builder_perp() {
61 // For builder perps, return the meta_index
62 (self.0 - 100_000) % 10_000
63 } else {
64 self.0
65 }
66 }
67
68 /// Gets the raw asset ID value.
69 pub fn to_raw(self) -> u32 {
70 self.0
71 }
72}
73
74impl Display for HyperliquidAssetId {
75 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
76 write!(f, "{}", self.0)
77 }
78}
79
80#[cfg(test)]
81mod tests {
82 use rstest::rstest;
83
84 use super::*;
85
86 #[rstest]
87 fn test_asset_id_perp() {
88 let asset_id = HyperliquidAssetId::perp(7);
89 assert_eq!(asset_id.to_raw(), 7);
90 assert!(!asset_id.is_spot());
91 assert!(!asset_id.is_builder_perp());
92 assert_eq!(asset_id.base_index(), 7);
93 }
94
95 #[rstest]
96 fn test_asset_id_spot() {
97 let asset_id = HyperliquidAssetId::spot(7);
98 assert_eq!(asset_id.to_raw(), 10_007);
99 assert!(asset_id.is_spot());
100 assert!(!asset_id.is_builder_perp());
101 assert_eq!(asset_id.base_index(), 7);
102 }
103
104 #[rstest]
105 fn test_asset_id_builder_perp() {
106 let asset_id = HyperliquidAssetId::builder_perp(1, 7);
107 assert_eq!(asset_id.to_raw(), 110_007);
108 assert!(!asset_id.is_spot());
109 assert!(asset_id.is_builder_perp());
110 assert_eq!(asset_id.base_index(), 7);
111 }
112}