-
Notifications
You must be signed in to change notification settings - Fork 15
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
frank/dlob #12
frank/dlob #12
Changes from all commits
8793f03
4a51b36
478d3d0
31b4d95
6c79c86
2c4ae6a
f4ea7f6
cae46e2
5b8d52c
09e05d9
f7caac7
8ff33b8
5c5a42a
3032dd4
f2d49cb
ca482a7
e718dc7
2785a23
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
use dashmap::{DashMap, DashSet}; | ||
use drift::controller::position::PositionDirection; | ||
use drift::state::user::{Order, OrderTriggerCondition, OrderType}; | ||
|
||
use crate::dlob::dlob_node::{Node, NodeType, SortDirection}; | ||
use crate::dlob::order_list::Orderlist; | ||
use crate::is_one_of_variant; | ||
use crate::math::order::is_resting_limit_order; | ||
|
||
#[derive(Clone)] | ||
pub(crate) struct Market { | ||
pub resting_limit_orders: Orderlist, | ||
pub floating_limit_orders: Orderlist, | ||
pub taking_limit_orders: Orderlist, | ||
pub market_orders: Orderlist, | ||
pub trigger_orders: Orderlist, | ||
} | ||
|
||
#[derive(Copy, Clone)] | ||
pub enum SubType { | ||
Bid, | ||
Ask, | ||
Above, | ||
Below, | ||
} | ||
|
||
impl Market { | ||
pub(crate) fn new() -> Market { | ||
Market { | ||
resting_limit_orders: Orderlist::new( | ||
SortDirection::Descending, | ||
SortDirection::Ascending, | ||
), | ||
floating_limit_orders: Orderlist::new( | ||
SortDirection::Descending, | ||
SortDirection::Ascending, | ||
), | ||
taking_limit_orders: Orderlist::new(SortDirection::Ascending, SortDirection::Ascending), | ||
market_orders: Orderlist::new(SortDirection::Ascending, SortDirection::Ascending), | ||
trigger_orders: Orderlist::new(SortDirection::Ascending, SortDirection::Descending), | ||
} | ||
} | ||
|
||
pub(crate) fn get_list_for_order( | ||
&mut self, | ||
order: &Order, | ||
slot: u64, | ||
) -> (Option<&mut Orderlist>, SubType) { | ||
let is_inactive_trigger_order = order.must_be_triggered() && !order.triggered(); | ||
|
||
let node_type = if is_inactive_trigger_order { | ||
NodeType::Trigger | ||
} else if is_one_of_variant( | ||
&order.order_type, | ||
&[ | ||
OrderType::Market, | ||
OrderType::TriggerMarket, | ||
OrderType::Oracle, | ||
], | ||
) { | ||
NodeType::Market | ||
} else if order.oracle_price_offset != 0 { | ||
NodeType::FloatingLimit | ||
} else { | ||
if is_resting_limit_order(order, slot) { | ||
NodeType::RestingLimit | ||
} else { | ||
NodeType::TakingLimit | ||
} | ||
}; | ||
|
||
let order_list = match node_type { | ||
NodeType::RestingLimit => &mut self.resting_limit_orders, | ||
NodeType::FloatingLimit => &mut self.floating_limit_orders, | ||
NodeType::TakingLimit => &mut self.taking_limit_orders, | ||
NodeType::Market => &mut self.market_orders, | ||
NodeType::Trigger => &mut self.trigger_orders, | ||
NodeType::VAMM => return (None, SubType::Bid), | ||
}; | ||
|
||
let sub_type = if is_inactive_trigger_order { | ||
if order.trigger_condition == OrderTriggerCondition::Above { | ||
SubType::Bid | ||
} else { | ||
SubType::Ask | ||
} | ||
} else { | ||
match order.direction { | ||
PositionDirection::Long => SubType::Bid, | ||
PositionDirection::Short => SubType::Ask, | ||
} | ||
}; | ||
|
||
(Some(order_list), sub_type) | ||
} | ||
|
||
pub(crate) fn get_best_order( | ||
&self, | ||
order_list: &mut Orderlist, | ||
sub_type: SubType, | ||
) -> Option<Node> { | ||
match sub_type { | ||
SubType::Bid => order_list.get_best_bid(), | ||
SubType::Ask => order_list.get_best_ask(), | ||
_ => unimplemented!(), | ||
}; | ||
|
||
None | ||
} | ||
|
||
pub(crate) fn get_order_list_for_node_type(&self, node_type: NodeType) -> Orderlist { | ||
match node_type { | ||
NodeType::RestingLimit => &self.resting_limit_orders, | ||
NodeType::FloatingLimit => &self.floating_limit_orders, | ||
NodeType::TakingLimit => &self.taking_limit_orders, | ||
NodeType::Market => &self.market_orders, | ||
NodeType::Trigger => &self.trigger_orders, | ||
NodeType::VAMM => panic!("VAMM order list not found"), | ||
} | ||
.clone() | ||
} | ||
} | ||
|
||
pub(crate) type Exchange = DashMap<String, DashMap<u16, Market>>; | ||
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. not sure if hashmap makes sense vs. e.g a struct struct Exchange {
perp: DashMap<u16, Market>>,
spot: DashMap<u16, Market>>,
} |
||
|
||
pub fn get_order_lists(exchange: &Exchange) -> Vec<Orderlist> { | ||
let mut order_lists = vec![]; | ||
|
||
for market_type_ref in exchange.iter() { | ||
for market_ref in market_type_ref.iter() { | ||
order_lists.push(market_ref.value().resting_limit_orders.clone()); | ||
order_lists.push(market_ref.value().floating_limit_orders.clone()); | ||
order_lists.push(market_ref.value().taking_limit_orders.clone()); | ||
order_lists.push(market_ref.value().market_orders.clone()); | ||
order_lists.push(market_ref.value().trigger_orders.clone()); | ||
} | ||
} | ||
|
||
order_lists | ||
} | ||
|
||
pub(crate) type OpenOrders = DashMap<String, DashSet<String>>; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
pub mod dlob; | ||
mod dlob_node; | ||
mod market; | ||
mod order_list; |
Original file line number | Diff line number | Diff line change | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,175 @@ | ||||||||||||||
use std::collections::BinaryHeap; | ||||||||||||||
|
||||||||||||||
use dashmap::DashMap; | ||||||||||||||
|
||||||||||||||
use crate::dlob::dlob_node::{get_order_signature, DLOBNode, DirectionalNode, Node, SortDirection}; | ||||||||||||||
|
||||||||||||||
#[derive(Clone, Debug)] | ||||||||||||||
pub struct Orderlist { | ||||||||||||||
pub bids: BinaryHeap<DirectionalNode>, | ||||||||||||||
pub asks: BinaryHeap<DirectionalNode>, | ||||||||||||||
pub order_sigs: DashMap<String, Node>, | ||||||||||||||
bid_sort_direction: SortDirection, | ||||||||||||||
ask_sort_direction: SortDirection, | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
impl Orderlist { | ||||||||||||||
pub fn new(bid_sort_direction: SortDirection, ask_sort_direction: SortDirection) -> Self { | ||||||||||||||
Orderlist { | ||||||||||||||
bids: BinaryHeap::new(), | ||||||||||||||
asks: BinaryHeap::new(), | ||||||||||||||
order_sigs: DashMap::new(), | ||||||||||||||
bid_sort_direction, | ||||||||||||||
ask_sort_direction, | ||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
pub fn insert_bid(&mut self, node: Node) { | ||||||||||||||
let order_sig = get_order_signature(node.get_order().order_id, node.get_user_account()); | ||||||||||||||
self.order_sigs.insert(order_sig.clone(), node.clone()); | ||||||||||||||
let directional = DirectionalNode::new(node, self.bid_sort_direction); | ||||||||||||||
self.bids.push(directional); | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
pub fn insert_ask(&mut self, node: Node) { | ||||||||||||||
let order_sig = get_order_signature(node.get_order().order_id, node.get_user_account()); | ||||||||||||||
self.order_sigs.insert(order_sig.clone(), node.clone()); | ||||||||||||||
let directional = DirectionalNode::new(node, self.ask_sort_direction); | ||||||||||||||
self.asks.push(directional); | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
pub fn get_best_bid(&mut self) -> Option<Node> { | ||||||||||||||
if let Some(node) = self.bids.pop().map(|node| node.node.clone()) { | ||||||||||||||
let order_sig = get_order_signature(node.get_order().order_id, node.get_user_account()); | ||||||||||||||
if self.order_sigs.contains_key(&order_sig) { | ||||||||||||||
self.order_sigs.remove(&order_sig); | ||||||||||||||
return Some(node); | ||||||||||||||
} | ||||||||||||||
Comment on lines
+44
to
+47
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.
Suggested change
same for asks 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 don't really like this because dashmap remove returns Option<K, V> so i'm getting Option<String, Node> out of here when all i actually want is Option Node and I can't transform Option<String, Node> with something annoying |
||||||||||||||
} | ||||||||||||||
None | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
pub fn get_best_ask(&mut self) -> Option<Node> { | ||||||||||||||
if let Some(node) = self.asks.pop().map(|node| node.node.clone()) { | ||||||||||||||
let order_sig = get_order_signature(node.get_order().order_id, node.get_user_account()); | ||||||||||||||
if self.order_sigs.contains_key(&order_sig) { | ||||||||||||||
self.order_sigs.remove(&order_sig); | ||||||||||||||
return Some(node); | ||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
None | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
pub fn get_node(&self, order_sig: &String) -> Option<Node> { | ||||||||||||||
self.order_sigs.get(order_sig).map(|node| node.clone()) | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
pub fn bids_empty(&self) -> bool { | ||||||||||||||
self.bids.is_empty() | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
pub fn asks_empty(&self) -> bool { | ||||||||||||||
self.asks.is_empty() | ||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
#[cfg(test)] | ||||||||||||||
mod tests { | ||||||||||||||
use crate::dlob::dlob_node::{create_node, NodeType}; | ||||||||||||||
|
||||||||||||||
use super::*; | ||||||||||||||
use drift::state::user::Order; | ||||||||||||||
use solana_sdk::pubkey::Pubkey; | ||||||||||||||
|
||||||||||||||
#[test] | ||||||||||||||
fn test_insertion_and_ordering() { | ||||||||||||||
let mut orderlist = Orderlist::new(SortDirection::Ascending, SortDirection::Ascending); | ||||||||||||||
let user_account = Pubkey::new_unique(); | ||||||||||||||
let order_1 = Order { | ||||||||||||||
order_id: 1, | ||||||||||||||
slot: 1, | ||||||||||||||
..Order::default() | ||||||||||||||
}; | ||||||||||||||
let order_2 = Order { | ||||||||||||||
order_id: 2, | ||||||||||||||
slot: 2, | ||||||||||||||
..Order::default() | ||||||||||||||
}; | ||||||||||||||
let order_3 = Order { | ||||||||||||||
order_id: 3, | ||||||||||||||
slot: 3, | ||||||||||||||
..Order::default() | ||||||||||||||
}; | ||||||||||||||
let order_4 = Order { | ||||||||||||||
order_id: 4, | ||||||||||||||
slot: 4, | ||||||||||||||
..Order::default() | ||||||||||||||
}; | ||||||||||||||
let order_5 = Order { | ||||||||||||||
order_id: 5, | ||||||||||||||
slot: 5, | ||||||||||||||
..Order::default() | ||||||||||||||
}; | ||||||||||||||
let order_6 = Order { | ||||||||||||||
order_id: 6, | ||||||||||||||
slot: 1, | ||||||||||||||
..Order::default() | ||||||||||||||
}; | ||||||||||||||
let order_7 = Order { | ||||||||||||||
order_id: 7, | ||||||||||||||
slot: 2, | ||||||||||||||
..Order::default() | ||||||||||||||
}; | ||||||||||||||
let order_8 = Order { | ||||||||||||||
order_id: 8, | ||||||||||||||
slot: 3, | ||||||||||||||
..Order::default() | ||||||||||||||
}; | ||||||||||||||
let order_9 = Order { | ||||||||||||||
order_id: 9, | ||||||||||||||
slot: 4, | ||||||||||||||
..Order::default() | ||||||||||||||
}; | ||||||||||||||
let order_10 = Order { | ||||||||||||||
order_id: 10, | ||||||||||||||
slot: 5, | ||||||||||||||
..Order::default() | ||||||||||||||
}; | ||||||||||||||
|
||||||||||||||
let node_1 = create_node(NodeType::TakingLimit, order_1, user_account); | ||||||||||||||
let node_2 = create_node(NodeType::TakingLimit, order_2, user_account); | ||||||||||||||
let node_3 = create_node(NodeType::TakingLimit, order_3, user_account); | ||||||||||||||
let node_4 = create_node(NodeType::TakingLimit, order_4, user_account); | ||||||||||||||
let node_5 = create_node(NodeType::TakingLimit, order_5, user_account); | ||||||||||||||
|
||||||||||||||
let node_6 = create_node(NodeType::TakingLimit, order_6, user_account); | ||||||||||||||
let node_7 = create_node(NodeType::TakingLimit, order_7, user_account); | ||||||||||||||
let node_8 = create_node(NodeType::TakingLimit, order_8, user_account); | ||||||||||||||
let node_9 = create_node(NodeType::TakingLimit, order_9, user_account); | ||||||||||||||
let node_10 = create_node(NodeType::TakingLimit, order_10, user_account); | ||||||||||||||
|
||||||||||||||
orderlist.insert_bid(node_1); | ||||||||||||||
orderlist.insert_bid(node_2); | ||||||||||||||
orderlist.insert_bid(node_3); | ||||||||||||||
orderlist.insert_bid(node_4); | ||||||||||||||
orderlist.insert_bid(node_5); | ||||||||||||||
|
||||||||||||||
orderlist.insert_ask(node_6); | ||||||||||||||
orderlist.insert_ask(node_7); | ||||||||||||||
orderlist.insert_ask(node_8); | ||||||||||||||
orderlist.insert_ask(node_9); | ||||||||||||||
orderlist.insert_ask(node_10); | ||||||||||||||
|
||||||||||||||
assert_eq!(orderlist.get_best_bid().unwrap().get_order().slot, 1); | ||||||||||||||
assert_eq!(orderlist.get_best_bid().unwrap().get_order().slot, 2); | ||||||||||||||
assert_eq!(orderlist.get_best_bid().unwrap().get_order().slot, 3); | ||||||||||||||
assert_eq!(orderlist.get_best_bid().unwrap().get_order().slot, 4); | ||||||||||||||
assert_eq!(orderlist.get_best_bid().unwrap().get_order().slot, 5); | ||||||||||||||
|
||||||||||||||
assert_eq!(orderlist.get_best_ask().unwrap().get_order().slot, 1); | ||||||||||||||
assert_eq!(orderlist.get_best_ask().unwrap().get_order().slot, 2); | ||||||||||||||
assert_eq!(orderlist.get_best_ask().unwrap().get_order().slot, 3); | ||||||||||||||
assert_eq!(orderlist.get_best_ask().unwrap().get_order().slot, 4); | ||||||||||||||
assert_eq!(orderlist.get_best_ask().unwrap().get_order().slot, 5); | ||||||||||||||
} | ||||||||||||||
} |
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.
bit flags could help here (probably another PR): https://docs.rs/bitflags/latest/bitflags/#working-with-flags-values
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.
sure will do down the line, just need this merged in for jit maker rn