From 40ddac5a9539f203c8c02020b9350900a10e3538 Mon Sep 17 00:00:00 2001 From: mindesbunister Date: Wed, 26 Nov 2025 20:53:35 +0100 Subject: [PATCH] feat: Revenge timing Option 2 - 90s confirmation (DEPLOYED) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 --- docker-compose.yml | 17 +++++++++++++++++ lib/trading/stop-hunt-tracker.ts | 16 ++++++++-------- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index dc95e7b..32d7ccf 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -80,6 +80,15 @@ services: depends_on: - postgres + deploy: + resources: + limits: + memory: 2G + cpus: '2.0' + reservations: + memory: 512M + cpus: '0.5' + healthcheck: test: ["CMD", "node", "-e", "require('http').get('http://localhost:3000/api/health', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})"] interval: 30s @@ -117,6 +126,14 @@ services: - ./prisma/init.sql:/docker-entrypoint-initdb.d/init.sql:ro networks: - trading-net + deploy: + resources: + limits: + memory: 1G + cpus: '1.0' + reservations: + memory: 512M + cpus: '0.25' healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres"] interval: 10s diff --git a/lib/trading/stop-hunt-tracker.ts b/lib/trading/stop-hunt-tracker.ts index 4513419..e9252dc 100644 --- a/lib/trading/stop-hunt-tracker.ts +++ b/lib/trading/stop-hunt-tracker.ts @@ -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 {