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