Perp: Merge ordertrees for fixed and oracle_pegged (#321)

By sharing the nodes list we're much less likely to grossly
over-allocate space.
This commit is contained in:
Christian Kamm 2022-12-07 20:54:18 +01:00 committed by GitHub
parent 6aea3f97db
commit c44dc045cf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 533 additions and 362 deletions

View File

@ -25,15 +25,13 @@ const_assert_eq!(
std::mem::size_of::<Orderbook>(),
2 * std::mem::size_of::<BookSide>() + 2400
);
const_assert_eq!(std::mem::size_of::<Orderbook>(), 495040);
const_assert_eq!(std::mem::size_of::<Orderbook>(), 249824);
const_assert_eq!(std::mem::size_of::<Orderbook>() % 8, 0);
impl Orderbook {
pub fn init(&mut self) {
self.bids.fixed.order_tree_type = OrderTreeType::Bids.into();
self.bids.oracle_pegged.order_tree_type = OrderTreeType::Bids.into();
self.asks.fixed.order_tree_type = OrderTreeType::Asks.into();
self.asks.oracle_pegged.order_tree_type = OrderTreeType::Asks.into();
self.bids.nodes.order_tree_type = OrderTreeType::Bids.into();
self.asks.nodes.order_tree_type = OrderTreeType::Asks.into();
}
pub fn bookside_mut(&mut self, side: Side) -> &mut BookSide {
@ -124,7 +122,7 @@ impl Orderbook {
let opposing_bookside = self.bookside_mut(other_side);
for best_opposing in opposing_bookside.iter_all_including_invalid(now_ts, oracle_price_lots)
{
if !best_opposing.is_valid {
if !best_opposing.is_valid() {
// Remove the order from the book unless we've done that enough
if number_of_dropped_expired_orders < DROP_EXPIRED_ORDER_LIMIT {
number_of_dropped_expired_orders += 1;
@ -212,7 +210,7 @@ impl Orderbook {
// Apply changes to matched asks (handles invalidate on delete!)
for (handle, new_quantity) in matched_order_changes {
opposing_bookside
.node_mut(handle)
.node_mut(handle.node)
.unwrap()
.as_leaf_mut()
.unwrap()
@ -229,10 +227,9 @@ impl Orderbook {
}
if let Some(order_tree_target) = post_target {
let bookside = self.bookside_mut(side);
let order_tree = bookside.orders_mut(order_tree_target);
// Drop an expired order if possible
if let Some(expired_order) = order_tree.remove_one_expired(now_ts) {
if let Some(expired_order) = bookside.remove_one_expired(order_tree_target, now_ts) {
let event = OutEvent::new(
side,
expired_order.owner_slot,
@ -244,12 +241,13 @@ impl Orderbook {
event_queue.push_back(cast(event)).unwrap();
}
if order_tree.is_full() {
if bookside.is_full() {
// If this bid is higher than lowest bid, boot that bid and insert this one
let worst_order = order_tree.remove_worst().unwrap();
let (worst_order, worst_price) =
bookside.remove_worst(now_ts, oracle_price_lots).unwrap();
// MangoErrorCode::OutOfSpace
require!(
side.is_price_data_better(price_data, worst_order.price_data()),
side.is_price_better(price_lots, worst_price),
MangoError::SomeError
);
let event = OutEvent::new(
@ -275,7 +273,7 @@ impl Orderbook {
order.time_in_force,
order.peg_limit(),
);
let _result = order_tree.insert_leaf(&new_order)?;
let _result = bookside.insert_leaf(order_tree_target, &new_order)?;
// TODO OPT remove if PlacePerpOrder needs more compute
msg!(
@ -357,8 +355,8 @@ impl Orderbook {
) -> Result<LeafNode> {
let side = side_and_tree.side();
let book_component = side_and_tree.order_tree();
let leaf_node = self.bookside_mut(side).orders_mut(book_component).
remove_by_key(order_id).ok_or_else(|| {
let leaf_node = self.bookside_mut(side).
remove_by_key(book_component, order_id).ok_or_else(|| {
error_msg!("invalid perp order id {order_id} for side {side:?} and component {book_component:?}")
})?;
if let Some(owner) = expected_owner {

View File

@ -17,8 +17,8 @@ use super::*;
)]
#[repr(u8)]
pub enum BookSideOrderTree {
Fixed,
OraclePegged,
Fixed = 0,
OraclePegged = 1,
}
/// Reference to a node in a book side component
@ -30,14 +30,16 @@ pub struct BookSideOrderHandle {
#[zero_copy]
#[derive(bytemuck::Pod, bytemuck::Zeroable)]
pub struct BookSide {
pub fixed: OrderTree,
pub oracle_pegged: OrderTree,
pub roots: [OrderTreeRoot; 2],
pub reserved_roots: [OrderTreeRoot; 4],
pub reserved: [u8; 256],
pub nodes: OrderTreeNodes,
}
const_assert_eq!(
std::mem::size_of::<BookSide>(),
std::mem::size_of::<OrderTree>() * 2
std::mem::size_of::<OrderTreeNodes>() + 6 * std::mem::size_of::<OrderTreeRoot>() + 256
);
const_assert_eq!(std::mem::size_of::<BookSide>(), 246320);
const_assert_eq!(std::mem::size_of::<BookSide>(), 123712);
const_assert_eq!(std::mem::size_of::<BookSide>() % 8, 0);
impl BookSide {
@ -50,7 +52,7 @@ impl BookSide {
now_ts: u64,
oracle_price_lots: i64,
) -> impl Iterator<Item = BookSideIterItem> {
BookSideIter::new(self, now_ts, oracle_price_lots).filter(|it| it.is_valid)
BookSideIter::new(self, now_ts, oracle_price_lots).filter(|it| it.is_valid())
}
/// Iterate over all entries, including invalid orders
@ -58,43 +60,76 @@ impl BookSide {
BookSideIter::new(self, now_ts, oracle_price_lots)
}
pub fn orders(&self, component: BookSideOrderTree) -> &OrderTree {
match component {
BookSideOrderTree::Fixed => &self.fixed,
BookSideOrderTree::OraclePegged => &self.oracle_pegged,
}
pub fn node(&self, handle: NodeHandle) -> Option<&AnyNode> {
self.nodes.node(handle)
}
pub fn orders_mut(&mut self, component: BookSideOrderTree) -> &mut OrderTree {
match component {
BookSideOrderTree::Fixed => &mut self.fixed,
BookSideOrderTree::OraclePegged => &mut self.oracle_pegged,
}
pub fn node_mut(&mut self, handle: NodeHandle) -> Option<&mut AnyNode> {
self.nodes.node_mut(handle)
}
pub fn node(&self, key: BookSideOrderHandle) -> Option<&AnyNode> {
self.orders(key.order_tree).node(key.node)
pub fn root(&self, component: BookSideOrderTree) -> &OrderTreeRoot {
&self.roots[component as usize]
}
pub fn node_mut(&mut self, key: BookSideOrderHandle) -> Option<&mut AnyNode> {
self.orders_mut(key.order_tree).node_mut(key.node)
pub fn root_mut(&mut self, component: BookSideOrderTree) -> &mut OrderTreeRoot {
&mut self.roots[component as usize]
}
pub fn is_full(&self, component: BookSideOrderTree) -> bool {
self.orders(component).is_full()
pub fn is_full(&self) -> bool {
self.nodes.is_full()
}
pub fn remove_worst(&mut self, component: BookSideOrderTree) -> Option<LeafNode> {
self.orders_mut(component).remove_worst()
pub fn insert_leaf(
&mut self,
component: BookSideOrderTree,
new_leaf: &LeafNode,
) -> Result<(NodeHandle, Option<LeafNode>)> {
let root = &mut self.roots[component as usize];
self.nodes.insert_leaf(root, new_leaf)
}
/// Remove the order with the lowest expiry timestamp, if that's < now_ts.
/// Remove the overall worst-price order.
pub fn remove_worst(&mut self, now_ts: u64, oracle_price_lots: i64) -> Option<(LeafNode, i64)> {
let worst_fixed = self.nodes.find_worst(&self.roots[0]);
let worst_pegged = self.nodes.find_worst(&self.roots[1]);
let side = match self.nodes.order_tree_type() {
OrderTreeType::Bids => Side::Bid,
OrderTreeType::Asks => Side::Ask,
};
let worse = rank_orders(
side,
worst_fixed,
worst_pegged,
true,
now_ts,
oracle_price_lots,
)?;
let price = worse.price_lots;
let key = worse.node.key;
let order_tree = worse.handle.order_tree;
let n = self.remove_by_key(order_tree, key)?;
Some((n, price))
}
/// Remove the order with the lowest expiry timestamp in the component, if that's < now_ts.
/// If there is none, try to remove the lowest expiry one from the other component.
pub fn remove_one_expired(
&mut self,
component: BookSideOrderTree,
now_ts: u64,
) -> Option<LeafNode> {
self.orders_mut(component).remove_one_expired(now_ts)
let root = &mut self.roots[component as usize];
if let Some(n) = self.nodes.remove_one_expired(root, now_ts) {
return Some(n);
}
let other_component = match component {
BookSideOrderTree::Fixed => BookSideOrderTree::OraclePegged,
BookSideOrderTree::OraclePegged => BookSideOrderTree::Fixed,
};
let other_root = &mut self.roots[other_component as usize];
self.nodes.remove_one_expired(other_root, now_ts)
}
pub fn remove_by_key(
@ -102,11 +137,8 @@ impl BookSide {
component: BookSideOrderTree,
search_key: u128,
) -> Option<LeafNode> {
self.orders_mut(component).remove_by_key(search_key)
}
pub fn remove(&mut self, key: BookSideOrderHandle) -> Option<AnyNode> {
self.orders_mut(key.order_tree).remove(key.node)
let root = &mut self.roots[component as usize];
self.nodes.remove_by_key(root, search_key)
}
}
@ -115,18 +147,10 @@ mod tests {
use super::*;
use bytemuck::Zeroable;
fn new_order_tree(order_tree_type: OrderTreeType) -> OrderTree {
OrderTree {
order_tree_type: order_tree_type.into(),
padding: [0u8; 3],
bump_index: 0,
free_list_len: 0,
free_list_head: 0,
root_node: 0,
leaf_count: 0,
nodes: [AnyNode::zeroed(); MAX_ORDERTREE_NODES],
reserved: [0; 256],
}
fn new_order_tree(order_tree_type: OrderTreeType) -> OrderTreeNodes {
let mut ot = OrderTreeNodes::zeroed();
ot.order_tree_type = order_tree_type.into();
ot
}
fn bookside_iteration_random_helper(side: Side) {
@ -138,8 +162,9 @@ mod tests {
Side::Ask => OrderTreeType::Asks,
};
let mut fixed = new_order_tree(order_tree_type);
let mut oracle_pegged = new_order_tree(order_tree_type);
let mut order_tree = new_order_tree(order_tree_type);
let mut root_fixed = OrderTreeRoot::zeroed();
let mut root_pegged = OrderTreeRoot::zeroed();
let new_leaf = |key: u128| {
LeafNode::new(
0,
@ -160,9 +185,11 @@ mod tests {
// ensure at least one oracle pegged order visible even at oracle price 1
let key = new_node_key(side, oracle_pegged_price_data(20), 0);
keys.push(key);
oracle_pegged.insert_leaf(&new_leaf(key)).unwrap();
order_tree
.insert_leaf(&mut root_pegged, &new_leaf(key))
.unwrap();
while oracle_pegged.leaf_count < 100 {
while root_pegged.leaf_count < 100 {
let price_data: u64 = oracle_pegged_price_data(rng.gen_range(-20..20));
let seq_num: u64 = rng.gen_range(0..1000);
let key = new_node_key(side, price_data, seq_num);
@ -170,10 +197,12 @@ mod tests {
continue;
}
keys.push(key);
oracle_pegged.insert_leaf(&new_leaf(key)).unwrap();
order_tree
.insert_leaf(&mut root_pegged, &new_leaf(key))
.unwrap();
}
while fixed.leaf_count < 100 {
while root_fixed.leaf_count < 100 {
let price_data: u64 = rng.gen_range(1..50);
let seq_num: u64 = rng.gen_range(0..1000);
let key = new_node_key(side, price_data, seq_num);
@ -181,12 +210,16 @@ mod tests {
continue;
}
keys.push(key);
fixed.insert_leaf(&new_leaf(key)).unwrap();
order_tree
.insert_leaf(&mut root_fixed, &new_leaf(key))
.unwrap();
}
let bookside = BookSide {
fixed,
oracle_pegged,
roots: [root_fixed, root_pegged],
reserved_roots: [OrderTreeRoot::zeroed(); 4],
reserved: [0; 256],
nodes: order_tree,
};
// verify iteration order for different oracle prices
@ -219,13 +252,15 @@ mod tests {
bookside_iteration_random_helper(Side::Ask);
}
#[test]
fn bookside_order_filtering() {
fn bookside_setup() -> BookSide {
use std::cell::RefCell;
let side = Side::Bid;
let order_tree_type = OrderTreeType::Bids;
let mut fixed = new_order_tree(order_tree_type);
let mut oracle_pegged = new_order_tree(order_tree_type);
let order_tree = RefCell::new(new_order_tree(order_tree_type));
let mut root_fixed = OrderTreeRoot::zeroed();
let mut root_pegged = OrderTreeRoot::zeroed();
let new_node = |key: u128, tif: u16, peg_limit: i64| {
LeafNode::new(
0,
@ -241,12 +276,16 @@ mod tests {
};
let mut add_fixed = |price: i64, tif: u16| {
let key = new_node_key(side, fixed_price_data(price).unwrap(), 0);
fixed.insert_leaf(&new_node(key, tif, -1)).unwrap();
order_tree
.borrow_mut()
.insert_leaf(&mut root_fixed, &new_node(key, tif, -1))
.unwrap();
};
let mut add_pegged = |price_offset: i64, tif: u16, peg_limit: i64| {
let key = new_node_key(side, oracle_pegged_price_data(price_offset), 0);
oracle_pegged
.insert_leaf(&new_node(key, tif, peg_limit))
order_tree
.borrow_mut()
.insert_leaf(&mut root_pegged, &new_node(key, tif, peg_limit))
.unwrap();
};
@ -256,10 +295,17 @@ mod tests {
add_pegged(-15, 0, -1);
add_pegged(-20, 7, 95);
let bookside = BookSide {
fixed,
oracle_pegged,
};
BookSide {
roots: [root_fixed, root_pegged],
reserved_roots: [OrderTreeRoot::zeroed(); 4],
reserved: [0; 256],
nodes: order_tree.into_inner(),
}
}
#[test]
fn bookside_order_filtering() {
let bookside = bookside_setup();
let order_prices = |now_ts: u64, oracle: i64| -> Vec<i64> {
bookside
@ -280,4 +326,44 @@ mod tests {
assert_eq!(order_prices(0, 2015), vec![2000, 120, 100]);
assert_eq!(order_prices(1010, 2015), vec![2000, 100]);
}
#[test]
fn bookside_remove_worst() {
use std::cell::RefCell;
let bookside = RefCell::new(bookside_setup());
let order_prices = |now_ts: u64, oracle: i64| -> Vec<i64> {
bookside
.borrow()
.iter_valid(now_ts, oracle)
.map(|it| it.price_lots)
.collect()
};
// remove pegged order
assert_eq!(order_prices(0, 100), vec![120, 100, 90, 85, 80]);
let (_, p) = bookside.borrow_mut().remove_worst(0, 100).unwrap();
assert_eq!(p, 80);
assert_eq!(order_prices(0, 100), vec![120, 100, 90, 85]);
// remove fixed order (order at 190=200-10 hits the peg limit)
assert_eq!(order_prices(0, 200), vec![185, 120, 100]);
let (_, p) = bookside.borrow_mut().remove_worst(0, 200).unwrap();
assert_eq!(p, 100);
assert_eq!(order_prices(0, 200), vec![185, 120]);
// remove until end
assert_eq!(order_prices(0, 100), vec![120, 90, 85]);
let (_, p) = bookside.borrow_mut().remove_worst(0, 100).unwrap();
assert_eq!(p, 85);
assert_eq!(order_prices(0, 100), vec![120, 90]);
let (_, p) = bookside.borrow_mut().remove_worst(0, 100).unwrap();
assert_eq!(p, 90);
assert_eq!(order_prices(0, 100), vec![120]);
let (_, p) = bookside.borrow_mut().remove_worst(0, 100).unwrap();
assert_eq!(p, 120);
assert_eq!(order_prices(0, 100), Vec::<i64>::new());
}
}

View File

@ -4,7 +4,13 @@ pub struct BookSideIterItem<'a> {
pub handle: BookSideOrderHandle,
pub node: &'a LeafNode,
pub price_lots: i64,
pub is_valid: bool,
pub state: OrderState,
}
impl<'a> BookSideIterItem<'a> {
pub fn is_valid(&self) -> bool {
self.state == OrderState::Valid
}
}
/// Iterates the fixed and oracle_pegged OrderTrees simultaneously, allowing users to
@ -25,8 +31,12 @@ pub struct BookSideIter<'a> {
impl<'a> BookSideIter<'a> {
pub fn new(book_side: &'a BookSide, now_ts: u64, oracle_price_lots: i64) -> Self {
Self {
fixed_iter: book_side.fixed.iter(),
oracle_pegged_iter: book_side.oracle_pegged.iter(),
fixed_iter: book_side
.nodes
.iter(book_side.root(BookSideOrderTree::Fixed)),
oracle_pegged_iter: book_side
.nodes
.iter(book_side.root(BookSideOrderTree::OraclePegged)),
now_ts,
oracle_price_lots,
}
@ -34,29 +44,26 @@ impl<'a> BookSideIter<'a> {
}
#[derive(Clone, Copy, PartialEq)]
enum OrderState {
pub enum OrderState {
Valid,
Invalid,
Skipped,
}
fn oracle_pegged_price(
oracle_price_lots: i64,
node: &LeafNode,
side: Side,
) -> (OrderState, Option<i64>) {
/// For pegged orders with offsets that let the price escape the 1..i64::MAX range,
/// this function returns Skipped and clamps `price` to that range.
fn oracle_pegged_price(oracle_price_lots: i64, node: &LeafNode, side: Side) -> (OrderState, i64) {
let price_data = node.price_data();
let price_offset = oracle_pegged_price_offset(price_data);
if let Some(price) = oracle_price_lots.checked_add(price_offset) {
if price >= 1 {
if node.peg_limit != -1 && side.is_price_better(price, node.peg_limit) {
return (OrderState::Invalid, Some(price));
} else {
return (OrderState::Valid, Some(price));
}
let price = oracle_price_lots.saturating_add(price_offset);
if price >= 1 && price < i64::MAX {
if node.peg_limit != -1 && side.is_price_better(price, node.peg_limit) {
return (OrderState::Invalid, price);
} else {
return (OrderState::Valid, price);
}
}
(OrderState::Skipped, None)
(OrderState::Skipped, price.max(1))
}
fn key_for_price(key: u128, price_lots: i64) -> u128 {
@ -68,6 +75,76 @@ fn key_for_price(key: u128, price_lots: i64) -> u128 {
upper | lower
}
fn fixed_to_result(fixed: (NodeHandle, &LeafNode), now_ts: u64) -> BookSideIterItem {
let (handle, node) = fixed;
let expired = node.is_expired(now_ts);
BookSideIterItem {
handle: BookSideOrderHandle {
order_tree: BookSideOrderTree::Fixed,
node: handle,
},
node,
price_lots: fixed_price_lots(node.price_data()),
state: if expired {
OrderState::Invalid
} else {
OrderState::Valid
},
}
}
fn oracle_pegged_to_result(
pegged: (NodeHandle, &LeafNode, i64, OrderState),
now_ts: u64,
) -> BookSideIterItem {
let (handle, node, price_lots, state) = pegged;
let expired = node.is_expired(now_ts);
BookSideIterItem {
handle: BookSideOrderHandle {
order_tree: BookSideOrderTree::OraclePegged,
node: handle,
},
node,
price_lots,
state: if expired { OrderState::Invalid } else { state },
}
}
/// Returns (better, worse); will return the same value twice if no second order passed in
pub fn rank_orders<'a>(
side: Side,
fixed: Option<(NodeHandle, &'a LeafNode)>,
oracle_pegged: Option<(NodeHandle, &'a LeafNode)>,
return_worse: bool,
now_ts: u64,
oracle_price_lots: i64,
) -> Option<BookSideIterItem<'a>> {
// Enrich with data that'll always be needed
let oracle_pegged = oracle_pegged.map(|(handle, node)| {
let (state, price_lots) = oracle_pegged_price(oracle_price_lots, node, side);
(handle, node, price_lots, state)
});
match (fixed, oracle_pegged) {
(Some(f), Some(o)) => {
let is_better = if side == Side::Bid {
|a, b| a > b
} else {
|a, b| a < b
};
if is_better(f.1.key, key_for_price(o.1.key, o.2)) ^ return_worse {
Some(fixed_to_result(f, now_ts))
} else {
Some(oracle_pegged_to_result(o, now_ts))
}
}
(None, Some(o)) => Some(oracle_pegged_to_result(o, now_ts)),
(Some(f), None) => Some(fixed_to_result(f, now_ts)),
(None, None) => None,
}
}
impl<'a> Iterator for BookSideIter<'a> {
type Item = BookSideIterItem<'a>;
@ -85,69 +162,21 @@ impl<'a> Iterator for BookSideIter<'a> {
o_peek = self.oracle_pegged_iter.next()
}
match (self.fixed_iter.peek(), o_peek) {
(Some((d_handle, d_node)), Some((o_handle, o_node))) => {
let is_better = if side == Side::Bid {
|a, b| a > b
} else {
|a, b| a < b
};
let f_peek = self.fixed_iter.peek();
let (o_valid, o_price_maybe) =
oracle_pegged_price(self.oracle_price_lots, o_node, side);
let o_price = o_price_maybe.unwrap(); // Skipped orders are skipped above
if is_better(d_node.key, key_for_price(o_node.key, o_price)) {
self.fixed_iter.next();
Some(Self::Item {
handle: BookSideOrderHandle {
order_tree: BookSideOrderTree::Fixed,
node: d_handle,
},
node: d_node,
price_lots: fixed_price_lots(d_node.price_data()),
is_valid: d_node.is_not_expired(self.now_ts),
})
} else {
self.oracle_pegged_iter.next();
Some(Self::Item {
handle: BookSideOrderHandle {
order_tree: BookSideOrderTree::OraclePegged,
node: o_handle,
},
node: o_node,
price_lots: o_price,
is_valid: o_valid == OrderState::Valid
&& o_node.is_not_expired(self.now_ts),
})
}
}
(None, Some((handle, node))) => {
self.oracle_pegged_iter.next();
let (valid, price_maybe) = oracle_pegged_price(self.oracle_price_lots, node, side);
let price_lots = price_maybe.unwrap(); // Skipped orders are skipped above
Some(Self::Item {
handle: BookSideOrderHandle {
order_tree: BookSideOrderTree::OraclePegged,
node: handle,
},
node,
price_lots,
is_valid: valid == OrderState::Valid && node.is_not_expired(self.now_ts),
})
}
(Some((handle, node)), None) => {
self.fixed_iter.next();
Some(Self::Item {
handle: BookSideOrderHandle {
order_tree: BookSideOrderTree::Fixed,
node: handle,
},
node,
price_lots: fixed_price_lots(node.price_data()),
is_valid: node.is_not_expired(self.now_ts),
})
}
(None, None) => None,
}
let better = rank_orders(
side,
f_peek,
o_peek,
false,
self.now_ts,
self.oracle_price_lots,
)?;
match better.handle.order_tree {
BookSideOrderTree::Fixed => self.fixed_iter.next(),
BookSideOrderTree::OraclePegged => self.oracle_pegged_iter.next(),
};
Some(better)
}
}

View File

@ -27,28 +27,27 @@ mod tests {
use fixed::types::I80F48;
use solana_program::pubkey::Pubkey;
fn order_tree_leaf_by_key(order_tree: &OrderTree, key: u128) -> Option<&LeafNode> {
for (_, leaf) in order_tree.iter() {
if leaf.key == key {
return Some(leaf);
fn order_tree_leaf_by_key(bookside: &BookSide, key: u128) -> Option<&LeafNode> {
for component in [BookSideOrderTree::Fixed, BookSideOrderTree::OraclePegged] {
for (_, leaf) in bookside.nodes.iter(bookside.root(component)) {
if leaf.key == key {
return Some(leaf);
}
}
}
None
}
fn order_tree_contains_key(order_tree: &OrderTree, key: u128) -> bool {
for (_, leaf) in order_tree.iter() {
if leaf.key == key {
return true;
}
}
false
fn order_tree_contains_key(bookside: &BookSide, key: u128) -> bool {
order_tree_leaf_by_key(bookside, key).is_some()
}
fn order_tree_contains_price(order_tree: &OrderTree, price_data: u64) -> bool {
for (_, leaf) in order_tree.iter() {
if leaf.price_data() == price_data {
return true;
fn order_tree_contains_price(bookside: &BookSide, price_data: u64) -> bool {
for component in [BookSideOrderTree::Fixed, BookSideOrderTree::OraclePegged] {
for (_, leaf) in bookside.nodes.iter(bookside.root(component)) {
if leaf.price_data() == price_data {
return true;
}
}
}
false
@ -137,41 +136,78 @@ mod tests {
1000 + i as i64,
1000011 as u64,
);
if book.bids.fixed.is_full() {
if book.bids.is_full() {
break;
}
}
assert!(book.bids.fixed.is_full());
assert_eq!(book.bids.fixed.min_leaf().unwrap().price_data(), 1001);
assert!(book.bids.is_full());
assert_eq!(
fixed_price_lots(book.bids.fixed.max_leaf().unwrap().price_data()),
(1000 + book.bids.fixed.leaf_count) as i64
book.bids
.nodes
.min_leaf(&book.bids.roots[0])
.unwrap()
.1
.price_data(),
1001
);
assert_eq!(
fixed_price_lots(
book.bids
.nodes
.max_leaf(&book.bids.roots[0])
.unwrap()
.1
.price_data()
),
(1000 + book.bids.roots[0].leaf_count) as i64
);
// add another bid at a higher price before expiry, replacing the lowest-price one (1001)
new_order(&mut book, &mut event_queue, Side::Bid, 1005, 1000000 - 1);
assert_eq!(book.bids.fixed.min_leaf().unwrap().price_data(), 1002);
assert_eq!(
book.bids
.nodes
.min_leaf(&book.bids.roots[0])
.unwrap()
.1
.price_data(),
1002
);
assert_eq!(event_queue.len(), 1);
// adding another bid after expiry removes the soonest-expiring order (1005)
new_order(&mut book, &mut event_queue, Side::Bid, 999, 2000000);
assert_eq!(book.bids.fixed.min_leaf().unwrap().price_data(), 999);
assert!(!order_tree_contains_key(&book.bids.fixed, 1005));
assert_eq!(
book.bids
.nodes
.min_leaf(&book.bids.roots[0])
.unwrap()
.1
.price_data(),
999
);
assert!(!order_tree_contains_key(&book.bids, 1005));
assert_eq!(event_queue.len(), 2);
// adding an ask will wipe up to three expired bids at the top of the book
let bids_max = book.bids.fixed.max_leaf().unwrap().price_data();
let bids_count = book.bids.fixed.leaf_count;
let bids_max = book
.bids
.nodes
.max_leaf(&book.bids.roots[0])
.unwrap()
.1
.price_data();
let bids_count = book.bids.roots[0].leaf_count;
new_order(&mut book, &mut event_queue, Side::Ask, 6000, 1500000);
assert_eq!(book.bids.fixed.leaf_count, bids_count - 5);
assert_eq!(book.asks.fixed.leaf_count, 1);
assert_eq!(book.bids.roots[0].leaf_count, bids_count - 5);
assert_eq!(book.asks.roots[0].leaf_count, 1);
assert_eq!(event_queue.len(), 2 + 5);
assert!(!order_tree_contains_price(&book.bids.fixed, bids_max));
assert!(!order_tree_contains_price(&book.bids.fixed, bids_max - 1));
assert!(!order_tree_contains_price(&book.bids.fixed, bids_max - 2));
assert!(!order_tree_contains_price(&book.bids.fixed, bids_max - 3));
assert!(!order_tree_contains_price(&book.bids.fixed, bids_max - 4));
assert!(order_tree_contains_price(&book.bids.fixed, bids_max - 5));
assert!(!order_tree_contains_price(&book.bids, bids_max));
assert!(!order_tree_contains_price(&book.bids, bids_max - 1));
assert!(!order_tree_contains_price(&book.bids, bids_max - 2));
assert!(!order_tree_contains_price(&book.bids, bids_max - 3));
assert!(!order_tree_contains_price(&book.bids, bids_max - 4));
assert!(order_tree_contains_price(&book.bids, bids_max - 5));
}
#[test]
@ -236,13 +272,10 @@ mod tests {
SideAndOrderTree::BidFixed
);
assert!(order_tree_contains_key(
&book.bids.fixed,
&book.bids,
maker.perp_order_mut_by_raw_index(0).id
));
assert!(order_tree_contains_price(
&book.bids.fixed,
price_lots as u64
));
assert!(order_tree_contains_price(&book.bids, price_lots as u64));
assert_eq!(
maker.perp_position_by_raw_index(0).bids_base_lots,
bid_quantity
@ -287,7 +320,7 @@ mod tests {
// the remainder of the maker order is still on the book
// (the maker account is unchanged: it was not even passed in)
let order =
order_tree_leaf_by_key(&book.bids.fixed, maker.perp_order_by_raw_index(0).id).unwrap();
order_tree_leaf_by_key(&book.bids, maker.perp_order_by_raw_index(0).id).unwrap();
assert_eq!(fixed_price_lots(order.price_data()), price_lots);
assert_eq!(order.quantity, bid_quantity - match_quantity);

View File

@ -203,8 +203,8 @@ impl LeafNode {
}
#[inline(always)]
pub fn is_not_expired(&self, now_ts: u64) -> bool {
self.time_in_force == 0 || now_ts < self.timestamp + self.time_in_force as u64
pub fn is_expired(&self, now_ts: u64) -> bool {
self.time_in_force > 0 && now_ts >= self.timestamp + self.time_in_force as u64
}
}

View File

@ -26,30 +26,47 @@ pub enum OrderTreeType {
Asks,
}
#[derive(Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
#[repr(C)]
pub struct OrderTreeRoot {
pub maybe_node: NodeHandle,
pub leaf_count: u32,
}
const_assert_eq!(std::mem::size_of::<OrderTreeRoot>(), 8);
const_assert_eq!(std::mem::size_of::<OrderTreeRoot>() % 8, 0);
impl OrderTreeRoot {
pub fn node(&self) -> Option<NodeHandle> {
if self.leaf_count == 0 {
None
} else {
Some(self.maybe_node)
}
}
}
/// A binary tree on AnyNode::key()
///
/// The key encodes the price in the top 64 bits.
#[zero_copy]
#[derive(bytemuck::Pod, bytemuck::Zeroable)]
pub struct OrderTree {
pub struct OrderTreeNodes {
pub order_tree_type: u8, // OrderTreeType, but that's not POD
pub padding: [u8; 3],
pub bump_index: u32,
pub free_list_len: u32,
pub free_list_head: NodeHandle,
pub root_node: NodeHandle,
pub leaf_count: u32,
pub reserved: [u8; 512],
pub nodes: [AnyNode; MAX_ORDERTREE_NODES],
pub reserved: [u8; 256],
}
const_assert_eq!(
std::mem::size_of::<OrderTree>(),
1 + 3 + 4 * 2 + 4 + 4 + 4 + 120 * 1024 + 256
std::mem::size_of::<OrderTreeNodes>(),
1 + 3 + 4 * 2 + 4 + 512 + 120 * 1024
);
const_assert_eq!(std::mem::size_of::<OrderTree>(), 123160);
const_assert_eq!(std::mem::size_of::<OrderTree>() % 8, 0);
const_assert_eq!(std::mem::size_of::<OrderTreeNodes>(), 123408);
const_assert_eq!(std::mem::size_of::<OrderTreeNodes>() % 8, 0);
impl OrderTree {
impl OrderTreeNodes {
pub fn order_tree_type(&self) -> OrderTreeType {
OrderTreeType::try_from(self.order_tree_type).unwrap()
}
@ -58,20 +75,20 @@ impl OrderTree {
///
/// smallest to highest for asks
/// highest to smallest for bids
pub fn iter(&self) -> OrderTreeIter {
OrderTreeIter::new(self)
pub fn iter(&self, root: &OrderTreeRoot) -> OrderTreeIter {
OrderTreeIter::new(self, root)
}
pub fn node_mut(&mut self, key: NodeHandle) -> Option<&mut AnyNode> {
let node = &mut self.nodes[key as usize];
pub fn node_mut(&mut self, handle: NodeHandle) -> Option<&mut AnyNode> {
let node = &mut self.nodes[handle as usize];
let tag = NodeTag::try_from(node.tag);
match tag {
Ok(NodeTag::InnerNode) | Ok(NodeTag::LeafNode) => Some(node),
_ => None,
}
}
pub fn node(&self, key: NodeHandle) -> Option<&AnyNode> {
let node = &self.nodes[key as usize];
pub fn node(&self, handle: NodeHandle) -> Option<&AnyNode> {
let node = &self.nodes[handle as usize];
let tag = NodeTag::try_from(node.tag);
match tag {
Ok(NodeTag::InnerNode) | Ok(NodeTag::LeafNode) => Some(node),
@ -79,45 +96,37 @@ impl OrderTree {
}
}
pub fn remove_min(&mut self) -> Option<LeafNode> {
self.remove_by_key(self.min_leaf()?.key)
pub fn remove_worst(&mut self, root: &mut OrderTreeRoot) -> Option<LeafNode> {
self.remove_by_key(root, self.find_worst(root)?.1.key)
}
pub fn remove_max(&mut self) -> Option<LeafNode> {
self.remove_by_key(self.max_leaf()?.key)
}
pub fn remove_worst(&mut self) -> Option<LeafNode> {
pub fn find_worst(&self, root: &OrderTreeRoot) -> Option<(NodeHandle, &LeafNode)> {
match self.order_tree_type() {
OrderTreeType::Bids => self.remove_min(),
OrderTreeType::Asks => self.remove_max(),
OrderTreeType::Bids => self.min_leaf(root),
OrderTreeType::Asks => self.max_leaf(root),
}
}
/// Remove the order with the lowest expiry timestamp, if that's < now_ts.
pub fn remove_one_expired(&mut self, now_ts: u64) -> Option<LeafNode> {
let (expired_h, expires_at) = self.find_earliest_expiry()?;
pub fn remove_one_expired(
&mut self,
root: &mut OrderTreeRoot,
now_ts: u64,
) -> Option<LeafNode> {
let (handle, expires_at) = self.find_earliest_expiry(root)?;
if expires_at < now_ts {
self.remove_by_key(self.node(expired_h)?.key()?)
self.remove_by_key(root, self.node(handle)?.key()?)
} else {
None
}
}
pub fn root(&self) -> Option<NodeHandle> {
if self.leaf_count == 0 {
None
} else {
Some(self.root_node)
}
}
// only for fixed-price ordertrees
#[cfg(test)]
#[allow(dead_code)]
fn to_price_quantity_vec(&self, reverse: bool) -> Vec<(i64, i64)> {
fn to_price_quantity_vec(&self, root: &OrderTreeRoot, reverse: bool) -> Vec<(i64, i64)> {
let mut pqs = vec![];
let mut current: NodeHandle = match self.root() {
let mut current: NodeHandle = match root.node() {
None => return pqs,
Some(node_handle) => node_handle,
};
@ -147,41 +156,49 @@ impl OrderTree {
}
}
pub fn min_leaf(&self) -> Option<&LeafNode> {
self.leaf_min_max(false)
pub fn min_leaf(&self, root: &OrderTreeRoot) -> Option<(NodeHandle, &LeafNode)> {
self.leaf_min_max(false, root)
}
pub fn max_leaf(&self) -> Option<&LeafNode> {
self.leaf_min_max(true)
pub fn max_leaf(&self, root: &OrderTreeRoot) -> Option<(NodeHandle, &LeafNode)> {
self.leaf_min_max(true, root)
}
fn leaf_min_max(&self, find_max: bool) -> Option<&LeafNode> {
let mut root: NodeHandle = self.root()?;
fn leaf_min_max(
&self,
find_max: bool,
root: &OrderTreeRoot,
) -> Option<(NodeHandle, &LeafNode)> {
let mut node_handle: NodeHandle = root.node()?;
let i = if find_max { 1 } else { 0 };
loop {
let root_contents = self.node(root)?;
match root_contents.case()? {
let node_contents = self.node(node_handle)?;
match node_contents.case()? {
NodeRef::Inner(inner) => {
root = inner.children[i];
node_handle = inner.children[i];
}
NodeRef::Leaf(leaf) => {
return Some(leaf);
return Some((node_handle, leaf));
}
}
}
}
pub fn remove_by_key(&mut self, search_key: u128) -> Option<LeafNode> {
pub fn remove_by_key(
&mut self,
root: &mut OrderTreeRoot,
search_key: u128,
) -> Option<LeafNode> {
// path of InnerNode handles that lead to the removed leaf
let mut stack: Vec<(NodeHandle, bool)> = vec![];
// special case potentially removing the root
let mut parent_h = self.root()?;
let mut parent_h = root.node()?;
let (mut child_h, mut crit_bit) = match self.node(parent_h).unwrap().case().unwrap() {
NodeRef::Leaf(&leaf) if leaf.key == search_key => {
assert_eq!(self.leaf_count, 1);
self.root_node = 0;
self.leaf_count = 0;
assert_eq!(root.leaf_count, 1);
root.maybe_node = 0;
root.leaf_count = 0;
let _old_root = self.remove(parent_h).unwrap();
return Some(leaf);
}
@ -215,7 +232,7 @@ impl OrderTree {
let other_child_node_contents = self.remove(other_child_h).unwrap();
let new_expiry = other_child_node_contents.earliest_expiry();
*self.node_mut(parent_h).unwrap() = other_child_node_contents;
self.leaf_count -= 1;
root.leaf_count -= 1;
let removed_leaf: LeafNode = cast(self.remove(child_h).unwrap());
// update child min expiry back up to the root
@ -226,7 +243,8 @@ impl OrderTree {
Some(removed_leaf)
}
pub fn remove(&mut self, key: NodeHandle) -> Option<AnyNode> {
/// Internal: Removes only the node, does not remove any links etc, use remove_key()
fn remove(&mut self, key: NodeHandle) -> Option<AnyNode> {
let val = *self.node(key)?;
self.nodes[key as usize] = cast(FreeNode {
@ -245,7 +263,8 @@ impl OrderTree {
Some(val)
}
pub fn insert(&mut self, val: &AnyNode) -> Result<NodeHandle> {
/// Internal: Adds only the node, does not add parent links etc, use insert_leaf()
fn insert(&mut self, val: &AnyNode) -> Result<NodeHandle> {
match NodeTag::try_from(val.tag) {
Ok(NodeTag::InnerNode) | Ok(NodeTag::LeafNode) => (),
_ => unreachable!(),
@ -279,18 +298,23 @@ impl OrderTree {
*node = *val;
Ok(key)
}
pub fn insert_leaf(&mut self, new_leaf: &LeafNode) -> Result<(NodeHandle, Option<LeafNode>)> {
pub fn insert_leaf(
&mut self,
root: &mut OrderTreeRoot,
new_leaf: &LeafNode,
) -> Result<(NodeHandle, Option<LeafNode>)> {
// path of InnerNode handles that lead to the new leaf
let mut stack: Vec<(NodeHandle, bool)> = vec![];
// deal with inserts into an empty tree
let mut root: NodeHandle = match self.root() {
let mut parent_handle: NodeHandle = match root.node() {
Some(h) => h,
None => {
// create a new root if none exists
let handle = self.insert(new_leaf.as_ref())?;
self.root_node = handle;
self.leaf_count = 1;
root.maybe_node = handle;
root.leaf_count = 1;
return Ok((handle, None));
}
};
@ -298,46 +322,46 @@ impl OrderTree {
// walk down the tree until we find the insert location
loop {
// require if the new node will be a child of the root
let root_contents = *self.node(root).unwrap();
let root_key = root_contents.key().unwrap();
if root_key == new_leaf.key {
let parent_contents = *self.node(parent_handle).unwrap();
let parent_key = parent_contents.key().unwrap();
if parent_key == new_leaf.key {
// This should never happen because key should never match
if let Some(NodeRef::Leaf(&old_root_as_leaf)) = root_contents.case() {
if let Some(NodeRef::Leaf(&old_parent_as_leaf)) = parent_contents.case() {
// clobber the existing leaf
*self.node_mut(root).unwrap() = *new_leaf.as_ref();
*self.node_mut(parent_handle).unwrap() = *new_leaf.as_ref();
self.update_parent_earliest_expiry(
&stack,
old_root_as_leaf.expiry(),
old_parent_as_leaf.expiry(),
new_leaf.expiry(),
);
return Ok((root, Some(old_root_as_leaf)));
return Ok((parent_handle, Some(old_parent_as_leaf)));
}
// InnerNodes have a random child's key, so matching can happen and is fine
}
let shared_prefix_len: u32 = (root_key ^ new_leaf.key).leading_zeros();
match root_contents.case() {
let shared_prefix_len: u32 = (parent_key ^ new_leaf.key).leading_zeros();
match parent_contents.case() {
None => unreachable!(),
Some(NodeRef::Inner(inner)) => {
let keep_old_root = shared_prefix_len >= inner.prefix_len;
if keep_old_root {
let keep_old_parent = shared_prefix_len >= inner.prefix_len;
if keep_old_parent {
let (child, crit_bit) = inner.walk_down(new_leaf.key);
stack.push((root, crit_bit));
root = child;
stack.push((parent_handle, crit_bit));
parent_handle = child;
continue;
};
}
_ => (),
};
// implies root is a Leaf or Inner where shared_prefix_len < prefix_len
// we'll replace root with a new InnerNode that has new_leaf and root as children
// implies parent is a Leaf or Inner where shared_prefix_len < prefix_len
// we'll replace parent with a new InnerNode that has new_leaf and parent as children
// change the root in place to represent the LCA of [new_leaf] and [root]
// change the parent in place to represent the LCA of [new_leaf] and [parent]
let crit_bit_mask: u128 = 1u128 << (127 - shared_prefix_len);
let new_leaf_crit_bit = (crit_bit_mask & new_leaf.key) != 0;
let old_root_crit_bit = !new_leaf_crit_bit;
let old_parent_crit_bit = !new_leaf_crit_bit;
let new_leaf_handle = self.insert(new_leaf.as_ref())?;
let moved_root_handle = match self.insert(&root_contents) {
let moved_parent_handle = match self.insert(&parent_contents) {
Ok(h) => h,
Err(e) => {
self.remove(new_leaf_handle).unwrap();
@ -345,23 +369,23 @@ impl OrderTree {
}
};
let new_root: &mut InnerNode = cast_mut(self.node_mut(root).unwrap());
*new_root = InnerNode::new(shared_prefix_len, new_leaf.key);
let new_parent: &mut InnerNode = cast_mut(self.node_mut(parent_handle).unwrap());
*new_parent = InnerNode::new(shared_prefix_len, new_leaf.key);
new_root.children[new_leaf_crit_bit as usize] = new_leaf_handle;
new_root.children[old_root_crit_bit as usize] = moved_root_handle;
new_parent.children[new_leaf_crit_bit as usize] = new_leaf_handle;
new_parent.children[old_parent_crit_bit as usize] = moved_parent_handle;
let new_leaf_expiry = new_leaf.expiry();
let old_root_expiry = root_contents.earliest_expiry();
new_root.child_earliest_expiry[new_leaf_crit_bit as usize] = new_leaf_expiry;
new_root.child_earliest_expiry[old_root_crit_bit as usize] = old_root_expiry;
let old_parent_expiry = parent_contents.earliest_expiry();
new_parent.child_earliest_expiry[new_leaf_crit_bit as usize] = new_leaf_expiry;
new_parent.child_earliest_expiry[old_parent_crit_bit as usize] = old_parent_expiry;
// walk up the stack and fix up the new min if needed
if new_leaf_expiry < old_root_expiry {
self.update_parent_earliest_expiry(&stack, old_root_expiry, new_leaf_expiry);
if new_leaf_expiry < old_parent_expiry {
self.update_parent_earliest_expiry(&stack, old_parent_expiry, new_leaf_expiry);
}
self.leaf_count += 1;
root.leaf_count += 1;
return Ok((new_leaf_handle, None));
}
}
@ -394,8 +418,8 @@ impl OrderTree {
}
/// Returns the handle of the node with the lowest expiry timestamp, and this timestamp
pub fn find_earliest_expiry(&self) -> Option<(NodeHandle, u64)> {
let mut current: NodeHandle = match self.root() {
pub fn find_earliest_expiry(&self, root: &OrderTreeRoot) -> Option<(NodeHandle, u64)> {
let mut current: NodeHandle = match root.node() {
Some(h) => h,
None => return None,
};
@ -423,34 +447,21 @@ mod tests {
use super::*;
use bytemuck::Zeroable;
fn new_order_tree(order_tree_type: OrderTreeType) -> OrderTree {
OrderTree {
order_tree_type: order_tree_type.into(),
padding: [0u8; 3],
bump_index: 0,
free_list_len: 0,
free_list_head: 0,
root_node: 0,
leaf_count: 0,
nodes: [AnyNode::zeroed(); MAX_ORDERTREE_NODES],
reserved: [0; 256],
}
fn new_order_tree(order_tree_type: OrderTreeType) -> OrderTreeNodes {
let mut ot = OrderTreeNodes::zeroed();
ot.order_tree_type = order_tree_type.into();
ot
}
fn verify_order_tree(order_tree: &OrderTree) {
verify_order_tree_invariant(order_tree);
verify_order_tree_iteration(order_tree);
verify_order_tree_expiry(order_tree);
fn verify_order_tree(order_tree: &OrderTreeNodes, root: &OrderTreeRoot) {
verify_order_tree_invariant(order_tree, root);
verify_order_tree_iteration(order_tree, root);
verify_order_tree_expiry(order_tree, root);
}
// check that BookSide binary tree key invariant holds
fn verify_order_tree_invariant(order_tree: &OrderTree) {
let r = match order_tree.root() {
Some(h) => h,
None => return,
};
fn recursive_check(order_tree: &OrderTree, h: NodeHandle) {
fn verify_order_tree_invariant(order_tree: &OrderTreeNodes, root: &OrderTreeRoot) {
fn recursive_check(order_tree: &OrderTreeNodes, h: NodeHandle) {
match order_tree.node(h).unwrap().case().unwrap() {
NodeRef::Inner(&inner) => {
let left = order_tree.node(inner.children[0]).unwrap().key().unwrap();
@ -471,15 +482,18 @@ mod tests {
_ => {}
}
}
recursive_check(order_tree, r);
if let Some(r) = root.node() {
recursive_check(order_tree, r);
}
}
// check that iteration of order tree has the right order and misses no leaves
fn verify_order_tree_iteration(order_tree: &OrderTree) {
fn verify_order_tree_iteration(order_tree: &OrderTreeNodes, root: &OrderTreeRoot) {
let mut total = 0;
let ascending = order_tree.order_tree_type() == OrderTreeType::Asks;
let mut last_key = if ascending { 0 } else { u128::MAX };
for (_, node) in order_tree.iter() {
for (_, node) in order_tree.iter(root) {
let key = node.key;
if ascending {
assert!(key >= last_key);
@ -489,17 +503,12 @@ mod tests {
last_key = key;
total += 1;
}
assert_eq!(order_tree.leaf_count, total);
assert_eq!(root.leaf_count, total);
}
// check that BookSide::child_expiry invariant holds
fn verify_order_tree_expiry(order_tree: &OrderTree) {
let r = match order_tree.root() {
Some(h) => h,
None => return,
};
fn recursive_check(order_tree: &OrderTree, h: NodeHandle) {
fn verify_order_tree_expiry(order_tree: &OrderTreeNodes, root: &OrderTreeRoot) {
fn recursive_check(order_tree: &OrderTreeNodes, h: NodeHandle) {
match order_tree.node(h).unwrap().case().unwrap() {
NodeRef::Inner(&inner) => {
let left = order_tree
@ -521,7 +530,9 @@ mod tests {
_ => {}
}
}
recursive_check(order_tree, r);
if let Some(r) = root.node() {
recursive_check(order_tree, r);
}
}
#[test]
@ -541,26 +552,38 @@ mod tests {
)
};
assert!(bids.find_earliest_expiry().is_none());
let mut root = OrderTreeRoot::zeroed();
bids.insert_leaf(&new_expiring_leaf(0, 5000)).unwrap();
assert_eq!(bids.find_earliest_expiry().unwrap(), (bids.root_node, 5000));
verify_order_tree(&bids);
assert!(bids.find_earliest_expiry(&root).is_none());
let (new4000_h, _) = bids.insert_leaf(&new_expiring_leaf(1, 4000)).unwrap();
assert_eq!(bids.find_earliest_expiry().unwrap(), (new4000_h, 4000));
verify_order_tree(&bids);
bids.insert_leaf(&mut root, &new_expiring_leaf(0, 5000))
.unwrap();
assert_eq!(
bids.find_earliest_expiry(&root).unwrap(),
(root.maybe_node, 5000)
);
verify_order_tree(&bids, &root);
let (_new4500_h, _) = bids.insert_leaf(&new_expiring_leaf(2, 4500)).unwrap();
assert_eq!(bids.find_earliest_expiry().unwrap(), (new4000_h, 4000));
verify_order_tree(&bids);
let (new4000_h, _) = bids
.insert_leaf(&mut root, &new_expiring_leaf(1, 4000))
.unwrap();
assert_eq!(bids.find_earliest_expiry(&root).unwrap(), (new4000_h, 4000));
verify_order_tree(&bids, &root);
let (new3500_h, _) = bids.insert_leaf(&new_expiring_leaf(3, 3500)).unwrap();
assert_eq!(bids.find_earliest_expiry().unwrap(), (new3500_h, 3500));
verify_order_tree(&bids);
let (_new4500_h, _) = bids
.insert_leaf(&mut root, &new_expiring_leaf(2, 4500))
.unwrap();
assert_eq!(bids.find_earliest_expiry(&root).unwrap(), (new4000_h, 4000));
verify_order_tree(&bids, &root);
let (new3500_h, _) = bids
.insert_leaf(&mut root, &new_expiring_leaf(3, 3500))
.unwrap();
assert_eq!(bids.find_earliest_expiry(&root).unwrap(), (new3500_h, 3500));
verify_order_tree(&bids, &root);
// the first two levels of the tree are innernodes, with 0;1 on one side and 2;3 on the other
assert_eq!(
bids.node_mut(bids.root_node)
bids.node_mut(root.maybe_node)
.unwrap()
.as_inner_mut()
.unwrap()
@ -568,37 +591,37 @@ mod tests {
[4000, 3500]
);
bids.remove_by_key(3).unwrap();
verify_order_tree(&bids);
bids.remove_by_key(&mut root, 3).unwrap();
verify_order_tree(&bids, &root);
assert_eq!(
bids.node_mut(bids.root_node)
bids.node_mut(root.maybe_node)
.unwrap()
.as_inner_mut()
.unwrap()
.child_earliest_expiry,
[4000, 4500]
);
assert_eq!(bids.find_earliest_expiry().unwrap().1, 4000);
assert_eq!(bids.find_earliest_expiry(&root).unwrap().1, 4000);
bids.remove_by_key(0).unwrap();
verify_order_tree(&bids);
bids.remove_by_key(&mut root, 0).unwrap();
verify_order_tree(&bids, &root);
assert_eq!(
bids.node_mut(bids.root_node)
bids.node_mut(root.maybe_node)
.unwrap()
.as_inner_mut()
.unwrap()
.child_earliest_expiry,
[4000, 4500]
);
assert_eq!(bids.find_earliest_expiry().unwrap().1, 4000);
assert_eq!(bids.find_earliest_expiry(&root).unwrap().1, 4000);
bids.remove_by_key(1).unwrap();
verify_order_tree(&bids);
assert_eq!(bids.find_earliest_expiry().unwrap().1, 4500);
bids.remove_by_key(&mut root, 1).unwrap();
verify_order_tree(&bids, &root);
assert_eq!(bids.find_earliest_expiry(&root).unwrap().1, 4500);
bids.remove_by_key(2).unwrap();
verify_order_tree(&bids);
assert!(bids.find_earliest_expiry().is_none());
bids.remove_by_key(&mut root, 2).unwrap();
verify_order_tree(&bids, &root);
assert!(bids.find_earliest_expiry(&root).is_none());
}
#[test]
@ -606,6 +629,7 @@ mod tests {
use rand::Rng;
let mut rng = rand::thread_rng();
let mut root = OrderTreeRoot::zeroed();
let mut bids = new_order_tree(OrderTreeType::Bids);
let new_expiring_leaf = |key: u128, expiry: u64| {
LeafNode::new(
@ -630,8 +654,9 @@ mod tests {
}
let expiry = rng.gen_range(1..200); // give good chance of duplicate expiry times
keys.push(key);
bids.insert_leaf(&new_expiring_leaf(key, expiry)).unwrap();
verify_order_tree(&bids);
bids.insert_leaf(&mut root, &new_expiring_leaf(key, expiry))
.unwrap();
verify_order_tree(&bids, &root);
}
// remove 50 at random
@ -640,9 +665,9 @@ mod tests {
break;
}
let k = keys[rng.gen_range(0..keys.len())];
bids.remove_by_key(k).unwrap();
bids.remove_by_key(&mut root, k).unwrap();
keys.retain(|v| *v != k);
verify_order_tree(&bids);
verify_order_tree(&bids, &root);
}
}
}

View File

@ -2,7 +2,7 @@ use super::*;
/// Iterate over orders in order (bids=descending, asks=ascending)
pub struct OrderTreeIter<'a> {
order_tree: &'a OrderTree,
order_tree: &'a OrderTreeNodes,
/// InnerNodes where the right side still needs to be iterated on
stack: Vec<&'a InnerNode>,
/// To be returned on `next()`
@ -14,7 +14,7 @@ pub struct OrderTreeIter<'a> {
}
impl<'a> OrderTreeIter<'a> {
pub fn new(order_tree: &'a OrderTree) -> Self {
pub fn new(order_tree: &'a OrderTreeNodes, root: &OrderTreeRoot) -> Self {
let (left, right) = if order_tree.order_tree_type() == OrderTreeType::Bids {
(1, 0)
} else {
@ -29,8 +29,8 @@ impl<'a> OrderTreeIter<'a> {
left,
right,
};
if order_tree.leaf_count != 0 {
iter.next_leaf = iter.find_leftmost_leaf(order_tree.root_node);
if let Some(r) = root.node() {
iter.next_leaf = iter.find_leftmost_leaf(r);
}
iter
}

View File

@ -119,7 +119,7 @@ async fn test_perp_fixed() -> Result<(), TransportError> {
check_prev_instruction_post_health(&solana, account_0).await;
let orderbook_data = solana.get_account_boxed::<Orderbook>(orderbook).await;
assert_eq!(orderbook_data.bids.fixed.leaf_count, 1);
assert_eq!(orderbook_data.bids.roots[0].leaf_count, 1);
let order_id_to_cancel = solana
.get_account::<MangoAccount>(account_0)
.await
@ -559,7 +559,7 @@ async fn test_perp_oracle_peg() -> Result<(), TransportError> {
check_prev_instruction_post_health(&solana, account_0).await;
let orderbook_data = solana.get_account_boxed::<Orderbook>(orderbook).await;
assert_eq!(orderbook_data.bids.oracle_pegged.leaf_count, 1);
assert_eq!(orderbook_data.bids.roots[1].leaf_count, 1);
let perp_order = solana
.get_account::<MangoAccount>(account_0)
.await