Skip to content

Commit 2aa80ab

Browse files
committed
Merge branch 'crispheaney/auto-cancel-tpsl' into devnet
2 parents b8fc014 + 84803e6 commit 2aa80ab

File tree

2 files changed

+287
-0
lines changed

2 files changed

+287
-0
lines changed

programs/drift/src/controller/orders.rs

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1394,6 +1394,20 @@ pub fn fill_perp_order(
13941394
)?
13951395
}
13961396

1397+
if base_asset_amount_after == 0 && user.perp_positions[position_index].open_orders != 0 {
1398+
cancel_reduce_only_trigger_orders(
1399+
user,
1400+
&user_key,
1401+
Some(&filler_key),
1402+
perp_market_map,
1403+
spot_market_map,
1404+
oracle_map,
1405+
now,
1406+
slot,
1407+
market_index,
1408+
)?;
1409+
}
1410+
13971411
if base_asset_amount == 0 {
13981412
return Ok((base_asset_amount, quote_asset_amount));
13991413
}
@@ -3109,6 +3123,57 @@ fn get_taker_and_maker_for_order_record(
31093123
}
31103124
}
31113125

3126+
fn cancel_reduce_only_trigger_orders(
3127+
user: &mut User,
3128+
user_key: &Pubkey,
3129+
filler_key: Option<&Pubkey>,
3130+
perp_market_map: &PerpMarketMap,
3131+
spot_market_map: &SpotMarketMap,
3132+
oracle_map: &mut OracleMap,
3133+
now: i64,
3134+
slot: u64,
3135+
perp_market_index: u16,
3136+
) -> DriftResult {
3137+
for order_index in 0..user.orders.len() {
3138+
if user.orders[order_index].status != OrderStatus::Open {
3139+
continue;
3140+
}
3141+
3142+
if user.orders[order_index].market_type != MarketType::Perp {
3143+
continue;
3144+
}
3145+
3146+
if user.orders[order_index].market_index != perp_market_index {
3147+
continue;
3148+
}
3149+
3150+
if !user.orders[order_index].must_be_triggered() {
3151+
continue;
3152+
}
3153+
3154+
if !user.orders[order_index].reduce_only {
3155+
continue;
3156+
}
3157+
3158+
cancel_order(
3159+
order_index,
3160+
user,
3161+
user_key,
3162+
perp_market_map,
3163+
spot_market_map,
3164+
oracle_map,
3165+
now,
3166+
slot,
3167+
OrderActionExplanation::ReduceOnlyOrderIncreasedPosition,
3168+
filler_key,
3169+
0,
3170+
false,
3171+
)?;
3172+
}
3173+
3174+
Ok(())
3175+
}
3176+
31123177
pub fn trigger_order(
31133178
order_id: u32,
31143179
state: &State,

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

Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10397,6 +10397,228 @@ pub mod force_cancel_orders {
1039710397
}
1039810398
}
1039910399

