Skip to content

Commit ffc1d81

Browse files
Crispheaney/auto cancel tpsl (#2102)
* program: add auto cancel reduce only tpsl * cargo test * cargo fmt -- * tweaks --------- Co-authored-by: Chris Heaney <chrisheaney30@gmail.com>
1 parent b177280 commit ffc1d81

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
@@ -1369,6 +1369,20 @@ pub fn fill_perp_order(
13691369
)?
13701370
}
13711371

1372+
if base_asset_amount_after == 0 && user.perp_positions[position_index].open_orders != 0 {
1373+
cancel_reduce_only_trigger_orders(
1374+
user,
1375+
&user_key,
1376+
Some(&filler_key),
1377+
perp_market_map,
1378+
spot_market_map,
1379+
oracle_map,
1380+
now,
1381+
slot,
1382+
market_index,
1383+
)?;
1384+
}
1385+
13721386
if base_asset_amount == 0 {
13731387
return Ok((base_asset_amount, quote_asset_amount));
13741388
}
@@ -3098,6 +3112,57 @@ fn get_taker_and_maker_for_order_record(
30983112
}
30993113
}
31003114

3115+
fn cancel_reduce_only_trigger_orders(
3116+
user: &mut User,
3117+
user_key: &Pubkey,
3118+
filler_key: Option<&Pubkey>,
3119+
perp_market_map: &PerpMarketMap,
3120+
spot_market_map: &SpotMarketMap,
3121+
oracle_map: &mut OracleMap,
3122+
now: i64,
3123+
slot: u64,
3124+
perp_market_index: u16,
3125+
) -> DriftResult {
3126+
for order_index in 0..user.orders.len() {
3127+
if user.orders[order_index].status != OrderStatus::Open {
3128+
continue;
3129+
}
3130+
3131+
if user.orders[order_index].market_type != MarketType::Perp {
3132+
continue;
3133+
}
3134+
3135+
if user.orders[order_index].market_index != perp_market_index {
3136+
continue;
3137+
}
3138+
3139+
if !user.orders[order_index].must_be_triggered() || user.orders[order_index].triggered() {
3140+
continue;
3141+
}
3142+
3143+
if !user.orders[order_index].reduce_only {
3144+
continue;
3145+
}
3146+
3147+
cancel_order(
3148+
order_index,
3149+
user,
3150+
user_key,
3151+
perp_market_map,
3152+
spot_market_map,
3153+
oracle_map,
3154+
now,
3155+
slot,
3156+
OrderActionExplanation::ReduceOnlyOrderIncreasedPosition,
3157+
filler_key,
3158+
0,
3159+
false,
3160+
)?;
3161+
}
3162+
3163+
Ok(())
3164+
}
3165+
31013166
pub fn trigger_order(
31023167
order_id: u32,
31033168
state: &State,

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

Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10593,6 +10593,228 @@ pub mod force_cancel_orders {
1059310593
}
1059410594
}
1059510595

