Files
trading_bot_v4/docs/ENHANCEMENT_6_ANALYSIS_GUIDE.md
mindesbunister db52299b55 feat: Enhancement #6 data collection + #1 implementation plan
Enhancement #6 - SL Distance Validation (Data Collection Phase):
- Added slDistanceAtEntry field to StopHunt schema
- Calculates distance from revenge entry to stop zone (LONG vs SHORT logic)
- Logs distance in dollars + × ATR multiplier
- Purpose: Collect 20+ revenge trade samples for optimal multiplier analysis
- Created comprehensive analysis guide with SQL queries
- Decision deferred until empirical data collected

Enhancement #1 - ADX Confirmation (Implementation Plan):
- Documented complete 1-minute TradingView alert strategy
- Storage analysis: 19.44 MB/month for 3 symbols (negligible)
- Two-phase approach: Cache-only MVP → Optional DB persistence
- Provided TradingView Pine Script (ready to use)
- Cost breakdown: Pro subscription $49.95/month required
- Benefits: Real-time ADX, pattern recognition, ML features
- Implementation checklist with validation phases

Files Changed:
- prisma/schema.prisma: +1 field (slDistanceAtEntry)
- lib/trading/stop-hunt-tracker.ts: +10 lines (distance calculation + logging)
- docs/1MIN_MARKET_DATA_IMPLEMENTATION.md: NEW (comprehensive plan)
- docs/ENHANCEMENT_6_ANALYSIS_GUIDE.md: NEW (SQL queries + decision matrix)

Status:
 Enhancement #4 and #10 deployed (previous commit)
 Enhancement #6 data collection enabled (this commit)
   Awaiting 20+ revenge trades for Enhancement #6 decision
2025-11-27 08:26:45 +01:00

9.9 KiB
Raw Permalink Blame History

Enhancement #6 - SL Distance Analysis Guide

Status: DATA COLLECTION ENABLED (Nov 27, 2025)
Purpose: Gather revenge trade data to determine optimal SL distance multiplier


What We're Tracking Now

Database Fields Added

-- In StopHunt table:
slDistanceAtEntry    Float?   -- Distance from entry to stop zone (absolute value)
revengeOutcome       String?  -- "TP1", "TP2", "SL", "TRAILING_SL"
revengePnL           Float?   -- Actual P&L from revenge trade
revengeFailedReason  String?  -- "stopped_again" if re-stopped
originalATR          Float?   -- ATR at original stop-out

What Gets Calculated

// When revenge trade executes:
const slDistance = direction === 'long'
  ? currentPrice - stopHuntPrice  // LONG: Room below entry
  : stopHuntPrice - currentPrice  // SHORT: Room above entry

// Stored: Math.abs(slDistance)
// Logged: Distance in $ and in ATR multiples
// Example: "$1.48 distance (2.47× ATR)"

Analysis Queries (After 20+ Revenge Trades)

Query 1: SL Distance vs Outcome

-- Do tighter entries get re-stopped more often?
SELECT 
  CASE 
    WHEN "slDistanceAtEntry" / "originalATR" < 1.0 THEN '<1× ATR (Very Tight)'
    WHEN "slDistanceAtEntry" / "originalATR" < 1.5 THEN '1-1.5× ATR (Tight)'
    WHEN "slDistanceAtEntry" / "originalATR" < 2.0 THEN '1.5-2× ATR (Moderate)'
    WHEN "slDistanceAtEntry" / "originalATR" < 2.5 THEN '2-2.5× ATR (Safe)'
    ELSE '2.5×+ ATR (Very Safe)'
  END as distance_tier,
  
  COUNT(*) as total_trades,
  
  -- Win rate
  ROUND(100.0 * SUM(CASE 
    WHEN "revengeOutcome" IN ('TP1', 'TP2', 'TRAILING_SL') THEN 1 
    ELSE 0 
  END) / COUNT(*), 1) as win_rate,
  
  -- Re-stopped rate
  ROUND(100.0 * SUM(CASE 
    WHEN "revengeFailedReason" = 'stopped_again' THEN 1 
    ELSE 0 
  END) / COUNT(*), 1) as restopped_rate,
  
  -- Average P&L
  ROUND(AVG("revengePnL"), 2) as avg_pnl,
  
  -- Total P&L
  ROUND(SUM("revengePnL"), 2) as total_pnl
  
FROM "StopHunt"
WHERE "revengeExecuted" = true
  AND "slDistanceAtEntry" IS NOT NULL
  AND "originalATR" IS NOT NULL
GROUP BY distance_tier
ORDER BY MIN("slDistanceAtEntry" / "originalATR");

