critical: Fix ghost detection P&L compounding - delete from Map BEFORE check

Bug: Multiple monitoring loops detect ghost simultaneously
- Loop 1: has(tradeId) → true → proceeds
- Loop 2: has(tradeId) → true → ALSO proceeds (race condition)
- Both send Telegram notifications with compounding P&L

Real incident (Dec 2, 2025):
- Manual SHORT at $138.84
- 23 duplicate notifications
- P&L compounded: -$47.96 → -$1,129.24 (23× accumulation)
- Database shows single trade with final compounded value

Fix: Map.delete() returns true if key existed, false if already removed
- Call delete() FIRST
- Check return value
 proceeds
- All other loops get false → skip immediately
- Atomic operation prevents race condition

Pattern: This is variant of Common Pitfalls #48, #49, #59, #60, #61
- All had "check then delete" pattern
- All vulnerable to async timing issues
- Solution: "delete then check" pattern
- Map.delete() is synchronous and atomic

Files changed:
- lib/trading/position-manager.ts lines 390-410

Related: DUPLICATE PREVENTED message was working but too late
This commit is contained in:
mindesbunister
2025-12-02 18:25:56 +01:00
parent d156abc976
commit 93dd950821
7 changed files with 893 additions and 6 deletions

View File

@@ -205,6 +205,13 @@ model BlockedSignal {
wouldHitTP2 Boolean? // Would TP2 have been hit?
wouldHitSL Boolean? // Would SL have been hit?
// EXACT TIMING (Dec 2, 2025): Minute-precision timestamps for TP/SL hits
// Purpose: Answer "EXACTLY when TP1/TP2 would have been hit" using 1-minute granular data
// Uses: MarketData query instead of Drift oracle polling (480 data points vs. 8 checkpoints)
tp1HitTime DateTime? @map("tp1_hit_time") // Exact timestamp when TP1 first hit
tp2HitTime DateTime? @map("tp2_hit_time") // Exact timestamp when TP2 first hit
slHitTime DateTime? @map("sl_hit_time") // Exact timestamp when SL first hit
// Max favorable/adverse excursion (mirror Trade model)
maxFavorablePrice Float? // Price at max profit
maxAdversePrice Float? // Price at max loss