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::{EventType, FillEvent, Group, OutEvent, PerpMarket};
use crate::logs::{emit_perp_balances, FillLog};
use crate::logs::{emit_perp_balances, FillLogV2};
#[derive(Accounts)]
pub struct PerpConsumeEvents<'info> {
@ -119,7 +119,7 @@ pub fn perp_consume_events(ctx: Context<PerpConsumeEvents>, limit: usize) -> Res
&perp_market,
);
}
emit!(FillLog {
emit!(FillLogV2 {
mango_group: group_key,
market_index: perp_market_index,
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,
seq_num: fill.seq_num,
maker: fill.maker,
maker_order_id: fill.maker_order_id,
maker_fee: fill.maker_fee.to_bits(),
maker_client_order_id: fill.maker_client_order_id,
maker_fee: fill.maker_fee,
maker_timestamp: fill.maker_timestamp,
taker: fill.taker,
taker_order_id: fill.taker_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,
quantity: fill.quantity,
});

View File

@ -112,6 +112,31 @@ pub struct FillLog {
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]
pub struct PerpUpdateFundingLog {
pub mango_group: Pubkey,

View File

@ -898,7 +898,7 @@ impl<
let side = fill.taker_side().invert_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 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_trade(perp_market, base_change, quote);

View File

@ -144,11 +144,10 @@ impl<'a> Orderbook<'a> {
now_ts,
event_queue.header.seq_num,
best_opposing.node.owner,
best_opposing.node.key,
best_opposing.node.client_order_id,
market.maker_fee,
best_opposing.node.timestamp,
*mango_account_pk,
order_id,
order.client_order_id,
market.taker_fee,
best_opposing_price,
@ -245,6 +244,7 @@ impl<'a> Orderbook<'a> {
PostOrderType::Limit, // TODO: Support order types? needed?
order.time_in_force,
order.peg_limit(),
order.client_order_id,
);
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 root_fixed = OrderTreeRoot::zeroed();
let mut root_pegged = OrderTreeRoot::zeroed();
let new_leaf =
|key: u128| LeafNode::new(0, key, Pubkey::default(), 0, 1, PostOrderType::Limit, 0, -1);
let new_leaf = |key: u128| {
LeafNode::new(
0,
key,
Pubkey::default(),
0,
1,
PostOrderType::Limit,
0,
-1,
0,
)
};
// add 100 leaves to each BookSide, mostly random
let mut keys = vec![];
@ -301,6 +312,7 @@ mod tests {
PostOrderType::Limit,
tif,
peg_limit,
0,
)
};
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
market.base_lot_size = 10;
market.quote_lot_size = 100;
market.maker_fee = I80F48::from_num(-0.001f64);
market.taker_fee = I80F48::from_num(0.01f64);
let maker_fee = I80F48::from_num(-0.001f32);
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 mut maker = MangoAccountValue::from_bytes(&buffer).unwrap();
@ -286,6 +288,10 @@ mod tests {
u8::MAX,
)
.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!(
maker.perp_order_mut_by_raw_index(0).market,
market.perp_market_index
@ -352,10 +358,7 @@ mod tests {
// fees were immediately accrued
let match_quote = I80F48::from(match_quantity * price_lots * market.quote_lot_size);
assert_eq!(
market.fees_accrued,
match_quote * (market.maker_fee + market.taker_fee)
);
assert_eq!(market.fees_accrued, match_quote * (maker_fee + taker_fee));
// the taker account is updated
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).quote_position_native(),
-match_quote * market.taker_fee
-match_quote * taker_fee
);
// 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.maker, maker_pk);
assert_eq!(fill.taker, taker_pk);
assert_eq!(fill.maker_fee, market.maker_fee);
assert_eq!(fill.taker_fee, market.taker_fee);
assert_eq!(fill.maker_fee, maker_fee.to_num::<f32>());
assert_eq!(fill.taker_fee, taker_fee.to_num::<f32>());
// simulate event queue processing
maker
@ -411,7 +414,7 @@ mod tests {
);
assert_eq!(
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);
@ -424,7 +427,7 @@ mod tests {
);
assert_eq!(
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
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!(
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>() % 8, 0);
@ -199,6 +202,7 @@ impl LeafNode {
order_type: PostOrderType,
time_in_force: u16,
peg_limit: i64,
client_order_id: u64,
) -> Self {
Self {
tag: NodeTag::LeafNode.into(),
@ -212,7 +216,8 @@ impl LeafNode {
quantity,
timestamp,
peg_limit,
reserved: [0; 40],
client_order_id,
reserved: [0; 32],
}
}

View File

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

View File

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