Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add test for exact_out orders #22

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
130 changes: 121 additions & 9 deletions src/state/markets/test_market.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ fn test_market_simple() {
) = market
.place_order(
&taker,
OrderPacket::new_ioc_by_lots(
OrderPacket::new_ioc_by_base_lots(
Side::Bid,
price.as_u64(),
size.as_u64(),
Expand Down Expand Up @@ -194,7 +194,7 @@ fn test_market_simple() {
) = market
.place_order(
&taker,
OrderPacket::new_ioc_by_lots(
OrderPacket::new_ioc_by_base_lots(
Side::Ask,
price.as_u64(),
size.as_u64(),
Expand Down Expand Up @@ -222,7 +222,7 @@ fn test_market_simple() {
market
.place_order(
&taker,
OrderPacket::new_ioc_by_lots(
OrderPacket::new_ioc_by_base_lots(
Side::Ask,
ladder.asks[0].price_in_ticks.as_u64(),
ladder.asks[0].size_in_base_lots.as_u64(),
Expand Down Expand Up @@ -264,7 +264,7 @@ fn test_market_simple() {
) = market
.place_order(
&taker,
OrderPacket::new_ioc_by_lots(
OrderPacket::new_ioc_by_base_lots(
Side::Ask,
price.as_u64(),
size.as_u64(),
Expand Down Expand Up @@ -903,7 +903,7 @@ fn test_orders_with_only_free_funds() {
assert!(market
.place_order(
&taker,
OrderPacket::new_ioc_by_lots(
OrderPacket::new_ioc_by_base_lots(
Side::Ask,
100,
5,
Expand All @@ -919,7 +919,7 @@ fn test_orders_with_only_free_funds() {
assert!(market
.place_order(
&taker,
OrderPacket::new_ioc_by_lots(
OrderPacket::new_ioc_by_base_lots(
Side::Ask,
100,
5,
Expand Down Expand Up @@ -985,7 +985,7 @@ fn test_orders_with_only_free_funds() {
assert!(market
.place_order(
&trader,
OrderPacket::new_ioc_by_lots(
OrderPacket::new_ioc_by_base_lots(
Side::Bid,
100,
6,
Expand Down Expand Up @@ -1614,6 +1614,118 @@ fn test_fok_with_slippage_3() {
assert!(ladder.bids[0].size_in_base_lots == prev_ladder.bids[0].size_in_base_lots);
}

#[test]
fn test_exact_out_order() {
let mut rng = StdRng::seed_from_u64(2);
let mut market = setup_market_with_params(10000, 100, 2);
let mut event_recorder = VecDeque::new();
let mut record_event_fn = |e: MarketEvent<TraderId>| event_recorder.push_back(e);

let maker = rng.gen::<u128>();
let bid_start = rng.gen_range(9996, 10_000);
let bid_end = rng.gen_range(9900, 9978);
let ask_start = rng.gen_range(10_001, 10_005);
let ask_end = rng.gen_range(10_033, 10_100);
let start_size = rng.gen_range(10, 20);
let size_step = rng.gen_range(1, 5);
layer_orders(
&mut market,
maker,
bid_start,
bid_end,
1,
start_size,
size_step,
Side::Bid,
&mut record_event_fn,
);
layer_orders(
&mut market,
maker,
ask_start,
ask_end,
1,
start_size,
size_step,
Side::Ask,
&mut record_event_fn,
);

let ladder = market.get_typed_ladder(5);
let taker = rng.gen::<u128>();

// Send IOC buy order specifying exact number of base lots out
let price = ladder.asks[0].price_in_ticks;
let base_lots_to_fill = BaseLots::new(ladder.asks[0].size_in_base_lots.as_u64() / 2); // don't take the whole level

let (
_,
MatchingEngineResponse {
num_quote_lots_in: num_quote_lots,
num_base_lots_out: num_base_lots,
..
},
) = market
.place_order(
&taker,
OrderPacket::new_ioc_by_base_lots(
Side::Bid,
price.as_u64() + 10, // supply limit price higher than the best ask
base_lots_to_fill.as_u64(),
SelfTradeBehavior::DecrementTake,
None,
rng.gen::<u128>(),
false,
),
&mut record_event_fn,
)
.unwrap();

// check that the exact number of base lots was filled, and that they were filled at the expected price
assert_eq!(base_lots_to_fill, num_base_lots);
// assert_eq!(
// num_quote_lots * market.base_lots_per_base_unit,
// market.tick_size_in_quote_lots_per_base_unit * price * base_lots_to_fill
// );

// Send IOC sell order specifying exact number of quote lots out
let price = ladder.bids[0].price_in_ticks;
let mut quote_lots_to_fill =
market.tick_size_in_quote_lots_per_base_unit * price * ladder.bids[0].size_in_base_lots
/ market.base_lots_per_base_unit;
quote_lots_to_fill = QuoteLots::new(quote_lots_to_fill.as_u64() / 2); // don't take the whole level

let (
_,
MatchingEngineResponse {
num_quote_lots_out: num_quote_lots,
num_base_lots_in: num_base_lots,
..
},
) = market
.place_order(
&taker,
OrderPacket::new_ioc_by_quote_lots(
Side::Ask,
price.as_u64() - 10, // supply limit price lower than the best bid
quote_lots_to_fill.as_u64(),
SelfTradeBehavior::DecrementTake,
None,
rng.gen::<u128>(),
false,
),
&mut record_event_fn,
)
.unwrap();

// check that the exact number of quote lots was filled, and that they were filled at the expected price
assert_eq!(quote_lots_to_fill, num_quote_lots);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test currently fails because of fees, cc @jarry-xiao

assert_eq!(
num_quote_lots * market.base_lots_per_base_unit,
market.tick_size_in_quote_lots_per_base_unit * price * num_base_lots
);
}

#[test]
fn test_fees_basic() {
let mut rng = StdRng::seed_from_u64(2);
Expand Down Expand Up @@ -1649,7 +1761,7 @@ fn test_fees_basic() {
let (o_id, release_quantities) = market
.place_order(
&taker,
OrderPacket::new_ioc_by_lots(
OrderPacket::new_ioc_by_base_lots(
Side::Bid,
10100,
10,
Expand All @@ -1675,7 +1787,7 @@ fn test_fees_basic() {
let (o_id, release_quantities) = market
.place_order(
&taker,
OrderPacket::new_ioc_by_lots(
OrderPacket::new_ioc_by_base_lots(
Side::Ask,
9900,
10,
Expand Down
25 changes: 24 additions & 1 deletion src/state/order_schema/order_packet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,7 @@ impl OrderPacket {
)
}

pub fn new_ioc_by_lots(
pub fn new_ioc_by_base_lots(
side: Side,
price_in_ticks: u64,
base_lot_budget: u64,
Expand All @@ -382,6 +382,29 @@ impl OrderPacket {
)
}

pub fn new_ioc_by_quote_lots(
side: Side,
price_in_ticks: u64,
quote_lot_budget: u64,
self_trade_behavior: SelfTradeBehavior,
match_limit: Option<u64>,
client_order_id: u128,
use_only_deposited_funds: bool,
) -> Self {
Self::new_ioc(
side,
Some(price_in_ticks),
0,
quote_lot_budget,
0,
0,
self_trade_behavior,
match_limit,
client_order_id,
use_only_deposited_funds,
)
}

pub fn new_ioc_buy_with_slippage(quote_lots_in: u64, min_base_lots_out: u64) -> Self {
Self::new_ioc(
Side::Bid,
Expand Down
26 changes: 13 additions & 13 deletions tests/test_phoenix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -973,7 +973,7 @@ async fn test_phoenix_admin() {
.is_ok(),
"Should be able to claim authority if you are the successor"
);
let params = OrderPacket::new_ioc_by_lots(
let params = OrderPacket::new_ioc_by_base_lots(
Side::Bid,
sdk.float_price_to_ticks(102.0),
1,
Expand Down Expand Up @@ -1312,7 +1312,7 @@ async fn test_phoenix_basic() {
.await;
sdk.set_payer(clone_keypair(&default_taker.user));

let params = OrderPacket::new_ioc_by_lots(
let params = OrderPacket::new_ioc_by_base_lots(
Side::Ask,
sdk.float_price_to_ticks(39.7),
sdk.raw_base_units_to_base_lots(10.0),
Expand Down Expand Up @@ -1820,7 +1820,7 @@ async fn test_phoenix_orders_with_free_funds() {
sdk.set_payer(clone_keypair(&default_taker.user));

//Attempt to use free funds to trade, will reject because the taker has no seat
let sell_params = OrderPacket::new_ioc_by_lots(
let sell_params = OrderPacket::new_ioc_by_base_lots(
Side::Ask,
sdk.float_price_to_ticks(31.0),
sdk.raw_base_units_to_base_lots(55.0),
Expand All @@ -1843,7 +1843,7 @@ async fn test_phoenix_orders_with_free_funds() {
.is_err());

//Trade through the first 10 levels of the book and self trade the last level on each side
let sell_params = OrderPacket::new_ioc_by_lots(
let sell_params = OrderPacket::new_ioc_by_base_lots(
Side::Ask,
sdk.float_price_to_ticks(31.0),
sdk.raw_base_units_to_base_lots(55.0),
Expand All @@ -1853,7 +1853,7 @@ async fn test_phoenix_orders_with_free_funds() {
false,
);

let buy_params = OrderPacket::new_ioc_by_lots(
let buy_params = OrderPacket::new_ioc_by_base_lots(
Side::Bid,
sdk.float_price_to_ticks(59.0),
sdk.raw_base_units_to_base_lots(55.0),
Expand All @@ -1863,7 +1863,7 @@ async fn test_phoenix_orders_with_free_funds() {
false,
);

let self_trade_bid_params = OrderPacket::new_ioc_by_lots(
let self_trade_bid_params = OrderPacket::new_ioc_by_base_lots(
Side::Ask,
sdk.float_price_to_ticks(30.0),
sdk.raw_base_units_to_base_lots(11.0),
Expand All @@ -1873,7 +1873,7 @@ async fn test_phoenix_orders_with_free_funds() {
false,
);

let self_trade_offer_params = OrderPacket::new_ioc_by_lots(
let self_trade_offer_params = OrderPacket::new_ioc_by_base_lots(
Side::Bid,
sdk.float_price_to_ticks(60.0),
sdk.raw_base_units_to_base_lots(11.0),
Expand Down Expand Up @@ -1953,7 +1953,7 @@ async fn test_phoenix_orders_with_free_funds() {
sdk.raw_base_units_to_base_lots(10.0),
);

let ioc_buy_params = OrderPacket::new_ioc_by_lots(
let ioc_buy_params = OrderPacket::new_ioc_by_base_lots(
Side::Bid,
sdk.float_price_to_ticks(34.1),
sdk.raw_base_units_to_base_lots(10.0),
Expand All @@ -1963,7 +1963,7 @@ async fn test_phoenix_orders_with_free_funds() {
true,
);

let ioc_sell_params = OrderPacket::new_ioc_by_lots(
let ioc_sell_params = OrderPacket::new_ioc_by_base_lots(
Side::Ask,
sdk.float_price_to_ticks(30.0),
sdk.raw_base_units_to_base_lots(10.0),
Expand Down Expand Up @@ -2051,7 +2051,7 @@ async fn test_phoenix_orders_with_free_funds() {
let new_order_ix = create_new_order_with_free_funds_instruction(
&sdk.core.active_market_key,
&second_maker.user.pubkey(),
&OrderPacket::new_ioc_by_lots(
&OrderPacket::new_ioc_by_base_lots(
Side::Bid,
sdk.float_price_to_ticks(250.0),
sdk.raw_base_units_to_base_lots(10.0),
Expand Down Expand Up @@ -3060,7 +3060,7 @@ async fn test_phoenix_place_multiple_memory_management() {
&default_taker.user.pubkey(),
&sdk.base_mint,
&sdk.quote_mint,
&OrderPacket::new_ioc_by_lots(
&OrderPacket::new_ioc_by_base_lots(
Side::Ask,
0,
u64::MAX,
Expand Down Expand Up @@ -3133,7 +3133,7 @@ async fn test_phoenix_place_multiple_limit_orders_adversarial() {
// Normally this would crash due to compute usage, but we now coalesce the orders
// at the same price in place multiple orders
sdk.set_payer(clone_keypair(&default_taker.user));
let order_packet = OrderPacket::new_ioc_by_lots(
let order_packet = OrderPacket::new_ioc_by_base_lots(
Side::Bid,
sdk.float_price_to_ticks(101.0),
700,
Expand Down Expand Up @@ -3265,7 +3265,7 @@ async fn test_phoenix_basic_with_raw_base_unit_adjustment() {
let second_cross_price = sdk.float_price_to_ticks(*bid_price_range.first().unwrap()); // Takes the last price in the bid price_range (40.0)
let second_cross_size = sdk.raw_base_units_to_base_lots(1000_f64);

let params = OrderPacket::new_ioc_by_lots(
let params = OrderPacket::new_ioc_by_base_lots(
Side::Ask,
second_cross_price,
first_cross_size + second_cross_size,
Expand Down