Skip to main content

rustrade_data/subscription/
book.rs

1use super::SubscriptionKind;
2use crate::books::{Level, OrderBook, mid_price, volume_weighted_mid_price};
3use chrono::{DateTime, Utc};
4use derive_more::Constructor;
5use rust_decimal::Decimal;
6use rustrade_macro::{DeSubKind, SerSubKind};
7use serde::{Deserialize, Serialize};
8
9/// Barter [`Subscription`](super::Subscription) [`SubscriptionKind`] that yields [`OrderBookL1`]
10/// market events.
11///
12/// Level 1 refers to the best non-aggregated bid and ask [`Level`] on each side of the
13/// [`OrderBook`].
14#[derive(
15    Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default, DeSubKind, SerSubKind,
16)]
17pub struct OrderBooksL1;
18
19impl SubscriptionKind for OrderBooksL1 {
20    type Event = OrderBookL1;
21    fn as_str(&self) -> &'static str {
22        "l1"
23    }
24}
25
26impl std::fmt::Display for OrderBooksL1 {
27    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28        write!(f, "{}", self.as_str())
29    }
30}
31
32/// Normalised Barter [`OrderBookL1`] snapshot containing the latest best bid and ask.
33#[derive(
34    Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Default, Deserialize, Serialize, Constructor,
35)]
36pub struct OrderBookL1 {
37    /// The instant this book state describes — the venue's own, where the venue publishes one.
38    ///
39    /// # Producer obligation
40    ///
41    /// A producer **must** set this to the same instant it puts in the wrapping
42    /// [`MarketEvent::time_exchange`](crate::event::MarketEvent::time_exchange), and it **should**
43    /// come from the venue rather than from a local or aggregator clock.
44    ///
45    /// Downstream state orders L1 updates on this field, not on the event's, so the two must not
46    /// disagree. A producer that lets them diverge gets no error and no log: a staleness guard keyed
47    /// on this field can reject legitimately newer updates and freeze the held book.
48    ///
49    /// # Not every in-tree producer has a venue instant to use
50    ///
51    /// The "should" above is not a "must", because some venues publish no book timestamp:
52    ///
53    /// - **Binance Spot** `bookTicker` carries `{u, s, b, B, a, A}` and no time field, so
54    ///   [`BinanceOrderBookL1`](crate::exchange::binance::book::l1::BinanceOrderBookL1) falls back
55    ///   to the host clock at deserialisation. (Binance USD-M futures *does* send `T`, and that path
56    ///   uses it.)
57    /// - **IBKR** quotes are assembled from separate bid/ask/size ticks, which carry no per-tick
58    ///   venue instant, so `exchange::ibkr` (behind the `ibkr` feature, hence no link) stamps the
59    ///   assembly time.
60    ///
61    /// Both fill the field from a local clock, and both keep it consistent with `time_exchange` as
62    /// required above. Consumers must therefore **not** rank this field against a *tick* timestamp
63    /// from another source — a trade's `time_exchange`, say — since the gap between them is
64    /// milliseconds and the comparison would be decided by skew rather than by the market. Ranking
65    /// it against a coarser instant, such as a candle's `close_time`, is sound while the gap stays
66    /// well above the skew. `DefaultInstrumentMarketData::price` in the `rustrade` crate does
67    /// exactly that, and documents where the margin runs out.
68    pub last_update_time: DateTime<Utc>,
69    pub best_bid: Option<Level>,
70    pub best_ask: Option<Level>,
71}
72
73impl OrderBookL1 {
74    /// Calculate the mid-price by taking the average of the best bid and ask prices.
75    ///
76    /// See Docs: <https://www.quantstart.com/articles/high-frequency-trading-ii-limit-order-book>
77    pub fn mid_price(&self) -> Option<Decimal> {
78        match (self.best_ask, self.best_bid) {
79            (Some(best_ask), Some(best_bid)) => Some(mid_price(best_bid.price, best_ask.price)),
80            _ => None,
81        }
82    }
83
84    /// Calculate the volume weighted mid-price (micro-price), weighing the best bid and ask prices
85    /// with their associated amount.
86    ///
87    /// `None` when either side is absent, and when both sides carry a **zero amount** — a feed that
88    /// publishes prices without sizes produces exactly that book, and the weighting is undefined for
89    /// it. Fall back to [`mid_price`](Self::mid_price) when a price is needed regardless of size.
90    ///
91    /// See Docs: <https://www.quantstart.com/articles/high-frequency-trading-ii-limit-order-book>
92    pub fn volume_weighed_mid_price(&self) -> Option<Decimal> {
93        match (self.best_ask, self.best_bid) {
94            (Some(best_ask), Some(best_bid)) => volume_weighted_mid_price(best_bid, best_ask),
95            _ => None,
96        }
97    }
98}
99
100/// Barter [`Subscription`](super::Subscription) [`SubscriptionKind`] that yields L2
101/// [`OrderBookEvent`] market events
102///
103/// Level 2 refers to an [`OrderBook`] with orders at each price level aggregated.
104#[derive(
105    Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default, DeSubKind, SerSubKind,
106)]
107pub struct OrderBooksL2;
108
109impl SubscriptionKind for OrderBooksL2 {
110    type Event = OrderBookEvent;
111    fn as_str(&self) -> &'static str {
112        "l2"
113    }
114}
115
116impl std::fmt::Display for OrderBooksL2 {
117    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
118        write!(f, "{}", self.as_str())
119    }
120}
121
122/// Barter [`Subscription`](super::Subscription) [`SubscriptionKind`] that yields
123/// L3 [`OrderBookEvent`] market events.
124///
125/// Level 3 refers to the non-aggregated [`OrderBook`]. This is a direct replication of the exchange
126/// [`OrderBook`].
127#[derive(
128    Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default, DeSubKind, SerSubKind,
129)]
130pub struct OrderBooksL3;
131
132impl SubscriptionKind for OrderBooksL3 {
133    type Event = OrderBookEvent;
134
135    fn as_str(&self) -> &'static str {
136        "l3"
137    }
138}
139
140impl std::fmt::Display for OrderBooksL3 {
141    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
142        write!(f, "{}", self.as_str())
143    }
144}
145
146#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize)]
147pub enum OrderBookEvent {
148    Snapshot(OrderBook),
149    Update(OrderBook),
150}