Skip to main content

off_grid_primitives/spot/
prices.rs

1use serde::{Deserialize, Serialize};
2use std::collections::BTreeMap;
3use std::fmt;
4
5#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
6pub struct Level {
7    /// price in 8 decimals
8    pub price: u64,
9    /// public quantity in 8 decimals
10    pub pqty: u64,
11    /// current quantity in 8 decimals
12    pub cqty: u64,
13}
14
15impl fmt::Display for Level {
16    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
17        write!(f, "Level(price: {}, pqty: {}, cqty: {})", self.price, self.pqty, self.cqty)
18    }
19}
20
21// Helper to format Vec<Level> for error messages
22fn format_levels(levels: &[Level]) -> String {
23    if levels.is_empty() {
24        return "[]".to_string();
25    }
26    let formatted: Vec<String> = levels.iter()
27        .map(|level| format!("{}", level))
28        .collect();
29    format!("[{}]", formatted.join(", "))
30}
31
32#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
33pub struct PriceNode {
34    pub prev: Option<u64>,
35    pub next: Option<u64>,
36}
37
38#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
39pub struct L2 {
40    /// Head of the bid price linked list
41    pub bid_price_head: Option<u64>,
42    /// Head of the ask price linked list
43    pub ask_price_head: Option<u64>,
44    /// Tail of the bid price linked list
45    pub bid_price_tail: Option<u64>,
46    /// Tail of the ask price linked list
47    pub ask_price_tail: Option<u64>,
48    /// Mapping price -> node of the bid price linked list
49    pub bid_price_nodes: BTreeMap<u64, PriceNode>,
50    /// Mapping price -> node of the ask price linked list
51    pub ask_price_nodes: BTreeMap<u64, PriceNode>,
52    /// Public bid levels sorted by price descending
53    pub public_bid_level_map: BTreeMap<u64, u64>,
54    /// Public ask levels sorted by price ascending
55    pub public_ask_level_map: BTreeMap<u64, u64>,
56    /// Current bid levels sorted by price descending
57    pub current_bid_level_map: BTreeMap<u64, u64>,
58    /// Current ask levels sorted by price ascending
59    pub current_ask_level_map: BTreeMap<u64, u64>,
60    /// Bid levels sorted by price descending for snapshot display
61    /// key is scale in 8 decimals integer (e.g. 100000000 for 1.00000000, 1000000000 for 10.00000000)
62    /// value is a vector of levels in the quantized price space
63    pub bid_level_list: BTreeMap<u64, Vec<Level>>,
64    /// Ask levels sorted by price ascending for snapshot display
65    /// key is scale in 8 decimals integer (e.g. 100000000 for 1.00000000, 1000000000 for 10.00000000)
66    /// value is a vector of levels in the quantized price space
67    pub ask_level_list: BTreeMap<u64, Vec<Level>>,
68}
69
70#[derive(Debug, thiserror::Error, PartialEq, Eq)]
71pub enum L2Error {
72    #[error("price is zero in L2 orderbook level")]
73    PriceIsZero,
74    #[error("price is missing in L2 orderbook level: {price} isBid: {is_bid} isPlaced: {is_placed}")]
75    PriceMissing { price: u64, is_bid: bool, is_placed: bool },
76    #[error("failed to set bid level: {price} {level}")]
77    FailedToSetBidLevel { price: u64, level: u64 },
78    #[error("failed to set ask level: {price} {level}")]
79    FailedToSetAskLevel { price: u64, level: u64 },
80    #[error("failed to set bid levels: scale={scale}, levels={}", format_levels(&levels))]
81    FailedToSetBidLevels { scale: u64, levels: Vec<Level> },
82    #[error("failed to set ask levels: scale={scale}, levels={}", format_levels(&levels))]
83    FailedToSetAskLevels { scale: u64, levels: Vec<Level> },
84}
85
86impl L2 {
87    pub fn new() -> Self {
88        Self {
89            bid_price_head: None,
90            bid_price_tail: None,
91            ask_price_head: None,
92            ask_price_tail: None,
93            bid_price_nodes: BTreeMap::new(),
94            ask_price_nodes: BTreeMap::new(),
95            public_bid_level_map: BTreeMap::new(),
96            public_ask_level_map: BTreeMap::new(),
97            current_bid_level_map: BTreeMap::new(),
98            current_ask_level_map: BTreeMap::new(),
99            bid_level_list: BTreeMap::new(),
100            ask_level_list: BTreeMap::new(),
101        }
102    }
103
104    fn ensure_price(price: u64) -> Result<(), L2Error> {
105        if price == 0 {
106            Err(L2Error::PriceIsZero)
107        } else {
108            Ok(())
109        }
110    }
111
112    pub fn bid_head(&self) -> Option<u64> {
113        self.bid_price_head
114    }
115
116    pub fn ask_head(&self) -> Option<u64> {
117        self.ask_price_head
118    }
119
120    pub fn public_bid_level(&self, price: u64) -> Option<u64> {
121        self.public_bid_level_map.get(&price).copied()
122    }
123
124    pub fn public_ask_level(&self, price: u64) -> Option<u64> {
125        self.public_ask_level_map.get(&price).copied()
126    }
127
128    pub fn current_bid_level(&self, price: u64) -> Option<u64> {
129        self.current_bid_level_map.get(&price).copied()
130    }
131
132    pub fn current_ask_level(&self, price: u64) -> Option<u64> {
133        self.current_ask_level_map.get(&price).copied()
134    }
135
136    pub fn bid_levels(&self, scale: u64) -> &Vec<Level> {
137        &self.bid_level_list.get(&scale).unwrap()
138    }
139
140    pub fn ask_levels(&self, scale: u64) -> &Vec<Level> {
141        &self.ask_level_list.get(&scale).unwrap()
142    }
143
144    pub fn scale_bid_levels(&self, scale: u64, n: u32) -> Vec<Level> {
145        let levels = self.bid_level_list.get(&scale).cloned().unwrap_or(Vec::new());
146        levels.iter().take(n as usize).cloned().collect()
147    }
148
149    pub fn scale_ask_levels(&self, scale: u64, n: u32) -> Vec<Level> {
150        let levels = self.ask_level_list.get(&scale).cloned().unwrap_or(Vec::new());
151        levels.iter().take(n as usize).cloned().collect()
152    }
153
154    pub fn set_current_bid_level(&mut self, price: u64, level: u64) -> Result<(), L2Error> {
155        self.current_bid_level_map.insert(price, level);
156        Ok(())
157    }
158
159    pub fn set_current_ask_level(&mut self, price: u64, level: u64) -> Result<(), L2Error> {
160        self.current_ask_level_map.insert(price, level);
161        Ok(())
162    }
163
164    pub fn set_public_bid_level(&mut self, price: u64, level: u64) -> Result<(), L2Error> {
165        self.public_bid_level_map.insert(price, level);
166        Ok(())
167    }
168
169    pub fn set_public_ask_level(&mut self, price: u64, level: u64) -> Result<(), L2Error> {
170        self.public_ask_level_map.insert(price, level);
171        Ok(())
172    }
173
174    pub fn set_bid_levels(&mut self, scale: u64, levels: Vec<Level>) -> Result<(), L2Error> {
175        self.bid_level_list.insert(scale, levels);
176        Ok(())
177    }
178
179    pub fn set_ask_levels(&mut self, scale: u64, levels: Vec<Level>) -> Result<(), L2Error> {
180        self.ask_level_list.insert(scale, levels);
181        Ok(())
182    }
183
184    pub fn clear_head(&mut self, is_bid: bool) -> Result<Option<u64>, L2Error> {
185        if is_bid {
186            let old_head = self.bid_price_head.unwrap();
187            // update the head of the bid price linked list
188            // if the next is empty, set the head and tail to none
189            if self.bid_price_nodes.get(&old_head).and_then(|node| node.next).is_none() {
190                self.bid_price_head = None;
191                self.bid_price_tail = None;
192            }
193            else {
194                self.bid_price_head = self.bid_price_nodes.get(&old_head).and_then(|node| node.next);
195            }
196            // remove the node from the bid price nodes map
197            self.bid_price_nodes.remove(&old_head);
198            Ok(self.bid_price_head)
199        } else {
200            let old_head = self.ask_price_head.unwrap();
201            // update the head of the ask price linked list
202            // if the next is empty, set the head and tail to none
203            if self.ask_price_nodes.get(&old_head).and_then(|node| node.next).is_none() {
204                self.ask_price_head = None;
205                self.ask_price_tail = None;
206            }
207            else {
208                self.ask_price_head = self.ask_price_nodes.get(&old_head).and_then(|node| node.next);
209            }
210            // remove the node from the ask price nodes map
211            self.ask_price_nodes.remove(&old_head);
212            Ok(self.ask_price_head)
213        }
214    }
215
216    pub fn price_exists(&self, is_bid: bool, price: u64) -> bool {
217        if is_bid {
218            self.bid_price_nodes.contains_key(&price)
219        }
220        else {
221            self.ask_price_nodes.contains_key(&price)
222        }
223    }
224
225    pub fn insert_price(&mut self, is_bid: bool, price: u64) -> Result<(), L2Error> {
226        if is_bid {
227            let _ = self._insert_bid_price(price)?;
228            self.set_public_bid_level(price, 0)?;
229            self.set_current_bid_level(price, 0)?;
230            Ok(())
231        }
232        else {
233            let _ = self._insert_ask_price(price)?;
234            self.set_public_ask_level(price, 0)?;
235            self.set_current_ask_level(price, 0)?;
236            Ok(())
237        }
238    }
239
240    /// inserts a bid price into the bid price linked list
241    /// price linked list is sorted in descending order
242    fn _insert_bid_price(&mut self, price: u64) -> Result<(), L2Error> {
243        Self::ensure_price(price)?;
244        // compare head of the bid price head
245        if self.bid_price_head == None {
246            self.bid_price_head = Some(price);
247            // set the tail of the bid price linked list
248            self.bid_price_tail = Some(price);
249            // create a node for the first price
250            self.bid_price_nodes.insert(price, PriceNode {
251                prev: None,
252                next: None,
253            });
254            return Ok(());
255        }
256        else if price > self.bid_price_head.unwrap() {
257            let old_head = self.bid_price_head.unwrap();
258            self.bid_price_head = Some(price);
259            // update old head's node to point back to new head
260            self.bid_price_nodes.get_mut(&old_head).map(|node| node.prev = Some(price));
261            // create node for new head
262            self.bid_price_nodes.insert(price, PriceNode {
263                prev: None,
264                next: Some(old_head),
265            });
266            return Ok(());
267        } 
268        else if price == self.bid_price_head.unwrap() {
269            return Ok(());
270        }
271        else {
272            // traverse through the bid price linked list and insert the price at the correct position so that the list is sorted in descending order
273            let mut current = self.bid_price_head;
274            while current.is_some() {
275                let next = self.bid_price_nodes.get(&current.unwrap()).and_then(|node| node.next);
276                // next does not exist
277                if next.is_none() {
278                    // insert the price at the current position
279                    let curr = current.unwrap();
280                    self.bid_price_nodes.get_mut(&curr).map(|node| node.next = Some(price));
281                    // create a node for the new price
282                    self.bid_price_nodes.insert(price, PriceNode {
283                        prev: Some(curr),
284                        next: None,
285                    });
286                    // set the tail of the bid price linked list
287                    self.bid_price_tail = Some(price);
288                    return Ok(());
289                }
290                else {
291                    // next exists
292                    let next_val = next.unwrap();
293                    if next_val > price {
294                        // traverse until the price is bigger than the current price
295                        current = Some(next_val);
296                    }
297                    else if next_val < price {
298                        // To avoid mutable and immutable borrow at the same time, collect value first
299                        let curr = current.unwrap();
300                        // Update current's next to point to new price
301                        self.bid_price_nodes.get_mut(&curr).map(|node| node.next = Some(price));
302                        // Update next_val's prev to point to new price
303                        self.bid_price_nodes.get_mut(&next_val).map(|node| node.prev = Some(price));
304                        // Create node for new price
305                        self.bid_price_nodes.insert(price, PriceNode {
306                            prev: Some(curr),
307                            next: Some(next_val),
308                        });
309                        return Ok(());
310                    }
311                }
312            }
313            return Ok(());
314        }
315    }
316
317    /// price linked list is sorted in ascending order
318    /// linked list is sorted in ascending order
319    fn _insert_ask_price(&mut self, price: u64) -> Result<(), L2Error> {
320        Self::ensure_price(price)?;
321        // compare head of the ask price head
322        if self.ask_price_head == None {
323            self.ask_price_head = Some(price);
324            // set the tail of the ask price linked list
325            self.ask_price_tail = Some(price);
326            // create a node for the first price
327            self.ask_price_nodes.insert(price, PriceNode {
328                prev: None,
329                next: None,
330            });
331            return Ok(());
332        }
333        else if price < self.ask_price_head.unwrap() {
334            let old_head = self.ask_price_head.unwrap();
335            self.ask_price_head = Some(price);
336            // update old head's prev pointer to point to new head
337            self.ask_price_nodes.get_mut(&old_head).map(|node| node.prev = Some(price));
338            // create node for new head
339            self.ask_price_nodes.insert(price, PriceNode {
340                prev: None,
341                next: Some(old_head),
342            });
343            return Ok(());
344        }
345        else if price == self.ask_price_head.unwrap() {
346            return Ok(());
347        }
348        else {
349            // traverse through the ask price linked list and insert the price at the correct position so that the list is sorted in ascending order
350            let mut current = self.ask_price_head;
351            while current.is_some() {
352                let next = self.ask_price_nodes.get(&current.unwrap()).and_then(|node| node.next);
353                // next does not exist
354                if next.is_none() {
355                    // insert the price at the tail position
356                    let curr = current.unwrap();
357                    // update current node's next pointer
358                    self.ask_price_nodes.get_mut(&curr).map(|node| node.next = Some(price));
359                    // create a node for the new price
360                    self.ask_price_nodes.insert(price, PriceNode {
361                        prev: Some(curr),
362                        next: None,
363                    });
364                    // set the tail of the ask price linked list
365                    self.ask_price_tail = Some(price);
366                    return Ok(());
367                }
368                else {
369                    // next exists
370                    let next_val = next.unwrap();
371                    if next_val < price {
372                        // traverse until the price is smaller than the current price
373                        current = Some(next_val);
374                    }
375                    else if next_val > price {
376                        // To avoid mutable and immutable borrow at the same time, collect value first
377                        let curr = current.unwrap();
378                        // Update current's next pointer to point to new price
379                        self.ask_price_nodes.get_mut(&curr).map(|node| node.next = Some(price));
380                        // Update next_val's prev pointer to point to new price
381                        self.ask_price_nodes.get_mut(&next_val).map(|node| node.prev = Some(price));
382                        // Create node for new price
383                        self.ask_price_nodes.insert(price, PriceNode {
384                            prev: Some(curr),
385                            next: Some(next_val),
386                        });
387                        return Ok(());
388                    }
389                    else {
390                        break;
391                    }
392                }
393            }
394            return Ok(());
395        }
396    }
397
398    // remove price from the price linked list
399    pub fn remove_price(&mut self, is_bid: bool, price: u64) -> Result<(), L2Error> {
400        if is_bid {
401            self._remove_bid_price(price)?;
402            // remove the level from the level map
403            self.public_bid_level_map.remove(&price);
404            self.current_bid_level_map.remove(&price);
405            Ok(())
406        }
407        else {
408            self._remove_ask_price(price)?;
409            // remove the level from the level map
410            self.public_ask_level_map.remove(&price);
411            self.current_ask_level_map.remove(&price);
412            Ok(())
413        }
414    }
415    // remove price from the bid price linked list
416    fn _remove_bid_price(&mut self, price: u64) -> Result<(), L2Error> {
417        // Get the node to be removed before removing it
418        let node = match self.bid_price_nodes.get(&price) {
419            Some(node) => node.clone(),
420            None => return Ok(()), // Price doesn't exist, nothing to remove
421        };
422
423        // Update the previous node's next pointer
424        if let Some(prev_price) = node.prev {
425            if let Some(prev_node) = self.bid_price_nodes.get_mut(&prev_price) {
426                prev_node.next = node.next;
427            }
428        } else {
429            // This is the head, update head pointer
430            self.bid_price_head = node.next;
431        }
432
433        // Update the next node's prev pointer
434        if let Some(next_price) = node.next {
435            if let Some(next_node) = self.bid_price_nodes.get_mut(&next_price) {
436                next_node.prev = node.prev;
437            }
438        } else {
439            // This is the tail, update tail pointer
440            self.bid_price_tail = node.prev;
441        }
442
443        // Remove the node from the map
444        self.bid_price_nodes.remove(&price);
445
446        // Remove the level from the level map
447        self.public_bid_level_map.remove(&price);
448        self.current_bid_level_map.remove(&price);
449
450        Ok(())
451    }
452
453    // remove price from the ask price linked list
454    fn _remove_ask_price(&mut self, price: u64) -> Result<(), L2Error> {
455        // Get the node to be removed before removing it
456        let node = match self.ask_price_nodes.get(&price) {
457            Some(node) => node.clone(),
458            None => return Ok(()), // Price doesn't exist, nothing to remove
459        };
460
461        // Update the previous node's next pointer
462        if let Some(prev_price) = node.prev {
463            if let Some(prev_node) = self.ask_price_nodes.get_mut(&prev_price) {
464                prev_node.next = node.next;
465            }
466        } else {
467            // This is the head, update head pointer
468            self.ask_price_head = node.next;
469        }
470
471        // Update the next node's prev pointer
472        if let Some(next_price) = node.next {
473            if let Some(next_node) = self.ask_price_nodes.get_mut(&next_price) {
474                next_node.prev = node.prev;
475            }
476        } else {
477            // This is the tail, update tail pointer
478            self.ask_price_tail = node.prev;
479        }
480
481        // Remove the node from the map
482        self.ask_price_nodes.remove(&price);
483
484        // Remove the level from the level map
485        self.public_ask_level_map.remove(&price);
486        self.current_ask_level_map.remove(&price);
487
488        Ok(())
489    }
490
491    /// Helper function to collect all bid prices in order (descending)
492    pub fn collect_bid_prices(&self) -> Vec<u64> {
493        let mut prices = Vec::new();
494        let mut current = self.bid_price_head;
495        while let Some(price) = current {
496            prices.push(price);
497            current = self.bid_price_nodes.get(&price).and_then(|node| node.next);
498        }
499        prices
500    }
501
502    /// Helper function to collect all ask prices in order (ascending)
503    pub fn collect_ask_prices(&self) -> Vec<u64> {
504        let mut prices = Vec::new();
505        let mut current = self.ask_price_head;
506        while let Some(price) = current {
507            prices.push(price);
508            current = self.ask_price_nodes.get(&price).and_then(|node| node.next);
509        }
510        prices
511    }
512
513    /// Helper function to format a u64 number (in 8 decimals) to a string with 8 decimal places
514    /// Example: 100_000_000 -> "1.00000000", 50_000_000 -> "0.50000000"
515    fn format_8_decimals(value: u64) -> String {
516        const DECIMAL_PLACES: u64 = 100_000_000; // 10^8
517        
518        let integer_part = value / DECIMAL_PLACES;
519        let decimal_part = value % DECIMAL_PLACES;
520        
521        format!("{}.{:08}", integer_part, decimal_part)
522    }
523
524    /// get L2 snapshot (raw numbers)
525    /// Returns an array of arrays where each inner array is [price in 8 decimals, base amount in 8 decimals]
526    /// The outer array has step length
527    pub fn get_snapshot_raw(&self, is_bid: bool, scale: u64, step: u32) -> Result<Vec<Vec<u64>>, L2Error> {
528        // Get the appropriate levels map based on is_bid
529        let levels_map = if is_bid {
530            &self.bid_level_list
531        } else {
532            &self.ask_level_list
533        };
534
535        // Get levels for the given scale, or empty vector if scale doesn't exist
536        let levels = levels_map.get(&scale).cloned().unwrap_or_default();
537
538        // Take step number of levels and convert to array format
539        let snapshot: Vec<Vec<u64>> = levels
540            .iter()
541            .take(step as usize)
542            .map(|level| {
543                vec![
544                    level.price,                    // price in 8 decimals
545                    level.pqty,          // base amount in 8 decimals (convert from u128 to u64)
546                    level.cqty,          // base amount in 8 decimals (convert from u128 to u64)
547                ]
548            })
549            .collect();
550
551        Ok(snapshot)
552    }
553
554    /// get L2 snapshot (formatted strings)
555    /// Returns an array of arrays where each inner array is [price as string with 8 decimals, base amount as string with 8 decimals]
556    /// The outer array has step length
557    /// Numbers are formatted from raw 8-decimal integers to strings with 8 decimal places
558    pub fn get_snapshot(&self, is_bid: bool, scale: u64, step: u32) -> Result<Vec<Vec<String>>, L2Error> {
559        // Get raw snapshot first
560        let raw_snapshot = self.get_snapshot_raw(is_bid, scale, step)?;
561
562        // Convert raw numbers to formatted strings
563        let snapshot: Vec<Vec<String>> = raw_snapshot
564            .iter()
565            .map(|level| {
566                vec![
567                    Self::format_8_decimals(level[0]),  // format price
568                    Self::format_8_decimals(level[1]),  // format public quantity
569                    Self::format_8_decimals(level[2]),  // format current quantity
570                ]
571            })
572            .collect();
573
574        Ok(snapshot)
575    }
576}