Expected Output:

distance_tier          | total | win_rate | restopped_rate | avg_pnl | total_pnl
-----------------------+-------+----------+----------------+---------+-----------
<1× ATR (Very Tight)   |     5 |     40.0 |           60.0 |  -25.50 |   -127.50
1-1.5× ATR (Tight)     |     8 |     62.5 |           25.0 |   15.25 |    122.00
1.5-2× ATR (Moderate)  |    12 |     75.0 |           16.7 |   42.30 |    507.60
2-2.5× ATR (Safe)      |     7 |     85.7 |           14.3 |   68.45 |    479.15
2.5×+ ATR (Very Safe)  |     3 |    100.0 |            0.0 |   92.30 |    276.90

Decision Logic:

  • If <1× ATR has high re-stop rate (>50%): Filter needed
  • If 1.5-2× ATR has best risk/reward: Use 1.5× multiplier
  • If 2-2.5× ATR has highest win rate: Use 2.0× multiplier
  • If Very Safe (2.5×+) rarely happens: Lower multiplier to catch more

Query 2: Missed Opportunities

-- How many revenge opportunities didn't execute because reversal wasn't deep enough?
SELECT 
  COUNT(*) as total_stop_hunts,
  
  -- How many reversed but didn't enter
  SUM(CASE 
    WHEN "revengeExecuted" = false 
    AND "revengeWindowExpired" = true 
    AND ("lowestPriceAfterStop" < "originalEntryPrice" -- LONG reversed
         OR "highestPriceAfterStop" > "originalEntryPrice") -- SHORT reversed
    THEN 1 ELSE 0 
  END) as missed_reversals,
  
  -- Average distance of missed reversals
  ROUND(AVG(CASE 
    WHEN "revengeExecuted" = false 
    AND "revengeWindowExpired" = true 
    THEN ABS("lowestPriceAfterStop" - "stopHuntPrice") 
  END), 2) as avg_missed_distance
  
FROM "StopHunt"
WHERE "originalATR" IS NOT NULL;

Tells us: If we filter too strictly (e.g., 3× ATR), how many opportunities do we lose?


Query 3: Time to Re-Stop

-- How quickly do re-stopped revenge trades fail?
SELECT 
  CASE 
    WHEN EXTRACT(EPOCH FROM ("Trade"."exitTime" - "StopHunt"."revengeTime")) < 300 THEN '<5min (Instant)'
    WHEN EXTRACT(EPOCH FROM ("Trade"."exitTime" - "StopHunt"."revengeTime")) < 900 THEN '5-15min (Fast)'
    WHEN EXTRACT(EPOCH FROM ("Trade"."exitTime" - "StopHunt"."revengeTime")) < 1800 THEN '15-30min (Moderate)'
    ELSE '30min+ (Slow)'
  END as time_to_restop,
  
  COUNT(*) as count,
  
  ROUND(AVG("StopHunt"."slDistanceAtEntry" / "StopHunt"."originalATR"), 2) as avg_atr_multiple
  
FROM "StopHunt"
INNER JOIN "Trade" ON "StopHunt"."revengeTradeId" = "Trade"."id"
WHERE "StopHunt"."revengeFailedReason" = 'stopped_again'
GROUP BY time_to_restop
ORDER BY MIN(EXTRACT(EPOCH FROM ("Trade"."exitTime" - "StopHunt"."revengeTime")));

Insight: If most re-stops happen <5min, they're wicks. Wider SL distance helps.


Query 4: Direction-Specific Analysis

-- Do LONGs vs SHORTs need different SL distances?
SELECT 
  "direction",
  
  COUNT(*) as total,
  
  ROUND(AVG("slDistanceAtEntry" / "originalATR"), 2) as avg_atr_multiple,
  
  ROUND(100.0 * SUM(CASE 
    WHEN "revengeFailedReason" = 'stopped_again' THEN 1 ELSE 0 
  END) / COUNT(*), 1) as restopped_rate,
  
  ROUND(AVG("revengePnL"), 2) as avg_pnl
  
FROM "StopHunt"
WHERE "revengeExecuted" = true
  AND "slDistanceAtEntry" IS NOT NULL
GROUP BY "direction";

Possible outcome: SHORTs need wider distance (more volatile reversals)


Decision Matrix (After 20+ Trades)

Scenario A: Tight Entries Work

Data shows: 1-1.5× ATR has 70%+ win rate, low re-stop rate Decision: Use 1.5× ATR multiplier (or no filter at all) Trade-off: More revenge opportunities, slightly higher re-stop risk

Scenario B: Moderate Distance Optimal

