Skip to content

Commit 9260e68

Browse files
0xbigzcrispheaney
andauthored
program: initial-margin-hlm-imf change (#1969)
* program: initial-margin-hlm-imf change * working loop multiple base amounts test * isolated hlm func * reorg func so its easier to read * rename cap to bound * add asserts to swift test * add more max perp size tests * apply comments/feedback * add sdk * fix test and cargo fmt * CHANGELOG --------- Co-authored-by: Chris Heaney <[email protected]>
1 parent 37f0781 commit 9260e68

File tree

8 files changed

+514
-66
lines changed

8 files changed

+514
-66
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
### Features
1111

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

1416
### Breaking

programs/drift/src/math/margin.rs

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
use crate::error::DriftResult;
22
use crate::error::ErrorCode;
33
use crate::math::constants::{
4-
MARGIN_PRECISION_U128, MAX_POSITIVE_UPNL_FOR_INITIAL_MARGIN, PRICE_PRECISION,
5-
SPOT_IMF_PRECISION_U128, SPOT_WEIGHT_PRECISION, SPOT_WEIGHT_PRECISION_U128,
4+
MARGIN_PRECISION_U128, MAX_POSITIVE_UPNL_FOR_INITIAL_MARGIN, PERCENTAGE_PRECISION,
5+
PRICE_PRECISION, SPOT_IMF_PRECISION_U128, SPOT_WEIGHT_PRECISION, SPOT_WEIGHT_PRECISION_U128,
66
};
77
use crate::math::position::calculate_base_asset_value_and_pnl_with_oracle_price;
88

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

17-
use crate::math::spot_balance::{get_strict_token_value, get_token_value};
18-
17+
use crate::math::helpers::get_proportion_u128;
1918
use crate::math::safe_math::SafeMath;
19+
use crate::math::spot_balance::{get_strict_token_value, get_token_value};
2020
use crate::msg;
2121
use crate::state::margin_calculation::{MarginCalculation, MarginContext, MarketIdentifier};
2222
use crate::state::oracle::{OraclePriceData, StrictOraclePrice};
@@ -45,6 +45,7 @@ pub fn calculate_size_premium_liability_weight(
4545
imf_factor: u32,
4646
liability_weight: u32,
4747
precision: u128,
48+
is_bounded: bool,
4849
) -> DriftResult<u32> {
4950
if imf_factor == 0 {
5051
return Ok(liability_weight);
@@ -66,8 +67,46 @@ pub fn calculate_size_premium_liability_weight(
6667
)?
6768
.cast::<u32>()?;
6869

69-
let max_liability_weight = max(liability_weight, size_premium_liability_weight);
70-
Ok(max_liability_weight)
70+
if is_bounded {
71+
let max_liability_weight = max(liability_weight, size_premium_liability_weight);
72+
return Ok(max_liability_weight);
73+
}
74+
75+
Ok(size_premium_liability_weight)
76+
}
77+
78+
pub fn calc_high_leverage_mode_initial_margin_ratio_from_size(
79+
pre_size_adj_margin_ratio: u32,
80+
size_adj_margin_ratio: u32,
81+
default_margin_ratio: u32,
82+
) -> DriftResult<u32> {
83+
let result = if size_adj_margin_ratio < pre_size_adj_margin_ratio {
84+
let size_pct_discount_factor = PERCENTAGE_PRECISION.saturating_sub(
85+
(pre_size_adj_margin_ratio
86+
.cast::<u128>()?
87+
.safe_sub(size_adj_margin_ratio.cast::<u128>()?)?
88+
.safe_mul(PERCENTAGE_PRECISION)?
89+
.safe_div((pre_size_adj_margin_ratio.safe_div(5)?).cast::<u128>()?)?),
90+
);
91+
92+
let hlm_margin_delta = pre_size_adj_margin_ratio
93+
.saturating_sub(default_margin_ratio)
94+
.max(1);
95+
96+
let hlm_margin_delta_proportion = get_proportion_u128(
97+
hlm_margin_delta.cast()?,
98+
size_pct_discount_factor,
99+
PERCENTAGE_PRECISION,
100+
)?
101+
.cast::<u32>()?;
102+
hlm_margin_delta_proportion.safe_add(default_margin_ratio)?
103+
} else if size_adj_margin_ratio == pre_size_adj_margin_ratio {
104+
default_margin_ratio
105+
} else {
106+
size_adj_margin_ratio
107+
};
108+
109+
Ok(result)
71110
}
72111

73112
pub fn calculate_size_discount_asset_weight(

programs/drift/src/math/orders/tests.rs

Lines changed: 192 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1956,7 +1956,7 @@ mod calculate_max_perp_order_size {
19561956
use crate::state::perp_market_map::PerpMarketMap;
19571957
use crate::state::spot_market::{SpotBalanceType, SpotMarket};
19581958
use crate::state::spot_market_map::SpotMarketMap;
1959-
use crate::state::user::{Order, PerpPosition, SpotPosition, User};
1959+
use crate::state::user::{MarginMode, Order, PerpPosition, SpotPosition, User, UserStatus};
19601960
use crate::test_utils::get_pyth_price;
19611961
use crate::test_utils::*;
19621962
use crate::{
@@ -3160,6 +3160,191 @@ mod calculate_max_perp_order_size {
31603160
assert!(total_collateral.unsigned_abs() - margin_requirement < 100 * QUOTE_PRECISION);
31613161
}
31623162

3163+
#[test]
3164+
pub fn sol_perp_hlm_with_imf() {
3165+
let slot = 0_u64;
3166+
3167+
let mut oracle_price = get_pyth_price(100, 6);
3168+
let oracle_price_key =
3169+
Pubkey::from_str("J83w4HKfqxwcq3BEMMkPFSppX3gqekLyLJBexebFVkix").unwrap();
3170+
let pyth_program = crate::ids::pyth_program::id();
3171+
create_account_info!(
3172+
oracle_price,
3173+
&oracle_price_key,
3174+
&pyth_program,
3175+
oracle_account_info
3176+
);
3177+
let mut oracle_map = OracleMap::load_one(&oracle_account_info, slot, None).unwrap();
3178+
3179+
let mut market = PerpMarket {
3180+
amm: AMM {
3181+
base_asset_reserve: 100 * AMM_RESERVE_PRECISION,
3182+
quote_asset_reserve: 100 * AMM_RESERVE_PRECISION,
3183+
bid_base_asset_reserve: 100 * AMM_RESERVE_PRECISION,
3184+
bid_quote_asset_reserve: 100 * AMM_RESERVE_PRECISION,
3185+
ask_base_asset_reserve: 100 * AMM_RESERVE_PRECISION,
3186+
ask_quote_asset_reserve: 100 * AMM_RESERVE_PRECISION,
3187+
sqrt_k: 100 * AMM_RESERVE_PRECISION,
3188+
peg_multiplier: 100 * PEG_PRECISION,
3189+
max_slippage_ratio: 50,
3190+
max_fill_reserve_fraction: 100,
3191+
order_step_size: 1000,
3192+
order_tick_size: 1,
3193+
oracle: oracle_price_key,
3194+
base_spread: 0, // 1 basis point
3195+
historical_oracle_data: HistoricalOracleData {
3196+
last_oracle_price: (100 * PRICE_PRECISION) as i64,
3197+
last_oracle_price_twap: (100 * PRICE_PRECISION) as i64,
3198+
last_oracle_price_twap_5min: (100 * PRICE_PRECISION) as i64,
3199+
3200+
..HistoricalOracleData::default()
3201+
},
3202+
..AMM::default()
3203+
},
3204+
margin_ratio_initial: 1000,
3205+
margin_ratio_maintenance: 500,
3206+
high_leverage_margin_ratio_initial: 100,
3207+
high_leverage_margin_ratio_maintenance: 66,
3208+
imf_factor: 50,
3209+
status: MarketStatus::Active,
3210+
..PerpMarket::default_test()
3211+
};
3212+
market.amm.max_base_asset_reserve = u128::MAX;
3213+
market.amm.min_base_asset_reserve = 0;
3214+
3215+
create_anchor_account_info!(market, PerpMarket, market_account_info);
3216+
let market_map = PerpMarketMap::load_one(&market_account_info, true).unwrap();
3217+
3218+
let mut usdc_spot_market = SpotMarket {
3219+
market_index: 0,
3220+
oracle_source: OracleSource::QuoteAsset,
3221+
cumulative_deposit_interest: SPOT_CUMULATIVE_INTEREST_PRECISION,
3222+
decimals: 6,
3223+
initial_asset_weight: SPOT_WEIGHT_PRECISION,
3224+
maintenance_asset_weight: SPOT_WEIGHT_PRECISION,
3225+
deposit_balance: 10000 * SPOT_BALANCE_PRECISION,
3226+
liquidator_fee: 0,
3227+
historical_oracle_data: HistoricalOracleData {
3228+
last_oracle_price_twap: PRICE_PRECISION_I64,
3229+
last_oracle_price_twap_5min: PRICE_PRECISION_I64,
3230+
..HistoricalOracleData::default()
3231+
},
3232+
..SpotMarket::default()
3233+
};
3234+
create_anchor_account_info!(usdc_spot_market, SpotMarket, usdc_spot_market_account_info);
3235+
let spot_market_account_infos = Vec::from([&usdc_spot_market_account_info]);
3236+
let spot_market_map =
3237+
SpotMarketMap::load_multiple(spot_market_account_infos, true).unwrap();
3238+
3239+
let mut spot_positions = [SpotPosition::default(); 8];
3240+
spot_positions[0] = SpotPosition {
3241+
market_index: 0,
3242+
balance_type: SpotBalanceType::Deposit,
3243+
scaled_balance: 10000 * SPOT_BALANCE_PRECISION_U64,
3244+
..SpotPosition::default()
3245+
};
3246+
let mut user = User {
3247+
orders: [Order::default(); 32],
3248+
perp_positions: get_positions(PerpPosition {
3249+
market_index: 0,
3250+
..PerpPosition::default()
3251+
}),
3252+
spot_positions,
3253+
margin_mode: MarginMode::HighLeverage,
3254+
..User::default()
3255+
};
3256+
3257+
let max_order_size = calculate_max_perp_order_size(
3258+
&user,
3259+
0,
3260+
0,
3261+
PositionDirection::Short,
3262+
&market_map,
3263+
&spot_market_map,
3264+
&mut oracle_map,
3265+
)
3266+
.unwrap();
3267+
assert_eq!(max_order_size, 4098356557000); // 4098
3268+
3269+
let mut spot_positions = [SpotPosition::default(); 8];
3270+
spot_positions[0] = SpotPosition {
3271+
market_index: 0,
3272+
balance_type: SpotBalanceType::Deposit,
3273+
scaled_balance: 100 * SPOT_BALANCE_PRECISION_U64,
3274+
..SpotPosition::default()
3275+
};
3276+
let mut user = User {
3277+
orders: [Order::default(); 32],
3278+
perp_positions: get_positions(PerpPosition {
3279+
market_index: 0,
3280+
..PerpPosition::default()
3281+
}),
3282+
spot_positions,
3283+
margin_mode: MarginMode::HighLeverage,
3284+
..User::default()
3285+
};
3286+
3287+
let max_order_size = calculate_max_perp_order_size(
3288+
&user,
3289+
0,
3290+
0,
3291+
PositionDirection::Short,
3292+
&market_map,
3293+
&spot_market_map,
3294+
&mut oracle_map,
3295+
)
3296+
.unwrap();
3297+
assert_eq!(max_order_size, 84737288000); // 84
3298+
3299+
let mut spot_positions = [SpotPosition::default(); 8];
3300+
spot_positions[0] = SpotPosition {
3301+
market_index: 0,
3302+
balance_type: SpotBalanceType::Deposit,
3303+
scaled_balance: 10 * SPOT_BALANCE_PRECISION_U64,
3304+
..SpotPosition::default()
3305+
};
3306+
let mut user = User {
3307+
orders: [Order::default(); 32],
3308+
perp_positions: get_positions(PerpPosition {
3309+
market_index: 0,
3310+
..PerpPosition::default()
3311+
}),
3312+
spot_positions,
3313+
margin_mode: MarginMode::HighLeverage,
3314+
..User::default()
3315+
};
3316+
3317+
let max_order_size = calculate_max_perp_order_size(
3318+
&user,
3319+
0,
3320+
0,
3321+
PositionDirection::Short,
3322+
&market_map,
3323+
&spot_market_map,
3324+
&mut oracle_map,
3325+
)
3326+
.unwrap();
3327+
assert_eq!(max_order_size, 9605769000); // 9.6
3328+
3329+
user.perp_positions[0].open_orders = 1;
3330+
user.perp_positions[0].open_asks = -(max_order_size as i64);
3331+
3332+
let MarginCalculation {
3333+
margin_requirement,
3334+
total_collateral,
3335+
..
3336+
} = calculate_margin_requirement_and_total_collateral_and_liability_info(
3337+
&user,
3338+
&market_map,
3339+
&spot_market_map,
3340+
&mut oracle_map,
3341+
MarginContext::standard(MarginRequirementType::Initial).strict(true),
3342+
)
3343+
.unwrap();
3344+
3345+
assert!(total_collateral.unsigned_abs() - margin_requirement < QUOTE_PRECISION);
3346+
}
3347+
31633348
#[test]
31643349
pub fn swift_failure() {
31653350
let clock_slot = 0_u64;
@@ -3385,6 +3570,8 @@ mod calculate_max_perp_order_size {
33853570
)
33863571
.unwrap();
33873572

3573+
assert_eq!(max_order_size, 1600000);
3574+
33883575
user.perp_positions[0].open_orders += 1;
33893576
user.perp_positions[0].open_bids += max_order_size as i64;
33903577

@@ -3401,7 +3588,10 @@ mod calculate_max_perp_order_size {
34013588
)
34023589
.unwrap();
34033590

3404-
assert!(total_collateral.unsigned_abs() - margin_requirement < QUOTE_PRECISION);
3591+
assert_eq!(total_collateral.unsigned_abs(), 2199358529); // ~$2200
3592+
assert_eq!(margin_requirement, 2186678676);
3593+
3594+
assert!(total_collateral.unsigned_abs() - margin_requirement < 13 * QUOTE_PRECISION);
34053595
}
34063596
}
34073597

0 commit comments

Comments
 (0)