feat: Add production logging gating (Phase 1, Task 1.1)
- Created logger utility with environment-based gating (lib/utils/logger.ts) - Replaced 517 console.log statements with logger.log (71% reduction) - Fixed import paths in 15 files (resolved comment-trapped imports) - Added DEBUG_LOGS=false to .env - Achieves 71% immediate log reduction (517/731 statements) - Expected 90% reduction in production when deployed Impact: Reduced I/O blocking, lower log volume in production Risk: LOW (easy rollback, non-invasive) Phase: Phase 1, Task 1.1 (Quick Wins - Console.log Production Gating) Files changed: - NEW: lib/utils/logger.ts (production-safe logging) - NEW: scripts/replace-console-logs.js (automation tool) - Modified: 15 lib/*.ts files (console.log → logger.log) - Modified: .env (DEBUG_LOGS=false) Next: Task 1.2 (Image Size Optimization)
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
import { logger } from '../utils/logger'
|
||||
|
||||
/**
|
||||
* Market Data Cache Service
|
||||
*
|
||||
@@ -29,7 +31,7 @@ class MarketDataCache {
|
||||
*/
|
||||
set(symbol: string, metrics: MarketMetrics): void {
|
||||
this.cache.set(symbol, metrics)
|
||||
console.log(
|
||||
logger.log(
|
||||
`📊 Cached market data for ${symbol}: ` +
|
||||
`ADX=${metrics.adx.toFixed(1)} ` +
|
||||
`ATR=${metrics.atr.toFixed(2)}% ` +
|
||||
@@ -46,18 +48,18 @@ class MarketDataCache {
|
||||
const data = this.cache.get(symbol)
|
||||
|
||||
if (!data) {
|
||||
console.log(`⚠️ No cached data for ${symbol}`)
|
||||
logger.log(`⚠️ No cached data for ${symbol}`)
|
||||
return null
|
||||
}
|
||||
|
||||
const ageSeconds = Math.round((Date.now() - data.timestamp) / 1000)
|
||||
|
||||
if (Date.now() - data.timestamp > this.MAX_AGE_MS) {
|
||||
console.log(`⏰ Cached data for ${symbol} is stale (${ageSeconds}s old, max 300s)`)
|
||||
logger.log(`⏰ Cached data for ${symbol} is stale (${ageSeconds}s old, max 300s)`)
|
||||
return null
|
||||
}
|
||||
|
||||
console.log(`✅ Using fresh TradingView data for ${symbol} (${ageSeconds}s old)`)
|
||||
logger.log(`✅ Using fresh TradingView data for ${symbol} (${ageSeconds}s old)`)
|
||||
return data
|
||||
}
|
||||
|
||||
@@ -102,7 +104,7 @@ class MarketDataCache {
|
||||
*/
|
||||
clear(): void {
|
||||
this.cache.clear()
|
||||
console.log('🗑️ Market data cache cleared')
|
||||
logger.log('🗑️ Market data cache cleared')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,7 +114,7 @@ let marketDataCache: MarketDataCache | null = null
|
||||
export function getMarketDataCache(): MarketDataCache {
|
||||
if (!marketDataCache) {
|
||||
marketDataCache = new MarketDataCache()
|
||||
console.log('🔧 Initialized Market Data Cache (5min expiry)')
|
||||
logger.log('🔧 Initialized Market Data Cache (5min expiry)')
|
||||
}
|
||||
return marketDataCache
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,6 +6,7 @@
|
||||
*/
|
||||
|
||||
import { getRecentSignals } from '../database/trades'
|
||||
import { logger } from '../utils/logger'
|
||||
|
||||
export interface SignalQualityResult {
|
||||
score: number
|
||||
@@ -235,7 +236,7 @@ export async function scoreSignalQuality(params: {
|
||||
(params.currentPrice - recentSignals.oppositeDirectionPrice) / recentSignals.oppositeDirectionPrice * 100
|
||||
)
|
||||
|
||||
console.log(`🔍 Flip-flop price check: $${recentSignals.oppositeDirectionPrice.toFixed(2)} → $${params.currentPrice.toFixed(2)} = ${priceChangePercent.toFixed(2)}%`)
|
||||
logger.log(`🔍 Flip-flop price check: $${recentSignals.oppositeDirectionPrice.toFixed(2)} → $${params.currentPrice.toFixed(2)} = ${priceChangePercent.toFixed(2)}%`)
|
||||
|
||||
if (priceChangePercent < 2.0) {
|
||||
// Small price move = consolidation/chop = BAD
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
*/
|
||||
|
||||
import { getMarketDataCache } from './market-data-cache'
|
||||
import { logger } from '../utils/logger'
|
||||
import { getPythPriceMonitor } from '../pyth/price-monitor'
|
||||
|
||||
export interface QueuedSignal {
|
||||
@@ -85,7 +86,7 @@ export class SmartEntryTimer {
|
||||
monitorIntervalMs: 15000 // 15 seconds
|
||||
}
|
||||
|
||||
console.log('💡 Smart Entry Timer initialized:', {
|
||||
logger.log('💡 Smart Entry Timer initialized:', {
|
||||
enabled: this.config.enabled,
|
||||
maxWait: `${this.config.maxWaitMs / 1000}s`,
|
||||
pullback: `${this.config.pullbackMin}-${this.config.pullbackMax}%`,
|
||||
@@ -137,10 +138,10 @@ export class SmartEntryTimer {
|
||||
|
||||
this.queuedSignals.set(signal.id, signal)
|
||||
|
||||
console.log(`📥 Smart Entry: Queued signal ${signal.id}`)
|
||||
console.log(` ${signal.direction.toUpperCase()} ${signal.symbol} @ $${signal.signalPrice.toFixed(2)}`)
|
||||
console.log(` Target pullback: ${this.config.pullbackMin}-${this.config.pullbackMax}%`)
|
||||
console.log(` Max wait: ${this.config.maxWaitMs / 1000}s`)
|
||||
logger.log(`📥 Smart Entry: Queued signal ${signal.id}`)
|
||||
logger.log(` ${signal.direction.toUpperCase()} ${signal.symbol} @ $${signal.signalPrice.toFixed(2)}`)
|
||||
logger.log(` Target pullback: ${this.config.pullbackMin}-${this.config.pullbackMax}%`)
|
||||
logger.log(` Max wait: ${this.config.maxWaitMs / 1000}s`)
|
||||
|
||||
// Start monitoring if not already running
|
||||
if (!this.monitoringInterval) {
|
||||
@@ -156,7 +157,7 @@ export class SmartEntryTimer {
|
||||
private startMonitoring(): void {
|
||||
if (this.monitoringInterval) return
|
||||
|
||||
console.log(`👁️ Smart Entry: Starting monitoring loop (${this.config.monitorIntervalMs / 1000}s interval)`)
|
||||
logger.log(`👁️ Smart Entry: Starting monitoring loop (${this.config.monitorIntervalMs / 1000}s interval)`)
|
||||
|
||||
this.monitoringInterval = setInterval(() => {
|
||||
this.checkAllSignals()
|
||||
@@ -170,7 +171,7 @@ export class SmartEntryTimer {
|
||||
if (this.monitoringInterval) {
|
||||
clearInterval(this.monitoringInterval)
|
||||
this.monitoringInterval = null
|
||||
console.log(`⏸️ Smart Entry: Monitoring stopped (no active signals)`)
|
||||
logger.log(`⏸️ Smart Entry: Monitoring stopped (no active signals)`)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,7 +188,7 @@ export class SmartEntryTimer {
|
||||
|
||||
// Check for timeout
|
||||
if (now >= signal.expiresAt) {
|
||||
console.log(`⏰ Smart Entry: Timeout for ${signal.symbol} (waited ${this.config.maxWaitMs / 1000}s)`)
|
||||
logger.log(`⏰ Smart Entry: Timeout for ${signal.symbol} (waited ${this.config.maxWaitMs / 1000}s)`)
|
||||
const priceMonitor = getPythPriceMonitor()
|
||||
const latestPrice = priceMonitor.getCachedPrice(signal.symbol)
|
||||
const currentPrice = latestPrice?.price || signal.signalPrice
|
||||
@@ -210,7 +211,7 @@ export class SmartEntryTimer {
|
||||
const latestPrice = priceMonitor.getCachedPrice(signal.symbol)
|
||||
|
||||
if (!latestPrice || !latestPrice.price) {
|
||||
console.log(`⚠️ Smart Entry: No price available for ${signal.symbol}, skipping check`)
|
||||
logger.log(`⚠️ Smart Entry: No price available for ${signal.symbol}, skipping check`)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -234,18 +235,18 @@ export class SmartEntryTimer {
|
||||
}
|
||||
|
||||
// Log check
|
||||
console.log(`🔍 Smart Entry: Checking ${signal.symbol} (check #${signal.checksPerformed})`)
|
||||
console.log(` Signal: $${signal.signalPrice.toFixed(2)} → Current: $${currentPrice.toFixed(2)}`)
|
||||
console.log(` Pullback: ${pullbackMagnitude.toFixed(2)}% (target ${signal.targetPullbackMin}-${signal.targetPullbackMax}%)`)
|
||||
logger.log(`🔍 Smart Entry: Checking ${signal.symbol} (check #${signal.checksPerformed})`)
|
||||
logger.log(` Signal: $${signal.signalPrice.toFixed(2)} → Current: $${currentPrice.toFixed(2)}`)
|
||||
logger.log(` Pullback: ${pullbackMagnitude.toFixed(2)}% (target ${signal.targetPullbackMin}-${signal.targetPullbackMax}%)`)
|
||||
|
||||
// Check if pullback is in target range
|
||||
if (pullbackMagnitude < signal.targetPullbackMin) {
|
||||
console.log(` ⏳ Waiting for pullback (${pullbackMagnitude.toFixed(2)}% < ${signal.targetPullbackMin}%)`)
|
||||
logger.log(` ⏳ Waiting for pullback (${pullbackMagnitude.toFixed(2)}% < ${signal.targetPullbackMin}%)`)
|
||||
return
|
||||
}
|
||||
|
||||
if (pullbackMagnitude > signal.targetPullbackMax) {
|
||||
console.log(` ⚠️ Pullback too large (${pullbackMagnitude.toFixed(2)}% > ${signal.targetPullbackMax}%), might be reversal - waiting`)
|
||||
logger.log(` ⚠️ Pullback too large (${pullbackMagnitude.toFixed(2)}% > ${signal.targetPullbackMax}%), might be reversal - waiting`)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -262,22 +263,22 @@ export class SmartEntryTimer {
|
||||
const now = Date.now()
|
||||
const dataAge = (now - latestMetrics.timestamp) / 1000
|
||||
|
||||
console.log(` 📊 Real-time validation (data age: ${dataAge.toFixed(0)}s):`)
|
||||
logger.log(` 📊 Real-time validation (data age: ${dataAge.toFixed(0)}s):`)
|
||||
|
||||
// 1. ADX degradation check (original logic)
|
||||
if (latestMetrics.adx) {
|
||||
const adxDrop = signal.signalADX - latestMetrics.adx
|
||||
|
||||
if (adxDrop > signal.adxTolerance) {
|
||||
console.log(` ❌ ADX degraded: ${signal.signalADX.toFixed(1)} → ${latestMetrics.adx.toFixed(1)} (dropped ${adxDrop.toFixed(1)} points, max ${signal.adxTolerance})`)
|
||||
logger.log(` ❌ ADX degraded: ${signal.signalADX.toFixed(1)} → ${latestMetrics.adx.toFixed(1)} (dropped ${adxDrop.toFixed(1)} points, max ${signal.adxTolerance})`)
|
||||
signal.status = 'cancelled'
|
||||
signal.executionReason = 'manual_override'
|
||||
this.queuedSignals.delete(signal.id)
|
||||
console.log(` 🚫 Signal cancelled: ADX degradation exceeded tolerance`)
|
||||
logger.log(` 🚫 Signal cancelled: ADX degradation exceeded tolerance`)
|
||||
return
|
||||
}
|
||||
|
||||
console.log(` ✅ ADX: ${signal.signalADX.toFixed(1)} → ${latestMetrics.adx.toFixed(1)} (within tolerance)`)
|
||||
logger.log(` ✅ ADX: ${signal.signalADX.toFixed(1)} → ${latestMetrics.adx.toFixed(1)} (within tolerance)`)
|
||||
}
|
||||
|
||||
// 2. Volume degradation check (NEW)
|
||||
@@ -289,15 +290,15 @@ export class SmartEntryTimer {
|
||||
|
||||
// Cancel if volume dropped >40%
|
||||
if (volumeDrop > 40) {
|
||||
console.log(` ❌ Volume collapsed: ${originalVolume.toFixed(2)}x → ${currentVolume.toFixed(2)}x (${volumeDrop.toFixed(0)}% drop)`)
|
||||
logger.log(` ❌ Volume collapsed: ${originalVolume.toFixed(2)}x → ${currentVolume.toFixed(2)}x (${volumeDrop.toFixed(0)}% drop)`)
|
||||
signal.status = 'cancelled'
|
||||
signal.executionReason = 'manual_override'
|
||||
this.queuedSignals.delete(signal.id)
|
||||
console.log(` 🚫 Signal cancelled: Volume degradation - momentum fading`)
|
||||
logger.log(` 🚫 Signal cancelled: Volume degradation - momentum fading`)
|
||||
return
|
||||
}
|
||||
|
||||
console.log(` ✅ Volume: ${originalVolume.toFixed(2)}x → ${currentVolume.toFixed(2)}x`)
|
||||
logger.log(` ✅ Volume: ${originalVolume.toFixed(2)}x → ${currentVolume.toFixed(2)}x`)
|
||||
}
|
||||
|
||||
// 3. RSI reversal check (NEW)
|
||||
@@ -309,26 +310,26 @@ export class SmartEntryTimer {
|
||||
if (signal.direction === 'long') {
|
||||
// LONG: Cancel if RSI dropped into oversold (<30)
|
||||
if (originalRSI >= 40 && currentRSI < 30) {
|
||||
console.log(` ❌ RSI collapsed: ${originalRSI.toFixed(1)} → ${currentRSI.toFixed(1)} (now oversold)`)
|
||||
logger.log(` ❌ RSI collapsed: ${originalRSI.toFixed(1)} → ${currentRSI.toFixed(1)} (now oversold)`)
|
||||
signal.status = 'cancelled'
|
||||
signal.executionReason = 'manual_override'
|
||||
this.queuedSignals.delete(signal.id)
|
||||
console.log(` 🚫 Signal cancelled: RSI reversal - trend weakening`)
|
||||
logger.log(` 🚫 Signal cancelled: RSI reversal - trend weakening`)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
// SHORT: Cancel if RSI rose into overbought (>70)
|
||||
if (originalRSI <= 60 && currentRSI > 70) {
|
||||
console.log(` ❌ RSI spiked: ${originalRSI.toFixed(1)} → ${currentRSI.toFixed(1)} (now overbought)`)
|
||||
logger.log(` ❌ RSI spiked: ${originalRSI.toFixed(1)} → ${currentRSI.toFixed(1)} (now overbought)`)
|
||||
signal.status = 'cancelled'
|
||||
signal.executionReason = 'manual_override'
|
||||
this.queuedSignals.delete(signal.id)
|
||||
console.log(` 🚫 Signal cancelled: RSI reversal - trend weakening`)
|
||||
logger.log(` 🚫 Signal cancelled: RSI reversal - trend weakening`)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
console.log(` ✅ RSI: ${originalRSI.toFixed(1)} → ${currentRSI.toFixed(1)}`)
|
||||
logger.log(` ✅ RSI: ${originalRSI.toFixed(1)} → ${currentRSI.toFixed(1)}`)
|
||||
}
|
||||
|
||||
// 4. MAGAP divergence check (NEW)
|
||||
@@ -338,36 +339,36 @@ export class SmartEntryTimer {
|
||||
|
||||
if (signal.direction === 'long' && currentMAGap < -1.0) {
|
||||
// LONG but MAs now bearish diverging
|
||||
console.log(` ❌ MA structure bearish: MAGAP ${currentMAGap.toFixed(2)}% (death cross accelerating)`)
|
||||
logger.log(` ❌ MA structure bearish: MAGAP ${currentMAGap.toFixed(2)}% (death cross accelerating)`)
|
||||
signal.status = 'cancelled'
|
||||
signal.executionReason = 'manual_override'
|
||||
this.queuedSignals.delete(signal.id)
|
||||
console.log(` 🚫 Signal cancelled: MA structure turned bearish`)
|
||||
logger.log(` 🚫 Signal cancelled: MA structure turned bearish`)
|
||||
return
|
||||
}
|
||||
|
||||
if (signal.direction === 'short' && currentMAGap > 1.0) {
|
||||
// SHORT but MAs now bullish diverging
|
||||
console.log(` ❌ MA structure bullish: MAGAP ${currentMAGap.toFixed(2)}% (golden cross accelerating)`)
|
||||
logger.log(` ❌ MA structure bullish: MAGAP ${currentMAGap.toFixed(2)}% (golden cross accelerating)`)
|
||||
signal.status = 'cancelled'
|
||||
signal.executionReason = 'manual_override'
|
||||
this.queuedSignals.delete(signal.id)
|
||||
console.log(` 🚫 Signal cancelled: MA structure turned bullish`)
|
||||
logger.log(` 🚫 Signal cancelled: MA structure turned bullish`)
|
||||
return
|
||||
}
|
||||
|
||||
console.log(` ✅ MAGAP: ${currentMAGap.toFixed(2)}%`)
|
||||
logger.log(` ✅ MAGAP: ${currentMAGap.toFixed(2)}%`)
|
||||
}
|
||||
|
||||
console.log(` ✅ All real-time validations passed - signal quality maintained`)
|
||||
logger.log(` ✅ All real-time validations passed - signal quality maintained`)
|
||||
} else {
|
||||
console.log(` ⚠️ No fresh market data available - proceeding with original signal`)
|
||||
logger.log(` ⚠️ No fresh market data available - proceeding with original signal`)
|
||||
}
|
||||
|
||||
// All conditions met - execute!
|
||||
console.log(`✅ Smart Entry: OPTIMAL ENTRY CONFIRMED`)
|
||||
console.log(` Pullback: ${pullbackMagnitude.toFixed(2)}% (target ${signal.targetPullbackMin}-${signal.targetPullbackMax}%)`)
|
||||
console.log(` Price improvement: $${signal.signalPrice.toFixed(2)} → $${currentPrice.toFixed(2)}`)
|
||||
logger.log(`✅ Smart Entry: OPTIMAL ENTRY CONFIRMED`)
|
||||
logger.log(` Pullback: ${pullbackMagnitude.toFixed(2)}% (target ${signal.targetPullbackMin}-${signal.targetPullbackMax}%)`)
|
||||
logger.log(` Price improvement: $${signal.signalPrice.toFixed(2)} → $${currentPrice.toFixed(2)}`)
|
||||
|
||||
await this.executeSignal(signal, currentPrice, 'pullback_confirmed')
|
||||
}
|
||||
@@ -388,10 +389,10 @@ export class SmartEntryTimer {
|
||||
const improvement = ((signal.signalPrice - entryPrice) / signal.signalPrice) * 100
|
||||
const improvementDirection = signal.direction === 'long' ? improvement : -improvement
|
||||
|
||||
console.log(`🎯 Smart Entry: EXECUTING ${signal.direction.toUpperCase()} ${signal.symbol}`)
|
||||
console.log(` Signal Price: $${signal.signalPrice.toFixed(2)}`)
|
||||
console.log(` Entry Price: $${entryPrice.toFixed(2)}`)
|
||||
console.log(` Improvement: ${improvementDirection >= 0 ? '+' : ''}${improvementDirection.toFixed(2)}%`)
|
||||
logger.log(`🎯 Smart Entry: EXECUTING ${signal.direction.toUpperCase()} ${signal.symbol}`)
|
||||
logger.log(` Signal Price: $${signal.signalPrice.toFixed(2)}`)
|
||||
logger.log(` Entry Price: $${entryPrice.toFixed(2)}`)
|
||||
logger.log(` Improvement: ${improvementDirection >= 0 ? '+' : ''}${improvementDirection.toFixed(2)}%`)
|
||||
|
||||
// Execute the actual trade through Drift
|
||||
try {
|
||||
@@ -423,7 +424,7 @@ export class SmartEntryTimer {
|
||||
signal.qualityScore
|
||||
)
|
||||
|
||||
console.log(` Opening position: $${positionSizeUSD.toFixed(2)} at ${leverage}x leverage`)
|
||||
logger.log(` Opening position: $${positionSizeUSD.toFixed(2)} at ${leverage}x leverage`)
|
||||
|
||||
// Open position
|
||||
const openResult = await openPosition({
|
||||
@@ -439,7 +440,7 @@ export class SmartEntryTimer {
|
||||
}
|
||||
|
||||
const fillPrice = openResult.fillPrice!
|
||||
console.log(`✅ Smart Entry: Position opened at $${fillPrice.toFixed(2)}`)
|
||||
logger.log(`✅ Smart Entry: Position opened at $${fillPrice.toFixed(2)}`)
|
||||
|
||||
// Calculate TP/SL prices
|
||||
let tp1Percent = config.takeProfit1Percent
|
||||
@@ -505,7 +506,7 @@ export class SmartEntryTimer {
|
||||
|
||||
if (exitRes.success) {
|
||||
exitOrderSignatures = exitRes.signatures || []
|
||||
console.log(`✅ Smart Entry: Exit orders placed - ${exitOrderSignatures.length} orders`)
|
||||
logger.log(`✅ Smart Entry: Exit orders placed - ${exitOrderSignatures.length} orders`)
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(`❌ Smart Entry: Error placing exit orders:`, err)
|
||||
@@ -551,7 +552,7 @@ export class SmartEntryTimer {
|
||||
}
|
||||
})
|
||||
|
||||
console.log(`💾 Smart Entry: Trade saved to database`)
|
||||
logger.log(`💾 Smart Entry: Trade saved to database`)
|
||||
} catch (dbError) {
|
||||
console.error(`❌ Smart Entry: Failed to save trade:`, dbError)
|
||||
const { logCriticalError } = await import('../utils/persistent-logger')
|
||||
@@ -616,14 +617,14 @@ export class SmartEntryTimer {
|
||||
}
|
||||
|
||||
await positionManager.addTrade(activeTrade)
|
||||
console.log(`📊 Smart Entry: Added to Position Manager`)
|
||||
logger.log(`📊 Smart Entry: Added to Position Manager`)
|
||||
} catch (pmError) {
|
||||
console.error(`❌ Smart Entry: Failed to add to Position Manager:`, pmError)
|
||||
}
|
||||
|
||||
console.log(`✅ Smart Entry: Execution complete for ${signal.symbol}`)
|
||||
console.log(` Entry improvement: ${improvementDirection >= 0 ? '+' : ''}${improvementDirection.toFixed(2)}%`)
|
||||
console.log(` Estimated value: $${(Math.abs(improvementDirection) / 100 * positionSizeUSD).toFixed(2)}`)
|
||||
logger.log(`✅ Smart Entry: Execution complete for ${signal.symbol}`)
|
||||
logger.log(` Entry improvement: ${improvementDirection >= 0 ? '+' : ''}${improvementDirection.toFixed(2)}%`)
|
||||
logger.log(` Estimated value: $${(Math.abs(improvementDirection) / 100 * positionSizeUSD).toFixed(2)}`)
|
||||
|
||||
} catch (error) {
|
||||
console.error(`❌ Smart Entry: Execution error:`, error)
|
||||
@@ -632,7 +633,7 @@ export class SmartEntryTimer {
|
||||
// Remove from queue after brief delay (for logging)
|
||||
setTimeout(() => {
|
||||
this.queuedSignals.delete(signal.id)
|
||||
console.log(`🗑️ Smart Entry: Cleaned up signal ${signal.id}`)
|
||||
logger.log(`🗑️ Smart Entry: Cleaned up signal ${signal.id}`)
|
||||
|
||||
if (this.queuedSignals.size === 0) {
|
||||
this.stopMonitoring()
|
||||
@@ -688,7 +689,7 @@ export class SmartEntryTimer {
|
||||
|
||||
signal.status = 'cancelled'
|
||||
this.queuedSignals.delete(signalId)
|
||||
console.log(`🚫 Smart Entry: Cancelled signal ${signalId}`)
|
||||
logger.log(`🚫 Smart Entry: Cancelled signal ${signalId}`)
|
||||
|
||||
return true
|
||||
}
|
||||
@@ -713,5 +714,5 @@ export function getSmartEntryTimer(): SmartEntryTimer {
|
||||
|
||||
export function startSmartEntryTracking(): void {
|
||||
getSmartEntryTimer()
|
||||
console.log('✅ Smart Entry Timer service initialized')
|
||||
logger.log('✅ Smart Entry Timer service initialized')
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
*/
|
||||
|
||||
import { getMarketDataCache } from './market-data-cache'
|
||||
import { logger } from '../utils/logger'
|
||||
import { getMergedConfig } from '../../config/trading'
|
||||
import { sendValidationNotification } from '../notifications/telegram'
|
||||
|
||||
@@ -49,7 +50,7 @@ class SmartValidationQueue {
|
||||
private isMonitoring = false
|
||||
|
||||
constructor() {
|
||||
console.log('🧠 Smart Validation Queue initialized')
|
||||
logger.log('🧠 Smart Validation Queue initialized')
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -110,8 +111,8 @@ class SmartValidationQueue {
|
||||
}
|
||||
|
||||
this.queue.set(signalId, queuedSignal)
|
||||
console.log(`⏰ Smart validation queued: ${params.symbol} ${params.direction.toUpperCase()} @ $${params.originalPrice.toFixed(2)} (quality: ${params.qualityScore})`)
|
||||
console.log(` Watching for ${queuedSignal.entryWindowMinutes}min: +${queuedSignal.confirmationThreshold}% confirms, ${queuedSignal.maxDrawdown}% abandons`)
|
||||
logger.log(`⏰ Smart validation queued: ${params.symbol} ${params.direction.toUpperCase()} @ $${params.originalPrice.toFixed(2)} (quality: ${params.qualityScore})`)
|
||||
logger.log(` Watching for ${queuedSignal.entryWindowMinutes}min: +${queuedSignal.confirmationThreshold}% confirms, ${queuedSignal.maxDrawdown}% abandons`)
|
||||
|
||||
// Send Telegram notification
|
||||
await sendValidationNotification({
|
||||
@@ -139,7 +140,7 @@ class SmartValidationQueue {
|
||||
}
|
||||
|
||||
this.isMonitoring = true
|
||||
console.log('👁️ Smart validation monitoring started (checks every 30s)')
|
||||
logger.log('👁️ Smart validation monitoring started (checks every 30s)')
|
||||
|
||||
// Check every 30 seconds
|
||||
this.monitoringInterval = setInterval(async () => {
|
||||
@@ -156,7 +157,7 @@ class SmartValidationQueue {
|
||||
this.monitoringInterval = undefined
|
||||
}
|
||||
this.isMonitoring = false
|
||||
console.log('⏸️ Smart validation monitoring stopped')
|
||||
logger.log('⏸️ Smart validation monitoring stopped')
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -171,7 +172,7 @@ class SmartValidationQueue {
|
||||
return
|
||||
}
|
||||
|
||||
console.log(`👁️ Smart validation check: ${pending.length} pending signals`)
|
||||
logger.log(`👁️ Smart validation check: ${pending.length} pending signals`)
|
||||
|
||||
for (const signal of pending) {
|
||||
try {
|
||||
@@ -195,7 +196,7 @@ class SmartValidationQueue {
|
||||
// Check if expired (beyond entry window)
|
||||
if (ageMinutes > signal.entryWindowMinutes) {
|
||||
signal.status = 'expired'
|
||||
console.log(`⏰ Signal expired: ${signal.symbol} ${signal.direction} (${ageMinutes.toFixed(1)}min old)`)
|
||||
logger.log(`⏰ Signal expired: ${signal.symbol} ${signal.direction} (${ageMinutes.toFixed(1)}min old)`)
|
||||
|
||||
// Send Telegram notification
|
||||
await sendValidationNotification({
|
||||
@@ -215,7 +216,7 @@ class SmartValidationQueue {
|
||||
const cachedData = marketDataCache.get(signal.symbol)
|
||||
|
||||
if (!cachedData || !cachedData.currentPrice) {
|
||||
console.log(`⚠️ No price data for ${signal.symbol}, skipping validation`)
|
||||
logger.log(`⚠️ No price data for ${signal.symbol}, skipping validation`)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -237,8 +238,8 @@ class SmartValidationQueue {
|
||||
// Price moved up enough - CONFIRMED!
|
||||
signal.status = 'confirmed'
|
||||
signal.validatedAt = now
|
||||
console.log(`✅ LONG CONFIRMED: ${signal.symbol} moved +${priceChange.toFixed(2)}% ($${signal.originalPrice.toFixed(2)} → $${currentPrice.toFixed(2)})`)
|
||||
console.log(` Validation time: ${ageMinutes.toFixed(1)} minutes, executing trade...`)
|
||||
logger.log(`✅ LONG CONFIRMED: ${signal.symbol} moved +${priceChange.toFixed(2)}% ($${signal.originalPrice.toFixed(2)} → $${currentPrice.toFixed(2)})`)
|
||||
logger.log(` Validation time: ${ageMinutes.toFixed(1)} minutes, executing trade...`)
|
||||
|
||||
// Send Telegram notification
|
||||
await sendValidationNotification({
|
||||
@@ -257,8 +258,8 @@ class SmartValidationQueue {
|
||||
} else if (priceChange <= signal.maxDrawdown) {
|
||||
// Price moved down too much - ABANDON
|
||||
signal.status = 'abandoned'
|
||||
console.log(`❌ LONG ABANDONED: ${signal.symbol} dropped ${priceChange.toFixed(2)}% ($${signal.originalPrice.toFixed(2)} → $${currentPrice.toFixed(2)})`)
|
||||
console.log(` Saved from potential loser after ${ageMinutes.toFixed(1)} minutes`)
|
||||
logger.log(`❌ LONG ABANDONED: ${signal.symbol} dropped ${priceChange.toFixed(2)}% ($${signal.originalPrice.toFixed(2)} → $${currentPrice.toFixed(2)})`)
|
||||
logger.log(` Saved from potential loser after ${ageMinutes.toFixed(1)} minutes`)
|
||||
|
||||
// Send Telegram notification
|
||||
await sendValidationNotification({
|
||||
@@ -273,7 +274,7 @@ class SmartValidationQueue {
|
||||
})
|
||||
} else {
|
||||
// Still pending, log progress
|
||||
console.log(`⏳ LONG watching: ${signal.symbol} at ${priceChange.toFixed(2)}% (need +${signal.confirmationThreshold}%, abandon at ${signal.maxDrawdown}%) - ${ageMinutes.toFixed(1)}min`)
|
||||
logger.log(`⏳ LONG watching: ${signal.symbol} at ${priceChange.toFixed(2)}% (need +${signal.confirmationThreshold}%, abandon at ${signal.maxDrawdown}%) - ${ageMinutes.toFixed(1)}min`)
|
||||
}
|
||||
} else {
|
||||
// SHORT: Need price to move DOWN to confirm
|
||||
@@ -281,8 +282,8 @@ class SmartValidationQueue {
|
||||
// Price moved down enough - CONFIRMED!
|
||||
signal.status = 'confirmed'
|
||||
signal.validatedAt = now
|
||||
console.log(`✅ SHORT CONFIRMED: ${signal.symbol} moved ${priceChange.toFixed(2)}% ($${signal.originalPrice.toFixed(2)} → $${currentPrice.toFixed(2)})`)
|
||||
console.log(` Validation time: ${ageMinutes.toFixed(1)} minutes, executing trade...`)
|
||||
logger.log(`✅ SHORT CONFIRMED: ${signal.symbol} moved ${priceChange.toFixed(2)}% ($${signal.originalPrice.toFixed(2)} → $${currentPrice.toFixed(2)})`)
|
||||
logger.log(` Validation time: ${ageMinutes.toFixed(1)} minutes, executing trade...`)
|
||||
|
||||
// Send Telegram notification
|
||||
await sendValidationNotification({
|
||||
@@ -301,8 +302,8 @@ class SmartValidationQueue {
|
||||
} else if (priceChange >= -signal.maxDrawdown) {
|
||||
// Price moved up too much - ABANDON
|
||||
signal.status = 'abandoned'
|
||||
console.log(`❌ SHORT ABANDONED: ${signal.symbol} rose +${priceChange.toFixed(2)}% ($${signal.originalPrice.toFixed(2)} → $${currentPrice.toFixed(2)})`)
|
||||
console.log(` Saved from potential loser after ${ageMinutes.toFixed(1)} minutes`)
|
||||
logger.log(`❌ SHORT ABANDONED: ${signal.symbol} rose +${priceChange.toFixed(2)}% ($${signal.originalPrice.toFixed(2)} → $${currentPrice.toFixed(2)})`)
|
||||
logger.log(` Saved from potential loser after ${ageMinutes.toFixed(1)} minutes`)
|
||||
|
||||
// Send Telegram notification
|
||||
await sendValidationNotification({
|
||||
@@ -317,7 +318,7 @@ class SmartValidationQueue {
|
||||
})
|
||||
} else {
|
||||
// Still pending, log progress
|
||||
console.log(`⏳ SHORT watching: ${signal.symbol} at ${priceChange.toFixed(2)}% (need ${-signal.confirmationThreshold}%, abandon at +${-signal.maxDrawdown}%) - ${ageMinutes.toFixed(1)}min`)
|
||||
logger.log(`⏳ SHORT watching: ${signal.symbol} at ${priceChange.toFixed(2)}% (need ${-signal.confirmationThreshold}%, abandon at +${-signal.maxDrawdown}%) - ${ageMinutes.toFixed(1)}min`)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -347,9 +348,9 @@ class SmartValidationQueue {
|
||||
validationDelayMinutes: (Date.now() - signal.blockedAt) / (1000 * 60),
|
||||
}
|
||||
|
||||
console.log(`🚀 Executing validated trade: ${signal.symbol} ${signal.direction.toUpperCase()} @ $${currentPrice.toFixed(2)}`)
|
||||
console.log(` Original signal: $${signal.originalPrice.toFixed(2)}, Quality: ${signal.qualityScore}`)
|
||||
console.log(` Entry delay: ${payload.validationDelayMinutes.toFixed(1)} minutes`)
|
||||
logger.log(`🚀 Executing validated trade: ${signal.symbol} ${signal.direction.toUpperCase()} @ $${currentPrice.toFixed(2)}`)
|
||||
logger.log(` Original signal: $${signal.originalPrice.toFixed(2)}, Quality: ${signal.qualityScore}`)
|
||||
logger.log(` Entry delay: ${payload.validationDelayMinutes.toFixed(1)} minutes`)
|
||||
|
||||
const response = await fetch(executeUrl, {
|
||||
method: 'POST',
|
||||
@@ -367,9 +368,9 @@ class SmartValidationQueue {
|
||||
signal.executedAt = Date.now()
|
||||
signal.executionPrice = currentPrice
|
||||
signal.tradeId = result.trade?.id
|
||||
console.log(`✅ Trade executed successfully: ${signal.symbol} ${signal.direction}`)
|
||||
console.log(` Trade ID: ${signal.tradeId}`)
|
||||
console.log(` Entry: $${currentPrice.toFixed(2)}, Size: $${result.trade?.positionSizeUSD || 'unknown'}`)
|
||||
logger.log(`✅ Trade executed successfully: ${signal.symbol} ${signal.direction}`)
|
||||
logger.log(` Trade ID: ${signal.tradeId}`)
|
||||
logger.log(` Entry: $${currentPrice.toFixed(2)}, Size: $${result.trade?.positionSizeUSD || 'unknown'}`)
|
||||
|
||||
// Send execution notification
|
||||
const slippage = ((currentPrice - signal.originalPrice) / signal.originalPrice) * 100
|
||||
@@ -408,7 +409,7 @@ class SmartValidationQueue {
|
||||
}
|
||||
|
||||
if (cleaned > 0) {
|
||||
console.log(`🧹 Cleaned up ${cleaned} old validated signals`)
|
||||
logger.log(`🧹 Cleaned up ${cleaned} old validated signals`)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -454,5 +455,5 @@ export function getSmartValidationQueue(): SmartValidationQueue {
|
||||
|
||||
export function startSmartValidation(): void {
|
||||
const queue = getSmartValidationQueue()
|
||||
console.log('🧠 Smart validation system ready')
|
||||
logger.log('🧠 Smart validation system ready')
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
*/
|
||||
|
||||
import { getPrismaClient } from '../database/trades'
|
||||
import { logger } from '../utils/logger'
|
||||
import { initializeDriftService } from '../drift/client'
|
||||
import { getPythPriceMonitor } from '../pyth/price-monitor'
|
||||
|
||||
@@ -70,7 +71,7 @@ export class StopHuntTracker {
|
||||
}): Promise<void> {
|
||||
// Only track quality 85+ stop-outs (high-confidence trades)
|
||||
if (params.originalQualityScore < 85) {
|
||||
console.log(`⚠️ Stop hunt not tracked: Quality ${params.originalQualityScore} < 85 threshold`)
|
||||
logger.log(`⚠️ Stop hunt not tracked: Quality ${params.originalQualityScore} < 85 threshold`)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -93,9 +94,9 @@ export class StopHuntTracker {
|
||||
}
|
||||
})
|
||||
|
||||
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()})`)
|
||||
logger.log(`🎯 STOP HUNT RECORDED: ${params.symbol} ${params.direction.toUpperCase()}`)
|
||||
logger.log(` Quality: ${params.originalQualityScore}, Loss: $${params.stopLossAmount.toFixed(2)}`)
|
||||
logger.log(` Revenge window: 4 hours (expires ${revengeExpiresAt.toLocaleTimeString()})`)
|
||||
|
||||
// Start monitoring if not already running
|
||||
if (!this.isMonitoring) {
|
||||
@@ -113,7 +114,7 @@ export class StopHuntTracker {
|
||||
if (this.isMonitoring) return
|
||||
|
||||
this.isMonitoring = true
|
||||
console.log('🔍 Stop Hunt Revenge Tracker: Monitoring started')
|
||||
logger.log('🔍 Stop Hunt Revenge Tracker: Monitoring started')
|
||||
|
||||
// Check every 30 seconds
|
||||
monitoringInterval = setInterval(async () => {
|
||||
@@ -130,7 +131,7 @@ export class StopHuntTracker {
|
||||
monitoringInterval = null
|
||||
}
|
||||
this.isMonitoring = false
|
||||
console.log('🛑 Stop Hunt Revenge Tracker: Monitoring stopped')
|
||||
logger.log('🛑 Stop Hunt Revenge Tracker: Monitoring stopped')
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -152,13 +153,13 @@ export class StopHuntTracker {
|
||||
if (activeStopHunts.length === 0) {
|
||||
// No active stop hunts, stop monitoring to save resources
|
||||
if (this.isMonitoring) {
|
||||
console.log('📊 No active stop hunts - pausing monitoring')
|
||||
logger.log('📊 No active stop hunts - pausing monitoring')
|
||||
this.stopMonitoring()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
console.log(`🔍 Checking ${activeStopHunts.length} active stop hunt(s)...`)
|
||||
logger.log(`🔍 Checking ${activeStopHunts.length} active stop hunt(s)...`)
|
||||
|
||||
for (const stopHunt of activeStopHunts) {
|
||||
await this.checkStopHunt(stopHunt as StopHuntRecord)
|
||||
@@ -211,7 +212,7 @@ export class StopHuntTracker {
|
||||
const shouldRevenge = await this.shouldExecuteRevenge(stopHunt, currentPrice)
|
||||
|
||||
if (shouldRevenge) {
|
||||
console.log(`🔥 REVENGE CONDITIONS MET: ${stopHunt.symbol} ${stopHunt.direction.toUpperCase()}`)
|
||||
logger.log(`🔥 REVENGE CONDITIONS MET: ${stopHunt.symbol} ${stopHunt.direction.toUpperCase()}`)
|
||||
await this.executeRevengeTrade(stopHunt, currentPrice)
|
||||
}
|
||||
|
||||
@@ -260,7 +261,7 @@ export class StopHuntTracker {
|
||||
lowestInZone: currentPrice,
|
||||
}
|
||||
})
|
||||
console.log(` ⏱️ LONG revenge zone entered at ${currentPrice.toFixed(2)}, waiting for 90s confirmation...`)
|
||||
logger.log(` ⏱️ LONG revenge zone entered at ${currentPrice.toFixed(2)}, waiting for 90s confirmation...`)
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -274,17 +275,17 @@ export class StopHuntTracker {
|
||||
// Check if we've been in zone for 90+ seconds (1.5 minutes)
|
||||
const timeInZone = now - stopHunt.firstCrossTime.getTime()
|
||||
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)}`)
|
||||
logger.log(` ✅ LONG revenge: Price held below entry for ${(timeInZone/60000).toFixed(1)}min, confirmed!`)
|
||||
logger.log(` Entry ${originalEntryPrice.toFixed(2)} → Current ${currentPrice.toFixed(2)}`)
|
||||
return true
|
||||
} else {
|
||||
console.log(` ⏱️ LONG revenge: ${(timeInZone/60000).toFixed(1)}min in zone (need 1.5min)`)
|
||||
logger.log(` ⏱️ LONG revenge: ${(timeInZone/60000).toFixed(1)}min in zone (need 1.5min)`)
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
// Price left revenge zone - reset timer and increment counter
|
||||
if (stopHunt.firstCrossTime) {
|
||||
console.log(` ❌ LONG revenge: Price bounced back up to ${currentPrice.toFixed(2)}, resetting timer`)
|
||||
logger.log(` ❌ LONG revenge: Price bounced back up to ${currentPrice.toFixed(2)}, resetting timer`)
|
||||
await this.prisma.stopHunt.update({
|
||||
where: { id: stopHunt.id },
|
||||
data: {
|
||||
@@ -310,7 +311,7 @@ export class StopHuntTracker {
|
||||
highestInZone: currentPrice,
|
||||
}
|
||||
})
|
||||
console.log(` ⏱️ SHORT revenge zone entered at ${currentPrice.toFixed(2)}, waiting for 90s confirmation...`)
|
||||
logger.log(` ⏱️ SHORT revenge zone entered at ${currentPrice.toFixed(2)}, waiting for 90s confirmation...`)
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -324,17 +325,17 @@ export class StopHuntTracker {
|
||||
// Check if we've been in zone for 90+ seconds (1.5 minutes)
|
||||
const timeInZone = now - stopHunt.firstCrossTime.getTime()
|
||||
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)}`)
|
||||
logger.log(` ✅ SHORT revenge: Price held above entry for ${(timeInZone/60000).toFixed(1)}min, confirmed!`)
|
||||
logger.log(` Entry ${originalEntryPrice.toFixed(2)} → Current ${currentPrice.toFixed(2)}`)
|
||||
return true
|
||||
} else {
|
||||
console.log(` ⏱️ SHORT revenge: ${(timeInZone/60000).toFixed(1)}min in zone (need 1.5min)`)
|
||||
logger.log(` ⏱️ SHORT revenge: ${(timeInZone/60000).toFixed(1)}min in zone (need 1.5min)`)
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
// Price left revenge zone - reset timer and increment counter
|
||||
if (stopHunt.firstCrossTime) {
|
||||
console.log(` ❌ SHORT revenge: Price dropped back to ${currentPrice.toFixed(2)}, resetting timer`)
|
||||
logger.log(` ❌ SHORT revenge: Price dropped back to ${currentPrice.toFixed(2)}, resetting timer`)
|
||||
await this.prisma.stopHunt.update({
|
||||
where: { id: stopHunt.id },
|
||||
data: {
|
||||
@@ -354,9 +355,9 @@ export class StopHuntTracker {
|
||||
*/
|
||||
private async executeRevengeTrade(stopHunt: StopHuntRecord, currentPrice: number): Promise<void> {
|
||||
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!)`)
|
||||
logger.log(`🔥 EXECUTING REVENGE TRADE: ${stopHunt.symbol} ${stopHunt.direction.toUpperCase()}`)
|
||||
logger.log(` Original loss: $${stopHunt.stopLossAmount.toFixed(2)}`)
|
||||
logger.log(` Revenge size: 1.2x (getting our money back!)`)
|
||||
|
||||
// CRITICAL: Validate current ADX from 1-minute data cache
|
||||
// Block revenge if trend has weakened (ADX < 20)
|
||||
@@ -368,10 +369,10 @@ export class StopHuntTracker {
|
||||
const currentADX = cachedData.adx
|
||||
const dataAge = Date.now() - cachedData.timestamp
|
||||
|
||||
console.log(` 📊 Fresh ADX check: ${currentADX.toFixed(1)} (${(dataAge/1000).toFixed(0)}s old)`)
|
||||
logger.log(` 📊 Fresh ADX check: ${currentADX.toFixed(1)} (${(dataAge/1000).toFixed(0)}s old)`)
|
||||
|
||||
if (currentADX < 20) {
|
||||
console.log(` ❌ REVENGE BLOCKED: ADX ${currentADX.toFixed(1)} < 20 (weak trend, not worth re-entry)`)
|
||||
logger.log(` ❌ REVENGE BLOCKED: ADX ${currentADX.toFixed(1)} < 20 (weak trend, not worth re-entry)`)
|
||||
|
||||
// Update database with failed reason
|
||||
await this.prisma.stopHunt.update({
|
||||
@@ -397,10 +398,10 @@ export class StopHuntTracker {
|
||||
return
|
||||
}
|
||||
|
||||
console.log(` ✅ ADX validation passed: ${currentADX.toFixed(1)} ≥ 20 (strong trend)`)
|
||||
logger.log(` ✅ ADX validation passed: ${currentADX.toFixed(1)} ≥ 20 (strong trend)`)
|
||||
} else {
|
||||
console.log(` ⚠️ No fresh ADX data (cache age: ${cachedData ? (Date.now() - cachedData.timestamp)/1000 : 'N/A'}s)`)
|
||||
console.log(` ⚠️ Proceeding with revenge but using original ADX ${stopHunt.originalADX}`)
|
||||
logger.log(` ⚠️ No fresh ADX data (cache age: ${cachedData ? (Date.now() - cachedData.timestamp)/1000 : 'N/A'}s)`)
|
||||
logger.log(` ⚠️ Proceeding with revenge but using original ADX ${stopHunt.originalADX}`)
|
||||
}
|
||||
|
||||
// Call execute endpoint with revenge parameters
|
||||
@@ -454,9 +455,9 @@ export class StopHuntTracker {
|
||||
}
|
||||
})
|
||||
|
||||
console.log(`✅ REVENGE TRADE EXECUTED: ${result.trade?.id}`)
|
||||
console.log(`📊 SL Distance: $${Math.abs(slDistance).toFixed(2)} (${stopHunt.originalATR ? `${(Math.abs(slDistance) / stopHunt.originalATR).toFixed(2)}× ATR` : 'no ATR'})`)
|
||||
console.log(`🔥 LET'S GET OUR MONEY BACK!`)
|
||||
logger.log(`✅ REVENGE TRADE EXECUTED: ${result.trade?.id}`)
|
||||
logger.log(`📊 SL Distance: $${Math.abs(slDistance).toFixed(2)} (${stopHunt.originalATR ? `${(Math.abs(slDistance) / stopHunt.originalATR).toFixed(2)}× ATR` : 'no ATR'})`)
|
||||
logger.log(`🔥 LET'S GET OUR MONEY BACK!`)
|
||||
|
||||
// Send special Telegram notification
|
||||
await this.sendRevengeNotification(stopHunt, result.trade)
|
||||
@@ -524,7 +525,7 @@ Reversal Confirmed: Price crossed back through entry
|
||||
})
|
||||
|
||||
if (!stopHunt) {
|
||||
console.log(`⚠️ No stop hunt found for revenge trade ${params.revengeTradeId}`)
|
||||
logger.log(`⚠️ No stop hunt found for revenge trade ${params.revengeTradeId}`)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -538,10 +539,10 @@ Reversal Confirmed: Price crossed back through entry
|
||||
})
|
||||
|
||||
const emoji = params.outcome.includes('TP') ? '✅' : '❌'
|
||||
console.log(`${emoji} REVENGE OUTCOME: ${params.outcome} (${params.pnl >= 0 ? '+' : ''}$${params.pnl.toFixed(2)})`)
|
||||
logger.log(`${emoji} REVENGE OUTCOME: ${params.outcome} (${params.pnl >= 0 ? '+' : ''}$${params.pnl.toFixed(2)})`)
|
||||
|
||||
if (params.failedReason) {
|
||||
console.log(` Reason: ${params.failedReason}`)
|
||||
logger.log(` Reason: ${params.failedReason}`)
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
@@ -568,7 +569,7 @@ Reversal Confirmed: Price crossed back through entry
|
||||
})
|
||||
|
||||
if (expired.count > 0) {
|
||||
console.log(`⏰ Expired ${expired.count} stop hunt revenge window(s)`)
|
||||
logger.log(`⏰ Expired ${expired.count} stop hunt revenge window(s)`)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('❌ Error expiring stop hunts:', error)
|
||||
@@ -605,10 +606,10 @@ export async function startStopHuntTracking(): Promise<void> {
|
||||
})
|
||||
|
||||
if (activeCount > 0) {
|
||||
console.log(`🎯 Found ${activeCount} active stop hunt(s) - starting revenge tracker`)
|
||||
logger.log(`🎯 Found ${activeCount} active stop hunt(s) - starting revenge tracker`)
|
||||
tracker.startMonitoring()
|
||||
} else {
|
||||
console.log('📊 No active stop hunts - tracker will start when needed')
|
||||
logger.log('📊 No active stop hunts - tracker will start when needed')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('❌ Error starting stop hunt tracker:', error)
|
||||
|
||||
Reference in New Issue
Block a user