use const_decimal::Decimal;
use getset::{
CopyGetters,
Getters,
Setters,
};
use crate::{
prelude::{
Currency,
MarketUpdate,
Mon,
PriceFilter,
QuoteCurrency,
},
types::TimestampNs,
};
#[derive(Debug, Default, Clone, Getters, CopyGetters, Setters)]
pub struct MarketState<I, const D: u8>
where
I: Mon<D>,
{
#[getset(get_copy = "pub", set = "pub(crate)")]
bid: QuoteCurrency<I, D>,
#[getset(get_copy = "pub", set = "pub(crate)")]
ask: QuoteCurrency<I, D>,
#[getset(get_copy = "pub", set = "pub")]
last_trade_price: QuoteCurrency<I, D>,
#[getset(get_copy = "pub")]
current_ts_ns: TimestampNs,
#[getset(get_copy = "pub")]
step: u64,
}
impl<I: Mon<D>, const D: u8> std::fmt::Display for MarketState<I, D> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"MarketState( bid: {}, ask: {}, ts_ns: {}, step: {} )",
self.bid, self.ask, self.current_ts_ns, self.step
)
}
}
impl<I, const D: u8> MarketState<I, D>
where
I: Mon<D>,
{
#[inline]
pub(crate) fn update_state<U, BaseOrQuote>(
&mut self,
market_update: &U,
price_filter: &PriceFilter<I, D>,
) where
U: MarketUpdate<I, D, BaseOrQuote>,
BaseOrQuote: Currency<I, D>,
{
assert2::debug_assert!(market_update.validate_market_update(price_filter).is_ok());
market_update.update_market_state(self);
self.current_ts_ns = market_update.timestamp_exchange_ns();
self.step += 1;
}
#[inline(always)]
pub fn mid_price(&self) -> QuoteCurrency<I, D> {
(self.bid + self.ask) / Decimal::TWO
}
#[inline(always)]
pub fn current_timestamp_ns(&self) -> TimestampNs {
self.current_ts_ns
}
#[allow(missing_docs)]
#[cfg(test)]
pub fn from_components(
bid: QuoteCurrency<I, D>,
ask: QuoteCurrency<I, D>,
last_trade_price: QuoteCurrency<I, D>,
current_ts_ns: TimestampNs,
step: u64,
) -> Self {
Self {
bid,
ask,
last_trade_price,
current_ts_ns,
step,
}
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::{
prelude::Bba,
types::BaseCurrency,
};
#[test]
fn market_state_display() {
let state = MarketState::<i64, 1>::default();
assert_eq!(
&state.to_string(),
"MarketState( bid: 0.0 Quote, ask: 0.0 Quote, ts_ns: 0, step: 0 )"
);
}
#[test]
fn market_state_mid_price() {
let mut state = MarketState::<i64, 1>::default();
let pf = PriceFilter::default();
state.update_state::<_, BaseCurrency<_, 1>>(
&Bba {
bid: QuoteCurrency::<i64, 1>::new(100, 0),
ask: QuoteCurrency::new(101, 0),
timestamp_exchange_ns: 1.into(),
},
&pf,
);
assert_eq!(state.mid_price(), QuoteCurrency::new(1005, 1));
}
}