Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bigz/improve-perp-match-get_fallback_price #797

Merged
merged 3 commits into from
Dec 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 3 additions & 6 deletions programs/drift/src/controller/orders.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2089,22 +2089,19 @@ pub fn fulfill_perp_order_with_match(
return Ok((0_u64, 0_u64, 0_u64));
}

let (bid_price, ask_price) = market.amm.bid_ask_price(market.amm.reserve_price()?)?;

let oracle_price = oracle_map.get_price_data(&market.amm.oracle)?.price;
let taker_direction = taker.orders[taker_order_index].direction;
let taker_direction: PositionDirection = taker.orders[taker_order_index].direction;

let taker_price = if let Some(taker_limit_price) = taker_limit_price {
taker_limit_price
} else {
let amm_available_liquidity =
calculate_amm_available_liquidity(&market.amm, &taker_direction)?;
get_fallback_price(
market.amm.get_fallback_price(
&taker_direction,
bid_price,
ask_price,
amm_available_liquidity,
oracle_price,
taker.orders[taker_order_index].seconds_til_expiry(now),
)?
};

Expand Down
20 changes: 0 additions & 20 deletions programs/drift/src/math/orders.rs
Original file line number Diff line number Diff line change
Expand Up @@ -693,26 +693,6 @@ pub fn calculate_fill_price(
.cast::<u64>()
}

pub fn get_fallback_price(
direction: &PositionDirection,
bid_price: u64,
ask_price: u64,
amm_available_liquidity: u64,
oracle_price: i64,
) -> DriftResult<u64> {
let oracle_price = oracle_price.unsigned_abs();
match direction {
PositionDirection::Long if amm_available_liquidity > 0 => {
ask_price.safe_add(ask_price / 200)
}
PositionDirection::Long => oracle_price.safe_add(oracle_price / 20),
PositionDirection::Short if amm_available_liquidity > 0 => {
bid_price.safe_sub(bid_price / 200)
}
PositionDirection::Short => oracle_price.safe_sub(oracle_price / 20),
}
}

pub fn get_max_fill_amounts(
user: &User,
user_order_index: usize,
Expand Down
143 changes: 143 additions & 0 deletions programs/drift/src/math/orders/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3718,3 +3718,146 @@ mod select_margin_type_for_perp_maker {
assert_eq!(margin_type, MarginRequirementType::Maintenance);
}
}

mod fallback_price_logic {
use crate::math::constants::{
AMM_RESERVE_PRECISION, PEG_PRECISION, PRICE_PRECISION, PRICE_PRECISION_I64,
};
use crate::state::oracle::HistoricalOracleData;
use crate::state::perp_market::{PerpMarket, AMM};
use crate::{MarketStatus, PositionDirection};

#[test]
fn test() {
let mut market = PerpMarket {
amm: AMM {
base_asset_reserve: 100 * AMM_RESERVE_PRECISION,
quote_asset_reserve: 100 * AMM_RESERVE_PRECISION,
bid_base_asset_reserve: 100 * AMM_RESERVE_PRECISION,
bid_quote_asset_reserve: 100 * AMM_RESERVE_PRECISION,
ask_base_asset_reserve: 100 * AMM_RESERVE_PRECISION,
ask_quote_asset_reserve: 100 * AMM_RESERVE_PRECISION,
sqrt_k: 100 * AMM_RESERVE_PRECISION,
peg_multiplier: 100 * PEG_PRECISION,
max_slippage_ratio: 50,
max_fill_reserve_fraction: 100,
order_step_size: 1000,
order_tick_size: 1,
min_order_size: 1000,
// oracle: oracle_price_key,
base_spread: 0,
historical_oracle_data: HistoricalOracleData {
last_oracle_price: (100 * PRICE_PRECISION) as i64,
last_oracle_price_twap: (100 * PRICE_PRECISION) as i64,
last_oracle_price_twap_5min: (100 * PRICE_PRECISION) as i64,

..HistoricalOracleData::default()
},
..AMM::default()
},
margin_ratio_initial: 2000,
margin_ratio_maintenance: 1000,
status: MarketStatus::Initialized,
..PerpMarket::default_test()
};
market.amm.max_base_asset_reserve = u128::MAX;
market.amm.min_base_asset_reserve = 0;

// fallback are wide from oracle cause twaps arent set on amm
let result = market
.amm
.get_fallback_price(
&PositionDirection::Long,
0,
2012 * PRICE_PRECISION_I64 / 100,
0,
)
.unwrap();
assert_eq!(result, 22132000);

let result = market
.amm
.get_fallback_price(
&PositionDirection::Short,
0,
2012 * PRICE_PRECISION_I64 / 100,
0,
)
.unwrap();
assert_eq!(result, 0);

// make non-zero bid/ask twaps
market.amm.last_ask_price_twap = (101 * PRICE_PRECISION) as u64;
market.amm.last_bid_price_twap = (99 * PRICE_PRECISION) as u64;

// fallback is offset from oracle
let result = market
.amm
.get_fallback_price(
&PositionDirection::Long,
0,
2012 * PRICE_PRECISION_I64 / 100,
0,
)
.unwrap();
assert_eq!(result, 23132000);

let result = market
.amm
.get_fallback_price(
&PositionDirection::Short,
0,
2012 * PRICE_PRECISION_I64 / 100,
0,
)
.unwrap();
assert_eq!(result, 17108000);

// ignores current oracle price and just prices fallback based on amm liquidity
let result = market
.amm
.get_fallback_price(
&PositionDirection::Long,
1000000000,
2012 * PRICE_PRECISION_I64 / 100,
0,
)
.unwrap();
assert_eq!(result, 101000000);

let result = market
.amm
.get_fallback_price(
&PositionDirection::Short,
1000000000,
2012 * PRICE_PRECISION_I64 / 100,
0,
)
.unwrap();
assert_eq!(result, 99000000);

// ignores current oracle price and just prices fallback based on amm liquidity
// tighter when seconds til expiry is long
let result = market
.amm
.get_fallback_price(
&PositionDirection::Long,
1000000000,
2012 * PRICE_PRECISION_I64 / 100,
100,
)
.unwrap();
assert_eq!(result, 100500000);

let result = market
.amm
.get_fallback_price(
&PositionDirection::Short,
1000000000,
2012 * PRICE_PRECISION_I64 / 100,
100,
)
.unwrap();
assert_eq!(result, 99500000);
}
}
52 changes: 52 additions & 0 deletions programs/drift/src/state/perp_market.rs
Original file line number Diff line number Diff line change
Expand Up @@ -771,6 +771,58 @@
}

