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