Skip to content

Commit b4fa440

Browse files
authored
ref price offset enhance (#1958)
* logic change * logic change + some tests * wrap tests * revert logic back * tests pass * add admin function + move field to amm + sdk math * remove msg * add additional tests and address comments * prettify
1 parent ec2731a commit b4fa440

File tree

12 files changed

+346
-54
lines changed

12 files changed

+346
-54
lines changed

programs/drift/src/controller/amm.rs

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -185,20 +185,33 @@ pub fn update_spreads(
185185
let max_ref_offset = market.amm.get_max_reference_price_offset()?;
186186

187187
let reference_price_offset = if max_ref_offset > 0 {
188-
let liquidity_ratio = amm_spread::calculate_inventory_liquidity_ratio(
189-
market.amm.base_asset_amount_with_amm,
190-
market.amm.base_asset_reserve,
191-
market.amm.min_base_asset_reserve,
192-
market.amm.max_base_asset_reserve,
193-
)?;
188+
let liquidity_ratio =
189+
amm_spread::calculate_inventory_liquidity_ratio_for_reference_price_offset(
190+
market.amm.base_asset_amount_with_amm,
191+
market.amm.base_asset_reserve,
192+
market.amm.min_base_asset_reserve,
193+
market.amm.max_base_asset_reserve,
194+
)?;
194195

195196
let signed_liquidity_ratio =
196197
liquidity_ratio.safe_mul(market.amm.get_protocol_owned_position()?.signum().cast()?)?;
197198

199+
let deadband_pct = market.amm.get_reference_price_offset_deadband_pct()?;
200+
let liquidity_fraction_after_deadband =
201+
if signed_liquidity_ratio.unsigned_abs() <= deadband_pct {
202+
0
203+
} else {
204+
signed_liquidity_ratio.safe_sub(
205+
deadband_pct
206+
.cast::<i128>()?
207+
.safe_mul(signed_liquidity_ratio.signum())?,
208+
)?
209+
};
210+
198211
amm_spread::calculate_reference_price_offset(
199212
reserve_price,
200213
market.amm.last_24h_avg_funding_rate,
201-
signed_liquidity_ratio,
214+
liquidity_fraction_after_deadband,
202215
market.amm.min_order_size,
203216
market
204217
.amm

programs/drift/src/controller/position/tests.rs

Lines changed: 25 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -843,7 +843,7 @@ fn amm_ref_price_offset_decay_logic() {
843843
.unwrap();
844844
assert_eq!(perp_market.amm.last_update_slot, clock_slot);
845845
assert_eq!(perp_market.amm.last_oracle_valid, true);
846-
assert_eq!(perp_market.amm.reference_price_offset, 7350);
846+
assert_eq!(perp_market.amm.reference_price_offset, 4458);
847847

848848
perp_market.amm.last_mark_price_twap_5min = (perp_market
849849
.amm
@@ -893,28 +893,28 @@ fn amm_ref_price_offset_decay_logic() {
893893
assert_eq!(
894894
offsets,
895895
[
896-
7140, 6930, 6720, 6510, 6300, 6090, 6070, 6050, 6030, 6010, 5800, 5590, 5380, 5170,
897-
4960, 4750, 4540, 4330, 4120, 3910, 3700, 3490, 3280, 3070, 2860, 2650, 2440, 2230,
898-
2020, 1810, 1620, 1449, 1296, 1158, 1034, 922, 821, 730, 648, 575, 509, 450, 396, 348,
899-
305, 266, 231, 199, 171, 145, 122, 101, 81, 61, 41, 21, 1, 0, 0, 0
896+
4248, 4038, 3828, 3618, 3408, 3198, 3178, 3158, 3138, 3118, 2908, 2698, 2488, 2278,
897+
2068, 1858, 1664, 1489, 1332, 1190, 1062, 947, 844, 751, 667, 592, 524, 463, 408, 359,
898+
315, 275, 239, 207, 178, 152, 128, 107, 87, 67, 47, 27, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0,
899+
0, 0, 0, 0, 0, 0, 0, 0
900900
]
901901
);
902902
assert_eq!(
903903
lspreads,
904904
[
905-
726, 726, 726, 726, 726, 726, 536, 536, 536, 536, 726, 726, 726, 726, 726, 726, 726,
906-
726, 726, 726, 726, 726, 726, 726, 726, 726, 726, 726, 726, 726, 706, 687, 669, 654,
907-
640, 628, 617, 607, 598, 589, 582, 575, 570, 564, 559, 555, 551, 548, 544, 542, 539,
908-
537, 536, 536, 536, 536, 536, 526, 526, 526
905+
726, 726, 726, 726, 726, 726, 536, 536, 536, 536, 726, 726, 726, 726, 726, 726, 710,
906+
691, 673, 658, 644, 631, 619, 609, 600, 591, 584, 577, 571, 565, 560, 556, 552, 548,
907+
545, 542, 540, 537, 536, 536, 536, 536, 536, 526, 526, 526, 526, 526, 526, 526, 526,
908+
526, 526, 526, 526, 526, 526, 526, 526, 526
909909
]
910910
);
911911
assert_eq!(
912912
sspreads,
913913
[
914-
7150, 6940, 6730, 6520, 6310, 6100, 6080, 6060, 6040, 6020, 5810, 5600, 5390, 5180,
915-
4970, 4760, 4550, 4340, 4130, 3920, 3710, 3500, 3290, 3080, 2870, 2660, 2450, 2240,
916-
2030, 1820, 1630, 1459, 1306, 1168, 1044, 932, 831, 740, 658, 585, 519, 460, 406, 358,
917-
315, 276, 241, 209, 181, 155, 132, 111, 91, 71, 51, 31, 11, 10, 10, 10
914+
4258, 4048, 3838, 3628, 3418, 3208, 3188, 3168, 3148, 3128, 2918, 2708, 2498, 2288,
915+
2078, 1868, 1674, 1499, 1342, 1200, 1072, 957, 854, 761, 677, 602, 534, 473, 418, 369,
916+
325, 285, 249, 217, 188, 162, 138, 117, 97, 77, 57, 37, 17, 10, 10, 10, 10, 10, 10, 10,
917+
10, 10, 10, 10, 10, 10, 10, 10, 10, 10
918918
]
919919
);
920920
}
@@ -1017,7 +1017,7 @@ fn amm_negative_ref_price_offset_decay_logic() {
10171017
.unwrap();
10181018
assert_eq!(perp_market.amm.last_update_slot, clock_slot);
10191019
assert_eq!(perp_market.amm.last_oracle_valid, true);
1020-
assert_eq!(perp_market.amm.reference_price_offset, 7350);
1020+
assert_eq!(perp_market.amm.reference_price_offset, 4458);
10211021

10221022
perp_market.amm.last_mark_price_twap_5min = (perp_market
10231023
.amm
@@ -1068,34 +1068,31 @@ fn amm_negative_ref_price_offset_decay_logic() {
10681068
assert_eq!(
10691069
offsets,
10701070
[
1071-
-7140, -6930, -6720, -6510, -6300, -6090, -6070, -6050, -6030, -6010, -5800, -5590,
1072-
-5380, -5170, -4960, -4750, -4540, -4330, -4120, -3910, -3700, -3490, -3280, -3070,
1073-
-2860, -2650, -2440, -2230, -2020, -1810, -1600, -1390, -1180, -970, -760, -550, -340,
1074-
-130, 0, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000,
1075-
10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000,
1076-
10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000,
1077-
10000, 10000, 10000, 10000, 10000, 10000
1071+
-4248, -4038, -3828, -3618, -3408, -3198, -3178, -3158, -3138, -3118, -2908, -2698,
1072+
-2488, -2278, -2068, -1858, -1648, -1438, -1228, -1018, -808, -598, -388, -178, 0,
1073+
7654, 7652, 7651, 7649, 7648, 7646, 7645, 7643, 7641, 7640, 7638, 7637, 7635, 7634,
1074+
7632, 7631, 7629, 7628, 7626, 7625, 7623, 7622, 7620, 7619, 7618, 7616, 7615, 7613,
1075+
7612, 7610, 7609, 7607, 7606, 7605, 7603, 7602, 7600, 7599, 7597, 7596, 7595, 7593,
1076+
7592, 7591, 7589, 7588, 7586, 7585, 7584, 7582, 7581, 7580, 7578, 7577, 7576
10781077
]
10791078
);
10801079
assert_eq!(
10811080
sspreads,
10821081
[
10831082
210, 210, 210, 210, 210, 210, 20, 20, 20, 20, 210, 210, 210, 210, 210, 210, 210, 210,
1084-
210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210,
1085-
210, 210, 210, 130, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
1083+
210, 210, 210, 210, 210, 210, 178, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
10861084
10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
1087-
10, 10
1085+
10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10
10881086
]
10891087
);
10901088
assert_eq!(
10911089
lspreads,
10921090
[
1093-
7666, 7456, 7246, 7036, 6826, 6616, 6596, 6576, 6556, 6536, 6326, 6116, 5906, 5696,
1094-
5486, 5276, 5066, 4856, 4646, 4436, 4226, 4016, 3806, 3596, 3386, 3176, 2966, 2756,
1095-
2546, 2336, 2126, 1916, 1706, 1496, 1286, 1076, 866, 656, 526, 526, 526, 526, 526, 526,
1091+
4774, 4564, 4354, 4144, 3934, 3724, 3704, 3684, 3664, 3644, 3434, 3224, 3014, 2804,
1092+
2594, 2384, 2174, 1964, 1754, 1544, 1334, 1124, 914, 704, 526, 526, 526, 526, 526, 526,
10961093
526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526,
10971094
526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526,
1098-
526, 526
1095+
526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526
10991096
]
11001097
);
11011098
}

programs/drift/src/instructions/admin.rs

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1079,7 +1079,8 @@ pub fn handle_initialize_perp_market(
10791079
quote_asset_amount_with_unsettled_lp: 0,
10801080
reference_price_offset: 0,
10811081
amm_inventory_spread_adjustment: 0,
1082-
padding: [0; 3],
1082+
reference_price_offset_deadband_pct: 0,
1083+
padding: [0; 2],
10831084
last_funding_oracle_twap: 0,
10841085
},
10851086
};
@@ -3439,6 +3440,49 @@ pub fn handle_update_perp_market_curve_update_intensity(
34393440
Ok(())
34403441
}
34413442

3443+
#[access_control(
3444+
perp_market_valid(&ctx.accounts.perp_market)
3445+
)]
3446+
pub fn handle_update_perp_market_reference_price_offset_deadband_pct(
3447+
ctx: Context<HotAdminUpdatePerpMarket>,
3448+
reference_price_offset_deadband_pct: u8,
3449+
) -> Result<()> {
3450+
validate!(
3451+
reference_price_offset_deadband_pct <= 100,
3452+
ErrorCode::DefaultError,
3453+
"invalid reference_price_offset_deadband_pct",
3454+
)?;
3455+
let perp_market = &mut load_mut!(ctx.accounts.perp_market)?;
3456+
msg!("perp market {}", perp_market.market_index);
3457+
3458+
msg!(
3459+
"perp_market.amm.reference_price_offset_deadband_pct: {} -> {}",
3460+
perp_market.amm.reference_price_offset_deadband_pct,
3461+
reference_price_offset_deadband_pct
3462+
);
3463+
3464+
let liquidity_ratio =
3465+
crate::math::amm_spread::calculate_inventory_liquidity_ratio_for_reference_price_offset(
3466+
perp_market.amm.base_asset_amount_with_amm,
3467+
perp_market.amm.base_asset_reserve,
3468+
perp_market.amm.min_base_asset_reserve,
3469+
perp_market.amm.max_base_asset_reserve,
3470+
)?;
3471+
3472+
let signed_liquidity_ratio = liquidity_ratio.safe_mul(
3473+
perp_market
3474+
.amm
3475+
.get_protocol_owned_position()?
3476+
.signum()
3477+
.cast()?,
3478+
)?;
3479+
3480+
msg!("current signed liquidity ratio: {}", signed_liquidity_ratio);
3481+
3482+
perp_market.amm.reference_price_offset_deadband_pct = reference_price_offset_deadband_pct;
3483+
Ok(())
3484+
}
3485+
34423486
pub fn handle_update_lp_cooldown_time(
34433487
ctx: Context<AdminUpdateState>,
34443488
lp_cooldown_time: u64,

programs/drift/src/lib.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1387,6 +1387,16 @@ pub mod drift {
13871387
handle_update_perp_market_curve_update_intensity(ctx, curve_update_intensity)
13881388
}
13891389

1390+
pub fn update_perp_market_reference_price_offset_deadband_pct(
1391+
ctx: Context<HotAdminUpdatePerpMarket>,
1392+
reference_price_offset_deadband_pct: u8,
1393+
) -> Result<()> {
1394+
handle_update_perp_market_reference_price_offset_deadband_pct(
1395+
ctx,
1396+
reference_price_offset_deadband_pct,
1397+
)
1398+
}
1399+
13901400
pub fn update_lp_cooldown_time(
13911401
ctx: Context<AdminUpdateState>,
13921402
lp_cooldown_time: u64,

programs/drift/src/math/amm_spread.rs

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ use crate::math::constants::{
1212
BID_ASK_SPREAD_PRECISION, BID_ASK_SPREAD_PRECISION_I128, DEFAULT_LARGE_BID_ASK_FACTOR,
1313
DEFAULT_REVENUE_SINCE_LAST_FUNDING_SPREAD_RETREAT, FUNDING_RATE_BUFFER,
1414
MAX_BID_ASK_INVENTORY_SKEW_FACTOR, PEG_PRECISION, PERCENTAGE_PRECISION,
15-
PERCENTAGE_PRECISION_I128, PERCENTAGE_PRECISION_U64, PRICE_PRECISION, PRICE_PRECISION_I128,
16-
PRICE_PRECISION_I64,
15+
PERCENTAGE_PRECISION_I128, PERCENTAGE_PRECISION_I64, PERCENTAGE_PRECISION_U64, PRICE_PRECISION,
16+
PRICE_PRECISION_I128, PRICE_PRECISION_I64,
1717
};
1818
use crate::math::safe_math::SafeMath;
1919
use crate::state::perp_market::{ContractType, PerpMarket, AMM};
@@ -193,6 +193,35 @@ pub fn calculate_inventory_liquidity_ratio(
193193
Ok(amm_inventory_pct)
194194
}
195195

196+
pub fn calculate_inventory_liquidity_ratio_for_reference_price_offset(
197+
base_asset_amount_with_amm: i128,
198+
base_asset_reserve: u128,
199+
min_base_asset_reserve: u128,
200+
max_base_asset_reserve: u128,
201+
) -> DriftResult<i128> {
202+
// inventory scale
203+
let (max_bids, max_asks) = _calculate_market_open_bids_asks(
204+
base_asset_reserve,
205+
min_base_asset_reserve,
206+
max_base_asset_reserve,
207+
)?;
208+
209+
let avg_liquidity = (max_bids.safe_add(max_asks.abs())?).safe_div(2)?;
210+
211+
let amm_inventory_pct = if base_asset_amount_with_amm.abs() < avg_liquidity {
212+
base_asset_amount_with_amm
213+
.abs()
214+
.safe_mul(PERCENTAGE_PRECISION_I128)
215+
.unwrap_or(i128::MAX)
216+
.safe_div(avg_liquidity.max(1))?
217+
.min(PERCENTAGE_PRECISION_I128)
218+
} else {
219+
PERCENTAGE_PRECISION_I128 // 100%
220+
};
221+
222+
Ok(amm_inventory_pct)
223+
}
224+
196225
pub fn calculate_spread_inventory_scale(
197226
base_asset_amount_with_amm: i128,
198227
base_asset_reserve: u128,
@@ -570,7 +599,7 @@ pub fn calculate_reference_price_offset(
570599
mark_twap_slow: u64,
571600
max_offset_pct: i64,
572601
) -> DriftResult<i32> {
573-
if last_24h_avg_funding_rate == 0 {
602+
if last_24h_avg_funding_rate == 0 || liquidity_fraction == 0 {
574603
return Ok(0);
575604
}
576605

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

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#[cfg(test)]
22
mod test {
3+
use crate::controller::amm::update_spreads;
34
use crate::math::amm::calculate_price;
45
use crate::math::amm_spread::*;
56
use crate::math::constants::{
@@ -189,6 +190,73 @@ mod test {
189190
assert_eq!(res, 0);
190191
}
191192

193+
#[test]
194+
fn calculate_reference_price_offset_deadband_tests() {
195+
let mut market = PerpMarket {
196+
amm: AMM {
197+
base_asset_reserve: AMM_RESERVE_PRECISION * 11,
198+
quote_asset_reserve: AMM_RESERVE_PRECISION * 10,
199+
sqrt_k: AMM_RESERVE_PRECISION * 10,
200+
peg_multiplier: 34_000_000,
201+
min_base_asset_reserve: AMM_RESERVE_PRECISION * 7,
202+
max_base_asset_reserve: AMM_RESERVE_PRECISION * 14,
203+
base_spread: 1000,
204+
max_spread: 20_000,
205+
curve_update_intensity: 110,
206+
last_24h_avg_funding_rate: 1,
207+
last_mark_price_twap_5min: 4216 * 10000 + 2 * 10000,
208+
last_mark_price_twap: 4216 * 10000 + 2 * 10000,
209+
historical_oracle_data: {
210+
let mut hod: crate::state::oracle::HistoricalOracleData = Default::default();
211+
hod.last_oracle_price_twap_5min = 4216 * 10000;
212+
hod.last_oracle_price_twap = 4216 * 10000;
213+
hod
214+
},
215+
..AMM::default()
216+
},
217+
..PerpMarket::default()
218+
};
219+
220+
let reserve_price = 4216 * 10000;
221+
222+
market.amm.base_asset_amount_with_amm = (AMM_RESERVE_PRECISION * 7 / 20) as i128;
223+
let inventory_ratio = calculate_inventory_liquidity_ratio_for_reference_price_offset(
224+
market.amm.base_asset_amount_with_amm,
225+
market.amm.base_asset_reserve,
226+
market.amm.min_base_asset_reserve,
227+
market.amm.max_base_asset_reserve,
228+
)
229+
.unwrap();
230+
assert_eq!(inventory_ratio, 100000); // 10%
231+
232+
market.amm.reference_price_offset_deadband_pct = 10; // 10%
233+
234+
// If inventory exceeds threshold positive ref price offset
235+
market.amm.base_asset_amount_with_amm = (AMM_RESERVE_PRECISION * 8 / 20) as i128;
236+
let (_l, _s) = update_spreads(&mut market, reserve_price as u64, None).unwrap();
237+
assert!(market.amm.reference_price_offset > 0);
238+
239+
// If inventory is small, goes to 0
240+
market.amm.base_asset_amount_with_amm = (AMM_RESERVE_PRECISION * 6 / 20) as i128;
241+
let (_l, _s) = update_spreads(&mut market, reserve_price as u64, None).unwrap();
242+
assert_eq!(market.amm.reference_price_offset, 0);
243+
244+
// Same for short pos
245+
// Make sure that the premium is also short
246+
market.amm.last_24h_avg_funding_rate = -1;
247+
market.amm.last_mark_price_twap_5min = 4216 * 10000 - 2 * 10000;
248+
market.amm.last_mark_price_twap = 4216 * 10000 - 2 * 10000;
249+
market.amm.base_asset_amount_with_amm = (AMM_RESERVE_PRECISION * 8 / 20) as i128 * -1;
250+
let (_l, _s) = update_spreads(&mut market, reserve_price as u64, None).unwrap();
251+
println!("ref offset: {}", market.amm.reference_price_offset);
252+
assert!(market.amm.reference_price_offset < 0);
253+
254+
// Same for short pos
255+
market.amm.base_asset_amount_with_amm = (AMM_RESERVE_PRECISION * 6 / 20) as i128 * -1;
256+
let (_l, _s) = update_spreads(&mut market, reserve_price as u64, None).unwrap();
257+
assert_eq!(market.amm.reference_price_offset, 0);
258+
}
259+
192260
#[test]
193261
fn calculate_spread_tests() {
194262
let base_spread = 1000; // .1%

programs/drift/src/state/perp_market.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1167,7 +1167,8 @@ pub struct AMM {
11671167
pub reference_price_offset: i32,
11681168
/// signed scale amm_spread similar to fee_adjustment logic (-100 = 0, 100 = double)
11691169
pub amm_inventory_spread_adjustment: i8,
1170-
pub padding: [u8; 3],
1170+
pub reference_price_offset_deadband_pct: u8,
1171+
pub padding: [u8; 2],
11711172
pub last_funding_oracle_twap: i64,
11721173
}
11731174

@@ -1258,13 +1259,18 @@ impl Default for AMM {
12581259
quote_asset_amount_with_unsettled_lp: 0,
12591260
reference_price_offset: 0,
12601261
amm_inventory_spread_adjustment: 0,
1261-
padding: [0; 3],
1262+
reference_price_offset_deadband_pct: 0,
1263+
padding: [0; 2],
12621264
last_funding_oracle_twap: 0,
12631265
}
12641266
}
12651267
}
12661268

12671269
impl AMM {
1270+
pub fn get_reference_price_offset_deadband_pct(&self) -> DriftResult<u128> {
1271+
let pct = self.reference_price_offset_deadband_pct as u128;
1272+
Ok(PERCENTAGE_PRECISION.safe_mul(pct)?.safe_div(100_u128)?)
1273+
}
12681274
pub fn get_fallback_price(
12691275
self,
12701276
direction: &PositionDirection,

0 commit comments

Comments
 (0)