diff --git a/lib/startup/init-position-manager.ts b/lib/startup/init-position-manager.ts index c183020..8527d5f 100644 --- a/lib/startup/init-position-manager.ts +++ b/lib/startup/init-position-manager.ts @@ -64,15 +64,22 @@ async function validateOpenTrades() { const marketConfig = getMarketConfig(trade.symbol) const position = await driftService.getPosition(marketConfig.driftMarketIndex) - // Calculate expected position size in base assets - const expectedSizeBase = trade.positionSizeUSD / trade.entryPrice + // Prefer Position Manager snapshot (captures partial closes) before falling back to original size + const configSnapshot = trade.configSnapshot as any + const pmState = configSnapshot?.positionManagerState + const expectedSizeUSD = typeof pmState?.currentSize === 'number' && pmState.currentSize > 0 + ? pmState.currentSize + : trade.positionSizeUSD + + // Calculate expected position size in base assets (approximate using entry price for consistency) + const expectedSizeBase = expectedSizeUSD / trade.entryPrice const actualSizeBase = position?.size || 0 // Check if position exists and size matches (with 50% tolerance for partial fills) const sizeDiff = Math.abs(expectedSizeBase - actualSizeBase) - const sizeRatio = actualSizeBase / expectedSizeBase - - if (!position || position.side === 'none' || sizeRatio < 0.5) { + const sizeRatio = expectedSizeBase > 0 ? actualSizeBase / expectedSizeBase : 0 + + if (!position || position.side === 'none' || sizeRatio < 0.2) { console.log(`⚠️ PHANTOM TRADE DETECTED:`) console.log(` Trade ID: ${trade.id.substring(0, 20)}...`) console.log(` Symbol: ${trade.symbol} ${trade.direction}`) diff --git a/lib/trading/position-manager.ts b/lib/trading/position-manager.ts index 0edb523..ee8e631 100644 --- a/lib/trading/position-manager.ts +++ b/lib/trading/position-manager.ts @@ -498,15 +498,31 @@ export class PositionManager { } // Position exists but size mismatch (partial close by TP1?) - if (position.size < trade.currentSize * 0.95) { // 5% tolerance - console.log(`⚠️ Position size mismatch: expected ${trade.currentSize}, got ${position.size}`) + const onChainBaseSize = Math.abs(position.size) + const onChainSizeUSD = onChainBaseSize * currentPrice + const trackedSizeUSD = trade.currentSize + + if (trackedSizeUSD > 0 && onChainSizeUSD < trackedSizeUSD * 0.95) { // 5% tolerance + const expectedBaseSize = trackedSizeUSD / currentPrice + console.log(`⚠️ Position size mismatch: tracking $${trackedSizeUSD.toFixed(2)} (~${expectedBaseSize.toFixed(4)} units) but on-chain shows $${onChainSizeUSD.toFixed(2)} (${onChainBaseSize.toFixed(4)} units)`) // CRITICAL: If mismatch is extreme (>50%), this is a phantom trade - const sizeRatio = (position.size * currentPrice) / trade.currentSize + const sizeRatio = trackedSizeUSD > 0 ? onChainSizeUSD / trackedSizeUSD : 0 if (sizeRatio < 0.5) { + const tradeAgeSeconds = (Date.now() - trade.entryTime) / 1000 + const probablyPartialRunner = trade.tp1Hit || tradeAgeSeconds > 60 + + if (probablyPartialRunner) { + console.log(`🛠️ Detected stray remainder (${(sizeRatio * 100).toFixed(1)}%) after on-chain exit - forcing market close`) + trade.currentSize = onChainSizeUSD + await this.saveTradeState(trade) + await this.executeExit(trade, 100, 'manual', currentPrice) + return + } + console.log(`🚨 EXTREME SIZE MISMATCH (${(sizeRatio * 100).toFixed(1)}%) - Closing phantom trade`) - console.log(` Expected: $${trade.currentSize.toFixed(2)}`) - console.log(` Actual: $${(position.size * currentPrice).toFixed(2)}`) + console.log(` Expected: $${trackedSizeUSD.toFixed(2)}`) + console.log(` Actual: $${onChainSizeUSD.toFixed(2)}`) // Close as phantom trade try { @@ -534,10 +550,15 @@ export class PositionManager { return } - // Update current size to match reality (convert base asset size to USD using current price) - trade.currentSize = position.size * currentPrice - trade.tp1Hit = true - await this.saveTradeState(trade) + // Update current size to match reality and run TP1 adjustments if needed + trade.currentSize = onChainSizeUSD + if (!trade.tp1Hit) { + trade.tp1Hit = true + await this.handlePostTp1Adjustments(trade, 'on-chain TP1 size sync') + } else { + await this.saveTradeState(trade) + } + return } } catch (error) {