Add tests using max_quote_quantity

This commit is contained in:
Christian Kamm 2022-02-19 13:11:42 +01:00
parent 256d01af3b
commit 9da7b8cda0
4 changed files with 174 additions and 21 deletions

View File

@ -617,6 +617,26 @@ impl SpotMarketCookie {
}
}
pub struct PlacePerpOptions {
pub reduce_only: bool,
pub max_quote_size: Option<f64>,
pub order_type: mango::matching::OrderType,
pub limit: u8,
pub expiry_timestamp: Option<u64>,
}
impl Default for PlacePerpOptions {
fn default() -> Self {
Self {
reduce_only: false,
max_quote_size: None,
order_type: mango::matching::OrderType::Limit,
limit: 10,
expiry_timestamp: None,
}
}
}
#[derive(Copy, Clone)]
pub struct PerpMarketCookie {
pub address: Pubkey,
@ -709,12 +729,12 @@ impl PerpMarketCookie {
user_index: usize,
side: mango::matching::Side,
base_size: f64,
quote_size: Option<f64>,
price: f64,
expiry_timestamp: Option<u64>,
options: PlacePerpOptions,
) {
let order_base_size = test.base_size_number_to_lots(&self.mint, base_size);
let order_quote_size = quote_size
let order_quote_size = options
.max_quote_size
.map(|s| ((s * test.quote_mint.unit) / self.mint.quote_lot) as u64)
.unwrap_or(u64::MAX);
let order_price = test.price_number_to_lots(&self.mint, price);
@ -728,10 +748,10 @@ impl PerpMarketCookie {
order_quote_size,
order_price,
mango_group_cookie.current_perp_order_id,
mango::matching::OrderType::Limit,
false,
expiry_timestamp,
39,
options.order_type,
options.reduce_only,
options.expiry_timestamp,
options.limit,
)
.await;

View File

@ -159,10 +159,9 @@ pub async fn place_perp_order_scenario(
user_index,
order_side,
order_size,
None,
order_price,
None, // expiry timestamp
) // TODO - pass in reduce only param
PlacePerpOptions::default(),
)
.await;
mango_group_cookie.users_with_perp_event[market_index].push(user_index);

View File

@ -1,4 +1,5 @@
mod program_test;
use fixed::types::I80F48;
use mango::{matching::*, state::*};
use program_test::assertions::*;
use program_test::cookies::*;
@ -197,9 +198,11 @@ async fn test_match_against_expired_orders() {
bidder_user_index,
Side::Bid,
1.0,
None,
(9930 + i) as f64,
Some(clock.unix_timestamp as u64 + 2),
PlacePerpOptions {
expiry_timestamp: Some(clock.unix_timestamp as u64 + 2),
..PlacePerpOptions::default()
},
)
.await;
}
@ -218,9 +221,8 @@ async fn test_match_against_expired_orders() {
asker_user_index,
Side::Ask,
1.0,
None,
9_950.0,
None,
PlacePerpOptions::default(),
)
.await;
// TODO: Would be very nice to be able to access compute units, stack use, heap use in the test!
@ -239,7 +241,7 @@ async fn test_full_book() {
// === Arrange ===
let config = MangoProgramTestConfig {
// Use intentionally low CU: this test wants to verify the limit is sufficient
compute_limit: 200_000,
compute_limit: 100_000,
num_users: 9,
..MangoProgramTestConfig::default_two_mints()
};
@ -278,9 +280,8 @@ async fn test_full_book() {
bidder_user_index,
Side::Bid,
0.0001 * ((i + 1) as f64),
None,
base_price,
None,
PlacePerpOptions::default(),
)
.await;
}
@ -294,9 +295,143 @@ async fn test_full_book() {
asker_user_index,
Side::Ask,
1.0,
None,
9_950.0,
None,
PlacePerpOptions {
limit: 16, // stays barely below 100k CU
..PlacePerpOptions::default()
},
)
.await;
}
#[tokio::test]
async fn test_perp_order_max_quote() {
// === Arrange ===
let config = MangoProgramTestConfig { ..MangoProgramTestConfig::default_two_mints() };
let mut test = MangoProgramTest::start_new(&config).await;
let mut mango_group_cookie = MangoGroupCookie::default(&mut test).await;
mango_group_cookie.full_setup(&mut test, config.num_users, config.num_mints - 1).await;
// General parameters
let bidder_user_index: usize = 0;
let asker_user_index: usize = 1;
let mint_index: usize = 0;
let base_price: f64 = 10_000.0;
let mint = test.mints[mint_index];
// Set oracles
mango_group_cookie.set_oracle(&mut test, mint_index, base_price).await;
// === Act ===
// Step 1: Make deposits
let user_deposits = vec![
(bidder_user_index, test.quote_index, 1000.0 * base_price),
(asker_user_index, mint_index, 1000.0),
];
deposit_scenario(&mut test, &mut mango_group_cookie, &user_deposits).await;
// Step 2: Setup ask orders
use mango::matching::Side;
let mut perp_market_cookie = mango_group_cookie.perp_markets[mint_index];
perp_market_cookie
.place_order(
&mut test,
&mut mango_group_cookie,
asker_user_index,
Side::Ask,
0.1,
10_000.0,
PlacePerpOptions::default(),
)
.await;
perp_market_cookie
.place_order(
&mut test,
&mut mango_group_cookie,
asker_user_index,
Side::Ask,
1.0,
10_100.0,
PlacePerpOptions::default(),
)
.await;
perp_market_cookie
.place_order(
&mut test,
&mut mango_group_cookie,
asker_user_index,
Side::Ask,
1.0,
10_200.0,
PlacePerpOptions::default(),
)
.await;
// Step 4: Place an immediate order that includes a quote limit
let max_quote = 0.1 * 10_000.0 + 0.5 * 10_100.0; // first ask order, plus half of the second one
perp_market_cookie
.place_order(
&mut test,
&mut mango_group_cookie,
bidder_user_index,
Side::Bid,
999999.0, // no max_base_quantity
90_000.0, // no price limit
PlacePerpOptions {
max_quote_size: Some(max_quote),
order_type: mango::matching::OrderType::ImmediateOrCancel,
..PlacePerpOptions::default()
},
)
.await;
mango_group_cookie.users_with_perp_event[mint_index].push(asker_user_index);
mango_group_cookie.users_with_perp_event[mint_index].push(bidder_user_index);
mango_group_cookie.consume_perp_events(&mut test).await;
// bought 1000 + 5000 MNGO
let mut expected_mngo = 6000;
let bidder_account = mango_group_cookie.mango_accounts[bidder_user_index].mango_account;
assert_eq!(bidder_account.perp_accounts[mint_index].base_position, expected_mngo);
// cost was 6050 + 1% taker fees
let mut expected_usdc_base = 1000.0 + 5050.0;
assert!(
(bidder_account.perp_accounts[mint_index].quote_position
+ I80F48::from_num(expected_usdc_base * 1000000.0 * 1.01))
.abs()
< 1.0
);
// Step 5: Place an quote_limit order that ends up partially on the book
let max_quote = 0.5 * 10_100.0 + 0.7 * 10_150.0; // remaining half of the second one plus some extra
perp_market_cookie
.place_order(
&mut test,
&mut mango_group_cookie,
bidder_user_index,
Side::Bid,
999999.0, // no max_base_quantity
10_150.0,
PlacePerpOptions { max_quote_size: Some(max_quote), ..PlacePerpOptions::default() },
)
.await;
mango_group_cookie.users_with_perp_event[mint_index].push(asker_user_index);
mango_group_cookie.users_with_perp_event[mint_index].push(bidder_user_index);
mango_group_cookie.consume_perp_events(&mut test).await;
// bought 5000 MNGO
expected_mngo += 5000;
let bidder_account = mango_group_cookie.mango_accounts[bidder_user_index].mango_account;
assert_eq!(bidder_account.perp_accounts[mint_index].base_position, expected_mngo);
// cost was 5050 + 1% taker fees
expected_usdc_base += 5050.0;
assert!(
(bidder_account.perp_accounts[mint_index].quote_position
+ I80F48::from_num(expected_usdc_base * 1000000.0 * 1.01))
.abs()
< 1.0
);
// the remainder was placed as a bid, as expected
let bids = test.load_account::<BookSide>(perp_market_cookie.bids_pk).await;
let top_order = bids.get_max().unwrap();
assert_eq!(top_order.price(), test.price_number_to_lots(&mint, 10_150.0) as i64);
assert_eq!(top_order.quantity, test.base_size_number_to_lots(&mint, 0.7) as i64);
}

View File

@ -318,9 +318,8 @@ async fn test_perp_trigger_orders_health() {
user2_index,
Side::Ask,
base_size,
None,
0.99 * base_price,
None,
PlacePerpOptions::default(),
)
.await;