critical: Bug #90 - Use placeAndTakePerpOrder for immediate MARKET order fills
ROOT CAUSE: placePerpOrder() only places orders on Drift order book, doesn't fill. SOLUTION: placeAndTakePerpOrder() places AND matches against makers atomically. Real Incident (Dec 31, 2025): - Dec 30 18:17: SHORT opened at $124.36 - Dec 31 00:30: LONG signal received - should flip position - Transaction confirmed but Solscan showed 'Place' not 'Fill' - Position remained open, eventually hit SL twice - Total loss: ~$40.21 Files changed: - lib/drift/orders.ts (line 662): placePerpOrder → placeAndTakePerpOrder - docs/COMMON_PITFALLS.md: Added Bug #90 documentation Deployment: Dec 31, 2025 11:38 CET (container trading-bot-v4)
This commit is contained in:
@@ -644,7 +644,7 @@ export async function closePosition(
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare close order (opposite direction) - use simple structure like v3
|
||||
// Prepare close order (opposite direction)
|
||||
const orderParams = {
|
||||
orderType: OrderType.MARKET,
|
||||
marketIndex: marketConfig.driftMarketIndex,
|
||||
@@ -655,14 +655,26 @@ export async function closePosition(
|
||||
reduceOnly: true, // Important: only close existing position
|
||||
}
|
||||
|
||||
// Place market close order using simple placePerpOrder (like v3)
|
||||
// CRITICAL: Wrap in retry logic for rate limit protection
|
||||
logger.log('🚀 Placing REAL market close order with retry protection...')
|
||||
// CRITICAL FIX (Dec 31, 2025 - Bug #89): Use placeAndTakePerpOrder instead of placePerpOrder
|
||||
//
|
||||
// ROOT CAUSE: placePerpOrder only PLACES an order on the order book.
|
||||
// For MARKET orders, the transaction confirms when the order is placed, NOT when it's filled.
|
||||
// This caused the flip operation to fail - close tx confirmed but position never closed.
|
||||
//
|
||||
// SOLUTION: placeAndTakePerpOrder places the order AND fills it against makers atomically.
|
||||
// This guarantees the position is closed when the transaction confirms.
|
||||
//
|
||||
// Evidence: Solscan showed tx 5E713pD6... as "Place long 24.72 SOL perp order at price 0"
|
||||
// - The order was placed on the book but never filled
|
||||
// - Position remained open despite "confirmed" transaction
|
||||
//
|
||||
// See: https://github.com/drift-labs/drift-vaults/blob/main/tests/testHelpers.ts#L1542
|
||||
console.log('🚀 Placing market close order with IMMEDIATE FILL (placeAndTakePerpOrder)...')
|
||||
const txSig = await retryWithBackoff(async () => {
|
||||
return await driftClient.placePerpOrder(orderParams)
|
||||
return await driftClient.placeAndTakePerpOrder(orderParams)
|
||||
}, 3, 8000) // 8s base delay, 3 max retries
|
||||
|
||||
logger.log(`✅ Close order placed! Transaction: ${txSig}`)
|
||||
console.log(`✅ Close order executed! Transaction: ${txSig}`)
|
||||
|
||||
// CRITICAL: Confirm transaction on-chain to prevent phantom closes
|
||||
// BUT: Use timeout to prevent API hangs during network congestion
|
||||
|
||||
Reference in New Issue
Block a user