CRITICAL FIX: Prevents silent stop-loss placement failures that caused $1,000+ losses Created lib/safety/sl-verification.ts (334 lines): 60s → 90s delays - Queries Drift protocol directly via user.getOpenOrders() - Filters SL orders: marketIndex + reduceOnly + TRIGGER_MARKET/LIMIT - Circuit breaker: haltTrading() blocks new trades on verification failure - Emergency shutdown: Force-closes position after 3 failed attempts - Event-driven architecture: Triggered once post-open (not polling) - Reduces Drift API calls by ~95% vs continuous polling Integrated in app/api/trading/execute/route.ts: - Line 54: Import shouldAcceptNewTrade for pre-execution check - Lines 215-221: Circuit breaker validates trading allowed (HTTP 503 if halted) - Lines 583-592: Triggers SL verification post-open (fire-and-forget) Root Cause - Bug #76: Silent SL placement failure Database Evidence: Trade cmj8abpjo00w8o407m3fndmx0 - tp1OrderTx: 'DsRv7E8vtAS4dKFmoQoTZMdiLTUju9cfmr9DPCgquP3V...' ✅ EXISTS - tp2OrderTx: '3cmYgGE828hZAhpepShXmpxqCTACFvXijqEjEzoed5PG...' ✅ EXISTS - slOrderTx: NULL ❌ - softStopOrderTx: NULL ❌ - hardStopOrderTx: NULL ❌ User Report: 'RISK MANAGEMENT WAS REMOVED WHEN PRICE WENT TO SL!!!!! POSITION STILL OPEN' Reality: SL orders never placed from start (not cancelled later) Solution Philosophy: 'better safe than sorry' - user's words Safety: Query on-chain state directly, don't trust internal success flags Deployed: 2025-12-16 13:50:18 UTC Docker Image: SHA256:80fd45004e71fa490fc4f472b252ecb25db91c6d90948de1516646b12a00446f Container: trading-bot-v4 restarted successfully
13 KiB
v11 Indicator Analysis - December 15, 2025
Executive Summary
Overall Performance: 7 trades, 57.1% win rate, -$1.80 total P&L Sample Size: TOO SMALL for definitive conclusions (need 20+ trades) Data Quality: Average quality score 88.6 (good filtering)
🚨 CRITICAL FINDINGS - IMMEDIATE ACTION REQUIRED
1. LONG TRADES ARE LOSING MONEY (-$2.22 total, 40% WR)
- Problem: LONGs at top of range (80-100%) are mixed results
- Worst Loser: Quality 75, RSI 73.5 (overbought), Position 94.4% → -$17.09 loss
- Pattern: Buying overbought conditions at range tops = disaster
2. SHORT TRADES ARE PERFECT (+$0.42 total, 100% WR)
- 2 trades, 2 wins (Quality 85-105, ADX 22-34)
- Both: RSI 38-44 (bearish zone), good momentum
3. QUALITY 95-99 FAILED (-$4.72 on 1 trade)
- Single failure: Q95, LONG at 63.5% position, RSI 59.5 → stopped out
- Contradicts: Q100+ had 100% WR (+$24.01)
- Issue: Quality 95 threshold may be too low, or 95-99 tier unlucky
4. RSI SWEET SPOT FOR LONGS: 60-70 (100% WR, +$26.97)
- Avoid: RSI >70 (overbought) → -$17.09
- Avoid: RSI 50-60 (weak bullish) → -$12.10
- Trade: RSI 60-70 (strong but not overbought) → +$26.97
Detailed Pattern Analysis
Direction Performance
| Direction | Trades | Win Rate | Total P&L | Avg P&L |
|---|---|---|---|---|
| SHORT | 2 | 100.0% | +$0.42 | +$0.21 |
| LONG | 5 | 40.0% | -$2.22 | -$0.44 |
Conclusion: v11 SHORTs are working, LONGs need filter improvements.
Quality Score Tiers
| Quality Tier | Trades | Win Rate | Total P&L | Avg P&L |
|---|---|---|---|---|
| 100+ | 2 | 100.0% | +$24.01 | +$12.01 |
| 95-99 | 1 | 0.0% | -$4.72 | -$4.72 |
| 85-89 | 2 | 100.0% | +$3.37 | +$1.69 |
| <85 | 2 | 0.0% | -$24.47 | -$12.23 |
Findings:
- ✅ Quality 100+ = perfect edge (2/2 wins)
- ✅ Quality 85-89 = solid (2/2 wins)
- ❌ Quality 95-99 = single failure (1/1 loss) - SAMPLE SIZE TOO SMALL
- ❌ Quality <85 = disaster (0/2 wins)
Action: Need 5+ more trades in 95-99 tier to validate if real problem or outlier.
ADX Strength Analysis
| ADX Tier | Trades | Avg ADX | Win Rate | Total P&L |
|---|---|---|---|---|
| 30+ Very Strong | 1 | 34.2 | 100.0% | +$0.38 |
| 20-25 Moderate | 4 | 23.6 | 50.0% | +$1.85 |
| 15-20 Weak | 2 | 16.2 | 50.0% | -$4.04 |
Finding: Strong ADX (30+) only 1 trade but won. Moderate (20-25) is 50/50.
Price Position Analysis (LONGs Only)
| Price Zone | Trades | Avg Pos | Win Rate | Total P&L |
|---|---|---|---|---|
| 80-100 Top | 3 | 89.5% | 66.7% | +$9.88 |
| 60-80 Upper | 1 | 63.5% | 0.0% | -$4.72 |
| 40-60 Middle | 1 | 54.2% | 0.0% | -$7.38 |
CRITICAL FINDING:
- ✅ LONGs at TOP of range (80-100%) = 66.7% WR, +$9.88
- ❌ LONGs at MIDDLE/UPPER (40-80%) = 0% WR, -$12.10
Explanation: This seems counterintuitive but:
- Top of range (80-100%): Strong momentum breakouts, continuation plays
- Middle/upper (40-80%): Weak momentum, often rejects or chops
BUT WARNING: Top winner (Q100, +$23.63) skews this. Need more data.
RSI Analysis by Direction
LONG Trades RSI Performance
| RSI Zone | Trades | Avg RSI | Win Rate | Total P&L |
|---|---|---|---|---|
| 70+ Overbought | 1 | 73.5 | 0.0% | -$17.09 ⚠️ |
| 60-70 Strong | 2 | 63.2 | 100.0% | +$26.97 ✅ |
| 50-60 Bullish | 2 | 57.3 | 0.0% | -$12.10 |
ACTIONABLE RULE FOR LONGs:
- ✅ ENTER: RSI 60-70 (strong but not overbought) = 100% WR
- ❌ BLOCK: RSI >70 (overbought) = biggest single loss (-$17.09)
- ❌ BLOCK: RSI <60 (weak momentum) = 0% WR
SHORT Trades RSI Performance
| RSI Zone | Trades | Avg RSI | Win Rate | Total P&L |
|---|---|---|---|---|
| 40-50 Bearish | 1 | 44.0 | 100.0% | +$0.03 |
| 30-40 Weak | 1 | 38.8 | 100.0% | +$0.38 |
Finding: Both SHORTs won with RSI 38-44 (bearish/weak zones) ✅
Exit Reason Breakdown
| Exit Reason | Count | Percentage | Avg P&L |
|---|---|---|---|
| SL | 4 | 57.1% | -$1.39 |
| TP1 | 2 | 28.6% | +$1.86 |
| manual | 1 | 14.3% | +$0.03 |
Finding: 57% of trades hit SL (stopped out). Only 29% hit TP1.
Winner vs Loser Comparison
Top Winner: Quality 100 LONG (+$23.63)
- Time: 12-10 19:35
- Metrics: ADX 24.8, RSI 63.0, Position 93.8%, Vol 1.30
- Exit: SL (but made profit somehow - check this)
- Pattern: Top of range (93.8%), RSI sweet spot (63.0), good volume
Worst Loser: Quality 75 LONG (-$17.09)
- Time: 12-15 02:19
- Metrics: ADX 23.4, RSI 73.5, Position 94.4%, Vol 1.41
- Exit: SL
- Pattern: Overbought RSI (73.5) at top of range
Key Difference: Winner at RSI 63, loser at RSI 73.5 → RSI 70+ is death zone
📊 OPTIMIZATION RECOMMENDATIONS
Immediate Filters (High Confidence)
1. BLOCK OVERBOUGHT LONGs (RSI >70)
if (direction === 'long' && rsi > 70) {
score -= 50 // Major penalty
blockReason = 'OVERBOUGHT_RSI_LONG'
}
Impact: Would have blocked -$17.09 loss (worst trade)
2. REQUIRE RSI 60-70 FOR LONGs
if (direction === 'long' && (rsi < 60 || rsi > 70)) {
score -= 30 // Significant penalty
}
Impact: Would have blocked 3 losers (-$29.19 total)
3. KEEP SHORT ENTRY LOGIC AS-IS
- SHORTs are 100% WR (2/2) with current filters
- RSI 30-50 range is working
- Don't change what's not broken
Medium Confidence Filters (Need More Data)
4. CONSIDER BLOCKING LOW ADX (<20)
- ADX 15-20 had 50% WR (-$4.04)
- But only 2 trades - need 5+ more
- Wait for more data
5. VALIDATE QUALITY 95-99 TIER
- Single failure at Q95 (-$4.72)
- Could be outlier
- Need 5+ trades in this tier before adjusting threshold
Research Questions (Collect More Data)
-
Price Position Paradox: Why do LONGs at top (80-100%) win more than middle (40-80%)?
- Hypothesis: Top = breakout momentum, middle = chop/rejection
- Need 10+ more trades to validate
-
Quality 95-99 Anomaly: Is Q95-99 actually worse than Q85-89?
- Current: Q85-89 = 100% WR, Q95-99 = 0% WR
- Need 5+ more trades at Q95-99
-
Volume Impact: Vol 1.30-1.42 on winners, 0.95-1.41 on losers
- Insufficient sample to determine threshold
- Need 20+ trades
Data Quality Assessment
Sample Size Status
- Total Trades: 7 (❌ Need 20+ minimum)
- LONG Trades: 5 (⚠️ Need 10+ minimum)
- SHORT Trades: 2 (❌ Need 10+ minimum)
- Quality Tiers: 1-2 per tier (❌ Need 5+ per tier)
- ADX Tiers: 1-4 per tier (⚠️ Borderline)
- RSI Zones: 1-2 per zone (❌ Need 5+ per zone)
Confidence Level: LOW - Most patterns need more data for validation
Exception: RSI >70 LONG disaster is strong signal (single worst trade, clear overbought condition)
Action Plan
Phase 1: Immediate (Today)
- ✅ Add RSI >70 penalty for LONGs (-50 points)
- ✅ Add RSI <60 penalty for LONGs (-30 points)
- 📊 Monitor next 10-15 trades to validate pattern
Phase 2: Data Collection (Next 7 Days)
- Collect 15+ more v11 trades (target: 20+ total)
- Validate RSI 60-70 sweet spot holds
- Check if quality 95-99 failure was outlier
- Analyze price position paradox with more data
Phase 3: Refinement (After 20+ Trades)
- Optimize ADX thresholds if pattern emerges
- Adjust quality score tiers based on larger sample
- Consider volume multiplier if correlation found
- Review position sizing based on RSI zones
Technical Notes
- Database: All queries from
Tradetable withindicatorVersion = 'v11' - Column Names:
adxAtEntry,rsiAtEntry,pricePositionAtEntry,volumeAtEntry - P&L:
realizedPnLfield (post-exit calculated values) - Date Range: Dec 10-15, 2025 (5 days of trading)
Appendix: All v11 Trades
Sorted by P&L (Best to Worst)
| Date | Symbol | Dir | Quality | ADX | RSI | Pos | Vol | Exit | P&L |
|---|---|---|---|---|---|---|---|---|---|
| 12-10 19:35 | SOL | LONG | 100 | 24.8 | 63.0 | 93.8 | 1.30 | SL | +$23.63 |
| 12-11 19:12 | SOL | LONG | 85 | 15.4 | 63.3 | 80.3 | 1.31 | TP1 | +$3.34 |
| 12-14 10:37 | SOL | SHORT | 105 | 34.2 | 38.8 | - | - | TP1 | +$0.38 |
| 12-10 22:37 | SOL | SHORT | 85 | 22.4 | 44.0 | - | - | manual | +$0.03 |
| 12-10 04:55 | SOL | LONG | 95 | 23.9 | 59.5 | 63.5 | 1.42 | SL | -$4.72 |
| 12-12 13:07 | SOL | LONG | 75 | 17.0 | 55.0 | 54.2 | 0.95 | SL | -$7.38 |
| 12-15 02:19 | SOL | LONG | 75 | 23.4 | 73.5 | 94.4 | 1.41 | SL | -$17.09 |
Total: -$1.80 (57.1% WR, 4 wins, 3 losses)
Conclusion
v11 has potential but needs RSI filter refinement for LONGs.
Clear Signal: RSI >70 for LONGs = disaster zone (-$17.09 worst trade)
Winning Pattern: RSI 60-70 for LONGs = 100% WR (+$26.97)
SHORTs: Working perfectly (2/2 wins), don't change
Next Steps:
- Add RSI 60-70 requirement for LONGs
- Block RSI >70 LONGs entirely
- Collect 15+ more trades to validate patterns
- Re-analyze after reaching 20+ trade sample size
🛡️ Bug #76 Protection System - DEPLOYED Dec 16, 2025
Root Cause Confirmed: Position cmj8abpjo00w8o407m3fndmx0 opened 07:52 UTC with TP1/TP2 orders but NO stop loss order (Bug #76 - Silent SL Placement Failure). Database shows:
tp1OrderTx: DsRv7E8v... ✅ (exists)tp2OrderTx: 3cmYgGE8... ✅ (exists)slOrderTx: NULL ❌ (never placed)softStopOrderTx: NULL ❌hardStopOrderTx: NULL ❌
User Impact: Position left completely unprotected. User saw TP orders in Drift UI and assumed SL existed. As price approached danger zone, checked more carefully and discovered SL missing.
User Interpretation: "TP1 and SL vanished as price approached stop loss" - but actually SL was never placed from the beginning (Drift order history only shows filled orders, not cancelled).
Prevention System Implemented:
Architecture: 3-Tier Exponential Backoff Verification
- Attempt 1: 30 seconds after position opens
- Attempt 2: 60 seconds (if Attempt 1 fails)
- Attempt 3: 90 seconds (if Attempt 2 fails)
- If all fail: Halt trading + close position immediately
Implementation Files
-
lib/safety/sl-verification.ts (new file)
querySLOrdersFromDrift()- Query Drift on-chain state for SL ordersverifySLWithRetries()- 3-tier verification with exponential backoffhaltTradingAndClosePosition()- Emergency halt + position closurecheckTradingAllowed()- Circuit breaker check before new trades
-
app/api/trading/execute/route.ts (modified)
- Circuit breaker check at line ~95 - rejects trades when halted
- Verification trigger at line ~1128 - starts after position added to manager
- Runs asynchronously in background (doesn't block trade execution)
Safety Features
- Drift On-Chain Verification: Queries actual Drift orders, not just database
- Circuit Breaker: Halts all new trades after critical SL placement failures
- Automatic Position Closure: Closes unprotected position immediately for safety
- Critical Telegram Alerts: Notifies user of halt + closure actions
- Rate Limit Efficient: 3-9 queries per position (vs 360/hour with interval-based)
User Mandate
"i mean the opening of the positions was/is working flawlessly so far. so i think simply check 30s/60s/90s after the position was opened that the risk management is in place. 3 calls after an action took place. thats still not much as we dont open trades that often.
if it fails. stop trading and close the current position. better safe than sorry"
Expected Behavior
- Position opens successfully at T+0s
- Verification Attempt 1 at T+30s → queries Drift for SL orders
- If SL found: SUCCESS, verification complete ✅
- If SL missing: Wait, retry at T+60s
- If still missing: Wait, retry at T+90s
- If still missing after 3 attempts:
- Set
tradingHalted = true(global flag) - Close position immediately via market order
- Send critical Telegram alert
- Reject all new trade requests with "Trading halted" error
- Require manual reset via API or Telegram command
- Set
Deployment
- Date: Dec 16, 2025 11:30 UTC
- Status: Code complete, ready for Docker build + deployment
- Git commits: Pending (to be committed after testing)
- Manual Reset: Required after halt - prevents cascading failures
"Better safe than sorry" - User's mandate prioritizes capital preservation over opportunity.