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