Skip to main content

engine/matcher/
simple.rs

1use crate::matcher::Matcher;
2use crate::types::{FillEvent, MarketState, Order, OrderState, Side};
3
4pub struct SimpleMatcher {
5    orders: Vec<Order>,
6}
7
8impl Default for SimpleMatcher {
9    fn default() -> Self {
10        Self::new()
11    }
12}
13
14impl SimpleMatcher {
15    pub fn new() -> Self {
16        Self { orders: Vec::new() }
17    }
18}
19
20impl Matcher for SimpleMatcher {
21    fn add_order(&mut self, order: Order) {
22        self.orders.push(order);
23    }
24
25    fn cancel_order(&mut self, order_id: u64) {
26        if let Some(order) = self.orders.iter_mut().find(|o| o.id == order_id) {
27            order.state = OrderState::Cancelled;
28        }
29        self.orders.retain(|o| o.state == OrderState::Open);
30    }
31
32    fn cancel_all(&mut self) {
33        self.orders.clear();
34    }
35
36    fn process_quote(&mut self, state: &MarketState) -> Vec<FillEvent> {
37        let mut fills = Vec::new();
38        let timestamp = state.timestamp;
39
40        let best_ask = state.best_ask;
41        let best_bid = state.best_bid;
42
43        for order in self.orders.iter_mut() {
44            if order.state != OrderState::Open {
45                continue;
46            }
47
48            let mut filled = false;
49            let mut fill_price = 0.0;
50
51            match order.side {
52                Side::Buy => {
53                    // Check: Is My Limit Price >= Current Best Ask?
54                    if order.limit_price >= best_ask {
55                        filled = true;
56                        fill_price = best_ask;
57                    }
58                }
59                Side::Sell => {
60                    // Check: Is My Limit Price <= Current Best Bid?
61                    if order.limit_price <= best_bid {
62                        filled = true;
63                        fill_price = best_bid;
64                    }
65                }
66            }
67
68            if filled {
69                order.state = OrderState::Filled;
70                fills.push(FillEvent {
71                    order_id: order.id,
72                    timestamp, // Use copied timestamp
73                    price: fill_price,
74                    quantity: order.quantity,
75                    side: order.side,
76                });
77            }
78        }
79
80        self.orders.retain(|o| o.state == OrderState::Open);
81
82        fills
83    }
84
85    fn get_orders(&self) -> &Vec<Order> {
86        &self.orders
87    }
88}
89
90#[cfg(test)]
91mod tests {
92    use super::*;
93    use crate::types::Side;
94
95    fn state(bid: f64, ask: f64) -> MarketState {
96        MarketState {
97            timestamp: 0.0,
98            best_bid: bid,
99            best_ask: ask,
100            last_price: None,
101            true_volatility: None,
102            true_drift: None,
103            parameters: None,
104        }
105    }
106
107    #[test]
108    fn test_buy_above_ask_fills_at_ask() {
109        // A buy whose limit exceeds the best_ask must fill at best_ask (not at limit).
110        let mut m = SimpleMatcher::new();
111        m.add_order(Order::new(1, Side::Buy, 101.0, 1.0));
112        let fills = m.process_quote(&state(99.0, 100.0));
113        assert_eq!(fills.len(), 1);
114        assert_eq!(fills[0].price, 100.0);
115    }
116
117    #[test]
118    fn test_buy_below_ask_does_not_fill() {
119        // A resting buy below the best_ask must not trigger a fill.
120        let mut m = SimpleMatcher::new();
121        m.add_order(Order::new(1, Side::Buy, 99.0, 1.0));
122        let fills = m.process_quote(&state(99.0, 100.0));
123        assert_eq!(fills.len(), 0);
124    }
125
126    #[test]
127    fn test_sell_at_or_below_bid_fills_at_bid() {
128        // A sell whose limit is at or below best_bid must fill at best_bid (not at limit).
129        let mut m = SimpleMatcher::new();
130        m.add_order(Order::new(1, Side::Sell, 98.5, 2.0));
131        let fills = m.process_quote(&state(99.0, 100.0));
132        assert_eq!(fills.len(), 1);
133        assert_eq!(fills[0].price, 99.0);
134        assert_eq!(fills[0].quantity, 2.0);
135    }
136
137    #[test]
138    fn test_sell_above_bid_does_not_fill() {
139        // A sell order priced above best_bid must not fill.
140        let mut m = SimpleMatcher::new();
141        m.add_order(Order::new(1, Side::Sell, 100.5, 1.0));
142        let fills = m.process_quote(&state(99.0, 100.0));
143        assert_eq!(fills.len(), 0);
144    }
145
146    #[test]
147    fn test_multiple_bids_only_aggressive_fills() {
148        // Two bids at different levels: only the one above the ask fills.
149        let mut m = SimpleMatcher::new();
150        m.add_order(Order::new(1, Side::Buy, 101.0, 1.0)); // above ask -> fills
151        m.add_order(Order::new(2, Side::Buy, 99.0, 1.0)); // below ask -> stays
152        let fills = m.process_quote(&state(99.5, 100.0));
153        assert_eq!(fills.len(), 1);
154        assert_eq!(fills[0].order_id, 1);
155    }
156
157    #[test]
158    fn test_multiple_bids_both_aggressive_fill_independently() {
159        // Two bids both above the ask must fill independently, each at best_ask.
160        let mut m = SimpleMatcher::new();
161        m.add_order(Order::new(1, Side::Buy, 101.0, 1.0));
162        m.add_order(Order::new(2, Side::Buy, 102.0, 2.0));
163        let fills = m.process_quote(&state(99.5, 100.0));
164        assert_eq!(fills.len(), 2);
165        for f in &fills {
166            assert_eq!(f.price, 100.0);
167        }
168    }
169
170    #[test]
171    fn test_cancel_order_prevents_fill() {
172        // Cancelling an order by ID before the quote must prevent it from filling.
173        let mut m = SimpleMatcher::new();
174        m.add_order(Order::new(1, Side::Buy, 101.0, 1.0));
175        m.cancel_order(1);
176        let fills = m.process_quote(&state(99.0, 100.0));
177        assert_eq!(fills.len(), 0);
178    }
179
180    #[test]
181    fn test_cancel_nonexistent_order_is_noop() {
182        // Cancelling an order that was never submitted must not affect other orders.
183        let mut m = SimpleMatcher::new();
184        m.add_order(Order::new(1, Side::Buy, 101.0, 1.0));
185        m.cancel_order(999);
186        let fills = m.process_quote(&state(99.0, 100.0));
187        assert_eq!(fills.len(), 1);
188    }
189
190    #[test]
191    fn test_filled_order_removed_from_book() {
192        // After a fill, the order must be removed so it cannot fill again on
193        // the next call to process_quote.
194        let mut m = SimpleMatcher::new();
195        m.add_order(Order::new(1, Side::Buy, 101.0, 1.0));
196        let fills1 = m.process_quote(&state(99.0, 100.0));
197        assert_eq!(fills1.len(), 1);
198        // No new orders added; book should be empty, so no further fills.
199        let fills2 = m.process_quote(&state(99.0, 100.0));
200        assert_eq!(fills2.len(), 0);
201    }
202}