docs: Add Common Pitfall #41 - Stats API P&L recalculation bug

Documented the stats API bug where recalculating P&L from entry/exit
prices doesn't work for TP1+runner partial closes.

Issue:
- Stats showed -$26.10 when actual was +$46.97
- Recalculation used average exit price × full size
- Doesn't account for different TP1 vs runner exit prices

Fix:
- Use database realizedPnL field directly
- Database values corrected to match Drift UI TP1+runner sums
- Each trade shows as 2 lines in Drift (TP1 + runner)

Commits referenced:
- cd6f590: Corrected database P&L values
- d8b0307: Fixed stats API calculation

Lesson: Partial closes require storing combined P&L, can't
recalculate from average exit price.
This commit is contained in:
mindesbunister
2025-11-19 21:55:14 +01:00
parent d8b0307e74
commit 042fb33002

View File

@@ -2049,7 +2049,58 @@ trade.realizedPnL += actualRealizedPnL // NOT: result.realizedPnL from SDK
- **Verification:** Container restart + new code = no more ghost accumulation possible
- **Lesson:** Critical validation logic must NEVER skip during error conditions - use fallback methods that don't require the failing resource
41. **Missing Telegram notifications for position closures (Fixed Nov 16, 2025):**
41. **Stats API recalculating P&L incorrectly for TP1+runner trades (CRITICAL - Fixed Nov 19, 2025):**
- **Symptom:** Withdrawal stats page showing -$26.10 P&L when Drift UI shows +$46.97
- **Root Cause:** Stats API recalculating P&L from entry/exit prices, which doesn't work for TP1+runner partial closes
- **The Problem:**
* Each trade has 2 closes: TP1 (60-75%) at one price, runner (25-40%) at different price
* Database stores combined P&L from both closes in `realizedPnL` field
* Stats API used `positionSizeUSD × (exit - entry) / entry` formula
* But `exitPrice` is AVERAGE of TP1 and runner exits, not actual exit prices
* Formula: `(TP1_price × 0.6 + runner_price × 0.4) / 1.0` = average exit
* Result: Incorrect P&L calculation (-$26.10 vs actual +$46.97)
- **Real Example:**
* Trade: Entry $138.36 → TP1 $137.66 (60%) + Runner $136.94 (40%)
* Database: Combined P&L $54.19 (from Drift: $22.78 TP1 + $31.41 runner)
* Stats recalc: $8,326 × (136.96 - 138.36) / 138.36 = $83.94 (wrong!)
* Correct: Use database `realizedPnL` $54.19 directly
- **Drift UI shows 10 lines for 5 trades:**
* Each trade = 2 lines (TP1 close + runner close)
* Line 1: TP1 60% at $137.66 = $22.78
* Line 2: Runner 40% at $136.94 = $31.41
* Total: $54.19 (stored in database `realizedPnL`)
- **Fix (Nov 19, 2025):**
```typescript
// BEFORE (BROKEN - recalculated from entry/exit):
const totalPnL = trades.reduce((sum, trade) => {
const correctPnL = trade.positionSizeUSD * (
trade.direction === 'long'
? (trade.exitPrice - trade.entryPrice) / trade.entryPrice
: (trade.entryPrice - trade.exitPrice) / trade.entryPrice
)
return sum + correctPnL
}, 0)
// AFTER (FIXED - use database realizedPnL):
const totalPnL = trades.reduce((sum, trade) => {
return sum + Number(trade.realizedPnL)
}, 0)
```
- **Database P&L Correction (Nov 19, 2025):**
* Corrected inflated P&L values to match Drift UI actual TP1+runner sums
* Trade cmi5p09y: 37.67 → 38.90 (TP1 $9.72 + runner $29.18)
* Trade cmi5ie3c: 59.35 → 40.09 (TP1 $21.67 + runner $18.42)
* Trade cmi5a6jm: 19.79 → 13.72 (TP1 $1.33 + runner $4.08 + $8.31)
* v8 total: $46.97 (matches Drift UI exactly)
* Commit: cd6f590 "fix: Correct v8 trade P&L to match Drift UI actual values"
- **Impact:** Stats page now shows accurate v8 performance (+$46.97)
- **Files Changed:**
* `app/api/withdrawals/stats/route.ts` - Use `realizedPnL` not recalculation
* Added debug logging: "📊 Stats API: Found X closed trades"
* Commit: d8b0307 "fix: Use database realizedPnL instead of recalculating"
- **Lesson:** When trades have partial closes (TP1/TP2/runner), the database `realizedPnL` is the source of truth. Entry/exit price calculations only work for full position closes. Average exit price × full size ≠ sum of partial close P&Ls.
42. **Missing Telegram notifications for position closures (Fixed Nov 16, 2025):**
- **Symptom:** Position Manager closes trades (TP/SL/manual) but user gets no immediate notification
- **Root Cause:** TODO comment in Position Manager for Telegram notifications, never implemented
- **Impact:** User unaware of P&L outcomes until checking dashboard or Drift UI manually