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

program: derive auction for crossing limit with no duration #802

Merged
merged 5 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
28 changes: 18 additions & 10 deletions programs/drift/src/controller/orders.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@
spot_market_map: &SpotMarketMap,
oracle_map: &mut OracleMap,
clock: &Clock,
params: OrderParams,
mut params: OrderParams,
mut options: PlaceOrderOptions,
) -> DriftResult {
let now = clock.unix_timestamp;
Expand Down Expand Up @@ -220,6 +220,10 @@
};

let oracle_price_data = oracle_map.get_price_data(&market.amm.oracle)?;

// updates auction params for crossing limit orders w/out auction duration
params.update_perp_auction_params(market, oracle_price_data.price)?;

Check warning on line 225 in programs/drift/src/controller/orders.rs

View check run for this annotation

Codecov / codecov/patch

programs/drift/src/controller/orders.rs#L225

Added line #L225 was not covered by tests

let (auction_start_price, auction_end_price, auction_duration) = get_auction_params(
&params,
oracle_price_data,
Expand Down Expand Up @@ -813,15 +817,19 @@
let oracle_price_offset = modify_order_params
.oracle_price_offset
.or(Some(existing_order.oracle_price_offset));
let auction_duration = modify_order_params
.auction_duration
.or(Some(existing_order.auction_duration));
let auction_start_price = modify_order_params
.auction_start_price
.or(Some(existing_order.auction_start_price));
let auction_end_price = modify_order_params
.auction_end_price
.or(Some(existing_order.auction_end_price));
let (auction_duration, auction_start_price, auction_end_price) =

Check warning on line 820 in programs/drift/src/controller/orders.rs

View check run for this annotation

Codecov / codecov/patch

programs/drift/src/controller/orders.rs#L820

Added line #L820 was not covered by tests
if modify_order_params.auction_duration.is_some()
&& modify_order_params.auction_start_price.is_some()
&& modify_order_params.auction_end_price.is_some()

Check warning on line 823 in programs/drift/src/controller/orders.rs

View check run for this annotation

Codecov / codecov/patch

programs/drift/src/controller/orders.rs#L822-L823

Added lines #L822 - L823 were not covered by tests
{
(
modify_order_params.auction_duration,
modify_order_params.auction_start_price,
modify_order_params.auction_end_price,

Check warning on line 828 in programs/drift/src/controller/orders.rs

View check run for this annotation

Codecov / codecov/patch

programs/drift/src/controller/orders.rs#L826-L828

Added lines #L826 - L828 were not covered by tests
)
} else {
(None, None, None)

Check warning on line 831 in programs/drift/src/controller/orders.rs

View check run for this annotation

Codecov / codecov/patch

programs/drift/src/controller/orders.rs#L831

Added line #L831 was not covered by tests
};

Ok(OrderParams {
order_type,
Expand Down
81 changes: 80 additions & 1 deletion programs/drift/src/state/order_params.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
use crate::controller::position::PositionDirection;
use crate::error::DriftResult;
use crate::math::casting::Cast;
use crate::math::safe_math::SafeMath;
use crate::state::perp_market::PerpMarket;
use crate::state::user::{MarketType, OrderTriggerCondition, OrderType};
use crate::PERCENTAGE_PRECISION_U64;
use anchor_lang::prelude::*;
use borsh::{BorshDeserialize, BorshSerialize};
use std::ops::Div;

#[derive(AnchorSerialize, AnchorDeserialize, Clone, Default, Copy)]
#[cfg(test)]
mod tests;

#[derive(AnchorSerialize, AnchorDeserialize, Clone, Default, Copy, Eq, PartialEq, Debug)]
pub struct OrderParams {
pub order_type: OrderType,
pub market_type: MarketType,
Expand All @@ -24,6 +33,76 @@
pub auction_end_price: Option<i64>,
}

impl OrderParams {
pub fn update_perp_auction_params(
&mut self,
perp_market: &PerpMarket,
oracle_price: i64,
) -> DriftResult {
if self.order_type != OrderType::Limit {
return Ok(());
}

if self.auction_duration.is_some() {
return Ok(());
}

if self.post_only != PostOnlyParam::None {
return Ok(());
}

if self.immediate_or_cancel {
return Ok(());
}

if self.oracle_price_offset.is_some() || self.price == 0 {
return Ok(());
}

match self.direction {
PositionDirection::Long => {

Check warning on line 63 in programs/drift/src/state/order_params.rs

View check run for this annotation

Codecov / codecov/patch

programs/drift/src/state/order_params.rs#L63

Added line #L63 was not covered by tests
let ask_premium = perp_market.amm.last_ask_premium()?;
let est_ask = oracle_price.safe_add(ask_premium)?.cast()?;
if self.price > est_ask {
let auction_duration =
get_auction_duration(self.price.safe_sub(est_ask)?, est_ask)?;

Check warning on line 68 in programs/drift/src/state/order_params.rs

View check run for this annotation

Codecov / codecov/patch

programs/drift/src/state/order_params.rs#L68

Added line #L68 was not covered by tests
let auction_start_price = est_ask as i64;
let auction_end_price = self.price as i64;
msg!("derived auction params for limit order. duration = {} start_price = {} end_price = {}", auction_duration, auction_start_price, auction_end_price);
self.auction_duration = Some(auction_duration);
self.auction_start_price = Some(auction_start_price);
self.auction_end_price = Some(auction_end_price);
}
}
PositionDirection::Short => {

Check warning on line 77 in programs/drift/src/state/order_params.rs

View check run for this annotation

Codecov / codecov/patch

programs/drift/src/state/order_params.rs#L77

Added line #L77 was not covered by tests
let bid_discount = perp_market.amm.last_bid_discount()?;
let est_bid = oracle_price.safe_sub(bid_discount)?.cast()?;
if self.price < est_bid {
let auction_duration =
get_auction_duration(est_bid.safe_sub(self.price)?, est_bid)?;

Check warning on line 82 in programs/drift/src/state/order_params.rs

View check run for this annotation

Codecov / codecov/patch

programs/drift/src/state/order_params.rs#L82

Added line #L82 was not covered by tests
let auction_start_price = est_bid as i64;
let auction_end_price = self.price as i64;
msg!("derived auction params for limit order. duration = {} start_price = {} end_price = {}", auction_duration, auction_start_price, auction_end_price);
self.auction_duration = Some(auction_duration);
self.auction_start_price = Some(auction_start_price);
self.auction_end_price = Some(auction_end_price);
}
}
}

Ok(())
}
}

fn get_auction_duration(price_diff: u64, price: u64) -> DriftResult<u8> {
let percent_diff = price_diff.safe_mul(PERCENTAGE_PRECISION_U64)?.div(price);

Ok(percent_diff
.safe_mul(60)?
.safe_div_ceil(PERCENTAGE_PRECISION_U64 / 100)? // 1% = 60 seconds

Check warning on line 102 in programs/drift/src/state/order_params.rs

View check run for this annotation

Codecov / codecov/patch

programs/drift/src/state/order_params.rs#L101-L102

Added lines #L101 - L102 were not covered by tests
.clamp(10, 60) as u8)
}

#[derive(Clone, Copy, BorshSerialize, BorshDeserialize, PartialEq, Debug, Eq)]
pub enum PostOnlyParam {
None,
Expand Down
223 changes: 223 additions & 0 deletions programs/drift/src/state/order_params/tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
mod get_auction_duration {
use crate::state::order_params::get_auction_duration;
use crate::PRICE_PRECISION_U64;

#[test]
fn test() {
let price_diff = 0;
let price = 100 * PRICE_PRECISION_U64;

let duration = get_auction_duration(price_diff, price).unwrap();
assert_eq!(duration, 10);

let price_diff = PRICE_PRECISION_U64 / 10;
let price = 100 * PRICE_PRECISION_U64;

let duration = get_auction_duration(price_diff, price).unwrap();
assert_eq!(duration, 10);

let price_diff = PRICE_PRECISION_U64 / 2;
let price = 100 * PRICE_PRECISION_U64;

let duration = get_auction_duration(price_diff, price).unwrap();
assert_eq!(duration, 30);

let price_diff = PRICE_PRECISION_U64;
let price = 100 * PRICE_PRECISION_U64;

let duration = get_auction_duration(price_diff, price).unwrap();
assert_eq!(duration, 60);

let price_diff = 2 * PRICE_PRECISION_U64;
let price = 100 * PRICE_PRECISION_U64;

let duration = get_auction_duration(price_diff, price).unwrap();
assert_eq!(duration, 60);
}
}

mod update_perp_auction_params {
use crate::state::order_params::PostOnlyParam;
use crate::state::perp_market::{PerpMarket, AMM};
use crate::state::user::OrderType;
use crate::{
OrderParams, PositionDirection, AMM_RESERVE_PRECISION, BID_ASK_SPREAD_PRECISION,
PEG_PRECISION, PRICE_PRECISION_I64, PRICE_PRECISION_U64,
};

#[test]
fn test() {
let oracle_price = 100 * PRICE_PRECISION_I64;
let mut amm = AMM {
base_asset_reserve: 100 * AMM_RESERVE_PRECISION,
quote_asset_reserve: 100 * AMM_RESERVE_PRECISION,
short_spread: (BID_ASK_SPREAD_PRECISION / 100) as u32,
long_spread: (BID_ASK_SPREAD_PRECISION / 100) as u32,
sqrt_k: 100 * AMM_RESERVE_PRECISION,
peg_multiplier: 100 * PEG_PRECISION,
..AMM::default()
};
amm.historical_oracle_data.last_oracle_price = oracle_price;
let perp_market = PerpMarket {
amm,
..PerpMarket::default()
};

let order_params_before = OrderParams {
order_type: OrderType::Market,
..OrderParams::default()
};
let mut order_params_after = order_params_before;
order_params_after
.update_perp_auction_params(&perp_market, oracle_price)
.unwrap();
assert_eq!(order_params_before, order_params_after);

let order_params_before = OrderParams {
order_type: OrderType::Limit,
auction_duration: Some(0),
..OrderParams::default()
};
let mut order_params_after = order_params_before;
order_params_after
.update_perp_auction_params(&perp_market, oracle_price)
.unwrap();
assert_eq!(order_params_before, order_params_after);

let order_params_before = OrderParams {
order_type: OrderType::Limit,
auction_duration: None,
post_only: PostOnlyParam::MustPostOnly,
..OrderParams::default()
};
let mut order_params_after = order_params_before;
order_params_after
.update_perp_auction_params(&perp_market, oracle_price)
.unwrap();
assert_eq!(order_params_before, order_params_after);

let order_params_before = OrderParams {
order_type: OrderType::Limit,
auction_duration: None,
post_only: PostOnlyParam::None,
immediate_or_cancel: true,
..OrderParams::default()
};
let mut order_params_after = order_params_before;
order_params_after
.update_perp_auction_params(&perp_market, oracle_price)
.unwrap();
assert_eq!(order_params_before, order_params_after);

let order_params_before = OrderParams {
order_type: OrderType::Limit,
auction_duration: None,
post_only: PostOnlyParam::None,
immediate_or_cancel: false,
oracle_price_offset: Some(0),
..OrderParams::default()
};
let mut order_params_after = order_params_before;
order_params_after
.update_perp_auction_params(&perp_market, oracle_price)
.unwrap();
assert_eq!(order_params_before, order_params_after);

let order_params_before = OrderParams {
order_type: OrderType::Limit,
auction_duration: None,
post_only: PostOnlyParam::None,
immediate_or_cancel: false,
oracle_price_offset: None,
price: 0,
..OrderParams::default()
};
let mut order_params_after = order_params_before;
order_params_after
.update_perp_auction_params(&perp_market, oracle_price)
.unwrap();
assert_eq!(order_params_before, order_params_after);

let order_params_before = OrderParams {
order_type: OrderType::Limit,
auction_duration: None,
post_only: PostOnlyParam::None,
immediate_or_cancel: false,
oracle_price_offset: None,
price: 100 * PRICE_PRECISION_U64,
direction: PositionDirection::Long,
..OrderParams::default()
};
let mut order_params_after = order_params_before;
order_params_after
.update_perp_auction_params(&perp_market, oracle_price)
.unwrap();
assert_eq!(order_params_before, order_params_after);

let order_params_before = OrderParams {
order_type: OrderType::Limit,
auction_duration: None,
post_only: PostOnlyParam::None,
immediate_or_cancel: false,
oracle_price_offset: None,
price: 102 * PRICE_PRECISION_U64,
direction: PositionDirection::Long,
..OrderParams::default()
};
let mut order_params_after = order_params_before;
order_params_after
.update_perp_auction_params(&perp_market, oracle_price)
.unwrap();
assert_ne!(order_params_before, order_params_after);
assert_eq!(order_params_after.auction_duration, Some(60));
assert_eq!(
order_params_after.auction_start_price,
Some(101 * PRICE_PRECISION_I64)
);
assert_eq!(
order_params_after.auction_end_price,
Some(102 * PRICE_PRECISION_I64)
);

let order_params_before = OrderParams {
order_type: OrderType::Limit,
auction_duration: None,
post_only: PostOnlyParam::None,
immediate_or_cancel: false,
oracle_price_offset: None,
price: 100 * PRICE_PRECISION_U64,
direction: PositionDirection::Short,
..OrderParams::default()
};
let mut order_params_after = order_params_before;
order_params_after
.update_perp_auction_params(&perp_market, oracle_price)
.unwrap();
assert_eq!(order_params_before, order_params_after);

let order_params_before = OrderParams {
order_type: OrderType::Limit,
auction_duration: None,
post_only: PostOnlyParam::None,
immediate_or_cancel: false,
oracle_price_offset: None,
price: 98 * PRICE_PRECISION_U64,
direction: PositionDirection::Short,
..OrderParams::default()
};
let mut order_params_after = order_params_before;
order_params_after
.update_perp_auction_params(&perp_market, oracle_price)
.unwrap();
assert_ne!(order_params_before, order_params_after);
assert_eq!(order_params_after.auction_duration, Some(60));
assert_eq!(
order_params_after.auction_start_price,
Some(99 * PRICE_PRECISION_I64)
);
assert_eq!(
order_params_after.auction_end_price,
Some(98 * PRICE_PRECISION_I64)
);
}
}
Loading
Loading