1use 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#[derive(Debug, Clone)]
23#[repr(align(16))] pub struct LimitOrder {
25 pub id: OrderId,
27
28 pub client_order_id: Option<ClientId>,
30
31 pub side: OrderSide,
33
34 pub price: Decimal,
36
37 pub original_quantity: Decimal,
39
40 pub remaining_quantity: Decimal,
42
43 pub creation_time_ns: u64,
45}
46
47impl LimitOrder {
48 #[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 #[must_use]
73 pub const fn is_filled(&self) -> bool {
74 self.remaining_quantity.is_zero()
75 }
76}
77
78#[derive(Debug, Clone)]
80pub struct ExecutedTrade {
81 pub id: Uuid,
83
84 pub instrument_id: InstrumentId,
86
87 pub maker_order_id: OrderId,
89
90 pub taker_order_id: OrderId,
92
93 pub price: Decimal,
95
96 pub quantity: Decimal,
98
99 pub side: OrderSide,
101
102 pub timestamp_ns: u64,
104}
105
106#[derive(Debug, Clone)]
108pub struct OrderResult {
109 pub order: Order,
111
112 pub trades: SmallVec<[ExecutedTrade; 4]>,
115
116 pub error: Option<String>,
118}
119
120#[derive(Debug)]
122pub struct MatchingEngine {
123 order_books: FxHashMap<InstrumentId, SharedSimdOrderBook>,
125
126 buy_orders: FxHashMap<InstrumentId, FxHashMap<OrderId, LimitOrder>>,
128
129 sell_orders: FxHashMap<InstrumentId, FxHashMap<OrderId, LimitOrder>>,
131
132 clock: Clock,
134}
135
136impl Default for MatchingEngine {
137 fn default() -> Self {
138 Self::new()
139 }
140}
141
142impl MatchingEngine {
143 #[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 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 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 fn process_market_order(&mut self, mut order: Order) -> OrderResult {
183 let instrument_id = InstrumentId::new(order.symbol.clone(), order.venue);
184
185 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 let mut orders_to_update: SmallVec<[(OrderId, OrderSide, Decimal); 8]> =
202 SmallVec::with_capacity(8);
203
204 match order.side {
206 OrderSide::Buy => {
207 let order_book = self
209 .order_books
210 .get(&instrument_id)
211 .expect("Order book should exist after contains_key check");
212
213 let sell_orders = self
215 .sell_orders
216 .get(&instrument_id)
217 .expect("Sell orders should exist after contains_key check");
218
219 order_book.read(|book| {
221 let asks = book.asks();
223
224 for ask_level in asks {
226 if remaining_quantity.is_zero() {
227 break;
228 }
229
230 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 orders_at_price.sort_by_key(|o| o.creation_time_ns);
239
240 for sell_order in orders_at_price {
242 if remaining_quantity.is_zero() {
243 break;
244 }
245
246 let match_quantity =
248 std::cmp::min(remaining_quantity, sell_order.remaining_quantity);
249
250 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 remaining_quantity -= match_quantity;
266
267 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 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 let order_book = self
288 .order_books
289 .get(&instrument_id)
290 .expect("Order book should exist after contains_key check");
291
292 let buy_orders = self
294 .buy_orders
295 .get(&instrument_id)
296 .expect("Buy orders should exist after contains_key check");
297
298 order_book.read(|book| {
300 let bids = book.bids();
302
303 for bid_level in bids {
305 if remaining_quantity.is_zero() {
306 break;
307 }
308
309 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 orders_at_price.sort_by_key(|o| o.creation_time_ns);
318
319 for buy_order in orders_at_price {
321 if remaining_quantity.is_zero() {
322 break;
323 }
324
325 let match_quantity =
327 std::cmp::min(remaining_quantity, buy_order.remaining_quantity);
328
329 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 remaining_quantity -= match_quantity;
345
346 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 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 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 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 self.update_order_book(&instrument_id);
394
395 OrderResult {
396 order,
397 trades,
398 error: None,
399 }
400 }
401
402 fn process_limit_order(&mut self, mut order: Order) -> OrderResult {
404 let instrument_id = InstrumentId::new(order.symbol.clone(), order.venue);
405
406 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 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 let mut orders_to_update: SmallVec<[(OrderId, OrderSide, Decimal); 8]> =
432 SmallVec::with_capacity(8);
433
434 match order.side {
436 OrderSide::Buy => {
437 let order_book = self
439 .order_books
440 .get(&instrument_id)
441 .expect("Order book should exist after contains_key check");
442
443 let sell_orders = self
445 .sell_orders
446 .get(&instrument_id)
447 .expect("Sell orders should exist after contains_key check");
448
449 order_book.read(|book| {
451 let asks = book.asks();
453
454 for ask_level in asks {
456 if remaining_quantity.is_zero() || ask_level.price > price {
457 break;
458 }
459
460 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 orders_at_price.sort_by_key(|o| o.creation_time_ns);
469
470 for sell_order in orders_at_price {
472 if remaining_quantity.is_zero() {
473 break;
474 }
475
476 let match_quantity =
478 std::cmp::min(remaining_quantity, sell_order.remaining_quantity);
479
480 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 remaining_quantity -= match_quantity;
496
497 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 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 !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 let order_book = self
535 .order_books
536 .get(&instrument_id)
537 .expect("Order book should exist after contains_key check");
538
539 let buy_orders = self
541 .buy_orders
542 .get(&instrument_id)
543 .expect("Buy orders should exist after contains_key check");
544
545 order_book.read(|book| {
547 let bids = book.bids();
549
550 for bid_level in bids {
552 if remaining_quantity.is_zero() || bid_level.price < price {
553 break;
554 }
555
556 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 orders_at_price.sort_by_key(|o| o.creation_time_ns);
565
566 for buy_order in orders_at_price {
568 if remaining_quantity.is_zero() {
569 break;
570 }
571
572 let match_quantity =
574 std::cmp::min(remaining_quantity, buy_order.remaining_quantity);
575
576 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 remaining_quantity -= match_quantity;
592
593 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 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 !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 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 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 self.update_order_book(&instrument_id);
658
659 OrderResult {
660 order,
661 trades,
662 error: None,
663 }
664 }
665
666 pub fn cancel_order(
673 &mut self,
674 order_id: OrderId,
675 instrument_id: &InstrumentId,
676 ) -> Option<Order> {
677 if !self.order_books.contains_key(instrument_id) {
679 return None;
680 }
681
682 let buy_orders = self.buy_orders.get_mut(instrument_id).unwrap();
684 if let Some(order) = buy_orders.remove(&order_id) {
685 self.update_order_book(instrument_id);
687
688 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 let sell_orders = self.sell_orders.get_mut(instrument_id).unwrap();
708 if let Some(order) = sell_orders.remove(&order_id) {
709 self.update_order_book(instrument_id);
711
712 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 #[must_use]
735 pub fn get_order_book(&self, instrument_id: &InstrumentId) -> Option<&SharedSimdOrderBook> {
736 self.order_books.get(instrument_id)
737 }
738
739 fn update_order_book(&self, instrument_id: &InstrumentId) {
741 let Some(order_book) = self.order_books.get(instrument_id) else {
743 return;
744 };
745
746 let buy_orders = self.buy_orders.get(instrument_id).unwrap();
748 let sell_orders = self.sell_orders.get(instrument_id).unwrap();
749
750 let mut bids = SmallVec::<[(Decimal, Decimal); 32]>::new();
752 let mut asks = SmallVec::<[(Decimal, Decimal); 32]>::new();
753
754 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 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 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 bids.sort_by(|a, b| b.0.cmp(&a.0));
787
788 asks.sort_by(|a, b| a.0.cmp(&b.0));
790
791 order_book.write(|book| {
793 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, self.clock.raw(), self.clock.raw(), );
811 book.apply_snapshot(snapshot);
812 });
813 }
814
815 pub fn clean_up_filled_orders(&mut self) {
817 let instrument_ids: SmallVec<[InstrumentId; 16]> =
820 self.buy_orders.keys().cloned().collect();
821
822 for instrument_id in &instrument_ids {
824 if let Some(buy_orders) = self.buy_orders.get_mut(instrument_id) {
826 buy_orders.retain(|_, order| !order.is_filled());
827 }
828
829 if let Some(sell_orders) = self.sell_orders.get_mut(instrument_id) {
831 sell_orders.retain(|_, order| !order.is_filled());
832 }
833 }
834
835 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 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 let result = engine.process_order(buy_order);
885
886 assert_eq!(result.order.status, OrderStatus::Open);
888 assert!(result.trades.is_empty());
889
890 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 let result = engine.process_order(sell_order_higher);
903
904 assert_eq!(result.order.status, OrderStatus::Open);
906 assert!(result.trades.is_empty());
907
908 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 let result = engine.process_order(sell_order_matching);
921
922 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 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 let result = engine.process_order(sell_order_matching2);
941
942 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 let order_book = engine.get_order_book(&instrument_id).unwrap();
950
951 order_book.read(|book| {
952 assert!(book.bids().is_empty());
954
955 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 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 let result = engine.process_order(sell_order);
982
983 assert_eq!(result.order.status, OrderStatus::Open);
985 assert!(result.trades.is_empty());
986
987 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 let result = engine.process_order(buy_order);
1000
1001 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 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 let result = engine.process_order(buy_order2);
1020
1021 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 let order_book = engine.get_order_book(&instrument_id).unwrap();
1029
1030 order_book.read(|book| {
1031 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 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 let result = engine.process_order(buy_order);
1056
1057 assert_eq!(result.order.status, OrderStatus::Open);
1059
1060 let canceled_order = engine.cancel_order(result.order.id, &instrument_id);
1062
1063 assert!(canceled_order.is_some());
1065 let canceled_order = canceled_order.unwrap();
1066 assert_eq!(canceled_order.status, OrderStatus::Cancelled);
1067
1068 let order_book = engine.get_order_book(&instrument_id).unwrap();
1070
1071 order_book.read(|book| {
1072 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 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 let _result = engine.process_order(buy_order);
1097
1098 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 let result = engine.process_order(sell_order);
1111
1112 assert_eq!(result.order.status, OrderStatus::Filled);
1114
1115 engine.clean_up_filled_orders();
1117
1118 let order_book = engine.get_order_book(&instrument_id).unwrap();
1120
1121 order_book.read(|book| {
1122 assert!(book.bids().is_empty());
1124 assert!(book.asks().is_empty());
1125 });
1126 }
1127}