-
Notifications
You must be signed in to change notification settings - Fork 102
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: expose things #14
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
use std::iter::Peekable; | ||
|
||
use crate::{ | ||
critbit::{LeafNodeIterator, Slab}, | ||
matching::Side, | ||
}; | ||
|
||
pub struct OrderBook<'a> { | ||
side: Side, | ||
slab: &'a Slab, | ||
} | ||
|
||
impl<'a> OrderBook<'a> { | ||
pub fn new(side: Side, slab: &'a Slab) -> Self { | ||
OrderBook { side, slab } | ||
} | ||
|
||
/// Iterate over level 2 data | ||
pub fn levels(&'a self) -> LevelsIterator<'a> { | ||
let is_descending = self.side == Side::Bid; | ||
LevelsIterator { | ||
leafs: self.slab.iter(is_descending).peekable(), | ||
} | ||
} | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Think you can do this in a single expression if you import
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The only way i found that would satisfy lifetime requirements is to add an iter function on LevelsIterator, is this how we want it? pub struct LevelsIterator<'a> {
leafs: LeafNodeIterator<'a>,
}
impl<'a> LevelsIterator<'a> {
fn iter(&'a self) -> Box<dyn Iterator<Item=Level> + 'a> {
Box::new(self.leafs
.map(|leaf| (leaf.price(), leaf.quantity()))
.group_by(|(p, q)| *p)
.into_iter()
.map(|(price, group)| Level {
price: price.get(),
quantity: group.map(|(_, q)| q).sum(),
}))
}
} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. lgtm There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I ended up not adding this, i think while more elegant we end up bringing this heap allocation we didn't have before so any program using this will pay. Let me know if you think it is essential |
||
|
||
/// Level 2 data | ||
/// | ||
/// Values are in lot sizes. | ||
#[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
pub struct Level { | ||
pub price: u64, | ||
pub quantity: u64, | ||
} | ||
|
||
pub struct LevelsIterator<'a> { | ||
leafs: Peekable<LeafNodeIterator<'a>>, | ||
} | ||
|
||
impl Iterator for LevelsIterator<'_> { | ||
type Item = Level; | ||
|
||
fn next(&mut self) -> Option<Self::Item> { | ||
let mut level: Option<Level> = None; | ||
while let Some(leaf) = self.leafs.peek() { | ||
let price = leaf.price().get(); | ||
let quantity = leaf.quantity(); | ||
match &mut level { | ||
Some(Level { | ||
price: cur_price, | ||
quantity: cur_quantity, | ||
}) => { | ||
if price == *cur_price { | ||
*cur_quantity += quantity; | ||
} else { | ||
return level; | ||
} | ||
} | ||
None => { | ||
level = Some(Level { price, quantity }); | ||
} | ||
} | ||
self.leafs.next(); | ||
} | ||
level | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
use crate::{critbit::LeafNode, fees::FeeTier}; | ||
|
||
use bytemuck::cast_slice_mut; | ||
use rand::prelude::*; | ||
|
||
#[test] | ||
fn test_levels() { | ||
let mut aligned_buf = vec![0u64; 10_000]; | ||
let bytes: &mut [u8] = cast_slice_mut(aligned_buf.as_mut_slice()); | ||
let slab = Slab::new(bytes); | ||
|
||
let mut rng = StdRng::from_entropy(); | ||
|
||
let orders: Vec<(u64, u64)> = vec![ | ||
(1000, 50), | ||
(1000, 25), | ||
(1300, 100), | ||
(1300, 40), | ||
(1300, 250), | ||
(1480, 36), | ||
(1495, 3000), | ||
(1495, 340), | ||
]; | ||
for (i, (price, qty)) in orders.into_iter().enumerate() { | ||
let offset = rng.gen(); | ||
let owner = rng.gen(); | ||
let key = ((price as u128) << 64) | (!(i as u64) as u128); | ||
|
||
slab.insert_leaf(&LeafNode::new(offset, key, owner, qty, FeeTier::Base, 0)) | ||
.unwrap(); | ||
} | ||
|
||
let ask_levels = vec![ | ||
Level { | ||
price: 1000, | ||
quantity: 75, | ||
}, | ||
Level { | ||
price: 1300, | ||
quantity: 390, | ||
}, | ||
Level { | ||
price: 1480, | ||
quantity: 36, | ||
}, | ||
Level { | ||
price: 1495, | ||
quantity: 3340, | ||
}, | ||
]; | ||
|
||
let orderbook_asks = OrderBook::new(Side::Ask, slab); | ||
assert!(ask_levels.clone().into_iter().eq(orderbook_asks.levels())); | ||
|
||
let orderbook_bids = OrderBook::new(Side::Bid, slab); | ||
assert!(ask_levels.into_iter().rev().eq(orderbook_bids.levels())); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i think it's worth initializing this one with a large enough capacity
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1 the bump allocator in solana runtime makes dynamic vector resizing deceptively brutal
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can't see anything in history giving an hint at what vec capacity we want to start with, any hint?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Vec::with_capacity(self.header().leaf_count as usize);