Skip to main content

df_hyperliquid_core/markets/
reads.rs

1//! Market data every market kind shares: `l2Book`, `candleSnapshot`,
2//! `recentTrades` and `allMids`. A coin is `BTC`, `xyz:XYZ100` on a HIP-3
3//! dex, `@107` or `PURR/USDC` for a pair, `#14730` for an outcome side.
4
5use crate::client::request::{body, dex, number, text};
6use crate::client::{ApiError, Transport, info};
7
8/// How finely a book's levels are bucketed: full precision, or 2 to 5
9/// significant figures, and at 5 figures a step of 1, 2 or 5 in the last.
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
11pub struct Precision {
12    figures: Option<u32>,
13    step: Option<u32>,
14}
15
16impl Precision {
17    pub const FULL: Precision = Precision { figures: None, step: None };
18
19    /// `nSigFigs`: 2, 3, 4 or 5.
20    #[must_use]
21    pub const fn figures(figures: u32) -> Option<Precision> {
22        match figures {
23            2..=5 => Some(Precision { figures: Some(figures), step: None }),
24            _ => None,
25        }
26    }
27
28    /// `mantissa`: 1, 2 or 5, and only at five figures.
29    #[must_use]
30    pub const fn step(self, step: u32) -> Option<Precision> {
31        match (self.figures, step) {
32            (Some(5), 1 | 2 | 5) => Some(Precision { figures: Some(5), step: Some(step) }),
33            _ => None,
34        }
35    }
36}
37
38/// One price level, the same from a read and from the socket.
39pub use crate::markets::book::Level;
40
41/// `l2Book`: at most twenty levels a side, best first; the same shape the socket sends.
42pub type L2Book = crate::markets::book::Book;
43
44/// A candle and its width, the same shape the socket sends.
45pub use crate::markets::candle::{Candle, Interval, UnknownInterval};
46
47/// A print; `side` is the aggressor's.
48pub type MarketTrade = crate::markets::trades::Trade;
49
50/// `allMids`: every coin's mid on one dex.
51pub use crate::markets::mids::Mids;
52
53/// `{"type":"l2Book","coin":..,"nSigFigs"?,"mantissa"?}`.
54#[must_use]
55pub fn l2_book_request(coin: &str, precision: Precision) -> String {
56    let figures = precision.figures.map(|figures| ("nSigFigs", number(u64::from(figures))));
57    let step = precision.step.map(|step| ("mantissa", number(u64::from(step))));
58    body("l2Book", [Some(("coin", text(coin))), figures, step].into_iter().flatten())
59}
60
61/// `{"type":"candleSnapshot","req":{"coin","interval","startTime","endTime"}}`,
62/// the times unix milliseconds. Only the 5000 most recent candles are kept.
63#[must_use]
64pub fn candle_snapshot_request(coin: &str, interval: Interval, start: u64, end: u64) -> String {
65    let req = crate::utils::Json::Object(vec![
66        ("coin".to_owned(), text(coin)),
67        ("interval".to_owned(), text(interval.wire())),
68        ("startTime".to_owned(), number(start)),
69        ("endTime".to_owned(), number(end)),
70    ]);
71    body("candleSnapshot", [("req", req)])
72}
73
74/// `{"type":"recentTrades","coin":..}`.
75#[must_use]
76pub fn recent_trades_request(coin: &str) -> String {
77    body("recentTrades", [("coin", text(coin))])
78}
79
80/// `{"type":"allMids","dex"?}`: the main dex, spot and outcomes with it,
81/// unless a HIP-3 dex is named.
82#[must_use]
83pub fn all_mids_request(on: Option<&str>) -> String {
84    body("allMids", dex(on))
85}
86
87pub async fn l2_book(transport: &impl Transport, coin: &str, precision: Precision) -> Result<L2Book, ApiError> {
88    info(transport, l2_book_request(coin, precision)).await
89}
90
91/// The candles in the window, oldest first; none when the venue answers `null`.
92pub async fn candle_snapshot(
93    transport: &impl Transport,
94    coin: &str,
95    interval: Interval,
96    start: u64,
97    end: u64,
98) -> Result<Vec<Candle>, ApiError> {
99    let candles: Option<Vec<Candle>> = info(transport, candle_snapshot_request(coin, interval, start, end)).await?;
100    Ok(candles.unwrap_or_default())
101}
102
103pub async fn recent_trades(transport: &impl Transport, coin: &str) -> Result<Vec<MarketTrade>, ApiError> {
104    info(transport, recent_trades_request(coin)).await
105}
106
107pub async fn all_mids(transport: &impl Transport, on: Option<&str>) -> Result<Mids, ApiError> {
108    info(transport, all_mids_request(on)).await
109}
110
111#[cfg(test)]
112#[path = "tests/reads.rs"]
113mod tests;