From c4c5c84c246e9fe258fd8c9d1e287ae1bb655108 Mon Sep 17 00:00:00 2001 From: mindesbunister Date: Thu, 20 Nov 2025 08:07:59 +0100 Subject: [PATCH] docs: Add Common Pitfall #43 - Runner trailing stop protection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Documented critical bug where runner trailing stop never activated after TP1: - Symptom: Runner exposed to full reversal with static SL - Real incident: 24 profit at risk, user forced manual close - Root cause: External closure detected before TP2 price check - Fix: Three-part solution with TP2 pre-check, diagnostics, exit reason detection - Impact: Runner now fully autonomous with trailing protection Also renumbered subsequent pitfalls 43→45, 44→46, etc. for consistency. Commit: 55582a4 "critical: Fix runner trailing stop protection after TP1" --- .github/copilot-instructions.md | 64 +++++++++++++++++++++++++++++++-- 1 file changed, 62 insertions(+), 2 deletions(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index dc9bb18..8d8a92e 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -2149,7 +2149,67 @@ trade.realizedPnL += actualRealizedPnL // NOT: result.realizedPnL from SDK - **Git commit:** b1ca454 "feat: Add Telegram notifications for position closures" - **Lesson:** User feedback channels (notifications) are as important as monitoring logic -42. **Telegram bot DNS resolution failures (Fixed Nov 16, 2025):** +43. **Runner trailing stop never activates after TP1 (CRITICAL - Fixed Nov 20, 2025):** + - **Symptom:** Runner after TP1 exposed to full reversal with only static SL, no trailing protection + - **Real incident (Nov 20, $224 profit at risk):** + * LONG SOL-PERP: Entry $135.22, quality score 95, ADX 26.9 + * TP1 hit: 60% closed at $136.26 for $6.28 profit + * Runner: 40% remaining, price rose to $143.50 (+$224.49 profit) + * Runner SL: Stayed at $134.48 (-0.55% ADX-based) - NEVER trailed + * Risk: $224 profit exposed to full reversal back to -$1.55 loss + * User action: Manually closed at $143.50 to protect profit + - **Root Cause:** Position Manager detected full closure before checking TP2 price trigger + - **Bug sequence:** + 1. TP1 filled on-chain at $136.26 (60% closed) + 2. Position Manager detected size reduction, moved runner SL to $134.48 ✅ + 3. Price rose to $143.50 but monitoring detected position fully gone ❌ + 4. External closure handler stopped all monitoring before TP2 check ❌ + 5. Trailing stop NEVER activated (requires tp2Hit flag set) + 6. Runner had static SL $9.02 below current price with NO trailing + - **Three-part fix (Nov 20, 2025):** + ```typescript + // Part 1: TP2 pre-check BEFORE external closure detection (lines 776-799) + if (trade.tp1Hit && !trade.tp2Hit && !trade.closingInProgress) { + const reachedTP2 = this.shouldTakeProfit2(currentPrice, trade) + if (reachedTP2) { + console.log(`🎊 TP2 PRICE REACHED: Activating trailing stop for runner`) + trade.tp2Hit = true + trade.trailingStopActive = true + trade.peakPrice = currentPrice // Initialize for trailing + await this.saveTradeState(trade) + } + } + + // Part 2: Enhanced diagnostics (lines 803-821) + if ((position === null) && trade.tp1Hit && !trade.tp2Hit) { + console.log(`⚠️ RUNNER CLOSED EXTERNALLY without reaching TP2`) + const reachedTP2 = currentPrice >= trade.tp2Price + if (reachedTP2) { + console.log(` Price reached TP2 but tp2Hit was false!`) + console.log(` Trailing stop should have been active`) + } + } + + // Part 3: Trailing stop-aware exit reason (lines 858-877) + if (trade.tp2Hit && trade.trailingStopActive) { + const isPullback = currentPrice < trade.peakPrice * 0.99 + exitReason = isPullback ? 'SL' : 'TP2' // Trailing hit vs peak close + } + ``` + - **How it works now:** + 1. TP1 closes 60% → Runner SL moves to $134.48 (ADX-based) + 2. **Before external closure check**: System checks if price ≥ TP2 ($137.30) + 3. If yes: Sets tp2Hit=true, trailingStopActive=true, initializes peakPrice + 4. As price rises: Trailing stop moves SL up dynamically (ATR × ADX multiplier) + 5. On pullback: Trailing stop triggers, locks in profit + 6. Fully autonomous: No manual intervention needed + - **Impact:** Runner system now works as designed - "let winners run" with protection + - **User requirement:** "bot has to work all the time especially when i am not on my laptop" + - **Files:** lib/trading/position-manager.ts (3 strategic fixes) + - **Git commit:** 55582a4 "critical: Fix runner trailing stop protection after TP1" + - **Lesson:** When detecting external closures, always check for intermediate state triggers (TP2) BEFORE assuming trade is fully done. Trailing stop requires tp2Hit flag but position can close before monitoring detects TP2 price crossed. + +44. **Telegram bot DNS resolution failures (Fixed Nov 16, 2025):** - **Symptom:** Telegram bot throws "Failed to resolve 'trading-bot-v4'" errors on /status and manual trades - **Root Cause:** Python urllib3 has transient DNS resolution failures (same as Node.js fetch failures) - **Error message:** `urllib3.exceptions.NameResolutionError: Failed to resolve 'trading-bot-v4'` @@ -2193,7 +2253,7 @@ trade.realizedPnL += actualRealizedPnL // NOT: result.realizedPnL from SDK - **Git commit:** bdf1be1 "fix: Add DNS retry logic to Telegram bot" - **Lesson:** Python urllib3 has same transient DNS issues as Node.js - apply same retry pattern -43. **Drift SDK position.entryPrice RECALCULATES after partial closes (CRITICAL - FINANCIAL LOSS BUG - Fixed Nov 16, 2025):** +45. **Drift SDK position.entryPrice RECALCULATES after partial closes (CRITICAL - FINANCIAL LOSS BUG - Fixed Nov 16, 2025):** - **Symptom:** Breakeven SL set $1.50+ ABOVE actual entry price, guaranteeing loss if triggered - **Root Cause:** Drift SDK's `position.entryPrice` returns COST BASIS of remaining position after TP1, NOT original entry - **Real incident (Nov 16, 02:47 CET):**