Skip to content

Commit 629c26a

Browse files
pranay-hftpranay123-stack
authored andcommitted
Fix: Auto-cancel trigger orders when position is closed
Resolves issue #923 where stop-loss (trigger) orders were not being automatically cancelled when a user closed their position. Changes: - Added new function `cancel_trigger_orders_for_closed_position()` that cancels all trigger orders (TriggerMarket and TriggerLimit) for a specific market when called - Modified `fulfill_perp_order()` to check if a position is completely closed after fulfillment (base_asset_amount == 0) - If position is closed, automatically cancel all trigger orders for that market to prevent unintended order execution This prevents the following risk scenario: 1. User opens a short position with a stop-loss order 2. User manually closes the short position 3. Previously: SL order remained active and could execute unexpectedly 4. Now: SL order is automatically cancelled when position closes
1 parent c74d5e9 commit 629c26a

File tree

1 file changed

+69
-0
lines changed

1 file changed

+69
-0
lines changed

programs/drift/src/controller/orders.rs

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -584,6 +584,57 @@ pub fn cancel_orders(
584584
Ok(canceled_order_ids)
585585
}
586586

587+
pub fn cancel_trigger_orders_for_closed_position(
588+
user: &mut User,
589+
user_key: &Pubkey,
590+
market_index: u16,
591+
market_type: MarketType,
592+
perp_market_map: &PerpMarketMap,
593+
spot_market_map: &SpotMarketMap,
594+
oracle_map: &mut OracleMap,
595+
now: i64,
596+
slot: u64,
597+
) -> DriftResult {
598+
for order_index in 0..user.orders.len() {
599+
if user.orders[order_index].status != OrderStatus::Open {
600+
continue;
601+
}
602+
603+
// Only cancel trigger orders (TriggerMarket or TriggerLimit)
604+
if !user.orders[order_index].must_be_triggered() {
605+
continue;
606+
}
607+
608+
// Only cancel orders for the specified market
609+
if user.orders[order_index].market_type != market_type {
610+
continue;
611+
}
612+
613+
if user.orders[order_index].market_index != market_index {
614+
continue;
615+
}
616+
617+
cancel_order(
618+
order_index,
619+
user,
620+
user_key,
621+
perp_market_map,
622+
spot_market_map,
623+
oracle_map,
624+
now,
625+
slot,
626+
OrderActionExplanation::OrderExpired,
627+
None,
628+
0,
629+
false,
630+
)?;
631+
}
632+
633+
user.update_last_active_slot(slot);
634+
635+
Ok(())
636+
}
637+
587638
pub fn cancel_order_by_order_id(
588639
order_id: u32,
589640
user: &AccountLoader<User>,
@@ -2042,6 +2093,24 @@ fn fulfill_perp_order(
20422093
)?;
20432094
}
20442095

2096+
// Cancel all trigger orders if position is completely closed
2097+
if base_asset_amount > 0 {
2098+
let position_index = get_position_index(&user.perp_positions, market_index)?;
2099+
if user.perp_positions[position_index].base_asset_amount == 0 {
2100+
cancel_trigger_orders_for_closed_position(
2101+
user,
2102+
user_key,
2103+
market_index,
2104+
MarketType::Perp,
2105+
perp_market_map,
2106+
spot_market_map,
2107+
oracle_map,
2108+
now,
2109+
slot,
2110+
)?;
2111+
}
2112+
}
2113+
20452114
Ok((base_asset_amount, quote_asset_amount))
20462115
}
20472116

0 commit comments

Comments
 (0)