use std::collections::HashMap;
use crate::types::orderbook::{OrderbookLevel, BookSnapshot, BookUpdate};
pub struct LocalOrderbook {
books: HashMap<String, (Vec<OrderbookLevel>, Vec<OrderbookLevel>)>,
}
impl LocalOrderbook {
pub fn new() -> Self {
Self {
books: HashMap::new(),
}
}
pub fn apply_snapshot(&mut self, snap: &BookSnapshot) {
let mut bids = snap.bids.clone();
let mut asks = snap.asks.clone();
bids.sort_by(|a, b| {
let pa: f64 = b.price.parse().unwrap_or(0.0);
let pb: f64 = a.price.parse().unwrap_or(0.0);
pa.partial_cmp(&pb).unwrap_or(std::cmp::Ordering::Equal)
});
asks.sort_by(|a, b| {
let pa: f64 = a.price.parse().unwrap_or(0.0);
let pb: f64 = b.price.parse().unwrap_or(0.0);
pa.partial_cmp(&pb).unwrap_or(std::cmp::Ordering::Equal)
});
self.books.insert(snap.asset_id.clone(), (bids, asks));
}
pub fn apply_update(&mut self, update: &BookUpdate) {
let book = self.books.entry(update.asset_id.clone())
.or_insert_with(|| (Vec::new(), Vec::new()));
apply_deltas(&mut book.0, &update.bids, true);
apply_deltas(&mut book.1, &update.asks, false);
}
pub fn get_book(&self, asset_id: &str) -> Option<(&[OrderbookLevel], &[OrderbookLevel])> {
self.books.get(asset_id).map(|(b, a)| (b.as_slice(), a.as_slice()))
}
pub fn best_bid(&self, asset_id: &str) -> Option<&OrderbookLevel> {
self.books.get(asset_id).and_then(|(bids, _)| bids.first())
}
pub fn best_ask(&self, asset_id: &str) -> Option<&OrderbookLevel> {
self.books.get(asset_id).and_then(|(_, asks)| asks.first())
}
pub fn spread(&self, asset_id: &str) -> Option<f64> {
let bid = self.best_bid(asset_id)?;
let ask = self.best_ask(asset_id)?;
let bid_price: f64 = bid.price.parse().ok()?;
let ask_price: f64 = ask.price.parse().ok()?;
Some(ask_price - bid_price)
}
pub fn len(&self) -> usize {
self.books.len()
}
pub fn is_empty(&self) -> bool {
self.books.is_empty()
}
pub fn clear(&mut self) {
self.books.clear();
}
}
impl Default for LocalOrderbook {
fn default() -> Self {
Self::new()
}
}
fn apply_deltas(levels: &mut Vec<OrderbookLevel>, deltas: &[OrderbookLevel], descending: bool) {
for delta in deltas {
levels.retain(|l| l.price != delta.price);
if delta.size != "0" && delta.size != "0.00" {
levels.push(delta.clone());
}
}
levels.sort_by(|a, b| {
let pa: f64 = a.price.parse().unwrap_or(0.0);
let pb: f64 = b.price.parse().unwrap_or(0.0);
if descending {
pb.partial_cmp(&pa).unwrap_or(std::cmp::Ordering::Equal)
} else {
pa.partial_cmp(&pb).unwrap_or(std::cmp::Ordering::Equal)
}
});
}