Add tests using max_quote_quantity
This commit is contained in:
parent
256d01af3b
commit
9da7b8cda0
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
Loading…
Reference in New Issue