1use crate::order_book::{
2 orderbook::OrderBook, types::{
3 BookDepth, CancelOrder, CancelOutcome, GlobalOrderRegistry, ModifyOrder, ModifyOutcome, NewOrder, OrderLocation, OrderNode, OrderType
4 }
5};
6use anyhow::{Context, anyhow};
7use std::collections::HashMap;
8use tracing::{Span, instrument};
9use uuid::Uuid;
10
11#[derive(Debug)]
12pub struct MatchingEngine {
13 _book: HashMap<Uuid, OrderBook>,
14 _global_registry: GlobalOrderRegistry,
15}
16
17impl MatchingEngine {
18
19 pub fn new() -> Self{
20 Self { _book: HashMap::new(), _global_registry: GlobalOrderRegistry::new() }
21 }
22
23 #[instrument(
24 name = "get_orderbook",
25 skip(self),
26 fields(
27 order_id = %global_order_id,
28 ),
29 )]
30 fn get_orderbook(
31 &mut self,
32 global_order_id: Uuid,
33 span: &Span
34 ) -> Option<(usize, bool,Uuid, &mut OrderBook)> {
35 let order_location = match self._global_registry.get_details(&global_order_id){
36 Some(location) => {
37 location
38 }
39 None => {
40 span.record("reason", "order not found in global registry");
41 return None;
42 }
43 };
44 let Some(book) = self._book.get_mut(&order_location.security_id) else {
45 span.record("reason", "orderbook doesn't exist");
46 return None;
47 };
48 Some((order_location.order_index, order_location.is_buy_side,order_location.security_id, book))
49 }
50
51 pub fn modify(
52 &mut self,
53 global_order_id: Uuid,
54 new_price: Option<u32>,
55 new_qty: Option<u32>,
56 span: &Span,
57 ) -> Result< &'static str, anyhow::Error> {
58 let _gaurd = span.enter();
59 let (order_index, is_buy_side,security_id, orderbook) = self
60 .get_orderbook(global_order_id, span)
61 .context("Could not find the orderbook")?;
62 if let Ok(potential_modfication) = orderbook.modify_order(
63 global_order_id,
64 ModifyOrder {
65 new_price,
66 order_index,
67 is_buy_side,
68 new_quantity: new_qty,
69 },
70 ) {
71 if let Some(modification_result) = potential_modfication {
72 match modification_result {
73 ModifyOutcome::Both {
74 new_price,
75 new_initial_qty,
76 old_current_qty,
77 } => {
78 span.record("modify_outcome", "price & qty");
79 if let Some(_) = self._global_registry.delete(&global_order_id){
80 let _ = self.match_order(
81 NewOrder {
82 engine_order_id: global_order_id,
83 price: Some(new_price),
84 initial_quantity: new_initial_qty,
85 current_quantity : old_current_qty,
86 is_buy_side,
87 security_id,
88 order_type: OrderType::Limit,
89 },
90 span);
91 return Ok("Both")
92 }
93 span.record("intermediate_error", "Failed to delete from global registry");
94 return Ok("Error occured while deleting from global registry")
95 },
96 ModifyOutcome::Repriced { new_price, old_initial_qty, old_current_qty } =>
97 {
98 span.record("modify_outcome", "price");
99 if let Some(_) = self._global_registry.delete(&global_order_id){
100 let _ = self.match_order(
101 NewOrder {
102 engine_order_id: global_order_id,
103 price: Some(new_price),
104 initial_quantity: old_initial_qty,
105 current_quantity : old_current_qty,
106 is_buy_side,
107 security_id,
108 order_type: OrderType::Limit,
109 },
110 span);
111 return Ok("Repriced")
112 }
113 span.record("intermediate_error", "Failed to delete from global registry");
114 return Ok("Error occured while deleting from global registry")
115 },
116 ModifyOutcome::Requantized { old_price, new_initial_qty, old_current_qty } => {
117 span.record("modify_outcome", "qty");
118 if let Some(_) = self._global_registry.delete(&global_order_id){
119 let _ = self.match_order(
120 NewOrder {
121 engine_order_id: global_order_id,
122 price: Some(old_price),
123 initial_quantity: new_initial_qty,
124 current_quantity : old_current_qty,
125 is_buy_side,
126 security_id,
127 order_type: OrderType::Limit,
128 }, span);
129 return Ok("Requantized")
130 }
131 span.record("intermediate_error", "Failed to delete from global registry");
132 return Ok("Error occured while deleting from global registry")
133 },
134 ModifyOutcome::Inplace => {
135 span.record("modify_outcome", "qty reduction");
136 return Ok("Inplace")
137 }
138 }
139 }
140 return Ok("No potential modification")
141 } else {
142 return Ok("No modification occured");
143 }
144 }
145
146 pub fn cancel(&mut self, global_order_id: Uuid, span: &Span) -> Result<CancelOutcome, anyhow::Error>{
147 let (order_index, is_buy_side,_, orderbook) = self
148 .get_orderbook(global_order_id, span)
149 .context("Could not find the orderbook")?;
150 if let Err(_) = orderbook.cancel_order(global_order_id, CancelOrder{is_buy_side, order_index}){
151 span.record("reason", "orderbook cancellation failed");
152 span.record("success_status", false);
153 return Ok(CancelOutcome::Failed);
154 };
155 if let Some(_) = self._global_registry.delete(&global_order_id){
156 span.record("success_status", true);
157 return Ok(CancelOutcome::Success)
158 };
159 span.record("reason", "Registry cancellation failed");
160 span.record("success_status", false);
161 Ok(CancelOutcome::Failed)
162 }
163
164 pub fn depth(&self, security_id : Uuid, levels_count :Option<u32>, span: &Span ) -> Result<BookDepth, anyhow::Error>{
165 let _gaurd = span.enter();
166 span.record("security_id", security_id.to_string());
167 let Some(order_book) = self._book.get(&security_id) else {
168 span.record("status", "failed");
169 span.record("reason", "orderbook doesn't exist");
170 return Err(anyhow!(""))
171 };
172 match order_book.depth(levels_count){
173 Ok(book_depth) => {
174 span.record("status", "success");
175 span.record("reason", "None");
176 Ok(book_depth)
177 },
178 Err(e) => Err(anyhow!("{}", e))
179 }
180 }
181
182 pub fn match_order(&mut self, order: NewOrder, span: &Span) -> Result<Option<usize>, anyhow::Error> {
183
184 let _gaurd = span.enter();
185
186 let orderbook = match self._book.get_mut(&order.security_id){
187 Some(orderbook) => {
188 orderbook
189 }
190 None => {
191 self._book.entry(order.security_id).or_insert(OrderBook::new())
192 }
193 };
194
195 if !order.is_buy_side {
196 match order.order_type {
198 OrderType::Market(None) => {
199 let mut fill_quantity = order.initial_quantity;
201 let mut levels_consumed = 0;
202 let mut orders_touched = 0;
203 while fill_quantity > 0 {
204 let remove_node: bool;
205 {
206 let Some(mut price_node) = orderbook.bid.price_map.last_entry() else {
207 break;
208 };
209 let price_level = price_node.get_mut();
210 while price_level.total_quantity > 0 && fill_quantity > 0 {
211 if let Some(head_idx) = price_level.head{
212
213 match orderbook.bid.order_pool[head_idx].as_mut(){
214 Some(first_order_node) => {
215
216 if fill_quantity >= first_order_node.current_quantity {
217 fill_quantity -= first_order_node.current_quantity;
218
219 price_level.total_quantity = price_level.total_quantity.checked_sub(first_order_node.current_quantity).ok_or(anyhow!("error occured in sub of total qty - current qyt"))?;
220 let next = first_order_node.next;
221 orderbook.bid.order_pool[head_idx] = None;
222 orderbook.bid.free_list.push(head_idx);
223 orders_touched += 1;
224 if let Some(next_order_idx) = next {
225 price_level.head = Some(next_order_idx);
226 } else {
227 span.record("reason", "exhausted");
228 price_level.total_quantity = 0;
229 price_level.head = None;
230 price_level.tail = None;
231 price_level.order_count = 0;
232 break;
233 }
234 } else {
235 first_order_node.current_quantity = first_order_node.current_quantity.checked_sub(fill_quantity).ok_or(anyhow!("error occured subtracting fnq - fq"))?;
236 price_level.total_quantity = price_level.total_quantity.checked_sub(fill_quantity).ok_or(anyhow!("error occured subtracting fntq - fq"))?;
237 fill_quantity = 0;
238 orders_touched += 1;
239 span.record("filled", true);
240 }
241 }
242 None => {
243 return Err(anyhow!("failed to get head_idx from order pool"));
244 }
245 };
246 }else {
247 break;
249 }
250 }
251 remove_node = price_level.total_quantity == 0;
252 }
253 if remove_node {
254 match orderbook.bid.price_map.pop_last(){
255 Some(_) => {
256 levels_consumed += 1;
257 }
258 None => {
259 break;
260 }
261 };
262 }
263 }
264 span.record("order_type", "market");
265 span.record("is_buy_side", false);
266 span.record("levels_consumed", levels_consumed);
267 span.record("orders_touched", orders_touched);
268 Ok(None)
269 }
270 OrderType::Market(market_limit) => {
271 let mut fill_quantity = order.initial_quantity;
272 let mut levels_consumed = 0;
273 let mut orders_touched = 0;
274 while fill_quantity > 0 {
275 let remove_node: bool;
276 {
277 let Some(mut price_node) = orderbook.bid.price_map.last_entry() else {
278 break;
279 };
280
281 match market_limit {
282 Some(price) => {
283 if price > *price_node.key(){
284 break;
285 }
286 }
287 None => {
288 return Err(anyhow!("did not recieve price for market-limit(SELL)"))
289 }
290 }
291 let price_level = price_node.get_mut();
292 while price_level.total_quantity > 0 && fill_quantity > 0 {
293 if let Some(head_idx) = price_level.head{
294
295 match orderbook.bid.order_pool[head_idx].as_mut(){
296 Some(first_order_node) => {
297
298 if fill_quantity >= first_order_node.current_quantity {
299 fill_quantity -= first_order_node.current_quantity;
300 price_level.total_quantity = price_level.total_quantity.checked_sub(first_order_node.current_quantity).ok_or(anyhow!("error occured in sub of total qty - current qyt"))?;
301 let next = first_order_node.next;
302 orderbook.bid.order_pool[head_idx] = None;
303 orderbook.bid.free_list.push(head_idx);
304 orders_touched += 1;
305 if let Some(next_order_idx) = next {
306 price_level.head = Some(next_order_idx);
307 } else {
308 span.record("reason", "exhausted");
309 price_level.total_quantity = 0;
310 price_level.head = None;
311 price_level.tail = None;
312 price_level.order_count = 0;
313 break;
314 }
315 } else {
316 first_order_node.current_quantity = first_order_node.current_quantity.checked_sub(fill_quantity).ok_or(anyhow!("error occured subtracting fnq - fq"))?;
317 price_level.total_quantity = price_level.total_quantity.checked_sub(fill_quantity).ok_or(anyhow!("error occured subtracting fntq - fq"))?;
318 fill_quantity = 0;
319 orders_touched += 1;
320 span.record("filled", true);
321 }
322 }
323 None => {
324 return Err(anyhow!("failed to get head_idx from order pool"));
325 }
326 };
327 }else {
328 break;
329 }
330 }
331 remove_node = price_level.total_quantity == 0;
332 }
333 if remove_node {
334 match orderbook.bid.price_map.pop_last(){
335 Some(_) => {
336 levels_consumed += 1;
337 }
338 None => {
339 break;
340 }
341 };
342 }
343 }
344 span.record("order_type", "market");
345 span.record("is_buy_side", false);
346 span.record("levels_consumed", levels_consumed);
347 span.record("orders_touched", orders_touched);
348 Ok(None)
349 }
350 OrderType::Limit => {
351 let mut fill_quantity = order.initial_quantity;
352 let mut levels_consumed = 0;
353 let mut orders_touched = 0;
354 while fill_quantity > 0 {
355 let remove_node: bool;
356 {
357 let Some(mut price_node) = orderbook.bid.price_map.last_entry() else {
358 break;
359 };
360
361 match order.price {
362 Some(price) => {
363 if price > *price_node.key(){
364 break;
365 }
366 }
367 None => {
368 return Err(anyhow!("did not recieve price for limit order (SELL)"))
369 }
370 }
371 let price_level = price_node.get_mut();
372 while price_level.total_quantity > 0 && fill_quantity > 0 {
373 if let Some(head_idx) = price_level.head{
374 match orderbook.bid.order_pool[head_idx].as_mut(){
375 Some(first_order_node) => {
376 if fill_quantity >= first_order_node.current_quantity {
377 fill_quantity -= first_order_node.current_quantity;
378 price_level.total_quantity = price_level.total_quantity.checked_sub(first_order_node.current_quantity).ok_or(anyhow!("error occured in sub of total qty - current qyt"))?;
379 let next = first_order_node.next;
380 orderbook.bid.order_pool[head_idx] = None;
381 orderbook.bid.free_list.push(head_idx);
382 orders_touched += 1;
383 if let Some(next_order_idx) = next {
384 price_level.head = Some(next_order_idx);
385 } else {
386 span.record("reason", "partially_filled");
387 price_level.total_quantity = 0;
388 price_level.head = None;
389 price_level.tail = None;
390 price_level.order_count = 0;
391 break;
392 }
393 } else {
394 first_order_node.current_quantity = first_order_node.current_quantity.checked_sub(fill_quantity).ok_or(anyhow!("error occured subtracting fnq - fq"))?;
395 price_level.total_quantity = price_level.total_quantity.checked_sub(fill_quantity).ok_or(anyhow!("error occured subtracting fntq - fq"))?;
396 fill_quantity = 0;
397 orders_touched += 1;
398 span.record("filled", true);
399 }
400 }
401 None => {
402 return Err(anyhow!("failed to get head_idx from order pool"));
403 }
404 };
405 }else {
406 break;
407 }
408 }
409 remove_node = price_level.total_quantity == 0;
410 }
411 if remove_node {
412 match orderbook.bid.price_map.pop_last(){
413 Some(_) => {
414 levels_consumed += 1;
415 }
416 None => {
417 break;
418 }
419 };
420 }
421 }
422 if fill_quantity > 0 {
423 let alloted_index = orderbook.create_sell_order(
424 order.engine_order_id,
425 OrderNode {
426 initial_quantity: order.initial_quantity,
427 current_quantity: fill_quantity,
428 market_limit: order.price.unwrap(),
429 next: None,
430 prev: None,
431 },
432 )?;
433 let order_location = OrderLocation {
434 security_id : order.security_id,
435 is_buy_side : order.is_buy_side,
436 order_index : alloted_index
437 };
438 self._global_registry.insert(order.engine_order_id, order_location);
439 span.record("order_type", "limit");
440 span.record("is_buy_side", false);
441 span.record("levels_consumed", levels_consumed);
442 span.record("orders_touched", orders_touched);
443 return Ok(Some(alloted_index))
444 }
445 Ok(None)
446 }
447 }
448 } else {
449 match order.order_type {
450 OrderType::Market(None) => {
451 let mut fill_quantity = order.initial_quantity;
453 let mut levels_consumed = 0;
454 let mut orders_touched = 0;
455 while fill_quantity > 0 {
456 let remove_node: bool;
457 {
458 let Some(mut price_node) = orderbook.ask.price_map.first_entry() else {
459 break;
460 };
461 let price_level = price_node.get_mut();
462 while price_level.total_quantity > 0 && fill_quantity > 0 {
463 if let Some(head_idx) = price_level.head{
464 match orderbook.ask.order_pool[head_idx].as_mut(){
465 Some(first_order_node) => {
466
467 if fill_quantity >= first_order_node.current_quantity {
468 fill_quantity -= first_order_node.current_quantity;
469 price_level.total_quantity = price_level.total_quantity.checked_sub(first_order_node.current_quantity).ok_or(anyhow!("error occured in sub of total qty - current qyt"))?;
470 let next = first_order_node.next;
471 orderbook.ask.order_pool[head_idx] = None;
472 orderbook.ask.free_list.push(head_idx);
473 orders_touched += 1;
474 if let Some(next_order_idx) = next {
475 price_level.head = Some(next_order_idx);
476 } else {
477 span.record("reason", "exhausted");
478 price_level.total_quantity = 0;
479 price_level.head = None;
480 price_level.tail = None;
481 price_level.order_count = 0;
482 break;
483 }
484 } else {
485 first_order_node.current_quantity = first_order_node.current_quantity.checked_sub(fill_quantity).ok_or(anyhow!("error occured subtracting fnq - fq"))?;
486 price_level.total_quantity = price_level.total_quantity.checked_sub(fill_quantity).ok_or(anyhow!("error occured subtracting fntq - fq"))?;
487 fill_quantity = 0;
488 orders_touched += 1;
489 span.record("filled", true);
490 }
491 }
492 None => {
493 return Err(anyhow!("failed to get head_idx from order pool"));
494 }
495 };
496 }
497 else {
498 break;
499 }
500 }
501 remove_node = price_level.total_quantity == 0;
502 }
503 if remove_node {
504 match orderbook.ask.price_map.pop_first(){
505 Some(_) => {
506 levels_consumed += 1;
507 }
508 None => {
509 break;
510 }
511 };
512 }
513 }
514 span.record("order_type", "market");
515 span.record("is_buy_side", true);
516 span.record("levels_consumed", levels_consumed);
517 span.record("orders_touched", orders_touched);
518 Ok(None)
519 }
520 OrderType::Market(market_limit) => {
521 let mut fill_quantity = order.initial_quantity;
522 let mut levels_consumed = 0;
523 let mut orders_touched = 0;
524 while fill_quantity > 0 {
525 let remove_node: bool;
526 {
527 let Some(mut price_node) = orderbook.ask.price_map.first_entry() else {
528 break;
529 };
530
531 match market_limit {
532 Some(price) => {
533 if price < *price_node.key(){
534 break;
535 }
536 }
537 None => {
538 return Err(anyhow!("did not recieve price for market-limit(BUY)"))
539 }
540 }
541 let price_level = price_node.get_mut();
542 while price_level.total_quantity > 0 && fill_quantity > 0 {
543 let head_pointer = price_level.head;
544 if let Some(head_idx) = head_pointer{
545 match orderbook.ask.order_pool[head_idx].as_mut(){
546 Some(first_order_node) => {
547
548 if fill_quantity >= first_order_node.current_quantity {
549 fill_quantity -= first_order_node.current_quantity;
550 price_level.total_quantity = price_level.total_quantity.checked_sub(first_order_node.current_quantity).ok_or(anyhow!("error occured in sub of total qty - current qyt"))?;
551 let next = first_order_node.next;
552 orderbook.ask.order_pool[head_idx] = None;
553 orderbook.ask.free_list.push(head_idx);
554 orders_touched += 1;
555 if let Some(next_order_idx) = next {
556 price_level.head = Some(next_order_idx);
557 } else {
558 span.record("reason", "exhausted");
559 price_level.head = None;
560 price_level.total_quantity = 0;
561 price_level.head = None;
562 price_level.tail = None;
563 price_level.order_count = 0;
564 break;
565 }
566 } else {
567 first_order_node.current_quantity = first_order_node.current_quantity.checked_sub(fill_quantity).ok_or(anyhow!("error occured subtracting fnq - fq"))?;
568 price_level.total_quantity = price_level.total_quantity.checked_sub(fill_quantity).ok_or(anyhow!("error occured subtracting fntq - fq"))?;
569 fill_quantity = 0;
570 orders_touched += 1;
571 span.record("filled", true);
572 }
573 }
574 None => {
575 return Err(anyhow!("failed to get head_idx from order pool"));
576 }
577 };
578 }
579 else {
580 break;
581 }
582 }
583 remove_node = price_level.total_quantity == 0;
584 }
585 if remove_node {
586 match orderbook.ask.price_map.pop_first(){
587 Some(_) => {
588 levels_consumed += 1;
589 }
590 None => {
591 break;
592 }
593 };
594 }
595 }
596 span.record("order_type", "market");
597 span.record("is_buy_side", true);
598 span.record("levels_consumed", levels_consumed);
599 span.record("orders_touched", orders_touched);
600 Ok(None)
601 }
602 OrderType::Limit => {
603 let mut fill_quantity = order.initial_quantity;
604 let mut levels_consumed = 0;
605 let mut orders_touched = 0;
606 while fill_quantity > 0 {
607 let remove_node: bool;
608 {
609 let Some(mut price_node) = orderbook.ask.price_map.first_entry() else {
610 break;
611 };
612
613 match order.price {
614 Some(price) => {
615 if price < *price_node.key(){
616 break;
617 }
618 }
619 None => {
620 return Err(anyhow!("did not recieve price for limit(BUY)"))
621 }
622 }
623 let price_level = price_node.get_mut();
624 while price_level.total_quantity > 0 && fill_quantity > 0 {
625 if let Some(head_idx) = price_level.head{
626
627 match orderbook.ask.order_pool[head_idx].as_mut(){
628 Some(first_order_node) => {
629
630 if fill_quantity >= first_order_node.current_quantity {
631 fill_quantity -= first_order_node.current_quantity;
632 price_level.total_quantity = price_level.total_quantity.checked_sub(first_order_node.current_quantity).ok_or(anyhow!("error occured in sub of total qty - current qyt"))?;
633 let next = first_order_node.next;
634 orderbook.ask.order_pool[head_idx] = None;
635 orderbook.ask.free_list.push(head_idx);
636 orders_touched += 1;
637 if let Some(next_order_idx) = next {
638 price_level.head = Some(next_order_idx);
639 } else {
640 span.record("reason", "partially_filled");
641 price_level.total_quantity = 0;
642 price_level.head = None;
643 price_level.tail = None;
644 price_level.order_count = 0;
645 break;
646 }
647 } else {
648 first_order_node.current_quantity = first_order_node.current_quantity.checked_sub(fill_quantity).ok_or(anyhow!("error occured subtracting fnq - fq"))?;
649 price_level.total_quantity = price_level.total_quantity.checked_sub(fill_quantity).ok_or(anyhow!("error occured subtracting fntq - fq"))?;
650 fill_quantity = 0;
651 orders_touched += 1;
652 span.record("filled", true);
653 }
654 }
655 None => {
656 return Err(anyhow!("failed to get head_idx from order pool"));
657 }
658 };
659 }else {
660 break;
661 }
662 }
663 remove_node = price_level.total_quantity == 0;
664 }
665 if remove_node {
666 match orderbook.ask.price_map.pop_first(){
667 Some(_) => {
668 levels_consumed += 1;
669 }
670 None => {
671 break;
672 }
673 };
674 }
675 }
676 if fill_quantity > 0{
677 let alloted_index = orderbook.create_buy_order(
678 order.engine_order_id,
679 OrderNode {
680 initial_quantity: order.initial_quantity,
681 current_quantity: fill_quantity,
682 market_limit: order.price.unwrap(),
683 next: None,
684 prev: None,
685 },
686 )?;
687 let order_location = OrderLocation {
688 security_id : order.security_id,
689 is_buy_side : order.is_buy_side,
690 order_index : alloted_index
691 };
692 self._global_registry.insert(order.engine_order_id, order_location);
693 span.record("order_type", "limit");
694 span.record("is_buy_side", true);
695 span.record("levels_consumed", levels_consumed);
696 span.record("orders_touched", orders_touched);
697 return Ok(Some(alloted_index))
698 }
699 Ok(None)
700 }
701 }
702 }
703 }
704}