Perp: Reorganize fill events, add client order id to LeafNodes (#426)

- don't store internal order ids: unneeded
- provide the maker client order id for user correlation
- store fees as f32 to waste fewer bytes
- new FillLogV2
This commit is contained in:
Christian Kamm 2023-02-02 09:15:06 +01:00 committed by GitHub
parent a91a9ae998
commit 5328003183
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 87 additions and 39 deletions

View File

@ -5,7 +5,7 @@ use crate::error::MangoError;
use crate::state::{EventQueue, IxGate, MangoAccountFixed, MangoAccountLoader}; use crate::state::{EventQueue, IxGate, MangoAccountFixed, MangoAccountLoader};
use crate::state::{EventType, FillEvent, Group, OutEvent, PerpMarket}; use crate::state::{EventType, FillEvent, Group, OutEvent, PerpMarket};
use crate::logs::{emit_perp_balances, FillLog}; use crate::logs::{emit_perp_balances, FillLogV2};
#[derive(Accounts)] #[derive(Accounts)]
pub struct PerpConsumeEvents<'info> { pub struct PerpConsumeEvents<'info> {
@ -119,7 +119,7 @@ pub fn perp_consume_events(ctx: Context<PerpConsumeEvents>, limit: usize) -> Res
&perp_market, &perp_market,
); );
} }
emit!(FillLog { emit!(FillLogV2 {
mango_group: group_key, mango_group: group_key,
market_index: perp_market_index, market_index: perp_market_index,
taker_side: fill.taker_side as u8, taker_side: fill.taker_side as u8,
@ -128,13 +128,12 @@ pub fn perp_consume_events(ctx: Context<PerpConsumeEvents>, limit: usize) -> Res
timestamp: fill.timestamp, timestamp: fill.timestamp,
seq_num: fill.seq_num, seq_num: fill.seq_num,
maker: fill.maker, maker: fill.maker,
maker_order_id: fill.maker_order_id, maker_client_order_id: fill.maker_client_order_id,
maker_fee: fill.maker_fee.to_bits(), maker_fee: fill.maker_fee,
maker_timestamp: fill.maker_timestamp, maker_timestamp: fill.maker_timestamp,
taker: fill.taker, taker: fill.taker,
taker_order_id: fill.taker_order_id,
taker_client_order_id: fill.taker_client_order_id, taker_client_order_id: fill.taker_client_order_id,
taker_fee: fill.taker_fee.to_bits(), taker_fee: fill.taker_fee,
price: fill.price, price: fill.price,
quantity: fill.quantity, quantity: fill.quantity,
}); });

View File

