Skip to content
9 changes: 7 additions & 2 deletions programs/drift/src/math/margin.rs
Original file line number Diff line number Diff line change
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,12 @@ 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 calculate_size_discount_asset_weight(
Expand Down
5 changes: 5 additions & 0 deletions programs/drift/src/math/orders/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3385,6 +3385,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,6 +3403,9 @@ mod calculate_max_perp_order_size {
)
.unwrap();

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

assert!(total_collateral.unsigned_abs() - margin_requirement < QUOTE_PRECISION);
}
}
Expand Down
99 changes: 82 additions & 17 deletions programs/drift/src/state/perp_market.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ use crate::math::constants::{
PERCENTAGE_PRECISION_I64, PERCENTAGE_PRECISION_U64, PRICE_PRECISION, PRICE_PRECISION_I128,
SPOT_WEIGHT_PRECISION, TWENTY_FOUR_HOUR,
};
use crate::math::helpers::get_proportion_u128;
use crate::math::margin::{
calculate_size_discount_asset_weight, calculate_size_premium_liability_weight,
MarginRequirementType,
Expand Down Expand Up @@ -239,6 +240,39 @@ pub struct PerpMarket {
pub padding: [u8; 24],
}

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.safe_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 + default_margin_ratio
} else if size_adj_margin_ratio == pre_size_adj_margin_ratio {
default_margin_ratio
} else {
size_adj_margin_ratio
};

Ok(result)
}

