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