diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 656f23a..891cb73 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -1874,6 +1874,38 @@ trade.realizedPnL += actualRealizedPnL // NOT: result.realizedPnL from SDK - **Drift documentation:** Account leverage must be set in UI, is persistent on-chain setting - **Lesson:** On-chain account settings cannot be changed via API - always verify account state matches bot assumptions before production trading +44. **Breakeven SL using database entry price instead of Drift's actual entry (Fixed Nov 16, 2025):** + - **Symptom:** After TP1 hits, SL moved to "breakeven" but at wrong price - SHORT with $139.07 entry gets SL at $139.18 ($0.11 loss if hit) + - **Root Cause:** Position Manager used `trade.entryPrice` from database, which can differ from Drift's actual fill price by $0.10-0.15 + - **Price discrepancy sources:** + * Database stores initial expected price + * Drift fills at market price (slippage) + * Orphaned position restorations use stale database price (see Common Pitfall #33) + * Oracle price delays during execution + - **Real incident (Nov 16, 01:37 CET):** + * User screenshot: Entry $139.07, SL $139.18, Current $138.58 + * Database: entryPrice=$139.18291 + * Drift UI: Entry Price=$139.07 + * Discrepancy: $0.11 (would lose money if SL hit on "breakeven") + - **Impact:** Breakeven SL not truly breakeven - gives back profit or locks in unnecessary loss + - **Fix:** Query Drift SDK for actual entry price when setting breakeven SL + ```typescript + // In lib/trading/position-manager.ts (line ~511): + // BEFORE: + trade.stopLossPrice = trade.entryPrice // Uses database value + + // AFTER: + const actualEntryPrice = position.entryPrice || trade.entryPrice + console.log(`📊 Breakeven calculation: DB entry=$${trade.entryPrice.toFixed(4)}, Drift entry=$${actualEntryPrice.toFixed(4)}`) + trade.stopLossPrice = actualEntryPrice // Uses Drift's on-chain calculated price + ``` + - **Drift SDK accuracy:** `position.entryPrice` calculated from on-chain data (quoteAssetAmount / baseAssetAmount) = authoritative + - **Fallback:** If position.entryPrice unavailable, falls back to database entry (rare edge case) + - **Logging:** Now shows both DB and Drift entry prices for verification + - **Related issues:** Common Pitfall #33 (orphaned position restoration with wrong entry) + - **Git commit:** 528a0f4 "fix: Use Drift's actual entry price for breakeven SL" + - **Lesson:** For critical price calculations (breakeven, TP, SL), always prefer on-chain data over cached database values + ## File Conventions - **API routes:** `app/api/[feature]/[action]/route.ts` (Next.js 15 App Router)