rustkernel_orderbook/
ring_messages.rs

1//! Ring message types for Order Matching Engine.
2//!
3//! These messages implement the `RingMessage` trait for GPU-native persistent
4//! actor communication with ultra-low latency (<10μs P99).
5//!
6//! The OrderMatchingEngine is a Tier 1 critical-path kernel that benefits from
7//! Ring mode for:
8//! - Sub-microsecond message serialization via rkyv
9//! - GPU-resident state for order book maintenance
10//! - K2K messaging for cross-symbol coordination
11
12use ringkernel_core::message::{CorrelationId, MessageId};
13use ringkernel_derive::RingMessage;
14use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize};
15
16use crate::types::{Order, Price, Quantity, Side};
17
18// ============================================================================
19// Ring Message Type IDs (OrderMatching domain: 500-599)
20// ============================================================================
21//
22// Type ID assignments within OrderMatching domain:
23// - 0-9: Order submission messages
24// - 10-19: Batch order messages
25// - 20-29: Cancel/modify messages
26// - 30-39: Query messages
27// - 40-49: K2K coordination messages
28// - 50+: Reserved
29
30// ============================================================================
31// Submit Order Ring Messages
32// ============================================================================
33
34/// Ring message for submitting a single order.
35///
36/// This is the primary message type for ultra-low latency order submission.
37/// Type ID: 500 (OrderMatching.base + 0)
38#[derive(Debug, Clone, Archive, RkyvSerialize, RkyvDeserialize, RingMessage)]
39#[message(type_id = 500)]
40#[archive(check_bytes)]
41pub struct SubmitOrderRing {
42    /// Message ID for tracking.
43    #[message(id)]
44    pub id: MessageId,
45    /// Correlation ID for request-response pairing.
46    #[message(correlation)]
47    pub correlation_id: CorrelationId,
48    /// Order ID.
49    pub order_id: u64,
50    /// Symbol ID.
51    pub symbol_id: u32,
52    /// Order side.
53    pub side: RingSide,
54    /// Order type.
55    pub order_type: RingOrderType,
56    /// Price (fixed-point, 0 for market orders).
57    pub price: i64,
58    /// Quantity (fixed-point).
59    pub quantity: u64,
60    /// Trader ID.
61    pub trader_id: u64,
62    /// Timestamp (nanoseconds).
63    pub timestamp: u64,
64}
65
66impl SubmitOrderRing {
67    /// Create a new limit order submission message.
68    pub fn limit(
69        order_id: u64,
70        symbol_id: u32,
71        side: Side,
72        price: Price,
73        quantity: Quantity,
74        trader_id: u64,
75        timestamp: u64,
76    ) -> Self {
77        Self {
78            id: MessageId::generate(),
79            correlation_id: CorrelationId::generate(),
80            order_id,
81            symbol_id,
82            side: side.into(),
83            order_type: RingOrderType::Limit,
84            price: price.0,
85            quantity: quantity.0,
86            trader_id,
87            timestamp,
88        }
89    }
90
91    /// Create a new market order submission message.
92    pub fn market(
93        order_id: u64,
94        symbol_id: u32,
95        side: Side,
96        quantity: Quantity,
97        trader_id: u64,
98        timestamp: u64,
99    ) -> Self {
100        Self {
101            id: MessageId::generate(),
102            correlation_id: CorrelationId::generate(),
103            order_id,
104            symbol_id,
105            side: side.into(),
106            order_type: RingOrderType::Market,
107            price: 0,
108            quantity: quantity.0,
109            trader_id,
110            timestamp,
111        }
112    }
113
114    /// Convert to domain Order type.
115    pub fn to_order(&self) -> Order {
116        match self.order_type {
117            RingOrderType::Limit => Order::limit(
118                self.order_id,
119                self.symbol_id,
120                self.side.into(),
121                Price(self.price),
122                Quantity(self.quantity),
123                self.trader_id,
124                self.timestamp,
125            ),
126            RingOrderType::Market => Order::market(
127                self.order_id,
128                self.symbol_id,
129                self.side.into(),
130                Quantity(self.quantity),
131                self.trader_id,
132                self.timestamp,
133            ),
134        }
135    }
136}
137
138/// Ring message response for order submission.
139///
140/// Type ID: 501 (OrderMatching.base + 1)
141#[derive(Debug, Clone, RingMessage, Archive, RkyvSerialize, RkyvDeserialize)]
142#[message(type_id = 501)]
143#[archive(check_bytes)]
144pub struct SubmitOrderResponse {
145    /// Correlation ID matching the request.
146    #[message(correlation)]
147    pub correlation_id: CorrelationId,
148    /// Order ID.
149    pub order_id: u64,
150    /// Result status.
151    pub status: RingOrderStatus,
152    /// Filled quantity.
153    pub filled_quantity: u64,
154    /// Average fill price (fixed-point).
155    pub avg_price: i64,
156    /// Remaining quantity.
157    pub remaining: u64,
158    /// Number of trades generated.
159    pub trade_count: u32,
160    /// Error message (empty if success).
161    pub error: RingString,
162}
163
164// ============================================================================
165// Cancel Order Ring Messages
166// ============================================================================
167
168/// Ring message for canceling an order.
169///
170/// Type ID: 520 (OrderMatching.base + 20)
171#[derive(Debug, Clone, RingMessage, Archive, RkyvSerialize, RkyvDeserialize)]
172#[message(type_id = 520)]
173#[archive(check_bytes)]
174pub struct CancelOrderRing {
175    /// Message ID.
176    #[message(id)]
177    pub id: MessageId,
178    /// Correlation ID.
179    #[message(correlation)]
180    pub correlation_id: CorrelationId,
181    /// Order ID to cancel.
182    pub order_id: u64,
183}
184
185impl CancelOrderRing {
186    /// Create a new cancel order message.
187    pub fn new(order_id: u64) -> Self {
188        Self {
189            id: MessageId::generate(),
190            correlation_id: CorrelationId::generate(),
191            order_id,
192        }
193    }
194}
195
196/// Ring message response for order cancellation.
197///
198/// Type ID: 521 (OrderMatching.base + 21)
199#[derive(Debug, Clone, RingMessage, Archive, RkyvSerialize, RkyvDeserialize)]
200#[message(type_id = 521)]
201#[archive(check_bytes)]
202pub struct CancelOrderResponse {
203    /// Correlation ID.
204    #[message(correlation)]
205    pub correlation_id: CorrelationId,
206    /// Order ID.
207    pub order_id: u64,
208    /// Whether cancellation succeeded.
209    pub success: bool,
210    /// Remaining quantity at cancellation.
211    pub remaining: u64,
212}
213
214// ============================================================================
215// Query Ring Messages
216// ============================================================================
217
218/// Ring message for querying order book state.
219///
220/// Type ID: 530 (OrderMatching.base + 30)
221#[derive(Debug, Clone, RingMessage, Archive, RkyvSerialize, RkyvDeserialize)]
222#[message(type_id = 530)]
223#[archive(check_bytes)]
224pub struct QueryBookRing {
225    /// Message ID.
226    #[message(id)]
227    pub id: MessageId,
228    /// Correlation ID.
229    #[message(correlation)]
230    pub correlation_id: CorrelationId,
231    /// Symbol ID.
232    pub symbol_id: u32,
233    /// Depth (number of price levels).
234    pub depth: u32,
235}
236
237impl QueryBookRing {
238    /// Create a new book query message.
239    pub fn new(symbol_id: u32, depth: u32) -> Self {
240        Self {
241            id: MessageId::generate(),
242            correlation_id: CorrelationId::generate(),
243            symbol_id,
244            depth,
245        }
246    }
247}
248
249/// Ring message response for book query.
250///
251/// Type ID: 531 (OrderMatching.base + 31)
252#[derive(Debug, Clone, RingMessage, Archive, RkyvSerialize, RkyvDeserialize)]
253#[message(type_id = 531)]
254#[archive(check_bytes)]
255pub struct QueryBookResponse {
256    /// Correlation ID.
257    #[message(correlation)]
258    pub correlation_id: CorrelationId,
259    /// Symbol ID.
260    pub symbol_id: u32,
261    /// Best bid price.
262    pub best_bid: i64,
263    /// Best ask price.
264    pub best_ask: i64,
265    /// Bid depth at requested levels.
266    pub bid_depth: u64,
267    /// Ask depth at requested levels.
268    pub ask_depth: u64,
269    /// Spread.
270    pub spread: i64,
271    /// Mid price.
272    pub mid_price: i64,
273}
274
275// ============================================================================
276// K2K Coordination Messages (for cross-symbol matching)
277// ============================================================================
278
279/// K2K message for cross-symbol trade coordination.
280///
281/// Type ID: 540 (OrderMatching.base + 40)
282#[derive(Debug, Clone, RingMessage, Archive, RkyvSerialize, RkyvDeserialize)]
283#[message(type_id = 540)]
284#[archive(check_bytes)]
285pub struct CrossSymbolTrade {
286    /// Message ID.
287    #[message(id)]
288    pub id: MessageId,
289    /// Correlation ID.
290    #[message(correlation)]
291    pub correlation_id: CorrelationId,
292    /// Source symbol ID.
293    pub source_symbol: u32,
294    /// Target symbol ID.
295    pub target_symbol: u32,
296    /// Trade quantity.
297    pub quantity: u64,
298    /// Execution price.
299    pub price: i64,
300    /// Timestamp.
301    pub timestamp: u64,
302}
303
304// ============================================================================
305// Ring-Compatible Types (rkyv-serializable)
306// ============================================================================
307
308/// Ring-compatible order side.
309#[derive(Debug, Clone, Copy, PartialEq, Eq, Archive, RkyvSerialize, RkyvDeserialize)]
310#[archive(check_bytes)]
311#[repr(u8)]
312pub enum RingSide {
313    /// Buy order.
314    Buy = 0,
315    /// Sell order.
316    Sell = 1,
317}
318
319impl From<Side> for RingSide {
320    fn from(side: Side) -> Self {
321        match side {
322            Side::Buy => RingSide::Buy,
323            Side::Sell => RingSide::Sell,
324        }
325    }
326}
327
328impl From<RingSide> for Side {
329    fn from(side: RingSide) -> Self {
330        match side {
331            RingSide::Buy => Side::Buy,
332            RingSide::Sell => Side::Sell,
333        }
334    }
335}
336
337/// Ring-compatible order type.
338#[derive(Debug, Clone, Copy, PartialEq, Eq, Archive, RkyvSerialize, RkyvDeserialize)]
339#[archive(check_bytes)]
340#[repr(u8)]
341pub enum RingOrderType {
342    /// Limit order.
343    Limit = 0,
344    /// Market order.
345    Market = 1,
346}
347
348/// Ring-compatible order status.
349#[derive(Debug, Clone, Copy, PartialEq, Eq, Archive, RkyvSerialize, RkyvDeserialize)]
350#[archive(check_bytes)]
351#[repr(u8)]
352pub enum RingOrderStatus {
353    /// New order accepted.
354    New = 0,
355    /// Partially filled.
356    PartiallyFilled = 1,
357    /// Fully filled.
358    Filled = 2,
359    /// Canceled.
360    Canceled = 3,
361    /// Rejected.
362    Rejected = 4,
363}
364
365/// Fixed-size string for Ring messages (64 bytes).
366#[derive(Debug, Clone, Archive, RkyvSerialize, RkyvDeserialize)]
367#[archive(check_bytes)]
368pub struct RingString {
369    /// String data (null-terminated).
370    pub data: [u8; 64],
371    /// Actual length.
372    pub len: u8,
373}
374
375impl RingString {
376    /// Create an empty string.
377    pub fn empty() -> Self {
378        Self {
379            data: [0; 64],
380            len: 0,
381        }
382    }
383
384    /// Create from a string slice.
385    pub fn from_str(s: &str) -> Self {
386        let bytes = s.as_bytes();
387        let len = bytes.len().min(63) as u8;
388        let mut data = [0u8; 64];
389        data[..len as usize].copy_from_slice(&bytes[..len as usize]);
390        Self { data, len }
391    }
392
393    /// Convert to string.
394    pub fn as_str(&self) -> &str {
395        std::str::from_utf8(&self.data[..self.len as usize]).unwrap_or("")
396    }
397}
398
399impl Default for RingString {
400    fn default() -> Self {
401        Self::empty()
402    }
403}
404
405#[cfg(test)]
406mod tests {
407    use super::*;
408
409    #[test]
410    fn test_submit_order_ring() {
411        let msg = SubmitOrderRing::limit(
412            1,
413            100,
414            Side::Buy,
415            Price::from_f64(100.0),
416            Quantity::from_f64(10.0),
417            1000,
418            0,
419        );
420        assert_eq!(msg.order_id, 1);
421        assert_eq!(msg.symbol_id, 100);
422        assert_eq!(msg.side, RingSide::Buy);
423    }
424
425    #[test]
426    fn test_ring_string() {
427        let s = RingString::from_str("Hello, World!");
428        assert_eq!(s.as_str(), "Hello, World!");
429        assert_eq!(s.len, 13);
430    }
431
432    #[test]
433    fn test_ring_string_truncation() {
434        let long = "a".repeat(100);
435        let s = RingString::from_str(&long);
436        assert_eq!(s.len, 63);
437    }
438}