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<(), anyhow::Error> {
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 ModifyOrder {
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 NewOrder {
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(());
91 }
92 span.record("intermediate_error", "Failed to delete from global registry");
93 },
94 ModifyOutcome::Repriced { new_price, old_initial_qty, old_current_qty } =>
95 {
96 span.record("modify_outcome", "price");
97 if let Some(_) = self._global_registry.delete(&global_order_id){
98 let _ = self.match_order(
99 NewOrder {
100 engine_order_id: global_order_id,
101 price: Some(new_price),
102 initial_quantity: old_initial_qty,
103 current_quantity : old_current_qty,
104 is_buy_side,
105 security_id,
106 order_type: OrderType::Limit,
107 },
108 span);
109 return Ok(());
110 }
111 span.record("intermediate_error", "Failed to delete from global registry");
112 },
113 ModifyOutcome::Requantized { old_price, new_initial_qty, old_current_qty } => {
114 span.record("modify_outcome", "qty");
115 if let Some(_) = self._global_registry.delete(&global_order_id){
116 let _ = self.match_order(
117 NewOrder {
118 engine_order_id: global_order_id,
119 price: Some(old_price),
120 initial_quantity: new_initial_qty,
121 current_quantity : old_current_qty,
122 is_buy_side,
123 security_id,
124 order_type: OrderType::Limit,
125 }, span);
126 return Ok(());
127 }
128 span.record("intermediate_error", "Failed to delete from global registry");
129 },
130 ModifyOutcome::Inplace => {
131 span.record("modify_outcome", "qty reduction");
132 return Ok(());
133 }
134 }
135 }
136 } else {
137 return Ok(());
138 }
139 Ok(())
140 }
141
142 pub fn cancel(&mut self, global_order_id: Uuid, span: &Span) -> Result<CancelOutcome, anyhow::Error>{
143 let (order_index, is_buy_side,_, orderbook) = self
144 .get_orderbook(global_order_id, span)
145 .context("Could not find the orderbook")?;
146 if let Err(_) = orderbook.cancel_order(global_order_id, CancelOrder{is_buy_side, order_index}){
147 span.record("reason", "orderbook cancellation failed");
148 span.record("success_status", false);
149 return Ok(CancelOutcome::Failed);
150 };
151 if let Some(_) = self._global_registry.delete(&global_order_id){
152 span.record("success_status", true);
153 return Ok(CancelOutcome::Success)
154 };
155 span.record("reason", "Registry cancellation failed");
156 span.record("success_status", false);
157 Ok(CancelOutcome::Failed)
158 }
159
160 pub fn depth(&self, security_id : Uuid, levels_count :Option<u32>, span: &Span ) -> Result<BookDepth, anyhow::Error>{
161 let _gaurd = span.enter();
162 span.record("security_id", security_id.to_string());
163 let Some(order_book) = self._book.get(&security_id) else {
164 span.record("status", "failed");
165 span.record("reason", "orderbook doesn't exist");
166 return Err(anyhow!(""))
167 };
168 match order_book.depth(levels_count){
169 Ok(book_depth) => {
170 span.record("status", "success");
171 span.record("reason", "None");
172 Ok(book_depth)
173 },
174 Err(e) => Err(anyhow!("{}", e))
175 }
176 }
177
178 pub fn match_order(&mut self, order: NewOrder, span: &Span) -> Result<(), anyhow::Error> {
179
180 let _gaurd = span.enter();
181
182 let orderbook = match self._book.get_mut(&order.security_id){
183 Some(orderbook) => {
184 orderbook
185 }
186 None => {
187 self._book.entry(order.security_id).or_insert(OrderBook::new(1))
188 }
189 };
190
191 if !order.is_buy_side {
192 match order.order_type {
194 OrderType::Market(None) => {
195 let mut fill_quantity = order.initial_quantity;
197 let mut levels_touched = 0;
198 let mut orders_consumed = 0;
199 while fill_quantity > 0 {
200 let remove_node: bool;
201 {
202 let Some(mut price_node) = orderbook.bid.price_map.last_entry() else {
203 break;
204 };
205 let price_level = price_node.get_mut();
206 while price_level.total_quantity > 0 && fill_quantity > 0 {
207 if let Some(head_idx) = price_level.head{
208
209 let first_order_node =
210 orderbook.bid.order_pool[head_idx].as_mut().unwrap();
211 if fill_quantity >= first_order_node.current_quantity {
212 fill_quantity -= first_order_node.current_quantity;
213 price_level.total_quantity -= first_order_node.current_quantity;
214 let next = first_order_node.next;
215 orderbook.bid.order_pool[head_idx] = None;
216 orderbook.bid.free_list.push(head_idx);
217 orders_consumed += 1;
218 if let Some(next_order_idx) = next {
219 price_level.head = Some(next_order_idx);
220 } else {
221 span.record("reason", "exhausted");
222 price_level.total_quantity = 0;
223 break;
224 }
225 } else {
226 first_order_node.current_quantity -= fill_quantity;
227 price_level.total_quantity -= fill_quantity;
228 fill_quantity = 0;
229 span.record("filled", true);
230 }
231 }else {
232 break;
233 }
234 }
235 remove_node = price_level.total_quantity == 0;
236 }
237 if remove_node {
238 orderbook.bid.price_map.pop_last();
239 levels_touched += 1;
240 }
241 }
242 span.record("order_type", "market");
243 span.record("is_buy_side", false);
244 span.record("levels_touched", levels_touched);
245 span.record("orders_consumed", orders_consumed);
246 }
247 OrderType::Market(market_limit) => {
248 let mut fill_quantity = order.initial_quantity;
249 let mut levels_touched = 0;
250 let mut orders_consumed = 0;
251 while fill_quantity > 0 {
252 let remove_node: bool;
253 {
254 let Some(mut price_node) = orderbook.bid.price_map.last_entry() else {
255 break;
256 };
257 if market_limit.unwrap() > *price_node.key() {
258 break;
259 }
260 let price_level = price_node.get_mut();
261 while price_level.total_quantity > 0 && fill_quantity > 0 {
262 if let Some(head_idx) = price_level.head{
263
264 let first_order_node =
265 orderbook.bid.order_pool[head_idx].as_mut().unwrap();
266 if fill_quantity >= first_order_node.current_quantity {
267 fill_quantity -= first_order_node.current_quantity;
268 price_level.total_quantity -= first_order_node.current_quantity;
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_consumed += 1;
273 if let Some(next_order_idx) = next {
274 price_level.head = Some(next_order_idx);
275 } else {
276 span.record("reason", "exhausted");
277 price_level.total_quantity = 0;
278 break;
279 }
280 } else {
281 first_order_node.current_quantity -= fill_quantity;
282 price_level.total_quantity -= fill_quantity;
283 fill_quantity = 0;
284 span.record("filled", true);
285 }
286 }else {
287 break;
288 }
289 }
290 remove_node = price_level.total_quantity == 0;
291 }
292 if remove_node {
293 orderbook.bid.price_map.pop_last();
294 levels_touched += 1;
295 }
296 }
297 span.record("order_type", "market");
298 span.record("is_buy_side", false);
299 span.record("levels_touched", levels_touched);
300 span.record("orders_consumed", orders_consumed);
301 }
302 OrderType::Limit => {
303 let mut fill_quantity = order.initial_quantity;
304 let mut levels_touched = 0;
305 let mut orders_consumed = 0;
306 while fill_quantity > 0 {
307 let remove_node: bool;
308 {
309 let Some(mut price_node) = orderbook.bid.price_map.last_entry() else {
310 break;
311 };
312 if order.price > Some(*price_node.key()) {
313 break;
314 }
315 let price_level = price_node.get_mut();
316 while price_level.total_quantity > 0 && fill_quantity > 0 {
317 if let Some(head_idx) = price_level.head{
318 let first_order_node = orderbook.bid.order_pool[head_idx].as_mut().unwrap();
319 if fill_quantity >= first_order_node.current_quantity {
320 fill_quantity -= first_order_node.current_quantity;
321 price_level.total_quantity -= first_order_node.current_quantity;
322 let next = first_order_node.next;
323 orderbook.bid.order_pool[head_idx] = None;
324 orderbook.bid.free_list.push(head_idx);
325 orders_consumed += 1;
326 if let Some(next_order_idx) = next {
327 price_level.head = Some(next_order_idx);
328 } else {
329 span.record("reason", "partially_filled");
330 price_level.total_quantity = 0;
331 break;
332 }
333 } else {
334 first_order_node.current_quantity -= fill_quantity;
335 price_level.total_quantity -= fill_quantity;
336 fill_quantity = 0;
337 span.record("filled", true);
338 }
339 }else {
340 break;
341 }
342 }
343 remove_node = price_level.total_quantity == 0;
344 }
345 if remove_node {
346 orderbook.bid.price_map.pop_last();
347 levels_touched += 1;
348 }
349 }
350 let alloted_index = orderbook.create_sell_order(
351 order.engine_order_id,
352 OrderNode {
353 initial_quantity: order.initial_quantity,
354 current_quantity: fill_quantity,
355 market_limit: order.price.unwrap(),
356 next: None,
357 prev: None,
358 },
359 )?;
360 let order_location = OrderLocation {
361 security_id : order.security_id,
362 is_buy_side : order.is_buy_side,
363 order_index : alloted_index
364 };
365 self._global_registry.insert(order.engine_order_id, order_location);
366 span.record("order_type", "limit");
367 span.record("is_buy_side", false);
368 span.record("levels_touched", levels_touched);
369 span.record("orders_consumed", orders_consumed);
370 }
371 }
372 } else {
373 match order.order_type {
374 OrderType::Market(None) => {
375 let mut fill_quantity = order.initial_quantity;
377 let mut levels_touched = 0;
378 let mut orders_consumed = 0;
379 while fill_quantity > 0 {
380 let remove_node: bool;
381 {
382 let Some(mut price_node) = orderbook.ask.price_map.first_entry() else {
383 break;
384 };
385 let price_level = price_node.get_mut();
386 while price_level.total_quantity > 0 && fill_quantity > 0 {
387 if let Some(head_idx) = price_level.head{
388 let first_order_node =
389 orderbook.ask.order_pool[head_idx].as_mut().unwrap();
390 if fill_quantity >= first_order_node.current_quantity {
391 fill_quantity -= first_order_node.current_quantity;
392 price_level.total_quantity -= first_order_node.current_quantity;
393 let next = first_order_node.next;
394 orderbook.ask.order_pool[head_idx] = None;
395 orderbook.ask.free_list.push(head_idx);
396 orders_consumed += 1;
397 if let Some(next_order_idx) = next {
398 price_level.head = Some(next_order_idx);
399 } else {
400 span.record("reason", "exhausted");
401 price_level.total_quantity = 0;
402 break;
403 }
404 } else {
405 first_order_node.current_quantity -= fill_quantity;
406 price_level.total_quantity -= fill_quantity;
407 fill_quantity = 0;
408 span.record("filled", true);
409 }
410 }
411 else {
412 break;
413 }
414 }
415 remove_node = price_level.total_quantity == 0;
416 }
417 if remove_node {
418 orderbook.bid.price_map.pop_first();
419 levels_touched += 1;
420 }
421 }
422 span.record("order_type", "market");
423 span.record("is_buy_side", true);
424 span.record("levels_touched", levels_touched);
425 span.record("orders_consumed", orders_consumed);
426 }
427 OrderType::Market(market_limit) => {
428 let mut fill_quantity = order.initial_quantity;
429 let mut levels_touched = 0;
430 let mut orders_consumed = 0;
431 while fill_quantity > 0 {
432 let remove_node: bool;
433 {
434 let Some(mut price_node) = orderbook.ask.price_map.first_entry() else {
435 break;
436 };
437 if market_limit.unwrap() < *price_node.key() {
438 break;
439 }
440 let price_level = price_node.get_mut();
441 while price_level.total_quantity > 0 && fill_quantity > 0 {
442 let head_pointer = price_level.head;
443 if let Some(head_idx) = head_pointer{
444 let first_order_node =
445 orderbook.ask.order_pool[head_idx].as_mut().unwrap();
446 if fill_quantity >= first_order_node.current_quantity {
447 fill_quantity -= first_order_node.current_quantity;
448 price_level.total_quantity -= first_order_node.current_quantity;
449 let next = first_order_node.next;
450 orderbook.ask.order_pool[head_idx] = None;
451 orderbook.ask.free_list.push(head_idx);
452 orders_consumed += 1;
453 if let Some(next_order_idx) = next {
454 price_level.head = Some(next_order_idx);
455 } else {
456 span.record("reason", "exhausted");
457 price_level.head = None;
458 price_level.total_quantity = 0;
459 break;
460 }
461 } else {
462 first_order_node.current_quantity -= fill_quantity;
463 price_level.total_quantity -= fill_quantity;
464 fill_quantity = 0;
465 span.record("filled", true);
466 }
467 }
468 else {
469 break;
470 }
471 }
472 remove_node = price_level.total_quantity == 0;
473 }
474 if remove_node {
475 orderbook.bid.price_map.pop_first();
476 levels_touched += 1;
477 }
478 }
479 span.record("order_type", "market");
480 span.record("is_buy_side", true);
481 span.record("levels_touched", levels_touched);
482 span.record("orders_consumed", orders_consumed);
483 }
484 OrderType::Limit => {
485 let mut fill_quantity = order.initial_quantity;
486 let mut levels_touched = 0;
487 let mut orders_consumed = 0;
488 while fill_quantity > 0 {
489 let remove_node: bool;
490 {
491 let Some(mut price_node) = orderbook.ask.price_map.first_entry() else {
492 break;
493 };
494 if order.price < Some(*price_node.key()) {
495 break;
496 }
497 let price_level = price_node.get_mut();
498 while price_level.total_quantity > 0 && fill_quantity > 0 {
499 if let Some(head_idx) = price_level.head{
500
501 let first_order_node = orderbook.ask.order_pool[head_idx].as_mut().unwrap();
502 if fill_quantity >= first_order_node.current_quantity {
503 fill_quantity -= first_order_node.current_quantity;
504 price_level.total_quantity -= first_order_node.current_quantity;
505 let next = first_order_node.next;
506 orderbook.ask.order_pool[head_idx] = None;
507 orderbook.ask.free_list.push(head_idx);
508 orders_consumed += 1;
509 if let Some(next_order_idx) = next {
510 price_level.head = Some(next_order_idx);
511 } else {
512 span.record("reason", "partially_filled");
513 price_level.total_quantity = 0;
514 break;
515 }
516 } else {
517 first_order_node.current_quantity -= fill_quantity;
518 price_level.total_quantity -= fill_quantity;
519 fill_quantity = 0;
520 span.record("filled", true);
521 }
522 }else {
523 break;
524 }
525 }
526 remove_node = price_level.total_quantity == 0;
527 }
528 if remove_node {
529 orderbook.bid.price_map.pop_first();
530 levels_touched += 1;
531 }
532 }
533 let alloted_index = orderbook.create_buy_order(
534 order.engine_order_id,
535 OrderNode {
536 initial_quantity: order.initial_quantity,
537 current_quantity: fill_quantity,
538 market_limit: order.price.unwrap(),
539 next: None,
540 prev: None,
541 },
542 )?;
543 let order_location = OrderLocation {
544 security_id : order.security_id,
545 is_buy_side : order.is_buy_side,
546 order_index : alloted_index
547 };
548 self._global_registry.insert(order.engine_order_id, order_location);
549 span.record("order_type", "limit");
550 span.record("is_buy_side", true);
551 span.record("levels_touched", levels_touched);
552 span.record("orders_consumed", orders_consumed);
553 }
554 }
555 }
556 Ok(())
557 }
558}