Skip to content

program: trigger price based on oracle and basis #1716

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

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
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
1 change: 1 addition & 0 deletions programs/drift/src/controller/funding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,7 @@ pub fn update_funding_rate(
.safe_add(funding_rate_short)?;

market.amm.last_funding_rate = funding_rate;
market.amm.last_funding_oracle_twap = oracle_price_twap;
market.amm.last_funding_rate_long = funding_rate_long.cast()?;
market.amm.last_funding_rate_short = funding_rate_short.cast()?;
market.amm.last_24h_avg_funding_rate = calculate_new_twap(
Expand Down
1 change: 1 addition & 0 deletions programs/drift/src/controller/liquidation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -691,6 +691,7 @@ pub fn liquidate_perp(
taker_existing_base_asset_amount: taker_existing_base_asset_amount,
maker_existing_quote_entry_amount: maker_existing_quote_entry_amount,
maker_existing_base_asset_amount: maker_existing_base_asset_amount,
trigger_price: None,
};
emit!(fill_record);

Expand Down
19 changes: 14 additions & 5 deletions programs/drift/src/controller/orders.rs
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,7 @@ pub fn place_perp_order(
None,
None,
None,
None,
)?;
emit_stack::<_, { OrderActionRecord::SIZE }>(order_action_record)?;

Expand Down Expand Up @@ -707,6 +708,7 @@ pub fn cancel_order(
None,
None,
None,
None,
)?;
emit_stack::<_, { OrderActionRecord::SIZE }>(order_action_record)?;
}
Expand Down Expand Up @@ -1295,7 +1297,7 @@ pub fn fill_perp_order(
let fill_price =
calculate_fill_price(quote_asset_amount, base_asset_amount, BASE_PRECISION_U64)?;

let perp_market = perp_market_map.get_ref(&market_index)?;
let mut perp_market = perp_market_map.get_ref_mut(&market_index)?;
validate_fill_price_within_price_bands(
fill_price,
order_direction,
Expand All @@ -1307,6 +1309,8 @@ pub fn fill_perp_order(
.max_oracle_twap_5min_percent_divergence(),
perp_market.is_prediction_market(),
)?;

perp_market.last_fill_price = fill_price;
}

let base_asset_amount_after = user.perp_positions[position_index].base_asset_amount;
Expand Down Expand Up @@ -2419,6 +2423,7 @@ pub fn fulfill_perp_order_with_amm(
taker_existing_base_asset_amount,
maker_existing_quote_entry_amount,
maker_existing_base_asset_amount,
None,
)?;
emit_stack::<_, { OrderActionRecord::SIZE }>(order_action_record)?;

Expand Down Expand Up @@ -2864,6 +2869,7 @@ pub fn fulfill_perp_order_with_match(
taker_existing_base_asset_amount,
maker_existing_quote_entry_amount,
maker_existing_base_asset_amount,
None,
)?;
emit_stack::<_, { OrderActionRecord::SIZE }>(order_action_record)?;

Expand Down Expand Up @@ -3016,10 +3022,8 @@ pub fn trigger_order(
"oracle price vs twap too divergent"
)?;

let can_trigger = order_satisfies_trigger_condition(
&user.orders[order_index],
oracle_price.unsigned_abs().cast()?,
)?;
let trigger_price = perp_market.get_trigger_price(oracle_price, now)?;
let can_trigger = order_satisfies_trigger_condition(&user.orders[order_index], trigger_price)?;
validate!(can_trigger, ErrorCode::OrderDidNotSatisfyTriggerCondition)?;

let (_, worst_case_liability_value_before) = user
Expand Down Expand Up @@ -3086,6 +3090,7 @@ pub fn trigger_order(
None,
None,
None,
Some(trigger_price),
)?;
emit!(order_action_record);

Expand Down Expand Up @@ -3761,6 +3766,7 @@ pub fn place_spot_order(
None,
None,
None,
None,
)?;
emit_stack::<_, { OrderActionRecord::SIZE }>(order_action_record)?;

Expand Down Expand Up @@ -4992,6 +4998,7 @@ pub fn fulfill_spot_order_with_match(
None,
None,
None,
None,
)?;
emit_stack::<_, { OrderActionRecord::SIZE }>(order_action_record)?;

Expand Down Expand Up @@ -5262,6 +5269,7 @@ pub fn fulfill_spot_order_with_external_market(
None,
None,
None,
None,
)?;
emit_stack::<_, { OrderActionRecord::SIZE }>(order_action_record)?;

Expand Down Expand Up @@ -5459,6 +5467,7 @@ pub fn trigger_spot_order(
None,
None,
None,
Some(oracle_price.unsigned_abs()),
)?;

emit!(order_action_record);
Expand Down
7 changes: 5 additions & 2 deletions programs/drift/src/instructions/admin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -955,7 +955,9 @@ pub fn handle_initialize_perp_market(
high_leverage_margin_ratio_maintenance: 0,
protected_maker_limit_price_divisor: 0,
protected_maker_dynamic_divisor: 0,
padding: [0; 36],
padding1: 0,
last_fill_price: 0,
padding: [0; 24],
amm: AMM {
oracle: *ctx.accounts.oracle.key,
oracle_source,
Expand Down Expand Up @@ -1052,7 +1054,8 @@ pub fn handle_initialize_perp_market(
quote_asset_amount_with_unsettled_lp: 0,
reference_price_offset: 0,
amm_inventory_spread_adjustment: 0,
padding: [0; 11],
padding: [0; 3],
last_funding_oracle_twap: 0,
},
};

Expand Down
1 change: 1 addition & 0 deletions programs/drift/src/instructions/user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1874,6 +1874,7 @@ pub fn handle_transfer_perp_position<'c: 'info, 'info>(
taker_existing_base_asset_amount: to_existing_base_asset_amount,
maker_existing_quote_entry_amount: from_existing_quote_entry_amount,
maker_existing_base_asset_amount: from_existing_base_asset_amount,
trigger_price: None,
};

emit_stack::<_, { OrderActionRecord::SIZE }>(fill_record)?;
Expand Down
2 changes: 2 additions & 0 deletions programs/drift/src/math/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,8 @@ pub const DEFAULT_QUOTE_ASSET_AMOUNT_TICK_SIZE: u64 =

// FUNDING
pub const FUNDING_RATE_OFFSET_DENOMINATOR: i64 = 5000; // 5000 => 7.3% annualized rate for hourly funding
pub const FUNDING_RATE_OFFSET_PERCENTAGE: i64 =
FUNDING_RATE_PRECISION_I64 / FUNDING_RATE_OFFSET_DENOMINATOR;

// ORDERS
pub const AUCTION_DERIVE_PRICE_FRACTION: i64 = 200;
Expand Down
6 changes: 5 additions & 1 deletion programs/drift/src/state/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -254,10 +254,12 @@ pub struct OrderActionRecord {
/// precision: BASE_PRECISION
/// Only Some if the maker flipped position direction
pub maker_existing_base_asset_amount: Option<u64>,
/// precision: PRICE_PRECISION
pub trigger_price: Option<u64>,
}

impl Size for OrderActionRecord {
const SIZE: usize = 448;
const SIZE: usize = 464;
}

pub fn get_order_action_record(
Expand Down Expand Up @@ -285,6 +287,7 @@ pub fn get_order_action_record(
taker_existing_base_asset_amount: Option<u64>,
maker_existing_quote_entry_amount: Option<u64>,
maker_existing_base_asset_amount: Option<u64>,
trigger_price: Option<u64>,
) -> DriftResult<OrderActionRecord> {
Ok(OrderActionRecord {
ts,
Expand Down Expand Up @@ -337,6 +340,7 @@ pub fn get_order_action_record(
taker_existing_base_asset_amount,
maker_existing_quote_entry_amount,
maker_existing_base_asset_amount,
trigger_price,
})
}

Expand Down
118 changes: 107 additions & 11 deletions programs/drift/src/state/perp_market.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,17 @@ use crate::error::{DriftResult, ErrorCode};
use crate::math::amm;
use crate::math::casting::Cast;
#[cfg(test)]
use crate::math::constants::{
AMM_RESERVE_PRECISION, MAX_CONCENTRATION_COEFFICIENT, PRICE_PRECISION_I64,
};
use crate::math::constants::{AMM_RESERVE_PRECISION, MAX_CONCENTRATION_COEFFICIENT};
use crate::math::constants::{
AMM_RESERVE_PRECISION_I128, AMM_TO_QUOTE_PRECISION_RATIO, BID_ASK_SPREAD_PRECISION,
BID_ASK_SPREAD_PRECISION_I128, BID_ASK_SPREAD_PRECISION_U128,
DEFAULT_REVENUE_SINCE_LAST_FUNDING_SPREAD_RETREAT, LIQUIDATION_FEE_PRECISION,
DEFAULT_REVENUE_SINCE_LAST_FUNDING_SPREAD_RETREAT, FUNDING_RATE_BUFFER_I128,
FUNDING_RATE_OFFSET_PERCENTAGE, LIQUIDATION_FEE_PRECISION,
LIQUIDATION_FEE_TO_MARGIN_PRECISION_RATIO, LP_FEE_SLICE_DENOMINATOR, LP_FEE_SLICE_NUMERATOR,
MARGIN_PRECISION, MARGIN_PRECISION_U128, MAX_LIQUIDATION_MULTIPLIER, PEG_PRECISION,
PERCENTAGE_PRECISION, PERCENTAGE_PRECISION_I128, PERCENTAGE_PRECISION_I64,
PERCENTAGE_PRECISION_U64, PRICE_PRECISION, SPOT_WEIGHT_PRECISION, TWENTY_FOUR_HOUR,
PERCENTAGE_PRECISION_U64, PRICE_PRECISION, PRICE_PRECISION_I128, PRICE_PRECISION_I64,
SPOT_WEIGHT_PRECISION, TWENTY_FOUR_HOUR,
};
use crate::math::helpers::get_proportion_i128;
use crate::math::margin::{
Expand Down Expand Up @@ -251,7 +251,9 @@ pub struct PerpMarket {
pub high_leverage_margin_ratio_maintenance: u16,
pub protected_maker_limit_price_divisor: u8,
pub protected_maker_dynamic_divisor: u8,
pub padding: [u8; 36],
pub padding1: u32,
pub last_fill_price: u64,
pub padding: [u8; 24],
}

impl Default for PerpMarket {
Expand Down Expand Up @@ -293,7 +295,9 @@ impl Default for PerpMarket {
high_leverage_margin_ratio_maintenance: 0,
protected_maker_limit_price_divisor: 0,
protected_maker_dynamic_divisor: 0,
padding: [0; 36],
padding1: 0,
last_fill_price: 0,
padding: [0; 24],
}
}
}
Expand Down Expand Up @@ -747,6 +751,96 @@ impl PerpMarket {
default_min_auction_duration
}
}

pub fn get_trigger_price(&self, oracle_price: i64, now: i64) -> DriftResult<u64> {
let last_fill_price = self.last_fill_price;

let mark_price_5min_twap = self.amm.last_mark_price_twap;
let last_oracle_price_twap_5min =
self.amm.historical_oracle_data.last_oracle_price_twap_5min;

let basis_5min = mark_price_5min_twap
.cast::<i64>()?
.safe_sub(last_oracle_price_twap_5min)?;

let oracle_plus_basis_5min = oracle_price.safe_add(basis_5min)?.unsigned_abs();

let last_funding_basis = self.get_last_funding_basis(oracle_price, now)?;

let oracle_plus_funding_basis = oracle_price.safe_add(last_funding_basis)?.unsigned_abs();

let median_price = if last_fill_price > 0 {
let mut prices = [
last_fill_price,
oracle_plus_funding_basis,
oracle_plus_basis_5min,
];
prices.sort_unstable();

prices[1]
} else {
let mut prices = [
oracle_price.unsigned_abs(),
oracle_plus_funding_basis,
oracle_plus_basis_5min,
];
prices.sort_unstable();

prices[1]
};

self.clamp_trigger_price(oracle_price.unsigned_abs(), median_price)
}

#[inline(always)]
fn get_last_funding_basis(&self, oracle_price: i64, now: i64) -> DriftResult<i64> {
if self.amm.last_funding_oracle_twap > 0 {
let last_funding_rate = self
.amm
.last_funding_rate
.cast::<i128>()?
.safe_mul(PRICE_PRECISION_I128)?
.safe_div(self.amm.last_funding_oracle_twap.cast::<i128>()?)?
.safe_mul(24)?;
let last_funding_rate_pre_adj =
last_funding_rate.safe_sub(FUNDING_RATE_OFFSET_PERCENTAGE as i128)?;

let time_left_until_funding_update = now
.safe_sub(self.amm.last_funding_rate_ts)?
.min(self.amm.funding_period);

let last_funding_basis = oracle_price
.cast::<i128>()?
.safe_mul(last_funding_rate_pre_adj)?
.safe_div(PERCENTAGE_PRECISION_I128)?
.safe_mul(
self.amm
.funding_period
.safe_sub(time_left_until_funding_update)?
.cast::<i128>()?,
)?
.safe_div(self.amm.funding_period.cast::<i128>()?)?
/ FUNDING_RATE_BUFFER_I128;

last_funding_basis.cast::<i64>()
} else {
Ok(0)
}
}

#[inline(always)]
fn clamp_trigger_price(&self, oracle_price: u64, median_price: u64) -> DriftResult<u64> {
let max_bps_diff = if matches!(self.contract_tier, ContractTier::A | ContractTier::B) {
500 // 20 BPS
} else {
100 // 100 BPS
};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

may be worth separating C/Speculative tiers

let max_oracle_diff = oracle_price / max_bps_diff;

Ok(median_price
.max(oracle_price.safe_sub(max_oracle_diff)?)
.min(oracle_price.safe_add(max_oracle_diff)?))
}
}

#[cfg(test)]
Expand Down Expand Up @@ -912,10 +1006,10 @@ pub struct AMM {
/// precision: AMM_RESERVE_PRECISION
pub user_lp_shares: u128,
/// last funding rate in this perp market (unit is quote per base)
/// precision: QUOTE_PRECISION
/// precision: FUNDING_RATE_PRECISION
pub last_funding_rate: i64,
/// last funding rate for longs in this perp market (unit is quote per base)
/// precision: QUOTE_PRECISION
/// precision: FUNDING_RATE_PRECISION
pub last_funding_rate_long: i64,
/// last funding rate for shorts in this perp market (unit is quote per base)
/// precision: QUOTE_PRECISION
Expand Down Expand Up @@ -1058,7 +1152,8 @@ pub struct AMM {
pub reference_price_offset: i32,
/// signed scale amm_spread similar to fee_adjustment logic (-100 = 0, 100 = double)
pub amm_inventory_spread_adjustment: i8,
pub padding: [u8; 11],
pub padding: [u8; 3],
pub last_funding_oracle_twap: i64,
}

impl Default for AMM {
Expand Down Expand Up @@ -1149,7 +1244,8 @@ impl Default for AMM {
quote_asset_amount_with_unsettled_lp: 0,
reference_price_offset: 0,
amm_inventory_spread_adjustment: 0,
padding: [0; 11],
padding: [0; 3],
last_funding_oracle_twap: 0,
}
}
}
Expand Down
Loading
Loading