impl AMM {
pub fn get_fallback_price(
self,
direction: &PositionDirection,
amm_available_liquidity: u64,
oracle_price: i64,
seconds_til_order_expiry: i64,
) -> DriftResult<u64> {
// PRICE_PRECISION
if direction.eq(&PositionDirection::Long) {
// pick amm ask + buffer if theres liquidity
// otherwise be aggressive vs oracle + 1hr premium
if amm_available_liquidity >= self.min_order_size {
let reserve_price = self.reserve_price()?;
let amm_ask_price: i64 = self.ask_price(reserve_price)?.cast()?;
amm_ask_price
.safe_add(amm_ask_price / (seconds_til_order_expiry * 20).clamp(100, 200))?
.cast::<u64>()
} else {
oracle_price
.safe_add(
self.last_ask_price_twap
.cast::<i64>()?

Check warning on line 795 in programs/drift/src/state/perp_market.rs

View check run for this annotation

Codecov / codecov/patch

programs/drift/src/state/perp_market.rs#L795

Added line #L795 was not covered by tests
.safe_sub(self.historical_oracle_data.last_oracle_price_twap)?
.max(0),

Check warning on line 797 in programs/drift/src/state/perp_market.rs

View check run for this annotation

Codecov / codecov/patch

programs/drift/src/state/perp_market.rs#L797

Added line #L797 was not covered by tests
)?
.safe_add(oracle_price / (seconds_til_order_expiry * 2).clamp(10, 50))?
.cast::<u64>()
}
} else {
// pick amm bid - buffer if theres liquidity
// otherwise be aggressive vs oracle + 1hr bid premium
if amm_available_liquidity >= self.min_order_size {
let reserve_price = self.reserve_price()?;
let amm_bid_price: i64 = self.bid_price(reserve_price)?.cast()?;
amm_bid_price
.safe_sub(amm_bid_price / (seconds_til_order_expiry * 20).clamp(100, 200))?
.cast::<u64>()
} else {
oracle_price
.safe_add(
self.last_bid_price_twap
.cast::<i64>()?

Check warning on line 815 in programs/drift/src/state/perp_market.rs

View check run for this annotation

Codecov / codecov/patch

programs/drift/src/state/perp_market.rs#L815

Added line #L815 was not covered by tests
.safe_sub(self.historical_oracle_data.last_oracle_price_twap)?
.min(0),

Check warning on line 817 in programs/drift/src/state/perp_market.rs

View check run for this annotation

Codecov / codecov/patch

programs/drift/src/state/perp_market.rs#L817

Added line #L817 was not covered by tests
)?
.safe_sub(oracle_price / (seconds_til_order_expiry * 2).clamp(10, 50))?
.max(0)
.cast::<u64>()
}
}
}

pub fn get_protocol_owned_position(self) -> DriftResult<i64> {
self.base_asset_amount_with_amm
.safe_add(self.base_asset_amount_with_unsettled_lp)?
Expand Down
4 changes: 4 additions & 0 deletions programs/drift/src/state/user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -999,6 +999,10 @@ pub enum AssetType {
}

impl Order {
pub fn seconds_til_expiry(self, now: i64) -> i64 {
(self.max_ts - now).max(0)
}

pub fn has_oracle_price_offset(self) -> bool {
self.oracle_price_offset != 0
}
Expand Down
10 changes: 6 additions & 4 deletions sdk/src/driftClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3274,8 +3274,9 @@ export class DriftClient {
subAccountId?: number
): Promise<TransactionInstruction> {
orderParams = getOrderParams(orderParams, { marketType: MarketType.SPOT });
const userAccountPublicKey =
await this.getUserAccountPublicKey(subAccountId);
const userAccountPublicKey = await this.getUserAccountPublicKey(
subAccountId
);

const remainingAccounts = this.getRemainingAccounts({
userAccounts: [this.getUserAccount(subAccountId)],
Expand Down Expand Up @@ -5785,8 +5786,9 @@ export class DriftClient {
}

if (initializeStakeAccount) {
const initializeIx =
await this.getInitializeInsuranceFundStakeIx(marketIndex);
const initializeIx = await this.getInitializeInsuranceFundStakeIx(
marketIndex
);
addIfStakeIxs.push(initializeIx);
}

Expand Down
Loading