Skip to main content

df_hyperliquid_core/markets/
mids.rs

1//! `allMids`: every coin's mid on one perp dex, the same map from `/info` and from the socket.
2
3use std::fmt;
4
5use serde::{Deserialize, Deserializer, de};
6
7use crate::client::{ApiError, Transport, info, info_request};
8use crate::markets::dex::Dex;
9use crate::utils::dec::Dec;
10
11/// `{"type":"allMids","dex"?}`: the main dex, spot and outcomes with it, unless a HIP-3 dex is
12/// named.
13#[must_use]
14pub fn all_mids_request(dex: Dex<'_>) -> String {
15    info_request("allMids", dex.field().as_slice())
16}
17
18pub async fn all_mids(transport: &impl Transport, dex: Dex<'_>) -> Result<Mids, ApiError> {
19    info(transport, all_mids_request(dex)).await
20}
21
22/// Every coin's mid on one dex, in the venue's order. The main dex's also carries spot pairs
23/// (`@107`, `PURR/USDC`) and outcome sides (`#14730`). Where a book is empty the venue prints its
24/// last trade instead, and a mid can sit between two ticks.
25#[derive(Debug, Clone, PartialEq, Eq, Default)]
26pub struct Mids(Vec<(String, Dec)>);
27
28impl Mids {
29    /// A coin's mid, the coin spelled as the venue spells it (`BTC`, `@107`, `#14730`).
30    #[must_use]
31    pub fn get(&self, coin: &str) -> Option<Dec> {
32        self.0.iter().find(|(name, _)| name == coin).map(|(_, mid)| *mid)
33    }
34
35    #[must_use]
36    pub fn len(&self) -> usize {
37        self.0.len()
38    }
39
40    #[must_use]
41    pub fn is_empty(&self) -> bool {
42        self.0.is_empty()
43    }
44
45    /// In the order written.
46    pub fn iter(&self) -> impl Iterator<Item = (&str, Dec)> {
47        self.0.iter().map(|(coin, mid)| (coin.as_str(), *mid))
48    }
49}
50
51impl FromIterator<(String, Dec)> for Mids {
52    fn from_iter<I: IntoIterator<Item = (String, Dec)>>(pairs: I) -> Self {
53        Mids(pairs.into_iter().collect())
54    }
55}
56
57/// Reads the JSON map into pairs to keep the order written, which a map type would lose.
58impl<'de> Deserialize<'de> for Mids {
59    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
60        struct Pairs;
61
62        impl<'de> de::Visitor<'de> for Pairs {
63            type Value = Mids;
64
65            fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
66                f.write_str("a map of coin to price")
67            }
68
69            fn visit_map<A: de::MapAccess<'de>>(self, mut map: A) -> Result<Self::Value, A::Error> {
70                let mut out = Vec::with_capacity(map.size_hint().unwrap_or(0));
71                while let Some(pair) = map.next_entry::<String, Dec>()? {
72                    out.push(pair);
73                }
74                Ok(Mids(out))
75            }
76        }
77
78        deserializer.deserialize_map(Pairs)
79    }
80}
81
82#[cfg(test)]
83#[path = "tests/mids.rs"]
84mod tests;