Skip to content
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

Merged
merged 18 commits into from
Mar 18, 2024
664 changes: 664 additions & 0 deletions src/dlob/dlob.rs

Large diffs are not rendered by default.

493 changes: 493 additions & 0 deletions src/dlob/dlob_node.rs

Large diffs are not rendered by default.

142 changes: 142 additions & 0 deletions src/dlob/market.rs
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,
Copy link
Collaborator

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

Copy link
Contributor Author

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

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>>;
Copy link
Collaborator

Choose a reason for hiding this comment

The 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>>;
4 changes: 4 additions & 0 deletions src/dlob/mod.rs
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;
175 changes: 175 additions & 0 deletions src/dlob/order_list.rs
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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if self.order_sigs.contains_key(&order_sig) {
self.order_sigs.remove(&order_sig);
return Some(node);
}
return self.order_sigs.remove(&order_sig)
}

same for asks

Copy link
Contributor Author

@soundsonacid soundsonacid Mar 18, 2024

Choose a reason for hiding this comment

The 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);
}
}
File renamed without changes.
4 changes: 3 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,12 @@ pub mod websocket_program_account_subscriber;

// subscribers
pub mod auction_subscriber;
pub mod dlob;
pub mod dlob_client;
pub mod event_subscriber;
pub mod slot_subscriber;

pub mod dlob;
pub mod math;
pub mod usermap;

use types::*;
Expand Down
Loading
Loading