Skip to main content

polyoxide_clob/ws/
market.rs

1//! Market channel message types.
2//!
3//! The market channel provides real-time order book and price updates.
4
5use rust_decimal::Decimal;
6use serde::{Deserialize, Serialize};
7
8/// Order summary in the order book
9#[derive(Debug, Clone, Serialize, Deserialize)]
10pub struct OrderSummary {
11    /// Price level
12    #[serde(with = "rust_decimal::serde::str")]
13    pub price: Decimal,
14    /// Size at this price level
15    #[serde(with = "rust_decimal::serde::str")]
16    pub size: Decimal,
17}
18
19/// Book message - full order book snapshot
20#[derive(Debug, Clone, Serialize, Deserialize)]
21pub struct BookMessage {
22    /// Event type (always "book")
23    pub event_type: String,
24    /// Asset ID (token ID)
25    pub asset_id: String,
26    /// Market condition ID
27    pub market: String,
28    /// Timestamp in milliseconds (as string)
29    pub timestamp: String,
30    /// Order book hash
31    pub hash: String,
32    /// Buy orders (bids)
33    pub bids: Vec<OrderSummary>,
34    /// Sell orders (asks)
35    pub asks: Vec<OrderSummary>,
36    /// Last trade price
37    pub last_trade_price: Option<String>,
38}
39
40/// Price change entry
41#[derive(Debug, Clone, Serialize, Deserialize)]
42pub struct PriceChange {
43    /// Asset ID (token ID)
44    pub asset_id: String,
45    /// Price level
46    #[serde(with = "rust_decimal::serde::str")]
47    pub price: Decimal,
48    /// Size at this price level
49    #[serde(with = "rust_decimal::serde::str")]
50    pub size: Decimal,
51    /// Order side (BUY or SELL)
52    pub side: String,
53    /// Order book hash
54    pub hash: String,
55    /// Best bid price
56    #[serde(default, with = "rust_decimal::serde::str_option")]
57    pub best_bid: Option<Decimal>,
58    /// Best ask price
59    #[serde(default, with = "rust_decimal::serde::str_option")]
60    pub best_ask: Option<Decimal>,
61}
62
63/// Price change message - incremental order book update
64#[derive(Debug, Clone, Serialize, Deserialize)]
65pub struct PriceChangeMessage {
66    /// Event type (always "price_change")
67    pub event_type: String,
68    /// Market condition ID
69    pub market: String,
70    /// List of price changes
71    pub price_changes: Vec<PriceChange>,
72    /// Timestamp in milliseconds (as string)
73    pub timestamp: String,
74}
75
76/// Tick size change message
77#[derive(Debug, Clone, Serialize, Deserialize)]
78pub struct TickSizeChangeMessage {
79    /// Event type (always "tick_size_change")
80    pub event_type: String,
81    /// Asset ID (token ID)
82    pub asset_id: String,
83    /// Market condition ID
84    pub market: String,
85    /// Old tick size
86    pub old_tick_size: String,
87    /// New tick size
88    pub new_tick_size: String,
89    /// Side (BUY or SELL)
90    pub side: String,
91    /// Timestamp in milliseconds (as string)
92    pub timestamp: String,
93}
94
95/// Last trade price message
96#[derive(Debug, Clone, Serialize, Deserialize)]
97pub struct LastTradePriceMessage {
98    /// Event type (always "last_trade_price")
99    pub event_type: String,
100    /// Asset ID (token ID)
101    pub asset_id: String,
102    /// Market condition ID
103    pub market: String,
104    /// Trade price
105    #[serde(with = "rust_decimal::serde::str")]
106    pub price: Decimal,
107    /// Trade side (BUY or SELL)
108    pub side: String,
109    /// Trade size
110    #[serde(with = "rust_decimal::serde::str")]
111    pub size: Decimal,
112    /// Fee rate
113    pub fee_rate_bps: Option<String>,
114    /// Timestamp in milliseconds (as string)
115    pub timestamp: String,
116}
117
118/// Market channel message types
119#[derive(Debug, Clone, Serialize)]
120#[serde(untagged)]
121pub enum MarketMessage {
122    /// Full order book snapshot
123    Book(BookMessage),
124    /// Incremental price change
125    PriceChange(PriceChangeMessage),
126    /// Tick size change
127    TickSizeChange(TickSizeChangeMessage),
128    /// Last trade price
129    LastTradePrice(LastTradePriceMessage),
130}
131
132impl MarketMessage {
133    /// Parse a market channel message from JSON
134    pub fn from_json(json: &str) -> Result<Self, serde_json::Error> {
135        // Book messages come as an array with a single element
136        if json.starts_with('[') {
137            let books: Vec<BookMessage> = serde_json::from_str(json)?;
138            if let Some(book) = books.into_iter().next() {
139                return Ok(MarketMessage::Book(book));
140            }
141            return Err(serde::de::Error::custom("Empty book array"));
142        }
143
144        #[derive(Deserialize)]
145        struct RawMessage {
146            event_type: String,
147        }
148
149        let raw: RawMessage = serde_json::from_str(json)?;
150        match raw.event_type.as_str() {
151            "book" => Ok(MarketMessage::Book(serde_json::from_str(json)?)),
152            "price_change" => Ok(MarketMessage::PriceChange(serde_json::from_str(json)?)),
153            "tick_size_change" => Ok(MarketMessage::TickSizeChange(serde_json::from_str(json)?)),
154            "last_trade_price" => Ok(MarketMessage::LastTradePrice(serde_json::from_str(json)?)),
155            _ => Err(serde::de::Error::custom(format!(
156                "Unknown market event type: {}",
157                raw.event_type
158            ))),
159        }
160    }
161}