Data shows: 1.5-2× ATR has best risk/reward Decision: Use 1.75× or 2.0× ATR multiplier Trade-off: Balanced approach (recommended starting point)

Scenario C: Wide Distance Required

Data shows: <2× ATR has >40% re-stop rate Decision: Use 2.5× or 3.0× ATR multiplier Trade-off: Fewer opportunities, but much higher win rate

Scenario D: Direction-Specific

Data shows: LONGs work at 1.5×, SHORTs need 2.5× Decision: Implement separate multipliers per direction Trade-off: More complex but optimized


Implementation Plan (After Data Collection)

Step 1: Review Data (After 20 Revenge Trades)

# Run Query 1 in database
docker exec trading-bot-postgres psql -U postgres -d trading_bot_v4 -c "
[Query 1 from above]
"

# Save results to CSV
# Analyze in spreadsheet or share with me

Step 2: Calculate Optimal Multiplier

# Simple calculation:
optimal_multiplier = (
  sum(distance * pnl for each tier) / 
  sum(pnl for each tier)
)

# Weight by win rate:
optimal_multiplier_weighted = (
  sum(distance * win_rate * count for each tier) /
  sum(win_rate * count for each tier)
)

Step 3: Implement Filter

// In stop-hunt-tracker.ts shouldExecuteRevenge()
const slDistance = stopHunt.direction === 'long'
  ? currentPrice - stopHunt.stopHuntPrice
  : stopHunt.stopHuntPrice - currentPrice

const minSafeDistance = stopHunt.originalATR * 2.0  // Use data-driven value

if (Math.abs(slDistance) < minSafeDistance) {
  console.log(`⚠️ SL distance too tight: ${Math.abs(slDistance).toFixed(2)} < ${minSafeDistance.toFixed(2)}`)
  return false
}

Step 4: A/B Test (Optional)

// Randomly assign filter on/off
const useFilter = Math.random() > 0.5

if (useFilter && Math.abs(slDistance) < minSafeDistance) {
  await prisma.stopHunt.update({
    where: { id: stopHunt.id },
    data: { 
      notes: 'Would have been filtered by SL distance check',
      filterTestGroup: 'A' 
    }
  })
  return false
}

// Compare groups after 40 trades (20 each)

Monitoring Dashboard

Key Metrics to Track

  1. Re-Stop Rate: % of revenge trades that hit SL immediately
  2. Average SL Distance: Median ATR multiple at entry
  3. Win Rate by Distance: Scatterplot (x=distance, y=outcome)
  4. Missed Opportunities: Reversals that didn't execute

Alert Thresholds

  • High re-stop rate: >30% revenge trades fail = tighten filter
  • Low execution rate: <50% stop hunts execute = loosen filter
  • Negative total P&L: Sum of revenge P&L < 0 = pause system

Timeline

Week 1-2: Data collection (first 5-10 revenge trades) Week 3: Preliminary analysis (spot obvious patterns) Week 4: Full analysis (20+ trades, statistical significance) Week 5: Implement optimal filter based on data Week 6+: Monitor improvement, adjust if needed

Current Status: Data collection enabled, awaiting first revenge trades


Example Real-World Analysis

Hypothetical Results After 25 Revenge Trades

Distance Tier        | Trades | Win Rate | Avg P&L | Total P&L
---------------------|--------|----------|---------|----------
<1× ATR              |      3 |     33%  |  -$45   |  -$135
1-1.5× ATR           |      7 |     57%  |   $12   |   $84
1.5-2× ATR           |      9 |     78%  |   $58   |  $522
2-2.5× ATR           |      4 |     75%  |   $62   |  $248
2.5×+ ATR            |      2 |    100%  |   $89   |  $178

Conclusion from data:

  • <1× ATR: High failure rate, negative P&L → FILTER OUT
  • 1-1.5× ATR: Break-even, mediocre win rate → QUESTIONABLE
  • 1.5-2× ATR: SWEET SPOT - 9 trades, 78% WR, +$522
  • 2-2.5× ATR: Good but fewer opportunities
  • 2.5×+ ATR: Perfect WR but rarely happens

Optimal setting: 1.5× ATR minimum distance Expected impact: Block 10 bad trades, keep 15 good trades = +$606 net improvement


Questions for Future AI Agents

  1. What's the optimal multiplier? → Check Query 1 results
  2. Are we missing too many opportunities? → Check Query 2 results
  3. Do re-stops happen instantly or slowly? → Check Query 3 results
  4. Do LONGs vs SHORTs need different filters? → Check Query 4 results
  5. Should we implement the filter? → If re-stop rate >30%, YES

Remember: Data > theory. Don't implement filter until data proves it helps.