10400+
pub mod cancel_reduce_only_trigger_orders {
10401+
use std::str::FromStr;
10402+
10403+
use anchor_lang::prelude::Clock;
10404+
10405+
use crate::controller::orders::cancel_reduce_only_trigger_orders;
10406+
use crate::controller::position::PositionDirection;
10407+
use crate::create_account_info;
10408+
use crate::create_anchor_account_info;
10409+
use crate::math::constants::{
10410+
AMM_RESERVE_PRECISION, BASE_PRECISION_I64, LAMPORTS_PER_SOL_I64, PEG_PRECISION,
10411+
SPOT_BALANCE_PRECISION, SPOT_BALANCE_PRECISION_U64, SPOT_CUMULATIVE_INTEREST_PRECISION,
10412+
SPOT_WEIGHT_PRECISION,
10413+
};
10414+
use crate::state::oracle::HistoricalOracleData;
10415+
use crate::state::oracle::OracleSource;
10416+
use crate::state::perp_market::{PerpMarket, AMM};
10417+
use crate::state::perp_market_map::PerpMarketMap;
10418+
use crate::state::spot_market::{SpotBalanceType, SpotMarket};
10419+
use crate::state::spot_market_map::SpotMarketMap;
10420+
use crate::state::state::State;
10421+
use crate::state::user::{MarketType, OrderStatus, OrderType, SpotPosition, User};
10422+
use crate::test_utils::*;
10423+
use crate::test_utils::{
10424+
create_account_info, get_positions, get_pyth_price, get_spot_positions,
10425+
};
10426+
10427+
use super::*;
10428+
10429+
#[test]
10430+
fn test() {
10431+
let clock = Clock {
10432+
slot: 6,
10433+
epoch_start_timestamp: 0,
10434+
epoch: 0,
10435+
leader_schedule_epoch: 0,
10436+
unix_timestamp: 0,
10437+
};
10438+
10439+
let mut oracle_price = get_pyth_price(100, 6);
10440+
let oracle_price_key =
10441+
Pubkey::from_str("J83w4HKfqxwcq3BEMMkPFSppX3gqekLyLJBexebFVkix").unwrap();
10442+
let pyth_program = crate::ids::pyth_program::id();
10443+
create_account_info!(
10444+
oracle_price,
10445+
&oracle_price_key,
10446+
&pyth_program,
10447+
oracle_account_info
10448+
);
10449+
let mut oracle_map = OracleMap::load_one(&oracle_account_info, clock.slot, None).unwrap();
10450+
10451+
let mut market = PerpMarket {
10452+
amm: AMM {
10453+
base_asset_reserve: 100 * AMM_RESERVE_PRECISION,
10454+
quote_asset_reserve: 100 * AMM_RESERVE_PRECISION,
10455+
terminal_quote_asset_reserve: 100 * AMM_RESERVE_PRECISION,
10456+
// bid_base_asset_reserve: 101 * AMM_RESERVE_PRECISION,
10457+
// bid_quote_asset_reserve: 99 * AMM_RESERVE_PRECISION,
10458+
// ask_base_asset_reserve: 99 * AMM_RESERVE_PRECISION,
10459+
// ask_quote_asset_reserve: 101 * AMM_RESERVE_PRECISION,
10460+
sqrt_k: 100 * AMM_RESERVE_PRECISION,
10461+
peg_multiplier: 100 * PEG_PRECISION,
10462+
max_slippage_ratio: 100,
10463+
max_fill_reserve_fraction: 100,
10464+
order_step_size: 1000,
10465+
order_tick_size: 1,
10466+
oracle: oracle_price_key,
10467+
max_spread: 1000,
10468+
base_spread: 0,
10469+
long_spread: 0,
10470+
short_spread: 0,
10471+
historical_oracle_data: HistoricalOracleData {
10472+
last_oracle_price_twap: oracle_price.twap,
10473+
last_oracle_price_twap_5min: oracle_price.twap,
10474+
last_oracle_price: oracle_price.agg.price,
10475+
..HistoricalOracleData::default()
10476+
},
10477+
..AMM::default()
10478+
},
10479+
margin_ratio_initial: 1000,
10480+
margin_ratio_maintenance: 500,
10481+
status: MarketStatus::Initialized,
10482+
..PerpMarket::default()
10483+
};
10484+
market.status = MarketStatus::Active;
10485+
market.amm.max_base_asset_reserve = u128::MAX;
10486+
market.amm.min_base_asset_reserve = 0;
10487+
let (new_ask_base_asset_reserve, new_ask_quote_asset_reserve) =
10488+
crate::math::amm_spread::calculate_spread_reserves(&market, PositionDirection::Long)
10489+
.unwrap();
10490+
let (new_bid_base_asset_reserve, new_bid_quote_asset_reserve) =
10491+
crate::math::amm_spread::calculate_spread_reserves(&market, PositionDirection::Short)
10492+
.unwrap();
10493+
market.amm.ask_base_asset_reserve = new_ask_base_asset_reserve;
10494+
market.amm.bid_base_asset_reserve = new_bid_base_asset_reserve;
10495+
market.amm.ask_quote_asset_reserve = new_ask_quote_asset_reserve;
10496+
market.amm.bid_quote_asset_reserve = new_bid_quote_asset_reserve;
10497+
create_anchor_account_info!(market, PerpMarket, market_account_info);
10498+
let market_map = PerpMarketMap::load_one(&market_account_info, true).unwrap();
10499+
10500+
let mut usdc_spot_market = SpotMarket {
10501+
market_index: 0,
10502+
oracle_source: OracleSource::QuoteAsset,
10503+
deposit_balance: SPOT_BALANCE_PRECISION,
10504+
cumulative_deposit_interest: SPOT_CUMULATIVE_INTEREST_PRECISION,
10505+
cumulative_borrow_interest: SPOT_CUMULATIVE_INTEREST_PRECISION,
10506+
decimals: 6,
10507+
initial_asset_weight: SPOT_WEIGHT_PRECISION,
10508+
maintenance_asset_weight: SPOT_WEIGHT_PRECISION,
10509+
..SpotMarket::default()
10510+
};
10511+
create_anchor_account_info!(usdc_spot_market, SpotMarket, usdc_spot_market_account_info);
10512+
10513+
let mut sol_spot_market = SpotMarket {
10514+
market_index: 1,
10515+
deposit_balance: SPOT_BALANCE_PRECISION,
10516+
cumulative_deposit_interest: SPOT_CUMULATIVE_INTEREST_PRECISION,
10517+
cumulative_borrow_interest: SPOT_CUMULATIVE_INTEREST_PRECISION,
10518+
oracle: oracle_price_key,
10519+
..SpotMarket::default_base_market()
10520+
};
10521+
create_anchor_account_info!(sol_spot_market, SpotMarket, sol_spot_market_account_info);
10522+
10523+
let spot_market_map = SpotMarketMap::load_multiple(
10524+
vec![
10525+
&usdc_spot_market_account_info,
10526+
&sol_spot_market_account_info,
10527+
],
10528+
true,
10529+
)
10530+
.unwrap();
10531+
10532+
let mut orders = [Order::default(); 32];
10533+
orders[0] = Order {
10534+
market_index: 0,
10535+
order_id: 1,
10536+
status: OrderStatus::Open,
10537+
order_type: OrderType::Limit,
10538+
market_type: MarketType::Perp,
10539+
..Order::default()
10540+
};
10541+
orders[1] = Order {
10542+
market_index: 1,
10543+
order_id: 2,
10544+
status: OrderStatus::Open,
10545+
order_type: OrderType::TriggerMarket,
10546+
market_type: MarketType::Perp,
10547+
reduce_only: true,
10548+
..Order::default()
10549+
};
10550+
orders[2] = Order {
10551+
market_index: 0,
10552+
order_id: 2,
10553+
status: OrderStatus::Open,
10554+
order_type: OrderType::TriggerMarket,
10555+
market_type: MarketType::Perp,
10556+
reduce_only: true,
10557+
..Order::default()
10558+
};
10559+
orders[3] = Order {
10560+
market_index: 0,
10561+
order_id: 2,
10562+
status: OrderStatus::Open,
10563+
order_type: OrderType::TriggerMarket,
10564+
market_type: MarketType::Spot,
10565+
reduce_only: true,
10566+
..Order::default()
10567+
};
10568+
orders[4] = Order {
10569+
market_index: 0,
10570+
order_id: 2,
10571+
status: OrderStatus::Open,
10572+
order_type: OrderType::TriggerLimit,
10573+
market_type: MarketType::Perp,
10574+
reduce_only: true,
10575+
..Order::default()
10576+
};
10577+
10578+
let mut user = User {
10579+
authority: Pubkey::from_str("My11111111111111111111111111111111111111111").unwrap(), // different authority than filler
10580+
orders,
10581+
perp_positions: get_positions(PerpPosition {
10582+
market_index: 0,
10583+
base_asset_amount: BASE_PRECISION_I64,
10584+
open_orders: 2,
10585+
open_bids: 100 * BASE_PRECISION_I64,
10586+
open_asks: -BASE_PRECISION_I64,
10587+
..PerpPosition::default()
10588+
}),
10589+
spot_positions: get_spot_positions(SpotPosition {
10590+
market_index: 1,
10591+
balance_type: SpotBalanceType::Deposit,
10592+
scaled_balance: SPOT_BALANCE_PRECISION_U64,
10593+
open_orders: 2,
10594+
open_bids: 100 * LAMPORTS_PER_SOL_I64,
10595+
open_asks: -LAMPORTS_PER_SOL_I64,
10596+
..SpotPosition::default()
10597+
}),
10598+
..User::default()
10599+
};
10600+
10601+
cancel_reduce_only_trigger_orders(
10602+
&mut user,
10603+
&Pubkey::default(),
10604+
Some(&Pubkey::default()),
10605+
&market_map,
10606+
&spot_market_map,
10607+
&mut oracle_map,
10608+
0,
10609+
0,
10610+
0,
10611+
)
10612+
.unwrap();
10613+
10614+
assert_eq!(user.orders[0].status, OrderStatus::Open);
10615+
assert_eq!(user.orders[1].status, OrderStatus::Open);
10616+
assert_eq!(user.orders[2].status, OrderStatus::Canceled);
10617+
assert_eq!(user.orders[3].status, OrderStatus::Open);
10618+
assert_eq!(user.orders[4].status, OrderStatus::Canceled);
10619+
}
10620+
}
10621+
1040010622
pub mod insert_maker_order_info {
1040110623
use crate::controller::orders::insert_maker_order_info;
1040210624
use crate::controller::position::PositionDirection;

0 commit comments

Comments
 (0)