10596+
pub mod cancel_reduce_only_trigger_orders {
10597+
use std::str::FromStr;
10598+
10599+
use anchor_lang::prelude::Clock;
10600+
10601+
use crate::controller::orders::cancel_reduce_only_trigger_orders;
10602+
use crate::controller::position::PositionDirection;
10603+
use crate::create_account_info;
10604+
use crate::create_anchor_account_info;
10605+
use crate::math::constants::{
10606+
AMM_RESERVE_PRECISION, BASE_PRECISION_I64, LAMPORTS_PER_SOL_I64, PEG_PRECISION,
10607+
SPOT_BALANCE_PRECISION, SPOT_BALANCE_PRECISION_U64, SPOT_CUMULATIVE_INTEREST_PRECISION,
10608+
SPOT_WEIGHT_PRECISION,
10609+
};
10610+
use crate::state::oracle::HistoricalOracleData;
10611+
use crate::state::oracle::OracleSource;
10612+
use crate::state::perp_market::{PerpMarket, AMM};
10613+
use crate::state::perp_market_map::PerpMarketMap;
10614+
use crate::state::spot_market::{SpotBalanceType, SpotMarket};
10615+
use crate::state::spot_market_map::SpotMarketMap;
10616+
use crate::state::state::State;
10617+
use crate::state::user::{MarketType, OrderStatus, OrderType, SpotPosition, User};
10618+
use crate::test_utils::*;
10619+
use crate::test_utils::{
10620+
create_account_info, get_positions, get_pyth_price, get_spot_positions,
10621+
};
10622+
10623+
use super::*;
10624+
10625+
#[test]
10626+
fn test() {
10627+
let clock = Clock {
10628+
slot: 6,
10629+
epoch_start_timestamp: 0,
10630+
epoch: 0,
10631+
leader_schedule_epoch: 0,
10632+
unix_timestamp: 0,
10633+
};
10634+
10635+
let mut oracle_price = get_pyth_price(100, 6);
10636+
let oracle_price_key =
10637+
Pubkey::from_str("J83w4HKfqxwcq3BEMMkPFSppX3gqekLyLJBexebFVkix").unwrap();
10638+
let pyth_program = crate::ids::pyth_program::id();
10639+
create_account_info!(
10640+
oracle_price,
10641+
&oracle_price_key,
10642+
&pyth_program,
10643+
oracle_account_info
10644+
);
10645+
let mut oracle_map = OracleMap::load_one(&oracle_account_info, clock.slot, None).unwrap();
10646+
10647+
let mut market = PerpMarket {
10648+
amm: AMM {
10649+
base_asset_reserve: 100 * AMM_RESERVE_PRECISION,
10650+
quote_asset_reserve: 100 * AMM_RESERVE_PRECISION,
10651+
terminal_quote_asset_reserve: 100 * AMM_RESERVE_PRECISION,
10652+
// bid_base_asset_reserve: 101 * AMM_RESERVE_PRECISION,
10653+
// bid_quote_asset_reserve: 99 * AMM_RESERVE_PRECISION,
10654+
// ask_base_asset_reserve: 99 * AMM_RESERVE_PRECISION,
10655+
// ask_quote_asset_reserve: 101 * AMM_RESERVE_PRECISION,
10656+
sqrt_k: 100 * AMM_RESERVE_PRECISION,
10657+
peg_multiplier: 100 * PEG_PRECISION,
10658+
max_slippage_ratio: 100,
10659+
max_fill_reserve_fraction: 100,
10660+
order_step_size: 1000,
10661+
order_tick_size: 1,
10662+
oracle: oracle_price_key,
10663+
max_spread: 1000,
10664+
base_spread: 0,
10665+
long_spread: 0,
10666+
short_spread: 0,
10667+
historical_oracle_data: HistoricalOracleData {
10668+
last_oracle_price_twap: oracle_price.twap,
10669+
last_oracle_price_twap_5min: oracle_price.twap,
10670+
last_oracle_price: oracle_price.agg.price,
10671+
..HistoricalOracleData::default()
10672+
},
10673+
..AMM::default()
10674+
},
10675+
margin_ratio_initial: 1000,
10676+
margin_ratio_maintenance: 500,
10677+
status: MarketStatus::Initialized,
10678+
..PerpMarket::default()
10679+
};
10680+
market.status = MarketStatus::Active;
10681+
market.amm.max_base_asset_reserve = u128::MAX;
10682+
market.amm.min_base_asset_reserve = 0;
10683+
let (new_ask_base_asset_reserve, new_ask_quote_asset_reserve) =
10684+
crate::math::amm_spread::calculate_spread_reserves(&market, PositionDirection::Long)
10685+
.unwrap();
10686+
let (new_bid_base_asset_reserve, new_bid_quote_asset_reserve) =
10687+
crate::math::amm_spread::calculate_spread_reserves(&market, PositionDirection::Short)
10688+
.unwrap();
10689+
market.amm.ask_base_asset_reserve = new_ask_base_asset_reserve;
10690+
market.amm.bid_base_asset_reserve = new_bid_base_asset_reserve;
10691+
market.amm.ask_quote_asset_reserve = new_ask_quote_asset_reserve;
10692+
market.amm.bid_quote_asset_reserve = new_bid_quote_asset_reserve;
10693+
create_anchor_account_info!(market, PerpMarket, market_account_info);
10694+
let market_map = PerpMarketMap::load_one(&market_account_info, true).unwrap();
10695+
10696+
let mut usdc_spot_market = SpotMarket {
10697+
market_index: 0,
10698+
oracle_source: OracleSource::QuoteAsset,
10699+
deposit_balance: SPOT_BALANCE_PRECISION,
10700+
cumulative_deposit_interest: SPOT_CUMULATIVE_INTEREST_PRECISION,
10701+
cumulative_borrow_interest: SPOT_CUMULATIVE_INTEREST_PRECISION,
10702+
decimals: 6,
10703+
initial_asset_weight: SPOT_WEIGHT_PRECISION,
10704+
maintenance_asset_weight: SPOT_WEIGHT_PRECISION,
10705+
..SpotMarket::default()
10706+
};
10707+
create_anchor_account_info!(usdc_spot_market, SpotMarket, usdc_spot_market_account_info);
10708+
10709+
let mut sol_spot_market = SpotMarket {
10710+
market_index: 1,
10711+
deposit_balance: SPOT_BALANCE_PRECISION,
10712+
cumulative_deposit_interest: SPOT_CUMULATIVE_INTEREST_PRECISION,
10713+
cumulative_borrow_interest: SPOT_CUMULATIVE_INTEREST_PRECISION,
10714+
oracle: oracle_price_key,
10715+
..SpotMarket::default_base_market()
10716+
};
10717+
create_anchor_account_info!(sol_spot_market, SpotMarket, sol_spot_market_account_info);
10718+
10719+
let spot_market_map = SpotMarketMap::load_multiple(
10720+
vec![
10721+
&usdc_spot_market_account_info,
10722+
&sol_spot_market_account_info,
10723+
],
10724+
true,
10725+
)
10726+
.unwrap();
10727+
10728+
let mut orders = [Order::default(); 32];
10729+
orders[0] = Order {
10730+
market_index: 0,
10731+
order_id: 1,
10732+
status: OrderStatus::Open,
10733+
order_type: OrderType::Limit,
10734+
market_type: MarketType::Perp,
10735+
..Order::default()
10736+
};
10737+
orders[1] = Order {
10738+
market_index: 1,
10739+
order_id: 2,
10740+
status: OrderStatus::Open,
10741+
order_type: OrderType::TriggerMarket,
10742+
market_type: MarketType::Perp,
10743+
reduce_only: true,
10744+
..Order::default()
10745+
};
10746+
orders[2] = Order {
10747+
market_index: 0,
10748+
order_id: 2,
10749+
status: OrderStatus::Open,
10750+
order_type: OrderType::TriggerMarket,
10751+
market_type: MarketType::Perp,
10752+
reduce_only: true,
10753+
..Order::default()
10754+
};
10755+
orders[3] = Order {
10756+
market_index: 0,
10757+
order_id: 2,
10758+
status: OrderStatus::Open,
10759+
order_type: OrderType::TriggerMarket,
10760+
market_type: MarketType::Spot,
10761+
reduce_only: true,
10762+
..Order::default()
10763+
};
10764+
orders[4] = Order {
10765+
market_index: 0,
10766+
order_id: 2,
10767+
status: OrderStatus::Open,
10768+
order_type: OrderType::TriggerLimit,
10769+
market_type: MarketType::Perp,
10770+
reduce_only: true,
10771+
..Order::default()
10772+
};
10773+
10774+
let mut user = User {
10775+
authority: Pubkey::from_str("My11111111111111111111111111111111111111111").unwrap(), // different authority than filler
10776+
orders,
10777+
perp_positions: get_positions(PerpPosition {
10778+
market_index: 0,
10779+
base_asset_amount: BASE_PRECISION_I64,
10780+
open_orders: 2,
10781+
open_bids: 100 * BASE_PRECISION_I64,
10782+
open_asks: -BASE_PRECISION_I64,
10783+
..PerpPosition::default()
10784+
}),
10785+
spot_positions: get_spot_positions(SpotPosition {
10786+
market_index: 1,
10787+
balance_type: SpotBalanceType::Deposit,
10788+
scaled_balance: SPOT_BALANCE_PRECISION_U64,
10789+
open_orders: 2,
10790+
open_bids: 100 * LAMPORTS_PER_SOL_I64,
10791+
open_asks: -LAMPORTS_PER_SOL_I64,
10792+
..SpotPosition::default()
10793+
}),
10794+
..User::default()
10795+
};
10796+
10797+
cancel_reduce_only_trigger_orders(
10798+
&mut user,
10799+
&Pubkey::default(),
10800+
Some(&Pubkey::default()),
10801+
&market_map,
10802+
&spot_market_map,
10803+
&mut oracle_map,
10804+
0,
10805+
0,
10806+
0,
10807+
)
10808+
.unwrap();
10809+
10810+
assert_eq!(user.orders[0].status, OrderStatus::Open);
10811+
assert_eq!(user.orders[1].status, OrderStatus::Open);
10812+
assert_eq!(user.orders[2].status, OrderStatus::Canceled);
10813+
assert_eq!(user.orders[3].status, OrderStatus::Open);
10814+
assert_eq!(user.orders[4].status, OrderStatus::Canceled);
10815+
}
10816+
}
10817+
1059610818
pub mod insert_maker_order_info {
1059710819
use crate::controller::orders::insert_maker_order_info;
1059810820
use crate::controller::position::PositionDirection;

0 commit comments

Comments
 (0)