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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Features

- program: make imf smoother between hlm and non hlm users ([#1969](https://github.com/drift-labs/protocol-v2/pull/1969))

### Fixes

### Breaking
Expand Down
51 changes: 45 additions & 6 deletions programs/drift/src/math/margin.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use crate::error::DriftResult;
use crate::error::ErrorCode;
use crate::math::constants::{
MARGIN_PRECISION_U128, MAX_POSITIVE_UPNL_FOR_INITIAL_MARGIN, PRICE_PRECISION,
SPOT_IMF_PRECISION_U128, SPOT_WEIGHT_PRECISION, SPOT_WEIGHT_PRECISION_U128,
MARGIN_PRECISION_U128, MAX_POSITIVE_UPNL_FOR_INITIAL_MARGIN, PERCENTAGE_PRECISION,
PRICE_PRECISION, SPOT_IMF_PRECISION_U128, SPOT_WEIGHT_PRECISION, SPOT_WEIGHT_PRECISION_U128,
};
use crate::math::position::calculate_base_asset_value_and_pnl_with_oracle_price;

Expand All @@ -14,9 +14,9 @@ use crate::math::casting::Cast;
use crate::math::funding::calculate_funding_payment;
use crate::math::oracle::{is_oracle_valid_for_action, DriftAction};

use crate::math::spot_balance::{get_strict_token_value, get_token_value};

use crate::math::helpers::get_proportion_u128;
use crate::math::safe_math::SafeMath;
use crate::math::spot_balance::{get_strict_token_value, get_token_value};
use crate::msg;
use crate::state::margin_calculation::{MarginCalculation, MarginContext, MarketIdentifier};
use crate::state::oracle::{OraclePriceData, StrictOraclePrice};
Expand Down Expand Up @@ -45,6 +45,7 @@ pub fn calculate_size_premium_liability_weight(
imf_factor: u32,
liability_weight: u32,
precision: u128,
is_bounded: bool,
) -> DriftResult<u32> {
if imf_factor == 0 {
return Ok(liability_weight);
Expand All @@ -66,8 +67,46 @@ pub fn calculate_size_premium_liability_weight(
)?
.cast::<u32>()?;

let max_liability_weight = max(liability_weight, size_premium_liability_weight);
Ok(max_liability_weight)
if is_bounded {
let max_liability_weight = max(liability_weight, size_premium_liability_weight);
return Ok(max_liability_weight);
}

Ok(size_premium_liability_weight)
}

pub fn calc_high_leverage_mode_initial_margin_ratio_from_size(
pre_size_adj_margin_ratio: u32,
size_adj_margin_ratio: u32,
default_margin_ratio: u32,
) -> DriftResult<u32> {
let result = if size_adj_margin_ratio < pre_size_adj_margin_ratio {
let size_pct_discount_factor = PERCENTAGE_PRECISION.saturating_sub(
(pre_size_adj_margin_ratio
.cast::<u128>()?
.safe_sub(size_adj_margin_ratio.cast::<u128>()?)?
.safe_mul(PERCENTAGE_PRECISION)?
.safe_div((pre_size_adj_margin_ratio.safe_div(5)?).cast::<u128>()?)?),
);

let hlm_margin_delta = pre_size_adj_margin_ratio
.saturating_sub(default_margin_ratio)
.max(1);

let hlm_margin_delta_proportion = get_proportion_u128(
hlm_margin_delta.cast()?,
size_pct_discount_factor,
PERCENTAGE_PRECISION,
)?
.cast::<u32>()?;
hlm_margin_delta_proportion.safe_add(default_margin_ratio)?
} else if size_adj_margin_ratio == pre_size_adj_margin_ratio {
default_margin_ratio
} else {
size_adj_margin_ratio
};

Ok(result)
}

pub fn calculate_size_discount_asset_weight(
Expand Down
194 changes: 192 additions & 2 deletions programs/drift/src/math/orders/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1956,7 +1956,7 @@ mod calculate_max_perp_order_size {
use crate::state::perp_market_map::PerpMarketMap;
use crate::state::spot_market::{SpotBalanceType, SpotMarket};
use crate::state::spot_market_map::SpotMarketMap;
use crate::state::user::{Order, PerpPosition, SpotPosition, User};
use crate::state::user::{MarginMode, Order, PerpPosition, SpotPosition, User, UserStatus};
use crate::test_utils::get_pyth_price;
use crate::test_utils::*;
use crate::{
Expand Down Expand Up @@ -3160,6 +3160,191 @@ mod calculate_max_perp_order_size {
assert!(total_collateral.unsigned_abs() - margin_requirement < 100 * QUOTE_PRECISION);
}

#[test]
pub fn sol_perp_hlm_with_imf() {
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: 1000,
margin_ratio_maintenance: 500,
high_leverage_margin_ratio_initial: 100,
high_leverage_margin_ratio_maintenance: 66,
imf_factor: 50,
status: MarketStatus::Active,
..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,
..PerpPosition::default()
}),
spot_positions,
margin_mode: MarginMode::HighLeverage,
..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, 4098356557000); // 4098

let mut spot_positions = [SpotPosition::default(); 8];
spot_positions[0] = SpotPosition {
market_index: 0,
balance_type: SpotBalanceType::Deposit,
scaled_balance: 100 * SPOT_BALANCE_PRECISION_U64,
..SpotPosition::default()
};
let mut user = User {
orders: [Order::default(); 32],
perp_positions: get_positions(PerpPosition {
market_index: 0,
..PerpPosition::default()
}),
spot_positions,
margin_mode: MarginMode::HighLeverage,
..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, 84737288000); // 84

let mut spot_positions = [SpotPosition::default(); 8];
spot_positions[0] = SpotPosition {
market_index: 0,
balance_type: SpotBalanceType::Deposit,
scaled_balance: 10 * SPOT_BALANCE_PRECISION_U64,
..SpotPosition::default()
};
let mut user = User {
orders: [Order::default(); 32],
perp_positions: get_positions(PerpPosition {
market_index: 0,
..PerpPosition::default()
}),
spot_positions,
margin_mode: MarginMode::HighLeverage,
..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, 9605769000); // 9.6

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!(total_collateral.unsigned_abs() - margin_requirement < QUOTE_PRECISION);
}

#[test]
pub fn swift_failure() {
let clock_slot = 0_u64;
Expand Down Expand Up @@ -3385,6 +3570,8 @@ mod calculate_max_perp_order_size {
)
.unwrap();

assert_eq!(max_order_size, 1600000);

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

Expand All @@ -3401,7 +3588,10 @@ mod calculate_max_perp_order_size {
)
.unwrap();

assert!(total_collateral.unsigned_abs() - margin_requirement < QUOTE_PRECISION);
assert_eq!(total_collateral.unsigned_abs(), 2199358529); // ~$2200
assert_eq!(margin_requirement, 2186678676);

assert!(total_collateral.unsigned_abs() - margin_requirement < 13 * QUOTE_PRECISION);
}
}

Expand Down
Loading
Loading