@ -112,6 +112,31 @@ pub struct FillLog {
pub quantity: i64, // number of base lots pub quantity: i64, // number of base lots
} }
#[event]
pub struct FillLogV2 {
pub mango_group: Pubkey,
pub market_index: u16,
pub taker_side: u8, // side from the taker's POV
pub maker_slot: u8,
pub maker_out: bool, // true if maker order quantity == 0
pub timestamp: u64,
pub seq_num: u64, // note: usize same as u64
pub maker: Pubkey,
pub maker_client_order_id: u64,
pub maker_fee: f32,
// Timestamp of when the maker order was placed; copied over from the LeafNode
pub maker_timestamp: u64,
pub taker: Pubkey,
pub taker_client_order_id: u64,
pub taker_fee: f32,
pub price: i64,
pub quantity: i64, // number of base lots
}
#[event] #[event]
pub struct PerpUpdateFundingLog { pub struct PerpUpdateFundingLog {
pub mango_group: Pubkey, pub mango_group: Pubkey,

View File

@ -898,7 +898,7 @@ impl<
let side = fill.taker_side().invert_side(); let side = fill.taker_side().invert_side();
let (base_change, quote_change) = fill.base_quote_change(side); let (base_change, quote_change) = fill.base_quote_change(side);
let quote = cm!(I80F48::from(perp_market.quote_lot_size) * I80F48::from(quote_change)); let quote = cm!(I80F48::from(perp_market.quote_lot_size) * I80F48::from(quote_change));
let fees = cm!(quote.abs() * fill.maker_fee); let fees = cm!(quote.abs() * I80F48::from_num(fill.maker_fee));
pa.record_trading_fee(fees); pa.record_trading_fee(fees);
pa.record_trade(perp_market, base_change, quote); pa.record_trade(perp_market, base_change, quote);

View File

@ -144,11 +144,10 @@ impl<'a> Orderbook<'a> {
now_ts, now_ts,
event_queue.header.seq_num, event_queue.header.seq_num,
best_opposing.node.owner, best_opposing.node.owner,
best_opposing.node.key, best_opposing.node.client_order_id,
market.maker_fee, market.maker_fee,
best_opposing.node.timestamp, best_opposing.node.timestamp,
*mango_account_pk, *mango_account_pk,
order_id,
order.client_order_id, order.client_order_id,
market.taker_fee, market.taker_fee,
best_opposing_price, best_opposing_price,
@ -245,6 +244,7 @@ impl<'a> Orderbook<'a> {
PostOrderType::Limit, // TODO: Support order types? needed? PostOrderType::Limit, // TODO: Support order types? needed?
order.time_in_force, order.time_in_force,
order.peg_limit(), order.peg_limit(),
order.client_order_id,
); );
let _result = bookside.insert_leaf(order_tree_target, &new_order)?; let _result = bookside.insert_leaf(order_tree_target, &new_order)?;

View File

@ -206,8 +206,19 @@ mod tests {
let mut order_tree = 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_fixed = OrderTreeRoot::zeroed();
let mut root_pegged = OrderTreeRoot::zeroed(); let mut root_pegged = OrderTreeRoot::zeroed();
let new_leaf = let new_leaf = |key: u128| {
|key: u128| LeafNode::new(0, key, Pubkey::default(), 0, 1, PostOrderType::Limit, 0, -1); LeafNode::new(
0,
key,
Pubkey::default(),
0,
1,
PostOrderType::Limit,
0,
-1,
0,
)
};
// add 100 leaves to each BookSide, mostly random // add 100 leaves to each BookSide, mostly random
let mut keys = vec![]; let mut keys = vec![];
@ -301,6 +312,7 @@ mod tests {
PostOrderType::Limit, PostOrderType::Limit,
tif, tif,
peg_limit, peg_limit,
0,
) )
}; };
let mut add_fixed = |price: i64, tif: u16| { let mut add_fixed = |price: i64, tif: u16| {

View File

@ -244,8 +244,10 @@ mod tests {
// Add lots and fees to make sure to exercise unit conversion // Add lots and fees to make sure to exercise unit conversion
market.base_lot_size = 10; market.base_lot_size = 10;
market.quote_lot_size = 100; market.quote_lot_size = 100;
market.maker_fee = I80F48::from_num(-0.001f64); let maker_fee = I80F48::from_num(-0.001f32);
market.taker_fee = I80F48::from_num(0.01f64); let taker_fee = I80F48::from_num(0.01f32);
market.maker_fee = maker_fee;
market.taker_fee = taker_fee;
let buffer = MangoAccount::default_for_tests().try_to_vec().unwrap(); let buffer = MangoAccount::default_for_tests().try_to_vec().unwrap();
let mut maker = MangoAccountValue::from_bytes(&buffer).unwrap(); let mut maker = MangoAccountValue::from_bytes(&buffer).unwrap();
@ -286,6 +288,10 @@ mod tests {
u8::MAX, u8::MAX,
) )
.unwrap(); .unwrap();
let order =
order_tree_leaf_by_key(&book.bids, maker.perp_order_by_raw_index(0).id).unwrap();
assert_eq!(order.client_order_id, 42);
assert_eq!(order.quantity, bid_quantity);
assert_eq!( assert_eq!(
maker.perp_order_mut_by_raw_index(0).market, maker.perp_order_mut_by_raw_index(0).market,
market.perp_market_index market.perp_market_index
@ -352,10 +358,7 @@ mod tests {
// fees were immediately accrued // fees were immediately accrued
let match_quote = I80F48::from(match_quantity * price_lots * market.quote_lot_size); let match_quote = I80F48::from(match_quantity * price_lots * market.quote_lot_size);
assert_eq!( assert_eq!(market.fees_accrued, match_quote * (maker_fee + taker_fee));
market.fees_accrued,
match_quote * (market.maker_fee + market.taker_fee)
);
// the taker account is updated // the taker account is updated
assert_eq!(taker.perp_order_by_raw_index(0).market, FREE_ORDER_SLOT); assert_eq!(taker.perp_order_by_raw_index(0).market, FREE_ORDER_SLOT);
@ -372,7 +375,7 @@ mod tests {
assert_eq!(taker.perp_position_by_raw_index(0).base_position_lots(), 0); assert_eq!(taker.perp_position_by_raw_index(0).base_position_lots(), 0);
assert_eq!( assert_eq!(
taker.perp_position_by_raw_index(0).quote_position_native(), taker.perp_position_by_raw_index(0).quote_position_native(),
-match_quote * market.taker_fee -match_quote * taker_fee
); );
// the fill gets added to the event queue // the fill gets added to the event queue
@ -385,8 +388,8 @@ mod tests {
assert_eq!(fill.taker_client_order_id, 43); assert_eq!(fill.taker_client_order_id, 43);
assert_eq!(fill.maker, maker_pk); assert_eq!(fill.maker, maker_pk);
assert_eq!(fill.taker, taker_pk); assert_eq!(fill.taker, taker_pk);
assert_eq!(fill.maker_fee, market.maker_fee); assert_eq!(fill.maker_fee, maker_fee.to_num::<f32>());
assert_eq!(fill.taker_fee, market.taker_fee); assert_eq!(fill.taker_fee, taker_fee.to_num::<f32>());
// simulate event queue processing // simulate event queue processing
maker maker
@ -411,7 +414,7 @@ mod tests {
); );
assert_eq!( assert_eq!(
maker.perp_position_by_raw_index(0).quote_position_native(), maker.perp_position_by_raw_index(0).quote_position_native(),
-match_quote - match_quote * market.maker_fee -match_quote - match_quote * maker_fee
); );
assert_eq!(taker.perp_position_by_raw_index(0).bids_base_lots, 0); assert_eq!(taker.perp_position_by_raw_index(0).bids_base_lots, 0);
@ -424,7 +427,7 @@ mod tests {
); );
assert_eq!( assert_eq!(
taker.perp_position_by_raw_index(0).quote_position_native(), taker.perp_position_by_raw_index(0).quote_position_native(),
match_quote - match_quote * market.taker_fee match_quote - match_quote * taker_fee
); );
} }

View File

@ -179,11 +179,14 @@ pub struct LeafNode {
/// Only applicable in the oracle_pegged OrderTree /// Only applicable in the oracle_pegged OrderTree
pub peg_limit: i64, pub peg_limit: i64,
pub reserved: [u8; 40], /// User defined id for this order, used in FillEvents
pub client_order_id: u64,
pub reserved: [u8; 32],
} }
const_assert_eq!( const_assert_eq!(
size_of::<LeafNode>(), size_of::<LeafNode>(),
4 + 1 + 1 + 1 + 1 + 16 + 32 + 8 + 8 + 8 + 40 4 + 1 + 1 + 1 + 1 + 16 + 32 + 8 + 8 + 8 + 8 + 32
); );
const_assert_eq!(size_of::<LeafNode>(), NODE_SIZE); const_assert_eq!(size_of::<LeafNode>(), NODE_SIZE);
const_assert_eq!(size_of::<LeafNode>() % 8, 0); const_assert_eq!(size_of::<LeafNode>() % 8, 0);
@ -199,6 +202,7 @@ impl LeafNode {
order_type: PostOrderType, order_type: PostOrderType,
time_in_force: u16, time_in_force: u16,
peg_limit: i64, peg_limit: i64,
client_order_id: u64,
) -> Self { ) -> Self {
Self { Self {
tag: NodeTag::LeafNode.into(), tag: NodeTag::LeafNode.into(),
@ -212,7 +216,8 @@ impl LeafNode {
quantity, quantity,
timestamp, timestamp,
peg_limit, peg_limit,
reserved: [0; 40], client_order_id,
reserved: [0; 32],
} }
} }

View File

@ -557,6 +557,7 @@ mod tests {
PostOrderType::Limit, PostOrderType::Limit,
1, 1,
-1, -1,
0,
) )
}; };
@ -649,6 +650,7 @@ mod tests {
PostOrderType::Limit, PostOrderType::Limit,
1, 1,
-1, -1,
0,
) )
}; };

