Skip to content
Merged
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
7 changes: 5 additions & 2 deletions programs/drift/src/math/orders.rs
Original file line number Diff line number Diff line change
Expand Up @@ -809,6 +809,7 @@ pub fn calculate_max_perp_order_size(
)?;

let user_custom_margin_ratio = user.max_margin_ratio;
let perp_position_margin_ratio = user.perp_positions[position_index].max_margin_ratio as u32;
let user_high_leverage_mode = user.is_high_leverage_mode(MarginRequirementType::Initial);

let free_collateral_before = total_collateral.safe_sub(margin_requirement.cast()?)?;
Expand Down Expand Up @@ -838,7 +839,8 @@ pub fn calculate_max_perp_order_size(
MarginRequirementType::Initial,
user_high_leverage_mode,
)?
.max(user_custom_margin_ratio);
.max(user_custom_margin_ratio)
.max(perp_position_margin_ratio);

let mut order_size_to_reduce_position = 0_u64;
let mut free_collateral_released = 0_i128;
Expand Down Expand Up @@ -915,7 +917,8 @@ pub fn calculate_max_perp_order_size(
MarginRequirementType::Initial,
user_high_leverage_mode,
)?
.max(user_custom_margin_ratio);
.max(user_custom_margin_ratio)
.max(perp_position_margin_ratio);

Ok((new_order_size, new_margin_ratio))
};
Expand Down
246 changes: 246 additions & 0 deletions programs/drift/src/math/orders/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2791,6 +2791,252 @@ mod calculate_max_perp_order_size {
assert!(total_collateral.unsigned_abs() - margin_requirement < 100 * QUOTE_PRECISION);
}

#[test]
pub fn sol_perp_5x_bid_perp_position_margin_ratio() {
let slot = 0_u64;

let mut oracle_price = get_pyth_price(100, 6);
let oracle_price_key =
Pubkey::from_str("J83w4HKfqxwcq3BEMMkPFSppX3gqekLyLJBexebFVkix").unwrap();
let pyth_program = crate::ids::pyth_program::id();
create_account_info!(
oracle_price,
&oracle_price_key,
&pyth_program,
oracle_account_info
);
let mut oracle_map = OracleMap::load_one(&oracle_account_info, slot, None).unwrap();

let mut market = PerpMarket {
amm: AMM {
base_asset_reserve: 100 * AMM_RESERVE_PRECISION,
quote_asset_reserve: 100 * AMM_RESERVE_PRECISION,
bid_base_asset_reserve: 100 * AMM_RESERVE_PRECISION,
bid_quote_asset_reserve: 100 * AMM_RESERVE_PRECISION,
ask_base_asset_reserve: 100 * AMM_RESERVE_PRECISION,
ask_quote_asset_reserve: 100 * AMM_RESERVE_PRECISION,
sqrt_k: 100 * AMM_RESERVE_PRECISION,
peg_multiplier: 100 * PEG_PRECISION,
max_slippage_ratio: 50,
max_fill_reserve_fraction: 100,
order_step_size: 1000,
order_tick_size: 1,
oracle: oracle_price_key,
base_spread: 0, // 1 basis point
historical_oracle_data: HistoricalOracleData {
last_oracle_price: (100 * PRICE_PRECISION) as i64,
last_oracle_price_twap: (100 * PRICE_PRECISION) as i64,
last_oracle_price_twap_5min: (100 * PRICE_PRECISION) as i64,

..HistoricalOracleData::default()
},
..AMM::default()
},
margin_ratio_initial: 2000,
margin_ratio_maintenance: 1000,
status: MarketStatus::Initialized,
..PerpMarket::default_test()
};
market.amm.max_base_asset_reserve = u128::MAX;
market.amm.min_base_asset_reserve = 0;

create_anchor_account_info!(market, PerpMarket, market_account_info);
let market_map = PerpMarketMap::load_one(&market_account_info, true).unwrap();

let mut usdc_spot_market = SpotMarket {
market_index: 0,
oracle_source: OracleSource::QuoteAsset,
cumulative_deposit_interest: SPOT_CUMULATIVE_INTEREST_PRECISION,
decimals: 6,
initial_asset_weight: SPOT_WEIGHT_PRECISION,
maintenance_asset_weight: SPOT_WEIGHT_PRECISION,
deposit_balance: 10000 * SPOT_BALANCE_PRECISION,
liquidator_fee: 0,
historical_oracle_data: HistoricalOracleData {
last_oracle_price_twap: PRICE_PRECISION_I64,
last_oracle_price_twap_5min: PRICE_PRECISION_I64,
..HistoricalOracleData::default()
},
..SpotMarket::default()
};
create_anchor_account_info!(usdc_spot_market, SpotMarket, usdc_spot_market_account_info);
let spot_market_account_infos = Vec::from([&usdc_spot_market_account_info]);
let spot_market_map =
SpotMarketMap::load_multiple(spot_market_account_infos, true).unwrap();

let mut spot_positions = [SpotPosition::default(); 8];
spot_positions[0] = SpotPosition {
market_index: 0,
balance_type: SpotBalanceType::Deposit,
scaled_balance: 10000 * SPOT_BALANCE_PRECISION_U64,
..SpotPosition::default()
};
let mut user = User {
orders: [Order::default(); 32],
perp_positions: get_positions(PerpPosition {
market_index: 0,
max_margin_ratio: 2 * MARGIN_PRECISION as u16,
..PerpPosition::default()
}),
spot_positions,
..User::default()
};

let max_order_size = calculate_max_perp_order_size(
&user,
0,
0,
PositionDirection::Long,
&market_map,
&spot_market_map,
&mut oracle_map,
)
.unwrap();

assert_eq!(max_order_size, 49999950000);

user.perp_positions[0].open_orders = 1;
user.perp_positions[0].open_bids = max_order_size as i64;

let MarginCalculation {
margin_requirement,
total_collateral,
..
} = calculate_margin_requirement_and_total_collateral_and_liability_info(
&user,
&market_map,
&spot_market_map,
&mut oracle_map,
MarginContext::standard(MarginRequirementType::Initial).strict(true),
)
.unwrap();

assert_eq!(total_collateral.unsigned_abs(), margin_requirement);
}

