diff --git a/lib/startup/init-position-manager.ts b/lib/startup/init-position-manager.ts index f7a82b1..3efeef1 100644 --- a/lib/startup/init-position-manager.ts +++ b/lib/startup/init-position-manager.ts @@ -22,7 +22,16 @@ export async function initializePositionManagerOnStartup() { console.log('🚀 Initializing Position Manager on startup...') try { - // Validate open trades against Drift positions BEFORE starting Position Manager + // CRITICAL: Run database sync validator FIRST to clean up duplicates + const { validateAllOpenTrades } = await import('../database/sync-validator') + console.log('🔍 Running database sync validation before Position Manager init...') + const validationResult = await validateAllOpenTrades() + + if (validationResult.ghosts > 0) { + console.log(`✅ Cleaned up ${validationResult.ghosts} ghost/duplicate trades`) + } + + // Then validate open trades against Drift positions await validateOpenTrades() const manager = await getInitializedPositionManager() @@ -77,10 +86,45 @@ async function validateOpenTrades() { console.log(`🔍 Validating ${openTrades.length} open + ${recentlyClosedTrades.length} recently closed trades against Drift...`) + // CRITICAL: Group trades by symbol to handle multiple DB entries for same Drift position + // This prevents reopening old closed trades when only the most recent should be restored + const tradesBySymbol = new Map() + for (const trade of allTradesToCheck) { + const existing = tradesBySymbol.get(trade.symbol) || [] + existing.push(trade) + tradesBySymbol.set(trade.symbol, existing) + } + const driftService = await initializeDriftService() const driftPositions = await driftService.getAllPositions() // Get all positions once - for (const trade of allTradesToCheck) { + // Process each symbol's trades (keep only most recent if multiple exist) + for (const [symbol, trades] of tradesBySymbol) { + // Sort by creation time, newest first + trades.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime()) + const mostRecentTrade = trades[0] + const olderTrades = trades.slice(1) + + // Close any older trades BEFORE validating the most recent + for (const oldTrade of olderTrades) { + if (oldTrade.exitReason === null) { + console.log(`🗑️ Closing duplicate old trade: ${oldTrade.id} (${symbol}, created ${oldTrade.createdAt.toISOString()})`) + await prisma.trade.update({ + where: { id: oldTrade.id }, + data: { + status: 'closed', + exitTime: new Date(), + exitReason: 'DUPLICATE_CLEANUP', + exitPrice: oldTrade.entryPrice, + realizedPnL: 0, + } + }) + } + } + + // Now validate only the most recent trade for this symbol + const trade = mostRecentTrade + try { const marketConfig = getMarketConfig(trade.symbol)