Major fixes for browser automation resource management: - Chromium processes accumulating over time during automated trading - Resource consumption growing after extended automation cycles - Incomplete cleanup during analysis operations New Components: - lib/enhanced-screenshot-robust.ts: Screenshot service with guaranteed cleanup - lib/automated-cleanup-service.ts: Background process monitoring - lib/auto-trading-service.ts: Comprehensive trading automation - ROBUST_CLEANUP_IMPLEMENTATION.md: Complete documentation - Finally blocks guarantee cleanup execution even during errors - Active session tracking prevents orphaned browser instances - Multiple kill strategies (graceful → force → process cleanup) - Timeout protection prevents hanging cleanup operations - Background monitoring every 30s catches missed processes - lib/aggressive-cleanup.ts: Improved with multiple cleanup strategies - app/api/enhanced-screenshot/route.js: Added finally block guarantees - lib/automation-service.ts: Updated for integration - validate-robust-cleanup.js: Implementation validation - test-robust-cleanup.js: Comprehensive cleanup testing The Chromium process accumulation issue is now resolved with guaranteed cleanup!
264 lines
9.1 KiB
TypeScript
264 lines
9.1 KiB
TypeScript
import { enhancedScreenshotService } from './enhanced-screenshot-robust'
|
|
import { aiAnalysisService } from './ai-analysis'
|
|
import { automatedCleanupService } from './automated-cleanup-service'
|
|
import aggressiveCleanup from './aggressive-cleanup'
|
|
|
|
export interface TradingConfig {
|
|
symbol: string
|
|
timeframes: string[]
|
|
layouts: string[]
|
|
intervalMs: number // How often to run analysis
|
|
maxCycles?: number // Optional limit for testing
|
|
}
|
|
|
|
export class AutoTradingService {
|
|
private isRunning = false
|
|
private currentCycle = 0
|
|
private config: TradingConfig | null = null
|
|
|
|
async start(config: TradingConfig) {
|
|
if (this.isRunning) {
|
|
console.log('⚠️ Trading service already running')
|
|
return
|
|
}
|
|
|
|
this.isRunning = true
|
|
this.config = config
|
|
this.currentCycle = 0
|
|
|
|
console.log('🚀 Starting automated trading service with robust cleanup...')
|
|
console.log('🔧 Config:', config)
|
|
|
|
// Start background cleanup service
|
|
automatedCleanupService.start(30000) // Every 30 seconds
|
|
|
|
// Start aggressive cleanup system
|
|
aggressiveCleanup.startPeriodicCleanup()
|
|
|
|
try {
|
|
while (this.isRunning && (!config.maxCycles || this.currentCycle < config.maxCycles)) {
|
|
this.currentCycle++
|
|
console.log(`\n🔄 === TRADING CYCLE ${this.currentCycle} ===`)
|
|
|
|
await this.runTradingCycle(config)
|
|
|
|
if (this.isRunning) {
|
|
console.log(`⏳ Waiting ${config.intervalMs/1000} seconds until next cycle...`)
|
|
await new Promise(resolve => setTimeout(resolve, config.intervalMs))
|
|
}
|
|
}
|
|
|
|
if (config.maxCycles && this.currentCycle >= config.maxCycles) {
|
|
console.log(`✅ Completed ${this.currentCycle} cycles (reached max limit)`)
|
|
}
|
|
|
|
} catch (error) {
|
|
console.error('❌ Trading service error:', error)
|
|
} finally {
|
|
await this.stop()
|
|
}
|
|
}
|
|
|
|
async stop() {
|
|
console.log('🛑 Stopping automated trading service...')
|
|
this.isRunning = false
|
|
|
|
// Stop cleanup services
|
|
automatedCleanupService.stop()
|
|
aggressiveCleanup.stop()
|
|
|
|
// Force cleanup all browser sessions
|
|
try {
|
|
await enhancedScreenshotService.cleanup()
|
|
await aggressiveCleanup.forceCleanup()
|
|
console.log('✅ Trading service stopped and cleaned up')
|
|
} catch (cleanupError) {
|
|
console.error('❌ Error during final cleanup:', cleanupError)
|
|
}
|
|
}
|
|
|
|
private async runTradingCycle(config: TradingConfig): Promise<void> {
|
|
console.log(`🔄 Running trading cycle ${this.currentCycle}...`)
|
|
|
|
// Process each timeframe sequentially to avoid resource conflicts
|
|
for (const timeframe of config.timeframes) {
|
|
if (!this.isRunning) break // Check if service was stopped
|
|
|
|
try {
|
|
console.log(`\n📊 Processing ${config.symbol} ${timeframe}...`)
|
|
|
|
// Capture screenshots with robust cleanup
|
|
const screenshots = await enhancedScreenshotService.captureWithLogin({
|
|
symbol: config.symbol,
|
|
timeframe: timeframe,
|
|
layouts: config.layouts,
|
|
analyze: false // We'll analyze separately
|
|
})
|
|
|
|
if (screenshots.length > 0) {
|
|
console.log(`✅ Captured ${screenshots.length} screenshots for ${timeframe}`)
|
|
|
|
// Analyze screenshots
|
|
try {
|
|
let analysis = null
|
|
if (screenshots.length === 1) {
|
|
analysis = await aiAnalysisService.analyzeScreenshot(screenshots[0])
|
|
} else if (screenshots.length > 1) {
|
|
analysis = await aiAnalysisService.analyzeMultipleScreenshots(screenshots)
|
|
}
|
|
|
|
if (analysis) {
|
|
console.log(`✅ Analysis completed for ${timeframe}`)
|
|
console.log(`📈 Sentiment: ${analysis.marketSentiment || 'Unknown'}`)
|
|
console.log(`🎯 Recommendation: ${analysis.recommendation}, Confidence: ${analysis.confidence}%`)
|
|
|
|
// Here you would implement your trading logic based on analysis
|
|
await this.processAnalysisForTrading(analysis, config.symbol, timeframe)
|
|
} else {
|
|
console.warn(`⚠️ No analysis returned for ${timeframe}`)
|
|
}
|
|
} catch (analysisError) {
|
|
console.error(`❌ Analysis failed for ${timeframe}:`, analysisError)
|
|
}
|
|
} else {
|
|
console.error(`❌ No screenshots captured for ${timeframe}`)
|
|
}
|
|
|
|
// Small delay between timeframes to prevent overwhelming the system
|
|
if (config.timeframes.indexOf(timeframe) < config.timeframes.length - 1) {
|
|
console.log('⏳ Brief pause before next timeframe...')
|
|
await new Promise(resolve => setTimeout(resolve, 5000))
|
|
}
|
|
|
|
} catch (error) {
|
|
console.error(`❌ Error processing ${timeframe}:`, error)
|
|
// Continue with next timeframe even if one fails
|
|
}
|
|
}
|
|
|
|
console.log(`✅ Trading cycle ${this.currentCycle} completed`)
|
|
|
|
// Run post-cycle cleanup to ensure no browser processes are left running
|
|
try {
|
|
await aggressiveCleanup.runPostAnalysisCleanup()
|
|
} catch (cleanupError) {
|
|
console.error('❌ Error in post-cycle cleanup:', cleanupError)
|
|
}
|
|
}
|
|
|
|
private async processAnalysisForTrading(analysis: any, symbol: string, timeframe: string): Promise<void> {
|
|
try {
|
|
console.log(`🎯 Processing trading analysis for ${symbol} ${timeframe}...`)
|
|
|
|
// Extract key trading signals from analysis
|
|
const sentiment = analysis.marketSentiment
|
|
const confidence = analysis.confidence || 0
|
|
const recommendation = analysis.recommendation
|
|
|
|
console.log(`📊 Sentiment: ${sentiment}, Recommendation: ${recommendation}, Confidence: ${confidence}%`)
|
|
|
|
if (analysis.keyLevels) {
|
|
console.log(`📈 Support levels: ${analysis.keyLevels.support.join(', ')}`)
|
|
console.log(`📉 Resistance levels: ${analysis.keyLevels.resistance.join(', ')}`)
|
|
}
|
|
|
|
// Trading decision logic
|
|
if (confidence >= 70) {
|
|
if (sentiment === 'BULLISH' && recommendation === 'BUY') {
|
|
console.log('🟢 HIGH CONFIDENCE BULLISH - Consider long position')
|
|
// await this.executeLongTrade(symbol, timeframe, analysis)
|
|
} else if (sentiment === 'BEARISH' && recommendation === 'SELL') {
|
|
console.log('🔴 HIGH CONFIDENCE BEARISH - Consider short position')
|
|
// await this.executeShortTrade(symbol, timeframe, analysis)
|
|
}
|
|
} else if (confidence >= 50) {
|
|
console.log('🟡 MODERATE CONFIDENCE - Monitor for confirmation')
|
|
} else {
|
|
console.log('⚪ LOW CONFIDENCE - Hold current positions')
|
|
}
|
|
|
|
} catch (error) {
|
|
console.error('❌ Error processing trading analysis:', error)
|
|
}
|
|
}
|
|
|
|
// Example trading execution methods (implement with your preferred exchange)
|
|
private async executeLongTrade(symbol: string, timeframe: string, analysis: any): Promise<void> {
|
|
console.log(`🟢 Executing LONG trade for ${symbol} based on ${timeframe} analysis`)
|
|
// Implement actual trading logic here
|
|
// This could use Drift Protocol, Jupiter DEX, or other trading interfaces
|
|
}
|
|
|
|
private async executeShortTrade(symbol: string, timeframe: string, analysis: any): Promise<void> {
|
|
console.log(`🔴 Executing SHORT trade for ${symbol} based on ${timeframe} analysis`)
|
|
// Implement actual trading logic here
|
|
}
|
|
|
|
// Status methods
|
|
getStatus() {
|
|
return {
|
|
isRunning: this.isRunning,
|
|
currentCycle: this.currentCycle,
|
|
config: this.config
|
|
}
|
|
}
|
|
|
|
getCurrentCycle(): number {
|
|
return this.currentCycle
|
|
}
|
|
|
|
isServiceRunning(): boolean {
|
|
return this.isRunning
|
|
}
|
|
}
|
|
|
|
// Example usage configurations
|
|
export const TRADING_CONFIGS = {
|
|
// Scalping configuration - frequent analysis of short timeframes
|
|
scalping: {
|
|
symbol: 'SOLUSD',
|
|
timeframes: ['5m', '15m'],
|
|
layouts: ['ai', 'diy'],
|
|
intervalMs: 5 * 60 * 1000, // Every 5 minutes
|
|
maxCycles: 100 // Limit for testing
|
|
},
|
|
|
|
// Day trading configuration - moderate frequency on intraday timeframes
|
|
dayTrading: {
|
|
symbol: 'SOLUSD',
|
|
timeframes: ['1h', '4h'],
|
|
layouts: ['ai', 'diy'],
|
|
intervalMs: 30 * 60 * 1000, // Every 30 minutes
|
|
maxCycles: 50 // Limit for testing
|
|
},
|
|
|
|
// Swing trading configuration - less frequent analysis of longer timeframes
|
|
swingTrading: {
|
|
symbol: 'SOLUSD',
|
|
timeframes: ['4h', '1d'],
|
|
layouts: ['ai', 'diy'],
|
|
intervalMs: 2 * 60 * 60 * 1000, // Every 2 hours
|
|
maxCycles: 24 // Limit for testing
|
|
}
|
|
}
|
|
|
|
// Singleton instance
|
|
export const autoTradingService = new AutoTradingService()
|
|
|
|
// Example startup function
|
|
export async function startTradingBot(configName: keyof typeof TRADING_CONFIGS = 'dayTrading') {
|
|
const config = TRADING_CONFIGS[configName]
|
|
if (!config) {
|
|
throw new Error(`Unknown trading configuration: ${configName}`)
|
|
}
|
|
|
|
console.log(`🚀 Starting trading bot with ${configName} configuration`)
|
|
await autoTradingService.start(config)
|
|
}
|
|
|
|
// Graceful shutdown
|
|
export async function stopTradingBot() {
|
|
console.log('🛑 Stopping trading bot...')
|
|
await autoTradingService.stop()
|
|
}
|