From be2410c63960b3daf6d84898e8a6842418de7d14 Mon Sep 17 00:00:00 2001 From: mindesbunister Date: Sun, 16 Nov 2025 22:18:56 +0100 Subject: [PATCH] critical: Auto-restore missing on-chain orders on startup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- lib/startup/init-position-manager.ts | 76 ++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/lib/startup/init-position-manager.ts b/lib/startup/init-position-manager.ts index 3efeef1..0bd3f64 100644 --- a/lib/startup/init-position-manager.ts +++ b/lib/startup/init-position-manager.ts @@ -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 { + 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`) + } +}