impl Default for PerpMarket {
fn default() -> Self {
PerpMarket {
Expand Down Expand Up @@ -438,18 +472,19 @@ impl PerpMarket {
user_high_leverage_mode: bool,
) -> DriftResult<u32> {
if self.status == MarketStatus::Settlement {
return Ok(0); // no liability weight on size
return Ok(0);
}

let (margin_ratio_initial, margin_ratio_maintenance) =
if user_high_leverage_mode && self.is_high_leverage_mode_enabled() {
(
self.high_leverage_margin_ratio_initial.cast::<u32>()?,
self.high_leverage_margin_ratio_maintenance.cast::<u32>()?,
)
} else {
(self.margin_ratio_initial, self.margin_ratio_maintenance)
};
let is_high_leverage_user = user_high_leverage_mode && self.is_high_leverage_mode_enabled();

let (margin_ratio_initial, margin_ratio_maintenance) = if is_high_leverage_user {
(
self.high_leverage_margin_ratio_initial.cast::<u32>()?,
self.high_leverage_margin_ratio_maintenance.cast::<u32>()?,
)
} else {
(self.margin_ratio_initial, self.margin_ratio_maintenance)
};

let default_margin_ratio = match margin_type {
MarginRequirementType::Initial => margin_ratio_initial,
Expand All @@ -459,14 +494,44 @@ impl PerpMarket {
MarginRequirementType::Maintenance => margin_ratio_maintenance,
};

let size_adj_margin_ratio = calculate_size_premium_liability_weight(
size,
self.imf_factor,
default_margin_ratio,
MARGIN_PRECISION_U128,
)?;
let margin_ratio = if is_high_leverage_user {
// use HLM maintenance margin but ordinary mode initial/fill margin for size adj calculation
let pre_size_adj_margin_ratio = match margin_type {
MarginRequirementType::Initial => self.margin_ratio_initial,
MarginRequirementType::Fill => {
self.margin_ratio_initial
.safe_add(self.margin_ratio_maintenance)?
/ 2
}
MarginRequirementType::Maintenance => margin_ratio_maintenance,
};

let bound_liability_weight = margin_type == MarginRequirementType::Maintenance;

let size_adj_margin_ratio = calculate_size_premium_liability_weight(
size,
self.imf_factor,
pre_size_adj_margin_ratio,
MARGIN_PRECISION_U128,
bound_liability_weight,
)?;

let margin_ratio = default_margin_ratio.max(size_adj_margin_ratio);
_calc_high_leverage_mode_initial_margin_ratio_from_size(
pre_size_adj_margin_ratio,
size_adj_margin_ratio,
default_margin_ratio,
)?
} else {
let size_adj_margin_ratio = calculate_size_premium_liability_weight(
size,
self.imf_factor,
default_margin_ratio,
MARGIN_PRECISION_U128,
true,
)?;

default_margin_ratio.max(size_adj_margin_ratio)
};

Ok(margin_ratio)
}
Expand Down
102 changes: 100 additions & 2 deletions programs/drift/src/state/perp_market/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,21 +84,119 @@ mod get_margin_ratio {
let margin_ratio_initial = perp_market
.get_margin_ratio(BASE_PRECISION, MarginRequirementType::Initial, true)
.unwrap();
assert_eq!(margin_ratio_initial, MARGIN_PRECISION / 50);

let margin_ratio_maintenance = perp_market
.get_margin_ratio(BASE_PRECISION, MarginRequirementType::Maintenance, true)
.unwrap();

assert_eq!(margin_ratio_maintenance, MARGIN_PRECISION / 100);

let margin_ratio_fill = perp_market
.get_margin_ratio(BASE_PRECISION, MarginRequirementType::Fill, true)
.unwrap();

assert_eq!(margin_ratio_initial, MARGIN_PRECISION / 50);
assert_eq!(
margin_ratio_fill,
(MARGIN_PRECISION / 50 + MARGIN_PRECISION / 100) / 2
);
assert_eq!(margin_ratio_maintenance, MARGIN_PRECISION / 100);
}

#[test]
fn smart_hlm_size_loop() {
let perp_market = PerpMarket {
margin_ratio_initial: MARGIN_PRECISION / 20,
margin_ratio_maintenance: MARGIN_PRECISION / 33,
high_leverage_margin_ratio_initial: (MARGIN_PRECISION / 100) as u16,
high_leverage_margin_ratio_maintenance: (MARGIN_PRECISION / 151) as u16,
imf_factor: 50,
..PerpMarket::default()
};

let mut cnt = 0;

for i in 1..1_000 {
let hlm_margin_ratio_initial = perp_market
.get_margin_ratio(
BASE_PRECISION * i * 1000,
MarginRequirementType::Initial,
true,
)
.unwrap();

let margin_ratio_initial = perp_market
.get_margin_ratio(
BASE_PRECISION * i * 1000,
MarginRequirementType::Initial,
false,
)
.unwrap();

if margin_ratio_initial != perp_market.margin_ratio_initial {
// crate::msg!("{}", BASE_PRECISION * i);
assert_eq!(hlm_margin_ratio_initial, margin_ratio_initial);
cnt += 1;
}
}

assert_eq!(cnt, 959_196 / 1_000);
}

#[test]
fn smart_hlm_size() {
let perp_market = PerpMarket {
margin_ratio_initial: MARGIN_PRECISION / 10,
margin_ratio_maintenance: MARGIN_PRECISION / 20,
high_leverage_margin_ratio_initial: (MARGIN_PRECISION / 100) as u16,
high_leverage_margin_ratio_maintenance: (MARGIN_PRECISION / 200) as u16,
imf_factor: 50,
..PerpMarket::default()
};

let normal_margin_ratio_initial = perp_market
.get_margin_ratio(
BASE_PRECISION * 1000000,
MarginRequirementType::Initial,
false,
)
.unwrap();

assert_eq!(normal_margin_ratio_initial, 1300);

let hlm_margin_ratio_initial = perp_market
.get_margin_ratio(BASE_PRECISION / 10, MarginRequirementType::Initial, true)
.unwrap();

assert_eq!(
hlm_margin_ratio_initial,
perp_market.high_leverage_margin_ratio_initial as u32
);

let hlm_margin_ratio_initial_sized = perp_market
.get_margin_ratio(BASE_PRECISION * 3000, MarginRequirementType::Initial, true)
.unwrap();
assert_eq!(hlm_margin_ratio_initial_sized, 221);
assert!(
hlm_margin_ratio_initial_sized > perp_market.high_leverage_margin_ratio_initial as u32
);

let hlm_margin_ratio_maint = perp_market
.get_margin_ratio(
BASE_PRECISION * 3000,
MarginRequirementType::Maintenance,
true,
)
.unwrap();
assert_eq!(hlm_margin_ratio_maint, 67); // hardly changed

let hlm_margin_ratio_maint = perp_market
.get_margin_ratio(
BASE_PRECISION * 300000,
MarginRequirementType::Maintenance,
true,
)
.unwrap();
assert_eq!(hlm_margin_ratio_maint, 313); // changed more at large size
}
}

Expand Down
1 change: 1 addition & 0 deletions programs/drift/src/state/spot_market.rs
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,7 @@ impl SpotMarket {
self.imf_factor,
default_liability_weight,
SPOT_WEIGHT_PRECISION_U128,
true,
)?;

let liability_weight = size_based_liability_weight.max(default_liability_weight);
Expand Down
Loading