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