hypercall/price_oracle/
hyperliquid_types.rs1use serde::{Deserialize, Serialize};
7
8#[derive(Debug, Clone, Serialize)]
12pub struct MetaAndAssetCtxsRequest {
13 #[serde(rename = "type")]
14 pub request_type: String,
15}
16
17impl MetaAndAssetCtxsRequest {
18 pub fn new() -> Self {
19 Self {
20 request_type: "metaAndAssetCtxs".to_string(),
21 }
22 }
23}
24
25impl Default for MetaAndAssetCtxsRequest {
26 fn default() -> Self {
27 Self::new()
28 }
29}
30
31#[derive(Debug, Clone)]
36pub struct MetaAndAssetCtxsResponse {
37 pub meta: Meta,
38 pub asset_ctxs: Vec<AssetCtx>,
39}
40
41impl<'de> Deserialize<'de> for MetaAndAssetCtxsResponse {
42 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
43 where
44 D: serde::Deserializer<'de>,
45 {
46 let tuple: (Meta, Vec<AssetCtx>) = Deserialize::deserialize(deserializer)?;
48 Ok(MetaAndAssetCtxsResponse {
49 meta: tuple.0,
50 asset_ctxs: tuple.1,
51 })
52 }
53}
54
55#[derive(Debug, Clone, Deserialize)]
57pub struct Meta {
58 pub universe: Vec<UniverseAsset>,
59}
60
61#[derive(Debug, Clone, Deserialize)]
63#[serde(rename_all = "camelCase")]
64pub struct UniverseAsset {
65 pub name: String,
67 pub sz_decimals: u32,
69 #[serde(default)]
71 pub max_leverage: u32,
72 #[serde(default)]
74 pub only_isolated: Option<bool>,
75}
76
77#[derive(Debug, Clone, Deserialize)]
81#[serde(rename_all = "camelCase")]
82pub struct AssetCtx {
83 pub funding: String,
85 pub open_interest: String,
87 #[serde(default)]
89 pub prev_day_px: String,
90 #[serde(default)]
92 pub day_ntl_vlm: String,
93 #[serde(default)]
95 pub premium: Option<String>,
96 pub oracle_px: String,
99 pub mark_px: String,
101 #[serde(default)]
103 pub mid_px: Option<String>,
104 #[serde(default)]
106 pub impact_pxs: Option<Vec<String>>,
107}
108
109impl AssetCtx {
110 pub fn oracle_price(&self) -> Option<f64> {
114 self.oracle_px.parse::<f64>().ok()
115 }
116
117 pub fn prev_day_price(&self) -> Option<f64> {
119 self.prev_day_px
120 .parse::<f64>()
121 .ok()
122 .filter(|v| v.is_finite() && *v > 0.0)
123 }
124
125 pub fn mark_price(&self) -> Option<f64> {
129 self.mark_px.parse::<f64>().ok()
130 }
131
132 pub fn funding_rate(&self) -> Option<f64> {
134 self.funding.parse::<f64>().ok()
135 }
136
137 pub fn open_interest_value(&self) -> Option<f64> {
139 self.open_interest.parse::<f64>().ok()
140 }
141}
142
143#[cfg(test)]
144mod tests {
145 use super::*;
146
147 #[test]
148 fn test_deserialize_meta_and_asset_ctxs() {
149 let json = r#"[
150 {
151 "universe": [
152 {"name": "BTC", "szDecimals": 5, "maxLeverage": 50},
153 {"name": "ETH", "szDecimals": 4, "maxLeverage": 50}
154 ]
155 },
156 [
157 {
158 "funding": "0.0001",
159 "openInterest": "1000000",
160 "prevDayPx": "42000.0",
161 "dayNtlVlm": "500000000",
162 "premium": "0.001",
163 "oraclePx": "42500.5",
164 "markPx": "42550.0"
165 },
166 {
167 "funding": "0.00005",
168 "openInterest": "500000",
169 "prevDayPx": "2200.0",
170 "dayNtlVlm": "100000000",
171 "premium": "0.0005",
172 "oraclePx": "2250.25",
173 "markPx": "2251.0"
174 }
175 ]
176 ]"#;
177
178 let response: MetaAndAssetCtxsResponse = sonic_rs::from_str(json).unwrap();
179
180 assert_eq!(response.meta.universe.len(), 2);
181 assert_eq!(response.meta.universe[0].name, "BTC");
182 assert_eq!(response.meta.universe[1].name, "ETH");
183
184 assert_eq!(response.asset_ctxs.len(), 2);
185 assert_eq!(response.asset_ctxs[0].oracle_price(), Some(42500.5));
186 assert_eq!(response.asset_ctxs[1].oracle_price(), Some(2250.25));
187 }
188
189 #[test]
190 fn test_asset_ctx_parsing() {
191 let ctx = AssetCtx {
192 funding: "0.0001".to_string(),
193 open_interest: "1000000.5".to_string(),
194 prev_day_px: "42000.0".to_string(),
195 day_ntl_vlm: "500000000".to_string(),
196 premium: Some("0.001".to_string()),
197 oracle_px: "42500.5".to_string(),
198 mark_px: "42550.0".to_string(),
199 mid_px: None,
200 impact_pxs: None,
201 };
202
203 assert_eq!(ctx.oracle_price(), Some(42500.5));
204 assert_eq!(ctx.mark_price(), Some(42550.0));
205 assert_eq!(ctx.funding_rate(), Some(0.0001));
206 assert_eq!(ctx.open_interest_value(), Some(1000000.5));
207 }
208}