/** * Stop Hunt Revenge Tracker (Nov 20, 2025) * * Tracks high-quality stop-outs (score 85+) and automatically re-enters * when the stop hunt reverses and the real move begins. * * How it works: * 1. When quality 85+ trade gets stopped out → Create StopHunt record * 2. Monitor price for 4 hours * 3. If price crosses back through original entry + ADX rebuilds → Auto re-enter with 1.2x size * 4. Send "🔥 REVENGE TRADE" notification * * Purpose: Catch the real move after getting swept by stop hunters */ import { getPrismaClient } from '../database/trades' import { initializeDriftService } from '../drift/client' import { getPythPriceMonitor } from '../pyth/price-monitor' interface StopHuntRecord { id: string originalTradeId: string symbol: string direction: 'long' | 'short' stopHuntPrice: number originalEntryPrice: number originalQualityScore: number originalADX: number | null originalATR: number | null stopLossAmount: number stopHuntTime: Date revengeExecuted: boolean revengeWindowExpired: boolean revengeExpiresAt: Date highestPriceAfterStop: number | null lowestPriceAfterStop: number | null } let trackerInstance: StopHuntTracker | null = null let monitoringInterval: NodeJS.Timeout | null = null export class StopHuntTracker { private prisma = getPrismaClient() private isMonitoring = false /** * Create stop hunt record when quality 85+ trade gets stopped out */ async recordStopHunt(params: { originalTradeId: string symbol: string direction: 'long' | 'short' stopHuntPrice: number originalEntryPrice: number originalQualityScore: number originalADX?: number originalATR?: number stopLossAmount: number }): Promise { // Only track quality 85+ stop-outs (high-confidence trades) if (params.originalQualityScore < 85) { console.log(`⚠️ Stop hunt not tracked: Quality ${params.originalQualityScore} < 85 threshold`) return } const revengeExpiresAt = new Date(Date.now() + 4 * 60 * 60 * 1000) // 4 hours try { await this.prisma.stopHunt.create({ data: { originalTradeId: params.originalTradeId, symbol: params.symbol, direction: params.direction, stopHuntPrice: params.stopHuntPrice, originalEntryPrice: params.originalEntryPrice, originalQualityScore: params.originalQualityScore, originalADX: params.originalADX || null, originalATR: params.originalATR || null, stopLossAmount: params.stopLossAmount, stopHuntTime: new Date(), revengeExpiresAt, } }) console.log(`🎯 STOP HUNT RECORDED: ${params.symbol} ${params.direction.toUpperCase()}`) console.log(` Quality: ${params.originalQualityScore}, Loss: $${params.stopLossAmount.toFixed(2)}`) console.log(` Revenge window: 4 hours (expires ${revengeExpiresAt.toLocaleTimeString()})`) // Start monitoring if not already running if (!this.isMonitoring) { this.startMonitoring() } } catch (error) { console.error('❌ Failed to record stop hunt:', error) } } /** * Start monitoring active stop hunts for revenge opportunities */ startMonitoring(): void { if (this.isMonitoring) return this.isMonitoring = true console.log('🔍 Stop Hunt Revenge Tracker: Monitoring started') // Check every 30 seconds monitoringInterval = setInterval(async () => { await this.checkRevengeOpportunities() }, 30 * 1000) } /** * Stop monitoring (cleanup on shutdown) */ stopMonitoring(): void { if (monitoringInterval) { clearInterval(monitoringInterval) monitoringInterval = null } this.isMonitoring = false console.log('🛑 Stop Hunt Revenge Tracker: Monitoring stopped') } /** * Check all active stop hunts for revenge entry conditions */ private async checkRevengeOpportunities(): Promise { try { // Get active stop hunts (not executed, not expired) const activeStopHunts = await this.prisma.stopHunt.findMany({ where: { revengeExecuted: false, revengeWindowExpired: false, revengeExpiresAt: { gt: new Date() // Not expired yet } } }) if (activeStopHunts.length === 0) { // No active stop hunts, stop monitoring to save resources if (this.isMonitoring) { console.log('📊 No active stop hunts - pausing monitoring') this.stopMonitoring() } return } console.log(`🔍 Checking ${activeStopHunts.length} active stop hunt(s)...`) for (const stopHunt of activeStopHunts) { await this.checkStopHunt(stopHunt as StopHuntRecord) } // Expire old stop hunts await this.expireOldStopHunts() } catch (error) { console.error('❌ Error checking revenge opportunities:', error) } } /** * Check individual stop hunt for revenge entry */ private async checkStopHunt(stopHunt: StopHuntRecord): Promise { try { // Get current price const priceMonitor = getPythPriceMonitor() const latestPrice = priceMonitor.getCachedPrice(stopHunt.symbol) if (!latestPrice || !latestPrice.price) { return // Price not available, skip } const currentPrice = latestPrice.price // Update high/low tracking const highestPrice = Math.max(currentPrice, stopHunt.highestPriceAfterStop || currentPrice) const lowestPrice = Math.min(currentPrice, stopHunt.lowestPriceAfterStop || currentPrice) await this.prisma.stopHunt.update({ where: { id: stopHunt.id }, data: { highestPriceAfterStop: highestPrice, lowestPriceAfterStop: lowestPrice, } }) // Check revenge conditions const shouldRevenge = this.shouldExecuteRevenge(stopHunt, currentPrice) if (shouldRevenge) { console.log(`🔥 REVENGE CONDITIONS MET: ${stopHunt.symbol} ${stopHunt.direction.toUpperCase()}`) await this.executeRevengeTrade(stopHunt, currentPrice) } } catch (error) { console.error(`❌ Error checking stop hunt ${stopHunt.id}:`, error) } } /** * Determine if revenge entry conditions are met */ private shouldExecuteRevenge(stopHunt: StopHuntRecord, currentPrice: number): boolean { const { direction, stopHuntPrice, originalEntryPrice } = stopHunt // REVENGE CONDITION: Price must cross back through original entry // This confirms the stop hunt has reversed and the real move is starting if (direction === 'long') { // Long stopped out above entry → price spiked up (stop hunt) // Revenge: Price drops back below original entry (confirms down move) const crossedBackDown = currentPrice < originalEntryPrice const movedEnoughFromStop = currentPrice < stopHuntPrice * 0.995 // 0.5% below stop if (crossedBackDown && movedEnoughFromStop) { console.log(` ✅ LONG revenge: Price ${currentPrice.toFixed(2)} crossed back below entry ${originalEntryPrice.toFixed(2)}`) return true } } else { // Short stopped out below entry → price spiked down (stop hunt) // Revenge: Price rises back above original entry (confirms up move) const crossedBackUp = currentPrice > originalEntryPrice const movedEnoughFromStop = currentPrice > stopHuntPrice * 1.005 // 0.5% above stop if (crossedBackUp && movedEnoughFromStop) { console.log(` ✅ SHORT revenge: Price ${currentPrice.toFixed(2)} crossed back above entry ${originalEntryPrice.toFixed(2)}`) return true } } return false } /** * Execute revenge trade automatically */ private async executeRevengeTrade(stopHunt: StopHuntRecord, currentPrice: number): Promise { try { console.log(`🔥 EXECUTING REVENGE TRADE: ${stopHunt.symbol} ${stopHunt.direction.toUpperCase()}`) console.log(` Original loss: $${stopHunt.stopLossAmount.toFixed(2)}`) console.log(` Revenge size: 1.2x (getting our money back!)`) // Call execute endpoint with revenge parameters const response = await fetch('http://localhost:3000/api/trading/execute', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${process.env.API_SECRET_KEY}` }, body: JSON.stringify({ symbol: stopHunt.symbol, direction: stopHunt.direction, currentPrice, timeframe: 'revenge', // Special timeframe for revenge trades signalSource: 'stop_hunt_revenge', // Use original quality metrics atr: stopHunt.originalATR || 0.45, adx: stopHunt.originalADX || 32, rsi: stopHunt.direction === 'long' ? 58 : 42, volumeRatio: 1.2, pricePosition: 50, // Metadata revengeMetadata: { originalTradeId: stopHunt.originalTradeId, stopHuntId: stopHunt.id, originalLoss: stopHunt.stopLossAmount, sizingMultiplier: 1.0 // Same size as original (user at 100% allocation) } }) }) const result = await response.json() if (result.success) { // Mark revenge as executed await this.prisma.stopHunt.update({ where: { id: stopHunt.id }, data: { revengeExecuted: true, revengeTradeId: result.trade?.id, revengeEntryPrice: currentPrice, revengeTime: new Date(), } }) console.log(`✅ REVENGE TRADE EXECUTED: ${result.trade?.id}`) console.log(`🔥 LET'S GET OUR MONEY BACK!`) // Send special Telegram notification await this.sendRevengeNotification(stopHunt, result.trade) } else { console.error(`❌ Revenge trade failed:`, result.error) } } catch (error) { console.error(`❌ Error executing revenge trade:`, error) } } /** * Send special Telegram notification for revenge trades */ private async sendRevengeNotification(stopHunt: StopHuntRecord, trade: any): Promise { try { const message = ` 🔥 REVENGE TRADE ACTIVATED 🔥 ${stopHunt.symbol} ${stopHunt.direction.toUpperCase()} 💀 Original Stop Hunt: -$${stopHunt.stopLossAmount.toFixed(2)} 🎯 Revenge Entry: $${trade.entryPrice.toFixed(2)} 💪 Position Size: $${trade.positionSizeUSD.toFixed(2)} (same as original) ⚔️ TIME FOR PAYBACK! Original Quality: ${stopHunt.originalQualityScore}/100 Stop Hunt Price: $${stopHunt.stopHuntPrice.toFixed(4)} Reversal Confirmed: Price crossed back through entry Let's get our money back! 💰 `.trim() await fetch(`https://api.telegram.org/bot${process.env.TELEGRAM_BOT_TOKEN}/sendMessage`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ chat_id: process.env.TELEGRAM_CHAT_ID, text: message, parse_mode: 'HTML' }) }) } catch (error) { console.error('❌ Failed to send revenge notification:', error) } } /** * Expire stop hunts that are past their 4-hour window */ private async expireOldStopHunts(): Promise { try { const expired = await this.prisma.stopHunt.updateMany({ where: { revengeExecuted: false, revengeWindowExpired: false, revengeExpiresAt: { lte: new Date() } }, data: { revengeWindowExpired: true } }) if (expired.count > 0) { console.log(`⏰ Expired ${expired.count} stop hunt revenge window(s)`) } } catch (error) { console.error('❌ Error expiring stop hunts:', error) } } } /** * Get singleton instance */ export function getStopHuntTracker(): StopHuntTracker { if (!trackerInstance) { trackerInstance = new StopHuntTracker() } return trackerInstance } /** * Start tracking (called on server startup) */ export async function startStopHuntTracking(): Promise { try { const tracker = getStopHuntTracker() const prisma = getPrismaClient() const activeCount = await prisma.stopHunt.count({ where: { revengeExecuted: false, revengeWindowExpired: false, revengeExpiresAt: { gt: new Date() } } }) if (activeCount > 0) { console.log(`🎯 Found ${activeCount} active stop hunt(s) - starting revenge tracker`) tracker.startMonitoring() } else { console.log('📊 No active stop hunts - tracker will start when needed') } } catch (error) { console.error('❌ Error starting stop hunt tracker:', error) } }