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 span.record("security_id", security_id.to_string());
162 let Some(order_book) = self._book.get(&security_id) else {
163 span.record("status", "failed");
164 span.record("reason", "orderbook doesn't exist");
165 return Err(anyhow!(""))
166 };
167 match order_book.depth(levels_count){
168 Ok(book_depth) => {
169 span.record("status", "success");
170 span.record("reason", "None");
171 Ok(book_depth)
172 },
173 Err(e) => Err(anyhow!("{}", e))
174 }
175 }
176
177 pub fn match_order(&mut self, order: NewOrder, span: &Span) -> Result<(), anyhow::Error> {
178
179 let _gaurd = span.enter();
180
181 let orderbook = match self._book.get_mut(&order.security_id){
182 Some(orderbook) => {
183 orderbook
184 }
185 None => {
186 self._book.entry(order.security_id).or_insert(OrderBook::new(1))
187 }
188 };
189
190 if !order.is_buy_side {
191 match order.order_type {
193 OrderType::Market(None) => {
194 let mut fill_quantity = order.initial_quantity;
196 let mut levels_touched = 0;
197 let mut orders_consumed = 0;
198 while fill_quantity > 0 {
199 let remove_node: bool;
200 {
201 let Some(mut price_node) = orderbook.bid.price_map.first_entry() else {
202 break;
203 };
204 let price_level = price_node.get_mut();
205 while price_level.total_quantity > 0 && fill_quantity > 0 {
206 let head_idx = price_level.head;
207 let first_order_node =
208 orderbook.bid.order_pool[head_idx].as_mut().unwrap();
209 if fill_quantity >= first_order_node.current_quantity {
210 fill_quantity -= first_order_node.current_quantity;
211 price_level.total_quantity -= first_order_node.current_quantity;
212 let next = first_order_node.next;
213 orderbook.bid.order_pool[head_idx] = None;
214 orderbook.bid.free_list.push(head_idx);
215 orders_consumed += 1;
216 if let Some(next_order_idx) = next {
217 price_level.head = next_order_idx;
218 } else {
219 span.record("reason", "exhausted");
220 break;
221 }
222 } else {
223 first_order_node.current_quantity -= fill_quantity;
224 price_level.total_quantity -= fill_quantity;
225 fill_quantity = 0;
226 span.record("filled", true);
227 }
228 }
229 span.record("reason", "orderbook is empty");
230 span.record("filled", false);
231 remove_node = price_level.total_quantity == 0;
232 }
233 if remove_node {
234 orderbook.bid.price_map.pop_first();
235 levels_touched += 1;
236 }
237 }
238 span.record("order_type", "market");
239 span.record("is_buy_side", false);
240 span.record("levels_touched", levels_touched);
241 span.record("orders_consumed", orders_consumed);
242 }
243 OrderType::Market(market_limit) => {
244 let mut fill_quantity = order.initial_quantity;
245 let mut levels_touched = 0;
246 let mut orders_consumed = 0;
247 while fill_quantity > 0 {
248 let remove_node: bool;
249 {
250 let Some(mut price_node) = orderbook.bid.price_map.first_entry() else {
251 break;
252 };
253 if market_limit.unwrap() >= *price_node.key() {
254 break;
255 }
256 let price_level = price_node.get_mut();
257 while price_level.total_quantity > 0 && fill_quantity > 0 {
258 let head_idx = price_level.head;
259 let first_order_node =
260 orderbook.bid.order_pool[head_idx].as_mut().unwrap();
261 if fill_quantity >= first_order_node.current_quantity {
262 fill_quantity -= first_order_node.current_quantity;
263 price_level.total_quantity -= first_order_node.current_quantity;
264 let next = first_order_node.next;
265 orderbook.bid.order_pool[head_idx] = None;
266 orderbook.bid.free_list.push(head_idx);
267 orders_consumed += 1;
268 if let Some(next_order_idx) = next {
269 price_level.head = next_order_idx;
270 } else {
271 span.record("reason", "exhausted");
272 break;
273 }
274 } else {
275 first_order_node.current_quantity -= fill_quantity;
276 price_level.total_quantity -= fill_quantity;
277 fill_quantity = 0;
278 span.record("filled", true);
279 }
280 }
281 span.record("reason", "orderbook is empty");
282 span.record("filled", false);
283 remove_node = price_level.total_quantity == 0;
284 }
285 if remove_node {
286 orderbook.bid.price_map.pop_first();
287 levels_touched += 1;
288 }
289 }
290 span.record("order_type", "market");
291 span.record("is_buy_side", false);
292 span.record("levels_touched", levels_touched);
293 span.record("orders_consumed", orders_consumed);
294 }
295 OrderType::Limit => {
296 let mut fill_quantity = order.initial_quantity;
297 let mut levels_touched = 0;
298 let mut orders_consumed = 0;
299 while fill_quantity > 0 {
300 let remove_node: bool;
301 {
302 let Some(mut price_node) = orderbook.bid.price_map.first_entry() else {
303 break;
304 };
305 if order.price >= Some(*price_node.key()) {
306 break;
307 }
308 let price_level = price_node.get_mut();
309 while price_level.total_quantity > 0 && fill_quantity > 0 {
310 let head_idx = price_level.head;
311 let first_order_node =
312 orderbook.bid.order_pool[head_idx].as_mut().unwrap();
313 if fill_quantity >= first_order_node.current_quantity {
314 fill_quantity -= first_order_node.current_quantity;
315 price_level.total_quantity -= first_order_node.current_quantity;
316 let next = first_order_node.next;
317 orderbook.bid.order_pool[head_idx] = None;
318 orderbook.bid.free_list.push(head_idx);
319 orders_consumed += 1;
320 if let Some(next_order_idx) = next {
321 price_level.head = next_order_idx;
322 } else {
323 span.record("reason", "partially_filled");
324 break;
325 }
326 } else {
327 first_order_node.current_quantity -= fill_quantity;
328 price_level.total_quantity -= fill_quantity;
329 fill_quantity = 0;
330 }
331 }
332 span.record("reason", "orderbook is empty");
333 span.record("filled", false);
334 remove_node = price_level.total_quantity == 0;
335 }
336 if remove_node {
337 orderbook.bid.price_map.pop_first();
338 levels_touched += 1;
339 }
340 }
341 let alloted_index = orderbook.create_sell_order(
342 order.engine_order_id,
343 OrderNode {
344 initial_quantity: order.initial_quantity,
345 current_quantity: fill_quantity,
346 market_limit: order.price.unwrap(),
347 next: None,
348 prev: None,
349 },
350 )?;
351 let order_location = OrderLocation {
352 security_id : order.security_id,
353 is_buy_side : order.is_buy_side,
354 order_index : alloted_index
355 };
356 self._global_registry.insert(order.engine_order_id, order_location);
357 span.record("order_type", "limit");
358 span.record("is_buy_side", false);
359 span.record("levels_touched", levels_touched);
360 span.record("orders_consumed", orders_consumed);
361 }
362 }
363 } else {
364 match order.order_type {
365 OrderType::Market(None) => {
366 let mut fill_quantity = order.initial_quantity;
368 let mut levels_touched = 0;
369 let mut orders_consumed = 0;
370 while fill_quantity > 0 {
371 let remove_node: bool;
372 {
373 let Some(mut price_node) = orderbook.ask.price_map.last_entry() else {
374 break;
375 };
376 let price_level = price_node.get_mut();
377 while price_level.total_quantity > 0 && fill_quantity > 0 {
378 let head_idx = price_level.head;
379 let first_order_node =
380 orderbook.ask.order_pool[head_idx].as_mut().unwrap();
381 if fill_quantity >= first_order_node.current_quantity {
382 fill_quantity -= first_order_node.current_quantity;
383 price_level.total_quantity -= first_order_node.current_quantity;
384 let next = first_order_node.next;
385 orderbook.ask.order_pool[head_idx] = None;
386 orderbook.ask.free_list.push(head_idx);
387 orders_consumed += 1;
388 if let Some(next_order_idx) = next {
389 price_level.head = next_order_idx;
390 } else {
391 span.record("reason", "exhausted");
392 break;
393 }
394 } else {
395 first_order_node.current_quantity -= fill_quantity;
396 price_level.total_quantity -= fill_quantity;
397 fill_quantity = 0;
398 span.record("filled", true);
399 }
400 }
401 span.record("reason", "orderbook is empty");
402 span.record("filled", false);
403 remove_node = price_level.total_quantity == 0;
404 }
405 if remove_node {
406 orderbook.bid.price_map.pop_last();
407 levels_touched += 1;
408 }
409 }
410 span.record("order_type", "market");
411 span.record("is_buy_side", true);
412 span.record("levels_touched", levels_touched);
413 span.record("orders_consumed", orders_consumed);
414 }
415 OrderType::Market(market_limit) => {
416 let mut fill_quantity = order.initial_quantity;
417 let mut levels_touched = 0;
418 let mut orders_consumed = 0;
419 while fill_quantity > 0 {
420 let remove_node: bool;
421 {
422 let Some(mut price_node) = orderbook.ask.price_map.last_entry() else {
423 break;
424 };
425 if market_limit.unwrap() <= *price_node.key() {
426 break;
427 }
428 let price_level = price_node.get_mut();
429 while price_level.total_quantity > 0 && fill_quantity > 0 {
430 let head_idx = price_level.head;
431 let first_order_node =
432 orderbook.ask.order_pool[head_idx].as_mut().unwrap();
433 if fill_quantity >= first_order_node.current_quantity {
434 fill_quantity -= first_order_node.current_quantity;
435 price_level.total_quantity -= first_order_node.current_quantity;
436 let next = first_order_node.next;
437 orderbook.ask.order_pool[head_idx] = None;
438 orderbook.ask.free_list.push(head_idx);
439 orders_consumed += 1;
440 if let Some(next_order_idx) = next {
441 price_level.head = next_order_idx;
442 } else {
443 span.record("reason", "exhausted");
444 break;
445 }
446 } else {
447 first_order_node.current_quantity -= fill_quantity;
448 price_level.total_quantity -= fill_quantity;
449 fill_quantity = 0;
450 span.record("filled", true);
451 }
452 }
453 span.record("reason", "orderbook is empty");
454 span.record("filled", false);
455 remove_node = price_level.total_quantity == 0;
456 }
457 if remove_node {
458 orderbook.bid.price_map.pop_last();
459 levels_touched += 1;
460 }
461 }
462 span.record("order_type", "market");
463 span.record("is_buy_side", true);
464 span.record("levels_touched", levels_touched);
465 span.record("orders_consumed", orders_consumed);
466 }
467 OrderType::Limit => {
468 let mut fill_quantity = order.initial_quantity;
469 let mut levels_touched = 0;
470 let mut orders_consumed = 0;
471 while fill_quantity > 0 {
472 let remove_node: bool;
473 {
474 let Some(mut price_node) = orderbook.ask.price_map.last_entry() else {
475 break;
476 };
477 if order.price <= Some(*price_node.key()) {
478 break;
479 }
480 let price_level = price_node.get_mut();
481 while price_level.total_quantity > 0 && fill_quantity > 0 {
482 let head_idx = price_level.head;
483 let first_order_node =
484 orderbook.ask.order_pool[head_idx].as_mut().unwrap();
485 if fill_quantity >= first_order_node.current_quantity {
486 fill_quantity -= first_order_node.current_quantity;
487 price_level.total_quantity -= first_order_node.current_quantity;
488 let next = first_order_node.next;
489 orderbook.ask.order_pool[head_idx] = None;
490 orderbook.ask.free_list.push(head_idx);
491 orders_consumed += 1;
492 if let Some(next_order_idx) = next {
493 price_level.head = next_order_idx;
494 } else {
495 span.record("reason", "partially_filled");
496 break;
497 }
498 } else {
499 first_order_node.current_quantity -= fill_quantity;
500 price_level.total_quantity -= fill_quantity;
501 fill_quantity = 0;
502 span.record("filled", false);
503 }
504 }
505 span.record("reason", "orderbook is empty");
506 span.record("filled", false);
507 remove_node = price_level.total_quantity == 0;
508 }
509 if remove_node {
510 orderbook.bid.price_map.pop_last();
511 levels_touched += 1;
512 }
513 }
514 let alloted_index = orderbook.create_buy_order(
515 order.engine_order_id,
516 OrderNode {
517 initial_quantity: order.initial_quantity,
518 current_quantity: fill_quantity,
519 market_limit: order.price.unwrap(),
520 next: None,
521 prev: None,
522 },
523 )?;
524 let order_location = OrderLocation {
525 security_id : order.security_id,
526 is_buy_side : order.is_buy_side,
527 order_index : alloted_index
528 };
529 self._global_registry.insert(order.engine_order_id, order_location);
530 span.record("order_type", "limit");
531 span.record("is_buy_side", true);
532 span.record("levels_touched", levels_touched);
533 span.record("orders_consumed", orders_consumed);
534 }
535 }
536 }
537 Ok(())
538 }
539}