Skip to main content

clob_engine/order_book/
orderbook.rs

1use std::collections::BTreeMap;
2use tracing::instrument;
3use uuid::Uuid;
4
5use crate::order_book::types::{BookDepth, CancelOrder, ModifyOrder, ModifyOutcome, OrderNode, PriceLevel, PriceLevelDepth};
6
7#[derive(Debug)]
8pub struct OrderBook{
9    pub security_id : u32,
10    pub ask : HalfBook,
11    pub bid : HalfBook
12}
13impl OrderBook {
14    pub fn new (security_id : u32,) -> Self{
15        Self { security_id , ask : HalfBook::new(), bid : HalfBook::new() }
16    }
17
18    #[instrument( // used for auto span creation & drop.
19        name = "create_buy_order",
20        skip(self),
21        fields(
22            order_id = %order_id,
23            price = resting_order.market_limit
24        ),
25        err
26    )]
27    pub fn create_buy_order(&mut self, order_id : Uuid, resting_order : OrderNode) -> Result<usize, anyhow::Error>{
28        
29        let mut order = resting_order;
30        let order_quantity = order.current_quantity;
31        let price = order.market_limit;
32        
33        if let Some(price_level) = self.bid.price_map.get_mut(&price){
34            order.prev = Some(price_level.tail);
35            if let Some(free_index) = self.bid.free_list.pop(){
36                self.bid.order_pool.insert(free_index, Some(order));
37                price_level.tail = free_index;
38                price_level.total_quantity += order_quantity;
39                price_level.order_count += 1;
40                if let Some(prev_order) = self.bid.order_pool.get_mut(price_level.tail).unwrap(){
41                    prev_order.next = Some(free_index);
42                };
43                return Ok(free_index);
44            }
45            else {
46            self.bid.order_pool.push(Some(order));
47            let new_tail = self.bid.order_pool.len() - 1;
48            price_level.tail = new_tail;
49            price_level.total_quantity += order_quantity;
50            price_level.order_count += 1;
51            if let Some(prev_order) = self.bid.order_pool.get_mut(price_level.tail).unwrap(){
52                prev_order.next = Some(new_tail);
53            };
54            return Ok(new_tail);
55            }
56        }
57
58        let mut new_price_level = PriceLevel{
59            head : 0,
60            tail : 0,
61            order_count : 0,
62            total_quantity : 0
63        };
64        if let Some(free_index) = self.bid.free_list.pop(){
65            self.bid.order_pool.insert(free_index, Some(order));
66            new_price_level.head = free_index;
67            new_price_level.tail = free_index;
68            new_price_level.order_count += 1;
69            new_price_level.total_quantity += order_quantity;
70            self.bid.price_map.entry(price).or_insert(new_price_level);
71            return Ok(free_index)
72        }
73        self.bid.order_pool.push(Some(order));
74        let new_index = self.bid.order_pool.len()-1;
75        new_price_level.head = new_index;
76        new_price_level.tail = new_index;
77        new_price_level.order_count += 1;
78        new_price_level.total_quantity += order_quantity;
79        self.bid.price_map.entry(price).or_insert(new_price_level);
80        
81        Ok(new_index)
82    }
83
84    #[instrument( 
85        name = "create_sell_order",
86        skip(self),
87        fields(
88            order_id = %order_id,
89            price = resting_order.market_limit 
90        ),
91        err
92    )]
93    pub fn create_sell_order(&mut self, order_id : Uuid, resting_order : OrderNode) -> Result<usize, anyhow::Error>{
94        let mut order = resting_order;
95        let order_quantity = order.current_quantity;
96        let price = order.market_limit;
97        
98        if let Some(price_level) = self.ask.price_map.get_mut(&price){
99            order.prev = Some(price_level.tail);
100            if let Some(free_index) = self.ask.free_list.pop(){
101                self.ask.order_pool.insert(free_index, Some(order));
102                price_level.tail = free_index;
103                price_level.total_quantity += order_quantity;
104                price_level.order_count += 1;
105                if let Some(prev_order) = self.ask.order_pool.get_mut(price_level.tail).unwrap(){
106                    prev_order.next = Some(free_index);
107                };
108                return Ok(free_index);
109            }
110            else {
111            self.ask.order_pool.push(Some(order));
112            let new_tail = self.ask.order_pool.len() - 1;
113            price_level.tail = new_tail;
114            price_level.total_quantity += order_quantity;
115            price_level.order_count += 1;
116            if let Some(prev_order) = self.ask.order_pool.get_mut(price_level.tail).unwrap(){
117                prev_order.next = Some(new_tail);
118            };
119            return Ok(new_tail);
120            }
121        }
122
123        let mut new_price_level = PriceLevel{
124            head : 0,
125            tail : 0,
126            order_count : 0,
127            total_quantity : 0
128        };
129        if let Some(free_index) = self.ask.free_list.pop(){
130            self.ask.order_pool.insert(free_index, Some(order));
131            new_price_level.head = free_index;
132            new_price_level.tail = free_index;
133            new_price_level.order_count += 1;
134            new_price_level.total_quantity += order_quantity;
135            self.ask.price_map.entry(price).or_insert(new_price_level);
136            return Ok(free_index)
137        }
138        self.ask.order_pool.push(Some(order));
139        let new_index = self.ask.order_pool.len()-1;
140        new_price_level.head = new_index;
141        new_price_level.tail = new_index;
142        new_price_level.order_count += 1;
143        new_price_level.total_quantity += order_quantity;
144        self.ask.price_map.entry(price).or_insert(new_price_level);
145        
146        Ok(new_index)
147    }
148
149    #[instrument( 
150        name = "cancel_order",
151        skip(self),
152        fields(
153            order_id = %order_id
154        ),
155        err
156    )]
157    pub fn cancel_order(&mut self, order_id : Uuid, order : CancelOrder) -> Result<(), anyhow::Error>{
158        if order.is_buy_side {
159                    let (prev, next) = {
160                        let node = self.bid.order_pool[order.order_index].as_ref().unwrap();
161                        (node.prev, node.next)
162                    };
163                    if let Some(prev_index) = prev{
164                        if let Some(possible_prev_node) = self.bid.order_pool.get_mut(prev_index){
165                            if let Some(prev_node) = possible_prev_node{
166                                prev_node.next = next
167                            }
168                        }
169                    }
170                    if let Some(next_index) = next{
171                        if let Some(possible_next_node) = self.bid.order_pool.get_mut(next_index){
172                            if let Some(next_node) = possible_next_node{
173                                next_node.prev = prev
174                            }
175                        }
176                    }
177                    self.bid.order_pool.insert(order.order_index, None);
178                    self.bid.free_list.push(order.order_index);
179               
180        } else {
181                    let (prev, next) = {
182                        let node = self.ask.order_pool[order.order_index].as_ref().unwrap();
183                        (node.prev, node.next)
184                    };
185                    if let Some(prev_index) = prev{
186                        if let Some(possible_prev_node) = self.ask.order_pool.get_mut(prev_index){
187                            if let Some(prev_node) = possible_prev_node{
188                                prev_node.next = next
189                            }
190                        }
191                    }
192                    if let Some(next_index) = next{
193                        if let Some(possible_next_node) = self.ask.order_pool.get_mut(next_index){
194                            if let Some(next_node) = possible_next_node{
195                                next_node.prev = prev
196                            }
197                        }
198                    }
199                self.ask.order_pool.insert(order.order_index, None);
200                self.ask.free_list.push(order.order_index);
201        }
202        Ok(())
203    }
204
205    #[instrument( 
206        name = "modify_order",
207        skip(self),
208        fields(
209            order_id = %order_id,
210        ),
211        err
212    )]
213    pub fn modify_order(&mut self, order_id : Uuid, order : ModifyOrder) -> Result<Option<ModifyOutcome>, anyhow::Error>{
214        if order.is_buy_side{
215                let (old_initial_qty, old_current_qty, old_price) = {
216                    let node = self.bid.order_pool[order.order_index].as_ref().unwrap();
217                    (node.initial_quantity, node.current_quantity, node.market_limit)
218                };
219                if let Some(new_price) = order.new_price && let Some(new_qty) = order.new_quantity{
220                    if new_price != old_price{
221                        if let Ok(_) = self.cancel_order(order_id ,CancelOrder { order_index : order.order_index, is_buy_side: order.is_buy_side,}){
222                            return Ok(Some(ModifyOutcome::Both {new_price, new_initial_qty: new_qty, old_current_qty }));
223                            }
224                        }
225                    return Ok(None);
226                } else if let Some(new_qty) = order.new_quantity  {
227                    if new_qty > old_initial_qty{
228                        if let Ok(_) = self.cancel_order(order_id ,CancelOrder { order_index : order.order_index, is_buy_side: order.is_buy_side,}){
229                            return Ok(Some(ModifyOutcome::Requantized {old_price, new_initial_qty: new_qty, old_current_qty }))
230                        }
231                        return Ok(None);
232                    }
233                    else {
234                        let order_node = self.bid.order_pool[order.order_index].as_mut().unwrap();
235                        order_node.initial_quantity = new_qty;
236                        return Ok(Some(ModifyOutcome::Inplace));
237                    }
238                } else {
239                    if let Ok(_) = self.cancel_order(order_id ,CancelOrder { order_index : order.order_index, is_buy_side: order.is_buy_side,}){
240                        return Ok(Some(ModifyOutcome::Repriced {new_price : order.new_price.unwrap(), old_initial_qty, old_current_qty }));
241                    }
242                    return Ok(None);
243                }
244        } else {
245                let (old_initial_qty, old_current_qty, old_price) = {
246                    let node = self.ask.order_pool[order.order_index].as_ref().unwrap();
247                    (node.initial_quantity, node.current_quantity, node.market_limit)
248                };
249
250                if let Some(new_price) = order.new_price && let Some(new_qty) = order.new_quantity{
251                    if new_price != old_price{
252                        if let Ok(_) = self.cancel_order(order_id ,CancelOrder { order_index : order.order_index, is_buy_side: order.is_buy_side,}){
253                           return Ok(Some(ModifyOutcome::Requantized {old_price, new_initial_qty: new_qty, old_current_qty }))
254                        }
255                    }
256                    return Ok(None);
257                } else if let Some(new_qty) = order.new_quantity  {
258                    if new_qty > old_initial_qty{
259                        if let Ok(_) = self.cancel_order(order_id ,CancelOrder { order_index : order.order_index, is_buy_side: order.is_buy_side,}){
260                            return Ok(Some(ModifyOutcome::Requantized { old_price, new_initial_qty: new_qty, old_current_qty }))
261                        }
262                        return Ok(None);
263                    }
264                    else {
265                        let order_node = self.ask.order_pool[order.order_index].as_mut().unwrap();
266                        order_node.initial_quantity = new_qty;
267                        return Ok(Some(ModifyOutcome::Inplace));
268                    }
269                }else {
270                    if let Ok(_) = self.cancel_order(order_id ,CancelOrder { order_index : order.order_index, is_buy_side: order.is_buy_side,}){
271                        return Ok(Some(ModifyOutcome::Repriced { new_price : order.new_price.unwrap(), old_initial_qty, old_current_qty }));
272                    }
273                    return Ok(None);
274                }
275        }
276    }
277    
278    #[instrument( 
279        name = "book_depth",
280        skip(self),
281        err
282    )]
283    pub fn depth(&self, levels_count : Option<u32>) -> Result<BookDepth, anyhow::Error>{
284
285        let ask_iter = self.ask.price_map.iter().rev();
286        let bid_iter = self.bid.price_map.iter();
287
288        let ask_depth : Vec<_> = match levels_count {
289            Some(n) => ask_iter.take(n as usize)
290            .map(|(price, price_level)| PriceLevelDepth {
291                price_level : *price,
292                quantity : price_level.total_quantity
293            })
294            .collect(),
295            None => ask_iter.map(|(price, price_level)| PriceLevelDepth {
296                price_level : *price,
297                quantity : price_level.total_quantity
298            }).collect()
299        };
300        let bid_depth = match levels_count {
301            Some(n) => bid_iter.take(n as usize)
302            .map(|(price, price_level)| PriceLevelDepth {
303                price_level : *price,
304                quantity : price_level.total_quantity
305            })
306            .collect(),
307            None => bid_iter.map(|(price, price_level)| PriceLevelDepth {
308                price_level : *price,
309                quantity : price_level.total_quantity
310            }).collect()
311        };
312        Ok(BookDepth { bid_depth, ask_depth })
313    }
314}
315
316#[derive(Debug)]
317pub struct HalfBook{
318    pub price_map : BTreeMap<u32, PriceLevel>,
319    pub order_pool : Vec<Option<OrderNode>>,
320    pub free_list : Vec<usize>, // we're storing the free indices from the price level to keep the cache lines hot.
321}
322
323impl HalfBook {
324    pub fn new() -> Self{
325        Self { price_map: BTreeMap::new(), order_pool: Vec::new(), free_list: Vec::new()}
326    }
327}