critical: Auto-restore missing on-chain orders on startup
PROBLEM (Nov 16, 22:03): - Ghost position closed with -$6.77 loss - Validator cleanup removed orders - Position existed on Drift with NO on-chain TP/SL - Only Position Manager software protection active - If bot crashes, position completely unprotected FIX: - Added restoreOrdersIfMissing() to startup validator - Checks every verified position for orders - Automatically places TP/SL if missing - Updates database with order transaction IDs BEHAVIOR: - Runs on every container startup - Validates all open positions - Logs: '✅ {symbol} has X on-chain orders' - Or: '⚠️ {symbol} has NO orders - restoring...' - Provides dual-layer protection always Impact: Eliminates unprotected position risk after validator cleanups, container restarts, or order issues.
This commit is contained in:
@@ -191,6 +191,13 @@ async function validateOpenTrades() {
|
||||
console.log(`✅ ${trade.symbol} ${trade.direction}: Position verified on Drift`)
|
||||
}
|
||||
|
||||
// CRITICAL FIX (Nov 16, 2025): Restore missing on-chain orders
|
||||
// Ghost position closed at 22:03 because orders were missing after validator cleanup
|
||||
// This ensures EVERY verified position has on-chain TP/SL protection
|
||||
if (position && Math.abs(position.size) >= 0.01) {
|
||||
await restoreOrdersIfMissing(trade, position, driftService, prisma)
|
||||
}
|
||||
|
||||
} catch (posError) {
|
||||
console.error(`❌ Error validating trade ${trade.symbol}:`, posError)
|
||||
}
|
||||
@@ -200,3 +207,72 @@ async function validateOpenTrades() {
|
||||
console.error('❌ Error in validateOpenTrades:', error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore on-chain exit orders if missing (Nov 16, 2025)
|
||||
*
|
||||
* CRITICAL: After validator cleanups or container restarts, positions may exist
|
||||
* on Drift without any on-chain TP/SL orders. This leaves only Position Manager
|
||||
* software protection - if bot crashes, position is completely unprotected.
|
||||
*
|
||||
* This function checks if orders exist and places them if missing.
|
||||
*/
|
||||
async function restoreOrdersIfMissing(
|
||||
trade: any,
|
||||
position: any,
|
||||
driftService: any,
|
||||
prisma: any
|
||||
): Promise<void> {
|
||||
try {
|
||||
// Check if position has any reduce-only orders
|
||||
const hasOrders = position.orders && position.orders.length > 0
|
||||
|
||||
if (hasOrders) {
|
||||
console.log(`✅ ${trade.symbol} has ${position.orders.length} on-chain orders - protection active`)
|
||||
return // Orders exist, nothing to do
|
||||
}
|
||||
|
||||
console.log(`⚠️ ${trade.symbol} has NO on-chain orders - restoring TP/SL protection...`)
|
||||
|
||||
// Import order placement function
|
||||
const { placeExitOrders } = await import('../drift/orders')
|
||||
|
||||
// Place exit orders using trade's TP/SL prices
|
||||
const result = await placeExitOrders({
|
||||
symbol: trade.symbol,
|
||||
direction: trade.direction,
|
||||
entryPrice: trade.entryPrice,
|
||||
tp1Price: trade.takeProfit1Price,
|
||||
tp2Price: trade.takeProfit2Price,
|
||||
stopLossPrice: trade.stopLossPrice,
|
||||
positionSizeUSD: trade.positionSizeUSD,
|
||||
tp1SizePercent: 75,
|
||||
tp2SizePercent: 0, // TP2-as-runner
|
||||
})
|
||||
|
||||
if (result.success) {
|
||||
console.log(`✅ Orders restored for ${trade.symbol}:`)
|
||||
console.log(` TP1: $${trade.takeProfit1Price.toFixed(4)} (75%)`)
|
||||
console.log(` TP2: $${trade.takeProfit2Price.toFixed(4)} (runner trigger)`)
|
||||
console.log(` SL: $${trade.stopLossPrice.toFixed(4)}`)
|
||||
console.log(` TX: ${result.signatures?.[0]?.slice(0, 8)}...`)
|
||||
|
||||
// Update database with order transaction signatures
|
||||
await prisma.trade.update({
|
||||
where: { id: trade.id },
|
||||
data: {
|
||||
takeProfit1OrderTx: result.signatures?.[0],
|
||||
takeProfit2OrderTx: result.signatures?.[1],
|
||||
stopLossOrderTx: result.signatures?.[2],
|
||||
}
|
||||
})
|
||||
} else {
|
||||
console.error(`❌ Failed to restore orders for ${trade.symbol}:`, result.error)
|
||||
console.error(` 🚨 CRITICAL: Position is unprotected - only Position Manager monitoring active`)
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error(`❌ Error restoring orders for ${trade.symbol}:`, error)
|
||||
console.error(` 🚨 CRITICAL: Position may be unprotected`)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user