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_capped: 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_capped {
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
76 changes: 64 additions & 12 deletions programs/drift/src/state/perp_market.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use anchor_lang::prelude::*;

use crate::state::state::{State, ValidityGuardRails};
use std::cmp::max;
use std::u128;

use crate::controller::position::PositionDirection;
use crate::error::{DriftResult, ErrorCode};
Expand All @@ -22,6 +23,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 @@ -438,18 +440,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 +462,63 @@ impl PerpMarket {
MarginRequirementType::Maintenance => margin_ratio_maintenance,
};

let pre_size_adj_margin_ratio = if is_high_leverage_user {
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, // use hlm maintenance to avoid changing liq prices
}
} else {
default_margin_ratio
};

let cap_size_premium =
!is_high_leverage_user || margin_type == MarginRequirementType::Maintenance;
let size_adj_margin_ratio = calculate_size_premium_liability_weight(
size,
self.imf_factor,
default_margin_ratio,
pre_size_adj_margin_ratio,
MARGIN_PRECISION_U128,
cap_size_premium,
)?;

let margin_ratio = default_margin_ratio.max(size_adj_margin_ratio);
let margin_ratio = if cap_size_premium {
default_margin_ratio.max(size_adj_margin_ratio)
} else {
if size_adj_margin_ratio < pre_size_adj_margin_ratio {
let size_pct_discount_factor = PERCENTAGE_PRECISION.safe_sub(
((pre_size_adj_margin_ratio as u128)
.safe_sub(size_adj_margin_ratio as u128)?
.safe_mul(PERCENTAGE_PRECISION)?
.safe_div((pre_size_adj_margin_ratio / 5) as u128)?),
)?;

let hlm_margin_delta = pre_size_adj_margin_ratio
.safe_sub(default_margin_ratio)?
.max(1);

let hlm_margin_delta_proportion = get_proportion_u128(
hlm_margin_delta as u128,
size_pct_discount_factor,
PERCENTAGE_PRECISION,
)? as u32;

crate::dlog!(
hlm_margin_delta_proportion,
hlm_margin_delta,
size_pct_discount_factor
);
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(margin_ratio)
}
Expand Down
90 changes: 88 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,107 @@ 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