From bba58da8fab0a8575eccff5bb7adc6e333cb955c Mon Sep 17 00:00:00 2001 From: mindesbunister Date: Wed, 12 Nov 2025 11:30:47 +0100 Subject: [PATCH] CRITICAL FIX: position.size is tokens not USD Fixed Position Manager incorrectly treating position.size as USD when Drift SDK actually returns base asset tokens (SOL, ETH, BTC). Impact: - FALSE TP1 detections (12.28 SOL misinterpreted as 2.28 USD) - Stop loss moved to breakeven prematurely - Runner system activated incorrectly - Positions stuck in wrong state Changes: - Line 322: Convert position.size to USD: position.size * currentPrice - Line 519: Calculate positionSizeUSD before comparison - Line 558: Use positionSizeUSD directly (already in USD) - Line 591: Save positionSizeUSD (no price multiplication needed) Before: Compared 12.28 tokens < 1950 USD = 99.4% reduction = FALSE TP1 This was causing current trade to think TP1 hit when position is still 100% open. --- lib/trading/position-manager.ts | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/lib/trading/position-manager.ts b/lib/trading/position-manager.ts index 8844f5e..8dc36c3 100644 --- a/lib/trading/position-manager.ts +++ b/lib/trading/position-manager.ts @@ -317,8 +317,8 @@ export class PositionManager { console.log(`⚠️ Position ${trade.symbol} was closed externally (by on-chain order)`) } else { // Position exists - check if size changed (TP1/TP2 filled) - // CRITICAL FIX: position.size from Drift SDK is already in USD notional value - const positionSizeUSD = Math.abs(position.size) // Drift SDK returns negative for shorts + // CRITICAL FIX: position.size from Drift SDK is base asset tokens, must convert to USD + const positionSizeUSD = Math.abs(position.size) * currentPrice // Convert tokens to USD const trackedSizeUSD = trade.currentSize const sizeDiffPercent = Math.abs(positionSizeUSD - trackedSizeUSD) / trackedSizeUSD * 100 @@ -515,9 +515,12 @@ export class PositionManager { return } + // CRITICAL: Convert position.size (base asset tokens) to USD for comparison + const positionSizeUSD = Math.abs(position.size) * currentPrice + // 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}`) + if (positionSizeUSD < trade.currentSize * 0.95) { // 5% tolerance + console.log(`⚠️ Position size mismatch: expected $${trade.currentSize.toFixed(2)}, got $${positionSizeUSD.toFixed(2)}`) // CRITICAL: Check if position direction changed (signal flip, not TP1!) const positionDirection = position.side === 'long' ? 'long' : 'short' @@ -555,11 +558,11 @@ export class PositionManager { } // CRITICAL: If mismatch is extreme (>50%), this is a phantom trade - const sizeRatio = (position.size * currentPrice) / trade.currentSize + const sizeRatio = positionSizeUSD / trade.currentSize if (sizeRatio < 0.5) { 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(` Actual: $${positionSizeUSD.toFixed(2)}`) // Close as phantom trade try { @@ -587,8 +590,8 @@ export class PositionManager { return } - // Update current size to match reality (convert base asset size to USD using current price) - trade.currentSize = position.size * currentPrice + // Update current size to match reality (already in USD) + trade.currentSize = positionSizeUSD trade.tp1Hit = true await this.saveTradeState(trade) }