use serde::Deserialize;
use crate::utils::dec::{Dec, Round};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize)]
pub struct Level {
pub px: Dec,
pub sz: Dec,
pub n: u32,
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
#[serde(from = "Raw")]
pub struct Book {
pub coin: String,
pub time: u64,
pub bids: Vec<Level>,
pub asks: Vec<Level>,
pub spread: Option<Dec>,
}
#[derive(Deserialize)]
struct Raw {
coin: String,
time: u64,
levels: (Vec<Level>, Vec<Level>),
#[serde(default)]
spread: Option<Dec>,
}
impl From<Raw> for Book {
fn from(raw: Raw) -> Self {
let (bids, asks) = raw.levels;
Book { coin: raw.coin, time: raw.time, bids, asks, spread: raw.spread }
}
}
impl Book {
#[must_use]
pub fn bids(&self) -> &[Level] {
&self.bids
}
#[must_use]
pub fn asks(&self) -> &[Level] {
&self.asks
}
#[must_use]
pub fn touch(&self) -> (Option<Dec>, Option<Dec>) {
(self.bids.first().map(|level| level.px), self.asks.first().map(|level| level.px))
}
#[must_use]
pub fn mid(&self) -> Option<Dec> {
let (Some(bid), Some(ask)) = self.touch() else { return None };
halfway(bid, ask)
}
}
fn halfway(bid: Dec, ask: Dec) -> Option<Dec> {
let places = bid.places().max(ask.places()).saturating_add(1);
bid.checked_add(ask)?.checked_div(Dec::from_int(2), places, Round::Down)
}
#[cfg(test)]
#[path = "tests/book.rs"]
mod tests;