df_hyperliquid_core/markets/
book.rs1use serde::Deserialize;
5
6use crate::utils::dec::{Dec, Round};
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize)]
10pub struct Level {
11 pub px: Dec,
12 pub sz: Dec,
13 pub n: u32,
15}
16
17#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
20#[serde(from = "Raw")]
21pub struct Book {
22 pub coin: String,
23 pub time: u64,
25 pub bids: Vec<Level>,
27 pub asks: Vec<Level>,
29 pub spread: Option<Dec>,
31}
32
33#[derive(Deserialize)]
35struct Raw {
36 coin: String,
37 time: u64,
38 levels: (Vec<Level>, Vec<Level>),
39 #[serde(default)]
40 spread: Option<Dec>,
41}
42
43impl From<Raw> for Book {
44 fn from(raw: Raw) -> Self {
45 let (bids, asks) = raw.levels;
46 Book { coin: raw.coin, time: raw.time, bids, asks, spread: raw.spread }
47 }
48}
49
50impl Book {
51 #[must_use]
52 pub fn bids(&self) -> &[Level] {
53 &self.bids
54 }
55
56 #[must_use]
57 pub fn asks(&self) -> &[Level] {
58 &self.asks
59 }
60
61 #[must_use]
63 pub fn touch(&self) -> (Option<Dec>, Option<Dec>) {
64 (self.bids.first().map(|level| level.px), self.asks.first().map(|level| level.px))
65 }
66
67 #[must_use]
70 pub fn mid(&self) -> Option<Dec> {
71 let (Some(bid), Some(ask)) = self.touch() else { return None };
72 halfway(bid, ask)
73 }
74}
75
76fn halfway(bid: Dec, ask: Dec) -> Option<Dec> {
78 let places = bid.places().max(ask.places()).saturating_add(1);
79 bid.checked_add(ask)?.checked_div(Dec::from_int(2), places, Round::Down)
80}
81
82#[cfg(test)]
83#[path = "tests/book.rs"]
84mod tests;