rustrade_data/subscription/trade.rs
1use super::SubscriptionKind;
2use rust_decimal::Decimal;
3use rustrade_instrument::Side;
4use rustrade_macro::{DeSubKind, SerSubKind};
5use serde::{Deserialize, Serialize};
6use smol_str::SmolStr;
7
8/// Barter [`Subscription`](super::Subscription) [`SubscriptionKind`] that yields [`PublicTrade`]
9/// [`MarketEvent<T>`](crate::event::MarketEvent) events.
10#[derive(
11 Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default, DeSubKind, SerSubKind,
12)]
13pub struct PublicTrades;
14
15impl SubscriptionKind for PublicTrades {
16 type Event = PublicTrade;
17
18 fn as_str(&self) -> &'static str {
19 "public_trades"
20 }
21}
22
23impl std::fmt::Display for PublicTrades {
24 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
25 write!(f, "{}", self.as_str())
26 }
27}
28
29/// Normalised Barter [`PublicTrade`] model.
30///
31/// Uses [`SmolStr`] for `id` to avoid heap allocation for typical trade IDs
32/// (up to 23 bytes on 64-bit systems are stored inline). Exceptions that
33/// heap-allocate: Bitmex UUIDs (36 bytes), Kraken composite IDs (~34 bytes).
34///
35/// # Side Field
36///
37/// The `side` field indicates the taker/aggressor side of the trade:
38/// - `Some(Side::Buy)`: Taker was buying (lifted the ask)
39/// - `Some(Side::Sell)`: Taker was selling (hit the bid)
40/// - `None`: Side information not available from the data source
41///
42/// Crypto exchanges typically provide side info. Equities feeds (e.g., Alpaca IEX/SIP)
43/// often do not, as consolidated tape data doesn't include aggressor information.
44#[derive(Clone, PartialEq, PartialOrd, Debug, Deserialize, Serialize)]
45pub struct PublicTrade {
46 pub id: SmolStr,
47 pub price: Decimal,
48 pub amount: Decimal,
49 pub side: Option<Side>,
50}