clob_engine/order_book/matching_engine.rs
1use crate::order_book::{
2 orderbook::OrderBook,
3 types::{
4 BookDepth, CancelOutcome, EngineCancelOrder, EngineModifyOrder, EngineNewOrder,
5 MatchOutcome, ModifyOutcome, OrderNode, OrderType,
6 },
7};
8use anyhow::{Context, anyhow};
9use std::{collections::HashMap, time::Instant};
10
11#[derive(Debug)]
12pub struct MatchingEngine {
13 _book: HashMap<u32, OrderBook>,
14}
15
16impl MatchingEngine {
17 pub fn new() -> Self {
18 Self {
19 _book: HashMap::new(),
20 }
21 }
22
23 fn get_orderbook(&mut self, security_id: u32) -> Option<&mut OrderBook> {
24 let Some(book) = self._book.get_mut(&security_id) else {
25 return None;
26 };
27 Some(book)
28 }
29
30 pub fn modify(
31 &mut self,
32 order_id: u64,
33 security_id: u32,
34 new_price: Option<u32>,
35 new_qty: Option<u32>,
36 is_buy_side: bool,
37 ) -> Result<&'static str, anyhow::Error> {
38 let orderbook = self
39 .get_orderbook(security_id)
40 .context("Could not find the orderbook")?;
41 if let Ok(potential_modfication) = orderbook.modify_order(
42 order_id,
43 EngineModifyOrder {
44 order_id,
45 security_id,
46 new_price,
47 is_buy_side,
48 new_quantity: new_qty,
49 },
50 ) {
51 if let Some(modification_result) = potential_modfication {
52 match modification_result {
53 ModifyOutcome::Both {
54 new_price,
55 new_initial_qty,
56 old_current_qty,
57 } => {
58 let _ = self.match_order(EngineNewOrder {
59 engine_order_id: order_id,
60 price: Some(new_price),
61 initial_quantity: new_initial_qty,
62 current_quantity: old_current_qty,
63 is_buy_side,
64 security_id,
65 order_type: OrderType::GoodTillCancel,
66 });
67 return Ok("Both");
68 }
69 ModifyOutcome::Repriced {
70 new_price,
71 old_initial_qty,
72 old_current_qty,
73 } => {
74 let _ = self.match_order(EngineNewOrder {
75 engine_order_id: order_id,
76 price: Some(new_price),
77 initial_quantity: old_initial_qty,
78 current_quantity: old_current_qty,
79 is_buy_side,
80 security_id,
81 order_type: OrderType::GoodTillCancel,
82 });
83 return Ok("Repriced");
84 }
85 ModifyOutcome::Requantized {
86 old_price,
87 new_initial_qty,
88 old_current_qty,
89 } => {
90 let _ = self.match_order(EngineNewOrder {
91 engine_order_id: order_id,
92 price: Some(old_price),
93 initial_quantity: new_initial_qty,
94 current_quantity: old_current_qty,
95 is_buy_side,
96 security_id,
97 order_type: OrderType::GoodTillCancel,
98 });
99 return Ok("Requantized");
100 }
101 ModifyOutcome::Inplace => return Ok("Inplace"),
102 }
103 }
104 return Ok("No potential modification");
105 } else {
106 return Ok("No modification occured");
107 }
108 }
109
110 pub fn cancel(
111 &mut self,
112 order_id: u64,
113 security_id: u32,
114 is_buy_side: bool,
115 ) -> Result<CancelOutcome, anyhow::Error> {
116 let timer = Instant::now();
117 let orderbook = self
118 .get_orderbook(security_id)
119 .context("Could not find the orderbook")?;
120 if let Err(_) = orderbook.cancel_order(
121 order_id,
122 EngineCancelOrder {
123 is_buy_side,
124 security_id,
125 order_id,
126 },
127 ) {
128 let elapsed_time = timer.elapsed().as_micros() as f64;
129 return Ok(CancelOutcome::Failed(elapsed_time));
130 };
131 let elapsed_time = timer.elapsed().as_micros() as f64;
132 return Ok(CancelOutcome::Success(elapsed_time));
133 }
134
135 pub fn depth(
136 &self,
137 security_id: u32,
138 levels_count: Option<u32>,
139 ) -> Result<BookDepth, anyhow::Error> {
140 let Some(order_book) = self._book.get(&security_id) else {
141 return Err(anyhow!("orderbook doesn't exist"));
142 };
143 match order_book.depth(levels_count) {
144 Ok(book_depth) => Ok(book_depth),
145 Err(e) => Err(anyhow!("{}", e)),
146 }
147 }
148
149 pub fn match_order(&mut self, order: EngineNewOrder) -> Result<MatchOutcome, anyhow::Error> {
150 let timer = Instant::now();
151
152 let orderbook = match self._book.get_mut(&order.security_id) {
153 Some(orderbook) => orderbook,
154 None => self
155 ._book
156 .entry(order.security_id)
157 .or_insert(OrderBook::new()),
158 };
159
160 if !order.is_buy_side {
161 // for ASK order
162 match order.order_type {
163 OrderType::Market => {
164 // need to immediatly execute the order on the best of other half
165 let mut fill_quantity = order.initial_quantity;
166 let mut levels_consumed = 0;
167 let mut orders_touched = 0;
168 while fill_quantity > 0 {
169 let remove_node: bool;
170 {
171 let Some(mut price_node) = orderbook.bid.price_map.last_entry() else {
172 break;
173 };
174 let price_level = price_node.get_mut();
175 while price_level.total_quantity > 0 && fill_quantity > 0 {
176 if let Some(head_idx) = price_level.head {
177 match orderbook.bid.order_pool[head_idx].as_mut() {
178 Some(first_order_node) => {
179 if fill_quantity >= first_order_node.current_quantity {
180 fill_quantity -= first_order_node.current_quantity;
181
182 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"))?;
183 let next = first_order_node.next;
184 orderbook.bid.order_pool[head_idx] = None;
185 orderbook.bid.free_list.push(head_idx);
186 orders_touched += 1;
187 if let Some(next_order_idx) = next {
188 price_level.head = Some(next_order_idx);
189 } else {
190 price_level.total_quantity = 0;
191 price_level.head = None;
192 price_level.tail = None;
193 price_level.order_count = 0;
194 break;
195 }
196 } else {
197 first_order_node.current_quantity =
198 first_order_node
199 .current_quantity
200 .checked_sub(fill_quantity)
201 .ok_or(anyhow!(
202 "error occured subtracting fnq - fq"
203 ))?;
204 price_level.total_quantity = price_level
205 .total_quantity
206 .checked_sub(fill_quantity)
207 .ok_or(anyhow!(
208 "error occured subtracting fntq - fq"
209 ))?;
210 fill_quantity = 0;
211 orders_touched += 1;
212 }
213 }
214 None => {
215 return Err(anyhow!(
216 "failed to get head_idx from order pool"
217 ));
218 }
219 };
220 } else {
221 // price level has no head. i.e head = None
222 break;
223 }
224 }
225 remove_node = price_level.total_quantity == 0;
226 }
227 if remove_node {
228 match orderbook.bid.price_map.pop_last() {
229 Some(_) => {
230 levels_consumed += 1;
231 }
232 None => {
233 break;
234 }
235 };
236 }
237 }
238 let elapsed_time = timer.elapsed().as_micros() as f64;
239 Ok(MatchOutcome {
240 order_index: None,
241 levels_consumed,
242 orders_touched,
243 timer: elapsed_time,
244 })
245 }
246 OrderType::ImmediateOrCancel(market_limit) => {
247 let mut fill_quantity = order.initial_quantity;
248 let mut levels_consumed = 0;
249 let mut orders_touched = 0;
250 while fill_quantity > 0 {
251 let remove_node: bool;
252 {
253 let Some(mut price_node) = orderbook.bid.price_map.last_entry() else {
254 break;
255 };
256
257 if market_limit > *price_node.key() {
258 break;
259 }
260
261 let price_level = price_node.get_mut();
262 while price_level.total_quantity > 0 && fill_quantity > 0 {
263 if let Some(head_idx) = price_level.head {
264 match orderbook.bid.order_pool[head_idx].as_mut() {
265 Some(first_order_node) => {
266 if fill_quantity >= first_order_node.current_quantity {
267 fill_quantity -= first_order_node.current_quantity;
268 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"))?;
269 let next = first_order_node.next;
270 orderbook.bid.order_pool[head_idx] = None;
271 orderbook.bid.free_list.push(head_idx);
272 orders_touched += 1;
273 if let Some(next_order_idx) = next {
274 price_level.head = Some(next_order_idx);
275 } else {
276 price_level.total_quantity = 0;
277 price_level.head = None;
278 price_level.tail = None;
279 price_level.order_count = 0;
280 break;
281 }
282 } else {
283 first_order_node.current_quantity =
284 first_order_node
285 .current_quantity
286 .checked_sub(fill_quantity)
287 .ok_or(anyhow!(
288 "error occured subtracting fnq - fq"
289 ))?;
290 price_level.total_quantity = price_level
291 .total_quantity
292 .checked_sub(fill_quantity)
293 .ok_or(anyhow!(
294 "error occured subtracting fntq - fq"
295 ))?;
296 fill_quantity = 0;
297 orders_touched += 1;
298 }
299 }
300 None => {
301 return Err(anyhow!(
302 "failed to get head_idx from order pool"
303 ));
304 }
305 };
306 } else {
307 break;
308 }
309 }
310 remove_node = price_level.total_quantity == 0;
311 }
312 if remove_node {
313 match orderbook.bid.price_map.pop_last() {
314 Some(_) => {
315 levels_consumed += 1;
316 }
317 None => {
318 break;
319 }
320 };
321 }
322 }
323 let elapsed_time = timer.elapsed().as_micros() as f64;
324 Ok(MatchOutcome {
325 order_index: None,
326 levels_consumed,
327 orders_touched,
328 timer: elapsed_time,
329 })
330 }
331 OrderType::GoodTillCancel => {
332 let mut fill_quantity = order.initial_quantity;
333 let mut levels_consumed = 0;
334 let mut orders_touched = 0;
335 while fill_quantity > 0 {
336 let remove_node: bool;
337 {
338 let Some(mut price_node) = orderbook.bid.price_map.last_entry() else {
339 break;
340 };
341
342 match order.price {
343 Some(price) => {
344 if price > *price_node.key() {
345 break;
346 }
347 }
348 None => {
349 return Err(anyhow!(
350 "did not recieve price for limit order (SELL)"
351 ));
352 }
353 }
354 let price_level = price_node.get_mut();
355 while price_level.total_quantity > 0 && fill_quantity > 0 {
356 if let Some(head_idx) = price_level.head {
357 match orderbook.bid.order_pool[head_idx].as_mut() {
358 Some(first_order_node) => {
359 if fill_quantity >= first_order_node.current_quantity {
360 fill_quantity -= first_order_node.current_quantity;
361 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"))?;
362 let next = first_order_node.next;
363 orderbook.bid.order_pool[head_idx] = None;
364 orderbook.bid.free_list.push(head_idx);
365 orders_touched += 1;
366 if let Some(next_order_idx) = next {
367 price_level.head = Some(next_order_idx);
368 } else {
369 price_level.total_quantity = 0;
370 price_level.head = None;
371 price_level.tail = None;
372 price_level.order_count = 0;
373 break;
374 }
375 } else {
376 first_order_node.current_quantity =
377 first_order_node
378 .current_quantity
379 .checked_sub(fill_quantity)
380 .ok_or(anyhow!(
381 "error occured subtracting fnq - fq"
382 ))?;
383 price_level.total_quantity = price_level
384 .total_quantity
385 .checked_sub(fill_quantity)
386 .ok_or(anyhow!(
387 "error occured subtracting fntq - fq"
388 ))?;
389 fill_quantity = 0;
390 orders_touched += 1;
391 }
392 }
393 None => {
394 return Err(anyhow!(
395 "failed to get head_idx from order pool"
396 ));
397 }
398 };
399 } else {
400 break;
401 }
402 }
403 remove_node = price_level.total_quantity == 0;
404 }
405 if remove_node {
406 match orderbook.bid.price_map.pop_last() {
407 Some(_) => {
408 levels_consumed += 1;
409 }
410 None => {
411 break;
412 }
413 };
414 }
415 }
416 if fill_quantity > 0 {
417 let alloted_index = orderbook.create_sell_order(OrderNode {
418 order_id: order.engine_order_id,
419 initial_quantity: order.initial_quantity,
420 current_quantity: fill_quantity,
421 market_limit: order.price.unwrap(),
422 next: None,
423 prev: None,
424 })?;
425 let elapsed_time = timer.elapsed().as_micros() as f64;
426 return Ok(MatchOutcome {
427 order_index: Some(alloted_index as u32),
428 levels_consumed,
429 orders_touched,
430 timer: elapsed_time,
431 });
432 }
433 let elapsed_time = timer.elapsed().as_micros() as f64;
434 Ok(MatchOutcome {
435 order_index: None,
436 levels_consumed,
437 orders_touched,
438 timer: elapsed_time,
439 })
440 }
441 OrderType::FillOrKill(limit_price) => {
442 // PASS 1 — read-only availability check
443 let mut available_quantity: u32 = 0;
444 for (level_price, level) in orderbook.bid.price_map.iter().rev() {
445 if limit_price > *level_price {
446 break;
447 }
448 available_quantity =
449 available_quantity
450 .checked_add(level.total_quantity)
451 .ok_or(anyhow!("overflow during FOK availability check"))?;
452 if available_quantity >= order.initial_quantity {
453 break;
454 }
455 }
456
457 if available_quantity < order.initial_quantity {
458 // KILL — insufficient liquidity, reject without touching the book
459 let elapsed_time = timer.elapsed().as_micros() as f64;
460 return Ok(MatchOutcome {
461 order_index: None,
462 levels_consumed: 0,
463 orders_touched: 0,
464 timer: elapsed_time,
465 });
466 }
467
468 // PASS 2 — guaranteed to fully fill, execute now
469 let mut fill_quantity = order.initial_quantity;
470 let mut levels_consumed = 0;
471 let mut orders_touched = 0;
472
473 while fill_quantity > 0 {
474 let remove_node: bool;
475 {
476 let Some(mut price_node) = orderbook.bid.price_map.last_entry() else {
477 return Err(anyhow!(
478 "FOK pass 2 ran out of liquidity unexpectedly"
479 ));
480 };
481
482 if limit_price > *price_node.key() {
483 return Err(anyhow!("FOK pass 2 hit price limit unexpectedly"));
484 }
485
486 let price_level = price_node.get_mut();
487 while price_level.total_quantity > 0 && fill_quantity > 0 {
488 if let Some(head_idx) = price_level.head {
489 match orderbook.bid.order_pool[head_idx].as_mut() {
490 Some(first_order_node) => {
491 if fill_quantity >= first_order_node.current_quantity {
492 fill_quantity -= first_order_node.current_quantity;
493 price_level.total_quantity = price_level
494 .total_quantity
495 .checked_sub(first_order_node.current_quantity)
496 .ok_or(anyhow!(
497 "underflow in total_qty - current_qty"
498 ))?;
499 let next = first_order_node.next;
500 orderbook.bid.order_pool[head_idx] = None;
501 orderbook.bid.free_list.push(head_idx);
502 orders_touched += 1;
503 if let Some(next_order_idx) = next {
504 price_level.head = Some(next_order_idx);
505 } else {
506 price_level.total_quantity = 0;
507 price_level.head = None;
508 price_level.tail = None;
509 price_level.order_count = 0;
510 break;
511 }
512 } else {
513 first_order_node.current_quantity =
514 first_order_node
515 .current_quantity
516 .checked_sub(fill_quantity)
517 .ok_or(anyhow!(
518 "underflow in current_qty - fill_qty"
519 ))?;
520 price_level.total_quantity = price_level
521 .total_quantity
522 .checked_sub(fill_quantity)
523 .ok_or(anyhow!(
524 "underflow in total_qty - fill_qty"
525 ))?;
526 fill_quantity = 0;
527 orders_touched += 1;
528 }
529 }
530 None => {
531 return Err(anyhow!(
532 "failed to get head_idx from order pool"
533 ));
534 }
535 };
536 } else {
537 break;
538 }
539 }
540 remove_node = price_level.total_quantity == 0;
541 }
542 if remove_node {
543 orderbook.bid.price_map.pop_last();
544 levels_consumed += 1;
545 }
546 }
547
548 let elapsed_time = timer.elapsed().as_micros() as f64;
549 Ok(MatchOutcome {
550 order_index: None,
551 levels_consumed,
552 orders_touched,
553 timer: elapsed_time,
554 })
555 }
556 }
557 } else {
558 match order.order_type {
559 OrderType::Market => {
560 // need to immediatly execute the order on the best of other half
561 let mut fill_quantity = order.initial_quantity;
562 let mut levels_consumed = 0;
563 let mut orders_touched = 0;
564 while fill_quantity > 0 {
565 let remove_node: bool;
566 {
567 let Some(mut price_node) = orderbook.ask.price_map.first_entry() else {
568 break;
569 };
570 let price_level = price_node.get_mut();
571 while price_level.total_quantity > 0 && fill_quantity > 0 {
572 if let Some(head_idx) = price_level.head {
573 match orderbook.ask.order_pool[head_idx].as_mut() {
574 Some(first_order_node) => {
575 if fill_quantity >= first_order_node.current_quantity {
576 fill_quantity -= first_order_node.current_quantity;
577 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"))?;
578 let next = first_order_node.next;
579 orderbook.ask.order_pool[head_idx] = None;
580 orderbook.ask.free_list.push(head_idx);
581 orders_touched += 1;
582 if let Some(next_order_idx) = next {
583 price_level.head = Some(next_order_idx);
584 } else {
585 price_level.total_quantity = 0;
586 price_level.head = None;
587 price_level.tail = None;
588 price_level.order_count = 0;
589 break;
590 }
591 } else {
592 first_order_node.current_quantity =
593 first_order_node
594 .current_quantity
595 .checked_sub(fill_quantity)
596 .ok_or(anyhow!(
597 "error occured subtracting fnq - fq"
598 ))?;
599 price_level.total_quantity = price_level
600 .total_quantity
601 .checked_sub(fill_quantity)
602 .ok_or(anyhow!(
603 "error occured subtracting fntq - fq"
604 ))?;
605 fill_quantity = 0;
606 orders_touched += 1;
607 }
608 }
609 None => {
610 return Err(anyhow!(
611 "failed to get head_idx from order pool"
612 ));
613 }
614 };
615 } else {
616 break;
617 }
618 }
619 remove_node = price_level.total_quantity == 0;
620 }
621 if remove_node {
622 match orderbook.ask.price_map.pop_first() {
623 Some(_) => {
624 levels_consumed += 1;
625 }
626 None => {
627 break;
628 }
629 };
630 }
631 }
632 let elapsed_time = timer.elapsed().as_micros() as f64;
633 Ok(MatchOutcome {
634 order_index: None,
635 levels_consumed,
636 orders_touched,
637 timer: elapsed_time,
638 })
639 }
640 OrderType::ImmediateOrCancel(market_limit) => {
641 let mut fill_quantity = order.initial_quantity;
642 let mut levels_consumed = 0;
643 let mut orders_touched = 0;
644 while fill_quantity > 0 {
645 let remove_node: bool;
646 {
647 let Some(mut price_node) = orderbook.ask.price_map.first_entry() else {
648 break;
649 };
650 if market_limit < *price_node.key() {
651 break;
652 }
653
654 let price_level = price_node.get_mut();
655 while price_level.total_quantity > 0 && fill_quantity > 0 {
656 let head_pointer = price_level.head;
657 if let Some(head_idx) = head_pointer {
658 match orderbook.ask.order_pool[head_idx].as_mut() {
659 Some(first_order_node) => {
660 if fill_quantity >= first_order_node.current_quantity {
661 fill_quantity -= first_order_node.current_quantity;
662 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"))?;
663 let next = first_order_node.next;
664 orderbook.ask.order_pool[head_idx] = None;
665 orderbook.ask.free_list.push(head_idx);
666 orders_touched += 1;
667 if let Some(next_order_idx) = next {
668 price_level.head = Some(next_order_idx);
669 } else {
670 price_level.head = None;
671 price_level.total_quantity = 0;
672 price_level.head = None;
673 price_level.tail = None;
674 price_level.order_count = 0;
675 break;
676 }
677 } else {
678 first_order_node.current_quantity =
679 first_order_node
680 .current_quantity
681 .checked_sub(fill_quantity)
682 .ok_or(anyhow!(
683 "error occured subtracting fnq - fq"
684 ))?;
685 price_level.total_quantity = price_level
686 .total_quantity
687 .checked_sub(fill_quantity)
688 .ok_or(anyhow!(
689 "error occured subtracting fntq - fq"
690 ))?;
691 fill_quantity = 0;
692 orders_touched += 1;
693 }
694 }
695 None => {
696 return Err(anyhow!(
697 "failed to get head_idx from order pool"
698 ));
699 }
700 };
701 } else {
702 break;
703 }
704 }
705 remove_node = price_level.total_quantity == 0;
706 }
707 if remove_node {
708 match orderbook.ask.price_map.pop_first() {
709 Some(_) => {
710 levels_consumed += 1;
711 }
712 None => {
713 break;
714 }
715 };
716 }
717 }
718 let elapsed_time = timer.elapsed().as_micros() as f64;
719 Ok(MatchOutcome {
720 order_index: None,
721 levels_consumed,
722 orders_touched,
723 timer: elapsed_time,
724 })
725 }
726 OrderType::GoodTillCancel => {
727 let mut fill_quantity = order.initial_quantity;
728 let mut levels_consumed = 0;
729 let mut orders_touched = 0;
730 while fill_quantity > 0 {
731 let remove_node: bool;
732 {
733 let Some(mut price_node) = orderbook.ask.price_map.first_entry() else {
734 break;
735 };
736
737 match order.price {
738 Some(price) => {
739 if price < *price_node.key() {
740 break;
741 }
742 }
743 None => {
744 return Err(anyhow!("did not recieve price for limit(BUY)"));
745 }
746 }
747 let price_level = price_node.get_mut();
748 while price_level.total_quantity > 0 && fill_quantity > 0 {
749 if let Some(head_idx) = price_level.head {
750 match orderbook.ask.order_pool[head_idx].as_mut() {
751 Some(first_order_node) => {
752 if fill_quantity >= first_order_node.current_quantity {
753 fill_quantity -= first_order_node.current_quantity;
754 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"))?;
755 let next = first_order_node.next;
756 orderbook.ask.order_pool[head_idx] = None;
757 orderbook.ask.free_list.push(head_idx);
758 orders_touched += 1;
759 if let Some(next_order_idx) = next {
760 price_level.head = Some(next_order_idx);
761 } else {
762 price_level.total_quantity = 0;
763 price_level.head = None;
764 price_level.tail = None;
765 price_level.order_count = 0;
766 break;
767 }
768 } else {
769 first_order_node.current_quantity =
770 first_order_node
771 .current_quantity
772 .checked_sub(fill_quantity)
773 .ok_or(anyhow!(
774 "error occured subtracting fnq - fq"
775 ))?;
776 price_level.total_quantity = price_level
777 .total_quantity
778 .checked_sub(fill_quantity)
779 .ok_or(anyhow!(
780 "error occured subtracting fntq - fq"
781 ))?;
782 fill_quantity = 0;
783 orders_touched += 1;
784 }
785 }
786 None => {
787 return Err(anyhow!(
788 "failed to get head_idx from order pool"
789 ));
790 }
791 };
792 } else {
793 break;
794 }
795 }
796 remove_node = price_level.total_quantity == 0;
797 }
798 if remove_node {
799 match orderbook.ask.price_map.pop_first() {
800 Some(_) => {
801 levels_consumed += 1;
802 }
803 None => {
804 break;
805 }
806 };
807 }
808 }
809 if fill_quantity > 0 {
810 let alloted_index = orderbook.create_buy_order(OrderNode {
811 order_id: order.engine_order_id,
812 initial_quantity: order.initial_quantity,
813 current_quantity: fill_quantity,
814 market_limit: order.price.unwrap(),
815 next: None,
816 prev: None,
817 })?;
818 let elapsed_time = timer.elapsed().as_micros() as f64;
819 return Ok(MatchOutcome {
820 order_index: Some(alloted_index as u32),
821 levels_consumed,
822 orders_touched,
823 timer: elapsed_time,
824 });
825 }
826 let elapsed_time = timer.elapsed().as_micros() as f64;
827 Ok(MatchOutcome {
828 order_index: None,
829 levels_consumed,
830 orders_touched,
831 timer: elapsed_time,
832 })
833 }
834 OrderType::FillOrKill(limit_price) => {
835 // PASS 1 — read-only availability check
836 let mut available_quantity: u32 = 0;
837 for (level_price, level) in orderbook.ask.price_map.iter() {
838 if limit_price < *level_price {
839 break;
840 }
841 available_quantity =
842 available_quantity
843 .checked_add(level.total_quantity)
844 .ok_or(anyhow!("overflow during FOK availability check"))?;
845 if available_quantity >= order.initial_quantity {
846 break;
847 }
848 }
849
850 if available_quantity < order.initial_quantity {
851 // KILL — insufficient liquidity, reject without touching the book
852 let elapsed_time = timer.elapsed().as_micros() as f64;
853 return Ok(MatchOutcome {
854 order_index: None,
855 levels_consumed: 0,
856 orders_touched: 0,
857 timer: elapsed_time,
858 });
859 }
860
861 // PASS 2 — guaranteed to fully fill, execute now
862 let mut fill_quantity = order.initial_quantity;
863 let mut levels_consumed = 0;
864 let mut orders_touched = 0;
865
866 while fill_quantity > 0 {
867 let remove_node: bool;
868 {
869 let Some(mut price_node) = orderbook.ask.price_map.first_entry() else {
870 return Err(anyhow!(
871 "FOK pass 2 ran out of liquidity unexpectedly"
872 ));
873 };
874
875 if limit_price < *price_node.key() {
876 return Err(anyhow!("FOK pass 2 hit price limit unexpectedly"));
877 }
878
879 let price_level = price_node.get_mut();
880 while price_level.total_quantity > 0 && fill_quantity > 0 {
881 if let Some(head_idx) = price_level.head {
882 match orderbook.ask.order_pool[head_idx].as_mut() {
883 Some(first_order_node) => {
884 if fill_quantity >= first_order_node.current_quantity {
885 fill_quantity -= first_order_node.current_quantity;
886 price_level.total_quantity = price_level
887 .total_quantity
888 .checked_sub(first_order_node.current_quantity)
889 .ok_or(anyhow!(
890 "underflow in total_qty - current_qty"
891 ))?;
892 let next = first_order_node.next;
893 orderbook.ask.order_pool[head_idx] = None;
894 orderbook.ask.free_list.push(head_idx);
895 orders_touched += 1;
896 if let Some(next_order_idx) = next {
897 price_level.head = Some(next_order_idx);
898 } else {
899 price_level.total_quantity = 0;
900 price_level.head = None;
901 price_level.tail = None;
902 price_level.order_count = 0;
903 break;
904 }
905 } else {
906 first_order_node.current_quantity =
907 first_order_node
908 .current_quantity
909 .checked_sub(fill_quantity)
910 .ok_or(anyhow!(
911 "underflow in current_qty - fill_qty"
912 ))?;
913 price_level.total_quantity = price_level
914 .total_quantity
915 .checked_sub(fill_quantity)
916 .ok_or(anyhow!(
917 "underflow in total_qty - fill_qty"
918 ))?;
919 fill_quantity = 0;
920 orders_touched += 1;
921 }
922 }
923 None => {
924 return Err(anyhow!(
925 "failed to get head_idx from order pool"
926 ));
927 }
928 };
929 } else {
930 break;
931 }
932 }
933 remove_node = price_level.total_quantity == 0;
934 }
935 if remove_node {
936 orderbook.ask.price_map.pop_first();
937 levels_consumed += 1;
938 }
939 }
940
941 let elapsed_time = timer.elapsed().as_micros() as f64;
942 Ok(MatchOutcome {
943 order_index: None,
944 levels_consumed,
945 orders_touched,
946 timer: elapsed_time,
947 })
948 }
949 }
950 }
951 }
952}