rustkernel_orderbook/
matching.rs

1//! Order matching engine kernel.
2//!
3//! This module provides a high-performance order matching engine:
4//! - Price-time priority matching
5//! - Support for limit and market orders
6//! - Self-trade prevention
7//! - Order book management
8
9use std::collections::HashMap;
10use std::time::Instant;
11
12use async_trait::async_trait;
13
14use crate::messages::{
15    BatchOrderInput, BatchOrderOutput, CancelOrderInput, CancelOrderOutput, GetSnapshotInput,
16    GetSnapshotOutput, ModifyOrderInput, ModifyOrderOutput, SubmitOrderInput, SubmitOrderOutput,
17};
18use crate::types::{
19    EngineConfig, L2Snapshot, MatchResult, Order, OrderBook, OrderStatus, OrderType, Price,
20    PriceLevel, Quantity, Side, TimeInForce, Trade,
21};
22use rustkernel_core::{
23    domain::Domain,
24    error::Result as KernelResult,
25    kernel::KernelMetadata,
26    traits::{BatchKernel, GpuKernel},
27};
28
29// ============================================================================
30// Order Matching Engine Kernel
31// ============================================================================
32
33/// Order matching engine kernel.
34///
35/// High-performance price-time priority matching engine with sub-10μs latency.
36#[derive(Debug)]
37pub struct OrderMatchingEngine {
38    metadata: KernelMetadata,
39    /// Order books by symbol.
40    books: HashMap<u32, OrderBook>,
41    /// All orders by ID.
42    orders: HashMap<u64, Order>,
43    /// Next trade ID.
44    next_trade_id: u64,
45    /// Engine configuration.
46    config: EngineConfig,
47}
48
49impl Default for OrderMatchingEngine {
50    fn default() -> Self {
51        Self::new()
52    }
53}
54
55impl OrderMatchingEngine {
56    /// Create a new order matching engine.
57    #[must_use]
58    pub fn new() -> Self {
59        Self::with_config(EngineConfig::default())
60    }
61
62    /// Create with custom configuration.
63    #[must_use]
64    pub fn with_config(config: EngineConfig) -> Self {
65        Self {
66            metadata: KernelMetadata::ring("orderbook/matching", Domain::OrderMatching)
67                .with_description("Price-time priority order matching")
68                .with_throughput(100_000)
69                .with_latency_us(10.0)
70                .with_gpu_native(true),
71            books: HashMap::new(),
72            orders: HashMap::new(),
73            next_trade_id: 1,
74            config,
75        }
76    }
77
78    /// Submit a new order.
79    pub fn submit_order(&mut self, mut order: Order) -> MatchResult {
80        // Validate order
81        if let Err(status) = self.validate_order(&order) {
82            order.status = status;
83            let mut result = MatchResult::no_match(order.id, order.remaining);
84            result.status = status;
85            self.orders.insert(order.id, order);
86            return result;
87        }
88
89        // Ensure order book exists
90        self.books
91            .entry(order.symbol_id)
92            .or_insert_with(|| OrderBook::new(order.symbol_id));
93
94        // Match the order
95        let mut result = self.match_order_internal(&mut order);
96
97        // Handle remaining quantity based on order type and TIF
98        if !order.is_filled() {
99            match (order.order_type, order.tif) {
100                (OrderType::Market, _) | (_, TimeInForce::IOC) => {
101                    // Cancel remaining
102                    order.status = if result.trades.is_empty() {
103                        OrderStatus::Canceled
104                    } else {
105                        OrderStatus::PartiallyFilled
106                    };
107                }
108                (_, TimeInForce::FOK) => {
109                    // Should have been fully filled or nothing
110                    if !result.trades.is_empty() {
111                        // This shouldn't happen with proper FOK handling
112                        order.status = OrderStatus::PartiallyFilled;
113                    } else {
114                        order.status = OrderStatus::Canceled;
115                    }
116                }
117                _ => {
118                    // Add to book
119                    self.add_to_book(&order);
120                    order.status = if result.trades.is_empty() {
121                        OrderStatus::New
122                    } else {
123                        OrderStatus::PartiallyFilled
124                    };
125                }
126            }
127        } else {
128            order.status = OrderStatus::Filled;
129        }
130
131        // Update result status to match order status
132        result.status = order.status;
133
134        // Store order
135        self.orders.insert(order.id, order);
136
137        result
138    }
139
140    /// Cancel an order.
141    pub fn cancel_order(&mut self, order_id: u64) -> Option<Order> {
142        // First check if order exists and is active
143        {
144            let order = self.orders.get(&order_id)?;
145            if !order.is_active() {
146                return None;
147            }
148        }
149
150        // Remove from book
151        self.remove_from_book(order_id);
152
153        // Update status
154        let order = self.orders.get_mut(&order_id)?;
155        order.status = OrderStatus::Canceled;
156        Some(order.clone())
157    }
158
159    /// Modify an order (cancel and replace).
160    pub fn modify_order(
161        &mut self,
162        order_id: u64,
163        new_price: Option<Price>,
164        new_quantity: Option<Quantity>,
165    ) -> Option<MatchResult> {
166        let old_order = self.orders.get(&order_id)?.clone();
167
168        if !old_order.is_active() {
169            return None;
170        }
171
172        // Cancel old order
173        self.cancel_order(order_id)?;
174
175        // Create new order with modifications
176        let new_order = Order {
177            id: order_id, // Reuse ID for simplicity
178            price: new_price.unwrap_or(old_order.price),
179            quantity: new_quantity.unwrap_or(old_order.remaining),
180            remaining: new_quantity.unwrap_or(old_order.remaining),
181            status: OrderStatus::New,
182            ..old_order
183        };
184
185        Some(self.submit_order(new_order))
186    }
187
188    /// Get order by ID.
189    pub fn get_order(&self, order_id: u64) -> Option<&Order> {
190        self.orders.get(&order_id)
191    }
192
193    /// Get order book snapshot.
194    pub fn get_snapshot(&self, symbol_id: u32, depth: usize) -> Option<L2Snapshot> {
195        let book = self.books.get(&symbol_id)?;
196        Some(L2Snapshot::from_book(
197            book,
198            depth,
199            std::time::SystemTime::now()
200                .duration_since(std::time::UNIX_EPOCH)
201                .unwrap()
202                .as_nanos() as u64,
203        ))
204    }
205
206    /// Get order book.
207    pub fn get_book(&self, symbol_id: u32) -> Option<&OrderBook> {
208        self.books.get(&symbol_id)
209    }
210
211    /// Validate an order.
212    fn validate_order(&self, order: &Order) -> Result<(), OrderStatus> {
213        // Check quantity bounds
214        if order.quantity.0 < self.config.min_order_size.0 {
215            return Err(OrderStatus::Rejected);
216        }
217        if order.quantity.0 > self.config.max_order_size.0 {
218            return Err(OrderStatus::Rejected);
219        }
220
221        // Check price tick size for limit orders
222        if order.order_type == OrderType::Limit
223            && self.config.tick_size.0 > 0
224            && order.price.0 % self.config.tick_size.0 != 0
225        {
226            return Err(OrderStatus::Rejected);
227        }
228
229        Ok(())
230    }
231
232    /// Match an order against the book.
233    fn match_order_internal(&mut self, order: &mut Order) -> MatchResult {
234        let book = self.books.get_mut(&order.symbol_id).unwrap();
235        let mut trades = Vec::new();
236
237        let opposite_side = match order.side {
238            Side::Buy => &mut book.asks,
239            Side::Sell => &mut book.bids,
240        };
241
242        // Collect prices to match against
243        let prices_to_match: Vec<Price> = match order.side {
244            Side::Buy => opposite_side.keys().copied().collect(),
245            Side::Sell => opposite_side.keys().rev().copied().collect(),
246        };
247
248        let mut levels_to_remove = Vec::new();
249
250        for price in prices_to_match {
251            // Check if order can match at this price
252            let can_match = match order.order_type {
253                OrderType::Market => true,
254                OrderType::Limit => match order.side {
255                    Side::Buy => price <= order.price,
256                    Side::Sell => price >= order.price,
257                },
258                _ => false,
259            };
260
261            if !can_match {
262                break;
263            }
264
265            // Get the price level
266            let level = match opposite_side.get_mut(&price) {
267                Some(l) => l,
268                None => continue,
269            };
270
271            // Match against orders at this level
272            let mut orders_to_remove = Vec::new();
273
274            for &resting_order_id in &level.orders {
275                if order.remaining.0 == 0 {
276                    break;
277                }
278
279                let resting_order = match self.orders.get_mut(&resting_order_id) {
280                    Some(o) => o,
281                    None => continue,
282                };
283
284                // Self-trade prevention
285                if self.config.self_trade_prevention && order.trader_id == resting_order.trader_id {
286                    continue;
287                }
288
289                // Calculate fill quantity
290                let fill_qty = Quantity(order.remaining.0.min(resting_order.remaining.0));
291
292                // Create trade
293                let trade = Trade {
294                    id: self.next_trade_id,
295                    symbol_id: order.symbol_id,
296                    buy_order_id: if order.side == Side::Buy {
297                        order.id
298                    } else {
299                        resting_order_id
300                    },
301                    sell_order_id: if order.side == Side::Sell {
302                        order.id
303                    } else {
304                        resting_order_id
305                    },
306                    price,
307                    quantity: fill_qty,
308                    timestamp: order.timestamp,
309                    aggressor: order.side,
310                };
311
312                self.next_trade_id += 1;
313                trades.push(trade);
314
315                // Update quantities
316                order.remaining.0 -= fill_qty.0;
317                resting_order.remaining.0 -= fill_qty.0;
318
319                // Update resting order status
320                if resting_order.remaining.0 == 0 {
321                    resting_order.status = OrderStatus::Filled;
322                    orders_to_remove.push(resting_order_id);
323                } else {
324                    resting_order.status = OrderStatus::PartiallyFilled;
325                }
326
327                // Update book volume
328                book.volume_24h.0 += fill_qty.0;
329                book.last_price = Some(price);
330            }
331
332            // Remove filled orders from level
333            for order_id in &orders_to_remove {
334                if let Some(resting) = self.orders.get(order_id) {
335                    level.remove_order(*order_id, resting.quantity);
336                }
337            }
338
339            // Mark empty levels for removal
340            if level.is_empty() {
341                levels_to_remove.push(price);
342            }
343
344            if order.remaining.0 == 0 {
345                break;
346            }
347        }
348
349        // Remove empty levels
350        for price in levels_to_remove {
351            opposite_side.remove(&price);
352        }
353
354        // Build result
355        if trades.is_empty() {
356            MatchResult::no_match(order.id, order.remaining)
357        } else if order.remaining.0 == 0 {
358            MatchResult::full_fill(order.id, trades)
359        } else {
360            MatchResult::partial_fill(order.id, trades, order.remaining)
361        }
362    }
363
364    /// Add order to book.
365    fn add_to_book(&mut self, order: &Order) {
366        let book = self
367            .books
368            .entry(order.symbol_id)
369            .or_insert_with(|| OrderBook::new(order.symbol_id));
370
371        let levels = match order.side {
372            Side::Buy => &mut book.bids,
373            Side::Sell => &mut book.asks,
374        };
375
376        let level = levels
377            .entry(order.price)
378            .or_insert_with(|| PriceLevel::new(order.price));
379
380        level.add_order(order.id, order.remaining);
381    }
382
383    /// Remove order from book.
384    fn remove_from_book(&mut self, order_id: u64) -> bool {
385        let order = match self.orders.get(&order_id) {
386            Some(o) => o.clone(),
387            None => return false,
388        };
389
390        let book = match self.books.get_mut(&order.symbol_id) {
391            Some(b) => b,
392            None => return false,
393        };
394
395        let levels = match order.side {
396            Side::Buy => &mut book.bids,
397            Side::Sell => &mut book.asks,
398        };
399
400        if let Some(level) = levels.get_mut(&order.price) {
401            level.remove_order(order_id, order.remaining);
402            if level.is_empty() {
403                levels.remove(&order.price);
404            }
405            true
406        } else {
407            false
408        }
409    }
410
411    /// Process a batch of orders.
412    pub fn process_batch(&mut self, orders: Vec<Order>) -> Vec<MatchResult> {
413        orders.into_iter().map(|o| self.submit_order(o)).collect()
414    }
415
416    /// Clear the engine (for testing).
417    pub fn clear(&mut self) {
418        self.books.clear();
419        self.orders.clear();
420        self.next_trade_id = 1;
421    }
422}
423
424impl Clone for OrderMatchingEngine {
425    fn clone(&self) -> Self {
426        Self {
427            metadata: self.metadata.clone(),
428            books: self.books.clone(),
429            orders: self.orders.clone(),
430            next_trade_id: self.next_trade_id,
431            config: self.config.clone(),
432        }
433    }
434}
435
436impl GpuKernel for OrderMatchingEngine {
437    fn metadata(&self) -> &KernelMetadata {
438        &self.metadata
439    }
440}
441
442#[async_trait]
443impl BatchKernel<SubmitOrderInput, SubmitOrderOutput> for OrderMatchingEngine {
444    async fn execute(&self, input: SubmitOrderInput) -> KernelResult<SubmitOrderOutput> {
445        let start = Instant::now();
446        // Note: This requires &mut self, but BatchKernel takes &self
447        // In practice, this would use interior mutability (Mutex/RwLock)
448        // For now, we clone and process
449        let mut engine = self.clone();
450        let result = engine.submit_order(input.order);
451        Ok(SubmitOrderOutput {
452            result,
453            compute_time_us: start.elapsed().as_micros() as u64,
454        })
455    }
456}
457
458#[async_trait]
459impl BatchKernel<BatchOrderInput, BatchOrderOutput> for OrderMatchingEngine {
460    async fn execute(&self, input: BatchOrderInput) -> KernelResult<BatchOrderOutput> {
461        let start = Instant::now();
462        let mut engine = self.clone();
463        let results = engine.process_batch(input.orders);
464        let total_trades: usize = results.iter().map(|r| r.trades.len()).sum();
465        Ok(BatchOrderOutput {
466            results,
467            total_trades,
468            compute_time_us: start.elapsed().as_micros() as u64,
469        })
470    }
471}
472
473#[async_trait]
474impl BatchKernel<CancelOrderInput, CancelOrderOutput> for OrderMatchingEngine {
475    async fn execute(&self, input: CancelOrderInput) -> KernelResult<CancelOrderOutput> {
476        let start = Instant::now();
477        let mut engine = self.clone();
478        let canceled_order = engine.cancel_order(input.order_id);
479        Ok(CancelOrderOutput {
480            success: canceled_order.is_some(),
481            canceled_order,
482            compute_time_us: start.elapsed().as_micros() as u64,
483        })
484    }
485}
486
487#[async_trait]
488impl BatchKernel<ModifyOrderInput, ModifyOrderOutput> for OrderMatchingEngine {
489    async fn execute(&self, input: ModifyOrderInput) -> KernelResult<ModifyOrderOutput> {
490        let start = Instant::now();
491        let mut engine = self.clone();
492        let result = engine.modify_order(input.order_id, input.new_price, input.new_quantity);
493        Ok(ModifyOrderOutput {
494            success: result.is_some(),
495            result,
496            compute_time_us: start.elapsed().as_micros() as u64,
497        })
498    }
499}
500
501#[async_trait]
502impl BatchKernel<GetSnapshotInput, GetSnapshotOutput> for OrderMatchingEngine {
503    async fn execute(&self, input: GetSnapshotInput) -> KernelResult<GetSnapshotOutput> {
504        let start = Instant::now();
505        let snapshot = self.get_snapshot(input.symbol_id, input.depth);
506        Ok(GetSnapshotOutput {
507            snapshot,
508            compute_time_us: start.elapsed().as_micros() as u64,
509        })
510    }
511}
512
513// ============================================================================
514// Ring Kernel Handler Implementation
515// ============================================================================
516
517use crate::ring_messages::{
518    CancelOrderResponse, CancelOrderRing, QueryBookResponse, QueryBookRing, RingOrderStatus,
519    RingString, SubmitOrderResponse, SubmitOrderRing,
520};
521use ringkernel_core::RingContext;
522use rustkernel_core::traits::RingKernelHandler;
523
524/// Ring kernel handler for ultra-low latency order submission.
525#[async_trait]
526impl RingKernelHandler<SubmitOrderRing, SubmitOrderResponse> for OrderMatchingEngine {
527    async fn handle(
528        &self,
529        _ctx: &mut RingContext,
530        msg: SubmitOrderRing,
531    ) -> KernelResult<SubmitOrderResponse> {
532        // Convert Ring message to domain Order
533        let order = msg.to_order();
534
535        // Clone self to get mutable access (in production, would use interior mutability)
536        let mut engine = self.clone();
537        let result = engine.submit_order(order);
538
539        // Convert result to Ring response
540        let status = match result.status {
541            OrderStatus::New => RingOrderStatus::New,
542            OrderStatus::PartiallyFilled => RingOrderStatus::PartiallyFilled,
543            OrderStatus::Filled => RingOrderStatus::Filled,
544            OrderStatus::Canceled => RingOrderStatus::Canceled,
545            OrderStatus::Rejected | OrderStatus::Expired => RingOrderStatus::Rejected,
546        };
547
548        Ok(SubmitOrderResponse {
549            correlation_id: msg.correlation_id,
550            order_id: msg.order_id,
551            status,
552            filled_quantity: result.filled_quantity.0,
553            avg_price: result.avg_price.0,
554            remaining: result.remaining.0,
555            trade_count: result.trades.len() as u32,
556            error: RingString::empty(),
557        })
558    }
559}
560
561/// Ring kernel handler for order cancellation.
562#[async_trait]
563impl RingKernelHandler<CancelOrderRing, CancelOrderResponse> for OrderMatchingEngine {
564    async fn handle(
565        &self,
566        _ctx: &mut RingContext,
567        msg: CancelOrderRing,
568    ) -> KernelResult<CancelOrderResponse> {
569        let mut engine = self.clone();
570        let canceled = engine.cancel_order(msg.order_id);
571
572        Ok(CancelOrderResponse {
573            correlation_id: msg.correlation_id,
574            order_id: msg.order_id,
575            success: canceled.is_some(),
576            remaining: canceled.map_or(0, |o| o.remaining.0),
577        })
578    }
579}
580
581/// Ring kernel handler for book queries.
582#[async_trait]
583impl RingKernelHandler<QueryBookRing, QueryBookResponse> for OrderMatchingEngine {
584    async fn handle(
585        &self,
586        _ctx: &mut RingContext,
587        msg: QueryBookRing,
588    ) -> KernelResult<QueryBookResponse> {
589        let book = self.get_book(msg.symbol_id);
590
591        let (best_bid, best_ask, bid_depth, ask_depth, spread, mid_price) = match book {
592            Some(b) => {
593                let (bid_d, ask_d) = b.depth_at_levels(msg.depth as usize);
594                (
595                    b.best_bid().map_or(0, |p| p.0),
596                    b.best_ask().map_or(0, |p| p.0),
597                    bid_d.0,
598                    ask_d.0,
599                    b.spread().map_or(0, |p| p.0),
600                    b.mid_price().map_or(0, |p| p.0),
601                )
602            }
603            None => (0, 0, 0, 0, 0, 0),
604        };
605
606        Ok(QueryBookResponse {
607            correlation_id: msg.correlation_id,
608            symbol_id: msg.symbol_id,
609            best_bid,
610            best_ask,
611            bid_depth,
612            ask_depth,
613            spread,
614            mid_price,
615        })
616    }
617}
618
619#[cfg(test)]
620mod tests {
621    use super::*;
622
623    fn create_engine() -> OrderMatchingEngine {
624        OrderMatchingEngine::new()
625    }
626
627    #[test]
628    fn test_engine_metadata() {
629        let engine = create_engine();
630        assert_eq!(engine.metadata().id, "orderbook/matching");
631        assert_eq!(engine.metadata().domain, Domain::OrderMatching);
632    }
633
634    #[test]
635    fn test_limit_order_submission() {
636        let mut engine = create_engine();
637
638        let order = Order::limit(
639            1,
640            100,
641            Side::Buy,
642            Price::from_f64(100.0),
643            Quantity::from_f64(10.0),
644            1000,
645            0,
646        );
647
648        let result = engine.submit_order(order);
649
650        assert_eq!(result.order_id, 1);
651        assert_eq!(result.status, OrderStatus::New);
652        assert!(result.trades.is_empty());
653    }
654
655    #[test]
656    fn test_market_order_no_liquidity() {
657        let mut engine = create_engine();
658
659        let order = Order::market(1, 100, Side::Buy, Quantity::from_f64(10.0), 1000, 0);
660
661        let result = engine.submit_order(order);
662
663        // Market order with no liquidity should be canceled
664        assert_eq!(result.status, OrderStatus::Canceled);
665        assert!(result.trades.is_empty());
666    }
667
668    #[test]
669    fn test_simple_match() {
670        let mut engine = create_engine();
671
672        // Add sell order
673        let sell = Order::limit(
674            1,
675            100,
676            Side::Sell,
677            Price::from_f64(100.0),
678            Quantity::from_f64(10.0),
679            1000,
680            0,
681        );
682        engine.submit_order(sell);
683
684        // Add matching buy order
685        let buy = Order::limit(
686            2,
687            100,
688            Side::Buy,
689            Price::from_f64(100.0),
690            Quantity::from_f64(10.0),
691            2000,
692            1,
693        );
694        let result = engine.submit_order(buy);
695
696        assert_eq!(result.status, OrderStatus::Filled);
697        assert_eq!(result.trades.len(), 1);
698        assert_eq!(result.filled_quantity.0, Quantity::from_f64(10.0).0);
699    }
700
701    #[test]
702    fn test_partial_fill() {
703        let mut engine = create_engine();
704
705        // Add sell order for 5 units
706        let sell = Order::limit(
707            1,
708            100,
709            Side::Sell,
710            Price::from_f64(100.0),
711            Quantity::from_f64(5.0),
712            1000,
713            0,
714        );
715        engine.submit_order(sell);
716
717        // Buy order for 10 units (partial fill)
718        let buy = Order::limit(
719            2,
720            100,
721            Side::Buy,
722            Price::from_f64(100.0),
723            Quantity::from_f64(10.0),
724            2000,
725            1,
726        );
727        let result = engine.submit_order(buy);
728
729        assert_eq!(result.status, OrderStatus::PartiallyFilled);
730        assert_eq!(result.trades.len(), 1);
731        assert_eq!(result.filled_quantity.0, Quantity::from_f64(5.0).0);
732        assert_eq!(result.remaining.0, Quantity::from_f64(5.0).0);
733    }
734
735    #[test]
736    fn test_price_priority() {
737        let mut engine = create_engine();
738
739        // Add two sell orders at different prices
740        let sell1 = Order::limit(
741            1,
742            100,
743            Side::Sell,
744            Price::from_f64(101.0),
745            Quantity::from_f64(10.0),
746            1000,
747            0,
748        );
749        engine.submit_order(sell1);
750
751        let sell2 = Order::limit(
752            2,
753            100,
754            Side::Sell,
755            Price::from_f64(100.0),
756            Quantity::from_f64(10.0),
757            1000,
758            1,
759        );
760        engine.submit_order(sell2);
761
762        // Buy should match against better price (100) first
763        let buy = Order::limit(
764            3,
765            100,
766            Side::Buy,
767            Price::from_f64(101.0),
768            Quantity::from_f64(10.0),
769            2000,
770            2,
771        );
772        let result = engine.submit_order(buy);
773
774        assert_eq!(result.status, OrderStatus::Filled);
775        assert_eq!(result.trades[0].price.to_f64(), 100.0);
776        assert_eq!(result.trades[0].sell_order_id, 2); // Matched sell2
777    }
778
779    #[test]
780    fn test_time_priority() {
781        let mut engine = create_engine();
782
783        // Add two sell orders at same price
784        let sell1 = Order::limit(
785            1,
786            100,
787            Side::Sell,
788            Price::from_f64(100.0),
789            Quantity::from_f64(5.0),
790            1000,
791            0, // Earlier timestamp
792        );
793        engine.submit_order(sell1);
794
795        let sell2 = Order::limit(
796            2,
797            100,
798            Side::Sell,
799            Price::from_f64(100.0),
800            Quantity::from_f64(5.0),
801            1000,
802            1, // Later timestamp
803        );
804        engine.submit_order(sell2);
805
806        // Buy should match against earlier order first
807        let buy = Order::limit(
808            3,
809            100,
810            Side::Buy,
811            Price::from_f64(100.0),
812            Quantity::from_f64(5.0),
813            2000,
814            2,
815        );
816        let result = engine.submit_order(buy);
817
818        assert_eq!(result.trades[0].sell_order_id, 1); // Matched sell1 (earlier)
819    }
820
821    #[test]
822    fn test_cancel_order() {
823        let mut engine = create_engine();
824
825        let order = Order::limit(
826            1,
827            100,
828            Side::Buy,
829            Price::from_f64(100.0),
830            Quantity::from_f64(10.0),
831            1000,
832            0,
833        );
834        engine.submit_order(order);
835
836        let canceled = engine.cancel_order(1);
837
838        assert!(canceled.is_some());
839        assert_eq!(canceled.unwrap().status, OrderStatus::Canceled);
840    }
841
842    #[test]
843    fn test_order_book_snapshot() {
844        let mut engine = create_engine();
845
846        // Add buy orders
847        for i in 0..5 {
848            let order = Order::limit(
849                i,
850                100,
851                Side::Buy,
852                Price::from_f64(100.0 - i as f64),
853                Quantity::from_f64(10.0),
854                1000,
855                i,
856            );
857            engine.submit_order(order);
858        }
859
860        // Add sell orders
861        for i in 5..10 {
862            let order = Order::limit(
863                i,
864                100,
865                Side::Sell,
866                Price::from_f64(101.0 + (i - 5) as f64),
867                Quantity::from_f64(10.0),
868                1000,
869                i,
870            );
871            engine.submit_order(order);
872        }
873
874        let snapshot = engine.get_snapshot(100, 3).unwrap();
875
876        assert_eq!(snapshot.bids.len(), 3);
877        assert_eq!(snapshot.asks.len(), 3);
878
879        // Best bid should be 100.0
880        assert_eq!(snapshot.bids[0].0.to_f64(), 100.0);
881        // Best ask should be 101.0
882        assert_eq!(snapshot.asks[0].0.to_f64(), 101.0);
883    }
884
885    #[test]
886    fn test_self_trade_prevention() {
887        let mut engine = create_engine();
888
889        // Add sell order
890        let sell = Order::limit(
891            1,
892            100,
893            Side::Sell,
894            Price::from_f64(100.0),
895            Quantity::from_f64(10.0),
896            1000, // Trader 1000
897            0,
898        );
899        engine.submit_order(sell);
900
901        // Same trader tries to buy
902        let buy = Order::limit(
903            2,
904            100,
905            Side::Buy,
906            Price::from_f64(100.0),
907            Quantity::from_f64(10.0),
908            1000, // Same trader
909            1,
910        );
911        let result = engine.submit_order(buy);
912
913        // Should not match due to self-trade prevention
914        assert!(result.trades.is_empty());
915    }
916
917    #[test]
918    fn test_market_order_fill() {
919        let mut engine = create_engine();
920
921        // Add liquidity
922        let sell = Order::limit(
923            1,
924            100,
925            Side::Sell,
926            Price::from_f64(100.0),
927            Quantity::from_f64(10.0),
928            1000,
929            0,
930        );
931        engine.submit_order(sell);
932
933        // Market buy
934        let buy = Order::market(2, 100, Side::Buy, Quantity::from_f64(5.0), 2000, 1);
935        let result = engine.submit_order(buy);
936
937        assert_eq!(result.status, OrderStatus::Filled);
938        assert_eq!(result.trades.len(), 1);
939    }
940
941    #[test]
942    fn test_book_depth() {
943        let mut engine = create_engine();
944
945        // Add buy orders
946        let buy = Order::limit(
947            1,
948            100,
949            Side::Buy,
950            Price::from_f64(100.0),
951            Quantity::from_f64(10.0),
952            1000,
953            0,
954        );
955        engine.submit_order(buy);
956
957        // Add sell orders
958        let sell = Order::limit(
959            2,
960            100,
961            Side::Sell,
962            Price::from_f64(101.0),
963            Quantity::from_f64(20.0),
964            1000,
965            1,
966        );
967        engine.submit_order(sell);
968
969        let book = engine.get_book(100).unwrap();
970
971        assert_eq!(book.best_bid().unwrap().to_f64(), 100.0);
972        assert_eq!(book.best_ask().unwrap().to_f64(), 101.0);
973        assert_eq!(book.bid_depth().to_f64(), 10.0);
974        assert_eq!(book.ask_depth().to_f64(), 20.0);
975    }
976
977    #[test]
978    fn test_modify_order() {
979        let mut engine = create_engine();
980
981        // Add order
982        let order = Order::limit(
983            1,
984            100,
985            Side::Buy,
986            Price::from_f64(100.0),
987            Quantity::from_f64(10.0),
988            1000,
989            0,
990        );
991        engine.submit_order(order);
992
993        // Modify price
994        let result = engine.modify_order(1, Some(Price::from_f64(99.0)), None);
995
996        assert!(result.is_some());
997        let order = engine.get_order(1).unwrap();
998        assert_eq!(order.price.to_f64(), 99.0);
999    }
1000
1001    #[test]
1002    fn test_batch_processing() {
1003        let mut engine = create_engine();
1004
1005        let orders = vec![
1006            Order::limit(
1007                1,
1008                100,
1009                Side::Sell,
1010                Price::from_f64(100.0),
1011                Quantity::from_f64(10.0),
1012                1000,
1013                0,
1014            ),
1015            Order::limit(
1016                2,
1017                100,
1018                Side::Buy,
1019                Price::from_f64(100.0),
1020                Quantity::from_f64(5.0),
1021                2000,
1022                1,
1023            ),
1024            Order::limit(
1025                3,
1026                100,
1027                Side::Buy,
1028                Price::from_f64(100.0),
1029                Quantity::from_f64(5.0),
1030                2000,
1031                2,
1032            ),
1033        ];
1034
1035        let results = engine.process_batch(orders);
1036
1037        assert_eq!(results.len(), 3);
1038        // First order rests
1039        assert_eq!(results[0].status, OrderStatus::New);
1040        // Second order fills
1041        assert_eq!(results[1].status, OrderStatus::Filled);
1042        // Third order fills remaining
1043        assert_eq!(results[2].status, OrderStatus::Filled);
1044    }
1045
1046    #[test]
1047    fn test_multiple_symbols() {
1048        let mut engine = create_engine();
1049
1050        // Symbol 100
1051        let order1 = Order::limit(
1052            1,
1053            100,
1054            Side::Buy,
1055            Price::from_f64(100.0),
1056            Quantity::from_f64(10.0),
1057            1000,
1058            0,
1059        );
1060        engine.submit_order(order1);
1061
1062        // Symbol 200
1063        let order2 = Order::limit(
1064            2,
1065            200,
1066            Side::Sell,
1067            Price::from_f64(50.0),
1068            Quantity::from_f64(20.0),
1069            1000,
1070            1,
1071        );
1072        engine.submit_order(order2);
1073
1074        assert!(engine.get_book(100).is_some());
1075        assert!(engine.get_book(200).is_some());
1076        assert!(engine.get_book(300).is_none());
1077    }
1078
1079    #[test]
1080    fn test_order_validation() {
1081        let mut engine = create_engine();
1082
1083        // Order too small
1084        let small_order = Order::limit(
1085            1,
1086            100,
1087            Side::Buy,
1088            Price::from_f64(100.0),
1089            Quantity(1), // Below minimum
1090            1000,
1091            0,
1092        );
1093        let result = engine.submit_order(small_order);
1094        assert_eq!(result.status, OrderStatus::Rejected);
1095
1096        // Order too large
1097        let large_order = Order::limit(
1098            2,
1099            100,
1100            Side::Buy,
1101            Price::from_f64(100.0),
1102            Quantity::from_f64(10_000_000.0), // Above maximum
1103            1000,
1104            1,
1105        );
1106        let result = engine.submit_order(large_order);
1107        assert_eq!(result.status, OrderStatus::Rejected);
1108    }
1109}