View File

@ -186,20 +186,22 @@ pub struct FillEvent {
pub seq_num: u64, pub seq_num: u64,
pub maker: Pubkey, pub maker: Pubkey,
pub maker_order_id: u128, pub padding2: [u8; 32],
pub maker_fee: I80F48,
// Timestamp of when the maker order was placed; copied over from the LeafNode // Timestamp of when the maker order was placed; copied over from the LeafNode
pub maker_timestamp: u64, pub maker_timestamp: u64,
pub taker: Pubkey, pub taker: Pubkey,
pub taker_order_id: u128, pub padding3: [u8; 16],
pub taker_client_order_id: u64, pub taker_client_order_id: u64,
pub taker_fee: I80F48, pub padding4: [u8; 16],
pub price: i64, pub price: i64,
pub quantity: i64, // number of quote lots pub quantity: i64, // number of quote lots
pub reserved: [u8; 24], pub maker_client_order_id: u64,
pub maker_fee: f32,
pub taker_fee: f32,
pub reserved: [u8; 8],
} }
const_assert_eq!(size_of::<FillEvent>() % 8, 0); const_assert_eq!(size_of::<FillEvent>() % 8, 0);
const_assert_eq!(size_of::<FillEvent>(), EVENT_SIZE); const_assert_eq!(size_of::<FillEvent>(), EVENT_SIZE);
@ -213,12 +215,10 @@ impl FillEvent {
timestamp: u64, timestamp: u64,
seq_num: u64, seq_num: u64,
maker: Pubkey, maker: Pubkey,
maker_order_id: u128, maker_client_order_id: u64,
maker_fee: I80F48, maker_fee: I80F48,
maker_timestamp: u64, maker_timestamp: u64,
taker: Pubkey, taker: Pubkey,
taker_order_id: u128,
taker_client_order_id: u64, taker_client_order_id: u64,
taker_fee: I80F48, taker_fee: I80F48,
price: i64, price: i64,
@ -229,20 +229,22 @@ impl FillEvent {
taker_side: taker_side.into(), taker_side: taker_side.into(),
maker_out: maker_out.into(), maker_out: maker_out.into(),
maker_slot, maker_slot,
padding: Default::default(),
timestamp, timestamp,
seq_num, seq_num,
maker, maker,
maker_order_id, maker_client_order_id,
maker_fee, maker_fee: maker_fee.to_num::<f32>(),
maker_timestamp, maker_timestamp,
taker, taker,
taker_order_id,
taker_client_order_id, taker_client_order_id,
taker_fee, taker_fee: taker_fee.to_num::<f32>(),
price, price,
quantity, quantity,
reserved: [0; 24], padding: Default::default(),
padding2: Default::default(),
padding3: Default::default(),
padding4: Default::default(),
reserved: [0; 8],
} }
} }