#[test]
pub fn sol_perp_5x_ask_perp_position_margin_ratio() {
let slot = 0_u64;

let mut oracle_price = get_pyth_price(100, 6);
let oracle_price_key =
Pubkey::from_str("J83w4HKfqxwcq3BEMMkPFSppX3gqekLyLJBexebFVkix").unwrap();
let pyth_program = crate::ids::pyth_program::id();
create_account_info!(
oracle_price,
&oracle_price_key,
&pyth_program,
oracle_account_info
);
let mut oracle_map = OracleMap::load_one(&oracle_account_info, slot, None).unwrap();

let mut market = PerpMarket {
amm: AMM {
base_asset_reserve: 100 * AMM_RESERVE_PRECISION,
quote_asset_reserve: 100 * AMM_RESERVE_PRECISION,
bid_base_asset_reserve: 100 * AMM_RESERVE_PRECISION,
bid_quote_asset_reserve: 100 * AMM_RESERVE_PRECISION,
ask_base_asset_reserve: 100 * AMM_RESERVE_PRECISION,
ask_quote_asset_reserve: 100 * AMM_RESERVE_PRECISION,
sqrt_k: 100 * AMM_RESERVE_PRECISION,
peg_multiplier: 100 * PEG_PRECISION,
max_slippage_ratio: 50,
max_fill_reserve_fraction: 100,
order_step_size: 1000,
order_tick_size: 1,
oracle: oracle_price_key,
base_spread: 0, // 1 basis point
historical_oracle_data: HistoricalOracleData {
last_oracle_price: (100 * PRICE_PRECISION) as i64,
last_oracle_price_twap: (100 * PRICE_PRECISION) as i64,
last_oracle_price_twap_5min: (100 * PRICE_PRECISION) as i64,

..HistoricalOracleData::default()
},
..AMM::default()
},
margin_ratio_initial: 2000,
margin_ratio_maintenance: 1000,
status: MarketStatus::Initialized,
..PerpMarket::default_test()
};
market.amm.max_base_asset_reserve = u128::MAX;
market.amm.min_base_asset_reserve = 0;

create_anchor_account_info!(market, PerpMarket, market_account_info);
let market_map = PerpMarketMap::load_one(&market_account_info, true).unwrap();

let mut usdc_spot_market = SpotMarket {
market_index: 0,
oracle_source: OracleSource::QuoteAsset,
cumulative_deposit_interest: SPOT_CUMULATIVE_INTEREST_PRECISION,
decimals: 6,
initial_asset_weight: SPOT_WEIGHT_PRECISION,
maintenance_asset_weight: SPOT_WEIGHT_PRECISION,
deposit_balance: 10000 * SPOT_BALANCE_PRECISION,
liquidator_fee: 0,
historical_oracle_data: HistoricalOracleData {
last_oracle_price_twap: PRICE_PRECISION_I64,
last_oracle_price_twap_5min: PRICE_PRECISION_I64,
..HistoricalOracleData::default()
},
..SpotMarket::default()
};
create_anchor_account_info!(usdc_spot_market, SpotMarket, usdc_spot_market_account_info);
let spot_market_account_infos = Vec::from([&usdc_spot_market_account_info]);
let spot_market_map =
SpotMarketMap::load_multiple(spot_market_account_infos, true).unwrap();

let mut spot_positions = [SpotPosition::default(); 8];
spot_positions[0] = SpotPosition {
market_index: 0,
balance_type: SpotBalanceType::Deposit,
scaled_balance: 10000 * SPOT_BALANCE_PRECISION_U64,
..SpotPosition::default()
};
let mut user = User {
orders: [Order::default(); 32],
perp_positions: get_positions(PerpPosition {
market_index: 0,
max_margin_ratio: 2 * MARGIN_PRECISION as u16,
..PerpPosition::default()
}),
spot_positions,
..User::default()
};

let max_order_size = calculate_max_perp_order_size(
&user,
0,
0,
PositionDirection::Short,
&market_map,
&spot_market_map,
&mut oracle_map,
)
.unwrap();

assert_eq!(max_order_size, 49999950000);

user.perp_positions[0].open_orders = 1;
user.perp_positions[0].open_asks = -(max_order_size as i64);

let MarginCalculation {
margin_requirement,
total_collateral,
..
} = calculate_margin_requirement_and_total_collateral_and_liability_info(
&user,
&market_map,
&spot_market_map,
&mut oracle_map,
MarginContext::standard(MarginRequirementType::Initial).strict(true),
)
.unwrap();

assert_eq!(total_collateral.unsigned_abs(), margin_requirement);
}

#[test]
pub fn sol_perp_10x_ask_with_imf() {
let slot = 0_u64;
Expand Down
Loading