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