Merge pull request #27 from blockworks-foundation/mc/ci

checked math
This commit is contained in:
microwavedcola1 2022-03-22 11:47:10 +01:00 committed by GitHub
commit 3443ab0d70
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 795 additions and 820 deletions

View File

@ -16,7 +16,7 @@ pub struct CreatePerpMarket<'info> {
#[account(
init,
seeds = [group.key().as_ref(), b"PerpMarket".as_ref(), &perp_market_index.to_le_bytes().as_ref()],
seeds = [group.key().as_ref(), b"PerpMarket".as_ref(), perp_market_index.to_le_bytes().as_ref()],
bump,
payer = payer,
space = 8 + std::mem::size_of::<PerpMarket>(),
@ -50,8 +50,8 @@ pub fn create_perp_market(
oracle: ctx.accounts.oracle.key(),
bids: ctx.accounts.bids.key(),
asks: ctx.accounts.asks.key(),
quote_lot_size: quote_lot_size,
base_lot_size: base_lot_size,
quote_lot_size,
base_lot_size,
seq_num: 0,
perp_market_index,
base_token_index: base_token_index_opt.ok_or(TokenIndex::MAX).unwrap(),

View File

@ -141,9 +141,9 @@ fn get_pre_cpi_amounts(ctx: &Context<MarginTrade>, cpi_ais: &Vec<AccountInfo>) -
fn adjust_for_post_cpi_amounts(
ctx: &Context<MarginTrade>,
cpi_ais: &Vec<AccountInfo>,
cpi_ais: &[AccountInfo],
pre_cpi_amounts: Vec<u64>,
banks: &mut Vec<AccountInfo>,
banks: &mut [AccountInfo],
account: &mut MangoAccount,
) -> Result<()> {
let token_accounts_iter = cpi_ais

View File

@ -1,8 +1,6 @@
use anchor_lang::prelude::*;
use crate::state::{
oracle_price, Book, BookSide, Group, MangoAccount, OrderType, PerpMarket, Side,
};
use crate::state::{Book, BookSide, Group, MangoAccount, OrderType, PerpMarket};
#[derive(Accounts)]
pub struct PlacePerpOrder<'info> {
@ -32,22 +30,19 @@ pub struct PlacePerpOrder<'info> {
pub owner: Signer<'info>,
}
// TODO
#[allow(clippy::too_many_arguments)]
pub fn place_perp_order(
ctx: Context<PlacePerpOrder>,
// TODO side is harcoded for now
// maybe new_bid and new_ask can be folded into one function
// side: Side,
price: i64,
max_base_quantity: i64,
max_quote_quantity: i64,
client_order_id: u64,
order_type: OrderType,
// TODO reduce_only relies on event queue
// reduce_only: bool,
expiry_timestamp: u64,
limit: u8,
) -> Result<()> {
let mut account = ctx.accounts.account.load_mut()?;
// let mut account = ctx.accounts.account.load_mut()?;
let mango_account_pk = ctx.accounts.account.key();
let mut perp_market = ctx.accounts.perp_market.load_mut()?;
@ -55,7 +50,7 @@ pub fn place_perp_order(
let asks = &ctx.accounts.asks.to_account_info();
let mut book = Book::load_checked(&bids, &asks, &perp_market)?;
let oracle_price = oracle_price(&ctx.accounts.oracle.to_account_info())?;
// let oracle_price = oracle_price(&ctx.accounts.oracle.to_account_info())?;
let now_ts = Clock::get()?.unix_timestamp as u64;
let time_in_force = if expiry_timestamp != 0 {
@ -75,14 +70,9 @@ pub fn place_perp_order(
// TODO reduce_only based on event queue
book.new_bid(
// program_id: &Pubkey,
// mango_group: &MangoGroup,
// mango_group_pk: &Pubkey,
// mango_cache: &MangoCache,
// event_queue: &mut EventQueue,
&mut perp_market,
oracle_price,
&mut account,
// oracle_price,
// &mut account,
&mango_account_pk,
// market_index: usize,
price,
@ -92,7 +82,6 @@ pub fn place_perp_order(
time_in_force,
client_order_id,
now_ts,
// referrer_mango_account_ai: Option<&AccountInfo>,
limit,
)?;

View File

@ -15,7 +15,7 @@ pub mod instructions;
mod serum3_cpi;
pub mod state;
use state::{OrderType, PerpMarketIndex, Serum3MarketIndex, Side, TokenIndex};
use state::{OrderType, PerpMarketIndex, Serum3MarketIndex, TokenIndex};
declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
@ -145,25 +145,21 @@ pub mod mango_v4 {
pub fn place_perp_order(
ctx: Context<PlacePerpOrder>,
side: Side,
price: i64,
max_base_quantity: i64,
max_quote_quantity: i64,
client_order_id: u64,
order_type: OrderType,
reduce_only: bool,
expiry_timestamp: u64,
limit: u8,
) -> Result<()> {
instructions::place_perp_order(
ctx,
// side,
price,
max_base_quantity,
max_quote_quantity,
client_order_id,
order_type,
// reduce_only,
expiry_timestamp,
limit,
)

View File

@ -14,7 +14,7 @@ use crate::state::*;
// MangoAccount size and health compute needs.
const MAX_INDEXED_POSITIONS: usize = 16;
const MAX_SERUM_OPEN_ORDERS: usize = 8;
const MAX_PERP_OPEN_ORDERS: usize = 8;
// const MAX_PERP_OPEN_ORDERS: usize = 8;
#[zero_copy]
pub struct TokenAccount {
@ -95,7 +95,7 @@ impl TokenAccountMap {
if let Some(i) = pos {
self.values[i] = TokenAccount {
indexed_value: I80F48::ZERO,
token_index: token_index,
token_index,
in_use_count: 0,
};
}

View File

@ -4,19 +4,18 @@ use crate::{
error::MangoError,
state::{
orderbook::{bookside::BookSide, nodes::LeafNode},
MangoAccount, PerpMarket,
PerpMarket,
},
};
use anchor_lang::prelude::*;
use bytemuck::cast;
use fixed::types::I80F48;
use fixed_macro::types::I80F48;
use super::{
nodes::NodeHandle,
order_type::{OrderType, Side},
queue::{EventQueue, FillEvent, OutEvent},
};
use crate::util::checked_math as cm;
pub const CENTIBPS_PER_UNIT: I80F48 = I80F48!(1_000_000);
// todo move to a constants module or something
@ -191,146 +190,146 @@ impl<'a> Book<'a> {
/// Iterate over the book and return
/// return changes to (taker_base, taker_quote, bids_quantity, asks_quantity)
pub fn sim_new_bid(
&self,
market: &PerpMarket,
// info: &PerpMarketInfo,
oracle_price: I80F48,
price: i64,
max_base_quantity: i64, // guaranteed to be greater than zero due to initial check
max_quote_quantity: i64, // guaranteed to be greater than zero due to initial check
order_type: OrderType,
now_ts: u64,
) -> std::result::Result<(i64, i64, i64, i64), Error> {
let (mut taker_base, mut taker_quote, mut bids_quantity, asks_quantity) = (0, 0, 0i64, 0);
// pub fn sim_new_bid(
// &self,
// market: &PerpMarket,
// // info: &PerpMarketInfo,
// oracle_price: I80F48,
// price: i64,
// max_base_quantity: i64, // guaranteed to be greater than zero due to initial check
// max_quote_quantity: i64, // guaranteed to be greater than zero due to initial check
// order_type: OrderType,
// now_ts: u64,
// ) -> std::result::Result<(i64, i64, i64, i64), Error> {
// let (mut taker_base, mut taker_quote, mut bids_quantity, asks_quantity) = (0, 0, 0i64, 0);
let (post_only, mut post_allowed, price) = match order_type {
OrderType::Limit => (false, true, price),
OrderType::ImmediateOrCancel => (false, false, price),
OrderType::PostOnly => (true, true, price),
OrderType::Market => (false, false, i64::MAX),
OrderType::PostOnlySlide => {
let price = if let Some(best_ask_price) = self.get_best_ask_price(now_ts) {
price.min(best_ask_price.checked_sub(1).ok_or(MangoError::SomeError)?)
// math_err
} else {
price
};
(true, true, price)
}
};
// if post_allowed {
// // price limit check computed lazily to save CU on average
// let native_price = market.lot_to_native_price(price);
// if native_price.checked_div(oracle_price).unwrap() > info.maint_liab_weight {
// msg!("Posting on book disallowed due to price limits");
// post_allowed = false;
// }
// }
// let (post_only, mut post_allowed, price) = match order_type {
// OrderType::Limit => (false, true, price),
// OrderType::ImmediateOrCancel => (false, false, price),
// OrderType::PostOnly => (true, true, price),
// OrderType::Market => (false, false, i64::MAX),
// OrderType::PostOnlySlide => {
// let price = if let Some(best_ask_price) = self.get_best_ask_price(now_ts) {
// price.min(best_ask_price.checked_sub(1).ok_or(MangoError::SomeError)?)
// // math_err
// } else {
// price
// };
// (true, true, price)
// }
// };
// // if post_allowed {
// // // price limit check computed lazily to save CU on average
// // let native_price = market.lot_to_native_price(price);
// // if native_price.checked_div(oracle_price).unwrap() > info.maint_liab_weight {
// // msg!("Posting on book disallowed due to price limits");
// // post_allowed = false;
// // }
// // }
let mut rem_base_quantity = max_base_quantity; // base lots (aka contracts)
let mut rem_quote_quantity = max_quote_quantity;
// let mut rem_base_quantity = max_base_quantity; // base lots (aka contracts)
// let mut rem_quote_quantity = max_quote_quantity;
for (_, best_ask) in self.asks.iter_valid(now_ts) {
let best_ask_price = best_ask.price();
if price < best_ask_price {
break;
} else if post_only {
return Ok((taker_base, taker_quote, bids_quantity, asks_quantity));
}
// for (_, best_ask) in self.asks.iter_valid(now_ts) {
// let best_ask_price = best_ask.price();
// if price < best_ask_price {
// break;
// } else if post_only {
// return Ok((taker_base, taker_quote, bids_quantity, asks_quantity));
// }
let max_match_by_quote = rem_quote_quantity / best_ask_price;
let match_quantity = rem_base_quantity
.min(best_ask.quantity)
.min(max_match_by_quote);
// let max_match_by_quote = rem_quote_quantity / best_ask_price;
// let match_quantity = rem_base_quantity
// .min(best_ask.quantity)
// .min(max_match_by_quote);
let match_quote = match_quantity * best_ask_price;
rem_base_quantity -= match_quantity;
rem_quote_quantity -= match_quote;
// let match_quote = match_quantity * best_ask_price;
// rem_base_quantity -= match_quantity;
// rem_quote_quantity -= match_quote;
taker_base += match_quantity;
taker_quote -= match_quote;
if match_quantity == max_match_by_quote || rem_base_quantity == 0 {
break;
}
}
let book_base_quantity = rem_base_quantity.min(rem_quote_quantity / price);
if post_allowed && book_base_quantity > 0 {
bids_quantity = bids_quantity.checked_add(book_base_quantity).unwrap();
}
Ok((taker_base, taker_quote, bids_quantity, asks_quantity))
}
// taker_base += match_quantity;
// taker_quote -= match_quote;
// if match_quantity == max_match_by_quote || rem_base_quantity == 0 {
// break;
// }
// }
// let book_base_quantity = rem_base_quantity.min(rem_quote_quantity / price);
// if post_allowed && book_base_quantity > 0 {
// bids_quantity = bids_quantity.checked_add(book_base_quantity).unwrap();
// }
// Ok((taker_base, taker_quote, bids_quantity, asks_quantity))
// }
pub fn sim_new_ask(
&self,
market: &PerpMarket,
// info: &PerpMarketInfo,
oracle_price: I80F48,
price: i64,
max_base_quantity: i64, // guaranteed to be greater than zero due to initial check
max_quote_quantity: i64, // guaranteed to be greater than zero due to initial check
order_type: OrderType,
now_ts: u64,
) -> std::result::Result<(i64, i64, i64, i64), Error> {
let (mut taker_base, mut taker_quote, bids_quantity, mut asks_quantity) = (0, 0, 0, 0i64);
// pub fn sim_new_ask(
// &self,
// market: &PerpMarket,
// // info: &PerpMarketInfo,
// oracle_price: I80F48,
// price: i64,
// max_base_quantity: i64, // guaranteed to be greater than zero due to initial check
// max_quote_quantity: i64, // guaranteed to be greater than zero due to initial check
// order_type: OrderType,
// now_ts: u64,
// ) -> std::result::Result<(i64, i64, i64, i64), Error> {
// let (mut taker_base, mut taker_quote, bids_quantity, mut asks_quantity) = (0, 0, 0, 0i64);
let (post_only, mut post_allowed, price) = match order_type {
OrderType::Limit => (false, true, price),
OrderType::ImmediateOrCancel => (false, false, price),
OrderType::PostOnly => (true, true, price),
OrderType::Market => (false, false, 1),
OrderType::PostOnlySlide => {
let price = if let Some(best_bid_price) = self.get_best_bid_price(now_ts) {
price.max(best_bid_price.checked_add(1).ok_or(MangoError::SomeError)?)
// todo math_err
} else {
price
};
(true, true, price)
}
};
// if post_allowed {
// // price limit check computed lazily to save CU on average
// let native_price = market.lot_to_native_price(price);
// if native_price.checked_div(oracle_price).unwrap() < info.maint_asset_weight {
// msg!("Posting on book disallowed due to price limits");
// post_allowed = false;
// }
// }
// let (post_only, mut post_allowed, price) = match order_type {
// OrderType::Limit => (false, true, price),
// OrderType::ImmediateOrCancel => (false, false, price),
// OrderType::PostOnly => (true, true, price),
// OrderType::Market => (false, false, 1),
// OrderType::PostOnlySlide => {
// let price = if let Some(best_bid_price) = self.get_best_bid_price(now_ts) {
// price.max(best_bid_price.checked_add(1).ok_or(MangoError::SomeError)?)
// // todo math_err
// } else {
// price
// };
// (true, true, price)
// }
// };
// // if post_allowed {
// // // price limit check computed lazily to save CU on average
// // let native_price = market.lot_to_native_price(price);
// // if native_price.checked_div(oracle_price).unwrap() < info.maint_asset_weight {
// // msg!("Posting on book disallowed due to price limits");
// // post_allowed = false;
// // }
// // }
let mut rem_base_quantity = max_base_quantity; // base lots (aka contracts)
let mut rem_quote_quantity = max_quote_quantity;
// let mut rem_base_quantity = max_base_quantity; // base lots (aka contracts)
// let mut rem_quote_quantity = max_quote_quantity;
for (_, best_bid) in self.bids.iter_valid(now_ts) {
let best_bid_price = best_bid.price();
if price > best_bid_price {
break;
} else if post_only {
return Ok((taker_base, taker_quote, bids_quantity, asks_quantity));
}
// for (_, best_bid) in self.bids.iter_valid(now_ts) {
// let best_bid_price = best_bid.price();
// if price > best_bid_price {
// break;
// } else if post_only {
// return Ok((taker_base, taker_quote, bids_quantity, asks_quantity));
// }
let max_match_by_quote = rem_quote_quantity / best_bid_price;
let match_quantity = rem_base_quantity
.min(best_bid.quantity)
.min(max_match_by_quote);
// let max_match_by_quote = rem_quote_quantity / best_bid_price;
// let match_quantity = rem_base_quantity
// .min(best_bid.quantity)
// .min(max_match_by_quote);
let match_quote = match_quantity * best_bid_price;
rem_base_quantity -= match_quantity;
rem_quote_quantity -= match_quote;
// let match_quote = match_quantity * best_bid_price;
// rem_base_quantity -= match_quantity;
// rem_quote_quantity -= match_quote;
taker_base -= match_quantity;
taker_quote += match_quote;
if match_quantity == max_match_by_quote || rem_base_quantity == 0 {
break;
}
}
// taker_base -= match_quantity;
// taker_quote += match_quote;
// if match_quantity == max_match_by_quote || rem_base_quantity == 0 {
// break;
// }
// }
let book_base_quantity = rem_base_quantity.min(rem_quote_quantity / price);
if post_allowed && book_base_quantity > 0 {
asks_quantity = asks_quantity.checked_add(book_base_quantity).unwrap();
}
Ok((taker_base, taker_quote, bids_quantity, asks_quantity))
}
// let book_base_quantity = rem_base_quantity.min(rem_quote_quantity / price);
// if post_allowed && book_base_quantity > 0 {
// asks_quantity = asks_quantity.checked_add(book_base_quantity).unwrap();
// }
// Ok((taker_base, taker_quote, bids_quantity, asks_quantity))
// }
// todo: can new_bid and new_ask be elegantly folded into one method?
#[inline(never)]
@ -342,8 +341,8 @@ impl<'a> Book<'a> {
// mango_cache: &MangoCache,
// event_queue: &mut EventQueue,
market: &mut PerpMarket,
oracle_price: I80F48,
mango_account: &mut MangoAccount,
// oracle_price: I80F48,
// mango_account: &mut MangoAccount,
mango_account_pk: &Pubkey,
// market_index: usize,
price: i64,
@ -438,12 +437,12 @@ impl<'a> Book<'a> {
.min(max_match_by_quote);
let done = match_quantity == max_match_by_quote || match_quantity == rem_base_quantity;
let match_quote = match_quantity * best_ask_price;
rem_base_quantity -= match_quantity;
rem_quote_quantity -= match_quote;
let match_quote = cm!(match_quantity * best_ask_price);
rem_base_quantity = cm!(rem_base_quantity - match_quantity);
rem_quote_quantity = cm!(rem_quote_quantity - match_quote);
// mango_account.perp_accounts[market_index].add_taker_trade(match_quantity, -match_quote);
let new_best_ask_quantity = best_ask.quantity - match_quantity;
let new_best_ask_quantity = cm!(best_ask.quantity - match_quantity);
let maker_out = new_best_ask_quantity == 0;
if maker_out {
ask_deletes.push(best_ask.key);
@ -495,7 +494,7 @@ impl<'a> Book<'a> {
break;
}
}
let total_quote_taken = max_quote_quantity - rem_quote_quantity;
// let total_quote_taken = cm!(max_quote_quantity - rem_quote_quantity);
// Apply changes to matched asks (handles invalidate on delete!)
for (handle, new_quantity) in ask_changes {
@ -514,17 +513,17 @@ impl<'a> Book<'a> {
let book_base_quantity = rem_base_quantity.min(rem_quote_quantity / price);
if post_allowed && book_base_quantity > 0 {
// Drop an expired order if possible
if let Some(expired_bid) = self.bids.remove_one_expired(now_ts) {
// let event = OutEvent::new(
// Side::Bid,
// expired_bid.owner_slot,
// now_ts,
// event_queue.header.seq_num,
// expired_bid.owner,
// expired_bid.quantity,
// );
// event_queue.push_back(cast(event)).unwrap();
}
// if let Some(expired_bid) = self.bids.remove_one_expired(now_ts) {
// let event = OutEvent::new(
// Side::Bid,
// expired_bid.owner_slot,
// now_ts,
// event_queue.header.seq_num,
// expired_bid.owner,
// expired_bid.quantity,
// );
// event_queue.push_back(cast(event)).unwrap();
// }
if self.bids.is_full() {
// If this bid is higher than lowest bid, boot that bid and insert this one
@ -599,271 +598,271 @@ impl<'a> Book<'a> {
Ok(())
}
#[inline(never)]
pub fn new_ask(
&mut self,
program_id: &Pubkey,
// mango_group: &MangoGroup,
mango_group_pk: &Pubkey,
// mango_cache: &MangoCache,
event_queue: &mut EventQueue,
market: &mut PerpMarket,
oracle_price: I80F48,
mango_account: &mut MangoAccount,
mango_account_pk: &Pubkey,
market_index: usize,
price: i64,
max_base_quantity: i64, // guaranteed to be greater than zero due to initial check
max_quote_quantity: i64, // guaranteed to be greater than zero due to initial check
order_type: OrderType,
time_in_force: u8,
client_order_id: u64,
now_ts: u64,
referrer_mango_account_ai: Option<&AccountInfo>,
mut limit: u8, // max number of FillEvents allowed; guaranteed to be greater than 0
) -> Result<()> {
let (post_only, mut post_allowed, price) = match order_type {
OrderType::Limit => (false, true, price),
OrderType::ImmediateOrCancel => (false, false, price),
OrderType::PostOnly => (true, true, price),
OrderType::Market => (false, false, 1),
OrderType::PostOnlySlide => {
let price = if let Some(best_bid_price) = self.get_best_bid_price(now_ts) {
price.max(best_bid_price.checked_add(1).ok_or(MangoError::SomeError)?)
// math_err
} else {
price
};
(true, true, price)
}
};
// #[inline(never)]
// pub fn new_ask(
// &mut self,
// program_id: &Pubkey,
// // mango_group: &MangoGroup,
// mango_group_pk: &Pubkey,
// // mango_cache: &MangoCache,
// event_queue: &mut EventQueue,
// market: &mut PerpMarket,
// oracle_price: I80F48,
// mango_account: &mut MangoAccount,
// mango_account_pk: &Pubkey,
// market_index: usize,
// price: i64,
// max_base_quantity: i64, // guaranteed to be greater than zero due to initial check
// max_quote_quantity: i64, // guaranteed to be greater than zero due to initial check
// order_type: OrderType,
// time_in_force: u8,
// client_order_id: u64,
// now_ts: u64,
// referrer_mango_account_ai: Option<&AccountInfo>,
// mut limit: u8, // max number of FillEvents allowed; guaranteed to be greater than 0
// ) -> Result<()> {
// let (post_only, mut post_allowed, price) = match order_type {
// OrderType::Limit => (false, true, price),
// OrderType::ImmediateOrCancel => (false, false, price),
// OrderType::PostOnly => (true, true, price),
// OrderType::Market => (false, false, 1),
// OrderType::PostOnlySlide => {
// let price = if let Some(best_bid_price) = self.get_best_bid_price(now_ts) {
// price.max(best_bid_price.checked_add(1).ok_or(MangoError::SomeError)?)
// // math_err
// } else {
// price
// };
// (true, true, price)
// }
// };
// let info = &mango_group.perp_markets[market_index];
// if post_allowed {
// // price limit check computed lazily to save CU on average
// let native_price = market.lot_to_native_price(price);
// if native_price.checked_div(oracle_price).unwrap() < info.maint_asset_weight {
// msg!("Posting on book disallowed due to price limits");
// post_allowed = false;
// }
// }
// // let info = &mango_group.perp_markets[market_index];
// // if post_allowed {
// // // price limit check computed lazily to save CU on average
// // let native_price = market.lot_to_native_price(price);
// // if native_price.checked_div(oracle_price).unwrap() < info.maint_asset_weight {
// // msg!("Posting on book disallowed due to price limits");
// // post_allowed = false;
// // }
// // }
// referral fee related variables
// let mut ref_fee_rate = None;
// let mut referrer_mango_account_opt = None;
// // referral fee related variables
// // let mut ref_fee_rate = None;
// // let mut referrer_mango_account_opt = None;
// generate new order id
let order_id = market.gen_order_id(Side::Ask, price);
// // generate new order id
// let order_id = market.gen_order_id(Side::Ask, price);
// Iterate through book and match against this new ask
//
// Any changes to matching bids are collected in bid_changes
// and then applied after this loop.
let mut rem_base_quantity = max_base_quantity; // base lots (aka contracts)
let mut rem_quote_quantity = max_quote_quantity;
let mut bid_changes: Vec<(NodeHandle, i64)> = vec![];
let mut bid_deletes: Vec<i128> = vec![];
let mut number_of_dropped_expired_orders = 0;
for (best_bid_h, best_bid) in self.bids.iter_all_including_invalid() {
if !best_bid.is_valid(now_ts) {
// 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;
let event = OutEvent::new(
Side::Bid,
best_bid.owner_slot,
now_ts,
event_queue.header.seq_num,
best_bid.owner,
best_bid.quantity,
);
event_queue.push_back(cast(event)).unwrap();
bid_deletes.push(best_bid.key);
}
continue;
}
// // Iterate through book and match against this new ask
// //
// // Any changes to matching bids are collected in bid_changes
// // and then applied after this loop.
// let mut rem_base_quantity = max_base_quantity; // base lots (aka contracts)
// let mut rem_quote_quantity = max_quote_quantity;
// let mut bid_changes: Vec<(NodeHandle, i64)> = vec![];
// let mut bid_deletes: Vec<i128> = vec![];
// let mut number_of_dropped_expired_orders = 0;
// for (best_bid_h, best_bid) in self.bids.iter_all_including_invalid() {
// if !best_bid.is_valid(now_ts) {
// // 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;
// let event = OutEvent::new(
// Side::Bid,
// best_bid.owner_slot,
// now_ts,
// event_queue.header.seq_num,
// best_bid.owner,
// best_bid.quantity,
// );
// event_queue.push_back(cast(event)).unwrap();
// bid_deletes.push(best_bid.key);
// }
// continue;
// }
let best_bid_price = best_bid.price();
// let best_bid_price = best_bid.price();
if price > best_bid_price {
break;
} else if post_only {
msg!("Order could not be placed due to PostOnly");
post_allowed = false;
break; // return silently to not fail other instructions in tx
} else if limit == 0 {
msg!("Order matching limit reached");
post_allowed = false;
break;
}
// if price > best_bid_price {
// break;
// } else if post_only {
// msg!("Order could not be placed due to PostOnly");
// post_allowed = false;
// break; // return silently to not fail other instructions in tx
// } else if limit == 0 {
// msg!("Order matching limit reached");
// post_allowed = false;
// break;
// }
let max_match_by_quote = rem_quote_quantity / best_bid_price;
let match_quantity = rem_base_quantity
.min(best_bid.quantity)
.min(max_match_by_quote);
let done = match_quantity == max_match_by_quote || match_quantity == rem_base_quantity;
// let max_match_by_quote = rem_quote_quantity / best_bid_price;
// let match_quantity = rem_base_quantity
// .min(best_bid.quantity)
// .min(max_match_by_quote);
// let done = match_quantity == max_match_by_quote || match_quantity == rem_base_quantity;
let match_quote = match_quantity * best_bid_price;
rem_base_quantity -= match_quantity;
rem_quote_quantity -= match_quote;
// mango_account.perp_accounts[market_index].add_taker_trade(-match_quantity, match_quote);
// let match_quote = match_quantity * best_bid_price;
// rem_base_quantity -= match_quantity;
// rem_quote_quantity -= match_quote;
// // mango_account.perp_accounts[market_index].add_taker_trade(-match_quantity, match_quote);
let new_best_bid_quantity = best_bid.quantity - match_quantity;
let maker_out = new_best_bid_quantity == 0;
if maker_out {
bid_deletes.push(best_bid.key);
} else {
bid_changes.push((best_bid_h, new_best_bid_quantity));
}
// let new_best_bid_quantity = best_bid.quantity - match_quantity;
// let maker_out = new_best_bid_quantity == 0;
// if maker_out {
// bid_deletes.push(best_bid.key);
// } else {
// bid_changes.push((best_bid_h, new_best_bid_quantity));
// }
// todo
// if ref_fee_rate is none, determine it
// if ref_valid, then pay into referrer, else pay to perp market
// if ref_fee_rate.is_none() {
// let (a, b) = determine_ref_vars(
// program_id,
// mango_group,
// mango_group_pk,
// mango_cache,
// mango_account,
// referrer_mango_account_ai,
// now_ts,
// )?;
// ref_fee_rate = Some(a);
// referrer_mango_account_opt = b;
// }
// // todo
// // if ref_fee_rate is none, determine it
// // if ref_valid, then pay into referrer, else pay to perp market
// // if ref_fee_rate.is_none() {
// // let (a, b) = determine_ref_vars(
// // program_id,
// // mango_group,
// // mango_group_pk,
// // mango_cache,
// // mango_account,
// // referrer_mango_account_ai,
// // now_ts,
// // )?;
// // ref_fee_rate = Some(a);
// // referrer_mango_account_opt = b;
// // }
// let fill = FillEvent::new(
// Side::Ask,
// best_bid.owner_slot,
// maker_out,
// now_ts,
// event_queue.header.seq_num,
// best_bid.owner,
// best_bid.key,
// best_bid.client_order_id,
// info.maker_fee,
// best_bid.best_initial,
// best_bid.timestamp,
// *mango_account_pk,
// order_id,
// client_order_id,
// info.taker_fee + ref_fee_rate.unwrap(),
// best_bid_price,
// match_quantity,
// best_bid.version,
// );
// // let fill = FillEvent::new(
// // Side::Ask,
// // best_bid.owner_slot,
// // maker_out,
// // now_ts,
// // event_queue.header.seq_num,
// // best_bid.owner,
// // best_bid.key,
// // best_bid.client_order_id,
// // info.maker_fee,
// // best_bid.best_initial,
// // best_bid.timestamp,
// // *mango_account_pk,
// // order_id,
// // client_order_id,
// // info.taker_fee + ref_fee_rate.unwrap(),
// // best_bid_price,
// // match_quantity,
// // best_bid.version,
// // );
// event_queue.push_back(cast(fill)).unwrap();
limit -= 1;
// // event_queue.push_back(cast(fill)).unwrap();
// limit -= 1;
if done {
break;
}
}
let total_quote_taken = max_quote_quantity - rem_quote_quantity;
// if done {
// break;
// }
// }
// let total_quote_taken = max_quote_quantity - rem_quote_quantity;
// Apply changes to matched bids (handles invalidate on delete!)
for (handle, new_quantity) in bid_changes {
self.bids
.get_mut(handle)
.unwrap()
.as_leaf_mut()
.unwrap()
.quantity = new_quantity;
}
for key in bid_deletes {
let _removed_leaf = self.bids.remove_by_key(key).unwrap();
}
// // Apply changes to matched bids (handles invalidate on delete!)
// for (handle, new_quantity) in bid_changes {
// self.bids
// .get_mut(handle)
// .unwrap()
// .as_leaf_mut()
// .unwrap()
// .quantity = new_quantity;
// }
// for key in bid_deletes {
// let _removed_leaf = self.bids.remove_by_key(key).unwrap();
// }
// If there are still quantity unmatched, place on the book
let book_base_quantity = rem_base_quantity.min(rem_quote_quantity / price);
if book_base_quantity > 0 && post_allowed {
// Drop an expired order if possible
if let Some(expired_ask) = self.asks.remove_one_expired(now_ts) {
let event = OutEvent::new(
Side::Ask,
expired_ask.owner_slot,
now_ts,
event_queue.header.seq_num,
expired_ask.owner,
expired_ask.quantity,
);
event_queue.push_back(cast(event)).unwrap();
}
// // If there are still quantity unmatched, place on the book
// let book_base_quantity = rem_base_quantity.min(rem_quote_quantity / price);
// if book_base_quantity > 0 && post_allowed {
// // Drop an expired order if possible
// if let Some(expired_ask) = self.asks.remove_one_expired(now_ts) {
// let event = OutEvent::new(
// Side::Ask,
// expired_ask.owner_slot,
// now_ts,
// event_queue.header.seq_num,
// expired_ask.owner,
// expired_ask.quantity,
// );
// event_queue.push_back(cast(event)).unwrap();
// }
if self.asks.is_full() {
// If this asks is lower than highest ask, boot that ask and insert this one
let max_ask = self.asks.remove_max().unwrap();
require!(price < max_ask.price(), MangoError::SomeError); // OutOfSpace
let event = OutEvent::new(
Side::Ask,
max_ask.owner_slot,
now_ts,
event_queue.header.seq_num,
max_ask.owner,
max_ask.quantity,
);
event_queue.push_back(cast(event)).unwrap();
}
// if self.asks.is_full() {
// // If this asks is lower than highest ask, boot that ask and insert this one
// let max_ask = self.asks.remove_max().unwrap();
// require!(price < max_ask.price(), MangoError::SomeError); // OutOfSpace
// let event = OutEvent::new(
// Side::Ask,
// max_ask.owner_slot,
// now_ts,
// event_queue.header.seq_num,
// max_ask.owner,
// max_ask.quantity,
// );
// event_queue.push_back(cast(event)).unwrap();
// }
// let best_initial = if market.meta_data.version == 0 {
// match self.get_best_ask_price(now_ts) {
// None => price,
// Some(p) => p,
// }
// } else {
// let max_depth: i64 = market.liquidity_mining_info.max_depth_bps.to_num();
// self.get_asks_size_below(price, max_depth, now_ts)
// };
// // let best_initial = if market.meta_data.version == 0 {
// // match self.get_best_ask_price(now_ts) {
// // None => price,
// // Some(p) => p,
// // }
// // } else {
// // let max_depth: i64 = market.liquidity_mining_info.max_depth_bps.to_num();
// // self.get_asks_size_below(price, max_depth, now_ts)
// // };
// let owner_slot = mango_account
// .next_order_slot()
// .ok_or(MangoError::SomeError)?; // TooManyOpenOrders
let new_ask = LeafNode::new(
1, // todo market.meta_data.version,
0, // todo owner_slot as u8,
order_id,
*mango_account_pk,
book_base_quantity,
client_order_id,
now_ts,
0, // todo best_initial,
order_type,
time_in_force,
);
let _result = self.asks.insert_leaf(&new_ask)?;
// // let owner_slot = mango_account
// // .next_order_slot()
// // .ok_or(MangoError::SomeError)?; // TooManyOpenOrders
// let new_ask = LeafNode::new(
// 1, // todo market.meta_data.version,
// 0, // todo owner_slot as u8,
// order_id,
// *mango_account_pk,
// book_base_quantity,
// client_order_id,
// now_ts,
// 0, // todo best_initial,
// order_type,
// time_in_force,
// );
// let _result = self.asks.insert_leaf(&new_ask)?;
// TODO OPT remove if PlacePerpOrder needs more compute
msg!(
"ask on book order_id={} quantity={} price={}",
order_id,
book_base_quantity,
price
);
// // TODO OPT remove if PlacePerpOrder needs more compute
// msg!(
// "ask on book order_id={} quantity={} price={}",
// order_id,
// book_base_quantity,
// price
// );
// mango_account.add_order(market_index, Side::Ask, &new_ask)?;
}
// // mango_account.add_order(market_index, Side::Ask, &new_ask)?;
// }
// if there were matched taker quote apply ref fees
// we know ref_fee_rate is not None if total_quote_taken > 0
// if total_quote_taken > 0 {
// apply_fees(
// market,
// info,
// mango_account,
// mango_account_pk,
// market_index,
// referrer_mango_account_opt,
// referrer_mango_account_ai,
// total_quote_taken,
// ref_fee_rate.unwrap(),
// // &mango_cache.perp_market_cache[market_index],
// );
// }
// // if there were matched taker quote apply ref fees
// // we know ref_fee_rate is not None if total_quote_taken > 0
// // if total_quote_taken > 0 {
// // apply_fees(
// // market,
// // info,
// // mango_account,
// // mango_account_pk,
// // market_index,
// // referrer_mango_account_opt,
// // referrer_mango_account_ai,
// // total_quote_taken,
// // ref_fee_rate.unwrap(),
// // // &mango_cache.perp_market_cache[market_index],
// // );
// // }
Ok(())
}
// Ok(())
// }
// pub fn cancel_order(&mut self, order_id: i128, side: Side) -> Result<()> {
// match side {

View File

@ -1,4 +1,3 @@
use bytemuck::Zeroable;
use std::cell::RefMut;
use anchor_lang::prelude::*;
@ -15,8 +14,6 @@ use crate::state::orderbook::nodes::{
};
use crate::util::LoadZeroCopy;
use super::Book;
pub const MAX_BOOK_NODES: usize = 1024;
#[derive(

View File

@ -1,7 +1,5 @@
use mango_macro::Pod;
use super::datatype::DataType;
#[derive(Copy, Clone, Pod, Default)]
#[repr(C)]
/// Stores meta information about the `Account` on chain

View File

@ -1,9 +1,7 @@
use anchor_lang::prelude::Error;
use bytemuck::{bytes_of, cast_slice_mut, from_bytes_mut, Contiguous, Pod};
use bytemuck::{cast_slice_mut, from_bytes_mut, Pod};
use solana_program::account_info::AccountInfo;
use solana_program::program_error::ProgramError;
use solana_program::pubkey::Pubkey;
use std::cell::RefMut;
use std::mem::size_of;

View File

@ -1,5 +1,4 @@
use anchor_lang::prelude::*;
use bytemuck::{Pod, Zeroable};
use num_enum::{IntoPrimitive, TryFromPrimitive};
#[derive(

View File

@ -1,438 +1,437 @@
use std::cell::RefMut;
use std::mem::size_of;
// use std::cell::RefMut;
// use std::mem::size_of;
use crate::error::MangoError;
use crate::state::orderbook::datatype::DataType;
use crate::state::PerpMarket;
use anchor_lang::prelude::*;
use fixed::types::I80F48;
use num_enum::{IntoPrimitive, TryFromPrimitive};
use solana_program::account_info::AccountInfo;
use solana_program::pubkey::Pubkey;
use solana_program::sysvar::rent::Rent;
use static_assertions::const_assert_eq;
// use crate::error::MangoError;
// use crate::state::PerpMarket;
// use anchor_lang::prelude::*;
// use fixed::types::I80F48;
// use num_enum::{IntoPrimitive, TryFromPrimitive};
// use solana_program::account_info::AccountInfo;
// use solana_program::pubkey::Pubkey;
// use solana_program::sysvar::rent::Rent;
// use static_assertions::const_assert_eq;
// use mango_logs::FillLog;
use mango_macro::Pod;
// // use mango_logs::FillLog;
// use mango_macro::Pod;
use super::metadata::MetaData;
use super::ob_utils::strip_header_mut;
use super::order_type::Side;
// use safe_transmute::{self, trivial::TriviallyTransmutable};
// use super::metadata::MetaData;
// use super::ob_utils::strip_header_mut;
// use super::order_type::Side;
// // use safe_transmute::{self, trivial::TriviallyTransmutable};
// use crate::error::{check_assert, MangoErrorCode, MangoResult, SourceFileId};
// use crate::matching::Side;
// use crate::state::{DataType, MetaData, PerpMarket};
// use crate::utils::strip_header_mut;
// // use crate::error::{check_assert, MangoErrorCode, MangoResult, SourceFileId};
// // use crate::matching::Side;
// // use crate::state::{DataType, MetaData, PerpMarket};
// // use crate::utils::strip_header_mut;
// Don't want event queue to become single threaded if it's logging liquidations
// Most common scenario will be liqors depositing USDC and withdrawing some other token
// So tying it to token deposited is not wise
// also can't tie it to token withdrawn because during bull market, liqs will be depositing all base tokens and withdrawing quote
//
// // Don't want event queue to become single threaded if it's logging liquidations
// // Most common scenario will be liqors depositing USDC and withdrawing some other token
// // So tying it to token deposited is not wise
// // also can't tie it to token withdrawn because during bull market, liqs will be depositing all base tokens and withdrawing quote
// //
pub trait QueueHeader: bytemuck::Pod {
type Item: bytemuck::Pod + Copy;
// pub trait QueueHeader: bytemuck::Pod {
// type Item: bytemuck::Pod + Copy;
fn head(&self) -> usize;
fn set_head(&mut self, value: usize);
fn count(&self) -> usize;
fn set_count(&mut self, value: usize);
// fn head(&self) -> usize;
// fn set_head(&mut self, value: usize);
// fn count(&self) -> usize;
// fn set_count(&mut self, value: usize);
fn incr_event_id(&mut self);
fn decr_event_id(&mut self, n: usize);
}
// fn incr_event_id(&mut self);
// fn decr_event_id(&mut self, n: usize);
// }
pub struct Queue<'a, H: QueueHeader> {
pub header: RefMut<'a, H>,
pub buf: RefMut<'a, [H::Item]>,
}
// pub struct Queue<'a, H: QueueHeader> {
// pub header: RefMut<'a, H>,
// pub buf: RefMut<'a, [H::Item]>,
// }
impl<'a, H: QueueHeader> Queue<'a, H> {
pub fn new(header: RefMut<'a, H>, buf: RefMut<'a, [H::Item]>) -> Self {
Self { header, buf }
}
// impl<'a, H: QueueHeader> Queue<'a, H> {
// pub fn new(header: RefMut<'a, H>, buf: RefMut<'a, [H::Item]>) -> Self {
// Self { header, buf }
// }
pub fn load_mut(account: &'a AccountInfo) -> Result<Self> {
let (header, buf) = strip_header_mut::<H, H::Item>(account)?;
Ok(Self { header, buf })
}
// pub fn load_mut(account: &'a AccountInfo) -> Result<Self> {
// let (header, buf) = strip_header_mut::<H, H::Item>(account)?;
// Ok(Self { header, buf })
// }
pub fn len(&self) -> usize {
self.header.count()
}
// pub fn len(&self) -> usize {
// self.header.count()
// }
pub fn full(&self) -> bool {
self.header.count() == self.buf.len()
}
// pub fn full(&self) -> bool {
// self.header.count() == self.buf.len()
// }
pub fn empty(&self) -> bool {
self.header.count() == 0
}
// pub fn empty(&self) -> bool {
// self.header.count() == 0
// }
pub fn push_back(&mut self, value: H::Item) -> std::result::Result<(), H::Item> {
if self.full() {
return Err(value);
}
let slot = (self.header.head() + self.header.count()) % self.buf.len();
self.buf[slot] = value;
// pub fn push_back(&mut self, value: H::Item) -> std::result::Result<(), H::Item> {
// if self.full() {
// return Err(value);
// }
// let slot = (self.header.head() + self.header.count()) % self.buf.len();
// self.buf[slot] = value;
let count = self.header.count();
self.header.set_count(count + 1);
// let count = self.header.count();
// self.header.set_count(count + 1);
self.header.incr_event_id();
Ok(())
}
// self.header.incr_event_id();
// Ok(())
// }
pub fn peek_front(&self) -> Option<&H::Item> {
if self.empty() {
return None;
}
Some(&self.buf[self.header.head()])
}
// pub fn peek_front(&self) -> Option<&H::Item> {
// if self.empty() {
// return None;
// }
// Some(&self.buf[self.header.head()])
// }
pub fn peek_front_mut(&mut self) -> Option<&mut H::Item> {
if self.empty() {
return None;
}
Some(&mut self.buf[self.header.head()])
}
// pub fn peek_front_mut(&mut self) -> Option<&mut H::Item> {
// if self.empty() {
// return None;
// }
// Some(&mut self.buf[self.header.head()])
// }
pub fn pop_front(&mut self) -> std::result::Result<H::Item, ()> {
if self.empty() {
return Err(());
}
let value = self.buf[self.header.head()];
// pub fn pop_front(&mut self) -> std::result::Result<H::Item, ()> {
// if self.empty() {
// return Err(());
// }
// let value = self.buf[self.header.head()];
let count = self.header.count();
self.header.set_count(count - 1);
// let count = self.header.count();
// self.header.set_count(count - 1);
let head = self.header.head();
self.header.set_head((head + 1) % self.buf.len());
// let head = self.header.head();
// self.header.set_head((head + 1) % self.buf.len());
Ok(value)
}
// Ok(value)
// }
pub fn revert_pushes(&mut self, desired_len: usize) -> Result<()> {
require!(desired_len <= self.header.count(), MangoError::SomeError);
let len_diff = self.header.count() - desired_len;
self.header.set_count(desired_len);
self.header.decr_event_id(len_diff);
Ok(())
}
// pub fn revert_pushes(&mut self, desired_len: usize) -> Result<()> {
// require!(desired_len <= self.header.count(), MangoError::SomeError);
// let len_diff = self.header.count() - desired_len;
// self.header.set_count(desired_len);
// self.header.decr_event_id(len_diff);
// Ok(())
// }
pub fn iter(&self) -> impl Iterator<Item = &H::Item> {
QueueIterator {
queue: self,
index: 0,
}
}
}
// pub fn iter(&self) -> impl Iterator<Item = &H::Item> {
// QueueIterator {
// queue: self,
// index: 0,
// }
// }
// }
struct QueueIterator<'a, 'b, H: QueueHeader> {
queue: &'b Queue<'a, H>,
index: usize,
}
// struct QueueIterator<'a, 'b, H: QueueHeader> {
// queue: &'b Queue<'a, H>,
// index: usize,
// }
impl<'a, 'b, H: QueueHeader> Iterator for QueueIterator<'a, 'b, H> {
type Item = &'b H::Item;
fn next(&mut self) -> Option<Self::Item> {
if self.index == self.queue.len() {
None
} else {
let item =
&self.queue.buf[(self.queue.header.head() + self.index) % self.queue.buf.len()];
self.index += 1;
Some(item)
}
}
}
// impl<'a, 'b, H: QueueHeader> Iterator for QueueIterator<'a, 'b, H> {
// type Item = &'b H::Item;
// fn next(&mut self) -> Option<Self::Item> {
// if self.index == self.queue.len() {
// None
// } else {
// let item =
// &self.queue.buf[(self.queue.header.head() + self.index) % self.queue.buf.len()];
// self.index += 1;
// Some(item)
// }
// }
// }
#[account(zero_copy)]
pub struct EventQueueHeader {
pub meta_data: MetaData,
head: usize,
count: usize,
pub seq_num: usize,
}
// unsafe impl TriviallyTransmutable for EventQueueHeader {}
// #[account(zero_copy)]
// pub struct EventQueueHeader {
// pub meta_data: MetaData,
// head: usize,
// count: usize,
// pub seq_num: usize,
// }
// // unsafe impl TriviallyTransmutable for EventQueueHeader {}
impl QueueHeader for EventQueueHeader {
type Item = AnyEvent;
// impl QueueHeader for EventQueueHeader {
// type Item = AnyEvent;
fn head(&self) -> usize {
self.head
}
fn set_head(&mut self, value: usize) {
self.head = value;
}
fn count(&self) -> usize {
self.count
}
fn set_count(&mut self, value: usize) {
self.count = value;
}
fn incr_event_id(&mut self) {
self.seq_num += 1;
}
fn decr_event_id(&mut self, n: usize) {
self.seq_num -= n;
}
}
// fn head(&self) -> usize {
// self.head
// }
// fn set_head(&mut self, value: usize) {
// self.head = value;
// }
// fn count(&self) -> usize {
// self.count
// }
// fn set_count(&mut self, value: usize) {
// self.count = value;
// }
// fn incr_event_id(&mut self) {
// self.seq_num += 1;
// }
// fn decr_event_id(&mut self, n: usize) {
// self.seq_num -= n;
// }
// }
pub type EventQueue<'a> = Queue<'a, EventQueueHeader>;
// pub type EventQueue<'a> = Queue<'a, EventQueueHeader>;
impl<'a> EventQueue<'a> {
pub fn load_mut_checked(
account: &'a AccountInfo,
program_id: &Pubkey,
perp_market: &PerpMarket,
) -> Result<Self> {
require!(account.owner == program_id, MangoError::SomeError); // MangoErrorCode::InvalidOwner
// require!(
// &perp_market.event_queue == account.key,
// MangoError::SomeError
// ); // MangoErrorCode::InvalidAccount
Self::load_mut(account)
}
// impl<'a> EventQueue<'a> {
// pub fn load_mut_checked(
// account: &'a AccountInfo,
// program_id: &Pubkey,
// perp_market: &PerpMarket,
// ) -> Result<Self> {
// require!(account.owner == program_id, MangoError::SomeError); // MangoErrorCode::InvalidOwner
// // require!(
// // &perp_market.event_queue == account.key,
// // MangoError::SomeError
// // ); // MangoErrorCode::InvalidAccount
// Self::load_mut(account)
// }
pub fn load_and_init(
account: &'a AccountInfo,
program_id: &Pubkey,
rent: &Rent,
) -> Result<Self> {
// NOTE: check this first so we can borrow account later
require!(
rent.is_exempt(account.lamports(), account.data_len()),
MangoError::SomeError
); //MangoErrorCode::AccountNotRentExempt
// pub fn load_and_init(
// account: &'a AccountInfo,
// program_id: &Pubkey,
// rent: &Rent,
// ) -> Result<Self> {
// // NOTE: check this first so we can borrow account later
// require!(
// rent.is_exempt(account.lamports(), account.data_len()),
// MangoError::SomeError
// ); //MangoErrorCode::AccountNotRentExempt
let mut state = Self::load_mut(account)?;
require!(account.owner == program_id, MangoError::SomeError); // MangoErrorCode::InvalidOwner
// let mut state = Self::load_mut(account)?;
// require!(account.owner == program_id, MangoError::SomeError); // MangoErrorCode::InvalidOwner
// require!(
// !state.header.meta_data.is_initialized,
// MangoError::SomeError
// );
// state.header.meta_data = MetaData::new(DataType::EventQueue, 0, true);
// // require!(
// // !state.header.meta_data.is_initialized,
// // MangoError::SomeError
// // );
// // state.header.meta_data = MetaData::new(DataType::EventQueue, 0, true);
Ok(state)
}
}
// Ok(state)
// }
// }
#[derive(Copy, Clone, IntoPrimitive, TryFromPrimitive, Eq, PartialEq)]
#[repr(u8)]
pub enum EventType {
Fill,
Out,
Liquidate,
}
// #[derive(Copy, Clone, IntoPrimitive, TryFromPrimitive, Eq, PartialEq)]
// #[repr(u8)]
// pub enum EventType {
// Fill,
// Out,
// Liquidate,
// }
const EVENT_SIZE: usize = 200;
#[derive(Copy, Clone, Debug, Pod)]
#[repr(C)]
pub struct AnyEvent {
pub event_type: u8,
pub padding: [u8; EVENT_SIZE - 1],
}
// unsafe impl TriviallyTransmutable for AnyEvent {}
// const EVENT_SIZE: usize = 200;
// #[derive(Copy, Clone, Debug, Pod)]
// #[repr(C)]
// pub struct AnyEvent {
// pub event_type: u8,
// pub padding: [u8; EVENT_SIZE - 1],
// }
// // unsafe impl TriviallyTransmutable for AnyEvent {}
#[derive(Copy, Clone, Debug, Pod)]
#[repr(C)]
pub struct FillEvent {
pub event_type: u8,
pub taker_side: Side, // side from the taker's POV
pub maker_slot: u8,
pub maker_out: bool, // true if maker order quantity == 0
pub version: u8,
pub market_fees_applied: bool,
pub padding: [u8; 2],
pub timestamp: u64,
pub seq_num: usize, // note: usize same as u64
// #[derive(Copy, Clone, Debug, Pod)]
// #[repr(C)]
// pub struct FillEvent {
// pub event_type: u8,
// pub taker_side: Side, // side from the taker's POV
// pub maker_slot: u8,
// pub maker_out: bool, // true if maker order quantity == 0
// pub version: u8,
// pub market_fees_applied: bool,
// pub padding: [u8; 2],
// pub timestamp: u64,
// pub seq_num: usize, // note: usize same as u64
pub maker: Pubkey,
pub maker_order_id: i128,
pub maker_client_order_id: u64,
pub maker_fee: I80F48,
// pub maker: Pubkey,
// pub maker_order_id: i128,
// pub maker_client_order_id: u64,
// pub maker_fee: I80F48,
// The best bid/ask at the time the maker order was placed. Used for liquidity incentives
pub best_initial: i64,
// // The best bid/ask at the time the maker order was placed. Used for liquidity incentives
// pub best_initial: i64,
// Timestamp of when the maker order was placed; copied over from the LeafNode
pub maker_timestamp: u64,
// // Timestamp of when the maker order was placed; copied over from the LeafNode
// pub maker_timestamp: u64,
pub taker: Pubkey,
pub taker_order_id: i128,
pub taker_client_order_id: u64,
pub taker_fee: I80F48,
// pub taker: Pubkey,
// pub taker_order_id: i128,
// pub taker_client_order_id: u64,
// pub taker_fee: I80F48,
pub price: i64,
pub quantity: i64, // number of quote lots
}
// unsafe impl TriviallyTransmutable for FillEvent {}
// pub price: i64,
// pub quantity: i64, // number of quote lots
// }
// // unsafe impl TriviallyTransmutable for FillEvent {}
impl FillEvent {
pub fn new(
taker_side: Side,
maker_slot: u8,
maker_out: bool,
timestamp: u64,
seq_num: usize,
maker: Pubkey,
maker_order_id: i128,
maker_client_order_id: u64,
maker_fee: I80F48,
best_initial: i64,
maker_timestamp: u64,
// impl FillEvent {
// pub fn new(
// taker_side: Side,
// maker_slot: u8,
// maker_out: bool,
// timestamp: u64,
// seq_num: usize,
// maker: Pubkey,
// maker_order_id: i128,
// maker_client_order_id: u64,
// maker_fee: I80F48,
// best_initial: i64,
// maker_timestamp: u64,
taker: Pubkey,
taker_order_id: i128,
taker_client_order_id: u64,
taker_fee: I80F48,
price: i64,
quantity: i64,
version: u8,
) -> FillEvent {
Self {
event_type: EventType::Fill as u8,
taker_side,
maker_slot,
maker_out,
version,
market_fees_applied: true, // Since mango v3.3.5, market fees are adjusted at matching time
padding: [0u8; 2],
timestamp,
seq_num,
maker,
maker_order_id,
maker_client_order_id,
maker_fee,
best_initial,
maker_timestamp,
taker,
taker_order_id,
taker_client_order_id,
taker_fee,
price,
quantity,
}
}
// taker: Pubkey,
// taker_order_id: i128,
// taker_client_order_id: u64,
// taker_fee: I80F48,
// price: i64,
// quantity: i64,
// version: u8,
// ) -> FillEvent {
// Self {
// event_type: EventType::Fill as u8,
// taker_side,
// maker_slot,
// maker_out,
// version,
// market_fees_applied: true, // Since mango v3.3.5, market fees are adjusted at matching time
// padding: [0u8; 2],
// timestamp,
// seq_num,
// maker,
// maker_order_id,
// maker_client_order_id,
// maker_fee,
// best_initial,
// maker_timestamp,
// taker,
// taker_order_id,
// taker_client_order_id,
// taker_fee,
// price,
// quantity,
// }
// }
pub fn base_quote_change(&self, side: Side) -> (i64, i64) {
match side {
Side::Bid => (
self.quantity,
-self.price.checked_mul(self.quantity).unwrap(),
),
Side::Ask => (
-self.quantity,
self.price.checked_mul(self.quantity).unwrap(),
),
}
}
// pub fn base_quote_change(&self, side: Side) -> (i64, i64) {
// match side {
// Side::Bid => (
// self.quantity,
// -self.price.checked_mul(self.quantity).unwrap(),
// ),
// Side::Ask => (
// -self.quantity,
// self.price.checked_mul(self.quantity).unwrap(),
// ),
// }
// }
// pub fn to_fill_log(&self, mango_group: Pubkey, market_index: usize) -> FillLog {
// FillLog {
// mango_group,
// market_index: market_index as u64,
// taker_side: self.taker_side as u8,
// maker_slot: self.maker_slot,
// maker_out: self.maker_out,
// timestamp: self.timestamp,
// seq_num: self.seq_num as u64,
// maker: self.maker,
// maker_order_id: self.maker_order_id,
// maker_client_order_id: self.maker_client_order_id,
// maker_fee: self.maker_fee.to_bits(),
// best_initial: self.best_initial,
// maker_timestamp: self.maker_timestamp,
// taker: self.taker,
// taker_order_id: self.taker_order_id,
// taker_client_order_id: self.taker_client_order_id,
// taker_fee: self.taker_fee.to_bits(),
// price: self.price,
// quantity: self.quantity,
// }
// }
}
// // pub fn to_fill_log(&self, mango_group: Pubkey, market_index: usize) -> FillLog {
// // FillLog {
// // mango_group,
// // market_index: market_index as u64,
// // taker_side: self.taker_side as u8,
// // maker_slot: self.maker_slot,
// // maker_out: self.maker_out,
// // timestamp: self.timestamp,
// // seq_num: self.seq_num as u64,
// // maker: self.maker,
// // maker_order_id: self.maker_order_id,
// // maker_client_order_id: self.maker_client_order_id,
// // maker_fee: self.maker_fee.to_bits(),
// // best_initial: self.best_initial,
// // maker_timestamp: self.maker_timestamp,
// // taker: self.taker,
// // taker_order_id: self.taker_order_id,
// // taker_client_order_id: self.taker_client_order_id,
// // taker_fee: self.taker_fee.to_bits(),
// // price: self.price,
// // quantity: self.quantity,
// // }
// // }
// }
#[derive(Copy, Clone, Debug, Pod)]
#[repr(C)]
pub struct OutEvent {
pub event_type: u8,
pub side: Side,
pub slot: u8,
padding0: [u8; 5],
pub timestamp: u64,
pub seq_num: usize,
pub owner: Pubkey,
pub quantity: i64,
padding1: [u8; EVENT_SIZE - 64],
}
// unsafe impl TriviallyTransmutable for OutEvent {}
impl OutEvent {
pub fn new(
side: Side,
slot: u8,
timestamp: u64,
seq_num: usize,
owner: Pubkey,
quantity: i64,
) -> Self {
Self {
event_type: EventType::Out.into(),
side,
slot,
padding0: [0; 5],
timestamp,
seq_num,
owner,
quantity,
padding1: [0; EVENT_SIZE - 64],
}
}
}
// #[derive(Copy, Clone, Debug, Pod)]
// #[repr(C)]
// pub struct OutEvent {
// pub event_type: u8,
// pub side: Side,
// pub slot: u8,
// padding0: [u8; 5],
// pub timestamp: u64,
// pub seq_num: usize,
// pub owner: Pubkey,
// pub quantity: i64,
// padding1: [u8; EVENT_SIZE - 64],
// }
// // unsafe impl TriviallyTransmutable for OutEvent {}
// impl OutEvent {
// pub fn new(
// side: Side,
// slot: u8,
// timestamp: u64,
// seq_num: usize,
// owner: Pubkey,
// quantity: i64,
// ) -> Self {
// Self {
// event_type: EventType::Out.into(),
// side,
// slot,
// padding0: [0; 5],
// timestamp,
// seq_num,
// owner,
// quantity,
// padding1: [0; EVENT_SIZE - 64],
// }
// }
// }
#[derive(Copy, Clone, Debug, Pod)]
#[repr(C)]
/// Liquidation for the PerpMarket this EventQueue is for
pub struct LiquidateEvent {
pub event_type: u8,
padding0: [u8; 7],
pub timestamp: u64,
pub seq_num: usize,
pub liqee: Pubkey,
pub liqor: Pubkey,
pub price: I80F48, // oracle price at the time of liquidation
pub quantity: i64, // number of contracts that were moved from liqee to liqor
pub liquidation_fee: I80F48, // liq fee for this earned for this market
padding1: [u8; EVENT_SIZE - 128],
}
// unsafe impl TriviallyTransmutable for LiquidateEvent {}
impl LiquidateEvent {
pub fn new(
timestamp: u64,
seq_num: usize,
liqee: Pubkey,
liqor: Pubkey,
price: I80F48,
quantity: i64,
liquidation_fee: I80F48,
) -> Self {
Self {
event_type: EventType::Liquidate.into(),
padding0: [0u8; 7],
timestamp,
seq_num,
liqee,
liqor,
price,
quantity,
liquidation_fee,
padding1: [0u8; EVENT_SIZE - 128],
}
}
}
const_assert_eq!(size_of::<AnyEvent>(), size_of::<FillEvent>());
const_assert_eq!(size_of::<AnyEvent>(), size_of::<OutEvent>());
const_assert_eq!(size_of::<AnyEvent>(), size_of::<LiquidateEvent>());
// #[derive(Copy, Clone, Debug, Pod)]
// #[repr(C)]
// /// Liquidation for the PerpMarket this EventQueue is for
// pub struct LiquidateEvent {
// pub event_type: u8,
// padding0: [u8; 7],
// pub timestamp: u64,
// pub seq_num: usize,
// pub liqee: Pubkey,
// pub liqor: Pubkey,
// pub price: I80F48, // oracle price at the time of liquidation
// pub quantity: i64, // number of contracts that were moved from liqee to liqor
// pub liquidation_fee: I80F48, // liq fee for this earned for this market
// padding1: [u8; EVENT_SIZE - 128],
// }
// // unsafe impl TriviallyTransmutable for LiquidateEvent {}
// impl LiquidateEvent {
// pub fn new(
// timestamp: u64,
// seq_num: usize,
// liqee: Pubkey,
// liqor: Pubkey,
// price: I80F48,
// quantity: i64,
// liquidation_fee: I80F48,
// ) -> Self {
// Self {
// event_type: EventType::Liquidate.into(),
// padding0: [0u8; 7],
// timestamp,
// seq_num,
// liqee,
// liqor,
// price,
// quantity,
// liquidation_fee,
// padding1: [0u8; EVENT_SIZE - 128],
// }
// }
// }
// const_assert_eq!(size_of::<AnyEvent>(), size_of::<FillEvent>());
// const_assert_eq!(size_of::<AnyEvent>(), size_of::<OutEvent>());
// const_assert_eq!(size_of::<AnyEvent>(), size_of::<LiquidateEvent>());

View File

@ -1159,13 +1159,11 @@ impl<'keypair> ClientInstruction for PlacePerpOrderInstruction<'keypair> {
) -> (Self::Accounts, instruction::Instruction) {
let program_id = mango_v4::id();
let instruction = Self::Instruction {
side: Side::Bid,
price: 1,
max_base_quantity: 1,
max_quote_quantity: 1,
client_order_id: 0,
order_type: OrderType::Limit,
reduce_only: false,
expiry_timestamp: 0,
limit: 1,
};

View File

@ -109,7 +109,9 @@ impl TestContextBuilder {
}));
// intentionally set to half the limit, to catch potential problems early
test.set_compute_max_units(100000);
// TODO make configurable
// margin trade test just goes above 100000 atm
test.set_compute_max_units(101000);
Self {
test,