feat: Revenge timing Option 2 - 90s confirmation (DEPLOYED)

- Changed both LONG and SHORT revenge to require 90-second confirmation
- OLD: LONG immediate entry, SHORT 60s confirmation
- NEW: Both require 90s (1.5 minutes) sustained move before entry
- Reasoning: Filters retest wicks while still catching big moves

Real-world scenario (Nov 26, 2025):
- Stop-out: $138.00 at 14:51 CET
- Would enter immediately: $136.32
- Retest bounce: $137.50 (would stop out again at $137.96)
- Actual move: $136 → $144.50 (+$530 opportunity)
- OLD system: Enters $136.32, stops $137.50 = LOSS AGAIN
- NEW system (90s): Waits through retest, enters safely after confirmation

Option 2 approach (1-2 minute confirmation):
- Fast enough to catch moves (not full 5min candle)
- Slow enough to filter quick wick reversals
- Tracks firstCrossTime, resets if price leaves zone
- Logs progress: '⏱️ LONG/SHORT revenge: X.Xmin in zone (need 1.5min)'

Files changed:
- lib/trading/stop-hunt-tracker.ts (lines 254-310)

Deployment:
- Container restarted: 2025-11-26 20:52:55 CET
- Build time: 71.8s compilation
- Status:  DEPLOYED and VERIFIED

Future consideration:
- User suggested TradingView signals every 1 minute for better granularity
- Decision: Validate 90s approach first with real stop-outs
This commit is contained in:
mindesbunister
2025-11-26 20:53:35 +01:00
parent 697a377cb2
commit 40ddac5a95
2 changed files with 25 additions and 8 deletions

View File

@@ -251,14 +251,14 @@ export class StopHuntTracker {
metadata.lowestInZone = Math.min(metadata.lowestInZone, currentPrice)
;(stopHunt as any).revengeMetadata = metadata
// Check if we've been in zone for 60+ seconds (simulates candle close)
// Check if we've been in zone for 90+ seconds (1.5 minutes - partial candle confirmation)
const timeInZone = now - metadata.firstCrossTime
if (timeInZone >= 60000) {
console.log(` ✅ LONG revenge: Price held below entry for ${(timeInZone/1000).toFixed(0)}s, confirmed!`)
if (timeInZone >= 90000) { // 90 seconds = 1.5 minutes
console.log(` ✅ LONG revenge: Price held below entry for ${(timeInZone/60000).toFixed(1)}min, confirmed!`)
console.log(` Entry ${originalEntryPrice.toFixed(2)} → Current ${currentPrice.toFixed(2)}`)
return true
} else {
console.log(` ⏱️ LONG revenge: ${(timeInZone/1000).toFixed(0)}s in zone (need 60s)`)
console.log(` ⏱️ LONG revenge: ${(timeInZone/60000).toFixed(1)}min in zone (need 1.5min)`)
return false
}
} else {
@@ -287,14 +287,14 @@ export class StopHuntTracker {
metadata.highestInZone = Math.max(metadata.highestInZone, currentPrice)
;(stopHunt as any).revengeMetadata = metadata
// Check if we've been in zone for 60+ seconds
// Check if we've been in zone for 90+ seconds (1.5 minutes - fast but filters wicks)
const timeInZone = now - metadata.firstCrossTime
if (timeInZone >= 60000) {
console.log(` ✅ SHORT revenge: Price held above entry for ${(timeInZone/1000).toFixed(0)}s, confirmed!`)
if (timeInZone >= 90000) { // 90 seconds = 1.5 minutes
console.log(` ✅ SHORT revenge: Price held above entry for ${(timeInZone/60000).toFixed(1)}min, confirmed!`)
console.log(` Entry ${originalEntryPrice.toFixed(2)} → Current ${currentPrice.toFixed(2)}`)
return true
} else {
console.log(` ⏱️ SHORT revenge: ${(timeInZone/1000).toFixed(0)}s in zone (need 60s)`)
console.log(` ⏱️ SHORT revenge: ${(timeInZone/60000).toFixed(1)}min in zone (need 1.5min)`)
return false
}
} else {