use std::{
cmp::Reverse,
collections::{BTreeMap, HashSet},
};
use crate::account::Collateral;
use crate::live::store::Stamped;
use crate::markets::perps::PerpCtx;
use crate::markets::spot::{SpotCtx, SpotMeta};
use crate::markets::{Book, Candle, Interval, Trade};
use crate::utils::Dec;
pub const TAPE: usize = 200;
pub const BARS: usize = 5_000;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Context<'a> {
Perp(&'a PerpCtx),
Spot(&'a SpotCtx),
}
impl Context<'_> {
#[must_use]
pub const fn mark(&self) -> Dec {
match self {
Context::Perp(ctx) => ctx.mark_px,
Context::Spot(ctx) => ctx.mark_px,
}
}
#[must_use]
pub const fn mid(&self) -> Option<Dec> {
match self {
Context::Perp(ctx) => ctx.mid_px,
Context::Spot(ctx) => ctx.mid_px,
}
}
}
#[derive(Debug, Default)]
pub(crate) struct Markets {
pub(crate) books: BTreeMap<String, Stamped<Book>>,
pub(crate) tapes: BTreeMap<String, Stamped<Vec<Trade>>>,
pub(crate) candles: BTreeMap<(String, Interval), Stamped<Vec<Candle>>>,
pub(crate) mids: BTreeMap<String, Stamped<BTreeMap<String, Dec>>>,
pub(crate) perps: BTreeMap<String, Stamped<PerpCtx>>,
pub(crate) spots: BTreeMap<String, Stamped<SpotCtx>>,
pub(crate) universes: BTreeMap<String, Vec<String>>,
pub(crate) collateral: Collateral,
pub(crate) spot_meta: Option<SpotMeta>,
}
pub(crate) fn fold_bar(bars: &mut Vec<Candle>, bar: Candle) -> bool {
let slot = match bars.last_mut() {
Some(last) if last.open_time > bar.open_time => bars.iter_mut().find(|one| one.open_time == bar.open_time),
Some(last) if last.open_time == bar.open_time => Some(last),
_ => {
bars.push(bar);
if bars.len() > BARS {
let extra = bars.len() - BARS;
bars.drain(..extra);
}
return true;
}
};
match slot {
Some(held) if *held != bar => {
*held = bar;
true
}
_ => false,
}
}
pub(crate) fn fold_prints(tape: &mut Vec<Trade>, prints: impl IntoIterator<Item = Trade>) -> bool {
let mut seen: HashSet<(u64, u64)> = tape.iter().map(|print| (print.tid, print.time)).collect();
let mut fresh: Vec<Trade> = prints.into_iter().filter(|print| seen.insert((print.tid, print.time))).collect();
if fresh.is_empty() {
return false;
}
fresh.append(tape);
fresh.sort_by_key(|print| Reverse(print.time));
fresh.truncate(TAPE);
*tape = fresh;
true
}
#[cfg(test)]
#[path = "tests/market.rs"]
mod tests;