From b2cb6a3ecd91dac2a241985406ca461d080470e6 Mon Sep 17 00:00:00 2001 From: mindesbunister Date: Wed, 19 Nov 2025 14:56:24 +0100 Subject: [PATCH] critical: Fix ADX-based runner SL in on-chain fill detection path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ADX-based runner SL logic was only applied in the direct price check path (lines 1065-1086) but NOT in the on-chain fill detection path (lines 590-650). When TP1 fills via on-chain order (most common), the system was using hard-coded breakeven SL instead of ADX-based positioning. Bug Impact: - ADX 20.0 trade got breakeven SL ($138.355) instead of -0.3% ($138.77) - Runner has $0.42 less room to breathe than intended - Weak trends protected correctly but moderate/strong trends not Fix: - Applied same ADX-based logic to on-chain fill detection - Added detailed logging for each ADX tier - Runner SL now correct regardless of TP1 trigger path Current trade (cmi5zpx5s0000lo07ncba1kzh): - Already hit TP1 via old code (breakeven SL active) - New ADX-based SL will apply to NEXT trade - Current position: SHORT $138.3550, runner at breakeven Code paths with ADX logic: 1. Direct price check (lines 1050-1100) ✅ 2. On-chain fill detection (lines 607-642) ✅ FIXED --- lib/trading/position-manager.ts | 36 +++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/lib/trading/position-manager.ts b/lib/trading/position-manager.ts index a909f78..8d4e763 100644 --- a/lib/trading/position-manager.ts +++ b/lib/trading/position-manager.ts @@ -614,16 +614,36 @@ export class PositionManager { trade.tp1Hit = true trade.currentSize = positionSizeUSD - // CRITICAL: Use DATABASE entry price for breakeven (Drift recalculates after partial closes) - // Drift's position.entryPrice is the COST BASIS of remaining position, NOT original entry - // For a true breakeven SL, we need the ORIGINAL entry price from when position opened - const breakevenPrice = trade.entryPrice - console.log(`📊 Breakeven SL: Using original entry price $${breakevenPrice.toFixed(4)} (Drift shows $${position.entryPrice.toFixed(4)} for remaining position)`) + // ADX-based runner SL positioning (Nov 19, 2025) + // Strong trends get more room, weak trends protect capital + let runnerSlPercent: number + const adx = trade.adxAtEntry || 0 - // Move SL to TRUE breakeven after TP1 - trade.stopLossPrice = breakevenPrice + if (adx < 20) { + runnerSlPercent = 0 // Weak trend: breakeven, preserve capital + console.log(`🔒 ADX-based runner SL: ${adx.toFixed(1)} → 0% (breakeven - weak trend)`) + } else if (adx < 25) { + runnerSlPercent = -0.3 // Moderate trend: some room + console.log(`🔒 ADX-based runner SL: ${adx.toFixed(1)} → -0.3% (moderate trend)`) + } else { + runnerSlPercent = -0.55 // Strong trend: full retracement room + console.log(`🔒 ADX-based runner SL: ${adx.toFixed(1)} → -0.55% (strong trend)`) + } + + // CRITICAL: Use DATABASE entry price (Drift recalculates after partial closes) + const newStopLossPrice = this.calculatePrice( + trade.entryPrice, + runnerSlPercent, + trade.direction + ) + + console.log(`📊 Runner SL calculation: Entry $${trade.entryPrice.toFixed(4)} ${runnerSlPercent >= 0 ? '+' : ''}${runnerSlPercent}% = $${newStopLossPrice.toFixed(4)}`) + console.log(` (${this.config.takeProfit1SizePercent}% closed, ${100 - this.config.takeProfit1SizePercent}% remaining)`) + + // Move SL to ADX-based position after TP1 + trade.stopLossPrice = newStopLossPrice trade.slMovedToBreakeven = true - console.log(`🛡️ Stop loss moved to breakeven: $${trade.stopLossPrice.toFixed(4)}`) + console.log(`🛡️ Stop loss moved to: $${trade.stopLossPrice.toFixed(4)}`) // CRITICAL: Update on-chain orders to reflect new SL at breakeven try {