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