use super::SubscriptionKind;
use rust_decimal::Decimal;
use rustrade_macro::{DeSubKind, SerSubKind};
use serde::{Deserialize, Serialize};
const BPS_FACTOR: Decimal = Decimal::from_parts(10_000, 0, 0, false, 0);
#[derive(
Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default, DeSubKind, SerSubKind,
)]
pub struct Quotes;
impl SubscriptionKind for Quotes {
type Event = Quote;
fn as_str(&self) -> &'static str {
"quotes"
}
}
impl std::fmt::Display for Quotes {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.as_str())
}
}
#[derive(Copy, Clone, PartialEq, PartialOrd, Debug, Deserialize, Serialize)]
pub struct Quote {
pub bid_price: Decimal,
pub bid_amount: Decimal,
pub ask_price: Decimal,
pub ask_amount: Decimal,
}
impl Quote {
pub fn mid_price(&self) -> Decimal {
(self.bid_price + self.ask_price) / Decimal::TWO
}
pub fn spread(&self) -> Decimal {
self.ask_price - self.bid_price
}
pub fn spread_bps(&self) -> Decimal {
let mid = self.mid_price();
if mid.is_zero() {
Decimal::ZERO
} else {
(self.spread() / mid) * BPS_FACTOR
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use rust_decimal_macros::dec;
#[test]
fn test_quote_mid_price() {
let quote = Quote {
bid_price: dec!(100),
bid_amount: dec!(10),
ask_price: dec!(102),
ask_amount: dec!(5),
};
assert_eq!(quote.mid_price(), dec!(101));
}
#[test]
fn test_quote_spread() {
let quote = Quote {
bid_price: dec!(100),
bid_amount: dec!(10),
ask_price: dec!(102),
ask_amount: dec!(5),
};
assert_eq!(quote.spread(), dec!(2));
}
#[test]
fn test_quote_spread_bps() {
let quote = Quote {
bid_price: dec!(100),
bid_amount: dec!(10),
ask_price: dec!(101),
ask_amount: dec!(5),
};
let bps = quote.spread_bps();
assert!(bps > dec!(99) && bps < dec!(100), "spread_bps={bps}");
}
}