- Root cause: Exit strategy, NOT entry timing - Smoking gun: Identical entry conditions (+83 winner AND -,129 loser) - Key problems: SL too wide (ATR 3.0), no catastrophic cap, trailing too tight - Phase 1 fixes: Tighten SL to ATR 2.0, add -2 avg winner cap, block extreme longs - Phase 2 fixes: Widen trailing to ATR 2.5, earlier profit acceleration - Expected impact: -,400 → +4 over 78 trades - Validation: 10/30/50 trade milestones - Files: Full analysis, implementation plan, executive summary
331 lines
9.8 KiB
Markdown
331 lines
9.8 KiB
Markdown
# Exit Strategy Fixes - Implementation Plan
|
||
|
||
**Date:** December 23, 2025
|
||
**Status:** READY TO IMPLEMENT
|
||
**Expected Impact:** -$1,400 loss → +$200 profit over 78 trades
|
||
|
||
---
|
||
|
||
## 🎯 Phase 1: Emergency Fixes (Deploy Immediately)
|
||
|
||
### Fix #1: Tighten Stop Loss Distance
|
||
|
||
**File:** `config/trading.ts` or `.env`
|
||
|
||
**Change:**
|
||
```typescript
|
||
// BEFORE:
|
||
ATR_MULTIPLIER_SL=3.0
|
||
|
||
// AFTER:
|
||
ATR_MULTIPLIER_SL=2.0 // 33% tighter, caps losses at ~0.86% instead of 1.29%
|
||
```
|
||
|
||
**Rationale:** Average loser ($42.43) is 2× average winner ($21.05). Tighter SL brings them closer to 1:1 ratio.
|
||
|
||
**Expected Impact:**
|
||
- Average loser: -$42.43 → -$28 (34% improvement)
|
||
- May increase stop-out rate by 3-5% (acceptable trade-off)
|
||
- Net effect: $882 improvement over current performance
|
||
|
||
---
|
||
|
||
### Fix #2: Catastrophic Loss Protection
|
||
|
||
**File:** `lib/trading/position-manager.ts`
|
||
|
||
**Add new function:**
|
||
```typescript
|
||
private async checkCatastrophicLoss(trade: ActiveTrade, currentPrice: number): Promise<boolean> {
|
||
// Get average winner from last 50 trades
|
||
const recentTrades = await this.prisma.trade.findMany({
|
||
where: {
|
||
exitReason: { not: null },
|
||
realizedPnL: { gt: 0 },
|
||
createdAt: { gte: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000) } // Last 30 days
|
||
},
|
||
orderBy: { createdAt: 'desc' },
|
||
take: 50,
|
||
select: { realizedPnL: true }
|
||
})
|
||
|
||
if (recentTrades.length === 0) {
|
||
// No recent winners, use default $30 threshold
|
||
return trade.unrealizedPnL < -60 // 2× $30
|
||
}
|
||
|
||
const averageWinner = recentTrades.reduce((sum, t) => sum + (t.realizedPnL || 0), 0) / recentTrades.length
|
||
const catastrophicThreshold = -(2 * averageWinner)
|
||
|
||
if (trade.unrealizedPnL < catastrophicThreshold) {
|
||
console.log(`🚨 CATASTROPHIC LOSS DETECTED: ${trade.symbol}`)
|
||
console.log(` Current P&L: ${trade.unrealizedPnL.toFixed(2)}`)
|
||
console.log(` Avg Winner: ${averageWinner.toFixed(2)}`)
|
||
console.log(` Threshold: ${catastrophicThreshold.toFixed(2)}`)
|
||
console.log(` FORCING IMMEDIATE CLOSE`)
|
||
return true
|
||
}
|
||
|
||
return false
|
||
}
|
||
```
|
||
|
||
**Add to monitoring loop (in `monitorTrades()`):**
|
||
```typescript
|
||
// BEFORE checking TP1/TP2/SL, add this:
|
||
if (await this.checkCatastrophicLoss(trade, currentPrice)) {
|
||
await this.executeExit(trade, currentPrice, 100, 'CATASTROPHIC_LOSS_PROTECTION')
|
||
continue
|
||
}
|
||
```
|
||
|
||
**Expected Impact:**
|
||
- Prevents losses >$50 (currently worst was -$1,129)
|
||
- Would have saved $1,087 on the catastrophic v5 trade
|
||
- Zero downside (only triggers on disaster scenarios)
|
||
|
||
---
|
||
|
||
### Fix #3: Block Extreme Long Positions
|
||
|
||
**File:** `workflows/trading/moneyline_v11_all_filters.pinescript`
|
||
|
||
**Change:**
|
||
```pinescript
|
||
// BEFORE:
|
||
longPosMax = input.float(100, "Long max position %", ...)
|
||
|
||
// AFTER:
|
||
longPosMax = input.float(85, "Long max position %", ...)
|
||
// Tooltip: "Block chasing tops - don't buy in top 15% of 100-bar range"
|
||
```
|
||
|
||
**Rationale:** LONGs at price_pos 94-96% + RSI 73-84 consistently lose. These are "chasing the top" entries.
|
||
|
||
**Expected Impact:**
|
||
- Filters 3-4 losing trades per 78 trades
|
||
- Saves ~$60 total
|
||
- Prevents entries like: RSI 73.5 at 94.4% pos = -$17.09, RSI 84.4 at 96.7% = -$13.05
|
||
|
||
---
|
||
|
||
## 🚀 Phase 2: Runner Optimization (Deploy After 20 Trades)
|
||
|
||
### Fix #4: Widen Trailing Stop After TP2
|
||
|
||
**File:** `lib/trading/position-manager.ts` around lines 1356-1450
|
||
|
||
**Change trailing stop multiplier:**
|
||
```typescript
|
||
// FIND this section (around line 1394):
|
||
let trailMultiplier = 1.5 // Base multiplier for trailing stop
|
||
|
||
// CHANGE TO:
|
||
let trailMultiplier = 2.5 // Wider to capture bigger moves (was 1.5)
|
||
```
|
||
|
||
**AND adjust ADX multipliers:**
|
||
```typescript
|
||
// FIND (around line 1410):
|
||
if (freshADX > 30) {
|
||
trailMultiplier *= 1.5 // Very strong trend
|
||
} else if (freshADX >= 25) {
|
||
trailMultiplier *= 1.25 // Strong trend
|
||
}
|
||
|
||
// CHANGE TO:
|
||
if (freshADX > 30) {
|
||
trailMultiplier *= 2.0 // Very strong trend (was 1.5)
|
||
} else if (freshADX >= 25) {
|
||
trailMultiplier *= 1.5 // Strong trend (was 1.25)
|
||
}
|
||
```
|
||
|
||
**Rationale:** $183 winner proves big moves exist. Need more room for runners to breathe. Current trailing too tight.
|
||
|
||
**Expected Impact:**
|
||
- Average winner: $21.05 → $35 (67% improvement)
|
||
- Some runners will give back gains (2-3% WR drop acceptable)
|
||
- Net effect: Positive P&L even with slightly lower WR
|
||
|
||
---
|
||
|
||
### Fix #5: Earlier Profit Acceleration
|
||
|
||
**File:** `lib/trading/position-manager.ts` around line 1425
|
||
|
||
**Change:**
|
||
```typescript
|
||
// FIND:
|
||
if (profitPercent > 2.0) { // Was 2%
|
||
trailMultiplier *= 1.3
|
||
console.log(`💰 Large profit (${profitPercent.toFixed(2)}%): Trail multiplier ${oldMult}× → ${trailMultiplier.toFixed(2)}×`)
|
||
}
|
||
|
||
// CHANGE TO:
|
||
if (profitPercent > 1.5) { // Changed to 1.5%
|
||
trailMultiplier *= 1.5 // Increased from 1.3
|
||
console.log(`💰 Profit acceleration (${profitPercent.toFixed(2)}%): Trail multiplier ${oldMult}× → ${trailMultiplier.toFixed(2)}×`)
|
||
}
|
||
```
|
||
|
||
**Rationale:** Trigger wider trailing sooner to capture mid-sized moves (1.5-3% range).
|
||
|
||
**Expected Impact:**
|
||
- Captures 1.5-3% moves that currently hit trailing too soon
|
||
- Estimated +$5-10 per winning trade in this range
|
||
- More winning TRAILING_SL exits (currently only 1 out of 34 winners)
|
||
|
||
---
|
||
|
||
## 📋 Deployment Checklist
|
||
|
||
### Pre-Deployment
|
||
|
||
- [ ] **Backup current .env:** `cp .env .env.backup_dec23`
|
||
- [ ] **Backup Position Manager:** `cp lib/trading/position-manager.ts lib/trading/position-manager.ts.backup_dec23`
|
||
- [ ] **Document current parameters:**
|
||
```
|
||
ATR_MULTIPLIER_SL=3.0
|
||
longPosMax=100
|
||
Base trail multiplier=1.5
|
||
```
|
||
|
||
### Phase 1 Deployment (Do All 3 Together)
|
||
|
||
- [ ] **Update .env:** Change `ATR_MULTIPLIER_SL=3.0` to `ATR_MULTIPLIER_SL=2.0`
|
||
- [ ] **Update v11 indicator:** Change `longPosMax=100` to `longPosMax=85` in TradingView
|
||
- [ ] **Add catastrophic protection:** Implement `checkCatastrophicLoss()` in Position Manager
|
||
- [ ] **Test compilation:** `npm run build`
|
||
- [ ] **Restart Docker:** `docker compose restart trading-bot`
|
||
- [ ] **Verify logs:** Check "CATASTROPHIC" appears in monitoring logs
|
||
- [ ] **Test with small position:** Open test trade, verify SL distance ~0.86% instead of 1.29%
|
||
|
||
### Phase 2 Deployment (After 20 Trades)
|
||
|
||
- [ ] **Analyze Phase 1 results:**
|
||
```sql
|
||
SELECT
|
||
COUNT(*) as trades,
|
||
ROUND(AVG(CASE WHEN "realizedPnL" > 0 THEN "realizedPnL" ELSE NULL END)::numeric, 2) as avg_winner,
|
||
ROUND(AVG(CASE WHEN "realizedPnL" <= 0 THEN "realizedPnL" ELSE NULL END)::numeric, 2) as avg_loser,
|
||
MAX("realizedPnL") as best_winner,
|
||
MIN("realizedPnL") as worst_loser
|
||
FROM "Trade"
|
||
WHERE "createdAt" > NOW() - INTERVAL '7 days'
|
||
AND "exitReason" IS NOT NULL;
|
||
```
|
||
- [ ] **If Phase 1 working (avg loser <$35):** Proceed with Phase 2
|
||
- [ ] **Update trailing multipliers:** Change base 1.5 → 2.5, ADX 1.5/1.25 → 2.0/1.5
|
||
- [ ] **Update profit acceleration:** Change 2.0% → 1.5%, 1.3× → 1.5×
|
||
- [ ] **Restart Docker**
|
||
- [ ] **Monitor first 10 trades:** Watch for TRAILING_SL exits increasing
|
||
|
||
---
|
||
|
||
## 📊 Validation Metrics
|
||
|
||
### After 10 Trades (Phase 1 Check)
|
||
|
||
**Must See:**
|
||
- ✅ Average loser <$35 (target: <$30)
|
||
- ✅ Zero losses >$50 (catastrophic protection working)
|
||
- ✅ No LONGs at price_pos >85% (extreme filter working)
|
||
|
||
**If Not:** Rollback and investigate
|
||
|
||
### After 30 Trades (Phase 2 Check)
|
||
|
||
**Must See:**
|
||
- ✅ Average winner >$28 (target: >$30)
|
||
- ✅ Win/Loss ratio >0.8 (target: >1.0)
|
||
- ✅ At least 3-5 TRAILING_SL exits (runner system working)
|
||
|
||
**If Not:** Adjust trailing multipliers (try 2.0× instead of 2.5×)
|
||
|
||
### After 50 Trades (Full Validation)
|
||
|
||
**Success Criteria:**
|
||
- ✅ Total P&L >$0 (profitable)
|
||
- ✅ Win rate 38-45% (acceptable range)
|
||
- ✅ Average winner / average loser ratio >1.0
|
||
- ✅ Zero catastrophic losses (no single loss >$60)
|
||
|
||
**If Success:** System is fixed, continue monitoring
|
||
**If Failure:** Review data and adjust (may need Phase 3 advanced logic)
|
||
|
||
---
|
||
|
||
## 🔄 Rollback Plan
|
||
|
||
**If fixes make things worse:**
|
||
|
||
1. **Restore .env:**
|
||
```bash
|
||
cp .env.backup_dec23 .env
|
||
docker compose restart trading-bot
|
||
```
|
||
|
||
2. **Restore TradingView indicator:**
|
||
- Change `longPosMax=85` back to `longPosMax=100`
|
||
- Update alert
|
||
|
||
3. **Restore Position Manager:**
|
||
```bash
|
||
cp lib/trading/position-manager.ts.backup_dec23 lib/trading/position-manager.ts
|
||
npm run build
|
||
docker compose build trading-bot
|
||
docker compose up -d --force-recreate trading-bot
|
||
```
|
||
|
||
4. **Verify rollback:**
|
||
- Check logs for old SL distance (~1.29%)
|
||
- Check no CATASTROPHIC messages
|
||
- Open test trade to confirm old behavior
|
||
|
||
---
|
||
|
||
## 💡 Alternative Approaches (If Above Fails)
|
||
|
||
### Plan B: Time-Based Exits
|
||
|
||
If asymmetric risk persists after tightening SL:
|
||
|
||
**Add time-based exit rules:**
|
||
- If no TP1 hit within 30 minutes: Close 50% at breakeven
|
||
- If no TP2 hit within 60 minutes: Close runner at +0.5%
|
||
- Forces exits before losses can compound
|
||
|
||
**Expected:** Reduces average loser AND average winner, but ratio improves
|
||
|
||
### Plan C: Adaptive SL Based on Volatility
|
||
|
||
If tighter SL increases stop-out rate too much:
|
||
|
||
**Use dynamic SL:**
|
||
- Low volatility (ATR <0.3%): SL = ATR × 2.0
|
||
- Medium volatility (ATR 0.3-0.6%): SL = ATR × 2.5
|
||
- High volatility (ATR >0.6%): SL = ATR × 3.0
|
||
|
||
**Expected:** Preserves tight stops in calm markets, allows room in volatile markets
|
||
|
||
---
|
||
|
||
## 📝 Notes for User
|
||
|
||
**This is NOT about:**
|
||
- ❌ Entry optimization (dynamic thresholds wouldn't help)
|
||
- ❌ Better indicators (v11 already has 60% WR)
|
||
- ❌ Signal quality (entries are fine)
|
||
|
||
**This IS about:**
|
||
- ✅ Cutting losses faster (SL too wide)
|
||
- ✅ Letting winners run longer (trailing too tight)
|
||
- ✅ Preventing catastrophic losses (no protection currently)
|
||
- ✅ Achieving 1:1 or better risk/reward ratio
|
||
|
||
**Bottom Line:**
|
||
Your entries are good enough (43.6% WR with v11 at 60% proves this). The problem is exits: winners close too soon, losers run too far. Fix the exits → system becomes profitable immediately.
|
||
|
||
**The $1,400 loss taught us:** It's not WHEN you enter (dynamic thresholds), it's WHEN you exit (stop loss + trailing stop tuning).
|