rusty_model/
matching_engine.rs

1//! Matching Engine for order matching and execution
2//!
3//! This module provides a high-performance matching engine implementation
4//! for matching buy and sell orders based on price-time priority.
5//! Should be partial-fill compatible because crypto exchanges support partial fills.
6
7use crate::data::{
8    book_snapshot::OrderBookSnapshot, orderbook::OrderBook, simd_orderbook::SharedSimdOrderBook,
9};
10use crate::enums::OrderSide;
11use crate::instruments::InstrumentId;
12use crate::trading_order::{Order, OrderStatus, OrderType};
13use crate::types::{ClientId, OrderId};
14use quanta::Clock;
15use rust_decimal::Decimal;
16use rusty_common::collections::FxHashMap;
17use smallvec::SmallVec;
18use smartstring::alias::String;
19use uuid::Uuid;
20
21/// Represents a limit order in the matching engine
22#[derive(Debug, Clone)]
23#[repr(align(16))] // Align for better memory access
24pub struct LimitOrder {
25    /// Unique order ID
26    pub id: OrderId,
27
28    /// Client-provided order ID (if any)
29    pub client_order_id: Option<ClientId>,
30
31    /// Order side (buy or sell)
32    pub side: OrderSide,
33
34    /// Limit price
35    pub price: Decimal,
36
37    /// Original order quantity
38    pub original_quantity: Decimal,
39
40    /// Remaining quantity
41    pub remaining_quantity: Decimal,
42
43    /// Order creation time in nanoseconds
44    pub creation_time_ns: u64,
45}
46
47impl LimitOrder {
48    /// Create a new limit order
49    #[must_use]
50    pub fn new(
51        id: OrderId,
52        client_order_id: Option<ClientId>,
53        side: OrderSide,
54        price: Decimal,
55        quantity: Decimal,
56    ) -> Self {
57        let clock = Clock::new();
58        let now = clock.raw();
59
60        Self {
61            id,
62            client_order_id,
63            side,
64            price,
65            original_quantity: quantity,
66            remaining_quantity: quantity,
67            creation_time_ns: now,
68        }
69    }
70
71    /// Check if the order is fully filled
72    #[must_use]
73    pub const fn is_filled(&self) -> bool {
74        self.remaining_quantity.is_zero()
75    }
76}
77
78/// Represents a trade resulting from order matching
79#[derive(Debug, Clone)]
80pub struct ExecutedTrade {
81    /// Unique trade ID
82    pub id: Uuid,
83
84    /// Instrument ID
85    pub instrument_id: InstrumentId,
86
87    /// Maker order ID
88    pub maker_order_id: OrderId,
89
90    /// Taker order ID
91    pub taker_order_id: OrderId,
92
93    /// Trade price
94    pub price: Decimal,
95
96    /// Trade quantity
97    pub quantity: Decimal,
98
99    /// Trade side (from taker's perspective)
100    pub side: OrderSide,
101
102    /// Trade timestamp in nanoseconds
103    pub timestamp_ns: u64,
104}
105
106/// Represents the result of order processing
107#[derive(Debug, Clone)]
108pub struct OrderResult {
109    /// Original order
110    pub order: Order,
111
112    /// Resulting trades (if any)
113    /// Most orders result in only a few trades, so we optimize for small collections
114    pub trades: SmallVec<[ExecutedTrade; 4]>,
115
116    /// Error message (if any)
117    pub error: Option<String>,
118}
119
120/// High-performance matching engine for order matching and execution
121#[derive(Debug)]
122pub struct MatchingEngine {
123    /// Order books by instrument ID
124    order_books: FxHashMap<InstrumentId, SharedSimdOrderBook>,
125
126    /// Buy limit orders by instrument ID
127    buy_orders: FxHashMap<InstrumentId, FxHashMap<OrderId, LimitOrder>>,
128
129    /// Sell limit orders by instrument ID
130    sell_orders: FxHashMap<InstrumentId, FxHashMap<OrderId, LimitOrder>>,
131
132    /// Shared clock instance for time synchronization
133    clock: Clock,
134}
135
136impl Default for MatchingEngine {
137    fn default() -> Self {
138        Self::new()
139    }
140}
141
142impl MatchingEngine {
143    /// Create a new matching engine
144    #[must_use]
145    pub fn new() -> Self {
146        Self {
147            order_books: FxHashMap::default(),
148            buy_orders: FxHashMap::default(),
149            sell_orders: FxHashMap::default(),
150            clock: Clock::new(),
151        }
152    }
153
154    /// Add a new instrument to the matching engine
155    pub fn add_instrument(&mut self, instrument_id: InstrumentId) {
156        let order_book: OrderBook<64> = OrderBook::new_empty(instrument_id.clone());
157        let shared_book = SharedSimdOrderBook::from_orderbook(&order_book);
158
159        self.order_books.insert(instrument_id.clone(), shared_book);
160        self.buy_orders
161            .insert(instrument_id.clone(), FxHashMap::default());
162        self.sell_orders.insert(instrument_id, FxHashMap::default());
163    }
164
165    /// Process a new order
166    pub fn process_order(&mut self, order: Order) -> OrderResult {
167        match order.order_type {
168            OrderType::Market => self.process_market_order(order),
169            OrderType::Limit => self.process_limit_order(order),
170            _ => OrderResult {
171                order: order.clone(),
172                trades: SmallVec::with_capacity(4),
173                error: Some(String::from(format!(
174                    "Unsupported order type: {:?}",
175                    order.order_type
176                ))),
177            },
178        }
179    }
180
181    /// Process a market order
182    fn process_market_order(&mut self, mut order: Order) -> OrderResult {
183        let instrument_id = InstrumentId::new(order.symbol.clone(), order.venue);
184
185        // Check if the instrument exists
186        if !self.order_books.contains_key(&instrument_id) {
187            return OrderResult {
188                order,
189                trades: SmallVec::with_capacity(4),
190                error: Some(String::from(format!(
191                    "Instrument not found: {instrument_id:?}"
192                ))),
193            };
194        }
195
196        let mut trades = SmallVec::with_capacity(8);
197        let mut remaining_quantity = order.quantity;
198
199        // Structure to hold orders that need to be updated and their new quantities
200        // Most matching operations update only a few orders
201        let mut orders_to_update: SmallVec<[(OrderId, OrderSide, Decimal); 8]> =
202            SmallVec::with_capacity(8);
203
204        // Match against the opposite side of the order book
205        match order.side {
206            OrderSide::Buy => {
207                // Get the order book
208                let order_book = self
209                    .order_books
210                    .get(&instrument_id)
211                    .expect("Order book should exist after contains_key check");
212
213                // Get sell orders for reading
214                let sell_orders = self
215                    .sell_orders
216                    .get(&instrument_id)
217                    .expect("Sell orders should exist after contains_key check");
218
219                // Match against the order book
220                order_book.read(|book| {
221                    // Get the ask levels
222                    let asks = book.asks();
223
224                    // Match against each ask level
225                    for ask_level in asks {
226                        if remaining_quantity.is_zero() {
227                            break;
228                        }
229
230                        // Find all sell orders at this price level
231                        // Typically few orders at the exact same price
232                        let mut orders_at_price: SmallVec<[_; 8]> = sell_orders
233                            .values()
234                            .filter(|o| o.price == ask_level.price && !o.is_filled())
235                            .collect();
236
237                        // Sort by creation time (oldest first)
238                        orders_at_price.sort_by_key(|o| o.creation_time_ns);
239
240                        // Match against each order
241                        for sell_order in orders_at_price {
242                            if remaining_quantity.is_zero() {
243                                break;
244                            }
245
246                            // Calculate match quantity
247                            let match_quantity =
248                                std::cmp::min(remaining_quantity, sell_order.remaining_quantity);
249
250                            // Create a trade
251                            let trade = ExecutedTrade {
252                                id: Uuid::new_v4(),
253                                instrument_id: instrument_id.clone(),
254                                maker_order_id: sell_order.id,
255                                taker_order_id: order.id,
256                                price: sell_order.price,
257                                quantity: match_quantity,
258                                side: OrderSide::Buy,
259                                timestamp_ns: self.clock.raw(),
260                            };
261
262                            trades.push(trade);
263
264                            // Update remaining quantities
265                            remaining_quantity -= match_quantity;
266
267                            // Store the order to update
268                            let new_remaining = sell_order.remaining_quantity - match_quantity;
269                            orders_to_update.push((sell_order.id, OrderSide::Sell, new_remaining));
270                        }
271                    }
272                });
273
274                // Now update the sell orders
275                let sell_orders = self
276                    .sell_orders
277                    .get_mut(&instrument_id)
278                    .expect("Sell orders should exist after contains_key check");
279                for (order_id, _, new_quantity) in orders_to_update {
280                    if let Some(order) = sell_orders.get_mut(&order_id) {
281                        order.remaining_quantity = new_quantity;
282                    }
283                }
284            }
285            OrderSide::Sell => {
286                // Get the order book
287                let order_book = self
288                    .order_books
289                    .get(&instrument_id)
290                    .expect("Order book should exist after contains_key check");
291
292                // Get buy orders for reading
293                let buy_orders = self
294                    .buy_orders
295                    .get(&instrument_id)
296                    .expect("Buy orders should exist after contains_key check");
297
298                // Match against the order book
299                order_book.read(|book| {
300                    // Get the bid levels
301                    let bids = book.bids();
302
303                    // Match against each bid level
304                    for bid_level in bids {
305                        if remaining_quantity.is_zero() {
306                            break;
307                        }
308
309                        // Find all buy orders at this price level
310                        // Typically few orders at the exact same price
311                        let mut orders_at_price: SmallVec<[_; 8]> = buy_orders
312                            .values()
313                            .filter(|o| o.price == bid_level.price && !o.is_filled())
314                            .collect();
315
316                        // Sort by creation time (oldest first)
317                        orders_at_price.sort_by_key(|o| o.creation_time_ns);
318
319                        // Match against each order
320                        for buy_order in orders_at_price {
321                            if remaining_quantity.is_zero() {
322                                break;
323                            }
324
325                            // Calculate match quantity
326                            let match_quantity =
327                                std::cmp::min(remaining_quantity, buy_order.remaining_quantity);
328
329                            // Create a trade
330                            let trade = ExecutedTrade {
331                                id: Uuid::new_v4(),
332                                instrument_id: instrument_id.clone(),
333                                maker_order_id: buy_order.id,
334                                taker_order_id: order.id,
335                                price: buy_order.price,
336                                quantity: match_quantity,
337                                side: OrderSide::Sell,
338                                timestamp_ns: self.clock.raw(),
339                            };
340
341                            trades.push(trade);
342
343                            // Update remaining quantities
344                            remaining_quantity -= match_quantity;
345
346                            // Store the order to update
347                            let new_remaining = buy_order.remaining_quantity - match_quantity;
348                            orders_to_update.push((buy_order.id, OrderSide::Buy, new_remaining));
349                        }
350                    }
351                });
352
353                // Now update the buy orders
354                let buy_orders = self
355                    .buy_orders
356                    .get_mut(&instrument_id)
357                    .expect("Buy orders should exist after contains_key check");
358                for (order_id, _, new_quantity) in orders_to_update {
359                    if let Some(order) = buy_orders.get_mut(&order_id) {
360                        order.remaining_quantity = new_quantity;
361                    }
362                }
363            }
364        }
365
366        // Update the order
367        order.filled_quantity = order.quantity - remaining_quantity;
368
369        if order.filled_quantity.is_zero() {
370            order.status = OrderStatus::Rejected;
371        } else if order.filled_quantity < order.quantity {
372            order.status = OrderStatus::PartiallyFilled;
373        } else {
374            order.status = OrderStatus::Filled;
375        }
376
377        // Calculate average fill price
378        if !trades.is_empty() {
379            let total_value = trades
380                .iter()
381                .fold(Decimal::ZERO, |acc, trade: &ExecutedTrade| {
382                    acc + trade.price * trade.quantity
383                });
384            let total_quantity = trades
385                .iter()
386                .fold(Decimal::ZERO, |acc, trade: &ExecutedTrade| {
387                    acc + trade.quantity
388                });
389            order.average_fill_price = Some(total_value / total_quantity);
390        }
391
392        // Update the order book
393        self.update_order_book(&instrument_id);
394
395        OrderResult {
396            order,
397            trades,
398            error: None,
399        }
400    }
401
402    /// Process a limit order
403    fn process_limit_order(&mut self, mut order: Order) -> OrderResult {
404        let instrument_id = InstrumentId::new(order.symbol.clone(), order.venue);
405
406        // Check if the instrument exists
407        if !self.order_books.contains_key(&instrument_id) {
408            return OrderResult {
409                order,
410                trades: SmallVec::with_capacity(4),
411                error: Some(String::from(format!(
412                    "Instrument not found: {instrument_id:?}"
413                ))),
414            };
415        }
416
417        // Check if price is provided
418        let Some(price) = order.price else {
419            return OrderResult {
420                order,
421                trades: SmallVec::with_capacity(4),
422                error: Some("Limit order requires a price".into()),
423            };
424        };
425
426        let mut trades = SmallVec::with_capacity(8);
427        let mut remaining_quantity = order.quantity;
428
429        // Structure to hold orders that need to be updated and their new quantities
430        // Most matching operations update only a few orders
431        let mut orders_to_update: SmallVec<[(OrderId, OrderSide, Decimal); 8]> =
432            SmallVec::with_capacity(8);
433
434        // Match against the opposite side of the order book
435        match order.side {
436            OrderSide::Buy => {
437                // Get the order book
438                let order_book = self
439                    .order_books
440                    .get(&instrument_id)
441                    .expect("Order book should exist after contains_key check");
442
443                // Get sell orders for reading
444                let sell_orders = self
445                    .sell_orders
446                    .get(&instrument_id)
447                    .expect("Sell orders should exist after contains_key check");
448
449                // Match against the order book
450                order_book.read(|book| {
451                    // Get the ask levels
452                    let asks = book.asks();
453
454                    // Match against each ask level that has a price <= our buy price
455                    for ask_level in asks {
456                        if remaining_quantity.is_zero() || ask_level.price > price {
457                            break;
458                        }
459
460                        // Find all sell orders at this price level
461                        // Typically few orders at the exact same price
462                        let mut orders_at_price: SmallVec<[_; 8]> = sell_orders
463                            .values()
464                            .filter(|o| o.price == ask_level.price && !o.is_filled())
465                            .collect();
466
467                        // Sort by creation time (oldest first)
468                        orders_at_price.sort_by_key(|o| o.creation_time_ns);
469
470                        // Match against each order
471                        for sell_order in orders_at_price {
472                            if remaining_quantity.is_zero() {
473                                break;
474                            }
475
476                            // Calculate match quantity
477                            let match_quantity =
478                                std::cmp::min(remaining_quantity, sell_order.remaining_quantity);
479
480                            // Create a trade
481                            let trade = ExecutedTrade {
482                                id: Uuid::new_v4(),
483                                instrument_id: instrument_id.clone(),
484                                maker_order_id: sell_order.id,
485                                taker_order_id: order.id,
486                                price: sell_order.price,
487                                quantity: match_quantity,
488                                side: OrderSide::Buy,
489                                timestamp_ns: self.clock.raw(),
490                            };
491
492                            trades.push(trade);
493
494                            // Update remaining quantities
495                            remaining_quantity -= match_quantity;
496
497                            // Store the order to update
498                            let new_remaining = sell_order.remaining_quantity - match_quantity;
499                            orders_to_update.push((sell_order.id, OrderSide::Sell, new_remaining));
500                        }
501                    }
502                });
503
504                // Now update the sell orders
505                let sell_orders = self
506                    .sell_orders
507                    .get_mut(&instrument_id)
508                    .expect("Sell orders should exist after contains_key check");
509                for (order_id, _, new_quantity) in orders_to_update {
510                    if let Some(order) = sell_orders.get_mut(&order_id) {
511                        order.remaining_quantity = new_quantity;
512                    }
513                }
514
515                // If we have remaining quantity, add it to the order book
516                if !remaining_quantity.is_zero() {
517                    let limit_order = LimitOrder::new(
518                        order.id,
519                        Some(order.client_id.clone()),
520                        OrderSide::Buy,
521                        price,
522                        remaining_quantity,
523                    );
524
525                    let buy_orders = self
526                        .buy_orders
527                        .get_mut(&instrument_id)
528                        .expect("Buy orders should exist after contains_key check");
529                    buy_orders.insert(order.id, limit_order);
530                }
531            }
532            OrderSide::Sell => {
533                // Get the order book
534                let order_book = self
535                    .order_books
536                    .get(&instrument_id)
537                    .expect("Order book should exist after contains_key check");
538
539                // Get buy orders for reading
540                let buy_orders = self
541                    .buy_orders
542                    .get(&instrument_id)
543                    .expect("Buy orders should exist after contains_key check");
544
545                // Match against the order book
546                order_book.read(|book| {
547                    // Get the bid levels
548                    let bids = book.bids();
549
550                    // Match against each bid level that has a price >= our sell price
551                    for bid_level in bids {
552                        if remaining_quantity.is_zero() || bid_level.price < price {
553                            break;
554                        }
555
556                        // Find all buy orders at this price level
557                        // Typically few orders at the exact same price
558                        let mut orders_at_price: SmallVec<[_; 8]> = buy_orders
559                            .values()
560                            .filter(|o| o.price == bid_level.price && !o.is_filled())
561                            .collect();
562
563                        // Sort by creation time (oldest first)
564                        orders_at_price.sort_by_key(|o| o.creation_time_ns);
565
566                        // Match against each order
567                        for buy_order in orders_at_price {
568                            if remaining_quantity.is_zero() {
569                                break;
570                            }
571
572                            // Calculate match quantity
573                            let match_quantity =
574                                std::cmp::min(remaining_quantity, buy_order.remaining_quantity);
575
576                            // Create a trade
577                            let trade = ExecutedTrade {
578                                id: Uuid::new_v4(),
579                                instrument_id: instrument_id.clone(),
580                                maker_order_id: buy_order.id,
581                                taker_order_id: order.id,
582                                price: buy_order.price,
583                                quantity: match_quantity,
584                                side: OrderSide::Sell,
585                                timestamp_ns: self.clock.raw(),
586                            };
587
588                            trades.push(trade);
589
590                            // Update remaining quantities
591                            remaining_quantity -= match_quantity;
592
593                            // Store the order to update
594                            let new_remaining = buy_order.remaining_quantity - match_quantity;
595                            orders_to_update.push((buy_order.id, OrderSide::Buy, new_remaining));
596                        }
597                    }
598                });
599
600                // Now update the buy orders
601                let buy_orders = self
602                    .buy_orders
603                    .get_mut(&instrument_id)
604                    .expect("Buy orders should exist after contains_key check");
605                for (order_id, _, new_quantity) in orders_to_update {
606                    if let Some(order) = buy_orders.get_mut(&order_id) {
607                        order.remaining_quantity = new_quantity;
608                    }
609                }
610
611                // If we have remaining quantity, add it to the order book
612                if !remaining_quantity.is_zero() {
613                    let limit_order = LimitOrder::new(
614                        order.id,
615                        Some(order.client_id.clone()),
616                        OrderSide::Sell,
617                        price,
618                        remaining_quantity,
619                    );
620
621                    let sell_orders = self
622                        .sell_orders
623                        .get_mut(&instrument_id)
624                        .expect("Sell orders should exist after contains_key check");
625                    sell_orders.insert(order.id, limit_order);
626                }
627            }
628        }
629
630        // Update the order
631        order.filled_quantity = order.quantity - remaining_quantity;
632
633        if order.filled_quantity.is_zero() {
634            order.status = OrderStatus::Open;
635        } else if order.filled_quantity < order.quantity {
636            order.status = OrderStatus::PartiallyFilled;
637        } else {
638            order.status = OrderStatus::Filled;
639        }
640
641        // Calculate average fill price
642        if !trades.is_empty() {
643            let total_value = trades
644                .iter()
645                .fold(Decimal::ZERO, |acc, trade: &ExecutedTrade| {
646                    acc + trade.price * trade.quantity
647                });
648            let total_quantity = trades
649                .iter()
650                .fold(Decimal::ZERO, |acc, trade: &ExecutedTrade| {
651                    acc + trade.quantity
652                });
653            order.average_fill_price = Some(total_value / total_quantity);
654        }
655
656        // Update the order book
657        self.update_order_book(&instrument_id);
658
659        OrderResult {
660            order,
661            trades,
662            error: None,
663        }
664    }
665
666    /// Cancel an order
667    ///
668    /// # Panics
669    ///
670    /// Panics if the instrument exists in `order_books` but not in `buy_orders` or `sell_orders`.
671    /// This should never happen in normal operation as these data structures are kept in sync.
672    pub fn cancel_order(
673        &mut self,
674        order_id: OrderId,
675        instrument_id: &InstrumentId,
676    ) -> Option<Order> {
677        // Check if the instrument exists
678        if !self.order_books.contains_key(instrument_id) {
679            return None;
680        }
681
682        // Check if the order exists in the buy orders
683        let buy_orders = self.buy_orders.get_mut(instrument_id).unwrap();
684        if let Some(order) = buy_orders.remove(&order_id) {
685            // Update the order book
686            self.update_order_book(instrument_id);
687
688            // Create an Order object to return
689            let mut result = Order::new(
690                instrument_id.venue,
691                instrument_id.symbol.clone(),
692                order.side,
693                OrderType::Limit,
694                order.original_quantity,
695                Some(order.price),
696                order.client_order_id.unwrap_or_else(|| ClientId::from("")),
697            );
698
699            result.id = order_id;
700            result.filled_quantity = order.original_quantity - order.remaining_quantity;
701            result.status = OrderStatus::Cancelled;
702
703            return Some(result);
704        }
705
706        // Check if the order exists in the sell orders
707        let sell_orders = self.sell_orders.get_mut(instrument_id).unwrap();
708        if let Some(order) = sell_orders.remove(&order_id) {
709            // Update the order book
710            self.update_order_book(instrument_id);
711
712            // Create an Order object to return
713            let mut result = Order::new(
714                instrument_id.venue,
715                instrument_id.symbol.clone(),
716                order.side,
717                OrderType::Limit,
718                order.original_quantity,
719                Some(order.price),
720                order.client_order_id.unwrap_or_else(|| ClientId::from("")),
721            );
722
723            result.id = order_id;
724            result.filled_quantity = order.original_quantity - order.remaining_quantity;
725            result.status = OrderStatus::Cancelled;
726
727            return Some(result);
728        }
729
730        None
731    }
732
733    /// Get the order book for an instrument
734    #[must_use]
735    pub fn get_order_book(&self, instrument_id: &InstrumentId) -> Option<&SharedSimdOrderBook> {
736        self.order_books.get(instrument_id)
737    }
738
739    /// Update the order book for an instrument
740    fn update_order_book(&self, instrument_id: &InstrumentId) {
741        // Get the order book
742        let Some(order_book) = self.order_books.get(instrument_id) else {
743            return;
744        };
745
746        // Get the buy and sell orders
747        let buy_orders = self.buy_orders.get(instrument_id).unwrap();
748        let sell_orders = self.sell_orders.get(instrument_id).unwrap();
749
750        // Create price levels for the order book
751        let mut bids = SmallVec::<[(Decimal, Decimal); 32]>::new();
752        let mut asks = SmallVec::<[(Decimal, Decimal); 32]>::new();
753
754        // Aggregate buy orders by price
755        let mut buy_levels = FxHashMap::default();
756        for order in buy_orders.values() {
757            if order.is_filled() {
758                continue;
759            }
760
761            let entry = buy_levels.entry(order.price).or_insert(Decimal::ZERO);
762            *entry += order.remaining_quantity;
763        }
764
765        // Aggregate sell orders by price
766        let mut sell_levels = FxHashMap::default();
767        for order in sell_orders.values() {
768            if order.is_filled() {
769                continue;
770            }
771
772            let entry = sell_levels.entry(order.price).or_insert(Decimal::ZERO);
773            *entry += order.remaining_quantity;
774        }
775
776        // Convert to vectors
777        for (price, quantity) in buy_levels {
778            bids.push((price, quantity));
779        }
780
781        for (price, quantity) in sell_levels {
782            asks.push((price, quantity));
783        }
784
785        // Sort bids in descending order by price
786        bids.sort_by(|a, b| b.0.cmp(&a.0));
787
788        // Sort asks in ascending order by price
789        asks.sort_by(|a, b| a.0.cmp(&b.0));
790
791        // Update the order book
792        order_book.write(|book| {
793            // Create a snapshot from the current state
794            let snapshot: OrderBookSnapshot<64> =
795                crate::data::book_snapshot::OrderBookSnapshot::new(
796                    instrument_id.clone(),
797                    bids.iter()
798                        .map(|(price, quantity)| {
799                            crate::data::orderbook::PriceLevel::new(*price, *quantity)
800                        })
801                        .collect(),
802                    asks.iter()
803                        .map(|(price, quantity)| {
804                            crate::data::orderbook::PriceLevel::new(*price, *quantity)
805                        })
806                        .collect(),
807                    0,                // sequence_id - not used in this context
808                    self.clock.raw(), // timestamp_event
809                    self.clock.raw(), // timestamp_init
810                );
811            book.apply_snapshot(snapshot);
812        });
813    }
814
815    /// Clean up filled orders
816    pub fn clean_up_filled_orders(&mut self) {
817        // Collect instrument IDs that need to be updated
818        // Typically a small number of instruments are active
819        let instrument_ids: SmallVec<[InstrumentId; 16]> =
820            self.buy_orders.keys().cloned().collect();
821
822        // First, clean up all buy and sell orders
823        for instrument_id in &instrument_ids {
824            // Clean up buy orders
825            if let Some(buy_orders) = self.buy_orders.get_mut(instrument_id) {
826                buy_orders.retain(|_, order| !order.is_filled());
827            }
828
829            // Clean up sell orders
830            if let Some(sell_orders) = self.sell_orders.get_mut(instrument_id) {
831                sell_orders.retain(|_, order| !order.is_filled());
832            }
833        }
834
835        // Then, update all order books
836        for instrument_id in instrument_ids {
837            self.update_order_book(&instrument_id);
838        }
839    }
840}
841
842#[cfg(test)]
843mod tests {
844    use super::*;
845    use crate::types::ClientId;
846    use crate::venues::Venue;
847    use rust_decimal_macros::dec;
848
849    fn create_instrument_id() -> InstrumentId {
850        InstrumentId::new(String::from("BTCUSDT"), Venue::Binance)
851    }
852
853    #[test]
854    fn test_matching_engine_creation() {
855        let mut engine = MatchingEngine::new();
856        let instrument_id = create_instrument_id();
857
858        engine.add_instrument(instrument_id.clone());
859
860        assert!(engine.order_books.contains_key(&instrument_id));
861        assert!(engine.buy_orders.contains_key(&instrument_id));
862        assert!(engine.sell_orders.contains_key(&instrument_id));
863    }
864
865    #[test]
866    fn test_limit_order_matching() {
867        let mut engine = MatchingEngine::new();
868        let instrument_id = create_instrument_id();
869
870        engine.add_instrument(instrument_id.clone());
871
872        // Create a buy limit order
873        let buy_order = Order::new(
874            Venue::Binance,
875            String::from("BTCUSDT"),
876            OrderSide::Buy,
877            OrderType::Limit,
878            dec!(1.0),
879            Some(dec!(100.0)),
880            ClientId::from("client1"),
881        );
882
883        // Process the buy order
884        let result = engine.process_order(buy_order);
885
886        // Check that the order was accepted
887        assert_eq!(result.order.status, OrderStatus::Open);
888        assert!(result.trades.is_empty());
889
890        // Create a sell limit order at a higher price
891        let sell_order_higher = Order::new(
892            Venue::Binance,
893            String::from("BTCUSDT"),
894            OrderSide::Sell,
895            OrderType::Limit,
896            dec!(1.0),
897            Some(dec!(110.0)),
898            ClientId::from("client2"),
899        );
900
901        // Process the sell order
902        let result = engine.process_order(sell_order_higher);
903
904        // Check that the order was accepted but not matched
905        assert_eq!(result.order.status, OrderStatus::Open);
906        assert!(result.trades.is_empty());
907
908        // Create a sell limit order at a matching price
909        let sell_order_matching = Order::new(
910            Venue::Binance,
911            "BTCUSDT",
912            OrderSide::Sell,
913            OrderType::Limit,
914            dec!(0.5),
915            Some(dec!(100.0)),
916            ClientId::from("client3"),
917        );
918
919        // Process the sell order
920        let result = engine.process_order(sell_order_matching);
921
922        // Check that the order was partially filled
923        assert_eq!(result.order.status, OrderStatus::Filled);
924        assert_eq!(result.trades.len(), 1);
925        assert_eq!(result.trades[0].price, dec!(100.0));
926        assert_eq!(result.trades[0].quantity, dec!(0.5));
927
928        // Create another sell limit order at a matching price
929        let sell_order_matching2 = Order::new(
930            Venue::Binance,
931            "BTCUSDT",
932            OrderSide::Sell,
933            OrderType::Limit,
934            dec!(0.5),
935            Some(dec!(100.0)),
936            ClientId::from("client4"),
937        );
938
939        // Process the sell order
940        let result = engine.process_order(sell_order_matching2);
941
942        // Check that the order was filled
943        assert_eq!(result.order.status, OrderStatus::Filled);
944        assert_eq!(result.trades.len(), 1);
945        assert_eq!(result.trades[0].price, dec!(100.0));
946        assert_eq!(result.trades[0].quantity, dec!(0.5));
947
948        // Check the order book
949        let order_book = engine.get_order_book(&instrument_id).unwrap();
950
951        order_book.read(|book| {
952            // The buy order should be fully matched
953            assert!(book.bids().is_empty());
954
955            // The higher-priced sell order should still be there
956            assert_eq!(book.asks().len(), 1);
957            assert_eq!(book.asks()[0].price, dec!(110.0));
958            assert_eq!(book.asks()[0].quantity, dec!(1.0));
959        });
960    }
961
962    #[test]
963    fn test_market_order_matching() {
964        let mut engine = MatchingEngine::new();
965        let instrument_id = create_instrument_id();
966
967        engine.add_instrument(instrument_id.clone());
968
969        // Create a sell limit order
970        let sell_order = Order::new(
971            Venue::Binance,
972            "BTCUSDT",
973            OrderSide::Sell,
974            OrderType::Limit,
975            dec!(1.0),
976            Some(dec!(100.0)),
977            ClientId::from("client1"),
978        );
979
980        // Process the sell order
981        let result = engine.process_order(sell_order);
982
983        // Check that the order was accepted
984        assert_eq!(result.order.status, OrderStatus::Open);
985        assert!(result.trades.is_empty());
986
987        // Create a buy market order
988        let buy_order = Order::new(
989            Venue::Binance,
990            "BTCUSDT",
991            OrderSide::Buy,
992            OrderType::Market,
993            dec!(0.5),
994            None,
995            ClientId::from("client2"),
996        );
997
998        // Process the buy order
999        let result = engine.process_order(buy_order);
1000
1001        // Check that the order was filled
1002        assert_eq!(result.order.status, OrderStatus::Filled);
1003        assert_eq!(result.trades.len(), 1);
1004        assert_eq!(result.trades[0].price, dec!(100.0));
1005        assert_eq!(result.trades[0].quantity, dec!(0.5));
1006
1007        // Create another buy market order that can't be fully filled
1008        let buy_order2 = Order::new(
1009            Venue::Binance,
1010            "BTCUSDT",
1011            OrderSide::Buy,
1012            OrderType::Market,
1013            dec!(1.0),
1014            None,
1015            ClientId::from("client3"),
1016        );
1017
1018        // Process the buy order
1019        let result = engine.process_order(buy_order2);
1020
1021        // Check that the order was partially filled
1022        assert_eq!(result.order.status, OrderStatus::PartiallyFilled);
1023        assert_eq!(result.trades.len(), 1);
1024        assert_eq!(result.trades[0].price, dec!(100.0));
1025        assert_eq!(result.trades[0].quantity, dec!(0.5));
1026
1027        // Check the order book
1028        let order_book = engine.get_order_book(&instrument_id).unwrap();
1029
1030        order_book.read(|book| {
1031            // The sell order should be fully matched
1032            assert!(book.asks().is_empty());
1033        });
1034    }
1035
1036    #[test]
1037    fn test_cancel_order() {
1038        let mut engine = MatchingEngine::new();
1039        let instrument_id = create_instrument_id();
1040
1041        engine.add_instrument(instrument_id.clone());
1042
1043        // Create a buy limit order
1044        let buy_order = Order::new(
1045            Venue::Binance,
1046            "BTCUSDT",
1047            OrderSide::Buy,
1048            OrderType::Limit,
1049            dec!(1.0),
1050            Some(dec!(100.0)),
1051            ClientId::from("client1"),
1052        );
1053
1054        // Process the buy order
1055        let result = engine.process_order(buy_order);
1056
1057        // Check that the order was accepted
1058        assert_eq!(result.order.status, OrderStatus::Open);
1059
1060        // Cancel the order
1061        let canceled_order = engine.cancel_order(result.order.id, &instrument_id);
1062
1063        // Check that the order was canceled
1064        assert!(canceled_order.is_some());
1065        let canceled_order = canceled_order.unwrap();
1066        assert_eq!(canceled_order.status, OrderStatus::Cancelled);
1067
1068        // Check the order book
1069        let order_book = engine.get_order_book(&instrument_id).unwrap();
1070
1071        order_book.read(|book| {
1072            // The buy order should be removed
1073            assert!(book.bids().is_empty());
1074        });
1075    }
1076
1077    #[test]
1078    fn test_clean_up_filled_orders() {
1079        let mut engine = MatchingEngine::new();
1080        let instrument_id = create_instrument_id();
1081
1082        engine.add_instrument(instrument_id.clone());
1083
1084        // Create a buy limit order
1085        let buy_order = Order::new(
1086            Venue::Binance,
1087            "BTCUSDT",
1088            OrderSide::Buy,
1089            OrderType::Limit,
1090            dec!(1.0),
1091            Some(dec!(100.0)),
1092            ClientId::from("client1"),
1093        );
1094
1095        // Process the buy order
1096        let _result = engine.process_order(buy_order);
1097
1098        // Create a sell limit order at a matching price
1099        let sell_order = Order::new(
1100            Venue::Binance,
1101            "BTCUSDT",
1102            OrderSide::Sell,
1103            OrderType::Limit,
1104            dec!(1.0),
1105            Some(dec!(100.0)),
1106            ClientId::from("client2"),
1107        );
1108
1109        // Process the sell order
1110        let result = engine.process_order(sell_order);
1111
1112        // Check that the order was filled
1113        assert_eq!(result.order.status, OrderStatus::Filled);
1114
1115        // Clean up filled orders
1116        engine.clean_up_filled_orders();
1117
1118        // Check the order book
1119        let order_book = engine.get_order_book(&instrument_id).unwrap();
1120
1121        order_book.read(|book| {
1122            // Both orders should be removed
1123            assert!(book.bids().is_empty());
1124            assert!(book.asks().is_empty());
1125        });
1126    }
1127}