1use crate::order_book::{
2 orderbook::OrderBook,
3 types::{
4 CancelOrder, CancelOutcome, GlobalOrderRegistry, ModifyOrder, ModifyOutcome, NewOrder, OrderLocation, OrderNode, OrderType
5 },
6};
7use anyhow::{Context};
8use std::collections::HashMap;
9use tracing::{Span, instrument};
10use uuid::Uuid;
11
12#[derive(Debug)]
13pub struct MatchingEngine {
14 _book: HashMap<Uuid, OrderBook>,
15 _global_registry: GlobalOrderRegistry,
16}
17
18impl MatchingEngine {
19
20 pub fn new() -> Self{
21 Self { _book: HashMap::new(), _global_registry: GlobalOrderRegistry::new() }
22 }
23
24 #[instrument(
25 name = "get_orderbook",
26 skip(self),
27 fields(
28 order_id = %global_order_id,
29 ),
30 )]
31 fn get_orderbook(
32 &mut self,
33 global_order_id: Uuid,
34 span: &Span
35 ) -> Option<(usize, bool,Uuid, &mut OrderBook)> {
36 let order_location = match self._global_registry.get_details(&global_order_id){
37 Some(location) => {
38 location
39 }
40 None => {
41 span.record("reason", "order not found in global registry");
42 return None;
43 }
44 };
45 let Some(book) = self._book.get_mut(&order_location.security_id) else {
46 span.record("reason", "orderbook doesn't exist");
47 return None;
48 };
49 Some((order_location.order_index, order_location.is_buy_side,order_location.security_id, book))
50 }
51
52 pub fn modify(
53 &mut self,
54 global_order_id: Uuid,
55 new_price: Option<u32>,
56 new_qty: Option<u32>,
57 span: &Span,
58 ) -> Result<(), anyhow::Error> {
59 let (order_index, is_buy_side,security_id, orderbook) = self
60 .get_orderbook(global_order_id, span)
61 .context("Could not find the orderbook")?;
62 if let Ok(potential_modfication) = orderbook.modify_order(
63 global_order_id,
64 ModifyOrder {
65 new_price,
66 order_index,
67 is_buy_side,
68 new_quantity: new_qty,
69 },
70 ) {
71 if let Some(modification_result) = potential_modfication {
72 match modification_result {
73 ModifyOutcome::Both {
74 new_price,
75 new_initial_qty,
76 old_current_qty,
77 } => {
78 span.record("modify_outcome", "price & qty");
79 if let Some(_) = self._global_registry.delete(&global_order_id){
80 let _ = self.match_order(
81 NewOrder {
82 engine_order_id: global_order_id,
83 price: Some(new_price),
84 initial_quantity: new_initial_qty,
85 current_quantity : old_current_qty,
86 is_buy_side,
87 security_id,
88 order_type: OrderType::Limit,
89 },
90 span);
91 return Ok(());
92 }
93 span.record("intermediate_error", "Failed to delete from global registry");
94 },
95 ModifyOutcome::Repriced { new_price, old_initial_qty, old_current_qty } =>
96 {
97 span.record("modify_outcome", "price");
98 if let Some(_) = self._global_registry.delete(&global_order_id){
99 let _ = self.match_order(
100 NewOrder {
101 engine_order_id: global_order_id,
102 price: Some(new_price),
103 initial_quantity: old_initial_qty,
104 current_quantity : old_current_qty,
105 is_buy_side,
106 security_id,
107 order_type: OrderType::Limit,
108 },
109 span);
110 return Ok(());
111 }
112 span.record("intermediate_error", "Failed to delete from global registry");
113 },
114 ModifyOutcome::Requantized { old_price, new_initial_qty, old_current_qty } => {
115 span.record("modify_outcome", "qty");
116 if let Some(_) = self._global_registry.delete(&global_order_id){
117 let _ = self.match_order(
118 NewOrder {
119 engine_order_id: global_order_id,
120 price: Some(old_price),
121 initial_quantity: new_initial_qty,
122 current_quantity : old_current_qty,
123 is_buy_side,
124 security_id,
125 order_type: OrderType::Limit,
126 }, span);
127 return Ok(());
128 }
129 span.record("intermediate_error", "Failed to delete from global registry");
130 },
131 ModifyOutcome::Inplace => {
132 span.record("modify_outcome", "qty reduction");
133 return Ok(());
134 }
135 }
136 }
137 } else {
138 return Ok(());
139 }
140 Ok(())
141 }
142
143 pub fn cancel(&mut self, global_order_id: Uuid, span: &Span) -> Result<CancelOutcome, anyhow::Error>{
144 let (order_index, is_buy_side,_, orderbook) = self
145 .get_orderbook(global_order_id, span)
146 .context("Could not find the orderbook")?;
147 if let Err(_) = orderbook.cancel_order(global_order_id, CancelOrder{is_buy_side, order_index}){
148 span.record("reason", "orderbook cancellation failed");
149 span.record("success_status", false);
150 return Ok(CancelOutcome::Failed);
151 };
152 if let Some(_) = self._global_registry.delete(&global_order_id){
153 span.record("success_status", true);
154 return Ok(CancelOutcome::Success)
155 };
156 span.record("reason", "Registry cancellation failed");
157 span.record("success_status", false);
158 Ok(CancelOutcome::Failed)
159 }
160
161 pub fn match_order(&mut self, order: NewOrder, span: &Span) -> Result<(), anyhow::Error> {
162
163 let _gaurd = span.enter();
164
165 let orderbook = match self._book.get_mut(&order.security_id){
166 Some(orderbook) => {
167 orderbook
168 }
169 None => {
170 let _ = self._book.insert(order.security_id, OrderBook::new(1)).unwrap();
171 self._book.get_mut(&order.security_id).unwrap()
172 }
173 };
174
175 if !order.is_buy_side {
176 match order.order_type {
178 OrderType::Market(None) => {
179 let mut fill_quantity = order.initial_quantity;
181 let mut levels_touched = 0;
182 let mut orders_consumed = 0;
183 while fill_quantity > 0 {
184 let remove_node: bool;
185 {
186 let Some(mut price_node) = orderbook.bid.price_map.first_entry() else {
187 break;
188 };
189 let price_level = price_node.get_mut();
190 while price_level.total_quantity > 0 && fill_quantity > 0 {
191 let head_idx = price_level.head;
192 let first_order_node =
193 orderbook.bid.order_pool[head_idx].as_mut().unwrap();
194 if fill_quantity >= first_order_node.current_quantity {
195 fill_quantity -= first_order_node.current_quantity;
196 price_level.total_quantity -= first_order_node.current_quantity;
197 let next = first_order_node.next;
198 orderbook.bid.order_pool[head_idx] = None;
199 orderbook.bid.free_list.push(head_idx);
200 orders_consumed += 1;
201 if let Some(next_order_idx) = next {
202 price_level.head = next_order_idx;
203 } else {
204 span.record("reason", "exhausted");
205 break;
206 }
207 } else {
208 first_order_node.current_quantity -= fill_quantity;
209 price_level.total_quantity -= fill_quantity;
210 fill_quantity = 0;
211 span.record("filled", true);
212 }
213 }
214 remove_node = price_level.total_quantity == 0;
215 }
216 if remove_node {
217 orderbook.bid.price_map.pop_first();
218 levels_touched += 1;
219 }
220 }
221 span.record("order_type", "market");
222 span.record("is_buy_side", false);
223 span.record("levels_touched", levels_touched);
224 span.record("orders_consumed", orders_consumed);
225 }
226 OrderType::Market(market_limit) => {
227 let mut fill_quantity = order.initial_quantity;
228 let mut levels_touched = 0;
229 let mut orders_consumed = 0;
230 while fill_quantity > 0 {
231 let remove_node: bool;
232 {
233 let Some(mut price_node) = orderbook.bid.price_map.first_entry() else {
234 break;
235 };
236 if market_limit.unwrap() >= *price_node.key() {
237 break;
238 }
239 let price_level = price_node.get_mut();
240 while price_level.total_quantity > 0 && fill_quantity > 0 {
241 let head_idx = price_level.head;
242 let first_order_node =
243 orderbook.bid.order_pool[head_idx].as_mut().unwrap();
244 if fill_quantity >= first_order_node.current_quantity {
245 fill_quantity -= first_order_node.current_quantity;
246 price_level.total_quantity -= first_order_node.current_quantity;
247 let next = first_order_node.next;
248 orderbook.bid.order_pool[head_idx] = None;
249 orderbook.bid.free_list.push(head_idx);
250 orders_consumed += 1;
251 if let Some(next_order_idx) = next {
252 price_level.head = next_order_idx;
253 } else {
254 span.record("reason", "exhausted");
255 break;
256 }
257 } else {
258 first_order_node.current_quantity -= fill_quantity;
259 price_level.total_quantity -= fill_quantity;
260 fill_quantity = 0;
261 span.record("filled", true);
262 }
263 }
264 remove_node = price_level.total_quantity == 0;
265 }
266 if remove_node {
267 orderbook.bid.price_map.pop_first();
268 levels_touched += 1;
269 }
270 }
271 span.record("order_type", "market");
272 span.record("is_buy_side", false);
273 span.record("levels_touched", levels_touched);
274 span.record("orders_consumed", orders_consumed);
275 }
276 OrderType::Limit => {
277 let mut fill_quantity = order.initial_quantity;
278 let mut levels_touched = 0;
279 let mut orders_consumed = 0;
280 while fill_quantity > 0 {
281 let remove_node: bool;
282 {
283 let Some(mut price_node) = orderbook.bid.price_map.first_entry() else {
284 break;
285 };
286 if order.price >= Some(*price_node.key()) {
287 break;
288 }
289 let price_level = price_node.get_mut();
290 while price_level.total_quantity > 0 && fill_quantity > 0 {
291 let head_idx = price_level.head;
292 let first_order_node =
293 orderbook.bid.order_pool[head_idx].as_mut().unwrap();
294 if fill_quantity >= first_order_node.current_quantity {
295 fill_quantity -= first_order_node.current_quantity;
296 price_level.total_quantity -= first_order_node.current_quantity;
297 let next = first_order_node.next;
298 orderbook.bid.order_pool[head_idx] = None;
299 orderbook.bid.free_list.push(head_idx);
300 orders_consumed += 1;
301 if let Some(next_order_idx) = next {
302 price_level.head = next_order_idx;
303 } else {
304 span.record("reason", "partially_filled");
305 break;
306 }
307 } else {
308 first_order_node.current_quantity -= fill_quantity;
309 price_level.total_quantity -= fill_quantity;
310 fill_quantity = 0;
311 }
312 }
313 remove_node = price_level.total_quantity == 0;
314 }
315 if remove_node {
316 orderbook.bid.price_map.pop_first();
317 levels_touched += 1;
318 }
319 }
320 let alloted_index = orderbook.create_sell_order(
321 order.engine_order_id,
322 OrderNode {
323 initial_quantity: order.initial_quantity,
324 current_quantity: fill_quantity,
325 market_limit: order.price.unwrap(),
326 next: None,
327 prev: None,
328 },
329 )?;
330 let order_location = OrderLocation {
331 security_id : order.security_id,
332 is_buy_side : order.is_buy_side,
333 order_index : alloted_index
334 };
335 self._global_registry.insert(order.engine_order_id, order_location);
336 span.record("order_type", "limit");
337 span.record("is_buy_side", false);
338 span.record("levels_touched", levels_touched);
339 span.record("orders_consumed", orders_consumed);
340 }
341 }
342 } else {
343 match order.order_type {
344 OrderType::Market(None) => {
345 let mut fill_quantity = order.initial_quantity;
347 let mut levels_touched = 0;
348 let mut orders_consumed = 0;
349 while fill_quantity > 0 {
350 let remove_node: bool;
351 {
352 let Some(mut price_node) = orderbook.ask.price_map.last_entry() else {
353 break;
354 };
355 let price_level = price_node.get_mut();
356 while price_level.total_quantity > 0 && fill_quantity > 0 {
357 let head_idx = price_level.head;
358 let first_order_node =
359 orderbook.ask.order_pool[head_idx].as_mut().unwrap();
360 if fill_quantity >= first_order_node.current_quantity {
361 fill_quantity -= first_order_node.current_quantity;
362 price_level.total_quantity -= first_order_node.current_quantity;
363 let next = first_order_node.next;
364 orderbook.ask.order_pool[head_idx] = None;
365 orderbook.ask.free_list.push(head_idx);
366 orders_consumed += 1;
367 if let Some(next_order_idx) = next {
368 price_level.head = next_order_idx;
369 } else {
370 span.record("reason", "exhausted");
371 break;
372 }
373 } else {
374 first_order_node.current_quantity -= fill_quantity;
375 price_level.total_quantity -= fill_quantity;
376 fill_quantity = 0;
377 span.record("filled", true);
378 }
379 }
380 remove_node = price_level.total_quantity == 0;
381 }
382 if remove_node {
383 orderbook.bid.price_map.pop_last();
384 levels_touched += 1;
385 }
386 }
387 span.record("order_type", "market");
388 span.record("is_buy_side", true);
389 span.record("levels_touched", levels_touched);
390 span.record("orders_consumed", orders_consumed);
391 }
392 OrderType::Market(market_limit) => {
393 let mut fill_quantity = order.initial_quantity;
394 let mut levels_touched = 0;
395 let mut orders_consumed = 0;
396 while fill_quantity > 0 {
397 let remove_node: bool;
398 {
399 let Some(mut price_node) = orderbook.ask.price_map.last_entry() else {
400 break;
401 };
402 if market_limit.unwrap() <= *price_node.key() {
403 break;
404 }
405 let price_level = price_node.get_mut();
406 while price_level.total_quantity > 0 && fill_quantity > 0 {
407 let head_idx = price_level.head;
408 let first_order_node =
409 orderbook.ask.order_pool[head_idx].as_mut().unwrap();
410 if fill_quantity >= first_order_node.current_quantity {
411 fill_quantity -= first_order_node.current_quantity;
412 price_level.total_quantity -= first_order_node.current_quantity;
413 let next = first_order_node.next;
414 orderbook.ask.order_pool[head_idx] = None;
415 orderbook.ask.free_list.push(head_idx);
416 orders_consumed += 1;
417 if let Some(next_order_idx) = next {
418 price_level.head = next_order_idx;
419 } else {
420 span.record("reason", "exhausted");
421 break;
422 }
423 } else {
424 first_order_node.current_quantity -= fill_quantity;
425 price_level.total_quantity -= fill_quantity;
426 fill_quantity = 0;
427 span.record("filled", true);
428 }
429 }
430 remove_node = price_level.total_quantity == 0;
431 }
432 if remove_node {
433 orderbook.bid.price_map.pop_last();
434 levels_touched += 1;
435 }
436 }
437 span.record("order_type", "market");
438 span.record("is_buy_side", true);
439 span.record("levels_touched", levels_touched);
440 span.record("orders_consumed", orders_consumed);
441 }
442 OrderType::Limit => {
443 let mut fill_quantity = order.initial_quantity;
444 let mut levels_touched = 0;
445 let mut orders_consumed = 0;
446 while fill_quantity > 0 {
447 let remove_node: bool;
448 {
449 let Some(mut price_node) = orderbook.ask.price_map.last_entry() else {
450 break;
451 };
452 if order.price <= Some(*price_node.key()) {
453 break;
454 }
455 let price_level = price_node.get_mut();
456 while price_level.total_quantity > 0 && fill_quantity > 0 {
457 let head_idx = price_level.head;
458 let first_order_node =
459 orderbook.ask.order_pool[head_idx].as_mut().unwrap();
460 if fill_quantity >= first_order_node.current_quantity {
461 fill_quantity -= first_order_node.current_quantity;
462 price_level.total_quantity -= first_order_node.current_quantity;
463 let next = first_order_node.next;
464 orderbook.ask.order_pool[head_idx] = None;
465 orderbook.ask.free_list.push(head_idx);
466 orders_consumed += 1;
467 if let Some(next_order_idx) = next {
468 price_level.head = next_order_idx;
469 } else {
470 span.record("reason", "partially_filled");
471 break;
472 }
473 } else {
474 first_order_node.current_quantity -= fill_quantity;
475 price_level.total_quantity -= fill_quantity;
476 fill_quantity = 0;
477 }
478 }
479 remove_node = price_level.total_quantity == 0;
480 }
481 if remove_node {
482 orderbook.bid.price_map.pop_last();
483 levels_touched += 1;
484 }
485 }
486 let alloted_index = orderbook.create_buy_order(
487 order.engine_order_id,
488 OrderNode {
489 initial_quantity: order.initial_quantity,
490 current_quantity: fill_quantity,
491 market_limit: order.price.unwrap(),
492 next: None,
493 prev: None,
494 },
495 )?;
496 let order_location = OrderLocation {
497 security_id : order.security_id,
498 is_buy_side : order.is_buy_side,
499 order_index : alloted_index
500 };
501 self._global_registry.insert(order.engine_order_id, order_location);
502 span.record("order_type", "limit");
503 span.record("is_buy_side", true);
504 span.record("levels_touched", levels_touched);
505 span.record("orders_consumed", orders_consumed);
506 }
507 }
508 }
509 Ok(())
510 }
511}