nautilus_hyperliquid/websocket/
enums.rs1use serde::{Deserialize, Serialize};
17use strum::{AsRefStr, Display, EnumIter, EnumString};
18
19#[derive(
21 Clone,
22 Debug,
23 Display,
24 PartialEq,
25 Eq,
26 Hash,
27 AsRefStr,
28 EnumIter,
29 EnumString,
30 Serialize,
31 Deserialize,
32)]
33pub enum HyperliquidWsChannel {
34 #[serde(rename = "subscriptionResponse")]
35 SubscriptionResponse,
36 #[serde(rename = "trades")]
37 Trades,
38 #[serde(rename = "l2Book")]
39 L2Book,
40 #[serde(rename = "bbo")]
41 Bbo,
42 #[serde(rename = "candle")]
43 Candle,
44 #[serde(rename = "allMids")]
45 AllMids,
46 #[serde(rename = "allDexsAssetCtxs")]
47 AllDexsAssetCtxs,
48 #[serde(rename = "notification")]
49 Notification,
50 #[serde(rename = "orderUpdates")]
51 OrderUpdates,
52 #[serde(rename = "userEvents")]
53 UserEvents,
54 #[serde(rename = "userFills")]
55 UserFills,
56 #[serde(rename = "userFundings")]
57 UserFundings,
58 #[serde(rename = "userNonFundingLedgerUpdates")]
59 UserNonFundingLedgerUpdates,
60 #[serde(rename = "activeAssetCtx")]
61 ActiveAssetCtx,
62 #[serde(rename = "activeSpotAssetCtx")]
63 ActiveSpotAssetCtx,
64 #[serde(rename = "activeAssetData")]
65 ActiveAssetData,
66 #[serde(rename = "userTwapSliceFills")]
67 UserTwapSliceFills,
68 #[serde(rename = "userTwapHistory")]
69 UserTwapHistory,
70 #[serde(rename = "webData2")]
71 WebData2,
72 #[serde(rename = "user")]
74 User,
75 #[serde(rename = "post")]
76 Post,
77 #[serde(rename = "pong")]
78 Pong,
79 #[serde(rename = "error")]
80 Error,
81}
82
83impl HyperliquidWsChannel {
84 pub fn as_str(&self) -> &'static str {
86 match self {
87 Self::SubscriptionResponse => "subscriptionResponse",
88 Self::Trades => "trades",
89 Self::L2Book => "l2Book",
90 Self::Bbo => "bbo",
91 Self::Candle => "candle",
92 Self::AllMids => "allMids",
93 Self::AllDexsAssetCtxs => "allDexsAssetCtxs",
94 Self::Notification => "notification",
95 Self::OrderUpdates => "orderUpdates",
96 Self::UserEvents => "userEvents",
97 Self::UserFills => "userFills",
98 Self::UserFundings => "userFundings",
99 Self::UserNonFundingLedgerUpdates => "userNonFundingLedgerUpdates",
100 Self::ActiveAssetCtx => "activeAssetCtx",
101 Self::ActiveSpotAssetCtx => "activeSpotAssetCtx",
102 Self::ActiveAssetData => "activeAssetData",
103 Self::UserTwapSliceFills => "userTwapSliceFills",
104 Self::UserTwapHistory => "userTwapHistory",
105 Self::WebData2 => "webData2",
106 Self::User => "user",
107 Self::Post => "post",
108 Self::Pong => "pong",
109 Self::Error => "error",
110 }
111 }
112
113 pub fn is_public(&self) -> bool {
115 matches!(
116 self,
117 Self::SubscriptionResponse
118 | Self::Trades
119 | Self::L2Book
120 | Self::Bbo
121 | Self::Candle
122 | Self::AllMids
123 | Self::AllDexsAssetCtxs
124 | Self::ActiveAssetCtx
125 | Self::ActiveSpotAssetCtx
126 | Self::Notification
127 | Self::Pong
128 | Self::Error
129 )
130 }
131
132 pub fn is_private(&self) -> bool {
134 !self.is_public()
135 }
136
137 pub fn from_wire_str(s: &str) -> Option<Self> {
139 serde_json::from_value(serde_json::Value::String(s.to_string())).ok()
140 }
141}
142
143#[cfg(test)]
144mod tests {
145 use std::str::FromStr;
146
147 use rstest::rstest;
148 use serde_json;
149 use strum::IntoEnumIterator;
150
151 use super::*;
152
153 #[rstest]
154 #[case(HyperliquidWsChannel::Trades, r#""trades""#)]
155 #[case(HyperliquidWsChannel::L2Book, r#""l2Book""#)]
156 #[case(HyperliquidWsChannel::UserFills, r#""userFills""#)]
157 #[case(HyperliquidWsChannel::Bbo, r#""bbo""#)]
158 #[case(
159 HyperliquidWsChannel::SubscriptionResponse,
160 r#""subscriptionResponse""#
161 )]
162 fn test_channel_serialization(#[case] channel: HyperliquidWsChannel, #[case] expected: &str) {
163 assert_eq!(serde_json::to_string(&channel).unwrap(), expected);
164 }
165
166 #[rstest]
167 #[case(r#""trades""#, HyperliquidWsChannel::Trades)]
168 #[case(r#""l2Book""#, HyperliquidWsChannel::L2Book)]
169 #[case(r#""userEvents""#, HyperliquidWsChannel::UserEvents)]
170 #[case(r#""bbo""#, HyperliquidWsChannel::Bbo)]
171 #[case(r#""pong""#, HyperliquidWsChannel::Pong)]
172 fn test_channel_deserialization(#[case] json: &str, #[case] expected: HyperliquidWsChannel) {
173 assert_eq!(
174 serde_json::from_str::<HyperliquidWsChannel>(json).unwrap(),
175 expected
176 );
177 }
178
179 #[rstest]
180 #[case(HyperliquidWsChannel::Trades, "trades")]
181 #[case(HyperliquidWsChannel::L2Book, "l2Book")]
182 #[case(HyperliquidWsChannel::UserFills, "userFills")]
183 #[case(
184 HyperliquidWsChannel::UserNonFundingLedgerUpdates,
185 "userNonFundingLedgerUpdates"
186 )]
187 #[case(HyperliquidWsChannel::Bbo, "bbo")]
188 fn test_as_str_method(#[case] channel: HyperliquidWsChannel, #[case] expected: &str) {
189 assert_eq!(channel.as_str(), expected);
190 }
191
192 #[rstest]
193 fn test_display_trait() {
194 assert_eq!(format!("{}", HyperliquidWsChannel::Trades), "Trades");
195 assert_eq!(format!("{}", HyperliquidWsChannel::L2Book), "L2Book");
196 assert_eq!(format!("{}", HyperliquidWsChannel::UserFills), "UserFills");
197 }
198
199 #[rstest]
200 fn test_is_public_channel() {
201 assert!(HyperliquidWsChannel::Trades.is_public());
202 assert!(HyperliquidWsChannel::L2Book.is_public());
203 assert!(HyperliquidWsChannel::Bbo.is_public());
204 assert!(HyperliquidWsChannel::SubscriptionResponse.is_public());
205 assert!(HyperliquidWsChannel::Pong.is_public());
206
207 assert!(!HyperliquidWsChannel::OrderUpdates.is_public());
208 assert!(!HyperliquidWsChannel::UserEvents.is_public());
209 assert!(!HyperliquidWsChannel::UserFills.is_public());
210 assert!(!HyperliquidWsChannel::UserFundings.is_public());
211 assert!(!HyperliquidWsChannel::UserNonFundingLedgerUpdates.is_public());
212 assert!(!HyperliquidWsChannel::Post.is_public());
213 }
214
215 #[rstest]
216 fn test_is_private_channel() {
217 assert!(!HyperliquidWsChannel::Trades.is_private());
218 assert!(!HyperliquidWsChannel::L2Book.is_private());
219 assert!(!HyperliquidWsChannel::Bbo.is_private());
220
221 assert!(HyperliquidWsChannel::OrderUpdates.is_private());
222 assert!(HyperliquidWsChannel::UserEvents.is_private());
223 assert!(HyperliquidWsChannel::UserFills.is_private());
224 assert!(HyperliquidWsChannel::UserFundings.is_private());
225 assert!(HyperliquidWsChannel::UserNonFundingLedgerUpdates.is_private());
226 assert!(HyperliquidWsChannel::Post.is_private());
227 }
228
229 #[rstest]
230 fn test_enum_iter() {
231 let channels: Vec<HyperliquidWsChannel> = HyperliquidWsChannel::iter().collect();
232 assert_eq!(channels.len(), 23);
233 assert!(channels.contains(&HyperliquidWsChannel::Trades));
234 assert!(channels.contains(&HyperliquidWsChannel::L2Book));
235 assert!(channels.contains(&HyperliquidWsChannel::UserFills));
236 assert!(channels.contains(&HyperliquidWsChannel::Candle));
237 assert!(channels.contains(&HyperliquidWsChannel::AllMids));
238 assert!(channels.contains(&HyperliquidWsChannel::AllDexsAssetCtxs));
239 assert!(channels.contains(&HyperliquidWsChannel::Notification));
240 }
241
242 #[rstest]
243 fn test_from_str() {
244 assert_eq!(
245 HyperliquidWsChannel::from_str("Trades").unwrap(),
246 HyperliquidWsChannel::Trades
247 );
248 assert_eq!(
249 HyperliquidWsChannel::from_str("L2Book").unwrap(),
250 HyperliquidWsChannel::L2Book
251 );
252 assert_eq!(
253 HyperliquidWsChannel::from_str("UserFills").unwrap(),
254 HyperliquidWsChannel::UserFills
255 );
256
257 assert!(HyperliquidWsChannel::from_str("InvalidChannel").is_err());
258 }
259}