From e5714e43769838491e96ea8a51a07ff73c980f3b Mon Sep 17 00:00:00 2001 From: mindesbunister Date: Wed, 10 Dec 2025 11:05:53 +0100 Subject: [PATCH] critical: Bug #82 EMERGENCY FIX - Disable Drift State Verifier automatic close Problem: Verifier can't distinguish OLD positions from NEW positions at same symbol - User opened manual trade with SL working - Verifier detected 6 old closed DB records (150-1064 min ago) - All showed "15.45 tokens open on Drift" (user's CURRENT trade!) - Automatic retry close removed user's SL orders Root Cause: Lines 279-283 call closePosition() for every mismatch - No verification if Drift position is OLD (should close) or NEW (active trade) - No position ID/timestamp matching - Result: Closes ACTIVE trades when cleaning up old database records Solution: DISABLED automatic retry close (lines 276-298) - Added BUG #82 warning logs - Requires manual intervention if true orphan detected - Will add proper position verification in follow-up fix Impact: Stops SL removal on active trades User incident: After Bug #81 fix deployed, THIS bug was killing SLs Deployment: Dec 10, 2025 11:06 CET --- lib/monitoring/drift-state-verifier.ts | 75 +++++++++++++++----------- 1 file changed, 45 insertions(+), 30 deletions(-) diff --git a/lib/monitoring/drift-state-verifier.ts b/lib/monitoring/drift-state-verifier.ts index 613f5dc..ca0bbc7 100644 --- a/lib/monitoring/drift-state-verifier.ts +++ b/lib/monitoring/drift-state-verifier.ts @@ -277,36 +277,51 @@ class DriftStateVerifier { const attemptTime = Date.now() this.recentCloseAttempts.set(mismatch.symbol, attemptTime) - const result = await closePosition({ - symbol: mismatch.symbol, - percentToClose: 100, - slippageTolerance: 0.05 // 5% slippage tolerance for market order - }) - - if (result.success) { - console.log(` ✅ Close transaction confirmed: ${result.transactionSignature}`) - console.log(` P&L: $${result.realizedPnL?.toFixed(2) || 0}`) - console.log(` ⏳ Drift API may take up to 5 minutes to reflect closure`) - - // Update database with retry close timestamp to prevent loop - await prisma.trade.update({ - where: { id: mismatch.tradeId }, - data: { - exitOrderTx: result.transactionSignature || 'RETRY_CLOSE', - realizedPnL: result.realizedPnL || 0, - configSnapshot: { - ...trade?.configSnapshot as any, - retryCloseAttempted: true, - retryCloseTime: new Date(attemptTime).toISOString(), - } - } - }) - - console.log(` 📝 Cooldown recorded: ${mismatch.symbol} → ${new Date(attemptTime).toISOString()}`) - } else { - console.error(` ❌ Failed to close ${mismatch.symbol}: ${result.error}`) - // Keep cooldown even on failure to prevent spam - } + // BUG #82 FIX (Dec 10, 2025): DISABLE automatic retry close + // Problem: Can't distinguish OLD position (should close) from NEW position at same symbol (should NOT touch) + // Result: Closes ACTIVE trades when trying to clean up old database records + // User incident: 6 old closed trades (150-1064 min ago) all showed "15.45 tokens" on Drift + // That was user's CURRENT manual trade, not 6 old ghosts + // Automatic close removed user's SL orders + // Solution: DISABLE automatic close until we add proper position ID/timestamp verification + + console.warn(`⚠️ BUG #82 SAFETY: Automatic retry close DISABLED`) + console.warn(` Would have closed ${mismatch.symbol} with 15.45 tokens`) + console.warn(` But can't verify if it's OLD position or NEW active trade`) + console.warn(` Manual intervention required if true orphan detected`) + return + + // ORIGINAL CODE (DISABLED): + // const result = await closePosition({ + // symbol: mismatch.symbol, + // percentToClose: 100, + // slippageTolerance: 0.05 // 5% slippage tolerance for market order + // }) + // + // if (result.success) { + // console.log(` ✅ Close transaction confirmed: ${result.transactionSignature}`) + // console.log(` P&L: $${result.realizedPnL?.toFixed(2) || 0}`) + // console.log(` ⏳ Drift API may take up to 5 minutes to reflect closure`) + // + // // Update database with retry close timestamp to prevent loop + // await prisma.trade.update({ + // where: { id: mismatch.tradeId }, + // data: { + // exitOrderTx: result.transactionSignature || 'RETRY_CLOSE', + // realizedPnL: result.realizedPnL || 0, + // configSnapshot: { + // ...trade?.configSnapshot as any, + // retryCloseAttempted: true, + // retryCloseTime: new Date(attemptTime).toISOString(), + // } + // } + // }) + // + // console.log(` 📝 Cooldown recorded: ${mismatch.symbol} → ${new Date(attemptTime).toISOString()}`) + // } else { + // console.error(` ❌ Failed to close ${mismatch.symbol}: ${result.error}`) + // // Keep cooldown even on failure to prevent spam + // } } catch (error) { console.error(` ❌ Error retrying close for ${mismatch.symbol}:`, error) // On error, still record attempt time to prevent rapid retries