Perps: Let all base lot changes go through a function
Direct access to base_position_lots and quote_position_native is not allowed anymore. Fixes an issue where quote_lots were used instead of quote_native, and also takes fees into account for the entry price.
This commit is contained in:
parent
e56ed3776c
commit
6c32077d1a
|
@ -58,7 +58,7 @@ pub fn perp_settle_fees(ctx: Context<PerpSettleFees>, max_settle_amount: I80F48)
|
||||||
|
|
||||||
// Calculate PnL for each account
|
// Calculate PnL for each account
|
||||||
let base_native = perp_position.base_position_native(&perp_market);
|
let base_native = perp_position.base_position_native(&perp_market);
|
||||||
let pnl: I80F48 = cm!(perp_position.quote_position_native + base_native * oracle_price);
|
let pnl: I80F48 = cm!(perp_position.quote_position_native() + base_native * oracle_price);
|
||||||
|
|
||||||
// Account perp position must have a loss to be able to settle against the fee account
|
// Account perp position must have a loss to be able to settle against the fee account
|
||||||
require!(pnl.is_negative(), MangoError::ProfitabilityMismatch);
|
require!(pnl.is_negative(), MangoError::ProfitabilityMismatch);
|
||||||
|
@ -72,7 +72,7 @@ pub fn perp_settle_fees(ctx: Context<PerpSettleFees>, max_settle_amount: I80F48)
|
||||||
.abs()
|
.abs()
|
||||||
.min(perp_market.fees_accrued.abs())
|
.min(perp_market.fees_accrued.abs())
|
||||||
.min(max_settle_amount);
|
.min(max_settle_amount);
|
||||||
perp_position.quote_position_native = cm!(perp_position.quote_position_native + settlement);
|
perp_position.change_quote_position(settlement);
|
||||||
perp_market.fees_accrued = cm!(perp_market.fees_accrued - settlement);
|
perp_market.fees_accrued = cm!(perp_market.fees_accrued - settlement);
|
||||||
|
|
||||||
// Update the account's net_settled with the new PnL
|
// Update the account's net_settled with the new PnL
|
||||||
|
|
|
@ -64,8 +64,8 @@ pub fn perp_settle_pnl(ctx: Context<PerpSettlePnl>, max_settle_amount: I80F48) -
|
||||||
perp_market.oracle_price(&AccountInfoRef::borrow(ctx.accounts.oracle.as_ref())?)?;
|
perp_market.oracle_price(&AccountInfoRef::borrow(ctx.accounts.oracle.as_ref())?)?;
|
||||||
|
|
||||||
// Fetch perp positions for accounts
|
// Fetch perp positions for accounts
|
||||||
let mut a_perp_position = account_a.perp_position_mut(perp_market.perp_market_index)?;
|
let a_perp_position = account_a.perp_position_mut(perp_market.perp_market_index)?;
|
||||||
let mut b_perp_position = account_b.perp_position_mut(perp_market.perp_market_index)?;
|
let b_perp_position = account_b.perp_position_mut(perp_market.perp_market_index)?;
|
||||||
|
|
||||||
// Settle funding before settling any PnL
|
// Settle funding before settling any PnL
|
||||||
a_perp_position.settle_funding(&perp_market);
|
a_perp_position.settle_funding(&perp_market);
|
||||||
|
@ -74,8 +74,8 @@ pub fn perp_settle_pnl(ctx: Context<PerpSettlePnl>, max_settle_amount: I80F48) -
|
||||||
// Calculate PnL for each account
|
// Calculate PnL for each account
|
||||||
let a_base_native = a_perp_position.base_position_native(&perp_market);
|
let a_base_native = a_perp_position.base_position_native(&perp_market);
|
||||||
let b_base_native = b_perp_position.base_position_native(&perp_market);
|
let b_base_native = b_perp_position.base_position_native(&perp_market);
|
||||||
let a_pnl: I80F48 = cm!(a_perp_position.quote_position_native + a_base_native * oracle_price);
|
let a_pnl: I80F48 = cm!(a_perp_position.quote_position_native() + a_base_native * oracle_price);
|
||||||
let b_pnl: I80F48 = cm!(b_perp_position.quote_position_native + b_base_native * oracle_price);
|
let b_pnl: I80F48 = cm!(b_perp_position.quote_position_native() + b_base_native * oracle_price);
|
||||||
|
|
||||||
// Account A must be profitable, and B must be unprofitable
|
// Account A must be profitable, and B must be unprofitable
|
||||||
// PnL must be opposite signs for there to be a settlement
|
// PnL must be opposite signs for there to be a settlement
|
||||||
|
@ -84,8 +84,8 @@ pub fn perp_settle_pnl(ctx: Context<PerpSettlePnl>, max_settle_amount: I80F48) -
|
||||||
|
|
||||||
// Settle for the maximum possible capped to max_settle_amount
|
// Settle for the maximum possible capped to max_settle_amount
|
||||||
let settlement = a_pnl.abs().min(b_pnl.abs()).min(max_settle_amount);
|
let settlement = a_pnl.abs().min(b_pnl.abs()).min(max_settle_amount);
|
||||||
cm!(a_perp_position.quote_position_native -= settlement);
|
a_perp_position.change_quote_position(-settlement);
|
||||||
cm!(b_perp_position.quote_position_native += settlement);
|
b_perp_position.change_quote_position(settlement);
|
||||||
|
|
||||||
// Update the account's net_settled with the new PnL
|
// Update the account's net_settled with the new PnL
|
||||||
let settlement_i64 = settlement.checked_to_num::<i64>().unwrap();
|
let settlement_i64 = settlement.checked_to_num::<i64>().unwrap();
|
||||||
|
|
|
@ -18,8 +18,8 @@ pub fn emit_perp_balances(
|
||||||
mango_group,
|
mango_group,
|
||||||
mango_account,
|
mango_account,
|
||||||
market_index,
|
market_index,
|
||||||
base_position: pp.base_position_lots,
|
base_position: pp.base_position_lots(),
|
||||||
quote_position: pp.quote_position_native.to_bits(),
|
quote_position: pp.quote_position_native().to_bits(),
|
||||||
long_settled_funding: pp.long_settled_funding.to_bits(),
|
long_settled_funding: pp.long_settled_funding.to_bits(),
|
||||||
short_settled_funding: pp.short_settled_funding.to_bits(),
|
short_settled_funding: pp.short_settled_funding.to_bits(),
|
||||||
price,
|
price,
|
||||||
|
|
|
@ -498,14 +498,14 @@ impl PerpInfo {
|
||||||
let base_info = &token_infos[base_index];
|
let base_info = &token_infos[base_index];
|
||||||
|
|
||||||
let base_lot_size = I80F48::from(perp_market.base_lot_size);
|
let base_lot_size = I80F48::from(perp_market.base_lot_size);
|
||||||
let base_lots = cm!(perp_position.base_position_lots + perp_position.taker_base_lots);
|
let base_lots = cm!(perp_position.base_position_lots() + perp_position.taker_base_lots);
|
||||||
|
|
||||||
let unsettled_funding = perp_position.unsettled_funding(&perp_market);
|
let unsettled_funding = perp_position.unsettled_funding(&perp_market);
|
||||||
let taker_quote = I80F48::from(cm!(
|
let taker_quote = I80F48::from(cm!(
|
||||||
perp_position.taker_quote_lots * perp_market.quote_lot_size
|
perp_position.taker_quote_lots * perp_market.quote_lot_size
|
||||||
));
|
));
|
||||||
let quote_current =
|
let quote_current =
|
||||||
cm!(perp_position.quote_position_native - unsettled_funding + taker_quote);
|
cm!(perp_position.quote_position_native() - unsettled_funding + taker_quote);
|
||||||
|
|
||||||
// Two scenarios:
|
// Two scenarios:
|
||||||
// 1. The price goes low and all bids execute, converting to base.
|
// 1. The price goes low and all bids execute, converting to base.
|
||||||
|
@ -1226,8 +1226,7 @@ mod tests {
|
||||||
|
|
||||||
let mut perp1 = mock_perp_market(group, 9, 4, 0.2, 0.1);
|
let mut perp1 = mock_perp_market(group, 9, 4, 0.2, 0.1);
|
||||||
let perpaccount = account.ensure_perp_position(9).unwrap().0;
|
let perpaccount = account.ensure_perp_position(9).unwrap().0;
|
||||||
perpaccount.base_position_lots = 3;
|
perpaccount.change_base_and_quote_positions(perp1.data(), 3, -I80F48::from(310u16));
|
||||||
perpaccount.quote_position_native = -I80F48::from(310u16);
|
|
||||||
perpaccount.bids_base_lots = 7;
|
perpaccount.bids_base_lots = 7;
|
||||||
perpaccount.asks_base_lots = 11;
|
perpaccount.asks_base_lots = 11;
|
||||||
perpaccount.taker_base_lots = 1;
|
perpaccount.taker_base_lots = 1;
|
||||||
|
@ -1398,8 +1397,11 @@ mod tests {
|
||||||
|
|
||||||
let mut perp1 = mock_perp_market(group, 9, 4, 0.2, 0.1);
|
let mut perp1 = mock_perp_market(group, 9, 4, 0.2, 0.1);
|
||||||
let perpaccount = account.ensure_perp_position(9).unwrap().0;
|
let perpaccount = account.ensure_perp_position(9).unwrap().0;
|
||||||
perpaccount.base_position_lots = testcase.perp1.0;
|
perpaccount.change_base_and_quote_positions(
|
||||||
perpaccount.quote_position_native = I80F48::from(testcase.perp1.1);
|
perp1.data(),
|
||||||
|
testcase.perp1.0,
|
||||||
|
I80F48::from(testcase.perp1.1),
|
||||||
|
);
|
||||||
perpaccount.bids_base_lots = testcase.perp1.2;
|
perpaccount.bids_base_lots = testcase.perp1.2;
|
||||||
perpaccount.asks_base_lots = testcase.perp1.3;
|
perpaccount.asks_base_lots = testcase.perp1.3;
|
||||||
|
|
||||||
|
@ -1708,8 +1710,7 @@ mod tests {
|
||||||
let mut perp1 = mock_perp_market(group, 9, 1, 0.2, 0.1);
|
let mut perp1 = mock_perp_market(group, 9, 1, 0.2, 0.1);
|
||||||
perp1.data().long_funding = I80F48::from_num(10.1);
|
perp1.data().long_funding = I80F48::from_num(10.1);
|
||||||
let perpaccount = account.ensure_perp_position(9).unwrap().0;
|
let perpaccount = account.ensure_perp_position(9).unwrap().0;
|
||||||
perpaccount.base_position_lots = 10; // 100 base native
|
perpaccount.change_base_and_quote_positions(perp1.data(), 10, I80F48::from(-110));
|
||||||
perpaccount.quote_position_native = I80F48::from(-110);
|
|
||||||
perpaccount.long_settled_funding = I80F48::from_num(10.0);
|
perpaccount.long_settled_funding = I80F48::from_num(10.0);
|
||||||
|
|
||||||
let ais = vec![
|
let ais = vec![
|
||||||
|
|
|
@ -712,10 +712,9 @@ impl<
|
||||||
if raw_index_opt.is_none() {
|
if raw_index_opt.is_none() {
|
||||||
raw_index_opt = self.all_perp_positions().position(|p| !p.is_active());
|
raw_index_opt = self.all_perp_positions().position(|p| !p.is_active());
|
||||||
if let Some(raw_index) = raw_index_opt {
|
if let Some(raw_index) = raw_index_opt {
|
||||||
*(self.perp_position_mut_by_raw_index(raw_index)) = PerpPosition {
|
let perp_position = self.perp_position_mut_by_raw_index(raw_index);
|
||||||
market_index: perp_market_index,
|
*perp_position = PerpPosition::default();
|
||||||
..Default::default()
|
perp_position.market_index = perp_market_index;
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(raw_index) = raw_index_opt {
|
if let Some(raw_index) = raw_index_opt {
|
||||||
|
@ -796,18 +795,13 @@ impl<
|
||||||
|
|
||||||
let side = fill.taker_side.invert_side();
|
let side = fill.taker_side.invert_side();
|
||||||
let (base_change, quote_change) = fill.base_quote_change(side);
|
let (base_change, quote_change) = fill.base_quote_change(side);
|
||||||
pa.change_base_and_entry_positions(perp_market, base_change, quote_change);
|
let quote = cm!(I80F48::from(perp_market.quote_lot_size) * I80F48::from(quote_change));
|
||||||
let quote = I80F48::from_num(
|
let fees = cm!(quote.abs() * fill.maker_fee);
|
||||||
perp_market
|
|
||||||
.quote_lot_size
|
|
||||||
.checked_mul(quote_change)
|
|
||||||
.unwrap(),
|
|
||||||
);
|
|
||||||
let fees = quote.abs() * fill.maker_fee;
|
|
||||||
if !fill.market_fees_applied {
|
if !fill.market_fees_applied {
|
||||||
perp_market.fees_accrued += fees;
|
cm!(perp_market.fees_accrued += fees);
|
||||||
}
|
}
|
||||||
pa.quote_position_native = pa.quote_position_native.checked_add(quote - fees).unwrap();
|
let quote_change_native = cm!(quote - fees);
|
||||||
|
pa.change_base_and_quote_positions(perp_market, base_change, quote_change_native);
|
||||||
|
|
||||||
if fill.maker_out {
|
if fill.maker_out {
|
||||||
self.remove_perp_order(fill.maker_slot as usize, base_change.abs())
|
self.remove_perp_order(fill.maker_slot as usize, base_change.abs())
|
||||||
|
@ -836,12 +830,11 @@ impl<
|
||||||
|
|
||||||
let (base_change, quote_change) = fill.base_quote_change(fill.taker_side);
|
let (base_change, quote_change) = fill.base_quote_change(fill.taker_side);
|
||||||
pa.remove_taker_trade(base_change, quote_change);
|
pa.remove_taker_trade(base_change, quote_change);
|
||||||
pa.change_base_and_entry_positions(perp_market, base_change, quote_change);
|
|
||||||
let quote = I80F48::from_num(perp_market.quote_lot_size * quote_change);
|
|
||||||
|
|
||||||
// fees are assessed at time of trade; no need to assess fees here
|
// fees are assessed at time of trade; no need to assess fees here
|
||||||
|
let quote_change_native =
|
||||||
|
cm!(I80F48::from(perp_market.quote_lot_size) * I80F48::from(quote_change));
|
||||||
|
pa.change_base_and_quote_positions(perp_market, base_change, quote_change_native);
|
||||||
|
|
||||||
pa.quote_position_native += quote;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -153,10 +153,10 @@ pub struct PerpPosition {
|
||||||
pub padding: [u8; 6],
|
pub padding: [u8; 6],
|
||||||
|
|
||||||
/// Active position size, measured in base lots
|
/// Active position size, measured in base lots
|
||||||
pub base_position_lots: i64,
|
base_position_lots: i64,
|
||||||
/// Active position in quote (conversation rate is that of the time the order was settled)
|
/// Active position in quote (conversation rate is that of the time the order was settled)
|
||||||
/// measured in native quote
|
/// measured in native quote
|
||||||
pub quote_position_native: I80F48,
|
quote_position_native: I80F48,
|
||||||
|
|
||||||
/// Tracks what the position is to calculate average entry & break even price
|
/// Tracks what the position is to calculate average entry & break even price
|
||||||
pub quote_entry_native: i64,
|
pub quote_entry_native: i64,
|
||||||
|
@ -240,8 +240,16 @@ impl PerpPosition {
|
||||||
I80F48::from(cm!(self.base_position_lots * market.base_lot_size))
|
I80F48::from(cm!(self.base_position_lots * market.base_lot_size))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn base_position_lots(&self) -> i64 {
|
||||||
|
self.base_position_lots
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn quote_position_native(&self) -> I80F48 {
|
||||||
|
self.quote_position_native
|
||||||
|
}
|
||||||
|
|
||||||
/// This assumes settle_funding was already called
|
/// This assumes settle_funding was already called
|
||||||
pub fn change_base_position(&mut self, perp_market: &mut PerpMarket, base_change: i64) {
|
fn change_base_position(&mut self, perp_market: &mut PerpMarket, base_change: i64) {
|
||||||
let start = self.base_position_lots;
|
let start = self.base_position_lots;
|
||||||
self.base_position_lots += base_change;
|
self.base_position_lots += base_change;
|
||||||
perp_market.open_interest += self.base_position_lots.abs() - start.abs();
|
perp_market.open_interest += self.base_position_lots.abs() - start.abs();
|
||||||
|
@ -271,25 +279,24 @@ impl PerpPosition {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update the quote entry position
|
/// Update the quote entry position
|
||||||
pub fn change_quote_entry(&mut self, base_change: i64, quote_change: i64) {
|
fn update_entry_price(&mut self, base_change: i64, quote_change_native: i64) {
|
||||||
if base_change == 0 {
|
if base_change == 0 {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let old_position = self.base_position_lots;
|
let old_position = self.base_position_lots;
|
||||||
let is_increasing = old_position == 0 || old_position.signum() == base_change.signum();
|
let is_increasing = old_position == 0 || old_position.signum() == base_change.signum();
|
||||||
cm!(self.quote_running_native += quote_change);
|
cm!(self.quote_running_native += quote_change_native);
|
||||||
match is_increasing {
|
match is_increasing {
|
||||||
true => {
|
true => {
|
||||||
cm!(self.quote_entry_native += quote_change);
|
cm!(self.quote_entry_native += quote_change_native);
|
||||||
}
|
}
|
||||||
false => {
|
false => {
|
||||||
let new_position = cm!(old_position + base_change);
|
let new_position = cm!(old_position + base_change);
|
||||||
let changes_side = old_position.signum() == -new_position.signum();
|
let changes_side = old_position.signum() == -new_position.signum();
|
||||||
self.quote_entry_native = if changes_side {
|
self.quote_entry_native = if changes_side {
|
||||||
cm!(
|
cm!(((new_position as f64) * (quote_change_native as f64)
|
||||||
((new_position as f64) * (quote_change as f64) / (base_change as f64))
|
/ (base_change as f64))
|
||||||
.round()
|
.round()) as i64
|
||||||
) as i64
|
|
||||||
} else {
|
} else {
|
||||||
let remaining_frac =
|
let remaining_frac =
|
||||||
(1f64 - (base_change.abs() as f64) / (old_position.abs() as f64)).max(0f64);
|
(1f64 - (base_change.abs() as f64) / (old_position.abs() as f64)).max(0f64);
|
||||||
|
@ -301,14 +308,22 @@ impl PerpPosition {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Change the base and quote positions as the result of a trade
|
/// Change the base and quote positions as the result of a trade
|
||||||
pub fn change_base_and_entry_positions(
|
pub fn change_base_and_quote_positions(
|
||||||
&mut self,
|
&mut self,
|
||||||
perp_market: &mut PerpMarket,
|
perp_market: &mut PerpMarket,
|
||||||
base_change: i64,
|
base_change: i64,
|
||||||
quote_change: i64,
|
quote_change_native: I80F48,
|
||||||
) {
|
) {
|
||||||
self.change_quote_entry(base_change, quote_change);
|
self.update_entry_price(
|
||||||
|
base_change,
|
||||||
|
quote_change_native.round().checked_to_num().unwrap(),
|
||||||
|
);
|
||||||
self.change_base_position(perp_market, base_change);
|
self.change_base_position(perp_market, base_change);
|
||||||
|
self.change_quote_position(quote_change_native);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn change_quote_position(&mut self, quote_change_native: I80F48) {
|
||||||
|
cm!(self.quote_position_native += quote_change_native);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculate the average entry price of the position
|
/// Calculate the average entry price of the position
|
||||||
|
@ -439,7 +454,7 @@ mod tests {
|
||||||
let mut market = create_perp_market();
|
let mut market = create_perp_market();
|
||||||
let mut pos = create_perp_position(0, 0, 0);
|
let mut pos = create_perp_position(0, 0, 0);
|
||||||
// Go long 10 @ 10
|
// Go long 10 @ 10
|
||||||
pos.change_base_and_entry_positions(&mut market, 10, -100);
|
pos.change_base_and_quote_positions(&mut market, 10, I80F48::from(-100));
|
||||||
assert_eq!(pos.quote_entry_native, -100);
|
assert_eq!(pos.quote_entry_native, -100);
|
||||||
assert_eq!(pos.quote_running_native, -100);
|
assert_eq!(pos.quote_running_native, -100);
|
||||||
assert_eq!(pos.avg_entry_price(), I80F48::from(10));
|
assert_eq!(pos.avg_entry_price(), I80F48::from(10));
|
||||||
|
@ -450,7 +465,7 @@ mod tests {
|
||||||
let mut market = create_perp_market();
|
let mut market = create_perp_market();
|
||||||
let mut pos = create_perp_position(0, 0, 0);
|
let mut pos = create_perp_position(0, 0, 0);
|
||||||
// Go short 10 @ 10
|
// Go short 10 @ 10
|
||||||
pos.change_base_and_entry_positions(&mut market, -10, 100);
|
pos.change_base_and_quote_positions(&mut market, -10, I80F48::from(100));
|
||||||
assert_eq!(pos.quote_entry_native, 100);
|
assert_eq!(pos.quote_entry_native, 100);
|
||||||
assert_eq!(pos.quote_running_native, 100);
|
assert_eq!(pos.quote_running_native, 100);
|
||||||
assert_eq!(pos.avg_entry_price(), I80F48::from(10));
|
assert_eq!(pos.avg_entry_price(), I80F48::from(10));
|
||||||
|
@ -461,7 +476,7 @@ mod tests {
|
||||||
let mut market = create_perp_market();
|
let mut market = create_perp_market();
|
||||||
let mut pos = create_perp_position(10, -100, -100);
|
let mut pos = create_perp_position(10, -100, -100);
|
||||||
// Go long 10 @ 30
|
// Go long 10 @ 30
|
||||||
pos.change_base_and_entry_positions(&mut market, 10, -300);
|
pos.change_base_and_quote_positions(&mut market, 10, I80F48::from(-300));
|
||||||
assert_eq!(pos.quote_entry_native, -400);
|
assert_eq!(pos.quote_entry_native, -400);
|
||||||
assert_eq!(pos.quote_running_native, -400);
|
assert_eq!(pos.quote_running_native, -400);
|
||||||
assert_eq!(pos.avg_entry_price(), I80F48::from(20));
|
assert_eq!(pos.avg_entry_price(), I80F48::from(20));
|
||||||
|
@ -472,7 +487,7 @@ mod tests {
|
||||||
let mut market = create_perp_market();
|
let mut market = create_perp_market();
|
||||||
let mut pos = create_perp_position(-10, 100, 100);
|
let mut pos = create_perp_position(-10, 100, 100);
|
||||||
// Go short 10 @ 10
|
// Go short 10 @ 10
|
||||||
pos.change_base_and_entry_positions(&mut market, -10, 300);
|
pos.change_base_and_quote_positions(&mut market, -10, I80F48::from(300));
|
||||||
assert_eq!(pos.quote_entry_native, 400);
|
assert_eq!(pos.quote_entry_native, 400);
|
||||||
assert_eq!(pos.quote_running_native, 400);
|
assert_eq!(pos.quote_running_native, 400);
|
||||||
assert_eq!(pos.avg_entry_price(), I80F48::from(20));
|
assert_eq!(pos.avg_entry_price(), I80F48::from(20));
|
||||||
|
@ -483,7 +498,7 @@ mod tests {
|
||||||
let mut market = create_perp_market();
|
let mut market = create_perp_market();
|
||||||
let mut pos = create_perp_position(-10, 100, 100);
|
let mut pos = create_perp_position(-10, 100, 100);
|
||||||
// Go long 5 @ 50
|
// Go long 5 @ 50
|
||||||
pos.change_base_and_entry_positions(&mut market, 5, -250);
|
pos.change_base_and_quote_positions(&mut market, 5, I80F48::from(-250));
|
||||||
assert_eq!(pos.quote_entry_native, 50);
|
assert_eq!(pos.quote_entry_native, 50);
|
||||||
assert_eq!(pos.quote_running_native, -150);
|
assert_eq!(pos.quote_running_native, -150);
|
||||||
assert_eq!(pos.avg_entry_price(), I80F48::from(10)); // Entry price remains the same when decreasing
|
assert_eq!(pos.avg_entry_price(), I80F48::from(10)); // Entry price remains the same when decreasing
|
||||||
|
@ -494,7 +509,7 @@ mod tests {
|
||||||
let mut market = create_perp_market();
|
let mut market = create_perp_market();
|
||||||
let mut pos = create_perp_position(10, -100, -100);
|
let mut pos = create_perp_position(10, -100, -100);
|
||||||
// Go short 5 @ 50
|
// Go short 5 @ 50
|
||||||
pos.change_base_and_entry_positions(&mut market, -5, 250);
|
pos.change_base_and_quote_positions(&mut market, -5, I80F48::from(250));
|
||||||
assert_eq!(pos.quote_entry_native, -50);
|
assert_eq!(pos.quote_entry_native, -50);
|
||||||
assert_eq!(pos.quote_running_native, 150);
|
assert_eq!(pos.quote_running_native, 150);
|
||||||
assert_eq!(pos.avg_entry_price(), I80F48::from(10)); // Entry price remains the same when decreasing
|
assert_eq!(pos.avg_entry_price(), I80F48::from(10)); // Entry price remains the same when decreasing
|
||||||
|
@ -505,7 +520,7 @@ mod tests {
|
||||||
let mut market = create_perp_market();
|
let mut market = create_perp_market();
|
||||||
let mut pos = create_perp_position(10, -100, -100);
|
let mut pos = create_perp_position(10, -100, -100);
|
||||||
// Go short 10 @ 50
|
// Go short 10 @ 50
|
||||||
pos.change_base_and_entry_positions(&mut market, -10, 250);
|
pos.change_base_and_quote_positions(&mut market, -10, I80F48::from(250));
|
||||||
assert_eq!(pos.quote_entry_native, 0);
|
assert_eq!(pos.quote_entry_native, 0);
|
||||||
assert_eq!(pos.quote_running_native, 150);
|
assert_eq!(pos.quote_running_native, 150);
|
||||||
assert_eq!(pos.avg_entry_price(), I80F48::from(0)); // Entry price zero when no position
|
assert_eq!(pos.avg_entry_price(), I80F48::from(0)); // Entry price zero when no position
|
||||||
|
@ -516,7 +531,7 @@ mod tests {
|
||||||
let mut market = create_perp_market();
|
let mut market = create_perp_market();
|
||||||
let mut pos = create_perp_position(-10, 100, 100);
|
let mut pos = create_perp_position(-10, 100, 100);
|
||||||
// Go long 10 @ 50
|
// Go long 10 @ 50
|
||||||
pos.change_base_and_entry_positions(&mut market, 10, -250);
|
pos.change_base_and_quote_positions(&mut market, 10, I80F48::from(-250));
|
||||||
assert_eq!(pos.quote_entry_native, 0);
|
assert_eq!(pos.quote_entry_native, 0);
|
||||||
assert_eq!(pos.quote_running_native, -150);
|
assert_eq!(pos.quote_running_native, -150);
|
||||||
assert_eq!(pos.avg_entry_price(), I80F48::from(0)); // Entry price zero when no position
|
assert_eq!(pos.avg_entry_price(), I80F48::from(0)); // Entry price zero when no position
|
||||||
|
@ -527,7 +542,7 @@ mod tests {
|
||||||
let mut market = create_perp_market();
|
let mut market = create_perp_market();
|
||||||
let mut pos = create_perp_position(10, -100, -100);
|
let mut pos = create_perp_position(10, -100, -100);
|
||||||
// Go short 15 @ 20
|
// Go short 15 @ 20
|
||||||
pos.change_base_and_entry_positions(&mut market, -15, 300);
|
pos.change_base_and_quote_positions(&mut market, -15, I80F48::from(300));
|
||||||
assert_eq!(pos.quote_entry_native, 100);
|
assert_eq!(pos.quote_entry_native, 100);
|
||||||
assert_eq!(pos.quote_running_native, 200);
|
assert_eq!(pos.quote_running_native, 200);
|
||||||
assert_eq!(pos.avg_entry_price(), I80F48::from(20)); // Entry price zero when no position
|
assert_eq!(pos.avg_entry_price(), I80F48::from(20)); // Entry price zero when no position
|
||||||
|
@ -538,7 +553,7 @@ mod tests {
|
||||||
let mut market = create_perp_market();
|
let mut market = create_perp_market();
|
||||||
let mut pos = create_perp_position(-10, 100, 100);
|
let mut pos = create_perp_position(-10, 100, 100);
|
||||||
// Go short 15 @ 20
|
// Go short 15 @ 20
|
||||||
pos.change_base_and_entry_positions(&mut market, 15, -300);
|
pos.change_base_and_quote_positions(&mut market, 15, I80F48::from(-300));
|
||||||
assert_eq!(pos.quote_entry_native, -100);
|
assert_eq!(pos.quote_entry_native, -100);
|
||||||
assert_eq!(pos.quote_running_native, -200);
|
assert_eq!(pos.quote_running_native, -200);
|
||||||
assert_eq!(pos.avg_entry_price(), I80F48::from(20)); // Entry price zero when no position
|
assert_eq!(pos.avg_entry_price(), I80F48::from(20)); // Entry price zero when no position
|
||||||
|
@ -549,9 +564,9 @@ mod tests {
|
||||||
let mut market = create_perp_market();
|
let mut market = create_perp_market();
|
||||||
let mut pos = create_perp_position(0, 0, 0);
|
let mut pos = create_perp_position(0, 0, 0);
|
||||||
// Buy 11 @ 10,000
|
// Buy 11 @ 10,000
|
||||||
pos.change_base_and_entry_positions(&mut market, 11, -11 * 10_000);
|
pos.change_base_and_quote_positions(&mut market, 11, I80F48::from(-11 * 10_000));
|
||||||
// Sell 1 @ 12,000
|
// Sell 1 @ 12,000
|
||||||
pos.change_base_and_entry_positions(&mut market, -1, 12_000);
|
pos.change_base_and_quote_positions(&mut market, -1, I80F48::from(12_000));
|
||||||
assert_eq!(pos.quote_entry_native, -10 * 10_000);
|
assert_eq!(pos.quote_entry_native, -10 * 10_000);
|
||||||
assert_eq!(pos.quote_running_native, -98_000);
|
assert_eq!(pos.quote_running_native, -98_000);
|
||||||
assert_eq!(pos.base_position_lots, 10);
|
assert_eq!(pos.base_position_lots, 10);
|
||||||
|
@ -574,7 +589,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
// Apply all of the trades going forward
|
// Apply all of the trades going forward
|
||||||
trades.iter().for_each(|[qty, quote]| {
|
trades.iter().for_each(|[qty, quote]| {
|
||||||
pos.change_base_and_entry_positions(&mut market, *qty, *quote);
|
pos.change_base_and_quote_positions(&mut market, *qty, I80F48::from(*quote));
|
||||||
});
|
});
|
||||||
// base_position should be sum of all base quantities
|
// base_position should be sum of all base quantities
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -583,7 +598,7 @@ mod tests {
|
||||||
);
|
);
|
||||||
// Reverse out all the trades
|
// Reverse out all the trades
|
||||||
trades.iter().for_each(|[qty, quote]| {
|
trades.iter().for_each(|[qty, quote]| {
|
||||||
pos.change_base_and_entry_positions(&mut market, -*qty, -*quote);
|
pos.change_base_and_quote_positions(&mut market, -*qty, I80F48::from(-*quote));
|
||||||
});
|
});
|
||||||
// base position should be 0
|
// base position should be 0
|
||||||
assert_eq!(pos.base_position_lots, 0);
|
assert_eq!(pos.base_position_lots, 0);
|
||||||
|
|
|
@ -468,7 +468,7 @@ fn apply_fees(
|
||||||
let perp_account = mango_account
|
let perp_account = mango_account
|
||||||
.ensure_perp_position(market.perp_market_index)?
|
.ensure_perp_position(market.perp_market_index)?
|
||||||
.0;
|
.0;
|
||||||
perp_account.quote_position_native -= taker_fees;
|
perp_account.change_quote_position(-taker_fees);
|
||||||
market.fees_accrued += taker_fees + maker_fees;
|
market.fees_accrued += taker_fees + maker_fees;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -247,11 +247,11 @@ mod tests {
|
||||||
assert_eq!(maker.perp_position_by_raw_index(0).asks_base_lots, 0);
|
assert_eq!(maker.perp_position_by_raw_index(0).asks_base_lots, 0);
|
||||||
assert_eq!(maker.perp_position_by_raw_index(0).taker_base_lots, 0);
|
assert_eq!(maker.perp_position_by_raw_index(0).taker_base_lots, 0);
|
||||||
assert_eq!(maker.perp_position_by_raw_index(0).taker_quote_lots, 0);
|
assert_eq!(maker.perp_position_by_raw_index(0).taker_quote_lots, 0);
|
||||||
assert_eq!(maker.perp_position_by_raw_index(0).base_position_lots, 0);
|
assert_eq!(maker.perp_position_by_raw_index(0).base_position_lots(), 0);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
maker
|
maker
|
||||||
.perp_position_by_raw_index(0)
|
.perp_position_by_raw_index(0)
|
||||||
.quote_position_native
|
.quote_position_native()
|
||||||
.to_num::<u32>(),
|
.to_num::<u32>(),
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
|
@ -305,9 +305,9 @@ mod tests {
|
||||||
taker.perp_position_by_raw_index(0).taker_quote_lots,
|
taker.perp_position_by_raw_index(0).taker_quote_lots,
|
||||||
match_quantity * price
|
match_quantity * price
|
||||||
);
|
);
|
||||||
assert_eq!(taker.perp_position_by_raw_index(0).base_position_lots, 0);
|
assert_eq!(taker.perp_position_by_raw_index(0).base_position_lots(), 0);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
taker.perp_position_by_raw_index(0).quote_position_native,
|
taker.perp_position_by_raw_index(0).quote_position_native(),
|
||||||
-match_quote * market.taker_fee
|
-match_quote * market.taker_fee
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -343,11 +343,11 @@ mod tests {
|
||||||
assert_eq!(maker.perp_position_by_raw_index(0).taker_base_lots, 0);
|
assert_eq!(maker.perp_position_by_raw_index(0).taker_base_lots, 0);
|
||||||
assert_eq!(maker.perp_position_by_raw_index(0).taker_quote_lots, 0);
|
assert_eq!(maker.perp_position_by_raw_index(0).taker_quote_lots, 0);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
maker.perp_position_by_raw_index(0).base_position_lots,
|
maker.perp_position_by_raw_index(0).base_position_lots(),
|
||||||
match_quantity
|
match_quantity
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
maker.perp_position_by_raw_index(0).quote_position_native,
|
maker.perp_position_by_raw_index(0).quote_position_native(),
|
||||||
-match_quote - match_quote * market.maker_fee
|
-match_quote - match_quote * market.maker_fee
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -356,11 +356,11 @@ mod tests {
|
||||||
assert_eq!(taker.perp_position_by_raw_index(0).taker_base_lots, 0);
|
assert_eq!(taker.perp_position_by_raw_index(0).taker_base_lots, 0);
|
||||||
assert_eq!(taker.perp_position_by_raw_index(0).taker_quote_lots, 0);
|
assert_eq!(taker.perp_position_by_raw_index(0).taker_quote_lots, 0);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
taker.perp_position_by_raw_index(0).base_position_lots,
|
taker.perp_position_by_raw_index(0).base_position_lots(),
|
||||||
-match_quantity
|
-match_quantity
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
taker.perp_position_by_raw_index(0).quote_position_native,
|
taker.perp_position_by_raw_index(0).quote_position_native(),
|
||||||
match_quote - match_quote * market.taker_fee
|
match_quote - match_quote * market.taker_fee
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,8 +88,8 @@ pub fn get_pnl_native(
|
||||||
) -> I80F48 {
|
) -> I80F48 {
|
||||||
let contract_size = perp_market.base_lot_size;
|
let contract_size = perp_market.base_lot_size;
|
||||||
let new_quote_pos =
|
let new_quote_pos =
|
||||||
I80F48::from_num(-perp_position.base_position_lots * contract_size) * oracle_price;
|
I80F48::from_num(-perp_position.base_position_lots() * contract_size) * oracle_price;
|
||||||
perp_position.quote_position_native - new_quote_pos
|
perp_position.quote_position_native() - new_quote_pos
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn assert_mango_error<T>(
|
pub fn assert_mango_error<T>(
|
||||||
|
|
|
@ -341,12 +341,12 @@ async fn test_perp() -> Result<(), TransportError> {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let mango_account_0 = solana.get_account::<MangoAccount>(account_0).await;
|
let mango_account_0 = solana.get_account::<MangoAccount>(account_0).await;
|
||||||
assert_eq!(mango_account_0.perps[0].base_position_lots, 1);
|
assert_eq!(mango_account_0.perps[0].base_position_lots(), 1);
|
||||||
assert!(mango_account_0.perps[0].quote_position_native < -100.019);
|
assert!(mango_account_0.perps[0].quote_position_native() < -100.019);
|
||||||
|
|
||||||
let mango_account_1 = solana.get_account::<MangoAccount>(account_1).await;
|
let mango_account_1 = solana.get_account::<MangoAccount>(account_1).await;
|
||||||
assert_eq!(mango_account_1.perps[0].base_position_lots, -1);
|
assert_eq!(mango_account_1.perps[0].base_position_lots(), -1);
|
||||||
assert_eq!(mango_account_1.perps[0].quote_position_native, 100);
|
assert_eq!(mango_account_1.perps[0].quote_position_native(), 100);
|
||||||
|
|
||||||
send_tx(
|
send_tx(
|
||||||
solana,
|
solana,
|
||||||
|
|
|
@ -302,13 +302,13 @@ async fn test_perp_settle_pnl() -> Result<(), TransportError> {
|
||||||
let mango_account_0 = solana.get_account::<MangoAccount>(account_0).await;
|
let mango_account_0 = solana.get_account::<MangoAccount>(account_0).await;
|
||||||
let mango_account_1 = solana.get_account::<MangoAccount>(account_1).await;
|
let mango_account_1 = solana.get_account::<MangoAccount>(account_1).await;
|
||||||
|
|
||||||
assert_eq!(mango_account_0.perps[0].base_position_lots, 1);
|
assert_eq!(mango_account_0.perps[0].base_position_lots(), 1);
|
||||||
assert_eq!(mango_account_1.perps[0].base_position_lots, -1);
|
assert_eq!(mango_account_1.perps[0].base_position_lots(), -1);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
mango_account_0.perps[0].quote_position_native.round(),
|
mango_account_0.perps[0].quote_position_native().round(),
|
||||||
-100_020
|
-100_020
|
||||||
);
|
);
|
||||||
assert_eq!(mango_account_1.perps[0].quote_position_native, 100_000);
|
assert_eq!(mango_account_1.perps[0].quote_position_native(), 100_000);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bank must be valid for quote currency
|
// Bank must be valid for quote currency
|
||||||
|
@ -545,21 +545,23 @@ async fn test_perp_settle_pnl() -> Result<(), TransportError> {
|
||||||
let mango_account_1 = solana.get_account::<MangoAccount>(account_1).await;
|
let mango_account_1 = solana.get_account::<MangoAccount>(account_1).await;
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
mango_account_0.perps[0].base_position_lots, 1,
|
mango_account_0.perps[0].base_position_lots(),
|
||||||
|
1,
|
||||||
"base position unchanged for account 0"
|
"base position unchanged for account 0"
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
mango_account_1.perps[0].base_position_lots, -1,
|
mango_account_1.perps[0].base_position_lots(),
|
||||||
|
-1,
|
||||||
"base position unchanged for account 1"
|
"base position unchanged for account 1"
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
mango_account_0.perps[0].quote_position_native.round(),
|
mango_account_0.perps[0].quote_position_native().round(),
|
||||||
I80F48::from(-100_020) - partial_settle_amount,
|
I80F48::from(-100_020) - partial_settle_amount,
|
||||||
"quote position reduced for profitable position by max_settle_amount"
|
"quote position reduced for profitable position by max_settle_amount"
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
mango_account_1.perps[0].quote_position_native.round(),
|
mango_account_1.perps[0].quote_position_native().round(),
|
||||||
I80F48::from(100_000) + partial_settle_amount,
|
I80F48::from(100_000) + partial_settle_amount,
|
||||||
"quote position increased for losing position by opposite of first account"
|
"quote position increased for losing position by opposite of first account"
|
||||||
);
|
);
|
||||||
|
@ -607,21 +609,23 @@ async fn test_perp_settle_pnl() -> Result<(), TransportError> {
|
||||||
let mango_account_1 = solana.get_account::<MangoAccount>(account_1).await;
|
let mango_account_1 = solana.get_account::<MangoAccount>(account_1).await;
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
mango_account_0.perps[0].base_position_lots, 1,
|
mango_account_0.perps[0].base_position_lots(),
|
||||||
|
1,
|
||||||
"base position unchanged for account 0"
|
"base position unchanged for account 0"
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
mango_account_1.perps[0].base_position_lots, -1,
|
mango_account_1.perps[0].base_position_lots(),
|
||||||
|
-1,
|
||||||
"base position unchanged for account 1"
|
"base position unchanged for account 1"
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
mango_account_0.perps[0].quote_position_native.round(),
|
mango_account_0.perps[0].quote_position_native().round(),
|
||||||
I80F48::from(-100_020) - expected_pnl_0,
|
I80F48::from(-100_020) - expected_pnl_0,
|
||||||
"quote position reduced for profitable position"
|
"quote position reduced for profitable position"
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
mango_account_1.perps[0].quote_position_native.round(),
|
mango_account_1.perps[0].quote_position_native().round(),
|
||||||
I80F48::from(100_000) + expected_pnl_0,
|
I80F48::from(100_000) + expected_pnl_0,
|
||||||
"quote position increased for losing position by opposite of first account"
|
"quote position increased for losing position by opposite of first account"
|
||||||
);
|
);
|
||||||
|
@ -700,21 +704,23 @@ async fn test_perp_settle_pnl() -> Result<(), TransportError> {
|
||||||
let mango_account_1 = solana.get_account::<MangoAccount>(account_1).await;
|
let mango_account_1 = solana.get_account::<MangoAccount>(account_1).await;
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
mango_account_0.perps[0].base_position_lots, 1,
|
mango_account_0.perps[0].base_position_lots(),
|
||||||
|
1,
|
||||||
"base position unchanged for account 0"
|
"base position unchanged for account 0"
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
mango_account_1.perps[0].base_position_lots, -1,
|
mango_account_1.perps[0].base_position_lots(),
|
||||||
|
-1,
|
||||||
"base position unchanged for account 1"
|
"base position unchanged for account 1"
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
mango_account_0.perps[0].quote_position_native.round(),
|
mango_account_0.perps[0].quote_position_native().round(),
|
||||||
I80F48::from(-100_500) + expected_pnl_1,
|
I80F48::from(-100_500) + expected_pnl_1,
|
||||||
"quote position increased for losing position"
|
"quote position increased for losing position"
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
mango_account_1.perps[0].quote_position_native.round(),
|
mango_account_1.perps[0].quote_position_native().round(),
|
||||||
I80F48::from(100_480) - expected_pnl_1,
|
I80F48::from(100_480) - expected_pnl_1,
|
||||||
"quote position reduced for losing position by opposite of first account"
|
"quote position reduced for losing position by opposite of first account"
|
||||||
);
|
);
|
||||||
|
|
|
@ -299,15 +299,15 @@ async fn test_perp_settle_fees() -> Result<(), TransportError> {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let mango_account_0 = solana.get_account::<MangoAccount>(account_0).await;
|
let mango_account_0 = solana.get_account::<MangoAccount>(account_0).await;
|
||||||
assert_eq!(mango_account_0.perps[0].base_position_lots, 1);
|
assert_eq!(mango_account_0.perps[0].base_position_lots(), 1);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
mango_account_0.perps[0].quote_position_native.round(),
|
mango_account_0.perps[0].quote_position_native().round(),
|
||||||
-100_020
|
-100_020
|
||||||
);
|
);
|
||||||
|
|
||||||
let mango_account_1 = solana.get_account::<MangoAccount>(account_1).await;
|
let mango_account_1 = solana.get_account::<MangoAccount>(account_1).await;
|
||||||
assert_eq!(mango_account_1.perps[0].base_position_lots, -1);
|
assert_eq!(mango_account_1.perps[0].base_position_lots(), -1);
|
||||||
assert_eq!(mango_account_1.perps[0].quote_position_native, 100_000);
|
assert_eq!(mango_account_1.perps[0].quote_position_native(), 100_000);
|
||||||
|
|
||||||
// Bank must be valid for quote currency
|
// Bank must be valid for quote currency
|
||||||
let result = send_tx(
|
let result = send_tx(
|
||||||
|
@ -528,12 +528,13 @@ async fn test_perp_settle_fees() -> Result<(), TransportError> {
|
||||||
let perp_market = solana.get_account::<PerpMarket>(perp_market).await;
|
let perp_market = solana.get_account::<PerpMarket>(perp_market).await;
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
mango_account_1.perps[0].base_position_lots, -1,
|
mango_account_1.perps[0].base_position_lots(),
|
||||||
|
-1,
|
||||||
"base position unchanged for account 1"
|
"base position unchanged for account 1"
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
mango_account_1.perps[0].quote_position_native.round(),
|
mango_account_1.perps[0].quote_position_native().round(),
|
||||||
I80F48::from(100_000) + partial_settle_amount,
|
I80F48::from(100_000) + partial_settle_amount,
|
||||||
"quote position increased for losing position by fee settle amount"
|
"quote position increased for losing position by fee settle amount"
|
||||||
);
|
);
|
||||||
|
@ -582,12 +583,13 @@ async fn test_perp_settle_fees() -> Result<(), TransportError> {
|
||||||
let perp_market = solana.get_account::<PerpMarket>(perp_market).await;
|
let perp_market = solana.get_account::<PerpMarket>(perp_market).await;
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
mango_account_1.perps[0].base_position_lots, -1,
|
mango_account_1.perps[0].base_position_lots(),
|
||||||
|
-1,
|
||||||
"base position unchanged for account 1"
|
"base position unchanged for account 1"
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
mango_account_1.perps[0].quote_position_native.round(),
|
mango_account_1.perps[0].quote_position_native().round(),
|
||||||
I80F48::from(100_000) + initial_fees,
|
I80F48::from(100_000) + initial_fees,
|
||||||
"quote position increased for losing position by fees settled"
|
"quote position increased for losing position by fees settled"
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in New Issue