Skip to content

Commit

Permalink
program: spread use liquidity ratio instead of base amount (#374)
Browse files Browse the repository at this point in the history
* bigz/amm-spread-inventory-pct

* wip2

* working rust

* amm.ts: add sdk for new inventory spread scale

* fix test

* fix test 2

* update CHANGELOG.md

* incorp feedback add tests

* add mirror rust test for ts test

* make ts more consistent w/ rust impl

---------

Co-authored-by: lil perp <[email protected]>
  • Loading branch information
0xbigz and crispheaney committed Feb 28, 2023
1 parent 86cf411 commit d67c73f
Show file tree
Hide file tree
Showing 8 changed files with 449 additions and 106 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Features

- program: max number of subaccounts to 3000
- program: amm spread logic more consistent across market by using liquidity ratio rather than base asset amount for inventory spread scaling([#374](https://github.com/drift-labs/protocol-v2/pull/374))
- program: add pyth1M/pyth1K as OracleSource ([#375](https://github.com/drift-labs/protocol-v2/pull/375))

### Fixes
Expand Down
22 changes: 14 additions & 8 deletions programs/drift/src/controller/repeg/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,10 @@ pub fn update_amm_test() {
assert_eq!(reserve_price, 12743902015);
assert_eq!(ask, 12762712014);
assert!(ask >= (oracle_price_data.price as u64));
assert!((ask - bid) * 1000000 / reserve_price == market.amm.max_spread as u64);
assert_eq!(
(ask - bid) * 1000000 / reserve_price,
(market.amm.max_spread) as u64
);
}

#[test]
Expand Down Expand Up @@ -264,8 +267,8 @@ pub fn update_amm_larg_conf_test() {
let cost_of_update = _update_amm(&mut market, &oracle_price_data, &state, now, slot).unwrap();
assert_eq!(cost_of_update, -42992787); // amm wins when price increases

assert_eq!(market.amm.short_spread, 975 - market.amm.long_spread);
assert_eq!(market.amm.long_spread, 76);
assert_eq!(market.amm.short_spread, 975 - 76);

let reserve_price_after = market.amm.reserve_price().unwrap();
assert_eq!(reserve_price_after, 18849999999);
Expand All @@ -291,7 +294,7 @@ pub fn update_amm_larg_conf_test() {

assert_eq!(market.amm.long_spread, 76);
assert_eq!(market.amm.peg_multiplier, 19443664550);
assert_eq!(market.amm.short_spread, 975 - 76);
assert_eq!(market.amm.short_spread, 975 - market.amm.long_spread);

// add move lower
let oracle_price_data = OraclePriceData {
Expand Down Expand Up @@ -320,7 +323,7 @@ pub fn update_amm_larg_conf_test() {
let cost_of_update = _update_amm(&mut market, &oracle_price_data, &state, now, slot).unwrap();
assert_eq!(cost_of_update, 30468749);
assert_eq!(market.amm.long_spread, 60);
assert_eq!(market.amm.short_spread, 975 - 60);
assert_eq!(market.amm.short_spread, 975 - market.amm.long_spread);

let mrk = market.amm.reserve_price().unwrap();
let (bid, ask) = market.amm.bid_ask_price(mrk).unwrap();
Expand All @@ -342,7 +345,7 @@ pub fn update_amm_larg_conf_test() {
let cost_of_update = _update_amm(&mut market, &oracle_price_data, &state, now, slot).unwrap();
assert_eq!(cost_of_update, -3046875);
assert_eq!(market.amm.long_spread, 60);
assert_eq!(market.amm.short_spread, 975 - 60);
assert_eq!(market.amm.short_spread, 975 - market.amm.long_spread);

let mrk = market.amm.reserve_price().unwrap();
let (bid, ask) = market.amm.bid_ask_price(mrk).unwrap();
Expand Down Expand Up @@ -416,7 +419,10 @@ pub fn update_amm_larg_conf_w_neg_tfmd_test() {
assert_eq!(market.amm.last_update_slot, slot);

assert_eq!(market.amm.long_spread, 389);
assert_eq!(market.amm.short_spread, 975 - 389);
assert_eq!(
market.amm.short_spread,
market.amm.max_spread - market.amm.long_spread
);

let reserve_price_after = market.amm.reserve_price().unwrap();
assert_eq!(reserve_price_after, 18849999999);
Expand Down Expand Up @@ -490,7 +496,7 @@ pub fn update_amm_larg_conf_w_neg_tfmd_test() {
assert_eq!(market.amm.last_update_slot, slot);

assert_eq!(market.amm.long_spread, 369);
assert_eq!(market.amm.short_spread, 975 - 369);
assert_eq!(market.amm.short_spread, 975 - market.amm.long_spread);

let mrk = market.amm.reserve_price().unwrap();
let (bid, ask) = market.amm.bid_ask_price(mrk).unwrap();
Expand All @@ -514,7 +520,7 @@ pub fn update_amm_larg_conf_w_neg_tfmd_test() {
let cost_of_update = _update_amm(&mut market, &oracle_price_data, &state, now, slot).unwrap();
assert_eq!(cost_of_update, 299367);
assert_eq!(market.amm.long_spread, 378);
assert_eq!(market.amm.short_spread, 975 - 378);
assert_eq!(market.amm.short_spread, 975 - market.amm.long_spread);

let mrk = market.amm.reserve_price().unwrap();
let (bid, ask) = market.amm.bid_ask_price(mrk).unwrap();
Expand Down
70 changes: 47 additions & 23 deletions programs/drift/src/math/amm_spread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,19 @@ use crate::math::amm::_calculate_market_open_bids_asks;
use crate::math::bn::U192;
use crate::math::casting::Cast;
use crate::math::constants::{
AMM_RESERVE_PRECISION_I128, AMM_TIMES_PEG_TO_QUOTE_PRECISION_RATIO_I128,
AMM_TO_QUOTE_PRECISION_RATIO_I128, BID_ASK_SPREAD_PRECISION, BID_ASK_SPREAD_PRECISION_I128,
BID_ASK_SPREAD_PRECISION_U128, DEFAULT_LARGE_BID_ASK_FACTOR,
DEFAULT_REVENUE_SINCE_LAST_FUNDING_SPREAD_RETREAT, MAX_BID_ASK_INVENTORY_SKEW_FACTOR,
PEG_PRECISION, PERCENTAGE_PRECISION, PRICE_PRECISION, PRICE_PRECISION_I128,
AMM_TIMES_PEG_TO_QUOTE_PRECISION_RATIO_I128, AMM_TO_QUOTE_PRECISION_RATIO_I128,
BID_ASK_SPREAD_PRECISION, BID_ASK_SPREAD_PRECISION_I128, BID_ASK_SPREAD_PRECISION_U128,
DEFAULT_LARGE_BID_ASK_FACTOR, DEFAULT_REVENUE_SINCE_LAST_FUNDING_SPREAD_RETREAT,
MAX_BID_ASK_INVENTORY_SKEW_FACTOR, PEG_PRECISION, PERCENTAGE_PRECISION, PRICE_PRECISION,
PRICE_PRECISION_I128,
};
use crate::math::safe_math::SafeMath;

use crate::state::perp_market::AMM;
use crate::validate;

use super::constants::PERCENTAGE_PRECISION_I128;

#[cfg(test)]
mod tests;

Expand Down Expand Up @@ -153,6 +155,37 @@ pub fn calculate_long_short_vol_spread(
))
}

pub fn calculate_inventory_liquidity_ratio(
base_asset_amount_with_amm: i128,
base_asset_reserve: u128,
min_base_asset_reserve: u128,
max_base_asset_reserve: u128,
) -> DriftResult<i128> {
// computes min(1, x/(1-x)) for 0 < x < 1

// inventory scale
let (max_bids, max_asks) = _calculate_market_open_bids_asks(
base_asset_reserve,
min_base_asset_reserve,
max_base_asset_reserve,
)?;

let min_side_liquidity = max_bids.min(max_asks.abs());

let amm_inventory_pct = if base_asset_amount_with_amm.abs() < min_side_liquidity {
base_asset_amount_with_amm
.abs()
.safe_mul(PERCENTAGE_PRECISION_I128)
.unwrap_or(i128::MAX)
.safe_div(min_side_liquidity.max(1))?
.min(PERCENTAGE_PRECISION_I128)
} else {
PERCENTAGE_PRECISION_I128 // 100%
};

Ok(amm_inventory_pct)
}

pub fn calculate_spread_inventory_scale(
base_asset_amount_with_amm: i128,
base_asset_reserve: u128,
Expand All @@ -165,38 +198,29 @@ pub fn calculate_spread_inventory_scale(
return Ok(BID_ASK_SPREAD_PRECISION);
}

// inventory scale
let (max_bids, max_asks) = _calculate_market_open_bids_asks(
let amm_inventory_pct = calculate_inventory_liquidity_ratio(
base_asset_amount_with_amm,
base_asset_reserve,
min_base_asset_reserve,
max_base_asset_reserve,
)?;

let min_side_liquidity = max_bids.min(max_asks.abs());

// cap so (6e9 * AMM_RESERVE_PRECISION)^2 < 2^127
let amm_inventory_size = base_asset_amount_with_amm.abs().min(6000000000000000000);

// inventory scale
let inventory_scale = amm_inventory_size
.safe_mul(amm_inventory_size.max(AMM_RESERVE_PRECISION_I128))?
.safe_div(AMM_RESERVE_PRECISION_I128)?
.safe_mul(DEFAULT_LARGE_BID_ASK_FACTOR.cast::<i128>()?)?
.safe_div(min_side_liquidity.max(1))?
.unsigned_abs();

// only allow up to scale up of larger of MAX_BID_ASK_INVENTORY_SKEW_FACTOR or half of max spread
// only allow up to scale up of larger of MAX_BID_ASK_INVENTORY_SKEW_FACTOR or max spread
let inventory_scale_max = MAX_BID_ASK_INVENTORY_SKEW_FACTOR.max(
max_spread
.safe_div(2)?
.safe_mul(BID_ASK_SPREAD_PRECISION)?
.safe_div(max(directional_spread, 1))?,
);

let inventory_scale_capped = min(
inventory_scale_max,
BID_ASK_SPREAD_PRECISION
.safe_add(inventory_scale.cast()?)
.safe_add(
inventory_scale_max
.safe_mul(amm_inventory_pct.unsigned_abs().cast()?)
.unwrap_or(u64::MAX)
.safe_div(PERCENTAGE_PRECISION_I128.cast()?)?,
)
.unwrap_or(u64::MAX),
);

Expand Down
Loading

0 comments on commit d67c73f

Please sign in to comment.