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