rustkernel_orderbook/
types.rs

1//! Order book types and data structures.
2
3use std::cmp::Ordering;
4use std::collections::BTreeMap;
5
6use serde::{Deserialize, Serialize};
7
8// ============================================================================
9// Order Types
10// ============================================================================
11
12/// A trading order.
13#[derive(Debug, Clone, Serialize, Deserialize)]
14pub struct Order {
15    /// Unique order ID.
16    pub id: u64,
17    /// Symbol/instrument ID.
18    pub symbol_id: u32,
19    /// Order side (buy/sell).
20    pub side: Side,
21    /// Order type.
22    pub order_type: OrderType,
23    /// Order price (for limit orders).
24    pub price: Price,
25    /// Order quantity.
26    pub quantity: Quantity,
27    /// Remaining quantity.
28    pub remaining: Quantity,
29    /// Time-in-force.
30    pub tif: TimeInForce,
31    /// Timestamp (nanoseconds since epoch).
32    pub timestamp: u64,
33    /// Trader/account ID.
34    pub trader_id: u64,
35    /// Order status.
36    pub status: OrderStatus,
37}
38
39impl Order {
40    /// Create a new limit order.
41    pub fn limit(
42        id: u64,
43        symbol_id: u32,
44        side: Side,
45        price: Price,
46        quantity: Quantity,
47        trader_id: u64,
48        timestamp: u64,
49    ) -> Self {
50        Self {
51            id,
52            symbol_id,
53            side,
54            order_type: OrderType::Limit,
55            price,
56            quantity,
57            remaining: quantity,
58            tif: TimeInForce::GTC,
59            timestamp,
60            trader_id,
61            status: OrderStatus::New,
62        }
63    }
64
65    /// Create a new market order.
66    pub fn market(
67        id: u64,
68        symbol_id: u32,
69        side: Side,
70        quantity: Quantity,
71        trader_id: u64,
72        timestamp: u64,
73    ) -> Self {
74        Self {
75            id,
76            symbol_id,
77            side,
78            order_type: OrderType::Market,
79            price: Price(0), // Market orders don't have a price
80            quantity,
81            remaining: quantity,
82            tif: TimeInForce::IOC,
83            timestamp,
84            trader_id,
85            status: OrderStatus::New,
86        }
87    }
88
89    /// Check if order is filled.
90    pub fn is_filled(&self) -> bool {
91        self.remaining.0 == 0
92    }
93
94    /// Check if order is active.
95    pub fn is_active(&self) -> bool {
96        matches!(self.status, OrderStatus::New | OrderStatus::PartiallyFilled)
97    }
98}
99
100/// Order side.
101#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
102pub enum Side {
103    /// Buy order.
104    Buy,
105    /// Sell order.
106    Sell,
107}
108
109/// Order type.
110#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
111pub enum OrderType {
112    /// Limit order (price specified).
113    Limit,
114    /// Market order (best available price).
115    Market,
116    /// Stop order.
117    Stop,
118    /// Stop-limit order.
119    StopLimit,
120}
121
122/// Time-in-force specification.
123#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
124pub enum TimeInForce {
125    /// Good-till-canceled.
126    GTC,
127    /// Immediate-or-cancel.
128    IOC,
129    /// Fill-or-kill.
130    FOK,
131    /// Day order.
132    Day,
133}
134
135/// Order status.
136#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
137pub enum OrderStatus {
138    /// New order.
139    New,
140    /// Partially filled.
141    PartiallyFilled,
142    /// Fully filled.
143    Filled,
144    /// Canceled.
145    Canceled,
146    /// Rejected.
147    Rejected,
148    /// Expired.
149    Expired,
150}
151
152// ============================================================================
153// Price and Quantity Types
154// ============================================================================
155
156/// Price in fixed-point representation (price * 10^8).
157#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
158pub struct Price(pub i64);
159
160impl Price {
161    /// Create price from float.
162    pub fn from_f64(price: f64) -> Self {
163        Self((price * 100_000_000.0) as i64)
164    }
165
166    /// Convert to float.
167    pub fn to_f64(self) -> f64 {
168        self.0 as f64 / 100_000_000.0
169    }
170}
171
172impl Ord for Price {
173    fn cmp(&self, other: &Self) -> Ordering {
174        self.0.cmp(&other.0)
175    }
176}
177
178impl PartialOrd for Price {
179    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
180        Some(self.cmp(other))
181    }
182}
183
184/// Quantity in fixed-point representation (quantity * 10^8).
185#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
186pub struct Quantity(pub u64);
187
188impl Quantity {
189    /// Create quantity from float.
190    pub fn from_f64(qty: f64) -> Self {
191        Self((qty * 100_000_000.0) as u64)
192    }
193
194    /// Convert to float.
195    pub fn to_f64(self) -> f64 {
196        self.0 as f64 / 100_000_000.0
197    }
198}
199
200// ============================================================================
201// Order Book
202// ============================================================================
203
204/// Price level in the order book.
205#[derive(Debug, Clone, Serialize, Deserialize)]
206pub struct PriceLevel {
207    /// Price at this level.
208    pub price: Price,
209    /// Orders at this level (in time priority).
210    pub orders: Vec<u64>, // Order IDs
211    /// Total quantity at this level.
212    pub total_quantity: Quantity,
213    /// Number of orders.
214    pub order_count: u32,
215}
216
217impl PriceLevel {
218    /// Create a new price level.
219    pub fn new(price: Price) -> Self {
220        Self {
221            price,
222            orders: Vec::new(),
223            total_quantity: Quantity(0),
224            order_count: 0,
225        }
226    }
227
228    /// Add an order to this level.
229    pub fn add_order(&mut self, order_id: u64, quantity: Quantity) {
230        self.orders.push(order_id);
231        self.total_quantity.0 += quantity.0;
232        self.order_count += 1;
233    }
234
235    /// Remove an order from this level.
236    pub fn remove_order(&mut self, order_id: u64, quantity: Quantity) -> bool {
237        if let Some(pos) = self.orders.iter().position(|&id| id == order_id) {
238            self.orders.remove(pos);
239            self.total_quantity.0 = self.total_quantity.0.saturating_sub(quantity.0);
240            self.order_count = self.order_count.saturating_sub(1);
241            true
242        } else {
243            false
244        }
245    }
246
247    /// Check if level is empty.
248    pub fn is_empty(&self) -> bool {
249        self.orders.is_empty()
250    }
251}
252
253/// Order book for a single symbol.
254#[derive(Debug, Clone, Serialize, Deserialize)]
255pub struct OrderBook {
256    /// Symbol ID.
257    pub symbol_id: u32,
258    /// Bid levels (price -> level), sorted descending by price.
259    pub bids: BTreeMap<Price, PriceLevel>,
260    /// Ask levels (price -> level), sorted ascending by price.
261    pub asks: BTreeMap<Price, PriceLevel>,
262    /// Last trade price.
263    pub last_price: Option<Price>,
264    /// 24h volume.
265    pub volume_24h: Quantity,
266}
267
268impl OrderBook {
269    /// Create a new order book.
270    pub fn new(symbol_id: u32) -> Self {
271        Self {
272            symbol_id,
273            bids: BTreeMap::new(),
274            asks: BTreeMap::new(),
275            last_price: None,
276            volume_24h: Quantity(0),
277        }
278    }
279
280    /// Get best bid price.
281    pub fn best_bid(&self) -> Option<Price> {
282        self.bids.keys().next_back().copied()
283    }
284
285    /// Get best ask price.
286    pub fn best_ask(&self) -> Option<Price> {
287        self.asks.keys().next().copied()
288    }
289
290    /// Get spread.
291    pub fn spread(&self) -> Option<Price> {
292        match (self.best_ask(), self.best_bid()) {
293            (Some(ask), Some(bid)) => Some(Price(ask.0 - bid.0)),
294            _ => None,
295        }
296    }
297
298    /// Get mid price.
299    pub fn mid_price(&self) -> Option<Price> {
300        match (self.best_ask(), self.best_bid()) {
301            (Some(ask), Some(bid)) => Some(Price((ask.0 + bid.0) / 2)),
302            _ => None,
303        }
304    }
305
306    /// Get total bid depth.
307    pub fn bid_depth(&self) -> Quantity {
308        Quantity(self.bids.values().map(|l| l.total_quantity.0).sum())
309    }
310
311    /// Get total ask depth.
312    pub fn ask_depth(&self) -> Quantity {
313        Quantity(self.asks.values().map(|l| l.total_quantity.0).sum())
314    }
315
316    /// Get depth at N levels.
317    pub fn depth_at_levels(&self, n: usize) -> (Quantity, Quantity) {
318        let bid_depth: u64 = self
319            .bids
320            .values()
321            .rev()
322            .take(n)
323            .map(|l| l.total_quantity.0)
324            .sum();
325        let ask_depth: u64 = self.asks.values().take(n).map(|l| l.total_quantity.0).sum();
326        (Quantity(bid_depth), Quantity(ask_depth))
327    }
328}
329
330// ============================================================================
331// Trade/Execution Types
332// ============================================================================
333
334/// A trade execution.
335#[derive(Debug, Clone, Serialize, Deserialize)]
336pub struct Trade {
337    /// Trade ID.
338    pub id: u64,
339    /// Symbol ID.
340    pub symbol_id: u32,
341    /// Buy order ID.
342    pub buy_order_id: u64,
343    /// Sell order ID.
344    pub sell_order_id: u64,
345    /// Execution price.
346    pub price: Price,
347    /// Execution quantity.
348    pub quantity: Quantity,
349    /// Timestamp.
350    pub timestamp: u64,
351    /// Aggressor side (which order initiated the trade).
352    pub aggressor: Side,
353}
354
355/// Matching result for an order.
356#[derive(Debug, Clone, Serialize, Deserialize)]
357pub struct MatchResult {
358    /// Original order ID.
359    pub order_id: u64,
360    /// Order status after matching.
361    pub status: OrderStatus,
362    /// Filled quantity.
363    pub filled_quantity: Quantity,
364    /// Average fill price.
365    pub avg_price: Price,
366    /// Generated trades.
367    pub trades: Vec<Trade>,
368    /// Remaining quantity (if not fully filled).
369    pub remaining: Quantity,
370}
371
372impl MatchResult {
373    /// Create result for no matches.
374    pub fn no_match(order_id: u64, quantity: Quantity) -> Self {
375        Self {
376            order_id,
377            status: OrderStatus::New,
378            filled_quantity: Quantity(0),
379            avg_price: Price(0),
380            trades: Vec::new(),
381            remaining: quantity,
382        }
383    }
384
385    /// Create result for full fill.
386    pub fn full_fill(order_id: u64, trades: Vec<Trade>) -> Self {
387        let filled: u64 = trades.iter().map(|t| t.quantity.0).sum();
388        // Calculate weighted average price using f64 to avoid overflow
389        let avg_price = if filled > 0 {
390            let total_value: f64 = trades
391                .iter()
392                .map(|t| t.price.to_f64() * t.quantity.to_f64())
393                .sum();
394            let total_qty: f64 = trades.iter().map(|t| t.quantity.to_f64()).sum();
395            Price::from_f64(total_value / total_qty)
396        } else {
397            Price(0)
398        };
399
400        Self {
401            order_id,
402            status: OrderStatus::Filled,
403            filled_quantity: Quantity(filled),
404            avg_price,
405            trades,
406            remaining: Quantity(0),
407        }
408    }
409
410    /// Create result for partial fill.
411    pub fn partial_fill(order_id: u64, trades: Vec<Trade>, remaining: Quantity) -> Self {
412        let filled: u64 = trades.iter().map(|t| t.quantity.0).sum();
413        // Calculate weighted average price using f64 to avoid overflow
414        let avg_price = if filled > 0 {
415            let total_value: f64 = trades
416                .iter()
417                .map(|t| t.price.to_f64() * t.quantity.to_f64())
418                .sum();
419            let total_qty: f64 = trades.iter().map(|t| t.quantity.to_f64()).sum();
420            Price::from_f64(total_value / total_qty)
421        } else {
422            Price(0)
423        };
424
425        Self {
426            order_id,
427            status: OrderStatus::PartiallyFilled,
428            filled_quantity: Quantity(filled),
429            avg_price,
430            trades,
431            remaining,
432        }
433    }
434}
435
436// ============================================================================
437// Order Book Snapshot
438// ============================================================================
439
440/// Level 2 market data snapshot.
441#[derive(Debug, Clone, Serialize, Deserialize)]
442pub struct L2Snapshot {
443    /// Symbol ID.
444    pub symbol_id: u32,
445    /// Bid levels (price, quantity).
446    pub bids: Vec<(Price, Quantity)>,
447    /// Ask levels (price, quantity).
448    pub asks: Vec<(Price, Quantity)>,
449    /// Timestamp.
450    pub timestamp: u64,
451}
452
453impl L2Snapshot {
454    /// Create from order book.
455    pub fn from_book(book: &OrderBook, depth: usize, timestamp: u64) -> Self {
456        let bids: Vec<_> = book
457            .bids
458            .iter()
459            .rev()
460            .take(depth)
461            .map(|(&price, level)| (price, level.total_quantity))
462            .collect();
463
464        let asks: Vec<_> = book
465            .asks
466            .iter()
467            .take(depth)
468            .map(|(&price, level)| (price, level.total_quantity))
469            .collect();
470
471        Self {
472            symbol_id: book.symbol_id,
473            bids,
474            asks,
475            timestamp,
476        }
477    }
478}
479
480// ============================================================================
481// Engine Configuration
482// ============================================================================
483
484/// Order matching engine configuration.
485#[derive(Debug, Clone, Serialize, Deserialize)]
486pub struct EngineConfig {
487    /// Maximum order size.
488    pub max_order_size: Quantity,
489    /// Minimum order size.
490    pub min_order_size: Quantity,
491    /// Price tick size.
492    pub tick_size: Price,
493    /// Maximum price levels in book.
494    pub max_price_levels: usize,
495    /// Enable self-trade prevention.
496    pub self_trade_prevention: bool,
497}
498
499impl Default for EngineConfig {
500    fn default() -> Self {
501        Self {
502            max_order_size: Quantity::from_f64(1_000_000.0),
503            min_order_size: Quantity::from_f64(0.001),
504            tick_size: Price::from_f64(0.01),
505            max_price_levels: 1000,
506            self_trade_prevention: true,
507        }
508    }
509}